From c507c72350e8a6a33d35e9141da929caffd1b149 Mon Sep 17 00:00:00 2001 From: RDFurman Date: Mon, 13 Dec 2021 21:38:43 -0700 Subject: [PATCH 01/36] Honeywell unique id fix (#59393) * Move error logging and remove reload * Change device assignment and improve logging * Use dictionary for devices * Check if new device exists in API response * Add test and make loop better * Make test assert on error in log --- .../components/honeywell/__init__.py | 35 ++++++++++++------- homeassistant/components/honeywell/climate.py | 2 +- tests/components/honeywell/test_init.py | 21 ++++++++++- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/honeywell/__init__.py b/homeassistant/components/honeywell/__init__.py index c61e4fc18eb..c7bff647dda 100644 --- a/homeassistant/components/honeywell/__init__.py +++ b/homeassistant/components/honeywell/__init__.py @@ -30,14 +30,13 @@ async def async_setup_entry(hass, config): loc_id = config.data.get(CONF_LOC_ID) dev_id = config.data.get(CONF_DEV_ID) - devices = [] + devices = {} for location in client.locations_by_id.values(): - for device in location.devices_by_id.values(): - if (not loc_id or location.locationid == loc_id) and ( - not dev_id or device.deviceid == dev_id - ): - devices.append(device) + if not loc_id or location.locationid == loc_id: + for device in location.devices_by_id.values(): + if not dev_id or device.deviceid == dev_id: + devices[device.deviceid] = device if len(devices) == 0: _LOGGER.debug("No devices found") @@ -107,23 +106,30 @@ class HoneywellData: if self._client is None: return False - devices = [ + refreshed_devices = [ device for location in self._client.locations_by_id.values() for device in location.devices_by_id.values() ] - if len(devices) == 0: - _LOGGER.error("Failed to find any devices") + if len(refreshed_devices) == 0: + _LOGGER.error("Failed to find any devices after retry") return False - self.devices = devices + for updated_device in refreshed_devices: + if updated_device.deviceid in self.devices: + self.devices[updated_device.deviceid] = updated_device + else: + _LOGGER.info( + "New device with ID %s detected, reload the honeywell integration if you want to access it in Home Assistant" + ) + await self._hass.config_entries.async_reload(self._config.entry_id) return True async def _refresh_devices(self): """Refresh each enabled device.""" - for device in self.devices: + for device in self.devices.values(): await self._hass.async_add_executor_job(device.refresh) await asyncio.sleep(UPDATE_LOOP_SLEEP_TIME) @@ -143,11 +149,16 @@ class HoneywellData: ) as exp: retries -= 1 if retries == 0: + _LOGGER.error( + "Ran out of retry attempts (3 attempts allocated). Error: %s", + exp, + ) raise exp result = await self._retry() if not result: + _LOGGER.error("Retry result was empty. Error: %s", exp) raise exp - _LOGGER.error("SomeComfort update failed, Retrying - Error: %s", exp) + _LOGGER.info("SomeComfort update failed, retrying. Error: %s", exp) diff --git a/homeassistant/components/honeywell/climate.py b/homeassistant/components/honeywell/climate.py index d2766515595..6c686e92b8e 100644 --- a/homeassistant/components/honeywell/climate.py +++ b/homeassistant/components/honeywell/climate.py @@ -122,7 +122,7 @@ async def async_setup_entry(hass, config, async_add_entities, discovery_info=Non async_add_entities( [ HoneywellUSThermostat(data, device, cool_away_temp, heat_away_temp) - for device in data.devices + for device in data.devices.values() ] ) diff --git a/tests/components/honeywell/test_init.py b/tests/components/honeywell/test_init.py index 619d770c59e..49917aae151 100644 --- a/tests/components/honeywell/test_init.py +++ b/tests/components/honeywell/test_init.py @@ -1,6 +1,8 @@ """Test honeywell setup process.""" -from unittest.mock import patch +from unittest.mock import create_autospec, patch + +import somecomfort from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant @@ -29,3 +31,20 @@ async def test_setup_multiple_thermostats( await hass.async_block_till_done() assert config_entry.state is ConfigEntryState.LOADED assert hass.states.async_entity_ids_count() == 2 + + +@patch("homeassistant.components.honeywell.UPDATE_LOOP_SLEEP_TIME", 0) +async def test_setup_multiple_thermostats_with_same_deviceid( + hass: HomeAssistant, caplog, config_entry: MockConfigEntry, device, client +) -> None: + """Test Honeywell TCC API returning duplicate device IDs.""" + mock_location2 = create_autospec(somecomfort.Location, instance=True) + mock_location2.locationid.return_value = "location2" + mock_location2.devices_by_id = {device.deviceid: device} + client.locations_by_id["location2"] = mock_location2 + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert config_entry.state is ConfigEntryState.LOADED + assert hass.states.async_entity_ids_count() == 1 + assert "Platform honeywell does not generate unique IDs" not in caplog.text From 22867acaf82b28a73f7a756289666e76bc8e641e Mon Sep 17 00:00:00 2001 From: Hans Oischinger Date: Fri, 17 Dec 2021 15:43:41 +0100 Subject: [PATCH 02/36] Add vicare strings (#61593) * Add vicare strings * Remove duplicates * Remove duplicates from english translation * Add missing strings --- homeassistant/components/vicare/strings.json | 26 +++++++++++++++++++ .../components/vicare/translations/en.json | 26 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 homeassistant/components/vicare/strings.json create mode 100644 homeassistant/components/vicare/translations/en.json diff --git a/homeassistant/components/vicare/strings.json b/homeassistant/components/vicare/strings.json new file mode 100644 index 00000000000..bf6b40fb6b2 --- /dev/null +++ b/homeassistant/components/vicare/strings.json @@ -0,0 +1,26 @@ +{ + "config": { + "flow_title": "{name} ({host})", + "step": { + "user": { + "title": "{name}", + "description": "Set up ViCare integration. To generate API key go to https://developer.viessmann.com", + "data": { + "name": "[%key:common::config_flow::data::name%]", + "scan_interval": "Scan Interval (seconds)", + "username": "[%key:common::config_flow::data::email%]", + "password": "[%key:common::config_flow::data::password%]", + "client_id": "[%key:common::config_flow::data::api_key%]", + "heating_type": "Heating type" + } + } + }, + "error": { + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]" + }, + "abort": { + "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vicare/translations/en.json b/homeassistant/components/vicare/translations/en.json new file mode 100644 index 00000000000..d693cbe76cc --- /dev/null +++ b/homeassistant/components/vicare/translations/en.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Already configured. Only a single configuration possible.", + "unknown": "Unexpected error" + }, + "error": { + "invalid_auth": "Invalid authentication" + }, + "flow_title": "{name} ({host})", + "step": { + "user": { + "data": { + "name": "Name", + "scan_interval": "Scan Interval (seconds)", + "client_id": "API Key", + "heating_type": "Heating type", + "password": "Password", + "username": "Email" + }, + "title": "{name}", + "description": "Set up ViCare integration. To generate API key go to https://developer.viessmann.com" + } + } + } +} \ No newline at end of file From 735deff45ebb5b8425bce881b3e90ecf527d4d81 Mon Sep 17 00:00:00 2001 From: jkuettner <12213711+jkuettner@users.noreply.github.com> Date: Fri, 17 Dec 2021 16:54:19 +0100 Subject: [PATCH 03/36] Fix "vevent" KeyError in caldav component (#61718) --- homeassistant/components/caldav/calendar.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/homeassistant/components/caldav/calendar.py b/homeassistant/components/caldav/calendar.py index d27555beb2c..8ed8ea24607 100644 --- a/homeassistant/components/caldav/calendar.py +++ b/homeassistant/components/caldav/calendar.py @@ -161,6 +161,9 @@ class WebDavCalendarData: ) event_list = [] for event in vevent_list: + if not hasattr(event.instance, "vevent"): + _LOGGER.warning("Skipped event with missing 'vevent' property") + continue vevent = event.instance.vevent if not self.is_matching(vevent, self.search): continue @@ -198,6 +201,9 @@ class WebDavCalendarData: # and they would not be properly parsed using their original start/end dates. new_events = [] for event in results: + if not hasattr(event.instance, "vevent"): + _LOGGER.warning("Skipped event with missing 'vevent' property") + continue vevent = event.instance.vevent for start_dt in vevent.getrruleset() or []: _start_of_today = start_of_today From a87ed13a048dcad950649cef55d04a27445be3c8 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Sun, 19 Dec 2021 11:37:14 +0100 Subject: [PATCH 04/36] Silently retry Fronius inverter endpoint 2 times (#61826) --- .../components/fronius/coordinator.py | 24 +++++++++--- tests/components/fronius/test_coordinator.py | 38 ++++++++++++++----- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/fronius/coordinator.py b/homeassistant/components/fronius/coordinator.py index e89f828f47d..f5ea498e381 100644 --- a/homeassistant/components/fronius/coordinator.py +++ b/homeassistant/components/fronius/coordinator.py @@ -5,7 +5,7 @@ from abc import ABC, abstractmethod from datetime import timedelta from typing import TYPE_CHECKING, Any, Dict, TypeVar -from pyfronius import FroniusError +from pyfronius import BadStatusError, FroniusError from homeassistant.components.sensor import SensorEntityDescription from homeassistant.core import callback @@ -43,6 +43,8 @@ class FroniusCoordinatorBase( error_interval: timedelta valid_descriptions: list[SensorEntityDescription] + MAX_FAILED_UPDATES = 3 + def __init__(self, *args: Any, solar_net: FroniusSolarNet, **kwargs: Any) -> None: """Set up the FroniusCoordinatorBase class.""" self._failed_update_count = 0 @@ -62,7 +64,7 @@ class FroniusCoordinatorBase( data = await self._update_method() except FroniusError as err: self._failed_update_count += 1 - if self._failed_update_count == 3: + if self._failed_update_count == self.MAX_FAILED_UPDATES: self.update_interval = self.error_interval raise UpdateFailed(err) from err @@ -116,6 +118,8 @@ class FroniusInverterUpdateCoordinator(FroniusCoordinatorBase): error_interval = timedelta(minutes=10) valid_descriptions = INVERTER_ENTITY_DESCRIPTIONS + SILENT_RETRIES = 3 + def __init__( self, *args: Any, inverter_info: FroniusDeviceInfo, **kwargs: Any ) -> None: @@ -125,9 +129,19 @@ class FroniusInverterUpdateCoordinator(FroniusCoordinatorBase): async def _update_method(self) -> dict[SolarNetId, Any]: """Return data per solar net id from pyfronius.""" - data = await self.solar_net.fronius.current_inverter_data( - self.inverter_info.solar_net_id - ) + # almost 1% of `current_inverter_data` requests on Symo devices result in + # `BadStatusError Code: 8 - LNRequestTimeout` due to flaky internal + # communication between the logger and the inverter. + for silent_retry in range(self.SILENT_RETRIES): + try: + data = await self.solar_net.fronius.current_inverter_data( + self.inverter_info.solar_net_id + ) + except BadStatusError as err: + if silent_retry == (self.SILENT_RETRIES - 1): + raise err + continue + break # wrap a single devices data in a dict with solar_net_id key for # FroniusCoordinatorBase _async_update_data and add_entities_for_seen_keys return {self.inverter_info.solar_net_id: data} diff --git a/tests/components/fronius/test_coordinator.py b/tests/components/fronius/test_coordinator.py index b729c4d97ac..a2368975128 100644 --- a/tests/components/fronius/test_coordinator.py +++ b/tests/components/fronius/test_coordinator.py @@ -1,7 +1,7 @@ """Test the Fronius update coordinators.""" from unittest.mock import patch -from pyfronius import FroniusError +from pyfronius import BadStatusError, FroniusError from homeassistant.components.fronius.coordinator import ( FroniusInverterUpdateCoordinator, @@ -18,27 +18,32 @@ async def test_adaptive_update_interval(hass, aioclient_mock): with patch("pyfronius.Fronius.current_inverter_data") as mock_inverter_data: mock_responses(aioclient_mock) await setup_fronius_integration(hass) - assert mock_inverter_data.call_count == 1 + mock_inverter_data.assert_called_once() + mock_inverter_data.reset_mock() async_fire_time_changed( hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval ) await hass.async_block_till_done() - assert mock_inverter_data.call_count == 2 + mock_inverter_data.assert_called_once() + mock_inverter_data.reset_mock() - mock_inverter_data.side_effect = FroniusError - # first 3 requests at default interval - 4th has different interval - for _ in range(4): + mock_inverter_data.side_effect = FroniusError() + # first 3 bad requests at default interval - 4th has different interval + for _ in range(3): async_fire_time_changed( hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval ) await hass.async_block_till_done() - assert mock_inverter_data.call_count == 5 + assert mock_inverter_data.call_count == 3 + mock_inverter_data.reset_mock() + async_fire_time_changed( hass, dt.utcnow() + FroniusInverterUpdateCoordinator.error_interval ) await hass.async_block_till_done() - assert mock_inverter_data.call_count == 6 + assert mock_inverter_data.call_count == 1 + mock_inverter_data.reset_mock() mock_inverter_data.side_effect = None # next successful request resets to default interval @@ -46,10 +51,23 @@ async def test_adaptive_update_interval(hass, aioclient_mock): hass, dt.utcnow() + FroniusInverterUpdateCoordinator.error_interval ) await hass.async_block_till_done() - assert mock_inverter_data.call_count == 7 + mock_inverter_data.assert_called_once() + mock_inverter_data.reset_mock() async_fire_time_changed( hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval ) await hass.async_block_till_done() - assert mock_inverter_data.call_count == 8 + mock_inverter_data.assert_called_once() + mock_inverter_data.reset_mock() + + # BadStatusError on inverter endpoints have special handling + mock_inverter_data.side_effect = BadStatusError("mock_endpoint", 8) + # first 3 requests at default interval - 4th has different interval + for _ in range(3): + async_fire_time_changed( + hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval + ) + await hass.async_block_till_done() + # BadStatusError does 3 silent retries for inverter endpoint * 3 request intervals = 9 + assert mock_inverter_data.call_count == 9 From 4e96ff78b54153e46f286114d69f688a3042f6e0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Dec 2021 00:26:19 -0600 Subject: [PATCH 05/36] Avoid setting nexia humidity to the same value since it causes the api to fail (#61843) --- homeassistant/components/nexia/climate.py | 24 +++++++++++++++++--- homeassistant/components/nexia/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/nexia/climate.py b/homeassistant/components/nexia/climate.py index 2c1fbf5a3f4..6212541f897 100644 --- a/homeassistant/components/nexia/climate.py +++ b/homeassistant/components/nexia/climate.py @@ -11,6 +11,7 @@ from nexia.const import ( SYSTEM_STATUS_IDLE, UNIT_FAHRENHEIT, ) +from nexia.util import find_humidity_setpoint import voluptuous as vol from homeassistant.components.climate import ClimateEntity @@ -58,6 +59,8 @@ from .coordinator import NexiaDataUpdateCoordinator from .entity import NexiaThermostatZoneEntity from .util import percent_conv +PARALLEL_UPDATES = 1 # keep data in sync with only one connection at a time + SERVICE_SET_AIRCLEANER_MODE = "set_aircleaner_mode" SERVICE_SET_HUMIDIFY_SETPOINT = "set_humidify_setpoint" SERVICE_SET_HVAC_RUN_MODE = "set_hvac_run_mode" @@ -231,9 +234,9 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): def set_humidity(self, humidity): """Dehumidify target.""" if self._thermostat.has_dehumidify_support(): - self._thermostat.set_dehumidify_setpoint(humidity / 100.0) + self.set_dehumidify_setpoint(humidity) else: - self._thermostat.set_humidify_setpoint(humidity / 100.0) + self.set_humidify_setpoint(humidity) self._signal_thermostat_update() @property @@ -453,7 +456,22 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): def set_humidify_setpoint(self, humidity): """Set the humidify setpoint.""" - self._thermostat.set_humidify_setpoint(humidity / 100.0) + target_humidity = find_humidity_setpoint(humidity / 100.0) + if self._thermostat.get_humidify_setpoint() == target_humidity: + # Trying to set the humidify setpoint to the + # same value will cause the api to timeout + return + self._thermostat.set_humidify_setpoint(target_humidity) + self._signal_thermostat_update() + + def set_dehumidify_setpoint(self, humidity): + """Set the dehumidify setpoint.""" + target_humidity = find_humidity_setpoint(humidity / 100.0) + if self._thermostat.get_dehumidify_setpoint() == target_humidity: + # Trying to set the dehumidify setpoint to the + # same value will cause the api to timeout + return + self._thermostat.set_dehumidify_setpoint(target_humidity) self._signal_thermostat_update() def _signal_thermostat_update(self): diff --git a/homeassistant/components/nexia/manifest.json b/homeassistant/components/nexia/manifest.json index 105cbdb62b7..624eee41db7 100644 --- a/homeassistant/components/nexia/manifest.json +++ b/homeassistant/components/nexia/manifest.json @@ -1,7 +1,7 @@ { "domain": "nexia", "name": "Nexia/American Standard/Trane", - "requirements": ["nexia==0.9.11"], + "requirements": ["nexia==0.9.12"], "codeowners": ["@bdraco"], "documentation": "https://www.home-assistant.io/integrations/nexia", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 151881e5613..7b5e4878e4d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1065,7 +1065,7 @@ nettigo-air-monitor==1.2.1 neurio==0.3.1 # homeassistant.components.nexia -nexia==0.9.11 +nexia==0.9.12 # homeassistant.components.nextcloud nextcloudmonitor==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c59e534e52d..9b79845b83b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -654,7 +654,7 @@ netmap==0.7.0.2 nettigo-air-monitor==1.2.1 # homeassistant.components.nexia -nexia==0.9.11 +nexia==0.9.12 # homeassistant.components.nfandroidtv notifications-android-tv==0.1.3 From 895dcaf69077b50d79bfa187eb21d68eb2abdab1 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Fri, 17 Dec 2021 11:33:41 +0000 Subject: [PATCH 06/36] Force Lyric token refresh on first authentication failure (#62100) --- homeassistant/components/lyric/__init__.py | 27 ++++++++++++++++++---- homeassistant/components/lyric/api.py | 12 ++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/lyric/__init__.py b/homeassistant/components/lyric/__init__.py index a9d5cbdec7d..5dcb5fc557a 100644 --- a/homeassistant/components/lyric/__init__.py +++ b/homeassistant/components/lyric/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations from datetime import timedelta +from http import HTTPStatus import logging from aiohttp.client_exceptions import ClientResponseError @@ -30,7 +31,11 @@ from homeassistant.helpers.update_coordinator import ( UpdateFailed, ) -from .api import ConfigEntryLyricClient, LyricLocalOAuth2Implementation +from .api import ( + ConfigEntryLyricClient, + LyricLocalOAuth2Implementation, + OAuth2SessionLyric, +) from .config_flow import OAuth2FlowHandler from .const import DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN @@ -84,21 +89,35 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) session = aiohttp_client.async_get_clientsession(hass) - oauth_session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) + oauth_session = OAuth2SessionLyric(hass, entry, implementation) client = ConfigEntryLyricClient(session, oauth_session) client_id = hass.data[DOMAIN][CONF_CLIENT_ID] lyric = Lyric(client, client_id) - async def async_update_data() -> Lyric: + async def async_update_data(force_refresh_token: bool = False) -> Lyric: """Fetch data from Lyric.""" - await oauth_session.async_ensure_token_valid() + try: + if not force_refresh_token: + await oauth_session.async_ensure_token_valid() + else: + await oauth_session.force_refresh_token() + except ClientResponseError as exception: + if exception.status in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN): + raise ConfigEntryAuthFailed from exception + raise UpdateFailed(exception) from exception + try: async with async_timeout.timeout(60): await lyric.get_locations() return lyric except LyricAuthenticationException as exception: + # Attempt to refresh the token before failing. + # Honeywell appear to have issues keeping tokens saved. + _LOGGER.debug("Authentication failed. Attempting to refresh token") + if not force_refresh_token: + return await async_update_data(force_refresh_token=True) raise ConfigEntryAuthFailed from exception except (LyricException, ClientResponseError) as exception: raise UpdateFailed(exception) from exception diff --git a/homeassistant/components/lyric/api.py b/homeassistant/components/lyric/api.py index 3b23f802ded..4a8aa44417f 100644 --- a/homeassistant/components/lyric/api.py +++ b/homeassistant/components/lyric/api.py @@ -8,6 +8,18 @@ from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.aiohttp_client import async_get_clientsession +class OAuth2SessionLyric(config_entry_oauth2_flow.OAuth2Session): + """OAuth2Session for Lyric.""" + + async def force_refresh_token(self) -> None: + """Force a token refresh.""" + new_token = await self.implementation.async_refresh_token(self.token) + + self.hass.config_entries.async_update_entry( + self.config_entry, data={**self.config_entry.data, "token": new_token} + ) + + class ConfigEntryLyricClient(LyricClient): """Provide Honeywell Lyric authentication tied to an OAuth2 based config entry.""" From 9a0f42f9a726fd958b29775f8819bd26a664d209 Mon Sep 17 00:00:00 2001 From: Gage Benne Date: Sun, 19 Dec 2021 07:05:51 -0500 Subject: [PATCH 07/36] Bump pydexcom to 0.2.2 (#62207) --- homeassistant/components/dexcom/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/dexcom/manifest.json b/homeassistant/components/dexcom/manifest.json index 1bf15776cad..6133a67bcf1 100644 --- a/homeassistant/components/dexcom/manifest.json +++ b/homeassistant/components/dexcom/manifest.json @@ -3,7 +3,7 @@ "name": "Dexcom", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dexcom", - "requirements": ["pydexcom==0.2.1"], + "requirements": ["pydexcom==0.2.2"], "codeowners": ["@gagebenne"], "iot_class": "cloud_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index 7b5e4878e4d..d93f75e934b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1435,7 +1435,7 @@ pydeconz==85 pydelijn==0.6.1 # homeassistant.components.dexcom -pydexcom==0.2.1 +pydexcom==0.2.2 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9b79845b83b..a7880c01cd5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -868,7 +868,7 @@ pydaikin==2.6.0 pydeconz==85 # homeassistant.components.dexcom -pydexcom==0.2.1 +pydexcom==0.2.2 # homeassistant.components.zwave pydispatcher==2.0.5 From 57b7b28d60863f43b47b4ef0410759e1bb34a599 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Fri, 17 Dec 2021 08:31:23 -0700 Subject: [PATCH 08/36] Fix spurious RainMachine config entry reload (#62215) --- homeassistant/components/rainmachine/config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/rainmachine/config_flow.py b/homeassistant/components/rainmachine/config_flow.py index 39884681967..989616f6367 100644 --- a/homeassistant/components/rainmachine/config_flow.py +++ b/homeassistant/components/rainmachine/config_flow.py @@ -85,7 +85,7 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ): await self.async_set_unique_id(controller.mac) self._abort_if_unique_id_configured( - updates={CONF_IP_ADDRESS: ip_address} + updates={CONF_IP_ADDRESS: ip_address}, reload_on_update=False ) # A new rain machine: We will change out the unique id From f665c4e588b0e6e76e85f1ee3f2c2eb861e95bb1 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 19 Dec 2021 13:52:21 -0700 Subject: [PATCH 09/36] Fix bug in which SimpliSafe websocket won't reconnect on error (#62241) --- .../components/simplisafe/__init__.py | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index dd3ff717e28..2b87f7aa8b0 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -12,6 +12,7 @@ from simplipy.errors import ( EndpointUnavailableError, InvalidCredentialsError, SimplipyError, + WebsocketError, ) from simplipy.system import SystemNotification from simplipy.system.v3 import ( @@ -472,6 +473,7 @@ class SimpliSafe: self._api = api self._hass = hass self._system_notifications: dict[int, set[SystemNotification]] = {} + self._websocket_reconnect_task: asyncio.Task | None = None self.entry = entry self.initial_event_to_use: dict[int, dict[str, Any]] = {} self.systems: dict[int, SystemType] = {} @@ -516,11 +518,29 @@ class SimpliSafe: self._system_notifications[system.system_id] = latest_notifications - async def _async_websocket_on_connect(self) -> None: + async def _async_start_websocket_loop(self) -> None: """Define a callback for connecting to the websocket.""" if TYPE_CHECKING: assert self._api.websocket - await self._api.websocket.async_listen() + + should_reconnect = True + + try: + await self._api.websocket.async_reconnect() + await self._api.websocket.async_listen() + except asyncio.CancelledError: + LOGGER.debug("Request to cancel websocket loop received") + raise + except WebsocketError as err: + LOGGER.error("Failed to connect to websocket: %s", err) + except Exception as err: # pylint: disable=broad-except + LOGGER.error("Unknown exception while connecting to websocket: %s", err) + + if should_reconnect: + LOGGER.info("Disconnected from websocket; reconnecting") + self._websocket_reconnect_task = self._hass.async_create_task( + self._async_start_websocket_loop() + ) @callback def _async_websocket_on_event(self, event: WebsocketEvent) -> None: @@ -560,17 +580,25 @@ class SimpliSafe: assert self._api.refresh_token assert self._api.websocket - self._api.websocket.add_connect_callback(self._async_websocket_on_connect) self._api.websocket.add_event_callback(self._async_websocket_on_event) - asyncio.create_task(self._api.websocket.async_connect()) + self._websocket_reconnect_task = asyncio.create_task( + self._async_start_websocket_loop() + ) async def async_websocket_disconnect_listener(_: Event) -> None: """Define an event handler to disconnect from the websocket.""" if TYPE_CHECKING: assert self._api.websocket - if self._api.websocket.connected: - await self._api.websocket.async_disconnect() + if self._websocket_reconnect_task: + self._websocket_reconnect_task.cancel() + try: + await self._websocket_reconnect_task + except asyncio.CancelledError: + LOGGER.debug("Websocket reconnection task successfully canceled") + self._websocket_reconnect_task = None + + await self._api.websocket.async_disconnect() self.entry.async_on_unload( self._hass.bus.async_listen_once( @@ -620,10 +648,10 @@ class SimpliSafe: if TYPE_CHECKING: assert self._api.websocket - if self._api.websocket.connected: - # If a websocket connection is open, reconnect it to use the - # new access token: - asyncio.create_task(self._api.websocket.async_reconnect()) + # Open a new websocket connection with the fresh token: + self._websocket_reconnect_task = self._hass.async_create_task( + self._async_start_websocket_loop() + ) self.entry.async_on_unload( self._api.add_refresh_token_callback(async_handle_refresh_token) From bc2949ef310ea39247d9a1753a86cc3d222c226c Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Sat, 18 Dec 2021 13:29:52 +0100 Subject: [PATCH 10/36] bump pynetgear to 0.8.0 (#62261) --- homeassistant/components/netgear/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/netgear/manifest.json b/homeassistant/components/netgear/manifest.json index aa4c57ecdde..1f4a15f1de1 100644 --- a/homeassistant/components/netgear/manifest.json +++ b/homeassistant/components/netgear/manifest.json @@ -2,7 +2,7 @@ "domain": "netgear", "name": "NETGEAR", "documentation": "https://www.home-assistant.io/integrations/netgear", - "requirements": ["pynetgear==0.7.0"], + "requirements": ["pynetgear==0.8.0"], "codeowners": ["@hacf-fr", "@Quentame", "@starkillerOG"], "iot_class": "local_polling", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index d93f75e934b..22460cb0305 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1661,7 +1661,7 @@ pymyq==3.1.4 pymysensors==0.22.1 # homeassistant.components.netgear -pynetgear==0.7.0 +pynetgear==0.8.0 # homeassistant.components.netio pynetio==0.1.9.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a7880c01cd5..13865dc6c47 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1019,7 +1019,7 @@ pymyq==3.1.4 pymysensors==0.22.1 # homeassistant.components.netgear -pynetgear==0.7.0 +pynetgear==0.8.0 # homeassistant.components.nuki pynuki==1.4.1 From 2395c753fe82c86d2bdda72ae49bd1490bf75bf3 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Sun, 19 Dec 2021 07:28:26 +0100 Subject: [PATCH 11/36] Fix logging for Shelly climate platform (#62264) Co-authored-by: Paulus Schoutsen --- homeassistant/components/shelly/climate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index 7a877e60109..06140fcba72 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -69,7 +69,6 @@ async def async_setup_climate_entities( ) -> None: """Set up online climate devices.""" - _LOGGER.info("Setup online climate device %s", wrapper.name) device_block: Block | None = None sensor_block: Block | None = None @@ -82,6 +81,7 @@ async def async_setup_climate_entities( sensor_block = block if sensor_block and device_block: + _LOGGER.debug("Setup online climate device %s", wrapper.name) async_add_entities([BlockSleepingClimate(wrapper, sensor_block, device_block)]) @@ -92,7 +92,6 @@ async def async_restore_climate_entities( wrapper: BlockDeviceWrapper, ) -> None: """Restore sleeping climate devices.""" - _LOGGER.info("Setup sleeping climate device %s", wrapper.name) ent_reg = await entity_registry.async_get_registry(hass) entries = entity_registry.async_entries_for_config_entry( @@ -104,6 +103,7 @@ async def async_restore_climate_entities( if entry.domain != CLIMATE_DOMAIN: continue + _LOGGER.debug("Setup sleeping climate device %s", wrapper.name) _LOGGER.debug("Found entry %s [%s]", entry.original_name, entry.domain) async_add_entities([BlockSleepingClimate(wrapper, None, None, entry)]) From cd65aaee6074246a016111acd570c68d6e5c262f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 18 Dec 2021 14:18:31 +0100 Subject: [PATCH 12/36] Upgrade tailscale to 0.1.6 (#62267) --- homeassistant/components/tailscale/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tailscale/manifest.json b/homeassistant/components/tailscale/manifest.json index eaa51855d38..ac7cbe84459 100644 --- a/homeassistant/components/tailscale/manifest.json +++ b/homeassistant/components/tailscale/manifest.json @@ -3,7 +3,7 @@ "name": "Tailscale", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tailscale", - "requirements": ["tailscale==0.1.5"], + "requirements": ["tailscale==0.1.6"], "codeowners": ["@frenck"], "quality_scale": "platinum", "iot_class": "cloud_polling" diff --git a/requirements_all.txt b/requirements_all.txt index 22460cb0305..d033ec06d51 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2272,7 +2272,7 @@ systembridge==2.2.3 tahoma-api==0.0.16 # homeassistant.components.tailscale -tailscale==0.1.5 +tailscale==0.1.6 # homeassistant.components.tank_utility tank_utility==1.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 13865dc6c47..20aed6ae1e7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1352,7 +1352,7 @@ surepy==0.7.2 systembridge==2.2.3 # homeassistant.components.tailscale -tailscale==0.1.5 +tailscale==0.1.6 # homeassistant.components.tellduslive tellduslive==0.10.11 From 4efa3b634e09584147fe743f13a4455d4f70f376 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Sat, 18 Dec 2021 15:00:07 +0100 Subject: [PATCH 13/36] Fix fitbit no SSL URL handling (#62270) --- homeassistant/components/fitbit/sensor.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/fitbit/sensor.py b/homeassistant/components/fitbit/sensor.py index 2de121920af..0c638f0c455 100644 --- a/homeassistant/components/fitbit/sensor.py +++ b/homeassistant/components/fitbit/sensor.py @@ -112,10 +112,11 @@ def request_app_setup( Then come back here and hit the below button. """ except NoURLAvailableError: - error_msg = """Could not find a SSL enabled URL for your Home Assistant instance. - Fitbit requires that your Home Assistant instance is accessible via HTTPS. - """ - configurator.notify_errors(_CONFIGURING["fitbit"], error_msg) + _LOGGER.error( + "Could not find an SSL enabled URL for your Home Assistant instance. " + "Fitbit requires that your Home Assistant instance is accessible via HTTPS" + ) + return submit = "I have saved my Client ID and Client Secret into fitbit.conf." From 3fde6bfd732633c2ee573e4fd4376c4731d029e4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 18 Dec 2021 17:14:41 -0600 Subject: [PATCH 14/36] Fix Non-thread-safe operation in rflink binary_sensor (#62286) --- homeassistant/components/rflink/binary_sensor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/rflink/binary_sensor.py b/homeassistant/components/rflink/binary_sensor.py index 77a8a522f65..82457bd767e 100644 --- a/homeassistant/components/rflink/binary_sensor.py +++ b/homeassistant/components/rflink/binary_sensor.py @@ -12,6 +12,7 @@ from homeassistant.const import ( CONF_FORCE_UPDATE, CONF_NAME, ) +from homeassistant.core import callback import homeassistant.helpers.config_validation as cv import homeassistant.helpers.event as evt @@ -81,6 +82,7 @@ class RflinkBinarySensor(RflinkDevice, BinarySensorEntity): if self._state and self._off_delay is not None: + @callback def off_delay_listener(now): """Switch device off after a delay.""" self._delay_listener = None From c1d0fe9eaedbe9b9cbe5c3dfce1e51dc2af3c478 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 18 Dec 2021 16:17:54 -0600 Subject: [PATCH 15/36] Fix Non-thread-safe operation in zwave node_added (#62287) --- homeassistant/components/zwave/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index ef3ab223248..bfbd3925c55 100644 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -490,7 +490,7 @@ async def async_setup_entry(hass, config_entry): # noqa: C901 await platform.async_add_entities([entity]) if entity.unique_id: - hass.async_add_job(_add_node_to_component()) + hass.create_task(_add_node_to_component()) return @callback From dc4659b167c53a62d80142df5c66e67323bd86fe Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Dec 2021 00:27:34 -0600 Subject: [PATCH 16/36] Bump flux_led to 0.27.8 to fix discovery of older devices (#62292) --- homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 22a8aa405e4..8d3d7416c00 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -3,7 +3,7 @@ "name": "Flux LED/MagicHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.26.15"], + "requirements": ["flux_led==0.27.8"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index d033ec06d51..95b7b7ef411 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -658,7 +658,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.26.15 +flux_led==0.27.8 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 20aed6ae1e7..100f35a3758 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -399,7 +399,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.26.15 +flux_led==0.27.8 # homeassistant.components.homekit fnvhash==0.1.0 From 311ebd4a964819238528bedc9d853bb4f4e3ca4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Elio=20Petten=C3=B2?= Date: Fri, 17 Dec 2021 22:57:02 +0000 Subject: [PATCH 17/36] Bump async-upnp-client to 0.23.0 (#62223) --- homeassistant/components/dlna_dmr/manifest.json | 2 +- homeassistant/components/ssdp/manifest.json | 2 +- homeassistant/components/upnp/manifest.json | 2 +- homeassistant/components/yeelight/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/dlna_dmr/test_media_player.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/dlna_dmr/manifest.json b/homeassistant/components/dlna_dmr/manifest.json index 9bc020f3693..fb942717817 100644 --- a/homeassistant/components/dlna_dmr/manifest.json +++ b/homeassistant/components/dlna_dmr/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Renderer", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dmr", - "requirements": ["async-upnp-client==0.22.12"], + "requirements": ["async-upnp-client==0.23.0"], "dependencies": ["ssdp"], "ssdp": [ { diff --git a/homeassistant/components/ssdp/manifest.json b/homeassistant/components/ssdp/manifest.json index ea8b8ff73a4..c11138e0816 100644 --- a/homeassistant/components/ssdp/manifest.json +++ b/homeassistant/components/ssdp/manifest.json @@ -2,7 +2,7 @@ "domain": "ssdp", "name": "Simple Service Discovery Protocol (SSDP)", "documentation": "https://www.home-assistant.io/integrations/ssdp", - "requirements": ["async-upnp-client==0.22.12"], + "requirements": ["async-upnp-client==0.23.0"], "dependencies": ["network"], "after_dependencies": ["zeroconf"], "codeowners": [], diff --git a/homeassistant/components/upnp/manifest.json b/homeassistant/components/upnp/manifest.json index da12c25c7d1..87c40346fed 100644 --- a/homeassistant/components/upnp/manifest.json +++ b/homeassistant/components/upnp/manifest.json @@ -3,7 +3,7 @@ "name": "UPnP/IGD", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/upnp", - "requirements": ["async-upnp-client==0.22.12"], + "requirements": ["async-upnp-client==0.23.0"], "dependencies": ["network", "ssdp"], "codeowners": ["@StevenLooman","@ehendrix23"], "ssdp": [ diff --git a/homeassistant/components/yeelight/manifest.json b/homeassistant/components/yeelight/manifest.json index 60098514125..1b4246f478f 100644 --- a/homeassistant/components/yeelight/manifest.json +++ b/homeassistant/components/yeelight/manifest.json @@ -2,7 +2,7 @@ "domain": "yeelight", "name": "Yeelight", "documentation": "https://www.home-assistant.io/integrations/yeelight", - "requirements": ["yeelight==0.7.8", "async-upnp-client==0.22.12"], + "requirements": ["yeelight==0.7.8", "async-upnp-client==0.23.0"], "codeowners": ["@rytilahti", "@zewelor", "@shenxn", "@starkillerOG"], "config_flow": true, "dependencies": ["network"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index e52542ccea0..a200f6b0a03 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -4,7 +4,7 @@ aiodiscover==1.4.5 aiohttp==3.8.1 aiohttp_cors==0.7.0 astral==2.2 -async-upnp-client==0.22.12 +async-upnp-client==0.23.0 async_timeout==4.0.0 atomicwrites==1.4.0 attrs==21.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index 95b7b7ef411..d93a567f1aa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -336,7 +336,7 @@ asterisk_mbox==0.5.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.22.12 +async-upnp-client==0.23.0 # homeassistant.components.supla asyncpysupla==0.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 100f35a3758..565a30b1f36 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -236,7 +236,7 @@ arcam-fmj==0.12.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.22.12 +async-upnp-client==0.23.0 # homeassistant.components.aurora auroranoaa==0.0.2 diff --git a/tests/components/dlna_dmr/test_media_player.py b/tests/components/dlna_dmr/test_media_player.py index fe2a916fdcc..b8c10b47b60 100644 --- a/tests/components/dlna_dmr/test_media_player.py +++ b/tests/components/dlna_dmr/test_media_player.py @@ -381,7 +381,7 @@ async def test_event_subscribe_rejected( Device state will instead be obtained via polling in async_update. """ - dmr_device_mock.async_subscribe_services.side_effect = UpnpResponseError(501) + dmr_device_mock.async_subscribe_services.side_effect = UpnpResponseError(status=501) mock_entity_id = await setup_mock_component(hass, config_entry_mock) mock_state = hass.states.get(mock_entity_id) From b327628b6ecc9bb804e49ff0dcce183e2868c562 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Sun, 19 Dec 2021 16:41:32 +1100 Subject: [PATCH 18/36] Update async-upnp-client library to 0.23.1 (#62298) --- homeassistant/components/dlna_dmr/manifest.json | 2 +- homeassistant/components/ssdp/manifest.json | 2 +- homeassistant/components/upnp/manifest.json | 2 +- homeassistant/components/yeelight/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/dlna_dmr/manifest.json b/homeassistant/components/dlna_dmr/manifest.json index fb942717817..ecc3cd4256d 100644 --- a/homeassistant/components/dlna_dmr/manifest.json +++ b/homeassistant/components/dlna_dmr/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Renderer", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dmr", - "requirements": ["async-upnp-client==0.23.0"], + "requirements": ["async-upnp-client==0.23.1"], "dependencies": ["ssdp"], "ssdp": [ { diff --git a/homeassistant/components/ssdp/manifest.json b/homeassistant/components/ssdp/manifest.json index c11138e0816..e95c0e13887 100644 --- a/homeassistant/components/ssdp/manifest.json +++ b/homeassistant/components/ssdp/manifest.json @@ -2,7 +2,7 @@ "domain": "ssdp", "name": "Simple Service Discovery Protocol (SSDP)", "documentation": "https://www.home-assistant.io/integrations/ssdp", - "requirements": ["async-upnp-client==0.23.0"], + "requirements": ["async-upnp-client==0.23.1"], "dependencies": ["network"], "after_dependencies": ["zeroconf"], "codeowners": [], diff --git a/homeassistant/components/upnp/manifest.json b/homeassistant/components/upnp/manifest.json index 87c40346fed..2644a91b20f 100644 --- a/homeassistant/components/upnp/manifest.json +++ b/homeassistant/components/upnp/manifest.json @@ -3,7 +3,7 @@ "name": "UPnP/IGD", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/upnp", - "requirements": ["async-upnp-client==0.23.0"], + "requirements": ["async-upnp-client==0.23.1"], "dependencies": ["network", "ssdp"], "codeowners": ["@StevenLooman","@ehendrix23"], "ssdp": [ diff --git a/homeassistant/components/yeelight/manifest.json b/homeassistant/components/yeelight/manifest.json index 1b4246f478f..c46878c0ef3 100644 --- a/homeassistant/components/yeelight/manifest.json +++ b/homeassistant/components/yeelight/manifest.json @@ -2,7 +2,7 @@ "domain": "yeelight", "name": "Yeelight", "documentation": "https://www.home-assistant.io/integrations/yeelight", - "requirements": ["yeelight==0.7.8", "async-upnp-client==0.23.0"], + "requirements": ["yeelight==0.7.8", "async-upnp-client==0.23.1"], "codeowners": ["@rytilahti", "@zewelor", "@shenxn", "@starkillerOG"], "config_flow": true, "dependencies": ["network"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index a200f6b0a03..fcad7a03bee 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -4,7 +4,7 @@ aiodiscover==1.4.5 aiohttp==3.8.1 aiohttp_cors==0.7.0 astral==2.2 -async-upnp-client==0.23.0 +async-upnp-client==0.23.1 async_timeout==4.0.0 atomicwrites==1.4.0 attrs==21.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index d93a567f1aa..b55024ed82e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -336,7 +336,7 @@ asterisk_mbox==0.5.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.23.0 +async-upnp-client==0.23.1 # homeassistant.components.supla asyncpysupla==0.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 565a30b1f36..c61d621f064 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -236,7 +236,7 @@ arcam-fmj==0.12.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.23.0 +async-upnp-client==0.23.1 # homeassistant.components.aurora auroranoaa==0.0.2 From 5f2a2280c566a878db003333ea2117e7e3e52697 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 18 Dec 2021 21:41:59 -0800 Subject: [PATCH 19/36] Bump ring to 0.7.2 (#62299) --- homeassistant/components/ring/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ring/manifest.json b/homeassistant/components/ring/manifest.json index 527fb143aff..3e745dc2d4b 100644 --- a/homeassistant/components/ring/manifest.json +++ b/homeassistant/components/ring/manifest.json @@ -2,7 +2,7 @@ "domain": "ring", "name": "Ring", "documentation": "https://www.home-assistant.io/integrations/ring", - "requirements": ["ring_doorbell==0.7.1"], + "requirements": ["ring_doorbell==0.7.2"], "dependencies": ["ffmpeg"], "codeowners": ["@balloob"], "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index b55024ed82e..641a100d132 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2058,7 +2058,7 @@ rfk101py==0.0.1 rflink==0.0.58 # homeassistant.components.ring -ring_doorbell==0.7.1 +ring_doorbell==0.7.2 # homeassistant.components.fleetgo ritassist==0.9.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c61d621f064..fae30d9a57b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1230,7 +1230,7 @@ restrictedpython==5.2 rflink==0.0.58 # homeassistant.components.ring -ring_doorbell==0.7.1 +ring_doorbell==0.7.2 # homeassistant.components.roku rokuecp==0.8.4 From 7db161868ec4989183efa101a3e5d43d7657eb94 Mon Sep 17 00:00:00 2001 From: Thijs Walcarius Date: Sun, 19 Dec 2021 20:14:56 +0100 Subject: [PATCH 20/36] Fix missing brightness for Velbus entities (#62314) * Fix #62169: missing brightness for Velbus-entities * Use default implementation of supported_features Co-authored-by: Thijs Walcarius --- homeassistant/components/velbus/light.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/velbus/light.py b/homeassistant/components/velbus/light.py index bd903c76790..f1a90651716 100644 --- a/homeassistant/components/velbus/light.py +++ b/homeassistant/components/velbus/light.py @@ -49,7 +49,7 @@ class VelbusLight(VelbusEntity, LightEntity): """Representation of a Velbus light.""" _channel: VelbusDimmer - _attr_supported_feature = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION + _attr_supported_features = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION @property def is_on(self) -> bool: @@ -96,7 +96,7 @@ class VelbusButtonLight(VelbusEntity, LightEntity): _channel: VelbusButton _attr_entity_registry_enabled_default = False - _attr_supported_feature = SUPPORT_FLASH + _attr_supported_features = SUPPORT_FLASH def __init__(self, channel: VelbusChannel) -> None: """Initialize the button light (led).""" From 7c925778eb8cef57d01602da236d4a1c723d07d1 Mon Sep 17 00:00:00 2001 From: Maikel Punie Date: Sun, 19 Dec 2021 20:22:41 +0100 Subject: [PATCH 21/36] Fix velbus climate current temp (#62329) --- homeassistant/components/velbus/climate.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/homeassistant/components/velbus/climate.py b/homeassistant/components/velbus/climate.py index c11698b1358..5dce4033074 100644 --- a/homeassistant/components/velbus/climate.py +++ b/homeassistant/components/velbus/climate.py @@ -61,6 +61,11 @@ class VelbusClimate(VelbusEntity, ClimateEntity): None, ) + @property + def current_temperature(self) -> int | None: + """Return the current temperature.""" + return self._channel.get_state() + async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperatures.""" if (temp := kwargs.get(ATTR_TEMPERATURE)) is None: From beb5a992e67e78538bf9f0a818a340dd3e122bb3 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 19 Dec 2021 15:05:13 -0700 Subject: [PATCH 22/36] Ensure existing SimpliSafe websocket tasks are cancelled appropriately (#62347) --- .../components/simplisafe/__init__.py | 33 +++++++++++-------- .../components/simplisafe/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/simplisafe/__init__.py b/homeassistant/components/simplisafe/__init__.py index 2b87f7aa8b0..59142674e66 100644 --- a/homeassistant/components/simplisafe/__init__.py +++ b/homeassistant/components/simplisafe/__init__.py @@ -519,14 +519,14 @@ class SimpliSafe: self._system_notifications[system.system_id] = latest_notifications async def _async_start_websocket_loop(self) -> None: - """Define a callback for connecting to the websocket.""" + """Start a websocket reconnection loop.""" if TYPE_CHECKING: assert self._api.websocket should_reconnect = True try: - await self._api.websocket.async_reconnect() + await self._api.websocket.async_connect() await self._api.websocket.async_listen() except asyncio.CancelledError: LOGGER.debug("Request to cancel websocket loop received") @@ -538,10 +538,25 @@ class SimpliSafe: if should_reconnect: LOGGER.info("Disconnected from websocket; reconnecting") + await self._async_cancel_websocket_loop() self._websocket_reconnect_task = self._hass.async_create_task( self._async_start_websocket_loop() ) + async def _async_cancel_websocket_loop(self) -> None: + """Stop any existing websocket reconnection loop.""" + if self._websocket_reconnect_task: + self._websocket_reconnect_task.cancel() + try: + await self._websocket_reconnect_task + except asyncio.CancelledError: + LOGGER.debug("Websocket reconnection task successfully canceled") + self._websocket_reconnect_task = None + + if TYPE_CHECKING: + assert self._api.websocket + await self._api.websocket.async_disconnect() + @callback def _async_websocket_on_event(self, event: WebsocketEvent) -> None: """Define a callback for receiving a websocket event.""" @@ -590,15 +605,7 @@ class SimpliSafe: if TYPE_CHECKING: assert self._api.websocket - if self._websocket_reconnect_task: - self._websocket_reconnect_task.cancel() - try: - await self._websocket_reconnect_task - except asyncio.CancelledError: - LOGGER.debug("Websocket reconnection task successfully canceled") - self._websocket_reconnect_task = None - - await self._api.websocket.async_disconnect() + await self._async_cancel_websocket_loop() self.entry.async_on_unload( self._hass.bus.async_listen_once( @@ -640,8 +647,7 @@ class SimpliSafe: data={**self.entry.data, CONF_TOKEN: token}, ) - @callback - def async_handle_refresh_token(token: str) -> None: + async def async_handle_refresh_token(token: str) -> None: """Handle a new refresh token.""" async_save_refresh_token(token) @@ -649,6 +655,7 @@ class SimpliSafe: assert self._api.websocket # Open a new websocket connection with the fresh token: + await self._async_cancel_websocket_loop() self._websocket_reconnect_task = self._hass.async_create_task( self._async_start_websocket_loop() ) diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json index 0b6cb385be6..8e494af013a 100644 --- a/homeassistant/components/simplisafe/manifest.json +++ b/homeassistant/components/simplisafe/manifest.json @@ -3,7 +3,7 @@ "name": "SimpliSafe", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/simplisafe", - "requirements": ["simplisafe-python==2021.12.1"], + "requirements": ["simplisafe-python==2021.12.2"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 641a100d132..eb0d15b4eb9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2146,7 +2146,7 @@ simplehound==0.3 simplepush==1.1.4 # homeassistant.components.simplisafe -simplisafe-python==2021.12.1 +simplisafe-python==2021.12.2 # homeassistant.components.sisyphus sisyphus-control==3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fae30d9a57b..4cf175b5e09 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1273,7 +1273,7 @@ sharkiqpy==0.1.8 simplehound==0.3 # homeassistant.components.simplisafe -simplisafe-python==2021.12.1 +simplisafe-python==2021.12.2 # homeassistant.components.slack slackclient==2.5.0 From bfd85795666beb93a16e3af86bb8b5d9e1320167 Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Sun, 19 Dec 2021 21:02:05 -0800 Subject: [PATCH 23/36] Bump pywemo==0.7.0 (#62360) --- homeassistant/components/wemo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/wemo/conftest.py | 2 +- tests/components/wemo/test_device_trigger.py | 10 +++++----- tests/components/wemo/test_wemo_device.py | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/wemo/manifest.json b/homeassistant/components/wemo/manifest.json index 59eae24c714..d0643ed51a9 100644 --- a/homeassistant/components/wemo/manifest.json +++ b/homeassistant/components/wemo/manifest.json @@ -3,7 +3,7 @@ "name": "Belkin WeMo", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/wemo", - "requirements": ["pywemo==0.6.7"], + "requirements": ["pywemo==0.7.0"], "ssdp": [ { "manufacturer": "Belkin International Inc." diff --git a/requirements_all.txt b/requirements_all.txt index eb0d15b4eb9..7d67fb479bf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2007,7 +2007,7 @@ pyvolumio==0.1.3 pywebpush==1.9.2 # homeassistant.components.wemo -pywemo==0.6.7 +pywemo==0.7.0 # homeassistant.components.wilight pywilight==0.0.70 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4cf175b5e09..78fb6dfa0c8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1206,7 +1206,7 @@ pyvolumio==0.1.3 pywebpush==1.9.2 # homeassistant.components.wemo -pywemo==0.6.7 +pywemo==0.7.0 # homeassistant.components.wilight pywilight==0.0.70 diff --git a/tests/components/wemo/conftest.py b/tests/components/wemo/conftest.py index 08abd140dac..13ec0cb2337 100644 --- a/tests/components/wemo/conftest.py +++ b/tests/components/wemo/conftest.py @@ -57,7 +57,7 @@ def pywemo_device_fixture(pywemo_registry, pywemo_model): device.port = MOCK_PORT device.name = MOCK_NAME device.serialnumber = MOCK_SERIAL_NUMBER - device.model_name = pywemo_model + device.model_name = pywemo_model.replace("LongPress", "") device.get_state.return_value = 0 # Default to Off device.supports_long_press.return_value = cls.supports_long_press() diff --git a/tests/components/wemo/test_device_trigger.py b/tests/components/wemo/test_device_trigger.py index 76016469b72..0ad7d95dd7a 100644 --- a/tests/components/wemo/test_device_trigger.py +++ b/tests/components/wemo/test_device_trigger.py @@ -3,7 +3,6 @@ import pytest from pywemo.subscribe import EVENT_TYPE_LONG_PRESS from homeassistant.components.automation import DOMAIN as AUTOMATION_DOMAIN -from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.components.wemo.const import DOMAIN, WEMO_SUBSCRIPTION_EVENT from homeassistant.const import ( CONF_DEVICE_ID, @@ -11,6 +10,7 @@ from homeassistant.const import ( CONF_ENTITY_ID, CONF_PLATFORM, CONF_TYPE, + Platform, ) from homeassistant.setup import async_setup_component @@ -26,8 +26,8 @@ DATA_MESSAGE = {"message": "service-called"} @pytest.fixture def pywemo_model(): - """Pywemo Dimmer models use the light platform (WemoDimmer class).""" - return "Dimmer" + """Pywemo LightSwitch models use the switch platform.""" + return "LightSwitchLongPress" async def setup_automation(hass, device_id, trigger_type): @@ -67,14 +67,14 @@ async def test_get_triggers(hass, wemo_entity): }, { CONF_DEVICE_ID: wemo_entity.device_id, - CONF_DOMAIN: LIGHT_DOMAIN, + CONF_DOMAIN: Platform.SWITCH, CONF_ENTITY_ID: wemo_entity.entity_id, CONF_PLATFORM: "device", CONF_TYPE: "turned_off", }, { CONF_DEVICE_ID: wemo_entity.device_id, - CONF_DOMAIN: LIGHT_DOMAIN, + CONF_DOMAIN: Platform.SWITCH, CONF_ENTITY_ID: wemo_entity.entity_id, CONF_PLATFORM: "device", CONF_TYPE: "turned_on", diff --git a/tests/components/wemo/test_wemo_device.py b/tests/components/wemo/test_wemo_device.py index e756e816a47..9ef9e6b5685 100644 --- a/tests/components/wemo/test_wemo_device.py +++ b/tests/components/wemo/test_wemo_device.py @@ -26,8 +26,8 @@ asyncio.set_event_loop_policy(runner.HassEventLoopPolicy(True)) @pytest.fixture def pywemo_model(): - """Pywemo Dimmer models use the light platform (WemoDimmer class).""" - return "Dimmer" + """Pywemo LightSwitch models use the switch platform.""" + return "LightSwitchLongPress" async def test_async_register_device_longpress_fails(hass, pywemo_device): From eb4b041d459da4a1c0cb25beebcd48b104501501 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 19 Dec 2021 22:28:15 -0800 Subject: [PATCH 24/36] Bump voluptuous_serialize to 2.5.0 (#62363) --- 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 fcad7a03bee..b943d4821aa 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -30,7 +30,7 @@ pyyaml==6.0 requests==2.26.0 scapy==2.4.5 sqlalchemy==1.4.27 -voluptuous-serialize==2.4.0 +voluptuous-serialize==2.5.0 voluptuous==0.12.2 yarl==1.6.3 zeroconf==0.37.0 diff --git a/requirements.txt b/requirements.txt index 5832d0ea2d0..4c6af849ce8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,5 +21,5 @@ python-slugify==4.0.1 pyyaml==6.0 requests==2.26.0 voluptuous==0.12.2 -voluptuous-serialize==2.4.0 +voluptuous-serialize==2.5.0 yarl==1.6.3 diff --git a/setup.py b/setup.py index ee163bc79f4..270f5c58f58 100755 --- a/setup.py +++ b/setup.py @@ -53,7 +53,7 @@ REQUIRES = [ "pyyaml==6.0", "requests==2.26.0", "voluptuous==0.12.2", - "voluptuous-serialize==2.4.0", + "voluptuous-serialize==2.5.0", "yarl==1.6.3", ] From 4802e4e33f025cd1c2bce92506281f6874ac4a20 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 19 Dec 2021 22:32:50 -0800 Subject: [PATCH 25/36] Bumped version to 2021.12.4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 81734046d2b..bda65572d4e 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from homeassistant.backports.enum import StrEnum MAJOR_VERSION: Final = 2021 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "3" +PATCH_VERSION: Final = "4" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0) From dcc08a0aac7917c133e85129e1bbab56fd896826 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 20 Dec 2021 14:28:02 +0100 Subject: [PATCH 26/36] Don't use the homeassistant media app when casting media (#62385) --- homeassistant/components/cast/media_player.py | 2 +- tests/components/cast/test_media_player.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index 61922a4cd8b..b687f96948b 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -526,7 +526,7 @@ class CastDevice(MediaPlayerEntity): controller.play_media(media) else: app_data = {"media_id": media_id, "media_type": media_type, **extra} - quick_play(self._chromecast, "homeassistant_media", app_data) + quick_play(self._chromecast, "default_media_receiver", app_data) def _media_status(self): """ diff --git a/tests/components/cast/test_media_player.py b/tests/components/cast/test_media_player.py index 85562f39761..dae9981ae67 100644 --- a/tests/components/cast/test_media_player.py +++ b/tests/components/cast/test_media_player.py @@ -791,7 +791,7 @@ async def test_entity_play_media(hass: HomeAssistant, quick_play_mock): chromecast.media_controller.play_media.assert_not_called() quick_play_mock.assert_called_once_with( chromecast, - "homeassistant_media", + "default_media_receiver", { "media_id": "best.mp3", "media_type": "audio", @@ -907,7 +907,7 @@ async def test_entity_play_media_sign_URL(hass: HomeAssistant, quick_play_mock): # Play_media await common.async_play_media(hass, "audio", "/best.mp3", entity_id) quick_play_mock.assert_called_once_with( - chromecast, "homeassistant_media", {"media_id": ANY, "media_type": "audio"} + chromecast, "default_media_receiver", {"media_id": ANY, "media_type": "audio"} ) assert quick_play_mock.call_args[0][2]["media_id"].startswith( "http://example.com:8123/best.mp3?authSig=" @@ -1311,7 +1311,7 @@ async def test_group_media_control(hass, mz_mock, quick_play_mock): assert not chromecast.media_controller.play_media.called quick_play_mock.assert_called_once_with( chromecast, - "homeassistant_media", + "default_media_receiver", {"media_id": "best.mp3", "media_type": "music"}, ) From 7ec369d8eface8a3f29907bb076cc4f928e94699 Mon Sep 17 00:00:00 2001 From: Eduard van Valkenburg Date: Mon, 20 Dec 2021 18:31:59 +0100 Subject: [PATCH 27/36] Bump brunt to 1.1.0 (#62386) --- homeassistant/components/brunt/__init__.py | 2 +- homeassistant/components/brunt/cover.py | 21 +++++++++----------- homeassistant/components/brunt/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/brunt/__init__.py b/homeassistant/components/brunt/__init__.py index 37c9fd73632..988a96ce08e 100644 --- a/homeassistant/components/brunt/__init__.py +++ b/homeassistant/components/brunt/__init__.py @@ -45,7 +45,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: try: async with async_timeout.timeout(10): things = await bapi.async_get_things(force=True) - return {thing.SERIAL: thing for thing in things} + return {thing.serial: thing for thing in things} except ServerDisconnectedError as err: raise UpdateFailed(f"Error communicating with API: {err}") from err except ClientResponseError as err: diff --git a/homeassistant/components/brunt/cover.py b/homeassistant/components/brunt/cover.py index cc0ecd0feab..230534a0848 100644 --- a/homeassistant/components/brunt/cover.py +++ b/homeassistant/components/brunt/cover.py @@ -100,7 +100,7 @@ class BruntDevice(CoordinatorEntity, CoverEntity): self._remove_update_listener = None - self._attr_name = self._thing.NAME + self._attr_name = self._thing.name self._attr_device_class = DEVICE_CLASS_SHADE self._attr_supported_features = COVER_FEATURES self._attr_attribution = ATTRIBUTION @@ -109,8 +109,8 @@ class BruntDevice(CoordinatorEntity, CoverEntity): name=self._attr_name, via_device=(DOMAIN, self._entry_id), manufacturer="Brunt", - sw_version=self._thing.FW_VERSION, - model=self._thing.MODEL, + sw_version=self._thing.fw_version, + model=self._thing.model, ) async def async_added_to_hass(self) -> None: @@ -127,8 +127,7 @@ class BruntDevice(CoordinatorEntity, CoverEntity): None is unknown, 0 is closed, 100 is fully open. """ - pos = self.coordinator.data[self.unique_id].currentPosition - return int(pos) if pos is not None else None + return self.coordinator.data[self.unique_id].current_position @property def request_cover_position(self) -> int | None: @@ -139,8 +138,7 @@ class BruntDevice(CoordinatorEntity, CoverEntity): to Brunt, at times there is a diff of 1 to current None is unknown, 0 is closed, 100 is fully open. """ - pos = self.coordinator.data[self.unique_id].requestPosition - return int(pos) if pos is not None else None + return self.coordinator.data[self.unique_id].request_position @property def move_state(self) -> int | None: @@ -149,8 +147,7 @@ class BruntDevice(CoordinatorEntity, CoverEntity): None is unknown, 0 when stopped, 1 when opening, 2 when closing """ - mov = self.coordinator.data[self.unique_id].moveState - return int(mov) if mov is not None else None + return self.coordinator.data[self.unique_id].move_state @property def is_opening(self) -> bool: @@ -190,11 +187,11 @@ class BruntDevice(CoordinatorEntity, CoverEntity): """Set the cover to the new position and wait for the update to be reflected.""" try: await self._bapi.async_change_request_position( - position, thingUri=self._thing.thingUri + position, thing_uri=self._thing.thing_uri ) except ClientResponseError as exc: raise HomeAssistantError( - f"Unable to reposition {self._thing.NAME}" + f"Unable to reposition {self._thing.name}" ) from exc self.coordinator.update_interval = FAST_INTERVAL await self.coordinator.async_request_refresh() @@ -204,7 +201,7 @@ class BruntDevice(CoordinatorEntity, CoverEntity): """Update the update interval after each refresh.""" if ( self.request_cover_position - == self._bapi.last_requested_positions[self._thing.thingUri] + == self._bapi.last_requested_positions[self._thing.thing_uri] and self.move_state == 0 ): self.coordinator.update_interval = REGULAR_INTERVAL diff --git a/homeassistant/components/brunt/manifest.json b/homeassistant/components/brunt/manifest.json index 7b9307e8ef2..f970419b787 100644 --- a/homeassistant/components/brunt/manifest.json +++ b/homeassistant/components/brunt/manifest.json @@ -3,7 +3,7 @@ "name": "Brunt Blind Engine", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/brunt", - "requirements": ["brunt==1.0.2"], + "requirements": ["brunt==1.1.0"], "codeowners": ["@eavanvalkenburg"], "iot_class": "cloud_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index 7d67fb479bf..67602f65d25 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -440,7 +440,7 @@ brother==1.1.0 brottsplatskartan==0.0.1 # homeassistant.components.brunt -brunt==1.0.2 +brunt==1.1.0 # homeassistant.components.bsblan bsblan==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 78fb6dfa0c8..b27b50b244b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -281,7 +281,7 @@ broadlink==0.18.0 brother==1.1.0 # homeassistant.components.brunt -brunt==1.0.2 +brunt==1.1.0 # homeassistant.components.bsblan bsblan==0.4.0 From e80f4e03a4c2b838bd09d29bb5469c1121343604 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 20 Dec 2021 18:49:16 +0100 Subject: [PATCH 28/36] Update frontend to 20211220.0 (#62389) --- 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 4ac95c38afc..6ae1709e418 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==20211215.0" + "home-assistant-frontend==20211220.0" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index b943d4821aa..5ea2198a3aa 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -16,7 +16,7 @@ ciso8601==2.2.0 cryptography==35.0.0 emoji==1.5.0 hass-nabucasa==0.50.0 -home-assistant-frontend==20211215.0 +home-assistant-frontend==20211220.0 httpx==0.21.0 ifaddr==0.1.7 jinja2==3.0.3 diff --git a/requirements_all.txt b/requirements_all.txt index 67602f65d25..0c7a139b094 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -819,7 +819,7 @@ hole==0.7.0 holidays==0.11.3.1 # homeassistant.components.frontend -home-assistant-frontend==20211215.0 +home-assistant-frontend==20211220.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b27b50b244b..3baf1637652 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -515,7 +515,7 @@ hole==0.7.0 holidays==0.11.3.1 # homeassistant.components.frontend -home-assistant-frontend==20211215.0 +home-assistant-frontend==20211220.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 From b8f8b30b9bd640de278c9ab6ea839a960ed44515 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 20 Dec 2021 18:37:11 +0100 Subject: [PATCH 29/36] Bump pychromecast to 10.2.2 (#62390) --- homeassistant/components/cast/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cast/manifest.json b/homeassistant/components/cast/manifest.json index bee18948a33..b084540bebb 100644 --- a/homeassistant/components/cast/manifest.json +++ b/homeassistant/components/cast/manifest.json @@ -3,7 +3,7 @@ "name": "Google Cast", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/cast", - "requirements": ["pychromecast==10.2.1"], + "requirements": ["pychromecast==10.2.2"], "after_dependencies": [ "cloud", "http", diff --git a/requirements_all.txt b/requirements_all.txt index 0c7a139b094..2f675b87de1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1396,7 +1396,7 @@ pycfdns==1.2.2 pychannels==1.0.0 # homeassistant.components.cast -pychromecast==10.2.1 +pychromecast==10.2.2 # homeassistant.components.pocketcasts pycketcasts==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3baf1637652..2c6c1dffe62 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -850,7 +850,7 @@ pybotvac==0.0.22 pycfdns==1.2.2 # homeassistant.components.cast -pychromecast==10.2.1 +pychromecast==10.2.2 # homeassistant.components.climacell pyclimacell==0.18.2 From 5a2bc8e4939c8014ed560cf25ef00889b491a21f Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Mon, 20 Dec 2021 19:22:04 +0100 Subject: [PATCH 30/36] Update xknx to 0.18.14 (#62411) Co-authored-by: Franck Nijhof --- homeassistant/components/knx/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/knx/test_config_flow.py | 10 +++++++++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index b793c667353..21ac4ce9ea4 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/knx", "requirements": [ - "xknx==0.18.13" + "xknx==0.18.14" ], "codeowners": [ "@Julius2342", diff --git a/requirements_all.txt b/requirements_all.txt index 2f675b87de1..cf2fef1e6bc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2448,7 +2448,7 @@ xbox-webapi==2.0.11 xboxapi==2.0.1 # homeassistant.components.knx -xknx==0.18.13 +xknx==0.18.14 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2c6c1dffe62..3fabae380c3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1450,7 +1450,7 @@ wolf_smartset==0.1.11 xbox-webapi==2.0.11 # homeassistant.components.knx -xknx==0.18.13 +xknx==0.18.14 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/tests/components/knx/test_config_flow.py b/tests/components/knx/test_config_flow.py index 65289c2b173..4f3e1734b69 100644 --- a/tests/components/knx/test_config_flow.py +++ b/tests/components/knx/test_config_flow.py @@ -28,7 +28,15 @@ from tests.common import MockConfigEntry def _gateway_descriptor(ip: str, port: int) -> GatewayDescriptor: """Get mock gw descriptor.""" - return GatewayDescriptor("Test", ip, port, "eth0", "127.0.0.1", True) + return GatewayDescriptor( + "Test", + ip, + port, + "eth0", + "127.0.0.1", + supports_routing=True, + supports_tunnelling=True, + ) async def test_user_single_instance(hass): From d90c107b1bb38d25f144eed3c987a7b39530d12c Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 20 Dec 2021 18:09:36 +0100 Subject: [PATCH 31/36] Invalidate CI cache when bumping dependencies, part 2 (#62412) --- .github/workflows/ci.yaml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ebbd2b2fb9e..73cec8b9635 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -517,10 +517,15 @@ jobs: key: >- ${{ runner.os }}-${{ matrix.python-version }}-${{ steps.generate-python-key.outputs.key }} - restore-keys: | - ${{ runner.os }}-${{ matrix.python-version }}-venv-${{ env.CACHE_VERSION }}-${{ hashFiles('requirements_test.txt') }}-${{ hashFiles('requirements_all.txt') }}- - ${{ runner.os }}-${{ matrix.python-version }}-venv-${{ env.CACHE_VERSION }}-${{ hashFiles('requirements_test.txt') }}- - ${{ runner.os }}-${{ matrix.python-version }}-venv-${{ env.CACHE_VERSION }}- + # Temporary disabling the restore of environments when bumping + # a dependency. It seems that we are experiencing issues with + # restoring environments in GitHub Actions, although unclear why. + # First attempt: https://github.com/home-assistant/core/pull/62383 + # + # restore-keys: | + # ${{ runner.os }}-${{ matrix.python-version }}-venv-${{ env.CACHE_VERSION }}-${{ hashFiles('requirements_test.txt') }}-${{ hashFiles('requirements_all.txt') }}- + # ${{ runner.os }}-${{ matrix.python-version }}-venv-${{ env.CACHE_VERSION }}-${{ hashFiles('requirements_test.txt') }}- + # ${{ runner.os }}-${{ matrix.python-version }}-venv-${{ env.CACHE_VERSION }}- - name: Create full Python ${{ matrix.python-version }} virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' run: | From 520c3411ddc0fa0c531eaa96cc23a08bb955bb07 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 20 Dec 2021 16:22:09 +0100 Subject: [PATCH 32/36] Invalidate CI cache when bumping dependencies (#62394) Co-authored-by: Martin Hjelmare --- .github/workflows/ci.yaml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 73cec8b9635..71fe8af00ea 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -155,10 +155,15 @@ jobs: key: >- ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ steps.generate-python-key.outputs.key }} - restore-keys: | - ${{ runner.os }}-${{ steps.python.outputs.python-version }}-base-venv-${{ env.CACHE_VERSION }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_test.txt') }}- - ${{ runner.os }}-${{ steps.python.outputs.python-version }}-base-venv-${{ env.CACHE_VERSION }}-${{ hashFiles('requirements.txt') }}- - ${{ runner.os }}-${{ steps.python.outputs.python-version }}-base-venv-${{ env.CACHE_VERSION }}- + # Temporary disabling the restore of environments when bumping + # a dependency. It seems that we are experiencing issues with + # restoring environments in GitHub Actions, although unclear why. + # First attempt: https://github.com/home-assistant/core/pull/62383 + # + # restore-keys: | + # ${{ runner.os }}-${{ steps.python.outputs.python-version }}-base-venv-${{ env.CACHE_VERSION }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_test.txt') }}- + # ${{ runner.os }}-${{ steps.python.outputs.python-version }}-base-venv-${{ env.CACHE_VERSION }}-${{ hashFiles('requirements.txt') }}- + # ${{ runner.os }}-${{ steps.python.outputs.python-version }}-base-venv-${{ env.CACHE_VERSION }}- - name: Create Python virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' run: | From 836d8a6fca9c3d38f61d12d4530c5391a14fd0e7 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 20 Dec 2021 20:18:11 +0100 Subject: [PATCH 33/36] Make it possible to turn on audio only google cast devices (#62420) --- homeassistant/components/cast/media_player.py | 13 ++++++++----- tests/components/cast/test_media_player.py | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index b687f96948b..8160c1f5bf0 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -395,11 +395,14 @@ class CastDevice(MediaPlayerEntity): return if self._chromecast.app_id is not None: - # Quit the previous app before starting splash screen + # Quit the previous app before starting splash screen or media player self._chromecast.quit_app() # The only way we can turn the Chromecast is on is by launching an app - self._chromecast.play_media(CAST_SPLASH, pychromecast.STREAM_TYPE_BUFFERED) + if self._chromecast.cast_type == pychromecast.const.CAST_TYPE_CHROMECAST: + self._chromecast.play_media(CAST_SPLASH, pychromecast.STREAM_TYPE_BUFFERED) + else: + self._chromecast.start_app(pychromecast.config.APP_MEDIA_RECEIVER) def turn_off(self): """Turn off the cast device.""" @@ -674,9 +677,9 @@ class CastDevice(MediaPlayerEntity): support = SUPPORT_CAST media_status = self._media_status()[0] - if ( - self._chromecast - and self._chromecast.cast_type == pychromecast.const.CAST_TYPE_CHROMECAST + if self._chromecast and self._chromecast.cast_type in ( + pychromecast.const.CAST_TYPE_CHROMECAST, + pychromecast.const.CAST_TYPE_AUDIO, ): support |= SUPPORT_TURN_ON diff --git a/tests/components/cast/test_media_player.py b/tests/components/cast/test_media_player.py index dae9981ae67..3c5d4705713 100644 --- a/tests/components/cast/test_media_player.py +++ b/tests/components/cast/test_media_player.py @@ -683,10 +683,12 @@ async def test_entity_cast_status(hass: HomeAssistant): | SUPPORT_PLAY_MEDIA | SUPPORT_STOP | SUPPORT_TURN_OFF + | SUPPORT_TURN_ON | SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET, SUPPORT_PLAY_MEDIA | SUPPORT_TURN_OFF + | SUPPORT_TURN_ON | SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET, ), From a1fc223914685cb8181baa18c7d7e774c51a0a4a Mon Sep 17 00:00:00 2001 From: rikroe <42204099+rikroe@users.noreply.github.com> Date: Mon, 20 Dec 2021 23:25:26 +0100 Subject: [PATCH 34/36] Bump bimmer_connected to 0.8.7 (#62435) Co-authored-by: rikroe --- homeassistant/components/bmw_connected_drive/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index fc641548aff..63046d9d441 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.8.5"], + "requirements": ["bimmer_connected==0.8.7"], "codeowners": ["@gerard33", "@rikroe"], "config_flow": true, "iot_class": "cloud_polling" diff --git a/requirements_all.txt b/requirements_all.txt index cf2fef1e6bc..b0d8a41215e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -387,7 +387,7 @@ beautifulsoup4==4.10.0 bellows==0.29.0 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.8.5 +bimmer_connected==0.8.7 # homeassistant.components.bizkaibus bizkaibus==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3fabae380c3..e391eeb234d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -257,7 +257,7 @@ base36==0.1.1 bellows==0.29.0 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.8.5 +bimmer_connected==0.8.7 # homeassistant.components.blebox blebox_uniapi==1.3.3 From 1f0c13f259effd0cd97f7af5476d40f834016d38 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Mon, 20 Dec 2021 22:45:38 +0100 Subject: [PATCH 35/36] bump aiohue to 3.0.7 (#62444) --- homeassistant/components/hue/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/hue/manifest.json b/homeassistant/components/hue/manifest.json index 7003a3a8ccf..ba2d97f44a5 100644 --- a/homeassistant/components/hue/manifest.json +++ b/homeassistant/components/hue/manifest.json @@ -3,7 +3,7 @@ "name": "Philips Hue", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/hue", - "requirements": ["aiohue==3.0.6"], + "requirements": ["aiohue==3.0.7"], "ssdp": [ { "manufacturer": "Royal Philips Electronics", diff --git a/requirements_all.txt b/requirements_all.txt index b0d8a41215e..0fa32a93289 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -186,7 +186,7 @@ aiohomekit==0.6.4 aiohttp_cors==0.7.0 # homeassistant.components.hue -aiohue==3.0.6 +aiohue==3.0.7 # homeassistant.components.imap aioimaplib==0.9.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e391eeb234d..e80eb4395a9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -131,7 +131,7 @@ aiohomekit==0.6.4 aiohttp_cors==0.7.0 # homeassistant.components.hue -aiohue==3.0.6 +aiohue==3.0.7 # homeassistant.components.apache_kafka aiokafka==0.6.0 From 5e0ea9fd240afa10399c7de771df06af7a7ebfc5 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Tue, 21 Dec 2021 01:09:14 +0100 Subject: [PATCH 36/36] Change Hue availability blacklist logic a bit (#62446) --- homeassistant/components/hue/v2/entity.py | 63 ++++++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/hue/v2/entity.py b/homeassistant/components/hue/v2/entity.py index ae345238c23..7371efff3bb 100644 --- a/homeassistant/components/hue/v2/entity.py +++ b/homeassistant/components/hue/v2/entity.py @@ -47,20 +47,9 @@ class HueBaseEntity(Entity): self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, self.device.id)}, ) - # some (3th party) Hue lights report their connection status incorrectly - # causing the zigbee availability to report as disconnected while in fact - # it can be controlled. Although this is in fact something the device manufacturer - # should fix, we work around it here. If the light is reported unavailable at - # startup, we ignore the availability status of the zigbee connection - self._ignore_availability = False - if self.device is None: - return - if zigbee := self.bridge.api.devices.get_zigbee_connectivity(self.device.id): - self._ignore_availability = ( - # Official Hue lights are reliable - self.device.product_data.manufacturer_name != "Signify Netherlands B.V." - and zigbee.status != ConnectivityServiceStatus.CONNECTED - ) + # used for availability workaround + self._ignore_availability = None + self._last_state = None @property def name(self) -> str: @@ -82,6 +71,7 @@ class HueBaseEntity(Entity): async def async_added_to_hass(self) -> None: """Call when entity is added.""" + self._check_availability_workaround() # Add value_changed callbacks. self.async_on_remove( self.controller.subscribe( @@ -140,5 +130,50 @@ class HueBaseEntity(Entity): ent_reg.async_remove(self.entity_id) else: self.logger.debug("Received status update for %s", self.entity_id) + self._check_availability_workaround() self.on_update() self.async_write_ha_state() + + @callback + def _check_availability_workaround(self): + """Check availability of the device.""" + if self.resource.type != ResourceTypes.LIGHT: + return + if self._ignore_availability is not None: + # already processed + return + cur_state = self.resource.on.on + if self._last_state is None: + self._last_state = cur_state + return + # some (3th party) Hue lights report their connection status incorrectly + # causing the zigbee availability to report as disconnected while in fact + # it can be controlled. Although this is in fact something the device manufacturer + # should fix, we work around it here. If the light is reported unavailable + # by the zigbee connectivity but the state changesm its considered as a + # malfunctioning device and we report it. + # while the user should actually fix this issue instead of ignoring it, we + # ignore the availability for this light from this point. + if zigbee := self.bridge.api.devices.get_zigbee_connectivity(self.device.id): + if ( + self._last_state != cur_state + and zigbee.status != ConnectivityServiceStatus.CONNECTED + ): + # the device state changed from on->off or off->on + # while it was reported as not connected! + self.logger.warning( + "Light %s changed state while reported as disconnected. " + "This is an indicator that routing is not working properly for this device. " + "Home Assistant will ignore availability for this light from now on. " + "Device details: %s - %s (%s) fw: %s", + self.name, + self.device.product_data.manufacturer_name, + self.device.product_data.product_name, + self.device.product_data.model_id, + self.device.product_data.software_version, + ) + # do we want to store this in some persistent storage? + self._ignore_availability = True + else: + self._ignore_availability = False + self._last_state = cur_state