Fix Tami4 component breaking API changes (#119158)

* fix tami4 api breaking changes

* fix tests
pull/119180/head
Guy Shefer 2024-06-08 23:52:15 +03:00 committed by GitHub
parent d6097573f5
commit 7e1806229b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 63 additions and 66 deletions

View File

@ -10,7 +10,7 @@ from homeassistant.core import HomeAssistant
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] = {

View File

@ -83,7 +83,8 @@ class Tami4ConfigFlow(ConfigFlow, domain=DOMAIN):
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(

View File

@ -17,27 +17,23 @@ _LOGGER = logging.getLogger(__name__)
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 @@ class Tami4EdgeWaterQualityCoordinator(DataUpdateCoordinator[FlattenedWaterQuali
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

View File

@ -21,14 +21,14 @@ class Tami4EdgeBaseEntity(Entity):
"""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",
)

View File

@ -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"]
}

View File

@ -17,30 +17,20 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
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 @@ ENTITY_DESCRIPTIONS = [
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:

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
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

View File

@ -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."""

View File

@ -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