diff --git a/homeassistant/components/qnap_qsw/sensor.py b/homeassistant/components/qnap_qsw/sensor.py index a00103a0f32..94534ced850 100644 --- a/homeassistant/components/qnap_qsw/sensor.py +++ b/homeassistant/components/qnap_qsw/sensor.py @@ -1,19 +1,22 @@ """Support for the QNAP QSW sensors.""" from __future__ import annotations -from dataclasses import dataclass +from dataclasses import dataclass, replace from typing import Final from aioqsw.const import ( QSD_FAN1_SPEED, QSD_FAN2_SPEED, + QSD_LACP_PORTS, QSD_LINK, QSD_PORT_NUM, + QSD_PORTS, QSD_PORTS_STATISTICS, QSD_PORTS_STATUS, QSD_RX_ERRORS, QSD_RX_OCTETS, QSD_RX_SPEED, + QSD_SPEED, QSD_SYSTEM_BOARD, QSD_SYSTEM_SENSOR, QSD_SYSTEM_TIME, @@ -43,7 +46,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ATTR_MAX, DOMAIN, QSW_COORD_DATA, RPM from .coordinator import QswDataCoordinator -from .entity import QswEntityDescription, QswSensorEntity +from .entity import QswEntityDescription, QswEntityType, QswSensorEntity @dataclass @@ -51,6 +54,8 @@ class QswSensorEntityDescription(SensorEntityDescription, QswEntityDescription): """A class that describes QNAP QSW sensor entities.""" attributes: dict[str, list[str]] | None = None + qsw_type: QswEntityType | None = None + sep_key: str = "_" SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( @@ -152,20 +157,195 @@ SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( ), ) +LACP_PORT_SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( + QswSensorEntityDescription( + device_class=SensorDeviceClass.DATA_RATE, + entity_registry_enabled_default=False, + icon="mdi:speedometer", + key=QSD_PORTS_STATUS, + name="Link Speed", + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, + qsw_type=QswEntityType.LACP_PORT, + state_class=SensorStateClass.MEASUREMENT, + subkey=QSD_SPEED, + ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:download-network", + key=QSD_PORTS_STATISTICS, + name="RX", + native_unit_of_measurement=UnitOfInformation.BYTES, + qsw_type=QswEntityType.LACP_PORT, + state_class=SensorStateClass.TOTAL_INCREASING, + subkey=QSD_RX_OCTETS, + ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:close-network", + key=QSD_PORTS_STATISTICS, + entity_category=EntityCategory.DIAGNOSTIC, + name="RX Errors", + qsw_type=QswEntityType.LACP_PORT, + state_class=SensorStateClass.TOTAL_INCREASING, + subkey=QSD_RX_ERRORS, + ), + QswSensorEntityDescription( + device_class=SensorDeviceClass.DATA_RATE, + entity_registry_enabled_default=False, + icon="mdi:download-network", + key=QSD_PORTS_STATISTICS, + name="RX Speed", + native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, + qsw_type=QswEntityType.LACP_PORT, + state_class=SensorStateClass.MEASUREMENT, + subkey=QSD_RX_SPEED, + ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:upload-network", + key=QSD_PORTS_STATISTICS, + name="TX", + native_unit_of_measurement=UnitOfInformation.BYTES, + qsw_type=QswEntityType.LACP_PORT, + state_class=SensorStateClass.TOTAL_INCREASING, + subkey=QSD_TX_OCTETS, + ), + QswSensorEntityDescription( + device_class=SensorDeviceClass.DATA_RATE, + entity_registry_enabled_default=False, + icon="mdi:upload-network", + key=QSD_PORTS_STATISTICS, + name="TX Speed", + native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, + qsw_type=QswEntityType.LACP_PORT, + state_class=SensorStateClass.MEASUREMENT, + subkey=QSD_TX_SPEED, + ), +) + +PORT_SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( + QswSensorEntityDescription( + device_class=SensorDeviceClass.DATA_RATE, + entity_registry_enabled_default=False, + icon="mdi:speedometer", + key=QSD_PORTS_STATUS, + name="Link Speed", + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, + qsw_type=QswEntityType.PORT, + state_class=SensorStateClass.MEASUREMENT, + subkey=QSD_SPEED, + ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:download-network", + key=QSD_PORTS_STATISTICS, + name="RX", + native_unit_of_measurement=UnitOfInformation.BYTES, + qsw_type=QswEntityType.PORT, + state_class=SensorStateClass.TOTAL_INCREASING, + subkey=QSD_RX_OCTETS, + ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:close-network", + key=QSD_PORTS_STATISTICS, + entity_category=EntityCategory.DIAGNOSTIC, + name="RX Errors", + qsw_type=QswEntityType.PORT, + state_class=SensorStateClass.TOTAL_INCREASING, + subkey=QSD_RX_ERRORS, + ), + QswSensorEntityDescription( + device_class=SensorDeviceClass.DATA_RATE, + entity_registry_enabled_default=False, + icon="mdi:download-network", + key=QSD_PORTS_STATISTICS, + name="RX Speed", + native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, + qsw_type=QswEntityType.PORT, + state_class=SensorStateClass.MEASUREMENT, + subkey=QSD_RX_SPEED, + ), + QswSensorEntityDescription( + entity_registry_enabled_default=False, + icon="mdi:upload-network", + key=QSD_PORTS_STATISTICS, + name="TX", + native_unit_of_measurement=UnitOfInformation.BYTES, + qsw_type=QswEntityType.PORT, + state_class=SensorStateClass.TOTAL_INCREASING, + subkey=QSD_TX_OCTETS, + ), + QswSensorEntityDescription( + device_class=SensorDeviceClass.DATA_RATE, + entity_registry_enabled_default=False, + icon="mdi:upload-network", + key=QSD_PORTS_STATISTICS, + name="TX Speed", + native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, + qsw_type=QswEntityType.PORT, + state_class=SensorStateClass.MEASUREMENT, + subkey=QSD_TX_SPEED, + ), +) + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Add QNAP QSW sensors from a config_entry.""" coordinator: QswDataCoordinator = hass.data[DOMAIN][entry.entry_id][QSW_COORD_DATA] - async_add_entities( - QswSensor(coordinator, description, entry) - for description in SENSOR_TYPES + + entities: list[QswSensor] = [] + + for description in SENSOR_TYPES: if ( description.key in coordinator.data and description.subkey in coordinator.data[description.key] - ) - ) + ): + entities.append(QswSensor(coordinator, description, entry)) + + for description in LACP_PORT_SENSOR_TYPES: + if ( + description.key not in coordinator.data + or QSD_LACP_PORTS not in coordinator.data[description.key] + ): + continue + + for port_id, port_values in coordinator.data[description.key][ + QSD_LACP_PORTS + ].items(): + if description.subkey not in port_values: + continue + + _desc = replace( + description, + sep_key=f"_lacp_port_{port_id}_", + name=f"LACP Port {port_id} {description.name}", + ) + entities.append(QswSensor(coordinator, _desc, entry, port_id)) + + for description in PORT_SENSOR_TYPES: + if ( + description.key not in coordinator.data + or QSD_PORTS not in coordinator.data[description.key] + ): + continue + + for port_id, port_values in coordinator.data[description.key][ + QSD_PORTS + ].items(): + if description.subkey not in port_values: + continue + + _desc = replace( + description, + sep_key=f"_port_{port_id}_", + name=f"Port {port_id} {description.name}", + ) + entities.append(QswSensor(coordinator, _desc, entry, port_id)) + + async_add_entities(entities) class QswSensor(QswSensorEntity, SensorEntity): @@ -178,13 +358,13 @@ class QswSensor(QswSensorEntity, SensorEntity): coordinator: QswDataCoordinator, description: QswSensorEntityDescription, entry: ConfigEntry, + type_id: int | None = None, ) -> None: """Initialize.""" - super().__init__(coordinator, entry) + super().__init__(coordinator, entry, type_id) + self._attr_name = f"{self.product} {description.name}" - self._attr_unique_id = ( - f"{entry.unique_id}_{description.key}_{description.subkey}" - ) + self._attr_unique_id = f"{entry.unique_id}_{description.key}{description.sep_key}{description.subkey}" self.entity_description = description self._async_update_attrs() @@ -192,7 +372,9 @@ class QswSensor(QswSensorEntity, SensorEntity): def _async_update_attrs(self) -> None: """Update sensor attributes.""" value = self.get_device_value( - self.entity_description.key, self.entity_description.subkey + self.entity_description.key, + self.entity_description.subkey, + self.entity_description.qsw_type, ) self._attr_native_value = value super()._async_update_attrs() diff --git a/tests/components/qnap_qsw/test_sensor.py b/tests/components/qnap_qsw/test_sensor.py index b37a45e441e..902f65d9258 100644 --- a/tests/components/qnap_qsw/test_sensor.py +++ b/tests/components/qnap_qsw/test_sensor.py @@ -47,3 +47,329 @@ async def test_qnap_qsw_create_sensors( state = hass.states.get("sensor.qsw_m408_4c_uptime") assert state.state == "91" + + # LACP Ports + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_1_link_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_1_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_1_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_1_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_1_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_1_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_2_link_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_2_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_2_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_2_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_2_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_2_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_3_link_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_3_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_3_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_3_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_3_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_3_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_4_link_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_4_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_4_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_4_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_4_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_4_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_5_link_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_5_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_5_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_5_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_5_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_5_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_6_link_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_6_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_6_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_6_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_6_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_lacp_port_6_tx_speed") + assert state.state == "0" + + # Ports + state = hass.states.get("sensor.qsw_m408_4c_port_1_link_speed") + assert state.state == "10000" + + state = hass.states.get("sensor.qsw_m408_4c_port_1_rx") + assert state.state == "20000" + + state = hass.states.get("sensor.qsw_m408_4c_port_1_rx_errors") + assert state.state == "20" + + state = hass.states.get("sensor.qsw_m408_4c_port_1_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_1_tx") + assert state.state == "10000" + + state = hass.states.get("sensor.qsw_m408_4c_port_1_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_2_link_speed") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_2_rx") + assert state.state == "2000" + + state = hass.states.get("sensor.qsw_m408_4c_port_2_rx_errors") + assert state.state == "2" + + state = hass.states.get("sensor.qsw_m408_4c_port_2_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_2_tx") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_2_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_3_link_speed") + assert state.state == "100" + + state = hass.states.get("sensor.qsw_m408_4c_port_3_rx") + assert state.state == "200" + + state = hass.states.get("sensor.qsw_m408_4c_port_3_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_3_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_3_tx") + assert state.state == "100" + + state = hass.states.get("sensor.qsw_m408_4c_port_3_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_4_link_speed") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_4_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_4_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_4_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_4_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_4_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_5_link_speed") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_5_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_5_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_5_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_5_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_5_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_6_link_speed") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_6_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_6_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_6_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_6_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_6_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_7_link_speed") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_7_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_7_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_7_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_7_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_7_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_8_link_speed") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_8_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_8_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_8_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_8_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_8_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_9_link_speed") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_9_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_9_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_9_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_9_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_9_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_10_link_speed") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_10_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_10_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_10_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_10_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_10_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_11_link_speed") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_11_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_11_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_11_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_11_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_11_tx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_12_link_speed") + assert state.state == "1000" + + state = hass.states.get("sensor.qsw_m408_4c_port_12_rx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_12_rx_errors") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_12_rx_speed") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_12_tx") + assert state.state == "0" + + state = hass.states.get("sensor.qsw_m408_4c_port_12_tx_speed") + assert state.state == "0"