diff --git a/homewizard_energy/models.py b/homewizard_energy/models.py index 9695994..556e1af 100644 --- a/homewizard_energy/models.py +++ b/homewizard_energy/models.py @@ -420,6 +420,27 @@ class DeviceType(Enum): ) +@dataclass(kw_only=True) +class StateUpdate(BaseModel): + """Represent State update config.""" + + power_on: bool | None = field(default=None) + switch_lock: bool | None = field(default=None) + brightness: int | None = field(default=None) + + def as_dict(self) -> dict[str, bool | int]: + """Return StateUpdate object as dict. + + Only include values that are not None. + """ + _dict = {k: v for k, v in asdict(self).items() if v is not None} + + if not _dict: + raise ValueError("No values to update") + + return _dict + + @dataclass(kw_only=True) class State(BaseModel): """Represent current state.""" @@ -439,8 +460,8 @@ class State(BaseModel): class SystemUpdate(BaseModel): """Represent System update config.""" - cloud_enabled: bool = field(default=None) - status_led_brightness_pct: int = field(default=None) + cloud_enabled: bool | None = field(default=None) + status_led_brightness_pct: int | None = field(default=None) api_v1_enabled: bool | None = field(default=None) def as_dict(self) -> dict[str, bool | int]: diff --git a/homewizard_energy/v1/__init__.py b/homewizard_energy/v1/__init__.py index d6d5a26..06b7441 100644 --- a/homewizard_energy/v1/__init__.py +++ b/homewizard_energy/v1/__init__.py @@ -20,7 +20,7 @@ ) from ..const import LOGGER -from ..models import Device, Measurement, State, System +from ..models import Device, Measurement, State, StateUpdate, System, SystemUpdate from .const import SUPPORTED_API_VERSION T = TypeVar("T") @@ -90,57 +90,48 @@ async def data(self) -> Measurement: return Measurement.from_json(response) @optional_method - async def state(self) -> State | None: + async def state(self, update: StateUpdate | None = None) -> State: """Return the state object.""" - _, response = await self._request("api/v1/state") - return State.from_json(response) - - @optional_method - async def state_set( - self, - power_on: bool | None = None, - switch_lock: bool | None = None, - brightness: int | None = None, - ) -> bool: - """Set state of device.""" - state: dict[str, bool | str] = {} + if update is not None: + data = update.as_dict() + status, response = await self._request( + "api/v1/state", method=METH_PUT, data=data + ) - if power_on is not None: - state["power_on"] = power_on - if switch_lock is not None: - state["switch_lock"] = switch_lock - if brightness is not None: - state["brightness"] = brightness + else: + status, response = await self._request("api/v1/state") - if not state: - LOGGER.error("At least one state update is required") - return False + if status != HTTPStatus.OK: + raise RequestError("Failed to get/set state") - await self._request("api/v1/state", method=METH_PUT, data=state) - return True + state = State.from_json(response) + return state @optional_method - async def system(self) -> System: + async def system(self, update: SystemUpdate | None = None) -> System: """Return the system object.""" - _, response = await self._request("api/v1/system") - return System.from_json(response) + if update is not None: + if ( + update.status_led_brightness_pct is not None + or update.api_v1_enabled is not None + ): + raise UnsupportedError( + "Setting status_led_brightness_pct and api_v1_enabled is not supported in v1" + ) - @optional_method - async def system_set( - self, - cloud_enabled: bool | None = None, - ) -> bool: - """Set state of device.""" - state = {} - if cloud_enabled is not None: - state["cloud_enabled"] = cloud_enabled + data = update.as_dict() + status, response = await self._request( + "api/v1/system", method=METH_PUT, data=data + ) - if not state: - LOGGER.error("At least one state update is required") - return False + else: + status, response = await self._request("api/v1/system") - await self._request("api/v1/system", method=METH_PUT, data=state) - return True + if status != HTTPStatus.OK: + raise RequestError("Failed to get/set system") + + system = System.from_json(response) + return system @optional_method async def identify( diff --git a/tests/v1/__snapshots__/test_v1_homewizard_energy.ambr b/tests/v1/__snapshots__/test_v1_homewizard_energy.ambr index 7fd31eb..51564b9 100644 --- a/tests/v1/__snapshots__/test_v1_homewizard_energy.ambr +++ b/tests/v1/__snapshots__/test_v1_homewizard_energy.ambr @@ -102,26 +102,26 @@ True # --- # name: test_state_set[HWE-SKT-fixtures0] - True + State(power_on=False, switch_lock=False, brightness=255) # --- # name: test_system_set[HWE-KWH1] - True + System(wifi_ssid=None, wifi_rssi_db=None, cloud_enabled=True, uptime_s=None, status_led_brightness_pct=None, api_v1_enabled=None) # --- # name: test_system_set[HWE-KWH3] - True + System(wifi_ssid=None, wifi_rssi_db=None, cloud_enabled=True, uptime_s=None, status_led_brightness_pct=None, api_v1_enabled=None) # --- # name: test_system_set[HWE-P1] - True + System(wifi_ssid=None, wifi_rssi_db=None, cloud_enabled=True, uptime_s=None, status_led_brightness_pct=None, api_v1_enabled=None) # --- # name: test_system_set[HWE-SKT] - True + System(wifi_ssid=None, wifi_rssi_db=None, cloud_enabled=True, uptime_s=None, status_led_brightness_pct=None, api_v1_enabled=None) # --- # name: test_system_set[HWE-WTR] - True + System(wifi_ssid=None, wifi_rssi_db=None, cloud_enabled=True, uptime_s=None, status_led_brightness_pct=None, api_v1_enabled=None) # --- # name: test_system_set[SDM230-wifi] - True + System(wifi_ssid=None, wifi_rssi_db=None, cloud_enabled=True, uptime_s=None, status_led_brightness_pct=None, api_v1_enabled=None) # --- # name: test_system_set[SDM630-wifi] - True + System(wifi_ssid=None, wifi_rssi_db=None, cloud_enabled=True, uptime_s=None, status_led_brightness_pct=None, api_v1_enabled=None) # --- diff --git a/tests/v1/test_v1_homewizard_energy.py b/tests/v1/test_v1_homewizard_energy.py index 40dc859..bc3c9a8 100644 --- a/tests/v1/test_v1_homewizard_energy.py +++ b/tests/v1/test_v1_homewizard_energy.py @@ -10,6 +10,7 @@ from homewizard_energy import HomeWizardEnergyV1 from homewizard_energy.errors import DisabledError, RequestError, UnsupportedError +from homewizard_energy.models import StateUpdate, SystemUpdate from . import load_fixtures @@ -354,8 +355,8 @@ async def test_state_set( async with aiohttp.ClientSession() as session: api = HomeWizardEnergyV1("example.com", clientsession=session) - state = await api.state_set( - power_on=False, switch_lock=False, brightness=255 + state = await api.state( + StateUpdate(power_on=False, switch_lock=False, brightness=255) ) assert state @@ -370,8 +371,8 @@ async def test_state_set_detects_no_statechange(): async with aiohttp.ClientSession() as session: api = HomeWizardEnergyV1("example.com", clientsession=session) - state = await api.state_set() - assert not state + with pytest.raises(ValueError): + await api.state(StateUpdate()) @pytest.mark.parametrize( @@ -528,7 +529,7 @@ async def test_system_set(model: str, snapshot: SnapshotAssertion, aresponses): async with aiohttp.ClientSession() as session: api = HomeWizardEnergyV1("example.com", clientsession=session) - system = await api.system_set(cloud_enabled=False) + system = await api.system(update=SystemUpdate(cloud_enabled=True)) assert system assert system == snapshot @@ -540,7 +541,22 @@ async def test_system_set_missing_arguments(): async with aiohttp.ClientSession() as session: api = HomeWizardEnergyV1("example.com", clientsession=session) - assert await api.system_set() is False + + with pytest.raises(ValueError): + await api.system(update=SystemUpdate()) + + +async def test_system_set_unsupported_arguments(): + """Test system set when no arguments are given.""" + + async with aiohttp.ClientSession() as session: + api = HomeWizardEnergyV1("example.com", clientsession=session) + + with pytest.raises(UnsupportedError): + await api.system(update=SystemUpdate(status_led_brightness_pct=50)) + + with pytest.raises(UnsupportedError): + await api.system(update=SystemUpdate(api_v1_enabled=True)) # pylint: disable=protected-access