Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for Plaato devices."""
2 
3 from datetime import timedelta
4 import logging
5 
6 from aiohttp import web
7 from pyplaato.models.airlock import PlaatoAirlock
8 from pyplaato.plaato import (
9  ATTR_ABV,
10  ATTR_BATCH_VOLUME,
11  ATTR_BPM,
12  ATTR_BUBBLES,
13  ATTR_CO2_VOLUME,
14  ATTR_DEVICE_ID,
15  ATTR_DEVICE_NAME,
16  ATTR_OG,
17  ATTR_SG,
18  ATTR_TEMP,
19  ATTR_TEMP_UNIT,
20  ATTR_VOLUME_UNIT,
21 )
22 import voluptuous as vol
23 
24 from homeassistant.components import webhook
25 from homeassistant.components.sensor import DOMAIN as SENSOR
26 from homeassistant.config_entries import ConfigEntry
27 from homeassistant.const import (
28  CONF_SCAN_INTERVAL,
29  CONF_TOKEN,
30  CONF_WEBHOOK_ID,
31  UnitOfTemperature,
32  UnitOfVolume,
33 )
34 from homeassistant.core import HomeAssistant, callback
36 from homeassistant.helpers.dispatcher import async_dispatcher_send
37 
38 from .const import (
39  CONF_DEVICE_NAME,
40  CONF_DEVICE_TYPE,
41  CONF_USE_WEBHOOK,
42  COORDINATOR,
43  DEFAULT_SCAN_INTERVAL,
44  DEVICE,
45  DEVICE_ID,
46  DEVICE_NAME,
47  DEVICE_TYPE,
48  DOMAIN,
49  PLATFORMS,
50  SENSOR_DATA,
51  UNDO_UPDATE_LISTENER,
52 )
53 from .coordinator import PlaatoCoordinator
54 
55 _LOGGER = logging.getLogger(__name__)
56 
57 DEPENDENCIES = ["webhook"]
58 
59 SENSOR_UPDATE = f"{DOMAIN}_sensor_update"
60 SENSOR_DATA_KEY = f"{DOMAIN}.{SENSOR}"
61 
62 WEBHOOK_SCHEMA = vol.Schema(
63  {
64  vol.Required(ATTR_DEVICE_NAME): cv.string,
65  vol.Required(ATTR_DEVICE_ID): cv.positive_int,
66  vol.Required(ATTR_TEMP_UNIT): vol.In(
67  [UnitOfTemperature.CELSIUS, UnitOfTemperature.FAHRENHEIT]
68  ),
69  vol.Required(ATTR_VOLUME_UNIT): vol.In(
70  [UnitOfVolume.LITERS, UnitOfVolume.GALLONS]
71  ),
72  vol.Required(ATTR_BPM): cv.positive_int,
73  vol.Required(ATTR_TEMP): vol.Coerce(float),
74  vol.Required(ATTR_SG): vol.Coerce(float),
75  vol.Required(ATTR_OG): vol.Coerce(float),
76  vol.Required(ATTR_ABV): vol.Coerce(float),
77  vol.Required(ATTR_CO2_VOLUME): vol.Coerce(float),
78  vol.Required(ATTR_BATCH_VOLUME): vol.Coerce(float),
79  vol.Required(ATTR_BUBBLES): cv.positive_int,
80  },
81  extra=vol.ALLOW_EXTRA,
82 )
83 
84 
85 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
86  """Configure based on config entry."""
87  hass.data.setdefault(DOMAIN, {})
88 
89  if entry.data[CONF_USE_WEBHOOK]:
90  async_setup_webhook(hass, entry)
91  else:
92  await async_setup_coordinator(hass, entry)
93 
94  await hass.config_entries.async_forward_entry_setups(
95  entry, [platform for platform in PLATFORMS if entry.options.get(platform, True)]
96  )
97 
98  return True
99 
100 
101 @callback
102 def async_setup_webhook(hass: HomeAssistant, entry: ConfigEntry):
103  """Init webhook based on config entry."""
104  webhook_id = entry.data[CONF_WEBHOOK_ID]
105  device_name = entry.data[CONF_DEVICE_NAME]
106 
107  _set_entry_data(entry, hass)
108 
109  webhook.async_register(
110  hass, DOMAIN, f"{DOMAIN}.{device_name}", webhook_id, handle_webhook
111  )
112 
113 
114 async def async_setup_coordinator(hass: HomeAssistant, entry: ConfigEntry):
115  """Init auth token based on config entry."""
116  auth_token = entry.data[CONF_TOKEN]
117  device_type = entry.data[CONF_DEVICE_TYPE]
118 
119  if entry.options.get(CONF_SCAN_INTERVAL):
120  update_interval = timedelta(minutes=entry.options[CONF_SCAN_INTERVAL])
121  else:
122  update_interval = timedelta(minutes=DEFAULT_SCAN_INTERVAL)
123 
124  coordinator = PlaatoCoordinator(hass, auth_token, device_type, update_interval)
125  await coordinator.async_config_entry_first_refresh()
126 
127  _set_entry_data(entry, hass, coordinator, auth_token)
128 
129  for platform in PLATFORMS:
130  if entry.options.get(platform, True):
131  coordinator.platforms.append(platform)
132 
133 
134 def _set_entry_data(entry, hass, coordinator=None, device_id=None):
135  device = {
136  DEVICE_NAME: entry.data[CONF_DEVICE_NAME],
137  DEVICE_TYPE: entry.data[CONF_DEVICE_TYPE],
138  DEVICE_ID: device_id,
139  }
140 
141  hass.data[DOMAIN][entry.entry_id] = {
142  COORDINATOR: coordinator,
143  DEVICE: device,
144  SENSOR_DATA: None,
145  UNDO_UPDATE_LISTENER: entry.add_update_listener(_async_update_listener),
146  }
147 
148 
149 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
150  """Unload a config entry."""
151  use_webhook = entry.data[CONF_USE_WEBHOOK]
152  hass.data[DOMAIN][entry.entry_id][UNDO_UPDATE_LISTENER]()
153 
154  if use_webhook:
155  return await async_unload_webhook(hass, entry)
156 
157  return await async_unload_coordinator(hass, entry)
158 
159 
160 async def async_unload_webhook(hass: HomeAssistant, entry: ConfigEntry):
161  """Unload webhook based entry."""
162  if entry.data[CONF_WEBHOOK_ID] is not None:
163  webhook.async_unregister(hass, entry.data[CONF_WEBHOOK_ID])
164  return await async_unload_platforms(hass, entry, PLATFORMS)
165 
166 
167 async def async_unload_coordinator(hass: HomeAssistant, entry: ConfigEntry):
168  """Unload auth token based entry."""
169  coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR]
170  return await async_unload_platforms(hass, entry, coordinator.platforms)
171 
172 
173 async def async_unload_platforms(hass: HomeAssistant, entry: ConfigEntry, platforms):
174  """Unload platforms."""
175  unloaded = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
176  if unloaded:
177  hass.data[DOMAIN].pop(entry.entry_id)
178 
179  return unloaded
180 
181 
182 async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
183  """Handle options update."""
184  await hass.config_entries.async_reload(entry.entry_id)
185 
186 
187 async def handle_webhook(
188  hass: HomeAssistant, webhook_id: str, request: web.Request
189 ) -> web.Response | None:
190  """Handle incoming webhook from Plaato."""
191  try:
192  data = WEBHOOK_SCHEMA(await request.json())
193  except vol.MultipleInvalid as error:
194  _LOGGER.warning("An error occurred when parsing webhook data <%s>", error)
195  return None
196 
197  device_id = _device_id(data)
198  sensor_data = PlaatoAirlock.from_web_hook(data)
199 
200  async_dispatcher_send(hass, SENSOR_UPDATE, *(device_id, sensor_data))
201 
202  return web.Response(text=f"Saving status for {device_id}")
203 
204 
205 def _device_id(data):
206  """Return name of device sensor."""
207  return f"{data.get(ATTR_DEVICE_NAME)}_{data.get(ATTR_DEVICE_ID)}"
def async_unload_webhook(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:160
def async_unload_platforms(HomeAssistant hass, ConfigEntry entry, platforms)
Definition: __init__.py:173
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:85
def async_unload_coordinator(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:167
web.Response|None handle_webhook(HomeAssistant hass, str webhook_id, web.Request request)
Definition: __init__.py:189
def async_setup_coordinator(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:114
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:149
None _async_update_listener(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:182
def async_setup_webhook(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:102
def _set_entry_data(entry, hass, coordinator=None, device_id=None)
Definition: __init__.py:134
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)
Definition: dispatcher.py:193