Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 56 additions & 23 deletions custom_components/winix/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import aiohttp

from homeassistant.exceptions import HomeAssistantError

from .const import ATTR_PM25, LOGGER

# Modified from https://github.com/hfern/winix to support async operations
Expand Down Expand Up @@ -152,23 +154,44 @@ async def sleep(self) -> None:

async def _rpc_attr(self, attr: str, value: str) -> None:
LOGGER.debug("_rpc_attr attribute=%s, value=%s", attr, value)
resp = await self._client.get(
self.CTRL_URL.format(deviceid=self.device_id, attribute=attr, value=value),
raise_for_status=True,
)
raw_resp = await resp.text()
LOGGER.debug("_rpc_attr response=%s", raw_resp)

try:
response = await self._client.get(
self.CTRL_URL.format(
deviceid=self.device_id, attribute=attr, value=value
)
)
response.raise_for_status()
raw_resp = await response.text()
LOGGER.debug("_rpc_attr response=%s", raw_resp)
except aiohttp.ClientResponseError as err:
raise HomeAssistantError(
f"Failed to download data: HTTP {err.status}"
) from err
except aiohttp.ClientError as err:
raise HomeAssistantError(f"Error communicating with Winix: {err}") from err
except TimeoutError as err:
raise HomeAssistantError("Timeout communicating with Winix") from err

async def get_filter_life(self) -> int | None:
"""Get the total filter life."""
response = await self._client.get(
self.PARAM_URL.format(deviceid=self.device_id)
)
if response.status != 200:
LOGGER.error("Error getting filter life, status code %s", response.status)
return None
"""Get the total filter life.

json = await response.json()
This raises HomeAssistantError on communication errors
"""
try:
response = await self._client.get(
self.PARAM_URL.format(deviceid=self.device_id)
)
response.raise_for_status()
json = await response.json()
except aiohttp.ClientResponseError as err:
raise HomeAssistantError(
f"Failed to download data: HTTP {err.status}"
) from err
except aiohttp.ClientError as err:
raise HomeAssistantError(f"Error communicating with Winix: {err}") from err
except TimeoutError as err:
raise HomeAssistantError("Timeout communicating with Winix") from err

# pylint: disable=pointless-string-statement
"""
Expand Down Expand Up @@ -198,19 +221,29 @@ async def get_filter_life(self) -> int | None:
return None

async def get_state(self) -> dict[str, str | int]:
"""Get device state."""
"""Get device state.

This raises HomeAssistantError on communication errors, but returns an empty dict if the response is successfully received but doesn't contain expected data.
This allows callers to handle missing data without crashing.
"""

# All devices seem to have max 9 months filter life so don't need to call this API.
# await self.get_filter_life()

response = await self._client.get(
self.STATE_URL.format(deviceid=self.device_id)
)
if response.status != 200:
LOGGER.error("Error getting data, status code %s", response.status)
return {}

json = await response.json()
try:
response = await self._client.get(
self.STATE_URL.format(deviceid=self.device_id)
)
response.raise_for_status()
json = await response.json()
except aiohttp.ClientResponseError as err:
raise HomeAssistantError(
f"Failed to download data: HTTP {err.status}"
) from err
except aiohttp.ClientError as err:
raise HomeAssistantError(f"Error communicating with Winix: {err}") from err
except TimeoutError as err:
raise HomeAssistantError("Timeout communicating with Winix") from err

# pylint: disable=pointless-string-statement
"""
Expand Down
3 changes: 2 additions & 1 deletion custom_components/winix/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from winix import WinixAccount, auth

from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError

from .const import (
DEFAULT_FILTER_ALARM_DURATION,
Expand Down Expand Up @@ -324,7 +325,7 @@ async def get_device_stubs(
]


class WinixException(Exception):
class WinixException(HomeAssistantError):
"""Wiinx related operation exception."""

result_code: str = ""
Expand Down
6 changes: 4 additions & 2 deletions tests/common.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Tests for Winix component."""

from unittest.mock import MagicMock, Mock, patch
from unittest.mock import AsyncMock, MagicMock, Mock, patch

from pytest_homeassistant_custom_component.common import MockConfigEntry
from pytest_homeassistant_custom_component.test_util.aiohttp import AiohttpClientMocker
Expand Down Expand Up @@ -105,6 +105,8 @@ def build_fake_manager(wrapper_count: Number) -> WinixManager:

manager = MagicMock()
manager.get_device_wrappers = Mock(return_value=wrappers)

manager.async_request_refresh = AsyncMock()
return manager


Expand All @@ -113,7 +115,7 @@ def build_purifier(
) -> WinixPurifier:
"""Return a WinixPurifier instance."""

device = WinixPurifier(device_wrapper, Mock())
device = WinixPurifier(device_wrapper, build_fake_manager(1))
device.add_to_platform_start(hass, MagicMock(platform_name="test-platform"), None)

# Use unique_id as entity_id, this is required for async_update_ha_state
Expand Down
2 changes: 1 addition & 1 deletion tests/test_device_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
)
from custom_components.winix.device_wrapper import WinixDeviceWrapper

from .common import build_mock_wrapper
from .common import build_mock_wrapper # noqa: TID251

WinixDriver_TypeName = "custom_components.winix.driver.WinixDriver"

Expand Down
Loading