mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 16:57:10 +00:00
Powerview Implement remaining types (#80097)
This commit is contained in:
parent
1262c0e221
commit
cc13641f29
@ -48,6 +48,13 @@ BUTTONS: Final = [
|
|||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
press_action=lambda shade: shade.jog(),
|
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 contextlib import suppress
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
from math import ceil
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aiopvapi.helpers.constants import (
|
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):
|
class PowerViewShadeDualRailBase(PowerViewShade):
|
||||||
"""Representation of a shade with top/down bottom/up capabilities.
|
"""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 = {
|
TYPE_TO_CLASSES = {
|
||||||
|
0: (PowerViewShade,),
|
||||||
1: (PowerViewShadeWithTiltOnClosed,),
|
1: (PowerViewShadeWithTiltOnClosed,),
|
||||||
2: (PowerViewShadeWithTiltAnywhere,),
|
2: (PowerViewShadeWithTiltAnywhere,),
|
||||||
|
3: (PowerViewShade,),
|
||||||
4: (PowerViewShadeWithTiltAnywhere,),
|
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…
x
Reference in New Issue
Block a user