From db0cd34b2422973ae598f195cd119bcabb95d9c6 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 21 Oct 2020 20:57:05 +0200 Subject: [PATCH 001/118] Bumped version to 0.117.0b0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 1a09efa5c4c..0b5b3f15a58 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "0.dev0" +PATCH_VERSION = "0b0" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 1ea103f090ed594b02c8d3e34ffb6c7a6647a38c Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 21 Oct 2020 15:35:20 -0600 Subject: [PATCH 002/118] Remove coordinator success from SimpliSafe availability calculation (#42180) --- homeassistant/components/simplisafe/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index dba17e6ef93..e81559bd1fa 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -622,11 +622,7 @@ class SimpliSafeEntity(CoordinatorEntity): # the entity as available if: # 1. We can verify that the system is online (assuming True if we can't) # 2. We can verify that the entity is online - return ( - self.coordinator.last_update_success - and not (self._system.version == 3 and self._system.offline) - and self._online - ) + return not (self._system.version == 3 and self._system.offline) and self._online @property def device_info(self): From bfd943c5dc367c8a1cc233d63abbe6f4028bdb14 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 22 Oct 2020 00:17:13 +0200 Subject: [PATCH 003/118] Updated frontend to 20201021.1 (#42185) --- 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 17a65a86b90..e04e25520ba 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==20201021.0"], + "requirements": ["home-assistant-frontend==20201021.1"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 059afd441fa..287cd336d82 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -13,7 +13,7 @@ defusedxml==0.6.0 distro==1.5.0 emoji==0.5.4 hass-nabucasa==0.37.1 -home-assistant-frontend==20201021.0 +home-assistant-frontend==20201021.1 httpx==0.16.1 importlib-metadata==1.6.0;python_version<'3.8' jinja2>=2.11.2 diff --git a/requirements_all.txt b/requirements_all.txt index 02fa4d96e85..e84eafb78b6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -759,7 +759,7 @@ hole==0.5.1 holidays==0.10.3 # homeassistant.components.frontend -home-assistant-frontend==20201021.0 +home-assistant-frontend==20201021.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0f5dea0c51e..a32f9ddfab6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -385,7 +385,7 @@ hole==0.5.1 holidays==0.10.3 # homeassistant.components.frontend -home-assistant-frontend==20201021.0 +home-assistant-frontend==20201021.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 From a83a5a26030b9ac2d2b8655b37602af19fa15cfe Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Thu, 22 Oct 2020 05:36:54 +0800 Subject: [PATCH 004/118] Remove duplicated finalize_stream code in stream (#42171) --- homeassistant/components/stream/worker.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/homeassistant/components/stream/worker.py b/homeassistant/components/stream/worker.py index 6893c69ee3f..6769413bafd 100644 --- a/homeassistant/components/stream/worker.py +++ b/homeassistant/components/stream/worker.py @@ -160,10 +160,6 @@ def _stream_worker_internal(hass, stream, quit_event): audio_stream = None except (av.AVError, StopIteration) as ex: - if not stream.keepalive: - # End of stream, clear listeners and stop thread - for fmt, _ in outputs.items(): - hass.loop.call_soon_threadsafe(stream.outputs[fmt].put, None) _LOGGER.error( "Error demuxing stream while finding first packet: %s", str(ex) ) From 66453429b07c9920420136908577969fa5d8524e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 21 Oct 2020 15:24:50 -0500 Subject: [PATCH 005/118] Avoid overloading the executor with service.yaml loads (#42172) --- homeassistant/helpers/service.py | 34 +++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 20f7aa2d2d7..d03fb8c91c6 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -38,7 +38,7 @@ from homeassistant.helpers import template import homeassistant.helpers.config_validation as cv from homeassistant.helpers.template import Template from homeassistant.helpers.typing import ConfigType, HomeAssistantType, TemplateVarsType -from homeassistant.loader import async_get_integration, bind_hass +from homeassistant.loader import Integration, async_get_integration, bind_hass from homeassistant.util.yaml import load_yaml from homeassistant.util.yaml.loader import JSON_TYPE @@ -252,21 +252,29 @@ async def async_extract_entity_ids( return extracted -async def _load_services_file(hass: HomeAssistantType, domain: str) -> JSON_TYPE: +def _load_services_file(hass: HomeAssistantType, integration: Integration) -> JSON_TYPE: """Load services file for an integration.""" - integration = await async_get_integration(hass, domain) try: - return await hass.async_add_executor_job( - load_yaml, str(integration.file_path / "services.yaml") - ) + return load_yaml(str(integration.file_path / "services.yaml")) except FileNotFoundError: - _LOGGER.warning("Unable to find services.yaml for the %s integration", domain) + _LOGGER.warning( + "Unable to find services.yaml for the %s integration", integration.domain + ) return {} except HomeAssistantError: - _LOGGER.warning("Unable to parse services.yaml for the %s integration", domain) + _LOGGER.warning( + "Unable to parse services.yaml for the %s integration", integration.domain + ) return {} +def _load_services_files( + hass: HomeAssistantType, integrations: Iterable[Integration] +) -> List[JSON_TYPE]: + """Load service files for multiple intergrations.""" + return [_load_services_file(hass, integration) for integration in integrations] + + @bind_hass async def async_get_all_descriptions( hass: HomeAssistantType, @@ -289,8 +297,12 @@ async def async_get_all_descriptions( loaded = {} if missing: - contents = await asyncio.gather( - *(_load_services_file(hass, domain) for domain in missing) + integrations = await asyncio.gather( + *(async_get_integration(hass, domain) for domain in missing) + ) + + contents = await hass.async_add_executor_job( + _load_services_files, hass, integrations ) for domain, content in zip(missing, contents): @@ -308,7 +320,7 @@ async def async_get_all_descriptions( # Cache missing descriptions if description is None: domain_yaml = loaded[domain] - yaml_description = domain_yaml.get(service, {}) + yaml_description = domain_yaml.get(service, {}) # type: ignore # Don't warn for missing services, because it triggers false # positives for things like scripts, that register as a service From a8342d93954f6c7ba15d092dea58170634745ab7 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 21 Oct 2020 15:35:48 -0600 Subject: [PATCH 006/118] Bump simplisafe-python to 9.5.1 (#42179) --- homeassistant/components/simplisafe/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json index 613187ef744..09682352576 100644 --- a/homeassistant/components/simplisafe/manifest.json +++ b/homeassistant/components/simplisafe/manifest.json @@ -3,6 +3,6 @@ "name": "SimpliSafe", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/simplisafe", - "requirements": ["simplisafe-python==9.4.1"], + "requirements": ["simplisafe-python==9.5.1"], "codeowners": ["@bachya"] } diff --git a/requirements_all.txt b/requirements_all.txt index e84eafb78b6..bf515d1d99d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2017,7 +2017,7 @@ simplehound==0.3 simplepush==1.1.4 # homeassistant.components.simplisafe -simplisafe-python==9.4.1 +simplisafe-python==9.5.1 # homeassistant.components.sisyphus sisyphus-control==2.2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a32f9ddfab6..5e7e53ef335 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -959,7 +959,7 @@ sharkiqpy==0.1.8 simplehound==0.3 # homeassistant.components.simplisafe -simplisafe-python==9.4.1 +simplisafe-python==9.5.1 # homeassistant.components.slack slackclient==2.5.0 From f1de6bf6b4c0e7e5fc8f66fb722fd9de3f7bd57a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 22 Oct 2020 00:24:25 +0200 Subject: [PATCH 007/118] Bumped version to 0.117.0b1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 0b5b3f15a58..0eb828c5b1a 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "0b0" +PATCH_VERSION = "0b1" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 3eece977f83466a717fdc4c2428cf01faaa861c9 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Thu, 22 Oct 2020 00:09:52 +0000 Subject: [PATCH 008/118] [ci skip] Translation update --- .../accuweather/translations/pl.json | 2 +- .../adguard/translations/zh-Hans.json | 3 ++ .../advantage_air/translations/zh-Hans.json | 11 ++++++ .../agent_dvr/translations/zh-Hans.json | 7 ++++ .../airvisual/translations/zh-Hans.json | 7 ++++ .../alarmdecoder/translations/de.json | 1 + .../alarmdecoder/translations/zh-Hans.json | 14 ++++++++ .../ambiclimate/translations/zh-Hans.json | 8 +++++ .../components/atag/translations/zh-Hans.json | 7 ++++ .../august/translations/zh-Hans.json | 11 ++++++ .../automation/translations/pl.json | 4 +-- .../azure_devops/translations/de.json | 15 ++++++++ .../binary_sensor/translations/pl.json | 4 +-- .../binary_sensor/translations/zh-Hans.json | 7 +++- .../blink/translations/zh-Hans.json | 11 ++++++ .../braviatv/translations/zh-Hans.json | 9 +++++ .../components/broadlink/translations/de.json | 9 ++++- .../brother/translations/zh-Hans.json | 8 +++++ .../bsblan/translations/zh-Hans.json | 7 ++++ .../components/calendar/translations/pl.json | 4 +-- .../canary/translations/zh-Hans.json | 12 +++++++ .../cloudflare/translations/ca.json | 35 +++++++++++++++++++ .../cloudflare/translations/cs.json | 34 ++++++++++++++++++ .../cloudflare/translations/en.json | 35 +++++++++++++++++++ .../cloudflare/translations/et.json | 34 ++++++++++++++++++ .../cloudflare/translations/pl.json | 35 +++++++++++++++++++ .../cloudflare/translations/ru.json | 35 +++++++++++++++++++ .../configurator/translations/pl.json | 2 +- .../control4/translations/zh-Hans.json | 11 ++++++ .../coolmaster/translations/zh-Hans.json | 14 ++++++++ .../cover/translations/zh-Hans.json | 8 +++++ .../daikin/translations/zh-Hans.json | 5 +++ .../deconz/translations/zh-Hans.json | 3 +- .../dexcom/translations/zh-Hans.json | 4 ++- .../doorbird/translations/zh-Hans.json | 11 ++++++ .../elgato/translations/zh-Hans.json | 10 ++++++ .../components/fan/translations/pl.json | 4 +-- .../components/fan/translations/zh-Hans.json | 2 +- .../components/flo/translations/zh-Hans.json | 11 ++++++ .../flume/translations/zh-Hans.json | 11 ++++++ .../fritzbox/translations/zh-Hans.json | 19 ++++++++++ .../garmin_connect/translations/zh-Hans.json | 11 ++++++ .../glances/translations/zh-Hans.json | 11 ++++++ .../gogogate2/translations/zh-Hans.json | 11 ++++++ .../components/gree/translations/zh-Hans.json | 3 ++ .../components/group/translations/pl.json | 4 +-- .../guardian/translations/zh-Hans.json | 7 ++++ .../hlk_sw16/translations/zh-Hans.json | 11 ++++++ .../components/homekit/translations/nl.json | 8 ++++- .../homematicip_cloud/translations/de.json | 1 + .../huawei_lte/translations/zh-Hans.json | 9 +++++ .../components/hue/translations/et.json | 2 +- .../components/hue/translations/zh-Hans.json | 5 +++ .../humidifier/translations/pl.json | 4 +-- .../humidifier/translations/zh-Hans.json | 7 ++++ .../hvv_departures/translations/zh-Hans.json | 11 ++++++ .../iaqualink/translations/zh-Hans.json | 12 +++++++ .../icloud/translations/zh-Hans.json | 8 ++++- .../input_boolean/translations/pl.json | 4 +-- .../components/insteon/translations/de.json | 3 ++ .../components/insteon/translations/et.json | 3 +- .../insteon/translations/zh-Hans.json | 16 +++++++++ .../components/ipp/translations/zh-Hans.json | 10 ++++++ .../isy994/translations/zh-Hans.json | 11 ++++++ .../components/kodi/translations/de.json | 9 +++-- .../components/kodi/translations/zh-Hans.json | 11 ++++++ .../components/konnected/translations/nl.json | 5 ++- .../life360/translations/zh-Hans.json | 4 +++ .../components/light/translations/pl.json | 4 +-- .../media_player/translations/pl.json | 4 +-- .../meteo_france/translations/de.json | 9 +++++ .../metoffice/translations/zh-Hans.json | 16 +++++++++ .../components/mill/translations/zh-Hans.json | 11 ++++++ .../components/mqtt/translations/zh-Hans.json | 9 +++++ .../components/myq/translations/zh-Hans.json | 11 ++++++ .../components/neato/translations/et.json | 4 ++- .../neato/translations/zh-Hans.json | 11 ++++++ .../components/nest/translations/ca.json | 6 ++++ .../components/nest/translations/cs.json | 6 ++++ .../components/nest/translations/en.json | 6 ++++ .../components/nest/translations/et.json | 3 ++ .../components/nest/translations/pl.json | 6 ++++ .../components/nest/translations/ru.json | 6 ++++ .../components/nest/translations/zh-Hans.json | 1 + .../components/nest/translations/zh-Hant.json | 6 ++++ .../components/netatmo/translations/et.json | 5 +++ .../nexia/translations/zh-Hans.json | 11 ++++++ .../nuheat/translations/zh-Hans.json | 11 ++++++ .../components/nut/translations/zh-Hans.json | 11 ++++++ .../components/nzbget/translations/de.json | 3 +- .../nzbget/translations/zh-Hans.json | 11 ++++++ .../omnilogic/translations/zh-Hans.json | 12 +++++++ .../onvif/translations/zh-Hans.json | 9 +++++ .../openweathermap/translations/de.json | 6 ++-- .../ovo_energy/translations/zh-Hans.json | 15 ++++++++ .../components/plex/translations/nl.json | 1 + .../components/plugwise/translations/de.json | 9 +++++ .../plugwise/translations/zh-Hans.json | 12 +++++++ .../plum_lightpad/translations/zh-Hans.json | 11 ++++++ .../components/point/translations/et.json | 2 +- .../components/ps4/translations/zh-Hans.json | 1 + .../rainmachine/translations/et.json | 3 +- .../components/remote/translations/pl.json | 4 +-- .../remote/translations/zh-Hans.json | 13 +++++++ .../components/rfxtrx/translations/de.json | 12 +++++++ .../rfxtrx/translations/zh-Hans.json | 11 ++++++ .../components/ring/translations/zh-Hans.json | 11 ++++++ .../risco/translations/zh-Hans.json | 11 ++++++ .../components/roon/translations/de.json | 5 +++ .../translations/zh-Hans.json | 15 ++++++++ .../components/script/translations/pl.json | 4 +-- .../components/sensor/translations/pl.json | 4 +-- .../sharkiq/translations/zh-Hans.json | 16 +++++++++ .../components/shelly/translations/ca.json | 2 +- .../components/shelly/translations/cs.json | 2 +- .../components/shelly/translations/pl.json | 2 +- .../components/shelly/translations/ru.json | 2 +- .../shelly/translations/zh-Hans.json | 11 ++++++ .../simplisafe/translations/et.json | 1 + .../simplisafe/translations/zh-Hans.json | 1 + .../components/smappee/translations/et.json | 4 +++ .../translations/zh-Hans.json | 11 ++++++ .../components/somfy/translations/et.json | 5 +++ .../spider/translations/zh-Hans.json | 11 ++++++ .../components/spotify/translations/et.json | 2 +- .../squeezebox/translations/zh-Hans.json | 11 ++++++ .../starline/translations/zh-Hans.json | 14 ++++++++ .../components/switch/translations/pl.json | 4 +-- .../switch/translations/zh-Hans.json | 12 +++++++ .../components/syncthru/translations/et.json | 1 + .../synology_dsm/translations/de.json | 3 +- .../components/tado/translations/zh-Hans.json | 11 ++++++ .../tibber/translations/zh-Hans.json | 7 ++++ .../components/tile/translations/zh-Hans.json | 11 ++++++ .../totalconnect/translations/zh-Hans.json | 14 ++++++++ .../transmission/translations/zh-Hans.json | 7 ++++ .../components/tuya/translations/zh-Hans.json | 11 ++++++ .../twentemilieu/translations/zh-Hans.json | 4 +++ .../components/unifi/translations/nl.json | 3 +- .../components/upcloud/translations/de.json | 9 +++++ .../upcloud/translations/zh-Hans.json | 15 ++++++++ .../components/vacuum/translations/pl.json | 4 +-- .../vesync/translations/zh-Hans.json | 8 +++++ .../water_heater/translations/zh-Hans.json | 8 +++++ .../components/wled/translations/zh-Hans.json | 10 ++++++ .../wolflink/translations/sensor.de.json | 7 ++++ .../wolflink/translations/sensor.pl.json | 2 +- .../wolflink/translations/zh-Hans.json | 11 ++++++ .../components/yeelight/translations/de.json | 2 ++ .../components/zha/translations/zh-Hans.json | 8 +++++ .../zoneminder/translations/zh-Hans.json | 11 ++++++ 151 files changed, 1249 insertions(+), 56 deletions(-) create mode 100644 homeassistant/components/advantage_air/translations/zh-Hans.json create mode 100644 homeassistant/components/agent_dvr/translations/zh-Hans.json create mode 100644 homeassistant/components/airvisual/translations/zh-Hans.json create mode 100644 homeassistant/components/alarmdecoder/translations/zh-Hans.json create mode 100644 homeassistant/components/ambiclimate/translations/zh-Hans.json create mode 100644 homeassistant/components/atag/translations/zh-Hans.json create mode 100644 homeassistant/components/august/translations/zh-Hans.json create mode 100644 homeassistant/components/azure_devops/translations/de.json create mode 100644 homeassistant/components/blink/translations/zh-Hans.json create mode 100644 homeassistant/components/braviatv/translations/zh-Hans.json create mode 100644 homeassistant/components/brother/translations/zh-Hans.json create mode 100644 homeassistant/components/bsblan/translations/zh-Hans.json create mode 100644 homeassistant/components/canary/translations/zh-Hans.json create mode 100644 homeassistant/components/cloudflare/translations/ca.json create mode 100644 homeassistant/components/cloudflare/translations/cs.json create mode 100644 homeassistant/components/cloudflare/translations/en.json create mode 100644 homeassistant/components/cloudflare/translations/et.json create mode 100644 homeassistant/components/cloudflare/translations/pl.json create mode 100644 homeassistant/components/cloudflare/translations/ru.json create mode 100644 homeassistant/components/control4/translations/zh-Hans.json create mode 100644 homeassistant/components/coolmaster/translations/zh-Hans.json create mode 100644 homeassistant/components/doorbird/translations/zh-Hans.json create mode 100644 homeassistant/components/elgato/translations/zh-Hans.json create mode 100644 homeassistant/components/flo/translations/zh-Hans.json create mode 100644 homeassistant/components/flume/translations/zh-Hans.json create mode 100644 homeassistant/components/fritzbox/translations/zh-Hans.json create mode 100644 homeassistant/components/garmin_connect/translations/zh-Hans.json create mode 100644 homeassistant/components/glances/translations/zh-Hans.json create mode 100644 homeassistant/components/gogogate2/translations/zh-Hans.json create mode 100644 homeassistant/components/gree/translations/zh-Hans.json create mode 100644 homeassistant/components/guardian/translations/zh-Hans.json create mode 100644 homeassistant/components/hlk_sw16/translations/zh-Hans.json create mode 100644 homeassistant/components/huawei_lte/translations/zh-Hans.json create mode 100644 homeassistant/components/hvv_departures/translations/zh-Hans.json create mode 100644 homeassistant/components/iaqualink/translations/zh-Hans.json create mode 100644 homeassistant/components/insteon/translations/zh-Hans.json create mode 100644 homeassistant/components/ipp/translations/zh-Hans.json create mode 100644 homeassistant/components/isy994/translations/zh-Hans.json create mode 100644 homeassistant/components/kodi/translations/zh-Hans.json create mode 100644 homeassistant/components/metoffice/translations/zh-Hans.json create mode 100644 homeassistant/components/mill/translations/zh-Hans.json create mode 100644 homeassistant/components/myq/translations/zh-Hans.json create mode 100644 homeassistant/components/neato/translations/zh-Hans.json create mode 100644 homeassistant/components/nexia/translations/zh-Hans.json create mode 100644 homeassistant/components/nuheat/translations/zh-Hans.json create mode 100644 homeassistant/components/nut/translations/zh-Hans.json create mode 100644 homeassistant/components/nzbget/translations/zh-Hans.json create mode 100644 homeassistant/components/omnilogic/translations/zh-Hans.json create mode 100644 homeassistant/components/ovo_energy/translations/zh-Hans.json create mode 100644 homeassistant/components/plugwise/translations/zh-Hans.json create mode 100644 homeassistant/components/plum_lightpad/translations/zh-Hans.json create mode 100644 homeassistant/components/rfxtrx/translations/zh-Hans.json create mode 100644 homeassistant/components/ring/translations/zh-Hans.json create mode 100644 homeassistant/components/risco/translations/zh-Hans.json create mode 100644 homeassistant/components/ruckus_unleashed/translations/zh-Hans.json create mode 100644 homeassistant/components/sharkiq/translations/zh-Hans.json create mode 100644 homeassistant/components/shelly/translations/zh-Hans.json create mode 100644 homeassistant/components/smart_meter_texas/translations/zh-Hans.json create mode 100644 homeassistant/components/spider/translations/zh-Hans.json create mode 100644 homeassistant/components/squeezebox/translations/zh-Hans.json create mode 100644 homeassistant/components/starline/translations/zh-Hans.json create mode 100644 homeassistant/components/tado/translations/zh-Hans.json create mode 100644 homeassistant/components/tibber/translations/zh-Hans.json create mode 100644 homeassistant/components/tile/translations/zh-Hans.json create mode 100644 homeassistant/components/totalconnect/translations/zh-Hans.json create mode 100644 homeassistant/components/transmission/translations/zh-Hans.json create mode 100644 homeassistant/components/tuya/translations/zh-Hans.json create mode 100644 homeassistant/components/upcloud/translations/zh-Hans.json create mode 100644 homeassistant/components/water_heater/translations/zh-Hans.json create mode 100644 homeassistant/components/wled/translations/zh-Hans.json create mode 100644 homeassistant/components/wolflink/translations/zh-Hans.json create mode 100644 homeassistant/components/zoneminder/translations/zh-Hans.json diff --git a/homeassistant/components/accuweather/translations/pl.json b/homeassistant/components/accuweather/translations/pl.json index eb607d3819a..2ac9aabfc91 100644 --- a/homeassistant/components/accuweather/translations/pl.json +++ b/homeassistant/components/accuweather/translations/pl.json @@ -27,7 +27,7 @@ "data": { "forecast": "Prognoza pogody" }, - "description": "Ze wzgl\u0119du na ograniczenia darmowej wersji klucza API AccuWeather po w\u0142\u0105czeniu prognozy pogody aktualizacje danych b\u0119d\u0105 wykonywane co 64 minut zamiast co 32 minut.", + "description": "Ze wzgl\u0119du na ograniczenia darmowej wersji klucza API AccuWeather po w\u0142\u0105czeniu prognozy pogody aktualizacje danych b\u0119d\u0105 wykonywane co 64 minuty zamiast co 32 minuty.", "title": "Opcje AccuWeather" } } diff --git a/homeassistant/components/adguard/translations/zh-Hans.json b/homeassistant/components/adguard/translations/zh-Hans.json index 4204beb5268..64a68e91877 100644 --- a/homeassistant/components/adguard/translations/zh-Hans.json +++ b/homeassistant/components/adguard/translations/zh-Hans.json @@ -3,6 +3,9 @@ "abort": { "existing_instance_updated": "\u66f4\u65b0\u4e86\u73b0\u6709\u914d\u7f6e\u3002" }, + "error": { + "connection_error": "\u8fde\u63a5\u5931\u8d25\u3002" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/advantage_air/translations/zh-Hans.json b/homeassistant/components/advantage_air/translations/zh-Hans.json new file mode 100644 index 00000000000..a25ab477c70 --- /dev/null +++ b/homeassistant/components/advantage_air/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "ip_address": "IP\u5730\u5740" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/agent_dvr/translations/zh-Hans.json b/homeassistant/components/agent_dvr/translations/zh-Hans.json new file mode 100644 index 00000000000..2941dfd9383 --- /dev/null +++ b/homeassistant/components/agent_dvr/translations/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual/translations/zh-Hans.json b/homeassistant/components/airvisual/translations/zh-Hans.json new file mode 100644 index 00000000000..2941dfd9383 --- /dev/null +++ b/homeassistant/components/airvisual/translations/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/alarmdecoder/translations/de.json b/homeassistant/components/alarmdecoder/translations/de.json index 69318f87b11..8e72173d150 100644 --- a/homeassistant/components/alarmdecoder/translations/de.json +++ b/homeassistant/components/alarmdecoder/translations/de.json @@ -6,6 +6,7 @@ "step": { "protocol": { "data": { + "device_path": "Ger\u00e4tepfad", "host": "Host", "port": "Port" } diff --git a/homeassistant/components/alarmdecoder/translations/zh-Hans.json b/homeassistant/components/alarmdecoder/translations/zh-Hans.json new file mode 100644 index 00000000000..1810fa0bcf0 --- /dev/null +++ b/homeassistant/components/alarmdecoder/translations/zh-Hans.json @@ -0,0 +1,14 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + }, + "step": { + "protocol": { + "data": { + "port": "\u7aef\u53e3" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ambiclimate/translations/zh-Hans.json b/homeassistant/components/ambiclimate/translations/zh-Hans.json new file mode 100644 index 00000000000..df2d0ac6af9 --- /dev/null +++ b/homeassistant/components/ambiclimate/translations/zh-Hans.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "\u8d26\u53f7\u5df2\u7ecf\u8bbe\u7f6e\u5b8c\u6210", + "missing_configuration": "\u7ec4\u4ef6\u5c1a\u672a\u914d\u7f6e\u3002\u8bf7\u53c2\u89c2\u6587\u4ef6\u8bf4\u660e\u3002" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/atag/translations/zh-Hans.json b/homeassistant/components/atag/translations/zh-Hans.json new file mode 100644 index 00000000000..2941dfd9383 --- /dev/null +++ b/homeassistant/components/atag/translations/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/august/translations/zh-Hans.json b/homeassistant/components/august/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/august/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/automation/translations/pl.json b/homeassistant/components/automation/translations/pl.json index b6cdef3db6e..959fb48b355 100644 --- a/homeassistant/components/automation/translations/pl.json +++ b/homeassistant/components/automation/translations/pl.json @@ -1,8 +1,8 @@ { "state": { "_": { - "off": "wy\u0142\u0105czona", - "on": "w\u0142\u0105czona" + "off": "wy\u0142.", + "on": "w\u0142." } }, "title": "Automatyzacja" diff --git a/homeassistant/components/azure_devops/translations/de.json b/homeassistant/components/azure_devops/translations/de.json new file mode 100644 index 00000000000..cd849b9f933 --- /dev/null +++ b/homeassistant/components/azure_devops/translations/de.json @@ -0,0 +1,15 @@ +{ + "config": { + "step": { + "reauth": { + "title": "Erneute Authentifizierung" + }, + "user": { + "data": { + "organization": "Organisation", + "project": "Projekt" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/binary_sensor/translations/pl.json b/homeassistant/components/binary_sensor/translations/pl.json index 67af6898c39..a3f578f1cef 100644 --- a/homeassistant/components/binary_sensor/translations/pl.json +++ b/homeassistant/components/binary_sensor/translations/pl.json @@ -91,8 +91,8 @@ }, "state": { "_": { - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony" + "off": "wy\u0142.", + "on": "w\u0142." }, "battery": { "off": "na\u0142adowana", diff --git a/homeassistant/components/binary_sensor/translations/zh-Hans.json b/homeassistant/components/binary_sensor/translations/zh-Hans.json index d2edb26163f..b13a3622aca 100644 --- a/homeassistant/components/binary_sensor/translations/zh-Hans.json +++ b/homeassistant/components/binary_sensor/translations/zh-Hans.json @@ -48,7 +48,12 @@ "connected": "{entity_name} \u5df2\u8fde\u63a5", "gas": "{entity_name} \u5f00\u59cb\u68c0\u6d4b\u5230\u71c3\u6c14\u6cc4\u6f0f", "hot": "{entity_name} \u53d8\u70ed", - "light": "{entity_name} \u5f00\u59cb\u68c0\u6d4b\u5230\u5149\u7ebf" + "light": "{entity_name} \u5f00\u59cb\u68c0\u6d4b\u5230\u5149\u7ebf", + "motion": "{entity_name} \u68c0\u6d4b\u5230\u6709\u4eba", + "no_motion": "{entity_name} \u672a\u68c0\u6d4b\u5230\u6709\u4eba", + "not_opened": "{entity_name}\u5df2\u5173\u95ed", + "turned_off": "{entity_name} \u88ab\u5173\u95ed", + "turned_on": "{entity_name} \u88ab\u6253\u5f00" } }, "state": { diff --git a/homeassistant/components/blink/translations/zh-Hans.json b/homeassistant/components/blink/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/blink/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/braviatv/translations/zh-Hans.json b/homeassistant/components/braviatv/translations/zh-Hans.json new file mode 100644 index 00000000000..c60c3f800ef --- /dev/null +++ b/homeassistant/components/braviatv/translations/zh-Hans.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "\u8bbe\u7f6eSony Bravia\u7535\u89c6\u96c6\u6210\u3002\u5982\u679c\u60a8\u5728\u914d\u7f6e\u65b9\u9762\u9047\u5230\u95ee\u9898\uff0c\u8bf7\u8bbf\u95ee\uff1ahttps://www.home-assistant.io/integrations/braviatv\n\u786e\u4fdd\u7535\u89c6\u5df2\u6253\u5f00\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/broadlink/translations/de.json b/homeassistant/components/broadlink/translations/de.json index b0d7a55c787..99af347f1f4 100644 --- a/homeassistant/components/broadlink/translations/de.json +++ b/homeassistant/components/broadlink/translations/de.json @@ -1,10 +1,17 @@ { "config": { + "abort": { + "not_supported": "Ger\u00e4t nicht unterst\u00fctzt" + }, "step": { + "auth": { + "title": "Authentifiziere dich beim Ger\u00e4t" + }, "finish": { "data": { "name": "Name" - } + }, + "title": "W\u00e4hle einen Namen f\u00fcr das Ger\u00e4t" }, "user": { "data": { diff --git a/homeassistant/components/brother/translations/zh-Hans.json b/homeassistant/components/brother/translations/zh-Hans.json new file mode 100644 index 00000000000..8f9e85e54a9 --- /dev/null +++ b/homeassistant/components/brother/translations/zh-Hans.json @@ -0,0 +1,8 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", + "snmp_error": "SNMP\u670d\u52a1\u5668\u5df2\u5173\u95ed\u6216\u4e0d\u652f\u6301\u6253\u5370\u3002" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bsblan/translations/zh-Hans.json b/homeassistant/components/bsblan/translations/zh-Hans.json new file mode 100644 index 00000000000..2941dfd9383 --- /dev/null +++ b/homeassistant/components/bsblan/translations/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/calendar/translations/pl.json b/homeassistant/components/calendar/translations/pl.json index 94ac2fd244d..b9106249175 100644 --- a/homeassistant/components/calendar/translations/pl.json +++ b/homeassistant/components/calendar/translations/pl.json @@ -1,8 +1,8 @@ { "state": { "_": { - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony" + "off": "wy\u0142.", + "on": "w\u0142." } }, "title": "Kalendarz" diff --git a/homeassistant/components/canary/translations/zh-Hans.json b/homeassistant/components/canary/translations/zh-Hans.json new file mode 100644 index 00000000000..b0b26b02261 --- /dev/null +++ b/homeassistant/components/canary/translations/zh-Hans.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "\u5bc6\u7801", + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cloudflare/translations/ca.json b/homeassistant/components/cloudflare/translations/ca.json new file mode 100644 index 00000000000..edd31662e56 --- /dev/null +++ b/homeassistant/components/cloudflare/translations/ca.json @@ -0,0 +1,35 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3.", + "unknown": "Error inesperat" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "invalid_zone": "Zona inv\u00e0lida" + }, + "flow_title": "Cloudflare: {name}", + "step": { + "records": { + "data": { + "records": "Registres" + }, + "title": "Selecciona els registres a actualitzar" + }, + "user": { + "data": { + "api_token": "Token d'API" + }, + "description": "Aquesta integraci\u00f3 necessita un token d'API creat amb permisos d'edici\u00f3 Zone:Zone:Read i Zone:DNS:Edit per a totes les zones del teu compte.", + "title": "Connexi\u00f3 amb Cloudflare" + }, + "zone": { + "data": { + "zone": "Zona" + }, + "title": "Selecciona la zona a actualitzar" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cloudflare/translations/cs.json b/homeassistant/components/cloudflare/translations/cs.json new file mode 100644 index 00000000000..d618c25c4f0 --- /dev/null +++ b/homeassistant/components/cloudflare/translations/cs.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace.", + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", + "invalid_zone": "Neplatn\u00e1 z\u00f3na" + }, + "flow_title": "Cloudflare: {name}", + "step": { + "records": { + "data": { + "records": "Z\u00e1znamy" + }, + "title": "Vyberte z\u00e1znamy, kter\u00e9 chcete aktualizovat" + }, + "user": { + "data": { + "api_token": "API token" + }, + "title": "P\u0159ipojen\u00ed ke Cloudflare" + }, + "zone": { + "data": { + "zone": "Z\u00f3na" + }, + "title": "Vyberte z\u00f3nu, kter\u00e1 m\u00e1 b\u00fdt aktualizov\u00e1na" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cloudflare/translations/en.json b/homeassistant/components/cloudflare/translations/en.json new file mode 100644 index 00000000000..4016e22d8ee --- /dev/null +++ b/homeassistant/components/cloudflare/translations/en.json @@ -0,0 +1,35 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Already configured. Only a single configuration possible.", + "unknown": "Unexpected error" + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "invalid_zone": "Invalid zone" + }, + "flow_title": "Cloudflare: {name}", + "step": { + "records": { + "data": { + "records": "Records" + }, + "title": "Choose the Records to Update" + }, + "user": { + "data": { + "api_token": "API Token" + }, + "description": "This integration requires an API Token created with Zone:Zone:Read and Zone:DNS:Edit permissions for all zones in your account.", + "title": "Connect to Cloudflare" + }, + "zone": { + "data": { + "zone": "Zone" + }, + "title": "Choose the Zone to Update" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cloudflare/translations/et.json b/homeassistant/components/cloudflare/translations/et.json new file mode 100644 index 00000000000..9c9ab666a83 --- /dev/null +++ b/homeassistant/components/cloudflare/translations/et.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "unknown": "Tundmatu viga" + }, + "error": { + "cannot_connect": "\u00dchendus nurjus", + "invalid_auth": "Tuvastamise viga", + "invalid_zone": "Sobimatu ala" + }, + "flow_title": "Cloudflare: {name}", + "step": { + "records": { + "data": { + "records": "Kirjed" + }, + "title": "Vali v\u00e4rskendatavad kirjed" + }, + "user": { + "data": { + "api_token": "Api v\u00f5ti" + }, + "description": "Selle sidumise jaoks on vaja API tokenit, mis on loodud Zone: Zone: Read ja Zone: DNS: Edit k\u00f5igi oma konto alade \u00f5igusi.!?", + "title": "Loo \u00fchendus Cloudflare'iga" + }, + "zone": { + "data": { + "zone": "Ala" + }, + "title": "Vali v\u00e4rskendatav ala" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cloudflare/translations/pl.json b/homeassistant/components/cloudflare/translations/pl.json new file mode 100644 index 00000000000..70c7869937a --- /dev/null +++ b/homeassistant/components/cloudflare/translations/pl.json @@ -0,0 +1,35 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja.", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "invalid_zone": "Nieprawid\u0142owa strefa" + }, + "flow_title": "Cloudflare: {name}", + "step": { + "records": { + "data": { + "records": "Rekordy" + }, + "title": "Wybierz rekordy do uaktualnienia" + }, + "user": { + "data": { + "api_token": "Token API" + }, + "description": "Ta integracja wymaga tokena API utworzonego z uprawnieniami \"Zone:Zone:Read\" oraz \"Zone:DNS:Edit\" dla wszystkich stref na Twoim koncie.", + "title": "Po\u0142\u0105czenie z Cloudflare" + }, + "zone": { + "data": { + "zone": "Strefa" + }, + "title": "Wybierz stref\u0119 do uaktualnienia" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cloudflare/translations/ru.json b/homeassistant/components/cloudflare/translations/ru.json new file mode 100644 index 00000000000..fa4819d8c83 --- /dev/null +++ b/homeassistant/components/cloudflare/translations/ru.json @@ -0,0 +1,35 @@ +{ + "config": { + "abort": { + "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": "\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_auth": "\u041d\u0435\u0432\u0435\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f.", + "invalid_zone": "\u041d\u0435\u0432\u0435\u0440\u043d\u0430\u044f \u0437\u043e\u043d\u0430" + }, + "flow_title": "Cloudflare: {name}", + "step": { + "records": { + "data": { + "records": "\u0417\u0430\u043f\u0438\u0441\u0438" + }, + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f" + }, + "user": { + "data": { + "api_token": "\u0422\u043e\u043a\u0435\u043d API" + }, + "description": "\u0414\u043b\u044f \u044d\u0442\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u043a\u0435\u043d API, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0441 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c\u0438 Zone:Zone:Read \u0438 Zone:DNS:Edit \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0437\u043e\u043d \u0432 \u0412\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438.", + "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a Cloudflare" + }, + "zone": { + "data": { + "zone": "\u0417\u043e\u043d\u0430" + }, + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u043e\u043d\u0443 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/configurator/translations/pl.json b/homeassistant/components/configurator/translations/pl.json index 45e5af46722..849a7e7bfef 100644 --- a/homeassistant/components/configurator/translations/pl.json +++ b/homeassistant/components/configurator/translations/pl.json @@ -1,7 +1,7 @@ { "state": { "_": { - "configure": "skonfiguruj", + "configure": "konfiguruj", "configured": "skonfigurowany" } }, diff --git a/homeassistant/components/control4/translations/zh-Hans.json b/homeassistant/components/control4/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/control4/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/coolmaster/translations/zh-Hans.json b/homeassistant/components/coolmaster/translations/zh-Hans.json new file mode 100644 index 00000000000..c296a15219e --- /dev/null +++ b/homeassistant/components/coolmaster/translations/zh-Hans.json @@ -0,0 +1,14 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + }, + "step": { + "user": { + "data": { + "off": "\u53ef\u4ee5\u5173\u95ed" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cover/translations/zh-Hans.json b/homeassistant/components/cover/translations/zh-Hans.json index 2929a2cd33e..0ff73407298 100644 --- a/homeassistant/components/cover/translations/zh-Hans.json +++ b/homeassistant/components/cover/translations/zh-Hans.json @@ -1,4 +1,12 @@ { + "device_automation": { + "condition_type": { + "is_closed": "{entity_name} \u5df2\u5173\u95ed" + }, + "trigger_type": { + "closed": "{entity_name}\u5df2\u5173\u95ed" + } + }, "state": { "_": { "closed": "\u5df2\u5173\u95ed", diff --git a/homeassistant/components/daikin/translations/zh-Hans.json b/homeassistant/components/daikin/translations/zh-Hans.json index 307fd15324a..0acb5110fec 100644 --- a/homeassistant/components/daikin/translations/zh-Hans.json +++ b/homeassistant/components/daikin/translations/zh-Hans.json @@ -4,9 +4,14 @@ "already_configured": "\u8bbe\u5907\u5df2\u914d\u7f6e\u5b8c\u6210", "cannot_connect": "\u8fde\u63a5\u5931\u8d25" }, + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", + "invalid_auth": "\u9a8c\u8bc1\u7801\u9519\u8bef" + }, "step": { "user": { "data": { + "api_key": "API\u5bc6\u7801", "host": "\u4e3b\u673a" }, "description": "\u8f93\u5165\u60a8\u7684 Daikin \u7a7a\u8c03\u7684 IP \u5730\u5740\u3002", diff --git a/homeassistant/components/deconz/translations/zh-Hans.json b/homeassistant/components/deconz/translations/zh-Hans.json index 42b8067f647..57993237696 100644 --- a/homeassistant/components/deconz/translations/zh-Hans.json +++ b/homeassistant/components/deconz/translations/zh-Hans.json @@ -22,7 +22,8 @@ "side_3": "\u7b2c 3 \u9762", "side_4": "\u7b2c 4 \u9762", "side_5": "\u7b2c 5 \u9762", - "side_6": "\u7b2c 6 \u9762" + "side_6": "\u7b2c 6 \u9762", + "turn_off": "\u5173\u95ed" }, "trigger_type": { "remote_awakened": "\u8bbe\u5907\u5524\u9192", diff --git a/homeassistant/components/dexcom/translations/zh-Hans.json b/homeassistant/components/dexcom/translations/zh-Hans.json index 41e54c84b56..7fd407af3aa 100644 --- a/homeassistant/components/dexcom/translations/zh-Hans.json +++ b/homeassistant/components/dexcom/translations/zh-Hans.json @@ -2,6 +2,7 @@ "config": { "error": { "account_error": "\u8ba4\u8bc1\u65e0\u6548", + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", "session_error": "\u8fde\u63a5\u5931\u8d25", "unknown": "\u672a\u77e5\u9519\u8bef" }, @@ -9,7 +10,8 @@ "user": { "data": { "password": "\u5bc6\u7801", - "server": "\u670d\u52a1\u5668" + "server": "\u670d\u52a1\u5668", + "username": "\u7528\u6237\u540d" } } } diff --git a/homeassistant/components/doorbird/translations/zh-Hans.json b/homeassistant/components/doorbird/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/doorbird/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/elgato/translations/zh-Hans.json b/homeassistant/components/elgato/translations/zh-Hans.json new file mode 100644 index 00000000000..254f6df9327 --- /dev/null +++ b/homeassistant/components/elgato/translations/zh-Hans.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + }, + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fan/translations/pl.json b/homeassistant/components/fan/translations/pl.json index fcf925e60ea..e99f92ae286 100644 --- a/homeassistant/components/fan/translations/pl.json +++ b/homeassistant/components/fan/translations/pl.json @@ -15,8 +15,8 @@ }, "state": { "_": { - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony" + "off": "wy\u0142.", + "on": "w\u0142." } }, "title": "Wentylator" diff --git a/homeassistant/components/fan/translations/zh-Hans.json b/homeassistant/components/fan/translations/zh-Hans.json index 6dde9459c1a..13b6917f4ad 100644 --- a/homeassistant/components/fan/translations/zh-Hans.json +++ b/homeassistant/components/fan/translations/zh-Hans.json @@ -10,7 +10,7 @@ }, "trigger_type": { "turned_off": "{entity_name} \u88ab\u5173\u95ed", - "turned_on": "{entity_name} \u88ab\u5f00\u542f" + "turned_on": "{entity_name} \u88ab\u6253\u5f00" } }, "state": { diff --git a/homeassistant/components/flo/translations/zh-Hans.json b/homeassistant/components/flo/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/flo/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/flume/translations/zh-Hans.json b/homeassistant/components/flume/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/flume/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fritzbox/translations/zh-Hans.json b/homeassistant/components/fritzbox/translations/zh-Hans.json new file mode 100644 index 00000000000..183c748b2d2 --- /dev/null +++ b/homeassistant/components/fritzbox/translations/zh-Hans.json @@ -0,0 +1,19 @@ +{ + "config": { + "error": { + "auth_failed": "\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef" + }, + "step": { + "confirm": { + "data": { + "username": "\u7528\u6237\u540d" + } + }, + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/garmin_connect/translations/zh-Hans.json b/homeassistant/components/garmin_connect/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/garmin_connect/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/glances/translations/zh-Hans.json b/homeassistant/components/glances/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/glances/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/gogogate2/translations/zh-Hans.json b/homeassistant/components/gogogate2/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/gogogate2/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/gree/translations/zh-Hans.json b/homeassistant/components/gree/translations/zh-Hans.json new file mode 100644 index 00000000000..c6d2c4c7cfb --- /dev/null +++ b/homeassistant/components/gree/translations/zh-Hans.json @@ -0,0 +1,3 @@ +{ + "title": "\u683c\u529b\u7a7a\u8c03" +} \ No newline at end of file diff --git a/homeassistant/components/group/translations/pl.json b/homeassistant/components/group/translations/pl.json index b82492d83d0..ce231ebaad9 100644 --- a/homeassistant/components/group/translations/pl.json +++ b/homeassistant/components/group/translations/pl.json @@ -5,9 +5,9 @@ "home": "w domu", "locked": "Zablokowane", "not_home": "poza domem", - "off": "wy\u0142\u0105czony", + "off": "wy\u0142.", "ok": "ok", - "on": "w\u0142\u0105czony", + "on": "w\u0142.", "open": "otwarte", "problem": "problem", "unlocked": "Odblokowany" diff --git a/homeassistant/components/guardian/translations/zh-Hans.json b/homeassistant/components/guardian/translations/zh-Hans.json new file mode 100644 index 00000000000..6e842e66fab --- /dev/null +++ b/homeassistant/components/guardian/translations/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hlk_sw16/translations/zh-Hans.json b/homeassistant/components/hlk_sw16/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/hlk_sw16/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit/translations/nl.json b/homeassistant/components/homekit/translations/nl.json index 75cb9e739b2..3c3a019c3ed 100644 --- a/homeassistant/components/homekit/translations/nl.json +++ b/homeassistant/components/homekit/translations/nl.json @@ -18,6 +18,10 @@ "options": { "step": { "advanced": { + "data": { + "safe_mode": "Veilige modus (alleen inschakelen als het koppelen mislukt)" + }, + "description": "Deze instellingen hoeven alleen te worden aangepast als HomeKit niet functioneert.", "title": "Geavanceerde configuratie" }, "exclude": { @@ -30,9 +34,11 @@ "init": { "data": { "include_domains": "Op te nemen domeinen" - } + }, + "title": "Selecteer domeinen om zichtbaar te maken." }, "yaml": { + "description": "Deze invoer wordt beheerd via YAML", "title": "Pas de HomeKit Bridge-opties aan" } } diff --git a/homeassistant/components/homematicip_cloud/translations/de.json b/homeassistant/components/homematicip_cloud/translations/de.json index c421620fd98..0718f629164 100644 --- a/homeassistant/components/homematicip_cloud/translations/de.json +++ b/homeassistant/components/homematicip_cloud/translations/de.json @@ -6,6 +6,7 @@ "unknown": "Ein unbekannter Fehler ist aufgetreten." }, "error": { + "invalid_pin": "Ung\u00fcltige PIN, bitte versuche es erneut.", "invalid_sgtin_or_pin": "Ung\u00fcltige PIN, bitte versuche es erneut.", "press_the_button": "Bitte dr\u00fccke die blaue Taste.", "register_failed": "Registrierung fehlgeschlagen, bitte versuche es erneut.", diff --git a/homeassistant/components/huawei_lte/translations/zh-Hans.json b/homeassistant/components/huawei_lte/translations/zh-Hans.json new file mode 100644 index 00000000000..2f499964108 --- /dev/null +++ b/homeassistant/components/huawei_lte/translations/zh-Hans.json @@ -0,0 +1,9 @@ +{ + "config": { + "error": { + "connection_failed": "\u8fde\u63a5\u5931\u8d25", + "incorrect_username": "\u7528\u6237\u540d\u9519\u8bef", + "incorrect_username_or_password": "\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hue/translations/et.json b/homeassistant/components/hue/translations/et.json index 55b078a952e..53695563f8b 100644 --- a/homeassistant/components/hue/translations/et.json +++ b/homeassistant/components/hue/translations/et.json @@ -18,7 +18,7 @@ "data": { "host": "" }, - "title": "Valige Hue sild" + "title": "Vali Hue sild" }, "link": { "description": "Vajutage silla nuppu, et registreerida Philips Hue Home Assistant abil. \n\n ! [Nupu asukoht sillal] (/ static / images / config_philips_hue.jpg)", diff --git a/homeassistant/components/hue/translations/zh-Hans.json b/homeassistant/components/hue/translations/zh-Hans.json index f3b763c7b0c..c34bd68aad8 100644 --- a/homeassistant/components/hue/translations/zh-Hans.json +++ b/homeassistant/components/hue/translations/zh-Hans.json @@ -25,5 +25,10 @@ "title": "\u8fde\u63a5\u4e2d\u67a2" } } + }, + "device_automation": { + "trigger_subtype": { + "turn_off": "\u5173\u95ed" + } } } \ No newline at end of file diff --git a/homeassistant/components/humidifier/translations/pl.json b/homeassistant/components/humidifier/translations/pl.json index 9a2f14e5c7e..af049417782 100644 --- a/homeassistant/components/humidifier/translations/pl.json +++ b/homeassistant/components/humidifier/translations/pl.json @@ -20,8 +20,8 @@ }, "state": { "_": { - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony" + "off": "wy\u0142.", + "on": "w\u0142." } }, "title": "Nawil\u017cacz" diff --git a/homeassistant/components/humidifier/translations/zh-Hans.json b/homeassistant/components/humidifier/translations/zh-Hans.json index 0a6aa709bcc..8fa6b0be0da 100644 --- a/homeassistant/components/humidifier/translations/zh-Hans.json +++ b/homeassistant/components/humidifier/translations/zh-Hans.json @@ -6,6 +6,13 @@ "toggle": "\u5207\u6362 {entity_name} \u5f00\u5173", "turn_off": "\u5173\u95ed {entity_name}", "turn_on": "\u6253\u5f00 {entity_name}" + }, + "condition_type": { + "is_off": "{entity_name} \u5df2\u5173\u95ed" + }, + "trigger_type": { + "turned_off": "{entity_name} \u88ab\u5173\u95ed", + "turned_on": "{entity_name} \u88ab\u6253\u5f00" } }, "state": { diff --git a/homeassistant/components/hvv_departures/translations/zh-Hans.json b/homeassistant/components/hvv_departures/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/hvv_departures/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/iaqualink/translations/zh-Hans.json b/homeassistant/components/iaqualink/translations/zh-Hans.json new file mode 100644 index 00000000000..d13c8a6b46a --- /dev/null +++ b/homeassistant/components/iaqualink/translations/zh-Hans.json @@ -0,0 +1,12 @@ +{ + "config": { + "error": { + "connection_failure": "\u65e0\u6cd5\u8fde\u63a5\u5230iAqualink\u3002\u68c0\u67e5\u60a8\u7684\u7528\u6237\u540d\u548c\u5bc6\u7801\u3002" + }, + "step": { + "user": { + "description": "\u8bf7\u8f93\u5165\u60a8\u7684iAqualink\u5e10\u6237\u7684\u7528\u6237\u540d\u548c\u5bc6\u7801\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/icloud/translations/zh-Hans.json b/homeassistant/components/icloud/translations/zh-Hans.json index 0fed17b7ce1..82927dc54b5 100644 --- a/homeassistant/components/icloud/translations/zh-Hans.json +++ b/homeassistant/components/icloud/translations/zh-Hans.json @@ -9,6 +9,11 @@ "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" @@ -18,7 +23,8 @@ }, "user": { "data": { - "password": "\u5bc6\u7801" + "password": "\u5bc6\u7801", + "username": "\u90ae\u7bb1" }, "description": "\u8bf7\u8f93\u5165\u51ed\u636e", "title": "iCloud \u51ed\u636e" diff --git a/homeassistant/components/input_boolean/translations/pl.json b/homeassistant/components/input_boolean/translations/pl.json index 031c361aeec..c50d1981595 100644 --- a/homeassistant/components/input_boolean/translations/pl.json +++ b/homeassistant/components/input_boolean/translations/pl.json @@ -1,8 +1,8 @@ { "state": { "_": { - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony" + "off": "wy\u0142.", + "on": "w\u0142." } }, "title": "Pole warto\u015bci logicznej" diff --git a/homeassistant/components/insteon/translations/de.json b/homeassistant/components/insteon/translations/de.json index dfefff8c559..c7f2ad94313 100644 --- a/homeassistant/components/insteon/translations/de.json +++ b/homeassistant/components/insteon/translations/de.json @@ -33,6 +33,9 @@ } }, "options": { + "error": { + "select_single": "W\u00e4hle eine Option aus." + }, "step": { "add_override": { "title": "Insteon" diff --git a/homeassistant/components/insteon/translations/et.json b/homeassistant/components/insteon/translations/et.json index b7eab44fe3c..33a6848f7e3 100644 --- a/homeassistant/components/insteon/translations/et.json +++ b/homeassistant/components/insteon/translations/et.json @@ -60,7 +60,8 @@ "already_configured": "Insteoni modemi \u00fchendus on juba seadistatud" }, "error": { - "cannot_connect": "\u00dchendamine nurjus" + "cannot_connect": "\u00dchendamine nurjus", + "input_error": "Vigane sisestus, kontrolli andmeid" }, "step": { "add_override": { diff --git a/homeassistant/components/insteon/translations/zh-Hans.json b/homeassistant/components/insteon/translations/zh-Hans.json new file mode 100644 index 00000000000..eb9facef178 --- /dev/null +++ b/homeassistant/components/insteon/translations/zh-Hans.json @@ -0,0 +1,16 @@ +{ + "config": { + "step": { + "hub2": { + "data": { + "username": "\u7528\u6237\u540d" + } + }, + "hubv2": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ipp/translations/zh-Hans.json b/homeassistant/components/ipp/translations/zh-Hans.json new file mode 100644 index 00000000000..254f6df9327 --- /dev/null +++ b/homeassistant/components/ipp/translations/zh-Hans.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + }, + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/isy994/translations/zh-Hans.json b/homeassistant/components/isy994/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/isy994/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/kodi/translations/de.json b/homeassistant/components/kodi/translations/de.json index ac98f2016b4..d8db0e86137 100644 --- a/homeassistant/components/kodi/translations/de.json +++ b/homeassistant/components/kodi/translations/de.json @@ -6,12 +6,17 @@ "data": { "password": "Passwort", "username": "Benutzername" - } + }, + "description": "Bitte gib deinen Kodi-Benutzernamen und Passwort ein. Diese findest du unter System/Einstellungen/Netzwerk/Dienste." + }, + "discovery_confirm": { + "description": "M\u00f6chtest du Kodi (` {name} `) zu Home Assistant hinzuf\u00fcgen?" }, "host": { "data": { "host": "Host", - "port": "Port" + "port": "Port", + "ssl": "\u00dcber SSL verbinden" } }, "user": { diff --git a/homeassistant/components/kodi/translations/zh-Hans.json b/homeassistant/components/kodi/translations/zh-Hans.json new file mode 100644 index 00000000000..6fe91b6e995 --- /dev/null +++ b/homeassistant/components/kodi/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "credentials": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/konnected/translations/nl.json b/homeassistant/components/konnected/translations/nl.json index 6d6d03d9af1..43e86642727 100644 --- a/homeassistant/components/konnected/translations/nl.json +++ b/homeassistant/components/konnected/translations/nl.json @@ -52,7 +52,8 @@ "poll_interval": "Poll interval (minuten) (optioneel)", "type": "Type sensor" }, - "description": "{zone} opties" + "description": "{zone} opties", + "title": "Configureer digitale sensor" }, "options_io": { "data": { @@ -90,11 +91,13 @@ }, "options_switch": { "data": { + "momentary": "Pulsduur (ms) (optioneel)", "more_states": "Aanvullende statussen voor deze zone configureren", "name": "Naam (optioneel)", "pause": "Pauze tussen de pulsen (ms) (optioneel)", "repeat": "Aantal herhalingen (-1=oneindig) (optioneel)" }, + "description": "{zone} opties: status {state}", "title": "Schakelbare uitgang configureren" } } diff --git a/homeassistant/components/life360/translations/zh-Hans.json b/homeassistant/components/life360/translations/zh-Hans.json index 9495323aa1c..14e6d907861 100644 --- a/homeassistant/components/life360/translations/zh-Hans.json +++ b/homeassistant/components/life360/translations/zh-Hans.json @@ -1,6 +1,10 @@ { "config": { + "abort": { + "invalid_auth": "\u65e0\u6548\u7684\u8eab\u4efd\u9a8c\u8bc1" + }, "error": { + "invalid_auth": "\u65e0\u6548\u7684\u8eab\u4efd\u9a8c\u8bc1", "invalid_credentials": "\u65e0\u6548\u7684\u8eab\u4efd\u8ba4\u8bc1", "invalid_username": "\u65e0\u6548\u7684\u7528\u6237\u540d" }, diff --git a/homeassistant/components/light/translations/pl.json b/homeassistant/components/light/translations/pl.json index 3bb887b8d67..3b26bfcafd8 100644 --- a/homeassistant/components/light/translations/pl.json +++ b/homeassistant/components/light/translations/pl.json @@ -19,8 +19,8 @@ }, "state": { "_": { - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony" + "off": "wy\u0142.", + "on": "w\u0142." } }, "title": "\u015awiat\u0142o" diff --git a/homeassistant/components/media_player/translations/pl.json b/homeassistant/components/media_player/translations/pl.json index 8d4f42a1a61..865d2e1df2c 100644 --- a/homeassistant/components/media_player/translations/pl.json +++ b/homeassistant/components/media_player/translations/pl.json @@ -11,8 +11,8 @@ "state": { "_": { "idle": "nieaktywny", - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony", + "off": "wy\u0142.", + "on": "w\u0142.", "paused": "wstrzymany", "playing": "odtwarzanie", "standby": "tryb czuwania" diff --git a/homeassistant/components/meteo_france/translations/de.json b/homeassistant/components/meteo_france/translations/de.json index 1c05beac361..65313f16c41 100644 --- a/homeassistant/components/meteo_france/translations/de.json +++ b/homeassistant/components/meteo_france/translations/de.json @@ -4,7 +4,16 @@ "already_configured": "Stadt bereits konfiguriert", "unknown": "Unbekannter Fehler: Bitte versuchen Sie es sp\u00e4ter erneut" }, + "error": { + "empty": "Kein Ergebnis bei der Stadtsuche: Bitte \u00fcberpr\u00fcfe das Stadtfeld" + }, "step": { + "cities": { + "data": { + "city": "Stadt" + }, + "description": "W\u00e4hle deine Stadt aus der Liste" + }, "user": { "data": { "city": "Stadt" diff --git a/homeassistant/components/metoffice/translations/zh-Hans.json b/homeassistant/components/metoffice/translations/zh-Hans.json new file mode 100644 index 00000000000..597bfc38a86 --- /dev/null +++ b/homeassistant/components/metoffice/translations/zh-Hans.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", + "unknown": "\u672a\u77e5\u9519\u8bef" + }, + "step": { + "user": { + "data": { + "latitude": "\u7eac\u5ea6", + "longitude": "\u7ecf\u5ea6" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mill/translations/zh-Hans.json b/homeassistant/components/mill/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/mill/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mqtt/translations/zh-Hans.json b/homeassistant/components/mqtt/translations/zh-Hans.json index 4a9b860f873..e508f2cb29e 100644 --- a/homeassistant/components/mqtt/translations/zh-Hans.json +++ b/homeassistant/components/mqtt/translations/zh-Hans.json @@ -47,5 +47,14 @@ "button_short_release": "\"{subtype}\" \u91ca\u653e", "button_triple_press": "\"{subtype}\" \u4e09\u8fde\u51fb" } + }, + "options": { + "step": { + "broker": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/myq/translations/zh-Hans.json b/homeassistant/components/myq/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/myq/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/neato/translations/et.json b/homeassistant/components/neato/translations/et.json index e761939220b..8eed7850b4b 100644 --- a/homeassistant/components/neato/translations/et.json +++ b/homeassistant/components/neato/translations/et.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "Seade on juba h\u00e4\u00e4lestatud", - "invalid_auth": "Tuvastamise viga" + "invalid_auth": "Tuvastamise viga", + "invalid_credentials": "Sobimatu mandaat" }, "error": { "invalid_auth": "Tuvastamise viga", + "invalid_credentials": "Sobimatu mandaat", "unexpected_error": "Ootamatu t\u00f5rge", "unknown": "Ootamatu t\u00f5rge" }, diff --git a/homeassistant/components/neato/translations/zh-Hans.json b/homeassistant/components/neato/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/neato/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nest/translations/ca.json b/homeassistant/components/nest/translations/ca.json index 8f96d0f635e..ecb15329348 100644 --- a/homeassistant/components/nest/translations/ca.json +++ b/homeassistant/components/nest/translations/ca.json @@ -8,6 +8,9 @@ "no_flows": "Necessites configurar Nest abans de poder autenticar-t'hi. Llegeix les [instruccions](https://www.home-assistant.io/components/nest/).", "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." }, + "create_entry": { + "default": "Autenticaci\u00f3 exitosa" + }, "error": { "internal_error": "Error intern al validar el codi", "invalid_code": "Codi inv\u00e0lid", @@ -29,6 +32,9 @@ }, "description": "Per enlla\u00e7ar el teu compte de Nest, [autoritza el teu compte]({url}). \n\nDespr\u00e9s de l'autoritzaci\u00f3, copia i enganxa el codi pin que es mostra a sota.", "title": "Enlla\u00e7 amb el compte de Nest" + }, + "pick_implementation": { + "title": "Selecciona el m\u00e8tode d'autenticaci\u00f3" } } } diff --git a/homeassistant/components/nest/translations/cs.json b/homeassistant/components/nest/translations/cs.json index 15a1e041acb..dd9240270a1 100644 --- a/homeassistant/components/nest/translations/cs.json +++ b/homeassistant/components/nest/translations/cs.json @@ -8,6 +8,9 @@ "no_flows": "Pot\u0159ebujete nakonfigurovat Nest, abyste se s n\u00edm mohli autentizovat. [P\u0159e\u010dt\u011bte si pros\u00edm pokyny] (https://www.home-assistant.io/components/nest/).", "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." }, + "create_entry": { + "default": "\u00dasp\u011b\u0161n\u011b ov\u011b\u0159eno" + }, "error": { "internal_error": "Intern\u00ed chyba ov\u011b\u0159en\u00ed k\u00f3du", "invalid_code": "Neplatn\u00fd k\u00f3d", @@ -29,6 +32,9 @@ }, "description": "Chcete-li propojit \u00fa\u010det Nest, [autorizujte sv\u016fj \u00fa\u010det]({url}). \n\n Po autorizaci zkop\u00edrujte n\u00ed\u017ee uveden\u00fd k\u00f3d PIN.", "title": "Propojit s Nest \u00fa\u010dtem" + }, + "pick_implementation": { + "title": "Vyberte metodu ov\u011b\u0159en\u00ed" } } } diff --git a/homeassistant/components/nest/translations/en.json b/homeassistant/components/nest/translations/en.json index bbe7d9dad91..4243926a55e 100644 --- a/homeassistant/components/nest/translations/en.json +++ b/homeassistant/components/nest/translations/en.json @@ -8,6 +8,9 @@ "no_flows": "You need to configure Nest before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/nest/).", "single_instance_allowed": "Already configured. Only a single configuration possible." }, + "create_entry": { + "default": "Successfully authenticated" + }, "error": { "internal_error": "Internal error validating code", "invalid_code": "Invalid code", @@ -29,6 +32,9 @@ }, "description": "To link your Nest account, [authorize your account]({url}).\n\nAfter authorization, copy-paste the provided pin code below.", "title": "Link Nest Account" + }, + "pick_implementation": { + "title": "Pick Authentication Method" } } } diff --git a/homeassistant/components/nest/translations/et.json b/homeassistant/components/nest/translations/et.json index 3280e6e038c..1389bbb5c14 100644 --- a/homeassistant/components/nest/translations/et.json +++ b/homeassistant/components/nest/translations/et.json @@ -29,6 +29,9 @@ }, "description": "Nest-i konto linkimiseks [authorize your account] ({url}).\n\nP\u00e4rast autoriseerimist kopeeri allolev PIN kood.", "title": "Lingi Nesti konto" + }, + "pick_implementation": { + "title": "Vali tuvastusmeetod" } } } diff --git a/homeassistant/components/nest/translations/pl.json b/homeassistant/components/nest/translations/pl.json index dbfcdb982c5..3b9f87a3f72 100644 --- a/homeassistant/components/nest/translations/pl.json +++ b/homeassistant/components/nest/translations/pl.json @@ -8,6 +8,9 @@ "no_flows": "Musisz skonfigurowa\u0107 Nest, aby m\u00f3c si\u0119 z nim uwierzytelni\u0107. Zapoznaj si\u0119 z [instrukcj\u0105](https://www.home-assistant.io/components/nest/).", "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." }, + "create_entry": { + "default": "Pomy\u015blnie uwierzytelniono" + }, "error": { "internal_error": "Wewn\u0119trzny b\u0142\u0105d sprawdzania poprawno\u015bci kodu", "invalid_code": "Nieprawid\u0142owy kod", @@ -29,6 +32,9 @@ }, "description": "Aby po\u0142\u0105czy\u0107 z kontem Nest, [wykonaj autoryzacj\u0119]({url}). \n\n Po autoryzacji skopiuj i wklej podany kod PIN poni\u017cej.", "title": "Po\u0142\u0105czenie z kontem Nest" + }, + "pick_implementation": { + "title": "Wybierz metod\u0119 uwierzytelniania" } } } diff --git a/homeassistant/components/nest/translations/ru.json b/homeassistant/components/nest/translations/ru.json index c9461ccbf3a..82c50bab5ee 100644 --- a/homeassistant/components/nest/translations/ru.json +++ b/homeassistant/components/nest/translations/ru.json @@ -8,6 +8,9 @@ "no_flows": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 Nest \u043f\u0435\u0440\u0435\u0434 \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0435\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438](https://www.home-assistant.io/components/nest/).", "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." }, + "create_entry": { + "default": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, "error": { "internal_error": "\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044f\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043a\u043e\u0434\u0430.", "invalid_code": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043e\u0434.", @@ -29,6 +32,9 @@ }, "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 \u0441\u0432\u043e\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Nest. \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 \u043f\u0438\u043d-\u043a\u043e\u0434.", "title": "\u041f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Nest" + }, + "pick_implementation": { + "title": "\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" } } } diff --git a/homeassistant/components/nest/translations/zh-Hans.json b/homeassistant/components/nest/translations/zh-Hans.json index ce0824075b2..55d610219a6 100644 --- a/homeassistant/components/nest/translations/zh-Hans.json +++ b/homeassistant/components/nest/translations/zh-Hans.json @@ -9,6 +9,7 @@ "error": { "internal_error": "\u9a8c\u8bc1\u4ee3\u7801\u65f6\u53d1\u751f\u5185\u90e8\u9519\u8bef", "invalid_code": "\u9a8c\u8bc1\u7801\u65e0\u6548", + "invalid_pin": "PIN\u7801\u65e0\u6548", "timeout": "\u9a8c\u8bc1\u7801\u8d85\u65f6", "unknown": "\u9a8c\u8bc1\u7801\u672a\u77e5\u9519\u8bef" }, diff --git a/homeassistant/components/nest/translations/zh-Hant.json b/homeassistant/components/nest/translations/zh-Hant.json index 9bf75cc6fa2..87f8b62427a 100644 --- a/homeassistant/components/nest/translations/zh-Hant.json +++ b/homeassistant/components/nest/translations/zh-Hant.json @@ -8,6 +8,9 @@ "no_flows": "\u5fc5\u9808\u5148\u8a2d\u5b9a Nest \u65b9\u80fd\u9032\u884c\u8a8d\u8b49\u3002[\u8acb\u53c3\u95b1\u6559\u5b78\u6307\u5f15](https://www.home-assistant.io/components/nest/)\u3002", "single_instance_allowed": "\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u8a2d\u5099\u3002" }, + "create_entry": { + "default": "\u5df2\u6210\u529f\u8a8d\u8b49" + }, "error": { "internal_error": "\u8a8d\u8b49\u78bc\u5167\u90e8\u932f\u8aa4", "invalid_code": "\u8a8d\u8b49\u78bc\u7121\u6548", @@ -29,6 +32,9 @@ }, "description": "\u6b32\u9023\u7d50 Nest \u5e33\u865f\uff0c[\u8a8d\u8b49\u5e33\u865f]({url}).\n\n\u65bc\u8a8d\u8b49\u5f8c\uff0c\u8907\u88fd\u4e26\u8cbc\u4e0a\u4e0b\u65b9\u7684\u8a8d\u8b49\u78bc\u3002", "title": "\u9023\u7d50 Nest \u5e33\u865f" + }, + "pick_implementation": { + "title": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f" } } } diff --git a/homeassistant/components/netatmo/translations/et.json b/homeassistant/components/netatmo/translations/et.json index c0f672dd18c..7e3a8ad5a8b 100644 --- a/homeassistant/components/netatmo/translations/et.json +++ b/homeassistant/components/netatmo/translations/et.json @@ -3,6 +3,11 @@ "abort": { "no_url_available": "URL pole saadaval. Rohkem teavet [check the help section]({docs_url})", "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." + }, + "step": { + "pick_implementation": { + "title": "Vali tuvastusmeetod" + } } }, "options": { diff --git a/homeassistant/components/nexia/translations/zh-Hans.json b/homeassistant/components/nexia/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/nexia/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nuheat/translations/zh-Hans.json b/homeassistant/components/nuheat/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/nuheat/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nut/translations/zh-Hans.json b/homeassistant/components/nut/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/nut/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nzbget/translations/de.json b/homeassistant/components/nzbget/translations/de.json index 6aa89d2e62e..2ebae1083eb 100644 --- a/homeassistant/components/nzbget/translations/de.json +++ b/homeassistant/components/nzbget/translations/de.json @@ -9,7 +9,8 @@ "password": "Passwort", "port": "Port", "username": "Benutzername" - } + }, + "title": "Mit NZBGet verbinden" } } }, diff --git a/homeassistant/components/nzbget/translations/zh-Hans.json b/homeassistant/components/nzbget/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/nzbget/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/omnilogic/translations/zh-Hans.json b/homeassistant/components/omnilogic/translations/zh-Hans.json new file mode 100644 index 00000000000..b0b26b02261 --- /dev/null +++ b/homeassistant/components/omnilogic/translations/zh-Hans.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "\u5bc6\u7801", + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/onvif/translations/zh-Hans.json b/homeassistant/components/onvif/translations/zh-Hans.json index c5269fccbd2..b6ad5c30a77 100644 --- a/homeassistant/components/onvif/translations/zh-Hans.json +++ b/homeassistant/components/onvif/translations/zh-Hans.json @@ -1,4 +1,13 @@ { + "config": { + "step": { + "auth": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + }, "options": { "step": { "onvif_devices": { diff --git a/homeassistant/components/openweathermap/translations/de.json b/homeassistant/components/openweathermap/translations/de.json index 6582b2046b8..964fdbb9419 100644 --- a/homeassistant/components/openweathermap/translations/de.json +++ b/homeassistant/components/openweathermap/translations/de.json @@ -1,7 +1,8 @@ { "config": { "error": { - "auth": "Der API-Schl\u00fcssel ist nicht korrekt." + "auth": "Der API-Schl\u00fcssel ist nicht korrekt.", + "connection": "Es kann keine Verbindung zur OWM-API hergestellt werden" }, "step": { "user": { @@ -9,7 +10,8 @@ "language": "Sprache", "latitude": "Breitengrad", "longitude": "L\u00e4ngengrad", - "mode": "Modus" + "mode": "Modus", + "name": "Name der Integration" }, "title": "OpenWeatherMap" } diff --git a/homeassistant/components/ovo_energy/translations/zh-Hans.json b/homeassistant/components/ovo_energy/translations/zh-Hans.json new file mode 100644 index 00000000000..a7477e8c370 --- /dev/null +++ b/homeassistant/components/ovo_energy/translations/zh-Hans.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", + "invalid_auth": "\u9a8c\u8bc1\u7801\u9519\u8bef" + }, + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/plex/translations/nl.json b/homeassistant/components/plex/translations/nl.json index 2450e510605..218e11ac76d 100644 --- a/homeassistant/components/plex/translations/nl.json +++ b/homeassistant/components/plex/translations/nl.json @@ -38,6 +38,7 @@ "step": { "plex_mp_settings": { "data": { + "ignore_new_shared_users": "Negeer nieuwe beheerde/gedeelde gebruikers", "use_episode_art": "Gebruik aflevering kunst" }, "description": "Opties voor Plex-mediaspelers" diff --git a/homeassistant/components/plugwise/translations/de.json b/homeassistant/components/plugwise/translations/de.json index 02b46f75110..43ea3c8e3b5 100644 --- a/homeassistant/components/plugwise/translations/de.json +++ b/homeassistant/components/plugwise/translations/de.json @@ -21,5 +21,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Scanintervall (Sekunden)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/zh-Hans.json b/homeassistant/components/plugwise/translations/zh-Hans.json new file mode 100644 index 00000000000..80c129af78d --- /dev/null +++ b/homeassistant/components/plugwise/translations/zh-Hans.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user_gateway": { + "data": { + "host": "IP\u5730\u5740", + "port": "\u7aef\u53e3" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/plum_lightpad/translations/zh-Hans.json b/homeassistant/components/plum_lightpad/translations/zh-Hans.json new file mode 100644 index 00000000000..6a9d4aa28c5 --- /dev/null +++ b/homeassistant/components/plum_lightpad/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u90ae\u7bb1" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/point/translations/et.json b/homeassistant/components/point/translations/et.json index b9573cda583..d019b687733 100644 --- a/homeassistant/components/point/translations/et.json +++ b/homeassistant/components/point/translations/et.json @@ -15,7 +15,7 @@ "step": { "user": { "description": "Kas alustan seadistamist?", - "title": "Vali tuvastsmeetod" + "title": "Vali tuvastusmeetod" } } } diff --git a/homeassistant/components/ps4/translations/zh-Hans.json b/homeassistant/components/ps4/translations/zh-Hans.json index 821cb0ed056..770a3d8953b 100644 --- a/homeassistant/components/ps4/translations/zh-Hans.json +++ b/homeassistant/components/ps4/translations/zh-Hans.json @@ -8,6 +8,7 @@ "port_997_bind_error": "\u65e0\u6cd5\u7ed1\u5b9a\u7aef\u53e3 997\u3002" }, "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", "login_failed": "\u65e0\u6cd5\u4e0e PlayStation 4 \u914d\u5bf9\u3002\u8bf7\u786e\u8ba4 PIN \u662f\u5426\u6b63\u786e\u3002", "not_ready": "PlayStation 4 \u672a\u5f00\u673a\u6216\u672a\u8fde\u63a5\u5230\u7f51\u7edc\u3002" }, diff --git a/homeassistant/components/rainmachine/translations/et.json b/homeassistant/components/rainmachine/translations/et.json index 201733b2a61..1b17200b762 100644 --- a/homeassistant/components/rainmachine/translations/et.json +++ b/homeassistant/components/rainmachine/translations/et.json @@ -5,7 +5,8 @@ }, "error": { "identifier_exists": "Konto on juba seadistatud", - "invalid_auth": "Tuvastamise viga" + "invalid_auth": "Tuvastamise viga", + "invalid_credentials": "Sobimatu mandaat" }, "step": { "user": { diff --git a/homeassistant/components/remote/translations/pl.json b/homeassistant/components/remote/translations/pl.json index 608e80c30b6..3c319ff548c 100644 --- a/homeassistant/components/remote/translations/pl.json +++ b/homeassistant/components/remote/translations/pl.json @@ -16,8 +16,8 @@ }, "state": { "_": { - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony" + "off": "wy\u0142.", + "on": "w\u0142." } }, "title": "Pilot" diff --git a/homeassistant/components/remote/translations/zh-Hans.json b/homeassistant/components/remote/translations/zh-Hans.json index 70ab64a957f..ba1344fbb74 100644 --- a/homeassistant/components/remote/translations/zh-Hans.json +++ b/homeassistant/components/remote/translations/zh-Hans.json @@ -1,4 +1,17 @@ { + "device_automation": { + "action_type": { + "turn_off": "\u5173\u95ed {entity_name}" + }, + "condition_type": { + "is_off": "{entity_name} \u5df2\u5173\u95ed", + "is_on": "{entity_name} \u5df2\u6253\u5f00" + }, + "trigger_type": { + "turned_off": "{entity_name} \u88ab\u5173\u95ed", + "turned_on": "{entity_name} \u88ab\u6253\u5f00" + } + }, "state": { "_": { "off": "\u5173", diff --git a/homeassistant/components/rfxtrx/translations/de.json b/homeassistant/components/rfxtrx/translations/de.json index cbd7d2f3ae4..c4b378db213 100644 --- a/homeassistant/components/rfxtrx/translations/de.json +++ b/homeassistant/components/rfxtrx/translations/de.json @@ -4,6 +4,9 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert" }, "step": { + "setup_network": { + "title": "Verbindungsadresse ausw\u00e4hlen" + }, "setup_serial": { "data": { "device": "Ger\u00e4t w\u00e4hlen" @@ -20,5 +23,14 @@ "title": "Verbindungstyp ausw\u00e4hlen" } } + }, + "options": { + "step": { + "prompt_options": { + "data": { + "debug": "Debugging aktivieren" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/rfxtrx/translations/zh-Hans.json b/homeassistant/components/rfxtrx/translations/zh-Hans.json new file mode 100644 index 00000000000..49e92607835 --- /dev/null +++ b/homeassistant/components/rfxtrx/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "setup_network": { + "data": { + "port": "\u7aef\u53e3" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ring/translations/zh-Hans.json b/homeassistant/components/ring/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/ring/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/risco/translations/zh-Hans.json b/homeassistant/components/risco/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/risco/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/roon/translations/de.json b/homeassistant/components/roon/translations/de.json index b73dc4d6444..00f85d6c1b6 100644 --- a/homeassistant/components/roon/translations/de.json +++ b/homeassistant/components/roon/translations/de.json @@ -1,3 +1,8 @@ { + "config": { + "error": { + "duplicate_entry": "Dieser Host wurde bereits hinzugef\u00fcgt." + } + }, "title": "Roon" } \ No newline at end of file diff --git a/homeassistant/components/ruckus_unleashed/translations/zh-Hans.json b/homeassistant/components/ruckus_unleashed/translations/zh-Hans.json new file mode 100644 index 00000000000..b6a239d7b99 --- /dev/null +++ b/homeassistant/components/ruckus_unleashed/translations/zh-Hans.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + }, + "step": { + "user": { + "data": { + "password": "\u5bc6\u7801", + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/script/translations/pl.json b/homeassistant/components/script/translations/pl.json index 8982527645e..625b8cf9d92 100644 --- a/homeassistant/components/script/translations/pl.json +++ b/homeassistant/components/script/translations/pl.json @@ -1,8 +1,8 @@ { "state": { "_": { - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony" + "off": "wy\u0142.", + "on": "w\u0142." } }, "title": "Skrypt" diff --git a/homeassistant/components/sensor/translations/pl.json b/homeassistant/components/sensor/translations/pl.json index 8d355cc5d93..aa2d9f4ca11 100644 --- a/homeassistant/components/sensor/translations/pl.json +++ b/homeassistant/components/sensor/translations/pl.json @@ -33,8 +33,8 @@ }, "state": { "_": { - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony" + "off": "wy\u0142.", + "on": "w\u0142." } }, "title": "Sensor" diff --git a/homeassistant/components/sharkiq/translations/zh-Hans.json b/homeassistant/components/sharkiq/translations/zh-Hans.json new file mode 100644 index 00000000000..7a170396cae --- /dev/null +++ b/homeassistant/components/sharkiq/translations/zh-Hans.json @@ -0,0 +1,16 @@ +{ + "config": { + "step": { + "reauth": { + "data": { + "username": "\u7528\u6237\u540d" + } + }, + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/ca.json b/homeassistant/components/shelly/translations/ca.json index 41ffcb34b89..6df92102699 100644 --- a/homeassistant/components/shelly/translations/ca.json +++ b/homeassistant/components/shelly/translations/ca.json @@ -10,7 +10,7 @@ "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "unknown": "Error inesperat" }, - "flow_title": "Shelly: {name}", + "flow_title": "{name}", "step": { "confirm_discovery": { "description": "Vols configurar {model} a {host}? \n\nAbans de configurar-lo, el dispositiu amb bateria ha d'estar despert, prem el bot\u00f3 del dispositiu per despertar-lo." diff --git a/homeassistant/components/shelly/translations/cs.json b/homeassistant/components/shelly/translations/cs.json index 3f477c35675..3f9397d5cc0 100644 --- a/homeassistant/components/shelly/translations/cs.json +++ b/homeassistant/components/shelly/translations/cs.json @@ -10,7 +10,7 @@ "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, - "flow_title": "Shelly: {name}", + "flow_title": "{name}", "step": { "confirm_discovery": { "description": "Chcete nastavit {model} na {host}?\n\nP\u0159ed nastaven\u00edm mus\u00ed b\u00fdt za\u0159\u00edzen\u00ed nap\u00e1jen\u00e9 z baterie probuzeno stisknut\u00edm tla\u010d\u00edtka na dan\u00e9m za\u0159\u00edzen\u00ed." diff --git a/homeassistant/components/shelly/translations/pl.json b/homeassistant/components/shelly/translations/pl.json index 688ac6ed7e6..c59ca677533 100644 --- a/homeassistant/components/shelly/translations/pl.json +++ b/homeassistant/components/shelly/translations/pl.json @@ -10,7 +10,7 @@ "invalid_auth": "Niepoprawne uwierzytelnienie", "unknown": "Nieoczekiwany b\u0142\u0105d" }, - "flow_title": "Shelly: {name}", + "flow_title": "{name}", "step": { "confirm_discovery": { "description": "Czy chcesz skonfigurowa\u0107 {model} ({host})?\n\nPrzed konfiguracj\u0105 urz\u0105dzenie zasilane bateryjnie nale\u017cy wybudzi\u0107, naciskaj\u0105c przycisk na urz\u0105dzeniu." diff --git a/homeassistant/components/shelly/translations/ru.json b/homeassistant/components/shelly/translations/ru.json index 24478afe0a4..47b4ad27c70 100644 --- a/homeassistant/components/shelly/translations/ru.json +++ b/homeassistant/components/shelly/translations/ru.json @@ -10,7 +10,7 @@ "invalid_auth": "\u041d\u0435\u0432\u0435\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, - "flow_title": "Shelly: {name}", + "flow_title": "{name}", "step": { "confirm_discovery": { "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c {model} ({host}) ?\n\n\u041f\u0435\u0440\u0435\u0434 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0435 \u043e\u0442 \u0431\u0430\u0442\u0430\u0440\u0435\u0438, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u0432\u0435\u0441\u0442\u0438 \u0438\u0437 \u0441\u043f\u044f\u0449\u0435\u0433\u043e \u0440\u0435\u0436\u0438\u043c\u0430, \u043d\u0430\u0436\u0430\u0432 \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435." diff --git a/homeassistant/components/shelly/translations/zh-Hans.json b/homeassistant/components/shelly/translations/zh-Hans.json new file mode 100644 index 00000000000..6fe91b6e995 --- /dev/null +++ b/homeassistant/components/shelly/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "credentials": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/et.json b/homeassistant/components/simplisafe/translations/et.json index 36178300e03..95a24352db9 100644 --- a/homeassistant/components/simplisafe/translations/et.json +++ b/homeassistant/components/simplisafe/translations/et.json @@ -6,6 +6,7 @@ }, "error": { "invalid_auth": "Tuvastamise viga", + "invalid_credentials": "Sobimatu mandaat", "unknown": "Tundmatu viga" }, "step": { diff --git a/homeassistant/components/simplisafe/translations/zh-Hans.json b/homeassistant/components/simplisafe/translations/zh-Hans.json index 721ce3a94f4..e4bb147aea2 100644 --- a/homeassistant/components/simplisafe/translations/zh-Hans.json +++ b/homeassistant/components/simplisafe/translations/zh-Hans.json @@ -2,6 +2,7 @@ "config": { "error": { "identifier_exists": "\u8d26\u6237\u5df2\u6ce8\u518c", + "invalid_auth": "\u65e0\u6548\u7684\u8eab\u4efd\u9a8c\u8bc1", "invalid_credentials": "\u65e0\u6548\u7684\u8eab\u4efd\u8ba4\u8bc1" }, "step": { diff --git a/homeassistant/components/smappee/translations/et.json b/homeassistant/components/smappee/translations/et.json index 1a2b1dd95a0..b57bd9b81ff 100644 --- a/homeassistant/components/smappee/translations/et.json +++ b/homeassistant/components/smappee/translations/et.json @@ -5,6 +5,7 @@ "already_configured_local_device": "Kohalikud seadmed on juba seadistatud. Enne pilveseadme seadistamist eemaldage need.", "cannot_connect": "\u00dchendamine nurjus", "connection_error": "Smappee seadmega \u00fchenduse loomine nurjus.", + "invalid_mdns": "Seade ei toeta Smappee'ga sidumist.", "missing_configuration": "Osis pole seadistatud. Palun vaata dokumentatsiooni.", "no_url_available": "URL pole saadaval. Rohkem teavet [check the help section]({docs_url})" }, @@ -15,6 +16,9 @@ }, "description": "Smappee kohaliku sidumise algatamiseks sisestage hostinimi" }, + "pick_implementation": { + "title": "Vali tuvastusmeetod" + }, "zeroconf_confirm": { "description": "Kas soovid lisada Home Assistanti Smappee seadme seerianumbriga \" {serial_number} \"?" } diff --git a/homeassistant/components/smart_meter_texas/translations/zh-Hans.json b/homeassistant/components/smart_meter_texas/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/smart_meter_texas/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/somfy/translations/et.json b/homeassistant/components/somfy/translations/et.json index fe64d4d4121..184faad8f40 100644 --- a/homeassistant/components/somfy/translations/et.json +++ b/homeassistant/components/somfy/translations/et.json @@ -4,6 +4,11 @@ "already_setup": "Saad h\u00e4\u00e4lestada ainult \u00fche Somfy konto.", "no_url_available": "URL pole saadaval. Rohkem teavet [check the help section]({docs_url})", "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." + }, + "step": { + "pick_implementation": { + "title": "Vali tuvastusmeetod" + } } } } \ No newline at end of file diff --git a/homeassistant/components/spider/translations/zh-Hans.json b/homeassistant/components/spider/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/spider/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/spotify/translations/et.json b/homeassistant/components/spotify/translations/et.json index 04d29ccd0a4..3403292db03 100644 --- a/homeassistant/components/spotify/translations/et.json +++ b/homeassistant/components/spotify/translations/et.json @@ -12,7 +12,7 @@ }, "step": { "pick_implementation": { - "title": "Valige autentimismeetod" + "title": "Vali tuvastusmeetod" }, "reauth_confirm": { "description": "Spotify konto: {account} sidumine tuleb uuesti autentida", diff --git a/homeassistant/components/squeezebox/translations/zh-Hans.json b/homeassistant/components/squeezebox/translations/zh-Hans.json new file mode 100644 index 00000000000..56359f49302 --- /dev/null +++ b/homeassistant/components/squeezebox/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "edit": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/starline/translations/zh-Hans.json b/homeassistant/components/starline/translations/zh-Hans.json new file mode 100644 index 00000000000..980744e116e --- /dev/null +++ b/homeassistant/components/starline/translations/zh-Hans.json @@ -0,0 +1,14 @@ +{ + "config": { + "error": { + "error_auth_user": "\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef" + }, + "step": { + "auth_user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/switch/translations/pl.json b/homeassistant/components/switch/translations/pl.json index 4b3aebf30b4..0d0d7263bf2 100644 --- a/homeassistant/components/switch/translations/pl.json +++ b/homeassistant/components/switch/translations/pl.json @@ -16,8 +16,8 @@ }, "state": { "_": { - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony" + "off": "wy\u0142.", + "on": "w\u0142." } }, "title": "Prze\u0142\u0105cznik" diff --git a/homeassistant/components/switch/translations/zh-Hans.json b/homeassistant/components/switch/translations/zh-Hans.json index eed1df46204..8820cb9e435 100644 --- a/homeassistant/components/switch/translations/zh-Hans.json +++ b/homeassistant/components/switch/translations/zh-Hans.json @@ -1,4 +1,16 @@ { + "device_automation": { + "action_type": { + "turn_off": "\u5173\u95ed {entity_name}" + }, + "condition_type": { + "is_off": "{entity_name} \u5df2\u5173\u95ed" + }, + "trigger_type": { + "turned_off": "{entity_name} \u88ab\u5173\u95ed", + "turned_on": "{entity_name} \u88ab\u6253\u5f00" + } + }, "state": { "_": { "off": "\u5173", diff --git a/homeassistant/components/syncthru/translations/et.json b/homeassistant/components/syncthru/translations/et.json index a6dd5820048..71fe5cfb03c 100644 --- a/homeassistant/components/syncthru/translations/et.json +++ b/homeassistant/components/syncthru/translations/et.json @@ -4,6 +4,7 @@ "already_configured": "Seade on juba h\u00e4\u00e4lestatud" }, "error": { + "invalid_url": "Sobimatu URL", "unknown_state": "Printeri olek teadmata, kontrolli URL-i ja v\u00f5rgu\u00fchendust" } } diff --git a/homeassistant/components/synology_dsm/translations/de.json b/homeassistant/components/synology_dsm/translations/de.json index f54a7e2a378..205bf5197b0 100644 --- a/homeassistant/components/synology_dsm/translations/de.json +++ b/homeassistant/components/synology_dsm/translations/de.json @@ -44,7 +44,8 @@ "step": { "init": { "data": { - "scan_interval": "Minuten zwischen den Scans" + "scan_interval": "Minuten zwischen den Scans", + "timeout": "Timeout (Sekunden)" } } } diff --git a/homeassistant/components/tado/translations/zh-Hans.json b/homeassistant/components/tado/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/tado/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tibber/translations/zh-Hans.json b/homeassistant/components/tibber/translations/zh-Hans.json new file mode 100644 index 00000000000..2941dfd9383 --- /dev/null +++ b/homeassistant/components/tibber/translations/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tile/translations/zh-Hans.json b/homeassistant/components/tile/translations/zh-Hans.json new file mode 100644 index 00000000000..6a9d4aa28c5 --- /dev/null +++ b/homeassistant/components/tile/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u90ae\u7bb1" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/totalconnect/translations/zh-Hans.json b/homeassistant/components/totalconnect/translations/zh-Hans.json new file mode 100644 index 00000000000..1d0c0eb1071 --- /dev/null +++ b/homeassistant/components/totalconnect/translations/zh-Hans.json @@ -0,0 +1,14 @@ +{ + "config": { + "error": { + "login": "\u767b\u5f55\u9519\u8bef\uff1a\u8bf7\u68c0\u67e5\u7528\u6237\u540d\u548c\u5bc6\u7801" + }, + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/transmission/translations/zh-Hans.json b/homeassistant/components/transmission/translations/zh-Hans.json new file mode 100644 index 00000000000..feebc8ea8a2 --- /dev/null +++ b/homeassistant/components/transmission/translations/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "wrong_credentials": "\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/zh-Hans.json b/homeassistant/components/tuya/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/tuya/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/twentemilieu/translations/zh-Hans.json b/homeassistant/components/twentemilieu/translations/zh-Hans.json index 80301cfd57b..507422c7355 100644 --- a/homeassistant/components/twentemilieu/translations/zh-Hans.json +++ b/homeassistant/components/twentemilieu/translations/zh-Hans.json @@ -2,6 +2,10 @@ "config": { "abort": { "address_exists": "\u5730\u5740\u5df2\u7ecf\u8bbe\u7f6e\u597d\u4e86\u3002" + }, + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", + "connection_error": "\u8fde\u63a5\u5931\u8d25\u3002" } } } \ No newline at end of file diff --git a/homeassistant/components/unifi/translations/nl.json b/homeassistant/components/unifi/translations/nl.json index f945e5c4d6d..9c4804c09a2 100644 --- a/homeassistant/components/unifi/translations/nl.json +++ b/homeassistant/components/unifi/translations/nl.json @@ -54,7 +54,8 @@ "data": { "block_client": "Cli\u00ebnten met netwerktoegang", "track_clients": "Volg netwerkclients" - } + }, + "description": "Configureer UniFi-integratie" }, "statistics_sensors": { "data": { diff --git a/homeassistant/components/upcloud/translations/de.json b/homeassistant/components/upcloud/translations/de.json index 6f398062876..ffdd1e0dd58 100644 --- a/homeassistant/components/upcloud/translations/de.json +++ b/homeassistant/components/upcloud/translations/de.json @@ -8,5 +8,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Aktualisierungsintervall in Sekunden, mindestens 30" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/upcloud/translations/zh-Hans.json b/homeassistant/components/upcloud/translations/zh-Hans.json new file mode 100644 index 00000000000..83be90bc292 --- /dev/null +++ b/homeassistant/components/upcloud/translations/zh-Hans.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "invalid_auth": "\u65e0\u6548\u7684\u8eab\u4efd\u9a8c\u8bc1" + }, + "step": { + "user": { + "data": { + "password": "\u5bc6\u7801", + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vacuum/translations/pl.json b/homeassistant/components/vacuum/translations/pl.json index 6eef0f893f7..27a1dfb1505 100644 --- a/homeassistant/components/vacuum/translations/pl.json +++ b/homeassistant/components/vacuum/translations/pl.json @@ -19,8 +19,8 @@ "docked": "w stacji dokuj\u0105cej", "error": "b\u0142\u0105d", "idle": "nieaktywny", - "off": "wy\u0142\u0105czony", - "on": "w\u0142\u0105czony", + "off": "wy\u0142.", + "on": "w\u0142.", "paused": "wstrzymany", "returning": "powr\u00f3t do stacji dokuj\u0105cej" } diff --git a/homeassistant/components/vesync/translations/zh-Hans.json b/homeassistant/components/vesync/translations/zh-Hans.json index caa00f36c89..e256110ffa7 100644 --- a/homeassistant/components/vesync/translations/zh-Hans.json +++ b/homeassistant/components/vesync/translations/zh-Hans.json @@ -2,6 +2,14 @@ "config": { "abort": { "already_setup": "\u53ea\u5141\u8bb8\u4e00\u4e2aVesync\u5b9e\u4f8b" + }, + "error": { + "invalid_login": "\u7528\u6237\u540d\u6216\u5bc6\u7801\u65e0\u6548" + }, + "step": { + "user": { + "title": "\u8f93\u5165\u7528\u6237\u540d\u548c\u5bc6\u7801" + } } } } \ No newline at end of file diff --git a/homeassistant/components/water_heater/translations/zh-Hans.json b/homeassistant/components/water_heater/translations/zh-Hans.json new file mode 100644 index 00000000000..21a77facc93 --- /dev/null +++ b/homeassistant/components/water_heater/translations/zh-Hans.json @@ -0,0 +1,8 @@ +{ + "device_automation": { + "action_type": { + "turn_off": "\u5173\u95ed {entity_name}", + "turn_on": "\u6253\u5f00 {entity_name}" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wled/translations/zh-Hans.json b/homeassistant/components/wled/translations/zh-Hans.json new file mode 100644 index 00000000000..254f6df9327 --- /dev/null +++ b/homeassistant/components/wled/translations/zh-Hans.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + }, + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/sensor.de.json b/homeassistant/components/wolflink/translations/sensor.de.json index ef60c1c1ae1..373e1989578 100644 --- a/homeassistant/components/wolflink/translations/sensor.de.json +++ b/homeassistant/components/wolflink/translations/sensor.de.json @@ -1,6 +1,13 @@ { "state": { "wolflink__state": { + "solarbetrieb": "Solarmodus", + "sparbetrieb": "Sparmodus", + "sparen": "Sparen", + "stabilisierung": "Stabilisierung", + "standby": "Standby", + "start": "Start", + "storung": "Fehler", "test": "Test", "tpw": "TPW" } diff --git a/homeassistant/components/wolflink/translations/sensor.pl.json b/homeassistant/components/wolflink/translations/sensor.pl.json index a0894f38816..176f0c49b57 100644 --- a/homeassistant/components/wolflink/translations/sensor.pl.json +++ b/homeassistant/components/wolflink/translations/sensor.pl.json @@ -9,7 +9,7 @@ "antilegionellenfunktion": "funkcja antylegionella", "at_abschaltung": "granica wy\u0142\u0105czenia temperatura zewn\u0119trzna", "at_frostschutz": "ochrona antyzamro\u017ceniowa AT", - "aus": "wy\u0142\u0105czone", + "aus": "wy\u0142\u0105czony", "auto": "automatyczny", "auto_off_cool": "AutoOffCool", "auto_on_cool": "AutoOnCool", diff --git a/homeassistant/components/wolflink/translations/zh-Hans.json b/homeassistant/components/wolflink/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/wolflink/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yeelight/translations/de.json b/homeassistant/components/yeelight/translations/de.json index 6930fca0a5b..b282a4d8b84 100644 --- a/homeassistant/components/yeelight/translations/de.json +++ b/homeassistant/components/yeelight/translations/de.json @@ -18,6 +18,8 @@ "step": { "init": { "data": { + "save_on_change": "Status bei \u00c4nderung speichern", + "transition": "\u00dcbergangszeit (ms)", "use_music_mode": "Musik-Modus aktivieren" } } diff --git a/homeassistant/components/zha/translations/zh-Hans.json b/homeassistant/components/zha/translations/zh-Hans.json index 7a841edbb3b..4df88473d0d 100644 --- a/homeassistant/components/zha/translations/zh-Hans.json +++ b/homeassistant/components/zha/translations/zh-Hans.json @@ -11,5 +11,13 @@ "title": "ZHA" } } + }, + "device_automation": { + "trigger_subtype": { + "turn_off": "\u5173\u95ed" + }, + "trigger_type": { + "device_offline": "\u8bbe\u5907\u79bb\u7ebf" + } } } \ No newline at end of file diff --git a/homeassistant/components/zoneminder/translations/zh-Hans.json b/homeassistant/components/zoneminder/translations/zh-Hans.json new file mode 100644 index 00000000000..a5f4ff11f09 --- /dev/null +++ b/homeassistant/components/zoneminder/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file From fcbe90cb80db8905a0177129b386bef79a908fdd Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Thu, 22 Oct 2020 10:06:16 +0200 Subject: [PATCH 009/118] Fix precision of climate devices in devolo Home Control (#41832) --- .../components/devolo_home_control/climate.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/devolo_home_control/climate.py b/homeassistant/components/devolo_home_control/climate.py index f243b5c508d..0cc2340dc18 100644 --- a/homeassistant/components/devolo_home_control/climate.py +++ b/homeassistant/components/devolo_home_control/climate.py @@ -9,7 +9,7 @@ from homeassistant.components.climate import ( ClimateEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PRECISION_HALVES +from homeassistant.const import PRECISION_HALVES, PRECISION_TENTHS from homeassistant.helpers.typing import HomeAssistantType from .const import DOMAIN @@ -60,9 +60,14 @@ class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntit @property def target_temperature(self) -> Optional[float]: - """Return the current temperature.""" + """Return the target temperature.""" return self._value + @property + def target_temperature_step(self) -> float: + """Return the precision of the target temperature.""" + return PRECISION_HALVES + @property def hvac_mode(self) -> str: """Return the supported HVAC mode.""" @@ -86,7 +91,7 @@ class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntit @property def precision(self) -> float: """Return the precision of the set temperature.""" - return PRECISION_HALVES + return PRECISION_TENTHS @property def supported_features(self): From 6c5e2af379f8a7818d7960a56973f9d24c45cf95 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Thu, 22 Oct 2020 10:01:43 +0200 Subject: [PATCH 010/118] Add Netatmo sensor enable default (#41928) --- homeassistant/components/netatmo/sensor.py | 62 ++++++++++++++-------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/netatmo/sensor.py b/homeassistant/components/netatmo/sensor.py index 9cecc19683b..84e18ba7241 100644 --- a/homeassistant/components/netatmo/sensor.py +++ b/homeassistant/components/netatmo/sensor.py @@ -48,39 +48,53 @@ SUPPORTED_PUBLIC_SENSOR_TYPES = [ ] SENSOR_TYPES = { - "temperature": ["Temperature", TEMP_CELSIUS, None, DEVICE_CLASS_TEMPERATURE], - "temp_trend": ["Temperature trend", None, "mdi:trending-up", None], - "co2": ["CO2", CONCENTRATION_PARTS_PER_MILLION, "mdi:molecule-co2", None], - "pressure": ["Pressure", PRESSURE_MBAR, None, DEVICE_CLASS_PRESSURE], - "pressure_trend": ["Pressure trend", None, "mdi:trending-up", None], - "noise": ["Noise", "dB", "mdi:volume-high", None], - "humidity": ["Humidity", PERCENTAGE, None, DEVICE_CLASS_HUMIDITY], - "rain": ["Rain", LENGTH_MILLIMETERS, "mdi:weather-rainy", None], - "sum_rain_1": ["Rain last hour", LENGTH_MILLIMETERS, "mdi:weather-rainy", None], - "sum_rain_24": ["Rain today", LENGTH_MILLIMETERS, "mdi:weather-rainy", None], - "battery_percent": ["Battery Percent", PERCENTAGE, None, DEVICE_CLASS_BATTERY], - "windangle": ["Direction", None, "mdi:compass-outline", None], - "windangle_value": ["Angle", DEGREE, "mdi:compass-outline", None], + "temperature": ["Temperature", TEMP_CELSIUS, None, DEVICE_CLASS_TEMPERATURE, True], + "temp_trend": ["Temperature trend", None, "mdi:trending-up", None, False], + "co2": ["CO2", CONCENTRATION_PARTS_PER_MILLION, "mdi:molecule-co2", None, True], + "pressure": ["Pressure", PRESSURE_MBAR, None, DEVICE_CLASS_PRESSURE, True], + "pressure_trend": ["Pressure trend", None, "mdi:trending-up", None, False], + "noise": ["Noise", "dB", "mdi:volume-high", None, True], + "humidity": ["Humidity", PERCENTAGE, None, DEVICE_CLASS_HUMIDITY, True], + "rain": ["Rain", LENGTH_MILLIMETERS, "mdi:weather-rainy", None, True], + "sum_rain_1": [ + "Rain last hour", + LENGTH_MILLIMETERS, + "mdi:weather-rainy", + None, + False, + ], + "sum_rain_24": ["Rain today", LENGTH_MILLIMETERS, "mdi:weather-rainy", None, True], + "battery_percent": [ + "Battery Percent", + PERCENTAGE, + None, + DEVICE_CLASS_BATTERY, + True, + ], + "windangle": ["Direction", None, "mdi:compass-outline", None, True], + "windangle_value": ["Angle", DEGREE, "mdi:compass-outline", None, False], "windstrength": [ "Wind Strength", SPEED_KILOMETERS_PER_HOUR, "mdi:weather-windy", None, + True, ], - "gustangle": ["Gust Direction", None, "mdi:compass-outline", None], - "gustangle_value": ["Gust Angle", DEGREE, "mdi:compass-outline", None], + "gustangle": ["Gust Direction", None, "mdi:compass-outline", None, False], + "gustangle_value": ["Gust Angle", DEGREE, "mdi:compass-outline", None, False], "guststrength": [ "Gust Strength", SPEED_KILOMETERS_PER_HOUR, "mdi:weather-windy", None, + False, ], - "reachable": ["Reachability", None, "mdi:signal", None], - "rf_status": ["Radio", None, "mdi:signal", None], - "rf_status_lvl": ["Radio Level", "", None, DEVICE_CLASS_SIGNAL_STRENGTH], - "wifi_status": ["Wifi", None, "mdi:wifi", None], - "wifi_status_lvl": ["Wifi Level", "dBm", None, DEVICE_CLASS_SIGNAL_STRENGTH], - "health_idx": ["Health", None, "mdi:cloud", None], + "reachable": ["Reachability", None, "mdi:signal", None, False], + "rf_status": ["Radio", None, "mdi:signal", None, False], + "rf_status_lvl": ["Radio Level", "", None, DEVICE_CLASS_SIGNAL_STRENGTH, False], + "wifi_status": ["Wifi", None, "mdi:wifi", None, False], + "wifi_status_lvl": ["Wifi Level", "dBm", None, DEVICE_CLASS_SIGNAL_STRENGTH, False], + "health_idx": ["Health", None, "mdi:cloud", None, True], } MODULE_TYPE_OUTDOOR = "NAModule1" @@ -257,6 +271,7 @@ class NetatmoSensor(NetatmoBase): self._unit_of_measurement = SENSOR_TYPES[self.type][1] self._model = device["type"] self._unique_id = f"{self._id}-{self.type}" + self._enabled_default = SENSOR_TYPES[self.type][4] @property def icon(self): @@ -283,6 +298,11 @@ class NetatmoSensor(NetatmoBase): """Return entity availability.""" return self._state is not None + @property + def entity_registry_enabled_default(self) -> bool: + """Return if the entity should be enabled when first added to the entity registry.""" + return self._enabled_default + @callback def async_update_callback(self): """Update the entity's state.""" From 1fb1bb0c08b2cd2db3cf743667ad933e9264e49b Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 23 Oct 2020 01:22:51 +0200 Subject: [PATCH 011/118] Support reloading Tasmota config entries (#42097) --- homeassistant/components/tasmota/__init__.py | 51 +++++++++++++-- .../components/tasmota/binary_sensor.py | 6 +- homeassistant/components/tasmota/const.py | 9 +++ .../components/tasmota/device_automation.py | 5 +- homeassistant/components/tasmota/discovery.py | 64 ++++++------------- homeassistant/components/tasmota/light.py | 6 +- .../components/tasmota/manifest.json | 2 +- homeassistant/components/tasmota/mixins.py | 6 ++ homeassistant/components/tasmota/sensor.py | 6 +- homeassistant/components/tasmota/switch.py | 6 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tasmota/conftest.py | 1 + tests/components/tasmota/test_discovery.py | 25 ++++---- tests/components/tasmota/test_sensor.py | 63 +++++++++++++++++- 15 files changed, 179 insertions(+), 75 deletions(-) diff --git a/homeassistant/components/tasmota/__init__.py b/homeassistant/components/tasmota/__init__.py index 1e11aff448e..5754b98b71e 100644 --- a/homeassistant/components/tasmota/__init__.py +++ b/homeassistant/components/tasmota/__init__.py @@ -1,4 +1,5 @@ """The Tasmota integration.""" +import asyncio import logging from hatasmota.const import ( @@ -21,8 +22,8 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import HomeAssistantType -from . import discovery -from .const import CONF_DISCOVERY_PREFIX +from . import device_automation, discovery +from .const import CONF_DISCOVERY_PREFIX, DATA_REMOVE_DISCOVER_COMPONENT, PLATFORMS from .discovery import TASMOTA_DISCOVERY_DEVICE _LOGGER = logging.getLogger(__name__) @@ -54,14 +55,52 @@ async def async_setup_entry(hass, entry): tasmota_mqtt = TasmotaMQTTClient(_publish, _subscribe_topics, _unsubscribe_topics) - discovery_prefix = entry.data[CONF_DISCOVERY_PREFIX] - await discovery.async_start(hass, discovery_prefix, entry, tasmota_mqtt) - async def async_discover_device(config, mac): """Discover and add a Tasmota device.""" await async_setup_device(hass, mac, config, entry, tasmota_mqtt) - async_dispatcher_connect(hass, TASMOTA_DISCOVERY_DEVICE, async_discover_device) + hass.data[ + DATA_REMOVE_DISCOVER_COMPONENT.format("device") + ] = async_dispatcher_connect(hass, TASMOTA_DISCOVERY_DEVICE, async_discover_device) + + async def start_platforms(): + await device_automation.async_setup_entry(hass, entry) + await asyncio.gather( + *[ + hass.config_entries.async_forward_entry_setup(entry, component) + for component in PLATFORMS + ] + ) + + discovery_prefix = entry.data[CONF_DISCOVERY_PREFIX] + await discovery.async_start(hass, discovery_prefix, entry, tasmota_mqtt) + + hass.async_create_task(start_platforms()) + return True + + +async def async_unload_entry(hass, entry): + """Unload a config entry.""" + + # cleanup platforms + unload_ok = all( + await asyncio.gather( + *[ + hass.config_entries.async_forward_entry_unload(entry, component) + for component in PLATFORMS + ] + ) + ) + if not unload_ok: + return False + + # disable discovery + await discovery.async_stop(hass) + hass.data.pop(DEVICE_MACS) + hass.data[DATA_REMOVE_DISCOVER_COMPONENT.format("device")]() + hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format("device_automation"))() + for component in PLATFORMS: + hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format(component))() return True diff --git a/homeassistant/components/tasmota/binary_sensor.py b/homeassistant/components/tasmota/binary_sensor.py index 549452c1949..ade309840ca 100644 --- a/homeassistant/components/tasmota/binary_sensor.py +++ b/homeassistant/components/tasmota/binary_sensor.py @@ -6,7 +6,7 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect import homeassistant.helpers.event as evt -from .const import DOMAIN as TASMOTA_DOMAIN +from .const import DATA_REMOVE_DISCOVER_COMPONENT, DOMAIN as TASMOTA_DOMAIN from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate @@ -25,7 +25,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ] ) - async_dispatcher_connect( + hass.data[ + DATA_REMOVE_DISCOVER_COMPONENT.format(binary_sensor.DOMAIN) + ] = async_dispatcher_connect( hass, TASMOTA_DISCOVERY_ENTITY_NEW.format(binary_sensor.DOMAIN, TASMOTA_DOMAIN), async_discover, diff --git a/homeassistant/components/tasmota/const.py b/homeassistant/components/tasmota/const.py index 11f43ed38bf..7cddeb7a603 100644 --- a/homeassistant/components/tasmota/const.py +++ b/homeassistant/components/tasmota/const.py @@ -1,8 +1,17 @@ """Constants used by multiple Tasmota modules.""" CONF_DISCOVERY_PREFIX = "discovery_prefix" +DATA_REMOVE_DISCOVER_COMPONENT = "tasmota_discover_{}" + DEFAULT_PREFIX = "tasmota/discovery" DOMAIN = "tasmota" +PLATFORMS = [ + "binary_sensor", + "light", + "sensor", + "switch", +] + TASMOTA_EVENT = "tasmota_event" diff --git a/homeassistant/components/tasmota/device_automation.py b/homeassistant/components/tasmota/device_automation.py index 6bbd8ce6d89..e921a186fea 100644 --- a/homeassistant/components/tasmota/device_automation.py +++ b/homeassistant/components/tasmota/device_automation.py @@ -6,6 +6,7 @@ from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import device_trigger +from .const import DATA_REMOVE_DISCOVER_COMPONENT from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW @@ -25,7 +26,9 @@ async def async_setup_entry(hass, config_entry): hass, tasmota_automation, config_entry, discovery_hash ) - async_dispatcher_connect( + hass.data[ + DATA_REMOVE_DISCOVER_COMPONENT.format("device_automation") + ] = async_dispatcher_connect( hass, TASMOTA_DISCOVERY_ENTITY_NEW.format("device_automation", "tasmota"), async_discover, diff --git a/homeassistant/components/tasmota/discovery.py b/homeassistant/components/tasmota/discovery.py index e137692445e..6d224a98707 100644 --- a/homeassistant/components/tasmota/discovery.py +++ b/homeassistant/components/tasmota/discovery.py @@ -1,5 +1,4 @@ """Support for Tasmota device discovery.""" -import asyncio import logging from hatasmota.discovery import ( @@ -9,7 +8,6 @@ from hatasmota.discovery import ( get_entity as tasmota_get_entity, get_trigger as tasmota_get_trigger, get_triggers as tasmota_get_triggers, - has_entities_with_platform as tasmota_has_entities_with_platform, unique_id_from_hash, ) @@ -18,23 +16,15 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity_registry import async_entries_for_device from homeassistant.helpers.typing import HomeAssistantType -from .const import DOMAIN +from .const import DOMAIN, PLATFORMS _LOGGER = logging.getLogger(__name__) -SUPPORTED_PLATFORMS = [ - "binary_sensor", - "light", - "sensor", - "switch", -] - ALREADY_DISCOVERED = "tasmota_discovered_components" -CONFIG_ENTRY_IS_SETUP = "tasmota_config_entry_is_setup" -DATA_CONFIG_ENTRY_LOCK = "tasmota_config_entry_lock" TASMOTA_DISCOVERY_DEVICE = "tasmota_discovery_device" TASMOTA_DISCOVERY_ENTITY_NEW = "tasmota_discovery_entity_new_{}" TASMOTA_DISCOVERY_ENTITY_UPDATED = "tasmota_discovery_entity_updated_{}_{}_{}_{}" +TASMOTA_DISCOVERY_INSTANCE = "tasmota_discovery_instance" def clear_discovery_hash(hass, discovery_hash): @@ -52,16 +42,6 @@ async def async_start( ) -> bool: """Start Tasmota device discovery.""" - async def _load_platform(platform): - """Load a Tasmota platform if not already done.""" - async with hass.data[DATA_CONFIG_ENTRY_LOCK]: - config_entries_key = f"{platform}.tasmota" - if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]: - await hass.config_entries.async_forward_entry_setup( - config_entry, platform - ) - hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) - async def _discover_entity(tasmota_entity_config, discovery_hash, platform): """Handle adding or updating a discovered entity.""" if not tasmota_entity_config: @@ -86,8 +66,13 @@ async def async_start( tasmota_entity_config, ) else: - _LOGGER.debug("Adding new entity: %s %s", platform, discovery_hash) tasmota_entity = tasmota_get_entity(tasmota_entity_config, tasmota_mqtt) + _LOGGER.debug( + "Adding new entity: %s %s %s", + platform, + discovery_hash, + tasmota_entity.unique_id, + ) hass.data[ALREADY_DISCOVERED][discovery_hash] = None @@ -102,7 +87,8 @@ async def async_start( """Process the received message.""" if ALREADY_DISCOVERED not in hass.data: - hass.data[ALREADY_DISCOVERED] = {} + # Discovery is shutting down + return _LOGGER.debug("Received discovery data for tasmota device: %s", mac) tasmota_device_config = tasmota_get_device_config(payload) @@ -114,17 +100,6 @@ async def async_start( return tasmota_triggers = tasmota_get_triggers(payload) - async with hass.data[DATA_CONFIG_ENTRY_LOCK]: - if any(trigger.is_active for trigger in tasmota_triggers): - config_entries_key = "device_automation.tasmota" - if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]: - # Local import to avoid circular dependencies - # pylint: disable=import-outside-toplevel - from . import device_automation - - await device_automation.async_setup_entry(hass, config_entry) - hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) - for trigger_config in tasmota_triggers: discovery_hash = (mac, "automation", "trigger", trigger_config.trigger_id) if discovery_hash in hass.data[ALREADY_DISCOVERED]: @@ -150,12 +125,7 @@ async def async_start( discovery_hash, ) - for platform in SUPPORTED_PLATFORMS: - if not tasmota_has_entities_with_platform(payload, platform): - continue - await _load_platform(platform) - - for platform in SUPPORTED_PLATFORMS: + for platform in PLATFORMS: tasmota_entities = tasmota_get_entities_for_platform(payload, platform) for (tasmota_entity_config, discovery_hash) in tasmota_entities: await _discover_entity(tasmota_entity_config, discovery_hash, platform) @@ -163,7 +133,6 @@ async def async_start( async def async_sensors_discovered(sensors, mac): """Handle discovery of (additional) sensors.""" platform = sensor.DOMAIN - await _load_platform(platform) device_registry = await hass.helpers.device_registry.async_get_registry() entity_registry = await hass.helpers.entity_registry.async_get_registry() @@ -188,10 +157,17 @@ async def async_start( _LOGGER.debug("Removing entity: %s %s", platform, entity_id) entity_registry.async_remove(entity_id) - hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock() - hass.data[CONFIG_ENTRY_IS_SETUP] = set() + hass.data[ALREADY_DISCOVERED] = {} tasmota_discovery = TasmotaDiscovery(discovery_topic, tasmota_mqtt) await tasmota_discovery.start_discovery( async_device_discovered, async_sensors_discovered ) + hass.data[TASMOTA_DISCOVERY_INSTANCE] = tasmota_discovery + + +async def async_stop(hass: HomeAssistantType) -> bool: + """Stop Tasmota device discovery.""" + hass.data.pop(ALREADY_DISCOVERED) + tasmota_discovery = hass.data.pop(TASMOTA_DISCOVERY_INSTANCE) + await tasmota_discovery.stop_discovery() diff --git a/homeassistant/components/tasmota/light.py b/homeassistant/components/tasmota/light.py index 00cedc4a611..c761d3436df 100644 --- a/homeassistant/components/tasmota/light.py +++ b/homeassistant/components/tasmota/light.py @@ -27,7 +27,7 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect import homeassistant.util.color as color_util -from .const import DOMAIN as TASMOTA_DOMAIN +from .const import DATA_REMOVE_DISCOVER_COMPONENT, DOMAIN as TASMOTA_DOMAIN from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate @@ -45,7 +45,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): [TasmotaLight(tasmota_entity=tasmota_entity, discovery_hash=discovery_hash)] ) - async_dispatcher_connect( + hass.data[ + DATA_REMOVE_DISCOVER_COMPONENT.format(light.DOMAIN) + ] = async_dispatcher_connect( hass, TASMOTA_DISCOVERY_ENTITY_NEW.format(light.DOMAIN, TASMOTA_DOMAIN), async_discover, diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 4157c416eca..b64d10c1070 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota (beta)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.0.19"], + "requirements": ["hatasmota==0.0.20"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"] diff --git a/homeassistant/components/tasmota/mixins.py b/homeassistant/components/tasmota/mixins.py index 13e511e4a8f..3b52fafd12b 100644 --- a/homeassistant/components/tasmota/mixins.py +++ b/homeassistant/components/tasmota/mixins.py @@ -153,6 +153,12 @@ class TasmotaDiscoveryUpdate(TasmotaEntity): ) ) + @callback + def add_to_platform_abort(self) -> None: + """Abort adding an entity to a platform.""" + clear_discovery_hash(self.hass, self._discovery_hash) + super().add_to_platform_abort() + async def async_will_remove_from_hass(self) -> None: """Stop listening to signal and cleanup discovery data..""" if not self._removed_from_hass: diff --git a/homeassistant/components/tasmota/sensor.py b/homeassistant/components/tasmota/sensor.py index 660c0efc1e6..076ca457a23 100644 --- a/homeassistant/components/tasmota/sensor.py +++ b/homeassistant/components/tasmota/sensor.py @@ -58,7 +58,7 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity -from .const import DOMAIN as TASMOTA_DOMAIN +from .const import DATA_REMOVE_DISCOVER_COMPONENT, DOMAIN as TASMOTA_DOMAIN from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate @@ -123,7 +123,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ] ) - async_dispatcher_connect( + hass.data[ + DATA_REMOVE_DISCOVER_COMPONENT.format(sensor.DOMAIN) + ] = async_dispatcher_connect( hass, TASMOTA_DISCOVERY_ENTITY_NEW.format(sensor.DOMAIN, TASMOTA_DOMAIN), async_discover_sensor, diff --git a/homeassistant/components/tasmota/switch.py b/homeassistant/components/tasmota/switch.py index fe8f4b9555c..0a97a5e2528 100644 --- a/homeassistant/components/tasmota/switch.py +++ b/homeassistant/components/tasmota/switch.py @@ -5,7 +5,7 @@ from homeassistant.components.switch import SwitchEntity from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from .const import DOMAIN as TASMOTA_DOMAIN +from .const import DATA_REMOVE_DISCOVER_COMPONENT, DOMAIN as TASMOTA_DOMAIN from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW from .mixins import TasmotaAvailability, TasmotaDiscoveryUpdate @@ -24,7 +24,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ] ) - async_dispatcher_connect( + hass.data[ + DATA_REMOVE_DISCOVER_COMPONENT.format(switch.DOMAIN) + ] = async_dispatcher_connect( hass, TASMOTA_DISCOVERY_ENTITY_NEW.format(switch.DOMAIN, TASMOTA_DOMAIN), async_discover, diff --git a/requirements_all.txt b/requirements_all.txt index bf515d1d99d..56b17074a4a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -732,7 +732,7 @@ hass-nabucasa==0.37.1 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.0.19 +hatasmota==0.0.20 # homeassistant.components.jewish_calendar hdate==0.9.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5e7e53ef335..8ca3780f66f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -367,7 +367,7 @@ hangups==0.4.11 hass-nabucasa==0.37.1 # homeassistant.components.tasmota -hatasmota==0.0.19 +hatasmota==0.0.20 # homeassistant.components.jewish_calendar hdate==0.9.5 diff --git a/tests/components/tasmota/conftest.py b/tests/components/tasmota/conftest.py index 456d157547d..09a3bfa4d63 100644 --- a/tests/components/tasmota/conftest.py +++ b/tests/components/tasmota/conftest.py @@ -72,6 +72,7 @@ async def setup_tasmota_helper(hass): entry.add_to_hass(hass) assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() assert "tasmota" in hass.config.components diff --git a/tests/components/tasmota/test_discovery.py b/tests/components/tasmota/test_discovery.py index f337b062839..607676615a7 100644 --- a/tests/components/tasmota/test_discovery.py +++ b/tests/components/tasmota/test_discovery.py @@ -27,40 +27,41 @@ async def test_valid_discovery_message(hass, mqtt_mock, caplog): config = copy.deepcopy(DEFAULT_CONFIG) with patch( - "homeassistant.components.tasmota.discovery.tasmota_has_entities_with_platform" - ) as mock_tasmota_has_entities: + "homeassistant.components.tasmota.discovery.tasmota_get_device_config", + return_value={}, + ) as mock_tasmota_get_device_config: await setup_tasmota_helper(hass) async_fire_mqtt_message( hass, f"{DEFAULT_PREFIX}/00000049A3BC/config", json.dumps(config) ) await hass.async_block_till_done() - assert mock_tasmota_has_entities.called + assert mock_tasmota_get_device_config.called async def test_invalid_topic(hass, mqtt_mock): """Test receiving discovery message on wrong topic.""" with patch( - "homeassistant.components.tasmota.discovery.tasmota_has_entities_with_platform" - ) as mock_tasmota_has_entities: + "homeassistant.components.tasmota.discovery.tasmota_get_device_config" + ) as mock_tasmota_get_device_config: await setup_tasmota_helper(hass) async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/123456/configuration", "{}") await hass.async_block_till_done() - assert not mock_tasmota_has_entities.called + assert not mock_tasmota_get_device_config.called async def test_invalid_message(hass, mqtt_mock, caplog): """Test receiving an invalid message.""" with patch( - "homeassistant.components.tasmota.discovery.tasmota_has_entities_with_platform" - ) as mock_tasmota_has_entities: + "homeassistant.components.tasmota.discovery.tasmota_get_device_config" + ) as mock_tasmota_get_device_config: await setup_tasmota_helper(hass) async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/123456/config", "asd") await hass.async_block_till_done() assert "Invalid discovery message" in caplog.text - assert not mock_tasmota_has_entities.called + assert not mock_tasmota_get_device_config.called async def test_invalid_mac(hass, mqtt_mock, caplog): @@ -68,8 +69,8 @@ async def test_invalid_mac(hass, mqtt_mock, caplog): config = copy.deepcopy(DEFAULT_CONFIG) with patch( - "homeassistant.components.tasmota.discovery.tasmota_has_entities_with_platform" - ) as mock_tasmota_has_entities: + "homeassistant.components.tasmota.discovery.tasmota_get_device_config" + ) as mock_tasmota_get_device_config: await setup_tasmota_helper(hass) async_fire_mqtt_message( @@ -77,7 +78,7 @@ async def test_invalid_mac(hass, mqtt_mock, caplog): ) await hass.async_block_till_done() assert "MAC mismatch" in caplog.text - assert not mock_tasmota_has_entities.called + assert not mock_tasmota_get_device_config.called async def test_correct_config_discovery( diff --git a/tests/components/tasmota/test_sensor.py b/tests/components/tasmota/test_sensor.py index 859c0f735ee..ab8498ed04c 100644 --- a/tests/components/tasmota/test_sensor.py +++ b/tests/components/tasmota/test_sensor.py @@ -1,5 +1,6 @@ """The tests for the Tasmota sensor platform.""" import copy +from datetime import timedelta import json from hatasmota.utils import ( @@ -9,9 +10,11 @@ from hatasmota.utils import ( ) import pytest +from homeassistant import config_entries from homeassistant.components import sensor from homeassistant.components.tasmota.const import DEFAULT_PREFIX from homeassistant.const import ATTR_ASSUMED_STATE, STATE_UNKNOWN +from homeassistant.util import dt from .test_common import ( DEFAULT_CONFIG, @@ -27,7 +30,7 @@ from .test_common import ( ) from tests.async_mock import patch -from tests.common import async_fire_mqtt_message +from tests.common import async_fire_mqtt_message, async_fire_time_changed DEFAULT_SENSOR_CONFIG = { "sn": { @@ -377,6 +380,64 @@ async def test_indexed_sensor_attributes(hass, mqtt_mock, setup_tasmota): assert state.attributes.get("unit_of_measurement") == "ppm" +@pytest.mark.parametrize("status_sensor_disabled", [False]) +async def test_enable_status_sensor(hass, mqtt_mock, setup_tasmota): + """Test enabling status sensor.""" + entity_reg = await hass.helpers.entity_registry.async_get_registry() + + config = copy.deepcopy(DEFAULT_CONFIG) + mac = config["mac"] + + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + await hass.async_block_till_done() + + state = hass.states.get("sensor.tasmota_status") + assert state is None + entry = entity_reg.async_get("sensor.tasmota_status") + assert entry.disabled + assert entry.disabled_by == "integration" + + # Enable the status sensor + updated_entry = entity_reg.async_update_entity( + "sensor.tasmota_status", disabled_by=None + ) + assert updated_entry != entry + assert updated_entry.disabled is False + await hass.async_block_till_done() + + async_fire_time_changed( + hass, + dt.utcnow() + + timedelta( + seconds=config_entries.EntityRegistryDisabledHandler.RELOAD_AFTER_UPDATE_DELAY + + 1 + ), + ) + await hass.async_block_till_done() + + # Fake re-send of retained discovery message + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + + state = hass.states.get("sensor.tasmota_status") + assert state.state == "unavailable" + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") + state = hass.states.get("sensor.tasmota_status") + assert state.state == STATE_UNKNOWN + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + async def test_availability_when_connection_lost( hass, mqtt_client_mock, mqtt_mock, setup_tasmota ): From e0621d2e5f380c95849c4757fc26edd6222fef93 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 22 Oct 2020 03:20:17 -0400 Subject: [PATCH 012/118] Set Vizio unique ID for discovery flow early and abort if configured to prevent duplicate discovery flows (#42194) --- homeassistant/components/vizio/config_flow.py | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/vizio/config_flow.py b/homeassistant/components/vizio/config_flow.py index 74fc0d746e3..f3b7720a33e 100644 --- a/homeassistant/components/vizio/config_flow.py +++ b/homeassistant/components/vizio/config_flow.py @@ -206,29 +206,29 @@ class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self, user_input: Dict[str, Any] = None ) -> Dict[str, Any]: """Handle a flow initialized by the user.""" + assert self.hass errors = {} if user_input is not None: # Store current values in case setup fails and user needs to edit self._user_schema = _get_config_schema(user_input) - unique_id = await VizioAsync.get_unique_id( - user_input[CONF_HOST], - user_input[CONF_DEVICE_CLASS], - session=async_get_clientsession(self.hass, False), - ) - - if not unique_id: - errors[CONF_HOST] = "cannot_connect" - else: - # Set unique ID and abort if a flow with the same unique ID is already in progress - existing_entry = await self.async_set_unique_id( - unique_id=unique_id, raise_on_progress=True + if self.unique_id is None: + unique_id = await VizioAsync.get_unique_id( + user_input[CONF_HOST], + user_input[CONF_DEVICE_CLASS], + session=async_get_clientsession(self.hass, False), ) - # If device was discovered, abort if existing entry found, otherwise display an error - # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167 - if self.context["source"] == SOURCE_ZEROCONF: - self._abort_if_unique_id_configured() - elif existing_entry: + + # Check if unique ID was found, set unique ID, and abort if a flow with + # the same unique ID is already in progress + if not unique_id: + errors[CONF_HOST] = "cannot_connect" + elif ( + await self.async_set_unique_id( + unique_id=unique_id, raise_on_progress=True + ) + is not None + ): errors[CONF_HOST] = "existing_config_entry_found" if not errors: @@ -346,6 +346,7 @@ class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self, discovery_info: Optional[DiscoveryInfoType] = None ) -> Dict[str, Any]: """Handle zeroconf discovery.""" + assert self.hass discovery_info[ CONF_HOST @@ -360,6 +361,15 @@ class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): discovery_info[CONF_HOST] ) + # Set unique ID early for discovery flow so we can abort if needed + unique_id = await VizioAsync.get_unique_id( + discovery_info[CONF_HOST], + discovery_info[CONF_DEVICE_CLASS], + session=async_get_clientsession(self.hass, False), + ) + await self.async_set_unique_id(unique_id=unique_id, raise_on_progress=True) + self._abort_if_unique_id_configured() + # Form must be shown after discovery so user can confirm/update configuration # before ConfigEntry creation. self._must_show_form = True From 3586e0c5e415e4796a0892a5265908b06e15ecc6 Mon Sep 17 00:00:00 2001 From: Niccolo Zapponi Date: Thu, 22 Oct 2020 17:01:10 +0100 Subject: [PATCH 013/118] Fix bug for SimpliSafe 2 systems repeatedly saying "your settings have been synchronised" (#42197) * Fix bug for SimpliSafe 2 systems * Improved loop and logging for SimpliSafe 2 --- .../components/simplisafe/__init__.py | 2 +- .../components/simplisafe/binary_sensor.py | 8 +++++-- homeassistant/components/simplisafe/sensor.py | 21 +++++++++++-------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index e81559bd1fa..a3f56a9e1a3 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -500,7 +500,7 @@ class SimpliSafe: async def async_update_system(system): """Update a system.""" - await system.update(cached=False) + await system.update(cached=system.version != 3) self._async_process_new_notifications(system) tasks = [async_update_system(system) for system in self.systems.values()] diff --git a/homeassistant/components/simplisafe/binary_sensor.py b/homeassistant/components/simplisafe/binary_sensor.py index cd6a4465261..263bbf51b13 100644 --- a/homeassistant/components/simplisafe/binary_sensor.py +++ b/homeassistant/components/simplisafe/binary_sensor.py @@ -12,7 +12,7 @@ from homeassistant.components.binary_sensor import ( from homeassistant.core import callback from . import SimpliSafeEntity -from .const import DATA_CLIENT, DOMAIN +from .const import DATA_CLIENT, DOMAIN, LOGGER SUPPORTED_BATTERY_SENSOR_TYPES = [ EntityTypes.carbon_monoxide, @@ -48,9 +48,13 @@ SENSOR_MODELS = { async def async_setup_entry(hass, entry, async_add_entities): """Set up SimpliSafe binary sensors based on a config entry.""" simplisafe = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id] - sensors = [] + for system in simplisafe.systems.values(): + if system.version == 2: + LOGGER.info("Skipping sensor setup for V2 system: %s", system.system_id) + continue + for sensor in system.sensors.values(): if sensor.type in SUPPORTED_SENSOR_TYPES: sensors.append(SimpliSafeBinarySensor(simplisafe, system, sensor)) diff --git a/homeassistant/components/simplisafe/sensor.py b/homeassistant/components/simplisafe/sensor.py index fd6f1121af9..5909471c455 100644 --- a/homeassistant/components/simplisafe/sensor.py +++ b/homeassistant/components/simplisafe/sensor.py @@ -5,21 +5,24 @@ from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_FAHRENHEIT from homeassistant.core import callback from . import SimpliSafeEntity -from .const import DATA_CLIENT, DOMAIN +from .const import DATA_CLIENT, DOMAIN, LOGGER async def async_setup_entry(hass, entry, async_add_entities): """Set up SimpliSafe freeze sensors based on a config entry.""" simplisafe = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id] + sensors = [] - async_add_entities( - [ - SimplisafeFreezeSensor(simplisafe, system, sensor) - for system in simplisafe.systems.values() - for sensor in system.sensors.values() - if sensor.type == EntityTypes.temperature - ] - ) + for system in simplisafe.systems.values(): + if system.version == 2: + LOGGER.info("Skipping sensor setup for V2 system: %s", system.system_id) + continue + + for sensor in system.sensors.values(): + if sensor.type == EntityTypes.temperature: + sensors.append(SimplisafeFreezeSensor(simplisafe, system, sensor)) + + async_add_entities(sensors) class SimplisafeFreezeSensor(SimpliSafeEntity): From 65eb5ea55fdd0bbedf612bcfd240712a376d7949 Mon Sep 17 00:00:00 2001 From: Tom Harris Date: Thu, 22 Oct 2020 16:04:06 -0400 Subject: [PATCH 014/118] Fix issue in python > 3.8.2 (#42213) --- homeassistant/components/insteon/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/insteon/__init__.py b/homeassistant/components/insteon/__init__.py index b0e6b8d10a2..0188671e07f 100644 --- a/homeassistant/components/insteon/__init__.py +++ b/homeassistant/components/insteon/__init__.py @@ -159,7 +159,7 @@ async def async_setup_entry(hass, entry): identifiers={(DOMAIN, str(devices.modem.address))}, manufacturer="Smart Home", name=f"{devices.modem.description} {devices.modem.address}", - model=f"{devices.modem.model} (0x{devices.modem.cat:02x}, 0x{devices.modem.subcat:02x})", + model=f"{devices.modem.model} ({devices.modem.cat!r}, 0x{devices.modem.subcat:02x})", sw_version=f"{devices.modem.firmware:02x} Engine Version: {devices.modem.engine_version}", ) From d00ca7bf039e197dba51e2b55ada5099eee6e924 Mon Sep 17 00:00:00 2001 From: rikroe <42204099+rikroe@users.noreply.github.com> Date: Thu, 22 Oct 2020 22:30:21 +0200 Subject: [PATCH 015/118] Bump bimmer-connected to 0.7.8 to fix login issue (#42215) Co-authored-by: rikroe --- homeassistant/components/bmw_connected_drive/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index c7cacfb6f63..bd76294c8e8 100644 --- a/homeassistant/components/bmw_connected_drive/manifest.json +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -2,7 +2,7 @@ "domain": "bmw_connected_drive", "name": "BMW Connected Drive", "documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive", - "requirements": ["bimmer_connected==0.7.7"], + "requirements": ["bimmer_connected==0.7.8"], "dependencies": [], "codeowners": ["@gerard33", "@rikroe"] } diff --git a/requirements_all.txt b/requirements_all.txt index 56b17074a4a..869653d56cc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -342,7 +342,7 @@ beautifulsoup4==4.9.1 bellows==0.20.3 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.7.7 +bimmer_connected==0.7.8 # homeassistant.components.bizkaibus bizkaibus==0.1.1 From 4f908bc58cded9ce85205abcbaced122c8bc5163 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Fri, 23 Oct 2020 00:11:02 +0200 Subject: [PATCH 016/118] Refresh now() templates on second=0 (#42225) --- homeassistant/helpers/event.py | 14 ++++++++------ tests/helpers/test_event.py | 12 +++++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index e15d3ed90ee..b4caccf5311 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -792,11 +792,13 @@ class _TrackTemplateResultInfo: @callback def _setup_time_listener(self, template: Template, has_time: bool) -> None: - if template in self._time_listeners: - self._time_listeners.pop(template)() - - # now() or utcnow() has left the scope of the template if not has_time: + if template in self._time_listeners: + # now() or utcnow() has left the scope of the template + self._time_listeners.pop(template)() + return + + if template in self._time_listeners: return track_templates = [ @@ -809,8 +811,8 @@ class _TrackTemplateResultInfo: def _refresh_from_time(now: datetime) -> None: self._refresh(None, track_templates=track_templates) - self._time_listeners[template] = async_call_later( - self.hass, 60.45, _refresh_from_time + self._time_listeners[template] = async_track_utc_time_change( + self.hass, _refresh_from_time, second=0 ) @callback diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index bd90edb20e7..9f6e66048ff 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -2172,15 +2172,17 @@ async def test_track_template_with_time_that_leaves_scope(hass): "time": True, } - # Verify we do not update a second time - # if the state change happens + # Verify we do not update before the minute rolls over callback_count_before_time_change = len(specific_runs) - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=59)) + test_time = dt_util.utcnow().replace(second=1) + async_fire_time_changed(hass, test_time) + await hass.async_block_till_done() + async_fire_time_changed(hass, test_time + timedelta(seconds=58)) await hass.async_block_till_done() assert len(specific_runs) == callback_count_before_time_change - # Verify we do update on the next time change - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=61)) + # Verify we do update on the next change of minute + async_fire_time_changed(hass, test_time + timedelta(seconds=59)) await hass.async_block_till_done() assert len(specific_runs) == callback_count_before_time_change + 1 From 69cace1d41977e748a162061cbedc2c3aa58d7f7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 22 Oct 2020 18:28:22 -0500 Subject: [PATCH 017/118] Ensure websocket event serializer cache is effective if subscription iden differs (#42226) Since someone websocket subscriptions will use an iden of 2 for state_changed event (most comment), and some will use another number for all events, the cache would not be used because the iden number was different. We now cache only the event and use a fast replace to insert the iden number into the serailized response. --- .../components/websocket_api/messages.py | 19 ++++++++-- .../components/websocket_api/test_messages.py | 37 ++++++++++++++++++- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/websocket_api/messages.py b/homeassistant/components/websocket_api/messages.py index 52e97b60ccf..f68beff5924 100644 --- a/homeassistant/components/websocket_api/messages.py +++ b/homeassistant/components/websocket_api/messages.py @@ -12,6 +12,7 @@ from homeassistant.util.json import ( find_paths_unserializable_data, format_unserializable_data, ) +from homeassistant.util.yaml.loader import JSON_TYPE from . import const @@ -27,6 +28,9 @@ MINIMAL_MESSAGE_SCHEMA = vol.Schema( # Base schema to extend by message handlers BASE_COMMAND_MESSAGE_SCHEMA = vol.Schema({vol.Required("id"): cv.positive_int}) +IDEN_TEMPLATE = "__IDEN__" +IDEN_JSON_TEMPLATE = '"__IDEN__"' + def result_message(iden: int, result: Any = None) -> Dict: """Return a success result message.""" @@ -43,12 +47,11 @@ def error_message(iden: int, code: str, message: str) -> Dict: } -def event_message(iden: int, event: Any) -> Dict: +def event_message(iden: JSON_TYPE, event: Any) -> Dict: """Return an event message.""" return {"id": iden, "type": "event", "event": event} -@lru_cache(maxsize=128) def cached_event_message(iden: int, event: Event) -> str: """Return an event message. @@ -58,7 +61,17 @@ def cached_event_message(iden: int, event: Event) -> str: all getting many of the same events (mostly state changed) we can avoid serializing the same data for each connection. """ - return message_to_json(event_message(iden, event)) + return _cached_event_message(event).replace(IDEN_JSON_TEMPLATE, str(iden), 1) + + +@lru_cache(maxsize=128) +def _cached_event_message(event: Event) -> str: + """Cache and serialize the event to json. + + The IDEN_TEMPLATE is used which will be replaced + with the actual iden in cached_event_message + """ + return message_to_json(event_message(IDEN_TEMPLATE, event)) def message_to_json(message: Any) -> str: diff --git a/tests/components/websocket_api/test_messages.py b/tests/components/websocket_api/test_messages.py index 832b72c5c1c..3ec156e6949 100644 --- a/tests/components/websocket_api/test_messages.py +++ b/tests/components/websocket_api/test_messages.py @@ -1,6 +1,7 @@ """Test Websocket API messages module.""" from homeassistant.components.websocket_api.messages import ( + _cached_event_message as lru_event_cache, cached_event_message, message_to_json, ) @@ -24,6 +25,7 @@ async def test_cached_event_message(hass): await hass.async_block_till_done() assert len(events) == 2 + lru_event_cache.cache_clear() msg0 = cached_event_message(2, events[0]) assert msg0 == cached_event_message(2, events[0]) @@ -33,18 +35,49 @@ async def test_cached_event_message(hass): assert msg0 != msg1 - cache_info = cached_event_message.cache_info() + cache_info = lru_event_cache.cache_info() assert cache_info.hits == 2 assert cache_info.misses == 2 assert cache_info.currsize == 2 cached_event_message(2, events[1]) - cache_info = cached_event_message.cache_info() + cache_info = lru_event_cache.cache_info() assert cache_info.hits == 3 assert cache_info.misses == 2 assert cache_info.currsize == 2 +async def test_cached_event_message_with_different_idens(hass): + """Test that we cache event messages when the subscrition idens differ.""" + + events = [] + + @callback + def _event_listener(event): + events.append(event) + + hass.bus.async_listen(EVENT_STATE_CHANGED, _event_listener) + + hass.states.async_set("light.window", "on") + await hass.async_block_till_done() + + assert len(events) == 1 + + lru_event_cache.cache_clear() + + msg0 = cached_event_message(2, events[0]) + msg1 = cached_event_message(3, events[0]) + msg2 = cached_event_message(4, events[0]) + + assert msg0 != msg1 + assert msg0 != msg2 + + cache_info = lru_event_cache.cache_info() + assert cache_info.hits == 2 + assert cache_info.misses == 1 + assert cache_info.currsize == 1 + + async def test_message_to_json(caplog): """Test we can serialize websocket messages.""" From e660577e43f11f4042ec3a40b61fdc7caa57e79b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 23 Oct 2020 01:17:47 +0200 Subject: [PATCH 018/118] Update frontend to 20201021.2 (#42242) --- 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 e04e25520ba..f02e9a4ec3a 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==20201021.1"], + "requirements": ["home-assistant-frontend==20201021.2"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 287cd336d82..41211fafb73 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -13,7 +13,7 @@ defusedxml==0.6.0 distro==1.5.0 emoji==0.5.4 hass-nabucasa==0.37.1 -home-assistant-frontend==20201021.1 +home-assistant-frontend==20201021.2 httpx==0.16.1 importlib-metadata==1.6.0;python_version<'3.8' jinja2>=2.11.2 diff --git a/requirements_all.txt b/requirements_all.txt index 869653d56cc..3406bfd3a3d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -759,7 +759,7 @@ hole==0.5.1 holidays==0.10.3 # homeassistant.components.frontend -home-assistant-frontend==20201021.1 +home-assistant-frontend==20201021.2 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8ca3780f66f..5bf62d047e4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -385,7 +385,7 @@ hole==0.5.1 holidays==0.10.3 # homeassistant.components.frontend -home-assistant-frontend==20201021.1 +home-assistant-frontend==20201021.2 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 From 868b14f15179969e996b71d1f0eda681453318f0 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Thu, 22 Oct 2020 19:30:32 -0400 Subject: [PATCH 019/118] Update ZHA dependencies (#42245) --- 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 d2c2cca36dd..01c24bdc6f6 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -8,7 +8,7 @@ "pyserial==3.4", "zha-quirks==0.0.45", "zigpy-cc==0.5.2", - "zigpy-deconz==0.10.0", + "zigpy-deconz==0.11.0", "zigpy==0.26.0", "zigpy-xbee==0.13.0", "zigpy-zigate==0.6.2", diff --git a/requirements_all.txt b/requirements_all.txt index 3406bfd3a3d..c02a23b986d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2343,7 +2343,7 @@ ziggo-mediabox-xl==1.1.0 zigpy-cc==0.5.2 # homeassistant.components.zha -zigpy-deconz==0.10.0 +zigpy-deconz==0.11.0 # homeassistant.components.zha zigpy-xbee==0.13.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5bf62d047e4..5444e4fee6b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1112,7 +1112,7 @@ zha-quirks==0.0.45 zigpy-cc==0.5.2 # homeassistant.components.zha -zigpy-deconz==0.10.0 +zigpy-deconz==0.11.0 # homeassistant.components.zha zigpy-xbee==0.13.0 From 8e4a325fdb809fb205d5b43d240c46ddd07183fa Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 22 Oct 2020 23:33:34 +0000 Subject: [PATCH 020/118] Bumped version to 0.117.0b2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 0eb828c5b1a..db8e92e2a42 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "0b1" +PATCH_VERSION = "0b2" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From e0466d4ac83ad2811323af0113721532c6911803 Mon Sep 17 00:00:00 2001 From: BrianWithAHat <19786223+BrianWithAHat@users.noreply.github.com> Date: Fri, 23 Oct 2020 03:25:55 -0400 Subject: [PATCH 021/118] Fix MaryTTS filename extensions (#42228) Co-authored-by: Brian Laferriere --- homeassistant/components/marytts/tts.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/marytts/tts.py b/homeassistant/components/marytts/tts.py index a5bdee618ee..0f75fbcee35 100644 --- a/homeassistant/components/marytts/tts.py +++ b/homeassistant/components/marytts/tts.py @@ -21,6 +21,8 @@ DEFAULT_VOICE = "cmu-slt-hsmm" DEFAULT_CODEC = "WAVE_FILE" DEFAULT_EFFECTS = {} +MAP_MARYTTS_CODEC = {"WAVE_FILE": "wav", "AIFF_FILE": "aiff", "AU_FILE": "au"} + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, @@ -81,5 +83,6 @@ class MaryTTSProvider(Provider): effects = options[CONF_EFFECT] data = self._mary.speak(message, effects) + audiotype = MAP_MARYTTS_CODEC[self._mary.codec] - return self._mary.codec, data + return audiotype, data From f6f95d04221c529fef4aef5326821030afa292c1 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Fri, 23 Oct 2020 15:14:37 +0200 Subject: [PATCH 022/118] Fix polling of color for ZHA lights not updating (#42248) --- homeassistant/components/zha/light.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index ba05d63df12..34c27f0af45 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -457,8 +457,8 @@ class Light(BaseLight, ZhaEntity): ): self._color_temp = results["color_temperature"] - color_x = results.get("color_x") - color_y = results.get("color_y") + color_x = results.get("current_x") + color_y = results.get("current_y") if color_x is not None and color_y is not None: self._hs_color = color_util.color_xy_to_hs( float(color_x / 65535), float(color_y / 65535) From aace9dce38ebf9e3c34f26705fba20d1e10b2d59 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Fri, 23 Oct 2020 04:17:10 -0700 Subject: [PATCH 023/118] Fix is_on test for Tesla Charger switch (#42251) closes #41755 --- homeassistant/components/tesla/switch.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/tesla/switch.py b/homeassistant/components/tesla/switch.py index cb57c1e3d5c..9fcdbae0c1c 100644 --- a/homeassistant/components/tesla/switch.py +++ b/homeassistant/components/tesla/switch.py @@ -2,7 +2,6 @@ import logging from homeassistant.components.switch import SwitchEntity -from homeassistant.const import STATE_ON from . import DOMAIN as TESLA_DOMAIN, TeslaDevice @@ -42,7 +41,7 @@ class ChargerSwitch(TeslaDevice, SwitchEntity): """Get whether the switch is in on state.""" if self.tesla_device.is_charging() is None: return None - return self.tesla_device.is_charging() == STATE_ON + return self.tesla_device.is_charging() class RangeSwitch(TeslaDevice, SwitchEntity): From 84ab40c57d0d4d5e26626381ad5e008834f89418 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 23 Oct 2020 17:40:43 +0200 Subject: [PATCH 024/118] Fix Tasmota relay acting as on/off light (#42259) --- .../components/tasmota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tasmota/test_light.py | 76 ++++++++++++++++++- tests/components/tasmota/test_switch.py | 4 +- 5 files changed, 79 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index b64d10c1070..41fffd9ea80 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota (beta)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.0.20"], + "requirements": ["hatasmota==0.0.21"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"] diff --git a/requirements_all.txt b/requirements_all.txt index c02a23b986d..10535ac8af1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -732,7 +732,7 @@ hass-nabucasa==0.37.1 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.0.20 +hatasmota==0.0.21 # homeassistant.components.jewish_calendar hdate==0.9.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5444e4fee6b..371c4917607 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -367,7 +367,7 @@ hangups==0.4.11 hass-nabucasa==0.37.1 # homeassistant.components.tasmota -hatasmota==0.0.20 +hatasmota==0.0.21 # homeassistant.components.jewish_calendar hdate==0.9.5 diff --git a/tests/components/tasmota/test_light.py b/tests/components/tasmota/test_light.py index d2f14a151d7..34164aa44f1 100644 --- a/tests/components/tasmota/test_light.py +++ b/tests/components/tasmota/test_light.py @@ -277,7 +277,39 @@ async def test_attributes_rgbww_reduced(hass, mqtt_mock, setup_tasmota): ) -async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): +async def test_controlling_state_via_mqtt_on_off(hass, mqtt_mock, setup_tasmota): + """Test state update via MQTT.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"][0] = 1 + config["so"]["30"] = 1 # Enforce Home Assistant auto-discovery as light + mac = config["mac"] + + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + + state = hass.states.get("light.test") + assert state.state == "unavailable" + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") + state = hass.states.get("light.test") + assert state.state == STATE_OFF + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON"}') + state = hass.states.get("light.test") + assert state.state == STATE_ON + + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"OFF"}') + state = hass.states.get("light.test") + assert state.state == STATE_OFF + + +async def test_controlling_state_via_mqtt_rgbww(hass, mqtt_mock, setup_tasmota): """Test state update via MQTT.""" config = copy.deepcopy(DEFAULT_CONFIG) config["rl"][0] = 2 @@ -344,7 +376,47 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): assert state.attributes.get("effect") == "Cycle down" -async def test_sending_mqtt_commands(hass, mqtt_mock, setup_tasmota): +async def test_sending_mqtt_commands_on_off(hass, mqtt_mock, setup_tasmota): + """Test the sending MQTT commands.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"][0] = 1 + config["so"]["30"] = 1 # Enforce Home Assistant auto-discovery as light + mac = config["mac"] + + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") + state = hass.states.get("light.test") + assert state.state == STATE_OFF + await hass.async_block_till_done() + await hass.async_block_till_done() + mqtt_mock.async_publish.reset_mock() + + # Turn the light on and verify MQTT message is sent + await common.async_turn_on(hass, "light.test") + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/Backlog", "NoDelay;Fade 0;NoDelay;Power1 ON", 0, False + ) + mqtt_mock.async_publish.reset_mock() + + # Tasmota is not optimistic, the state should still be off + state = hass.states.get("light.test") + assert state.state == STATE_OFF + + # Turn the light off and verify MQTT message is sent + await common.async_turn_off(hass, "light.test") + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/Backlog", "NoDelay;Fade 0;NoDelay;Power1 OFF", 0, False + ) + mqtt_mock.async_publish.reset_mock() + + +async def test_sending_mqtt_commands_rgbww(hass, mqtt_mock, setup_tasmota): """Test the sending MQTT commands.""" config = copy.deepcopy(DEFAULT_CONFIG) config["rl"][0] = 2 diff --git a/tests/components/tasmota/test_switch.py b/tests/components/tasmota/test_switch.py index 5757d161129..564dc2f2868 100644 --- a/tests/components/tasmota/test_switch.py +++ b/tests/components/tasmota/test_switch.py @@ -80,7 +80,7 @@ async def test_sending_mqtt_commands(hass, mqtt_mock, setup_tasmota): # Turn the switch on and verify MQTT message is sent await common.async_turn_on(hass, "switch.test") mqtt_mock.async_publish.assert_called_once_with( - "tasmota_49A3BC/cmnd/POWER1", "ON", 0, False + "tasmota_49A3BC/cmnd/Power1", "ON", 0, False ) mqtt_mock.async_publish.reset_mock() @@ -91,7 +91,7 @@ async def test_sending_mqtt_commands(hass, mqtt_mock, setup_tasmota): # Turn the switch off and verify MQTT message is sent await common.async_turn_off(hass, "switch.test") mqtt_mock.async_publish.assert_called_once_with( - "tasmota_49A3BC/cmnd/POWER1", "OFF", 0, False + "tasmota_49A3BC/cmnd/Power1", "OFF", 0, False ) state = hass.states.get("switch.test") From d8fea1c5821bd30378ca91cf56f7ff1f2dfeb625 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 23 Oct 2020 10:38:46 -0500 Subject: [PATCH 025/118] Ensure event listener integrations do the queue insert as a callback (#42265) --- homeassistant/components/influxdb/__init__.py | 2 ++ homeassistant/components/watson_iot/__init__.py | 2 ++ homeassistant/components/zabbix/__init__.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/homeassistant/components/influxdb/__init__.py b/homeassistant/components/influxdb/__init__.py index 5696424b402..16b6971b11f 100644 --- a/homeassistant/components/influxdb/__init__.py +++ b/homeassistant/components/influxdb/__init__.py @@ -26,6 +26,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) +from homeassistant.core import callback from homeassistant.helpers import event as event_helper, state as state_helper import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_values import EntityValues @@ -500,6 +501,7 @@ class InfluxThread(threading.Thread): self.shutdown = False hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener) + @callback def _event_listener(self, event): """Listen for new messages on the bus and queue them for Influx.""" item = (time.monotonic(), event) diff --git a/homeassistant/components/watson_iot/__init__.py b/homeassistant/components/watson_iot/__init__.py index ecb546f4a8c..99f6b63ec90 100644 --- a/homeassistant/components/watson_iot/__init__.py +++ b/homeassistant/components/watson_iot/__init__.py @@ -21,6 +21,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) +from homeassistant.core import callback from homeassistant.helpers import state as state_helper import homeassistant.helpers.config_validation as cv @@ -165,6 +166,7 @@ class WatsonIOTThread(threading.Thread): self.shutdown = False hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener) + @callback def _event_listener(self, event): """Listen for new messages on the bus and queue them for Watson IoT.""" item = (time.monotonic(), event) diff --git a/homeassistant/components/zabbix/__init__.py b/homeassistant/components/zabbix/__init__.py index 0afe8050d63..b9d02731570 100644 --- a/homeassistant/components/zabbix/__init__.py +++ b/homeassistant/components/zabbix/__init__.py @@ -22,6 +22,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) +from homeassistant.core import callback from homeassistant.helpers import event as event_helper, state as state_helper import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entityfilter import ( @@ -186,6 +187,7 @@ class ZabbixThread(threading.Thread): self.queue.put(None) self.join() + @callback def _event_listener(self, event): """Listen for new messages on the bus and queue them for Zabbix.""" item = (time.monotonic(), event) From 9c601957808846511c7b0791f43aa2a8ad600144 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 23 Oct 2020 15:45:06 +0000 Subject: [PATCH 026/118] Bumped version to 0.117.0b3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index db8e92e2a42..d9f7d4003ac 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "0b2" +PATCH_VERSION = "0b3" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 0c1c7d797c592ac2329d95ca9852c23c09c0ed55 Mon Sep 17 00:00:00 2001 From: Teemu R Date: Sun, 25 Oct 2020 01:53:36 +0200 Subject: [PATCH 027/118] Only log xiaomi_miio update exceptions once (#41226) * xiaomi_miio: only log update exceptions once Replaces #37695 * add som more missed exception logger cases + do not change the control flow as pointed out by @cgtobi * Use patch&MagickMock from tests.async_mock * Fix linting for alarm_control_panel * update the test to verify that the warning on update is only logged when the device was previously available --- .../components/xiaomi_miio/air_quality.py | 10 ++- .../xiaomi_miio/alarm_control_panel.py | 12 +-- homeassistant/components/xiaomi_miio/fan.py | 21 +++-- .../components/xiaomi_miio/gateway.py | 7 +- homeassistant/components/xiaomi_miio/light.py | 48 ++++++++---- .../components/xiaomi_miio/sensor.py | 5 +- .../components/xiaomi_miio/switch.py | 26 ++++--- .../components/xiaomi_miio/vacuum.py | 8 +- tests/components/xiaomi_miio/test_vacuum.py | 77 ++++++++++--------- 9 files changed, 127 insertions(+), 87 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/air_quality.py b/homeassistant/components/xiaomi_miio/air_quality.py index baeb0bf39e5..1e1e1b58632 100644 --- a/homeassistant/components/xiaomi_miio/air_quality.py +++ b/homeassistant/components/xiaomi_miio/air_quality.py @@ -195,8 +195,9 @@ class AirMonitorS1(AirMonitorB1): self._humidity = state.humidity self._available = True except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) class AirMonitorV1(AirMonitorB1): @@ -210,8 +211,9 @@ class AirMonitorV1(AirMonitorB1): self._air_quality_index = state.aqi self._available = True except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) @property def unit_of_measurement(self): diff --git a/homeassistant/components/xiaomi_miio/alarm_control_panel.py b/homeassistant/components/xiaomi_miio/alarm_control_panel.py index 89fcb45b864..6880202cbd6 100644 --- a/homeassistant/components/xiaomi_miio/alarm_control_panel.py +++ b/homeassistant/components/xiaomi_miio/alarm_control_panel.py @@ -3,7 +3,7 @@ from functools import partial import logging -from miio.gateway import GatewayException +from miio import DeviceException from homeassistant.components.alarm_control_panel import ( SUPPORT_ALARM_ARM_AWAY, @@ -103,7 +103,7 @@ class XiaomiGatewayAlarm(AlarmControlPanelEntity): partial(func, *args, **kwargs) ) _LOGGER.debug("Response received from miio device: %s", result) - except GatewayException as exc: + except DeviceException as exc: _LOGGER.error(mask_error, exc) async def async_alarm_arm_away(self, code=None): @@ -122,9 +122,11 @@ class XiaomiGatewayAlarm(AlarmControlPanelEntity): """Fetch state from the device.""" try: state = await self.hass.async_add_executor_job(self._gateway.alarm.status) - except GatewayException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + except DeviceException as ex: + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) + return _LOGGER.debug("Got new state: %s", state) diff --git a/homeassistant/components/xiaomi_miio/fan.py b/homeassistant/components/xiaomi_miio/fan.py index b25f69f68c3..8d971446a53 100644 --- a/homeassistant/components/xiaomi_miio/fan.py +++ b/homeassistant/components/xiaomi_miio/fan.py @@ -655,8 +655,10 @@ class XiaomiGenericDevice(FanEntity): return result == SUCCESS except DeviceException as exc: - _LOGGER.error(mask_error, exc) - self._available = False + if self._available: + _LOGGER.error(mask_error, exc) + self._available = False + return False async def async_turn_on(self, speed: str = None, **kwargs) -> None: @@ -785,8 +787,9 @@ class XiaomiAirPurifier(XiaomiGenericDevice): ) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) @property def speed_list(self) -> list: @@ -1029,8 +1032,9 @@ class XiaomiAirHumidifier(XiaomiGenericDevice): ) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) @property def speed_list(self) -> list: @@ -1138,8 +1142,9 @@ class XiaomiAirFresh(XiaomiGenericDevice): ) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) @property def speed_list(self) -> list: diff --git a/homeassistant/components/xiaomi_miio/gateway.py b/homeassistant/components/xiaomi_miio/gateway.py index 8c928cb36e4..eb2f4cdf2eb 100644 --- a/homeassistant/components/xiaomi_miio/gateway.py +++ b/homeassistant/components/xiaomi_miio/gateway.py @@ -65,7 +65,7 @@ class XiaomiGatewayDevice(Entity): self._entry = entry self._unique_id = sub_device.sid self._name = f"{sub_device.name} ({sub_device.sid})" - self._available = None + self._available = False @property def unique_id(self): @@ -100,5 +100,6 @@ class XiaomiGatewayDevice(Entity): await self.hass.async_add_executor_job(self._sub_device.update) self._available = True except gateway.GatewayException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) diff --git a/homeassistant/components/xiaomi_miio/light.py b/homeassistant/components/xiaomi_miio/light.py index 34ebb550adf..d1746fcd889 100644 --- a/homeassistant/components/xiaomi_miio/light.py +++ b/homeassistant/components/xiaomi_miio/light.py @@ -324,8 +324,10 @@ class XiaomiPhilipsAbstractLight(LightEntity): return result == SUCCESS except DeviceException as exc: - _LOGGER.error(mask_error, exc) - self._available = False + if self._available: + _LOGGER.error(mask_error, exc) + self._available = False + return False async def async_turn_on(self, **kwargs): @@ -356,8 +358,10 @@ class XiaomiPhilipsAbstractLight(LightEntity): try: state = await self.hass.async_add_executor_job(self._light.status) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) + return _LOGGER.debug("Got new state: %s", state) @@ -380,8 +384,10 @@ class XiaomiPhilipsGenericLight(XiaomiPhilipsAbstractLight): try: state = await self.hass.async_add_executor_job(self._light.status) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) + return _LOGGER.debug("Got new state: %s", state) @@ -536,8 +542,10 @@ class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight): try: state = await self.hass.async_add_executor_job(self._light.status) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) + return _LOGGER.debug("Got new state: %s", state) @@ -593,8 +601,10 @@ class XiaomiPhilipsCeilingLamp(XiaomiPhilipsBulb): try: state = await self.hass.async_add_executor_job(self._light.status) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) + return _LOGGER.debug("Got new state: %s", state) @@ -637,8 +647,10 @@ class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight): try: state = await self.hass.async_add_executor_job(self._light.status) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) + return _LOGGER.debug("Got new state: %s", state) @@ -778,8 +790,10 @@ class XiaomiPhilipsEyecareLampAmbientLight(XiaomiPhilipsAbstractLight): try: state = await self.hass.async_add_executor_job(self._light.status) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) + return _LOGGER.debug("Got new state: %s", state) @@ -932,8 +946,10 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb): try: state = await self.hass.async_add_executor_job(self._light.status) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) + return _LOGGER.debug("Got new state: %s", state) diff --git a/homeassistant/components/xiaomi_miio/sensor.py b/homeassistant/components/xiaomi_miio/sensor.py index 55f6c26c567..d20c2dfac1e 100644 --- a/homeassistant/components/xiaomi_miio/sensor.py +++ b/homeassistant/components/xiaomi_miio/sensor.py @@ -233,8 +233,9 @@ class XiaomiAirQualityMonitor(Entity): ) except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) class XiaomiGatewaySensor(XiaomiGatewayDevice): diff --git a/homeassistant/components/xiaomi_miio/switch.py b/homeassistant/components/xiaomi_miio/switch.py index 781dd10a825..b9e90cc5c23 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -280,8 +280,10 @@ class XiaomiPlugGenericSwitch(SwitchEntity): return result == SUCCESS except DeviceException as exc: - _LOGGER.error(mask_error, exc) - self._available = False + if self._available: + _LOGGER.error(mask_error, exc) + self._available = False + return False async def async_turn_on(self, **kwargs): @@ -316,8 +318,9 @@ class XiaomiPlugGenericSwitch(SwitchEntity): self._state_attrs[ATTR_TEMPERATURE] = state.temperature except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) async def async_set_wifi_led_on(self): """Turn the wifi led on.""" @@ -402,8 +405,9 @@ class XiaomiPowerStripSwitch(XiaomiPlugGenericSwitch): self._state_attrs[ATTR_POWER_PRICE] = state.power_price except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) async def async_set_power_mode(self, mode: str): """Set the power mode.""" @@ -492,8 +496,9 @@ class ChuangMiPlugSwitch(XiaomiPlugGenericSwitch): self._state_attrs[ATTR_LOAD_POWER] = state.load_power except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) class XiaomiAirConditioningCompanionSwitch(XiaomiPlugGenericSwitch): @@ -541,5 +546,6 @@ class XiaomiAirConditioningCompanionSwitch(XiaomiPlugGenericSwitch): self._state_attrs[ATTR_LOAD_POWER] = state.load_power except DeviceException as ex: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) + if self._available: + self._available = False + _LOGGER.error("Got exception while fetching the state: %s", ex) diff --git a/homeassistant/components/xiaomi_miio/vacuum.py b/homeassistant/components/xiaomi_miio/vacuum.py index 53fc458c50d..2a9ae1187b2 100644 --- a/homeassistant/components/xiaomi_miio/vacuum.py +++ b/homeassistant/components/xiaomi_miio/vacuum.py @@ -485,10 +485,10 @@ class MiroboVacuum(StateVacuumEntity): self.dnd_state = self._vacuum.dnd_status() self._available = True - except OSError as exc: - _LOGGER.error("Got OSError while fetching the state: %s", exc) - except DeviceException as exc: - _LOGGER.warning("Got exception while fetching the state: %s", exc) + except (OSError, DeviceException) as exc: + if self._available: + self._available = False + _LOGGER.warning("Got exception while fetching the state: %s", exc) # Fetch timers separately, see #38285 try: diff --git a/tests/components/xiaomi_miio/test_vacuum.py b/tests/components/xiaomi_miio/test_vacuum.py index 3b1234622ad..0fa241fb0b9 100644 --- a/tests/components/xiaomi_miio/test_vacuum.py +++ b/tests/components/xiaomi_miio/test_vacuum.py @@ -2,6 +2,7 @@ from datetime import datetime, time, timedelta from unittest import mock +from miio import DeviceException import pytest from pytz import utc @@ -52,9 +53,12 @@ from homeassistant.const import ( CONF_PLATFORM, STATE_OFF, STATE_ON, + STATE_UNAVAILABLE, ) from homeassistant.setup import async_setup_component +from tests.async_mock import MagicMock, patch + PLATFORM = "xiaomi_miio" # calls made when device status is requested @@ -70,7 +74,7 @@ STATUS_CALLS = [ @pytest.fixture(name="mock_mirobo_is_got_error") def mirobo_is_got_error_fixture(): """Mock mock_mirobo.""" - mock_vacuum = mock.MagicMock() + mock_vacuum = MagicMock() mock_vacuum.status().data = {"test": "raw"} mock_vacuum.status().is_on = False mock_vacuum.status().fanspeed = 38 @@ -98,21 +102,19 @@ def mirobo_is_got_error_fixture(): mock_vacuum.dnd_status().start = time(hour=22, minute=0) mock_vacuum.dnd_status().end = time(hour=6, minute=0) - mock_timer_1 = mock.MagicMock() + mock_timer_1 = MagicMock() mock_timer_1.enabled = True mock_timer_1.cron = "5 5 1 8 1" mock_timer_1.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc) - mock_timer_2 = mock.MagicMock() + mock_timer_2 = MagicMock() mock_timer_2.enabled = False mock_timer_2.cron = "5 5 1 8 2" mock_timer_2.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc) mock_vacuum.timer.return_value = [mock_timer_1, mock_timer_2] - with mock.patch( - "homeassistant.components.xiaomi_miio.vacuum.Vacuum" - ) as mock_vaccum_cls: + with patch("homeassistant.components.xiaomi_miio.vacuum.Vacuum") as mock_vaccum_cls: mock_vaccum_cls.return_value = mock_vacuum yield mock_vacuum @@ -135,14 +137,12 @@ new_fanspeeds = { @pytest.fixture(name="mock_mirobo_fanspeeds", params=[old_fanspeeds, new_fanspeeds]) def mirobo_old_speeds_fixture(request): """Fixture for testing both types of fanspeeds.""" - mock_vacuum = mock.MagicMock() + mock_vacuum = MagicMock() mock_vacuum.status().battery = 32 mock_vacuum.fan_speed_presets.return_value = request.param mock_vacuum.status().fanspeed = list(request.param.values())[0] - with mock.patch( - "homeassistant.components.xiaomi_miio.vacuum.Vacuum" - ) as mock_vaccum_cls: + with patch("homeassistant.components.xiaomi_miio.vacuum.Vacuum") as mock_vaccum_cls: mock_vaccum_cls.return_value = mock_vacuum yield mock_vacuum @@ -150,7 +150,7 @@ def mirobo_old_speeds_fixture(request): @pytest.fixture(name="mock_mirobo_is_on") def mirobo_is_on_fixture(): """Mock mock_mirobo.""" - mock_vacuum = mock.MagicMock() + mock_vacuum = MagicMock() mock_vacuum.status().data = {"test": "raw"} mock_vacuum.status().is_on = True mock_vacuum.status().fanspeed = 99 @@ -176,46 +176,53 @@ def mirobo_is_on_fixture(): mock_vacuum.status().state_code = 5 mock_vacuum.dnd_status().enabled = False - mock_timer_1 = mock.MagicMock() + mock_timer_1 = MagicMock() mock_timer_1.enabled = True mock_timer_1.cron = "5 5 1 8 1" mock_timer_1.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc) - mock_timer_2 = mock.MagicMock() + mock_timer_2 = MagicMock() mock_timer_2.enabled = False mock_timer_2.cron = "5 5 1 8 2" mock_timer_2.next_schedule = datetime(2020, 5, 23, 13, 21, 10, tzinfo=utc) mock_vacuum.timer.return_value = [mock_timer_1, mock_timer_2] - with mock.patch( - "homeassistant.components.xiaomi_miio.vacuum.Vacuum" - ) as mock_vaccum_cls: + with patch("homeassistant.components.xiaomi_miio.vacuum.Vacuum") as mock_vaccum_cls: mock_vaccum_cls.return_value = mock_vacuum yield mock_vacuum -@pytest.fixture(name="mock_mirobo_errors") -def mirobo_errors_fixture(): - """Mock mock_mirobo_errors to simulate a bad vacuum status request.""" - mock_vacuum = mock.MagicMock() - mock_vacuum.status.side_effect = OSError() - with mock.patch( - "homeassistant.components.xiaomi_miio.vacuum.Vacuum" - ) as mock_vaccum_cls: - mock_vaccum_cls.return_value = mock_vacuum - yield mock_vacuum - - -async def test_xiaomi_exceptions(hass, caplog, mock_mirobo_errors): - """Test vacuum supported features.""" +async def test_xiaomi_exceptions(hass, caplog, mock_mirobo_is_on): + """Test error logging on exceptions.""" entity_name = "test_vacuum_cleaner_error" - await setup_component(hass, entity_name) + entity_id = await setup_component(hass, entity_name) + def is_available(): + state = hass.states.get(entity_id) + return state.state != STATE_UNAVAILABLE + + # The initial setup has to be done successfully assert "Initializing with host 192.168.1.100 (token 12345...)" in caplog.text - assert mock_mirobo_errors.status.call_count == 1 - assert "ERROR" in caplog.text - assert "Got OSError while fetching the state" in caplog.text + assert "WARNING" not in caplog.text + assert is_available() + + # Second update causes an exception, which should be logged + mock_mirobo_is_on.status.side_effect = DeviceException("dummy exception") + await hass.helpers.entity_component.async_update_entity(entity_id) + assert "WARNING" in caplog.text + assert "Got exception while fetching the state" in caplog.text + assert not is_available() + + # Third update does not get logged as the device is already unavailable, + # so we clear the log and reset the status to test that + caplog.clear() + mock_mirobo_is_on.status.reset_mock() + + await hass.helpers.entity_component.async_update_entity(entity_id) + assert "Got exception while fetching the state" not in caplog.text + assert not is_available() + assert mock_mirobo_is_on.status.call_count == 1 async def test_xiaomi_vacuum_services(hass, caplog, mock_mirobo_is_got_error): @@ -463,7 +470,7 @@ async def test_xiaomi_vacuum_fanspeeds(hass, caplog, mock_mirobo_fanspeeds): {"entity_id": entity_id, "fan_speed": "invent"}, blocking=True, ) - assert "ERROR" in caplog.text + assert "Fan speed step not recognized" in caplog.text async def test_xiaomi_vacuum_goto_service(hass, caplog, mock_mirobo_is_on): From b6994689b18ef2b0ff5b4a2b4463510e969d150c Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 24 Oct 2020 11:48:28 -0700 Subject: [PATCH 028/118] Update nest integration with fixes from initial PR (#42250) --- homeassistant/components/nest/__init__.py | 10 +++- homeassistant/components/nest/config_flow.py | 10 +++- homeassistant/components/nest/sensor.py | 4 +- homeassistant/components/nest/sensor_sdm.py | 30 +++++----- tests/components/nest/sensor_sdm_test.py | 62 ++++++++++++++++++-- 5 files changed, 87 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/nest/__init__.py b/homeassistant/components/nest/__init__.py index ae32527bdc4..16ff05f9091 100644 --- a/homeassistant/components/nest/__init__.py +++ b/homeassistant/components/nest/__init__.py @@ -30,7 +30,11 @@ from homeassistant.helpers import ( config_entry_oauth2_flow, config_validation as cv, ) -from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, + dispatcher_send, +) from homeassistant.helpers.entity import Entity from . import api, config_flow, local_auth @@ -176,7 +180,7 @@ class SignalUpdateCallback(EventCallback): # This event triggered an update to a device that changed some # properties which the DeviceManager should already have received. # Send a signal to refresh state of all listening devices. - dispatcher_send(self._hass, SIGNAL_NEST_UPDATE) + async_dispatcher_send(self._hass, SIGNAL_NEST_UPDATE) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): @@ -203,7 +207,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): auth, config[CONF_PROJECT_ID], config[CONF_SUBSCRIBER_ID] ) subscriber.set_update_callback(SignalUpdateCallback(hass)) - hass.loop.create_task(subscriber.start_async()) + asyncio.create_task(subscriber.start_async()) hass.data[DOMAIN][entry.entry_id] = subscriber for component in PLATFORMS: diff --git a/homeassistant/components/nest/config_flow.py b/homeassistant/components/nest/config_flow.py index aa4b7af7e12..e792b496da5 100644 --- a/homeassistant/components/nest/config_flow.py +++ b/homeassistant/components/nest/config_flow.py @@ -61,6 +61,10 @@ class CodeInvalid(NestAuthError): """Raised when invalid authorization code.""" +class UnexpectedStateError(HomeAssistantError): + """Raised when the config flow is invoked in a 'should not happen' case.""" + + @config_entries.HANDLERS.register(DOMAIN) class NestFlowHandler( config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN @@ -111,7 +115,7 @@ class NestFlowHandler( async def async_step_init(self, user_input=None): """Handle a flow start.""" if self.is_sdm_api(): - return None + raise UnexpectedStateError("Step only supported for legacy API") flows = self.hass.data.get(DATA_FLOW_IMPL, {}) @@ -142,7 +146,7 @@ class NestFlowHandler( deliver the authentication code. """ if self.is_sdm_api(): - return None + raise UnexpectedStateError("Step only supported for legacy API") flow = self.hass.data[DATA_FLOW_IMPL][self.flow_impl] @@ -185,7 +189,7 @@ class NestFlowHandler( async def async_step_import(self, info): """Import existing auth from Nest.""" if self.is_sdm_api(): - return None + raise UnexpectedStateError("Step only supported for legacy API") if self.hass.config_entries.async_entries(DOMAIN): return self.async_abort(reason="single_instance_allowed") diff --git a/homeassistant/components/nest/sensor.py b/homeassistant/components/nest/sensor.py index b3df3dd393e..794e8210f82 100644 --- a/homeassistant/components/nest/sensor.py +++ b/homeassistant/components/nest/sensor.py @@ -13,5 +13,5 @@ async def async_setup_entry( ) -> None: """Set up the sensors.""" if DATA_SDM not in entry.data: - return await async_setup_legacy_entry(hass, entry, async_add_entities) - return await async_setup_sdm_entry(hass, entry, async_add_entities) + await async_setup_legacy_entry(hass, entry, async_add_entities) + await async_setup_sdm_entry(hass, entry, async_add_entities) diff --git a/homeassistant/components/nest/sensor_sdm.py b/homeassistant/components/nest/sensor_sdm.py index 8c567c9b36e..6c4e37e57b3 100644 --- a/homeassistant/components/nest/sensor_sdm.py +++ b/homeassistant/components/nest/sensor_sdm.py @@ -18,6 +18,13 @@ from homeassistant.helpers.typing import HomeAssistantType from .const import DOMAIN, SIGNAL_NEST_UPDATE +DEVICE_TYPE_MAP = { + "sdm.devices.types.CAMERA": "Camera", + "sdm.devices.types.DISPLAY": "Display", + "sdm.devices.types.DOORBELL": "Doorbell", + "sdm.devices.types.THERMOSTAT": "Thermostat", +} + async def async_setup_sdm_entry( hass: HomeAssistantType, entry: ConfigEntry, async_add_entities @@ -46,7 +53,7 @@ class SensorBase(Entity): self._device = device @property - def should_pool(self) -> bool: + def should_poll(self) -> bool: """Disable polling since entities have state pushed via pubsub.""" return False @@ -89,28 +96,19 @@ class SensorBase(Entity): # The API intentionally returns minimal information about specific # devices, instead relying on traits, but we can infer a generic model # name based on the type - if self._device.type == "sdm.devices.types.CAMERA": - return "Camera" - if self._device.type == "sdm.devices.types.DISPLAY": - return "Display" - if self._device.type == "sdm.devices.types.DOORBELL": - return "Doorbell" - if self._device.type == "sdm.devices.types.THERMOSTAT": - return "Thermostat" - return None + return DEVICE_TYPE_MAP.get(self._device.type) async def async_added_to_hass(self): """Run when entity is added to register update signal handler.""" - - async def async_update_state(): - """Update sensor state.""" - await self.async_update_ha_state(True) - # Event messages trigger the SIGNAL_NEST_UPDATE, which is intercepted # here to re-fresh the signals from _device. Unregister this callback # when the entity is removed. self.async_on_remove( - async_dispatcher_connect(self.hass, SIGNAL_NEST_UPDATE, async_update_state) + async_dispatcher_connect( + self.hass, + SIGNAL_NEST_UPDATE, + self.async_write_ha_state, + ) ) diff --git a/tests/components/nest/sensor_sdm_test.py b/tests/components/nest/sensor_sdm_test.py index fbd13701260..ab2cae3d9f3 100644 --- a/tests/components/nest/sensor_sdm_test.py +++ b/tests/components/nest/sensor_sdm_test.py @@ -41,6 +41,8 @@ CONFIG_ENTRY_DATA = { }, } +THERMOSTAT_TYPE = "sdm.devices.types.THERMOSTAT" + class FakeDeviceManager(DeviceManager): """Fake DeviceManager that can supply a list of devices and structures.""" @@ -104,7 +106,7 @@ async def setup_sensor(hass, devices={}, structures={}): "homeassistant.components.nest.GoogleNestSubscriber", return_value=subscriber ): assert await async_setup_component(hass, DOMAIN, CONFIG) - await hass.async_block_till_done() + await hass.async_block_till_done() return subscriber @@ -114,7 +116,7 @@ async def test_thermostat_device(hass): "some-device-id": Device.MakeDevice( { "name": "some-device-id", - "type": "sdm.devices.types.Thermostat", + "type": THERMOSTAT_TYPE, "traits": { "sdm.devices.traits.Info": { "customName": "My Sensor", @@ -140,6 +142,18 @@ async def test_thermostat_device(hass): assert humidity is not None assert humidity.state == "35.0" + registry = await hass.helpers.entity_registry.async_get_registry() + entry = registry.async_get("sensor.my_sensor_temperature") + assert entry.unique_id == "some-device-id-temperature" + assert entry.original_name == "My Sensor Temperature" + assert entry.domain == "sensor" + + device_registry = await hass.helpers.device_registry.async_get_registry() + device = device_registry.async_get(entry.device_id) + assert device.name == "My Sensor" + assert device.model == "Thermostat" + assert device.identifiers == {("nest", "some-device-id")} + async def test_no_devices(hass): """Test no devices returned by the api.""" @@ -158,7 +172,7 @@ async def test_device_no_sensor_traits(hass): "some-device-id": Device.MakeDevice( { "name": "some-device-id", - "type": "sdm.devices.types.Thermostat", + "type": THERMOSTAT_TYPE, "traits": {}, }, auth=None, @@ -179,7 +193,7 @@ async def test_device_name_from_structure(hass): "some-device-id": Device.MakeDevice( { "name": "some-device-id", - "type": "sdm.devices.types.Thermostat", + "type": THERMOSTAT_TYPE, "traits": { "sdm.devices.traits.Temperature": { "ambientTemperatureCelsius": 25.2, @@ -205,7 +219,7 @@ async def test_event_updates_sensor(hass): "some-device-id": Device.MakeDevice( { "name": "some-device-id", - "type": "sdm.devices.types.Thermostat", + "type": THERMOSTAT_TYPE, "traits": { "sdm.devices.traits.Info": { "customName": "My Sensor", @@ -246,3 +260,41 @@ async def test_event_updates_sensor(hass): temperature = hass.states.get("sensor.my_sensor_temperature") assert temperature is not None assert temperature.state == "26.2" + + +async def test_device_with_unknown_type(hass): + """Test a device without a custom name, inferring name from structure.""" + devices = { + "some-device-id": Device.MakeDevice( + { + "name": "some-device-id", + "type": "some-unknown-type", + "traits": { + "sdm.devices.traits.Info": { + "customName": "My Sensor", + }, + "sdm.devices.traits.Temperature": { + "ambientTemperatureCelsius": 25.1, + }, + }, + }, + auth=None, + ) + } + await setup_sensor(hass, devices) + + temperature = hass.states.get("sensor.my_sensor_temperature") + assert temperature is not None + assert temperature.state == "25.1" + + registry = await hass.helpers.entity_registry.async_get_registry() + entry = registry.async_get("sensor.my_sensor_temperature") + assert entry.unique_id == "some-device-id-temperature" + assert entry.original_name == "My Sensor Temperature" + assert entry.domain == "sensor" + + device_registry = await hass.helpers.device_registry.async_get_registry() + device = device_registry.async_get(entry.device_id) + assert device.name == "My Sensor" + assert device.model is None + assert device.identifiers == {("nest", "some-device-id")} From 21917e86c98b31596fe8876959eaaefb3f209962 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sat, 24 Oct 2020 12:13:40 -0700 Subject: [PATCH 029/118] Fix Tesla attribute refreshing (#42257) * Fix Tesla attribute refreshing * Remove extraneous copy --- homeassistant/components/tesla/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/tesla/__init__.py b/homeassistant/components/tesla/__init__.py index a04c5975881..51090d34271 100644 --- a/homeassistant/components/tesla/__init__.py +++ b/homeassistant/components/tesla/__init__.py @@ -282,7 +282,7 @@ class TeslaDevice(CoordinatorEntity): @property def device_state_attributes(self): """Return the state attributes of the device.""" - attr = self._attributes.copy() + attr = self._attributes if self.tesla_device.has_battery(): attr[ATTR_BATTERY_LEVEL] = self.tesla_device.battery_level() attr[ATTR_BATTERY_CHARGING] = self.tesla_device.battery_charging() @@ -310,4 +310,5 @@ class TeslaDevice(CoordinatorEntity): This assumes the coordinator has updated the controller. """ self.tesla_device.refresh() + self._attributes = self.tesla_device.attrs.copy() self.async_write_ha_state() From 00284a87d0ab8fca3405d8a1173cec3aa2fd5433 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Sun, 25 Oct 2020 10:55:12 +0800 Subject: [PATCH 030/118] Fix AVError and allow more missing DTS packets in stream (#42277) * Fix AVError and allow more missing DTS in stream init * Avoid recreating container demux iterator * Relax missing dts requirement * Fix spelling error * Fix error message count * Add timeout to av.open read * Increase STREAM_TIMEOUT to 30 --- homeassistant/components/stream/const.py | 3 ++ homeassistant/components/stream/worker.py | 59 ++++++++++++++--------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/stream/const.py b/homeassistant/components/stream/const.py index b986cddaf68..9406c24eaf6 100644 --- a/homeassistant/components/stream/const.py +++ b/homeassistant/components/stream/const.py @@ -20,3 +20,6 @@ MIN_SEGMENT_DURATION = 1.5 # Each segment is at least this many seconds PACKETS_TO_WAIT_FOR_AUDIO = 20 # Some streams have an audio stream with no audio MAX_TIMESTAMP_GAP = 10000 # seconds - anything from 10 to 50000 is probably reasonable + +MAX_MISSING_DTS = 6 # Number of packets missing DTS to allow +STREAM_TIMEOUT = 30 # Timeout for reading stream diff --git a/homeassistant/components/stream/worker.py b/homeassistant/components/stream/worker.py index 6769413bafd..4f972774fcc 100644 --- a/homeassistant/components/stream/worker.py +++ b/homeassistant/components/stream/worker.py @@ -6,7 +6,13 @@ import time import av -from .const import MAX_TIMESTAMP_GAP, MIN_SEGMENT_DURATION, PACKETS_TO_WAIT_FOR_AUDIO +from .const import ( + MAX_MISSING_DTS, + MAX_TIMESTAMP_GAP, + MIN_SEGMENT_DURATION, + PACKETS_TO_WAIT_FOR_AUDIO, + STREAM_TIMEOUT, +) from .core import Segment, StreamBuffer _LOGGER = logging.getLogger(__name__) @@ -62,7 +68,7 @@ def stream_worker(hass, stream, quit_event): def _stream_worker_internal(hass, stream, quit_event): """Handle consuming streams.""" - container = av.open(stream.source, options=stream.options) + container = av.open(stream.source, options=stream.options, timeout=STREAM_TIMEOUT) try: video_stream = container.streams.video[0] except (KeyError, IndexError): @@ -81,13 +87,15 @@ def _stream_worker_internal(hass, stream, quit_event): if audio_stream and audio_stream.profile is None: audio_stream = None + # Iterator for demuxing + container_packets = None # The presentation timestamps of the first packet in each stream we receive # Use to adjust before muxing or outputting, but we don't adjust internally first_pts = {} # The decoder timestamps of the latest packet in each stream we processed last_dts = None # Keep track of consecutive packets without a dts to detect end of stream. - last_packet_was_without_dts = False + missing_dts = 0 # Holds the buffers for each stream provider outputs = None # Keep track of the number of segments we've processed @@ -102,8 +110,8 @@ def _stream_worker_internal(hass, stream, quit_event): # 2 - seeking can be problematic https://trac.ffmpeg.org/ticket/7815 def peek_first_pts(): - nonlocal first_pts, audio_stream - missing_dts = False + nonlocal first_pts, audio_stream, container_packets + missing_dts = 0 def empty_stream_dict(): return { @@ -112,17 +120,20 @@ def _stream_worker_internal(hass, stream, quit_event): } try: + container_packets = container.demux((video_stream, audio_stream)) first_packet = empty_stream_dict() first_pts = empty_stream_dict() # Get to first video keyframe while first_packet[video_stream] is None: - packet = next(container.demux()) + packet = next(container_packets) if ( packet.dts is None - ): # Allow single packet with no dts, raise error on second - if missing_dts: - raise av.AVError - missing_dts = True + ): # Allow MAX_MISSING_DTS packets with no dts, raise error on the next one + if missing_dts >= MAX_MISSING_DTS: + raise StopIteration( + f"Invalid data - got {MAX_MISSING_DTS+1} packets with missing DTS while initializing" + ) + missing_dts += 1 continue if packet.stream == video_stream and packet.is_keyframe: first_packet[video_stream] = packet @@ -131,13 +142,15 @@ def _stream_worker_internal(hass, stream, quit_event): while any( [pts is None for pts in {**first_packet, **first_pts}.values()] ) and (len(initial_packets) < PACKETS_TO_WAIT_FOR_AUDIO): - packet = next(container.demux((video_stream, audio_stream))) + packet = next(container_packets) if ( packet.dts is None - ): # Allow single packet with no dts, raise error on second - if missing_dts: - raise av.AVError - missing_dts = True + ): # Allow MAX_MISSING_DTS packet with no dts, raise error on the next one + if missing_dts >= MAX_MISSING_DTS: + raise StopIteration( + f"Invalid data - got {MAX_MISSING_DTS+1} packets with missing DTS while initializing" + ) + missing_dts += 1 continue if ( first_packet[packet.stream] is None @@ -223,16 +236,16 @@ def _stream_worker_internal(hass, stream, quit_event): if len(initial_packets) > 0: packet = initial_packets.popleft() else: - packet = next(container.demux((video_stream, audio_stream))) + packet = next(container_packets) if packet.dts is None: - _LOGGER.error("Stream packet without dts detected, skipping...") - # Allow a single packet without dts before terminating the stream. - if last_packet_was_without_dts: - # If we get a "flushing" packet, the stream is done - raise StopIteration("No dts in consecutive packets") - last_packet_was_without_dts = True + # Allow MAX_MISSING_DTS consecutive packets without dts. Terminate the stream on the next one. + if missing_dts >= MAX_MISSING_DTS: + raise StopIteration( + f"No dts in {MAX_MISSING_DTS+1} consecutive packets" + ) + missing_dts += 1 continue - last_packet_was_without_dts = False + missing_dts = 0 except (av.AVError, StopIteration) as ex: _LOGGER.error("Error demuxing stream: %s", str(ex)) finalize_stream() From 3032f9280cbcd4a41fde355a96aa53f6c57effbb Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 24 Oct 2020 14:29:26 +0200 Subject: [PATCH 031/118] Fix Tasmota CT light (#42287) --- .../components/tasmota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tasmota/test_light.py | 55 +++++++++++++++++++ 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 41fffd9ea80..bdd25f4466a 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota (beta)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.0.21"], + "requirements": ["hatasmota==0.0.22"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"] diff --git a/requirements_all.txt b/requirements_all.txt index 10535ac8af1..8258793ebf8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -732,7 +732,7 @@ hass-nabucasa==0.37.1 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.0.21 +hatasmota==0.0.22 # homeassistant.components.jewish_calendar hdate==0.9.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 371c4917607..4806de987c5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -367,7 +367,7 @@ hangups==0.4.11 hass-nabucasa==0.37.1 # homeassistant.components.tasmota -hatasmota==0.0.21 +hatasmota==0.0.22 # homeassistant.components.jewish_calendar hdate==0.9.5 diff --git a/tests/components/tasmota/test_light.py b/tests/components/tasmota/test_light.py index 34164aa44f1..afb583a5a2a 100644 --- a/tests/components/tasmota/test_light.py +++ b/tests/components/tasmota/test_light.py @@ -309,6 +309,61 @@ async def test_controlling_state_via_mqtt_on_off(hass, mqtt_mock, setup_tasmota) assert state.state == STATE_OFF +async def test_controlling_state_via_mqtt_ct(hass, mqtt_mock, setup_tasmota): + """Test state update via MQTT.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"][0] = 2 + config["lt_st"] = 2 # 2 channel light (CT) + mac = config["mac"] + + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + + state = hass.states.get("light.test") + assert state.state == "unavailable" + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") + state = hass.states.get("light.test") + assert state.state == STATE_OFF + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON"}') + state = hass.states.get("light.test") + assert state.state == STATE_ON + + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"OFF"}') + state = hass.states.get("light.test") + assert state.state == STATE_OFF + + async_fire_mqtt_message( + hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Dimmer":50}' + ) + state = hass.states.get("light.test") + assert state.state == STATE_ON + assert state.attributes.get("brightness") == 127.5 + + async_fire_mqtt_message( + hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","CT":300}' + ) + state = hass.states.get("light.test") + assert state.state == STATE_ON + assert state.attributes.get("color_temp") == 300 + + # Tasmota will send "Color" also for CT light, this should be ignored + async_fire_mqtt_message( + hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Color":"255,128"}' + ) + state = hass.states.get("light.test") + assert state.state == STATE_ON + assert state.attributes.get("color_temp") == 300 + assert state.attributes.get("brightness") == 127.5 + + async def test_controlling_state_via_mqtt_rgbww(hass, mqtt_mock, setup_tasmota): """Test state update via MQTT.""" config = copy.deepcopy(DEFAULT_CONFIG) From 5c608eb1bc98ef9397c2cc770b1b6acaedad1275 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 24 Oct 2020 14:31:08 +0200 Subject: [PATCH 032/118] Fix reconfiguring of Tasmota lights (#42288) --- homeassistant/components/tasmota/light.py | 5 ++-- homeassistant/components/tasmota/mixins.py | 5 ++-- tests/components/tasmota/test_common.py | 2 +- tests/components/tasmota/test_light.py | 34 ++++++++++++++++++++++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/tasmota/light.py b/homeassistant/components/tasmota/light.py index c761d3436df..1a41e7373fd 100644 --- a/homeassistant/components/tasmota/light.py +++ b/homeassistant/components/tasmota/light.py @@ -80,10 +80,11 @@ class TasmotaLight( self._setup_from_entity() - async def discovery_update(self, update): + async def discovery_update(self, update, write_state=True): """Handle updated discovery message.""" + await super().discovery_update(update, write_state=False) self._setup_from_entity() - await super().discovery_update(update) + self.async_write_ha_state() def _setup_from_entity(self): """(Re)Setup the entity.""" diff --git a/homeassistant/components/tasmota/mixins.py b/homeassistant/components/tasmota/mixins.py index 3b52fafd12b..5a1c7a9f3de 100644 --- a/homeassistant/components/tasmota/mixins.py +++ b/homeassistant/components/tasmota/mixins.py @@ -38,11 +38,12 @@ class TasmotaEntity(Entity): await self._tasmota_entity.unsubscribe_topics() await super().async_will_remove_from_hass() - async def discovery_update(self, update): + async def discovery_update(self, update, write_state=True): """Handle updated discovery message.""" self._tasmota_entity.config_update(update) await self._subscribe_topics() - self.async_write_ha_state() + if write_state: + self.async_write_ha_state() async def _subscribe_topics(self): """(Re)Subscribe to topics.""" diff --git a/tests/components/tasmota/test_common.py b/tests/components/tasmota/test_common.py index ccd87b2e434..5c18f2f401c 100644 --- a/tests/components/tasmota/test_common.py +++ b/tests/components/tasmota/test_common.py @@ -385,7 +385,7 @@ async def help_test_discovery_update_unchanged( entity_id="test", name="Test", ): - """Test update of discovered component without changes. + """Test update of discovered component with and without changes. This is a test helper for the MqttDiscoveryUpdate mixin. """ diff --git a/tests/components/tasmota/test_light.py b/tests/components/tasmota/test_light.py index afb583a5a2a..871d55a4d73 100644 --- a/tests/components/tasmota/test_light.py +++ b/tests/components/tasmota/test_light.py @@ -2,6 +2,8 @@ import copy import json +from hatasmota.const import CONF_MAC + from homeassistant.components import light from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, @@ -771,6 +773,38 @@ async def test_unlinked_light2(hass, mqtt_mock, setup_tasmota): await _test_unlinked_light(hass, mqtt_mock, config, 2) +async def test_discovery_update_reconfigure_light( + hass, mqtt_mock, caplog, setup_tasmota +): + """Test reconfigure of discovered light.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"][0] = 2 + config["lt_st"] = 1 # 1 channel light (Dimmer) + config2 = copy.deepcopy(DEFAULT_CONFIG) + config2["rl"][0] = 2 + config2["lt_st"] = 3 # 3 channel light (RGB) + data1 = json.dumps(config) + data2 = json.dumps(config2) + + # Simple dimmer + async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data1) + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert ( + state.attributes.get("supported_features") + == SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION + ) + + # Reconfigure as RGB light + async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{config[CONF_MAC]}/config", data2) + await hass.async_block_till_done() + state = hass.states.get("light.test") + assert ( + state.attributes.get("supported_features") + == SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_EFFECT | SUPPORT_TRANSITION + ) + + async def test_availability_when_connection_lost( hass, mqtt_client_mock, mqtt_mock, setup_tasmota ): From b925ae39cbc1229b3f53ca64ddbef73dafb2de43 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 24 Oct 2020 18:19:57 +0200 Subject: [PATCH 033/118] Upgrade spotipy to 2.16.1 (#42293) --- 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 db2f35ded91..c4d7378c060 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.16.0"], + "requirements": ["spotipy==2.16.1"], "zeroconf": ["_spotify-connect._tcp.local."], "dependencies": ["http"], "codeowners": ["@frenck"], diff --git a/requirements_all.txt b/requirements_all.txt index 8258793ebf8..6f6173dae4c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2088,7 +2088,7 @@ spiderpy==1.3.1 spotcrime==1.0.4 # homeassistant.components.spotify -spotipy==2.16.0 +spotipy==2.16.1 # homeassistant.components.recorder # homeassistant.components.sql diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4806de987c5..6d010c5e213 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -995,7 +995,7 @@ speedtest-cli==2.1.2 spiderpy==1.3.1 # homeassistant.components.spotify -spotipy==2.16.0 +spotipy==2.16.1 # homeassistant.components.recorder # homeassistant.components.sql From 2e7ce63cbc036a4674c544f9a1c39b240039111c Mon Sep 17 00:00:00 2001 From: airthusiast <67556031+airthusiast@users.noreply.github.com> Date: Sun, 25 Oct 2020 13:06:45 +0100 Subject: [PATCH 034/118] Fix for Fibaro HC3 support, climate temp sensor and target temp (#42300) --- homeassistant/components/fibaro/climate.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/fibaro/climate.py b/homeassistant/components/fibaro/climate.py index eee1c08bd36..6a7a6f145d8 100644 --- a/homeassistant/components/fibaro/climate.py +++ b/homeassistant/components/fibaro/climate.py @@ -132,7 +132,10 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): elif ( self._temp_sensor_device is None and "unit" in device.properties - and "value" in device.properties + and ( + "value" in device.properties + or "heatingThermostatSetpoint" in device.properties + ) and (device.properties.unit == "C" or device.properties.unit == "F") ): self._temp_sensor_device = FibaroDevice(device) @@ -141,6 +144,7 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): if ( "setTargetLevel" in device.actions or "setThermostatSetpoint" in device.actions + or "setHeatingThermostatSetpoint" in device.actions ): self._target_temp_device = FibaroDevice(device) self._support_flags |= SUPPORT_TARGET_TEMPERATURE @@ -317,6 +321,8 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): """Return the current temperature.""" if self._temp_sensor_device: device = self._temp_sensor_device.fibaro_device + if device.properties.heatingThermostatSetpoint: + return float(device.properties.heatingThermostatSetpoint) return float(device.properties.value) return None @@ -325,6 +331,8 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): """Return the temperature we try to reach.""" if self._target_temp_device: device = self._target_temp_device.fibaro_device + if device.properties.heatingThermostatSetpointFuture: + return float(device.properties.heatingThermostatSetpointFuture) return float(device.properties.targetLevel) return None @@ -335,5 +343,7 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): if temperature is not None: if "setThermostatSetpoint" in target.fibaro_device.actions: target.action("setThermostatSetpoint", self.fibaro_op_mode, temperature) + elif "setHeatingThermostatSetpoint" in target.fibaro_device.actions: + target.action("setHeatingThermostatSetpoint", temperature) else: target.action("setTargetLevel", temperature) From 883fb8c168a439c4e1439112be504b1e97bb6092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 25 Oct 2020 01:07:17 +0200 Subject: [PATCH 035/118] Fix iCloud matching accounts (#42303) `name` is no longer defined but `username`. Fixes #38393 --- homeassistant/components/icloud/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/icloud/__init__.py b/homeassistant/components/icloud/__init__.py index 723e89ed781..6a3897a54c0 100644 --- a/homeassistant/components/icloud/__init__.py +++ b/homeassistant/components/icloud/__init__.py @@ -187,7 +187,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool icloud_account = hass.data[DOMAIN].get(account_identifier) if icloud_account is None: for account in hass.data[DOMAIN].values(): - if account.name == account_identifier: + if account.username == account_identifier: icloud_account = account if icloud_account is None: From a2c157b5e98c8cc43b0e7b2e7de33ee70497c164 Mon Sep 17 00:00:00 2001 From: mezz64 <2854333+mezz64@users.noreply.github.com> Date: Sun, 25 Oct 2020 08:32:41 -0400 Subject: [PATCH 036/118] Bump pyhik to 0.2.8 (#42322) --- homeassistant/components/hikvision/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hikvision/manifest.json b/homeassistant/components/hikvision/manifest.json index e6dec7b8e89..8abe4519166 100644 --- a/homeassistant/components/hikvision/manifest.json +++ b/homeassistant/components/hikvision/manifest.json @@ -2,6 +2,6 @@ "domain": "hikvision", "name": "Hikvision", "documentation": "https://www.home-assistant.io/integrations/hikvision", - "requirements": ["pyhik==0.2.7"], + "requirements": ["pyhik==0.2.8"], "codeowners": ["@mezz64"] } diff --git a/requirements_all.txt b/requirements_all.txt index 6f6173dae4c..ba6d0edb546 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1412,7 +1412,7 @@ pyhaversion==3.4.2 pyheos==0.6.0 # homeassistant.components.hikvision -pyhik==0.2.7 +pyhik==0.2.8 # homeassistant.components.hive pyhiveapi==0.2.20.2 From 331d5ba7efc2dd6f2ee6ddb018b12007f1e2f6f7 Mon Sep 17 00:00:00 2001 From: Andre Lengwenus Date: Sun, 25 Oct 2020 13:44:39 +0100 Subject: [PATCH 037/118] Fix parameter issue in LCN cover close/stop (#42342) --- homeassistant/components/lcn/cover.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/lcn/cover.py b/homeassistant/components/lcn/cover.py index a4fa0a19b76..512a7978c8c 100644 --- a/homeassistant/components/lcn/cover.py +++ b/homeassistant/components/lcn/cover.py @@ -89,7 +89,7 @@ class LcnOutputsCover(LcnDevice, CoverEntity): self._is_opening = False self._is_closing = True state = pypck.lcn_defs.MotorStateModifier.DOWN - self.address_connection.control_motors_outputs(state) + self.address_connection.control_motors_outputs(state, self.reverse_time) self.async_write_ha_state() async def async_open_cover(self, **kwargs): @@ -106,7 +106,7 @@ class LcnOutputsCover(LcnDevice, CoverEntity): self._is_closing = False self._is_opening = False state = pypck.lcn_defs.MotorStateModifier.STOP - self.address_connection.control_motors_outputs(state, self.reverse_time) + self.address_connection.control_motors_outputs(state) self.async_write_ha_state() def input_received(self, input_obj): From 35d2badb2478cd52a062f430579173950bdd1edc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 25 Oct 2020 17:53:31 -0500 Subject: [PATCH 038/118] Ensure config entry platforms are excluded from reload (#42367) --- homeassistant/helpers/entity_platform.py | 4 ++++ homeassistant/helpers/reload.py | 10 +++++++--- tests/helpers/test_reload.py | 10 ++++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index 2792debff53..693c01b982f 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -79,6 +79,10 @@ class EntityPlatform: self.platform_name, [] ).append(self) + def __repr__(self): + """Represent an EntityPlatform.""" + return f"" + @callback def _get_parallel_updates_semaphore( self, entity_has_async_update: bool diff --git a/homeassistant/helpers/reload.py b/homeassistant/helpers/reload.py index 1c11afdb46b..e596027b7e1 100644 --- a/homeassistant/helpers/reload.py +++ b/homeassistant/helpers/reload.py @@ -80,7 +80,9 @@ async def _resetup_platform( # If its an entity platform, we use the entity_platform # async_reset method - platform = async_get_platform(hass, integration_name, integration_platform) + platform = async_get_platform_without_config_entry( + hass, integration_name, integration_platform + ) if platform: await _async_reconfig_platform(platform, root_config[integration_platform]) return @@ -137,11 +139,13 @@ async def async_integration_yaml_config( @callback -def async_get_platform( +def async_get_platform_without_config_entry( hass: HomeAssistantType, integration_name: str, integration_platform_name: str ) -> Optional[EntityPlatform]: - """Find an existing platform.""" + """Find an existing platform that is not a config entry.""" for integration_platform in async_get_platforms(hass, integration_name): + if integration_platform.config_entry is not None: + continue if integration_platform.domain == integration_platform_name: platform: EntityPlatform = integration_platform return platform diff --git a/tests/helpers/test_reload.py b/tests/helpers/test_reload.py index 25844151533..3ed8d17b3f4 100644 --- a/tests/helpers/test_reload.py +++ b/tests/helpers/test_reload.py @@ -7,8 +7,9 @@ import pytest from homeassistant import config from homeassistant.const import SERVICE_RELOAD from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.entity_platform import async_get_platforms from homeassistant.helpers.reload import ( - async_get_platform, + async_get_platform_without_config_entry, async_integration_yaml_config, async_reload_integration_platforms, async_setup_reload_service, @@ -52,7 +53,7 @@ async def test_reload_platform(hass): assert f"{DOMAIN}.{PLATFORM}" in hass.config.components assert len(setup_called) == 1 - platform = async_get_platform(hass, PLATFORM, DOMAIN) + platform = async_get_platform_without_config_entry(hass, PLATFORM, DOMAIN) assert platform.platform_name == PLATFORM assert platform.domain == DOMAIN @@ -66,6 +67,11 @@ async def test_reload_platform(hass): assert len(setup_called) == 2 + existing_platforms = async_get_platforms(hass, PLATFORM) + for existing_platform in existing_platforms: + existing_platform.config_entry = "abc" + assert not async_get_platform_without_config_entry(hass, PLATFORM, DOMAIN) + async def test_setup_reload_service(hass): """Test setting up a reload service.""" From 4eacf3f6c0b911c282ea1ed2d03f255983a822f0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Oct 2020 22:54:50 +0000 Subject: [PATCH 039/118] Bumped version to 0.117.0b4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index d9f7d4003ac..d51fa29a2fb 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "0b3" +PATCH_VERSION = "0b4" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 9c6351c1b397b9066d3ef44d3ce660db68c3235a Mon Sep 17 00:00:00 2001 From: Maksym D Date: Mon, 26 Oct 2020 08:38:37 +0100 Subject: [PATCH 040/118] Change AfterShip polling interval from 5m to 15m (#42360) This change will prevent reaching API requests limit of AfterShip free-tier account. --- homeassistant/components/aftership/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/aftership/sensor.py b/homeassistant/components/aftership/sensor.py index 83ab52499da..07267c1185d 100644 --- a/homeassistant/components/aftership/sensor.py +++ b/homeassistant/components/aftership/sensor.py @@ -31,7 +31,7 @@ UPDATE_TOPIC = f"{DOMAIN}_update" ICON = "mdi:package-variant-closed" -MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5) +MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15) SERVICE_ADD_TRACKING = "add_tracking" SERVICE_REMOVE_TRACKING = "remove_tracking" From 1fb18580b2573f7997e786027502bce93d815deb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2020 11:30:58 +0100 Subject: [PATCH 041/118] Store original result on template results (#42391) * Store original result on template results * Fix shell command test --- homeassistant/helpers/config_validation.py | 6 +- homeassistant/helpers/template.py | 80 +++++++++++++++++---- tests/components/shell_command/test_init.py | 2 +- tests/helpers/test_config_validation.py | 17 ++++- tests/helpers/test_template.py | 22 ++++++ 5 files changed, 108 insertions(+), 19 deletions(-) diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index e41e26e5e0f..190cee5e050 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -486,7 +486,11 @@ def string(value: Any) -> str: """Coerce value to string, except for None.""" if value is None: raise vol.Invalid("string value is None") - if isinstance(value, (list, dict)): + + if isinstance(value, template_helper.ResultWrapper): + value = value.render_result + + elif isinstance(value, (list, dict)): raise vol.Invalid("value should be a string") return str(value) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 8920060d8e2..9ffc6593955 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -11,7 +11,7 @@ import math from operator import attrgetter import random import re -from typing import Any, Generator, Iterable, List, Optional, Union +from typing import Any, Dict, Generator, Iterable, List, Optional, Type, Union from urllib.parse import urlencode as urllib_urlencode import weakref @@ -124,6 +124,43 @@ def is_template_string(maybe_template: str) -> bool: return _RE_JINJA_DELIMITERS.search(maybe_template) is not None +class ResultWrapper: + """Result wrapper class to store render result.""" + + render_result: str + + +def gen_result_wrapper(kls): + """Generate a result wrapper.""" + + class Wrapper(kls, ResultWrapper): + """Wrapper of a kls that can store render_result.""" + + def __init__(self, value: kls, render_result: str) -> None: + super().__init__(value) + self.render_result = render_result + + return Wrapper + + +class TupleWrapper(tuple, ResultWrapper): + """Wrap a tuple.""" + + def __new__(cls, value: tuple, render_result: str) -> "TupleWrapper": + """Create a new tuple class.""" + return super().__new__(cls, tuple(value)) + + def __init__(self, value: tuple, render_result: str): + """Initialize a new tuple class.""" + self.render_result = render_result + + +RESULT_WRAPPERS: Dict[Type, Type] = { + kls: gen_result_wrapper(kls) for kls in (list, dict, set) +} +RESULT_WRAPPERS[tuple] = TupleWrapper + + def extract_entities( hass: HomeAssistantType, template: Optional[str], @@ -285,7 +322,7 @@ class Template: if not isinstance(template, str): raise TypeError("Expected template to be a string") - self.template: str = template + self.template: str = template.strip() self._compiled_code = None self._compiled = None self.hass = hass @@ -322,7 +359,9 @@ class Template: def render(self, variables: TemplateVarsType = None, **kwargs: Any) -> Any: """Render given template.""" if self.is_static: - return self.template.strip() + if self.hass.config.legacy_templates: + return self.template + return self._parse_result(self.template) if variables is not None: kwargs.update(variables) @@ -338,7 +377,9 @@ class Template: This method must be run in the event loop. """ if self.is_static: - return self.template.strip() + if self.hass.config.legacy_templates: + return self.template + return self._parse_result(self.template) compiled = self._compiled or self._ensure_compiled() @@ -352,18 +393,27 @@ class Template: render_result = render_result.strip() - if not self.hass.config.legacy_templates: - try: - result = literal_eval(render_result) + if self.hass.config.legacy_templates: + return render_result - # If the literal_eval result is a string, use the original - # render, by not returning right here. The evaluation of strings - # resulting in strings impacts quotes, to avoid unexpected - # output; use the original render instead of the evaluated one. - if not isinstance(result, str): - return result - except (ValueError, SyntaxError, MemoryError): - pass + return self._parse_result(render_result) + + def _parse_result(self, render_result: str) -> Any: + """Parse the result.""" + try: + result = literal_eval(render_result) + + if type(result) in RESULT_WRAPPERS: + result = RESULT_WRAPPERS[type(result)](result, render_result) + + # If the literal_eval result is a string, use the original + # render, by not returning right here. The evaluation of strings + # resulting in strings impacts quotes, to avoid unexpected + # output; use the original render instead of the evaluated one. + if not isinstance(result, str): + return result + except (ValueError, SyntaxError, MemoryError): + pass return render_result diff --git a/tests/components/shell_command/test_init.py b/tests/components/shell_command/test_init.py index f5ad37cc617..440375438c1 100644 --- a/tests/components/shell_command/test_init.py +++ b/tests/components/shell_command/test_init.py @@ -174,7 +174,7 @@ async def test_do_no_run_forever(hass, caplog): assert await async_setup_component( hass, shell_command.DOMAIN, - {shell_command.DOMAIN: {"test_service": "sleep 10000"}}, + {shell_command.DOMAIN: {"test_service": "sleep 10000s"}}, ) await hass.async_block_till_done() diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index d8956c143df..693785f4ea7 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -9,7 +9,7 @@ import pytest import voluptuous as vol import homeassistant -import homeassistant.helpers.config_validation as cv +from homeassistant.helpers import config_validation as cv, template from tests.async_mock import Mock, patch @@ -365,7 +365,7 @@ def test_slug(): schema(value) -def test_string(): +def test_string(hass): """Test string validation.""" schema = vol.Schema(cv.string) @@ -381,6 +381,19 @@ def test_string(): for value in (True, 1, "hello"): schema(value) + # Test template support + for text, native in ( + ("[1, 2]", [1, 2]), + ("{1, 2}", {1, 2}), + ("(1, 2)", (1, 2)), + ('{"hello": True}', {"hello": True}), + ): + tpl = template.Template(text, hass) + result = tpl.async_render() + assert isinstance(result, template.ResultWrapper) + assert result == native + assert schema(result) == text + def test_string_with_no_html(): """Test string with no html validation.""" diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 33f0e3f7f04..86e17f8cb0b 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -2651,3 +2651,25 @@ async def test_legacy_templates(hass): template.Template("{{ states.sensor.temperature.state }}", hass).async_render() == "12" ) + + +async def test_is_static_still_ast_evals(hass): + """Test is_static still convers 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): + """Test result wrappers.""" + for text, native in ( + ("[1, 2]", [1, 2]), + ("{1, 2}", {1, 2}), + ("(1, 2)", (1, 2)), + ('{"hello": True}', {"hello": True}), + ): + tpl = template.Template(text, hass) + result = tpl.async_render() + assert isinstance(result, template.ResultWrapper) + assert result == native + assert result.render_result == text From c00ab762d3e57763217e11adc5ed5412ccbb4ac9 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 26 Oct 2020 14:58:36 +0100 Subject: [PATCH 042/118] Bump hatasmota to 0.0.23 (#42394) --- .../components/tasmota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tasmota/test_light.py | 32 ++++++++++++++++++- tests/components/tasmota/test_switch.py | 23 ++++++++++++- 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index bdd25f4466a..8cc3006c63a 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota (beta)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.0.22"], + "requirements": ["hatasmota==0.0.23"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"] diff --git a/requirements_all.txt b/requirements_all.txt index ba6d0edb546..5686eb6df63 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -732,7 +732,7 @@ hass-nabucasa==0.37.1 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.0.22 +hatasmota==0.0.23 # homeassistant.components.jewish_calendar hdate==0.9.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6d010c5e213..af83a664369 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -367,7 +367,7 @@ hangups==0.4.11 hass-nabucasa==0.37.1 # homeassistant.components.tasmota -hatasmota==0.0.22 +hatasmota==0.0.23 # homeassistant.components.jewish_calendar hdate==0.9.5 diff --git a/tests/components/tasmota/test_light.py b/tests/components/tasmota/test_light.py index 871d55a4d73..f9d839516e8 100644 --- a/tests/components/tasmota/test_light.py +++ b/tests/components/tasmota/test_light.py @@ -3,6 +3,11 @@ import copy import json from hatasmota.const import CONF_MAC +from hatasmota.utils import ( + get_topic_stat_result, + get_topic_tele_state, + get_topic_tele_will, +) from homeassistant.components import light from homeassistant.components.light import ( @@ -310,6 +315,16 @@ async def test_controlling_state_via_mqtt_on_off(hass, mqtt_mock, setup_tasmota) state = hass.states.get("light.test") assert state.state == STATE_OFF + async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/RESULT", '{"POWER":"ON"}') + + state = hass.states.get("light.test") + assert state.state == STATE_ON + + async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/RESULT", '{"POWER":"OFF"}') + + state = hass.states.get("light.test") + assert state.state == STATE_OFF + async def test_controlling_state_via_mqtt_ct(hass, mqtt_mock, setup_tasmota): """Test state update via MQTT.""" @@ -432,6 +447,16 @@ async def test_controlling_state_via_mqtt_rgbww(hass, mqtt_mock, setup_tasmota): assert state.state == STATE_ON assert state.attributes.get("effect") == "Cycle down" + async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/RESULT", '{"POWER":"ON"}') + + state = hass.states.get("light.test") + assert state.state == STATE_ON + + async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/RESULT", '{"POWER":"OFF"}') + + state = hass.states.get("light.test") + assert state.state == STATE_OFF + async def test_sending_mqtt_commands_on_off(hass, mqtt_mock, setup_tasmota): """Test the sending MQTT commands.""" @@ -914,8 +939,13 @@ async def test_entity_id_update_subscriptions(hass, mqtt_mock, setup_tasmota): config = copy.deepcopy(DEFAULT_CONFIG) config["rl"][0] = 2 config["lt_st"] = 1 # 1 channel light (Dimmer) + topics = [ + get_topic_stat_result(config), + get_topic_tele_state(config), + get_topic_tele_will(config), + ] await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, light.DOMAIN, config + hass, mqtt_mock, light.DOMAIN, config, topics ) diff --git a/tests/components/tasmota/test_switch.py b/tests/components/tasmota/test_switch.py index 564dc2f2868..208026c2de5 100644 --- a/tests/components/tasmota/test_switch.py +++ b/tests/components/tasmota/test_switch.py @@ -2,6 +2,12 @@ import copy import json +from hatasmota.utils import ( + get_topic_stat_result, + get_topic_tele_state, + get_topic_tele_will, +) + from homeassistant.components import switch from homeassistant.components.tasmota.const import DEFAULT_PREFIX from homeassistant.const import ATTR_ASSUMED_STATE, STATE_OFF, STATE_ON @@ -56,6 +62,16 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): state = hass.states.get("switch.test") assert state.state == STATE_OFF + async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/RESULT", '{"POWER":"ON"}') + + state = hass.states.get("switch.test") + assert state.state == STATE_ON + + async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/RESULT", '{"POWER":"OFF"}') + + state = hass.states.get("switch.test") + assert state.state == STATE_OFF + async def test_sending_mqtt_commands(hass, mqtt_mock, setup_tasmota): """Test the sending MQTT commands.""" @@ -211,8 +227,13 @@ async def test_entity_id_update_subscriptions(hass, mqtt_mock, setup_tasmota): """Test MQTT subscriptions are managed when entity_id is updated.""" config = copy.deepcopy(DEFAULT_CONFIG) config["rl"][0] = 1 + topics = [ + get_topic_stat_result(config), + get_topic_tele_state(config), + get_topic_tele_will(config), + ] await help_test_entity_id_update_subscriptions( - hass, mqtt_mock, switch.DOMAIN, config + hass, mqtt_mock, switch.DOMAIN, config, topics ) From bcd2ae78a0ed54522e1d0fa827ec5866698422da Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Mon, 26 Oct 2020 16:26:21 +0200 Subject: [PATCH 043/118] Bump libhdate depndency (#42396) * Bump libhdate depndency This version fixes an important bug found by @amitfin in py-libhdate/py-libhdate#78 * Update reqs Co-authored-by: Paulus Schoutsen --- homeassistant/components/jewish_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/jewish_calendar/manifest.json b/homeassistant/components/jewish_calendar/manifest.json index 9f0d55433f0..500d98dbe9f 100644 --- a/homeassistant/components/jewish_calendar/manifest.json +++ b/homeassistant/components/jewish_calendar/manifest.json @@ -2,6 +2,6 @@ "domain": "jewish_calendar", "name": "Jewish Calendar", "documentation": "https://www.home-assistant.io/integrations/jewish_calendar", - "requirements": ["hdate==0.9.5"], + "requirements": ["hdate==0.9.12"], "codeowners": ["@tsvi"] } diff --git a/requirements_all.txt b/requirements_all.txt index 5686eb6df63..7b4478d9c9b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -735,7 +735,7 @@ hass_splunk==0.1.1 hatasmota==0.0.23 # homeassistant.components.jewish_calendar -hdate==0.9.5 +hdate==0.9.12 # homeassistant.components.heatmiser heatmiserV3==1.1.18 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index af83a664369..21d331f3f35 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -370,7 +370,7 @@ hass-nabucasa==0.37.1 hatasmota==0.0.23 # homeassistant.components.jewish_calendar -hdate==0.9.5 +hdate==0.9.12 # homeassistant.components.here_travel_time herepy==2.0.0 From 934ac73ae5755edb40aefdd0552228155a0e370a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2020 13:36:03 +0100 Subject: [PATCH 044/118] Fix CI (#42397) --- homeassistant/helpers/event.py | 2 +- homeassistant/helpers/template.py | 6 +++++- tests/helpers/test_event.py | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index b4caccf5311..2044dbd7856 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -988,7 +988,7 @@ def async_track_template_result( action: TrackTemplateResultListener, raise_on_template_error: bool = False, ) -> _TrackTemplateResultInfo: - """Add a listener that fires when a the result of a template changes. + """Add a listener that fires when the result of a template changes. The action will fire with the initial result from the template, and then whenever the output from the template changes. The template will diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 9ffc6593955..de131d8dc5c 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -146,10 +146,14 @@ def gen_result_wrapper(kls): class TupleWrapper(tuple, ResultWrapper): """Wrap a tuple.""" + # This is all magic to be allowed to subclass a tuple. + def __new__(cls, value: tuple, render_result: str) -> "TupleWrapper": """Create a new tuple class.""" return super().__new__(cls, tuple(value)) + # pylint: disable=super-init-not-called + def __init__(self, value: tuple, render_result: str): """Initialize a new tuple class.""" self.render_result = render_result @@ -398,7 +402,7 @@ class Template: return self._parse_result(render_result) - def _parse_result(self, render_result: str) -> Any: + def _parse_result(self, render_result: str) -> Any: # pylint: disable=no-self-use """Parse the result.""" try: result = literal_eval(render_result) diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index 9f6e66048ff..8add5cc1cf3 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -1771,7 +1771,7 @@ async def test_specifically_referenced_entity_is_not_rate_limited(hass): async def test_track_two_templates_with_different_rate_limits(hass): """Test two templates with different rate limits.""" - template_one = Template("{{ states | count }} ", hass) + template_one = Template("{{ (states | count) + 0 }}", hass) template_five = Template("{{ states | count }}", hass) refresh_runs = { From b7c958719ec1291c325cf6526977a004482f0be3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2020 16:46:26 +0100 Subject: [PATCH 045/118] Fix MQTT publish from a script with templates (#42398) --- homeassistant/components/mqtt/__init__.py | 6 +- homeassistant/components/snips/__init__.py | 2 +- .../mqtt/test_alarm_control_panel.py | 8 +-- tests/components/mqtt/test_climate.py | 13 ++-- tests/components/mqtt/test_cover.py | 62 +++++++++++++------ tests/components/mqtt/test_init.py | 45 +++++++++++++- tests/components/mqtt/test_light.py | 34 +++++----- 7 files changed, 126 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index d7d11e6f49a..09655ababda 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -322,7 +322,7 @@ MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( MQTT_PUBLISH_SCHEMA = vol.Schema( { vol.Required(ATTR_TOPIC): valid_publish_topic, - vol.Exclusive(ATTR_PAYLOAD, CONF_PAYLOAD): object, + 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_RETAIN, default=DEFAULT_RETAIN): cv.boolean, @@ -567,7 +567,9 @@ async def async_setup_entry(hass, entry): retain: bool = call.data[ATTR_RETAIN] if payload_template is not None: try: - payload = template.Template(payload_template, hass).async_render() + payload = template.Template(payload_template, hass).async_render( + parse_result=False + ) except template.jinja2.TemplateError as exc: _LOGGER.error( "Unable to publish to %s: rendering payload template of " diff --git a/homeassistant/components/snips/__init__.py b/homeassistant/components/snips/__init__.py index 618190e8e70..11f732a3bd5 100644 --- a/homeassistant/components/snips/__init__.py +++ b/homeassistant/components/snips/__init__.py @@ -98,7 +98,7 @@ async def async_setup(hass, config): for site_id in site_ids: payload = json.dumps({"siteId": site_id}) hass.components.mqtt.async_publish( - FEEDBACK_ON_TOPIC, None, qos=0, retain=False + FEEDBACK_ON_TOPIC, "", qos=0, retain=False ) hass.components.mqtt.async_publish( topic, payload, qos=int(state), retain=state diff --git a/tests/components/mqtt/test_alarm_control_panel.py b/tests/components/mqtt/test_alarm_control_panel.py index b6c162eb87e..524448e5839 100644 --- a/tests/components/mqtt/test_alarm_control_panel.py +++ b/tests/components/mqtt/test_alarm_control_panel.py @@ -397,9 +397,9 @@ async def test_disarm_publishes_mqtt_with_template(hass, mqtt_mock): """ config = copy.deepcopy(DEFAULT_CONFIG_CODE) config[alarm_control_panel.DOMAIN]["code"] = "0123" - config[alarm_control_panel.DOMAIN]["command_template"] = ( - '{"action":"{{ action }}",' '"code":"{{ code }}"}' - ) + config[alarm_control_panel.DOMAIN][ + "command_template" + ] = '{"action":"{{ action }}","code":"{{ code }}"}' assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -409,7 +409,7 @@ async def test_disarm_publishes_mqtt_with_template(hass, mqtt_mock): await common.async_alarm_disarm(hass, "0123") mqtt_mock.async_publish.assert_called_once_with( - "alarm/command", {"action": "DISARM", "code": "0123"}, 0, False + "alarm/command", '{"action":"DISARM","code":"0123"}', 0, False ) diff --git a/tests/components/mqtt/test_climate.py b/tests/components/mqtt/test_climate.py index d60af211d71..3c629a82012 100644 --- a/tests/components/mqtt/test_climate.py +++ b/tests/components/mqtt/test_climate.py @@ -322,7 +322,9 @@ async def test_set_target_temperature(hass, mqtt_mock): await common.async_set_temperature(hass, temperature=47, entity_id=ENTITY_CLIMATE) state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("temperature") == 47 - mqtt_mock.async_publish.assert_called_once_with("temperature-topic", 47, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "temperature-topic", "47.0", 0, False + ) # also test directly supplying the operation mode to set_temperature mqtt_mock.async_publish.reset_mock() @@ -333,7 +335,10 @@ async def test_set_target_temperature(hass, mqtt_mock): assert state.state == "cool" assert state.attributes.get("temperature") == 21 mqtt_mock.async_publish.assert_has_calls( - [call("mode-topic", "cool", 0, False), call("temperature-topic", 21, 0, False)] + [ + call("mode-topic", "cool", 0, False), + call("temperature-topic", "21.0", 0, False), + ] ) mqtt_mock.async_publish.reset_mock() @@ -372,8 +377,8 @@ async def test_set_target_temperature_low_high(hass, mqtt_mock): state = hass.states.get(ENTITY_CLIMATE) assert state.attributes.get("target_temp_low") == 20 assert state.attributes.get("target_temp_high") == 23 - mqtt_mock.async_publish.assert_any_call("temperature-low-topic", 20, 0, False) - mqtt_mock.async_publish.assert_any_call("temperature-high-topic", 23, 0, False) + mqtt_mock.async_publish.assert_any_call("temperature-low-topic", "20.0", 0, False) + mqtt_mock.async_publish.assert_any_call("temperature-high-topic", "23.0", 0, False) async def test_set_target_temperature_low_highpessimistic(hass, mqtt_mock): diff --git a/tests/components/mqtt/test_cover.py b/tests/components/mqtt/test_cover.py index dd879338a41..629d9674b22 100644 --- a/tests/components/mqtt/test_cover.py +++ b/tests/components/mqtt/test_cover.py @@ -702,7 +702,9 @@ async def test_set_position_templated(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("set-position-topic", 38, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "set-position-topic", "38", 0, False + ) async def test_set_position_untemplated(hass, mqtt_mock): @@ -732,7 +734,7 @@ async def test_set_position_untemplated(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("position-topic", 62, 0, False) + mqtt_mock.async_publish.assert_called_once_with("position-topic", "62", 0, False) async def test_set_position_untemplated_custom_percentage_range(hass, mqtt_mock): @@ -764,7 +766,7 @@ async def test_set_position_untemplated_custom_percentage_range(hass, mqtt_mock) blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("position-topic", 62, 0, False) + mqtt_mock.async_publish.assert_called_once_with("position-topic", "62", 0, False) async def test_no_command_topic(hass, mqtt_mock): @@ -896,7 +898,9 @@ async def test_tilt_via_invocation_defaults(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 100, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "100", 0, False + ) mqtt_mock.async_publish.reset_mock() await hass.services.async_call( @@ -906,7 +910,7 @@ async def test_tilt_via_invocation_defaults(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 0, 0, False) + mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", "0", 0, False) mqtt_mock.async_publish.reset_mock() # Close tilt status would be received from device when non-optimistic @@ -924,7 +928,9 @@ async def test_tilt_via_invocation_defaults(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 100, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "100", 0, False + ) mqtt_mock.async_publish.reset_mock() # Open tilt status would be received from device when non-optimistic @@ -942,7 +948,7 @@ async def test_tilt_via_invocation_defaults(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 0, 0, False) + mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", "0", 0, False) async def test_tilt_given_value(hass, mqtt_mock): @@ -976,7 +982,9 @@ async def test_tilt_given_value(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 80, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "80", 0, False + ) mqtt_mock.async_publish.reset_mock() await hass.services.async_call( @@ -986,7 +994,9 @@ async def test_tilt_given_value(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 25, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "25", 0, False + ) mqtt_mock.async_publish.reset_mock() # Close tilt status would be received from device when non-optimistic @@ -1004,7 +1014,9 @@ async def test_tilt_given_value(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 80, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "80", 0, False + ) mqtt_mock.async_publish.reset_mock() # Open tilt status would be received from device when non-optimistic @@ -1022,7 +1034,9 @@ async def test_tilt_given_value(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 25, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "25", 0, False + ) async def test_tilt_given_value_optimistic(hass, mqtt_mock): @@ -1062,7 +1076,9 @@ async def test_tilt_given_value_optimistic(hass, mqtt_mock): ] assert current_cover_tilt_position == 80 - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 80, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "80", 0, False + ) mqtt_mock.async_publish.reset_mock() await hass.services.async_call( @@ -1077,7 +1093,9 @@ async def test_tilt_given_value_optimistic(hass, mqtt_mock): ] assert current_cover_tilt_position == 25 - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 25, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "25", 0, False + ) async def test_tilt_given_value_altered_range(hass, mqtt_mock): @@ -1119,7 +1137,9 @@ async def test_tilt_given_value_altered_range(hass, mqtt_mock): ] assert current_cover_tilt_position == 50 - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 25, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "25", 0, False + ) mqtt_mock.async_publish.reset_mock() await hass.services.async_call( @@ -1134,7 +1154,7 @@ async def test_tilt_given_value_altered_range(hass, mqtt_mock): ] assert current_cover_tilt_position == 0 - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 0, 0, False) + mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", "0", 0, False) mqtt_mock.async_publish.reset_mock() await hass.services.async_call( @@ -1149,7 +1169,9 @@ async def test_tilt_given_value_altered_range(hass, mqtt_mock): ] assert current_cover_tilt_position == 50 - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 25, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "25", 0, False + ) async def test_tilt_via_topic(hass, mqtt_mock): @@ -1353,7 +1375,9 @@ async def test_tilt_position(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 50, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "50", 0, False + ) async def test_tilt_position_altered_range(hass, mqtt_mock): @@ -1389,7 +1413,9 @@ async def test_tilt_position_altered_range(hass, mqtt_mock): blocking=True, ) - mqtt_mock.async_publish.assert_called_once_with("tilt-command-topic", 25, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "tilt-command-topic", "25", 0, False + ) async def test_find_percentage_in_range_defaults(hass, mqtt_mock): diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 7b634714a5b..7a833a72d51 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -122,7 +122,7 @@ async def test_service_call_with_template_payload_renders_template(hass, mqtt_mo mqtt.async_publish_template(hass, "test/topic", "{{ 1+1 }}") await hass.async_block_till_done() assert mqtt_mock.async_publish.called - assert mqtt_mock.async_publish.call_args[0][1] == 2 + assert mqtt_mock.async_publish.call_args[0][1] == "2" async def test_service_call_with_payload_doesnt_render_template(hass, mqtt_mock): @@ -1497,3 +1497,46 @@ async def test_debug_info_qos_retain(hass, mqtt_mock): "time": start_dt, "topic": "sensor/abc", } in debug_info_data["entities"][0]["subscriptions"][0]["messages"] + + +async def test_publish_json_from_template(hass, mqtt_mock): + """Test the publishing of call to services.""" + test_str = "{'valid': 'python', 'invalid': 'json'}" + + await async_setup_component( + hass, + "script", + { + "script": { + "test_script_payload": { + "sequence": { + "service": "mqtt.publish", + "data": {"topic": "test-topic", "payload": test_str}, + } + }, + "test_script_payload_template": { + "sequence": { + "service": "mqtt.publish", + "data": {"topic": "test-topic", "payload_template": test_str}, + } + }, + } + }, + ) + + await hass.services.async_call("script", "test_script_payload", blocking=True) + await hass.async_block_till_done() + + assert mqtt_mock.async_publish.called + assert mqtt_mock.async_publish.call_args[0][1] == test_str + + mqtt_mock.async_publish.reset_mock() + assert not mqtt_mock.async_publish.called + + await hass.services.async_call( + "script", "test_script_payload_template", blocking=True + ) + await hass.async_block_till_done() + + assert mqtt_mock.async_publish.called + assert mqtt_mock.async_publish.call_args[0][1] == test_str diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index 62291fd18b7..102d1726ccc 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -760,11 +760,11 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock): [ call("test_light_rgb/set", "on", 2, False), call("test_light_rgb/rgb/set", "255,128,0", 2, False), - call("test_light_rgb/brightness/set", 50, 2, False), + call("test_light_rgb/brightness/set", "50", 2, False), call("test_light_rgb/hs/set", "359.0,78.0", 2, False), - call("test_light_rgb/white_value/set", 80, 2, False), + call("test_light_rgb/white_value/set", "80", 2, False), call("test_light_rgb/xy/set", "0.14,0.131", 2, False), - call("test_light_rgb/color_temp/set", 125, 2, False), + call("test_light_rgb/color_temp/set", "125", 2, False), ], any_order=True, ) @@ -841,7 +841,7 @@ async def test_sending_mqtt_color_temp_command_with_template(hass, mqtt_mock): mqtt_mock.async_publish.assert_has_calls( [ call("test_light_color_temp/set", "on", 0, False), - call("test_light_color_temp/color_temp/set", 10, 0, False), + call("test_light_color_temp/color_temp/set", "10", 0, False), ], any_order=True, ) @@ -877,7 +877,7 @@ async def test_on_command_first(hass, mqtt_mock): mqtt_mock.async_publish.assert_has_calls( [ call("test_light/set", "ON", 0, False), - call("test_light/bright", 50, 0, False), + call("test_light/bright", "50", 0, False), ], ) mqtt_mock.async_publish.reset_mock() @@ -911,7 +911,7 @@ async def test_on_command_last(hass, mqtt_mock): # test_light/set: 'ON' mqtt_mock.async_publish.assert_has_calls( [ - call("test_light/bright", 50, 0, False), + call("test_light/bright", "50", 0, False), call("test_light/set", "ON", 0, False), ], ) @@ -946,7 +946,9 @@ async def test_on_command_brightness(hass, mqtt_mock): # Should get the following MQTT messages. # test_light/bright: 255 - mqtt_mock.async_publish.assert_called_once_with("test_light/bright", 255, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "test_light/bright", "255", 0, False + ) mqtt_mock.async_publish.reset_mock() await common.async_turn_off(hass, "light.test") @@ -957,7 +959,7 @@ async def test_on_command_brightness(hass, mqtt_mock): # Turn on w/ brightness await common.async_turn_on(hass, "light.test", brightness=50) - mqtt_mock.async_publish.assert_called_once_with("test_light/bright", 50, 0, False) + mqtt_mock.async_publish.assert_called_once_with("test_light/bright", "50", 0, False) mqtt_mock.async_publish.reset_mock() await common.async_turn_off(hass, "light.test") @@ -969,7 +971,7 @@ async def test_on_command_brightness(hass, mqtt_mock): mqtt_mock.async_publish.assert_has_calls( [ call("test_light/rgb", "255,128,0", 0, False), - call("test_light/bright", 50, 0, False), + call("test_light/bright", "50", 0, False), ], any_order=True, ) @@ -1000,7 +1002,9 @@ async def test_on_command_brightness_scaled(hass, mqtt_mock): # Should get the following MQTT messages. # test_light/bright: 100 - mqtt_mock.async_publish.assert_called_once_with("test_light/bright", 100, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "test_light/bright", "100", 0, False + ) mqtt_mock.async_publish.reset_mock() await common.async_turn_off(hass, "light.test") @@ -1011,19 +1015,21 @@ async def test_on_command_brightness_scaled(hass, mqtt_mock): # Turn on w/ brightness await common.async_turn_on(hass, "light.test", brightness=50) - mqtt_mock.async_publish.assert_called_once_with("test_light/bright", 20, 0, False) + mqtt_mock.async_publish.assert_called_once_with("test_light/bright", "20", 0, False) mqtt_mock.async_publish.reset_mock() # Turn on w/ max brightness await common.async_turn_on(hass, "light.test", brightness=255) - mqtt_mock.async_publish.assert_called_once_with("test_light/bright", 100, 0, False) + mqtt_mock.async_publish.assert_called_once_with( + "test_light/bright", "100", 0, False + ) mqtt_mock.async_publish.reset_mock() # Turn on w/ min brightness await common.async_turn_on(hass, "light.test", brightness=1) - mqtt_mock.async_publish.assert_called_once_with("test_light/bright", 1, 0, False) + mqtt_mock.async_publish.assert_called_once_with("test_light/bright", "1", 0, False) mqtt_mock.async_publish.reset_mock() await common.async_turn_off(hass, "light.test") @@ -1035,7 +1041,7 @@ async def test_on_command_brightness_scaled(hass, mqtt_mock): mqtt_mock.async_publish.assert_has_calls( [ call("test_light/rgb", "255,128,0", 0, False), - call("test_light/bright", 1, 0, False), + call("test_light/bright", "1", 0, False), ], any_order=True, ) From f716b7714b0a27cc99406bf57a698b4245497375 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 26 Oct 2020 16:01:09 +0100 Subject: [PATCH 046/118] Allow skip parsing template result (#42401) --- homeassistant/components/api/__init__.py | 2 +- .../components/shell_command/__init__.py | 4 ++- homeassistant/components/template/cover.py | 7 ++++- homeassistant/components/template/fan.py | 7 ++++- homeassistant/components/template/light.py | 22 +++++++++------ homeassistant/helpers/condition.py | 5 +++- homeassistant/helpers/template.py | 28 ++++++++++++------- tests/components/shell_command/test_init.py | 2 +- tests/helpers/test_condition.py | 23 +++++++++++++++ tests/helpers/test_template.py | 22 +++++++++++++++ 10 files changed, 98 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/api/__init__.py b/homeassistant/components/api/__init__.py index 779df785164..72fb2636067 100644 --- a/homeassistant/components/api/__init__.py +++ b/homeassistant/components/api/__init__.py @@ -410,7 +410,7 @@ class APITemplateView(HomeAssistantView): try: data = await request.json() tpl = template.Template(data["template"], request.app["hass"]) - return str(tpl.async_render(data.get("variables"))) + return tpl.async_render(variables=data.get("variables"), parse_result=False) except (ValueError, TemplateError) as ex: return self.json_message( f"Error rendering template: {ex}", HTTP_BAD_REQUEST diff --git a/homeassistant/components/shell_command/__init__.py b/homeassistant/components/shell_command/__init__.py index bce980035dc..0d221cb6b0b 100644 --- a/homeassistant/components/shell_command/__init__.py +++ b/homeassistant/components/shell_command/__init__.py @@ -45,7 +45,9 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool: if args_compiled: try: - rendered_args = args_compiled.async_render(service.data) + rendered_args = args_compiled.async_render( + variables=service.data, parse_result=False + ) except TemplateError as ex: _LOGGER.exception("Error rendering command template: %s", ex) return diff --git a/homeassistant/components/template/cover.py b/homeassistant/components/template/cover.py index 7c35663bb5f..cc91bdabd7d 100644 --- a/homeassistant/components/template/cover.py +++ b/homeassistant/components/template/cover.py @@ -39,6 +39,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.script import Script +from homeassistant.helpers.template import ResultWrapper from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS from .template_entity import TemplateEntity @@ -258,7 +259,11 @@ class CoverTemplate(TemplateEntity, CoverEntity): self._position = None return - state = str(result).lower() + if isinstance(result, ResultWrapper): + state = result.render_result.lower() + else: + state = str(result).lower() + if state in _VALID_STATES: if state in ("true", STATE_OPEN): self._position = 100 diff --git a/homeassistant/components/template/fan.py b/homeassistant/components/template/fan.py index f0b22fb46b3..e5b3569e14b 100644 --- a/homeassistant/components/template/fan.py +++ b/homeassistant/components/template/fan.py @@ -34,6 +34,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.script import Script +from homeassistant.helpers.template import ResultWrapper from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS from .template_entity import TemplateEntity @@ -367,7 +368,11 @@ class TemplateFan(TemplateEntity, FanEntity): @callback def _update_speed(self, speed): # Validate speed - speed = str(speed) + if isinstance(speed, ResultWrapper): + speed = speed.render_result + else: + speed = str(speed) + if speed in self._speed_list: self._speed = speed elif speed in [STATE_UNAVAILABLE, STATE_UNKNOWN]: diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 5d6c1befd4a..84939beecad 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -33,6 +33,7 @@ from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.script import Script +from homeassistant.helpers.template import ResultWrapper from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS from .template_entity import TemplateEntity @@ -405,23 +406,28 @@ class LightTemplate(TemplateEntity, LightEntity): def _update_state(self, result): """Update the state from the template.""" - if isinstance(result, TemplateError): + if isinstance(result, (TemplateError, ResultWrapper)): # This behavior is legacy self._state = False if not self._availability_template: self._available = True return + if isinstance(result, bool): + self._state = result + return + state = str(result).lower() if state in _VALID_STATES: self._state = state in ("true", STATE_ON) - else: - _LOGGER.error( - "Received invalid light is_on state: %s. Expected: %s", - state, - ", ".join(_VALID_STATES), - ) - self._state = None + return + + _LOGGER.error( + "Received invalid light is_on state: %s. Expected: %s", + state, + ", ".join(_VALID_STATES), + ) + self._state = None @callback def _update_temperature(self, render): diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index 57271ccaf81..9cfa6c00996 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -461,7 +461,10 @@ def async_template( if isinstance(value, bool): return value - return str(value).lower() == "true" + if isinstance(value, str): + return value.lower() == "true" + + return False def async_template_from_config( diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index de131d8dc5c..75846100161 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -4,7 +4,7 @@ import asyncio import base64 import collections.abc from datetime import datetime, timedelta -from functools import wraps +from functools import partial, wraps import json import logging import math @@ -360,28 +360,36 @@ class Template: return extract_entities(self.hass, self.template, variables) - def render(self, variables: TemplateVarsType = None, **kwargs: Any) -> Any: + def render( + self, + variables: TemplateVarsType = None, + parse_result: bool = True, + **kwargs: Any, + ) -> Any: """Render given template.""" if self.is_static: - if self.hass.config.legacy_templates: + if self.hass.config.legacy_templates or not parse_result: return self.template return self._parse_result(self.template) - if variables is not None: - kwargs.update(variables) - return run_callback_threadsafe( - self.hass.loop, self.async_render, kwargs + self.hass.loop, + partial(self.async_render, variables, parse_result, **kwargs), ).result() @callback - def async_render(self, variables: TemplateVarsType = None, **kwargs: Any) -> Any: + def async_render( + self, + variables: TemplateVarsType = None, + parse_result: bool = True, + **kwargs: Any, + ) -> Any: """Render given template. This method must be run in the event loop. """ if self.is_static: - if self.hass.config.legacy_templates: + if self.hass.config.legacy_templates or not parse_result: return self.template return self._parse_result(self.template) @@ -397,7 +405,7 @@ class Template: render_result = render_result.strip() - if self.hass.config.legacy_templates: + if self.hass.config.legacy_templates or not parse_result: return render_result return self._parse_result(render_result) diff --git a/tests/components/shell_command/test_init.py b/tests/components/shell_command/test_init.py index 440375438c1..f5ad37cc617 100644 --- a/tests/components/shell_command/test_init.py +++ b/tests/components/shell_command/test_init.py @@ -174,7 +174,7 @@ async def test_do_no_run_forever(hass, caplog): assert await async_setup_component( hass, shell_command.DOMAIN, - {shell_command.DOMAIN: {"test_service": "sleep 10000s"}}, + {shell_command.DOMAIN: {"test_service": "sleep 10000"}}, ) await hass.async_block_till_done() diff --git a/tests/helpers/test_condition.py b/tests/helpers/test_condition.py index 001c59e2f2c..802ad7699e3 100644 --- a/tests/helpers/test_condition.py +++ b/tests/helpers/test_condition.py @@ -915,3 +915,26 @@ async def test_condition_template_error(hass, caplog): assert caplog.records[0].message.startswith( "Error during template condition: UndefinedError:" ) + + +async def test_condition_template_invalid_results(hass): + """Test template condition render false with invalid results.""" + test = await condition.async_from_config( + hass, {"condition": "template", "value_template": "{{ 'string' }}"} + ) + assert not test(hass) + + test = await condition.async_from_config( + hass, {"condition": "template", "value_template": "{{ 10.1 }}"} + ) + assert not test(hass) + + test = await condition.async_from_config( + hass, {"condition": "template", "value_template": "{{ 42 }}"} + ) + assert not test(hass) + + test = await condition.async_from_config( + hass, {"condition": "template", "value_template": "{{ [1, 2, 3] }}"} + ) + assert not test(hass) diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 86e17f8cb0b..750978b84ee 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -2653,6 +2653,28 @@ async def test_legacy_templates(hass): ) +async def test_no_result_parsing(hass): + """Test if templates results are not parsed.""" + hass.states.async_set("sensor.temperature", "12") + + assert ( + template.Template("{{ states.sensor.temperature.state }}", hass).async_render( + parse_result=False + ) + == "12" + ) + + assert ( + template.Template("{{ false }}", hass).async_render(parse_result=False) + == "False" + ) + + assert ( + template.Template("{{ [1, 2, 3] }}", hass).async_render(parse_result=False) + == "[1, 2, 3]" + ) + + async def test_is_static_still_ast_evals(hass): """Test is_static still convers to native type.""" tpl = template.Template("[1, 2]", hass) From 3ec83dc29e7ccbc760ffaa9f98da6a2d3b137e95 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2020 16:38:48 +0100 Subject: [PATCH 047/118] Catch ValueError in google_translate (#42405) --- CODEOWNERS | 1 - .../components/google_translate/manifest.json | 2 +- homeassistant/components/google_translate/tts.py | 11 ++++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 18439b3bb83..87112840f5b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -161,7 +161,6 @@ homeassistant/components/goalzero/* @tkdrob homeassistant/components/gogogate2/* @vangorra homeassistant/components/google_assistant/* @home-assistant/cloud homeassistant/components/google_cloud/* @lufton -homeassistant/components/google_translate/* @awarecan homeassistant/components/gpsd/* @fabaff homeassistant/components/gree/* @cmroche homeassistant/components/greeneye_monitor/* @jkeljo diff --git a/homeassistant/components/google_translate/manifest.json b/homeassistant/components/google_translate/manifest.json index 452a5352aac..c949e514d87 100644 --- a/homeassistant/components/google_translate/manifest.json +++ b/homeassistant/components/google_translate/manifest.json @@ -3,5 +3,5 @@ "name": "Google Translate Text-to-Speech", "documentation": "https://www.home-assistant.io/integrations/google_translate", "requirements": ["gTTS-token==1.1.3"], - "codeowners": ["@awarecan"] + "codeowners": [] } diff --git a/homeassistant/components/google_translate/tts.py b/homeassistant/components/google_translate/tts.py index 7988b699c12..66c00008046 100644 --- a/homeassistant/components/google_translate/tts.py +++ b/homeassistant/components/google_translate/tts.py @@ -123,9 +123,14 @@ class GoogleProvider(Provider): data = b"" for idx, part in enumerate(message_parts): - part_token = await self.hass.async_add_executor_job( - token.calculate_token, part - ) + try: + part_token = await self.hass.async_add_executor_job( + token.calculate_token, part + ) + except ValueError as err: + # If token seed fetching fails. + _LOGGER.warning(err) + return None, None url_param = { "ie": "UTF-8", From 287b33eef3ebe3f9f90868f6dc31f497666b16db Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2020 16:40:43 +0100 Subject: [PATCH 048/118] Do not set up DSM when setting up Nest legacy (#42406) --- homeassistant/components/nest/sensor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/nest/sensor.py b/homeassistant/components/nest/sensor.py index 794e8210f82..6245c5d83d0 100644 --- a/homeassistant/components/nest/sensor.py +++ b/homeassistant/components/nest/sensor.py @@ -14,4 +14,6 @@ async def async_setup_entry( """Set up the sensors.""" if DATA_SDM not in entry.data: await async_setup_legacy_entry(hass, entry, async_add_entities) + return + await async_setup_sdm_entry(hass, entry, async_add_entities) From 326d36d30385505bd148e4a0c99ccebebb5a0dd3 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 26 Oct 2020 19:29:10 +0100 Subject: [PATCH 049/118] Skip template result parsing in several places (#42408) * Skip template result parsing in several places * Adjust alert integration * Adjust Alexa integration * Adjust apns integration * Adjust arest integration * Adjust dialogflow integration * Adjust generic camera integration * Adjust imap email content integration * Adjust InfluxDB integration * Adjust intent integration * Adjust logbook integration * Adjust HP ILO integration * Adjust manual alarm control panel integration * Adjust manual mqtt alarm control panel integration * Adjust minio integration * Adjust mqtt integration * Adjust notify integration * Adjust persistent notification integration * Adjust rest integration * Adjust rss feed template integration * Adjust slack integration * Adjust Xiaomi integration * Adjust TCP integration * Adjust Telegram Bot integration * Bump CI cache version * Revert "Bump CI cache version" This reverts commit 875efe58cf165d84ce68aa8867b99a169fad4ea5. * Adjust demo tests --- homeassistant/components/alert/__init__.py | 6 +++--- .../components/alexa/flash_briefings.py | 16 ++++++++++++---- homeassistant/components/alexa/intent.py | 2 +- homeassistant/components/apns/notify.py | 2 +- homeassistant/components/arest/sensor.py | 2 +- homeassistant/components/dialogflow/__init__.py | 2 +- homeassistant/components/generic/camera.py | 4 ++-- homeassistant/components/hp_ilo/sensor.py | 4 +++- .../components/imap_email_content/sensor.py | 2 +- homeassistant/components/influxdb/sensor.py | 4 ++-- .../components/intent_script/__init__.py | 7 ++++--- homeassistant/components/logbook/__init__.py | 2 +- .../components/manual/alarm_control_panel.py | 4 +++- .../manual_mqtt/alarm_control_panel.py | 4 +++- homeassistant/components/minio/__init__.py | 2 +- .../components/mqtt/alarm_control_panel.py | 2 +- homeassistant/components/mqtt/cover.py | 2 +- .../components/mqtt/light/schema_template.py | 8 ++++++-- homeassistant/components/notify/__init__.py | 8 ++++---- .../persistent_notification/__init__.py | 4 ++-- homeassistant/components/rest/binary_sensor.py | 4 ++-- homeassistant/components/rest/notify.py | 2 +- homeassistant/components/rest/sensor.py | 4 ++-- homeassistant/components/rest/switch.py | 4 ++-- .../components/rest_command/__init__.py | 11 ++++++++--- .../components/rss_feed_template/__init__.py | 8 +++++--- homeassistant/components/slack/notify.py | 2 +- homeassistant/components/tcp/sensor.py | 4 +++- .../components/telegram_bot/__init__.py | 4 +++- homeassistant/components/xiaomi/camera.py | 2 +- tests/components/demo/test_notify.py | 2 +- 31 files changed, 82 insertions(+), 52 deletions(-) diff --git a/homeassistant/components/alert/__init__.py b/homeassistant/components/alert/__init__.py index 755bf6b3c49..53b1a1248dc 100644 --- a/homeassistant/components/alert/__init__.py +++ b/homeassistant/components/alert/__init__.py @@ -276,7 +276,7 @@ class Alert(ToggleEntity): self._send_done_message = True if self._message_template is not None: - message = self._message_template.async_render() + message = self._message_template.async_render(parse_result=False) else: message = self._name @@ -291,7 +291,7 @@ class Alert(ToggleEntity): if self._done_message_template is None: return - message = self._done_message_template.async_render() + message = self._done_message_template.async_render(parse_result=False) await self._send_notification_message(message) @@ -300,7 +300,7 @@ class Alert(ToggleEntity): msg_payload = {ATTR_MESSAGE: message} if self._title_template is not None: - title = self._title_template.async_render() + title = self._title_template.async_render(parse_result=False) msg_payload.update({ATTR_TITLE: title}) if self._data: msg_payload.update({ATTR_DATA: self._data}) diff --git a/homeassistant/components/alexa/flash_briefings.py b/homeassistant/components/alexa/flash_briefings.py index ed3da1d10be..b8f78705e10 100644 --- a/homeassistant/components/alexa/flash_briefings.py +++ b/homeassistant/components/alexa/flash_briefings.py @@ -80,13 +80,17 @@ class AlexaFlashBriefingView(http.HomeAssistantView): output = {} if item.get(CONF_TITLE) is not None: if isinstance(item.get(CONF_TITLE), template.Template): - output[ATTR_TITLE_TEXT] = item[CONF_TITLE].async_render() + output[ATTR_TITLE_TEXT] = item[CONF_TITLE].async_render( + parse_result=False + ) else: output[ATTR_TITLE_TEXT] = item.get(CONF_TITLE) if item.get(CONF_TEXT) is not None: if isinstance(item.get(CONF_TEXT), template.Template): - output[ATTR_MAIN_TEXT] = item[CONF_TEXT].async_render() + output[ATTR_MAIN_TEXT] = item[CONF_TEXT].async_render( + parse_result=False + ) else: output[ATTR_MAIN_TEXT] = item.get(CONF_TEXT) @@ -97,13 +101,17 @@ class AlexaFlashBriefingView(http.HomeAssistantView): if item.get(CONF_AUDIO) is not None: if isinstance(item.get(CONF_AUDIO), template.Template): - output[ATTR_STREAM_URL] = item[CONF_AUDIO].async_render() + output[ATTR_STREAM_URL] = item[CONF_AUDIO].async_render( + parse_result=False + ) else: output[ATTR_STREAM_URL] = item.get(CONF_AUDIO) if item.get(CONF_DISPLAY_URL) is not None: if isinstance(item.get(CONF_DISPLAY_URL), template.Template): - output[ATTR_REDIRECTION_URL] = item[CONF_DISPLAY_URL].async_render() + output[ATTR_REDIRECTION_URL] = item[CONF_DISPLAY_URL].async_render( + parse_result=False + ) else: output[ATTR_REDIRECTION_URL] = item.get(CONF_DISPLAY_URL) diff --git a/homeassistant/components/alexa/intent.py b/homeassistant/components/alexa/intent.py index c04b493beec..f64031250e2 100644 --- a/homeassistant/components/alexa/intent.py +++ b/homeassistant/components/alexa/intent.py @@ -271,7 +271,7 @@ class AlexaResponse: self.reprompt = { "type": speech_type.value, - key: text.async_render(self.variables), + key: text.async_render(self.variables, parse_result=False), } def as_dict(self): diff --git a/homeassistant/components/apns/notify.py b/homeassistant/components/apns/notify.py index 59b2a7aa9fa..24d666fa59b 100644 --- a/homeassistant/components/apns/notify.py +++ b/homeassistant/components/apns/notify.py @@ -229,7 +229,7 @@ class ApnsNotificationService(BaseNotificationService): if isinstance(message, str): rendered_message = message elif isinstance(message, template_helper.Template): - rendered_message = message.render() + rendered_message = message.render(parse_result=False) else: rendered_message = "" diff --git a/homeassistant/components/arest/sensor.py b/homeassistant/components/arest/sensor.py index 7e50b1df8ff..d213a3d2903 100644 --- a/homeassistant/components/arest/sensor.py +++ b/homeassistant/components/arest/sensor.py @@ -78,7 +78,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): def _render(value): try: - return value_template.async_render({"value": value}) + return value_template.async_render({"value": value}, parse_result=False) except TemplateError: _LOGGER.exception("Error parsing value") return value diff --git a/homeassistant/components/dialogflow/__init__.py b/homeassistant/components/dialogflow/__init__.py index b9ba977e422..e623919f099 100644 --- a/homeassistant/components/dialogflow/__init__.py +++ b/homeassistant/components/dialogflow/__init__.py @@ -161,7 +161,7 @@ class DialogflowResponse: assert self.speech is None if isinstance(text, template.Template): - text = text.async_render(self.parameters) + text = text.async_render(self.parameters, parse_result=False) self.speech = text diff --git a/homeassistant/components/generic/camera.py b/homeassistant/components/generic/camera.py index 3656e669561..2e798b8cc4b 100644 --- a/homeassistant/components/generic/camera.py +++ b/homeassistant/components/generic/camera.py @@ -121,7 +121,7 @@ class GenericCamera(Camera): async def async_camera_image(self): """Return a still image response from the camera.""" try: - url = self._still_image_url.async_render() + url = self._still_image_url.async_render(parse_result=False) except TemplateError as err: _LOGGER.error("Error parsing template %s: %s", self._still_image_url, err) return self._last_image @@ -178,7 +178,7 @@ class GenericCamera(Camera): return None try: - return self._stream_source.async_render() + return self._stream_source.async_render(parse_result=False) except TemplateError as err: _LOGGER.error("Error parsing template %s: %s", self._stream_source, err) return None diff --git a/homeassistant/components/hp_ilo/sensor.py b/homeassistant/components/hp_ilo/sensor.py index 1cb65292c7d..da597acb8b7 100644 --- a/homeassistant/components/hp_ilo/sensor.py +++ b/homeassistant/components/hp_ilo/sensor.py @@ -157,7 +157,9 @@ class HpIloSensor(Entity): ilo_data = getattr(self.hp_ilo_data.data, self._ilo_function)() if self._sensor_value_template is not None: - ilo_data = self._sensor_value_template.render(ilo_data=ilo_data) + ilo_data = self._sensor_value_template.render( + ilo_data=ilo_data, parse_result=False + ) self._state = ilo_data diff --git a/homeassistant/components/imap_email_content/sensor.py b/homeassistant/components/imap_email_content/sensor.py index 21b535450a1..04d4ca97c5a 100644 --- a/homeassistant/components/imap_email_content/sensor.py +++ b/homeassistant/components/imap_email_content/sensor.py @@ -183,7 +183,7 @@ class EmailContentSensor(Entity): ATTR_DATE: email_message["Date"], ATTR_BODY: EmailContentSensor.get_msg_text(email_message), } - return self._value_template.render(variables) + return self._value_template.render(variables, parse_result=False) def sender_allowed(self, email_message): """Check if the sender is in the allowed senders list.""" diff --git a/homeassistant/components/influxdb/sensor.py b/homeassistant/components/influxdb/sensor.py index 2fcf4baaba8..ff9f6f93153 100644 --- a/homeassistant/components/influxdb/sensor.py +++ b/homeassistant/components/influxdb/sensor.py @@ -268,7 +268,7 @@ class InfluxFluxSensorData: """Get the latest data by querying influx.""" _LOGGER.debug(RENDERING_QUERY_MESSAGE, self.query) try: - rendered_query = self.query.render() + rendered_query = self.query.render(parse_result=False) except TemplateError as ex: _LOGGER.error(RENDERING_QUERY_ERROR_MESSAGE, ex) return @@ -312,7 +312,7 @@ class InfluxQLSensorData: """Get the latest data with a shell command.""" _LOGGER.debug(RENDERING_WHERE_MESSAGE, self.where) try: - where_clause = self.where.render() + where_clause = self.where.render(parse_result=False) except TemplateError as ex: _LOGGER.error(RENDERING_WHERE_ERROR_MESSAGE, ex) return diff --git a/homeassistant/components/intent_script/__init__.py b/homeassistant/components/intent_script/__init__.py index ebbd2bb824e..892ea83982c 100644 --- a/homeassistant/components/intent_script/__init__.py +++ b/homeassistant/components/intent_script/__init__.py @@ -87,13 +87,14 @@ class ScriptIntentHandler(intent.IntentHandler): if speech is not None: response.async_set_speech( - speech[CONF_TEXT].async_render(slots), speech[CONF_TYPE] + speech[CONF_TEXT].async_render(slots, parse_result=False), + speech[CONF_TYPE], ) if card is not None: response.async_set_card( - card[CONF_TITLE].async_render(slots), - card[CONF_CONTENT].async_render(slots), + card[CONF_TITLE].async_render(slots, parse_result=False), + card[CONF_CONTENT].async_render(slots, parse_result=False), card[CONF_TYPE], ) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 99fdc54603b..254d99ed848 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -144,7 +144,7 @@ async def async_setup(hass, config): domain = DOMAIN message.hass = hass - message = message.async_render() + message = message.async_render(parse_result=False) async_log_entry(hass, name, message, domain, entity_id) hass.components.frontend.async_register_built_in_panel( diff --git a/homeassistant/components/manual/alarm_control_panel.py b/homeassistant/components/manual/alarm_control_panel.py index 168d5b637e9..2313bcace19 100644 --- a/homeassistant/components/manual/alarm_control_panel.py +++ b/homeassistant/components/manual/alarm_control_panel.py @@ -385,7 +385,9 @@ class ManualAlarm(alarm.AlarmControlPanelEntity, RestoreEntity): if isinstance(self._code, str): alarm_code = self._code else: - alarm_code = self._code.render(from_state=self._state, to_state=state) + alarm_code = self._code.render( + parse_result=False, from_state=self._state, to_state=state + ) check = not alarm_code or code == alarm_code if not check: _LOGGER.warning("Invalid code given for %s", state) diff --git a/homeassistant/components/manual_mqtt/alarm_control_panel.py b/homeassistant/components/manual_mqtt/alarm_control_panel.py index 10c2a473a29..f11938396a7 100644 --- a/homeassistant/components/manual_mqtt/alarm_control_panel.py +++ b/homeassistant/components/manual_mqtt/alarm_control_panel.py @@ -406,7 +406,9 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity): if isinstance(self._code, str): alarm_code = self._code else: - alarm_code = self._code.render(from_state=self._state, to_state=state) + alarm_code = self._code.render( + from_state=self._state, to_state=state, parse_result=False + ) check = not alarm_code or code == alarm_code if not check: _LOGGER.warning("Invalid code given for %s", state) diff --git a/homeassistant/components/minio/__init__.py b/homeassistant/components/minio/__init__.py index 24e7049dd18..178058986cc 100644 --- a/homeassistant/components/minio/__init__.py +++ b/homeassistant/components/minio/__init__.py @@ -124,7 +124,7 @@ def setup(hass, config): def _render_service_value(service, key): value = service.data[key] value.hass = hass - return value.async_render() + return value.async_render(parse_result=False) def put_file(service): """Upload file service.""" diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index 6d33175e6ca..35d0e1fb42e 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -338,7 +338,7 @@ class MqttAlarm( """Publish via mqtt.""" command_template = self._config[CONF_COMMAND_TEMPLATE] values = {"action": action, "code": code} - payload = command_template.async_render(**values) + payload = command_template.async_render(**values, parse_result=False) mqtt.async_publish( self.hass, self._config[CONF_COMMAND_TOPIC], diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 20146b0b7d6..c3a78133246 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -557,7 +557,7 @@ class MqttCover( position = kwargs[ATTR_POSITION] percentage_position = position if set_position_template is not None: - position = set_position_template.async_render(**kwargs) + position = set_position_template.async_render(parse_result=False, **kwargs) else: position = self.find_in_range_from_percent(position, COVER_PAYLOAD) diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 46a2b9f7ab7..44a87f2af2c 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -441,7 +441,9 @@ class MqttLightTemplate( mqtt.async_publish( self.hass, self._topics[CONF_COMMAND_TOPIC], - self._templates[CONF_COMMAND_ON_TEMPLATE].async_render(**values), + self._templates[CONF_COMMAND_ON_TEMPLATE].async_render( + parse_result=False, **values + ), self._config[CONF_QOS], self._config[CONF_RETAIN], ) @@ -464,7 +466,9 @@ class MqttLightTemplate( mqtt.async_publish( self.hass, self._topics[CONF_COMMAND_TOPIC], - self._templates[CONF_COMMAND_OFF_TEMPLATE].async_render(**values), + self._templates[CONF_COMMAND_OFF_TEMPLATE].async_render( + parse_result=False, **values + ), self._config[CONF_QOS], self._config[CONF_RETAIN], ) diff --git a/homeassistant/components/notify/__init__.py b/homeassistant/components/notify/__init__.py index 016c1aa9e89..08219567ed6 100644 --- a/homeassistant/components/notify/__init__.py +++ b/homeassistant/components/notify/__init__.py @@ -133,7 +133,7 @@ class BaseNotificationService: if title: title.hass = self.hass - kwargs[ATTR_TITLE] = title.async_render() + kwargs[ATTR_TITLE] = title.async_render(parse_result=False) if self._registered_targets.get(service.service) is not None: kwargs[ATTR_TARGET] = [self._registered_targets[service.service]] @@ -141,7 +141,7 @@ class BaseNotificationService: kwargs[ATTR_TARGET] = service.data.get(ATTR_TARGET) message.hass = self.hass - kwargs[ATTR_MESSAGE] = message.async_render() + kwargs[ATTR_MESSAGE] = message.async_render(parse_result=False) kwargs[ATTR_DATA] = service.data.get(ATTR_DATA) await self.async_send_message(**kwargs) @@ -229,12 +229,12 @@ async def async_setup(hass, config): payload = {} message = service.data[ATTR_MESSAGE] message.hass = hass - payload[ATTR_MESSAGE] = message.async_render() + payload[ATTR_MESSAGE] = message.async_render(parse_result=False) title = service.data.get(ATTR_TITLE) if title: title.hass = hass - payload[ATTR_TITLE] = title.async_render() + payload[ATTR_TITLE] = title.async_render(parse_result=False) await hass.services.async_call( pn.DOMAIN, pn.SERVICE_CREATE, payload, blocking=True diff --git a/homeassistant/components/persistent_notification/__init__.py b/homeassistant/components/persistent_notification/__init__.py index 9a1f35c947d..49a9f4c3bea 100644 --- a/homeassistant/components/persistent_notification/__init__.py +++ b/homeassistant/components/persistent_notification/__init__.py @@ -119,7 +119,7 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool: if title is not None: try: title.hass = hass - title = title.async_render() + title = title.async_render(parse_result=False) except TemplateError as ex: _LOGGER.error("Error rendering title %s: %s", title, ex) title = title.template @@ -128,7 +128,7 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool: try: message.hass = hass - message = message.async_render() + message = message.async_render(parse_result=False) except TemplateError as ex: _LOGGER.error("Error rendering message %s: %s", message, ex) message = message.template diff --git a/homeassistant/components/rest/binary_sensor.py b/homeassistant/components/rest/binary_sensor.py index c25750e2a2e..ec8b3ba0122 100644 --- a/homeassistant/components/rest/binary_sensor.py +++ b/homeassistant/components/rest/binary_sensor.py @@ -84,7 +84,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= if resource_template is not None: resource_template.hass = hass - resource = resource_template.render() + resource = resource_template.render(parse_result=False) if value_template is not None: value_template.hass = hass @@ -189,6 +189,6 @@ class RestBinarySensor(BinarySensorEntity): async def async_update(self): """Get the latest data from REST API and updates the state.""" if self._resource_template is not None: - self.rest.set_url(self._resource_template.render()) + self.rest.set_url(self._resource_template.render(parse_result=False)) await self.rest.async_update() diff --git a/homeassistant/components/rest/notify.py b/homeassistant/components/rest/notify.py index d7ee57b4f8e..b8f81b19e92 100644 --- a/homeassistant/components/rest/notify.py +++ b/homeassistant/components/rest/notify.py @@ -163,7 +163,7 @@ class RestNotificationService(BaseNotificationService): key: _data_template_creator(item) for key, item in value.items() } value.hass = self._hass - return value.async_render(kwargs) + return value.async_render(kwargs, parse_result=False) data.update(_data_template_creator(self._data_template)) diff --git a/homeassistant/components/rest/sensor.py b/homeassistant/components/rest/sensor.py index e9783e3fea3..323447ba1b3 100644 --- a/homeassistant/components/rest/sensor.py +++ b/homeassistant/components/rest/sensor.py @@ -103,7 +103,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= if resource_template is not None: resource_template.hass = hass - resource = resource_template.render() + resource = resource_template.render(parse_result=False) if username and password: if config.get(CONF_AUTHENTICATION) == HTTP_DIGEST_AUTHENTICATION: @@ -202,7 +202,7 @@ class RestSensor(Entity): async def async_update(self): """Get the latest data from REST API and update the state.""" if self._resource_template is not None: - self.rest.set_url(self._resource_template.render()) + self.rest.set_url(self._resource_template.render(parse_result=False)) await self.rest.async_update() diff --git a/homeassistant/components/rest/switch.py b/homeassistant/components/rest/switch.py index 865f4d01b3a..1b980e12b75 100644 --- a/homeassistant/components/rest/switch.py +++ b/homeassistant/components/rest/switch.py @@ -162,7 +162,7 @@ class RestSwitch(SwitchEntity): async def async_turn_on(self, **kwargs): """Turn the device on.""" - body_on_t = self._body_on.async_render() + body_on_t = self._body_on.async_render(parse_result=False) try: req = await self.set_device_state(body_on_t) @@ -178,7 +178,7 @@ class RestSwitch(SwitchEntity): async def async_turn_off(self, **kwargs): """Turn the device off.""" - body_off_t = self._body_off.async_render() + body_off_t = self._body_off.async_render(parse_result=False) try: req = await self.set_device_state(body_off_t) diff --git a/homeassistant/components/rest_command/__init__.py b/homeassistant/components/rest_command/__init__.py index b1996bdce50..7ba46d3bf50 100644 --- a/homeassistant/components/rest_command/__init__.py +++ b/homeassistant/components/rest_command/__init__.py @@ -93,17 +93,22 @@ async def async_setup(hass, config): payload = None if template_payload: payload = bytes( - template_payload.async_render(variables=service.data), "utf-8" + template_payload.async_render( + variables=service.data, parse_result=False + ), + "utf-8", ) - request_url = template_url.async_render(variables=service.data) + request_url = template_url.async_render( + variables=service.data, parse_result=False + ) headers = None if template_headers: headers = {} for header_name, template_header in template_headers.items(): headers[header_name] = template_header.async_render( - variables=service.data + variables=service.data, parse_result=False ) if content_type: diff --git a/homeassistant/components/rss_feed_template/__init__.py b/homeassistant/components/rss_feed_template/__init__.py index b979e4d5261..c9871c8f6b5 100644 --- a/homeassistant/components/rss_feed_template/__init__.py +++ b/homeassistant/components/rss_feed_template/__init__.py @@ -83,17 +83,19 @@ class RssView(HomeAssistantView): response += "\n" if self._title is not None: - response += " %s\n" % escape(self._title.async_render()) + response += " %s\n" % escape( + self._title.async_render(parse_result=False) + ) for item in self._items: response += " \n" if "title" in item: response += " " - response += escape(item["title"].async_render()) + response += escape(item["title"].async_render(parse_result=False)) response += "\n" if "description" in item: response += " " - response += escape(item["description"].async_render()) + response += escape(item["description"].async_render(parse_result=False)) response += "\n" response += " \n" diff --git a/homeassistant/components/slack/notify.py b/homeassistant/components/slack/notify.py index 5fff857825b..88317b31585 100644 --- a/homeassistant/components/slack/notify.py +++ b/homeassistant/components/slack/notify.py @@ -118,7 +118,7 @@ def _async_templatize_blocks(hass, value): } tmpl = template.Template(value, hass=hass) - return tmpl.async_render() + return tmpl.async_render(parse_result=False) class SlackNotificationService(BaseNotificationService): diff --git a/homeassistant/components/tcp/sensor.py b/homeassistant/components/tcp/sensor.py index 9eee857fd83..868cd9b8557 100644 --- a/homeassistant/components/tcp/sensor.py +++ b/homeassistant/components/tcp/sensor.py @@ -136,7 +136,9 @@ class TcpSensor(Entity): if self._config[CONF_VALUE_TEMPLATE] is not None: try: - self._state = self._config[CONF_VALUE_TEMPLATE].render(value=value) + self._state = self._config[CONF_VALUE_TEMPLATE].render( + parse_result=False, value=value + ) return except TemplateError: _LOGGER.error( diff --git a/homeassistant/components/telegram_bot/__init__.py b/homeassistant/components/telegram_bot/__init__.py index 7a9d0c18e92..60e33414b1e 100644 --- a/homeassistant/components/telegram_bot/__init__.py +++ b/homeassistant/components/telegram_bot/__init__.py @@ -329,7 +329,9 @@ async def async_setup(hass, config): else: attribute_templ.hass = hass try: - data[attribute] = attribute_templ.async_render() + data[attribute] = attribute_templ.async_render( + parse_result=False + ) except TemplateError as exc: _LOGGER.error( "TemplateError in %s: %s -> %s", diff --git a/homeassistant/components/xiaomi/camera.py b/homeassistant/components/xiaomi/camera.py index 45466be2cc4..97a0aca20f7 100644 --- a/homeassistant/components/xiaomi/camera.py +++ b/homeassistant/components/xiaomi/camera.py @@ -142,7 +142,7 @@ class XiaomiCamera(Camera): """Return a still image response from the camera.""" try: - host = self.host.async_render() + host = self.host.async_render(parse_result=False) except TemplateError as exc: _LOGGER.error("Error parsing template %s: %s", self.host, exc) return self._last_image diff --git a/tests/components/demo/test_notify.py b/tests/components/demo/test_notify.py index eac5e6a6006..893b9d57e65 100644 --- a/tests/components/demo/test_notify.py +++ b/tests/components/demo/test_notify.py @@ -108,7 +108,7 @@ async def test_sending_templated_message(hass, events): await hass.async_block_till_done() last_event = events[-1] assert last_event.data[notify.ATTR_TITLE] == "temperature" - assert last_event.data[notify.ATTR_MESSAGE] == 10 + assert last_event.data[notify.ATTR_MESSAGE] == "10" async def test_method_forwards_correct_data(hass, events): From 13b8bc629095f872886e1f604bc42fbe8e9f3dfd Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 26 Oct 2020 20:29:20 +0100 Subject: [PATCH 050/118] Add missing config flow translation key for Nest (#42423) --- homeassistant/components/nest/strings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/nest/strings.json b/homeassistant/components/nest/strings.json index 943afd254c8..6882e8f55e7 100644 --- a/homeassistant/components/nest/strings.json +++ b/homeassistant/components/nest/strings.json @@ -25,7 +25,8 @@ "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]", "missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]", "authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]", - "authorize_url_fail": "Unknown error generating an authorize url." + "authorize_url_fail": "Unknown error generating an authorize url.", + "no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]" }, "create_entry": { "default": "[%key:common::config_flow::create_entry::authenticated%]" From 2893972c69ee6f95962a81e87216e2ba6132b3eb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2020 19:30:18 +0000 Subject: [PATCH 051/118] Bumped version to 0.117.0b5 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index d51fa29a2fb..cebee253df0 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "0b4" +PATCH_VERSION = "0b5" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 93e71ef21827f01543b30eb00ce1ce1f2b1aff3b Mon Sep 17 00:00:00 2001 From: Angelo Gagliano <25516409+TheGardenMonkey@users.noreply.github.com> Date: Tue, 27 Oct 2020 05:02:16 -0400 Subject: [PATCH 052/118] Add retry for unavailable static tplink devices after HA starts (#42247) --- homeassistant/components/tplink/common.py | 30 +++++++++++++++++++++++ homeassistant/components/tplink/light.py | 21 ++++++---------- homeassistant/components/tplink/switch.py | 20 ++++++--------- tests/components/tplink/test_light.py | 30 +++++++++++++++++++++++ 4 files changed, 74 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/tplink/common.py b/homeassistant/components/tplink/common.py index ad4c4ff63fa..a8ba48e3935 100644 --- a/homeassistant/components/tplink/common.py +++ b/homeassistant/components/tplink/common.py @@ -13,6 +13,8 @@ from pyHS100 import ( from homeassistant.helpers.typing import HomeAssistantType +from .const import DOMAIN as TPLINK_DOMAIN + _LOGGER = logging.getLogger(__name__) @@ -127,3 +129,31 @@ def get_static_devices(config_data) -> SmartDevices: "Failed to setup device %s due to %s; not retrying", host, sde ) return SmartDevices(lights, switches) + + +def add_available_devices(hass, device_type, device_class, async_add_entities): + """Get sysinfo for all devices.""" + + devices = hass.data[TPLINK_DOMAIN][device_type] + + if f"{device_type}_remaining" in hass.data[TPLINK_DOMAIN]: + devices = hass.data[TPLINK_DOMAIN][f"{device_type}_remaining"] + + entities_ready = [] + entities_unavailable = [] + for device in devices: + try: + device.get_sysinfo() + entities_ready.append(device_class(device)) + except SmartDeviceException as ex: + entities_unavailable.append(device) + _LOGGER.warning( + "Unable to communicate with device %s: %s", + device.host, + ex, + ) + + hass.data[TPLINK_DOMAIN][f"{device_type}_remaining"] = entities_unavailable + + if entities_ready: + async_add_entities(entities_ready, update_before_add=True) diff --git a/homeassistant/components/tplink/light.py b/homeassistant/components/tplink/light.py index 21dc382f486..e2acf3fcd90 100644 --- a/homeassistant/components/tplink/light.py +++ b/homeassistant/components/tplink/light.py @@ -16,7 +16,7 @@ from homeassistant.components.light import ( SUPPORT_COLOR_TEMP, LightEntity, ) -from homeassistant.exceptions import HomeAssistantError +from homeassistant.exceptions import HomeAssistantError, PlatformNotReady import homeassistant.helpers.device_registry as dr from homeassistant.helpers.typing import HomeAssistantType from homeassistant.util.color import ( @@ -26,6 +26,7 @@ from homeassistant.util.color import ( import homeassistant.util.dt as dt_util from . import CONF_LIGHT, DOMAIN as TPLINK_DOMAIN +from .common import add_available_devices PARALLEL_UPDATES = 0 SCAN_INTERVAL = timedelta(seconds=5) @@ -60,20 +61,12 @@ SLEEP_TIME = 2 async def async_setup_entry(hass: HomeAssistantType, config_entry, async_add_entities): """Set up lights.""" - devices = hass.data[TPLINK_DOMAIN][CONF_LIGHT] - entities = [] + await hass.async_add_executor_job( + add_available_devices, hass, CONF_LIGHT, TPLinkSmartBulb, async_add_entities + ) - await hass.async_add_executor_job(get_devices_sysinfo, devices) - for device in devices: - entities.append(TPLinkSmartBulb(device)) - - async_add_entities(entities, update_before_add=True) - - -def get_devices_sysinfo(devices): - """Get sysinfo for all devices.""" - for device in devices: - device.get_sysinfo() + if hass.data[TPLINK_DOMAIN][f"{CONF_LIGHT}_remaining"]: + raise PlatformNotReady def brightness_to_percentage(byt): diff --git a/homeassistant/components/tplink/switch.py b/homeassistant/components/tplink/switch.py index 236607f4cd7..b7e31e82e78 100644 --- a/homeassistant/components/tplink/switch.py +++ b/homeassistant/components/tplink/switch.py @@ -11,10 +11,12 @@ from homeassistant.components.switch import ( SwitchEntity, ) from homeassistant.const import ATTR_VOLTAGE +from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.device_registry as dr from homeassistant.helpers.typing import HomeAssistantType from . import CONF_SWITCH, DOMAIN as TPLINK_DOMAIN +from .common import add_available_devices PARALLEL_UPDATES = 0 @@ -29,20 +31,12 @@ SLEEP_TIME = 2 async def async_setup_entry(hass: HomeAssistantType, config_entry, async_add_entities): """Set up switches.""" - devices = hass.data[TPLINK_DOMAIN][CONF_SWITCH] - entities = [] + await hass.async_add_executor_job( + add_available_devices, hass, CONF_SWITCH, SmartPlugSwitch, async_add_entities + ) - await hass.async_add_executor_job(get_devices_sysinfo, devices) - for device in devices: - entities.append(SmartPlugSwitch(device)) - - async_add_entities(entities, update_before_add=True) - - -def get_devices_sysinfo(devices): - """Get sysinfo for all devices.""" - for device in devices: - device.get_sysinfo() + if hass.data[TPLINK_DOMAIN][f"{CONF_SWITCH}_remaining"]: + raise PlatformNotReady class SmartPlugSwitch(SwitchEntity): diff --git a/tests/components/tplink/test_light.py b/tests/components/tplink/test_light.py index 56de30b1951..4987f4f26cb 100644 --- a/tests/components/tplink/test_light.py +++ b/tests/components/tplink/test_light.py @@ -577,3 +577,33 @@ async def test_update_failure( in caplog.text ) assert "Device 123.123.123.123|light1 responded after " in caplog.text + + +async def test_async_setup_entry_unavailable( + hass: HomeAssistant, light_mock_data: LightMockData, caplog +): + """Test unavailable devices trigger a later retry.""" + caplog.clear() + caplog.set_level(logging.WARNING) + + with patch( + "homeassistant.components.tplink.common.SmartDevice.get_sysinfo", + side_effect=SmartDeviceException, + ): + await async_setup_component(hass, HA_DOMAIN, {}) + await hass.async_block_till_done() + + await async_setup_component( + hass, + tplink.DOMAIN, + { + tplink.DOMAIN: { + CONF_DISCOVERY: False, + CONF_LIGHT: [{CONF_HOST: "123.123.123.123"}], + } + }, + ) + + await hass.async_block_till_done() + assert "Unable to communicate with device 123.123.123.123" in caplog.text + assert len(hass.data[tplink.DOMAIN][f"{CONF_LIGHT}_remaining"]) == 1 From e3d29429d38f1bd0c5ef05eb911b4e2817b953c2 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Mon, 26 Oct 2020 22:12:21 +0100 Subject: [PATCH 053/118] Make sure to clean up httpx session on failed connection to Axis device (#42428) --- homeassistant/components/axis/device.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/axis/device.py b/homeassistant/components/axis/device.py index 94aa149c09e..79204bf3002 100644 --- a/homeassistant/components/axis/device.py +++ b/homeassistant/components/axis/device.py @@ -280,12 +280,15 @@ async def get_device(hass, host, port, username, password): except axis.Unauthorized as err: LOGGER.warning("Connected to device at %s but not registered.", host) + await device.vapix.close() raise AuthenticationRequired from err except (asyncio.TimeoutError, axis.RequestError) as err: LOGGER.error("Error connecting to the Axis device at %s", host) + await device.vapix.close() raise CannotConnect from err except axis.AxisException as err: LOGGER.exception("Unknown Axis communication error occurred") + await device.vapix.close() raise AuthenticationRequired from err From af31aa7995ce2254952587c0fe39ad900e3f97c3 Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Tue, 27 Oct 2020 15:56:45 +0200 Subject: [PATCH 054/118] Fix DLNA DMR media receiver when using Python 3.9 (#42430) --- homeassistant/components/dlna_dmr/media_player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index acbd69138d4..2b01ad2a4ae 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -168,7 +168,7 @@ async def async_setup_platform( requester = AiohttpSessionRequester(session, True) # ensure event handler has been started - with await hass.data[DLNA_DMR_DATA]["lock"]: + async with hass.data[DLNA_DMR_DATA]["lock"]: server_host = config.get(CONF_LISTEN_IP) if server_host is None: server_host = get_local_ip() @@ -220,7 +220,7 @@ class DlnaDmrDevice(MediaPlayerEntity): async def _async_on_hass_stop(self, event): """Event handler on Home Assistant stop.""" - with await self.hass.data[DLNA_DMR_DATA]["lock"]: + async with self.hass.data[DLNA_DMR_DATA]["lock"]: await self._device.async_unsubscribe_services() async def async_update(self): From 29e7aa753cafe24ed67a240364df630ddf720648 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Mon, 26 Oct 2020 17:10:30 -0400 Subject: [PATCH 055/118] Remove title string from Xbox translations (#42431) --- homeassistant/components/xbox/strings.json | 1 - homeassistant/components/xbox/translations/en.json | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/xbox/strings.json b/homeassistant/components/xbox/strings.json index ccece2d7f73..accd6775941 100644 --- a/homeassistant/components/xbox/strings.json +++ b/homeassistant/components/xbox/strings.json @@ -1,5 +1,4 @@ { - "title": "xbox", "config": { "step": { "pick_implementation": { diff --git a/homeassistant/components/xbox/translations/en.json b/homeassistant/components/xbox/translations/en.json index ae5465f889e..0bb1266bded 100644 --- a/homeassistant/components/xbox/translations/en.json +++ b/homeassistant/components/xbox/translations/en.json @@ -13,6 +13,5 @@ "title": "Pick Authentication Method" } } - }, - "title": "xbox" + } } \ No newline at end of file From 9fa97473cb6693001f513ab9f619cd5033aea238 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Mon, 26 Oct 2020 17:26:03 -0600 Subject: [PATCH 056/118] Bump simplisafe-python to 9.6.0 (#42437) --- homeassistant/components/simplisafe/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json index 09682352576..0f209b366e8 100644 --- a/homeassistant/components/simplisafe/manifest.json +++ b/homeassistant/components/simplisafe/manifest.json @@ -3,6 +3,6 @@ "name": "SimpliSafe", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/simplisafe", - "requirements": ["simplisafe-python==9.5.1"], + "requirements": ["simplisafe-python==9.6.0"], "codeowners": ["@bachya"] } diff --git a/requirements_all.txt b/requirements_all.txt index 7b4478d9c9b..7a6ea6537a4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2017,7 +2017,7 @@ simplehound==0.3 simplepush==1.1.4 # homeassistant.components.simplisafe -simplisafe-python==9.5.1 +simplisafe-python==9.6.0 # homeassistant.components.sisyphus sisyphus-control==2.2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 21d331f3f35..1e7d4830d76 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -959,7 +959,7 @@ sharkiqpy==0.1.8 simplehound==0.3 # homeassistant.components.simplisafe -simplisafe-python==9.5.1 +simplisafe-python==9.6.0 # homeassistant.components.slack slackclient==2.5.0 From 91e8593fb67bd8c8863bbbf273e0171bc84be189 Mon Sep 17 00:00:00 2001 From: rajlaud <50647620+rajlaud@users.noreply.github.com> Date: Mon, 26 Oct 2020 23:12:03 -0500 Subject: [PATCH 057/118] Improve Squeezebox media browser performance (#42439) * Bump pysqueezebox version to better handle large libraries for media browser * Merge changes into requirements --- homeassistant/components/squeezebox/browse_media.py | 2 +- homeassistant/components/squeezebox/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/squeezebox/browse_media.py b/homeassistant/components/squeezebox/browse_media.py index 3e5523e8475..bab24e39cbc 100644 --- a/homeassistant/components/squeezebox/browse_media.py +++ b/homeassistant/components/squeezebox/browse_media.py @@ -65,7 +65,7 @@ CONTENT_TYPE_TO_CHILD_TYPE = { "Genres": MEDIA_TYPE_GENRE, } -BROWSE_LIMIT = 500 +BROWSE_LIMIT = 1000 async def build_item_response(player, payload): diff --git a/homeassistant/components/squeezebox/manifest.json b/homeassistant/components/squeezebox/manifest.json index af358b832f7..a0d0d1ef50e 100644 --- a/homeassistant/components/squeezebox/manifest.json +++ b/homeassistant/components/squeezebox/manifest.json @@ -6,7 +6,7 @@ "@rajlaud" ], "requirements": [ - "pysqueezebox==0.5.1" + "pysqueezebox==0.5.4" ], "config_flow": true } diff --git a/requirements_all.txt b/requirements_all.txt index 7a6ea6537a4..aa488f262d9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1694,7 +1694,7 @@ pysonos==0.0.35 pyspcwebgw==0.4.0 # homeassistant.components.squeezebox -pysqueezebox==0.5.1 +pysqueezebox==0.5.4 # homeassistant.components.stiebel_eltron pystiebeleltron==0.0.1.dev2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1e7d4830d76..24907d07213 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -835,7 +835,7 @@ pysonos==0.0.35 pyspcwebgw==0.4.0 # homeassistant.components.squeezebox -pysqueezebox==0.5.1 +pysqueezebox==0.5.4 # homeassistant.components.syncthru pysyncthru==0.7.0 From 2697056ba9b2ea89897262b8657ec6de3f812bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Gabriel?= Date: Tue, 27 Oct 2020 05:03:07 -0300 Subject: [PATCH 058/118] Add device info default values to Panasonic Viera (#42441) --- .../components/panasonic_viera/__init__.py | 4 +- .../components/panasonic_viera/const.py | 3 + .../panasonic_viera/media_player.py | 16 ++-- .../panasonic_viera/test_config_flow.py | 76 ++++++++++++++++--- tests/components/panasonic_viera/test_init.py | 6 +- 5 files changed, 85 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/panasonic_viera/__init__.py b/homeassistant/components/panasonic_viera/__init__.py index 8ebf5c8c4b1..f51c6d9d372 100644 --- a/homeassistant/components/panasonic_viera/__init__.py +++ b/homeassistant/components/panasonic_viera/__init__.py @@ -245,7 +245,9 @@ class Remote: """Return device info.""" if self._control is None: return None - return await self._handle_errors(self._control.get_device_info) + device_info = await self._handle_errors(self._control.get_device_info) + _LOGGER.debug("Fetched device info: %s", str(device_info)) + return device_info async def _handle_errors(self, func, *args): """Handle errors from func, set available and reconnect if needed.""" diff --git a/homeassistant/components/panasonic_viera/const.py b/homeassistant/components/panasonic_viera/const.py index 36b61360441..a2e3fc2eece 100644 --- a/homeassistant/components/panasonic_viera/const.py +++ b/homeassistant/components/panasonic_viera/const.py @@ -18,4 +18,7 @@ ATTR_MANUFACTURER = "manufacturer" ATTR_MODEL_NUMBER = "modelNumber" ATTR_UDN = "UDN" +DEFAULT_MANUFACTURER = "Panasonic" +DEFAULT_MODEL_NUMBER = "Panasonic Viera" + ERROR_INVALID_PIN_CODE = "invalid_pin_code" diff --git a/homeassistant/components/panasonic_viera/media_player.py b/homeassistant/components/panasonic_viera/media_player.py index 316385f7d96..c59c77b569c 100644 --- a/homeassistant/components/panasonic_viera/media_player.py +++ b/homeassistant/components/panasonic_viera/media_player.py @@ -26,6 +26,8 @@ from .const import ( ATTR_MODEL_NUMBER, ATTR_REMOTE, ATTR_UDN, + DEFAULT_MANUFACTURER, + DEFAULT_MODEL_NUMBER, DOMAIN, ) @@ -69,11 +71,11 @@ class PanasonicVieraTVEntity(MediaPlayerEntity): self._device_info = device_info @property - def unique_id(self) -> str: + def unique_id(self): """Return the unique ID of the device.""" - if self._device_info is not None: - return self._device_info[ATTR_UDN] - return None + if self._device_info is None: + return None + return self._device_info[ATTR_UDN] @property def device_info(self): @@ -83,8 +85,10 @@ class PanasonicVieraTVEntity(MediaPlayerEntity): return { "name": self._name, "identifiers": {(DOMAIN, self._device_info[ATTR_UDN])}, - "manufacturer": self._device_info[ATTR_MANUFACTURER], - "model": self._device_info[ATTR_MODEL_NUMBER], + "manufacturer": self._device_info.get( + ATTR_MANUFACTURER, DEFAULT_MANUFACTURER + ), + "model": self._device_info.get(ATTR_MODEL_NUMBER, DEFAULT_MODEL_NUMBER), } @property diff --git a/tests/components/panasonic_viera/test_config_flow.py b/tests/components/panasonic_viera/test_config_flow.py index f3d1f1cc8f1..15e6b73202d 100644 --- a/tests/components/panasonic_viera/test_config_flow.py +++ b/tests/components/panasonic_viera/test_config_flow.py @@ -12,6 +12,8 @@ from homeassistant.components.panasonic_viera.const import ( CONF_APP_ID, CONF_ENCRYPTION_KEY, CONF_ON_ACTION, + DEFAULT_MANUFACTURER, + DEFAULT_MODEL_NUMBER, DEFAULT_NAME, DEFAULT_PORT, DOMAIN, @@ -37,13 +39,14 @@ def panasonic_viera_setup_fixture(): def get_mock_remote( host="1.2.3.4", + request_error=None, authorize_error=None, encrypted=False, app_id=None, encryption_key=None, name=DEFAULT_NAME, - manufacturer="mock-manufacturer", - model_number="mock-model-number", + manufacturer=DEFAULT_MANUFACTURER, + model_number=DEFAULT_MODEL_NUMBER, unique_id="mock-unique-id", ): """Return a mock remote.""" @@ -54,7 +57,8 @@ def get_mock_remote( mock_remote.enc_key = encryption_key def request_pin_code(name=None): - return + if request_error is not None: + raise request_error mock_remote.request_pin_code = request_pin_code @@ -110,8 +114,8 @@ async def test_flow_non_encrypted(hass): CONF_ON_ACTION: None, ATTR_DEVICE_INFO: { ATTR_FRIENDLY_NAME: DEFAULT_NAME, - ATTR_MANUFACTURER: "mock-manufacturer", - ATTR_MODEL_NUMBER: "mock-model-number", + ATTR_MANUFACTURER: DEFAULT_MANUFACTURER, + ATTR_MODEL_NUMBER: DEFAULT_MODEL_NUMBER, ATTR_UDN: "mock-unique-id", }, } @@ -164,6 +168,56 @@ async def test_flow_unknown_abort(hass): assert result["reason"] == "unknown" +async def test_flow_encrypted_not_connected_pin_code_request(hass): + """Test flow with encryption and PIN code request connection error abortion during pairing request step.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == "form" + assert result["step_id"] == "user" + + mock_remote = get_mock_remote(encrypted=True, request_error=TimeoutError) + + with patch( + "homeassistant.components.panasonic_viera.config_flow.RemoteControl", + return_value=mock_remote, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_HOST: "1.2.3.4", CONF_NAME: DEFAULT_NAME}, + ) + + assert result["type"] == "abort" + assert result["reason"] == "cannot_connect" + + +async def test_flow_encrypted_unknown_pin_code_request(hass): + """Test flow with encryption and PIN code request unknown error abortion during pairing request step.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == "form" + assert result["step_id"] == "user" + + mock_remote = get_mock_remote(encrypted=True, request_error=Exception) + + with patch( + "homeassistant.components.panasonic_viera.config_flow.RemoteControl", + return_value=mock_remote, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_HOST: "1.2.3.4", CONF_NAME: DEFAULT_NAME}, + ) + + assert result["type"] == "abort" + assert result["reason"] == "unknown" + + async def test_flow_encrypted_valid_pin_code(hass): """Test flow with encryption and valid PIN code.""" @@ -208,8 +262,8 @@ async def test_flow_encrypted_valid_pin_code(hass): CONF_ENCRYPTION_KEY: "test-encryption-key", ATTR_DEVICE_INFO: { ATTR_FRIENDLY_NAME: DEFAULT_NAME, - ATTR_MANUFACTURER: "mock-manufacturer", - ATTR_MODEL_NUMBER: "mock-model-number", + ATTR_MANUFACTURER: DEFAULT_MANUFACTURER, + ATTR_MODEL_NUMBER: DEFAULT_MODEL_NUMBER, ATTR_UDN: "mock-unique-id", }, } @@ -392,8 +446,8 @@ async def test_imported_flow_non_encrypted(hass): CONF_ON_ACTION: "test-on-action", ATTR_DEVICE_INFO: { ATTR_FRIENDLY_NAME: DEFAULT_NAME, - ATTR_MANUFACTURER: "mock-manufacturer", - ATTR_MODEL_NUMBER: "mock-model-number", + ATTR_MANUFACTURER: DEFAULT_MANUFACTURER, + ATTR_MODEL_NUMBER: DEFAULT_MODEL_NUMBER, ATTR_UDN: "mock-unique-id", }, } @@ -442,8 +496,8 @@ async def test_imported_flow_encrypted_valid_pin_code(hass): CONF_ENCRYPTION_KEY: "test-encryption-key", ATTR_DEVICE_INFO: { ATTR_FRIENDLY_NAME: DEFAULT_NAME, - ATTR_MANUFACTURER: "mock-manufacturer", - ATTR_MODEL_NUMBER: "mock-model-number", + ATTR_MANUFACTURER: DEFAULT_MANUFACTURER, + ATTR_MODEL_NUMBER: DEFAULT_MODEL_NUMBER, ATTR_UDN: "mock-unique-id", }, } diff --git a/tests/components/panasonic_viera/test_init.py b/tests/components/panasonic_viera/test_init.py index 70b8db656e1..3ac6b7e12da 100644 --- a/tests/components/panasonic_viera/test_init.py +++ b/tests/components/panasonic_viera/test_init.py @@ -8,6 +8,8 @@ from homeassistant.components.panasonic_viera.const import ( CONF_APP_ID, CONF_ENCRYPTION_KEY, CONF_ON_ACTION, + DEFAULT_MANUFACTURER, + DEFAULT_MODEL_NUMBER, DEFAULT_NAME, DEFAULT_PORT, DOMAIN, @@ -33,8 +35,8 @@ MOCK_ENCRYPTION_DATA = { MOCK_DEVICE_INFO = { ATTR_FRIENDLY_NAME: DEFAULT_NAME, - ATTR_MANUFACTURER: "mock-manufacturer", - ATTR_MODEL_NUMBER: "mock-model-number", + ATTR_MANUFACTURER: DEFAULT_MANUFACTURER, + ATTR_MODEL_NUMBER: DEFAULT_MODEL_NUMBER, ATTR_UDN: "mock-unique-id", } From 8da732a89ddb5fc1a565d08f7ee7430c3e92e359 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 27 Oct 2020 11:04:11 +0100 Subject: [PATCH 059/118] Fix hassio-version (#42449) --- azure-pipelines-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines-release.yml b/azure-pipelines-release.yml index 4668e62892a..2420c2e44a0 100644 --- a/azure-pipelines-release.yml +++ b/azure-pipelines-release.yml @@ -143,8 +143,8 @@ stages: version="$(homeassistantRelease)" - git clone https://github.com/home-assistant/hassio-version - cd hassio-version + git clone https://github.com/home-assistant/version + cd version dev_version="$(jq --raw-output '.homeassistant.default' dev.json)" beta_version="$(jq --raw-output '.homeassistant.default' beta.json)" From 1962a7c7ceb496f7ac3e96ba9122f93164e0f03a Mon Sep 17 00:00:00 2001 From: Quentame Date: Tue, 27 Oct 2020 14:50:20 +0100 Subject: [PATCH 060/118] Fix DSM Surveillance Station when only home mode is enabled (#42456) --- homeassistant/components/synology_dsm/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/synology_dsm/__init__.py b/homeassistant/components/synology_dsm/__init__.py index c77ea1d2490..44a68b4c18c 100644 --- a/homeassistant/components/synology_dsm/__init__.py +++ b/homeassistant/components/synology_dsm/__init__.py @@ -316,6 +316,8 @@ class SynoApi: ) self._with_surveillance_station = bool( self._fetching_entities.get(SynoSurveillanceStation.CAMERA_API_KEY) + ) or bool( + self._fetching_entities.get(SynoSurveillanceStation.HOME_MODE_API_KEY) ) # Reset not used API, information is not reset since it's used in device_info From 45c1c2acac34168fde500e88550535d18d7beeab Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 27 Oct 2020 15:08:56 +0100 Subject: [PATCH 061/118] Fix initial state of pi4ioe (#42459) --- homeassistant/components/pi4ioe5v9xxxx/switch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/pi4ioe5v9xxxx/switch.py b/homeassistant/components/pi4ioe5v9xxxx/switch.py index 6bac673459f..81de76c086c 100644 --- a/homeassistant/components/pi4ioe5v9xxxx/switch.py +++ b/homeassistant/components/pi4ioe5v9xxxx/switch.py @@ -55,7 +55,7 @@ class Pi4ioe5v9Switch(SwitchEntity): self._name = name or DEVICE_DEFAULT_NAME self._pin = pin self._invert_logic = invert_logic - self._state = False + self._state = not invert_logic @property def name(self): From ba6acc286c3e8de14478887d7833087f68498aaf Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 27 Oct 2020 23:01:13 +0100 Subject: [PATCH 062/118] Bump hatasmota to 0.0.24 (#42472) --- homeassistant/components/tasmota/discovery.py | 3 + .../components/tasmota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../components/tasmota/test_binary_sensor.py | 24 ++++++ .../components/tasmota/test_device_trigger.py | 50 ++++++++++++ tests/components/tasmota/test_light.py | 77 +++++++++++++++++-- 7 files changed, 152 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/tasmota/discovery.py b/homeassistant/components/tasmota/discovery.py index 6d224a98707..06a88333230 100644 --- a/homeassistant/components/tasmota/discovery.py +++ b/homeassistant/components/tasmota/discovery.py @@ -29,6 +29,9 @@ TASMOTA_DISCOVERY_INSTANCE = "tasmota_discovery_instance" def clear_discovery_hash(hass, discovery_hash): """Clear entry in ALREADY_DISCOVERED list.""" + if ALREADY_DISCOVERED not in hass.data: + # Discovery is shutting down + return del hass.data[ALREADY_DISCOVERED][discovery_hash] diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 8cc3006c63a..991e38e6a95 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota (beta)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.0.23"], + "requirements": ["hatasmota==0.0.24"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"] diff --git a/requirements_all.txt b/requirements_all.txt index aa488f262d9..1a51d5313f1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -732,7 +732,7 @@ hass-nabucasa==0.37.1 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.0.23 +hatasmota==0.0.24 # homeassistant.components.jewish_calendar hdate==0.9.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 24907d07213..ca30214d585 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -367,7 +367,7 @@ hangups==0.4.11 hass-nabucasa==0.37.1 # homeassistant.components.tasmota -hatasmota==0.0.23 +hatasmota==0.0.24 # homeassistant.components.jewish_calendar hdate==0.9.12 diff --git a/tests/components/tasmota/test_binary_sensor.py b/tests/components/tasmota/test_binary_sensor.py index 69d49a6ca8f..52ab88b0ecb 100644 --- a/tests/components/tasmota/test_binary_sensor.py +++ b/tests/components/tasmota/test_binary_sensor.py @@ -92,6 +92,30 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): assert state.state == STATE_OFF +async def test_friendly_names(hass, mqtt_mock, setup_tasmota): + """Test state update via MQTT.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"][0] = 1 + config["swc"][0] = 1 + config["swc"][1] = 1 + mac = config["mac"] + + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + + state = hass.states.get("binary_sensor.tasmota_binary_sensor_1") + assert state.state == "unavailable" + assert state.attributes.get("friendly_name") == "Tasmota binary_sensor 1" + + state = hass.states.get("binary_sensor.beer") + assert state.state == "unavailable" + assert state.attributes.get("friendly_name") == "Beer" + + async def test_off_delay(hass, mqtt_mock, setup_tasmota): """Test off_delay option.""" config = copy.deepcopy(DEFAULT_CONFIG) diff --git a/tests/components/tasmota/test_device_trigger.py b/tests/components/tasmota/test_device_trigger.py index 9d6d1cac793..35f3b4be9d8 100644 --- a/tests/components/tasmota/test_device_trigger.py +++ b/tests/components/tasmota/test_device_trigger.py @@ -830,3 +830,53 @@ async def test_attach_unknown_remove_device_from_registry( # Remove the device device_reg.async_remove_device(device_entry.id) await hass.async_block_till_done() + + +async def test_attach_remove_config_entry(hass, mqtt_mock, setup_tasmota, device_reg): + """Test trigger cleanup when removing a Tasmota config entry.""" + # Discover a device with device trigger + config = copy.deepcopy(DEFAULT_CONFIG) + config["swc"][0] = 0 + mac = config["mac"] + + mqtt_mock.async_subscribe.reset_mock() + + async_fire_mqtt_message(hass, f"{DEFAULT_PREFIX}/{mac}/config", json.dumps(config)) + await hass.async_block_till_done() + + device_entry = device_reg.async_get_device(set(), {("mac", mac)}) + + calls = [] + + def callback(trigger, context): + calls.append(trigger["trigger"]["description"]) + + await async_attach_trigger( + hass, + { + "platform": "device", + "domain": DOMAIN, + "device_id": device_entry.id, + "discovery_id": "00000049A3BC_switch_1_TOGGLE", + "type": "button_short_press", + "subtype": "switch_1", + }, + callback, + None, + ) + + # Fake short press. + async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/SWITCH1T", '{"TRIG":"TOGGLE"}') + await hass.async_block_till_done() + assert len(calls) == 1 + assert calls[0] == "event 'tasmota_event'" + + # Remove the Tasmota config entry + config_entries = hass.config_entries.async_entries("tasmota") + await hass.config_entries.async_remove(config_entries[0].entry_id) + await hass.async_block_till_done() + + # Verify the triggers are no longer active + async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/SWITCH1T", '{"TRIG":"TOGGLE"}') + await hass.async_block_till_done() + assert len(calls) == 1 diff --git a/tests/components/tasmota/test_light.py b/tests/components/tasmota/test_light.py index f9d839516e8..48b1dec7232 100644 --- a/tests/components/tasmota/test_light.py +++ b/tests/components/tasmota/test_light.py @@ -482,7 +482,7 @@ async def test_sending_mqtt_commands_on_off(hass, mqtt_mock, setup_tasmota): # Turn the light on and verify MQTT message is sent await common.async_turn_on(hass, "light.test") mqtt_mock.async_publish.assert_called_once_with( - "tasmota_49A3BC/cmnd/Backlog", "NoDelay;Fade 0;NoDelay;Power1 ON", 0, False + "tasmota_49A3BC/cmnd/Power1", "ON", 0, False ) mqtt_mock.async_publish.reset_mock() @@ -493,7 +493,7 @@ async def test_sending_mqtt_commands_on_off(hass, mqtt_mock, setup_tasmota): # Turn the light off and verify MQTT message is sent await common.async_turn_off(hass, "light.test") mqtt_mock.async_publish.assert_called_once_with( - "tasmota_49A3BC/cmnd/Backlog", "NoDelay;Fade 0;NoDelay;Power1 OFF", 0, False + "tasmota_49A3BC/cmnd/Power1", "OFF", 0, False ) mqtt_mock.async_publish.reset_mock() @@ -581,6 +581,57 @@ async def test_sending_mqtt_commands_rgbww(hass, mqtt_mock, setup_tasmota): mqtt_mock.async_publish.reset_mock() +async def test_sending_mqtt_commands_power_unlinked(hass, mqtt_mock, setup_tasmota): + """Test the sending MQTT commands to a light with unlinked dimlevel and power.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"][0] = 2 + config["lt_st"] = 1 # 1 channel light (dimmer) + config["so"]["20"] = 1 # Update of Dimmer/Color/CT without turning power on + mac = config["mac"] + + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") + state = hass.states.get("light.test") + assert state.state == STATE_OFF + await hass.async_block_till_done() + await hass.async_block_till_done() + mqtt_mock.async_publish.reset_mock() + + # Turn the light on and verify MQTT message is sent + await common.async_turn_on(hass, "light.test") + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/Backlog", "NoDelay;Fade 0;NoDelay;Power1 ON", 0, False + ) + mqtt_mock.async_publish.reset_mock() + + # Tasmota is not optimistic, the state should still be off + state = hass.states.get("light.test") + assert state.state == STATE_OFF + + # Turn the light off and verify MQTT message is sent + await common.async_turn_off(hass, "light.test") + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/Backlog", "NoDelay;Fade 0;NoDelay;Power1 OFF", 0, False + ) + mqtt_mock.async_publish.reset_mock() + + # Turn the light on and verify MQTT messages are sent; POWER should be sent + await common.async_turn_on(hass, "light.test", brightness=192) + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/Backlog", + "NoDelay;Fade 0;NoDelay;Dimmer 75;NoDelay;Power1 ON", + 0, + False, + ) + mqtt_mock.async_publish.reset_mock() + + async def test_transition(hass, mqtt_mock, setup_tasmota): """Test transition commands.""" config = copy.deepcopy(DEFAULT_CONFIG) @@ -733,7 +784,7 @@ async def test_split_light2(hass, mqtt_mock, setup_tasmota): async def _test_unlinked_light(hass, mqtt_mock, config, num_switches): - """Test multi-channel light split to single-channel dimmers.""" + """Test rgbww light split to rgb+ww.""" mac = config["mac"] num_lights = 2 @@ -775,7 +826,7 @@ async def _test_unlinked_light(hass, mqtt_mock, config, num_switches): async def test_unlinked_light(hass, mqtt_mock, setup_tasmota): - """Test multi-channel light split to rgb+ww.""" + """Test rgbww light split to rgb+ww.""" config = copy.deepcopy(DEFAULT_CONFIG) config["rl"][0] = 2 config["rl"][1] = 2 @@ -786,7 +837,7 @@ async def test_unlinked_light(hass, mqtt_mock, setup_tasmota): async def test_unlinked_light2(hass, mqtt_mock, setup_tasmota): - """Test multi-channel light split to single-channel dimmers.""" + """Test rgbww light split to rgb+ww.""" config = copy.deepcopy(DEFAULT_CONFIG) config["rl"][0] = 1 config["rl"][1] = 1 @@ -899,6 +950,22 @@ async def test_discovery_removal_relay_as_light(hass, mqtt_mock, caplog, setup_t ) +async def test_discovery_removal_relay_as_light2( + hass, mqtt_mock, caplog, setup_tasmota +): + """Test removal of discovered relay as light.""" + config1 = copy.deepcopy(DEFAULT_CONFIG) + config1["rl"][0] = 1 + config1["so"]["30"] = 1 # Enforce Home Assistant auto-discovery as light + config2 = copy.deepcopy(DEFAULT_CONFIG) + config2["rl"][0] = 0 + config2["so"]["30"] = 0 # Disable Home Assistant auto-discovery as light + + await help_test_discovery_removal( + hass, mqtt_mock, caplog, light.DOMAIN, config1, config2 + ) + + async def test_discovery_update_unchanged_light(hass, mqtt_mock, caplog, setup_tasmota): """Test update of discovered light.""" config = copy.deepcopy(DEFAULT_CONFIG) From 2effbb66048518cfc0db467fa83929d204e97585 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 27 Oct 2020 23:00:39 +0100 Subject: [PATCH 063/118] Update frontend to 20201021.3 (#42486) --- 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 f02e9a4ec3a..ee27bb08d74 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==20201021.2"], + "requirements": ["home-assistant-frontend==20201021.3"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 41211fafb73..0b5fb019cd6 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -13,7 +13,7 @@ defusedxml==0.6.0 distro==1.5.0 emoji==0.5.4 hass-nabucasa==0.37.1 -home-assistant-frontend==20201021.2 +home-assistant-frontend==20201021.3 httpx==0.16.1 importlib-metadata==1.6.0;python_version<'3.8' jinja2>=2.11.2 diff --git a/requirements_all.txt b/requirements_all.txt index 1a51d5313f1..cab26593191 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -759,7 +759,7 @@ hole==0.5.1 holidays==0.10.3 # homeassistant.components.frontend -home-assistant-frontend==20201021.2 +home-assistant-frontend==20201021.3 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ca30214d585..5d3df986871 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -385,7 +385,7 @@ hole==0.5.1 holidays==0.10.3 # homeassistant.components.frontend -home-assistant-frontend==20201021.2 +home-assistant-frontend==20201021.3 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 From cb453d3ae1209125459af35d03a3ff4138b07f4c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 27 Oct 2020 22:51:58 +0100 Subject: [PATCH 064/118] Make result wrappers more robust (#42488) --- homeassistant/helpers/template.py | 16 ++++++++++------ tests/helpers/test_template.py | 13 ++++++++----- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 75846100161..a314fcb07ef 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -127,7 +127,7 @@ def is_template_string(maybe_template: str) -> bool: class ResultWrapper: """Result wrapper class to store render result.""" - render_result: str + render_result: Optional[str] def gen_result_wrapper(kls): @@ -136,8 +136,8 @@ def gen_result_wrapper(kls): class Wrapper(kls, ResultWrapper): """Wrapper of a kls that can store render_result.""" - def __init__(self, value: kls, render_result: str) -> None: - super().__init__(value) + def __init__(self, *args: tuple, render_result: Optional[str] = None) -> None: + super().__init__(*args) self.render_result = render_result return Wrapper @@ -148,13 +148,15 @@ class TupleWrapper(tuple, ResultWrapper): # This is all magic to be allowed to subclass a tuple. - def __new__(cls, value: tuple, render_result: str) -> "TupleWrapper": + def __new__( + cls, value: tuple, *, render_result: Optional[str] = None + ) -> "TupleWrapper": """Create a new tuple class.""" return super().__new__(cls, tuple(value)) # pylint: disable=super-init-not-called - def __init__(self, value: tuple, render_result: str): + def __init__(self, value: tuple, *, render_result: Optional[str] = None): """Initialize a new tuple class.""" self.render_result = render_result @@ -416,7 +418,9 @@ class Template: result = literal_eval(render_result) if type(result) in RESULT_WRAPPERS: - result = RESULT_WRAPPERS[type(result)](result, render_result) + result = RESULT_WRAPPERS[type(result)]( + result, render_result=render_result + ) # If the literal_eval result is a string, use the original # render, by not returning right here. The evaluation of strings diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 750978b84ee..856fc1495e5 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -5,6 +5,7 @@ import random import pytest import pytz +import voluptuous as vol from homeassistant.components import group from homeassistant.config import async_process_ha_core_config @@ -2684,14 +2685,16 @@ async def test_is_static_still_ast_evals(hass): async def test_result_wrappers(hass): """Test result wrappers.""" - for text, native in ( - ("[1, 2]", [1, 2]), - ("{1, 2}", {1, 2}), - ("(1, 2)", (1, 2)), - ('{"hello": True}', {"hello": True}), + for text, native, orig_type, schema in ( + ("[1, 2]", [1, 2], list, vol.Schema([int])), + ("{1, 2}", {1, 2}, set, vol.Schema({int})), + ("(1, 2)", (1, 2), tuple, vol.ExactSequence([int, int])), + ('{"hello": True}', {"hello": True}, dict, vol.Schema({"hello": bool})), ): tpl = template.Template(text, hass) result = tpl.async_render() + assert isinstance(result, orig_type) assert isinstance(result, template.ResultWrapper) assert result == native assert result.render_result == text + schema(result) # should not raise From 7dcebbe89d219e4301b164bb6b64d62eb14ab9bc Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 27 Oct 2020 22:59:49 +0100 Subject: [PATCH 065/118] Bump cryptography to 3.2 (#42489) --- homeassistant/package_constraints.txt | 2 +- requirements.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 0b5fb019cd6..10cb7858aba 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -8,7 +8,7 @@ attrs==19.3.0 bcrypt==3.1.7 certifi>=2020.6.20 ciso8601==2.1.3 -cryptography==2.9.2 +cryptography==3.2.0 defusedxml==0.6.0 distro==1.5.0 emoji==0.5.4 diff --git a/requirements.txt b/requirements.txt index 5bee6b7f944..1586fdddee4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ httpx==0.16.1 importlib-metadata==1.6.0;python_version<'3.8' jinja2>=2.11.2 PyJWT==1.7.1 -cryptography==2.9.2 +cryptography==3.2.0 pip>=8.0.3 python-slugify==4.0.1 pytz>=2020.1 diff --git a/setup.py b/setup.py index 490a7da56b8..e501ac6cf12 100755 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ REQUIRES = [ "jinja2>=2.11.2", "PyJWT==1.7.1", # PyJWT has loose dependency. We want the latest one. - "cryptography==2.9.2", + "cryptography==3.2.0", "pip>=8.0.3", "python-slugify==4.0.1", "pytz>=2020.1", From 40be42299df640629e092d551da44ad0d0b5c5bf Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 27 Oct 2020 23:22:59 +0100 Subject: [PATCH 066/118] Improve mqtt test (#42490) --- homeassistant/helpers/template.py | 2 +- tests/components/mqtt/test_init.py | 8 ++++++-- tests/helpers/test_template.py | 10 ++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index a314fcb07ef..0614b3c3b4f 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -428,7 +428,7 @@ class Template: # output; use the original render instead of the evaluated one. if not isinstance(result, str): return result - except (ValueError, SyntaxError, MemoryError): + except (ValueError, TypeError, SyntaxError, MemoryError): pass return render_result diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 7a833a72d51..39ea8bc92ec 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1502,6 +1502,7 @@ async def test_debug_info_qos_retain(hass, mqtt_mock): async def test_publish_json_from_template(hass, mqtt_mock): """Test the publishing of call to services.""" test_str = "{'valid': 'python', 'invalid': 'json'}" + test_str_tpl = "{'valid': '{{ \"python\" }}', 'invalid': 'json'}" await async_setup_component( hass, @@ -1511,13 +1512,16 @@ async def test_publish_json_from_template(hass, mqtt_mock): "test_script_payload": { "sequence": { "service": "mqtt.publish", - "data": {"topic": "test-topic", "payload": test_str}, + "data": {"topic": "test-topic", "payload": test_str_tpl}, } }, "test_script_payload_template": { "sequence": { "service": "mqtt.publish", - "data": {"topic": "test-topic", "payload_template": test_str}, + "data": { + "topic": "test-topic", + "payload_template": test_str_tpl, + }, } }, } diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 856fc1495e5..9ac546c6a75 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -2698,3 +2698,13 @@ async def test_result_wrappers(hass): assert result == native assert result.render_result == text schema(result) # should not raise + + +async def test_parse_result(hass): + """Test parse result.""" + for tpl, result in ( + ('{{ "{{}}" }}', "{{}}"), + ("not-something", "not-something"), + ("2a", "2a"), + ): + assert template.Template(tpl, hass).async_render() == result From a88e011bba29831925147358d3070e29275b865c Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 27 Oct 2020 23:24:54 +0100 Subject: [PATCH 067/118] Fix race in Tasmota discovery (#42492) --- homeassistant/components/tasmota/__init__.py | 29 ++++++++----------- homeassistant/components/tasmota/discovery.py | 7 ++--- .../components/tasmota/test_device_trigger.py | 2 +- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/tasmota/__init__.py b/homeassistant/components/tasmota/__init__.py index 5754b98b71e..a82d95474cc 100644 --- a/homeassistant/components/tasmota/__init__.py +++ b/homeassistant/components/tasmota/__init__.py @@ -19,12 +19,10 @@ from homeassistant.components.mqtt.subscription import ( ) from homeassistant.core import callback from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import HomeAssistantType from . import device_automation, discovery from .const import CONF_DISCOVERY_PREFIX, DATA_REMOVE_DISCOVER_COMPONENT, PLATFORMS -from .discovery import TASMOTA_DISCOVERY_DEVICE _LOGGER = logging.getLogger(__name__) @@ -55,13 +53,11 @@ async def async_setup_entry(hass, entry): tasmota_mqtt = TasmotaMQTTClient(_publish, _subscribe_topics, _unsubscribe_topics) - async def async_discover_device(config, mac): - """Discover and add a Tasmota device.""" - await async_setup_device(hass, mac, config, entry, tasmota_mqtt) + device_registry = await hass.helpers.device_registry.async_get_registry() - hass.data[ - DATA_REMOVE_DISCOVER_COMPONENT.format("device") - ] = async_dispatcher_connect(hass, TASMOTA_DISCOVERY_DEVICE, async_discover_device) + def async_discover_device(config, mac): + """Discover and add a Tasmota device.""" + async_setup_device(hass, mac, config, entry, tasmota_mqtt, device_registry) async def start_platforms(): await device_automation.async_setup_entry(hass, entry) @@ -73,7 +69,9 @@ async def async_setup_entry(hass, entry): ) discovery_prefix = entry.data[CONF_DISCOVERY_PREFIX] - await discovery.async_start(hass, discovery_prefix, entry, tasmota_mqtt) + await discovery.async_start( + hass, discovery_prefix, entry, tasmota_mqtt, async_discover_device + ) hass.async_create_task(start_platforms()) return True @@ -97,7 +95,6 @@ async def async_unload_entry(hass, entry): # disable discovery await discovery.async_stop(hass) hass.data.pop(DEVICE_MACS) - hass.data[DATA_REMOVE_DISCOVER_COMPONENT.format("device")]() hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format("device_automation"))() for component in PLATFORMS: hass.data.pop(DATA_REMOVE_DISCOVER_COMPONENT.format(component))() @@ -105,9 +102,8 @@ async def async_unload_entry(hass, entry): return True -async def _remove_device(hass, config_entry, mac, tasmota_mqtt): +def _remove_device(hass, config_entry, mac, tasmota_mqtt, device_registry): """Remove device from device registry.""" - device_registry = await hass.helpers.device_registry.async_get_registry() device = device_registry.async_get_device(set(), {(CONNECTION_NETWORK_MAC, mac)}) if device is None: @@ -118,9 +114,8 @@ async def _remove_device(hass, config_entry, mac, tasmota_mqtt): clear_discovery_topic(mac, config_entry.data[CONF_DISCOVERY_PREFIX], tasmota_mqtt) -async def _update_device(hass, config_entry, config): +def _update_device(hass, config_entry, config, device_registry): """Add or update device registry.""" - device_registry = await hass.helpers.device_registry.async_get_registry() config_entry_id = config_entry.entry_id device_info = { "connections": {(CONNECTION_NETWORK_MAC, config[CONF_MAC])}, @@ -135,9 +130,9 @@ async def _update_device(hass, config_entry, config): hass.data[DEVICE_MACS][device.id] = config[CONF_MAC] -async def async_setup_device(hass, mac, config, config_entry, tasmota_mqtt): +def async_setup_device(hass, mac, config, config_entry, tasmota_mqtt, device_registry): """Set up the Tasmota device.""" if not config: - await _remove_device(hass, config_entry, mac, tasmota_mqtt) + _remove_device(hass, config_entry, mac, tasmota_mqtt, device_registry) else: - await _update_device(hass, config_entry, config) + _update_device(hass, config_entry, config, device_registry) diff --git a/homeassistant/components/tasmota/discovery.py b/homeassistant/components/tasmota/discovery.py index 06a88333230..2313a8327c5 100644 --- a/homeassistant/components/tasmota/discovery.py +++ b/homeassistant/components/tasmota/discovery.py @@ -21,7 +21,6 @@ from .const import DOMAIN, PLATFORMS _LOGGER = logging.getLogger(__name__) ALREADY_DISCOVERED = "tasmota_discovered_components" -TASMOTA_DISCOVERY_DEVICE = "tasmota_discovery_device" TASMOTA_DISCOVERY_ENTITY_NEW = "tasmota_discovery_entity_new_{}" TASMOTA_DISCOVERY_ENTITY_UPDATED = "tasmota_discovery_entity_updated_{}_{}_{}_{}" TASMOTA_DISCOVERY_INSTANCE = "tasmota_discovery_instance" @@ -41,7 +40,7 @@ def set_discovery_hash(hass, discovery_hash): async def async_start( - hass: HomeAssistantType, discovery_topic, config_entry, tasmota_mqtt + hass: HomeAssistantType, discovery_topic, config_entry, tasmota_mqtt, setup_device ) -> bool: """Start Tasmota device discovery.""" @@ -95,9 +94,7 @@ async def async_start( _LOGGER.debug("Received discovery data for tasmota device: %s", mac) tasmota_device_config = tasmota_get_device_config(payload) - async_dispatcher_send( - hass, TASMOTA_DISCOVERY_DEVICE, tasmota_device_config, mac - ) + setup_device(tasmota_device_config, mac) if not payload: return diff --git a/tests/components/tasmota/test_device_trigger.py b/tests/components/tasmota/test_device_trigger.py index 35f3b4be9d8..b027b2c095d 100644 --- a/tests/components/tasmota/test_device_trigger.py +++ b/tests/components/tasmota/test_device_trigger.py @@ -832,7 +832,7 @@ async def test_attach_unknown_remove_device_from_registry( await hass.async_block_till_done() -async def test_attach_remove_config_entry(hass, mqtt_mock, setup_tasmota, device_reg): +async def test_attach_remove_config_entry(hass, device_reg, mqtt_mock, setup_tasmota): """Test trigger cleanup when removing a Tasmota config entry.""" # Discover a device with device trigger config = copy.deepcopy(DEFAULT_CONFIG) From 5a460e609fd7fd0bc045f9b88a004bd83479bc30 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 27 Oct 2020 22:26:04 +0000 Subject: [PATCH 068/118] Bumped version to 0.117.0b6 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index cebee253df0..a1ca4783640 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "0b5" +PATCH_VERSION = "0b6" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 68d2938c6b0c32101099ae4ac304b519d3cd8928 Mon Sep 17 00:00:00 2001 From: Angelo Gagliano <25516409+TheGardenMonkey@users.noreply.github.com> Date: Wed, 28 Oct 2020 04:51:53 -0400 Subject: [PATCH 069/118] Move async_add_entities back to event loop for tplink component (#42454) --- homeassistant/components/tplink/common.py | 12 +++++------- homeassistant/components/tplink/light.py | 7 +++++-- homeassistant/components/tplink/switch.py | 7 +++++-- tests/components/tplink/test_light.py | 11 +++++++++-- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/tplink/common.py b/homeassistant/components/tplink/common.py index a8ba48e3935..1aca4bf7edc 100644 --- a/homeassistant/components/tplink/common.py +++ b/homeassistant/components/tplink/common.py @@ -131,7 +131,7 @@ def get_static_devices(config_data) -> SmartDevices: return SmartDevices(lights, switches) -def add_available_devices(hass, device_type, device_class, async_add_entities): +def add_available_devices(hass, device_type, device_class): """Get sysinfo for all devices.""" devices = hass.data[TPLINK_DOMAIN][device_type] @@ -140,20 +140,18 @@ def add_available_devices(hass, device_type, device_class, async_add_entities): devices = hass.data[TPLINK_DOMAIN][f"{device_type}_remaining"] entities_ready = [] - entities_unavailable = [] + devices_unavailable = [] for device in devices: try: device.get_sysinfo() entities_ready.append(device_class(device)) except SmartDeviceException as ex: - entities_unavailable.append(device) + devices_unavailable.append(device) _LOGGER.warning( "Unable to communicate with device %s: %s", device.host, ex, ) - hass.data[TPLINK_DOMAIN][f"{device_type}_remaining"] = entities_unavailable - - if entities_ready: - async_add_entities(entities_ready, update_before_add=True) + hass.data[TPLINK_DOMAIN][f"{device_type}_remaining"] = devices_unavailable + return entities_ready diff --git a/homeassistant/components/tplink/light.py b/homeassistant/components/tplink/light.py index e2acf3fcd90..ceb0944efe6 100644 --- a/homeassistant/components/tplink/light.py +++ b/homeassistant/components/tplink/light.py @@ -61,10 +61,13 @@ SLEEP_TIME = 2 async def async_setup_entry(hass: HomeAssistantType, config_entry, async_add_entities): """Set up lights.""" - await hass.async_add_executor_job( - add_available_devices, hass, CONF_LIGHT, TPLinkSmartBulb, async_add_entities + entities = await hass.async_add_executor_job( + add_available_devices, hass, CONF_LIGHT, TPLinkSmartBulb ) + if entities: + async_add_entities(entities, update_before_add=True) + if hass.data[TPLINK_DOMAIN][f"{CONF_LIGHT}_remaining"]: raise PlatformNotReady diff --git a/homeassistant/components/tplink/switch.py b/homeassistant/components/tplink/switch.py index b7e31e82e78..23000fe7b59 100644 --- a/homeassistant/components/tplink/switch.py +++ b/homeassistant/components/tplink/switch.py @@ -31,10 +31,13 @@ SLEEP_TIME = 2 async def async_setup_entry(hass: HomeAssistantType, config_entry, async_add_entities): """Set up switches.""" - await hass.async_add_executor_job( - add_available_devices, hass, CONF_SWITCH, SmartPlugSwitch, async_add_entities + entities = await hass.async_add_executor_job( + add_available_devices, hass, CONF_SWITCH, SmartPlugSwitch ) + if entities: + async_add_entities(entities, update_before_add=True) + if hass.data[TPLINK_DOMAIN][f"{CONF_SWITCH}_remaining"]: raise PlatformNotReady diff --git a/tests/components/tplink/test_light.py b/tests/components/tplink/test_light.py index 4987f4f26cb..48812f8fb2b 100644 --- a/tests/components/tplink/test_light.py +++ b/tests/components/tplink/test_light.py @@ -1,4 +1,5 @@ """Tests for light platform.""" +from datetime import timedelta import logging from typing import Callable, NamedTuple @@ -30,8 +31,10 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component +from homeassistant.util.dt import utcnow from tests.async_mock import Mock, PropertyMock, patch +from tests.common import async_fire_time_changed class LightMockData(NamedTuple): @@ -605,5 +608,9 @@ async def test_async_setup_entry_unavailable( ) await hass.async_block_till_done() - assert "Unable to communicate with device 123.123.123.123" in caplog.text - assert len(hass.data[tplink.DOMAIN][f"{CONF_LIGHT}_remaining"]) == 1 + assert not hass.states.get("light.light1") + + future = utcnow() + timedelta(seconds=30) + async_fire_time_changed(hass, future) + await hass.async_block_till_done() + assert hass.states.get("light.light1") From 5b857765664bb74b6b66d05f096dc5156cecdebf Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 28 Oct 2020 09:11:08 +0100 Subject: [PATCH 070/118] Fix string representation of template result wrappers (#42494) --- homeassistant/components/template/cover.py | 6 +----- homeassistant/components/template/fan.py | 6 +----- homeassistant/components/template/light.py | 3 +-- homeassistant/helpers/template.py | 17 +++++++++++++++++ tests/helpers/test_template.py | 6 ++++++ 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/template/cover.py b/homeassistant/components/template/cover.py index cc91bdabd7d..93ffd2fd988 100644 --- a/homeassistant/components/template/cover.py +++ b/homeassistant/components/template/cover.py @@ -39,7 +39,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.script import Script -from homeassistant.helpers.template import ResultWrapper from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS from .template_entity import TemplateEntity @@ -259,10 +258,7 @@ class CoverTemplate(TemplateEntity, CoverEntity): self._position = None return - if isinstance(result, ResultWrapper): - state = result.render_result.lower() - else: - state = str(result).lower() + state = str(result).lower() if state in _VALID_STATES: if state in ("true", STATE_OPEN): diff --git a/homeassistant/components/template/fan.py b/homeassistant/components/template/fan.py index e5b3569e14b..fa67f60dac0 100644 --- a/homeassistant/components/template/fan.py +++ b/homeassistant/components/template/fan.py @@ -34,7 +34,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.script import Script -from homeassistant.helpers.template import ResultWrapper from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS from .template_entity import TemplateEntity @@ -368,10 +367,7 @@ class TemplateFan(TemplateEntity, FanEntity): @callback def _update_speed(self, speed): # Validate speed - if isinstance(speed, ResultWrapper): - speed = speed.render_result - else: - speed = str(speed) + speed = str(speed) if speed in self._speed_list: self._speed = speed diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 84939beecad..42493136b48 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -33,7 +33,6 @@ from homeassistant.helpers.config_validation import PLATFORM_SCHEMA from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.script import Script -from homeassistant.helpers.template import ResultWrapper from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS from .template_entity import TemplateEntity @@ -406,7 +405,7 @@ class LightTemplate(TemplateEntity, LightEntity): def _update_state(self, result): """Update the state from the template.""" - if isinstance(result, (TemplateError, ResultWrapper)): + if isinstance(result, TemplateError): # This behavior is legacy self._state = False if not self._availability_template: diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 0614b3c3b4f..d6c89f28e6e 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -140,6 +140,16 @@ def gen_result_wrapper(kls): super().__init__(*args) self.render_result = render_result + def __str__(self) -> str: + if self.render_result is None: + # Can't get set repr to work + if kls is set: + return str(set(self)) + + return kls.__str__(self) + + return self.render_result + return Wrapper @@ -160,6 +170,13 @@ class TupleWrapper(tuple, ResultWrapper): """Initialize a new tuple class.""" self.render_result = render_result + def __str__(self) -> str: + """Return string representation.""" + if self.render_result is None: + return super().__str__() + + return self.render_result + RESULT_WRAPPERS: Dict[Type, Type] = { kls: gen_result_wrapper(kls) for kls in (list, dict, set) diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 9ac546c6a75..e3eafda52f1 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -2698,6 +2698,12 @@ async def test_result_wrappers(hass): assert result == native assert result.render_result == text schema(result) # should not raise + # Result with render text stringifies to original text + assert str(result) == text + # Result without render text stringifies same as original type + assert str(template.RESULT_WRAPPERS[orig_type](native)) == str( + orig_type(native) + ) async def test_parse_result(hass): From a34d06e363e1fd1ef9cd8d0fb0baa66ca9932ac7 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 28 Oct 2020 16:58:16 +0100 Subject: [PATCH 071/118] Default legacy templates to true (#42511) * Default legacy templates to true * Disable legacy_templates in tests Co-authored-by: Franck Nijhof --- homeassistant/core.py | 2 +- tests/common.py | 1 + tests/test_core.py | 198 +++++++++++++++++++++++------------------- 3 files changed, 113 insertions(+), 88 deletions(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index 53dc75472c7..ca4a27eef3d 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -1530,7 +1530,7 @@ class Config: self.safe_mode: bool = False # Use legacy template behavior - self.legacy_templates: bool = False + self.legacy_templates: bool = True def distance(self, lat: float, lon: float) -> Optional[float]: """Calculate distance from Home Assistant. diff --git a/tests/common.py b/tests/common.py index 8f4ceb57546..0b56d10188e 100644 --- a/tests/common.py +++ b/tests/common.py @@ -207,6 +207,7 @@ async def async_test_home_assistant(loop): hass.config.units = METRIC_SYSTEM hass.config.media_dirs = {"local": get_test_config_dir("media")} hass.config.skip_pip = True + hass.config.legacy_templates = False hass.config_entries = config_entries.ConfigEntries(hass, {}) hass.config_entries._entries = [] diff --git a/tests/test_core.py b/tests/test_core.py index 9c5ab205972..119c0269ba7 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -885,108 +885,132 @@ class TestServiceRegistry(unittest.TestCase): self.hass.block_till_done() -class TestConfig(unittest.TestCase): - """Test configuration methods.""" +def test_config_defaults(): + """Test config defaults.""" + hass = Mock() + config = ha.Config(hass) + assert config.hass is hass + assert config.latitude == 0 + assert config.longitude == 0 + assert config.elevation == 0 + assert config.location_name == "Home" + assert config.time_zone == dt_util.UTC + assert config.internal_url is None + assert config.external_url is None + assert config.config_source == "default" + assert config.skip_pip is False + assert config.components == set() + assert config.api is None + assert config.config_dir is None + assert config.allowlist_external_dirs == set() + assert config.allowlist_external_urls == set() + assert config.media_dirs == {} + assert config.safe_mode is False + assert config.legacy_templates is True - # pylint: disable=invalid-name - def setUp(self): - """Set up things to be run when tests are started.""" - self.config = ha.Config(None) - assert self.config.config_dir is None - def test_path_with_file(self): - """Test get_config_path method.""" - self.config.config_dir = "/test/ha-config" - assert self.config.path("test.conf") == "/test/ha-config/test.conf" +def test_config_path_with_file(): + """Test get_config_path method.""" + config = ha.Config(None) + config.config_dir = "/test/ha-config" + assert config.path("test.conf") == "/test/ha-config/test.conf" - def test_path_with_dir_and_file(self): - """Test get_config_path method.""" - self.config.config_dir = "/test/ha-config" - assert self.config.path("dir", "test.conf") == "/test/ha-config/dir/test.conf" - def test_as_dict(self): - """Test as dict.""" - self.config.config_dir = "/test/ha-config" - self.config.hass = MagicMock() - type(self.config.hass.state).value = PropertyMock(return_value="RUNNING") - expected = { - "latitude": 0, - "longitude": 0, - "elevation": 0, - CONF_UNIT_SYSTEM: METRIC_SYSTEM.as_dict(), - "location_name": "Home", - "time_zone": "UTC", - "components": set(), - "config_dir": "/test/ha-config", - "whitelist_external_dirs": set(), - "allowlist_external_dirs": set(), - "allowlist_external_urls": set(), - "version": __version__, - "config_source": "default", - "safe_mode": False, - "state": "RUNNING", - "external_url": None, - "internal_url": None, - } +def test_config_path_with_dir_and_file(): + """Test get_config_path method.""" + config = ha.Config(None) + config.config_dir = "/test/ha-config" + assert config.path("dir", "test.conf") == "/test/ha-config/dir/test.conf" - assert expected == self.config.as_dict() - def test_is_allowed_path(self): - """Test is_allowed_path method.""" - with TemporaryDirectory() as tmp_dir: - # The created dir is in /tmp. This is a symlink on OS X - # causing this test to fail unless we resolve path first. - self.config.allowlist_external_dirs = {os.path.realpath(tmp_dir)} +def test_config_as_dict(): + """Test as dict.""" + config = ha.Config(None) + config.config_dir = "/test/ha-config" + config.hass = MagicMock() + type(config.hass.state).value = PropertyMock(return_value="RUNNING") + expected = { + "latitude": 0, + "longitude": 0, + "elevation": 0, + CONF_UNIT_SYSTEM: METRIC_SYSTEM.as_dict(), + "location_name": "Home", + "time_zone": "UTC", + "components": set(), + "config_dir": "/test/ha-config", + "whitelist_external_dirs": set(), + "allowlist_external_dirs": set(), + "allowlist_external_urls": set(), + "version": __version__, + "config_source": "default", + "safe_mode": False, + "state": "RUNNING", + "external_url": None, + "internal_url": None, + } - test_file = os.path.join(tmp_dir, "test.jpg") - with open(test_file, "w") as tmp_file: - tmp_file.write("test") + assert expected == config.as_dict() - valid = [test_file, tmp_dir, os.path.join(tmp_dir, "notfound321")] - for path in valid: - assert self.config.is_allowed_path(path) - self.config.allowlist_external_dirs = {"/home", "/var"} +def test_config_is_allowed_path(): + """Test is_allowed_path method.""" + config = ha.Config(None) + with TemporaryDirectory() as tmp_dir: + # The created dir is in /tmp. This is a symlink on OS X + # causing this test to fail unless we resolve path first. + config.allowlist_external_dirs = {os.path.realpath(tmp_dir)} - invalid = [ - "/hass/config/secure", - "/etc/passwd", - "/root/secure_file", - "/var/../etc/passwd", - test_file, - ] - for path in invalid: - assert not self.config.is_allowed_path(path) + test_file = os.path.join(tmp_dir, "test.jpg") + with open(test_file, "w") as tmp_file: + tmp_file.write("test") - with pytest.raises(AssertionError): - self.config.is_allowed_path(None) + valid = [test_file, tmp_dir, os.path.join(tmp_dir, "notfound321")] + for path in valid: + assert config.is_allowed_path(path) - def test_is_allowed_external_url(self): - """Test is_allowed_external_url method.""" - self.config.allowlist_external_urls = [ - "http://x.com/", - "https://y.com/bla/", - "https://z.com/images/1.jpg/", - ] - - valid = [ - "http://x.com/1.jpg", - "http://x.com", - "https://y.com/bla/", - "https://y.com/bla/2.png", - "https://z.com/images/1.jpg", - ] - for url in valid: - assert self.config.is_allowed_external_url(url) + config.allowlist_external_dirs = {"/home", "/var"} invalid = [ - "https://a.co", - "https://y.com/bla_wrong", - "https://y.com/bla/../image.jpg", - "https://z.com/images", + "/hass/config/secure", + "/etc/passwd", + "/root/secure_file", + "/var/../etc/passwd", + test_file, ] - for url in invalid: - assert not self.config.is_allowed_external_url(url) + for path in invalid: + assert not config.is_allowed_path(path) + + with pytest.raises(AssertionError): + config.is_allowed_path(None) + + +def test_config_is_allowed_external_url(): + """Test is_allowed_external_url method.""" + config = ha.Config(None) + config.allowlist_external_urls = [ + "http://x.com/", + "https://y.com/bla/", + "https://z.com/images/1.jpg/", + ] + + valid = [ + "http://x.com/1.jpg", + "http://x.com", + "https://y.com/bla/", + "https://y.com/bla/2.png", + "https://z.com/images/1.jpg", + ] + for url in valid: + assert config.is_allowed_external_url(url) + + invalid = [ + "https://a.co", + "https://y.com/bla_wrong", + "https://y.com/bla/../image.jpg", + "https://z.com/images", + ] + for url in invalid: + assert not config.is_allowed_external_url(url) async def test_event_on_update(hass): From 529b849efed8046530254ad9082e18d3b91590fe Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 28 Oct 2020 18:08:23 +0100 Subject: [PATCH 072/118] Bumped version to 0.117.0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index a1ca4783640..fdf4b26567c 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "0b6" +PATCH_VERSION = "0" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 0959777b8166690088dc8abe438fbbb63e630fcb Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Thu, 29 Oct 2020 09:22:36 +0100 Subject: [PATCH 073/118] Fix adding Virtual DSM system in synology_dsm (#42523) --- .../components/synology_dsm/config_flow.py | 1 - .../synology_dsm/test_config_flow.py | 48 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/synology_dsm/config_flow.py b/homeassistant/components/synology_dsm/config_flow.py index 2267d0b099c..314bab54806 100644 --- a/homeassistant/components/synology_dsm/config_flow.py +++ b/homeassistant/components/synology_dsm/config_flow.py @@ -275,7 +275,6 @@ def _login_and_fetch_syno_info(api, otp_code): if ( not api.information.serial or api.utilisation.cpu_user_load is None - or not api.storage.disks_ids or not api.storage.volumes_ids or not api.network.macs ): diff --git a/tests/components/synology_dsm/test_config_flow.py b/tests/components/synology_dsm/test_config_flow.py index 5a1a86ef673..fd060ce3529 100644 --- a/tests/components/synology_dsm/test_config_flow.py +++ b/tests/components/synology_dsm/test_config_flow.py @@ -81,6 +81,20 @@ def mock_controller_service_2sa(): yield service_mock +@pytest.fixture(name="service_vdsm") +def mock_controller_service_vdsm(): + """Mock a successful service.""" + with patch( + "homeassistant.components.synology_dsm.config_flow.SynologyDSM" + ) as service_mock: + service_mock.return_value.information.serial = SERIAL + service_mock.return_value.utilisation.cpu_user_load = 1 + service_mock.return_value.storage.disks_ids = [] + service_mock.return_value.storage.volumes_ids = ["volume_1"] + service_mock.return_value.network.macs = MACS + yield service_mock + + @pytest.fixture(name="service_failed") def mock_controller_service_failed(): """Mock a failed service.""" @@ -196,6 +210,40 @@ async def test_user_2sa(hass: HomeAssistantType, service_2sa: MagicMock): assert result["data"].get(CONF_VOLUMES) is None +async def test_user_vdsm(hass: HomeAssistantType, service_vdsm: MagicMock): + """Test user config.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=None + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + + # test with all provided + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_USER}, + data={ + CONF_HOST: HOST, + CONF_PORT: PORT, + CONF_SSL: SSL, + CONF_USERNAME: USERNAME, + CONF_PASSWORD: PASSWORD, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["result"].unique_id == SERIAL + assert result["title"] == HOST + assert result["data"][CONF_HOST] == HOST + assert result["data"][CONF_PORT] == PORT + assert result["data"][CONF_SSL] == SSL + assert result["data"][CONF_USERNAME] == USERNAME + assert result["data"][CONF_PASSWORD] == PASSWORD + assert result["data"][CONF_MAC] == MACS + assert result["data"].get("device_token") is None + assert result["data"].get(CONF_DISKS) is None + assert result["data"].get(CONF_VOLUMES) is None + + async def test_import(hass: HomeAssistantType, service: MagicMock): """Test import step.""" # import with minimum setup From 048db9cb7da8a579551a380669f8c07dd5fd858a Mon Sep 17 00:00:00 2001 From: Rob Bierbooms Date: Fri, 30 Oct 2020 09:11:25 +0100 Subject: [PATCH 074/118] Enable polling for DSMR derivative entity (#42524) --- homeassistant/components/dsmr/sensor.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/homeassistant/components/dsmr/sensor.py b/homeassistant/components/dsmr/sensor.py index 411088c5091..7b9f9bab6f1 100644 --- a/homeassistant/components/dsmr/sensor.py +++ b/homeassistant/components/dsmr/sensor.py @@ -320,6 +320,16 @@ class DerivativeDSMREntity(DSMREntity): """Return the calculated current hourly rate.""" return self._state + @property + def force_update(self): + """Disable force update.""" + return False + + @property + def should_poll(self): + """Enable polling.""" + return True + async def async_update(self): """Recalculate hourly rate if timestamp has changed. From 1ab2d55a7ab7ea5d60170ea2e297cc18c0a4c3f1 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Wed, 28 Oct 2020 23:06:01 +0000 Subject: [PATCH 075/118] Bump aiohomekit to 0.2.54 (#42532) --- 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 1fb4c05c595..efe842bad0f 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", "requirements": [ - "aiohomekit==0.2.53" + "aiohomekit==0.2.54" ], "zeroconf": [ "_hap._tcp.local." diff --git a/requirements_all.txt b/requirements_all.txt index cab26593191..aa7b58fbd80 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -178,7 +178,7 @@ aioguardian==1.0.1 aioharmony==0.2.6 # homeassistant.components.homekit_controller -aiohomekit==0.2.53 +aiohomekit==0.2.54 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5d3df986871..e6831b84225 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -109,7 +109,7 @@ aioguardian==1.0.1 aioharmony==0.2.6 # homeassistant.components.homekit_controller -aiohomekit==0.2.53 +aiohomekit==0.2.54 # homeassistant.components.emulated_hue # homeassistant.components.http From c604595f987106a8448637370f6f1f9527280a02 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Thu, 29 Oct 2020 11:50:39 +0100 Subject: [PATCH 076/118] Fix RMV giving wrong data and ignoring given parameters (#42561) --- homeassistant/components/rmvtransport/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/rmvtransport/sensor.py b/homeassistant/components/rmvtransport/sensor.py index 76e75d77a58..4a619437d52 100644 --- a/homeassistant/components/rmvtransport/sensor.py +++ b/homeassistant/components/rmvtransport/sensor.py @@ -263,10 +263,10 @@ class RMVDepartureData: if not dest_found: continue - elif self._lines and journey["number"] not in self._lines: + if self._lines and journey["number"] not in self._lines: continue - elif journey["minutes"] < self._time_offset: + if journey["minutes"] < self._time_offset: continue for attr in ["direction", "departure_time", "product", "minutes"]: From c57d697df530d2df3c04d11ca99af2d8fc8f0909 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 30 Oct 2020 00:14:07 +0100 Subject: [PATCH 077/118] Update frontend to 20201021.4 (#42590) --- 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 ee27bb08d74..0957c6fd776 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==20201021.3"], + "requirements": ["home-assistant-frontend==20201021.4"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 10cb7858aba..203bbb62027 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -13,7 +13,7 @@ defusedxml==0.6.0 distro==1.5.0 emoji==0.5.4 hass-nabucasa==0.37.1 -home-assistant-frontend==20201021.3 +home-assistant-frontend==20201021.4 httpx==0.16.1 importlib-metadata==1.6.0;python_version<'3.8' jinja2>=2.11.2 diff --git a/requirements_all.txt b/requirements_all.txt index aa7b58fbd80..0a0f919eaf5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -759,7 +759,7 @@ hole==0.5.1 holidays==0.10.3 # homeassistant.components.frontend -home-assistant-frontend==20201021.3 +home-assistant-frontend==20201021.4 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e6831b84225..1dd47f41cb6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -385,7 +385,7 @@ hole==0.5.1 holidays==0.10.3 # homeassistant.components.frontend -home-assistant-frontend==20201021.3 +home-assistant-frontend==20201021.4 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 From aa52ade5a06d52e895b34d76a279170b6a660a8c Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 29 Oct 2020 20:09:41 +0100 Subject: [PATCH 078/118] Fix MQTT template light (#42598) --- homeassistant/components/mqtt/light/schema_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 44a87f2af2c..faf987881b9 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -252,7 +252,7 @@ class MqttLightTemplate( except ValueError: _LOGGER.warning("Invalid color value received") - if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None: + if self._templates[CONF_WHITE_VALUE_TEMPLATE] is not None: try: self._white_value = int( self._templates[ From d26bceb110bf9bbf656826aeaaee6c10cdab4a2a Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 30 Oct 2020 00:13:49 +0100 Subject: [PATCH 079/118] Bump hatasmota to 0.0.25 (#42605) --- .../components/tasmota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tasmota/test_sensor.py | 91 +++++++++++-------- 4 files changed, 56 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 991e38e6a95..b087e13ece0 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota (beta)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.0.24"], + "requirements": ["hatasmota==0.0.25"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"] diff --git a/requirements_all.txt b/requirements_all.txt index 0a0f919eaf5..0c8f9b4fed9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -732,7 +732,7 @@ hass-nabucasa==0.37.1 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.0.24 +hatasmota==0.0.25 # homeassistant.components.jewish_calendar hdate==0.9.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1dd47f41cb6..c92af14b834 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -367,7 +367,7 @@ hangups==0.4.11 hass-nabucasa==0.37.1 # homeassistant.components.tasmota -hatasmota==0.0.24 +hatasmota==0.0.25 # homeassistant.components.jewish_calendar hdate==0.9.12 diff --git a/tests/components/tasmota/test_sensor.py b/tests/components/tasmota/test_sensor.py index ab8498ed04c..e5d55b75a3a 100644 --- a/tests/components/tasmota/test_sensor.py +++ b/tests/components/tasmota/test_sensor.py @@ -105,12 +105,12 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): ) await hass.async_block_till_done() - state = hass.states.get("sensor.dht11_temperature") + state = hass.states.get("sensor.tasmota_dht11_temperature") assert state.state == "unavailable" assert not state.attributes.get(ATTR_ASSUMED_STATE) async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") - state = hass.states.get("sensor.dht11_temperature") + state = hass.states.get("sensor.tasmota_dht11_temperature") assert state.state == STATE_UNKNOWN assert not state.attributes.get(ATTR_ASSUMED_STATE) @@ -118,7 +118,7 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/SENSOR", '{"DHT11":{"Temperature":20.5}}' ) - state = hass.states.get("sensor.dht11_temperature") + state = hass.states.get("sensor.tasmota_dht11_temperature") assert state.state == "20.5" # Test polled state update @@ -127,7 +127,7 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): "tasmota_49A3BC/stat/STATUS8", '{"StatusSNS":{"DHT11":{"Temperature":20.0}}}', ) - state = hass.states.get("sensor.dht11_temperature") + state = hass.states.get("sensor.tasmota_dht11_temperature") assert state.state == "20.0" @@ -150,12 +150,12 @@ async def test_nested_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): ) await hass.async_block_till_done() - state = hass.states.get("sensor.tx23_speed_act") + state = hass.states.get("sensor.tasmota_tx23_speed_act") assert state.state == "unavailable" assert not state.attributes.get(ATTR_ASSUMED_STATE) async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") - state = hass.states.get("sensor.tx23_speed_act") + state = hass.states.get("sensor.tasmota_tx23_speed_act") assert state.state == STATE_UNKNOWN assert not state.attributes.get(ATTR_ASSUMED_STATE) @@ -163,7 +163,7 @@ async def test_nested_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/SENSOR", '{"TX23":{"Speed":{"Act":"12.3"}}}' ) - state = hass.states.get("sensor.tx23_speed_act") + state = hass.states.get("sensor.tasmota_tx23_speed_act") assert state.state == "12.3" # Test polled state update @@ -172,7 +172,7 @@ async def test_nested_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): "tasmota_49A3BC/stat/STATUS8", '{"StatusSNS":{"TX23":{"Speed":{"Act":"23.4"}}}}', ) - state = hass.states.get("sensor.tx23_speed_act") + state = hass.states.get("sensor.tasmota_tx23_speed_act") assert state.state == "23.4" @@ -195,12 +195,12 @@ async def test_indexed_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): ) await hass.async_block_till_done() - state = hass.states.get("sensor.energy_totaltariff_1") + state = hass.states.get("sensor.tasmota_energy_totaltariff_1") assert state.state == "unavailable" assert not state.attributes.get(ATTR_ASSUMED_STATE) async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") - state = hass.states.get("sensor.energy_totaltariff_1") + state = hass.states.get("sensor.tasmota_energy_totaltariff_1") assert state.state == STATE_UNKNOWN assert not state.attributes.get(ATTR_ASSUMED_STATE) @@ -208,7 +208,7 @@ async def test_indexed_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/SENSOR", '{"ENERGY":{"TotalTariff":[1.2,3.4]}}' ) - state = hass.states.get("sensor.energy_totaltariff_1") + state = hass.states.get("sensor.tasmota_energy_totaltariff_1") assert state.state == "3.4" # Test polled state update @@ -217,7 +217,7 @@ async def test_indexed_sensor_state_via_mqtt(hass, mqtt_mock, setup_tasmota): "tasmota_49A3BC/stat/STATUS8", '{"StatusSNS":{"ENERGY":{"TotalTariff":[5.6,7.8]}}}', ) - state = hass.states.get("sensor.energy_totaltariff_1") + state = hass.states.get("sensor.tasmota_energy_totaltariff_1") assert state.state == "7.8" @@ -297,15 +297,15 @@ async def test_attributes(hass, mqtt_mock, setup_tasmota): ) await hass.async_block_till_done() - state = hass.states.get("sensor.dht11_temperature") + state = hass.states.get("sensor.tasmota_dht11_temperature") assert state.attributes.get("device_class") == "temperature" - assert state.attributes.get("friendly_name") == "DHT11 Temperature" + assert state.attributes.get("friendly_name") == "Tasmota DHT11 Temperature" assert state.attributes.get("icon") is None assert state.attributes.get("unit_of_measurement") == "C" - state = hass.states.get("sensor.beer_CarbonDioxide") + state = hass.states.get("sensor.tasmota_beer_CarbonDioxide") assert state.attributes.get("device_class") is None - assert state.attributes.get("friendly_name") == "Beer CarbonDioxide" + assert state.attributes.get("friendly_name") == "Tasmota Beer CarbonDioxide" assert state.attributes.get("icon") == "mdi:molecule-co2" assert state.attributes.get("unit_of_measurement") == "ppm" @@ -329,15 +329,15 @@ async def test_nested_sensor_attributes(hass, mqtt_mock, setup_tasmota): ) await hass.async_block_till_done() - state = hass.states.get("sensor.tx23_speed_act") + state = hass.states.get("sensor.tasmota_tx23_speed_act") assert state.attributes.get("device_class") is None - assert state.attributes.get("friendly_name") == "TX23 Speed Act" + assert state.attributes.get("friendly_name") == "Tasmota TX23 Speed Act" assert state.attributes.get("icon") is None assert state.attributes.get("unit_of_measurement") == "km/h" - state = hass.states.get("sensor.tx23_dir_avg") + state = hass.states.get("sensor.tasmota_tx23_dir_avg") assert state.attributes.get("device_class") is None - assert state.attributes.get("friendly_name") == "TX23 Dir Avg" + assert state.attributes.get("friendly_name") == "Tasmota TX23 Dir Avg" assert state.attributes.get("icon") is None assert state.attributes.get("unit_of_measurement") == " " @@ -367,15 +367,15 @@ async def test_indexed_sensor_attributes(hass, mqtt_mock, setup_tasmota): ) await hass.async_block_till_done() - state = hass.states.get("sensor.dummy1_temperature_0") + state = hass.states.get("sensor.tasmota_dummy1_temperature_0") assert state.attributes.get("device_class") == "temperature" - assert state.attributes.get("friendly_name") == "Dummy1 Temperature 0" + assert state.attributes.get("friendly_name") == "Tasmota Dummy1 Temperature 0" assert state.attributes.get("icon") is None assert state.attributes.get("unit_of_measurement") == "C" - state = hass.states.get("sensor.dummy2_carbondioxide_1") + state = hass.states.get("sensor.tasmota_dummy2_carbondioxide_1") assert state.attributes.get("device_class") is None - assert state.attributes.get("friendly_name") == "Dummy2 CarbonDioxide 1" + assert state.attributes.get("friendly_name") == "Tasmota Dummy2 CarbonDioxide 1" assert state.attributes.get("icon") == "mdi:molecule-co2" assert state.attributes.get("unit_of_measurement") == "ppm" @@ -396,15 +396,15 @@ async def test_enable_status_sensor(hass, mqtt_mock, setup_tasmota): await hass.async_block_till_done() await hass.async_block_till_done() - state = hass.states.get("sensor.tasmota_status") + state = hass.states.get("sensor.tasmota_signal") assert state is None - entry = entity_reg.async_get("sensor.tasmota_status") + entry = entity_reg.async_get("sensor.tasmota_signal") assert entry.disabled assert entry.disabled_by == "integration" # Enable the status sensor updated_entry = entity_reg.async_update_entity( - "sensor.tasmota_status", disabled_by=None + "sensor.tasmota_signal", disabled_by=None ) assert updated_entry != entry assert updated_entry.disabled is False @@ -428,12 +428,12 @@ async def test_enable_status_sensor(hass, mqtt_mock, setup_tasmota): ) await hass.async_block_till_done() - state = hass.states.get("sensor.tasmota_status") + state = hass.states.get("sensor.tasmota_signal") assert state.state == "unavailable" assert not state.attributes.get(ATTR_ASSUMED_STATE) async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") - state = hass.states.get("sensor.tasmota_status") + state = hass.states.get("sensor.tasmota_signal") assert state.state == STATE_UNKNOWN assert not state.attributes.get(ATTR_ASSUMED_STATE) @@ -451,7 +451,7 @@ async def test_availability_when_connection_lost( sensor.DOMAIN, config, sensor_config, - "dht11_temperature", + "tasmota_dht11_temperature", ) @@ -460,7 +460,12 @@ async def test_availability(hass, mqtt_mock, setup_tasmota): config = copy.deepcopy(DEFAULT_CONFIG) sensor_config = copy.deepcopy(DEFAULT_SENSOR_CONFIG) await help_test_availability( - hass, mqtt_mock, sensor.DOMAIN, config, sensor_config, "dht11_temperature" + hass, + mqtt_mock, + sensor.DOMAIN, + config, + sensor_config, + "tasmota_dht11_temperature", ) @@ -469,7 +474,12 @@ async def test_availability_discovery_update(hass, mqtt_mock, setup_tasmota): config = copy.deepcopy(DEFAULT_CONFIG) sensor_config = copy.deepcopy(DEFAULT_SENSOR_CONFIG) await help_test_availability_discovery_update( - hass, mqtt_mock, sensor.DOMAIN, config, sensor_config, "dht11_temperature" + hass, + mqtt_mock, + sensor.DOMAIN, + config, + sensor_config, + "tasmota_dht11_temperature", ) @@ -506,8 +516,8 @@ async def test_discovery_removal_sensor(hass, mqtt_mock, caplog, setup_tasmota): config, sensor_config1, {}, - "dht11_temperature", - "DHT11 Temperature", + "tasmota_dht11_temperature", + "Tasmota DHT11 Temperature", ) @@ -528,8 +538,8 @@ async def test_discovery_update_unchanged_sensor( config, discovery_update, sensor_config, - "dht11_temperature", - "DHT11 Temperature", + "tasmota_dht11_temperature", + "Tasmota DHT11 Temperature", ) @@ -559,7 +569,7 @@ async def test_entity_id_update_subscriptions(hass, mqtt_mock, setup_tasmota): config, topics, sensor_config, - "dht11_temperature", + "tasmota_dht11_temperature", ) @@ -568,5 +578,10 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock, setup_tasmota) config = copy.deepcopy(DEFAULT_CONFIG) sensor_config = copy.deepcopy(DEFAULT_SENSOR_CONFIG) await help_test_entity_id_update_discovery_update( - hass, mqtt_mock, sensor.DOMAIN, config, sensor_config, "dht11_temperature" + hass, + mqtt_mock, + sensor.DOMAIN, + config, + sensor_config, + "tasmota_dht11_temperature", ) From 26766d68aaf7cc6293a65b0aa6ccaff185a55652 Mon Sep 17 00:00:00 2001 From: Clifford Roche Date: Thu, 29 Oct 2020 19:49:13 -0400 Subject: [PATCH 080/118] Update greeclimate to 0.9.2 (#42616) Fixes issue with erroneous await in UDP recv queue --- homeassistant/components/gree/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/gree/manifest.json b/homeassistant/components/gree/manifest.json index ea04eb12f51..c5fb412f1d1 100644 --- a/homeassistant/components/gree/manifest.json +++ b/homeassistant/components/gree/manifest.json @@ -3,6 +3,6 @@ "name": "Gree Climate", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/gree", - "requirements": ["greeclimate==0.9.0"], + "requirements": ["greeclimate==0.9.2"], "codeowners": ["@cmroche"] } diff --git a/requirements_all.txt b/requirements_all.txt index 0c8f9b4fed9..8d0a2be17d5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -696,7 +696,7 @@ gpiozero==1.5.1 gps3==0.33.3 # homeassistant.components.gree -greeclimate==0.9.0 +greeclimate==0.9.2 # homeassistant.components.greeneye_monitor greeneye_monitor==2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c92af14b834..f3e61a25d4d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -352,7 +352,7 @@ google-cloud-pubsub==0.39.1 google-nest-sdm==0.1.6 # homeassistant.components.gree -greeclimate==0.9.0 +greeclimate==0.9.2 # homeassistant.components.griddy griddypower==0.1.0 From 2dcd57bf38789bda3ced4509d718c21b8f1fd128 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 30 Oct 2020 08:31:58 +0000 Subject: [PATCH 081/118] Bumped version to 0.117.1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index fdf4b26567c..95ce48ca88f 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "0" +PATCH_VERSION = "1" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 750bb11895bde00f14c9506be5944a69b4e7b555 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 29 Oct 2020 22:34:06 +0100 Subject: [PATCH 082/118] Fix broken time trigger test (#42606) --- tests/components/config/test_core.py | 35 ++++++++++--------- tests/components/hassio/test_init.py | 3 +- .../homeassistant/triggers/test_time.py | 1 - 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/tests/components/config/test_core.py b/tests/components/config/test_core.py index 1f379727b44..72e655dbb66 100644 --- a/tests/components/config/test_core.py +++ b/tests/components/config/test_core.py @@ -61,22 +61,23 @@ 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" - await client.send_json( - { - "id": 5, - "type": "config/core/update", - "latitude": 60, - "longitude": 50, - "elevation": 25, - "location_name": "Huis", - CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, - "time_zone": "America/New_York", - "external_url": "https://www.example.com", - "internal_url": "http://example.local", - } - ) + with patch("homeassistant.util.dt.set_default_time_zone") as mock_set_tz: + await client.send_json( + { + "id": 5, + "type": "config/core/update", + "latitude": 60, + "longitude": 50, + "elevation": 25, + "location_name": "Huis", + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + "time_zone": "America/New_York", + "external_url": "https://www.example.com", + "internal_url": "http://example.local", + } + ) - msg = await client.receive_json() + msg = await client.receive_json() assert msg["id"] == 5 assert msg["type"] == TYPE_RESULT @@ -86,11 +87,11 @@ async def test_websocket_core_update(hass, client): assert hass.config.elevation == 25 assert hass.config.location_name == "Huis" assert hass.config.units.name == CONF_UNIT_SYSTEM_IMPERIAL - assert hass.config.time_zone.zone == "America/New_York" assert hass.config.external_url == "https://www.example.com" assert hass.config.internal_url == "http://example.local" - dt_util.set_default_time_zone(ORIG_TIME_ZONE) + assert len(mock_set_tz.mock_calls) == 1 + assert mock_set_tz.mock_calls[0][1][0].zone == "America/New_York" async def test_websocket_core_update_not_admin(hass, hass_ws_client, hass_admin_user): diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index 56792295fec..62b4a4adbd2 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -186,7 +186,8 @@ async def test_setup_core_push_timezone(hass, aioclient_mock): assert aioclient_mock.call_count == 7 assert aioclient_mock.mock_calls[2][2]["timezone"] == "testzone" - await hass.config.async_update(time_zone="America/New_York") + with patch("homeassistant.util.dt.set_default_time_zone"): + await hass.config.async_update(time_zone="America/New_York") await hass.async_block_till_done() assert aioclient_mock.mock_calls[-1][2]["timezone"] == "America/New_York" diff --git a/tests/components/homeassistant/triggers/test_time.py b/tests/components/homeassistant/triggers/test_time.py index db10e836629..91fd57beed3 100644 --- a/tests/components/homeassistant/triggers/test_time.py +++ b/tests/components/homeassistant/triggers/test_time.py @@ -74,7 +74,6 @@ async def test_if_fires_using_at_input_datetime(hass, calls, has_date, has_time) "input_datetime", {"input_datetime": {"trigger": {"has_date": has_date, "has_time": has_time}}}, ) - now = dt_util.now() trigger_dt = now.replace( From faf73e304a26f164e294bedd0a91136a68261690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Oldag?= Date: Sun, 1 Nov 2020 15:23:42 +0100 Subject: [PATCH 083/118] Bump pwmled to v1.6.6 (#42607) --- homeassistant/components/rpi_gpio_pwm/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/rpi_gpio_pwm/manifest.json b/homeassistant/components/rpi_gpio_pwm/manifest.json index cde9f24843f..2467673e950 100644 --- a/homeassistant/components/rpi_gpio_pwm/manifest.json +++ b/homeassistant/components/rpi_gpio_pwm/manifest.json @@ -2,6 +2,6 @@ "domain": "rpi_gpio_pwm", "name": "pigpio Daemon PWM LED", "documentation": "https://www.home-assistant.io/integrations/rpi_gpio_pwm", - "requirements": ["pwmled==1.5.3"], + "requirements": ["pwmled==1.6.6"], "codeowners": [] } diff --git a/requirements_all.txt b/requirements_all.txt index 8d0a2be17d5..4418da89d70 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1182,7 +1182,7 @@ pushbullet.py==0.11.0 pushover_complete==1.1.1 # homeassistant.components.rpi_gpio_pwm -pwmled==1.5.3 +pwmled==1.6.6 # homeassistant.components.august py-august==0.25.0 From 6d54ed14b4b736830b492e5777bc24cbed787dd4 Mon Sep 17 00:00:00 2001 From: airthusiast <67556031+airthusiast@users.noreply.github.com> Date: Fri, 30 Oct 2020 18:50:59 +0100 Subject: [PATCH 084/118] Fix Fibaro HC2 climate device missing temperature (#42627) --- homeassistant/components/fibaro/climate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/fibaro/climate.py b/homeassistant/components/fibaro/climate.py index 6a7a6f145d8..58fde1e370b 100644 --- a/homeassistant/components/fibaro/climate.py +++ b/homeassistant/components/fibaro/climate.py @@ -321,7 +321,7 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): """Return the current temperature.""" if self._temp_sensor_device: device = self._temp_sensor_device.fibaro_device - if device.properties.heatingThermostatSetpoint: + if "heatingThermostatSetpoint" in device.properties: return float(device.properties.heatingThermostatSetpoint) return float(device.properties.value) return None @@ -331,7 +331,7 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): """Return the temperature we try to reach.""" if self._target_temp_device: device = self._target_temp_device.fibaro_device - if device.properties.heatingThermostatSetpointFuture: + if "heatingThermostatSetpointFuture" in device.properties: return float(device.properties.heatingThermostatSetpointFuture) return float(device.properties.targetLevel) return None From 2430c5ea22a4b203fed8b3b8e07287fdcdabe2c6 Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Fri, 30 Oct 2020 22:23:07 +1100 Subject: [PATCH 085/118] Fix geo_rss_events import statement (#42629) * bump integration library version * fix import statement --- homeassistant/components/geo_rss_events/manifest.json | 2 +- homeassistant/components/geo_rss_events/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/geo_rss_events/manifest.json b/homeassistant/components/geo_rss_events/manifest.json index 77d38d58ad7..4a434aed8d7 100644 --- a/homeassistant/components/geo_rss_events/manifest.json +++ b/homeassistant/components/geo_rss_events/manifest.json @@ -2,6 +2,6 @@ "domain": "geo_rss_events", "name": "GeoRSS", "documentation": "https://www.home-assistant.io/integrations/geo_rss_events", - "requirements": ["georss_generic_client==0.3"], + "requirements": ["georss_generic_client==0.4"], "codeowners": ["@exxamalte"] } diff --git a/homeassistant/components/geo_rss_events/sensor.py b/homeassistant/components/geo_rss_events/sensor.py index 5a11136fd43..c75234f5f2b 100644 --- a/homeassistant/components/geo_rss_events/sensor.py +++ b/homeassistant/components/geo_rss_events/sensor.py @@ -9,7 +9,7 @@ from datetime import timedelta import logging from georss_client import UPDATE_OK, UPDATE_OK_NO_DATA -from georss_client.generic_feed import GenericFeed +from georss_generic_client import GenericFeed import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA diff --git a/requirements_all.txt b/requirements_all.txt index 4418da89d70..b93fd488492 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -638,7 +638,7 @@ geojson_client==0.4 geopy==1.21.0 # homeassistant.components.geo_rss_events -georss_generic_client==0.3 +georss_generic_client==0.4 # homeassistant.components.ign_sismologia georss_ign_sismologia_client==0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f3e61a25d4d..482025a58eb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -315,7 +315,7 @@ geojson_client==0.4 geopy==1.21.0 # homeassistant.components.geo_rss_events -georss_generic_client==0.3 +georss_generic_client==0.4 # homeassistant.components.ign_sismologia georss_ign_sismologia_client==0.2 From 92f9a77770fab371118f6af30f4004f65d6362e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Fri, 30 Oct 2020 15:20:42 +0100 Subject: [PATCH 086/118] Bump pycfdns to 1.2.1 (#42634) --- 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 cab768953b1..e2f55b13a7f 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.1.1"], + "requirements": ["pycfdns==1.2.1"], "codeowners": ["@ludeeus", "@ctalkington"], "config_flow": true } diff --git a/requirements_all.txt b/requirements_all.txt index b93fd488492..3484a7a3857 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1288,7 +1288,7 @@ pybotvac==0.0.17 pycarwings2==2.9 # homeassistant.components.cloudflare -pycfdns==1.1.1 +pycfdns==1.2.1 # homeassistant.components.channels pychannels==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 482025a58eb..b48e0665681 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -636,7 +636,7 @@ pyblackbird==0.5 pybotvac==0.0.17 # homeassistant.components.cloudflare -pycfdns==1.1.1 +pycfdns==1.2.1 # homeassistant.components.cast pychromecast==7.5.1 From 5f28c82837b273f0d6dd174e31f8de5e4ba404a4 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 30 Oct 2020 19:04:11 +0100 Subject: [PATCH 087/118] Make sure Tasmota status sensors are disabled (#42643) --- homeassistant/components/tasmota/sensor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/tasmota/sensor.py b/homeassistant/components/tasmota/sensor.py index 076ca457a23..bdaac51f09c 100644 --- a/homeassistant/components/tasmota/sensor.py +++ b/homeassistant/components/tasmota/sensor.py @@ -1,6 +1,7 @@ """Support for Tasmota sensors.""" from typing import Optional +from hatasmota import status_sensor from hatasmota.const import ( SENSOR_AMBIENT, SENSOR_APPARENT_POWERUSAGE, @@ -162,7 +163,7 @@ class TasmotaSensor(TasmotaAvailability, TasmotaDiscoveryUpdate, Entity): def entity_registry_enabled_default(self) -> bool: """Return if the entity should be enabled when first added to the entity registry.""" # Hide status sensors to not overwhelm users - if self._tasmota_entity.quantity == SENSOR_STATUS_SIGNAL: + if self._tasmota_entity.quantity in status_sensor.SENSORS: return False return True From 562ccbbe251f5ece061eab80ff9979c1dbc792be Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sat, 31 Oct 2020 09:15:11 -0400 Subject: [PATCH 088/118] attempt to renew subscription immediately to stop endless loop if it fails after setup (#42651) --- homeassistant/components/onvif/event.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/onvif/event.py b/homeassistant/components/onvif/event.py index eb61cbb4b71..eaf23236042 100644 --- a/homeassistant/components/onvif/event.py +++ b/homeassistant/components/onvif/event.py @@ -74,6 +74,14 @@ class EventManager: async def async_start(self) -> bool: """Start polling events.""" if await self.device.create_pullpoint_subscription(): + # Create subscription manager + self._subscription = self.device.create_subscription_service( + "PullPointSubscription" + ) + + # Renew immediately + await self.async_renew() + # Initialize events pullpoint = self.device.create_pullpoint_service() try: @@ -87,11 +95,6 @@ class EventManager: # Parse event initialization await self.async_parse_messages(response.NotificationMessage) - # Create subscription manager - self._subscription = self.device.create_subscription_service( - "PullPointSubscription" - ) - self.started = True return True From a93ae418a2d86dca3db5ebaf63592b9a4294364d Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Sun, 1 Nov 2020 11:05:55 -0500 Subject: [PATCH 089/118] Bump up ZHA dependencies (#42679) * Bump up ZHA dependencies * Fix tests because of zigpy request signature change --- homeassistant/components/zha/core/gateway.py | 2 +- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/zha/test_cover.py | 37 ++++++++++---------- tests/components/zha/test_discover.py | 1 + tests/components/zha/test_light.py | 13 ++++--- tests/components/zha/test_switch.py | 4 +-- 8 files changed, 33 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index bb57d7f03f4..bdfcf1b24f2 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -637,7 +637,7 @@ class ZHAGateway: _LOGGER.debug("Shutting down ZHA ControllerApplication") for unsubscribe in self._unsubs: unsubscribe() - await self.application_controller.shutdown() + await self.application_controller.pre_shutdown() @callback diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 01c24bdc6f6..be8619811d8 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -9,7 +9,7 @@ "zha-quirks==0.0.45", "zigpy-cc==0.5.2", "zigpy-deconz==0.11.0", - "zigpy==0.26.0", + "zigpy==0.27.0", "zigpy-xbee==0.13.0", "zigpy-zigate==0.6.2", "zigpy-znp==0.2.2" diff --git a/requirements_all.txt b/requirements_all.txt index 3484a7a3857..93942625b63 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2355,7 +2355,7 @@ zigpy-zigate==0.6.2 zigpy-znp==0.2.2 # homeassistant.components.zha -zigpy==0.26.0 +zigpy==0.27.0 # homeassistant.components.zoneminder zm-py==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b48e0665681..425838fe4e3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1124,4 +1124,4 @@ zigpy-zigate==0.6.2 zigpy-znp==0.2.2 # homeassistant.components.zha -zigpy==0.26.0 +zigpy==0.27.0 diff --git a/tests/components/zha/test_cover.py b/tests/components/zha/test_cover.py index b295543b3e8..783637d26d7 100644 --- a/tests/components/zha/test_cover.py +++ b/tests/components/zha/test_cover.py @@ -32,7 +32,7 @@ from .common import ( send_attributes_report, ) -from tests.async_mock import AsyncMock, MagicMock, call, patch +from tests.async_mock import AsyncMock, MagicMock, patch from tests.common import async_capture_events, mock_coro, mock_restore_cache @@ -144,9 +144,10 @@ async def test_cover(m1, hass, zha_device_joined_restored, zigpy_cover_device): DOMAIN, SERVICE_CLOSE_COVER, {"entity_id": entity_id}, blocking=True ) assert cluster.request.call_count == 1 - assert cluster.request.call_args == call( - False, 0x1, (), expect_reply=True, manufacturer=None, tsn=None - ) + assert cluster.request.call_args[0][0] is False + assert cluster.request.call_args[0][1] == 0x01 + assert cluster.request.call_args[0][2] == () + assert cluster.request.call_args[1]["expect_reply"] is True # open from UI with patch( @@ -156,9 +157,10 @@ async def test_cover(m1, hass, zha_device_joined_restored, zigpy_cover_device): DOMAIN, SERVICE_OPEN_COVER, {"entity_id": entity_id}, blocking=True ) assert cluster.request.call_count == 1 - assert cluster.request.call_args == call( - False, 0x0, (), expect_reply=True, manufacturer=None, tsn=None - ) + assert cluster.request.call_args[0][0] is False + assert cluster.request.call_args[0][1] == 0x00 + assert cluster.request.call_args[0][2] == () + assert cluster.request.call_args[1]["expect_reply"] is True # set position UI with patch( @@ -171,15 +173,11 @@ async def test_cover(m1, hass, zha_device_joined_restored, zigpy_cover_device): blocking=True, ) assert cluster.request.call_count == 1 - assert cluster.request.call_args == call( - False, - 0x5, - (zigpy.types.uint8_t,), - 53, - expect_reply=True, - manufacturer=None, - tsn=None, - ) + assert cluster.request.call_args[0][0] is False + assert cluster.request.call_args[0][1] == 0x05 + assert cluster.request.call_args[0][2] == (zigpy.types.uint8_t,) + assert cluster.request.call_args[0][3] == 53 + assert cluster.request.call_args[1]["expect_reply"] is True # stop from UI with patch( @@ -189,9 +187,10 @@ async def test_cover(m1, hass, zha_device_joined_restored, zigpy_cover_device): DOMAIN, SERVICE_STOP_COVER, {"entity_id": entity_id}, blocking=True ) assert cluster.request.call_count == 1 - assert cluster.request.call_args == call( - False, 0x2, (), expect_reply=True, manufacturer=None, tsn=None - ) + assert cluster.request.call_args[0][0] is False + assert cluster.request.call_args[0][1] == 0x02 + assert cluster.request.call_args[0][2] == () + assert cluster.request.call_args[1]["expect_reply"] is True # test rejoin await async_test_rejoin(hass, zigpy_cover_device, [cluster], (1,)) diff --git a/tests/components/zha/test_discover.py b/tests/components/zha/test_discover.py index 5589a7d94ac..b5da98dc01f 100644 --- a/tests/components/zha/test_discover.py +++ b/tests/components/zha/test_discover.py @@ -113,6 +113,7 @@ async def test_devices( 0, expect_reply=True, manufacturer=None, + tries=1, tsn=None, ) diff --git a/tests/components/zha/test_light.py b/tests/components/zha/test_light.py index 642504384cc..ea1b8487b7c 100644 --- a/tests/components/zha/test_light.py +++ b/tests/components/zha/test_light.py @@ -323,7 +323,7 @@ async def async_test_on_off_from_hass(hass, cluster, entity_id): assert cluster.request.call_count == 1 assert cluster.request.await_count == 1 assert cluster.request.call_args == call( - False, ON, (), expect_reply=True, manufacturer=None, tsn=None + False, ON, (), expect_reply=True, manufacturer=None, tries=1, tsn=None ) await async_test_off_from_hass(hass, cluster, entity_id) @@ -340,7 +340,7 @@ async def async_test_off_from_hass(hass, cluster, entity_id): assert cluster.request.call_count == 1 assert cluster.request.await_count == 1 assert cluster.request.call_args == call( - False, OFF, (), expect_reply=True, manufacturer=None, tsn=None + False, OFF, (), expect_reply=True, manufacturer=None, tries=1, tsn=None ) @@ -360,7 +360,7 @@ async def async_test_level_on_off_from_hass( assert level_cluster.request.call_count == 0 assert level_cluster.request.await_count == 0 assert on_off_cluster.request.call_args == call( - False, ON, (), expect_reply=True, manufacturer=None, tsn=None + False, ON, (), expect_reply=True, manufacturer=None, tries=1, tsn=None ) on_off_cluster.request.reset_mock() level_cluster.request.reset_mock() @@ -373,7 +373,7 @@ async def async_test_level_on_off_from_hass( assert level_cluster.request.call_count == 1 assert level_cluster.request.await_count == 1 assert on_off_cluster.request.call_args == call( - False, ON, (), expect_reply=True, manufacturer=None, tsn=None + False, ON, (), expect_reply=True, manufacturer=None, tries=1, tsn=None ) assert level_cluster.request.call_args == call( False, @@ -383,6 +383,7 @@ async def async_test_level_on_off_from_hass( 100.0, expect_reply=True, manufacturer=None, + tries=1, tsn=None, ) on_off_cluster.request.reset_mock() @@ -396,7 +397,7 @@ async def async_test_level_on_off_from_hass( assert level_cluster.request.call_count == 1 assert level_cluster.request.await_count == 1 assert on_off_cluster.request.call_args == call( - False, ON, (), expect_reply=True, manufacturer=None, tsn=None + False, ON, (), expect_reply=True, manufacturer=None, tries=1, tsn=None ) assert level_cluster.request.call_args == call( False, @@ -406,6 +407,7 @@ async def async_test_level_on_off_from_hass( 1, expect_reply=True, manufacturer=None, + tries=1, tsn=None, ) on_off_cluster.request.reset_mock() @@ -445,6 +447,7 @@ async def async_test_flash_from_hass(hass, cluster, entity_id, flash): 0, expect_reply=True, manufacturer=None, + tries=1, tsn=None, ) diff --git a/tests/components/zha/test_switch.py b/tests/components/zha/test_switch.py index aab8dafef4f..80412d95fb7 100644 --- a/tests/components/zha/test_switch.py +++ b/tests/components/zha/test_switch.py @@ -136,7 +136,7 @@ async def test_switch(hass, zha_device_joined_restored, zigpy_device): ) assert len(cluster.request.mock_calls) == 1 assert cluster.request.call_args == call( - False, ON, (), expect_reply=True, manufacturer=None, tsn=None + False, ON, (), expect_reply=True, manufacturer=None, tries=1, tsn=None ) # turn off from HA @@ -150,7 +150,7 @@ async def test_switch(hass, zha_device_joined_restored, zigpy_device): ) assert len(cluster.request.mock_calls) == 1 assert cluster.request.call_args == call( - False, OFF, (), expect_reply=True, manufacturer=None, tsn=None + False, OFF, (), expect_reply=True, manufacturer=None, tries=1, tsn=None ) # test joining a new switch to the network and HA From 769c5c1779071b9502ff52a63f924d2ea0ced88a Mon Sep 17 00:00:00 2001 From: Chris Talkington Date: Sat, 31 Oct 2020 16:05:59 -0500 Subject: [PATCH 090/118] Fix canary camera entity inheritance (#42691) --- homeassistant/components/canary/camera.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/canary/camera.py b/homeassistant/components/canary/camera.py index 608dfe02b8a..a90eb5a2825 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -78,6 +78,7 @@ class CanaryCamera(CoordinatorEntity, Camera): def __init__(self, hass, coordinator, location_id, device, timeout, ffmpeg_args): """Initialize a Canary security camera.""" super().__init__(coordinator) + Camera.__init__(self) self._ffmpeg = hass.data[DATA_FFMPEG] self._ffmpeg_arguments = ffmpeg_args self._location_id = location_id From 43a990350b8413762ccc34e62c19ee19540fe8dd Mon Sep 17 00:00:00 2001 From: Matthew Donoughe Date: Sun, 1 Nov 2020 12:25:34 -0500 Subject: [PATCH 091/118] Use pylutron_caseta 0.7.1 (#42701) --- homeassistant/components/lutron_caseta/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/lutron_caseta/manifest.json b/homeassistant/components/lutron_caseta/manifest.json index f80e34a6b5c..366c9cb7f14 100644 --- a/homeassistant/components/lutron_caseta/manifest.json +++ b/homeassistant/components/lutron_caseta/manifest.json @@ -3,7 +3,7 @@ "name": "Lutron Caséta", "documentation": "https://www.home-assistant.io/integrations/lutron_caseta", "requirements": [ - "pylutron-caseta==0.7.0" + "pylutron-caseta==0.7.1" ], "codeowners": [ "@swails" diff --git a/requirements_all.txt b/requirements_all.txt index 93942625b63..0113e7e81bf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1487,7 +1487,7 @@ pylitejet==0.1 pyloopenergy==0.2.1 # homeassistant.components.lutron_caseta -pylutron-caseta==0.7.0 +pylutron-caseta==0.7.1 # homeassistant.components.lutron pylutron==0.2.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 425838fe4e3..81c138dbda2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -730,7 +730,7 @@ pylibrespot-java==0.1.0 pylitejet==0.1 # homeassistant.components.lutron_caseta -pylutron-caseta==0.7.0 +pylutron-caseta==0.7.1 # homeassistant.components.mailgun pymailgunner==1.4 From aecc3476e84fcdd87675c381c9eea01247f2a338 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 1 Nov 2020 20:00:21 +0000 Subject: [PATCH 092/118] Bumped version to 0.117.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 95ce48ca88f..44c3669db9e 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "1" +PATCH_VERSION = "2" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 64707d89ddc592bf00acd779a4550886c90f5e8e Mon Sep 17 00:00:00 2001 From: Andreas Billmeier Date: Wed, 4 Nov 2020 10:06:35 +0100 Subject: [PATCH 093/118] Please set exact cryptography version cryptography==3.2 (#42611) --- homeassistant/package_constraints.txt | 2 +- requirements.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 203bbb62027..01c2a30d2de 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -8,7 +8,7 @@ attrs==19.3.0 bcrypt==3.1.7 certifi>=2020.6.20 ciso8601==2.1.3 -cryptography==3.2.0 +cryptography==3.2 defusedxml==0.6.0 distro==1.5.0 emoji==0.5.4 diff --git a/requirements.txt b/requirements.txt index 1586fdddee4..498b948e478 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ httpx==0.16.1 importlib-metadata==1.6.0;python_version<'3.8' jinja2>=2.11.2 PyJWT==1.7.1 -cryptography==3.2.0 +cryptography==3.2 pip>=8.0.3 python-slugify==4.0.1 pytz>=2020.1 diff --git a/setup.py b/setup.py index e501ac6cf12..a1794f7880b 100755 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ REQUIRES = [ "jinja2>=2.11.2", "PyJWT==1.7.1", # PyJWT has loose dependency. We want the latest one. - "cryptography==3.2.0", + "cryptography==3.2", "pip>=8.0.3", "python-slugify==4.0.1", "pytz>=2020.1", From dc0d27ec18202c3c5798d040400ba5a50692608d Mon Sep 17 00:00:00 2001 From: Andreas Billmeier Date: Wed, 4 Nov 2020 10:17:40 +0100 Subject: [PATCH 094/118] Fix broken maxcube component (#42674) --- homeassistant/components/maxcube/binary_sensor.py | 2 +- homeassistant/components/maxcube/climate.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/maxcube/binary_sensor.py b/homeassistant/components/maxcube/binary_sensor.py index 06b53456973..376076352a6 100644 --- a/homeassistant/components/maxcube/binary_sensor.py +++ b/homeassistant/components/maxcube/binary_sensor.py @@ -16,7 +16,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): name = f"{cube.room_by_id(device.room_id).name} {device.name}" # Only add Window Shutters - if cube.is_windowshutter(device): + if device.is_windowshutter(): devices.append(MaxCubeShutter(handler, name, device.rf_address)) if devices: diff --git a/homeassistant/components/maxcube/climate.py b/homeassistant/components/maxcube/climate.py index e222784ca57..c17cc988c1d 100644 --- a/homeassistant/components/maxcube/climate.py +++ b/homeassistant/components/maxcube/climate.py @@ -70,7 +70,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): for device in cube.devices: name = f"{cube.room_by_id(device.room_id).name} {device.name}" - if cube.is_thermostat(device) or cube.is_wallthermostat(device): + if device.is_thermostat() or device.is_wallthermostat(): devices.append(MaxCubeClimate(handler, name, device.rf_address)) if devices: @@ -180,11 +180,11 @@ class MaxCubeClimate(ClimateEntity): device = cube.device_by_rf(self._rf_address) valve = 0 - if cube.is_thermostat(device): + if device.is_thermostat(): valve = device.valve_position - elif cube.is_wallthermostat(device): + elif device.is_wallthermostat(): for device in cube.devices_by_room(cube.room_by_id(device.room_id)): - if cube.is_thermostat(device) and device.valve_position > 0: + if device.is_thermostat() and device.valve_position > 0: valve = device.valve_position break else: @@ -287,7 +287,7 @@ class MaxCubeClimate(ClimateEntity): cube = self._cubehandle.cube device = cube.device_by_rf(self._rf_address) - if not cube.is_thermostat(device): + if not device.is_thermostat(): return {} return {ATTR_VALVE_POSITION: device.valve_position} From 74d8569aab8efd9b6b4d00ee8601631de0529eab Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Mon, 2 Nov 2020 07:08:36 -0500 Subject: [PATCH 095/118] Fix Vizio host string for zeroconf discovery (#42738) --- homeassistant/components/vizio/config_flow.py | 8 +++-- tests/components/vizio/test_config_flow.py | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/vizio/config_flow.py b/homeassistant/components/vizio/config_flow.py index f3b7720a33e..b6d6d9bfb05 100644 --- a/homeassistant/components/vizio/config_flow.py +++ b/homeassistant/components/vizio/config_flow.py @@ -348,9 +348,11 @@ class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle zeroconf discovery.""" assert self.hass - discovery_info[ - CONF_HOST - ] = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}" + # If host already has port, no need to add it again + if ":" not in discovery_info[CONF_HOST]: + discovery_info[ + CONF_HOST + ] = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}" # Set default name to discovered device name by stripping zeroconf service # (`type`) from `name` diff --git a/tests/components/vizio/test_config_flow.py b/tests/components/vizio/test_config_flow.py index 5cbc2fa5564..32469fabd05 100644 --- a/tests/components/vizio/test_config_flow.py +++ b/tests/components/vizio/test_config_flow.py @@ -27,6 +27,7 @@ from homeassistant.const import ( CONF_HOST, CONF_NAME, CONF_PIN, + CONF_PORT, ) from homeassistant.helpers.typing import HomeAssistantType @@ -777,6 +778,35 @@ async def test_zeroconf_flow_already_configured( assert result["reason"] == "already_configured" +async def test_zeroconf_flow_with_port_in_host( + hass: HomeAssistantType, + vizio_connect: pytest.fixture, + vizio_bypass_setup: pytest.fixture, + vizio_guess_device_type: pytest.fixture, +) -> None: + """Test entity is already configured during zeroconf setup when port is in host.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_SPEAKER_CONFIG, + options={CONF_VOLUME_STEP: VOLUME_STEP}, + unique_id=UNIQUE_ID, + ) + entry.add_to_hass(hass) + + # Try rediscovering same device, this time with port already in host + discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + discovery_info[ + CONF_HOST + ] = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info + ) + + # Flow should abort because device is already setup + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + async def test_zeroconf_dupe_fail( hass: HomeAssistantType, vizio_connect: pytest.fixture, From beaaa1478c4c59612708476d7b14e3f836bf5255 Mon Sep 17 00:00:00 2001 From: rikroe <42204099+rikroe@users.noreply.github.com> Date: Mon, 2 Nov 2020 15:13:53 +0100 Subject: [PATCH 096/118] Bump bimmer_connected to 0.7.10: fix login issues, add (PH)EV services (#42747) Co-authored-by: rikroe --- homeassistant/components/bmw_connected_drive/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index bd76294c8e8..9e49703c434 100644 --- a/homeassistant/components/bmw_connected_drive/manifest.json +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -2,7 +2,7 @@ "domain": "bmw_connected_drive", "name": "BMW Connected Drive", "documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive", - "requirements": ["bimmer_connected==0.7.8"], + "requirements": ["bimmer_connected==0.7.10"], "dependencies": [], "codeowners": ["@gerard33", "@rikroe"] } diff --git a/requirements_all.txt b/requirements_all.txt index 0113e7e81bf..34dea8659f1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -342,7 +342,7 @@ beautifulsoup4==4.9.1 bellows==0.20.3 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.7.8 +bimmer_connected==0.7.10 # homeassistant.components.bizkaibus bizkaibus==0.1.1 From b7d0a21a8bd2848b18423b02e01d1d3f02014163 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Mon, 2 Nov 2020 10:51:03 -0700 Subject: [PATCH 097/118] Bump pyairvisual to 5.0.4 (#42760) --- homeassistant/components/airvisual/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/airvisual/manifest.json b/homeassistant/components/airvisual/manifest.json index d11f3ab69a9..351c7251102 100644 --- a/homeassistant/components/airvisual/manifest.json +++ b/homeassistant/components/airvisual/manifest.json @@ -3,6 +3,6 @@ "name": "AirVisual", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/airvisual", - "requirements": ["pyairvisual==5.0.3"], + "requirements": ["pyairvisual==5.0.4"], "codeowners": ["@bachya"] } diff --git a/requirements_all.txt b/requirements_all.txt index 34dea8659f1..9b13ab3a932 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1252,7 +1252,7 @@ pyaehw4a1==0.3.9 pyaftership==0.1.2 # homeassistant.components.airvisual -pyairvisual==5.0.3 +pyairvisual==5.0.4 # homeassistant.components.almond pyalmond==0.0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 81c138dbda2..8b20708f03f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -615,7 +615,7 @@ py_nextbusnext==0.1.4 pyaehw4a1==0.3.9 # homeassistant.components.airvisual -pyairvisual==5.0.3 +pyairvisual==5.0.4 # homeassistant.components.almond pyalmond==0.0.2 From e6cc35cff2fdc4861d3ba9d7fa138ed085696c95 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Mon, 2 Nov 2020 14:57:53 -0700 Subject: [PATCH 098/118] Fix incorrect property usage for SimpliSafe (#42770) --- .../components/simplisafe/alarm_control_panel.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/simplisafe/alarm_control_panel.py b/homeassistant/components/simplisafe/alarm_control_panel.py index 11f794eef5d..7634f1cce86 100644 --- a/homeassistant/components/simplisafe/alarm_control_panel.py +++ b/homeassistant/components/simplisafe/alarm_control_panel.py @@ -159,7 +159,7 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity): try: await self._system.set_off() except SimplipyError as err: - LOGGER.error('Error while disarming "%s": %s', self._system.name, err) + LOGGER.error('Error while disarming "%s": %s', self._system.system_id, err) return self._state = STATE_ALARM_DISARMED @@ -172,7 +172,9 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity): try: await self._system.set_home() except SimplipyError as err: - LOGGER.error('Error while arming "%s" (home): %s', self._system.name, err) + LOGGER.error( + 'Error while arming "%s" (home): %s', self._system.system_id, err + ) return self._state = STATE_ALARM_ARMED_HOME @@ -185,7 +187,9 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanelEntity): try: await self._system.set_away() except SimplipyError as err: - LOGGER.error('Error while arming "%s" (away): %s', self._system.name, err) + LOGGER.error( + 'Error while arming "%s" (away): %s', self._system.system_id, err + ) return self._state = STATE_ALARM_ARMING From a7bdd2aafe64ead72e3ef96be7d525ed68e96582 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 4 Nov 2020 10:03:32 +0100 Subject: [PATCH 099/118] Force color or white mode exclusivity for Tasmota lights (#42772) --- homeassistant/components/tasmota/light.py | 5 +++++ tests/components/tasmota/test_light.py | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/homeassistant/components/tasmota/light.py b/homeassistant/components/tasmota/light.py index 1a41e7373fd..a680d873f9f 100644 --- a/homeassistant/components/tasmota/light.py +++ b/homeassistant/components/tasmota/light.py @@ -128,6 +128,11 @@ class TasmotaLight( white_value = float(attributes["white_value"]) percent_white = white_value / TASMOTA_BRIGHTNESS_MAX self._white_value = percent_white * 255 + if self._white_value == 0: + self._color_temp = None + self._white_value = None + if self._white_value is not None and self._white_value > 0: + self._hs = None self.async_write_ha_state() @property diff --git a/tests/components/tasmota/test_light.py b/tests/components/tasmota/test_light.py index 48b1dec7232..9210c577a5e 100644 --- a/tests/components/tasmota/test_light.py +++ b/tests/components/tasmota/test_light.py @@ -432,6 +432,8 @@ async def test_controlling_state_via_mqtt_rgbww(hass, mqtt_mock, setup_tasmota): state = hass.states.get("light.test") assert state.state == STATE_ON assert state.attributes.get("white_value") == 127.5 + # Setting white > 0 should clear the color + assert not state.attributes.get("rgb_color") async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","CT":300}' @@ -440,6 +442,15 @@ async def test_controlling_state_via_mqtt_rgbww(hass, mqtt_mock, setup_tasmota): assert state.state == STATE_ON assert state.attributes.get("color_temp") == 300 + async_fire_mqtt_message( + hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","White":0}' + ) + state = hass.states.get("light.test") + assert state.state == STATE_ON + # Setting white to 0 should clear the white_value and color_temp + assert not state.attributes.get("white_value") + assert not state.attributes.get("color_temp") + async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/STATE", '{"POWER":"ON","Scheme":3}' ) From d967ecc8c69295f802d4c763581ff341c8fcbc4e Mon Sep 17 00:00:00 2001 From: cgtobi Date: Tue, 3 Nov 2020 00:17:37 +0100 Subject: [PATCH 100/118] Bump pyatmo to 4.2.0 (#42774) --- 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 12c9220c361..824b835b01a 100644 --- a/homeassistant/components/netatmo/manifest.json +++ b/homeassistant/components/netatmo/manifest.json @@ -3,7 +3,7 @@ "name": "Netatmo", "documentation": "https://www.home-assistant.io/integrations/netatmo", "requirements": [ - "pyatmo==4.1.0" + "pyatmo==4.2.0" ], "after_dependencies": [ "cloud", diff --git a/requirements_all.txt b/requirements_all.txt index 9b13ab3a932..91ef29800b4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1264,7 +1264,7 @@ pyarlo==0.2.3 pyatag==0.3.4.4 # homeassistant.components.netatmo -pyatmo==4.1.0 +pyatmo==4.2.0 # homeassistant.components.atome pyatome==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8b20708f03f..9bd8137a1e6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -627,7 +627,7 @@ pyarlo==0.2.3 pyatag==0.3.4.4 # homeassistant.components.netatmo -pyatmo==4.1.0 +pyatmo==4.2.0 # homeassistant.components.blackbird pyblackbird==0.5 From 43486917bfd947549dfe1130bc08702a8922b224 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Tue, 3 Nov 2020 00:20:02 +0100 Subject: [PATCH 101/118] Fix broken 2fa authentication in synology_dsm (#42775) --- homeassistant/components/synology_dsm/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/synology_dsm/__init__.py b/homeassistant/components/synology_dsm/__init__.py index 44a68b4c18c..a9c2f47e6b9 100644 --- a/homeassistant/components/synology_dsm/__init__.py +++ b/homeassistant/components/synology_dsm/__init__.py @@ -258,10 +258,9 @@ class SynoApi: self._entry.data[CONF_PASSWORD], self._entry.data[CONF_SSL], timeout=self._entry.options.get(CONF_TIMEOUT), + device_token=self._entry.data.get("device_token"), ) - await self._hass.async_add_executor_job( - self.dsm.login, self._entry.data.get("device_token") - ) + await self._hass.async_add_executor_job(self.dsm.login) self._with_surveillance_station = bool( self.dsm.apis.get(SynoSurveillanceStation.CAMERA_API_KEY) From 0dc926a13112a6211d30d6e13e1457e788a1fc96 Mon Sep 17 00:00:00 2001 From: rikroe <42204099+rikroe@users.noreply.github.com> Date: Wed, 4 Nov 2020 10:04:30 +0100 Subject: [PATCH 102/118] Bump bimmer_connected to 0.7.11 (#42788) --- homeassistant/components/bmw_connected_drive/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index 9e49703c434..6a886fc9ef4 100644 --- a/homeassistant/components/bmw_connected_drive/manifest.json +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -2,7 +2,7 @@ "domain": "bmw_connected_drive", "name": "BMW Connected Drive", "documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive", - "requirements": ["bimmer_connected==0.7.10"], + "requirements": ["bimmer_connected==0.7.11"], "dependencies": [], "codeowners": ["@gerard33", "@rikroe"] } diff --git a/requirements_all.txt b/requirements_all.txt index 91ef29800b4..4121b1ad034 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -342,7 +342,7 @@ beautifulsoup4==4.9.1 bellows==0.20.3 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.7.10 +bimmer_connected==0.7.11 # homeassistant.components.bizkaibus bizkaibus==0.1.1 From 32652fde488832be7674bd3cb1e631dd0c0ca392 Mon Sep 17 00:00:00 2001 From: Rob Bierbooms Date: Tue, 3 Nov 2020 23:01:52 +0100 Subject: [PATCH 103/118] Filter rfxtrx replace devices in option flow on existing config entry (#42800) * Fix function _can_replace_device * Rearrange --- homeassistant/components/rfxtrx/config_flow.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/rfxtrx/config_flow.py b/homeassistant/components/rfxtrx/config_flow.py index 624b7ade69d..f81fdde3c4c 100644 --- a/homeassistant/components/rfxtrx/config_flow.py +++ b/homeassistant/components/rfxtrx/config_flow.py @@ -360,13 +360,17 @@ class OptionsFlow(config_entries.OptionsFlow): """Check if device can be replaced with selected device.""" device_data = self._get_device_data(entry_id) event_code = device_data[CONF_EVENT_CODE] - rfx_obj = get_rfx_object(event_code) - if ( - rfx_obj.device.packettype == self._selected_device_object.device.packettype - and rfx_obj.device.subtype == self._selected_device_object.device.subtype - and self._selected_device_event_code != event_code - ): - return True + + if event_code is not None: + rfx_obj = get_rfx_object(event_code) + if ( + rfx_obj.device.packettype + == self._selected_device_object.device.packettype + and rfx_obj.device.subtype + == self._selected_device_object.device.subtype + and self._selected_device_event_code != event_code + ): + return True return False From e4f88a02f7a41f46dc02222e30269ae43d65fc4f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 Nov 2020 21:52:47 -1000 Subject: [PATCH 104/118] Fix rest sensors with resource templates (#42818) --- homeassistant/components/rest/binary_sensor.py | 4 ++-- homeassistant/components/rest/sensor.py | 4 ++-- tests/components/rest/test_binary_sensor.py | 2 +- tests/components/rest/test_sensor.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/rest/binary_sensor.py b/homeassistant/components/rest/binary_sensor.py index ec8b3ba0122..c19bfe307d0 100644 --- a/homeassistant/components/rest/binary_sensor.py +++ b/homeassistant/components/rest/binary_sensor.py @@ -84,7 +84,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= if resource_template is not None: resource_template.hass = hass - resource = resource_template.render(parse_result=False) + resource = resource_template.async_render(parse_result=False) if value_template is not None: value_template.hass = hass @@ -189,6 +189,6 @@ class RestBinarySensor(BinarySensorEntity): async def async_update(self): """Get the latest data from REST API and updates the state.""" if self._resource_template is not None: - self.rest.set_url(self._resource_template.render(parse_result=False)) + self.rest.set_url(self._resource_template.async_render(parse_result=False)) await self.rest.async_update() diff --git a/homeassistant/components/rest/sensor.py b/homeassistant/components/rest/sensor.py index 323447ba1b3..826160604ba 100644 --- a/homeassistant/components/rest/sensor.py +++ b/homeassistant/components/rest/sensor.py @@ -103,7 +103,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= if resource_template is not None: resource_template.hass = hass - resource = resource_template.render(parse_result=False) + resource = resource_template.async_render(parse_result=False) if username and password: if config.get(CONF_AUTHENTICATION) == HTTP_DIGEST_AUTHENTICATION: @@ -202,7 +202,7 @@ class RestSensor(Entity): async def async_update(self): """Get the latest data from REST API and update the state.""" if self._resource_template is not None: - self.rest.set_url(self._resource_template.render(parse_result=False)) + self.rest.set_url(self._resource_template.async_render(parse_result=False)) await self.rest.async_update() diff --git a/tests/components/rest/test_binary_sensor.py b/tests/components/rest/test_binary_sensor.py index 276dc293f87..1f2c88f4278 100644 --- a/tests/components/rest/test_binary_sensor.py +++ b/tests/components/rest/test_binary_sensor.py @@ -116,7 +116,7 @@ async def test_setup_minimum_resource_template(hass): { "binary_sensor": { "platform": "rest", - "resource_template": "http://localhost", + "resource_template": "{% set url = 'http://localhost' %}{{ url }}", } }, ) diff --git a/tests/components/rest/test_sensor.py b/tests/components/rest/test_sensor.py index 6a7e444cedd..d841f69e45f 100644 --- a/tests/components/rest/test_sensor.py +++ b/tests/components/rest/test_sensor.py @@ -102,7 +102,7 @@ async def test_setup_minimum_resource_template(hass): { "sensor": { "platform": "rest", - "resource_template": "http://localhost", + "resource_template": "{% set url = 'http://localhost' %}{{ url }}", } }, ) From 45dbd498524727bd4a45f1690010ca195b0b9476 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 4 Nov 2020 10:04:45 +0100 Subject: [PATCH 105/118] Fix evohome scheduling coroutines (#42821) --- homeassistant/components/evohome/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/evohome/__init__.py b/homeassistant/components/evohome/__init__.py index f429f5bd2f1..818744f7d05 100644 --- a/homeassistant/components/evohome/__init__.py +++ b/homeassistant/components/evohome/__init__.py @@ -431,7 +431,7 @@ class EvoBroker: return if refresh: - self.hass.helpers.event.async_call_later(1, self.async_update()) + self.hass.helpers.event.async_call_later(1, self.async_update) return result From f3c7b77afa6e9e679a68582fe552f0cb25039033 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 4 Nov 2020 09:24:41 +0000 Subject: [PATCH 106/118] Bumped version to 0.117.3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 44c3669db9e..2360191b144 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "2" +PATCH_VERSION = "3" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 36b099592eff2e0773fe88fe73d2ebe8bdc8f2cb Mon Sep 17 00:00:00 2001 From: Clifford Roche Date: Wed, 4 Nov 2020 10:43:33 -0500 Subject: [PATCH 107/118] Update greeclimate to 0.9.5 (#42796) Provides better discovery across subnets in some cases --- homeassistant/components/gree/manifest.json | 4 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/gree/manifest.json b/homeassistant/components/gree/manifest.json index c5fb412f1d1..3480d84d97f 100644 --- a/homeassistant/components/gree/manifest.json +++ b/homeassistant/components/gree/manifest.json @@ -3,6 +3,6 @@ "name": "Gree Climate", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/gree", - "requirements": ["greeclimate==0.9.2"], + "requirements": ["greeclimate==0.9.5"], "codeowners": ["@cmroche"] -} +} \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index 4121b1ad034..c5249892b15 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -696,7 +696,7 @@ gpiozero==1.5.1 gps3==0.33.3 # homeassistant.components.gree -greeclimate==0.9.2 +greeclimate==0.9.5 # homeassistant.components.greeneye_monitor greeneye_monitor==2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9bd8137a1e6..6a172830d5c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -352,7 +352,7 @@ google-cloud-pubsub==0.39.1 google-nest-sdm==0.1.6 # homeassistant.components.gree -greeclimate==0.9.2 +greeclimate==0.9.5 # homeassistant.components.griddy griddypower==0.1.0 From d4b68454a3967d3be3f4c385f0a73b5d4d265ac8 Mon Sep 17 00:00:00 2001 From: Rob Bierbooms Date: Wed, 4 Nov 2020 11:30:46 +0100 Subject: [PATCH 108/118] Cleanup dispatchers when unloading rfxtrx (#42803) * Cleanup dispatchers * Fix name * Refactor code * Fix typo in function description * Fix pylint error * Change function definition * Move cleanup_callbacks data to domain --- homeassistant/components/rfxtrx/__init__.py | 20 +++++++++++++++++-- .../components/rfxtrx/binary_sensor.py | 8 ++------ homeassistant/components/rfxtrx/const.py | 4 ++++ homeassistant/components/rfxtrx/cover.py | 6 ++---- homeassistant/components/rfxtrx/light.py | 6 ++---- homeassistant/components/rfxtrx/sensor.py | 6 ++---- homeassistant/components/rfxtrx/switch.py | 6 ++---- 7 files changed, 32 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/rfxtrx/__init__.py b/homeassistant/components/rfxtrx/__init__.py index 9c0615fdb5e..5f8d14710e3 100644 --- a/homeassistant/components/rfxtrx/__init__.py +++ b/homeassistant/components/rfxtrx/__init__.py @@ -49,6 +49,9 @@ from .const import ( CONF_OFF_DELAY, CONF_REMOVE_DEVICE, CONF_SIGNAL_REPETITIONS, + DATA_CLEANUP_CALLBACKS, + DATA_LISTENER, + DATA_RFXOBJECT, DEVICE_PACKET_TYPE_LIGHTING4, EVENT_RFXTRX_EVENT, SERVICE_SEND, @@ -93,8 +96,6 @@ DATA_TYPES = OrderedDict( ) _LOGGER = logging.getLogger(__name__) -DATA_RFXOBJECT = "rfxobject" -DATA_LISTENER = "ha_stop" def _bytearray_string(data): @@ -188,6 +189,8 @@ async def async_setup_entry(hass, entry: config_entries.ConfigEntry): """Set up the RFXtrx component.""" hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN][DATA_CLEANUP_CALLBACKS] = [] + await async_setup_internal(hass, entry) for domain in DOMAINS: @@ -212,12 +215,17 @@ async def async_unload_entry(hass, entry: config_entries.ConfigEntry): hass.services.async_remove(DOMAIN, SERVICE_SEND) + for cleanup_callback in hass.data[DOMAIN][DATA_CLEANUP_CALLBACKS]: + cleanup_callback() + listener = hass.data[DOMAIN][DATA_LISTENER] listener() rfx_object = hass.data[DOMAIN][DATA_RFXOBJECT] await hass.async_add_executor_job(rfx_object.close_connection) + hass.data.pop(DOMAIN) + return True @@ -428,6 +436,14 @@ def get_device_id(device, data_bits=None): return (f"{device.packettype:x}", f"{device.subtype:x}", id_string) +def connect_auto_add(hass, entry_data, callback_fun): + """Connect to dispatcher for automatic add.""" + if entry_data[CONF_AUTOMATIC_ADD]: + hass.data[DOMAIN][DATA_CLEANUP_CALLBACKS].append( + hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, callback_fun) + ) + + class RfxtrxEntity(RestoreEntity): """Represents a Rfxtrx device. diff --git a/homeassistant/components/rfxtrx/binary_sensor.py b/homeassistant/components/rfxtrx/binary_sensor.py index 7fe89e747bc..78eb49740d5 100644 --- a/homeassistant/components/rfxtrx/binary_sensor.py +++ b/homeassistant/components/rfxtrx/binary_sensor.py @@ -19,11 +19,10 @@ from homeassistant.core import callback from homeassistant.helpers import event as evt from . import ( - CONF_AUTOMATIC_ADD, CONF_DATA_BITS, CONF_OFF_DELAY, - SIGNAL_EVENT, RfxtrxEntity, + connect_auto_add, find_possible_pt2262_device, get_device_id, get_pt2262_cmd, @@ -147,10 +146,7 @@ async def async_setup_entry( async_add_entities([sensor]) # Subscribe to main RFXtrx events - if discovery_info[CONF_AUTOMATIC_ADD]: - hass.helpers.dispatcher.async_dispatcher_connect( - SIGNAL_EVENT, binary_sensor_update - ) + connect_auto_add(hass, discovery_info, binary_sensor_update) class RfxtrxBinarySensor(RfxtrxEntity, BinarySensorEntity): diff --git a/homeassistant/components/rfxtrx/const.py b/homeassistant/components/rfxtrx/const.py index 404d344cc71..28aec125644 100644 --- a/homeassistant/components/rfxtrx/const.py +++ b/homeassistant/components/rfxtrx/const.py @@ -33,3 +33,7 @@ SERVICE_SEND = "send" DEVICE_PACKET_TYPE_LIGHTING4 = 0x13 EVENT_RFXTRX_EVENT = "rfxtrx_event" + +DATA_RFXOBJECT = "rfxobject" +DATA_LISTENER = "ha_stop" +DATA_CLEANUP_CALLBACKS = "cleanup_callbacks" diff --git a/homeassistant/components/rfxtrx/cover.py b/homeassistant/components/rfxtrx/cover.py index 86950308f55..dfbaa60f589 100644 --- a/homeassistant/components/rfxtrx/cover.py +++ b/homeassistant/components/rfxtrx/cover.py @@ -6,12 +6,11 @@ from homeassistant.const import CONF_DEVICES, STATE_OPEN from homeassistant.core import callback from . import ( - CONF_AUTOMATIC_ADD, CONF_DATA_BITS, CONF_SIGNAL_REPETITIONS, DEFAULT_SIGNAL_REPETITIONS, - SIGNAL_EVENT, RfxtrxCommandEntity, + connect_auto_add, get_device_id, get_rfx_object, ) @@ -81,8 +80,7 @@ async def async_setup_entry( async_add_entities([entity]) # Subscribe to main RFXtrx events - if discovery_info[CONF_AUTOMATIC_ADD]: - hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, cover_update) + connect_auto_add(hass, discovery_info, cover_update) class RfxtrxCover(RfxtrxCommandEntity, CoverEntity): diff --git a/homeassistant/components/rfxtrx/light.py b/homeassistant/components/rfxtrx/light.py index 33ee5ea4748..fd790581eda 100644 --- a/homeassistant/components/rfxtrx/light.py +++ b/homeassistant/components/rfxtrx/light.py @@ -12,12 +12,11 @@ from homeassistant.const import CONF_DEVICES, STATE_ON from homeassistant.core import callback from . import ( - CONF_AUTOMATIC_ADD, CONF_DATA_BITS, CONF_SIGNAL_REPETITIONS, DEFAULT_SIGNAL_REPETITIONS, - SIGNAL_EVENT, RfxtrxCommandEntity, + connect_auto_add, get_device_id, get_rfx_object, ) @@ -95,8 +94,7 @@ async def async_setup_entry( async_add_entities([entity]) # Subscribe to main RFXtrx events - if discovery_info[CONF_AUTOMATIC_ADD]: - hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, light_update) + connect_auto_add(hass, discovery_info, light_update) class RfxtrxLight(RfxtrxCommandEntity, LightEntity): diff --git a/homeassistant/components/rfxtrx/sensor.py b/homeassistant/components/rfxtrx/sensor.py index 81e0c60e055..c897e164119 100644 --- a/homeassistant/components/rfxtrx/sensor.py +++ b/homeassistant/components/rfxtrx/sensor.py @@ -20,11 +20,10 @@ from homeassistant.const import ( from homeassistant.core import callback from . import ( - CONF_AUTOMATIC_ADD, CONF_DATA_BITS, DATA_TYPES, - SIGNAL_EVENT, RfxtrxEntity, + connect_auto_add, get_device_id, get_rfx_object, ) @@ -127,8 +126,7 @@ async def async_setup_entry( async_add_entities([entity]) # Subscribe to main RFXtrx events - if discovery_info[CONF_AUTOMATIC_ADD]: - hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, sensor_update) + connect_auto_add(hass, discovery_info, sensor_update) class RfxtrxSensor(RfxtrxEntity): diff --git a/homeassistant/components/rfxtrx/switch.py b/homeassistant/components/rfxtrx/switch.py index 53069210794..96c066d5f3e 100644 --- a/homeassistant/components/rfxtrx/switch.py +++ b/homeassistant/components/rfxtrx/switch.py @@ -8,13 +8,12 @@ from homeassistant.const import CONF_DEVICES, STATE_ON from homeassistant.core import callback from . import ( - CONF_AUTOMATIC_ADD, CONF_DATA_BITS, CONF_SIGNAL_REPETITIONS, DEFAULT_SIGNAL_REPETITIONS, DOMAIN, - SIGNAL_EVENT, RfxtrxCommandEntity, + connect_auto_add, get_device_id, get_rfx_object, ) @@ -92,8 +91,7 @@ async def async_setup_entry( async_add_entities([entity]) # Subscribe to main RFXtrx events - if discovery_info[CONF_AUTOMATIC_ADD]: - hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, switch_update) + connect_auto_add(hass, discovery_info, switch_update) class RfxtrxSwitch(RfxtrxCommandEntity, SwitchEntity): From 6bacda3cee276d287878da52a03ab51f6d4ae865 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Wed, 4 Nov 2020 15:35:54 +0100 Subject: [PATCH 109/118] Call coordinator.shutdown() when ConfigEntryNotReady (#42833) --- homeassistant/components/brother/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/brother/__init__.py b/homeassistant/components/brother/__init__.py index f7e5fbdb0d1..8c5cdb2d7ed 100644 --- a/homeassistant/components/brother/__init__.py +++ b/homeassistant/components/brother/__init__.py @@ -34,6 +34,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): await coordinator.async_refresh() if not coordinator.last_update_success: + coordinator.shutdown() raise ConfigEntryNotReady hass.data.setdefault(DOMAIN, {}) From df6ee2e51f49d096664d2cb1fa94d69847ada132 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 4 Nov 2020 15:47:28 +0000 Subject: [PATCH 110/118] Bumped version to 0.117.4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 2360191b144..8911ed2c5f2 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "3" +PATCH_VERSION = "4" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 2f1d30fa8509dac21c08a4caa18d336311b00d81 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 4 Nov 2020 21:59:08 +0100 Subject: [PATCH 111/118] Bump hatasmota to 0.0.25.1 (#42850) --- .../components/tasmota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tasmota/test_common.py | 38 +++++++++++++++++++ tests/components/tasmota/test_discovery.py | 25 +++++++++++- 5 files changed, 65 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index b087e13ece0..a0414b6450b 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota (beta)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.0.25"], + "requirements": ["hatasmota==0.0.25.1"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"] diff --git a/requirements_all.txt b/requirements_all.txt index c5249892b15..7504b10e9e2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -732,7 +732,7 @@ hass-nabucasa==0.37.1 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.0.25 +hatasmota==0.0.25.1 # homeassistant.components.jewish_calendar hdate==0.9.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6a172830d5c..2e74a82e8c4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -367,7 +367,7 @@ hangups==0.4.11 hass-nabucasa==0.37.1 # homeassistant.components.tasmota -hatasmota==0.0.25 +hatasmota==0.0.25.1 # homeassistant.components.jewish_calendar hdate==0.9.12 diff --git a/tests/components/tasmota/test_common.py b/tests/components/tasmota/test_common.py index 5c18f2f401c..665249f312a 100644 --- a/tests/components/tasmota/test_common.py +++ b/tests/components/tasmota/test_common.py @@ -58,6 +58,44 @@ DEFAULT_CONFIG = { } +DEFAULT_CONFIG_9_0_0_4 = { + "ip": "192.168.15.10", + "dn": "Tasmota", + "fn": ["Test", "Beer", "Milk", "Four", None], + "hn": "tasmota_49A3BC-0956", + "if": 0, # iFan + "lk": 1, # RGB + white channels linked to a single light + "mac": "00000049A3BC", + "md": "Sonoff Basic", + "ofln": "Offline", + "onln": "Online", + "state": ["OFF", "ON", "TOGGLE", "HOLD"], + "sw": "8.4.0.2", + "swn": [None, None, None, None, None], + "t": "tasmota_49A3BC", + "ft": "%topic%/%prefix%/", + "tp": ["cmnd", "stat", "tele"], + "rl": [0, 0, 0, 0, 0, 0, 0, 0], + "swc": [-1, -1, -1, -1, -1, -1, -1, -1], + "btn": [0, 0, 0, 0], + "so": { + "4": 0, # Return MQTT response as RESULT or %COMMAND% + "11": 0, # Swap button single and double press functionality + "13": 0, # Allow immediate action on single button press + "17": 1, # Show Color string as hex or comma-separated + "20": 0, # Update of Dimmer/Color/CT without turning power on + "30": 0, # Enforce Home Assistant auto-discovery as light + "68": 0, # Multi-channel PWM instead of a single light + "73": 0, # Enable Buttons decoupling and send multi-press and hold MQTT messages + "82": 0, # Reduce the CT range from 153..500 to 200.380 + "114": 0, # Enable sending switch MQTT messages + }, + "ty": 0, # Tuya MCU + "lt_st": 0, + "ver": 1, +} + + async def help_test_availability_when_connection_lost( hass, mqtt_client_mock, diff --git a/tests/components/tasmota/test_discovery.py b/tests/components/tasmota/test_discovery.py index 607676615a7..ba288ef93ae 100644 --- a/tests/components/tasmota/test_discovery.py +++ b/tests/components/tasmota/test_discovery.py @@ -6,7 +6,7 @@ from homeassistant.components.tasmota.const import DEFAULT_PREFIX from homeassistant.components.tasmota.discovery import ALREADY_DISCOVERED from .conftest import setup_tasmota_helper -from .test_common import DEFAULT_CONFIG +from .test_common import DEFAULT_CONFIG, DEFAULT_CONFIG_9_0_0_4 from tests.async_mock import patch from tests.common import async_fire_mqtt_message @@ -132,6 +132,29 @@ async def test_device_discover( assert device_entry.sw_version == config["sw"] +async def test_device_discover_deprecated( + hass, mqtt_mock, caplog, device_reg, entity_reg, setup_tasmota +): + """Test setting up a device with deprecated discovery message.""" + config = copy.deepcopy(DEFAULT_CONFIG_9_0_0_4) + mac = config["mac"] + + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + + # Verify device and registry entries are created + device_entry = device_reg.async_get_device(set(), {("mac", mac)}) + assert device_entry is not None + assert device_entry.manufacturer == "Tasmota" + assert device_entry.model == config["md"] + assert device_entry.name == config["dn"] + assert device_entry.sw_version == config["sw"] + + async def test_device_update( hass, mqtt_mock, caplog, device_reg, entity_reg, setup_tasmota ): From ba48fd6c517a1f52d4d5b0ceedf7c93b89659dd1 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 4 Nov 2020 11:41:08 -0700 Subject: [PATCH 112/118] Clean up SimpliSafe binary sensor implementation (#42841) * Clean up SimpliSafe binary sensor implementation * Cleanup * Constant name --- .../components/simplisafe/binary_sensor.py | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/simplisafe/binary_sensor.py b/homeassistant/components/simplisafe/binary_sensor.py index 263bbf51b13..67a4af4752d 100644 --- a/homeassistant/components/simplisafe/binary_sensor.py +++ b/homeassistant/components/simplisafe/binary_sensor.py @@ -23,25 +23,25 @@ SUPPORTED_BATTERY_SENSOR_TYPES = [ EntityTypes.temperature, ] -SUPPORTED_SENSOR_TYPES = [ - EntityTypes.entry, +SUPPORTED_TRIGGERED_SENSOR_TYPES = [ EntityTypes.carbon_monoxide, - EntityTypes.smoke, + EntityTypes.entry, EntityTypes.leak, + EntityTypes.smoke, ] -HA_SENSOR_TYPES = { - EntityTypes.entry: DEVICE_CLASS_DOOR, +DEVICE_CLASSES = { EntityTypes.carbon_monoxide: DEVICE_CLASS_GAS, - EntityTypes.smoke: DEVICE_CLASS_SMOKE, + EntityTypes.entry: DEVICE_CLASS_DOOR, EntityTypes.leak: DEVICE_CLASS_MOISTURE, + EntityTypes.smoke: DEVICE_CLASS_SMOKE, } SENSOR_MODELS = { - EntityTypes.entry: "Entry Sensor", EntityTypes.carbon_monoxide: "Carbon Monoxide Detector", - EntityTypes.smoke: "Smoke Detector", + EntityTypes.entry: "Entry Sensor", EntityTypes.leak: "Water Sensor", + EntityTypes.smoke: "Smoke Detector", } @@ -56,10 +56,10 @@ async def async_setup_entry(hass, entry, async_add_entities): continue for sensor in system.sensors.values(): - if sensor.type in SUPPORTED_SENSOR_TYPES: - sensors.append(SimpliSafeBinarySensor(simplisafe, system, sensor)) + if sensor.type in SUPPORTED_TRIGGERED_SENSOR_TYPES: + sensors.append(TriggeredBinarySensor(simplisafe, system, sensor)) if sensor.type in SUPPORTED_BATTERY_SENSOR_TYPES: - sensors.append(SimpliSafeSensorBattery(simplisafe, system, sensor)) + sensors.append(BatteryBinarySensor(simplisafe, system, sensor)) async_add_entities(sensors) @@ -70,6 +70,17 @@ class SimpliSafeBinarySensor(SimpliSafeEntity, BinarySensorEntity): def __init__(self, simplisafe, system, sensor): """Initialize.""" super().__init__(simplisafe, system, sensor.name, serial=sensor.serial) + self._device_info["identifiers"] = {(DOMAIN, sensor.serial)} + self._device_info["model"] = SENSOR_MODELS[sensor.type] + self._device_info["name"] = sensor.name + + +class TriggeredBinarySensor(SimpliSafeBinarySensor): + """Define a binary sensor related to whether an entity has been triggered.""" + + def __init__(self, simplisafe, system, sensor): + """Initialize.""" + super().__init__(simplisafe, system, sensor) self._system = system self._sensor = sensor self._is_on = False @@ -77,16 +88,7 @@ class SimpliSafeBinarySensor(SimpliSafeEntity, BinarySensorEntity): @property def device_class(self): """Return type of sensor.""" - return HA_SENSOR_TYPES[self._sensor.type] - - @property - def device_info(self): - """Return device registry information for this entity.""" - info = super().device_info - info["identifiers"] = {(DOMAIN, self._sensor.serial)} - info["model"] = SENSOR_MODELS[self._sensor.type] - info["name"] = self._sensor.name - return info + return DEVICE_CLASSES[self._sensor.type] @property def is_on(self): @@ -99,19 +101,15 @@ class SimpliSafeBinarySensor(SimpliSafeEntity, BinarySensorEntity): self._is_on = self._sensor.triggered -class SimpliSafeSensorBattery(SimpliSafeEntity, BinarySensorEntity): +class BatteryBinarySensor(SimpliSafeEntity, BinarySensorEntity): """Define a SimpliSafe battery binary sensor entity.""" def __init__(self, simplisafe, system, sensor): """Initialize.""" - super().__init__(simplisafe, system, sensor.name, serial=sensor.serial) + super().__init__(simplisafe, system, sensor) self._sensor = sensor self._is_low = False - self._device_info["identifiers"] = {(DOMAIN, sensor.serial)} - self._device_info["model"] = SENSOR_MODELS[sensor.type] - self._device_info["name"] = sensor.name - @property def device_class(self): """Return type of sensor.""" From 943cc243b5e8af4b0ed0d18b92e8ef369dbd718a Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 4 Nov 2020 12:34:14 -0700 Subject: [PATCH 113/118] Fix missing sensor exceptions in SimpliSafe (#42845) * Fix missing sensor exceptions in SimpliSafe * Entity types * Don't overdo it --- .../components/simplisafe/__init__.py | 12 +++++ .../components/simplisafe/binary_sensor.py | 52 ++++++------------- homeassistant/components/simplisafe/sensor.py | 11 ++-- 3 files changed, 31 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index a3f56a9e1a3..1389c1d1f08 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -724,3 +724,15 @@ class SimpliSafeEntity(CoordinatorEntity): @callback def async_update_from_websocket_event(self, event): """Update the entity with the provided websocket event.""" + + +class SimpliSafeBaseSensor(SimpliSafeEntity): + """Define a SimpliSafe base (binary) sensor.""" + + def __init__(self, simplisafe, system, sensor): + """Initialize.""" + super().__init__(simplisafe, system, sensor.name, serial=sensor.serial) + self._device_info["identifiers"] = {(DOMAIN, sensor.serial)} + self._device_info["model"] = sensor.type.name + self._device_info["name"] = sensor.name + self._sensor = sensor diff --git a/homeassistant/components/simplisafe/binary_sensor.py b/homeassistant/components/simplisafe/binary_sensor.py index 67a4af4752d..aa0132cb330 100644 --- a/homeassistant/components/simplisafe/binary_sensor.py +++ b/homeassistant/components/simplisafe/binary_sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.binary_sensor import ( ) from homeassistant.core import callback -from . import SimpliSafeEntity +from . import SimpliSafeBaseSensor from .const import DATA_CLIENT, DOMAIN, LOGGER SUPPORTED_BATTERY_SENSOR_TYPES = [ @@ -23,27 +23,13 @@ SUPPORTED_BATTERY_SENSOR_TYPES = [ EntityTypes.temperature, ] -SUPPORTED_TRIGGERED_SENSOR_TYPES = [ - EntityTypes.carbon_monoxide, - EntityTypes.entry, - EntityTypes.leak, - EntityTypes.smoke, -] - -DEVICE_CLASSES = { +TRIGGERED_SENSOR_TYPES = { EntityTypes.carbon_monoxide: DEVICE_CLASS_GAS, EntityTypes.entry: DEVICE_CLASS_DOOR, EntityTypes.leak: DEVICE_CLASS_MOISTURE, EntityTypes.smoke: DEVICE_CLASS_SMOKE, } -SENSOR_MODELS = { - EntityTypes.carbon_monoxide: "Carbon Monoxide Detector", - EntityTypes.entry: "Entry Sensor", - EntityTypes.leak: "Water Sensor", - EntityTypes.smoke: "Smoke Detector", -} - async def async_setup_entry(hass, entry, async_add_entities): """Set up SimpliSafe binary sensors based on a config entry.""" @@ -56,39 +42,34 @@ async def async_setup_entry(hass, entry, async_add_entities): continue for sensor in system.sensors.values(): - if sensor.type in SUPPORTED_TRIGGERED_SENSOR_TYPES: - sensors.append(TriggeredBinarySensor(simplisafe, system, sensor)) + if sensor.type in TRIGGERED_SENSOR_TYPES: + sensors.append( + TriggeredBinarySensor( + simplisafe, + system, + sensor, + TRIGGERED_SENSOR_TYPES[sensor.type], + ) + ) if sensor.type in SUPPORTED_BATTERY_SENSOR_TYPES: sensors.append(BatteryBinarySensor(simplisafe, system, sensor)) async_add_entities(sensors) -class SimpliSafeBinarySensor(SimpliSafeEntity, BinarySensorEntity): - """Define a SimpliSafe binary sensor entity.""" - - def __init__(self, simplisafe, system, sensor): - """Initialize.""" - super().__init__(simplisafe, system, sensor.name, serial=sensor.serial) - self._device_info["identifiers"] = {(DOMAIN, sensor.serial)} - self._device_info["model"] = SENSOR_MODELS[sensor.type] - self._device_info["name"] = sensor.name - - -class TriggeredBinarySensor(SimpliSafeBinarySensor): +class TriggeredBinarySensor(SimpliSafeBaseSensor, BinarySensorEntity): """Define a binary sensor related to whether an entity has been triggered.""" - def __init__(self, simplisafe, system, sensor): + def __init__(self, simplisafe, system, sensor, device_class): """Initialize.""" super().__init__(simplisafe, system, sensor) - self._system = system - self._sensor = sensor + self._device_class = device_class self._is_on = False @property def device_class(self): """Return type of sensor.""" - return DEVICE_CLASSES[self._sensor.type] + return self._device_class @property def is_on(self): @@ -101,13 +82,12 @@ class TriggeredBinarySensor(SimpliSafeBinarySensor): self._is_on = self._sensor.triggered -class BatteryBinarySensor(SimpliSafeEntity, BinarySensorEntity): +class BatteryBinarySensor(SimpliSafeBaseSensor, BinarySensorEntity): """Define a SimpliSafe battery binary sensor entity.""" def __init__(self, simplisafe, system, sensor): """Initialize.""" super().__init__(simplisafe, system, sensor) - self._sensor = sensor self._is_low = False @property diff --git a/homeassistant/components/simplisafe/sensor.py b/homeassistant/components/simplisafe/sensor.py index 5909471c455..7e927ee942e 100644 --- a/homeassistant/components/simplisafe/sensor.py +++ b/homeassistant/components/simplisafe/sensor.py @@ -4,7 +4,7 @@ from simplipy.entity import EntityTypes from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_FAHRENHEIT from homeassistant.core import callback -from . import SimpliSafeEntity +from . import SimpliSafeBaseSensor from .const import DATA_CLIENT, DOMAIN, LOGGER @@ -25,19 +25,14 @@ async def async_setup_entry(hass, entry, async_add_entities): async_add_entities(sensors) -class SimplisafeFreezeSensor(SimpliSafeEntity): +class SimplisafeFreezeSensor(SimpliSafeBaseSensor): """Define a SimpliSafe freeze sensor entity.""" def __init__(self, simplisafe, system, sensor): """Initialize.""" - super().__init__(simplisafe, system, sensor.name, serial=sensor.serial) - self._sensor = sensor + super().__init__(simplisafe, system, sensor) self._state = None - self._device_info["identifiers"] = {(DOMAIN, sensor.serial)} - self._device_info["model"] = "Freeze Sensor" - self._device_info["name"] = sensor.name - @property def device_class(self): """Return type of sensor.""" From 7c084975b72d52e6015ce6103949b01a8573b5f3 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 4 Nov 2020 18:59:54 -0700 Subject: [PATCH 114/118] Fix missing/incorrect SimpliSafe entities (#42846) * Entity types * Don't overdo it * Fix missing/incorrect SimpliSafe entities --- .../components/simplisafe/__init__.py | 20 ++++++++++++++++--- .../components/simplisafe/binary_sensor.py | 6 +++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index 1389c1d1f08..f71e69d9aab 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -3,6 +3,7 @@ import asyncio from uuid import UUID from simplipy import API +from simplipy.entity import EntityTypes from simplipy.errors import EndpointUnavailable, InvalidCredentialsError, SimplipyError from simplipy.websocket import ( EVENT_CAMERA_MOTION_DETECTED, @@ -590,6 +591,13 @@ class SimpliSafeEntity(CoordinatorEntity): else: self._serial = system.serial + try: + sensor_type = EntityTypes( + simplisafe.initial_event_to_use[system.system_id].get("sensorType") + ) + except ValueError: + sensor_type = EntityTypes.unknown + self._attrs = { ATTR_LAST_EVENT_INFO: simplisafe.initial_event_to_use[system.system_id].get( "info" @@ -597,9 +605,7 @@ class SimpliSafeEntity(CoordinatorEntity): ATTR_LAST_EVENT_SENSOR_NAME: simplisafe.initial_event_to_use[ system.system_id ].get("sensorName"), - ATTR_LAST_EVENT_SENSOR_TYPE: simplisafe.initial_event_to_use[ - system.system_id - ].get("sensorType"), + ATTR_LAST_EVENT_SENSOR_TYPE: sensor_type.name, ATTR_LAST_EVENT_TIMESTAMP: simplisafe.initial_event_to_use[ system.system_id ].get("eventTimestamp"), @@ -736,3 +742,11 @@ class SimpliSafeBaseSensor(SimpliSafeEntity): self._device_info["model"] = sensor.type.name self._device_info["name"] = sensor.name self._sensor = sensor + self._sensor_type_human_name = " ".join( + [w.title() for w in self._sensor.type.name.split("_")] + ) + + @property + def name(self): + """Return the name of the sensor.""" + return f"{self._system.address} {self._name} {self._sensor_type_human_name}" diff --git a/homeassistant/components/simplisafe/binary_sensor.py b/homeassistant/components/simplisafe/binary_sensor.py index aa0132cb330..2a4f430e9cc 100644 --- a/homeassistant/components/simplisafe/binary_sensor.py +++ b/homeassistant/components/simplisafe/binary_sensor.py @@ -6,6 +6,8 @@ from homeassistant.components.binary_sensor import ( DEVICE_CLASS_DOOR, DEVICE_CLASS_GAS, DEVICE_CLASS_MOISTURE, + DEVICE_CLASS_MOTION, + DEVICE_CLASS_SAFETY, DEVICE_CLASS_SMOKE, BinarySensorEntity, ) @@ -18,7 +20,7 @@ SUPPORTED_BATTERY_SENSOR_TYPES = [ EntityTypes.carbon_monoxide, EntityTypes.entry, EntityTypes.leak, - EntityTypes.lock, + EntityTypes.lock_keypad, EntityTypes.smoke, EntityTypes.temperature, ] @@ -26,7 +28,9 @@ SUPPORTED_BATTERY_SENSOR_TYPES = [ TRIGGERED_SENSOR_TYPES = { EntityTypes.carbon_monoxide: DEVICE_CLASS_GAS, EntityTypes.entry: DEVICE_CLASS_DOOR, + EntityTypes.glass_break: DEVICE_CLASS_SAFETY, EntityTypes.leak: DEVICE_CLASS_MOISTURE, + EntityTypes.motion: DEVICE_CLASS_MOTION, EntityTypes.smoke: DEVICE_CLASS_SMOKE, } From b65c92ce912ef8c65b00c6d3eea9f8ea0570f70c Mon Sep 17 00:00:00 2001 From: cgtobi Date: Thu, 5 Nov 2020 14:16:51 +0100 Subject: [PATCH 115/118] Fix Netatmo public weather sensor bug (#42858) --- .../components/netatmo/netatmo_entity_base.py | 8 ++--- homeassistant/components/netatmo/sensor.py | 32 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/netatmo/netatmo_entity_base.py b/homeassistant/components/netatmo/netatmo_entity_base.py index 6bae7d54168..db2f8877f61 100644 --- a/homeassistant/components/netatmo/netatmo_entity_base.py +++ b/homeassistant/components/netatmo/netatmo_entity_base.py @@ -45,10 +45,10 @@ class NetatmoBase(Entity): data_class["name"], signal_name, self.async_update_callback, - LAT_NE=data_class["LAT_NE"], - LON_NE=data_class["LON_NE"], - LAT_SW=data_class["LAT_SW"], - LON_SW=data_class["LON_SW"], + lat_ne=data_class["lat_ne"], + lon_ne=data_class["lon_ne"], + lat_sw=data_class["lat_sw"], + lon_sw=data_class["lon_sw"], ) else: diff --git a/homeassistant/components/netatmo/sensor.py b/homeassistant/components/netatmo/sensor.py index 84e18ba7241..2e810ba2271 100644 --- a/homeassistant/components/netatmo/sensor.py +++ b/homeassistant/components/netatmo/sensor.py @@ -202,10 +202,10 @@ async def async_setup_entry(hass, entry, async_add_entities): PUBLICDATA_DATA_CLASS_NAME, signal_name, None, - LAT_NE=area.lat_ne, - LON_NE=area.lon_ne, - LAT_SW=area.lat_sw, - LON_SW=area.lon_sw, + lat_ne=area.lat_ne, + lon_ne=area.lon_ne, + lat_sw=area.lat_sw, + lon_sw=area.lon_sw, ) for sensor_type in SUPPORTED_PUBLIC_SENSOR_TYPES: new_entities.append( @@ -473,10 +473,10 @@ class NetatmoPublicSensor(NetatmoBase): self._data_classes.append( { "name": PUBLICDATA_DATA_CLASS_NAME, - "LAT_NE": area.lat_ne, - "LON_NE": area.lon_ne, - "LAT_SW": area.lat_sw, - "LON_SW": area.lon_sw, + "lat_ne": area.lat_ne, + "lon_ne": area.lon_ne, + "lat_sw": area.lat_sw, + "lon_sw": area.lon_sw, "area_name": area.area_name, SIGNAL_NAME: self._signal_name, } @@ -563,10 +563,10 @@ class NetatmoPublicSensor(NetatmoBase): self._data_classes = [ { "name": PUBLICDATA_DATA_CLASS_NAME, - "LAT_NE": area.lat_ne, - "LON_NE": area.lon_ne, - "LAT_SW": area.lat_sw, - "LON_SW": area.lon_sw, + "lat_ne": area.lat_ne, + "lon_ne": area.lon_ne, + "lat_sw": area.lat_sw, + "lon_sw": area.lon_sw, "area_name": area.area_name, SIGNAL_NAME: self._signal_name, } @@ -577,10 +577,10 @@ class NetatmoPublicSensor(NetatmoBase): PUBLICDATA_DATA_CLASS_NAME, self._signal_name, self.async_update_callback, - LAT_NE=area.lat_ne, - LON_NE=area.lon_ne, - LAT_SW=area.lat_sw, - LON_SW=area.lon_sw, + lat_ne=area.lat_ne, + lon_ne=area.lon_ne, + lat_sw=area.lat_sw, + lon_sw=area.lon_sw, ) @callback From 7d0f5fa31a8400332656b28a8fd7e00cd3becf6a Mon Sep 17 00:00:00 2001 From: Andreas Billmeier Date: Thu, 5 Nov 2020 09:45:29 +0100 Subject: [PATCH 116/118] Revert "Fix broken maxcube component" (#42859) This reverts commit 898f50386ff509ffb7b60a6e3f61eafc96ad484d. --- homeassistant/components/maxcube/binary_sensor.py | 2 +- homeassistant/components/maxcube/climate.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/maxcube/binary_sensor.py b/homeassistant/components/maxcube/binary_sensor.py index 376076352a6..06b53456973 100644 --- a/homeassistant/components/maxcube/binary_sensor.py +++ b/homeassistant/components/maxcube/binary_sensor.py @@ -16,7 +16,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): name = f"{cube.room_by_id(device.room_id).name} {device.name}" # Only add Window Shutters - if device.is_windowshutter(): + if cube.is_windowshutter(device): devices.append(MaxCubeShutter(handler, name, device.rf_address)) if devices: diff --git a/homeassistant/components/maxcube/climate.py b/homeassistant/components/maxcube/climate.py index c17cc988c1d..e222784ca57 100644 --- a/homeassistant/components/maxcube/climate.py +++ b/homeassistant/components/maxcube/climate.py @@ -70,7 +70,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): for device in cube.devices: name = f"{cube.room_by_id(device.room_id).name} {device.name}" - if device.is_thermostat() or device.is_wallthermostat(): + if cube.is_thermostat(device) or cube.is_wallthermostat(device): devices.append(MaxCubeClimate(handler, name, device.rf_address)) if devices: @@ -180,11 +180,11 @@ class MaxCubeClimate(ClimateEntity): device = cube.device_by_rf(self._rf_address) valve = 0 - if device.is_thermostat(): + if cube.is_thermostat(device): valve = device.valve_position - elif device.is_wallthermostat(): + elif cube.is_wallthermostat(device): for device in cube.devices_by_room(cube.room_by_id(device.room_id)): - if device.is_thermostat() and device.valve_position > 0: + if cube.is_thermostat(device) and device.valve_position > 0: valve = device.valve_position break else: @@ -287,7 +287,7 @@ class MaxCubeClimate(ClimateEntity): cube = self._cubehandle.cube device = cube.device_by_rf(self._rf_address) - if not device.is_thermostat(): + if not cube.is_thermostat(device): return {} return {ATTR_VALVE_POSITION: device.valve_position} From dfed834617736a4130a4a3c1da85e310cd62582e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 5 Nov 2020 13:45:58 +0000 Subject: [PATCH 117/118] Bumped version to 0.117.5 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 8911ed2c5f2..7b660116296 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 117 -PATCH_VERSION = "4" +PATCH_VERSION = "5" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1) From 8895c65dd497c3630dd193dc95c4ce0b4a45d960 Mon Sep 17 00:00:00 2001 From: rikroe <42204099+rikroe@users.noreply.github.com> Date: Thu, 5 Nov 2020 16:32:02 +0100 Subject: [PATCH 118/118] Bump bimmer_connected to 0.7.12 (#42875) Co-authored-by: rikroe --- homeassistant/components/bmw_connected_drive/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index 6a886fc9ef4..85b521caa39 100644 --- a/homeassistant/components/bmw_connected_drive/manifest.json +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -2,7 +2,7 @@ "domain": "bmw_connected_drive", "name": "BMW Connected Drive", "documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive", - "requirements": ["bimmer_connected==0.7.11"], + "requirements": ["bimmer_connected==0.7.12"], "dependencies": [], "codeowners": ["@gerard33", "@rikroe"] } diff --git a/requirements_all.txt b/requirements_all.txt index 7504b10e9e2..c490cb121ea 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -342,7 +342,7 @@ beautifulsoup4==4.9.1 bellows==0.20.3 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.7.11 +bimmer_connected==0.7.12 # homeassistant.components.bizkaibus bizkaibus==0.1.1