mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 13:47:35 +00:00
Add select platform to La Marzocco integration (#108222)
* add select * change check, icons * fix docstrings, use []
This commit is contained in:
parent
1b2a4d2bf3
commit
74d53a4231
@ -9,6 +9,7 @@ from .coordinator import LaMarzoccoUpdateCoordinator
|
|||||||
|
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
Platform.BINARY_SENSOR,
|
Platform.BINARY_SENSOR,
|
||||||
|
Platform.SELECT,
|
||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
Platform.SWITCH,
|
Platform.SWITCH,
|
||||||
]
|
]
|
||||||
|
92
homeassistant/components/lamarzocco/select.py
Normal file
92
homeassistant/components/lamarzocco/select.py
Normal file
@ -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"
|
"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": {
|
"sensor": {
|
||||||
"current_temp_coffee": {
|
"current_temp_coffee": {
|
||||||
"name": "Current coffee temperature"
|
"name": "Current coffee temperature"
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"steam_boiler_enable": true,
|
"steam_boiler_enable": true,
|
||||||
"steam_temp": 113,
|
"steam_temp": 113,
|
||||||
"steam_set_temp": 128,
|
"steam_set_temp": 128,
|
||||||
|
"steam_level_set": 3,
|
||||||
"coffee_temp": 93,
|
"coffee_temp": 93,
|
||||||
"coffee_set_temp": 95,
|
"coffee_set_temp": 95,
|
||||||
"water_reservoir_contact": true,
|
"water_reservoir_contact": true,
|
||||||
|
221
tests/components/lamarzocco/snapshots/test_select.ambr
Normal file
221
tests/components/lamarzocco/snapshots/test_select.ambr
Normal file
@ -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,
|
||||||
|
})
|
||||||
|
# ---
|
124
tests/components/lamarzocco/test_select.py
Normal file
124
tests/components/lamarzocco/test_select.py
Normal file
@ -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…
x
Reference in New Issue
Block a user