From 3d580259e1936d85316216f8b0ac45036464ccf2 Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Fri, 28 Jun 2024 19:21:59 +1000 Subject: [PATCH] Check Tessie scopes to fix startup bug (#120710) * Add scope check * Add tests * Bump Teslemetry --- .../components/teslemetry/manifest.json | 2 +- homeassistant/components/tessie/__init__.py | 68 +++++++++++-------- homeassistant/components/tessie/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tessie/common.py | 11 +++ tests/components/tessie/conftest.py | 11 +++ tests/components/tessie/test_init.py | 14 +++- 8 files changed, 76 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/teslemetry/manifest.json b/homeassistant/components/teslemetry/manifest.json index 2eb3e22185584..49d73909a711c 100644 --- a/homeassistant/components/teslemetry/manifest.json +++ b/homeassistant/components/teslemetry/manifest.json @@ -7,5 +7,5 @@ "iot_class": "cloud_polling", "loggers": ["tesla-fleet-api"], "quality_scale": "platinum", - "requirements": ["tesla-fleet-api==0.6.1"] + "requirements": ["tesla-fleet-api==0.6.2"] } diff --git a/homeassistant/components/tessie/__init__.py b/homeassistant/components/tessie/__init__.py index e8891d6665f81..1d6e2a27786fd 100644 --- a/homeassistant/components/tessie/__init__.py +++ b/homeassistant/components/tessie/__init__.py @@ -6,6 +6,7 @@ from aiohttp import ClientError, ClientResponseError from tesla_fleet_api import EnergySpecific, Tessie +from tesla_fleet_api.const import Scope from tesla_fleet_api.exceptions import TeslaFleetError from tessie_api import get_state_of_all_vehicles @@ -94,41 +95,48 @@ async def async_setup_entry(hass: HomeAssistant, entry: TessieConfigEntry) -> bo # Energy Sites tessie = Tessie(session, api_key) + energysites: list[TessieEnergyData] = [] + try: - products = (await tessie.products())["response"] + scopes = await tessie.scopes() except TeslaFleetError as e: raise ConfigEntryNotReady from e - energysites: list[TessieEnergyData] = [] - for product in products: - if "energy_site_id" in product: - site_id = product["energy_site_id"] - api = EnergySpecific(tessie.energy, site_id) - energysites.append( - TessieEnergyData( - api=api, - id=site_id, - live_coordinator=TessieEnergySiteLiveCoordinator(hass, api), - info_coordinator=TessieEnergySiteInfoCoordinator(hass, api), - device=DeviceInfo( - identifiers={(DOMAIN, str(site_id))}, - manufacturer="Tesla", - name=product.get("site_name", "Energy Site"), - ), + if Scope.ENERGY_DEVICE_DATA in scopes: + try: + products = (await tessie.products())["response"] + except TeslaFleetError as e: + raise ConfigEntryNotReady from e + + for product in products: + if "energy_site_id" in product: + site_id = product["energy_site_id"] + api = EnergySpecific(tessie.energy, site_id) + energysites.append( + TessieEnergyData( + api=api, + id=site_id, + live_coordinator=TessieEnergySiteLiveCoordinator(hass, api), + info_coordinator=TessieEnergySiteInfoCoordinator(hass, api), + device=DeviceInfo( + identifiers={(DOMAIN, str(site_id))}, + manufacturer="Tesla", + name=product.get("site_name", "Energy Site"), + ), + ) ) - ) - - # Populate coordinator data before forwarding to platforms - await asyncio.gather( - *( - energysite.live_coordinator.async_config_entry_first_refresh() - for energysite in energysites - ), - *( - energysite.info_coordinator.async_config_entry_first_refresh() - for energysite in energysites - ), - ) + + # Populate coordinator data before forwarding to platforms + await asyncio.gather( + *( + energysite.live_coordinator.async_config_entry_first_refresh() + for energysite in energysites + ), + *( + energysite.info_coordinator.async_config_entry_first_refresh() + for energysite in energysites + ), + ) entry.runtime_data = TessieData(vehicles, energysites) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) diff --git a/homeassistant/components/tessie/manifest.json b/homeassistant/components/tessie/manifest.json index bf1ab5f61e4eb..493feeaa77eda 100644 --- a/homeassistant/components/tessie/manifest.json +++ b/homeassistant/components/tessie/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/tessie", "iot_class": "cloud_polling", "loggers": ["tessie"], - "requirements": ["tessie-api==0.0.9", "tesla-fleet-api==0.6.1"] + "requirements": ["tessie-api==0.0.9", "tesla-fleet-api==0.6.2"] } diff --git a/requirements_all.txt b/requirements_all.txt index e9be441c93f4f..f88887a732d4f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2711,7 +2711,7 @@ temperusb==1.6.1 # homeassistant.components.teslemetry # homeassistant.components.tessie -tesla-fleet-api==0.6.1 +tesla-fleet-api==0.6.2 # homeassistant.components.powerwall tesla-powerwall==0.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d9ccf11d754ab..9ab639908a6f2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2109,7 +2109,7 @@ temperusb==1.6.1 # homeassistant.components.teslemetry # homeassistant.components.tessie -tesla-fleet-api==0.6.1 +tesla-fleet-api==0.6.2 # homeassistant.components.powerwall tesla-powerwall==0.5.2 diff --git a/tests/components/tessie/common.py b/tests/components/tessie/common.py index 3d24c6b233a78..37a38fffaa480 100644 --- a/tests/components/tessie/common.py +++ b/tests/components/tessie/common.py @@ -54,6 +54,17 @@ SITE_INFO = load_json_object_fixture("site_info.json", DOMAIN) RESPONSE_OK = {"response": {}, "error": None} COMMAND_OK = {"response": {"result": True, "reason": ""}} +SCOPES = [ + "user_data", + "vehicle_device_data", + "vehicle_cmds", + "vehicle_charging_cmds", + "energy_device_data", + "energy_cmds", + "offline_access", + "openid", +] +NO_SCOPES = ["user_data", "offline_access", "openid"] async def setup_platform( diff --git a/tests/components/tessie/conftest.py b/tests/components/tessie/conftest.py index 79cc9aa44c60e..e0aba73af176e 100644 --- a/tests/components/tessie/conftest.py +++ b/tests/components/tessie/conftest.py @@ -11,6 +11,7 @@ COMMAND_OK, LIVE_STATUS, PRODUCTS, + SCOPES, SITE_INFO, TEST_STATE_OF_ALL_VEHICLES, TEST_VEHICLE_STATE_ONLINE, @@ -51,6 +52,16 @@ def mock_get_state_of_all_vehicles(): # Fleet API +@pytest.fixture(autouse=True) +def mock_scopes(): + """Mock scopes function.""" + with patch( + "homeassistant.components.tessie.Tessie.scopes", + return_value=SCOPES, + ) as mock_scopes: + yield mock_scopes + + @pytest.fixture(autouse=True) def mock_products(): """Mock Tesla Fleet Api products method.""" diff --git a/tests/components/tessie/test_init.py b/tests/components/tessie/test_init.py index e37512ea8c48d..921ef93b1ae24 100644 --- a/tests/components/tessie/test_init.py +++ b/tests/components/tessie/test_init.py @@ -50,11 +50,21 @@ async def test_connection_failure( assert entry.state is ConfigEntryState.SETUP_RETRY -async def test_fleet_error(hass: HomeAssistant) -> None: - """Test init with a fleet error.""" +async def test_products_error(hass: HomeAssistant) -> None: + """Test init with a fleet error on products.""" with patch( "homeassistant.components.tessie.Tessie.products", side_effect=TeslaFleetError ): entry = await setup_platform(hass) assert entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_scopes_error(hass: HomeAssistant) -> None: + """Test init with a fleet error on scopes.""" + + with patch( + "homeassistant.components.tessie.Tessie.scopes", side_effect=TeslaFleetError + ): + entry = await setup_platform(hass) + assert entry.state is ConfigEntryState.SETUP_RETRY