Skip to content

Commit 59aba33

Browse files
authored
Add support for more cover devices in Fibaro (home-assistant#146486)
1 parent 864e440 commit 59aba33

File tree

3 files changed

+327
-46
lines changed

3 files changed

+327
-46
lines changed

homeassistant/components/fibaro/cover.py

Lines changed: 77 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,66 +28,52 @@ async def async_setup_entry(
2828
) -> None:
2929
"""Set up the Fibaro covers."""
3030
controller = entry.runtime_data
31-
async_add_entities(
32-
[FibaroCover(device) for device in controller.fibaro_devices[Platform.COVER]],
33-
True,
34-
)
3531

32+
entities: list[FibaroEntity] = []
33+
for device in controller.fibaro_devices[Platform.COVER]:
34+
# Positionable covers report the position over value
35+
if device.value.has_value:
36+
entities.append(PositionableFibaroCover(device))
37+
else:
38+
entities.append(FibaroCover(device))
39+
async_add_entities(entities, True)
3640

37-
class FibaroCover(FibaroEntity, CoverEntity):
38-
"""Representation a Fibaro Cover."""
41+
42+
class PositionableFibaroCover(FibaroEntity, CoverEntity):
43+
"""Representation of a fibaro cover which supports positioning."""
3944

4045
def __init__(self, fibaro_device: DeviceModel) -> None:
41-
"""Initialize the Vera device."""
46+
"""Initialize the device."""
4247
super().__init__(fibaro_device)
4348
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
4449

45-
if self._is_open_close_only():
46-
self._attr_supported_features = (
47-
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
48-
)
49-
if "stop" in self.fibaro_device.actions:
50-
self._attr_supported_features |= CoverEntityFeature.STOP
51-
5250
@staticmethod
53-
def bound(position):
51+
def bound(position: int | None) -> int | None:
5452
"""Normalize the position."""
5553
if position is None:
5654
return None
57-
position = int(position)
5855
if position <= 5:
5956
return 0
6057
if position >= 95:
6158
return 100
6259
return position
6360

64-
def _is_open_close_only(self) -> bool:
65-
"""Return if only open / close is supported."""
66-
# Normally positionable devices report the position over value,
67-
# so if it is missing we have a device which supports open / close only
68-
return not self.fibaro_device.value.has_value
69-
7061
def update(self) -> None:
7162
"""Update the state."""
7263
super().update()
7364

7465
self._attr_current_cover_position = self.bound(self.level)
7566
self._attr_current_cover_tilt_position = self.bound(self.level2)
7667

77-
device_state = self.fibaro_device.state
78-
7968
# Be aware that opening and closing is only available for some modern
8069
# devices.
8170
# For example the Fibaro Roller Shutter 4 reports this correctly.
82-
if device_state.has_value:
83-
self._attr_is_opening = device_state.str_value().lower() == "opening"
84-
self._attr_is_closing = device_state.str_value().lower() == "closing"
71+
device_state = self.fibaro_device.state.str_value(default="").lower()
72+
self._attr_is_opening = device_state == "opening"
73+
self._attr_is_closing = device_state == "closing"
8574

8675
closed: bool | None = None
87-
if self._is_open_close_only():
88-
if device_state.has_value and device_state.str_value().lower() != "unknown":
89-
closed = device_state.str_value().lower() == "closed"
90-
elif self.current_cover_position is not None:
76+
if self.current_cover_position is not None:
9177
closed = self.current_cover_position == 0
9278
self._attr_is_closed = closed
9379

@@ -96,7 +82,7 @@ def set_cover_position(self, **kwargs: Any) -> None:
9682
self.set_level(cast(int, kwargs.get(ATTR_POSITION)))
9783

9884
def set_cover_tilt_position(self, **kwargs: Any) -> None:
99-
"""Move the cover to a specific position."""
85+
"""Move the slats to a specific position."""
10086
self.set_level2(cast(int, kwargs.get(ATTR_TILT_POSITION)))
10187

10288
def open_cover(self, **kwargs: Any) -> None:
@@ -118,3 +104,62 @@ def close_cover_tilt(self, **kwargs: Any) -> None:
118104
def stop_cover(self, **kwargs: Any) -> None:
119105
"""Stop the cover."""
120106
self.action("stop")
107+
108+
109+
class FibaroCover(FibaroEntity, CoverEntity):
110+
"""Representation of a fibaro cover which supports only open / close commands."""
111+
112+
def __init__(self, fibaro_device: DeviceModel) -> None:
113+
"""Initialize the device."""
114+
super().__init__(fibaro_device)
115+
self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)
116+
117+
self._attr_supported_features = (
118+
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
119+
)
120+
if "stop" in self.fibaro_device.actions:
121+
self._attr_supported_features |= CoverEntityFeature.STOP
122+
if "rotateSlatsUp" in self.fibaro_device.actions:
123+
self._attr_supported_features |= CoverEntityFeature.OPEN_TILT
124+
if "rotateSlatsDown" in self.fibaro_device.actions:
125+
self._attr_supported_features |= CoverEntityFeature.CLOSE_TILT
126+
if "stopSlats" in self.fibaro_device.actions:
127+
self._attr_supported_features |= CoverEntityFeature.STOP_TILT
128+
129+
def update(self) -> None:
130+
"""Update the state."""
131+
super().update()
132+
133+
device_state = self.fibaro_device.state.str_value(default="").lower()
134+
135+
self._attr_is_opening = device_state == "opening"
136+
self._attr_is_closing = device_state == "closing"
137+
138+
closed: bool | None = None
139+
if device_state not in {"", "unknown"}:
140+
closed = device_state == "closed"
141+
self._attr_is_closed = closed
142+
143+
def open_cover(self, **kwargs: Any) -> None:
144+
"""Open the cover."""
145+
self.action("open")
146+
147+
def close_cover(self, **kwargs: Any) -> None:
148+
"""Close the cover."""
149+
self.action("close")
150+
151+
def stop_cover(self, **kwargs: Any) -> None:
152+
"""Stop the cover."""
153+
self.action("stop")
154+
155+
def open_cover_tilt(self, **kwargs: Any) -> None:
156+
"""Open the cover slats."""
157+
self.action("rotateSlatsUp")
158+
159+
def close_cover_tilt(self, **kwargs: Any) -> None:
160+
"""Close the cover slats."""
161+
self.action("rotateSlatsDown")
162+
163+
def stop_cover_tilt(self, **kwargs: Any) -> None:
164+
"""Stop the cover slats turning."""
165+
self.action("stopSlats")

