Skip to content
Open
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
3 changes: 2 additions & 1 deletion src/devices/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
from datetime import datetime
from typing import Any

from ..helper.compat_aliases import HubCompatMixin
from ..helper.const import HIVETOHA, HTTP_OK
from ..helper.device_handler_base import BaseDeviceHandler
from ..helper.hivedataclasses import Device

_LOGGER = logging.getLogger(__name__)


class HiveHub(BaseDeviceHandler):
class HiveHub(HubCompatMixin, BaseDeviceHandler):
"""Hive hub.

Returns:
Expand Down
148 changes: 146 additions & 2 deletions src/helper/compat_aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,66 @@ async def setBoostOff(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for set_boost_off."""
return await self.set_boost_off(device) # type: ignore[attr-defined]

async def setHeatOnDemand(self, device: Device, state: str): # pylint: disable=invalid-name
"""Backwards-compatible alias for set_heat_on_demand."""
return await self.set_heat_on_demand(device, state) # type: ignore[attr-defined]

async def getClimate(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_climate."""
return await self.get_climate(device) # type: ignore[attr-defined]

async def getMinTemperature(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_min_temperature."""
return await self.get_min_temperature(device) # type: ignore[attr-defined]

async def getMaxTemperature(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_max_temperature."""
return await self.get_max_temperature(device) # type: ignore[attr-defined]

async def getCurrentTemperature(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_current_temperature."""
return await self.get_current_temperature(device) # type: ignore[attr-defined]

async def getTargetTemperature(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_target_temperature."""
return await self.get_target_temperature(device) # type: ignore[attr-defined]

async def getMode(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_mode."""
return await self.get_mode(device) # type: ignore[attr-defined]

async def getState(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_state."""
return await self.get_state(device) # type: ignore[attr-defined]

async def getCurrentOperation(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_current_operation."""
return await self.get_current_operation(device) # type: ignore[attr-defined]

async def getBoostStatus(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_boost_status."""
return await self.get_boost_status(device) # type: ignore[attr-defined]

async def getBoostTime(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_boost_time."""
return await self.get_boost_time(device) # type: ignore[attr-defined]

async def getHeatOnDemand(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_heat_on_demand."""
return await self.get_heat_on_demand(device) # type: ignore[attr-defined]

async def getOperationModes(self): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_operation_modes."""
return await self.get_operation_modes() # type: ignore[attr-defined]

async def getScheduleNowNextLater(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_schedule_now_next_later."""
return await self.get_schedule_now_next_later(device) # type: ignore[attr-defined]

async def minmaxTemperature(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for minmax_temperature."""
return await self.minmax_temperature(device) # type: ignore[attr-defined]


class LightCompatMixin:
"""CamelCase aliases for Light public methods."""
Expand All @@ -56,6 +112,34 @@ async def getLight(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_light."""
return await self.get_light(device) # type: ignore[attr-defined]

async def getState(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_state."""
return await self.get_state(device) # type: ignore[attr-defined]

async def getBrightness(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_brightness."""
return await self.get_brightness(device) # type: ignore[attr-defined]

async def getMinColorTemp(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_min_color_temp."""
return await self.get_min_color_temp(device) # type: ignore[attr-defined]

async def getMaxColorTemp(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_max_color_temp."""
return await self.get_max_color_temp(device) # type: ignore[attr-defined]

async def getColorTemp(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_color_temp."""
return await self.get_color_temp(device) # type: ignore[attr-defined]

async def getColor(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_color."""
return await self.get_color(device) # type: ignore[attr-defined]

async def getColorMode(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_color_mode."""
return await self.get_color_mode(device) # type: ignore[attr-defined]


class SwitchCompatMixin:
"""CamelCase aliases for Switch (plug) public methods."""
Expand All @@ -72,14 +156,34 @@ async def getSwitch(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_switch."""
return await self.get_switch(device) # type: ignore[attr-defined]

async def getState(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_state."""
return await self.get_state(device) # type: ignore[attr-defined]

async def getPowerUsage(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_power_usage."""
return await self.get_power_usage(device) # type: ignore[attr-defined]

async def getSwitchState(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_switch_state."""
return await self.get_switch_state(device) # type: ignore[attr-defined]


class WaterHeaterCompatMixin:
"""CamelCase aliases for WaterHeater (hotwater) public methods."""

async def get_boost(self, device: Device): # pylint: disable=invalid-name
async def get_boost(self, device: Device):
"""Alias for get_boost_status, used by the Hotwater_Boost sensor."""
return await self.get_boost_status(device) # type: ignore[attr-defined]

async def getBoost(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_boost_status."""
return await self.get_boost_status(device) # type: ignore[attr-defined]

async def getBoostTime(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_boost_time."""
return await self.get_boost_time(device) # type: ignore[attr-defined]

async def setMode(self, device: Device, new_mode: str): # pylint: disable=invalid-name
"""Backwards-compatible alias for set_mode."""
return await self.set_mode(device, new_mode) # type: ignore[attr-defined]
Expand All @@ -96,14 +200,34 @@ async def getWaterHeater(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_water_heater."""
return await self.get_water_heater(device) # type: ignore[attr-defined]

async def getMode(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_mode."""
return await self.get_mode(device) # type: ignore[attr-defined]

async def getState(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_state."""
return await self.get_state(device) # type: ignore[attr-defined]

class SensorCompatMixin: # pylint: disable=too-few-public-methods
async def getOperationModes(self): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_operation_modes."""
return await self.get_operation_modes() # type: ignore[attr-defined]

async def getScheduleNowNextLater(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_schedule_now_next_later."""
return await self.get_schedule_now_next_later(device) # type: ignore[attr-defined]


class SensorCompatMixin:
"""CamelCase aliases for Sensor public methods."""

async def getSensor(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_sensor."""
return await self.get_sensor(device) # type: ignore[attr-defined]

async def getState(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_state."""
return await self.get_state(device) # type: ignore[attr-defined]


class ActionCompatMixin:
"""CamelCase aliases for HiveAction public methods."""
Expand All @@ -120,6 +244,26 @@ async def setStatusOff(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for set_status_off."""
return await self.set_status_off(device) # type: ignore[attr-defined]

async def getState(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_state."""
return await self.get_state(device) # type: ignore[attr-defined]


class HubCompatMixin:
"""CamelCase aliases for HiveHub public methods."""

async def getSmokeStatus(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_smoke_status."""
return await self.get_smoke_status(device) # type: ignore[attr-defined]

async def getDogBarkStatus(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_dog_bark_status."""
return await self.get_dog_bark_status(device) # type: ignore[attr-defined]

async def getGlassBreakStatus(self, device: Device): # pylint: disable=invalid-name
"""Backwards-compatible alias for get_glass_break_status."""
return await self.get_glass_break_status(device) # type: ignore[attr-defined]


class SessionCompatMixin:
"""CamelCase and legacy aliases for HiveSession public methods."""
Expand Down
89 changes: 89 additions & 0 deletions tests/unit/test_compat_aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

from unittest.mock import AsyncMock

import pytest
from apyhiveapi.helper.compat_aliases import (
ActionCompatMixin,
HeatingCompatMixin,
HubCompatMixin,
LightCompatMixin,
SensorCompatMixin,
SessionCompatMixin,
Expand Down Expand Up @@ -220,6 +222,20 @@ class Stub(WaterHeaterCompatMixin):
s.get_boost_status.assert_called_once_with(d)
assert result == "OFF"

async def test_camelcase_get_boost_delegates_to_get_boost_status(self):
"""getBoost delegates to get_boost_status and returns its result."""

class Stub(WaterHeaterCompatMixin):
"""Stub with mocked get_boost_status."""

get_boost_status = AsyncMock(return_value="ON")

s = Stub()
d = _make_device()
result = await s.getBoost(d)
s.get_boost_status.assert_called_once_with(d)
assert result == "ON"

async def test_set_mode_delegates(self):
"""setMode delegates to set_mode."""

Expand Down Expand Up @@ -457,3 +473,76 @@ class Stub(ActionCompatMixin):
result = await s.setStatusOff(d)
s.set_status_off.assert_called_once_with(d)
assert result is True


# ---------------------------------------------------------------------------
# Getter aliases called by Home Assistant core's hive platforms
# ---------------------------------------------------------------------------

_DEVICE = object()

_GETTER_ALIASES = [
(HeatingCompatMixin, "getMinTemperature", "get_min_temperature", (_DEVICE,)),
(HeatingCompatMixin, "getMaxTemperature", "get_max_temperature", (_DEVICE,)),
(
HeatingCompatMixin,
"getCurrentTemperature",
"get_current_temperature",
(_DEVICE,),
),
(HeatingCompatMixin, "getTargetTemperature", "get_target_temperature", (_DEVICE,)),
(HeatingCompatMixin, "getMode", "get_mode", (_DEVICE,)),
(HeatingCompatMixin, "getState", "get_state", (_DEVICE,)),
(HeatingCompatMixin, "getCurrentOperation", "get_current_operation", (_DEVICE,)),
(HeatingCompatMixin, "getBoostStatus", "get_boost_status", (_DEVICE,)),
(HeatingCompatMixin, "getBoostTime", "get_boost_time", (_DEVICE,)),
(HeatingCompatMixin, "getHeatOnDemand", "get_heat_on_demand", (_DEVICE,)),
(HeatingCompatMixin, "setHeatOnDemand", "set_heat_on_demand", (_DEVICE, "ENABLED")),
(HeatingCompatMixin, "getOperationModes", "get_operation_modes", ()),
(
HeatingCompatMixin,
"getScheduleNowNextLater",
"get_schedule_now_next_later",
(_DEVICE,),
),
(HeatingCompatMixin, "minmaxTemperature", "minmax_temperature", (_DEVICE,)),
(LightCompatMixin, "getState", "get_state", (_DEVICE,)),
(LightCompatMixin, "getBrightness", "get_brightness", (_DEVICE,)),
(LightCompatMixin, "getMinColorTemp", "get_min_color_temp", (_DEVICE,)),
(LightCompatMixin, "getMaxColorTemp", "get_max_color_temp", (_DEVICE,)),
(LightCompatMixin, "getColorTemp", "get_color_temp", (_DEVICE,)),
(LightCompatMixin, "getColor", "get_color", (_DEVICE,)),
(LightCompatMixin, "getColorMode", "get_color_mode", (_DEVICE,)),
(SwitchCompatMixin, "getState", "get_state", (_DEVICE,)),
(SwitchCompatMixin, "getPowerUsage", "get_power_usage", (_DEVICE,)),
(SwitchCompatMixin, "getSwitchState", "get_switch_state", (_DEVICE,)),
(WaterHeaterCompatMixin, "getBoostTime", "get_boost_time", (_DEVICE,)),
(WaterHeaterCompatMixin, "getMode", "get_mode", (_DEVICE,)),
(WaterHeaterCompatMixin, "getState", "get_state", (_DEVICE,)),
(WaterHeaterCompatMixin, "getOperationModes", "get_operation_modes", ()),
(
WaterHeaterCompatMixin,
"getScheduleNowNextLater",
"get_schedule_now_next_later",
(_DEVICE,),
),
(SensorCompatMixin, "getState", "get_state", (_DEVICE,)),
(ActionCompatMixin, "getState", "get_state", (_DEVICE,)),
(HubCompatMixin, "getSmokeStatus", "get_smoke_status", (_DEVICE,)),
(HubCompatMixin, "getDogBarkStatus", "get_dog_bark_status", (_DEVICE,)),
(HubCompatMixin, "getGlassBreakStatus", "get_glass_break_status", (_DEVICE,)),
]


@pytest.mark.parametrize(
("mixin", "alias", "target", "args"),
_GETTER_ALIASES,
ids=[f"{m.__name__}.{a}" for m, a, _, _ in _GETTER_ALIASES],
)
async def test_getter_alias_delegates(mixin, alias, target, args):
"""Each camelCase alias delegates to its snake_case method and returns its result."""
sentinel = object()
stub = type("Stub", (mixin,), {target: AsyncMock(return_value=sentinel)})()
result = await getattr(stub, alias)(*args)
getattr(stub, target).assert_called_once_with(*args)
assert result is sentinel
Loading