Refactor Huawei LTE to use Entity Description (#83904)

* Refactor Huawei LTE to use Entity Description

* Apply suggestions from code review

Co-authored-by: Ville Skyttä <ville.skytta@iki.fi>

Co-authored-by: Ville Skyttä <ville.skytta@iki.fi>
This commit is contained in:
epenet 2022-12-13 19:48:46 +01:00 committed by GitHub
parent 42a9069338
commit b7b7cdcfcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -6,22 +6,22 @@ from collections.abc import Callable
from dataclasses import dataclass, field from dataclasses import dataclass, field
import logging import logging
import re import re
from typing import NamedTuple
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
DOMAIN as SENSOR_DOMAIN, DOMAIN as SENSOR_DOMAIN,
SensorDeviceClass, SensorDeviceClass,
SensorEntity, SensorEntity,
SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
DATA_BYTES,
FREQUENCY_MEGAHERTZ,
PERCENTAGE, PERCENTAGE,
STATE_UNKNOWN, STATE_UNKNOWN,
TIME_SECONDS,
UnitOfDataRate, UnitOfDataRate,
UnitOfFrequency,
UnitOfInformation,
UnitOfTime,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity, EntityCategory from homeassistant.helpers.entity import Entity, EntityCategory
@ -46,88 +46,122 @@ from .const import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
class SensorMeta(NamedTuple): def format_default(value: StateType) -> tuple[StateType, str | None]:
"""Metadata for defining sensors.""" """Format value."""
unit = None
if value is not None:
# Clean up value and infer unit, e.g. -71dBm, 15 dB
if match := re.match(
r"([>=<]*)(?P<value>.+?)\s*(?P<unit>[a-zA-Z]+)\s*$", str(value)
):
try:
value = float(match.group("value"))
unit = match.group("unit")
except ValueError:
pass
return value, unit
name: str | None = None
device_class: SensorDeviceClass | None = None @dataclass
icon: str | Callable[[StateType], str] | None = None class HuaweiSensorGroup:
native_unit_of_measurement: str | None = None """Class describing Huawei LTE sensor groups."""
state_class: SensorStateClass | None = None
entity_registry_enabled_default: bool = False descriptions: dict[str, HuaweiSensorEntityDescription]
entity_category: EntityCategory | None = None
include: re.Pattern[str] | None = None include: re.Pattern[str] | None = None
exclude: re.Pattern[str] | None = None exclude: re.Pattern[str] | None = None
formatter: Callable[[str], tuple[StateType, str | None]] | None = None
SENSOR_META: dict[str | tuple[str, str], SensorMeta] = { @dataclass
class HuaweiSensorEntityDescription(SensorEntityDescription):
"""Class describing Huawei LTE sensor entities."""
formatter: Callable[[str], tuple[StateType, str | None]] = format_default
icon_fn: Callable[[StateType], str] | None = None
SENSOR_META: dict[str, HuaweiSensorGroup] = {
# #
# Device information # Device information
# #
KEY_DEVICE_INFORMATION: SensorMeta( KEY_DEVICE_INFORMATION: HuaweiSensorGroup(
include=re.compile(r"^(WanIP.*Address|uptime)$", re.IGNORECASE) include=re.compile(r"^(WanIP.*Address|uptime)$", re.IGNORECASE),
), descriptions={
(KEY_DEVICE_INFORMATION, "uptime"): SensorMeta( "uptime": HuaweiSensorEntityDescription(
key="uptime",
name="Uptime", name="Uptime",
icon="mdi:timer-outline", icon="mdi:timer-outline",
native_unit_of_measurement=TIME_SECONDS, native_unit_of_measurement=UnitOfTime.SECONDS,
device_class=SensorDeviceClass.DURATION,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_INFORMATION, "WanIPAddress"): SensorMeta( "WanIPAddress": HuaweiSensorEntityDescription(
key="WanIPAddress",
name="WAN IP address", name="WAN IP address",
icon="mdi:ip", icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=True, entity_registry_enabled_default=True,
), ),
(KEY_DEVICE_INFORMATION, "WanIPv6Address"): SensorMeta( "WanIPv6Address": HuaweiSensorEntityDescription(
key="WanIPv6Address",
name="WAN IPv6 address", name="WAN IPv6 address",
icon="mdi:ip", icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
},
),
# #
# Signal # Signal
# #
(KEY_DEVICE_SIGNAL, "band"): SensorMeta( KEY_DEVICE_SIGNAL: HuaweiSensorGroup(
descriptions={
"band": HuaweiSensorEntityDescription(
key="band",
name="Band", name="Band",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "cell_id"): SensorMeta( "cell_id": HuaweiSensorEntityDescription(
key="cell_id",
name="Cell ID", name="Cell ID",
icon="mdi:transmission-tower", icon="mdi:transmission-tower",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "cqi0"): SensorMeta( "cqi0": HuaweiSensorEntityDescription(
key="cqi0",
name="CQI 0", name="CQI 0",
icon="mdi:speedometer", icon="mdi:speedometer",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "cqi1"): SensorMeta( "cqi1": HuaweiSensorEntityDescription(
key="cqi1",
name="CQI 1", name="CQI 1",
icon="mdi:speedometer", icon="mdi:speedometer",
), ),
(KEY_DEVICE_SIGNAL, "dl_mcs"): SensorMeta( "dl_mcs": HuaweiSensorEntityDescription(
key="dl_mcs",
name="Downlink MCS", name="Downlink MCS",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "dlbandwidth"): SensorMeta( "dlbandwidth": HuaweiSensorEntityDescription(
key="dlbandwidth",
name="Downlink bandwidth", name="Downlink bandwidth",
icon=lambda x: ( icon_fn=lambda x: (
"mdi:speedometer-slow", "mdi:speedometer-slow",
"mdi:speedometer-medium", "mdi:speedometer-medium",
"mdi:speedometer", "mdi:speedometer",
)[bisect((8, 15), x if x is not None else -1000)], )[bisect((8, 15), x if x is not None else -1000)],
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "earfcn"): SensorMeta( "earfcn": HuaweiSensorEntityDescription(
key="earfcn",
name="EARFCN", name="EARFCN",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "ecio"): SensorMeta( "ecio": HuaweiSensorEntityDescription(
key="ecio",
name="EC/IO", name="EC/IO",
device_class=SensorDeviceClass.SIGNAL_STRENGTH, device_class=SensorDeviceClass.SIGNAL_STRENGTH,
# https://wiki.teltonika.lt/view/EC/IO # https://wiki.teltonika.lt/view/EC/IO
icon=lambda x: ( icon_fn=lambda x: (
"mdi:signal-cellular-outline", "mdi:signal-cellular-outline",
"mdi:signal-cellular-1", "mdi:signal-cellular-1",
"mdi:signal-cellular-2", "mdi:signal-cellular-2",
@ -136,64 +170,81 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "enodeb_id"): SensorMeta( "enodeb_id": HuaweiSensorEntityDescription(
key="enodeb_id",
name="eNodeB ID", name="eNodeB ID",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "lac"): SensorMeta( "lac": HuaweiSensorEntityDescription(
key="lac",
name="LAC", name="LAC",
icon="mdi:map-marker", icon="mdi:map-marker",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "ltedlfreq"): SensorMeta( "ltedlfreq": HuaweiSensorEntityDescription(
key="ltedlfreq",
name="Downlink frequency", name="Downlink frequency",
formatter=lambda x: ( formatter=lambda x: (
round(int(x) / 10) if x is not None else None, round(int(x) / 10) if x is not None else None,
FREQUENCY_MEGAHERTZ, UnitOfFrequency.MEGAHERTZ,
), ),
device_class=SensorDeviceClass.FREQUENCY,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "lteulfreq"): SensorMeta( "lteulfreq": HuaweiSensorEntityDescription(
key="lteulfreq",
name="Uplink frequency", name="Uplink frequency",
formatter=lambda x: ( formatter=lambda x: (
round(int(x) / 10) if x is not None else None, round(int(x) / 10) if x is not None else None,
FREQUENCY_MEGAHERTZ, UnitOfFrequency.MEGAHERTZ,
), ),
device_class=SensorDeviceClass.FREQUENCY,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "mode"): SensorMeta( "mode": HuaweiSensorEntityDescription(
key="mode",
name="Mode", name="Mode",
formatter=lambda x: ({"0": "2G", "2": "3G", "7": "4G"}.get(x, "Unknown"), None), formatter=lambda x: (
icon=lambda x: ( {"0": "2G", "2": "3G", "7": "4G"}.get(x, "Unknown"),
{"2G": "mdi:signal-2g", "3G": "mdi:signal-3g", "4G": "mdi:signal-4g"}.get( None,
str(x), "mdi:signal" ),
) icon_fn=lambda x: (
{
"2G": "mdi:signal-2g",
"3G": "mdi:signal-3g",
"4G": "mdi:signal-4g",
}.get(str(x), "mdi:signal")
), ),
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "pci"): SensorMeta( "pci": HuaweiSensorEntityDescription(
key="pci",
name="PCI", name="PCI",
icon="mdi:transmission-tower", icon="mdi:transmission-tower",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "plmn"): SensorMeta( "plmn": HuaweiSensorEntityDescription(
key="plmn",
name="PLMN", name="PLMN",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "rac"): SensorMeta( "rac": HuaweiSensorEntityDescription(
key="rac",
name="RAC", name="RAC",
icon="mdi:map-marker", icon="mdi:map-marker",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "rrc_status"): SensorMeta( "rrc_status": HuaweiSensorEntityDescription(
key="rrc_status",
name="RRC status", name="RRC status",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "rscp"): SensorMeta( "rscp": HuaweiSensorEntityDescription(
key="rscp",
name="RSCP", name="RSCP",
device_class=SensorDeviceClass.SIGNAL_STRENGTH, device_class=SensorDeviceClass.SIGNAL_STRENGTH,
# https://wiki.teltonika.lt/view/RSCP # https://wiki.teltonika.lt/view/RSCP
icon=lambda x: ( icon_fn=lambda x: (
"mdi:signal-cellular-outline", "mdi:signal-cellular-outline",
"mdi:signal-cellular-1", "mdi:signal-cellular-1",
"mdi:signal-cellular-2", "mdi:signal-cellular-2",
@ -202,11 +253,12 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "rsrp"): SensorMeta( "rsrp": HuaweiSensorEntityDescription(
key="rsrp",
name="RSRP", name="RSRP",
device_class=SensorDeviceClass.SIGNAL_STRENGTH, device_class=SensorDeviceClass.SIGNAL_STRENGTH,
# http://www.lte-anbieter.info/technik/rsrp.php # http://www.lte-anbieter.info/technik/rsrp.php
icon=lambda x: ( icon_fn=lambda x: (
"mdi:signal-cellular-outline", "mdi:signal-cellular-outline",
"mdi:signal-cellular-1", "mdi:signal-cellular-1",
"mdi:signal-cellular-2", "mdi:signal-cellular-2",
@ -216,11 +268,12 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=True, entity_registry_enabled_default=True,
), ),
(KEY_DEVICE_SIGNAL, "rsrq"): SensorMeta( "rsrq": HuaweiSensorEntityDescription(
key="rsrq",
name="RSRQ", name="RSRQ",
device_class=SensorDeviceClass.SIGNAL_STRENGTH, device_class=SensorDeviceClass.SIGNAL_STRENGTH,
# http://www.lte-anbieter.info/technik/rsrq.php # http://www.lte-anbieter.info/technik/rsrq.php
icon=lambda x: ( icon_fn=lambda x: (
"mdi:signal-cellular-outline", "mdi:signal-cellular-outline",
"mdi:signal-cellular-1", "mdi:signal-cellular-1",
"mdi:signal-cellular-2", "mdi:signal-cellular-2",
@ -230,11 +283,12 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=True, entity_registry_enabled_default=True,
), ),
(KEY_DEVICE_SIGNAL, "rssi"): SensorMeta( "rssi": HuaweiSensorEntityDescription(
key="rssi",
name="RSSI", name="RSSI",
device_class=SensorDeviceClass.SIGNAL_STRENGTH, device_class=SensorDeviceClass.SIGNAL_STRENGTH,
# https://eyesaas.com/wi-fi-signal-strength/ # https://eyesaas.com/wi-fi-signal-strength/
icon=lambda x: ( icon_fn=lambda x: (
"mdi:signal-cellular-outline", "mdi:signal-cellular-outline",
"mdi:signal-cellular-1", "mdi:signal-cellular-1",
"mdi:signal-cellular-2", "mdi:signal-cellular-2",
@ -244,11 +298,12 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=True, entity_registry_enabled_default=True,
), ),
(KEY_DEVICE_SIGNAL, "sinr"): SensorMeta( "sinr": HuaweiSensorEntityDescription(
key="sinr",
name="SINR", name="SINR",
device_class=SensorDeviceClass.SIGNAL_STRENGTH, device_class=SensorDeviceClass.SIGNAL_STRENGTH,
# http://www.lte-anbieter.info/technik/sinr.php # http://www.lte-anbieter.info/technik/sinr.php
icon=lambda x: ( icon_fn=lambda x: (
"mdi:signal-cellular-outline", "mdi:signal-cellular-outline",
"mdi:signal-cellular-1", "mdi:signal-cellular-1",
"mdi:signal-cellular-2", "mdi:signal-cellular-2",
@ -258,176 +313,227 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=True, entity_registry_enabled_default=True,
), ),
(KEY_DEVICE_SIGNAL, "tac"): SensorMeta( "tac": HuaweiSensorEntityDescription(
key="tac",
name="TAC", name="TAC",
icon="mdi:map-marker", icon="mdi:map-marker",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "tdd"): SensorMeta( "tdd": HuaweiSensorEntityDescription(
key="tdd",
name="TDD", name="TDD",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "transmode"): SensorMeta( "transmode": HuaweiSensorEntityDescription(
key="transmode",
name="Transmission mode", name="Transmission mode",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "txpower"): SensorMeta( "txpower": HuaweiSensorEntityDescription(
key="txpower",
name="Transmit power", name="Transmit power",
device_class=SensorDeviceClass.SIGNAL_STRENGTH, device_class=SensorDeviceClass.SIGNAL_STRENGTH,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "ul_mcs"): SensorMeta( "ul_mcs": HuaweiSensorEntityDescription(
key="ul_mcs",
name="Uplink MCS", name="Uplink MCS",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "ulbandwidth"): SensorMeta( "ulbandwidth": HuaweiSensorEntityDescription(
key="ulbandwidth",
name="Uplink bandwidth", name="Uplink bandwidth",
icon=lambda x: ( icon_fn=lambda x: (
"mdi:speedometer-slow", "mdi:speedometer-slow",
"mdi:speedometer-medium", "mdi:speedometer-medium",
"mdi:speedometer", "mdi:speedometer",
)[bisect((8, 15), x if x is not None else -1000)], )[bisect((8, 15), x if x is not None else -1000)],
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
}
),
# #
# Monitoring # Monitoring
# #
KEY_MONITORING_CHECK_NOTIFICATIONS: SensorMeta( KEY_MONITORING_CHECK_NOTIFICATIONS: HuaweiSensorGroup(
exclude=re.compile( exclude=re.compile(
r"^(onlineupdatestatus|smsstoragefull)$", r"^(onlineupdatestatus|smsstoragefull)$",
re.IGNORECASE, re.IGNORECASE,
)
), ),
(KEY_MONITORING_CHECK_NOTIFICATIONS, "UnreadMessage"): SensorMeta( descriptions={
name="SMS unread", icon="mdi:email-arrow-left" "UnreadMessage": HuaweiSensorEntityDescription(
key="UnreadMessage", name="SMS unread", icon="mdi:email-arrow-left"
), ),
KEY_MONITORING_MONTH_STATISTICS: SensorMeta( },
exclude=re.compile(r"^month(duration|lastcleartime)$", re.IGNORECASE)
), ),
(KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthDownload"): SensorMeta( KEY_MONITORING_MONTH_STATISTICS: HuaweiSensorGroup(
exclude=re.compile(r"^month(duration|lastcleartime)$", re.IGNORECASE),
descriptions={
"CurrentMonthDownload": HuaweiSensorEntityDescription(
key="CurrentMonthDownload",
name="Current month download", name="Current month download",
native_unit_of_measurement=DATA_BYTES, native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:download", icon="mdi:download",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
(KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthUpload"): SensorMeta( "CurrentMonthUpload": HuaweiSensorEntityDescription(
key="CurrentMonthUpload",
name="Current month upload", name="Current month upload",
native_unit_of_measurement=DATA_BYTES, native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:upload", icon="mdi:upload",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
KEY_MONITORING_STATUS: SensorMeta( },
),
KEY_MONITORING_STATUS: HuaweiSensorGroup(
include=re.compile( include=re.compile(
r"^(batterypercent|currentwifiuser|(primary|secondary).*dns)$", r"^(batterypercent|currentwifiuser|(primary|secondary).*dns)$",
re.IGNORECASE, re.IGNORECASE,
)
), ),
(KEY_MONITORING_STATUS, "BatteryPercent"): SensorMeta( descriptions={
"BatteryPercent": HuaweiSensorEntityDescription(
key="BatteryPercent",
name="Battery", name="Battery",
device_class=SensorDeviceClass.BATTERY, device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_MONITORING_STATUS, "CurrentWifiUser"): SensorMeta( "CurrentWifiUser": HuaweiSensorEntityDescription(
key="CurrentWifiUser",
name="WiFi clients connected", name="WiFi clients connected",
icon="mdi:wifi", icon="mdi:wifi",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_MONITORING_STATUS, "PrimaryDns"): SensorMeta( "PrimaryDns": HuaweiSensorEntityDescription(
key="PrimaryDns",
name="Primary DNS server", name="Primary DNS server",
icon="mdi:ip", icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_MONITORING_STATUS, "PrimaryIPv6Dns"): SensorMeta( "PrimaryIPv6Dns": HuaweiSensorEntityDescription(
key="PrimaryIPv6Dns",
name="Primary IPv6 DNS server", name="Primary IPv6 DNS server",
icon="mdi:ip", icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_MONITORING_STATUS, "SecondaryDns"): SensorMeta( "SecondaryDns": HuaweiSensorEntityDescription(
key="SecondaryDns",
name="Secondary DNS server", name="Secondary DNS server",
icon="mdi:ip", icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_MONITORING_STATUS, "SecondaryIPv6Dns"): SensorMeta( "SecondaryIPv6Dns": HuaweiSensorEntityDescription(
key="SecondaryIPv6Dns",
name="Secondary IPv6 DNS server", name="Secondary IPv6 DNS server",
icon="mdi:ip", icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
KEY_MONITORING_TRAFFIC_STATISTICS: SensorMeta( },
exclude=re.compile(r"^showtraffic$", re.IGNORECASE)
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentConnectTime"): SensorMeta( KEY_MONITORING_TRAFFIC_STATISTICS: HuaweiSensorGroup(
exclude=re.compile(r"^showtraffic$", re.IGNORECASE),
descriptions={
"CurrentConnectTime": HuaweiSensorEntityDescription(
key="CurrentConnectTime",
name="Current connection duration", name="Current connection duration",
native_unit_of_measurement=TIME_SECONDS, native_unit_of_measurement=UnitOfTime.SECONDS,
device_class=SensorDeviceClass.DURATION,
icon="mdi:timer-outline", icon="mdi:timer-outline",
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownload"): SensorMeta( "CurrentDownload": HuaweiSensorEntityDescription(
key="CurrentDownload",
name="Current connection download", name="Current connection download",
native_unit_of_measurement=DATA_BYTES, native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:download", icon="mdi:download",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownloadRate"): SensorMeta( "CurrentDownloadRate": HuaweiSensorEntityDescription(
key="CurrentDownloadRate",
name="Current download rate", name="Current download rate",
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE, device_class=SensorDeviceClass.DATA_RATE,
icon="mdi:download", icon="mdi:download",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUpload"): SensorMeta( "CurrentUpload": HuaweiSensorEntityDescription(
key="CurrentUpload",
name="Current connection upload", name="Current connection upload",
native_unit_of_measurement=DATA_BYTES, native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:upload", icon="mdi:upload",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUploadRate"): SensorMeta( "CurrentUploadRate": HuaweiSensorEntityDescription(
key="CurrentUploadRate",
name="Current upload rate", name="Current upload rate",
native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE, device_class=SensorDeviceClass.DATA_RATE,
icon="mdi:upload", icon="mdi:upload",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "TotalConnectTime"): SensorMeta( "TotalConnectTime": HuaweiSensorEntityDescription(
key="TotalConnectTime",
name="Total connected duration", name="Total connected duration",
native_unit_of_measurement=TIME_SECONDS, native_unit_of_measurement=UnitOfTime.SECONDS,
device_class=SensorDeviceClass.DURATION,
icon="mdi:timer-outline", icon="mdi:timer-outline",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "TotalDownload"): SensorMeta( "TotalDownload": HuaweiSensorEntityDescription(
key="TotalDownload",
name="Total download", name="Total download",
native_unit_of_measurement=DATA_BYTES, native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:download", icon="mdi:download",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "TotalUpload"): SensorMeta( "TotalUpload": HuaweiSensorEntityDescription(
key="TotalUpload",
name="Total upload", name="Total upload",
native_unit_of_measurement=DATA_BYTES, native_unit_of_measurement=UnitOfInformation.BYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:upload", icon="mdi:upload",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
},
),
# #
# Network # Network
# #
KEY_NET_CURRENT_PLMN: SensorMeta( KEY_NET_CURRENT_PLMN: HuaweiSensorGroup(
exclude=re.compile(r"^(Rat|ShortName|Spn)$", re.IGNORECASE) exclude=re.compile(r"^(Rat|ShortName|Spn)$", re.IGNORECASE),
), descriptions={
(KEY_NET_CURRENT_PLMN, "FullName"): SensorMeta( "FullName": HuaweiSensorEntityDescription(
key="FullName",
name="Operator name", name="Operator name",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_NET_CURRENT_PLMN, "Numeric"): SensorMeta( "Numeric": HuaweiSensorEntityDescription(
key="Numeric",
name="Operator code", name="Operator code",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_NET_CURRENT_PLMN, "State"): SensorMeta( "State": HuaweiSensorEntityDescription(
key="State",
name="Operator search mode", name="Operator search mode",
formatter=lambda x: ({"0": "Auto", "1": "Manual"}.get(x, "Unknown"), None), formatter=lambda x: (
{"0": "Auto", "1": "Manual"}.get(x, "Unknown"),
None,
),
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
KEY_NET_NET_MODE: SensorMeta(include=re.compile(r"^NetworkMode$", re.IGNORECASE)), },
(KEY_NET_NET_MODE, "NetworkMode"): SensorMeta( ),
KEY_NET_NET_MODE: HuaweiSensorGroup(
include=re.compile(r"^NetworkMode$", re.IGNORECASE),
descriptions={
"NetworkMode": HuaweiSensorEntityDescription(
key="NetworkMode",
name="Preferred mode", name="Preferred mode",
formatter=lambda x: ( formatter=lambda x: (
{ {
@ -443,57 +549,75 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
), ),
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
},
),
# #
# SMS # SMS
# #
(KEY_SMS_SMS_COUNT, "LocalDeleted"): SensorMeta( KEY_SMS_SMS_COUNT: HuaweiSensorGroup(
descriptions={
"LocalDeleted": HuaweiSensorEntityDescription(
key="LocalDeleted",
name="SMS deleted (device)", name="SMS deleted (device)",
icon="mdi:email-minus", icon="mdi:email-minus",
), ),
(KEY_SMS_SMS_COUNT, "LocalDraft"): SensorMeta( "LocalDraft": HuaweiSensorEntityDescription(
key="LocalDraft",
name="SMS drafts (device)", name="SMS drafts (device)",
icon="mdi:email-arrow-right-outline", icon="mdi:email-arrow-right-outline",
), ),
(KEY_SMS_SMS_COUNT, "LocalInbox"): SensorMeta( "LocalInbox": HuaweiSensorEntityDescription(
key="LocalInbox",
name="SMS inbox (device)", name="SMS inbox (device)",
icon="mdi:email", icon="mdi:email",
), ),
(KEY_SMS_SMS_COUNT, "LocalMax"): SensorMeta( "LocalMax": HuaweiSensorEntityDescription(
key="LocalMax",
name="SMS capacity (device)", name="SMS capacity (device)",
icon="mdi:email", icon="mdi:email",
), ),
(KEY_SMS_SMS_COUNT, "LocalOutbox"): SensorMeta( "LocalOutbox": HuaweiSensorEntityDescription(
key="LocalOutbox",
name="SMS outbox (device)", name="SMS outbox (device)",
icon="mdi:email-arrow-right", icon="mdi:email-arrow-right",
), ),
(KEY_SMS_SMS_COUNT, "LocalUnread"): SensorMeta( "LocalUnread": HuaweiSensorEntityDescription(
key="LocalUnread",
name="SMS unread (device)", name="SMS unread (device)",
icon="mdi:email-arrow-left", icon="mdi:email-arrow-left",
), ),
(KEY_SMS_SMS_COUNT, "SimDraft"): SensorMeta( "SimDraft": HuaweiSensorEntityDescription(
key="SimDraft",
name="SMS drafts (SIM)", name="SMS drafts (SIM)",
icon="mdi:email-arrow-right-outline", icon="mdi:email-arrow-right-outline",
), ),
(KEY_SMS_SMS_COUNT, "SimInbox"): SensorMeta( "SimInbox": HuaweiSensorEntityDescription(
key="SimInbox",
name="SMS inbox (SIM)", name="SMS inbox (SIM)",
icon="mdi:email", icon="mdi:email",
), ),
(KEY_SMS_SMS_COUNT, "SimMax"): SensorMeta( "SimMax": HuaweiSensorEntityDescription(
key="SimMax",
name="SMS capacity (SIM)", name="SMS capacity (SIM)",
icon="mdi:email", icon="mdi:email",
), ),
(KEY_SMS_SMS_COUNT, "SimOutbox"): SensorMeta( "SimOutbox": HuaweiSensorEntityDescription(
key="SimOutbox",
name="SMS outbox (SIM)", name="SMS outbox (SIM)",
icon="mdi:email-arrow-right", icon="mdi:email-arrow-right",
), ),
(KEY_SMS_SMS_COUNT, "SimUnread"): SensorMeta( "SimUnread": HuaweiSensorEntityDescription(
key="SimUnread",
name="SMS unread (SIM)", name="SMS unread (SIM)",
icon="mdi:email-arrow-left", icon="mdi:email-arrow-left",
), ),
(KEY_SMS_SMS_COUNT, "SimUsed"): SensorMeta( "SimUsed": HuaweiSensorEntityDescription(
key="SimUsed",
name="SMS messages (SIM)", name="SMS messages (SIM)",
icon="mdi:email-arrow-left", icon="mdi:email-arrow-left",
), ),
},
),
} }
@ -516,43 +640,32 @@ async def async_setup_entry(
for item in items: for item in items:
sensors.append( sensors.append(
HuaweiLteSensor( HuaweiLteSensor(
router, key, item, SENSOR_META.get((key, item), SensorMeta()) router,
key,
item,
SENSOR_META[key].descriptions.get(
item, HuaweiSensorEntityDescription(key=item)
),
) )
) )
async_add_entities(sensors, True) async_add_entities(sensors, True)
def format_default(value: StateType) -> tuple[StateType, str | None]:
"""Format value."""
unit = None
if value is not None:
# Clean up value and infer unit, e.g. -71dBm, 15 dB
if match := re.match(
r"([>=<]*)(?P<value>.+?)\s*(?P<unit>[a-zA-Z]+)\s*$", str(value)
):
try:
value = float(match.group("value"))
unit = match.group("unit")
except ValueError:
pass
return value, unit
@dataclass @dataclass
class HuaweiLteSensor(HuaweiLteBaseEntityWithDevice, SensorEntity): class HuaweiLteSensor(HuaweiLteBaseEntityWithDevice, SensorEntity):
"""Huawei LTE sensor entity.""" """Huawei LTE sensor entity."""
key: str key: str
item: str item: str
meta: SensorMeta entity_description: HuaweiSensorEntityDescription
_state: StateType = field(default=STATE_UNKNOWN, init=False) _state: StateType = field(default=STATE_UNKNOWN, init=False)
_unit: str | None = field(default=None, init=False) _unit: str | None = field(default=None, init=False)
def __post_init__(self) -> None: def __post_init__(self) -> None:
"""Initialize remaining attributes.""" """Initialize remaining attributes."""
self._attr_name = self.meta.name or self.item self._attr_name = self.entity_description.name or self.item
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
"""Subscribe to needed data on add.""" """Subscribe to needed data on add."""
@ -573,33 +686,17 @@ class HuaweiLteSensor(HuaweiLteBaseEntityWithDevice, SensorEntity):
"""Return sensor state.""" """Return sensor state."""
return self._state return self._state
@property
def device_class(self) -> SensorDeviceClass | None:
"""Return sensor device class."""
return self.meta.device_class
@property @property
def native_unit_of_measurement(self) -> str | None: def native_unit_of_measurement(self) -> str | None:
"""Return sensor's unit of measurement.""" """Return sensor's unit of measurement."""
return self.meta.native_unit_of_measurement or self._unit return self.entity_description.native_unit_of_measurement or self._unit
@property @property
def icon(self) -> str | None: def icon(self) -> str | None:
"""Return icon for sensor.""" """Return icon for sensor."""
icon = self.meta.icon if self.entity_description.icon_fn:
if callable(icon): return self.entity_description.icon_fn(self.state)
return icon(self.state) return self.entity_description.icon
return icon
@property
def state_class(self) -> SensorStateClass | None:
"""Return sensor state class."""
return self.meta.state_class
@property
def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry."""
return self.meta.entity_registry_enabled_default
async def async_update(self) -> None: async def async_update(self) -> None:
"""Update state.""" """Update state."""
@ -609,14 +706,7 @@ class HuaweiLteSensor(HuaweiLteBaseEntityWithDevice, SensorEntity):
_LOGGER.debug("%s[%s] not in data", self.key, self.item) _LOGGER.debug("%s[%s] not in data", self.key, self.item)
value = None value = None
formatter = self.meta.formatter formatter = self.entity_description.formatter
if not callable(formatter):
formatter = format_default
self._state, self._unit = formatter(value) self._state, self._unit = formatter(value)
self._available = value is not None self._available = value is not None
@property
def entity_category(self) -> EntityCategory | None:
"""Return category of entity, if any."""
return self.meta.entity_category