tests/components/fibaro/conftest.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ def mock_power_sensor() -> Mock:
8383

8484

8585
@pytest.fixture
86-
def mock_cover() -> Mock:
87-
"""Fixture for a cover."""
86+
def mock_positionable_cover() -> Mock:
87+
"""Fixture for a positionable cover."""
8888
cover = Mock()
8989
cover.fibaro_id = 3
9090
cover.parent_fibaro_id = 0
@@ -112,6 +112,42 @@ def mock_cover() -> Mock:
112112
return cover
113113

114114

115+
@pytest.fixture
116+
def mock_cover() -> Mock:
117+
"""Fixture for a cover supporting slats but without positioning."""
118+
cover = Mock()
119+
cover.fibaro_id = 4
120+
cover.parent_fibaro_id = 0
121+
cover.name = "Test cover"
122+
cover.room_id = 1
123+
cover.dead = False
124+
cover.visible = True
125+
cover.enabled = True
126+
cover.type = "com.fibaro.baseShutter"
127+
cover.base_type = "com.fibaro.actor"
128+
cover.properties = {"manufacturer": ""}
129+
cover.actions = {
130+
"open": 0,
131+
"close": 0,
132+
"stop": 0,
133+
"rotateSlatsUp": 0,
134+
"rotateSlatsDown": 0,
135+
"stopSlats": 0,
136+
}
137+
cover.supported_features = {}
138+
value_mock = Mock()
139+
value_mock.has_value = False
140+
cover.value = value_mock
141+
value2_mock = Mock()
142+
value2_mock.has_value = False
143+
cover.value_2 = value2_mock
144+
state_mock = Mock()
145+
state_mock.has_value = True
146+
state_mock.str_value.return_value = "closed"
147+
cover.state = state_mock
148+
return cover
149+
150+
115151
@pytest.fixture
116152
def mock_light() -> Mock:
117153
"""Fixture for a dimmmable light."""

0 commit comments

Comments
 (0)