From 6df4fc67080050236a5a83897b42d011723e7278 Mon Sep 17 00:00:00 2001 From: Milan Meulemans Date: Thu, 12 Jan 2023 00:43:04 +0100 Subject: [PATCH 01/11] Upgrade aionanoleaf to 0.2.1 (#83669) --- homeassistant/components/nanoleaf/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nanoleaf/manifest.json b/homeassistant/components/nanoleaf/manifest.json index 1ce3210a206..8cc690f4fc0 100644 --- a/homeassistant/components/nanoleaf/manifest.json +++ b/homeassistant/components/nanoleaf/manifest.json @@ -3,7 +3,7 @@ "name": "Nanoleaf", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/nanoleaf", - "requirements": ["aionanoleaf==0.2.0"], + "requirements": ["aionanoleaf==0.2.1"], "zeroconf": ["_nanoleafms._tcp.local.", "_nanoleafapi._tcp.local."], "homekit": { "models": ["NL29", "NL42", "NL47", "NL48", "NL52", "NL59"] diff --git a/requirements_all.txt b/requirements_all.txt index 111afd8cf3c..a391b7f1b3e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -220,7 +220,7 @@ aiomodernforms==0.1.8 aiomusiccast==0.14.4 # homeassistant.components.nanoleaf -aionanoleaf==0.2.0 +aionanoleaf==0.2.1 # homeassistant.components.keyboard_remote aionotify==0.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0845fd0913b..ad63662ba8c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -198,7 +198,7 @@ aiomodernforms==0.1.8 aiomusiccast==0.14.4 # homeassistant.components.nanoleaf -aionanoleaf==0.2.0 +aionanoleaf==0.2.1 # homeassistant.components.notion aionotion==3.0.2 From c3859f91706a7fdbbded2fc3505f5149cf572b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Thu, 12 Jan 2023 07:47:38 +0200 Subject: [PATCH 02/11] Improve Huawei LTE SSDP inclusion (#85572) * Probe Huawei LTE API for device support on SSDP match More or less as expected, the loosening of SSDP/UPnP data matches done in #81643 started to yield false positives, as in #85402. Coming up with robust matches solely based on the SSDP/UPnP data still does not seem possible, so keep the matches as loose as they were made, but additionally invoke a probe request on the API to determine if the device looks like a supported one. * Probe only after unique id checks Prevents throwaway probes for discoveries already in progress. * Fix SSDP result URL test, add missing assert on it --- .../components/huawei_lte/config_flow.py | 18 +++++++++ .../components/huawei_lte/strings.json | 3 +- .../huawei_lte/translations/en.json | 4 +- .../components/huawei_lte/test_config_flow.py | 37 ++++++++++++++++--- 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/huawei_lte/config_flow.py b/homeassistant/components/huawei_lte/config_flow.py index d1ab0547801..2319cc9a3ed 100644 --- a/homeassistant/components/huawei_lte/config_flow.py +++ b/homeassistant/components/huawei_lte/config_flow.py @@ -250,6 +250,24 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await self.async_set_unique_id(unique_id) self._abort_if_unique_id_configured(updates={CONF_URL: url}) + def _is_supported_device() -> bool: + """ + See if we are looking at a possibly supported device. + + Matching solely on SSDP data does not yield reliable enough results. + """ + try: + with Connection(url=url, timeout=CONNECTION_TIMEOUT) as conn: + basic_info = Client(conn).device.basic_information() + except ResponseErrorException: # API compatible error + return True + except Exception: # API incompatible error # pylint: disable=broad-except + return False + return isinstance(basic_info, dict) # Crude content check + + if not await self.hass.async_add_executor_job(_is_supported_device): + return self.async_abort(reason="unsupported_device") + self.context.update( { "title_placeholders": { diff --git a/homeassistant/components/huawei_lte/strings.json b/homeassistant/components/huawei_lte/strings.json index dbc30510d13..3875433888d 100644 --- a/homeassistant/components/huawei_lte/strings.json +++ b/homeassistant/components/huawei_lte/strings.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", + "unsupported_device": "Unsupported device" }, "error": { "connection_timeout": "Connection timeout", diff --git a/homeassistant/components/huawei_lte/translations/en.json b/homeassistant/components/huawei_lte/translations/en.json index 134a5372f71..42d28a26871 100644 --- a/homeassistant/components/huawei_lte/translations/en.json +++ b/homeassistant/components/huawei_lte/translations/en.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "not_huawei_lte": "Not a Huawei LTE device", - "reauth_successful": "Re-authentication was successful" + "reauth_successful": "Re-authentication was successful", + "unsupported_device": "Unsupported device" }, "error": { "connection_timeout": "Connection timeout", diff --git a/tests/components/huawei_lte/test_config_flow.py b/tests/components/huawei_lte/test_config_flow.py index d29c0554bca..9e723204b33 100644 --- a/tests/components/huawei_lte/test_config_flow.py +++ b/tests/components/huawei_lte/test_config_flow.py @@ -211,9 +211,14 @@ async def test_success(hass, login_requests_mock): @pytest.mark.parametrize( - ("upnp_data", "expected_result"), + ("requests_mock_request_kwargs", "upnp_data", "expected_result"), ( ( + { + "method": ANY, + "url": f"{FIXTURE_USER_INPUT[CONF_URL]}api/device/basic_information", + "text": "Mock device", + }, { ssdp.ATTR_UPNP_FRIENDLY_NAME: "Mobile Wi-Fi", ssdp.ATTR_UPNP_SERIAL: "00000000", @@ -225,6 +230,11 @@ async def test_success(hass, login_requests_mock): }, ), ( + { + "method": ANY, + "url": f"{FIXTURE_USER_INPUT[CONF_URL]}api/device/basic_information", + "text": "100002", + }, { ssdp.ATTR_UPNP_FRIENDLY_NAME: "Mobile Wi-Fi", # No ssdp.ATTR_UPNP_SERIAL @@ -235,19 +245,36 @@ async def test_success(hass, login_requests_mock): "errors": {}, }, ), + ( + { + "method": ANY, + "url": f"{FIXTURE_USER_INPUT[CONF_URL]}api/device/basic_information", + "exc": Exception("Something unexpected"), + }, + { + # Does not matter + }, + { + "type": data_entry_flow.FlowResultType.ABORT, + "reason": "unsupported_device", + }, + ), ), ) -async def test_ssdp(hass, upnp_data, expected_result): +async def test_ssdp( + hass, login_requests_mock, requests_mock_request_kwargs, upnp_data, expected_result +): """Test SSDP discovery initiates config properly.""" - url = "http://192.168.100.1/" + url = FIXTURE_USER_INPUT[CONF_URL][:-1] # strip trailing slash for appending port context = {"source": config_entries.SOURCE_SSDP} + login_requests_mock.request(**requests_mock_request_kwargs) result = await hass.config_entries.flow.async_init( DOMAIN, context=context, data=ssdp.SsdpServiceInfo( ssdp_usn="mock_usn", ssdp_st="upnp:rootdevice", - ssdp_location="http://192.168.100.1:60957/rootDesc.xml", + ssdp_location=f"{url}:60957/rootDesc.xml", upnp={ ssdp.ATTR_UPNP_DEVICE_TYPE: "urn:schemas-upnp-org:device:InternetGatewayDevice:1", ssdp.ATTR_UPNP_MANUFACTURER: "Huawei", @@ -264,7 +291,7 @@ async def test_ssdp(hass, upnp_data, expected_result): for k, v in expected_result.items(): assert result[k] == v if result.get("data_schema"): - result["data_schema"]({})[CONF_URL] == url + assert result["data_schema"]({})[CONF_URL] == url + "/" @pytest.mark.parametrize( From 1e852e761c4e79897ca8d1ceda8c8ca88d4a8deb Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Wed, 11 Jan 2023 22:02:02 -0500 Subject: [PATCH 03/11] Remove oauth2client dependency in Google Sheets (#85637) Remove oauth2client dependency --- .../google_sheets/application_credentials.py | 10 +++----- .../google_sheets/test_config_flow.py | 23 ++++++++++--------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/google_sheets/application_credentials.py b/homeassistant/components/google_sheets/application_credentials.py index 415ab5947bf..f10f6891125 100644 --- a/homeassistant/components/google_sheets/application_credentials.py +++ b/homeassistant/components/google_sheets/application_credentials.py @@ -1,6 +1,4 @@ """application_credentials platform for Google Sheets.""" -import oauth2client - from homeassistant.components.application_credentials import AuthorizationServer from homeassistant.core import HomeAssistant @@ -8,17 +6,15 @@ from homeassistant.core import HomeAssistant async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer: """Return authorization server.""" return AuthorizationServer( - oauth2client.GOOGLE_AUTH_URI, - oauth2client.GOOGLE_TOKEN_URI, + "https://accounts.google.com/o/oauth2/v2/auth", + "https://oauth2.googleapis.com/token", ) async def async_get_description_placeholders(hass: HomeAssistant) -> dict[str, str]: """Return description placeholders for the credentials dialog.""" return { - "oauth_consent_url": ( - "https://console.cloud.google.com/apis/credentials/consent" - ), + "oauth_consent_url": "https://console.cloud.google.com/apis/credentials/consent", "more_info_url": "https://www.home-assistant.io/integrations/google_sheets/", "oauth_creds_url": "https://console.cloud.google.com/apis/credentials", } diff --git a/tests/components/google_sheets/test_config_flow.py b/tests/components/google_sheets/test_config_flow.py index e74602dc8a1..7f434e19953 100644 --- a/tests/components/google_sheets/test_config_flow.py +++ b/tests/components/google_sheets/test_config_flow.py @@ -4,7 +4,6 @@ from collections.abc import Generator from unittest.mock import Mock, patch from gspread import GSpreadException -import oauth2client import pytest from homeassistant import config_entries @@ -21,6 +20,8 @@ from tests.common import MockConfigEntry CLIENT_ID = "1234" CLIENT_SECRET = "5678" +GOOGLE_AUTH_URI = "https://accounts.google.com/o/oauth2/v2/auth" +GOOGLE_TOKEN_URI = "https://oauth2.googleapis.com/token" SHEET_ID = "google-sheet-id" TITLE = "Google Sheets" @@ -66,7 +67,7 @@ async def test_full_flow( ) assert result["url"] == ( - f"{oauth2client.GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" + f"{GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" "&redirect_uri=https://example.com/auth/external/callback" f"&state={state}&scope=https://www.googleapis.com/auth/drive.file" "&access_type=offline&prompt=consent" @@ -83,7 +84,7 @@ async def test_full_flow( mock_client.return_value.create = mock_create aioclient_mock.post( - oauth2client.GOOGLE_TOKEN_URI, + GOOGLE_TOKEN_URI, json={ "refresh_token": "mock-refresh-token", "access_token": "mock-access-token", @@ -133,7 +134,7 @@ async def test_create_sheet_error( ) assert result["url"] == ( - f"{oauth2client.GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" + f"{GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" "&redirect_uri=https://example.com/auth/external/callback" f"&state={state}&scope=https://www.googleapis.com/auth/drive.file" "&access_type=offline&prompt=consent" @@ -150,7 +151,7 @@ async def test_create_sheet_error( mock_client.return_value.create = mock_create aioclient_mock.post( - oauth2client.GOOGLE_TOKEN_URI, + GOOGLE_TOKEN_URI, json={ "refresh_token": "mock-refresh-token", "access_token": "mock-access-token", @@ -202,7 +203,7 @@ async def test_reauth( }, ) assert result["url"] == ( - f"{oauth2client.GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" + f"{GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" "&redirect_uri=https://example.com/auth/external/callback" f"&state={state}&scope=https://www.googleapis.com/auth/drive.file" "&access_type=offline&prompt=consent" @@ -218,7 +219,7 @@ async def test_reauth( mock_client.return_value.open_by_key = mock_open aioclient_mock.post( - oauth2client.GOOGLE_TOKEN_URI, + GOOGLE_TOKEN_URI, json={ "refresh_token": "mock-refresh-token", "access_token": "updated-access-token", @@ -283,7 +284,7 @@ async def test_reauth_abort( }, ) assert result["url"] == ( - f"{oauth2client.GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" + f"{GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" "&redirect_uri=https://example.com/auth/external/callback" f"&state={state}&scope=https://www.googleapis.com/auth/drive.file" "&access_type=offline&prompt=consent" @@ -300,7 +301,7 @@ async def test_reauth_abort( mock_client.return_value.open_by_key = mock_open aioclient_mock.post( - oauth2client.GOOGLE_TOKEN_URI, + GOOGLE_TOKEN_URI, json={ "refresh_token": "mock-refresh-token", "access_token": "updated-access-token", @@ -346,7 +347,7 @@ async def test_already_configured( ) assert result["url"] == ( - f"{oauth2client.GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" + f"{GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" "&redirect_uri=https://example.com/auth/external/callback" f"&state={state}&scope=https://www.googleapis.com/auth/drive.file" "&access_type=offline&prompt=consent" @@ -363,7 +364,7 @@ async def test_already_configured( mock_client.return_value.create = mock_create aioclient_mock.post( - oauth2client.GOOGLE_TOKEN_URI, + GOOGLE_TOKEN_URI, json={ "refresh_token": "mock-refresh-token", "access_token": "mock-access-token", From d0f95d84b4601cc5b4990d6d82d9448936a36255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Wed, 11 Jan 2023 18:44:55 +0200 Subject: [PATCH 04/11] Upgrade huawei-lte-api to 1.6.11 (#85669) --- homeassistant/components/huawei_lte/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/huawei_lte/manifest.json b/homeassistant/components/huawei_lte/manifest.json index f0997e2e165..1bd81536aa5 100644 --- a/homeassistant/components/huawei_lte/manifest.json +++ b/homeassistant/components/huawei_lte/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/huawei_lte", "requirements": [ - "huawei-lte-api==1.6.7", + "huawei-lte-api==1.6.11", "stringcase==1.2.0", "url-normalize==1.4.3" ], diff --git a/requirements_all.txt b/requirements_all.txt index a391b7f1b3e..fa67309f57c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -906,7 +906,7 @@ horimote==0.4.1 httplib2==0.20.4 # homeassistant.components.huawei_lte -huawei-lte-api==1.6.7 +huawei-lte-api==1.6.11 # homeassistant.components.hydrawise hydrawiser==0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ad63662ba8c..e6e1e73ed89 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -683,7 +683,7 @@ homepluscontrol==0.0.5 httplib2==0.20.4 # homeassistant.components.huawei_lte -huawei-lte-api==1.6.7 +huawei-lte-api==1.6.11 # homeassistant.components.hyperion hyperion-py==0.7.5 From 45d14739c59743cbf9f58c2e49f1b0559ce6543e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Thu, 12 Jan 2023 03:15:28 +0100 Subject: [PATCH 05/11] Update pyTibber to 0.26.8 (#85702) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Tibber, update pyTibber Signed-off-by: Daniel Hjelseth Høyer * Tibber, update pyTibber Signed-off-by: Daniel Hjelseth Høyer Signed-off-by: Daniel Hjelseth Høyer --- homeassistant/components/tibber/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tibber/manifest.json b/homeassistant/components/tibber/manifest.json index 2082d6ddf30..115f3ed7d2e 100644 --- a/homeassistant/components/tibber/manifest.json +++ b/homeassistant/components/tibber/manifest.json @@ -3,7 +3,7 @@ "domain": "tibber", "name": "Tibber", "documentation": "https://www.home-assistant.io/integrations/tibber", - "requirements": ["pyTibber==0.26.7"], + "requirements": ["pyTibber==0.26.8"], "codeowners": ["@danielhiversen"], "quality_scale": "silver", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index fa67309f57c..85609083f51 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1439,7 +1439,7 @@ pyRFXtrx==0.30.0 pySwitchmate==0.5.1 # homeassistant.components.tibber -pyTibber==0.26.7 +pyTibber==0.26.8 # homeassistant.components.dlink pyW215==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e6e1e73ed89..bf9b37e4540 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1039,7 +1039,7 @@ pyMetno==0.9.0 pyRFXtrx==0.30.0 # homeassistant.components.tibber -pyTibber==0.26.7 +pyTibber==0.26.8 # homeassistant.components.nextbus py_nextbusnext==0.1.5 From 2789747b0f153152248cade446550b3aa45a7338 Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Mon, 9 Jan 2023 03:58:06 -0700 Subject: [PATCH 06/11] Bump pylitterbot to 2023.1.0 (#85484) --- homeassistant/components/litterrobot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/litterrobot/manifest.json b/homeassistant/components/litterrobot/manifest.json index f81e663f302..ea656a3488e 100644 --- a/homeassistant/components/litterrobot/manifest.json +++ b/homeassistant/components/litterrobot/manifest.json @@ -3,7 +3,7 @@ "name": "Litter-Robot", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/litterrobot", - "requirements": ["pylitterbot==2022.12.0"], + "requirements": ["pylitterbot==2023.1.0"], "codeowners": ["@natekspencer", "@tkdrob"], "dhcp": [{ "hostname": "litter-robot4" }], "iot_class": "cloud_push", diff --git a/requirements_all.txt b/requirements_all.txt index 85609083f51..1c78073896e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1719,7 +1719,7 @@ pylibrespot-java==0.1.1 pylitejet==0.3.0 # homeassistant.components.litterrobot -pylitterbot==2022.12.0 +pylitterbot==2023.1.0 # homeassistant.components.lutron_caseta pylutron-caseta==0.17.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bf9b37e4540..ab21e7c4deb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1220,7 +1220,7 @@ pylibrespot-java==0.1.1 pylitejet==0.3.0 # homeassistant.components.litterrobot -pylitterbot==2022.12.0 +pylitterbot==2023.1.0 # homeassistant.components.lutron_caseta pylutron-caseta==0.17.1 From 856f68252b1e2ad5f33c74e9ed92afb437cb752e Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Wed, 28 Dec 2022 12:30:48 -0700 Subject: [PATCH 07/11] Use built in polling for litterrobot update entity (#84678) * Use built in polling * Define scan interval --- .../components/litterrobot/update.py | 44 +++++-------------- 1 file changed, 10 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/litterrobot/update.py b/homeassistant/components/litterrobot/update.py index d6475ea486b..2ed46220a8c 100644 --- a/homeassistant/components/litterrobot/update.py +++ b/homeassistant/components/litterrobot/update.py @@ -1,8 +1,7 @@ """Support for Litter-Robot updates.""" from __future__ import annotations -from collections.abc import Callable -from datetime import datetime, timedelta +from datetime import timedelta from typing import Any from pylitterbot import LitterRobot4 @@ -17,12 +16,12 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.event import async_call_later -from homeassistant.helpers.start import async_at_start from .const import DOMAIN from .entity import LitterRobotEntity, LitterRobotHub +SCAN_INTERVAL = timedelta(days=1) + FIRMWARE_UPDATE_ENTITY = UpdateEntityDescription( key="firmware", name="Firmware", @@ -43,7 +42,7 @@ async def async_setup_entry( for robot in robots if isinstance(robot, LitterRobot4) ] - async_add_entities(entities) + async_add_entities(entities, True) class RobotUpdateEntity(LitterRobotEntity[LitterRobot4], UpdateEntity): @@ -53,16 +52,6 @@ class RobotUpdateEntity(LitterRobotEntity[LitterRobot4], UpdateEntity): UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS ) - def __init__( - self, - robot: LitterRobot4, - hub: LitterRobotHub, - description: UpdateEntityDescription, - ) -> None: - """Initialize a Litter-Robot update entity.""" - super().__init__(robot, hub, description) - self._poll_unsub: Callable[[], None] | None = None - @property def installed_version(self) -> str: """Version installed and in use.""" @@ -73,10 +62,13 @@ class RobotUpdateEntity(LitterRobotEntity[LitterRobot4], UpdateEntity): """Update installation progress.""" return self.robot.firmware_update_triggered - async def _async_update(self, _: HomeAssistant | datetime | None = None) -> None: - """Update the entity.""" - self._poll_unsub = None + @property + def should_poll(self) -> bool: + """Set polling to True.""" + return True + async def async_update(self) -> None: + """Update the entity.""" if await self.robot.has_firmware_update(): latest_version = await self.robot.get_latest_firmware() else: @@ -84,16 +76,6 @@ class RobotUpdateEntity(LitterRobotEntity[LitterRobot4], UpdateEntity): if self._attr_latest_version != self.installed_version: self._attr_latest_version = latest_version - self.async_write_ha_state() - - self._poll_unsub = async_call_later( - self.hass, timedelta(days=1), self._async_update - ) - - async def async_added_to_hass(self) -> None: - """Set up a listener for the entity.""" - await super().async_added_to_hass() - self.async_on_remove(async_at_start(self.hass, self._async_update)) async def async_install( self, version: str | None, backup: bool, **kwargs: Any @@ -103,9 +85,3 @@ class RobotUpdateEntity(LitterRobotEntity[LitterRobot4], UpdateEntity): if not await self.robot.update_firmware(): message = f"Unable to start firmware update on {self.robot.name}" raise HomeAssistantError(message) - - async def async_will_remove_from_hass(self) -> None: - """Call when entity will be removed.""" - if self._poll_unsub: - self._poll_unsub() - self._poll_unsub = None From 2e9ea0c934b4cb518817acca7bad1fd8571d96e3 Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Wed, 11 Jan 2023 19:53:06 -0700 Subject: [PATCH 08/11] Fix Litter-Robot 4 firmware versions reported while updating (#85710) --- .../components/litterrobot/manifest.json | 2 +- .../components/litterrobot/update.py | 15 +++++---- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/litterrobot/test_update.py | 33 ++++++++++++++++++- 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/litterrobot/manifest.json b/homeassistant/components/litterrobot/manifest.json index ea656a3488e..a6c392f4f62 100644 --- a/homeassistant/components/litterrobot/manifest.json +++ b/homeassistant/components/litterrobot/manifest.json @@ -3,7 +3,7 @@ "name": "Litter-Robot", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/litterrobot", - "requirements": ["pylitterbot==2023.1.0"], + "requirements": ["pylitterbot==2023.1.1"], "codeowners": ["@natekspencer", "@tkdrob"], "dhcp": [{ "hostname": "litter-robot4" }], "iot_class": "cloud_push", diff --git a/homeassistant/components/litterrobot/update.py b/homeassistant/components/litterrobot/update.py index 2ed46220a8c..845b42efaee 100644 --- a/homeassistant/components/litterrobot/update.py +++ b/homeassistant/components/litterrobot/update.py @@ -69,19 +69,20 @@ class RobotUpdateEntity(LitterRobotEntity[LitterRobot4], UpdateEntity): async def async_update(self) -> None: """Update the entity.""" - if await self.robot.has_firmware_update(): - latest_version = await self.robot.get_latest_firmware() - else: - latest_version = self.installed_version - - if self._attr_latest_version != self.installed_version: + # If the robot has a firmware update already in progress, checking for the + # latest firmware informs that an update has already been triggered, no + # firmware information is returned and we won't know the latest version. + if not self.robot.firmware_update_triggered: + latest_version = await self.robot.get_latest_firmware(True) + if not await self.robot.has_firmware_update(): + latest_version = self.robot.firmware self._attr_latest_version = latest_version async def async_install( self, version: str | None, backup: bool, **kwargs: Any ) -> None: """Install an update.""" - if await self.robot.has_firmware_update(): + if await self.robot.has_firmware_update(True): if not await self.robot.update_firmware(): message = f"Unable to start firmware update on {self.robot.name}" raise HomeAssistantError(message) diff --git a/requirements_all.txt b/requirements_all.txt index 1c78073896e..91f8a093284 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1719,7 +1719,7 @@ pylibrespot-java==0.1.1 pylitejet==0.3.0 # homeassistant.components.litterrobot -pylitterbot==2023.1.0 +pylitterbot==2023.1.1 # homeassistant.components.lutron_caseta pylutron-caseta==0.17.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ab21e7c4deb..e7b260a5b67 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1220,7 +1220,7 @@ pylibrespot-java==0.1.1 pylitejet==0.3.0 # homeassistant.components.litterrobot -pylitterbot==2023.1.0 +pylitterbot==2023.1.1 # homeassistant.components.lutron_caseta pylutron-caseta==0.17.1 diff --git a/tests/components/litterrobot/test_update.py b/tests/components/litterrobot/test_update.py index f4311992c8e..4940ec64824 100644 --- a/tests/components/litterrobot/test_update.py +++ b/tests/components/litterrobot/test_update.py @@ -11,7 +11,13 @@ from homeassistant.components.update import ( SERVICE_INSTALL, UpdateDeviceClass, ) -from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, STATE_OFF, STATE_ON +from homeassistant.const import ( + ATTR_DEVICE_CLASS, + ATTR_ENTITY_ID, + STATE_OFF, + STATE_ON, + STATE_UNKNOWN, +) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError @@ -28,6 +34,7 @@ async def test_robot_with_no_update( """Tests the update entity was set up.""" robot: LitterRobot4 = mock_account_with_litterrobot_4.robots[0] robot.has_firmware_update = AsyncMock(return_value=False) + robot.get_latest_firmware = AsyncMock(return_value=None) entry = await setup_integration( hass, mock_account_with_litterrobot_4, PLATFORM_DOMAIN @@ -79,3 +86,27 @@ async def test_robot_with_update( ) await hass.async_block_till_done() assert robot.update_firmware.call_count == 1 + + +async def test_robot_with_update_already_in_progress( + hass: HomeAssistant, mock_account_with_litterrobot_4: MagicMock +): + """Tests the update entity was set up.""" + robot: LitterRobot4 = mock_account_with_litterrobot_4.robots[0] + robot._update_data( # pylint:disable=protected-access + {"isFirmwareUpdateTriggered": True}, partial=True + ) + + entry = await setup_integration( + hass, mock_account_with_litterrobot_4, PLATFORM_DOMAIN + ) + + state = hass.states.get(ENTITY_ID) + assert state + assert state.state == STATE_UNKNOWN + assert state.attributes[ATTR_DEVICE_CLASS] == UpdateDeviceClass.FIRMWARE + assert state.attributes[ATTR_INSTALLED_VERSION] == OLD_FIRMWARE + assert state.attributes[ATTR_LATEST_VERSION] is None + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() From 32fc0e03a50e2d711210ee437a11d19889051e22 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 12 Jan 2023 11:59:44 +0100 Subject: [PATCH 09/11] Use jemalloc in Docker builds (#85738) --- Dockerfile | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 03bd9131ea0..b80e86fb33c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,22 +11,45 @@ WORKDIR /usr/src COPY requirements.txt homeassistant/ COPY homeassistant/package_constraints.txt homeassistant/homeassistant/ RUN \ - pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \ - -r homeassistant/requirements.txt --use-deprecated=legacy-resolver + pip3 install \ + --no-cache-dir \ + --no-index \ + --only-binary=:all: \ + --find-links "${WHEELS_LINKS}" \ + --use-deprecated=legacy-resolver \ + -r homeassistant/requirements.txt + COPY requirements_all.txt home_assistant_frontend-* homeassistant/ RUN \ if ls homeassistant/home_assistant_frontend*.whl 1> /dev/null 2>&1; then \ - pip3 install --no-cache-dir --no-index homeassistant/home_assistant_frontend-*.whl; \ + pip3 install \ + --no-cache-dir \ + --no-index \ + homeassistant/home_assistant_frontend-*.whl; \ fi \ - && pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \ - -r homeassistant/requirements_all.txt --use-deprecated=legacy-resolver + && \ + LD_PRELOAD="/usr/local/lib/libjemalloc.so.2" \ + MALLOC_CONF="background_thread:true,metadata_thp:auto,dirty_decay_ms:20000,muzzy_decay_ms:20000" \ + pip3 install \ + --no-cache-dir \ + --no-index \ + --only-binary=:all: \ + --find-links "${WHEELS_LINKS}" \ + --use-deprecated=legacy-resolver \ + -r homeassistant/requirements_all.txt ## Setup Home Assistant Core COPY . homeassistant/ RUN \ - pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \ - -e ./homeassistant --use-deprecated=legacy-resolver \ - && python3 -m compileall homeassistant/homeassistant + pip3 install \ + --no-cache-dir \ + --no-index \ + --only-binary=:all: \ + --find-links "${WHEELS_LINKS}" \ + --use-deprecated=legacy-resolver \ + -e ./homeassistant \ + && python3 -m compileall \ + homeassistant/homeassistant # Home Assistant S6-Overlay COPY rootfs / From 197634503fac8792dd4f186fb4e2a5713ceb57f2 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Thu, 12 Jan 2023 20:11:27 +0200 Subject: [PATCH 10/11] Bump aiowebostv to 0.3.0 (#85756) --- 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 2547663be28..b4e761b067a 100644 --- a/homeassistant/components/webostv/manifest.json +++ b/homeassistant/components/webostv/manifest.json @@ -3,7 +3,7 @@ "name": "LG webOS Smart TV", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/webostv", - "requirements": ["aiowebostv==0.2.1"], + "requirements": ["aiowebostv==0.3.0"], "codeowners": ["@bendavid", "@thecode"], "ssdp": [{ "st": "urn:lge-com:service:webos-second-screen:1" }], "quality_scale": "platinum", diff --git a/requirements_all.txt b/requirements_all.txt index 91f8a093284..8e6e063ed8c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -297,7 +297,7 @@ aiovlc==0.1.0 aiowatttime==0.1.1 # homeassistant.components.webostv -aiowebostv==0.2.1 +aiowebostv==0.3.0 # homeassistant.components.yandex_transport aioymaps==1.2.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e7b260a5b67..c6e06374bcb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -272,7 +272,7 @@ aiovlc==0.1.0 aiowatttime==0.1.1 # homeassistant.components.webostv -aiowebostv==0.2.1 +aiowebostv==0.3.0 # homeassistant.components.yandex_transport aioymaps==1.2.2 From 6581bad7ce89ab429d3e6829b3f8d301cfc185b4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 12 Jan 2023 13:20:23 -0500 Subject: [PATCH 11/11] Bumped version to 2023.1.4 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index d7bd1fece49..2e8992e14b1 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2023 MINOR_VERSION: Final = 1 -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, 9, 0) diff --git a/pyproject.toml b/pyproject.toml index e61e5b6e824..ec14caa7515 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2023.1.3" +version = "2023.1.4" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst"