mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 01:08:12 +00:00
Add smart standby functionality to lamarzocco (#129333)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
7929895b11
commit
478bf643bf
@ -43,6 +43,9 @@
|
||||
"preinfusion_off": {
|
||||
"default": "mdi:water"
|
||||
},
|
||||
"smart_standby_time": {
|
||||
"default": "mdi:timer"
|
||||
},
|
||||
"steam_temp": {
|
||||
"default": "mdi:thermometer-water"
|
||||
},
|
||||
@ -51,6 +54,13 @@
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"smart_standby_mode": {
|
||||
"default": "mdi:power",
|
||||
"state": {
|
||||
"poweron": "mdi:power",
|
||||
"lastbrewing": "mdi:coffee"
|
||||
}
|
||||
},
|
||||
"steam_temp_select": {
|
||||
"default": "mdi:thermometer",
|
||||
"state": {
|
||||
@ -100,6 +110,12 @@
|
||||
"off": "mdi:alarm-off"
|
||||
}
|
||||
},
|
||||
"smart_standby_enabled": {
|
||||
"state": {
|
||||
"on": "mdi:sleep",
|
||||
"off": "mdi:sleep-off"
|
||||
}
|
||||
},
|
||||
"steam_boiler": {
|
||||
"default": "mdi:water-boiler",
|
||||
"state": {
|
||||
|
@ -109,6 +109,22 @@ ENTITIES: tuple[LaMarzoccoNumberEntityDescription, ...] = (
|
||||
MachineModel.GS3_MP,
|
||||
),
|
||||
),
|
||||
LaMarzoccoNumberEntityDescription(
|
||||
key="smart_standby_time",
|
||||
translation_key="smart_standby_time",
|
||||
device_class=NumberDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
native_step=10,
|
||||
native_min_value=10,
|
||||
native_max_value=240,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
set_value_fn=lambda machine, value: machine.set_smart_standby(
|
||||
enabled=machine.config.smart_standby.enabled,
|
||||
mode=machine.config.smart_standby.mode,
|
||||
minutes=int(value),
|
||||
),
|
||||
native_value_fn=lambda config: config.smart_standby.minutes,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@ from collections.abc import Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from lmcloud.const import MachineModel, PrebrewMode, SteamLevel
|
||||
from lmcloud.const import MachineModel, PrebrewMode, SmartStandbyMode, SteamLevel
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
from lmcloud.lm_machine import LaMarzoccoMachine
|
||||
from lmcloud.models import LaMarzoccoMachineConfig
|
||||
@ -43,6 +43,13 @@ PREBREW_MODE_LM_TO_HA = {
|
||||
PrebrewMode.PREINFUSION: "preinfusion",
|
||||
}
|
||||
|
||||
STANDBY_MODE_HA_TO_LM = {
|
||||
"power_on": SmartStandbyMode.POWER_ON,
|
||||
"last_brewing": SmartStandbyMode.LAST_BREWING,
|
||||
}
|
||||
|
||||
STANDBY_MODE_LM_TO_HA = {value: key for key, value in STANDBY_MODE_HA_TO_LM.items()}
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class LaMarzoccoSelectEntityDescription(
|
||||
@ -83,6 +90,20 @@ ENTITIES: tuple[LaMarzoccoSelectEntityDescription, ...] = (
|
||||
MachineModel.LINEA_MINI,
|
||||
),
|
||||
),
|
||||
LaMarzoccoSelectEntityDescription(
|
||||
key="smart_standby_mode",
|
||||
translation_key="smart_standby_mode",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
options=["power_on", "last_brewing"],
|
||||
select_option_fn=lambda machine, option: machine.set_smart_standby(
|
||||
enabled=machine.config.smart_standby.enabled,
|
||||
mode=STANDBY_MODE_HA_TO_LM[option],
|
||||
minutes=machine.config.smart_standby.minutes,
|
||||
),
|
||||
current_option_fn=lambda config: STANDBY_MODE_LM_TO_HA[
|
||||
config.smart_standby.mode
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
@ -116,6 +116,9 @@
|
||||
"preinfusion_off_key": {
|
||||
"name": "Preinfusion time Key {key}"
|
||||
},
|
||||
"smart_standby_time": {
|
||||
"name": "Smart standby time"
|
||||
},
|
||||
"steam_temp": {
|
||||
"name": "Steam target temperature"
|
||||
},
|
||||
@ -132,6 +135,13 @@
|
||||
"preinfusion": "Preinfusion"
|
||||
}
|
||||
},
|
||||
"smart_standby_mode": {
|
||||
"name": "Smart standby mode",
|
||||
"state": {
|
||||
"last_brewing": "Last brewing",
|
||||
"power_on": "Power on"
|
||||
}
|
||||
},
|
||||
"steam_temp_select": {
|
||||
"name": "Steam level",
|
||||
"state": {
|
||||
@ -162,6 +172,9 @@
|
||||
"auto_on_off": {
|
||||
"name": "Auto on/off ({id})"
|
||||
},
|
||||
"smart_standby_enabled": {
|
||||
"name": "Smart standby enabled"
|
||||
},
|
||||
"steam_boiler": {
|
||||
"name": "Steam boiler"
|
||||
}
|
||||
|
@ -46,6 +46,17 @@ ENTITIES: tuple[LaMarzoccoSwitchEntityDescription, ...] = (
|
||||
control_fn=lambda machine, state: machine.set_steam(state),
|
||||
is_on_fn=lambda config: config.boilers[BoilerType.STEAM].enabled,
|
||||
),
|
||||
LaMarzoccoSwitchEntityDescription(
|
||||
key="smart_standby_enabled",
|
||||
translation_key="smart_standby_enabled",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
control_fn=lambda machine, state: machine.set_smart_standby(
|
||||
enabled=state,
|
||||
mode=machine.config.smart_standby.mode,
|
||||
minutes=machine.config.smart_standby.minutes,
|
||||
),
|
||||
is_on_fn=lambda config: config.smart_standby.enabled,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# serializer version: 1
|
||||
# name: test_coffee_boiler
|
||||
# name: test_general_numbers[coffee_target_temperature-94-set_temp-kwargs0]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'temperature',
|
||||
@ -18,7 +18,7 @@
|
||||
'state': '95',
|
||||
})
|
||||
# ---
|
||||
# name: test_coffee_boiler.1
|
||||
# name: test_general_numbers[coffee_target_temperature-94-set_temp-kwargs0].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@ -56,6 +56,63 @@
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_general_numbers[smart_standby_time-23-set_smart_standby-kwargs1]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'duration',
|
||||
'friendly_name': 'GS01234 Smart standby time',
|
||||
'max': 240,
|
||||
'min': 10,
|
||||
'mode': <NumberMode.AUTO: 'auto'>,
|
||||
'step': 10,
|
||||
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.gs01234_smart_standby_time',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '10',
|
||||
})
|
||||
# ---
|
||||
# name: test_general_numbers[smart_standby_time-23-set_smart_standby-kwargs1].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 240,
|
||||
'min': 10,
|
||||
'mode': <NumberMode.AUTO: 'auto'>,
|
||||
'step': 10,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.gs01234_smart_standby_time',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <NumberDeviceClass.DURATION: 'duration'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Smart standby time',
|
||||
'platform': 'lamarzocco',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'smart_standby_time',
|
||||
'unique_id': 'GS01234_smart_standby_time',
|
||||
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_gs3_exclusive[steam_target_temperature-131-set_temp-kwargs0-GS3 AV]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
|
@ -170,6 +170,61 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_smart_standby_mode
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'GS01234 Smart standby mode',
|
||||
'options': list([
|
||||
'power_on',
|
||||
'last_brewing',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.gs01234_smart_standby_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'last_brewing',
|
||||
})
|
||||
# ---
|
||||
# name: test_smart_standby_mode.1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'power_on',
|
||||
'last_brewing',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.gs01234_smart_standby_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Smart standby mode',
|
||||
'platform': 'lamarzocco',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'smart_standby_mode',
|
||||
'unique_id': 'GS01234_smart_standby_mode',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_steam_boiler_level[Micra]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
|
@ -123,7 +123,7 @@
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switches[-set_power]
|
||||
# name: test_switches[-set_power-kwargs0]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'GS01234',
|
||||
@ -136,7 +136,7 @@
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_switches[-set_power].1
|
||||
# name: test_switches[-set_power-kwargs0].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@ -169,7 +169,53 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switches[_steam_boiler-set_steam]
|
||||
# name: test_switches[_smart_standby_enabled-set_smart_standby-kwargs2]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'GS01234 Smart standby enabled',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.gs01234_smart_standby_enabled',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_switches[_smart_standby_enabled-set_smart_standby-kwargs2].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'switch',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'switch.gs01234_smart_standby_enabled',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Smart standby enabled',
|
||||
'platform': 'lamarzocco',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'smart_standby_enabled',
|
||||
'unique_id': 'GS01234_smart_standby_enabled',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_switches[_steam_boiler-set_steam-kwargs1]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'GS01234 Steam boiler',
|
||||
@ -182,7 +228,7 @@
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_switches[_steam_boiler-set_steam].1
|
||||
# name: test_switches[_steam_boiler-set_steam-kwargs1].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
|
@ -1,5 +1,6 @@
|
||||
"""Tests for the La Marzocco number entities."""
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from lmcloud.const import (
|
||||
@ -28,20 +29,41 @@ from . import async_init_integration
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_coffee_boiler(
|
||||
@pytest.mark.parametrize(
|
||||
("entity_name", "value", "func_name", "kwargs"),
|
||||
[
|
||||
(
|
||||
"coffee_target_temperature",
|
||||
94,
|
||||
"set_temp",
|
||||
{"boiler": BoilerType.COFFEE, "temperature": 94},
|
||||
),
|
||||
(
|
||||
"smart_standby_time",
|
||||
23,
|
||||
"set_smart_standby",
|
||||
{"enabled": True, "mode": "LastBrewing", "minutes": 23},
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_general_numbers(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_name: str,
|
||||
value: float,
|
||||
func_name: str,
|
||||
kwargs: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test the La Marzocco coffee temperature Number."""
|
||||
"""Test the numbers available to all machines."""
|
||||
|
||||
await async_init_integration(hass, mock_config_entry)
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
|
||||
state = hass.states.get(f"number.{serial_number}_coffee_target_temperature")
|
||||
state = hass.states.get(f"number.{serial_number}_{entity_name}")
|
||||
|
||||
assert state
|
||||
assert state == snapshot
|
||||
@ -59,16 +81,14 @@ async def test_coffee_boiler(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"number.{serial_number}_coffee_target_temperature",
|
||||
ATTR_VALUE: 94,
|
||||
ATTR_ENTITY_ID: f"number.{serial_number}_{entity_name}",
|
||||
ATTR_VALUE: value,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(mock_lamarzocco.set_temp.mock_calls) == 1
|
||||
mock_lamarzocco.set_temp.assert_called_once_with(
|
||||
boiler=BoilerType.COFFEE, temperature=94
|
||||
)
|
||||
mock_func = getattr(mock_lamarzocco, func_name)
|
||||
mock_func.assert_called_once_with(**kwargs)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", [MachineModel.GS3_AV, MachineModel.GS3_MP])
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from lmcloud.const import MachineModel, PrebrewMode, SteamLevel
|
||||
from lmcloud.const import MachineModel, PrebrewMode, SmartStandbyMode, SteamLevel
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
@ -121,6 +121,40 @@ async def test_pre_brew_infusion_select_none(
|
||||
assert state is None
|
||||
|
||||
|
||||
async def test_smart_standby_mode(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_lamarzocco: MagicMock,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the La Marzocco Smart Standby mode select."""
|
||||
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
|
||||
state = hass.states.get(f"select.{serial_number}_smart_standby_mode")
|
||||
|
||||
assert state
|
||||
assert state == snapshot
|
||||
|
||||
entry = entity_registry.async_get(state.entity_id)
|
||||
assert entry
|
||||
assert entry == snapshot
|
||||
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"select.{serial_number}_smart_standby_mode",
|
||||
ATTR_OPTION: "power_on",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_lamarzocco.set_smart_standby.assert_called_once_with(
|
||||
enabled=True, mode=SmartStandbyMode.POWER_ON, minutes=10
|
||||
)
|
||||
|
||||
|
||||
async def test_select_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
|
@ -1,5 +1,6 @@
|
||||
"""Tests for La Marzocco switches."""
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
@ -25,15 +26,15 @@ from tests.common import MockConfigEntry
|
||||
(
|
||||
"entity_name",
|
||||
"method_name",
|
||||
"kwargs",
|
||||
),
|
||||
[
|
||||
("", "set_power", {}),
|
||||
("_steam_boiler", "set_steam", {}),
|
||||
(
|
||||
"",
|
||||
"set_power",
|
||||
),
|
||||
(
|
||||
"_steam_boiler",
|
||||
"set_steam",
|
||||
"_smart_standby_enabled",
|
||||
"set_smart_standby",
|
||||
{"mode": "LastBrewing", "minutes": 10},
|
||||
),
|
||||
],
|
||||
)
|
||||
@ -45,6 +46,7 @@ async def test_switches(
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_name: str,
|
||||
method_name: str,
|
||||
kwargs: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test the La Marzocco switches."""
|
||||
await async_init_integration(hass, mock_config_entry)
|
||||
@ -71,7 +73,7 @@ async def test_switches(
|
||||
)
|
||||
|
||||
assert len(control_fn.mock_calls) == 1
|
||||
control_fn.assert_called_once_with(False)
|
||||
control_fn.assert_called_once_with(enabled=False, **kwargs)
|
||||
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
@ -83,7 +85,7 @@ async def test_switches(
|
||||
)
|
||||
|
||||
assert len(control_fn.mock_calls) == 2
|
||||
control_fn.assert_called_with(True)
|
||||
control_fn.assert_called_with(enabled=True, **kwargs)
|
||||
|
||||
|
||||
async def test_device(
|
||||
|
Loading…
x
Reference in New Issue
Block a user