Fix Tami4 component breaking API changes (#119158)
* fix tami4 api breaking changes * fix testspull/119180/head
parent
d6097573f5
commit
7e1806229b
|
@ -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] = {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
)
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue