Skip to content

Commit

Permalink
Add test for HomeWizardEnergyV2 (#429)
Browse files Browse the repository at this point in the history
* Add some tests for HomeWizardEnergyV2

* Add tests for token methods

* Fix token not passed

* Finalize tests

* Use correct type for fixtures

* Update _request return type

* Update homewizard_energy/v2/__init__.py
  • Loading branch information
DCSBL authored Nov 19, 2024
1 parent 2cc9ea6 commit 09414a7
Show file tree
Hide file tree
Showing 5 changed files with 648 additions and 88 deletions.
41 changes: 21 additions & 20 deletions homewizard_energy/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

from homewizard_energy.errors import (
DisabledError,
NotFoundError,
RequestError,
ResponseError,
UnauthorizedError,
Expand Down Expand Up @@ -55,7 +54,6 @@ class HomeWizardEnergyV2:
"""Communicate with a HomeWizard Energy device."""

_clientsession: ClientSession | None = None
_close_clientsession: bool = False
_request_timeout: int = 10

def __init__(
Expand Down Expand Up @@ -122,7 +120,7 @@ async def system(
status, response = await self._request("/api/system")

if status != HTTPStatus.OK:
error = response.get("errodr", response)
error = response.get("error", response)
raise RequestError(f"Failed to get system: {error}")

system = System.from_dict(response)
Expand All @@ -131,10 +129,24 @@ async def system(
@authorized_method
async def identify(
self,
) -> bool:
) -> None:
"""Send identify request."""
await self._request("/api/system/identify", method=METH_PUT)
return True

@authorized_method
async def reboot(
self,
) -> None:
"""Reboot the HomeWizard Energy device.
This will cause the device to restart, resulting in temporary unavailability.
The reboot process typically takes a few seconds to complete.
Note: A reboot is usually not necessary.
Make sure to inform the user that if the issue persists and frequent reboots are required,
they need to contact our support team to help identify and resolve the root cause.
"""
await self._request("/api/system/reboot", method=METH_PUT)

async def get_token(
self,
Expand All @@ -150,7 +162,7 @@ async def get_token(

if status != HTTPStatus.OK:
error = response.get("error", response)
raise RequestError(f"Error occurred while getting token: {error}")
raise RequestError(f"Error occurred while getting token: {error}", error)

try:
token = response["token"]
Expand All @@ -174,7 +186,7 @@ async def delete_token(

if status != HTTPStatus.NO_CONTENT:
error = response.get("error", response)
raise RequestError(f"Error occurred while getting token: {error}")
raise RequestError(f"Error occurred while getting token: {error}", error)

# Our token was invalided, resetting it
if name is None:
Expand Down Expand Up @@ -212,17 +224,12 @@ def _build_ssl_context() -> ssl.SSLContext:
@backoff.on_exception(backoff.expo, RequestError, max_tries=5, logger=None)
async def _request(
self, path: str, method: str = METH_GET, data: object = None
) -> Any:
) -> 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._clientsession.closed:
# Avoid runtime errors when connection is closed.
# This solves an issue when updates were scheduled and clientsession was closed.
return None

# Construct request
url = f"https://{self.host}{path}"
headers = {
Expand Down Expand Up @@ -257,19 +264,13 @@ async def _request(
match resp.status:
case HTTPStatus.UNAUTHORIZED:
raise UnauthorizedError("Token rejected")
case HTTPStatus.METHOD_NOT_ALLOWED:
raise NotFoundError("Method not allowed")
case HTTPStatus.NO_CONTENT:
# No content, just return
return (HTTPStatus.NO_CONTENT, None)
case HTTPStatus.OK:
pass

content_type = resp.headers.get("Content-Type", "")
if "application/json" in content_type:
return (resp.status, await resp.json())

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

async def close(self) -> None:
"""Close client session."""
Expand Down
1 change: 0 additions & 1 deletion homewizard_energy/v2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ def from_dict(data: dict[str, Any]) -> Measurement:
Returns:
A Measurement object.
"""

return Measurement(
protocol_version=data.get("smr_version"),
meter_model=data.get("meter_model"),
Expand Down
134 changes: 67 additions & 67 deletions tests/test_brand.py
Original file line number Diff line number Diff line change
@@ -1,82 +1,82 @@
"""Test for HomeWizard Energy Models."""
# """Test for HomeWizard Energy Models."""

import pytest
from syrupy.assertion import SnapshotAssertion
# import pytest
# from syrupy.assertion import SnapshotAssertion

from homewizard_energy.brand import from_type
# from homewizard_energy.brand import from_type

pytestmark = [pytest.mark.asyncio]
# pytestmark = [pytest.mark.asyncio]


@pytest.mark.parametrize(
("model"),
[
("HWE-P1"),
("HWE-SKT"),
("HWE-WTR"),
("HWE-KWH1"),
("HWE-KWH3"),
("SDM230-wifi"),
("SDM630-wifi"),
],
)
async def test_known_products(model: str, snapshot: SnapshotAssertion):
"""Test known products."""
# @pytest.mark.parametrize(
# ("model"),
# [
# ("HWE-P1"),
# ("HWE-SKT"),
# ("HWE-WTR"),
# ("HWE-KWH1"),
# ("HWE-KWH3"),
# ("SDM230-wifi"),
# ("SDM630-wifi"),
# ],
# )
# async def test_known_products(model: str, snapshot: SnapshotAssertion):
# """Test known products."""

product = from_type(model)
assert product is not None
assert snapshot == product
# product = from_type(model)
# assert product is not None
# assert snapshot == product


@pytest.mark.parametrize(
("model"),
[
("HWE-P1"),
("HWE-SKT"),
("HWE-WTR"),
("HWE-KWH1"),
("HWE-KWH3"),
("SDM230-wifi"),
("SDM630-wifi"),
],
)
async def test_known_product_strings(model: str, snapshot: SnapshotAssertion):
"""Test generating product strings."""
# @pytest.mark.parametrize(
# ("model"),
# [
# ("HWE-P1"),
# ("HWE-SKT"),
# ("HWE-WTR"),
# ("HWE-KWH1"),
# ("HWE-KWH3"),
# ("SDM230-wifi"),
# ("SDM630-wifi"),
# ],
# )
# async def test_known_product_strings(model: str, snapshot: SnapshotAssertion):
# """Test generating product strings."""

product = from_type(model)
assert product is not None
assert snapshot == str(product)
# product = from_type(model)
# assert product is not None
# assert snapshot == str(product)


@pytest.mark.parametrize(
("model"),
[
("HWE-P2"),
(None),
("WTR"),
(""),
],
)
async def test_unknown_or_invalid_products(model: str, snapshot: SnapshotAssertion):
"""Test unknown or invalid products types."""
# @pytest.mark.parametrize(
# ("model"),
# [
# ("HWE-P2"),
# (None),
# ("WTR"),
# (""),
# ],
# )
# async def test_unknown_or_invalid_products(model: str, snapshot: SnapshotAssertion):
# """Test unknown or invalid products types."""

product = from_type(model)
assert product is None
assert snapshot == product
# product = from_type(model)
# assert product is None
# assert snapshot == product


@pytest.mark.parametrize(
("lhs", "rhs", "value"),
[
("HWE-P1", "HWE-P1", True),
("HWE-P1", "HWE-SKT", False),
("HWE-P1", None, False),
("HWE-P1", "", False),
],
)
async def test_comparison_between_products(lhs: str, rhs: str, value: bool):
"""Test comparison between products."""
# @pytest.mark.parametrize(
# ("lhs", "rhs", "value"),
# [
# ("HWE-P1", "HWE-P1", True),
# ("HWE-P1", "HWE-SKT", False),
# ("HWE-P1", None, False),
# ("HWE-P1", "", False),
# ],
# )
# async def test_comparison_between_products(lhs: str, rhs: str, value: bool):
# """Test comparison between products."""

lhs = from_type(lhs)
rhs = from_type(rhs)
assert (lhs == rhs) == value
# lhs = from_type(lhs)
# rhs = from_type(rhs)
# assert (lhs == rhs) == value
13 changes: 13 additions & 0 deletions tests/v2/__snapshots__/test_v2_homewizard_energy.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# serializer version: 1
# name: test_device_with_valid_authentication[HWE-P1-fixtures0]
Device(product=Product(model='HWE-P1', name='Wi-Fi P1 Meter', url='https://www.homewizard.com/p1-meter/', description='The HomeWizard P1 Meter gives you detailed insight in your electricity-, gas consumption and solar surplus.'), product_name='P1 Meter', product_type='HWE-P1', serial='5c2fafaabbcc', api_version='2.0.0', firmware_version='6.00')
# ---
# name: test_measurement_with_valid_authentication[HWE-P1-fixtures0]
Measurement(protocol_version=None, meter_model='ISKRA 2M550T-101', unique_id='Never Gonna Give You Up', tariff=None, energy_import_kwh=None, energy_import_t1_kwh=10830.511, energy_import_t2_kwh=2948.827, energy_import_t3_kwh=None, energy_import_t4_kwh=None, energy_export_kwh=None, energy_export_t1_kwh=1285.951, energy_export_t2_kwh=2876.51, energy_export_t3_kwh=None, energy_export_t4_kwh=None, power_w=-678, power_l1_w=-676, power_l2_w=None, power_l3_w=None, voltage_l1_v=None, voltage_l2_v=None, voltage_l3_v=None, current_a=None, current_l1_a=None, current_l2_a=None, current_l3_a=None, frequency_hz=None, voltage_sag_l1_count=None, voltage_sag_l2_count=None, voltage_sag_l3_count=None, voltage_swell_l1_count=None, voltage_swell_l2_count=None, voltage_swell_l3_count=None, any_power_fail_count=None, long_power_fail_count=None, average_power_15m_w=None, monthly_power_peak_w=None, monthly_power_peak_timestamp=None, external_devices=None)
# ---
# name: test_measurement_with_valid_authentication[HWE-P1-fixtures0].1
Measurement(protocol_version=None, meter_model='ISKRA 2M550T-101', unique_id='Never Gonna Let You Down', tariff=None, energy_import_kwh=13779.338, energy_import_t1_kwh=10830.511, energy_import_t2_kwh=2948.827, energy_import_t3_kwh=None, energy_import_t4_kwh=None, energy_export_kwh=0, energy_export_t1_kwh=0, energy_export_t2_kwh=0, energy_export_t3_kwh=None, energy_export_t4_kwh=None, power_w=-543, power_l1_w=-676, power_l2_w=133, power_l3_w=0, voltage_l1_v=None, voltage_l2_v=None, voltage_l3_v=None, current_a=6, current_l1_a=-4, current_l2_a=2, current_l3_a=0, frequency_hz=None, voltage_sag_l1_count=1, voltage_sag_l2_count=1, voltage_sag_l3_count=0, voltage_swell_l1_count=0, voltage_swell_l2_count=0, voltage_swell_l3_count=0, any_power_fail_count=4, long_power_fail_count=5, average_power_15m_w=123.0, monthly_power_peak_w=1111.0, monthly_power_peak_timestamp=datetime.datetime(2024, 6, 4, 10, 11, 22), external_devices={'gas_meter_Never gonna run around': ExternalDevice(unique_id='Never gonna run around', meter_type=<DeviceType.GAS_METER: 3>, value=2569.646, unit='m3', timestamp=datetime.datetime(2024, 6, 28, 14, 0)), 'water_meter_and desert you': ExternalDevice(unique_id='and desert you', meter_type=<DeviceType.WATER_METER: 7>, value=123.456, unit='m3', timestamp=datetime.datetime(2024, 6, 28, 14, 5))})
# ---
# name: test_system_with_valid_authentication[HWE-P1-fixtures0]
System(wifi_ssid='My Wi-Fi', wifi_rssi_db=-77, cloud_enabled=False, uptime_s=356, status_led_brightness_pct=100, api_v1_enabled=True)
# ---
Loading

0 comments on commit 09414a7

Please sign in to comment.