Skip to content

Commit

Permalink
Fix Tami4 component breaking API changes (#119158)
Browse files Browse the repository at this point in the history
* fix tami4 api breaking changes

* fix tests
  • Loading branch information
Guy293 committed Jun 8, 2024
1 parent d609757 commit 7e18062
Show file tree
Hide file tree
Showing 12 changed files with 63 additions and 66 deletions.
4 changes: 2 additions & 2 deletions homeassistant/components/tami4/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand All @@ -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] = {
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/tami4/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
22 changes: 8 additions & 14 deletions homeassistant/components/tami4/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
6 changes: 3 additions & 3 deletions homeassistant/components/tami4/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
)
2 changes: 1 addition & 1 deletion homeassistant/components/tami4/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
26 changes: 8 additions & 18 deletions homeassistant/components/tami4/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,29 @@
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",
translation_key="filter_upcoming_replacement",
device_class=SensorDeviceClass.DATE,
),
SensorEntityDescription(
key="filter_status",
translation_key="filter_status",
key="filter_installed",
translation_key="filter_installed",
),
SensorEntityDescription(
key="filter_litters_passed",
Expand All @@ -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(
Expand All @@ -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:
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/tami4/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@
"uv_upcoming_replacement": {
"name": "UV upcoming replacement"
},
"uv_status": {
"name": "UV status"
"uv_installed": {
"name": "UV installed"
},
"filter_last_replacement": {
"name": "Filter last replacement"
},
"filter_upcoming_replacement": {
"name": "Filter upcoming replacement"
},
"filter_status": {
"name": "Filter status"
"filter_installed": {
"name": "Filter installed"
},
"filter_litters_passed": {
"name": "Filter water passed"
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 25 additions & 13 deletions tests/components/tami4/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down
10 changes: 5 additions & 5 deletions tests/components/tami4/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

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

Expand Down Expand Up @@ -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."""
Expand All @@ -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."""

Expand Down Expand Up @@ -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."""
Expand Down
6 changes: 3 additions & 3 deletions tests/components/tami4/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand All @@ -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,
Expand All @@ -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
Expand Down

0 comments on commit 7e18062

Please sign in to comment.