Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Support for APCUPSd via its Network Information Server (NIS)."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from datetime import timedelta
7 import logging
8 from typing import Final
9 
10 import aioapcaccess
11 
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.core import HomeAssistant
14 from homeassistant.helpers.debounce import Debouncer
15 from homeassistant.helpers.device_registry import DeviceInfo
17  REQUEST_REFRESH_DEFAULT_IMMEDIATE,
18  DataUpdateCoordinator,
19  UpdateFailed,
20 )
21 
22 from .const import CONNECTION_TIMEOUT, DOMAIN
23 
24 _LOGGER = logging.getLogger(__name__)
25 UPDATE_INTERVAL: Final = timedelta(seconds=60)
26 REQUEST_REFRESH_COOLDOWN: Final = 5
27 
28 
29 class APCUPSdData(dict[str, str]):
30  """Store data about an APCUPSd and provide a few helper methods for easier accesses."""
31 
32  @property
33  def name(self) -> str | None:
34  """Return the name of the UPS, if available."""
35  return self.get("UPSNAME")
36 
37  @property
38  def model(self) -> str | None:
39  """Return the model of the UPS, if available."""
40  # Different UPS models may report slightly different keys for model, here we
41  # try them all.
42  return self.get("APCMODEL") or self.get("MODEL")
43 
44  @property
45  def serial_no(self) -> str | None:
46  """Return the unique serial number of the UPS, if available."""
47  return self.get("SERIALNO")
48 
49 
51  """Store and coordinate the data retrieved from APCUPSd for all sensors.
52 
53  For each entity to use, acts as the single point responsible for fetching
54  updates from the server.
55  """
56 
57  config_entry: ConfigEntry
58 
59  def __init__(self, hass: HomeAssistant, host: str, port: int) -> None:
60  """Initialize the data object."""
61  super().__init__(
62  hass,
63  _LOGGER,
64  name=DOMAIN,
65  update_interval=UPDATE_INTERVAL,
66  request_refresh_debouncer=Debouncer(
67  hass,
68  _LOGGER,
69  cooldown=REQUEST_REFRESH_COOLDOWN,
70  immediate=REQUEST_REFRESH_DEFAULT_IMMEDIATE,
71  ),
72  )
73  self._host_host = host
74  self._port_port = port
75 
76  @property
77  def device_info(self) -> DeviceInfo:
78  """Return the DeviceInfo of this APC UPS, if serial number is available."""
79  return DeviceInfo(
80  identifiers={(DOMAIN, self.datadata.serial_no or self.config_entryconfig_entry.entry_id)},
81  model=self.datadata.model,
82  manufacturer="APC",
83  name=self.datadata.name or "APC UPS",
84  hw_version=self.datadata.get("FIRMWARE"),
85  sw_version=self.datadata.get("VERSION"),
86  )
87 
88  async def _async_update_data(self) -> APCUPSdData:
89  """Fetch the latest status from APCUPSd.
90 
91  Note that the result dict uses upper case for each resource, where our
92  integration uses lower cases as keys internally.
93  """
94  async with asyncio.timeout(CONNECTION_TIMEOUT):
95  try:
96  data = await aioapcaccess.request_status(self._host_host, self._port_port)
97  return APCUPSdData(data)
98  except (OSError, asyncio.IncompleteReadError) as error:
99  raise UpdateFailed(error) from error
None __init__(self, HomeAssistant hass, str host, int port)
Definition: coordinator.py:59
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88