From 1e81d148222ee07e6b0f2c431036876dfa48eab3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 6 Feb 2020 03:55:11 -0800 Subject: [PATCH 01/32] Fix automation sun import (#31521) --- homeassistant/components/automation/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 528a314dd7b..1a73de885c0 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -5,7 +5,6 @@ from typing import Any, Awaitable, Callable, List, Optional, Set import voluptuous as vol -from homeassistant.components import sun from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_NAME, @@ -578,6 +577,6 @@ def _trigger_extract_entities(trigger_conf: dict) -> List[str]: return [trigger_conf[CONF_ZONE]] if trigger_conf[CONF_PLATFORM] == "sun": - return [sun.ENTITY_ID] + return ["sun.sun"] return [] From 60c5ef5b343eefa80c1081a174faf329b7e48e3d Mon Sep 17 00:00:00 2001 From: Robert Chmielowiec Date: Thu, 6 Feb 2020 15:53:16 +0100 Subject: [PATCH 02/32] Fix migrating huawei_lte entry without recipient (#31522) --- homeassistant/components/huawei_lte/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/huawei_lte/__init__.py b/homeassistant/components/huawei_lte/__init__.py index 1b8cb658c28..1d54f972907 100644 --- a/homeassistant/components/huawei_lte/__init__.py +++ b/homeassistant/components/huawei_lte/__init__.py @@ -510,7 +510,7 @@ async def async_migrate_entry(hass: HomeAssistantType, config_entry: ConfigEntry """Migrate config entry to new version.""" if config_entry.version == 1: options = config_entry.options - recipient = options[CONF_RECIPIENT] + recipient = options.get(CONF_RECIPIENT) if isinstance(recipient, str): options[CONF_RECIPIENT] = [x.strip() for x in recipient.split(",")] config_entry.version = 2 From 6b5cd74771e78489514d194bfa67d8f4dd0ebe40 Mon Sep 17 00:00:00 2001 From: Dougal Matthews Date: Thu, 6 Feb 2020 16:52:46 +0000 Subject: [PATCH 03/32] Only normalise Garmin connect data to minutes if the value is not None (#31526) Otherwise this causes additional TypeError messages to be logged for division of None. --- homeassistant/components/garmin_connect/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/garmin_connect/sensor.py b/homeassistant/components/garmin_connect/sensor.py index 6a3128cae01..154f7010db4 100644 --- a/homeassistant/components/garmin_connect/sensor.py +++ b/homeassistant/components/garmin_connect/sensor.py @@ -165,9 +165,9 @@ class GarminConnectSensor(Entity): return data = self._data.data - if "Duration" in self._type: + if "Duration" in self._type and data[self._type]: self._state = data[self._type] // 60 - elif "Seconds" in self._type: + elif "Seconds" in self._type and data[self._type]: self._state = data[self._type] // 60 else: self._state = data[self._type] From bf1b78f621543d7fce2df074493afe4cfc049451 Mon Sep 17 00:00:00 2001 From: Quentame Date: Thu, 6 Feb 2020 17:53:42 +0100 Subject: [PATCH 04/32] Fix iCloud determine_interval: add default interval to max_interval (#31533) --- homeassistant/components/icloud/account.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/icloud/account.py b/homeassistant/components/icloud/account.py index af7963d8dc1..5d681539668 100644 --- a/homeassistant/components/icloud/account.py +++ b/homeassistant/components/icloud/account.py @@ -26,6 +26,7 @@ from .const import ( DEVICE_DISPLAY_NAME, DEVICE_ID, DEVICE_LOCATION, + DEVICE_LOCATION_HORIZONTAL_ACCURACY, DEVICE_LOCATION_LATITUDE, DEVICE_LOCATION_LONGITUDE, DEVICE_LOST_MODE_CAPABLE, @@ -175,8 +176,9 @@ class IcloudAccount: def _determine_interval(self) -> int: """Calculate new interval between two API fetch (in minutes).""" - intervals = {} + intervals = {"default": self._max_interval} for device in self._devices.values(): + # Max interval if no location if device.location is None: continue @@ -186,10 +188,11 @@ class IcloudAccount: self.hass, device.location[DEVICE_LOCATION_LATITUDE], device.location[DEVICE_LOCATION_LONGITUDE], + device.location[DEVICE_LOCATION_HORIZONTAL_ACCURACY], ).result() + # Max interval if in zone if current_zone is not None: - intervals[device.name] = self._max_interval continue zones = ( @@ -209,6 +212,7 @@ class IcloudAccount: ) distances.append(round(zone_distance / 1000, 1)) + # Max interval if no zone if not distances: continue mindistance = min(distances) From 080f9725dcf10e667bbeb49eb00f64aebed67b05 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 6 Feb 2020 17:00:27 +0100 Subject: [PATCH 05/32] Limit OAuth scopes for Netatmo and Home Assistant Cloud (#31538) * Limit OAuth scopes for Netatmo and Home Assistant Cloud * Fix tests by making order of scopes predictable --- .../components/netatmo/config_flow.py | 34 +++++++++---------- tests/components/netatmo/test_config_flow.py | 8 ++--- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/netatmo/config_flow.py b/homeassistant/components/netatmo/config_flow.py index 8f59382dd46..dce87fb7931 100644 --- a/homeassistant/components/netatmo/config_flow.py +++ b/homeassistant/components/netatmo/config_flow.py @@ -25,24 +25,22 @@ class NetatmoFlowHandler( @property def extra_authorize_data(self) -> dict: """Extra data that needs to be appended to the authorize url.""" - return { - "scope": ( - " ".join( - [ - "read_station", - "read_camera", - "access_camera", - "write_camera", - "read_presence", - "access_presence", - "read_homecoach", - "read_smokedetector", - "read_thermostat", - "write_thermostat", - ] - ) - ) - } + scopes = [ + "read_camera", + "read_homecoach", + "read_presence", + "read_smokedetector", + "read_station", + "read_thermostat", + "write_camera", + "write_thermostat", + ] + + if self.flow_impl.name != "Home Assistant Cloud": + scopes.extend(["access_camera", "access_presence"]) + scopes.sort() + + return {"scope": " ".join(scopes)} async def async_step_user(self, user_input=None): """Handle a flow start.""" diff --git a/tests/components/netatmo/test_config_flow.py b/tests/components/netatmo/test_config_flow.py index 24aac6dc878..d76578d277c 100644 --- a/tests/components/netatmo/test_config_flow.py +++ b/tests/components/netatmo/test_config_flow.py @@ -54,15 +54,15 @@ async def test_full_flow(hass, aiohttp_client, aioclient_mock): scope = "+".join( [ - "read_station", - "read_camera", "access_camera", - "write_camera", - "read_presence", "access_presence", + "read_camera", "read_homecoach", + "read_presence", "read_smokedetector", + "read_station", "read_thermostat", + "write_camera", "write_thermostat", ] ) From 64fc7103bebbdba7f2b215cb8face574466c9735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Thu, 6 Feb 2020 20:54:01 +0200 Subject: [PATCH 06/32] Use min and m as units in Garmin Connect for consistency and correctness (#31543) --- .../components/garmin_connect/const.py | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/garmin_connect/const.py b/homeassistant/components/garmin_connect/const.py index 57cd35e667f..e38bd72c1ee 100644 --- a/homeassistant/components/garmin_connect/const.py +++ b/homeassistant/components/garmin_connect/const.py @@ -27,7 +27,7 @@ GARMIN_ENTITY_LIST = { False, ], "netCalorieGoal": ["Net Calorie Goal", "cal", "mdi:food", None, False], - "totalDistanceMeters": ["Total Distance Mtr", "mtr", "mdi:walk", None, True], + "totalDistanceMeters": ["Total Distance Mtr", "m", "mdi:walk", None, True], "wellnessStartTimeLocal": [ "Wellness Start Time", "", @@ -43,7 +43,7 @@ GARMIN_ENTITY_LIST = { False, ], "wellnessDescription": ["Wellness Description", "", "mdi:clock", None, False], - "wellnessDistanceMeters": ["Wellness Distance Mtr", "mtr", "mdi:walk", None, False], + "wellnessDistanceMeters": ["Wellness Distance Mtr", "m", "mdi:walk", None, False], "wellnessActiveKilocalories": [ "Wellness Active KiloCalories", "kcal", @@ -52,16 +52,16 @@ GARMIN_ENTITY_LIST = { False, ], "wellnessKilocalories": ["Wellness KiloCalories", "kcal", "mdi:food", None, False], - "highlyActiveSeconds": ["Highly Active Time", "minutes", "mdi:fire", None, False], - "activeSeconds": ["Active Time", "minutes", "mdi:fire", None, True], - "sedentarySeconds": ["Sedentary Time", "minutes", "mdi:seat", None, True], - "sleepingSeconds": ["Sleeping Time", "minutes", "mdi:sleep", None, True], - "measurableAwakeDuration": ["Awake Duration", "minutes", "mdi:sleep", None, True], - "measurableAsleepDuration": ["Sleep Duration", "minutes", "mdi:sleep", None, True], - "floorsAscendedInMeters": ["Floors Ascended Mtr", "mtr", "mdi:stairs", None, False], + "highlyActiveSeconds": ["Highly Active Time", "min", "mdi:fire", None, False], + "activeSeconds": ["Active Time", "min", "mdi:fire", None, True], + "sedentarySeconds": ["Sedentary Time", "min", "mdi:seat", None, True], + "sleepingSeconds": ["Sleeping Time", "min", "mdi:sleep", None, True], + "measurableAwakeDuration": ["Awake Duration", "min", "mdi:sleep", None, True], + "measurableAsleepDuration": ["Sleep Duration", "min", "mdi:sleep", None, True], + "floorsAscendedInMeters": ["Floors Ascended Mtr", "m", "mdi:stairs", None, False], "floorsDescendedInMeters": [ "Floors Descended Mtr", - "mtr", + "m", "mdi:stairs", None, False, @@ -97,52 +97,46 @@ GARMIN_ENTITY_LIST = { "averageStressLevel": ["Avg Stress Level", "", "mdi:flash-alert", None, True], "maxStressLevel": ["Max Stress Level", "", "mdi:flash-alert", None, True], "stressQualifier": ["Stress Qualifier", "", "mdi:flash-alert", None, False], - "stressDuration": ["Stress Duration", "minutes", "mdi:flash-alert", None, False], + "stressDuration": ["Stress Duration", "min", "mdi:flash-alert", None, False], "restStressDuration": [ "Rest Stress Duration", - "minutes", + "min", "mdi:flash-alert", None, True, ], "activityStressDuration": [ "Activity Stress Duration", - "minutes", + "min", "mdi:flash-alert", None, True, ], "uncategorizedStressDuration": [ "Uncat. Stress Duration", - "minutes", + "min", "mdi:flash-alert", None, True, ], "totalStressDuration": [ "Total Stress Duration", - "minutes", - "mdi:flash-alert", - None, - True, - ], - "lowStressDuration": [ - "Low Stress Duration", - "minutes", + "min", "mdi:flash-alert", None, True, ], + "lowStressDuration": ["Low Stress Duration", "min", "mdi:flash-alert", None, True], "mediumStressDuration": [ "Medium Stress Duration", - "minutes", + "min", "mdi:flash-alert", None, True, ], "highStressDuration": [ "High Stress Duration", - "minutes", + "min", "mdi:flash-alert", None, True, @@ -192,19 +186,19 @@ GARMIN_ENTITY_LIST = { ], "moderateIntensityMinutes": [ "Moderate Intensity", - "minutes", + "min", "mdi:flash-alert", None, False, ], "vigorousIntensityMinutes": [ "Vigorous Intensity", - "minutes", + "min", "mdi:run-fast", None, False, ], - "intensityMinutesGoal": ["Intensity Goal", "minutes", "mdi:run-fast", None, False], + "intensityMinutesGoal": ["Intensity Goal", "min", "mdi:run-fast", None, False], "bodyBatteryChargedValue": [ "Body Battery Charged", "%", From 774f0f9b675b997c8f8709928d5780cac0e9100d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 6 Feb 2020 09:26:51 -0800 Subject: [PATCH 07/32] Deprecate old netatmo keys (#31544) --- homeassistant/components/netatmo/__init__.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/netatmo/__init__.py b/homeassistant/components/netatmo/__init__.py index ace12d3838c..bd79f597b5b 100644 --- a/homeassistant/components/netatmo/__init__.py +++ b/homeassistant/components/netatmo/__init__.py @@ -5,7 +5,12 @@ import logging import voluptuous as vol from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET +from homeassistant.const import ( + CONF_CLIENT_ID, + CONF_CLIENT_SECRET, + CONF_DISCOVERY, + CONF_USERNAME, +) from homeassistant.core import HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow, config_validation as cv @@ -14,12 +19,19 @@ from .const import AUTH, DATA_PERSONS, DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN _LOGGER = logging.getLogger(__name__) +CONF_SECRET_KEY = "secret_key" +CONF_WEBHOOKS = "webhooks" + CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.Schema( { vol.Required(CONF_CLIENT_ID): cv.string, vol.Required(CONF_CLIENT_SECRET): cv.string, + cv.deprecated(CONF_SECRET_KEY): cv.match_all, + cv.deprecated(CONF_USERNAME): cv.match_all, + cv.deprecated(CONF_WEBHOOKS): cv.match_all, + cv.deprecated(CONF_DISCOVERY): cv.match_all, } ) }, From bb96584d6ea66c4aebf7d4f35ee9e0feeabc51c9 Mon Sep 17 00:00:00 2001 From: Josh Bendavid Date: Thu, 6 Feb 2020 19:00:54 +0100 Subject: [PATCH 08/32] update aiopylgtv to 0.3.3 (#31545) --- homeassistant/components/webostv/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/webostv/manifest.json b/homeassistant/components/webostv/manifest.json index e55867432cc..acdee1d9ca9 100644 --- a/homeassistant/components/webostv/manifest.json +++ b/homeassistant/components/webostv/manifest.json @@ -2,7 +2,7 @@ "domain": "webostv", "name": "LG webOS Smart TV", "documentation": "https://www.home-assistant.io/integrations/webostv", - "requirements": ["aiopylgtv==0.3.2"], + "requirements": ["aiopylgtv==0.3.3"], "dependencies": ["configurator"], "codeowners": ["@bendavid"] } diff --git a/requirements_all.txt b/requirements_all.txt index 2e32aee8db0..627481a8d40 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -190,7 +190,7 @@ aionotion==1.1.0 aiopvapi==1.6.14 # homeassistant.components.webostv -aiopylgtv==0.3.2 +aiopylgtv==0.3.3 # homeassistant.components.switcher_kis aioswitcher==2019.4.26 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e2ac2d07cd6..c974743c516 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -69,7 +69,7 @@ aiohue==1.10.1 aionotion==1.1.0 # homeassistant.components.webostv -aiopylgtv==0.3.2 +aiopylgtv==0.3.3 # homeassistant.components.switcher_kis aioswitcher==2019.4.26 From d31eb9f3527c8a0e73ee943fd9683c4c55bed614 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 6 Feb 2020 10:55:12 -0800 Subject: [PATCH 09/32] Guard for reloading with no zone config (#31547) --- homeassistant/components/zone/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zone/__init__.py b/homeassistant/components/zone/__init__.py index 91a1338b671..33ac15853f4 100644 --- a/homeassistant/components/zone/__init__.py +++ b/homeassistant/components/zone/__init__.py @@ -228,7 +228,7 @@ async def async_setup(hass: HomeAssistant, config: Dict) -> bool: conf = await component.async_prepare_reload(skip_reset=True) if conf is None: return - await yaml_collection.async_load(conf[DOMAIN]) + await yaml_collection.async_load(conf.get(DOMAIN, [])) service.async_register_admin_service( hass, From 4e5c74d29384e379dae1492bb02b5df44f32e406 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 6 Feb 2020 11:08:26 -0800 Subject: [PATCH 10/32] Bumped version to 0.105.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 7d2513f3cae..c2d59eb2833 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 105 -PATCH_VERSION = "1" +PATCH_VERSION = "2" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From 8ab91eb6bb5f70c51d5e34fdac15ead4d5378e5c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 6 Feb 2020 11:39:44 -0800 Subject: [PATCH 11/32] Update MQTT service description --- homeassistant/components/mqtt/services.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/services.yaml b/homeassistant/components/mqtt/services.yaml index 77b3e3b27a1..2af3c22fe50 100644 --- a/homeassistant/components/mqtt/services.yaml +++ b/homeassistant/components/mqtt/services.yaml @@ -30,7 +30,7 @@ dump: fields: topic: description: topic to listen to - example: "openzwave/#" + example: "OpenZWave/#" duration: description: how long we should listen for messages in seconds example: 5 From cc5377747f38e6b701fc729f49e3cd2226864d3a Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Thu, 6 Feb 2020 16:34:28 -0500 Subject: [PATCH 12/32] Bump ZHA dependencies. (#31555) --- homeassistant/components/zha/manifest.json | 4 ++-- requirements_all.txt | 4 ++-- requirements_test_all.txt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index f7f70db590a..c56544d1784 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -4,10 +4,10 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zha", "requirements": [ - "bellows-homeassistant==0.13.1", + "bellows-homeassistant==0.13.2", "zha-quirks==0.0.32", "zigpy-deconz==0.7.0", - "zigpy-homeassistant==0.13.0", + "zigpy-homeassistant==0.13.1", "zigpy-xbee-homeassistant==0.9.0", "zigpy-zigate==0.5.1" ], diff --git a/requirements_all.txt b/requirements_all.txt index 627481a8d40..b8a93521a22 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -299,7 +299,7 @@ beautifulsoup4==4.8.2 beewi_smartclim==0.0.7 # homeassistant.components.zha -bellows-homeassistant==0.13.1 +bellows-homeassistant==0.13.2 # homeassistant.components.bmw_connected_drive bimmer_connected==0.6.2 @@ -2130,7 +2130,7 @@ ziggo-mediabox-xl==1.1.0 zigpy-deconz==0.7.0 # homeassistant.components.zha -zigpy-homeassistant==0.13.0 +zigpy-homeassistant==0.13.1 # homeassistant.components.zha zigpy-xbee-homeassistant==0.9.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c974743c516..b189316ba55 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -112,7 +112,7 @@ av==6.1.2 axis==25 # homeassistant.components.zha -bellows-homeassistant==0.13.1 +bellows-homeassistant==0.13.2 # homeassistant.components.bom bomradarloop==0.1.3 @@ -702,7 +702,7 @@ zha-quirks==0.0.32 zigpy-deconz==0.7.0 # homeassistant.components.zha -zigpy-homeassistant==0.13.0 +zigpy-homeassistant==0.13.1 # homeassistant.components.zha zigpy-xbee-homeassistant==0.9.0 From a02bb893ef8846d832ab105a91c19e6ca3964985 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 8 Feb 2020 13:22:48 -0600 Subject: [PATCH 13/32] Resolve August integration makes too many requests and hits rate limits (#31558) --- homeassistant/components/august/__init__.py | 69 ++++++++++--------- .../components/august/binary_sensor.py | 2 +- homeassistant/components/august/camera.py | 2 +- homeassistant/components/august/lock.py | 9 ++- homeassistant/components/august/manifest.json | 2 +- requirements_all.txt | 2 +- 6 files changed, 46 insertions(+), 40 deletions(-) diff --git a/homeassistant/components/august/__init__.py b/homeassistant/components/august/__init__.py index 0bb0d639896..a52df5e361c 100644 --- a/homeassistant/components/august/__init__.py +++ b/homeassistant/components/august/__init__.py @@ -36,8 +36,15 @@ AUGUST_CONFIG_FILE = ".august.conf" DATA_AUGUST = "august" DOMAIN = "august" DEFAULT_ENTITY_NAMESPACE = "august" -MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5) -DEFAULT_SCAN_INTERVAL = timedelta(seconds=5) + +# Limit battery and hardware updates to 1800 seconds +# in order to reduce the number of api requests and +# avoid hitting rate limits +MIN_TIME_BETWEEN_LOCK_DETAIL_UPDATES = timedelta(seconds=1800) + +DEFAULT_SCAN_INTERVAL = timedelta(seconds=10) +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10) + LOGIN_METHODS = ["phone", "email"] CONFIG_SCHEMA = vol.Schema( @@ -180,7 +187,9 @@ class AugustData: self._access_token = access_token self._doorbells = self._api.get_doorbells(self._access_token) or [] self._locks = self._api.get_operable_locks(self._access_token) or [] - self._house_ids = [d.house_id for d in self._doorbells + self._locks] + self._house_ids = set() + for device in self._doorbells + self._locks: + self._house_ids.add(device.house_id) self._doorbell_detail_by_id = {} self._lock_status_by_id = {} @@ -284,58 +293,51 @@ class AugustData: This is the status from the door sensor. """ - self._update_doors() + self._update_locks_status() return self._door_state_by_id.get(lock_id) + def _update_locks(self): + self._update_locks_status() + self._update_locks_detail() + @Throttle(MIN_TIME_BETWEEN_UPDATES) - def _update_doors(self): + def _update_locks_status(self): + status_by_id = {} state_by_id = {} - _LOGGER.debug("Start retrieving door status") + _LOGGER.debug("Start retrieving lock and door status") for lock in self._locks: - _LOGGER.debug("Updating door status for %s", lock.device_name) - + _LOGGER.debug("Updating lock and door status for %s", lock.device_name) try: - state_by_id[lock.device_id] = self._api.get_lock_door_status( - self._access_token, lock.device_id + ( + status_by_id[lock.device_id], + state_by_id[lock.device_id], + ) = self._api.get_lock_status( + self._access_token, lock.device_id, door_status=True ) except RequestException as ex: _LOGGER.error( - "Request error trying to retrieve door status for %s. %s", + "Request error trying to retrieve lock and door status for %s. %s", lock.device_name, ex, ) + status_by_id[lock.device_id] = None state_by_id[lock.device_id] = None except Exception: + status_by_id[lock.device_id] = None state_by_id[lock.device_id] = None raise - _LOGGER.debug("Completed retrieving door status") + _LOGGER.debug("Completed retrieving lock and door status") + self._lock_status_by_id = status_by_id self._door_state_by_id = state_by_id - @Throttle(MIN_TIME_BETWEEN_UPDATES) - def _update_locks(self): - status_by_id = {} + @Throttle(MIN_TIME_BETWEEN_LOCK_DETAIL_UPDATES) + def _update_locks_detail(self): detail_by_id = {} - _LOGGER.debug("Start retrieving locks status") + _LOGGER.debug("Start retrieving locks detail") for lock in self._locks: - _LOGGER.debug("Updating lock status for %s", lock.device_name) - try: - status_by_id[lock.device_id] = self._api.get_lock_status( - self._access_token, lock.device_id - ) - except RequestException as ex: - _LOGGER.error( - "Request error trying to retrieve door status for %s. %s", - lock.device_name, - ex, - ) - status_by_id[lock.device_id] = None - except Exception: - status_by_id[lock.device_id] = None - raise - try: detail_by_id[lock.device_id] = self._api.get_lock_detail( self._access_token, lock.device_id @@ -351,8 +353,7 @@ class AugustData: detail_by_id[lock.device_id] = None raise - _LOGGER.debug("Completed retrieving locks status") - self._lock_status_by_id = status_by_id + _LOGGER.debug("Completed retrieving locks detail") self._lock_detail_by_id = detail_by_id def lock(self, device_id): diff --git a/homeassistant/components/august/binary_sensor.py b/homeassistant/components/august/binary_sensor.py index 14d03189c92..f840d3db532 100644 --- a/homeassistant/components/august/binary_sensor.py +++ b/homeassistant/components/august/binary_sensor.py @@ -11,7 +11,7 @@ from . import DATA_AUGUST _LOGGER = logging.getLogger(__name__) -SCAN_INTERVAL = timedelta(seconds=5) +SCAN_INTERVAL = timedelta(seconds=10) def _retrieve_door_state(data, lock): diff --git a/homeassistant/components/august/camera.py b/homeassistant/components/august/camera.py index 2492eb75418..885ee444c6b 100644 --- a/homeassistant/components/august/camera.py +++ b/homeassistant/components/august/camera.py @@ -7,7 +7,7 @@ from homeassistant.components.camera import Camera from . import DATA_AUGUST, DEFAULT_TIMEOUT -SCAN_INTERVAL = timedelta(seconds=5) +SCAN_INTERVAL = timedelta(seconds=10) def setup_platform(hass, config, add_entities, discovery_info=None): diff --git a/homeassistant/components/august/lock.py b/homeassistant/components/august/lock.py index a541be67097..d336e21653b 100644 --- a/homeassistant/components/august/lock.py +++ b/homeassistant/components/august/lock.py @@ -12,7 +12,7 @@ from . import DATA_AUGUST _LOGGER = logging.getLogger(__name__) -SCAN_INTERVAL = timedelta(seconds=5) +SCAN_INTERVAL = timedelta(seconds=10) def setup_platform(hass, config, add_entities, discovery_info=None): @@ -88,7 +88,12 @@ class AugustLock(LockDevice): if self._lock_detail is None: return None - return {ATTR_BATTERY_LEVEL: self._lock_detail.battery_level} + attributes = {ATTR_BATTERY_LEVEL: self._lock_detail.battery_level} + + if self._lock_detail.keypad is not None: + attributes["keypad_battery_level"] = self._lock_detail.keypad.battery_level + + return attributes @property def unique_id(self) -> str: diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index e3e417d20e0..bacd7346ca7 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,7 +2,7 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["py-august==0.7.0"], + "requirements": ["py-august==0.8.1"], "dependencies": ["configurator"], "codeowners": [] } diff --git a/requirements_all.txt b/requirements_all.txt index b8a93521a22..d13bdb09a22 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1067,7 +1067,7 @@ pushetta==1.0.15 pwmled==1.4.1 # homeassistant.components.august -py-august==0.7.0 +py-august==0.8.1 # homeassistant.components.canary py-canary==0.5.0 From 8da948e909e9d77eed47aee59c43929463f78a9e Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 7 Feb 2020 01:02:00 +0100 Subject: [PATCH 14/32] Bump adguardhome to 0.4.1 (#31565) --- homeassistant/components/adguard/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/adguard/manifest.json b/homeassistant/components/adguard/manifest.json index bdfec1f254b..c77e0b3254d 100644 --- a/homeassistant/components/adguard/manifest.json +++ b/homeassistant/components/adguard/manifest.json @@ -3,7 +3,7 @@ "name": "AdGuard Home", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/adguard", - "requirements": ["adguardhome==0.4.0"], + "requirements": ["adguardhome==0.4.1"], "dependencies": [], "codeowners": ["@frenck"] } diff --git a/requirements_all.txt b/requirements_all.txt index d13bdb09a22..152aefb80f5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -117,7 +117,7 @@ adafruit-circuitpython-mcp230xx==1.1.2 adb-shell==0.1.1 # homeassistant.components.adguard -adguardhome==0.4.0 +adguardhome==0.4.1 # homeassistant.components.frontier_silicon afsapi==0.0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b189316ba55..cefc3e29f9c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -32,7 +32,7 @@ abodepy==0.17.0 adb-shell==0.1.1 # homeassistant.components.adguard -adguardhome==0.4.0 +adguardhome==0.4.1 # homeassistant.components.geonetnz_quakes aio_geojson_geonetnz_quakes==0.11 From b3aacfeb90fa9f5b7b909977d14287dd67945bd7 Mon Sep 17 00:00:00 2001 From: Rami Mosleh Date: Fri, 7 Feb 2020 15:03:32 +0200 Subject: [PATCH 15/32] Fix librouteros response error handling (#31588) --- homeassistant/components/mikrotik/hub.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mikrotik/hub.py b/homeassistant/components/mikrotik/hub.py index 2243b6cc5ce..6e64c443d8b 100644 --- a/homeassistant/components/mikrotik/hub.py +++ b/homeassistant/components/mikrotik/hub.py @@ -126,7 +126,7 @@ class MikrotikData: def get_info(self, param): """Return device model name.""" cmd = IDENTITY if param == NAME else INFO - data = list(self.command(MIKROTIK_SERVICES[cmd])) + data = self.command(MIKROTIK_SERVICES[cmd]) return data[0].get(param) if data else None def get_hub_details(self): @@ -148,7 +148,7 @@ class MikrotikData: def get_list_from_interface(self, interface): """Get devices from interface.""" - result = list(self.command(MIKROTIK_SERVICES[interface])) + result = self.command(MIKROTIK_SERVICES[interface]) return self.load_mac(result) if result else {} def restore_device(self, mac): @@ -224,7 +224,7 @@ class MikrotikData: "address": ip_address, } cmd = "/ping" - data = list(self.command(cmd, params)) + data = self.command(cmd, params) if data is not None: status = 0 for result in data: @@ -242,9 +242,9 @@ class MikrotikData: try: _LOGGER.info("Running command %s", cmd) if params: - response = self.api(cmd=cmd, **params) + response = list(self.api(cmd=cmd, **params)) else: - response = self.api(cmd=cmd) + response = list(self.api(cmd=cmd)) except ( librouteros.exceptions.ConnectionClosed, socket.error, From 4212fd899960f632bae2ea3f5b94ce98712abf2e Mon Sep 17 00:00:00 2001 From: MatthewFlamm <39341281+MatthewFlamm@users.noreply.github.com> Date: Fri, 7 Feb 2020 09:16:56 -0500 Subject: [PATCH 16/32] update pynws to 0.10.4 (#31591) --- homeassistant/components/nws/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nws/manifest.json b/homeassistant/components/nws/manifest.json index 5bb4cb46ee0..2bb77c2d95b 100644 --- a/homeassistant/components/nws/manifest.json +++ b/homeassistant/components/nws/manifest.json @@ -4,5 +4,5 @@ "documentation": "https://www.home-assistant.io/integrations/nws", "dependencies": [], "codeowners": ["@MatthewFlamm"], - "requirements": ["pynws==0.10.1"] + "requirements": ["pynws==0.10.4"] } diff --git a/requirements_all.txt b/requirements_all.txt index 152aefb80f5..ed5ab34747e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1399,7 +1399,7 @@ pynuki==1.3.3 pynut2==2.1.2 # homeassistant.components.nws -pynws==0.10.1 +pynws==0.10.4 # homeassistant.components.nx584 pynx584==0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cefc3e29f9c..a14b5e12f74 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -487,7 +487,7 @@ pymodbus==1.5.2 pymonoprice==0.3 # homeassistant.components.nws -pynws==0.10.1 +pynws==0.10.4 # homeassistant.components.nx584 pynx584==0.4 From 881c501cef693ce76b7c037b99538180b5bffe5e Mon Sep 17 00:00:00 2001 From: Ron Klinkien Date: Sat, 8 Feb 2020 18:47:54 +0100 Subject: [PATCH 17/32] Catch garmin_connect keyerrors with unknown entity type updates (#31608) * Catch keyerrors with unknown entity type updates * Change debug level and removed . from log call --- homeassistant/components/garmin_connect/const.py | 6 +++--- .../components/garmin_connect/sensor.py | 16 ++++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/garmin_connect/const.py b/homeassistant/components/garmin_connect/const.py index e38bd72c1ee..b5faeab77b4 100644 --- a/homeassistant/components/garmin_connect/const.py +++ b/homeassistant/components/garmin_connect/const.py @@ -256,21 +256,21 @@ GARMIN_ENTITY_LIST = { "brpm", "mdi:progress-clock", None, - True, + False, ], "lowestRespirationValue": [ "Lowest Respiration", "brpm", "mdi:progress-clock", None, - True, + False, ], "latestRespirationValue": [ "Latest Respiration", "brpm", "mdi:progress-clock", None, - True, + False, ], "latestRespirationTimeGMT": [ "Latest Respiration Update", diff --git a/homeassistant/components/garmin_connect/sensor.py b/homeassistant/components/garmin_connect/sensor.py index 154f7010db4..0ebd39e78d4 100644 --- a/homeassistant/components/garmin_connect/sensor.py +++ b/homeassistant/components/garmin_connect/sensor.py @@ -165,12 +165,16 @@ class GarminConnectSensor(Entity): return data = self._data.data - if "Duration" in self._type and data[self._type]: - self._state = data[self._type] // 60 - elif "Seconds" in self._type and data[self._type]: - self._state = data[self._type] // 60 - else: - self._state = data[self._type] + try: + if "Duration" in self._type and data[self._type]: + self._state = data[self._type] // 60 + elif "Seconds" in self._type and data[self._type]: + self._state = data[self._type] // 60 + else: + self._state = data[self._type] + except KeyError: + _LOGGER.debug("Entity type %s not found in fetched data", self._type) + return _LOGGER.debug( "Entity %s set to state %s %s", self._type, self._state, self._unit From fb6eedb4c2eefb223177e114878b25ac4632d06b Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Sat, 8 Feb 2020 06:50:50 -0500 Subject: [PATCH 18/32] Bump ZHA dependencies. (#31619) Bump up zigpy-homeassistant==0.13.2 --- 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 c56544d1784..f5ec96690bc 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -7,7 +7,7 @@ "bellows-homeassistant==0.13.2", "zha-quirks==0.0.32", "zigpy-deconz==0.7.0", - "zigpy-homeassistant==0.13.1", + "zigpy-homeassistant==0.13.2", "zigpy-xbee-homeassistant==0.9.0", "zigpy-zigate==0.5.1" ], diff --git a/requirements_all.txt b/requirements_all.txt index ed5ab34747e..79d7d694f82 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2130,7 +2130,7 @@ ziggo-mediabox-xl==1.1.0 zigpy-deconz==0.7.0 # homeassistant.components.zha -zigpy-homeassistant==0.13.1 +zigpy-homeassistant==0.13.2 # homeassistant.components.zha zigpy-xbee-homeassistant==0.9.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a14b5e12f74..3c4e17ac465 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -702,7 +702,7 @@ zha-quirks==0.0.32 zigpy-deconz==0.7.0 # homeassistant.components.zha -zigpy-homeassistant==0.13.1 +zigpy-homeassistant==0.13.2 # homeassistant.components.zha zigpy-xbee-homeassistant==0.9.0 From 925fa25ac75221b22aa4d8e616f3cbc2bebc7bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sat, 8 Feb 2020 16:00:20 +0100 Subject: [PATCH 19/32] Fix hvac_action for mill (#31630) --- homeassistant/components/mill/climate.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mill/climate.py b/homeassistant/components/mill/climate.py index 8f880c74c6e..d904538451c 100644 --- a/homeassistant/components/mill/climate.py +++ b/homeassistant/components/mill/climate.py @@ -6,6 +6,8 @@ import voluptuous as vol from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice from homeassistant.components.climate.const import ( + CURRENT_HVAC_HEAT, + CURRENT_HVAC_IDLE, FAN_ON, HVAC_MODE_HEAT, HVAC_MODE_OFF, @@ -167,13 +169,20 @@ class MillHeater(ClimateDevice): """Return the maximum temperature.""" return MAX_TEMP + @property + def hvac_action(self): + """Return current hvac i.e. heat, cool, idle.""" + if self._heater.is_gen1 or self._heater.is_heating == 1: + return CURRENT_HVAC_HEAT + return CURRENT_HVAC_IDLE + @property def hvac_mode(self) -> str: """Return hvac operation ie. heat, cool mode. Need to be one of HVAC_MODE_*. """ - if self._heater.is_gen1 or self._heater.is_heating == 1: + if self._heater.is_gen1 or self._heater.power_status == 1: return HVAC_MODE_HEAT return HVAC_MODE_OFF From 834b0e6cf0380a23bcebbd44532d8a761f84aefd Mon Sep 17 00:00:00 2001 From: cgtobi Date: Mon, 10 Feb 2020 12:33:07 +0100 Subject: [PATCH 20/32] Fix wrong error message in netatmo integration (#31690) --- homeassistant/components/netatmo/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/netatmo/sensor.py b/homeassistant/components/netatmo/sensor.py index 82c3748d19b..afdb7c053f3 100644 --- a/homeassistant/components/netatmo/sensor.py +++ b/homeassistant/components/netatmo/sensor.py @@ -217,7 +217,7 @@ class NetatmoSensor(Entity): if data is None: _LOGGER.info("No data found for %s (%s)", self.module_name, self._module_id) - _LOGGER.error("data: %s", self.netatmo_data.data) + _LOGGER.debug("data: %s", self.netatmo_data.data) self._state = None return From 992484fbe68e4c5e1d4ad360b7ae5153c0407f73 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 10 Feb 2020 14:45:44 -0800 Subject: [PATCH 21/32] Bumped version to 0.105.3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index c2d59eb2833..4ca501b1d23 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 105 -PATCH_VERSION = "2" +PATCH_VERSION = "3" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From 966df6a4116e7808b0d4199d6eb343db14cf35cc Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 8 Feb 2020 17:26:58 -0800 Subject: [PATCH 22/32] Guard writing automation/scene/script config (#31568) --- homeassistant/components/config/__init__.py | 28 ++++++++++++--------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/config/__init__.py b/homeassistant/components/config/__init__.py index ad7ae14ecb7..682e23dd14c 100644 --- a/homeassistant/components/config/__init__.py +++ b/homeassistant/components/config/__init__.py @@ -94,6 +94,7 @@ class BaseEditConfigView(HomeAssistantView): self.data_schema = data_schema self.post_write_hook = post_write_hook self.data_validator = data_validator + self.mutation_lock = asyncio.Lock() def _empty_config(self): """Empty config if file not found.""" @@ -114,8 +115,9 @@ class BaseEditConfigView(HomeAssistantView): async def get(self, request, config_key): """Fetch device specific config.""" hass = request.app["hass"] - current = await self.read_config(hass) - value = self._get_value(hass, current, config_key) + async with self.mutation_lock: + current = await self.read_config(hass) + value = self._get_value(hass, current, config_key) if value is None: return self.json_message("Resource not found", 404) @@ -148,10 +150,11 @@ class BaseEditConfigView(HomeAssistantView): path = hass.config.path(self.path) - current = await self.read_config(hass) - self._write_value(hass, current, config_key, data) + async with self.mutation_lock: + current = await self.read_config(hass) + self._write_value(hass, current, config_key, data) - await hass.async_add_executor_job(_write, path, current) + await hass.async_add_executor_job(_write, path, current) if self.post_write_hook is not None: hass.async_create_task( @@ -163,15 +166,16 @@ class BaseEditConfigView(HomeAssistantView): async def delete(self, request, config_key): """Remove an entry.""" hass = request.app["hass"] - current = await self.read_config(hass) - value = self._get_value(hass, current, config_key) - path = hass.config.path(self.path) + async with self.mutation_lock: + current = await self.read_config(hass) + value = self._get_value(hass, current, config_key) + path = hass.config.path(self.path) - if value is None: - return self.json_message("Resource not found", 404) + if value is None: + return self.json_message("Resource not found", 404) - self._delete_value(hass, current, config_key) - await hass.async_add_executor_job(_write, path, current) + self._delete_value(hass, current, config_key) + await hass.async_add_executor_job(_write, path, current) if self.post_write_hook is not None: hass.async_create_task(self.post_write_hook(ACTION_DELETE, config_key)) From 295963f8e8c554bf8b614dae06aca95ccc99ade6 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Mon, 10 Feb 2020 17:54:52 -0500 Subject: [PATCH 23/32] For vizio integration, set unique ID early to prevent multiple zeroconf discovery items for the same device to appear (#31686) * set unique ID early to prevent multiple zeroconf discovery items for the same device to appear * add test --- homeassistant/components/vizio/config_flow.py | 5 ++++ tests/components/vizio/test_config_flow.py | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/homeassistant/components/vizio/config_flow.py b/homeassistant/components/vizio/config_flow.py index 04f70da4a8c..4fba0f06165 100644 --- a/homeassistant/components/vizio/config_flow.py +++ b/homeassistant/components/vizio/config_flow.py @@ -205,6 +205,11 @@ class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) -> Dict[str, Any]: """Handle zeroconf discovery.""" + # Set unique ID early to prevent device from getting rediscovered multiple times + await self.async_set_unique_id( + unique_id=discovery_info[CONF_HOST].split(":")[0], raise_on_progress=True + ) + discovery_info[ CONF_HOST ] = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}" diff --git a/tests/components/vizio/test_config_flow.py b/tests/components/vizio/test_config_flow.py index cf6cdb6afdb..044ca11bc8d 100644 --- a/tests/components/vizio/test_config_flow.py +++ b/tests/components/vizio/test_config_flow.py @@ -506,3 +506,29 @@ async def test_zeroconf_flow_already_configured( # Flow should abort because device is already setup assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "already_setup" + + +async def test_zeroconf_dupe_fail( + hass: HomeAssistantType, + vizio_connect: pytest.fixture, + vizio_bypass_setup: pytest.fixture, + vizio_guess_device_type: pytest.fixture, +) -> None: + """Test zeroconf config flow when device gets discovered multiple times.""" + discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info + ) + + # Form should always show even if all required properties are discovered + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + + discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + 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_in_progress" From 7edf0460cc5ecb02d6e75b731a69f04fee44016e Mon Sep 17 00:00:00 2001 From: cgtobi Date: Wed, 12 Feb 2020 03:02:13 +0100 Subject: [PATCH 24/32] Fix missing device class in netatmo binary sensors (#31693) * Bring back device class * Add door tag sensors types * Actually discover individual tags per camera --- .../components/netatmo/binary_sensor.py | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/netatmo/binary_sensor.py b/homeassistant/components/netatmo/binary_sensor.py index 6d0de6dcceb..5f419bda2c2 100644 --- a/homeassistant/components/netatmo/binary_sensor.py +++ b/homeassistant/components/netatmo/binary_sensor.py @@ -24,7 +24,10 @@ PRESENCE_SENSOR_TYPES = { } TAG_SENSOR_TYPES = {"Tag Vibration": "vibration", "Tag Open": "opening"} -SENSOR_TYPES = {"NACamera": WELCOME_SENSOR_TYPES, "NOC": PRESENCE_SENSOR_TYPES} +SENSOR_TYPES = { + "NACamera": WELCOME_SENSOR_TYPES, + "NOC": PRESENCE_SENSOR_TYPES, +} CONF_HOME = "home" CONF_CAMERAS = "cameras" @@ -61,12 +64,28 @@ async def async_setup_entry(hass, entry, async_add_entities): sensor_types.update(SENSOR_TYPES[camera["type"]]) # Tags are only supported with Netatmo Welcome indoor cameras - if camera["type"] == "NACamera" and data.get_modules(camera["id"]): - sensor_types.update(TAG_SENSOR_TYPES) + modules = data.get_modules(camera["id"]) + if camera["type"] == "NACamera" and modules: + for module in modules: + for sensor_type in TAG_SENSOR_TYPES: + _LOGGER.debug( + "Adding camera tag %s (%s)", + module["name"], + module["id"], + ) + entities.append( + NetatmoBinarySensor( + data, + camera["id"], + home_id, + sensor_type, + module["id"], + ) + ) - for sensor_name in sensor_types: + for sensor_type in sensor_types: entities.append( - NetatmoBinarySensor(data, camera["id"], home_id, sensor_name) + NetatmoBinarySensor(data, camera["id"], home_id, sensor_type) ) except pyatmo.NoDevice: _LOGGER.debug("No camera entities to add") @@ -115,6 +134,15 @@ class NetatmoBinarySensor(BinarySensorDevice): """Return the unique ID for this sensor.""" return self._unique_id + @property + def device_class(self): + """Return the class of this sensor.""" + if self._camera_type == "NACamera": + return WELCOME_SENSOR_TYPES.get(self._sensor_type) + if self._camera_type == "NOC": + return PRESENCE_SENSOR_TYPES.get(self._sensor_type) + return TAG_SENSOR_TYPES.get(self._sensor_type) + @property def device_info(self): """Return the device info for the sensor.""" From 30e302a8a3ddf35791e458f8528fcb94c8502d9d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 11 Feb 2020 00:07:46 -0800 Subject: [PATCH 25/32] Fix person reload service (#31716) --- homeassistant/components/person/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/person/__init__.py b/homeassistant/components/person/__init__.py index dabcc046f7a..a1620c578e3 100644 --- a/homeassistant/components/person/__init__.py +++ b/homeassistant/components/person/__init__.py @@ -315,7 +315,9 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType): conf = await entity_component.async_prepare_reload(skip_reset=True) if conf is None: return - await yaml_collection.async_load(await filter_yaml_data(hass, conf[DOMAIN])) + await yaml_collection.async_load( + await filter_yaml_data(hass, conf.get(DOMAIN, [])) + ) service.async_register_admin_service( hass, DOMAIN, SERVICE_RELOAD, async_reload_yaml From 656c901f7b26234f9ddad6fb7ded99ece065abe1 Mon Sep 17 00:00:00 2001 From: SukramJ Date: Wed, 12 Feb 2020 19:12:26 +0100 Subject: [PATCH 26/32] Fix smoke detection for HomematicIP Cloud (#31753) --- homeassistant/components/homematicip_cloud/binary_sensor.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/homematicip_cloud/binary_sensor.py b/homeassistant/components/homematicip_cloud/binary_sensor.py index f16dfc986f0..52a4583be46 100644 --- a/homeassistant/components/homematicip_cloud/binary_sensor.py +++ b/homeassistant/components/homematicip_cloud/binary_sensor.py @@ -227,7 +227,11 @@ class HomematicipSmokeDetector(HomematicipGenericDevice, BinarySensorDevice): @property def is_on(self) -> bool: """Return true if smoke is detected.""" - return self._device.smokeDetectorAlarmType != SmokeDetectorAlarmType.IDLE_OFF + if self._device.smokeDetectorAlarmType: + return ( + self._device.smokeDetectorAlarmType != SmokeDetectorAlarmType.IDLE_OFF + ) + return False class HomematicipWaterDetector(HomematicipGenericDevice, BinarySensorDevice): From f042c6f8d577cddc7b1b3485451e803568be7216 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 12 Feb 2020 23:53:26 +0100 Subject: [PATCH 27/32] Updated frontend to 20200130.3 (#31771) --- 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 8c54d20429c..10d2884f182 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -3,7 +3,7 @@ "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", "requirements": [ - "home-assistant-frontend==20200130.2" + "home-assistant-frontend==20200130.3" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index af83672603b..50b9a4398dd 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -11,7 +11,7 @@ cryptography==2.8 defusedxml==0.6.0 distro==1.4.0 hass-nabucasa==0.31 -home-assistant-frontend==20200130.2 +home-assistant-frontend==20200130.3 importlib-metadata==1.4.0 jinja2>=2.10.3 netdisco==2.6.0 diff --git a/requirements_all.txt b/requirements_all.txt index 79d7d694f82..b3346d8fa0f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -679,7 +679,7 @@ hole==0.5.0 holidays==0.9.12 # homeassistant.components.frontend -home-assistant-frontend==20200130.2 +home-assistant-frontend==20200130.3 # homeassistant.components.zwave homeassistant-pyozw==0.1.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3c4e17ac465..6cb9a63a208 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -247,7 +247,7 @@ hole==0.5.0 holidays==0.9.12 # homeassistant.components.frontend -home-assistant-frontend==20200130.2 +home-assistant-frontend==20200130.3 # homeassistant.components.zwave homeassistant-pyozw==0.1.8 From af9832d46826c799107d020ca3b657c80ae8c58a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 14 Feb 2020 15:27:31 -0800 Subject: [PATCH 28/32] Fix person device_trackers null (#31829) --- homeassistant/components/person/__init__.py | 39 ++++++++++++++++----- homeassistant/helpers/collection.py | 6 +++- tests/components/person/test_init.py | 14 +++++++- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/person/__init__.py b/homeassistant/components/person/__init__.py index a1620c578e3..9cd3e882c48 100644 --- a/homeassistant/components/person/__init__.py +++ b/homeassistant/components/person/__init__.py @@ -88,7 +88,11 @@ _UNDEF = object() async def async_create_person(hass, name, *, user_id=None, device_trackers=None): """Create a new person.""" await hass.data[DOMAIN][1].async_create_item( - {ATTR_NAME: name, ATTR_USER_ID: user_id, "device_trackers": device_trackers} + { + ATTR_NAME: name, + ATTR_USER_ID: user_id, + CONF_DEVICE_TRACKERS: device_trackers or [], + } ) @@ -103,14 +107,14 @@ async def async_add_user_device_tracker( if person.get(ATTR_USER_ID) != user_id: continue - device_trackers = person["device_trackers"] + device_trackers = person[CONF_DEVICE_TRACKERS] if device_tracker_entity_id in device_trackers: return await coll.async_update_item( person[collection.CONF_ID], - {"device_trackers": device_trackers + [device_tracker_entity_id]}, + {CONF_DEVICE_TRACKERS: device_trackers + [device_tracker_entity_id]}, ) break @@ -161,6 +165,23 @@ class PersonStorageCollection(collection.StorageCollection): super().__init__(store, logger, id_manager) self.yaml_collection = yaml_collection + async def _async_load_data(self) -> Optional[dict]: + """Load the data. + + A past bug caused onboarding to create invalid person objects. + This patches it up. + """ + data = await super()._async_load_data() + + if data is None: + return data + + for person in data["items"]: + if person[CONF_DEVICE_TRACKERS] is None: + person[CONF_DEVICE_TRACKERS] = [] + + return data + async def async_load(self) -> None: """Load the Storage collection.""" await super().async_load() @@ -179,14 +200,16 @@ class PersonStorageCollection(collection.StorageCollection): return for person in list(self.data.values()): - if entity_id not in person["device_trackers"]: + if entity_id not in person[CONF_DEVICE_TRACKERS]: continue await self.async_update_item( person[collection.CONF_ID], { - "device_trackers": [ - devt for devt in person["device_trackers"] if devt != entity_id + CONF_DEVICE_TRACKERS: [ + devt + for devt in person[CONF_DEVICE_TRACKERS] + if devt != entity_id ] }, ) @@ -408,7 +431,7 @@ class Person(RestoreEntity): self._unsub_track_device() self._unsub_track_device = None - trackers = self._config.get(CONF_DEVICE_TRACKERS) + trackers = self._config[CONF_DEVICE_TRACKERS] if trackers: _LOGGER.debug("Subscribe to device trackers for %s", self.entity_id) @@ -428,7 +451,7 @@ class Person(RestoreEntity): def _update_state(self): """Update the state.""" latest_non_gps_home = latest_not_home = latest_gps = latest = None - for entity_id in self._config.get(CONF_DEVICE_TRACKERS, []): + for entity_id in self._config[CONF_DEVICE_TRACKERS]: state = self.hass.states.get(entity_id) if not state or state.state in IGNORE_STATES: diff --git a/homeassistant/helpers/collection.py b/homeassistant/helpers/collection.py index 1b3721788f5..025c6c07dee 100644 --- a/homeassistant/helpers/collection.py +++ b/homeassistant/helpers/collection.py @@ -158,9 +158,13 @@ class StorageCollection(ObservableCollection): """Home Assistant object.""" return self.store.hass + async def _async_load_data(self) -> Optional[dict]: + """Load the data.""" + return cast(Optional[dict], await self.store.async_load()) + async def async_load(self) -> None: """Load the storage Manager.""" - raw_storage = cast(Optional[dict], await self.store.async_load()) + raw_storage = await self._async_load_data() if raw_storage is None: raw_storage = {"items": []} diff --git a/tests/components/person/test_init.py b/tests/components/person/test_init.py index e5a414d95ad..76350619983 100644 --- a/tests/components/person/test_init.py +++ b/tests/components/person/test_init.py @@ -1,7 +1,7 @@ """The tests for the person component.""" import logging -from unittest.mock import patch +from asynctest import patch import pytest from homeassistant.components import person @@ -773,3 +773,15 @@ async def test_reload(hass, hass_admin_user): assert state_2 is None assert state_3 is not None assert state_3.name == "Person 3" + + +async def test_person_storage_fixing_device_trackers(storage_collection): + """Test None device trackers become lists.""" + with patch.object( + storage_collection.store, + "async_load", + return_value={"items": [{"id": "bla", "name": "bla", "device_trackers": None}]}, + ): + await storage_collection.async_load() + + assert storage_collection.data["bla"]["device_trackers"] == [] From 7705eb7941c6d25ce9ef273cad7144ba817f3308 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 14 Feb 2020 15:28:11 -0800 Subject: [PATCH 29/32] Google Assistant: Remove speaker type and earlier filter out devices from being locally exposed (#31830) * Remove speaker type * Do not expose locks or alarms to Google Local --- .../components/google_assistant/const.py | 3 ++- .../components/google_assistant/helpers.py | 15 ++++++++++++++- .../components/google_assistant/smart_home.py | 4 +--- tests/components/google_assistant/test_helpers.py | 10 ++++++++++ .../google_assistant/test_smart_home.py | 1 - 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/google_assistant/const.py b/homeassistant/components/google_assistant/const.py index add625d2de4..c9f8d857b62 100644 --- a/homeassistant/components/google_assistant/const.py +++ b/homeassistant/components/google_assistant/const.py @@ -133,7 +133,6 @@ DEVICE_CLASS_TO_GOOGLE_TYPES = { (binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_OPENING): TYPE_SENSOR, (binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_WINDOW): TYPE_SENSOR, (media_player.DOMAIN, media_player.DEVICE_CLASS_TV): TYPE_TV, - (media_player.DOMAIN, media_player.DEVICE_CLASS_SPEAKER): TYPE_SPEAKER, (sensor.DOMAIN, sensor.DEVICE_CLASS_TEMPERATURE): TYPE_SENSOR, (sensor.DOMAIN, sensor.DEVICE_CLASS_HUMIDITY): TYPE_SENSOR, } @@ -146,3 +145,5 @@ STORE_AGENT_USER_IDS = "agent_user_ids" SOURCE_CLOUD = "cloud" SOURCE_LOCAL = "local" + +NOT_EXPOSE_LOCAL = {TYPE_ALARM, TYPE_LOCK} diff --git a/homeassistant/components/google_assistant/helpers.py b/homeassistant/components/google_assistant/helpers.py index f1b7a89bffe..6ba301c01e8 100644 --- a/homeassistant/components/google_assistant/helpers.py +++ b/homeassistant/components/google_assistant/helpers.py @@ -28,6 +28,7 @@ from .const import ( DOMAIN, DOMAIN_TO_GOOGLE_TYPES, ERR_FUNCTION_NOT_SUPPORTED, + NOT_EXPOSE_LOCAL, SOURCE_LOCAL, STORE_AGENT_USER_IDS, ) @@ -351,6 +352,18 @@ class GoogleEntity: """If entity should be exposed.""" return self.config.should_expose(self.state) + @callback + def should_expose_local(self) -> bool: + """Return if the entity should be exposed locally.""" + return ( + self.should_expose() + and get_google_type( + self.state.domain, self.state.attributes.get(ATTR_DEVICE_CLASS) + ) + not in NOT_EXPOSE_LOCAL + and not self.might_2fa() + ) + @callback def is_supported(self) -> bool: """Return if the entity is supported by Google.""" @@ -401,7 +414,7 @@ class GoogleEntity: if aliases: device["name"]["nicknames"] = [name] + aliases - if self.config.is_local_sdk_active: + if self.config.is_local_sdk_active and self.should_expose_local(): device["otherDeviceIds"] = [{"deviceId": self.entity_id}] device["customData"] = { "webhookId": self.config.local_sdk_webhook_id, diff --git a/homeassistant/components/google_assistant/smart_home.py b/homeassistant/components/google_assistant/smart_home.py index bf6c32505aa..97c872bdaf8 100644 --- a/homeassistant/components/google_assistant/smart_home.py +++ b/homeassistant/components/google_assistant/smart_home.py @@ -243,9 +243,7 @@ async def async_devices_reachable(hass, data: RequestData, payload): "devices": [ entity.reachable_device_serialize() for entity in async_get_entities(hass, data.config) - if entity.entity_id in google_ids - and entity.should_expose() - and not entity.might_2fa() + if entity.entity_id in google_ids and entity.should_expose_local() ] } diff --git a/tests/components/google_assistant/test_helpers.py b/tests/components/google_assistant/test_helpers.py index 9c8a868e68d..8d2aaa63c48 100644 --- a/tests/components/google_assistant/test_helpers.py +++ b/tests/components/google_assistant/test_helpers.py @@ -7,6 +7,7 @@ import pytest from homeassistant.components.google_assistant import helpers from homeassistant.components.google_assistant.const import ( # noqa: F401 EVENT_COMMAND_RECEIVED, + NOT_EXPOSE_LOCAL, ) from homeassistant.setup import async_setup_component from homeassistant.util import dt @@ -46,6 +47,15 @@ async def test_google_entity_sync_serialize_with_local_sdk(hass): "webhookId": "mock-webhook-id", } + for device_type in NOT_EXPOSE_LOCAL: + with patch( + "homeassistant.components.google_assistant.helpers.get_google_type", + return_value=device_type, + ): + serialized = await entity.sync_serialize(None) + assert "otherDeviceIds" not in serialized + assert "customData" not in serialized + async def test_config_local_sdk(hass, hass_client): """Test the local SDK.""" diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py index aa073c699f8..7e98f162f22 100644 --- a/tests/components/google_assistant/test_smart_home.py +++ b/tests/components/google_assistant/test_smart_home.py @@ -682,7 +682,6 @@ async def test_device_class_cover(hass, device_class, google_type): "device_class,google_type", [ ("non_existing_class", "action.devices.types.SWITCH"), - ("speaker", "action.devices.types.SPEAKER"), ("tv", "action.devices.types.TV"), ], ) From f26cbbdef9a097f9172c4eba869ef43f3a23afdc Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 14 Feb 2020 22:58:39 +0100 Subject: [PATCH 30/32] Spotify integration hotfixes (#31835) * Remove services file, incorrect info * Guard currently playing for being a NoneType * Revert "Guard currently playing for being a NoneType" This reverts commit f5f56b0db03b407e058d45cd3549af1388916e06. * Guard currently playing item is None * Process review suggestions --- homeassistant/components/spotify/media_player.py | 9 ++++++--- homeassistant/components/spotify/services.yaml | 9 --------- 2 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 homeassistant/components/spotify/services.yaml diff --git a/homeassistant/components/spotify/media_player.py b/homeassistant/components/spotify/media_player.py index 8bd5782f7ee..9588f428a66 100644 --- a/homeassistant/components/spotify/media_player.py +++ b/homeassistant/components/spotify/media_player.py @@ -157,7 +157,8 @@ class SpotifyMediaPlayer(MediaPlayerDevice): @property def media_content_id(self) -> Optional[str]: """Return the media URL.""" - return self._currently_playing.get("item", {}).get("name") + item = self._currently_playing.get("item") or {} + return item.get("name") @property def media_content_type(self) -> Optional[str]: @@ -203,7 +204,8 @@ class SpotifyMediaPlayer(MediaPlayerDevice): @property def media_title(self) -> Optional[str]: """Return the media title.""" - return self._currently_playing.get("item", {}).get("name") + item = self._currently_playing.get("item") or {} + return item.get("name") @property def media_artist(self) -> Optional[str]: @@ -224,7 +226,8 @@ class SpotifyMediaPlayer(MediaPlayerDevice): @property def media_track(self) -> Optional[int]: """Track number of current playing media, music track only.""" - return self._currently_playing.get("item", {}).get("track_number") + item = self._currently_playing.get("item") or {} + return item.get("track_number") @property def media_playlist(self): diff --git a/homeassistant/components/spotify/services.yaml b/homeassistant/components/spotify/services.yaml deleted file mode 100644 index e532f736652..00000000000 --- a/homeassistant/components/spotify/services.yaml +++ /dev/null @@ -1,9 +0,0 @@ -play_playlist: - description: Play a Spotify playlist. - fields: - media_content_id: - description: Spotify URI of the playlist. - example: 'spotify:playlist:0IpRnqCHSjun48oQRX1Dy7' - random_song: - description: True to select random song at start, False to start from beginning. - example: true \ No newline at end of file From 8140c033fa23584da130c077c0f5aeafe36a7734 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 14 Feb 2020 15:30:23 -0800 Subject: [PATCH 31/32] Bumped version to 0.105.4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 4ca501b1d23..bdd22081c29 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 105 -PATCH_VERSION = "3" +PATCH_VERSION = "4" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0) From 350726d938a525b9d89c2500a9a59572a58d1c4c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 14 Feb 2020 15:47:14 -0800 Subject: [PATCH 32/32] Revert "For vizio integration, set unique ID early to prevent multiple zeroconf discovery items for the same device to appear (#31686)" This reverts commit 295963f8e8c554bf8b614dae06aca95ccc99ade6. --- homeassistant/components/vizio/config_flow.py | 5 ---- tests/components/vizio/test_config_flow.py | 26 ------------------- 2 files changed, 31 deletions(-) diff --git a/homeassistant/components/vizio/config_flow.py b/homeassistant/components/vizio/config_flow.py index 4fba0f06165..04f70da4a8c 100644 --- a/homeassistant/components/vizio/config_flow.py +++ b/homeassistant/components/vizio/config_flow.py @@ -205,11 +205,6 @@ class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) -> Dict[str, Any]: """Handle zeroconf discovery.""" - # Set unique ID early to prevent device from getting rediscovered multiple times - await self.async_set_unique_id( - unique_id=discovery_info[CONF_HOST].split(":")[0], raise_on_progress=True - ) - discovery_info[ CONF_HOST ] = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}" diff --git a/tests/components/vizio/test_config_flow.py b/tests/components/vizio/test_config_flow.py index 044ca11bc8d..cf6cdb6afdb 100644 --- a/tests/components/vizio/test_config_flow.py +++ b/tests/components/vizio/test_config_flow.py @@ -506,29 +506,3 @@ async def test_zeroconf_flow_already_configured( # Flow should abort because device is already setup assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "already_setup" - - -async def test_zeroconf_dupe_fail( - hass: HomeAssistantType, - vizio_connect: pytest.fixture, - vizio_bypass_setup: pytest.fixture, - vizio_guess_device_type: pytest.fixture, -) -> None: - """Test zeroconf config flow when device gets discovered multiple times.""" - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info - ) - - # Form should always show even if all required properties are discovered - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "user" - - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() - 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_in_progress"