diff --git a/homeassistant/components/huawei_lte/__init__.py b/homeassistant/components/huawei_lte/__init__.py index c68231ba0e4..51d0dc5d3a2 100644 --- a/homeassistant/components/huawei_lte/__init__.py +++ b/homeassistant/components/huawei_lte/__init__.py @@ -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): diff --git a/homeassistant/components/huawei_lte/manifest.json b/homeassistant/components/huawei_lte/manifest.json index bfdc6f167aa..f31ff74c055 100644 --- a/homeassistant/components/huawei_lte/manifest.json +++ b/homeassistant/components/huawei_lte/manifest.json @@ -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": [], diff --git a/homeassistant/components/huawei_lte/sensor.py b/homeassistant/components/huawei_lte/sensor.py index 02ccff82c52..e72bd3aa438 100644 --- a/homeassistant/components/huawei_lte/sensor.py +++ b/homeassistant/components/huawei_lte/sensor.py @@ -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: diff --git a/requirements_all.txt b/requirements_all.txt index 5d365c9c732..ff2d33f5d77 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1feeab8f32d..c2139f7f4e0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -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 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index bc3a2133607..edf74b93793 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -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", diff --git a/tests/components/huawei_lte/test_init.py b/tests/components/huawei_lte/test_init.py index 46cec10963a..70a00b02b4e 100644 --- a/tests/components/huawei_lte/test_init.py +++ b/tests/components/huawei_lte/test_init.py @@ -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