Skip to content

Commit

Permalink
Bump PySuez to 1.3.1 (#129825)
Browse files Browse the repository at this point in the history
  • Loading branch information
jb101010-2 authored Nov 7, 2024
1 parent a3ba780 commit 0e324c0
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 151 deletions.
10 changes: 4 additions & 6 deletions homeassistant/components/suez_water/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
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
Expand All @@ -26,7 +25,7 @@
)


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.
Expand All @@ -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
Expand All @@ -58,7 +56,7 @@ async def async_step_user(
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:
Expand Down
90 changes: 16 additions & 74 deletions homeassistant/components/suez_water/coordinator.py
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -48,61 +29,22 @@ def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
)

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
2 changes: 1 addition & 1 deletion homeassistant/components/suez_water/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 19 additions & 16 deletions tests/components/suez_water/conftest.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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(
Expand All @@ -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
84 changes: 38 additions & 46 deletions tests/components/suez_water/test_config_flow.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -15,20 +15,21 @@
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}
)
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"
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
Loading

0 comments on commit 0e324c0

Please sign in to comment.