"""The La Marzocco integration.""" import logging from lmcloud.client_bluetooth import LaMarzoccoBluetoothClient from lmcloud.client_cloud import LaMarzoccoCloudClient from lmcloud.client_local import LaMarzoccoLocalClient from lmcloud.const import BT_MODEL_PREFIXES, FirmwareType from lmcloud.exceptions import AuthFail, RequestNotSuccessful from packaging import version from homeassistant.components.bluetooth import async_discovered_service_info from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_HOST, CONF_MAC, CONF_MODEL, CONF_NAME, CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME, Platform, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import issue_registry as ir from homeassistant.helpers.httpx_client import get_async_client from .const import CONF_USE_BLUETOOTH, DOMAIN from .coordinator import LaMarzoccoUpdateCoordinator PLATFORMS = [ Platform.BINARY_SENSOR, Platform.BUTTON, Platform.CALENDAR, Platform.NUMBER, Platform.SELECT, Platform.SENSOR, Platform.SWITCH, Platform.UPDATE, ] _LOGGER = logging.getLogger(__name__) type LaMarzoccoConfigEntry = ConfigEntry[LaMarzoccoUpdateCoordinator] async def async_setup_entry(hass: HomeAssistant, entry: LaMarzoccoConfigEntry) -> bool: """Set up La Marzocco as config entry.""" assert entry.unique_id serial = entry.unique_id cloud_client = LaMarzoccoCloudClient( username=entry.data[CONF_USERNAME], password=entry.data[CONF_PASSWORD], client=get_async_client(hass), ) # initialize local API local_client: LaMarzoccoLocalClient | None = None if (host := entry.data.get(CONF_HOST)) is not None: _LOGGER.debug("Initializing local API") local_client = LaMarzoccoLocalClient( host=host, local_bearer=entry.data[CONF_TOKEN], client=get_async_client(hass), ) # initialize Bluetooth bluetooth_client: LaMarzoccoBluetoothClient | None = None if entry.options.get(CONF_USE_BLUETOOTH, True): def bluetooth_configured() -> bool: return entry.data.get(CONF_MAC, "") and entry.data.get(CONF_NAME, "") if not bluetooth_configured(): for discovery_info in async_discovered_service_info(hass): if ( (name := discovery_info.name) and name.startswith(BT_MODEL_PREFIXES) and name.split("_")[1] == serial ): _LOGGER.debug("Found Bluetooth device, configuring with Bluetooth") # found a device, add MAC address to config entry hass.config_entries.async_update_entry( entry, data={ **entry.data, CONF_MAC: discovery_info.address, CONF_NAME: discovery_info.name, }, ) break if bluetooth_configured(): _LOGGER.debug("Initializing Bluetooth device") bluetooth_client = LaMarzoccoBluetoothClient( username=entry.data[CONF_USERNAME], serial_number=serial, token=entry.data[CONF_TOKEN], address_or_ble_device=entry.data[CONF_MAC], ) coordinator = LaMarzoccoUpdateCoordinator( hass=hass, local_client=local_client, cloud_client=cloud_client, bluetooth_client=bluetooth_client, ) await coordinator.async_setup() await coordinator.async_config_entry_first_refresh() entry.runtime_data = coordinator gateway_version = coordinator.device.firmware[FirmwareType.GATEWAY].current_version if version.parse(gateway_version) < version.parse("v3.4-rc5"): # incompatible gateway firmware, create an issue ir.async_create_issue( hass, DOMAIN, "unsupported_gateway_firmware", is_fixable=False, severity=ir.IssueSeverity.ERROR, translation_key="unsupported_gateway_firmware", translation_placeholders={"gateway_version": gateway_version}, ) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: await hass.config_entries.async_reload(entry.entry_id) entry.async_on_unload(entry.add_update_listener(update_listener)) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Migrate config entry.""" if entry.version > 2: # guard against downgrade from a future version return False if entry.version == 1: cloud_client = LaMarzoccoCloudClient( username=entry.data[CONF_USERNAME], password=entry.data[CONF_PASSWORD], ) try: fleet = await cloud_client.get_customer_fleet() except (AuthFail, RequestNotSuccessful) as exc: _LOGGER.error("Migration failed with error %s", exc) return False assert entry.unique_id is not None device = fleet[entry.unique_id] v2_data = { CONF_USERNAME: entry.data[CONF_USERNAME], CONF_PASSWORD: entry.data[CONF_PASSWORD], CONF_MODEL: device.model, CONF_NAME: device.name, CONF_TOKEN: device.communication_key, } if CONF_HOST in entry.data: v2_data[CONF_HOST] = entry.data[CONF_HOST] if CONF_MAC in entry.data: v2_data[CONF_MAC] = entry.data[CONF_MAC] hass.config_entries.async_update_entry( entry, data=v2_data, version=2, ) _LOGGER.debug("Migrated La Marzocco config entry to version 2") return True