Support multiple EDL21 meters (#33594)
* edl21: Add 'name' option * edl21: Include domain and electricity ID in unique ID As a side-effect, this makes electricity IDs mandatory, i.e. devices which don't transmit their ID won't register any sensors. * edl21: Implement upgrade path for unique IDs * Update homeassistant/components/edl21/sensor.py Drop DOMAIN from unique ID. Co-Authored-By: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
e4e89becc6
commit
73fb57fd32
1 changed files with 64 additions and 8 deletions
|
@ -8,6 +8,7 @@ from sml.asyncio import SmlProtocol
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
|
@ -15,6 +16,7 @@ from homeassistant.helpers.dispatcher import (
|
|||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_registry import async_get_registry
|
||||
from homeassistant.helpers.typing import Optional
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
|
@ -26,7 +28,12 @@ ICON_POWER = "mdi:flash"
|
|||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
|
||||
SIGNAL_EDL21_TELEGRAM = "edl21_telegram"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({vol.Required(CONF_SERIAL_PORT): cv.string})
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_SERIAL_PORT): cv.string,
|
||||
vol.Optional(CONF_NAME, default=""): cv.string,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
|
@ -74,6 +81,7 @@ class EDL21:
|
|||
self._registered_obis = set()
|
||||
self._hass = hass
|
||||
self._async_add_entities = async_add_entities
|
||||
self._name = config[CONF_NAME]
|
||||
self._proto = SmlProtocol(config[CONF_SERIAL_PORT])
|
||||
self._proto.add_listener(self.event, ["SmlGetListResponse"])
|
||||
|
||||
|
@ -85,19 +93,35 @@ class EDL21:
|
|||
"""Handle events from pysml."""
|
||||
assert isinstance(message_body, SmlGetListResponse)
|
||||
|
||||
electricity_id = None
|
||||
for telegram in message_body.get("valList", []):
|
||||
if telegram.get("objName") == "1-0:0.0.9*255":
|
||||
electricity_id = telegram.get("value")
|
||||
break
|
||||
|
||||
if electricity_id is None:
|
||||
return
|
||||
electricity_id = electricity_id.replace(" ", "")
|
||||
|
||||
new_entities = []
|
||||
for telegram in message_body.get("valList", []):
|
||||
obis = telegram.get("objName")
|
||||
if not obis:
|
||||
continue
|
||||
|
||||
if obis in self._registered_obis:
|
||||
async_dispatcher_send(self._hass, SIGNAL_EDL21_TELEGRAM, telegram)
|
||||
if (electricity_id, obis) in self._registered_obis:
|
||||
async_dispatcher_send(
|
||||
self._hass, SIGNAL_EDL21_TELEGRAM, electricity_id, telegram
|
||||
)
|
||||
else:
|
||||
name = self._OBIS_NAMES.get(obis)
|
||||
if name:
|
||||
new_entities.append(EDL21Entity(obis, name, telegram))
|
||||
self._registered_obis.add(obis)
|
||||
if self._name:
|
||||
name = f"{self._name}: {name}"
|
||||
new_entities.append(
|
||||
EDL21Entity(electricity_id, obis, name, telegram)
|
||||
)
|
||||
self._registered_obis.add((electricity_id, obis))
|
||||
elif obis not in self._OBIS_BLACKLIST:
|
||||
_LOGGER.warning(
|
||||
"Unhandled sensor %s detected. Please report at "
|
||||
|
@ -107,16 +131,41 @@ class EDL21:
|
|||
self._OBIS_BLACKLIST.add(obis)
|
||||
|
||||
if new_entities:
|
||||
self._async_add_entities(new_entities, update_before_add=True)
|
||||
self._hass.loop.create_task(self.add_entities(new_entities))
|
||||
|
||||
async def add_entities(self, new_entities) -> None:
|
||||
"""Migrate old unique IDs, then add entities to hass."""
|
||||
registry = await async_get_registry(self._hass)
|
||||
|
||||
for entity in new_entities:
|
||||
old_entity_id = registry.async_get_entity_id(
|
||||
"sensor", DOMAIN, entity.old_unique_id
|
||||
)
|
||||
if old_entity_id is not None:
|
||||
_LOGGER.debug(
|
||||
"Migrating unique_id from [%s] to [%s]",
|
||||
entity.old_unique_id,
|
||||
entity.unique_id,
|
||||
)
|
||||
if registry.async_get_entity_id("sensor", DOMAIN, entity.unique_id):
|
||||
registry.async_remove(old_entity_id)
|
||||
else:
|
||||
registry.async_update_entity(
|
||||
old_entity_id, new_unique_id=entity.unique_id
|
||||
)
|
||||
|
||||
self._async_add_entities(new_entities, update_before_add=True)
|
||||
|
||||
|
||||
class EDL21Entity(Entity):
|
||||
"""Entity reading values from EDL21 telegram."""
|
||||
|
||||
def __init__(self, obis, name, telegram):
|
||||
def __init__(self, electricity_id, obis, name, telegram):
|
||||
"""Initialize an EDL21Entity."""
|
||||
self._electricity_id = electricity_id
|
||||
self._obis = obis
|
||||
self._name = name
|
||||
self._unique_id = f"{electricity_id}_{obis}"
|
||||
self._telegram = telegram
|
||||
self._min_time = MIN_TIME_BETWEEN_UPDATES
|
||||
self._last_update = utcnow()
|
||||
|
@ -132,8 +181,10 @@ class EDL21Entity(Entity):
|
|||
"""Run when entity about to be added to hass."""
|
||||
|
||||
@callback
|
||||
def handle_telegram(telegram):
|
||||
def handle_telegram(electricity_id, telegram):
|
||||
"""Update attributes from last received telegram for this object."""
|
||||
if self._electricity_id != electricity_id:
|
||||
return
|
||||
if self._obis != telegram.get("objName"):
|
||||
return
|
||||
if self._telegram == telegram:
|
||||
|
@ -164,6 +215,11 @@ class EDL21Entity(Entity):
|
|||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique ID."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def old_unique_id(self) -> str:
|
||||
"""Return a less unique ID as used in the first version of edl21."""
|
||||
return self._obis
|
||||
|
||||
@property
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue