diff --git a/homeassistant/components/devolo_home_network/sensor.py b/homeassistant/components/devolo_home_network/sensor.py index 2e175141303e..2e8aa55c6643 100644 --- a/homeassistant/components/devolo_home_network/sensor.py +++ b/homeassistant/components/devolo_home_network/sensor.py @@ -256,13 +256,18 @@ class DevoloPlcDataRateSensorEntity( @property @override - def native_value(self) -> float: + def native_value(self) -> float | None: """State of the sensor.""" - return self.entity_description.value_func( - next( - data_rate - for data_rate in self.coordinator.data.data_rates - if data_rate.mac_address_from == self.device.mac - and data_rate.mac_address_to == self._peer + if ( + data_rate := next( + ( + data_rate + for data_rate in self.coordinator.data.data_rates + if data_rate.mac_address_from == self.device.mac + and data_rate.mac_address_to == self._peer + ), + None, ) - ) + ) is None: + return None + return self.entity_description.value_func(data_rate) diff --git a/tests/components/devolo_home_network/const.py b/tests/components/devolo_home_network/const.py index ddb02879bf40..781ebf8c82c7 100644 --- a/tests/components/devolo_home_network/const.py +++ b/tests/components/devolo_home_network/const.py @@ -172,4 +172,35 @@ PLCNET_ATTACHED = LogicalNetwork( ], ) +PLCNET_MISSING_DATA_RATE = LogicalNetwork( + devices=[ + { + "mac_address": "00:00:5E:00:53:00", + "attached_to_router": False, + "topology": LOCAL, + "user_device_name": "test1", + }, + { + "mac_address": "00:00:5E:00:53:02", + "attached_to_router": True, + "topology": REMOTE, + "user_device_name": "test2", + }, + { + "mac_address": "00:00:5E:00:53:03", + "attached_to_router": False, + "topology": REMOTE, + "user_device_name": "test3", + }, + ], + data_rates=[ + { + "mac_address_from": "00:00:5E:00:53:00", + "mac_address_to": "00:00:5E:00:53:03", + "rx_rate": 150.0, + "tx_rate": 150.0, + }, + ], +) + UPTIME = 100 diff --git a/tests/components/devolo_home_network/snapshots/test_sensor.ambr b/tests/components/devolo_home_network/snapshots/test_sensor.ambr index d770c6bc002d..50e8dd4ed800 100644 --- a/tests/components/devolo_home_network/snapshots/test_sensor.ambr +++ b/tests/components/devolo_home_network/snapshots/test_sensor.ambr @@ -313,3 +313,113 @@ 'unit_of_measurement': , }) # --- +# name: test_update_plc_phyrates_missing_data_rate + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'data_rate', + : 'Mock Title PLC downlink PHY rate (test2)', + : , + }), + 'context': , + 'entity_id': 'sensor.mock_title_plc_downlink_phy_rate_test2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '100.0', + }) +# --- +# name: test_update_plc_phyrates_missing_data_rate.1 + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.mock_title_plc_downlink_phy_rate_test2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'PLC downlink PHY rate (test2)', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'PLC downlink PHY rate (test2)', + 'platform': 'devolo_home_network', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'plc_rx_rate', + 'unique_id': '1234567890_plc_rx_rate_00:00:5E:00:53:02', + 'unit_of_measurement': , + }) +# --- +# name: test_update_plc_phyrates_missing_data_rate.2 + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'data_rate', + : 'Mock Title PLC uplink PHY rate (test2)', + : , + }), + 'context': , + 'entity_id': 'sensor.mock_title_plc_uplink_phy_rate_test2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '100.0', + }) +# --- +# name: test_update_plc_phyrates_missing_data_rate.3 + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.mock_title_plc_uplink_phy_rate_test2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'PLC uplink PHY rate (test2)', + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'PLC uplink PHY rate (test2)', + 'platform': 'devolo_home_network', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'plc_tx_rate', + 'unique_id': '1234567890_plc_tx_rate_00:00:5E:00:53:02', + 'unit_of_measurement': , + }) +# --- diff --git a/tests/components/devolo_home_network/test_sensor.py b/tests/components/devolo_home_network/test_sensor.py index fa2f4ceadb2c..35e769e92742 100644 --- a/tests/components/devolo_home_network/test_sensor.py +++ b/tests/components/devolo_home_network/test_sensor.py @@ -15,12 +15,12 @@ from homeassistant.components.devolo_home_network.const import ( ) from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState -from homeassistant.const import STATE_UNAVAILABLE +from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from . import configure_integration -from .const import PLCNET +from .const import PLCNET, PLCNET_MISSING_DATA_RATE from .mock import MockDevice from tests.common import async_fire_time_changed @@ -193,6 +193,49 @@ async def test_update_plc_phyrates( assert state.state == str(PLCNET.data_rates[0].tx_rate) +async def test_update_plc_phyrates_missing_data_rate( + hass: HomeAssistant, + mock_device: MockDevice, + entity_registry: er.EntityRegistry, + freezer: FrozenDateTimeFactory, + snapshot: SnapshotAssertion, +) -> None: + """Test plc_downlink_phyrate and plc_uplink_phyrate sensors on a lost PLC link.""" + entry = configure_integration(hass) + device_name = entry.title.replace(" ", "_").lower() + entity_id_downlink = ( + f"{SENSOR_DOMAIN}.{device_name}_plc_downlink_phy_rate_" + f"{PLCNET.devices[1].user_device_name}" + ) + entity_id_uplink = ( + f"{SENSOR_DOMAIN}.{device_name}_plc_uplink_phy_rate_" + f"{PLCNET.devices[1].user_device_name}" + ) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(entity_id_downlink) == snapshot + assert entity_registry.async_get(entity_id_downlink) == snapshot + assert hass.states.get(entity_id_uplink) == snapshot + assert entity_registry.async_get(entity_id_uplink) == snapshot + + # Emulate the PLC link to the peer getting lost + mock_device.plcnet.async_get_network_overview = AsyncMock( + return_value=PLCNET_MISSING_DATA_RATE + ) + freezer.tick(LONG_UPDATE_INTERVAL) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + state = hass.states.get(entity_id_downlink) + assert state is not None + assert state.state == STATE_UNKNOWN + + state = hass.states.get(entity_id_uplink) + assert state is not None + assert state.state == STATE_UNKNOWN + + async def test_update_last_update_auth_failed( hass: HomeAssistant, mock_device: MockDevice ) -> None: