Skip to content

Commit

Permalink
fix inheritance in entity classes
Browse files Browse the repository at this point in the history
This simplifies and fixes multiple inheritance between base platform class and coordinator entity, which in turn fixes translated entity name property.
  • Loading branch information
denpamusic committed Dec 21, 2023
1 parent fb3c564 commit c824853
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 90 deletions.
47 changes: 23 additions & 24 deletions custom_components/econet300/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .entity import EconetEntity

from .common import EconetDataCoordinator, Econet300Api

from .const import (
Expand All @@ -20,8 +22,6 @@
SERVICE_API,
)

from .entity import EconetEntity

_LOGGER = logging.getLogger(__name__)


Expand All @@ -30,7 +30,6 @@ class EconetBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Describes Econet binary sensor entity."""

icon_off: str | None = None
availability_key: str = ""


BINARY_SENSOR_TYPES: tuple[EconetBinarySensorEntityDescription, ...] = (
Expand All @@ -40,23 +39,36 @@ class EconetBinarySensorEntityDescription(BinarySensorEntityDescription):
icon="mdi:pump",
icon_off="mdi:pump-off",
device_class=BinarySensorDeviceClass.RUNNING,
has_entity_name=True,
),
EconetBinarySensorEntityDescription(
key="1541",
translation_key="boiler_pump",
icon="mdi:pump",
icon_off="mdi:pump-off",
device_class=BinarySensorDeviceClass.RUNNING,
has_entity_name=True,
),
)


class EconetBinarySensor(BinarySensorEntity):
class EconetBinarySensor(EconetEntity, BinarySensorEntity):
"""Describe Econet Binary Sensor"""

entity_description: EconetBinarySensorEntityDescription

def __init__(
self,
entity_description: EconetBinarySensorEntityDescription,
coordinator: EconetDataCoordinator,
api: Econet300Api,
):
self.entity_description = entity_description
self.api = api
super().__init__(coordinator)

def _sync_state(self, value):
"""Sync state"""

self._attr_is_on = value
self.async_write_ha_state()

Expand All @@ -70,39 +82,26 @@ def icon(self) -> str | None:
)


class ControllerBinarySensor(EconetEntity, EconetBinarySensor):
"""Describes Econet binary sensor entity."""

def __init__(
self,
description: EconetBinarySensorEntityDescription,
coordinator: EconetDataCoordinator,
api: Econet300Api,
):
super().__init__(description, coordinator, api)


def can_add(
desc: EconetBinarySensorEntityDescription, coordinator: EconetDataCoordinator
):
"""Check can add key"""
return (
coordinator.has_data(desc.availability_key)
and coordinator.data[desc.availability_key] is not False
)
return coordinator.has_data(desc.key)


def create_binary_sensors(coordinator: EconetDataCoordinator, api: Econet300Api):
"""create binary sensors"""
entities = []
entities: list[EconetBinarySensor] = []

for description in BINARY_SENSOR_TYPES:
if can_add(description, coordinator):
entities.append(ControllerBinarySensor(description, coordinator, api))
entities.append(EconetBinarySensor(description, coordinator, api))
else:
_LOGGER.debug(
"Availability key: %s does not exist, entity will not be added",
description.key,
)

return entities


Expand All @@ -116,7 +115,7 @@ async def async_setup_entry(
coordinator = hass.data[DOMAIN][entry.entry_id][SERVICE_COORDINATOR]
api = hass.data[DOMAIN][entry.entry_id][SERVICE_API]

entities: list[ControllerBinarySensor] = []
entities: list[EconetBinarySensor] = []
entities = entities + create_binary_sensors(coordinator, api)

return async_add_entities(entities)
41 changes: 15 additions & 26 deletions custom_components/econet300/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .api import Econet300Api
from .common import EconetDataCoordinator
from .const import (
DEVICE_INFO_CONTROLLER_NAME,
DEVICE_INFO_MANUFACTURER,
Expand All @@ -20,69 +19,59 @@
class EconetEntity(CoordinatorEntity):
"""Representes EconetEntity"""

def __init__(
self,
description: EntityDescription,
coordinator: EconetDataCoordinator,
api: Econet300Api,
):
super().__init__(coordinator)
api: Econet300Api
entity_description: EntityDescription

self.entity_description = description

self._api = api
self._coordinator = coordinator
@property
def has_entity_name(self):
"""Return if the name of the entity is describing only the entity itself."""
return True

@property
def unique_id(self) -> str | None:
"""Return the unique_id of the entity"""
return f"{self._api.uid()}-{self.entity_description.key}"
return f"{self.api.uid()}-{self.entity_description.key}"

@property
def device_info(self) -> DeviceInfo | None:
"""Return device info of the entity"""
return DeviceInfo(
identifiers={(DOMAIN, self._api.uid())},
identifiers={(DOMAIN, self.api.uid())},
name=DEVICE_INFO_CONTROLLER_NAME,
manufacturer=DEVICE_INFO_MANUFACTURER,
model=DEVICE_INFO_MODEL,
configuration_url=self._api.host(),
sw_version=self._api.sw_rev(),
hw_version=self._api.hw_ver(),
configuration_url=self.api.host(),
sw_version=self.api.sw_rev(),
hw_version=self.api.hw_ver(),
)

@property
def name(self) -> str:
"""Return the name of the entity."""
return self.entity_description.name

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
_LOGGER.debug(
"Update EconetEntity, entity name: %s", self.entity_description.name
)

if self._coordinator.data[self.entity_description.key] is None:
if self.coordinator.data[self.entity_description.key] is None:
return

value = self._coordinator.data[self.entity_description.key]
value = self.coordinator.data[self.entity_description.key]

self._sync_state(value)

async def async_added_to_hass(self):
"""Handle added to hass."""
_LOGGER.debug("Added to HASS: %s", self.entity_description.name)

if self._coordinator.data[self.entity_description.key] is None:
if self.coordinator.data[self.entity_description.key] is None:
_LOGGER.warning(
"Data key: %s was expected to exist but it doesn't",
self.entity_description.key,
)

return

value = self._coordinator.data[self.entity_description.key]
value = self.coordinator.data[self.entity_description.key]

await super().async_added_to_hass()
self._sync_state(value)
59 changes: 19 additions & 40 deletions custom_components/econet300/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,70 +38,48 @@ class EconetSensorEntityDescription(SensorEntityDescription):
process_val: Callable[[Any], Any] = lambda x: x


class EconetSensor(SensorEntity):
class EconetSensor(EconetEntity, SensorEntity):
"""Econet Sensor"""

def __init__(self, entity_description, unique_id):
super().__init__(name=None, unique_id=unique_id)
entity_description: EconetSensorEntityDescription

def __init__(
self,
entity_description: EconetSensorEntityDescription,
coordinator: EconetDataCoordinator,
api: Econet300Api,
):
self.entity_description = entity_description
self.api = api
self._attr_native_value = None
super().__init__(coordinator)
_LOGGER.debug(
"EconetSensor initialized with name: %s, unique_id: %s, entity_description: %s",
self.name,
"EconetSensor initialized with unique_id: %s, entity_description: %s",
self.unique_id,
self.entity_description,
)

@property
def name(self):
"""Return the name of the sensor."""
if self.entity_description.translation_key:
_LOGGER.debug(
"Using translation key for sensor: %s",
self.entity_description.translation_key,
)
return f"entity.sensor.{self.entity_description.translation_key}"
else:
_LOGGER.debug("Using name for sensor: %s", self.entity_description.name)
return self.entity_description.name

def _sync_state(self, value):
"""Sync state"""
_LOGGER.debug("Update EconetSensor entity: %s", self.entity_description.name)

self._attr_native_value = self.entity_description.process_val(value)

self.async_write_ha_state()


class ControllerSensor(EconetEntity, EconetSensor):
"""class controller"""

def __init__(
self,
description: EconetSensorEntityDescription,
coordinator: EconetDataCoordinator,
api: Econet300Api,
):
super().__init__(description, coordinator, api)


def camel_to_snake(key):
def camel_to_snake(key: str) -> str:
"""Converting camel case return from api ti snake case to mach translations keys structure"""
key = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", key)
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", key).lower()


def create_entity_description(key: str):
def create_entity_description(key: str) -> EconetSensorEntityDescription:
"""Creates Econect300 sensor entity based on supplied key"""
map_key = REG_PARAM_MAP.get(key, key)
translation_key = camel_to_snake(map_key)
_LOGGER.debug("REG_PARAM_MAP: %s", REG_PARAM_MAP)
_LOGGER.debug("Creating entity description for key: %s, map_key: %s", key, map_key)
entity_description = EconetSensorEntityDescription(
key=key,
name=translation_key,
translation_key=translation_key,
translation_key=camel_to_snake(map_key),
native_unit_of_measurement=REG_PARAM_UNIT.get(map_key, None),
state_class=REG_PARAM_STATE_CLASS.get(map_key, None),
device_class=REG_PARAM_DEVICE_CLASS.get(map_key, None),
Expand All @@ -112,14 +90,15 @@ def create_entity_description(key: str):
_LOGGER.debug("Created entity description: %s", entity_description)
return entity_description


def create_controller_sensors(coordinator: EconetDataCoordinator, api: Econet300Api):
"""Creating controller sensor entities"""
entities = []
entities: list[EconetSensor] = []
coordinator_data = coordinator.data
for data_key in coordinator_data:
if data_key in REG_PARAM_MAP:
entities.append(
ControllerSensor(create_entity_description(data_key), coordinator, api)
EconetSensor(create_entity_description(data_key), coordinator, api)
)
_LOGGER.debug(
"Key: %s mapped, entity will be added",
Expand All @@ -144,6 +123,6 @@ async def async_setup_entry(
api = hass.data[DOMAIN][entry.entry_id][SERVICE_API]

entities: list[EconetSensor] = []
entities = entities + create_controller_sensors(coordinator, api)
entities.extend(create_controller_sensors(coordinator, api))

return async_add_entities(entities)

0 comments on commit c824853

Please sign in to comment.