From 5bbddb5b26b1640a993f70e26596f01733c64b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Mon, 28 Sep 2020 20:33:32 +0300 Subject: [PATCH] Use NamedTuple for Huawei LTE sensor metadata (#40655) --- homeassistant/components/huawei_lte/sensor.py | 172 ++++++++++-------- 1 file changed, 93 insertions(+), 79 deletions(-) diff --git a/homeassistant/components/huawei_lte/sensor.py b/homeassistant/components/huawei_lte/sensor.py index 7fe1a7c1571..591506df652 100644 --- a/homeassistant/components/huawei_lte/sensor.py +++ b/homeassistant/components/huawei_lte/sensor.py @@ -2,7 +2,7 @@ import logging import re -from typing import Optional +from typing import Callable, Dict, NamedTuple, Optional, Pattern, Tuple, Union import attr @@ -17,6 +17,7 @@ from homeassistant.const import ( STATE_UNKNOWN, TIME_SECONDS, ) +from homeassistant.helpers.typing import StateType from . import HuaweiLteBaseEntity from .const import ( @@ -36,20 +37,33 @@ from .const import ( _LOGGER = logging.getLogger(__name__) -SENSOR_META = { - KEY_DEVICE_INFORMATION: dict( +class SensorMeta(NamedTuple): + """Metadata for defining sensors.""" + + name: Optional[str] = None + device_class: Optional[str] = None + icon: Union[str, Callable[[StateType], str], None] = None + unit: Optional[str] = None + enabled_default: bool = False + include: Optional[Pattern[str]] = None + exclude: Optional[Pattern[str]] = None + formatter: Optional[Callable[[str], Tuple[StateType, Optional[str]]]] = None + + +SENSOR_META: Dict[Union[str, Tuple[str, str]], SensorMeta] = { + KEY_DEVICE_INFORMATION: SensorMeta( include=re.compile(r"^WanIP.*Address$", re.IGNORECASE) ), - (KEY_DEVICE_INFORMATION, "WanIPAddress"): dict( + (KEY_DEVICE_INFORMATION, "WanIPAddress"): SensorMeta( name="WAN IP address", icon="mdi:ip", enabled_default=True ), - (KEY_DEVICE_INFORMATION, "WanIPv6Address"): dict( + (KEY_DEVICE_INFORMATION, "WanIPv6Address"): SensorMeta( name="WAN IPv6 address", icon="mdi:ip" ), - (KEY_DEVICE_SIGNAL, "band"): dict(name="Band"), - (KEY_DEVICE_SIGNAL, "cell_id"): dict(name="Cell ID"), - (KEY_DEVICE_SIGNAL, "dl_mcs"): dict(name="Downlink MCS"), - (KEY_DEVICE_SIGNAL, "dlbandwidth"): dict( + (KEY_DEVICE_SIGNAL, "band"): SensorMeta(name="Band"), + (KEY_DEVICE_SIGNAL, "cell_id"): SensorMeta(name="Cell ID"), + (KEY_DEVICE_SIGNAL, "dl_mcs"): SensorMeta(name="Downlink MCS"), + (KEY_DEVICE_SIGNAL, "dlbandwidth"): SensorMeta( name="Downlink bandwidth", icon=lambda x: (x is None or x < 8) and "mdi:speedometer-slow" @@ -57,19 +71,19 @@ SENSOR_META = { and "mdi:speedometer-medium" or "mdi:speedometer", ), - (KEY_DEVICE_SIGNAL, "earfcn"): dict(name="EARFCN"), - (KEY_DEVICE_SIGNAL, "lac"): dict(name="LAC", icon="mdi:map-marker"), - (KEY_DEVICE_SIGNAL, "plmn"): dict(name="PLMN"), - (KEY_DEVICE_SIGNAL, "rac"): dict(name="RAC", icon="mdi:map-marker"), - (KEY_DEVICE_SIGNAL, "rrc_status"): dict(name="RRC status"), - (KEY_DEVICE_SIGNAL, "tac"): dict(name="TAC", icon="mdi:map-marker"), - (KEY_DEVICE_SIGNAL, "tdd"): dict(name="TDD"), - (KEY_DEVICE_SIGNAL, "txpower"): dict( + (KEY_DEVICE_SIGNAL, "earfcn"): SensorMeta(name="EARFCN"), + (KEY_DEVICE_SIGNAL, "lac"): SensorMeta(name="LAC", icon="mdi:map-marker"), + (KEY_DEVICE_SIGNAL, "plmn"): SensorMeta(name="PLMN"), + (KEY_DEVICE_SIGNAL, "rac"): SensorMeta(name="RAC", icon="mdi:map-marker"), + (KEY_DEVICE_SIGNAL, "rrc_status"): SensorMeta(name="RRC status"), + (KEY_DEVICE_SIGNAL, "tac"): SensorMeta(name="TAC", icon="mdi:map-marker"), + (KEY_DEVICE_SIGNAL, "tdd"): SensorMeta(name="TDD"), + (KEY_DEVICE_SIGNAL, "txpower"): SensorMeta( name="Transmit power", device_class=DEVICE_CLASS_SIGNAL_STRENGTH, ), - (KEY_DEVICE_SIGNAL, "ul_mcs"): dict(name="Uplink MCS"), - (KEY_DEVICE_SIGNAL, "ulbandwidth"): dict( + (KEY_DEVICE_SIGNAL, "ul_mcs"): SensorMeta(name="Uplink MCS"), + (KEY_DEVICE_SIGNAL, "ulbandwidth"): SensorMeta( name="Uplink bandwidth", icon=lambda x: (x is None or x < 8) and "mdi:speedometer-slow" @@ -77,12 +91,12 @@ SENSOR_META = { and "mdi:speedometer-medium" or "mdi:speedometer", ), - (KEY_DEVICE_SIGNAL, "mode"): dict( + (KEY_DEVICE_SIGNAL, "mode"): SensorMeta( name="Mode", formatter=lambda x: ({"0": "2G", "2": "3G", "7": "4G"}.get(x, "Unknown"), None), ), - (KEY_DEVICE_SIGNAL, "pci"): dict(name="PCI"), - (KEY_DEVICE_SIGNAL, "rsrq"): dict( + (KEY_DEVICE_SIGNAL, "pci"): SensorMeta(name="PCI"), + (KEY_DEVICE_SIGNAL, "rsrq"): SensorMeta( name="RSRQ", device_class=DEVICE_CLASS_SIGNAL_STRENGTH, # http://www.lte-anbieter.info/technik/rsrq.php @@ -95,7 +109,7 @@ SENSOR_META = { or "mdi:signal-cellular-3", enabled_default=True, ), - (KEY_DEVICE_SIGNAL, "rsrp"): dict( + (KEY_DEVICE_SIGNAL, "rsrp"): SensorMeta( name="RSRP", device_class=DEVICE_CLASS_SIGNAL_STRENGTH, # http://www.lte-anbieter.info/technik/rsrp.php @@ -108,7 +122,7 @@ SENSOR_META = { or "mdi:signal-cellular-3", enabled_default=True, ), - (KEY_DEVICE_SIGNAL, "rssi"): dict( + (KEY_DEVICE_SIGNAL, "rssi"): SensorMeta( name="RSSI", device_class=DEVICE_CLASS_SIGNAL_STRENGTH, # https://eyesaas.com/wi-fi-signal-strength/ @@ -121,7 +135,7 @@ SENSOR_META = { or "mdi:signal-cellular-3", enabled_default=True, ), - (KEY_DEVICE_SIGNAL, "sinr"): dict( + (KEY_DEVICE_SIGNAL, "sinr"): SensorMeta( name="SINR", device_class=DEVICE_CLASS_SIGNAL_STRENGTH, # http://www.lte-anbieter.info/technik/sinr.php @@ -134,7 +148,7 @@ SENSOR_META = { or "mdi:signal-cellular-3", enabled_default=True, ), - (KEY_DEVICE_SIGNAL, "rscp"): dict( + (KEY_DEVICE_SIGNAL, "rscp"): SensorMeta( name="RSCP", device_class=DEVICE_CLASS_SIGNAL_STRENGTH, # https://wiki.teltonika.lt/view/RSCP @@ -146,7 +160,7 @@ SENSOR_META = { and "mdi:signal-cellular-2" or "mdi:signal-cellular-3", ), - (KEY_DEVICE_SIGNAL, "ecio"): dict( + (KEY_DEVICE_SIGNAL, "ecio"): SensorMeta( name="EC/IO", device_class=DEVICE_CLASS_SIGNAL_STRENGTH, # https://wiki.teltonika.lt/view/EC/IO @@ -158,90 +172,90 @@ SENSOR_META = { and "mdi:signal-cellular-2" or "mdi:signal-cellular-3", ), - KEY_MONITORING_CHECK_NOTIFICATIONS: dict( + KEY_MONITORING_CHECK_NOTIFICATIONS: SensorMeta( exclude=re.compile( r"^(onlineupdatestatus|smsstoragefull)$", re.IGNORECASE, ) ), - (KEY_MONITORING_CHECK_NOTIFICATIONS, "UnreadMessage"): dict( + (KEY_MONITORING_CHECK_NOTIFICATIONS, "UnreadMessage"): SensorMeta( name="SMS unread", icon="mdi:email-receive" ), - KEY_MONITORING_MONTH_STATISTICS: dict( + KEY_MONITORING_MONTH_STATISTICS: SensorMeta( exclude=re.compile(r"^month(duration|lastcleartime)$", re.IGNORECASE) ), - (KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthDownload"): dict( + (KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthDownload"): SensorMeta( name="Current month download", unit=DATA_BYTES, icon="mdi:download" ), - (KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthUpload"): dict( + (KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthUpload"): SensorMeta( name="Current month upload", unit=DATA_BYTES, icon="mdi:upload" ), - KEY_MONITORING_STATUS: dict( + KEY_MONITORING_STATUS: SensorMeta( include=re.compile( r"^(currentwifiuser|(primary|secondary).*dns)$", re.IGNORECASE ) ), - (KEY_MONITORING_STATUS, "CurrentWifiUser"): dict( + (KEY_MONITORING_STATUS, "CurrentWifiUser"): SensorMeta( name="WiFi clients connected", icon="mdi:wifi" ), - (KEY_MONITORING_STATUS, "PrimaryDns"): dict( + (KEY_MONITORING_STATUS, "PrimaryDns"): SensorMeta( name="Primary DNS server", icon="mdi:ip" ), - (KEY_MONITORING_STATUS, "SecondaryDns"): dict( + (KEY_MONITORING_STATUS, "SecondaryDns"): SensorMeta( name="Secondary DNS server", icon="mdi:ip" ), - (KEY_MONITORING_STATUS, "PrimaryIPv6Dns"): dict( + (KEY_MONITORING_STATUS, "PrimaryIPv6Dns"): SensorMeta( name="Primary IPv6 DNS server", icon="mdi:ip" ), - (KEY_MONITORING_STATUS, "SecondaryIPv6Dns"): dict( + (KEY_MONITORING_STATUS, "SecondaryIPv6Dns"): SensorMeta( name="Secondary IPv6 DNS server", icon="mdi:ip" ), - KEY_MONITORING_TRAFFIC_STATISTICS: dict( + KEY_MONITORING_TRAFFIC_STATISTICS: SensorMeta( exclude=re.compile(r"^showtraffic$", re.IGNORECASE) ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentConnectTime"): dict( + (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentConnectTime"): SensorMeta( name="Current connection duration", unit=TIME_SECONDS, icon="mdi:timer-outline" ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownload"): dict( + (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownload"): SensorMeta( name="Current connection download", unit=DATA_BYTES, icon="mdi:download" ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownloadRate"): dict( + (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownloadRate"): SensorMeta( name="Current download rate", unit=DATA_RATE_BYTES_PER_SECOND, icon="mdi:download", ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUpload"): dict( + (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUpload"): SensorMeta( name="Current connection upload", unit=DATA_BYTES, icon="mdi:upload" ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUploadRate"): dict( + (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUploadRate"): SensorMeta( name="Current upload rate", unit=DATA_RATE_BYTES_PER_SECOND, icon="mdi:upload", ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalConnectTime"): dict( + (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalConnectTime"): SensorMeta( name="Total connected duration", unit=TIME_SECONDS, icon="mdi:timer-outline" ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalDownload"): dict( + (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalDownload"): SensorMeta( name="Total download", unit=DATA_BYTES, icon="mdi:download" ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalUpload"): dict( + (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalUpload"): SensorMeta( name="Total upload", unit=DATA_BYTES, icon="mdi:upload" ), - KEY_NET_CURRENT_PLMN: dict( + KEY_NET_CURRENT_PLMN: SensorMeta( exclude=re.compile(r"^(Rat|ShortName|Spn)$", re.IGNORECASE) ), - (KEY_NET_CURRENT_PLMN, "State"): dict( + (KEY_NET_CURRENT_PLMN, "State"): SensorMeta( name="Operator search mode", formatter=lambda x: ({"0": "Auto", "1": "Manual"}.get(x, "Unknown"), None), ), - (KEY_NET_CURRENT_PLMN, "FullName"): dict( + (KEY_NET_CURRENT_PLMN, "FullName"): SensorMeta( name="Operator name", ), - (KEY_NET_CURRENT_PLMN, "Numeric"): dict( + (KEY_NET_CURRENT_PLMN, "Numeric"): SensorMeta( name="Operator code", ), - KEY_NET_NET_MODE: dict(include=re.compile(r"^NetworkMode$", re.IGNORECASE)), - (KEY_NET_NET_MODE, "NetworkMode"): dict( + KEY_NET_NET_MODE: SensorMeta(include=re.compile(r"^NetworkMode$", re.IGNORECASE)), + (KEY_NET_NET_MODE, "NetworkMode"): SensorMeta( name="Preferred mode", formatter=lambda x: ( { @@ -256,51 +270,51 @@ SENSOR_META = { None, ), ), - (KEY_SMS_SMS_COUNT, "LocalDeleted"): dict( + (KEY_SMS_SMS_COUNT, "LocalDeleted"): SensorMeta( name="SMS deleted (device)", icon="mdi:email-minus", ), - (KEY_SMS_SMS_COUNT, "LocalDraft"): dict( + (KEY_SMS_SMS_COUNT, "LocalDraft"): SensorMeta( name="SMS drafts (device)", icon="mdi:email-send-outline", ), - (KEY_SMS_SMS_COUNT, "LocalInbox"): dict( + (KEY_SMS_SMS_COUNT, "LocalInbox"): SensorMeta( name="SMS inbox (device)", icon="mdi:email", ), - (KEY_SMS_SMS_COUNT, "LocalMax"): dict( + (KEY_SMS_SMS_COUNT, "LocalMax"): SensorMeta( name="SMS capacity (device)", icon="mdi:email", ), - (KEY_SMS_SMS_COUNT, "LocalOutbox"): dict( + (KEY_SMS_SMS_COUNT, "LocalOutbox"): SensorMeta( name="SMS outbox (device)", icon="mdi:email-send", ), - (KEY_SMS_SMS_COUNT, "LocalUnread"): dict( + (KEY_SMS_SMS_COUNT, "LocalUnread"): SensorMeta( name="SMS unread (device)", icon="mdi:email-receive", ), - (KEY_SMS_SMS_COUNT, "SimDraft"): dict( + (KEY_SMS_SMS_COUNT, "SimDraft"): SensorMeta( name="SMS drafts (SIM)", icon="mdi:email-send-outline", ), - (KEY_SMS_SMS_COUNT, "SimInbox"): dict( + (KEY_SMS_SMS_COUNT, "SimInbox"): SensorMeta( name="SMS inbox (SIM)", icon="mdi:email", ), - (KEY_SMS_SMS_COUNT, "SimMax"): dict( + (KEY_SMS_SMS_COUNT, "SimMax"): SensorMeta( name="SMS capacity (SIM)", icon="mdi:email", ), - (KEY_SMS_SMS_COUNT, "SimOutbox"): dict( + (KEY_SMS_SMS_COUNT, "SimOutbox"): SensorMeta( name="SMS outbox (SIM)", icon="mdi:email-send", ), - (KEY_SMS_SMS_COUNT, "SimUnread"): dict( + (KEY_SMS_SMS_COUNT, "SimUnread"): SensorMeta( name="SMS unread (SIM)", icon="mdi:email-receive", ), - (KEY_SMS_SMS_COUNT, "SimUsed"): dict( + (KEY_SMS_SMS_COUNT, "SimUsed"): SensorMeta( name="SMS messages (SIM)", icon="mdi:email-receive", ), @@ -317,15 +331,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities): continue key_meta = SENSOR_META.get(key) if key_meta: - include = key_meta.get("include") - if include: - items = filter(include.search, items) - exclude = key_meta.get("exclude") - if exclude: - items = [x for x in items if not exclude.search(x)] + if key_meta.include: + items = filter(key_meta.include.search, items) + if key_meta.exclude: + items = [x for x in items if not key_meta.exclude.search(x)] for item in items: sensors.append( - HuaweiLteSensor(router, key, item, SENSOR_META.get((key, item), {})) + HuaweiLteSensor( + router, key, item, SENSOR_META.get((key, item), SensorMeta()) + ) ) async_add_entities(sensors, True) @@ -354,7 +368,7 @@ class HuaweiLteSensor(HuaweiLteBaseEntity): key: str = attr.ib() item: str = attr.ib() - meta: dict = attr.ib() + meta: SensorMeta = attr.ib() _state = attr.ib(init=False, default=STATE_UNKNOWN) _unit: str = attr.ib(init=False) @@ -371,7 +385,7 @@ class HuaweiLteSensor(HuaweiLteBaseEntity): @property def _entity_name(self) -> str: - return self.meta.get("name", self.item) + return self.meta.name or self.item @property def _device_unique_id(self) -> str: @@ -385,17 +399,17 @@ class HuaweiLteSensor(HuaweiLteBaseEntity): @property def device_class(self) -> Optional[str]: """Return sensor device class.""" - return self.meta.get("device_class") + return self.meta.device_class @property def unit_of_measurement(self): """Return sensor's unit of measurement.""" - return self.meta.get("unit", self._unit) + return self.meta.unit or self._unit @property def icon(self): """Return icon for sensor.""" - icon = self.meta.get("icon") + icon = self.meta.icon if callable(icon): return icon(self.state) return icon @@ -403,7 +417,7 @@ class HuaweiLteSensor(HuaweiLteBaseEntity): @property def entity_registry_enabled_default(self) -> bool: """Return if the entity should be enabled when first added to the entity registry.""" - return bool(self.meta.get("enabled_default")) + return self.meta.enabled_default async def async_update(self): """Update state.""" @@ -415,7 +429,7 @@ class HuaweiLteSensor(HuaweiLteBaseEntity): return self._available = True - formatter = self.meta.get("formatter") + formatter = self.meta.formatter if not callable(formatter): formatter = format_default