Skip to content

Commit

Permalink
add core preview
Browse files Browse the repository at this point in the history
  • Loading branch information
zweckj committed Feb 19, 2024
1 parent f59ab34 commit 60bc4ab
Show file tree
Hide file tree
Showing 21 changed files with 1,623 additions and 1,056 deletions.
105 changes: 81 additions & 24 deletions custom_components/lamarzocco/__init__.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,125 @@
"""The La Marzocco integration."""

import logging

from lmcloud.client_bluetooth import LaMarzoccoBluetoothClient
from lmcloud.client_cloud import LaMarzoccoCloudClient
from lmcloud.exceptions import AuthFail, RequestNotSuccessful

from homeassistant.components import bluetooth
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
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 config_validation as cv

from .const import DOMAIN
from .coordinator import LmApiCoordinator
from .services import async_setup_services
from .const import CONF_USE_BLUETOOTH, DOMAIN
from .coordinator import LaMarzoccoMachineUpdateCoordinator

PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.CALENDAR,
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
Platform.UPDATE,
Platform.WATER_HEATER,
]

CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up La Marzocco as config entry."""

coordinator = LmApiCoordinator(hass, entry)
if entry.data.get(CONF_USE_BLUETOOTH, True):
assert entry.unique_id

# check if there are any bluetooth adapters to use
count = bluetooth.async_scanner_count(hass, connectable=True)
if count > 0:
_LOGGER.debug("Found Bluetooth adapters, initializing with Bluetooth")
bt_devices = await LaMarzoccoBluetoothClient.discover_devices(
scanner=bluetooth.async_get_scanner(hass)
)
for bt_device in bt_devices:
if bt_device.name is not None and entry.unique_id in bt_device.name:
# found a device, add MAC address to config entry
_LOGGER.debug("Found Bluetooth device %s", bt_device.name)
new_data = entry.data.copy()
new_data[CONF_MAC] = bt_device.address
hass.config_entries.async_update_entry(
entry,
data=new_data,
)

coordinator = LaMarzoccoMachineUpdateCoordinator(hass)

await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

# Set up global services.
await async_setup_services(hass, entry)
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(options_update_listener))
entry.async_on_unload(entry.add_update_listener(update_listener))

return True


async def options_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""

services = hass.services.async_services().get(DOMAIN)
if services is not None:
for service in list(services.keys()):
hass.services.async_remove(DOMAIN, service)

unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
hass.data[DOMAIN] = {}

return unload_ok


async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate old entry."""
if entry.version != 1:
"""Migrate config entry."""
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]

entry.version = 2
hass.config_entries.async_update_entry(
entry,
data=v2_data,
)
_LOGGER.debug("Migrated La Marzocco config entry to version 2")
return True
28 changes: 14 additions & 14 deletions custom_components/lamarzocco/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from collections.abc import Callable
from dataclasses import dataclass

from lmcloud.lm_machine import LaMarzoccoMachine

from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
Expand All @@ -15,35 +17,33 @@

from .const import DOMAIN
from .entity import LaMarzoccoEntity, LaMarzoccoEntityDescription
from .lm_client import LaMarzoccoClient


@dataclass(frozen=True, kw_only=True)
class LaMarzoccoBinarySensorEntityDescription(
LaMarzoccoEntityDescription,
BinarySensorEntityDescription,
):
"""Description of an La Marzocco Binary Sensor."""
is_on_fn: Callable[[LaMarzoccoClient], bool]
"""Description of a La Marzocco binary sensor."""

is_on_fn: Callable[[LaMarzoccoMachine], bool]


ENTITIES: tuple[LaMarzoccoBinarySensorEntityDescription, ...] = (
LaMarzoccoBinarySensorEntityDescription(
key="water_reservoir",
translation_key="water_reservoir",
key="water_tank",
translation_key="water_tank",
device_class=BinarySensorDeviceClass.PROBLEM,
icon="mdi:water-well",
is_on_fn=lambda client: not client.current_status.get(
"water_reservoir_contact"
),
is_on_fn=lambda device: not device.config.water_contact,
entity_category=EntityCategory.DIAGNOSTIC,
supported_fn=lambda coordinator: coordinator.local_connection_configured,
),
LaMarzoccoBinarySensorEntityDescription(
key="brew_active",
translation_key="brew_active",
device_class=BinarySensorDeviceClass.RUNNING,
icon="mdi:cup-water",
is_on_fn=lambda client: bool(client.current_status.get("brew_active")),
is_on_fn=lambda device: device.config.brew_active,
available_fn=lambda device: device.websocket_connected,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
Expand All @@ -58,9 +58,9 @@ async def async_setup_entry(
coordinator = hass.data[DOMAIN][config_entry.entry_id]

async_add_entities(
LaMarzoccoBinarySensorEntity(coordinator, hass, description)
LaMarzoccoBinarySensorEntity(coordinator, description)
for description in ENTITIES
if coordinator.data.model_name in description.supported_models
if description.supported_fn(coordinator)
)


Expand All @@ -72,4 +72,4 @@ class LaMarzoccoBinarySensorEntity(LaMarzoccoEntity, BinarySensorEntity):
@property
def is_on(self) -> bool:
"""Return true if the binary sensor is on."""
return self.entity_description.is_on_fn(self._lm_client)
return self.entity_description.is_on_fn(self.coordinator.device)
27 changes: 14 additions & 13 deletions custom_components/lamarzocco/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,32 @@
from dataclasses import dataclass
from typing import Any

from lmcloud.lm_machine import LaMarzoccoMachine

from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN
from .entity import LaMarzoccoEntity, LaMarzoccoEntityDescription
from .lm_client import LaMarzoccoClient


@dataclass(frozen=True, kw_only=True)
class LaMarzoccoButtonEntityDescription(
ButtonEntityDescription,
LaMarzoccoEntityDescription,
ButtonEntityDescription,
):
"""Description of an La Marzocco Button."""
press_fn: Callable[[LaMarzoccoClient], Coroutine[Any, Any, None]]
"""Description of a La Marzocco button."""

press_fn: Callable[[LaMarzoccoMachine], Coroutine[Any, Any, None]]


ENTITIES: tuple[LaMarzoccoButtonEntityDescription, ...] = (
LaMarzoccoButtonEntityDescription(
key="start_backflush",
translation_key="start_backflush",
icon="mdi:water-sync",
press_fn=lambda client: client.start_backflush(),
press_fn=lambda lm: lm.start_backflush(),
),
)

Expand All @@ -37,22 +39,21 @@ async def async_setup_entry(
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up button entities and services."""
"""Set up button entities."""

coordinator = hass.data[DOMAIN][config_entry.entry_id]
async_add_entities(
LaMarzoccoButtonEntity(coordinator, hass, description)
LaMarzoccoButtonEntity(coordinator, description)
for description in ENTITIES
if coordinator.data.model_name in description.supported_models
if description.supported_fn(coordinator)
)


class LaMarzoccoButtonEntity(LaMarzoccoEntity, ButtonEntity):
"""Button supporting backflush."""
"""La Marzocco Button Entity."""

entity_description: LaMarzoccoButtonEntityDescription

async def async_press(self, **kwargs) -> None:
async def async_press(self) -> None:
"""Press button."""
await self.entity_description.press_fn(self._lm_client)
self.async_write_ha_state()
await self.entity_description.press_fn(self.coordinator.device)
Loading

0 comments on commit 60bc4ab

Please sign in to comment.