Huawei LTE sensor unique id improvements (#25609)

* Convert sensor setup to async

* Improve sensor unique ids

* Save some indent levels, use f-string formatting

* Require getmac in tests

* Fix RouterData init in tests

* Make discovery_info optional in async_setup_platform signature
pull/25633/head
Ville Skyttä 2019-08-01 19:22:04 +03:00 committed by GitHub
parent 36129af447
commit c2556d90ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 14 deletions

View File

@ -1,12 +1,15 @@
"""Support for Huawei LTE routers."""
from datetime import timedelta
from functools import reduce
from urllib.parse import urlparse
import ipaddress
import logging
import operator
from typing import Any, Callable
import voluptuous as vol
import attr
from getmac import get_mac_address
from huawei_lte_api.AuthorizedConnection import AuthorizedConnection
from huawei_lte_api.Client import Client
from huawei_lte_api.exceptions import ResponseErrorNotSupportedException
@ -55,6 +58,7 @@ class RouterData:
"""Class for router state."""
client = attr.ib()
mac = attr.ib()
device_information = attr.ib(init=False, factory=dict)
device_signal = attr.ib(init=False, factory=dict)
monitoring_traffic_statistics = attr.ib(init=False, factory=dict)
@ -62,12 +66,6 @@ class RouterData:
_subscriptions = attr.ib(init=False, factory=set)
def __attrs_post_init__(self) -> None:
"""Fetch device information once, for serial number in @unique_ids."""
self.subscribe("device_information")
self._update()
self.unsubscribe("device_information")
def __getitem__(self, path: str):
"""
Get value corresponding to a dotted path.
@ -148,10 +146,24 @@ def _setup_lte(hass, lte_config) -> None:
username = lte_config[CONF_USERNAME]
password = lte_config[CONF_PASSWORD]
# Get MAC address for use in unique ids. Being able to use something
# from the API would be nice, but all of that seems to be available only
# through authenticated calls (e.g. device_information.SerialNumber), and
# we want this available and the same when unauthenticated too.
host = urlparse(url).hostname
try:
if ipaddress.ip_address(host).version == 6:
mode = "ip6"
else:
mode = "ip"
except ValueError:
mode = "hostname"
mac = get_mac_address(**{mode: host})
connection = AuthorizedConnection(url, username=username, password=password)
client = Client(connection)
data = RouterData(client)
data = RouterData(client, mac)
hass.data[DATA_KEY].data[url] = data
def cleanup(event):

View File

@ -3,6 +3,7 @@
"name": "Huawei lte",
"documentation": "https://www.home-assistant.io/components/huawei_lte",
"requirements": [
"getmac==0.8.1",
"huawei-lte-api==1.2.0"
],
"dependencies": [],

View File

@ -11,6 +11,7 @@ from homeassistant.components.sensor import (
PLATFORM_SCHEMA,
DEVICE_CLASS_SIGNAL_STRENGTH,
)
from homeassistant.helpers import entity_registry
from homeassistant.helpers.entity import Entity
import homeassistant.helpers.config_validation as cv
@ -101,7 +102,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
)
def setup_platform(hass, config, add_entities, discovery_info):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up Huawei LTE sensor devices."""
data = hass.data[DATA_KEY].get_data(config)
sensors = []
@ -111,7 +112,24 @@ def setup_platform(hass, config, add_entities, discovery_info):
data.subscribe(path)
sensors.append(HuaweiLteSensor(data, path, SENSOR_META.get(path, {})))
add_entities(sensors, True)
# Pre-0.97 unique id migration. Old ones used the device serial number
# (see comments in HuaweiLteData._setup_lte for more info), as well as
# had a bug that joined the path str with periods, not the path components,
# resulting e.g. *_device_signal.sinr to end up as
# *_d.e.v.i.c.e._.s.i.g.n.a.l...s.i.n.r
entreg = await entity_registry.async_get_registry(hass)
for entid, ent in entreg.entities.items():
if ent.platform != "huawei_lte":
continue
for sensor in sensors:
oldsuf = ".".join(sensor.path)
if ent.unique_id.endswith(f"_{oldsuf}"):
entreg.async_update_entity(entid, new_unique_id=sensor.unique_id)
_LOGGER.debug(
"Updated entity %s unique id to %s", entid, sensor.unique_id
)
async_add_entities(sensors, True)
def format_default(value):
@ -134,7 +152,7 @@ class HuaweiLteSensor(Entity):
"""Huawei LTE sensor entity."""
data = attr.ib(type=RouterData)
path = attr.ib(type=list)
path = attr.ib(type=str)
meta = attr.ib(type=dict)
_state = attr.ib(init=False, default=STATE_UNKNOWN)
@ -143,9 +161,7 @@ class HuaweiLteSensor(Entity):
@property
def unique_id(self) -> str:
"""Return unique ID for sensor."""
return "{}_{}".format(
self.data["device_information.SerialNumber"], ".".join(self.path)
)
return "{}-{}".format(self.data.mac, self.path)
@property
def name(self) -> str:

View File

@ -537,6 +537,7 @@ georss_ign_sismologia_client==0.2
georss_qld_bushfire_alert_client==0.3
# homeassistant.components.braviatv
# homeassistant.components.huawei_lte
# homeassistant.components.nmap_tracker
getmac==0.8.1

View File

@ -147,6 +147,11 @@ georss_ign_sismologia_client==0.2
# homeassistant.components.qld_bushfire
georss_qld_bushfire_alert_client==0.3
# homeassistant.components.braviatv
# homeassistant.components.huawei_lte
# homeassistant.components.nmap_tracker
getmac==0.8.1
# homeassistant.components.google
google-api-python-client==1.6.4

View File

@ -73,6 +73,7 @@ TEST_REQUIREMENTS = (
"georss_generic_client",
"georss_ign_sismologia_client",
"georss_qld_bushfire_alert_client",
"getmac",
"google-api-python-client",
"gTTS-token",
"ha-ffmpeg",

View File

@ -9,7 +9,7 @@ from homeassistant.components import huawei_lte
@pytest.fixture(autouse=True)
def routerdata():
"""Set up a router data for testing."""
rd = huawei_lte.RouterData(Mock())
rd = huawei_lte.RouterData(Mock(), "de:ad:be:ef:00:00")
rd.device_information = {"SoftwareVersion": "1.0", "nested": {"foo": "bar"}}
return rd