Add select platform to La Marzocco integration (#108222)
* add select * change check, icons * fix docstrings, use []pull/108231/head
parent
1b2a4d2bf3
commit
74d53a4231
|
@ -9,6 +9,7 @@ from .coordinator import LaMarzoccoUpdateCoordinator
|
|||
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.SELECT,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
"""Select platform for La Marzocco espresso machines."""
|
||||
|
||||
from collections.abc import Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from lmcloud import LMCloud as LaMarzoccoClient
|
||||
from lmcloud.const import LaMarzoccoModel
|
||||
|
||||
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import LaMarzoccoUpdateCoordinator
|
||||
from .entity import LaMarzoccoEntity, LaMarzoccoEntityDescription
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class LaMarzoccoSelectEntityDescription(
|
||||
LaMarzoccoEntityDescription,
|
||||
SelectEntityDescription,
|
||||
):
|
||||
"""Description of a La Marzocco select entity."""
|
||||
|
||||
current_option_fn: Callable[[LaMarzoccoClient], str]
|
||||
select_option_fn: Callable[
|
||||
[LaMarzoccoUpdateCoordinator, str], Coroutine[Any, Any, bool]
|
||||
]
|
||||
|
||||
|
||||
ENTITIES: tuple[LaMarzoccoSelectEntityDescription, ...] = (
|
||||
LaMarzoccoSelectEntityDescription(
|
||||
key="steam_temp_select",
|
||||
translation_key="steam_temp_select",
|
||||
icon="mdi:water-thermometer",
|
||||
options=["1", "2", "3"],
|
||||
select_option_fn=lambda coordinator, option: coordinator.lm.set_steam_level(
|
||||
int(option)
|
||||
),
|
||||
current_option_fn=lambda lm: lm.current_status["steam_level_set"],
|
||||
supported_fn=lambda coordinator: coordinator.lm.model_name
|
||||
== LaMarzoccoModel.LINEA_MICRA,
|
||||
),
|
||||
LaMarzoccoSelectEntityDescription(
|
||||
key="prebrew_infusion_select",
|
||||
translation_key="prebrew_infusion_select",
|
||||
icon="mdi:water-plus",
|
||||
options=["disabled", "prebrew", "preinfusion"],
|
||||
select_option_fn=lambda coordinator,
|
||||
option: coordinator.lm.select_pre_brew_infusion_mode(option.capitalize()),
|
||||
current_option_fn=lambda lm: lm.pre_brew_infusion_mode.lower(),
|
||||
supported_fn=lambda coordinator: coordinator.lm.model_name
|
||||
in (
|
||||
LaMarzoccoModel.GS3_AV,
|
||||
LaMarzoccoModel.LINEA_MICRA,
|
||||
LaMarzoccoModel.LINEA_MINI,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up select entities."""
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
LaMarzoccoSelectEntity(coordinator, description)
|
||||
for description in ENTITIES
|
||||
if description.supported_fn(coordinator)
|
||||
)
|
||||
|
||||
|
||||
class LaMarzoccoSelectEntity(LaMarzoccoEntity, SelectEntity):
|
||||
"""La Marzocco select entity."""
|
||||
|
||||
entity_description: LaMarzoccoSelectEntityDescription
|
||||
|
||||
@property
|
||||
def current_option(self) -> str:
|
||||
"""Return the current selected option."""
|
||||
return str(self.entity_description.current_option_fn(self.coordinator.lm))
|
||||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
await self.entity_description.select_option_fn(self.coordinator, option)
|
||||
self.async_write_ha_state()
|
|
@ -51,6 +51,24 @@
|
|||
"name": "Water tank empty"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"prebrew_infusion_select": {
|
||||
"name": "Prebrew/-infusion mode",
|
||||
"state": {
|
||||
"disabled": "Disabled",
|
||||
"prebrew": "Prebrew",
|
||||
"preinfusion": "Preinfusion"
|
||||
}
|
||||
},
|
||||
"steam_temp_select": {
|
||||
"name": "Steam level",
|
||||
"state": {
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"current_temp_coffee": {
|
||||
"name": "Current coffee temperature"
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"steam_boiler_enable": true,
|
||||
"steam_temp": 113,
|
||||
"steam_set_temp": 128,
|
||||
"steam_level_set": 3,
|
||||
"coffee_temp": 93,
|
||||
"coffee_set_temp": 95,
|
||||
"water_reservoir_contact": true,
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
# serializer version: 1
|
||||
# name: test_pre_brew_infusion_select[GS3 AV]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'GS01234 Prebrew/-infusion mode',
|
||||
'icon': 'mdi:water-plus',
|
||||
'options': list([
|
||||
'disabled',
|
||||
'prebrew',
|
||||
'preinfusion',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.gs01234_prebrew_infusion_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_pre_brew_infusion_select[GS3 AV].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'disabled',
|
||||
'prebrew',
|
||||
'preinfusion',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': None,
|
||||
'entity_id': 'select.gs01234_prebrew_infusion_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:water-plus',
|
||||
'original_name': 'Prebrew/-infusion mode',
|
||||
'platform': 'lamarzocco',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'prebrew_infusion_select',
|
||||
'unique_id': 'GS01234_prebrew_infusion_select',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_pre_brew_infusion_select[Linea Mini]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'LM01234 Prebrew/-infusion mode',
|
||||
'icon': 'mdi:water-plus',
|
||||
'options': list([
|
||||
'disabled',
|
||||
'prebrew',
|
||||
'preinfusion',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.lm01234_prebrew_infusion_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_pre_brew_infusion_select[Linea Mini].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'disabled',
|
||||
'prebrew',
|
||||
'preinfusion',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': None,
|
||||
'entity_id': 'select.lm01234_prebrew_infusion_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:water-plus',
|
||||
'original_name': 'Prebrew/-infusion mode',
|
||||
'platform': 'lamarzocco',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'prebrew_infusion_select',
|
||||
'unique_id': 'LM01234_prebrew_infusion_select',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_pre_brew_infusion_select[Micra]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'MR01234 Prebrew/-infusion mode',
|
||||
'icon': 'mdi:water-plus',
|
||||
'options': list([
|
||||
'disabled',
|
||||
'prebrew',
|
||||
'preinfusion',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.mr01234_prebrew_infusion_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_pre_brew_infusion_select[Micra].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'disabled',
|
||||
'prebrew',
|
||||
'preinfusion',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': None,
|
||||
'entity_id': 'select.mr01234_prebrew_infusion_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:water-plus',
|
||||
'original_name': 'Prebrew/-infusion mode',
|
||||
'platform': 'lamarzocco',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'prebrew_infusion_select',
|
||||
'unique_id': 'MR01234_prebrew_infusion_select',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_steam_boiler_level[Micra]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'MR01234 Steam level',
|
||||
'icon': 'mdi:water-thermometer',
|
||||
'options': list([
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.mr01234_steam_level',
|
||||
'last_changed': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '3',
|
||||
})
|
||||
# ---
|
||||
# name: test_steam_boiler_level[Micra].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': None,
|
||||
'entity_id': 'select.mr01234_steam_level',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:water-thermometer',
|
||||
'original_name': 'Steam level',
|
||||
'platform': 'lamarzocco',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'steam_temp_select',
|
||||
'unique_id': 'MR01234_steam_temp_select',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
|
@ -0,0 +1,124 @@
|
|||
"""Tests for the La Marzocco select entities."""
|
||||
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from lmcloud.const import LaMarzoccoModel
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.select import (
|
||||
ATTR_OPTION,
|
||||
DOMAIN as SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("init_integration")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_fixture", [LaMarzoccoModel.LINEA_MICRA])
|
||||
async def test_steam_boiler_level(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_lamarzocco: MagicMock,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the La Marzocco Steam Level Select (only for Micra Models)."""
|
||||
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
|
||||
state = hass.states.get(f"select.{serial_number}_steam_level")
|
||||
|
||||
assert state
|
||||
assert state == snapshot
|
||||
|
||||
entry = entity_registry.async_get(state.entity_id)
|
||||
assert entry
|
||||
assert entry == snapshot
|
||||
|
||||
# on/off service calls
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"select.{serial_number}_steam_level",
|
||||
ATTR_OPTION: "1",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(mock_lamarzocco.set_steam_level.mock_calls) == 1
|
||||
mock_lamarzocco.set_steam_level.assert_called_once_with(level=1)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"device_fixture",
|
||||
[LaMarzoccoModel.GS3_AV, LaMarzoccoModel.GS3_MP, LaMarzoccoModel.LINEA_MINI],
|
||||
)
|
||||
async def test_steam_boiler_level_none(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
) -> None:
|
||||
"""Ensure the La Marzocco Steam Level Select is not created for non-Micra models."""
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
state = hass.states.get(f"select.{serial_number}_steam_level")
|
||||
|
||||
assert state is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"device_fixture",
|
||||
[LaMarzoccoModel.LINEA_MICRA, LaMarzoccoModel.GS3_AV, LaMarzoccoModel.LINEA_MINI],
|
||||
)
|
||||
async def test_pre_brew_infusion_select(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_lamarzocco: MagicMock,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the Prebrew/-infusion select."""
|
||||
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
|
||||
state = hass.states.get(f"select.{serial_number}_prebrew_infusion_mode")
|
||||
|
||||
assert state
|
||||
assert state == snapshot
|
||||
|
||||
entry = entity_registry.async_get(state.entity_id)
|
||||
assert entry
|
||||
assert entry == snapshot
|
||||
|
||||
# on/off service calls
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"select.{serial_number}_prebrew_infusion_mode",
|
||||
ATTR_OPTION: "preinfusion",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(mock_lamarzocco.select_pre_brew_infusion_mode.mock_calls) == 1
|
||||
mock_lamarzocco.select_pre_brew_infusion_mode.assert_called_once_with(
|
||||
mode="Preinfusion"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"device_fixture",
|
||||
[LaMarzoccoModel.GS3_MP],
|
||||
)
|
||||
async def test_pre_brew_infusion_select_none(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
) -> None:
|
||||
"""Ensure the La Marzocco Steam Level Select is not created for non-Micra models."""
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
state = hass.states.get(f"select.{serial_number}_prebrew_infusion_mode")
|
||||
|
||||
assert state is None
|
Loading…
Reference in New Issue