Bump PySuez to 1.3.1 (#129825)

This commit is contained in:
jb101010-2 2024-11-07 14:25:38 +01:00 committed by GitHub
parent a3ba7803db
commit 0e324c074a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 88 additions and 151 deletions

View File

@ -5,8 +5,7 @@ from __future__ import annotations
import logging
from typing import Any
from pysuez import SuezClient
from pysuez.client import PySuezError
from pysuez import PySuezError, SuezClient
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
@ -26,7 +25,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
)
def validate_input(data: dict[str, Any]) -> None:
async def validate_input(data: dict[str, Any]) -> None:
"""Validate the user input allows us to connect.
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
@ -36,9 +35,8 @@ def validate_input(data: dict[str, Any]) -> None:
data[CONF_USERNAME],
data[CONF_PASSWORD],
data[CONF_COUNTER_ID],
provider=None,
)
if not client.check_credentials():
if not await client.check_credentials():
raise InvalidAuth
except PySuezError as ex:
raise CannotConnect from ex
@ -58,7 +56,7 @@ class SuezWaterConfigFlow(ConfigFlow, domain=DOMAIN):
await self.async_set_unique_id(user_input[CONF_USERNAME])
self._abort_if_unique_id_configured()
try:
await self.hass.async_add_executor_job(validate_input, user_input)
await validate_input(user_input)
except CannotConnect:
errors["base"] = "cannot_connect"
except InvalidAuth:

View File

@ -1,39 +1,20 @@
"""Suez water update coordinator."""
import asyncio
from dataclasses import dataclass
from datetime import date
from pysuez import SuezClient
from pysuez.client import PySuezError
from pysuez import AggregatedData, PySuezError, SuezClient
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import _LOGGER, HomeAssistant
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
from homeassistant.exceptions import ConfigEntryError
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_COUNTER_ID, DATA_REFRESH_INTERVAL, DOMAIN
@dataclass
class AggregatedSensorData:
"""Hold suez water aggregated sensor data."""
value: float
current_month: dict[date, float]
previous_month: dict[date, float]
previous_year: dict[str, float]
current_year: dict[str, float]
history: dict[date, float]
highest_monthly_consumption: float
attribution: str
class SuezWaterCoordinator(DataUpdateCoordinator[AggregatedSensorData]):
class SuezWaterCoordinator(DataUpdateCoordinator[AggregatedData]):
"""Suez water coordinator."""
_sync_client: SuezClient
_suez_client: SuezClient
config_entry: ConfigEntry
def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
@ -48,61 +29,22 @@ class SuezWaterCoordinator(DataUpdateCoordinator[AggregatedSensorData]):
)
async def _async_setup(self) -> None:
self._sync_client = await self.hass.async_add_executor_job(self._get_client)
self._suez_client = SuezClient(
username=self.config_entry.data[CONF_USERNAME],
password=self.config_entry.data[CONF_PASSWORD],
counter_id=self.config_entry.data[CONF_COUNTER_ID],
)
if not await self._suez_client.check_credentials():
raise ConfigEntryError("Invalid credentials for suez water")
async def _async_update_data(self) -> AggregatedSensorData:
async def _async_update_data(self) -> AggregatedData:
"""Fetch data from API endpoint."""
async with asyncio.timeout(30):
return await self.hass.async_add_executor_job(self._fetch_data)
def _fetch_data(self) -> AggregatedSensorData:
"""Fetch latest data from Suez."""
try:
self._sync_client.update()
data = await self._suez_client.fetch_aggregated_data()
except PySuezError as err:
_LOGGER.exception(err)
raise UpdateFailed(
f"Suez coordinator error communicating with API: {err}"
) from err
current_month = {}
for item in self._sync_client.attributes["thisMonthConsumption"]:
current_month[item] = self._sync_client.attributes["thisMonthConsumption"][
item
]
previous_month = {}
for item in self._sync_client.attributes["previousMonthConsumption"]:
previous_month[item] = self._sync_client.attributes[
"previousMonthConsumption"
][item]
highest_monthly_consumption = self._sync_client.attributes[
"highestMonthlyConsumption"
]
previous_year = self._sync_client.attributes["lastYearOverAll"]
current_year = self._sync_client.attributes["thisYearOverAll"]
history = {}
for item in self._sync_client.attributes["history"]:
history[item] = self._sync_client.attributes["history"][item]
_LOGGER.debug("Retrieved consumption: " + str(self._sync_client.state))
return AggregatedSensorData(
self._sync_client.state,
current_month,
previous_month,
previous_year,
current_year,
history,
highest_monthly_consumption,
self._sync_client.attributes["attribution"],
)
def _get_client(self) -> SuezClient:
try:
client = SuezClient(
username=self.config_entry.data[CONF_USERNAME],
password=self.config_entry.data[CONF_PASSWORD],
counter_id=self.config_entry.data[CONF_COUNTER_ID],
provider=None,
)
if not client.check_credentials():
raise ConfigEntryError
except PySuezError as ex:
raise ConfigEntryNotReady from ex
return client
_LOGGER.debug("Successfully fetched suez data")
return data

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/suez_water",
"iot_class": "cloud_polling",
"loggers": ["pysuez", "regex"],
"requirements": ["pysuezV2==0.2.2"]
"requirements": ["pysuezV2==1.3.1"]
}

View File

@ -2284,7 +2284,7 @@ pysqueezebox==0.10.0
pystiebeleltron==0.0.1.dev2
# homeassistant.components.suez_water
pysuezV2==0.2.2
pysuezV2==1.3.1
# homeassistant.components.switchbee
pyswitchbee==1.8.3

View File

@ -1841,7 +1841,7 @@ pyspeex-noise==1.0.2
pysqueezebox==0.10.0
# homeassistant.components.suez_water
pysuezV2==0.2.2
pysuezV2==1.3.1
# homeassistant.components.switchbee
pyswitchbee==1.8.3

View File

@ -1,11 +1,12 @@
"""Common fixtures for the Suez Water tests."""
from collections.abc import Generator
from unittest.mock import AsyncMock, MagicMock, patch
from unittest.mock import AsyncMock, patch
import pytest
from homeassistant.components.suez_water.const import DOMAIN
from homeassistant.components.suez_water.coordinator import AggregatedData
from tests.common import MockConfigEntry
@ -37,7 +38,7 @@ def mock_setup_entry() -> Generator[AsyncMock]:
@pytest.fixture(name="suez_client")
def mock_suez_client() -> Generator[MagicMock]:
def mock_suez_data() -> Generator[AsyncMock]:
"""Create mock for suez_water external api."""
with (
patch(
@ -48,28 +49,30 @@ def mock_suez_client() -> Generator[MagicMock]:
new=mock_client,
),
):
client = mock_client.return_value
client.check_credentials.return_value = True
client.update.return_value = None
client.state = 160
client.attributes = {
"thisMonthConsumption": {
suez_client = mock_client.return_value
suez_client.check_credentials.return_value = True
result = AggregatedData(
value=160,
current_month={
"2024-01-01": 130,
"2024-01-02": 145,
},
"previousMonthConsumption": {
previous_month={
"2024-12-01": 154,
"2024-12-02": 166,
},
"highestMonthlyConsumption": 2558,
"lastYearOverAll": 1000,
"thisYearOverAll": 1500,
"history": {
current_year=1500,
previous_year=1000,
attribution="suez water mock test",
highest_monthly_consumption=2558,
history={
"2024-01-01": 130,
"2024-01-02": 145,
"2024-12-01": 154,
"2024-12-02": 166,
},
"attribution": "suez water mock test",
}
yield client
)
suez_client.fetch_aggregated_data.return_value = result
yield suez_client

View File

@ -1,8 +1,8 @@
"""Test the Suez Water config flow."""
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock
from pysuez.client import PySuezError
from pysuez.exception import PySuezError
import pytest
from homeassistant import config_entries
@ -15,7 +15,9 @@ from .conftest import MOCK_DATA
from tests.common import MockConfigEntry
async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
async def test_form(
hass: HomeAssistant, mock_setup_entry: AsyncMock, suez_client: AsyncMock
) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -23,12 +25,11 @@ async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
with patch("homeassistant.components.suez_water.config_flow.SuezClient"):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_DATA,
)
await hass.async_block_till_done()
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_DATA,
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "test-username"
@ -38,37 +39,28 @@ async def test_form(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
async def test_form_invalid_auth(
hass: HomeAssistant, mock_setup_entry: AsyncMock
hass: HomeAssistant, mock_setup_entry: AsyncMock, suez_client: AsyncMock
) -> None:
"""Test we handle invalid auth."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with (
patch(
"homeassistant.components.suez_water.config_flow.SuezClient.__init__",
return_value=None,
),
patch(
"homeassistant.components.suez_water.config_flow.SuezClient.check_credentials",
return_value=False,
),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_DATA,
)
suez_client.check_credentials.return_value = False
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_DATA,
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "invalid_auth"}
with patch("homeassistant.components.suez_water.config_flow.SuezClient"):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_DATA,
)
await hass.async_block_till_done()
suez_client.check_credentials.return_value = True
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_DATA,
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "test-username"
@ -104,32 +96,32 @@ async def test_form_already_configured(hass: HomeAssistant) -> None:
("exception", "error"), [(PySuezError, "cannot_connect"), (Exception, "unknown")]
)
async def test_form_error(
hass: HomeAssistant, mock_setup_entry: AsyncMock, exception: Exception, error: str
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
exception: Exception,
suez_client: AsyncMock,
error: str,
) -> None:
"""Test we handle errors."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(
"homeassistant.components.suez_water.config_flow.SuezClient",
side_effect=exception,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_DATA,
)
suez_client.check_credentials.side_effect = exception
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_DATA,
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": error}
with patch(
"homeassistant.components.suez_water.config_flow.SuezClient",
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_DATA,
)
suez_client.check_credentials.return_value = True
suez_client.check_credentials.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_DATA,
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "test-username"

View File

@ -1,5 +1,7 @@
"""Test Suez_water integration initialization."""
from unittest.mock import AsyncMock
from homeassistant.components.suez_water.coordinator import PySuezError
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
@ -11,7 +13,7 @@ from tests.common import MockConfigEntry
async def test_initialization_invalid_credentials(
hass: HomeAssistant,
suez_client,
suez_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test that suez_water can't be loaded with invalid credentials."""
@ -24,7 +26,7 @@ async def test_initialization_invalid_credentials(
async def test_initialization_setup_api_error(
hass: HomeAssistant,
suez_client,
suez_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test that suez_water needs to retry loading if api failed to connect."""

View File

@ -1,6 +1,6 @@
"""Test Suez_water sensor platform."""
from unittest.mock import MagicMock, patch
from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory
from syrupy import SnapshotAssertion
@ -20,7 +20,7 @@ from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_plat
async def test_sensors_valid_state(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
suez_client: MagicMock,
suez_client: AsyncMock,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
) -> None:
@ -34,7 +34,7 @@ async def test_sensors_valid_state(
async def test_sensors_failed_update(
hass: HomeAssistant,
suez_client,
suez_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
@ -51,7 +51,7 @@ async def test_sensors_failed_update(
assert entity_ids[0]
assert state.state != STATE_UNAVAILABLE
suez_client.update.side_effect = PySuezError("Should fail to update")
suez_client.fetch_aggregated_data.side_effect = PySuezError("Should fail to update")
freezer.tick(DATA_REFRESH_INTERVAL)
async_fire_time_changed(hass)