diff --git a/blebox_uniapi/cover.py b/blebox_uniapi/cover.py index 553001c..0236fdf 100644 --- a/blebox_uniapi/cover.py +++ b/blebox_uniapi/cover.py @@ -33,12 +33,21 @@ class ShutterBoxControlType(IntEnum): SEGMENTED_SHUTTER = 1 NO_CALIBRATION = 2 - TILT_SHUTTER = 3 + TILT_SHUTTER_90 = 3 WINDOW_OPENER = 4 MATERIAL_SHUTTER = 5 AWNING = 6 SCREEN = 7 CURTAIN = 8 + ROOF_SEGMENTED_SHUTTER = 9 + SERVOMOTOR = 10 + CURTAIN_ONE_SIDED = 11 + CURTAIN_TWO_SIDED = 12 + TILT_ONLY = 13 + PERGOLA_ROOF = 14 + PERGOLA_ROOF_TILT_ONLY = 15 + TILT_SHUTTER_180 = 16 + TILT_SHUTTER_WITHOUT_POSITIONING = 17 class GateBoxControlType(IntEnum): @@ -155,7 +164,34 @@ def min_position(self) -> int: @property def has_tilt(self) -> bool: - return self._control_type == ShutterBoxControlType.TILT_SHUTTER + return self._control_type in ( + ShutterBoxControlType.TILT_SHUTTER_90, + ShutterBoxControlType.TILT_SHUTTER_180, + ShutterBoxControlType.TILT_ONLY, + ShutterBoxControlType.PERGOLA_ROOF_TILT_ONLY, + ShutterBoxControlType.TILT_SHUTTER_WITHOUT_POSITIONING, + ) + + @property + def tilt_only(self) -> bool: + return self._control_type in ( + ShutterBoxControlType.TILT_ONLY, + ShutterBoxControlType.PERGOLA_ROOF_TILT_ONLY, + ) + + @property + def is_tilt_180(self) -> bool: + return self._control_type == ShutterBoxControlType.TILT_SHUTTER_180 + + @property + def is_slider(self) -> bool: + return self._control_type not in ( + ShutterBoxControlType.NO_CALIBRATION, + ShutterBoxControlType.CURTAIN, + ShutterBoxControlType.TILT_ONLY, + ShutterBoxControlType.PERGOLA_ROOF_TILT_ONLY, + ShutterBoxControlType.TILT_SHUTTER_WITHOUT_POSITIONING, + ) def read_cover_type( self, alias: str, raw_value: Any, product: "Box" @@ -164,7 +200,7 @@ def read_cover_type( return UnifiedCoverType.SHUTTER if self._control_type == ShutterBoxControlType.NO_CALIBRATION: return UnifiedCoverType.SHUTTER - if self._control_type == ShutterBoxControlType.TILT_SHUTTER: + if self._control_type == ShutterBoxControlType.TILT_SHUTTER_90: return UnifiedCoverType.SHUTTER if self._control_type == ShutterBoxControlType.WINDOW_OPENER: return UnifiedCoverType.WINDOW @@ -176,6 +212,24 @@ def read_cover_type( return UnifiedCoverType.SHADE if self._control_type == ShutterBoxControlType.CURTAIN: return UnifiedCoverType.CURTAIN + if self._control_type == ShutterBoxControlType.ROOF_SEGMENTED_SHUTTER: + return UnifiedCoverType.SHUTTER + if self._control_type == ShutterBoxControlType.SERVOMOTOR: + return UnifiedCoverType.SHUTTER + if self._control_type == ShutterBoxControlType.CURTAIN_ONE_SIDED: + return UnifiedCoverType.CURTAIN + if self._control_type == ShutterBoxControlType.CURTAIN_TWO_SIDED: + return UnifiedCoverType.CURTAIN + if self._control_type == ShutterBoxControlType.TILT_ONLY: + return UnifiedCoverType.SHUTTER + if self._control_type == ShutterBoxControlType.PERGOLA_ROOF: + return UnifiedCoverType.AWNING + if self._control_type == ShutterBoxControlType.PERGOLA_ROOF_TILT_ONLY: + return UnifiedCoverType.AWNING + if self._control_type == ShutterBoxControlType.TILT_SHUTTER_180: + return UnifiedCoverType.SHUTTER + if self._control_type == ShutterBoxControlType.TILT_SHUTTER_WITHOUT_POSITIONING: + return UnifiedCoverType.SHUTTER class GateBox(Gate): @@ -345,6 +399,22 @@ def is_position_inverted(self) -> bool: def has_stop(self) -> bool: return self._has_stop + @property + def tilt_only(self) -> bool: + return ( + self._attributes.tilt_only + if hasattr(self._attributes, "tilt_only") + else False + ) + + @property + def is_tilt_180(self) -> bool: + return ( + self._attributes.is_tilt_180 + if hasattr(self._attributes, "is_tilt_180") + else False + ) + @property def cover_type(self) -> Optional[UnifiedCoverType]: return self._cover_type diff --git a/tests/test_cover.py b/tests/test_cover.py index ecffcf1..d7d46cc 100644 --- a/tests/test_cover.py +++ b/tests/test_cover.py @@ -6,6 +6,7 @@ from blebox_uniapi.box_types import get_latest_api_level from blebox_uniapi import error +from blebox_uniapi.cover import Shutter, ShutterBoxControlType, UnifiedCoverType from .conftest import CommonEntity, DefaultBoxTest, future_date, jmerge @@ -142,6 +143,114 @@ def assert_state(self, entity, state): assert entity.is_closed is closed +class TestShutterAttributes: + """Unit tests for Shutter attribute properties based on control type.""" + + @pytest.mark.parametrize( + "control_type", + [ + ShutterBoxControlType.TILT_SHUTTER_90, + ShutterBoxControlType.TILT_SHUTTER_180, + ShutterBoxControlType.TILT_ONLY, + ShutterBoxControlType.PERGOLA_ROOF_TILT_ONLY, + ShutterBoxControlType.TILT_SHUTTER_WITHOUT_POSITIONING, + ], + ) + def test_has_tilt_true(self, control_type): + assert Shutter(control_type).has_tilt is True + + @pytest.mark.parametrize( + "control_type", + [ + ShutterBoxControlType.SEGMENTED_SHUTTER, + ShutterBoxControlType.NO_CALIBRATION, + ShutterBoxControlType.WINDOW_OPENER, + ShutterBoxControlType.PERGOLA_ROOF, + ShutterBoxControlType.CURTAIN, + ], + ) + def test_has_tilt_false(self, control_type): + assert Shutter(control_type).has_tilt is False + + @pytest.mark.parametrize( + "control_type", + [ + ShutterBoxControlType.TILT_ONLY, + ShutterBoxControlType.PERGOLA_ROOF_TILT_ONLY, + ], + ) + def test_tilt_only_true(self, control_type): + assert Shutter(control_type).tilt_only is True + + @pytest.mark.parametrize( + "control_type", + [ + ShutterBoxControlType.TILT_SHUTTER_90, + ShutterBoxControlType.TILT_SHUTTER_180, + ShutterBoxControlType.TILT_SHUTTER_WITHOUT_POSITIONING, + ShutterBoxControlType.SEGMENTED_SHUTTER, + ], + ) + def test_tilt_only_false(self, control_type): + assert Shutter(control_type).tilt_only is False + + def test_is_tilt_180_true(self): + assert Shutter(ShutterBoxControlType.TILT_SHUTTER_180).is_tilt_180 is True + + @pytest.mark.parametrize( + "control_type", + [ + ShutterBoxControlType.TILT_SHUTTER_90, + ShutterBoxControlType.TILT_ONLY, + ShutterBoxControlType.TILT_SHUTTER_WITHOUT_POSITIONING, + ], + ) + def test_is_tilt_180_false(self, control_type): + assert Shutter(control_type).is_tilt_180 is False + + @pytest.mark.parametrize( + "control_type", + [ + ShutterBoxControlType.NO_CALIBRATION, + ShutterBoxControlType.CURTAIN, + ShutterBoxControlType.TILT_ONLY, + ShutterBoxControlType.PERGOLA_ROOF_TILT_ONLY, + ShutterBoxControlType.TILT_SHUTTER_WITHOUT_POSITIONING, + ], + ) + def test_is_slider_false(self, control_type): + assert Shutter(control_type).is_slider is False + + @pytest.mark.parametrize( + "control_type", + [ + ShutterBoxControlType.TILT_SHUTTER_90, + ShutterBoxControlType.TILT_SHUTTER_180, + ShutterBoxControlType.PERGOLA_ROOF, + ShutterBoxControlType.SEGMENTED_SHUTTER, + ], + ) + def test_is_slider_true(self, control_type): + assert Shutter(control_type).is_slider is True + + @pytest.mark.parametrize( + "control_type, expected", + [ + (ShutterBoxControlType.ROOF_SEGMENTED_SHUTTER, UnifiedCoverType.SHUTTER), + (ShutterBoxControlType.SERVOMOTOR, UnifiedCoverType.SHUTTER), + (ShutterBoxControlType.CURTAIN_ONE_SIDED, UnifiedCoverType.CURTAIN), + (ShutterBoxControlType.CURTAIN_TWO_SIDED, UnifiedCoverType.CURTAIN), + (ShutterBoxControlType.TILT_ONLY, UnifiedCoverType.SHUTTER), + (ShutterBoxControlType.PERGOLA_ROOF, UnifiedCoverType.AWNING), + (ShutterBoxControlType.PERGOLA_ROOF_TILT_ONLY, UnifiedCoverType.AWNING), + (ShutterBoxControlType.TILT_SHUTTER_180, UnifiedCoverType.SHUTTER), + (ShutterBoxControlType.TILT_SHUTTER_WITHOUT_POSITIONING, UnifiedCoverType.SHUTTER), + ], + ) + def test_read_cover_type_new_types(self, control_type, expected): + assert Shutter(control_type).read_cover_type(None, None, None) == expected + + class TestShutter(CoverTest): """Tests for cover devices representing a BleBox ShutterBox."""