Add myuplink specific sensor descriptions (#109867)

* Add specific sensor descriptions

* Address review suggestions

* Adress more review comments

* Change variable name according to code review

* Qualify parameter_id:s to avoid collisions

* Qualify sensor descriptions with a model group prefix

* Improve lookup of sensor descriptions

* Address review comments

* Add constant for unknown value from API

* Add raw value as attribute to enum sensors

* Create extra raw_value sensors with enum sensors

* Moved attributes to class attributes

* Move capitalize() to class setup
pull/110358/head
Åke Strandberg 2024-02-12 16:37:44 +01:00 committed by GitHub
parent 89331d0ff3
commit 29ed82332c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 127 additions and 5 deletions

View File

@ -22,7 +22,7 @@ from . import MyUplinkDataCoordinator
from .const import DOMAIN
from .entity import MyUplinkEntity
DEVICE_POINT_DESCRIPTIONS = {
DEVICE_POINT_UNIT_DESCRIPTIONS: dict[str, SensorEntityDescription] = {
"°C": SensorEntityDescription(
key="celsius",
device_class=SensorDeviceClass.TEMPERATURE,
@ -43,6 +43,52 @@ DEVICE_POINT_DESCRIPTIONS = {
),
}
MARKER_FOR_UNKNOWN_VALUE = -32768
CATEGORY_BASED_DESCRIPTIONS: dict[str, dict[str, SensorEntityDescription]] = {
"NIBEF": {
"43108": SensorEntityDescription(
key="fan_mode",
icon="mdi:fan",
),
"43427": SensorEntityDescription(
key="status_compressor",
device_class=SensorDeviceClass.ENUM,
icon="mdi:heat-pump-outline",
),
"49993": SensorEntityDescription(
key="elect_add",
device_class=SensorDeviceClass.ENUM,
icon="mdi:heat-wave",
),
"49994": SensorEntityDescription(
key="priority",
device_class=SensorDeviceClass.ENUM,
icon="mdi:priority-high",
),
},
"NIBE": {},
}
def get_description(device_point: DevicePoint) -> SensorEntityDescription | None:
"""Get description for a device point.
Priorities:
1. Category specific prefix e.g "NIBEF"
2. Global parameter_unit e.g. "°C"
3. Default to None
"""
description = None
prefix, _, _ = device_point.category.partition(" ")
description = CATEGORY_BASED_DESCRIPTIONS.get(prefix, {}).get(
device_point.parameter_id
)
if description is None:
description = DEVICE_POINT_UNIT_DESCRIPTIONS.get(device_point.parameter_unit)
return description
async def async_setup_entry(
hass: HomeAssistant,
@ -50,20 +96,36 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up myUplink sensor."""
entities: list[SensorEntity] = []
coordinator: MyUplinkDataCoordinator = hass.data[DOMAIN][config_entry.entry_id]
# Setup device point sensors
for device_id, point_data in coordinator.data.points.items():
for point_id, device_point in point_data.items():
description = get_description(device_point)
entity_class = MyUplinkDevicePointSensor
if (
description is not None
and description.device_class == SensorDeviceClass.ENUM
):
entities.append(
MyUplinkEnumRawSensor(
coordinator=coordinator,
device_id=device_id,
device_point=device_point,
entity_description=description,
unique_id_suffix=f"{point_id}-raw",
)
)
entity_class = MyUplinkEnumSensor
entities.append(
MyUplinkDevicePointSensor(
entity_class(
coordinator=coordinator,
device_id=device_id,
device_point=device_point,
entity_description=DEVICE_POINT_DESCRIPTIONS.get(
device_point.parameter_unit
),
entity_description=description,
unique_id_suffix=point_id,
)
)
@ -102,4 +164,64 @@ class MyUplinkDevicePointSensor(MyUplinkEntity, SensorEntity):
def native_value(self) -> StateType:
"""Sensor state value."""
device_point = self.coordinator.data.points[self.device_id][self.point_id]
if device_point.value == MARKER_FOR_UNKNOWN_VALUE:
return None
return device_point.value # type: ignore[no-any-return]
class MyUplinkEnumSensor(MyUplinkDevicePointSensor):
"""Representation of a myUplink device point sensor for ENUM device_class."""
def __init__(
self,
coordinator: MyUplinkDataCoordinator,
device_id: str,
device_point: DevicePoint,
entity_description: SensorEntityDescription | None,
unique_id_suffix: str,
) -> None:
"""Initialize the sensor."""
super().__init__(
coordinator=coordinator,
device_id=device_id,
device_point=device_point,
entity_description=entity_description,
unique_id_suffix=unique_id_suffix,
)
self._attr_options = [x["text"].capitalize() for x in device_point.enum_values]
self.options_map = {
x["value"]: x["text"].capitalize() for x in device_point.enum_values
}
@property
def native_value(self) -> str:
"""Sensor state value for enum sensor."""
device_point = self.coordinator.data.points[self.device_id][self.point_id]
return self.options_map[str(int(device_point.value))] # type: ignore[no-any-return]
class MyUplinkEnumRawSensor(MyUplinkDevicePointSensor):
"""Representation of a myUplink device point sensor for raw value from ENUM device_class."""
_attr_entity_registry_enabled_default = False
_attr_device_class = None
def __init__(
self,
coordinator: MyUplinkDataCoordinator,
device_id: str,
device_point: DevicePoint,
entity_description: SensorEntityDescription | None,
unique_id_suffix: str,
) -> None:
"""Initialize the sensor."""
super().__init__(
coordinator=coordinator,
device_id=device_id,
device_point=device_point,
entity_description=entity_description,
unique_id_suffix=unique_id_suffix,
)
self._attr_name = f"{device_point.parameter_name} raw"