Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The Soundavo WS66i 6-Zone Amplifier integration."""
2 
3 from __future__ import annotations
4 
5 import logging
6 
7 from pyws66i import WS66i, get_ws66i
8 
9 from homeassistant.config_entries import ConfigEntry
10 from homeassistant.const import CONF_IP_ADDRESS, EVENT_HOMEASSISTANT_STOP, Platform
11 from homeassistant.core import HomeAssistant, callback
12 from homeassistant.exceptions import ConfigEntryNotReady
13 
14 from .const import CONF_SOURCES, DOMAIN
15 from .coordinator import Ws66iDataUpdateCoordinator
16 from .models import SourceRep, Ws66iData
17 
18 _LOGGER = logging.getLogger(__name__)
19 
20 PLATFORMS = [Platform.MEDIA_PLAYER]
21 
22 
23 @callback
24 def _get_sources_from_dict(data) -> SourceRep:
25  sources_config = data[CONF_SOURCES]
26 
27  # Dict index to custom name
28  source_id_name = {int(index): name for index, name in sources_config.items()}
29 
30  # Dict custom name to index
31  source_name_id = {v: k for k, v in source_id_name.items()}
32 
33  # List of custom names
34  source_names = sorted(source_name_id.keys(), key=lambda v: source_name_id[v])
35 
36  return SourceRep(source_id_name, source_name_id, source_names)
37 
38 
39 def _find_zones(hass: HomeAssistant, ws66i: WS66i) -> list[int]:
40  """Generate zones list by searching for presence of zones."""
41  # Zones 11 - 16 are the master amp
42  # Zones 21,31 - 26,36 are the daisy-chained amps
43  zone_list = []
44  for amp_num in range(1, 4):
45  if amp_num > 1:
46  # Don't add entities that aren't present
47  status = ws66i.zone_status(amp_num * 10 + 1)
48  if status is None:
49  break
50 
51  for zone_num in range(1, 7):
52  zone_id = (amp_num * 10) + zone_num
53  zone_list.append(zone_id)
54 
55  _LOGGER.debug("Detected %d amp(s)", amp_num - 1)
56  return zone_list
57 
58 
59 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
60  """Set up Soundavo WS66i 6-Zone Amplifier from a config entry."""
61  # Get the source names from the options flow
62  options: dict[str, dict[str, str]]
63  options = {CONF_SOURCES: entry.options[CONF_SOURCES]}
64  # Get the WS66i object and open up a connection to it
65  ws66i = get_ws66i(entry.data[CONF_IP_ADDRESS])
66  try:
67  await hass.async_add_executor_job(ws66i.open)
68  except ConnectionError as err:
69  # Amplifier is probably turned off
70  raise ConfigEntryNotReady("Could not connect to WS66i Amp. Is it off?") from err
71 
72  # Create the zone Representation dataclass
73  source_rep: SourceRep = _get_sources_from_dict(options)
74 
75  # Create a list of discovered zones
76  zones = await hass.async_add_executor_job(_find_zones, hass, ws66i)
77 
78  # Create the coordinator for the WS66i
79  coordinator: Ws66iDataUpdateCoordinator = Ws66iDataUpdateCoordinator(
80  hass,
81  ws66i,
82  zones,
83  )
84 
85  # Fetch initial data, retry on failed poll
86  await coordinator.async_config_entry_first_refresh()
87 
88  # Create the Ws66iData data class save it to hass
89  hass.data.setdefault(DOMAIN, {})[entry.entry_id] = Ws66iData(
90  host_ip=entry.data[CONF_IP_ADDRESS],
91  device=ws66i,
92  sources=source_rep,
93  coordinator=coordinator,
94  zones=zones,
95  )
96 
97  @callback
98  def shutdown(event):
99  """Close the WS66i connection to the amplifier."""
100  ws66i.close()
101 
102  entry.async_on_unload(entry.add_update_listener(_update_listener))
103  entry.async_on_unload(
104  hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, shutdown)
105  )
106 
107  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
108 
109  return True
110 
111 
112 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
113  """Unload a config entry."""
114  unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
115  if unload_ok:
116  ws66i: WS66i = hass.data[DOMAIN][entry.entry_id].device
117  ws66i.close()
118  hass.data[DOMAIN].pop(entry.entry_id)
119 
120  return unload_ok
121 
122 
123 async def _update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
124  """Handle options update."""
125  await hass.config_entries.async_reload(entry.entry_id)
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:112
None _update_listener(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:123
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:59
list[int] _find_zones(HomeAssistant hass, WS66i ws66i)
Definition: __init__.py:39
SourceRep _get_sources_from_dict(data)
Definition: __init__.py:24