Nut: Use coordinator data, code cleanup and add test coverage (#57643)
parent
8c326198cf
commit
827501659c
|
@ -731,7 +731,6 @@ omit =
|
|||
homeassistant/components/nuki/const.py
|
||||
homeassistant/components/nuki/binary_sensor.py
|
||||
homeassistant/components/nuki/lock.py
|
||||
homeassistant/components/nut/sensor.py
|
||||
homeassistant/components/nx584/alarm_control_panel.py
|
||||
homeassistant/components/nzbget/coordinator.py
|
||||
homeassistant/components/obihai/*
|
||||
|
|
|
@ -361,7 +361,7 @@ homeassistant/components/nsw_rural_fire_service_feed/* @exxamalte
|
|||
homeassistant/components/nuki/* @pschmitt @pvizeli @pree
|
||||
homeassistant/components/numato/* @clssn
|
||||
homeassistant/components/number/* @home-assistant/core @Shulyaka
|
||||
homeassistant/components/nut/* @bdraco
|
||||
homeassistant/components/nut/* @bdraco @ollo69
|
||||
homeassistant/components/nws/* @MatthewFlamm
|
||||
homeassistant/components/nzbget/* @chriscla
|
||||
homeassistant/components/obihai/* @dshokouhi
|
||||
|
|
|
@ -16,7 +16,6 @@ from homeassistant.const import (
|
|||
CONF_USERNAME,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import (
|
||||
|
@ -28,7 +27,6 @@ from .const import (
|
|||
PYNUT_FIRMWARE,
|
||||
PYNUT_MANUFACTURER,
|
||||
PYNUT_MODEL,
|
||||
PYNUT_NAME,
|
||||
PYNUT_UNIQUE_ID,
|
||||
UNDO_UPDATE_LISTENER,
|
||||
)
|
||||
|
@ -61,6 +59,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
await hass.async_add_executor_job(data.update)
|
||||
if not data.status:
|
||||
raise UpdateFailed("Error fetching UPS state")
|
||||
return data.status
|
||||
|
||||
coordinator = DataUpdateCoordinator(
|
||||
hass,
|
||||
|
@ -72,11 +71,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
|
||||
# Fetch initial data so we have data when entities subscribe
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
status = data.status
|
||||
|
||||
if not status:
|
||||
_LOGGER.error("NUT Sensor has no data, unable to set up")
|
||||
raise ConfigEntryNotReady
|
||||
status = coordinator.data
|
||||
|
||||
_LOGGER.debug("NUT Sensors Available: %s", status)
|
||||
|
||||
|
@ -95,7 +90,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
PYNUT_MANUFACTURER: _manufacturer_from_status(status),
|
||||
PYNUT_MODEL: _model_from_status(status),
|
||||
PYNUT_FIRMWARE: _firmware_from_status(status),
|
||||
PYNUT_NAME: data.name,
|
||||
UNDO_UPDATE_LISTENER: undo_listener,
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ PYNUT_UNIQUE_ID = "unique_id"
|
|||
PYNUT_MANUFACTURER = "manufacturer"
|
||||
PYNUT_MODEL = "model"
|
||||
PYNUT_FIRMWARE = "firmware"
|
||||
PYNUT_NAME = "name"
|
||||
|
||||
SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
"ups.status.display": SensorEntityDescription(
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Network UPS Tools (NUT)",
|
||||
"documentation": "https://www.home-assistant.io/integrations/nut",
|
||||
"requirements": ["pynut2==2.1.2"],
|
||||
"codeowners": ["@bdraco"],
|
||||
"codeowners": ["@bdraco", "@ollo69"],
|
||||
"config_flow": true,
|
||||
"zeroconf": ["_nut._tcp.local."],
|
||||
"iot_class": "local_polling"
|
||||
|
|
|
@ -20,7 +20,6 @@ from .const import (
|
|||
PYNUT_FIRMWARE,
|
||||
PYNUT_MANUFACTURER,
|
||||
PYNUT_MODEL,
|
||||
PYNUT_NAME,
|
||||
PYNUT_UNIQUE_ID,
|
||||
SENSOR_TYPES,
|
||||
STATE_TYPES,
|
||||
|
@ -37,10 +36,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
manufacturer = pynut_data[PYNUT_MANUFACTURER]
|
||||
model = pynut_data[PYNUT_MODEL]
|
||||
firmware = pynut_data[PYNUT_FIRMWARE]
|
||||
name = pynut_data[PYNUT_NAME]
|
||||
coordinator = pynut_data[COORDINATOR]
|
||||
data = pynut_data[PYNUT_DATA]
|
||||
status = data.status
|
||||
status = coordinator.data
|
||||
|
||||
enabled_resources = [
|
||||
resource.lower() for resource in config_entry.data[CONF_RESOURCES]
|
||||
|
@ -55,7 +53,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
NUTSensor(
|
||||
coordinator,
|
||||
data,
|
||||
name.title(),
|
||||
SENSOR_TYPES[sensor_type],
|
||||
unique_id,
|
||||
manufacturer,
|
||||
|
@ -76,7 +73,6 @@ class NUTSensor(CoordinatorEntity, SensorEntity):
|
|||
self,
|
||||
coordinator: DataUpdateCoordinator,
|
||||
data: PyNUTData,
|
||||
name: str,
|
||||
sensor_description: SensorEntityDescription,
|
||||
unique_id: str,
|
||||
manufacturer: str | None,
|
||||
|
@ -90,20 +86,16 @@ class NUTSensor(CoordinatorEntity, SensorEntity):
|
|||
self._manufacturer = manufacturer
|
||||
self._firmware = firmware
|
||||
self._model = model
|
||||
self._device_name = name
|
||||
self._data = data
|
||||
self._device_name = data.name.title()
|
||||
self._unique_id = unique_id
|
||||
self._attr_entity_registry_enabled_default = enabled_default
|
||||
|
||||
self._attr_name = f"{name} {sensor_description.name}"
|
||||
if unique_id is not None:
|
||||
self._attr_unique_id = f"{unique_id}_{sensor_description.key}"
|
||||
self._attr_entity_registry_enabled_default = enabled_default
|
||||
self._attr_name = f"{self._device_name} {sensor_description.name}"
|
||||
self._attr_unique_id = f"{unique_id}_{sensor_description.key}"
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Device info for the ups."""
|
||||
if not self._unique_id:
|
||||
return None
|
||||
device_info = {
|
||||
"identifiers": {(DOMAIN, self._unique_id)},
|
||||
"name": self._device_name,
|
||||
|
@ -119,17 +111,14 @@ class NUTSensor(CoordinatorEntity, SensorEntity):
|
|||
@property
|
||||
def native_value(self):
|
||||
"""Return entity state from ups."""
|
||||
if not self._data.status:
|
||||
return None
|
||||
status = self.coordinator.data
|
||||
if self.entity_description.key == KEY_STATUS_DISPLAY:
|
||||
return _format_display_state(self._data.status)
|
||||
return self._data.status.get(self.entity_description.key)
|
||||
return _format_display_state(status)
|
||||
return status.get(self.entity_description.key)
|
||||
|
||||
|
||||
def _format_display_state(status):
|
||||
"""Return UPS display state."""
|
||||
if status is None:
|
||||
return STATE_TYPES["OFF"]
|
||||
try:
|
||||
return " ".join(STATE_TYPES[state] for state in status[KEY_STATUS].split())
|
||||
except KeyError:
|
||||
|
|
|
@ -1,9 +1,20 @@
|
|||
"""The sensor tests for the nut platform."""
|
||||
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.nut.const import DOMAIN
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_PORT,
|
||||
CONF_RESOURCES,
|
||||
PERCENTAGE,
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .util import async_init_integration
|
||||
from .util import _get_mock_pynutclient, async_init_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_pr3000rt2u(hass):
|
||||
|
@ -204,6 +215,64 @@ async def test_blazer_usb(hass):
|
|||
)
|
||||
|
||||
|
||||
async def test_state_sensors(hass):
|
||||
"""Test creation of status display sensors."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "mock",
|
||||
CONF_PORT: "mock",
|
||||
CONF_RESOURCES: ["ups.status", "ups.status.display"],
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
mock_pynut = _get_mock_pynutclient(
|
||||
list_ups={"ups1": "UPS 1"}, list_vars={"ups.status": "OL"}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.nut.PyNUTClient",
|
||||
return_value=mock_pynut,
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state1 = hass.states.get("sensor.ups1_status")
|
||||
state2 = hass.states.get("sensor.ups1_status_data")
|
||||
assert state1.state == "Online"
|
||||
assert state2.state == "OL"
|
||||
|
||||
|
||||
async def test_unknown_state_sensors(hass):
|
||||
"""Test creation of unknown status display sensors."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "mock",
|
||||
CONF_PORT: "mock",
|
||||
CONF_RESOURCES: ["ups.status", "ups.status.display"],
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
mock_pynut = _get_mock_pynutclient(
|
||||
list_ups={"ups1": "UPS 1"}, list_vars={"ups.status": "OQ"}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.nut.PyNUTClient",
|
||||
return_value=mock_pynut,
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state1 = hass.states.get("sensor.ups1_status")
|
||||
state2 = hass.states.get("sensor.ups1_status_data")
|
||||
assert state1.state == STATE_UNKNOWN
|
||||
assert state2.state == "OQ"
|
||||
|
||||
|
||||
async def test_stale_options(hass):
|
||||
"""Test creation of sensors with stale options to remove."""
|
||||
|
||||
|
|
Loading…
Reference in New Issue