From 7e1806229b62265ba39d16d72c9cc3846407730d Mon Sep 17 00:00:00 2001 From: Guy Shefer Date: Sat, 8 Jun 2024 23:52:15 +0300 Subject: [PATCH] Fix Tami4 component breaking API changes (#119158) * fix tami4 api breaking changes * fix tests --- homeassistant/components/tami4/__init__.py | 4 +- homeassistant/components/tami4/config_flow.py | 3 +- homeassistant/components/tami4/coordinator.py | 22 ++++------- homeassistant/components/tami4/entity.py | 6 +-- homeassistant/components/tami4/manifest.json | 2 +- homeassistant/components/tami4/sensor.py | 26 ++++--------- homeassistant/components/tami4/strings.json | 8 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tami4/conftest.py | 38 ++++++++++++------- tests/components/tami4/test_config_flow.py | 10 ++--- tests/components/tami4/test_init.py | 6 +-- 12 files changed, 63 insertions(+), 66 deletions(-) diff --git a/homeassistant/components/tami4/__init__.py b/homeassistant/components/tami4/__init__.py index 2755157214e1d2..8c597409c77a1c 100644 --- a/homeassistant/components/tami4/__init__.py +++ b/homeassistant/components/tami4/__init__.py @@ -10,7 +10,7 @@ from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady from .const import API, CONF_REFRESH_TOKEN, COORDINATOR, DOMAIN -from .coordinator import Tami4EdgeWaterQualityCoordinator +from .coordinator import Tami4EdgeCoordinator PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.SENSOR] @@ -26,7 +26,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except exceptions.TokenRefreshFailedException as ex: raise ConfigEntryNotReady("Error connecting to API") from ex - coordinator = Tami4EdgeWaterQualityCoordinator(hass, api) + coordinator = Tami4EdgeCoordinator(hass, api) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { diff --git a/homeassistant/components/tami4/config_flow.py b/homeassistant/components/tami4/config_flow.py index 83d426f47de4e5..8c1edbfb60fd67 100644 --- a/homeassistant/components/tami4/config_flow.py +++ b/homeassistant/components/tami4/config_flow.py @@ -83,7 +83,8 @@ async def async_step_otp( errors["base"] = "unknown" else: return self.async_create_entry( - title=api.device.name, data={CONF_REFRESH_TOKEN: refresh_token} + title=api.device_metadata.name, + data={CONF_REFRESH_TOKEN: refresh_token}, ) return self.async_show_form( diff --git a/homeassistant/components/tami4/coordinator.py b/homeassistant/components/tami4/coordinator.py index 78a3723a876b10..4764562bc34949 100644 --- a/homeassistant/components/tami4/coordinator.py +++ b/homeassistant/components/tami4/coordinator.py @@ -17,27 +17,23 @@ class FlattenedWaterQuality: """Flattened WaterQuality dataclass.""" - uv_last_replacement: date uv_upcoming_replacement: date - uv_status: str - filter_last_replacement: date + uv_installed: bool filter_upcoming_replacement: date - filter_status: str + filter_installed: bool filter_litters_passed: float def __init__(self, water_quality: WaterQuality) -> None: - """Flatten WaterQuality dataclass.""" + """Flattened WaterQuality dataclass.""" - self.uv_last_replacement = water_quality.uv.last_replacement self.uv_upcoming_replacement = water_quality.uv.upcoming_replacement - self.uv_status = water_quality.uv.status - self.filter_last_replacement = water_quality.filter.last_replacement + self.uv_installed = water_quality.uv.installed self.filter_upcoming_replacement = water_quality.filter.upcoming_replacement - self.filter_status = water_quality.filter.status + self.filter_installed = water_quality.filter.installed self.filter_litters_passed = water_quality.filter.milli_litters_passed / 1000 -class Tami4EdgeWaterQualityCoordinator(DataUpdateCoordinator[FlattenedWaterQuality]): +class Tami4EdgeCoordinator(DataUpdateCoordinator[FlattenedWaterQuality]): """Tami4Edge water quality coordinator.""" def __init__(self, hass: HomeAssistant, api: Tami4EdgeAPI) -> None: @@ -53,10 +49,8 @@ def __init__(self, hass: HomeAssistant, api: Tami4EdgeAPI) -> None: async def _async_update_data(self) -> FlattenedWaterQuality: """Fetch data from the API endpoint.""" try: - water_quality = await self.hass.async_add_executor_job( - self._api.get_water_quality - ) + device = await self.hass.async_add_executor_job(self._api.get_device) - return FlattenedWaterQuality(water_quality) + return FlattenedWaterQuality(device.water_quality) except exceptions.APIRequestFailedException as ex: raise UpdateFailed("Error communicating with API") from ex diff --git a/homeassistant/components/tami4/entity.py b/homeassistant/components/tami4/entity.py index d84cd82f39a8be..b99ca21663dc26 100644 --- a/homeassistant/components/tami4/entity.py +++ b/homeassistant/components/tami4/entity.py @@ -21,14 +21,14 @@ def __init__( """Initialize the Tami4Edge.""" self._state = None self._api = api - device_id = api.device.psn + device_id = api.device_metadata.psn self.entity_description = entity_description self._attr_unique_id = f"{device_id}_{self.entity_description.key}" self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, device_id)}, manufacturer="Stratuss", - name=api.device.name, + name=api.device_metadata.name, model="Tami4", - sw_version=api.device.device_firmware, + sw_version=api.device_metadata.device_firmware, suggested_area="Kitchen", ) diff --git a/homeassistant/components/tami4/manifest.json b/homeassistant/components/tami4/manifest.json index 49cbf6fe1c6ec3..e09970c341da78 100644 --- a/homeassistant/components/tami4/manifest.json +++ b/homeassistant/components/tami4/manifest.json @@ -5,5 +5,5 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tami4", "iot_class": "cloud_polling", - "requirements": ["Tami4EdgeAPI==2.1"] + "requirements": ["Tami4EdgeAPI==3.0"] } diff --git a/homeassistant/components/tami4/sensor.py b/homeassistant/components/tami4/sensor.py index 3772ef0bccbe13..888acda9372a40 100644 --- a/homeassistant/components/tami4/sensor.py +++ b/homeassistant/components/tami4/sensor.py @@ -17,30 +17,20 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import API, COORDINATOR, DOMAIN -from .coordinator import Tami4EdgeWaterQualityCoordinator +from .coordinator import Tami4EdgeCoordinator from .entity import Tami4EdgeBaseEntity _LOGGER = logging.getLogger(__name__) ENTITY_DESCRIPTIONS = [ - SensorEntityDescription( - key="uv_last_replacement", - translation_key="uv_last_replacement", - device_class=SensorDeviceClass.DATE, - ), SensorEntityDescription( key="uv_upcoming_replacement", translation_key="uv_upcoming_replacement", device_class=SensorDeviceClass.DATE, ), SensorEntityDescription( - key="uv_status", - translation_key="uv_status", - ), - SensorEntityDescription( - key="filter_last_replacement", - translation_key="filter_last_replacement", - device_class=SensorDeviceClass.DATE, + key="uv_installed", + translation_key="uv_installed", ), SensorEntityDescription( key="filter_upcoming_replacement", @@ -48,8 +38,8 @@ device_class=SensorDeviceClass.DATE, ), SensorEntityDescription( - key="filter_status", - translation_key="filter_status", + key="filter_installed", + translation_key="filter_installed", ), SensorEntityDescription( key="filter_litters_passed", @@ -67,7 +57,7 @@ async def async_setup_entry( """Perform the setup for Tami4Edge.""" data = hass.data[DOMAIN][entry.entry_id] api: Tami4EdgeAPI = data[API] - coordinator: Tami4EdgeWaterQualityCoordinator = data[COORDINATOR] + coordinator: Tami4EdgeCoordinator = data[COORDINATOR] async_add_entities( Tami4EdgeSensorEntity( @@ -81,14 +71,14 @@ async def async_setup_entry( class Tami4EdgeSensorEntity( Tami4EdgeBaseEntity, - CoordinatorEntity[Tami4EdgeWaterQualityCoordinator], + CoordinatorEntity[Tami4EdgeCoordinator], SensorEntity, ): """Representation of the entity.""" def __init__( self, - coordinator: Tami4EdgeWaterQualityCoordinator, + coordinator: Tami4EdgeCoordinator, api: Tami4EdgeAPI, entity_description: SensorEntityDescription, ) -> None: diff --git a/homeassistant/components/tami4/strings.json b/homeassistant/components/tami4/strings.json index 79447d93e9e6b3..406964a3bffd95 100644 --- a/homeassistant/components/tami4/strings.json +++ b/homeassistant/components/tami4/strings.json @@ -7,8 +7,8 @@ "uv_upcoming_replacement": { "name": "UV upcoming replacement" }, - "uv_status": { - "name": "UV status" + "uv_installed": { + "name": "UV installed" }, "filter_last_replacement": { "name": "Filter last replacement" @@ -16,8 +16,8 @@ "filter_upcoming_replacement": { "name": "Filter upcoming replacement" }, - "filter_status": { - "name": "Filter status" + "filter_installed": { + "name": "Filter installed" }, "filter_litters_passed": { "name": "Filter water passed" diff --git a/requirements_all.txt b/requirements_all.txt index 025cb02f0a3b62..00b10baacedef2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -122,7 +122,7 @@ RtmAPI==0.7.2 SQLAlchemy==2.0.30 # homeassistant.components.tami4 -Tami4EdgeAPI==2.1 +Tami4EdgeAPI==3.0 # homeassistant.components.travisci TravisPy==0.3.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 084913891a4236..dfc3cb7b6748fb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -107,7 +107,7 @@ RtmAPI==0.7.2 SQLAlchemy==2.0.30 # homeassistant.components.tami4 -Tami4EdgeAPI==2.1 +Tami4EdgeAPI==3.0 # homeassistant.components.onvif WSDiscovery==2.0.0 diff --git a/tests/components/tami4/conftest.py b/tests/components/tami4/conftest.py index 26d6e043dea32a..84b96c047359d5 100644 --- a/tests/components/tami4/conftest.py +++ b/tests/components/tami4/conftest.py @@ -5,6 +5,7 @@ import pytest from Tami4EdgeAPI.device import Device +from Tami4EdgeAPI.device_metadata import DeviceMetadata from Tami4EdgeAPI.water_quality import UV, Filter, WaterQuality from typing_extensions import Generator @@ -32,17 +33,17 @@ async def create_config_entry(hass: HomeAssistant) -> MockConfigEntry: @pytest.fixture -def mock_api(mock__get_devices, mock_get_water_quality): +def mock_api(mock__get_devices_metadata, mock_get_device): """Fixture to mock all API calls.""" @pytest.fixture -def mock__get_devices(request: pytest.FixtureRequest) -> Generator[None]: +def mock__get_devices_metadata(request: pytest.FixtureRequest) -> Generator[None]: """Fixture to mock _get_devices which makes a call to the API.""" side_effect = getattr(request, "param", None) - device = Device( + device_metadata = DeviceMetadata( id=1, name="Drink Water", connected=True, @@ -52,38 +53,49 @@ def mock__get_devices(request: pytest.FixtureRequest) -> Generator[None]: ) with patch( - "Tami4EdgeAPI.Tami4EdgeAPI.Tami4EdgeAPI._get_devices", - return_value=[device], + "Tami4EdgeAPI.Tami4EdgeAPI.Tami4EdgeAPI._get_devices_metadata", + return_value=[device_metadata], side_effect=side_effect, ): yield @pytest.fixture -def mock_get_water_quality( +def mock_get_device( request: pytest.FixtureRequest, ) -> Generator[None]: - """Fixture to mock get_water_quality which makes a call to the API.""" + """Fixture to mock get_device which makes a call to the API.""" side_effect = getattr(request, "param", None) water_quality = WaterQuality( uv=UV( - last_replacement=int(datetime.now().timestamp()), upcoming_replacement=int(datetime.now().timestamp()), - status="on", + installed=True, ), filter=Filter( - last_replacement=int(datetime.now().timestamp()), upcoming_replacement=int(datetime.now().timestamp()), - status="on", milli_litters_passed=1000, + installed=True, ), ) + device_metadata = DeviceMetadata( + id=1, + name="Drink Water", + connected=True, + psn="psn", + type="type", + device_firmware="v1.1", + ) + + device = Device( + water_quality=water_quality, device_metadata=device_metadata, drinks=[] + ) + with patch( - "Tami4EdgeAPI.Tami4EdgeAPI.Tami4EdgeAPI.get_water_quality", - return_value=water_quality, + "Tami4EdgeAPI.Tami4EdgeAPI.Tami4EdgeAPI.get_device", + return_value=device, side_effect=side_effect, ): yield diff --git a/tests/components/tami4/test_config_flow.py b/tests/components/tami4/test_config_flow.py index cf81b015254773..4210c391d70f30 100644 --- a/tests/components/tami4/test_config_flow.py +++ b/tests/components/tami4/test_config_flow.py @@ -13,7 +13,7 @@ async def test_step_user_valid_number( hass: HomeAssistant, mock_setup_entry, mock_request_otp, - mock__get_devices, + mock__get_devices_metadata, ) -> None: """Test user step with valid phone number.""" @@ -37,7 +37,7 @@ async def test_step_user_invalid_number( hass: HomeAssistant, mock_setup_entry, mock_request_otp, - mock__get_devices, + mock__get_devices_metadata, ) -> None: """Test user step with invalid phone number.""" @@ -66,7 +66,7 @@ async def test_step_user_exception( hass: HomeAssistant, mock_setup_entry, mock_request_otp, - mock__get_devices, + mock__get_devices_metadata, expected_error, ) -> None: """Test user step with exception.""" @@ -92,7 +92,7 @@ async def test_step_otp_valid( mock_setup_entry, mock_request_otp, mock_submit_otp, - mock__get_devices, + mock__get_devices_metadata, ) -> None: """Test user step with valid phone number.""" @@ -134,7 +134,7 @@ async def test_step_otp_exception( mock_setup_entry, mock_request_otp, mock_submit_otp, - mock__get_devices, + mock__get_devices_metadata, expected_error, ) -> None: """Test user step with valid phone number.""" diff --git a/tests/components/tami4/test_init.py b/tests/components/tami4/test_init.py index 2e9663c2728320..2fe16d84cdbf41 100644 --- a/tests/components/tami4/test_init.py +++ b/tests/components/tami4/test_init.py @@ -17,7 +17,7 @@ async def test_init_success(mock_api, hass: HomeAssistant) -> None: @pytest.mark.parametrize( - "mock_get_water_quality", [exceptions.APIRequestFailedException], indirect=True + "mock_get_device", [exceptions.APIRequestFailedException], indirect=True ) async def test_init_with_api_error(mock_api, hass: HomeAssistant) -> None: """Test init with api error.""" @@ -27,7 +27,7 @@ async def test_init_with_api_error(mock_api, hass: HomeAssistant) -> None: @pytest.mark.parametrize( - ("mock__get_devices", "expected_state"), + ("mock__get_devices_metadata", "expected_state"), [ ( exceptions.RefreshTokenExpiredException, @@ -38,7 +38,7 @@ async def test_init_with_api_error(mock_api, hass: HomeAssistant) -> None: ConfigEntryState.SETUP_RETRY, ), ], - indirect=["mock__get_devices"], + indirect=["mock__get_devices_metadata"], ) async def test_init_error_raised( mock_api, hass: HomeAssistant, expected_state: ConfigEntryState