diff --git a/homeassistant/components/nut/__init__.py b/homeassistant/components/nut/__init__.py index 04d85a10aeea..756ad87d4af4 100644 --- a/homeassistant/components/nut/__init__.py +++ b/homeassistant/components/nut/__init__.py @@ -336,7 +336,11 @@ class PyNUTData: self._alias = await self._async_get_alias() if TYPE_CHECKING: assert self._alias is not None - return await self._client.list_vars(self._alias) + status = await self._client.list_vars(self._alias) + if not status: + return status + # a driver formatting a missing C string reports the literal (null) + return {key: value for key, value in status.items() if value != "(null)"} async def async_update(self) -> dict[str, str]: """Fetch the latest status from NUT.""" diff --git a/tests/components/nut/test_sensor.py b/tests/components/nut/test_sensor.py index 9dc7d6d123d6..202072289263 100644 --- a/tests/components/nut/test_sensor.py +++ b/tests/components/nut/test_sensor.py @@ -1,5 +1,6 @@ """The sensor tests for the nut platform.""" +from datetime import timedelta from unittest.mock import patch import pytest @@ -21,6 +22,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er, translation +from homeassistant.util import dt as dt_util from .util import ( _get_mock_nutclient, @@ -28,7 +30,7 @@ from .util import ( async_init_integration, ) -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_fire_time_changed @pytest.mark.parametrize( @@ -211,6 +213,48 @@ async def test_unknown_state_sensors(hass: HomeAssistant) -> None: assert state2.state == "OQ" +async def test_null_variable_values( + hass: HomeAssistant, entity_registry: er.EntityRegistry +) -> None: + """Test variables a NUT driver reports as a literal (null) count as absent.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_HOST: "mock", CONF_PORT: "mock"}, + ) + entry.add_to_hass(hass) + + mock_pynut = _get_mock_nutclient( + list_ups={"ups1": "UPS 1"}, + list_vars={"battery.charger.status": "(null)", "battery.charge": "10"}, + ) + + with patch( + "homeassistant.components.nut.AIONUTClient", + return_value=mock_pynut, + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + # no sensor for a variable the driver has no value for + assert ( + entity_registry.async_get_entity_id( + Platform.SENSOR, DOMAIN, f"{entry.entry_id}_battery.charger.status" + ) + is None + ) + assert hass.states.get("sensor.ups1_battery_charge").state == "10" + + # a value that goes missing later leaves its sensor unknown + mock_pynut.list_vars.return_value = { + "battery.charger.status": "charging", + "battery.charge": "(null)", + } + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=61)) + await hass.async_block_till_done() + + assert hass.states.get("sensor.ups1_battery_charge").state == STATE_UNKNOWN + + async def test_stale_options( hass: HomeAssistant, entity_registry: er.EntityRegistry ) -> None: