Skip to content

Commit

Permalink
Implement Forced Option
Browse files Browse the repository at this point in the history
  • Loading branch information
derjoerg committed Nov 6, 2024
1 parent 599cfd8 commit 429a391
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 5 deletions.
45 changes: 45 additions & 0 deletions src/abbfreeathome/devices/dimming_actuator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class DimmingActuator(Base):
"""Free@Home DimmingActuator Class."""

_state_refresh_output_pairings: list[Pairing] = [
Pairing.AL_INFO_FORCE,
Pairing.AL_INFO_ON_OFF,
Pairing.AL_INFO_ACTUAL_DIMMING_VALUE,
]
Expand All @@ -31,6 +32,7 @@ def __init__(
"""Initialize the Free@Home DimmingActuator class."""
self._state: bool | None = None
self._brightness: int | None = None
self._forced: int | None = None

super().__init__(
device_id,
Expand All @@ -55,6 +57,11 @@ def brightness(self) -> int:
"""Get the brightness level of the dimmer."""
return int(self._brightness)

@property
def forced(self) -> int | None:
"""Get the forced state of the dimmer."""
return self._forced

async def turn_on(self):
"""Turn on the dimmer."""
await self._set_switching_datapoint("1")
Expand All @@ -78,6 +85,24 @@ async def set_brightness(self, value: int):
await self._set_brightness_datapoint(str(value))
self._brightness = value

async def set_forced(self, value: int):
"""
Set the forced-option on the dimmer.
0 means None
2 means force off
3 means force on
"""
if value in (0, 2, 3):
await self._set_force_datapoint(str(value))

if value == 0:
self._forced = 0
elif value == 2:
self._forced = 5
elif value == 3:
self._forced = 4

def _refresh_state_from_output(self, output: dict[str, Any]) -> bool:
"""
Refresh the state of the device from a given output.
Expand All @@ -90,6 +115,14 @@ def _refresh_state_from_output(self, output: dict[str, Any]) -> bool:
if output.get("pairingID") == Pairing.AL_INFO_ACTUAL_DIMMING_VALUE.value:
self._brightness = output.get("value")
return True
if output.get("pairingID") == Pairing.AL_INFO_FORCE.value:
"""
0 means off
4 means force on
5 means force off
"""
self._forced = int(output.get("value"))
return True
return False

async def _set_switching_datapoint(self, value: str):
Expand All @@ -115,3 +148,15 @@ async def _set_brightness_datapoint(self, value: str):
datapoint=_brightness_input_id,
value=value,
)

async def _set_force_datapoint(self, value: str):
"""Set the force datapoint on the api."""
_force_input_id, _force_input_value = self.get_input_by_pairing(
pairing=Pairing.AL_FORCED
)
return await self._api.set_datapoint(
device_id=self.device_id,
channel_id=self.channel_id,
datapoint=_force_input_id,
value=value,
)
45 changes: 45 additions & 0 deletions src/abbfreeathome/devices/switch_actuator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class SwitchActuator(Base):
"""Free@Home SwitchActuator Class."""

_state_refresh_output_pairings: list[Pairing] = [
Pairing.AL_INFO_FORCE,
Pairing.AL_INFO_ON_OFF,
]

Expand All @@ -29,6 +30,7 @@ def __init__(
) -> None:
"""Initialize the Free@Home SwitchActuator class."""
self._state: bool | None = None
self._forced: int | None = None

super().__init__(
device_id,
Expand All @@ -48,6 +50,11 @@ def state(self) -> bool | None:
"""Get the state of the switch."""
return self._state

@property
def forced(self) -> int | None:
"""Get the forced state of the switch."""
return self._forced

async def turn_on(self):
"""Turn on the switch."""
await self._set_switching_datapoint("1")
Expand All @@ -58,6 +65,24 @@ async def turn_off(self):
await self._set_switching_datapoint("0")
self._state = False

async def set_forced(self, value: int):
"""
Set the forced-option on the switch.
0 means None
2 means force off
3 means force on
"""
if value in (0, 2, 3):
await self._set_force_datapoint(str(value))

if value == 0:
self._forced = 0
elif value == 2:
self._forced = 5
elif value == 3:
self._forced = 4

def _refresh_state_from_output(self, output: dict[str, Any]) -> bool:
"""
Refresh the state of the device from a given output.
Expand All @@ -67,6 +92,14 @@ def _refresh_state_from_output(self, output: dict[str, Any]) -> bool:
if output.get("pairingID") == Pairing.AL_INFO_ON_OFF.value:
self._state = output.get("value") == "1"
return True
if output.get("pairingID") == Pairing.AL_INFO_FORCE.value:
"""
0 means off
4 means force on
5 means force off
"""
self._forced = int(output.get("value"))
return True
return False

async def _set_switching_datapoint(self, value: str):
Expand All @@ -80,3 +113,15 @@ async def _set_switching_datapoint(self, value: str):
datapoint=_switch_input_id,
value=value,
)

async def _set_force_datapoint(self, value: str):
"""Set the force datapoint on the api."""
_force_input_id, _force_input_value = self.get_input_by_pairing(
pairing=Pairing.AL_FORCED
)
return await self._api.set_datapoint(
device_id=self.device_id,
channel_id=self.channel_id,
datapoint=_force_input_id,
value=value,
)
39 changes: 34 additions & 5 deletions tests/test_dimming_actuator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ def dimming_actuator(mock_api):
inputs = {
"idp0000": {"pairingID": 1, "value": "0"},
"idp0002": {"pairingID": 17, "value": "50"},
"idp0004": {"pairingID": 3, "value": "0"},
}
outputs = {
"odp0000": {"pairingID": 256, "value": "0"},
"odp0001": {"pairingID": 272, "value": "50"},
"odp0002": {"pairingID": 273, "value": "0"},
"odp0003": {"pairingID": 257, "value": "0"},
}
parameters = {}
Expand Down Expand Up @@ -83,6 +85,35 @@ async def test_set_brightness(dimming_actuator):
assert dimming_actuator.brightness == 100


@pytest.mark.asyncio
async def test_set_forced(dimming_actuator):
"""Test to set the forced option of the DimmingActuator."""
await dimming_actuator.set_forced(0)
assert dimming_actuator.forced == 0
dimming_actuator._api.set_datapoint.assert_called_with(
device_id="ABB70139AF8A",
channel_id="ch0000",
datapoint="idp0004",
value="0",
)
await dimming_actuator.set_forced(2)
assert dimming_actuator.forced == 5
dimming_actuator._api.set_datapoint.assert_called_with(
device_id="ABB70139AF8A",
channel_id="ch0000",
datapoint="idp0004",
value="2",
)
await dimming_actuator.set_forced(3)
assert dimming_actuator.forced == 4
dimming_actuator._api.set_datapoint.assert_called_with(
device_id="ABB70139AF8A",
channel_id="ch0000",
datapoint="idp0004",
value="3",
)


@pytest.mark.asyncio
async def test_refresh_state(dimming_actuator):
"""Test refreshing the state of the DimmingActuator."""
Expand Down Expand Up @@ -110,13 +141,11 @@ def test_refresh_state_from_output(dimming_actuator):
)
assert dimming_actuator.brightness == 75

# Check output that does NOT affect the state,
# ensure state, brightness are unchanged.
# Check output that affects the force-option
dimming_actuator._refresh_state_from_output(
output={"pairingID": 257, "value": "0"},
output={"pairingID": 257, "value": "4"},
)
assert dimming_actuator.brightness == 75
assert dimming_actuator.state is True
assert dimming_actuator.forced == 4


def test_update_device(dimming_actuator):
Expand Down
30 changes: 30 additions & 0 deletions tests/test_switch_actuator.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def switch_actuator(mock_api):
outputs = {
"odp0000": {"pairingID": 256, "value": "0"},
"odp0001": {"pairingID": 257, "value": "0"},
"odp0004": {"pairingID": 273, "value": "0"},
}
parameters = {}

Expand Down Expand Up @@ -74,6 +75,35 @@ async def test_turn_off(switch_actuator):
)


@pytest.mark.asyncio
async def test_set_forced(switch_actuator):
"""Test to set the forced option of the switch."""
await switch_actuator.set_forced(0)
assert switch_actuator.forced == 0
switch_actuator._api.set_datapoint.assert_called_with(
device_id="ABB7F500E17A",
channel_id="ch0003",
datapoint="idp0002",
value="0",
)
await switch_actuator.set_forced(2)
assert switch_actuator.forced == 5
switch_actuator._api.set_datapoint.assert_called_with(
device_id="ABB7F500E17A",
channel_id="ch0003",
datapoint="idp0002",
value="2",
)
await switch_actuator.set_forced(3)
assert switch_actuator.forced == 4
switch_actuator._api.set_datapoint.assert_called_with(
device_id="ABB7F500E17A",
channel_id="ch0003",
datapoint="idp0002",
value="3",
)


@pytest.mark.asyncio
async def test_refresh_state(switch_actuator):
"""Test refreshing the state of the switch."""
Expand Down

0 comments on commit 429a391

Please sign in to comment.