Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The AirVisual Pro integration."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from contextlib import suppress
7 from dataclasses import dataclass
8 from datetime import timedelta
9 from typing import Any
10 
11 from pyairvisual.node import (
12  InvalidAuthenticationError,
13  NodeConnectionError,
14  NodeProError,
15  NodeSamba,
16 )
17 
18 from homeassistant.config_entries import ConfigEntry
19 from homeassistant.const import (
20  CONF_IP_ADDRESS,
21  CONF_PASSWORD,
22  EVENT_HOMEASSISTANT_STOP,
23  Platform,
24 )
25 from homeassistant.core import Event, HomeAssistant
26 from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
27 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
28 
29 from .const import LOGGER
30 
31 PLATFORMS = [Platform.SENSOR]
32 
33 UPDATE_INTERVAL = timedelta(minutes=1)
34 
35 type AirVisualProConfigEntry = ConfigEntry[AirVisualProData]
36 
37 
38 @dataclass
40  """Define a data class."""
41 
42  coordinator: DataUpdateCoordinator
43  node: NodeSamba
44 
45 
47  hass: HomeAssistant, entry: AirVisualProConfigEntry
48 ) -> bool:
49  """Set up AirVisual Pro from a config entry."""
50  node = NodeSamba(entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD])
51 
52  try:
53  await node.async_connect()
54  except NodeProError as err:
55  raise ConfigEntryNotReady from err
56 
57  reload_task: asyncio.Task | None = None
58 
59  async def async_get_data() -> dict[str, Any]:
60  """Get data from the device."""
61  try:
62  data = await node.async_get_latest_measurements()
63  data["history"] = {}
64  if data["settings"].get("follow_mode") == "device":
65  history = await node.async_get_history(include_trends=False)
66  data["history"] = history.get("measurements", [])[-1]
67  except InvalidAuthenticationError as err:
68  raise ConfigEntryAuthFailed("Invalid Samba password") from err
69  except NodeConnectionError as err:
70  nonlocal reload_task
71  if not reload_task:
72  reload_task = hass.async_create_task(
73  hass.config_entries.async_reload(entry.entry_id)
74  )
75  raise UpdateFailed(f"Connection to Pro unit lost: {err}") from err
76  except NodeProError as err:
77  raise UpdateFailed(f"Error while retrieving data: {err}") from err
78 
79  return data
80 
81  coordinator = DataUpdateCoordinator(
82  hass,
83  LOGGER,
84  config_entry=entry,
85  name="Node/Pro data",
86  update_interval=UPDATE_INTERVAL,
87  update_method=async_get_data,
88  )
89 
90  await coordinator.async_config_entry_first_refresh()
91  entry.runtime_data = AirVisualProData(coordinator=coordinator, node=node)
92 
93  async def async_shutdown(_: Event) -> None:
94  """Define an event handler to disconnect from the websocket."""
95  nonlocal reload_task
96  if reload_task:
97  with suppress(asyncio.CancelledError):
98  reload_task.cancel()
99  await node.async_disconnect()
100 
101  entry.async_on_unload(
102  hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_shutdown)
103  )
104 
105  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
106 
107  return True
108 
109 
111  hass: HomeAssistant, entry: AirVisualProConfigEntry
112 ) -> bool:
113  """Unload a config entry."""
114  if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
115  await entry.runtime_data.node.async_disconnect()
116 
117  return unload_ok
bool async_unload_entry(HomeAssistant hass, AirVisualProConfigEntry entry)
Definition: __init__.py:112
bool async_setup_entry(HomeAssistant hass, AirVisualProConfigEntry entry)
Definition: __init__.py:48
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
RadioThermUpdate async_get_data(HomeAssistant hass, CommonThermostat device)
Definition: data.py:73