99)
1010from homeassistant .components .cover import (
1111 ATTR_CURRENT_POSITION ,
12+ ATTR_CURRENT_TILT_POSITION ,
1213 ATTR_POSITION ,
14+ ATTR_TILT_POSITION ,
1315 CoverDeviceClass ,
1416 CoverEntity ,
1517 CoverEntityFeature ,
@@ -32,11 +34,18 @@ async def async_setup_entry(
3234 """Set up the demo cover platform."""
3335
3436 coordinator : PVCoordinator = config_entry .runtime_data
35- async_add_entities ([PowerViewCover (coordinator )])
37+ model : Final [str | None ] = coordinator .dev_details .get ("model" )
38+ entities : list [PowerViewCover ] = []
39+ if model in ["39" ]:
40+ entities .append (PowerViewCoverTiltOnly (coordinator ))
41+ else :
42+ entities .append (PowerViewCover (coordinator ))
43+
44+ async_add_entities (entities )
3645
3746
3847class PowerViewCover (PassiveBluetoothCoordinatorEntity [PVCoordinator ], CoverEntity ): # type: ignore[reportIncompatibleVariableOverride]
39- """Representation of a powerview shade."""
48+ """Representation of a PowerView shade with Up/Down functionality only ."""
4049
4150 _attr_has_entity_name = True
4251 _attr_device_class = CoverDeviceClass .SHADE
@@ -52,8 +61,9 @@ def __init__(
5261 coordinator : PVCoordinator ,
5362 ) -> None :
5463 """Initialize the shade."""
64+ LOGGER .debug ("%s: init() PowerViewCover" , coordinator .name )
5565 self ._attr_name = CoverDeviceClass .SHADE
56- self ._coord = coordinator
66+ self ._coord : PVCoordinator = coordinator
5767 self ._attr_device_info = self ._coord .device_info
5868 self ._target_position : int | None = round (
5969 self ._coord .data .get (ATTR_CURRENT_POSITION , OPEN_POSITION )
@@ -171,3 +181,117 @@ async def async_stop_cover(self, **kwargs: Any) -> None:
171181 self .async_write_ha_state ()
172182 except BleakError as err :
173183 LOGGER .error ("Failed to stop cover '%s': %s" , self .name , err )
184+
185+
186+ class PowerViewCoverTilt (PowerViewCover ):
187+ """Representation of a PowerView shade with additional tilt functionality."""
188+
189+ _attr_supported_features = (
190+ CoverEntityFeature .OPEN
191+ | CoverEntityFeature .CLOSE
192+ | CoverEntityFeature .STOP
193+ | CoverEntityFeature .SET_POSITION
194+ | CoverEntityFeature .OPEN_TILT
195+ | CoverEntityFeature .CLOSE_TILT
196+ | CoverEntityFeature .STOP_TILT
197+ | CoverEntityFeature .SET_TILT_POSITION
198+ )
199+
200+ def __init__ (
201+ self ,
202+ coordinator : PVCoordinator ,
203+ ) -> None :
204+ """Initialize the shade with tilt."""
205+ LOGGER .debug ("%s: init() PowerViewCoverTilt" , coordinator .name )
206+ super ().__init__ (coordinator )
207+
208+ @property
209+ def current_cover_tilt_position (self ) -> int | None : # type: ignore[reportIncompatibleVariableOverride]
210+ """Return current tilt of cover.
211+
212+ None is unknown
213+ """
214+ pos : Final = self ._coord .data .get (ATTR_CURRENT_TILT_POSITION )
215+ return round (pos ) if pos is not None else None
216+
217+ async def async_set_cover_tilt_position (self , ** kwargs : Any ) -> None :
218+ """Move the tilt to a specific position."""
219+
220+ if isinstance (target_position := kwargs .get (ATTR_TILT_POSITION ), int ):
221+ LOGGER .debug ("set cover tilt to position %i" , target_position )
222+ if (
223+ self .current_cover_tilt_position == round (target_position )
224+ or self .current_cover_position is None
225+ ):
226+ return
227+
228+ try :
229+ await self ._coord .api .set_position (
230+ self .current_cover_position , tilt = target_position
231+ )
232+ self .async_write_ha_state ()
233+ except BleakError as err :
234+ LOGGER .error (
235+ "Failed to tilt cover '%s' to %f%%: %s" ,
236+ self .name ,
237+ target_position ,
238+ err ,
239+ )
240+
241+ async def async_stop_cover_tilt (self , ** kwargs : Any ) -> None :
242+ """Stop the cover."""
243+ await self .async_stop_cover (kwargs = kwargs )
244+
245+ async def async_open_cover_tilt (self , ** kwargs : Any ) -> None :
246+ """Open the cover tilt."""
247+ LOGGER .debug ("open cover tilt" )
248+ _kwargs = {** kwargs , ATTR_TILT_POSITION : OPEN_POSITION }
249+ await self .async_set_cover_tilt_position (** _kwargs )
250+
251+ async def async_close_cover_tilt (self , ** kwargs : Any ) -> None :
252+ """Close the cover tilt."""
253+ LOGGER .debug ("close cover tilt" )
254+ _kwargs = {** kwargs , ATTR_TILT_POSITION : CLOSED_POSITION }
255+ await self .async_set_cover_tilt_position (** _kwargs )
256+
257+
258+ class PowerViewCoverTiltOnly (PowerViewCoverTilt ):
259+ """Representation of a PowerView shade with additional tilt functionality."""
260+
261+ OPENCLOSED_THRESHOLD = 5
262+
263+ _attr_device_class = CoverDeviceClass .BLIND
264+ _attr_supported_features = (
265+ CoverEntityFeature .OPEN_TILT
266+ | CoverEntityFeature .CLOSE_TILT
267+ | CoverEntityFeature .STOP_TILT
268+ | CoverEntityFeature .SET_TILT_POSITION
269+ )
270+
271+ def __init__ (
272+ self ,
273+ coordinator : PVCoordinator ,
274+ ) -> None :
275+ """Initialize the shade with tilt only."""
276+ LOGGER .debug ("%s: init() PowerViewCoverTiltOnly" , coordinator .name )
277+ super ().__init__ (coordinator )
278+
279+ @property
280+ def is_opening (self ) -> bool | None : # type: ignore[reportIncompatibleVariableOverride]
281+ """Return if the cover is opening or not."""
282+ return False
283+
284+ @property
285+ def is_closing (self ) -> bool | None : # type: ignore[reportIncompatibleVariableOverride]
286+ """Return if the cover is closing or not."""
287+ return False
288+
289+ @property
290+ def is_closed (self ) -> bool : # type: ignore[reportIncompatibleVariableOverride]
291+ """Return if the cover is closed."""
292+ return isinstance (self .current_cover_tilt_position , int ) and (
293+ self .current_cover_tilt_position
294+ >= OPEN_POSITION - PowerViewCoverTiltOnly .OPENCLOSED_THRESHOLD
295+ or self .current_cover_tilt_position
296+ <= CLOSED_POSITION + PowerViewCoverTiltOnly .OPENCLOSED_THRESHOLD
297+ )
0 commit comments