Huawei LTE simplifications (#62770)

* Use enum types rather than strs in sensor type hints

* Name sensor meta fields same as in SensorEntityDescription

* Make integration shared state a NamedTuple

* Use dataclasses instead of attr
This commit is contained in:
Ville Skyttä 2021-12-26 09:17:59 +02:00 committed by GitHub
parent e982e7403a
commit 3e3fb52dfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 101 additions and 95 deletions

View File

@ -4,12 +4,12 @@ from __future__ import annotations
from collections import defaultdict from collections import defaultdict
from collections.abc import Callable from collections.abc import Callable
from contextlib import suppress from contextlib import suppress
from dataclasses import dataclass, field
from datetime import timedelta from datetime import timedelta
import logging import logging
import time import time
from typing import Any, cast from typing import Any, NamedTuple, cast
import attr
from huawei_lte_api.AuthorizedConnection import AuthorizedConnection from huawei_lte_api.AuthorizedConnection import AuthorizedConnection
from huawei_lte_api.Client import Client from huawei_lte_api.Client import Client
from huawei_lte_api.Connection import Connection from huawei_lte_api.Connection import Connection
@ -126,26 +126,28 @@ PLATFORMS = [
] ]
@attr.s @dataclass
class Router: class Router:
"""Class for router state.""" """Class for router state."""
hass: HomeAssistant = attr.ib() hass: HomeAssistant
config_entry: ConfigEntry = attr.ib() config_entry: ConfigEntry
connection: Connection = attr.ib() connection: Connection
url: str = attr.ib() url: str
data: dict[str, Any] = attr.ib(init=False, factory=dict) data: dict[str, Any] = field(default_factory=dict, init=False)
subscriptions: dict[str, set[str]] = attr.ib( subscriptions: dict[str, set[str]] = field(
default_factory=lambda: defaultdict(
set, ((x, {"initial_scan"}) for x in ALL_KEYS)
),
init=False, init=False,
factory=lambda: defaultdict(set, ((x, {"initial_scan"}) for x in ALL_KEYS)),
) )
inflight_gets: set[str] = attr.ib(init=False, factory=set) inflight_gets: set[str] = field(default_factory=set, init=False)
client: Client client: Client = field(init=False)
suspended = attr.ib(init=False, default=False) suspended: bool = field(default=False, init=False)
notify_last_attempt: float = attr.ib(init=False, default=-1) notify_last_attempt: float = field(default=-1, init=False)
def __attrs_post_init__(self) -> None: def __post_init__(self) -> None:
"""Set up internal state on init.""" """Set up internal state on init."""
self.client = Client(self.connection) self.client = Client(self.connection)
@ -293,14 +295,13 @@ class Router:
self.logout() self.logout()
@attr.s class HuaweiLteData(NamedTuple):
class HuaweiLteData:
"""Shared state.""" """Shared state."""
hass_config: ConfigType = attr.ib() hass_config: ConfigType
# Our YAML config, keyed by router URL # Our YAML config, keyed by router URL
config: dict[str, dict[str, Any]] = attr.ib() config: dict[str, dict[str, Any]]
routers: dict[str, Router] = attr.ib(init=False, factory=dict) routers: dict[str, Router]
async def async_setup_entry( # noqa: C901 async def async_setup_entry( # noqa: C901
@ -509,7 +510,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
# Arrange our YAML config to dict with normalized URLs as keys # Arrange our YAML config to dict with normalized URLs as keys
domain_config: dict[str, dict[str, Any]] = {} domain_config: dict[str, dict[str, Any]] = {}
if DOMAIN not in hass.data: if DOMAIN not in hass.data:
hass.data[DOMAIN] = HuaweiLteData(hass_config=config, config=domain_config) hass.data[DOMAIN] = HuaweiLteData(
hass_config=config, config=domain_config, routers={}
)
for router_config in config.get(DOMAIN, []): for router_config in config.get(DOMAIN, []):
domain_config[url_normalize(router_config.pop(CONF_URL))] = router_config domain_config[url_normalize(router_config.pop(CONF_URL))] = router_config
@ -607,14 +610,14 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
return True return True
@attr.s @dataclass
class HuaweiLteBaseEntity(Entity): class HuaweiLteBaseEntity(Entity):
"""Huawei LTE entity base class.""" """Huawei LTE entity base class."""
router: Router = attr.ib() router: Router
_available: bool = attr.ib(init=False, default=True) _available: bool = field(default=True, init=False)
_unsub_handlers: list[Callable] = attr.ib(init=False, factory=list) _unsub_handlers: list[Callable] = field(default_factory=list, init=False)
@property @property
def _entity_name(self) -> str: def _entity_name(self) -> str:

View File

@ -1,10 +1,10 @@
"""Support for Huawei LTE binary sensors.""" """Support for Huawei LTE binary sensors."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field
import logging import logging
from typing import Any from typing import Any
import attr
from huawei_lte_api.enums.cradle import ConnectionStatusEnum from huawei_lte_api.enums.cradle import ConnectionStatusEnum
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
@ -48,13 +48,13 @@ async def async_setup_entry(
async_add_entities(entities, True) async_add_entities(entities, True)
@attr.s @dataclass
class HuaweiLteBaseBinarySensor(HuaweiLteBaseEntity, BinarySensorEntity): class HuaweiLteBaseBinarySensor(HuaweiLteBaseEntity, BinarySensorEntity):
"""Huawei LTE binary sensor device base class.""" """Huawei LTE binary sensor device base class."""
key: str key: str = field(init=False)
item: str item: str = field(init=False)
_raw_state: str | None = attr.ib(init=False, default=None) _raw_state: str | None = field(default=None, init=False)
@property @property
def entity_registry_enabled_default(self) -> bool: def entity_registry_enabled_default(self) -> bool:
@ -101,11 +101,11 @@ CONNECTION_STATE_ATTRIBUTES = {
} }
@attr.s @dataclass
class HuaweiLteMobileConnectionBinarySensor(HuaweiLteBaseBinarySensor): class HuaweiLteMobileConnectionBinarySensor(HuaweiLteBaseBinarySensor):
"""Huawei LTE mobile connection binary sensor.""" """Huawei LTE mobile connection binary sensor."""
def __attrs_post_init__(self) -> None: def __post_init__(self) -> None:
"""Initialize identifiers.""" """Initialize identifiers."""
self.key = KEY_MONITORING_STATUS self.key = KEY_MONITORING_STATUS
self.item = "ConnectionStatus" self.item = "ConnectionStatus"
@ -172,11 +172,11 @@ class HuaweiLteBaseWifiStatusBinarySensor(HuaweiLteBaseBinarySensor):
return "mdi:wifi" if self.is_on else "mdi:wifi-off" return "mdi:wifi" if self.is_on else "mdi:wifi-off"
@attr.s @dataclass
class HuaweiLteWifiStatusBinarySensor(HuaweiLteBaseWifiStatusBinarySensor): class HuaweiLteWifiStatusBinarySensor(HuaweiLteBaseWifiStatusBinarySensor):
"""Huawei LTE WiFi status binary sensor.""" """Huawei LTE WiFi status binary sensor."""
def __attrs_post_init__(self) -> None: def __post_init__(self) -> None:
"""Initialize identifiers.""" """Initialize identifiers."""
self.key = KEY_MONITORING_STATUS self.key = KEY_MONITORING_STATUS
self.item = "WifiStatus" self.item = "WifiStatus"
@ -186,11 +186,11 @@ class HuaweiLteWifiStatusBinarySensor(HuaweiLteBaseWifiStatusBinarySensor):
return "WiFi status" return "WiFi status"
@attr.s @dataclass
class HuaweiLteWifi24ghzStatusBinarySensor(HuaweiLteBaseWifiStatusBinarySensor): class HuaweiLteWifi24ghzStatusBinarySensor(HuaweiLteBaseWifiStatusBinarySensor):
"""Huawei LTE 2.4GHz WiFi status binary sensor.""" """Huawei LTE 2.4GHz WiFi status binary sensor."""
def __attrs_post_init__(self) -> None: def __post_init__(self) -> None:
"""Initialize identifiers.""" """Initialize identifiers."""
self.key = KEY_WLAN_WIFI_FEATURE_SWITCH self.key = KEY_WLAN_WIFI_FEATURE_SWITCH
self.item = "wifi24g_switch_enable" self.item = "wifi24g_switch_enable"
@ -200,11 +200,11 @@ class HuaweiLteWifi24ghzStatusBinarySensor(HuaweiLteBaseWifiStatusBinarySensor):
return "2.4GHz WiFi status" return "2.4GHz WiFi status"
@attr.s @dataclass
class HuaweiLteWifi5ghzStatusBinarySensor(HuaweiLteBaseWifiStatusBinarySensor): class HuaweiLteWifi5ghzStatusBinarySensor(HuaweiLteBaseWifiStatusBinarySensor):
"""Huawei LTE 5GHz WiFi status binary sensor.""" """Huawei LTE 5GHz WiFi status binary sensor."""
def __attrs_post_init__(self) -> None: def __post_init__(self) -> None:
"""Initialize identifiers.""" """Initialize identifiers."""
self.key = KEY_WLAN_WIFI_FEATURE_SWITCH self.key = KEY_WLAN_WIFI_FEATURE_SWITCH
self.item = "wifi5g_enabled" self.item = "wifi5g_enabled"
@ -214,11 +214,11 @@ class HuaweiLteWifi5ghzStatusBinarySensor(HuaweiLteBaseWifiStatusBinarySensor):
return "5GHz WiFi status" return "5GHz WiFi status"
@attr.s @dataclass
class HuaweiLteSmsStorageFullBinarySensor(HuaweiLteBaseBinarySensor): class HuaweiLteSmsStorageFullBinarySensor(HuaweiLteBaseBinarySensor):
"""Huawei LTE SMS storage full binary sensor.""" """Huawei LTE SMS storage full binary sensor."""
def __attrs_post_init__(self) -> None: def __post_init__(self) -> None:
"""Initialize identifiers.""" """Initialize identifiers."""
self.key = KEY_MONITORING_CHECK_NOTIFICATIONS self.key = KEY_MONITORING_CHECK_NOTIFICATIONS
self.item = "SmsStorageFull" self.item = "SmsStorageFull"

View File

@ -1,11 +1,11 @@
"""Support for device tracking of Huawei LTE routers.""" """Support for device tracking of Huawei LTE routers."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field
import logging import logging
import re import re
from typing import Any, Dict, List, cast from typing import Any, Dict, List, cast
import attr
from stringcase import snakecase from stringcase import snakecase
from homeassistant.components.device_tracker.config_entry import ScannerEntity from homeassistant.components.device_tracker.config_entry import ScannerEntity
@ -173,16 +173,16 @@ def _better_snakecase(text: str) -> str:
return cast(str, snakecase(text)) return cast(str, snakecase(text))
@attr.s @dataclass
class HuaweiLteScannerEntity(HuaweiLteBaseEntity, ScannerEntity): class HuaweiLteScannerEntity(HuaweiLteBaseEntity, ScannerEntity):
"""Huawei LTE router scanner entity.""" """Huawei LTE router scanner entity."""
_mac_address: str = attr.ib() _mac_address: str
_ip_address: str | None = attr.ib(init=False, default=None) _ip_address: str | None = field(default=None, init=False)
_is_connected: bool = attr.ib(init=False, default=False) _is_connected: bool = field(default=False, init=False)
_hostname: str | None = attr.ib(init=False, default=None) _hostname: str | None = field(default=None, init=False)
_extra_state_attributes: dict[str, Any] = attr.ib(init=False, factory=dict) _extra_state_attributes: dict[str, Any] = field(default_factory=dict, init=False)
@property @property
def _entity_name(self) -> str: def _entity_name(self) -> str:

View File

@ -1,11 +1,11 @@
"""Support for Huawei LTE router notifications.""" """Support for Huawei LTE router notifications."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
import logging import logging
import time import time
from typing import Any from typing import Any
import attr
from huawei_lte_api.exceptions import ResponseErrorException from huawei_lte_api.exceptions import ResponseErrorException
from homeassistant.components.notify import ATTR_TARGET, BaseNotificationService from homeassistant.components.notify import ATTR_TARGET, BaseNotificationService
@ -33,12 +33,12 @@ async def async_get_service(
return HuaweiLteSmsNotificationService(router, default_targets) return HuaweiLteSmsNotificationService(router, default_targets)
@attr.s @dataclass
class HuaweiLteSmsNotificationService(BaseNotificationService): class HuaweiLteSmsNotificationService(BaseNotificationService):
"""Huawei LTE router SMS notification service.""" """Huawei LTE router SMS notification service."""
router: Router = attr.ib() router: Router
default_targets: list[str] = attr.ib() default_targets: list[str]
def send_message(self, message: str = "", **kwargs: Any) -> None: def send_message(self, message: str = "", **kwargs: Any) -> None:
"""Send message to target numbers.""" """Send message to target numbers."""

View File

@ -3,12 +3,11 @@ from __future__ import annotations
from bisect import bisect from bisect import bisect
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass, field
import logging import logging
import re import re
from typing import NamedTuple from typing import NamedTuple
import attr
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
DOMAIN as SENSOR_DOMAIN, DOMAIN as SENSOR_DOMAIN,
SensorDeviceClass, SensorDeviceClass,
@ -51,12 +50,12 @@ class SensorMeta(NamedTuple):
"""Metadata for defining sensors.""" """Metadata for defining sensors."""
name: str | None = None name: str | None = None
device_class: str | None = None device_class: SensorDeviceClass | None = None
icon: str | Callable[[StateType], str] | None = None icon: str | Callable[[StateType], str] | None = None
unit: str | None = None native_unit_of_measurement: str | None = None
state_class: str | None = None state_class: SensorStateClass | None = None
enabled_default: bool = False entity_registry_enabled_default: bool = False
entity_category: str | None = None 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 formatter: Callable[[str], tuple[StateType, str | None]] | None = None
@ -70,7 +69,7 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
name="WAN IP address", name="WAN IP address",
icon="mdi:ip", icon="mdi:ip",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
enabled_default=True, entity_registry_enabled_default=True,
), ),
(KEY_DEVICE_INFORMATION, "WanIPv6Address"): SensorMeta( (KEY_DEVICE_INFORMATION, "WanIPv6Address"): SensorMeta(
name="WAN IPv6 address", name="WAN IPv6 address",
@ -80,7 +79,7 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
(KEY_DEVICE_INFORMATION, "uptime"): SensorMeta( (KEY_DEVICE_INFORMATION, "uptime"): SensorMeta(
name="Uptime", name="Uptime",
icon="mdi:timer-outline", icon="mdi:timer-outline",
unit=TIME_SECONDS, native_unit_of_measurement=TIME_SECONDS,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
(KEY_DEVICE_SIGNAL, "band"): SensorMeta( (KEY_DEVICE_SIGNAL, "band"): SensorMeta(
@ -181,7 +180,7 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
)[bisect((-11, -8, -5), x if x is not None else -1000)], )[bisect((-11, -8, -5), x if x is not None else -1000)],
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
enabled_default=True, entity_registry_enabled_default=True,
), ),
(KEY_DEVICE_SIGNAL, "rsrp"): SensorMeta( (KEY_DEVICE_SIGNAL, "rsrp"): SensorMeta(
name="RSRP", name="RSRP",
@ -195,7 +194,7 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
)[bisect((-110, -95, -80), x if x is not None else -1000)], )[bisect((-110, -95, -80), x if x is not None else -1000)],
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
enabled_default=True, entity_registry_enabled_default=True,
), ),
(KEY_DEVICE_SIGNAL, "rssi"): SensorMeta( (KEY_DEVICE_SIGNAL, "rssi"): SensorMeta(
name="RSSI", name="RSSI",
@ -209,7 +208,7 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
)[bisect((-80, -70, -60), x if x is not None else -1000)], )[bisect((-80, -70, -60), x if x is not None else -1000)],
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
enabled_default=True, entity_registry_enabled_default=True,
), ),
(KEY_DEVICE_SIGNAL, "sinr"): SensorMeta( (KEY_DEVICE_SIGNAL, "sinr"): SensorMeta(
name="SINR", name="SINR",
@ -223,7 +222,7 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
)[bisect((0, 5, 10), x if x is not None else -1000)], )[bisect((0, 5, 10), x if x is not None else -1000)],
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
enabled_default=True, entity_registry_enabled_default=True,
), ),
(KEY_DEVICE_SIGNAL, "rscp"): SensorMeta( (KEY_DEVICE_SIGNAL, "rscp"): SensorMeta(
name="RSCP", name="RSCP",
@ -298,13 +297,13 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
), ),
(KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthDownload"): SensorMeta( (KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthDownload"): SensorMeta(
name="Current month download", name="Current month download",
unit=DATA_BYTES, native_unit_of_measurement=DATA_BYTES,
icon="mdi:download", icon="mdi:download",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
(KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthUpload"): SensorMeta( (KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthUpload"): SensorMeta(
name="Current month upload", name="Current month upload",
unit=DATA_BYTES, native_unit_of_measurement=DATA_BYTES,
icon="mdi:upload", icon="mdi:upload",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
@ -317,7 +316,7 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
(KEY_MONITORING_STATUS, "BatteryPercent"): SensorMeta( (KEY_MONITORING_STATUS, "BatteryPercent"): SensorMeta(
name="Battery", name="Battery",
device_class=SensorDeviceClass.BATTERY, device_class=SensorDeviceClass.BATTERY,
unit=PERCENTAGE, native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
@ -351,47 +350,49 @@ SENSOR_META: dict[str | tuple[str, str], SensorMeta] = {
exclude=re.compile(r"^showtraffic$", re.IGNORECASE) exclude=re.compile(r"^showtraffic$", re.IGNORECASE)
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentConnectTime"): SensorMeta( (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentConnectTime"): SensorMeta(
name="Current connection duration", unit=TIME_SECONDS, icon="mdi:timer-outline" name="Current connection duration",
native_unit_of_measurement=TIME_SECONDS,
icon="mdi:timer-outline",
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownload"): SensorMeta( (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownload"): SensorMeta(
name="Current connection download", name="Current connection download",
unit=DATA_BYTES, native_unit_of_measurement=DATA_BYTES,
icon="mdi:download", icon="mdi:download",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownloadRate"): SensorMeta( (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownloadRate"): SensorMeta(
name="Current download rate", name="Current download rate",
unit=DATA_RATE_BYTES_PER_SECOND, native_unit_of_measurement=DATA_RATE_BYTES_PER_SECOND,
icon="mdi:download", icon="mdi:download",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUpload"): SensorMeta( (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUpload"): SensorMeta(
name="Current connection upload", name="Current connection upload",
unit=DATA_BYTES, native_unit_of_measurement=DATA_BYTES,
icon="mdi:upload", icon="mdi:upload",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUploadRate"): SensorMeta( (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUploadRate"): SensorMeta(
name="Current upload rate", name="Current upload rate",
unit=DATA_RATE_BYTES_PER_SECOND, native_unit_of_measurement=DATA_RATE_BYTES_PER_SECOND,
icon="mdi:upload", icon="mdi:upload",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "TotalConnectTime"): SensorMeta( (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalConnectTime"): SensorMeta(
name="Total connected duration", name="Total connected duration",
unit=TIME_SECONDS, native_unit_of_measurement=TIME_SECONDS,
icon="mdi:timer-outline", icon="mdi:timer-outline",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "TotalDownload"): SensorMeta( (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalDownload"): SensorMeta(
name="Total download", name="Total download",
unit=DATA_BYTES, native_unit_of_measurement=DATA_BYTES,
icon="mdi:download", icon="mdi:download",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
(KEY_MONITORING_TRAFFIC_STATISTICS, "TotalUpload"): SensorMeta( (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalUpload"): SensorMeta(
name="Total upload", name="Total upload",
unit=DATA_BYTES, native_unit_of_measurement=DATA_BYTES,
icon="mdi:upload", icon="mdi:upload",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
@ -521,16 +522,16 @@ def format_default(value: StateType) -> tuple[StateType, str | None]:
return value, unit return value, unit
@attr.s @dataclass
class HuaweiLteSensor(HuaweiLteBaseEntity, SensorEntity): class HuaweiLteSensor(HuaweiLteBaseEntity, SensorEntity):
"""Huawei LTE sensor entity.""" """Huawei LTE sensor entity."""
key: str = attr.ib() key: str
item: str = attr.ib() item: str
meta: SensorMeta = attr.ib() meta: SensorMeta
_state: StateType = attr.ib(init=False, default=STATE_UNKNOWN) _state: StateType = field(default=STATE_UNKNOWN, init=False)
_unit: str | None = attr.ib(init=False) _unit: str | None = field(default=None, init=False)
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."""
@ -556,14 +557,14 @@ class HuaweiLteSensor(HuaweiLteBaseEntity, SensorEntity):
return self._state return self._state
@property @property
def device_class(self) -> str | None: def device_class(self) -> SensorDeviceClass | None:
"""Return sensor device class.""" """Return sensor device class."""
return self.meta.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.unit or self._unit return self.meta.native_unit_of_measurement or self._unit
@property @property
def icon(self) -> str | None: def icon(self) -> str | None:
@ -574,14 +575,14 @@ class HuaweiLteSensor(HuaweiLteBaseEntity, SensorEntity):
return icon return icon
@property @property
def state_class(self) -> str | None: def state_class(self) -> SensorStateClass | None:
"""Return sensor state class.""" """Return sensor state class."""
return self.meta.state_class return self.meta.state_class
@property @property
def entity_registry_enabled_default(self) -> bool: def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry.""" """Return if the entity should be enabled when first added to the entity registry."""
return self.meta.enabled_default return self.meta.entity_registry_enabled_default
async def async_update(self) -> None: async def async_update(self) -> None:
"""Update state.""" """Update state."""
@ -599,6 +600,6 @@ class HuaweiLteSensor(HuaweiLteBaseEntity, SensorEntity):
self._available = value is not None self._available = value is not None
@property @property
def entity_category(self) -> str | None: def entity_category(self) -> EntityCategory | None:
"""Return category of entity, if any.""" """Return category of entity, if any."""
return self.meta.entity_category return self.meta.entity_category

View File

@ -1,11 +1,10 @@
"""Support for Huawei LTE switches.""" """Support for Huawei LTE switches."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field
import logging import logging
from typing import Any from typing import Any
import attr
from homeassistant.components.switch import ( from homeassistant.components.switch import (
DOMAIN as SWITCH_DOMAIN, DOMAIN as SWITCH_DOMAIN,
SwitchDeviceClass, SwitchDeviceClass,
@ -37,14 +36,17 @@ async def async_setup_entry(
async_add_entities(switches, True) async_add_entities(switches, True)
@attr.s @dataclass
class HuaweiLteBaseSwitch(HuaweiLteBaseEntity, SwitchEntity): class HuaweiLteBaseSwitch(HuaweiLteBaseEntity, SwitchEntity):
"""Huawei LTE switch device base class.""" """Huawei LTE switch device base class."""
key: str key: str = field(init=False)
item: str item: str = field(init=False)
_attr_device_class = SwitchDeviceClass.SWITCH
_raw_state: str | None = attr.ib(init=False, default=None) _attr_device_class: SwitchDeviceClass = field(
default=SwitchDeviceClass.SWITCH, init=False
)
_raw_state: str | None = field(default=None, init=False)
def _turn(self, state: bool) -> None: def _turn(self, state: bool) -> None:
raise NotImplementedError raise NotImplementedError
@ -79,11 +81,11 @@ class HuaweiLteBaseSwitch(HuaweiLteBaseEntity, SwitchEntity):
self._raw_state = str(value) self._raw_state = str(value)
@attr.s @dataclass
class HuaweiLteMobileDataSwitch(HuaweiLteBaseSwitch): class HuaweiLteMobileDataSwitch(HuaweiLteBaseSwitch):
"""Huawei LTE mobile data switch device.""" """Huawei LTE mobile data switch device."""
def __attrs_post_init__(self) -> None: def __post_init__(self) -> None:
"""Initialize identifiers.""" """Initialize identifiers."""
self.key = KEY_DIALUP_MOBILE_DATASWITCH self.key = KEY_DIALUP_MOBILE_DATASWITCH
self.item = "dataswitch" self.item = "dataswitch"