mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 23:27:37 +00:00
Add system diagnostic sensors to SFR Box (#85184)
* Add system diagnostic sensor * Add tests
This commit is contained in:
parent
b0d4b73874
commit
679e971131
@ -1,6 +1,8 @@
|
|||||||
"""SFR Box sensor platform."""
|
"""SFR Box sensor platform."""
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable, Iterable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from itertools import chain
|
||||||
|
from typing import Generic, TypeVar
|
||||||
|
|
||||||
from sfrbox_api.models import DslInfo, SystemInfo
|
from sfrbox_api.models import DslInfo, SystemInfo
|
||||||
|
|
||||||
@ -11,7 +13,12 @@ from homeassistant.components.sensor import (
|
|||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import SIGNAL_STRENGTH_DECIBELS, UnitOfDataRate
|
from homeassistant.const import (
|
||||||
|
SIGNAL_STRENGTH_DECIBELS,
|
||||||
|
UnitOfDataRate,
|
||||||
|
UnitOfElectricPotential,
|
||||||
|
UnitOfTemperature,
|
||||||
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity import EntityCategory
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
@ -22,42 +29,44 @@ from .const import DOMAIN
|
|||||||
from .coordinator import SFRDataUpdateCoordinator
|
from .coordinator import SFRDataUpdateCoordinator
|
||||||
from .models import DomainData
|
from .models import DomainData
|
||||||
|
|
||||||
|
_T = TypeVar("_T")
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SFRBoxSensorMixin:
|
class SFRBoxSensorMixin(Generic[_T]):
|
||||||
"""Mixin for SFR Box sensors."""
|
"""Mixin for SFR Box sensors."""
|
||||||
|
|
||||||
value_fn: Callable[[DslInfo], StateType]
|
value_fn: Callable[[_T], StateType]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SFRBoxSensorEntityDescription(SensorEntityDescription, SFRBoxSensorMixin):
|
class SFRBoxSensorEntityDescription(SensorEntityDescription, SFRBoxSensorMixin[_T]):
|
||||||
"""Description for SFR Box sensors."""
|
"""Description for SFR Box sensors."""
|
||||||
|
|
||||||
|
|
||||||
SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription, ...] = (
|
DSL_SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription[DslInfo], ...] = (
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="linemode",
|
key="linemode",
|
||||||
name="Line mode",
|
name="Line mode",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
value_fn=lambda x: x.linemode,
|
value_fn=lambda x: x.linemode,
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="counter",
|
key="counter",
|
||||||
name="Counter",
|
name="Counter",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
value_fn=lambda x: x.counter,
|
value_fn=lambda x: x.counter,
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="crc",
|
key="crc",
|
||||||
name="CRC",
|
name="CRC",
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
value_fn=lambda x: x.crc,
|
value_fn=lambda x: x.crc,
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="noise_down",
|
key="noise_down",
|
||||||
name="Noise down",
|
name="Noise down",
|
||||||
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||||
@ -67,7 +76,7 @@ SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda x: x.noise_down,
|
value_fn=lambda x: x.noise_down,
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="noise_up",
|
key="noise_up",
|
||||||
name="Noise up",
|
name="Noise up",
|
||||||
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||||
@ -77,7 +86,7 @@ SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda x: x.noise_up,
|
value_fn=lambda x: x.noise_up,
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="attenuation_down",
|
key="attenuation_down",
|
||||||
name="Attenuation down",
|
name="Attenuation down",
|
||||||
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||||
@ -87,7 +96,7 @@ SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda x: x.attenuation_down,
|
value_fn=lambda x: x.attenuation_down,
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="attenuation_up",
|
key="attenuation_up",
|
||||||
name="Attenuation up",
|
name="Attenuation up",
|
||||||
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||||
@ -97,7 +106,7 @@ SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda x: x.attenuation_up,
|
value_fn=lambda x: x.attenuation_up,
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="rate_down",
|
key="rate_down",
|
||||||
name="Rate down",
|
name="Rate down",
|
||||||
device_class=SensorDeviceClass.DATA_RATE,
|
device_class=SensorDeviceClass.DATA_RATE,
|
||||||
@ -105,7 +114,7 @@ SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda x: x.rate_down,
|
value_fn=lambda x: x.rate_down,
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="rate_up",
|
key="rate_up",
|
||||||
name="Rate up",
|
name="Rate up",
|
||||||
device_class=SensorDeviceClass.DATA_RATE,
|
device_class=SensorDeviceClass.DATA_RATE,
|
||||||
@ -113,7 +122,7 @@ SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda x: x.rate_up,
|
value_fn=lambda x: x.rate_up,
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="line_status",
|
key="line_status",
|
||||||
name="Line status",
|
name="Line status",
|
||||||
device_class=SensorDeviceClass.ENUM,
|
device_class=SensorDeviceClass.ENUM,
|
||||||
@ -130,7 +139,7 @@ SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription, ...] = (
|
|||||||
translation_key="line_status",
|
translation_key="line_status",
|
||||||
value_fn=lambda x: x.line_status.lower().replace(" ", "_"),
|
value_fn=lambda x: x.line_status.lower().replace(" ", "_"),
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription(
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="training",
|
key="training",
|
||||||
name="Training",
|
name="Training",
|
||||||
device_class=SensorDeviceClass.ENUM,
|
device_class=SensorDeviceClass.ENUM,
|
||||||
@ -152,6 +161,40 @@ SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription, ...] = (
|
|||||||
value_fn=lambda x: x.training.lower().replace(" ", "_").replace(".", "_"),
|
value_fn=lambda x: x.training.lower().replace(" ", "_").replace(".", "_"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
SYSTEM_SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription[SystemInfo], ...] = (
|
||||||
|
SFRBoxSensorEntityDescription[SystemInfo](
|
||||||
|
key="net_infra",
|
||||||
|
name="Network infrastructure",
|
||||||
|
device_class=SensorDeviceClass.ENUM,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
options=[
|
||||||
|
"adsl",
|
||||||
|
"ftth",
|
||||||
|
"gprs",
|
||||||
|
],
|
||||||
|
translation_key="net_infra",
|
||||||
|
value_fn=lambda x: x.net_infra,
|
||||||
|
),
|
||||||
|
SFRBoxSensorEntityDescription[SystemInfo](
|
||||||
|
key="alimvoltage",
|
||||||
|
name="Voltage",
|
||||||
|
device_class=SensorDeviceClass.VOLTAGE,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT,
|
||||||
|
value_fn=lambda x: x.alimvoltage,
|
||||||
|
),
|
||||||
|
SFRBoxSensorEntityDescription[SystemInfo](
|
||||||
|
key="temperature",
|
||||||
|
name="Temperature",
|
||||||
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||||
|
value_fn=lambda x: x.temperature / 1000,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
@ -160,29 +203,38 @@ async def async_setup_entry(
|
|||||||
"""Set up the sensors."""
|
"""Set up the sensors."""
|
||||||
data: DomainData = hass.data[DOMAIN][entry.entry_id]
|
data: DomainData = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
entities = [
|
entities: Iterable[SFRBoxSensor] = chain(
|
||||||
SFRBoxSensor(data.dsl, description, data.system.data)
|
(
|
||||||
for description in SENSOR_TYPES
|
SFRBoxSensor(data.dsl, description, data.system.data)
|
||||||
]
|
for description in DSL_SENSOR_TYPES
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SFRBoxSensor(data.system, description, data.system.data)
|
||||||
|
for description in SYSTEM_SENSOR_TYPES
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class SFRBoxSensor(CoordinatorEntity[SFRDataUpdateCoordinator[DslInfo]], SensorEntity):
|
class SFRBoxSensor(CoordinatorEntity[SFRDataUpdateCoordinator[_T]], SensorEntity):
|
||||||
"""SFR Box sensor."""
|
"""SFR Box sensor."""
|
||||||
|
|
||||||
entity_description: SFRBoxSensorEntityDescription
|
entity_description: SFRBoxSensorEntityDescription[_T]
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: SFRDataUpdateCoordinator[DslInfo],
|
coordinator: SFRDataUpdateCoordinator[_T],
|
||||||
description: SFRBoxSensorEntityDescription,
|
description: SFRBoxSensorEntityDescription,
|
||||||
system_info: SystemInfo,
|
system_info: SystemInfo,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_unique_id = f"{system_info.mac_addr}_dsl_{description.key}"
|
self._attr_unique_id = (
|
||||||
|
f"{system_info.mac_addr}_{coordinator.name}_{description.key}"
|
||||||
|
)
|
||||||
self._attr_device_info = {"identifiers": {(DOMAIN, system_info.mac_addr)}}
|
self._attr_device_info = {"identifiers": {(DOMAIN, system_info.mac_addr)}}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -26,6 +26,13 @@
|
|||||||
"unknown": "Unknown"
|
"unknown": "Unknown"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"net_infra": {
|
||||||
|
"state": {
|
||||||
|
"adsl": "ADSL",
|
||||||
|
"ftth": "FTTH",
|
||||||
|
"gprs": "GPRS"
|
||||||
|
}
|
||||||
|
},
|
||||||
"training": {
|
"training": {
|
||||||
"state": {
|
"state": {
|
||||||
"idle": "Idle",
|
"idle": "Idle",
|
||||||
|
@ -27,6 +27,13 @@
|
|||||||
"unknown": "Unknown"
|
"unknown": "Unknown"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"net_infra": {
|
||||||
|
"state": {
|
||||||
|
"adsl": "ADSL",
|
||||||
|
"ftth": "FTTH",
|
||||||
|
"gprs": "GPRS"
|
||||||
|
}
|
||||||
|
},
|
||||||
"training": {
|
"training": {
|
||||||
"state": {
|
"state": {
|
||||||
"g_922_channel_analysis": "G.922 Channel Analysis",
|
"g_922_channel_analysis": "G.922 Channel Analysis",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Constants for SFR Box tests."""
|
"""Constants for SFR Box tests."""
|
||||||
from homeassistant.components.select.const import ATTR_OPTIONS
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
|
ATTR_OPTIONS,
|
||||||
ATTR_STATE_CLASS,
|
ATTR_STATE_CLASS,
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
@ -18,6 +18,8 @@ from homeassistant.const import (
|
|||||||
SIGNAL_STRENGTH_DECIBELS,
|
SIGNAL_STRENGTH_DECIBELS,
|
||||||
Platform,
|
Platform,
|
||||||
UnitOfDataRate,
|
UnitOfDataRate,
|
||||||
|
UnitOfElectricPotential,
|
||||||
|
UnitOfTemperature,
|
||||||
)
|
)
|
||||||
|
|
||||||
ATTR_DEFAULT_DISABLED = "default_disabled"
|
ATTR_DEFAULT_DISABLED = "default_disabled"
|
||||||
@ -37,6 +39,30 @@ EXPECTED_ENTITIES = {
|
|||||||
ATTR_SW_VERSION: "NB6VAC-MAIN-R4.0.44k",
|
ATTR_SW_VERSION: "NB6VAC-MAIN-R4.0.44k",
|
||||||
},
|
},
|
||||||
Platform.SENSOR: [
|
Platform.SENSOR: [
|
||||||
|
{
|
||||||
|
ATTR_DEFAULT_DISABLED: True,
|
||||||
|
ATTR_DEVICE_CLASS: SensorDeviceClass.ENUM,
|
||||||
|
ATTR_ENTITY_ID: "sensor.sfr_box_network_infrastructure",
|
||||||
|
ATTR_OPTIONS: ["adsl", "ftth", "gprs"],
|
||||||
|
ATTR_STATE: "adsl",
|
||||||
|
ATTR_UNIQUE_ID: "e4:5d:51:00:11:22_system_net_infra",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ATTR_DEFAULT_DISABLED: True,
|
||||||
|
ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE,
|
||||||
|
ATTR_ENTITY_ID: "sensor.sfr_box_temperature",
|
||||||
|
ATTR_STATE: "27.56",
|
||||||
|
ATTR_UNIQUE_ID: "e4:5d:51:00:11:22_system_temperature",
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ATTR_DEFAULT_DISABLED: True,
|
||||||
|
ATTR_DEVICE_CLASS: SensorDeviceClass.VOLTAGE,
|
||||||
|
ATTR_ENTITY_ID: "sensor.sfr_box_voltage",
|
||||||
|
ATTR_STATE: "12251",
|
||||||
|
ATTR_UNIQUE_ID: "e4:5d:51:00:11:22_system_alimvoltage",
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: UnitOfElectricPotential.MILLIVOLT,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ATTR_DEFAULT_DISABLED: True,
|
ATTR_DEFAULT_DISABLED: True,
|
||||||
ATTR_ENTITY_ID: "sensor.sfr_box_line_mode",
|
ATTR_ENTITY_ID: "sensor.sfr_box_line_mode",
|
||||||
|
@ -33,6 +33,7 @@ def _check_and_enable_disabled_entities(
|
|||||||
if expected_entity.get(ATTR_DEFAULT_DISABLED):
|
if expected_entity.get(ATTR_DEFAULT_DISABLED):
|
||||||
entity_id = expected_entity[ATTR_ENTITY_ID]
|
entity_id = expected_entity[ATTR_ENTITY_ID]
|
||||||
registry_entry = entity_registry.entities.get(entity_id)
|
registry_entry = entity_registry.entities.get(entity_id)
|
||||||
|
assert registry_entry, f"Registry entry not found for {entity_id}"
|
||||||
assert registry_entry.disabled
|
assert registry_entry.disabled
|
||||||
assert registry_entry.disabled_by is RegistryEntryDisabler.INTEGRATION
|
assert registry_entry.disabled_by is RegistryEntryDisabler.INTEGRATION
|
||||||
entity_registry.async_update_entity(entity_id, **{"disabled_by": None})
|
entity_registry.async_update_entity(entity_id, **{"disabled_by": None})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user