Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The La Marzocco integration."""
2 
3 import logging
4 
5 from packaging import version
6 from pylamarzocco.client_bluetooth import LaMarzoccoBluetoothClient
7 from pylamarzocco.client_cloud import LaMarzoccoCloudClient
8 from pylamarzocco.client_local import LaMarzoccoLocalClient
9 from pylamarzocco.const import BT_MODEL_PREFIXES, FirmwareType
10 from pylamarzocco.exceptions import AuthFail, RequestNotSuccessful
11 
12 from homeassistant.components.bluetooth import async_discovered_service_info
13 from homeassistant.config_entries import ConfigEntry
14 from homeassistant.const import (
15  CONF_HOST,
16  CONF_MAC,
17  CONF_MODEL,
18  CONF_NAME,
19  CONF_PASSWORD,
20  CONF_TOKEN,
21  CONF_USERNAME,
22  Platform,
23 )
24 from homeassistant.core import HomeAssistant
25 from homeassistant.helpers import issue_registry as ir
26 from homeassistant.helpers.httpx_client import create_async_httpx_client
27 
28 from .const import CONF_USE_BLUETOOTH, DOMAIN
29 from .coordinator import LaMarzoccoConfigEntry, LaMarzoccoUpdateCoordinator
30 
31 PLATFORMS = [
32  Platform.BINARY_SENSOR,
33  Platform.BUTTON,
34  Platform.CALENDAR,
35  Platform.NUMBER,
36  Platform.SELECT,
37  Platform.SENSOR,
38  Platform.SWITCH,
39  Platform.UPDATE,
40 ]
41 
42 _LOGGER = logging.getLogger(__name__)
43 
44 
45 async def async_setup_entry(hass: HomeAssistant, entry: LaMarzoccoConfigEntry) -> bool:
46  """Set up La Marzocco as config entry."""
47 
48  assert entry.unique_id
49  serial = entry.unique_id
50  client = create_async_httpx_client(hass)
51  cloud_client = LaMarzoccoCloudClient(
52  username=entry.data[CONF_USERNAME],
53  password=entry.data[CONF_PASSWORD],
54  client=client,
55  )
56 
57  # initialize local API
58  local_client: LaMarzoccoLocalClient | None = None
59  if (host := entry.data.get(CONF_HOST)) is not None:
60  _LOGGER.debug("Initializing local API")
61  local_client = LaMarzoccoLocalClient(
62  host=host,
63  local_bearer=entry.data[CONF_TOKEN],
64  client=client,
65  )
66 
67  # initialize Bluetooth
68  bluetooth_client: LaMarzoccoBluetoothClient | None = None
69  if entry.options.get(CONF_USE_BLUETOOTH, True):
70 
71  def bluetooth_configured() -> bool:
72  return entry.data.get(CONF_MAC, "") and entry.data.get(CONF_NAME, "")
73 
74  if not bluetooth_configured():
75  for discovery_info in async_discovered_service_info(hass):
76  if (
77  (name := discovery_info.name)
78  and name.startswith(BT_MODEL_PREFIXES)
79  and name.split("_")[1] == serial
80  ):
81  _LOGGER.debug("Found Bluetooth device, configuring with Bluetooth")
82  # found a device, add MAC address to config entry
83  hass.config_entries.async_update_entry(
84  entry,
85  data={
86  **entry.data,
87  CONF_MAC: discovery_info.address,
88  CONF_NAME: discovery_info.name,
89  },
90  )
91  break
92 
93  if bluetooth_configured():
94  _LOGGER.debug("Initializing Bluetooth device")
95  bluetooth_client = LaMarzoccoBluetoothClient(
96  username=entry.data[CONF_USERNAME],
97  serial_number=serial,
98  token=entry.data[CONF_TOKEN],
99  address_or_ble_device=entry.data[CONF_MAC],
100  )
101 
102  coordinator = LaMarzoccoUpdateCoordinator(
103  hass=hass,
104  entry=entry,
105  local_client=local_client,
106  cloud_client=cloud_client,
107  bluetooth_client=bluetooth_client,
108  )
109 
110  await coordinator.async_config_entry_first_refresh()
111  entry.runtime_data = coordinator
112 
113  gateway_version = coordinator.device.firmware[FirmwareType.GATEWAY].current_version
114  if version.parse(gateway_version) < version.parse("v3.4-rc5"):
115  # incompatible gateway firmware, create an issue
116  ir.async_create_issue(
117  hass,
118  DOMAIN,
119  "unsupported_gateway_firmware",
120  is_fixable=False,
121  severity=ir.IssueSeverity.ERROR,
122  translation_key="unsupported_gateway_firmware",
123  translation_placeholders={"gateway_version": gateway_version},
124  )
125 
126  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
127 
128  async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
129  await hass.config_entries.async_reload(entry.entry_id)
130 
131  entry.async_on_unload(entry.add_update_listener(update_listener))
132 
133  return True
134 
135 
136 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
137  """Unload a config entry."""
138  return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
139 
140 
141 async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
142  """Migrate config entry."""
143  if entry.version > 2:
144  # guard against downgrade from a future version
145  return False
146 
147  if entry.version == 1:
148  cloud_client = LaMarzoccoCloudClient(
149  username=entry.data[CONF_USERNAME],
150  password=entry.data[CONF_PASSWORD],
151  )
152  try:
153  fleet = await cloud_client.get_customer_fleet()
154  except (AuthFail, RequestNotSuccessful) as exc:
155  _LOGGER.error("Migration failed with error %s", exc)
156  return False
157 
158  assert entry.unique_id is not None
159  device = fleet[entry.unique_id]
160  v2_data = {
161  CONF_USERNAME: entry.data[CONF_USERNAME],
162  CONF_PASSWORD: entry.data[CONF_PASSWORD],
163  CONF_MODEL: device.model,
164  CONF_NAME: device.name,
165  CONF_TOKEN: device.communication_key,
166  }
167 
168  if CONF_HOST in entry.data:
169  v2_data[CONF_HOST] = entry.data[CONF_HOST]
170 
171  if CONF_MAC in entry.data:
172  v2_data[CONF_MAC] = entry.data[CONF_MAC]
173 
174  hass.config_entries.async_update_entry(
175  entry,
176  data=v2_data,
177  version=2,
178  )
179  _LOGGER.debug("Migrated La Marzocco config entry to version 2")
180  return True
Iterable[BluetoothServiceInfoBleak] async_discovered_service_info(HomeAssistant hass, bool connectable=True)
Definition: api.py:72
bool async_migrate_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:141
bool async_setup_entry(HomeAssistant hass, LaMarzoccoConfigEntry entry)
Definition: __init__.py:45
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:136
None update_listener(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:30
httpx.AsyncClient create_async_httpx_client(HomeAssistant hass, bool verify_ssl=True, bool auto_cleanup=True, SSLCipherList ssl_cipher_list=SSLCipherList.PYTHON_DEFAULT, **Any kwargs)
Definition: httpx_client.py:72