Add entity translations to hunterdouglas powerview (#98232)

This commit is contained in:
Joost Lekkerkerker 2023-08-14 22:26:20 +02:00 committed by GitHub
parent 9713466817
commit 49a9d0e439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 81 additions and 43 deletions

View File

@ -7,7 +7,11 @@ from typing import Any, Final
from aiopvapi.resources.shade import BaseShade, factory as PvShade from aiopvapi.resources.shade import BaseShade, factory as PvShade
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.components.button import (
ButtonDeviceClass,
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -36,21 +40,20 @@ class PowerviewButtonDescription(
BUTTONS: Final = [ BUTTONS: Final = [
PowerviewButtonDescription( PowerviewButtonDescription(
key="calibrate", key="calibrate",
name="Calibrate", translation_key="calibrate",
icon="mdi:swap-vertical-circle-outline", icon="mdi:swap-vertical-circle-outline",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
press_action=lambda shade: shade.calibrate(), press_action=lambda shade: shade.calibrate(),
), ),
PowerviewButtonDescription( PowerviewButtonDescription(
key="identify", key="identify",
name="Identify", device_class=ButtonDeviceClass.IDENTIFY,
icon="mdi:crosshairs-question",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
press_action=lambda shade: shade.jog(), press_action=lambda shade: shade.jog(),
), ),
PowerviewButtonDescription( PowerviewButtonDescription(
key="favorite", key="favorite",
name="Favorite", translation_key="favorite",
icon="mdi:heart", icon="mdi:heart",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
press_action=lambda shade: shade.favorite(), press_action=lambda shade: shade.favorite(),
@ -104,7 +107,6 @@ class PowerviewButton(ShadeEntity, ButtonEntity):
"""Initialize the button entity.""" """Initialize the button entity."""
super().__init__(coordinator, device_info, room_name, shade, name) super().__init__(coordinator, device_info, room_name, shade, name)
self.entity_description: PowerviewButtonDescription = description self.entity_description: PowerviewButtonDescription = description
self._attr_name = f"{self._shade_name} {description.name}"
self._attr_unique_id = f"{self._attr_unique_id}_{description.key}" self._attr_unique_id = f"{self._attr_unique_id}_{description.key}"
async def async_press(self) -> None: async def async_press(self) -> None:

View File

@ -118,7 +118,11 @@ class PowerViewShadeBase(ShadeEntity, CoverEntity):
"""Representation of a powerview shade.""" """Representation of a powerview shade."""
_attr_device_class = CoverDeviceClass.SHADE _attr_device_class = CoverDeviceClass.SHADE
_attr_supported_features = CoverEntityFeature(0) _attr_supported_features = (
CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE
| CoverEntityFeature.SET_POSITION
)
def __init__( def __init__(
self, self,
@ -131,7 +135,6 @@ class PowerViewShadeBase(ShadeEntity, CoverEntity):
"""Initialize the shade.""" """Initialize the shade."""
super().__init__(coordinator, device_info, room_name, shade, name) super().__init__(coordinator, device_info, room_name, shade, name)
self._shade: BaseShade = shade self._shade: BaseShade = shade
self._attr_name = self._shade_name
self._scheduled_transition_update: CALLBACK_TYPE | None = None self._scheduled_transition_update: CALLBACK_TYPE | None = None
if self._device_info.model != LEGACY_DEVICE_MODEL: if self._device_info.model != LEGACY_DEVICE_MODEL:
self._attr_supported_features |= CoverEntityFeature.STOP self._attr_supported_features |= CoverEntityFeature.STOP
@ -346,26 +349,14 @@ class PowerViewShadeBase(ShadeEntity, CoverEntity):
class PowerViewShade(PowerViewShadeBase): class PowerViewShade(PowerViewShadeBase):
"""Represent a standard shade.""" """Represent a standard shade."""
def __init__( _attr_name = None
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
| CoverEntityFeature.CLOSE
| CoverEntityFeature.SET_POSITION
)
class PowerViewShadeWithTiltBase(PowerViewShade): class PowerViewShadeWithTiltBase(PowerViewShadeBase):
"""Representation for PowerView shades with tilt capabilities.""" """Representation for PowerView shades with tilt capabilities."""
_attr_name = None
def __init__( def __init__(
self, self,
coordinator: PowerviewShadeUpdateCoordinator, coordinator: PowerviewShadeUpdateCoordinator,
@ -453,9 +444,11 @@ class PowerViewShadeWithTiltOnClosed(PowerViewShadeWithTiltBase):
API Class: ShadeBottomUpTiltOnClosed + ShadeBottomUpTiltOnClosed90 API Class: ShadeBottomUpTiltOnClosed + ShadeBottomUpTiltOnClosed90
Type 1 - Bottom Up w/ 90° Tilt Type 1 - Bottom Up w/ 90° Tilt
Shade 44 - a shade thought to have been a firmware issue (type 0 usually dont tilt) Shade 44 - a shade thought to have been a firmware issue (type 0 usually don't tilt)
""" """
_attr_name = None
@property @property
def open_position(self) -> PowerviewShadeMove: def open_position(self) -> PowerviewShadeMove:
"""Return the open position and required additional positions.""" """Return the open position and required additional positions."""
@ -570,7 +563,7 @@ class PowerViewShadeTiltOnly(PowerViewShadeWithTiltBase):
self._max_tilt = self._shade.shade_limits.tilt_max self._max_tilt = self._shade.shade_limits.tilt_max
class PowerViewShadeTopDown(PowerViewShade): class PowerViewShadeTopDown(PowerViewShadeBase):
"""Representation of a shade that lowers from the roof to the floor. """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 These shades are inverted where MAX_POSITION equates to closed and MIN_POSITION is open
@ -579,6 +572,8 @@ class PowerViewShadeTopDown(PowerViewShade):
Type 6 - Top Down Type 6 - Top Down
""" """
_attr_name = None
@property @property
def current_cover_position(self) -> int: def current_cover_position(self) -> int:
"""Return the current position of cover.""" """Return the current position of cover."""
@ -594,7 +589,7 @@ class PowerViewShadeTopDown(PowerViewShade):
await self._async_set_cover_position(100 - kwargs[ATTR_POSITION]) await self._async_set_cover_position(100 - kwargs[ATTR_POSITION])
class PowerViewShadeDualRailBase(PowerViewShade): class PowerViewShadeDualRailBase(PowerViewShadeBase):
"""Representation of a shade with top/down bottom/up capabilities. """Representation of a shade with top/down bottom/up capabilities.
Base methods shared between the two shades created Base methods shared between the two shades created
@ -613,11 +608,13 @@ class PowerViewShadeDualRailBase(PowerViewShade):
class PowerViewShadeTDBUBottom(PowerViewShadeDualRailBase): class PowerViewShadeTDBUBottom(PowerViewShadeDualRailBase):
"""Representation of the bottom PowerViewShadeDualRailBase shade. """Representation of the bottom PowerViewShadeDualRailBase shade.
These shades have top/down bottom up functionality and two entiites. These shades have top/down bottom up functionality and two entities.
Sibling Class: PowerViewShadeTDBUTop Sibling Class: PowerViewShadeTDBUTop
API Class: ShadeTopDownBottomUp API Class: ShadeTopDownBottomUp
""" """
_attr_translation_key = "bottom"
def __init__( def __init__(
self, self,
coordinator: PowerviewShadeUpdateCoordinator, coordinator: PowerviewShadeUpdateCoordinator,
@ -629,7 +626,6 @@ class PowerViewShadeTDBUBottom(PowerViewShadeDualRailBase):
"""Initialize the shade.""" """Initialize the shade."""
super().__init__(coordinator, device_info, room_name, shade, name) super().__init__(coordinator, device_info, room_name, shade, name)
self._attr_unique_id = f"{self._shade.id}_bottom" self._attr_unique_id = f"{self._shade.id}_bottom"
self._attr_name = f"{self._shade_name} Bottom"
@callback @callback
def _clamp_cover_limit(self, target_hass_position: int) -> int: def _clamp_cover_limit(self, target_hass_position: int) -> int:
@ -655,11 +651,13 @@ class PowerViewShadeTDBUBottom(PowerViewShadeDualRailBase):
class PowerViewShadeTDBUTop(PowerViewShadeDualRailBase): class PowerViewShadeTDBUTop(PowerViewShadeDualRailBase):
"""Representation of the top PowerViewShadeDualRailBase shade. """Representation of the top PowerViewShadeDualRailBase shade.
These shades have top/down bottom up functionality and two entiites. These shades have top/down bottom up functionality and two entities.
Sibling Class: PowerViewShadeTDBUBottom Sibling Class: PowerViewShadeTDBUBottom
API Class: ShadeTopDownBottomUp API Class: ShadeTopDownBottomUp
""" """
_attr_translation_key = "top"
def __init__( def __init__(
self, self,
coordinator: PowerviewShadeUpdateCoordinator, coordinator: PowerviewShadeUpdateCoordinator,
@ -671,7 +669,6 @@ class PowerViewShadeTDBUTop(PowerViewShadeDualRailBase):
"""Initialize the shade.""" """Initialize the shade."""
super().__init__(coordinator, device_info, room_name, shade, name) super().__init__(coordinator, device_info, room_name, shade, name)
self._attr_unique_id = f"{self._shade.id}_top" self._attr_unique_id = f"{self._shade.id}_top"
self._attr_name = f"{self._shade_name} Top"
@property @property
def should_poll(self) -> bool: def should_poll(self) -> bool:
@ -711,7 +708,7 @@ class PowerViewShadeTDBUTop(PowerViewShadeDualRailBase):
@callback @callback
def _clamp_cover_limit(self, target_hass_position: int) -> int: def _clamp_cover_limit(self, target_hass_position: int) -> int:
"""Dont allow a cover to go into an impossbile position.""" """Don't allow a cover to go into an impossbile position."""
cover_bottom = hd_position_to_hass(self.positions.primary, MAX_POSITION) cover_bottom = hd_position_to_hass(self.positions.primary, MAX_POSITION)
return min(target_hass_position, (100 - cover_bottom)) return min(target_hass_position, (100 - cover_bottom))
@ -730,7 +727,7 @@ class PowerViewShadeTDBUTop(PowerViewShadeDualRailBase):
) )
class PowerViewShadeDualOverlappedBase(PowerViewShade): class PowerViewShadeDualOverlappedBase(PowerViewShadeBase):
"""Represent a shade that has a front sheer and rear opaque panel. """Represent a shade that has a front sheer and rear opaque panel.
This equates to two shades being controlled by one motor This equates to two shades being controlled by one motor
@ -783,6 +780,8 @@ class PowerViewShadeDualOverlappedCombined(PowerViewShadeDualOverlappedBase):
Type 8 - Duolite (front and rear shades) Type 8 - Duolite (front and rear shades)
""" """
_attr_translation_key = "combined"
# type # type
def __init__( def __init__(
self, self,
@ -795,7 +794,6 @@ class PowerViewShadeDualOverlappedCombined(PowerViewShadeDualOverlappedBase):
"""Initialize the shade.""" """Initialize the shade."""
super().__init__(coordinator, device_info, room_name, shade, name) super().__init__(coordinator, device_info, room_name, shade, name)
self._attr_unique_id = f"{self._shade.id}_combined" self._attr_unique_id = f"{self._shade.id}_combined"
self._attr_name = f"{self._shade_name} Combined"
@property @property
def is_closed(self) -> bool: def is_closed(self) -> bool:
@ -842,7 +840,7 @@ class PowerViewShadeDualOverlappedCombined(PowerViewShadeDualOverlappedBase):
class PowerViewShadeDualOverlappedFront(PowerViewShadeDualOverlappedBase): class PowerViewShadeDualOverlappedFront(PowerViewShadeDualOverlappedBase):
"""Represent the shade front panel - These have a opaque panel too. """Represent the shade front panel - These have an opaque panel too.
This equates to two shades being controlled by one motor. This equates to two shades being controlled by one motor.
The front shade must be completely down before the rear shade will move. The front shade must be completely down before the rear shade will move.
@ -857,6 +855,8 @@ class PowerViewShadeDualOverlappedFront(PowerViewShadeDualOverlappedBase):
Type 10 - Duolite with 180° Tilt Type 10 - Duolite with 180° Tilt
""" """
_attr_translation_key = "front"
def __init__( def __init__(
self, self,
coordinator: PowerviewShadeUpdateCoordinator, coordinator: PowerviewShadeUpdateCoordinator,
@ -868,7 +868,6 @@ class PowerViewShadeDualOverlappedFront(PowerViewShadeDualOverlappedBase):
"""Initialize the shade.""" """Initialize the shade."""
super().__init__(coordinator, device_info, room_name, shade, name) super().__init__(coordinator, device_info, room_name, shade, name)
self._attr_unique_id = f"{self._shade.id}_front" self._attr_unique_id = f"{self._shade.id}_front"
self._attr_name = f"{self._shade_name} Front"
@property @property
def should_poll(self) -> bool: def should_poll(self) -> bool:
@ -906,7 +905,7 @@ class PowerViewShadeDualOverlappedFront(PowerViewShadeDualOverlappedBase):
class PowerViewShadeDualOverlappedRear(PowerViewShadeDualOverlappedBase): class PowerViewShadeDualOverlappedRear(PowerViewShadeDualOverlappedBase):
"""Represent the shade front panel - These have a opaque panel too. """Represent the shade front panel - These have an opaque panel too.
This equates to two shades being controlled by one motor. This equates to two shades being controlled by one motor.
The front shade must be completely down before the rear shade will move. The front shade must be completely down before the rear shade will move.
@ -921,6 +920,8 @@ class PowerViewShadeDualOverlappedRear(PowerViewShadeDualOverlappedBase):
Type 10 - Duolite with 180° Tilt Type 10 - Duolite with 180° Tilt
""" """
_attr_translation_key = "rear"
def __init__( def __init__(
self, self,
coordinator: PowerviewShadeUpdateCoordinator, coordinator: PowerviewShadeUpdateCoordinator,
@ -932,7 +933,6 @@ class PowerViewShadeDualOverlappedRear(PowerViewShadeDualOverlappedBase):
"""Initialize the shade.""" """Initialize the shade."""
super().__init__(coordinator, device_info, room_name, shade, name) super().__init__(coordinator, device_info, room_name, shade, name)
self._attr_unique_id = f"{self._shade.id}_rear" self._attr_unique_id = f"{self._shade.id}_rear"
self._attr_name = f"{self._shade_name} Rear"
@property @property
def should_poll(self) -> bool: def should_poll(self) -> bool:

View File

@ -25,6 +25,8 @@ from .shade_data import PowerviewShadeData, PowerviewShadePositions
class HDEntity(CoordinatorEntity[PowerviewShadeUpdateCoordinator]): class HDEntity(CoordinatorEntity[PowerviewShadeUpdateCoordinator]):
"""Base class for hunter douglas entities.""" """Base class for hunter douglas entities."""
_attr_has_entity_name = True
def __init__( def __init__(
self, self,
coordinator: PowerviewShadeUpdateCoordinator, coordinator: PowerviewShadeUpdateCoordinator,

View File

@ -47,7 +47,7 @@ class PowerviewSelectDescription(
DROPDOWNS: Final = [ DROPDOWNS: Final = [
PowerviewSelectDescription( PowerviewSelectDescription(
key="powersource", key="powersource",
name="Power Source", translation_key="power_source",
icon="mdi:power-plug-outline", icon="mdi:power-plug-outline",
current_fn=lambda shade: POWER_SUPPLY_TYPE_MAP.get( current_fn=lambda shade: POWER_SUPPLY_TYPE_MAP.get(
shade.raw_data.get(ATTR_BATTERY_KIND), None shade.raw_data.get(ATTR_BATTERY_KIND), None
@ -106,7 +106,6 @@ class PowerViewSelect(ShadeEntity, SelectEntity):
"""Initialize the select entity.""" """Initialize the select entity."""
super().__init__(coordinator, device_info, room_name, shade, name) super().__init__(coordinator, device_info, room_name, shade, name)
self.entity_description: PowerviewSelectDescription = description self.entity_description: PowerviewSelectDescription = description
self._attr_name = f"{self._shade_name} {description.name}"
self._attr_unique_id = f"{self._attr_unique_id}_{description.key}" self._attr_unique_id = f"{self._attr_unique_id}_{description.key}"
@property @property

View File

@ -55,7 +55,6 @@ class PowerviewSensorDescription(
SENSORS: Final = [ SENSORS: Final = [
PowerviewSensorDescription( PowerviewSensorDescription(
key="charge", key="charge",
name="Battery",
device_class=SensorDeviceClass.BATTERY, device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
native_value_fn=lambda shade: round( native_value_fn=lambda shade: round(
@ -69,7 +68,7 @@ SENSORS: Final = [
), ),
PowerviewSensorDescription( PowerviewSensorDescription(
key="signal", key="signal",
name="Signal", translation_key="signal_strength",
icon="mdi:signal", icon="mdi:signal",
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
native_value_fn=lambda shade: round( native_value_fn=lambda shade: round(
@ -129,7 +128,6 @@ class PowerViewSensor(ShadeEntity, SensorEntity):
"""Initialize the select entity.""" """Initialize the select entity."""
super().__init__(coordinator, device_info, room_name, shade, name) super().__init__(coordinator, device_info, room_name, shade, name)
self.entity_description = description self.entity_description = description
self._attr_name = f"{self._shade_name} {description.name}"
self._attr_unique_id = f"{self._attr_unique_id}_{description.key}" self._attr_unique_id = f"{self._attr_unique_id}_{description.key}"
self._attr_native_unit_of_measurement = description.native_unit_of_measurement self._attr_native_unit_of_measurement = description.native_unit_of_measurement

View File

@ -20,5 +20,42 @@
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]" "already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
} }
},
"entity": {
"button": {
"calibrate": {
"name": "Calibrate"
},
"favorite": {
"name": "Favorite"
}
},
"cover": {
"bottom": {
"name": "Bottom"
},
"top": {
"name": "Top"
},
"combined": {
"name": "Combined"
},
"front": {
"name": "Front"
},
"rear": {
"name": "Rear"
}
},
"select": {
"power_source": {
"name": "Power source"
}
},
"sensor": {
"signal_strength": {
"name": "[%key:component::sensor::entity_component::signal_strength::name%]"
}
}
} }
} }