Skip to content

Commit

Permalink
Add HomeWizardEnergy base class which is implemented in v1 and v2
Browse files Browse the repository at this point in the history
  • Loading branch information
DCSBL committed Jan 1, 2025
1 parent b4170f7 commit 8d87a39
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 147 deletions.
109 changes: 67 additions & 42 deletions homewizard_energy/homewizard_energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@

from __future__ import annotations

import asyncio
import json
import ssl
from collections.abc import Callable, Coroutine
from http import HTTPStatus
from typing import Any, TypeVar

import async_timeout
import backoff
from aiohttp.client import (
ClientError,
ClientResponseError,
ClientSession,
ClientTimeout,
TCPConnector,
)
from aiohttp.hdrs import METH_DELETE, METH_GET, METH_POST, METH_PUT
from mashumaro.exceptions import InvalidFieldValue, MissingField
from ..models import Device, Measurement, System, SystemUpdate, Token, State

from homewizard_energy.errors import (
DisabledError,
RequestError,
ResponseError,
UnauthorizedError,
)
from typing import Any

from aiohttp.client import ClientSession

from .const import LOGGER
from .models import Device, Measurement, State, StateUpdate, System, SystemUpdate


class HomeWizardEnergy:
"""Base class for HomeWizard Energy API."""

_clientsession: ClientSession | None = None
_session: ClientSession | None = None
_close_session: bool = False
_request_timeout: int = 10
_host: str


def __init__(
self,
host: str,
clientsession: ClientSession = None,
timeout: int = 10,
):
"""Create a HomeWizard Energy object.
Args:
host: IP or URL for device.
clientsession: The clientsession.
timeout: Request timeout in seconds.
"""
self._host = host
self._session = clientsession
self._close_session = clientsession is None
self._request_timeout = timeout

@property
def host(self) -> str:
"""Return the hostname of the device.
Expand All @@ -45,34 +45,59 @@ def host(self) -> str:
"""
return self._host

async def device(self) -> Device:
"""Get the device information."""
raise NotImplementedError

async def measurement(self) -> Measurement:
"""Get the current measurement."""
raise NotImplementedError

async def system(
self,
update: SystemUpdate | None = None,
) -> System:
"""Get the system information."""
"""Get/set the system."""
raise NotImplementedError

async def state(self) -> State:
"""Get the current state."""

async def state(
self,
update: StateUpdate | None = None,
) -> State:
"""Get/set the state."""
raise NotImplementedError

async def identify(
self,
) -> None:
"""Identify the device."""
raise NotImplementedError

# async def reboot(
# self,
# ) -> None:
# """Reboot the device."""
# raise NotImplementedError

async def reboot(
self,
) -> None:
"""Reboot the device."""
raise NotImplementedError

async def close(self) -> None:
"""Close client session."""
LOGGER.debug("Closing clientsession")
if self._session and self._close_session:
await self._session.close()

async def __aenter__(self) -> HomeWizardEnergy:
"""Async enter.
Returns:
The HomeWizardEnergy object.
"""
return self

async def __aexit__(self, *_exc_info: Any) -> None:
"""Async exit.
Args:
_exc_info: Exec type.
"""
await self.close()
57 changes: 4 additions & 53 deletions homewizard_energy/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
)

from ..const import LOGGER
from ..homewizard_energy import HomeWizardEnergy
from ..models import Device, Measurement, State, StateUpdate, System, SystemUpdate
from .const import SUPPORTED_API_VERSION

Expand All @@ -40,38 +41,10 @@ async def wrapper(self, *args, **kwargs) -> T:
return wrapper


class HomeWizardEnergyV1:
# pylint: disable=abstract-method
class HomeWizardEnergyV1(HomeWizardEnergy):
"""Communicate with a HomeWizard Energy device."""

_session: ClientSession | None
_close_session: bool = False
_request_timeout: int = 10

def __init__(
self, host: str, clientsession: ClientSession = None, timeout: int = 10
):
"""Create a HomeWizard Energy object.
Args:
host: IP or URL for device.
clientsession: The clientsession.
timeout: Request timeout in seconds.
"""

self._host = host
self._session = clientsession
self._request_timeout = timeout

@property
def host(self) -> str:
"""Return the hostname of the device.
Returns:
host: The used host
"""
return self._host

async def device(self) -> Device:
"""Return the device object."""
_, response = await self._request("api")
Expand All @@ -84,7 +57,7 @@ async def device(self) -> Device:

return device

async def data(self) -> Measurement:
async def measurement(self) -> Measurement:
"""Return the data object."""
_, response = await self._request("api/v1/data")
return Measurement.from_json(response)
Expand Down Expand Up @@ -193,25 +166,3 @@ async def _request(
raise RequestError(f"API request error ({resp.status})")

return (resp.status, await resp.text())

async def close(self) -> None:
"""Close client session."""
LOGGER.debug("Closing clientsession")
if self._session and self._close_session:
await self._session.close()

async def __aenter__(self) -> HomeWizardEnergyV1:
"""Async enter.
Returns:
The HomeWizardEnergyV1 object.
"""
return self

async def __aexit__(self, *_exc_info: Any) -> None:
"""Async exit.
Args:
_exc_info: Exec type.
"""
await self.close()
54 changes: 11 additions & 43 deletions homewizard_energy/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
)

from ..const import LOGGER
from ..homewizard_energy import HomeWizardEnergy
from ..models import Device, Measurement, System, SystemUpdate, Token
from .cacert import CACERT

Expand All @@ -50,17 +51,18 @@ async def wrapper(self, *args, **kwargs) -> T:
return wrapper


class HomeWizardEnergyV2:
# pylint: disable=abstract-method
class HomeWizardEnergyV2(HomeWizardEnergy):
"""Communicate with a HomeWizard Energy device."""

_clientsession: ClientSession | None = None
_request_timeout: int = 10

# pylint: disable=too-many-arguments
# pylint: disable=too-many-positional-arguments
def __init__(
self,
host: str,
identifier: str | None = None,
token: str | None = None,
clientsession: ClientSession = None,
timeout: int = 10,
):
"""Create a HomeWizard Energy object.
Expand All @@ -71,21 +73,9 @@ def __init__(
token: Token for device.
timeout: Request timeout in seconds.
"""

self._host = host
super().__init__(host, clientsession, timeout)
self._identifier = identifier
self._token = token
self._request_timeout = timeout

@property
def host(self) -> str:
"""Return the hostname of the device.
Returns:
host: The used host
"""
return self._host

@authorized_method
async def device(self) -> Device:
Expand Down Expand Up @@ -192,7 +182,7 @@ async def delete_token(
if name is None:
self._token = None

async def _get_clientsession(self) -> ClientSession:
async def _get_session(self) -> ClientSession:
"""
Get a clientsession that is tuned for communication with the HomeWizard Energy Device
"""
Expand Down Expand Up @@ -227,8 +217,8 @@ async def _request(
) -> tuple[HTTPStatus, dict[str, Any] | None]:
"""Make a request to the API."""

if self._clientsession is None:
self._clientsession = await self._get_clientsession()
if self._session is None:
self._session = await self._get_session()

# Construct request
url = f"https://{self.host}{path}"
Expand All @@ -242,7 +232,7 @@ async def _request(

try:
async with async_timeout.timeout(self._request_timeout):
resp = await self._clientsession.request(
resp = await self._session.request(
method,
url,
json=data,
Expand Down Expand Up @@ -271,25 +261,3 @@ async def _request(
pass

return (resp.status, await resp.text())

async def close(self) -> None:
"""Close client session."""
LOGGER.debug("Closing clientsession")
if self._clientsession is not None:
await self._clientsession.close()

async def __aenter__(self) -> HomeWizardEnergyV2:
"""Async enter.
Returns:
The HomeWizardEnergyV2 object.
"""
return self

async def __aexit__(self, *_exc_info: Any) -> None:
"""Async exit.
Args:
_exc_info: Exec type.
"""
await self.close()
25 changes: 25 additions & 0 deletions tests/test_homewizard_energy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Test the base class."""

import pytest

from homewizard_energy.homewizard_energy import HomeWizardEnergy

pytestmark = [pytest.mark.asyncio]


@pytest.mark.parametrize(
("function"),
[
("device"),
("measurement"),
("system"),
("state"),
("identify"),
("reboot"),
],
)
async def test_base_class_raises_notimplementederror(function: str):
"""Test the base class raises NotImplementedError."""
with pytest.raises(NotImplementedError):
async with HomeWizardEnergy("host") as api:
await getattr(api, function)()
12 changes: 6 additions & 6 deletions tests/v1/test_v1_homewizard_energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,9 @@ async def test_get_data_object(
async with aiohttp.ClientSession() as session:
api = HomeWizardEnergyV1("example.com", clientsession=session)

data = await api.data()
assert data is not None
assert data == snapshot
measurement = await api.measurement()
assert measurement is not None
assert measurement == snapshot

await api.close()

Expand Down Expand Up @@ -275,9 +275,9 @@ async def test_get_data_object_with_known_device(
# pylint: disable=protected-access
api._detected_api_version = "v1"

data = await api.data()
assert data is not None
assert data == snapshot
measurement = await api.measurement()
assert measurement is not None
assert measurement == snapshot

await api.close()

Expand Down
Loading

0 comments on commit 8d87a39

Please sign in to comment.