Powerview Implement remaining types (#80097)
This commit is contained in:
parent
1262c0e221
commit
cc13641f29
2 changed files with 404 additions and 1 deletions
|
@ -48,6 +48,13 @@ BUTTONS: Final = [
|
|||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
press_action=lambda shade: shade.jog(),
|
||||
),
|
||||
PowerviewButtonDescription(
|
||||
key="favorite",
|
||||
name="Favorite",
|
||||
icon="mdi:heart",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
press_action=lambda shade: shade.favorite(),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ from collections.abc import Iterable
|
|||
from contextlib import suppress
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from math import ceil
|
||||
from typing import Any
|
||||
|
||||
from aiopvapi.helpers.constants import (
|
||||
|
@ -541,6 +542,58 @@ class PowerViewShadeWithTiltAnywhere(PowerViewShadeWithTiltBase):
|
|||
)
|
||||
|
||||
|
||||
class PowerViewShadeTiltOnly(PowerViewShadeWithTiltBase):
|
||||
"""Representation of a shade with tilt only capability, no move.
|
||||
|
||||
API Class: ShadeTiltOnly
|
||||
|
||||
Type 5 - Tilt Only 180°
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PowerviewShadeUpdateCoordinator,
|
||||
device_info: PowerviewDeviceInfo,
|
||||
room_name: str,
|
||||
shade: BaseShade,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize the shade."""
|
||||
super().__init__(coordinator, device_info, room_name, shade, name)
|
||||
self._attr_supported_features = (
|
||||
CoverEntityFeature.OPEN_TILT
|
||||
| CoverEntityFeature.CLOSE_TILT
|
||||
| CoverEntityFeature.SET_TILT_POSITION
|
||||
)
|
||||
if self._device_info.model != LEGACY_DEVICE_MODEL:
|
||||
self._attr_supported_features |= CoverEntityFeature.STOP_TILT
|
||||
self._max_tilt = self._shade.shade_limits.tilt_max
|
||||
|
||||
|
||||
class PowerViewShadeTopDown(PowerViewShade):
|
||||
"""Representation of a shade that lowers from the roof to the floor.
|
||||
|
||||
These shades are inverted where MAX_POSITION equates to closed and MIN_POSITION is open
|
||||
API Class: ShadeTopDown
|
||||
|
||||
Type 6 - Top Down
|
||||
"""
|
||||
|
||||
@property
|
||||
def current_cover_position(self) -> int:
|
||||
"""Return the current position of cover."""
|
||||
return hd_position_to_hass(MAX_POSITION - self.positions.primary, MAX_POSITION)
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool:
|
||||
"""Return if the cover is closed."""
|
||||
return (MAX_POSITION - self.positions.primary) <= CLOSED_POSITION
|
||||
|
||||
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
||||
"""Move the shade to a specific position."""
|
||||
await self._async_set_cover_position(100 - kwargs[ATTR_POSITION])
|
||||
|
||||
|
||||
class PowerViewShadeDualRailBase(PowerViewShade):
|
||||
"""Representation of a shade with top/down bottom/up capabilities.
|
||||
|
||||
|
@ -677,11 +730,354 @@ class PowerViewShadeTDBUTop(PowerViewShadeDualRailBase):
|
|||
)
|
||||
|
||||
|
||||
class PowerViewShadeDualOverlappedBase(PowerViewShade):
|
||||
"""Represent a shade that has a front sheer and rear blackout panel.
|
||||
|
||||
This equates to two shades being controlled by one motor
|
||||
"""
|
||||
|
||||
@property
|
||||
def transition_steps(self) -> int:
|
||||
"""Return the steps to make a move."""
|
||||
# poskind 1 represents the second half of the shade in hass
|
||||
# front must be fully closed before rear can move
|
||||
# 51 - 100 is equiv to 1-100 on other shades - one motor, two shades
|
||||
primary = (hd_position_to_hass(self.positions.primary, MAX_POSITION) / 2) + 50
|
||||
# poskind 2 represents the shade first half of the shade in hass
|
||||
# rear (blackout) must be fully open before front can move
|
||||
# 51 - 100 is equiv to 1-100 on other shades - one motor, two shades
|
||||
secondary = hd_position_to_hass(self.positions.secondary, MAX_POSITION) / 2
|
||||
return ceil(primary + secondary)
|
||||
|
||||
@property
|
||||
def open_position(self) -> PowerviewShadeMove:
|
||||
"""Return the open position and required additional positions."""
|
||||
return PowerviewShadeMove(
|
||||
{
|
||||
ATTR_POSITION1: MAX_POSITION,
|
||||
ATTR_POSKIND1: POS_KIND_PRIMARY,
|
||||
},
|
||||
{POS_KIND_SECONDARY: MIN_POSITION, POS_KIND_VANE: MIN_POSITION},
|
||||
)
|
||||
|
||||
@property
|
||||
def close_position(self) -> PowerviewShadeMove:
|
||||
"""Return the open position and required additional positions."""
|
||||
return PowerviewShadeMove(
|
||||
{
|
||||
ATTR_POSITION1: MIN_POSITION,
|
||||
ATTR_POSKIND1: POS_KIND_SECONDARY,
|
||||
},
|
||||
{POS_KIND_PRIMARY: MIN_POSITION, POS_KIND_VANE: MIN_POSITION},
|
||||
)
|
||||
|
||||
|
||||
class PowerViewShadeDualOverlappedCombined(PowerViewShadeDualOverlappedBase):
|
||||
"""Represent a shade that has a front sheer and rear blackout panel.
|
||||
|
||||
This equates to two shades being controlled by one motor.
|
||||
The front shade must be completely down before the rear shade will move.
|
||||
Sibling Class: PowerViewShadeDualOverlappedFront, PowerViewShadeDualOverlappedRear
|
||||
API Class: ShadeDualOverlapped
|
||||
|
||||
Type 8 - Duolite (front and rear shades)
|
||||
"""
|
||||
|
||||
# type
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PowerviewShadeUpdateCoordinator,
|
||||
device_info: PowerviewDeviceInfo,
|
||||
room_name: str,
|
||||
shade: BaseShade,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize the shade."""
|
||||
super().__init__(coordinator, device_info, room_name, shade, name)
|
||||
self._attr_unique_id = f"{self._shade.id}_combined"
|
||||
self._attr_name = f"{self._shade_name} Combined"
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool:
|
||||
"""Return if the cover is closed."""
|
||||
# if rear shade is down it is closed
|
||||
return self.positions.secondary <= CLOSED_POSITION
|
||||
|
||||
@property
|
||||
def current_cover_position(self) -> int:
|
||||
"""Return the current position of cover."""
|
||||
# if front is open return that (other positions are impossible)
|
||||
# if front shade is closed get position of rear
|
||||
position = (hd_position_to_hass(self.positions.primary, MAX_POSITION) / 2) + 50
|
||||
if self.positions.primary == MIN_POSITION:
|
||||
position = hd_position_to_hass(self.positions.secondary, MAX_POSITION) / 2
|
||||
|
||||
return ceil(position)
|
||||
|
||||
@callback
|
||||
def _get_shade_move(self, target_hass_position: int) -> PowerviewShadeMove:
|
||||
position_shade = hass_position_to_hd(target_hass_position, MAX_POSITION)
|
||||
# note we set POS_KIND_VANE: MIN_POSITION here even with shades without tilt so no additional
|
||||
# override is required for differences between type 8/9/10
|
||||
# this just stores the value in the coordinator for future reference
|
||||
if target_hass_position <= 50:
|
||||
target_hass_position = target_hass_position * 2
|
||||
return PowerviewShadeMove(
|
||||
{
|
||||
ATTR_POSITION1: position_shade,
|
||||
ATTR_POSKIND1: POS_KIND_SECONDARY,
|
||||
},
|
||||
{POS_KIND_PRIMARY: MIN_POSITION, POS_KIND_VANE: MIN_POSITION},
|
||||
)
|
||||
|
||||
# 51 <= target_hass_position <= 100 (51-100 represents front sheer shade)
|
||||
target_hass_position = (target_hass_position - 50) * 2
|
||||
return PowerviewShadeMove(
|
||||
{
|
||||
ATTR_POSITION1: position_shade,
|
||||
ATTR_POSKIND1: POS_KIND_PRIMARY,
|
||||
},
|
||||
{POS_KIND_SECONDARY: MAX_POSITION, POS_KIND_VANE: MIN_POSITION},
|
||||
)
|
||||
|
||||
|
||||
class PowerViewShadeDualOverlappedFront(PowerViewShadeDualOverlappedBase):
|
||||
"""Represent the shade front panel - These have a blackout panel too.
|
||||
|
||||
This equates to two shades being controlled by one motor.
|
||||
The front shade must be completely down before the rear shade will move.
|
||||
Sibling Class: PowerViewShadeDualOverlappedCombined, PowerViewShadeDualOverlappedRear
|
||||
API Class: ShadeDualOverlapped + ShadeDualOverlappedTilt90 + ShadeDualOverlappedTilt180
|
||||
|
||||
Type 8 - Duolite (front and rear shades)
|
||||
Type 9 - Duolite with 90° Tilt (front bottom up shade that also tilts plus a rear blackout (non-tilting) shade)
|
||||
Type 10 - Duolite with 180° Tilt
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PowerviewShadeUpdateCoordinator,
|
||||
device_info: PowerviewDeviceInfo,
|
||||
room_name: str,
|
||||
shade: BaseShade,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize the shade."""
|
||||
super().__init__(coordinator, device_info, room_name, shade, name)
|
||||
self._attr_unique_id = f"{self._shade.id}_front"
|
||||
self._attr_name = f"{self._shade_name} Front"
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Certain shades create multiple entities.
|
||||
|
||||
Do not poll shade multiple times. Combined shade will return data
|
||||
and multiple polling will cause timeouts.
|
||||
"""
|
||||
return False
|
||||
|
||||
@callback
|
||||
def _get_shade_move(self, target_hass_position: int) -> PowerviewShadeMove:
|
||||
position_shade = hass_position_to_hd(target_hass_position, MAX_POSITION)
|
||||
# note we set POS_KIND_VANE: MIN_POSITION here even with shades without tilt so no additional
|
||||
# override is required for differences between type 8/9/10
|
||||
# this just stores the value in the coordinator for future reference
|
||||
return PowerviewShadeMove(
|
||||
{
|
||||
ATTR_POSITION1: position_shade,
|
||||
ATTR_POSKIND1: POS_KIND_PRIMARY,
|
||||
},
|
||||
{POS_KIND_SECONDARY: MAX_POSITION, POS_KIND_VANE: MIN_POSITION},
|
||||
)
|
||||
|
||||
@property
|
||||
def close_position(self) -> PowerviewShadeMove:
|
||||
"""Return the close position and required additional positions."""
|
||||
return PowerviewShadeMove(
|
||||
{
|
||||
ATTR_POSITION1: MIN_POSITION,
|
||||
ATTR_POSKIND1: POS_KIND_PRIMARY,
|
||||
},
|
||||
{POS_KIND_SECONDARY: MAX_POSITION, POS_KIND_VANE: MIN_POSITION},
|
||||
)
|
||||
|
||||
|
||||
class PowerViewShadeDualOverlappedRear(PowerViewShadeDualOverlappedBase):
|
||||
"""Represent the shade front panel - These have a blackout panel too.
|
||||
|
||||
This equates to two shades being controlled by one motor.
|
||||
The front shade must be completely down before the rear shade will move.
|
||||
Sibling Class: PowerViewShadeDualOverlappedCombined, PowerViewShadeDualOverlappedFront
|
||||
API Class: ShadeDualOverlapped + ShadeDualOverlappedTilt90 + ShadeDualOverlappedTilt180
|
||||
|
||||
Type 8 - Duolite (front and rear shades)
|
||||
Type 9 - Duolite with 90° Tilt (front bottom up shade that also tilts plus a rear blackout (non-tilting) shade)
|
||||
Type 10 - Duolite with 180° Tilt
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PowerviewShadeUpdateCoordinator,
|
||||
device_info: PowerviewDeviceInfo,
|
||||
room_name: str,
|
||||
shade: BaseShade,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize the shade."""
|
||||
super().__init__(coordinator, device_info, room_name, shade, name)
|
||||
self._attr_unique_id = f"{self._shade.id}_rear"
|
||||
self._attr_name = f"{self._shade_name} Rear"
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Certain shades create multiple entities.
|
||||
|
||||
Do not poll shade multiple times. Combined shade will return data
|
||||
and multiple polling will cause timeouts.
|
||||
"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool:
|
||||
"""Return if the cover is closed."""
|
||||
# if rear shade is down it is closed
|
||||
return self.positions.secondary <= CLOSED_POSITION
|
||||
|
||||
@property
|
||||
def current_cover_position(self) -> int:
|
||||
"""Return the current position of cover."""
|
||||
return hd_position_to_hass(self.positions.secondary, MAX_POSITION)
|
||||
|
||||
@callback
|
||||
def _get_shade_move(self, target_hass_position: int) -> PowerviewShadeMove:
|
||||
position_shade = hass_position_to_hd(target_hass_position, MAX_POSITION)
|
||||
# note we set POS_KIND_VANE: MIN_POSITION here even with shades without tilt so no additional
|
||||
# override is required for differences between type 8/9/10
|
||||
# this just stores the value in the coordinator for future reference
|
||||
return PowerviewShadeMove(
|
||||
{
|
||||
ATTR_POSITION1: position_shade,
|
||||
ATTR_POSKIND1: POS_KIND_SECONDARY,
|
||||
},
|
||||
{POS_KIND_PRIMARY: MIN_POSITION, POS_KIND_VANE: MIN_POSITION},
|
||||
)
|
||||
|
||||
@property
|
||||
def open_position(self) -> PowerviewShadeMove:
|
||||
"""Return the open position and required additional positions."""
|
||||
return PowerviewShadeMove(
|
||||
{
|
||||
ATTR_POSITION1: MAX_POSITION,
|
||||
ATTR_POSKIND1: POS_KIND_SECONDARY,
|
||||
},
|
||||
{POS_KIND_PRIMARY: MIN_POSITION, POS_KIND_VANE: MIN_POSITION},
|
||||
)
|
||||
|
||||
|
||||
class PowerViewShadeDualOverlappedCombinedTilt(PowerViewShadeDualOverlappedCombined):
|
||||
"""Represent a shade that has a front sheer and rear blackout panel.
|
||||
|
||||
This equates to two shades being controlled by one motor.
|
||||
The front shade must be completely down before the rear shade will move.
|
||||
Tilting this shade will also force positional change of the main roller.
|
||||
|
||||
Sibling Class: PowerViewShadeDualOverlappedFront, PowerViewShadeDualOverlappedRear
|
||||
API Class: ShadeDualOverlappedTilt90 + ShadeDualOverlappedTilt180
|
||||
|
||||
Type 9 - Duolite with 90° Tilt (front bottom up shade that also tilts plus a rear blackout (non-tilting) shade)
|
||||
Type 10 - Duolite with 180° Tilt
|
||||
"""
|
||||
|
||||
# type
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PowerviewShadeUpdateCoordinator,
|
||||
device_info: PowerviewDeviceInfo,
|
||||
room_name: str,
|
||||
shade: BaseShade,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize the shade."""
|
||||
super().__init__(coordinator, device_info, room_name, shade, name)
|
||||
self._attr_supported_features |= (
|
||||
CoverEntityFeature.OPEN_TILT
|
||||
| CoverEntityFeature.CLOSE_TILT
|
||||
| CoverEntityFeature.SET_TILT_POSITION
|
||||
)
|
||||
if self._device_info.model != LEGACY_DEVICE_MODEL:
|
||||
self._attr_supported_features |= CoverEntityFeature.STOP_TILT
|
||||
self._max_tilt = self._shade.shade_limits.tilt_max
|
||||
|
||||
@property
|
||||
def transition_steps(self) -> int:
|
||||
"""Return the steps to make a move."""
|
||||
# poskind 1 represents the second half of the shade in hass
|
||||
# front must be fully closed before rear can move
|
||||
# 51 - 100 is equiv to 1-100 on other shades - one motor, two shades
|
||||
primary = (hd_position_to_hass(self.positions.primary, MAX_POSITION) / 2) + 50
|
||||
# poskind 2 represents the shade first half of the shade in hass
|
||||
# rear (blackout) must be fully open before front can move
|
||||
# 51 - 100 is equiv to 1-100 on other shades - one motor, two shades
|
||||
secondary = hd_position_to_hass(self.positions.secondary, MAX_POSITION) / 2
|
||||
vane = hd_position_to_hass(self.positions.vane, self._max_tilt)
|
||||
return ceil(primary + secondary + vane)
|
||||
|
||||
@callback
|
||||
def _get_shade_tilt(self, target_hass_tilt_position: int) -> PowerviewShadeMove:
|
||||
"""Return a PowerviewShadeMove."""
|
||||
position_vane = hass_position_to_hd(target_hass_tilt_position, self._max_tilt)
|
||||
return PowerviewShadeMove(
|
||||
{
|
||||
ATTR_POSITION1: position_vane,
|
||||
ATTR_POSKIND1: POS_KIND_VANE,
|
||||
},
|
||||
{POS_KIND_PRIMARY: MIN_POSITION, POS_KIND_SECONDARY: MAX_POSITION},
|
||||
)
|
||||
|
||||
@property
|
||||
def open_tilt_position(self) -> PowerviewShadeMove:
|
||||
"""Return the open tilt position and required additional positions."""
|
||||
return PowerviewShadeMove(
|
||||
self._shade.open_position_tilt,
|
||||
{POS_KIND_PRIMARY: MIN_POSITION, POS_KIND_SECONDARY: MAX_POSITION},
|
||||
)
|
||||
|
||||
@property
|
||||
def close_tilt_position(self) -> PowerviewShadeMove:
|
||||
"""Return the open tilt position and required additional positions."""
|
||||
return PowerviewShadeMove(
|
||||
self._shade.open_position_tilt,
|
||||
{POS_KIND_PRIMARY: MIN_POSITION, POS_KIND_SECONDARY: MAX_POSITION},
|
||||
)
|
||||
|
||||
|
||||
TYPE_TO_CLASSES = {
|
||||
0: (PowerViewShade,),
|
||||
1: (PowerViewShadeWithTiltOnClosed,),
|
||||
2: (PowerViewShadeWithTiltAnywhere,),
|
||||
3: (PowerViewShade,),
|
||||
4: (PowerViewShadeWithTiltAnywhere,),
|
||||
7: (PowerViewShadeTDBUTop, PowerViewShadeTDBUBottom),
|
||||
5: (PowerViewShadeTiltOnly,),
|
||||
6: (PowerViewShadeTopDown,),
|
||||
7: (
|
||||
PowerViewShadeTDBUTop,
|
||||
PowerViewShadeTDBUBottom,
|
||||
),
|
||||
8: (
|
||||
PowerViewShadeDualOverlappedCombined,
|
||||
PowerViewShadeDualOverlappedFront,
|
||||
PowerViewShadeDualOverlappedRear,
|
||||
),
|
||||
9: (
|
||||
PowerViewShadeDualOverlappedCombinedTilt,
|
||||
PowerViewShadeDualOverlappedFront,
|
||||
PowerViewShadeDualOverlappedRear,
|
||||
),
|
||||
10: (
|
||||
PowerViewShadeDualOverlappedCombinedTilt,
|
||||
PowerViewShadeDualOverlappedFront,
|
||||
PowerViewShadeDualOverlappedRear,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue