Add additonal number entities to La Marzocco (#108258)

This commit is contained in:
Josef Zweck 2024-02-13 09:47:39 +01:00 committed by GitHub
parent 545a34a849
commit ee25f6b960
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 1004 additions and 4 deletions

View File

@ -25,9 +25,21 @@
"coffee_temp": { "coffee_temp": {
"default": "mdi:thermometer-water" "default": "mdi:thermometer-water"
}, },
"dose": {
"default": "mdi:weight-kilogram"
},
"steam_temp": { "steam_temp": {
"default": "mdi:thermometer-water" "default": "mdi:thermometer-water"
}, },
"prebrew_off": {
"default": "mdi:water-off"
},
"prebrew_on": {
"default": "mdi:water"
},
"preinfusion_off": {
"default": "mdi:water"
},
"tea_water_duration": { "tea_water_duration": {
"default": "mdi:timer-sand" "default": "mdi:timer-sand"
} }

View File

@ -5,7 +5,7 @@ from dataclasses import dataclass
from typing import Any from typing import Any
from lmcloud import LMCloud as LaMarzoccoClient from lmcloud import LMCloud as LaMarzoccoClient
from lmcloud.const import LaMarzoccoModel from lmcloud.const import KEYS_PER_MODEL, LaMarzoccoModel
from homeassistant.components.number import ( from homeassistant.components.number import (
NumberDeviceClass, NumberDeviceClass,
@ -16,6 +16,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
PRECISION_TENTHS, PRECISION_TENTHS,
PRECISION_WHOLE, PRECISION_WHOLE,
EntityCategory,
UnitOfTemperature, UnitOfTemperature,
UnitOfTime, UnitOfTime,
) )
@ -40,6 +41,19 @@ class LaMarzoccoNumberEntityDescription(
] ]
@dataclass(frozen=True, kw_only=True)
class LaMarzoccoKeyNumberEntityDescription(
LaMarzoccoEntityDescription,
NumberEntityDescription,
):
"""Description of an La Marzocco number entity with keys."""
native_value_fn: Callable[[LaMarzoccoClient, int], float | int]
set_value_fn: Callable[
[LaMarzoccoClient, float | int, int], Coroutine[Any, Any, bool]
]
ENTITIES: tuple[LaMarzoccoNumberEntityDescription, ...] = ( ENTITIES: tuple[LaMarzoccoNumberEntityDescription, ...] = (
LaMarzoccoNumberEntityDescription( LaMarzoccoNumberEntityDescription(
key="coffee_temp", key="coffee_temp",
@ -89,6 +103,103 @@ ENTITIES: tuple[LaMarzoccoNumberEntityDescription, ...] = (
) )
async def _set_prebrew_on(
lm: LaMarzoccoClient,
value: float,
key: int,
) -> bool:
return await lm.configure_prebrew(
on_time=int(value * 1000),
off_time=int(lm.current_status[f"prebrewing_toff_k{key}"] * 1000),
key=key,
)
async def _set_prebrew_off(
lm: LaMarzoccoClient,
value: float,
key: int,
) -> bool:
return await lm.configure_prebrew(
on_time=int(lm.current_status[f"prebrewing_ton_k{key}"] * 1000),
off_time=int(value * 1000),
key=key,
)
async def _set_preinfusion(
lm: LaMarzoccoClient,
value: float,
key: int,
) -> bool:
return await lm.configure_prebrew(
off_time=int(value * 1000),
key=key,
)
KEY_ENTITIES: tuple[LaMarzoccoKeyNumberEntityDescription, ...] = (
LaMarzoccoKeyNumberEntityDescription(
key="prebrew_off",
translation_key="prebrew_off",
device_class=NumberDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
native_step=PRECISION_TENTHS,
native_min_value=1,
native_max_value=10,
entity_category=EntityCategory.CONFIG,
set_value_fn=_set_prebrew_off,
native_value_fn=lambda lm, key: lm.current_status[f"prebrewing_ton_k{key}"],
available_fn=lambda lm: lm.current_status["enable_prebrewing"],
supported_fn=lambda coordinator: coordinator.lm.model_name
!= LaMarzoccoModel.GS3_MP,
),
LaMarzoccoKeyNumberEntityDescription(
key="prebrew_on",
translation_key="prebrew_on",
device_class=NumberDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
native_step=PRECISION_TENTHS,
native_min_value=2,
native_max_value=10,
entity_category=EntityCategory.CONFIG,
set_value_fn=_set_prebrew_on,
native_value_fn=lambda lm, key: lm.current_status[f"prebrewing_toff_k{key}"],
available_fn=lambda lm: lm.current_status["enable_prebrewing"],
supported_fn=lambda coordinator: coordinator.lm.model_name
!= LaMarzoccoModel.GS3_MP,
),
LaMarzoccoKeyNumberEntityDescription(
key="preinfusion_off",
translation_key="preinfusion_off",
device_class=NumberDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
native_step=PRECISION_TENTHS,
native_min_value=2,
native_max_value=29,
entity_category=EntityCategory.CONFIG,
set_value_fn=_set_preinfusion,
native_value_fn=lambda lm, key: lm.current_status[f"preinfusion_k{key}"],
available_fn=lambda lm: lm.current_status["enable_preinfusion"],
supported_fn=lambda coordinator: coordinator.lm.model_name
!= LaMarzoccoModel.GS3_MP,
),
LaMarzoccoKeyNumberEntityDescription(
key="dose",
translation_key="dose",
native_unit_of_measurement="ticks",
native_step=PRECISION_WHOLE,
native_min_value=0,
native_max_value=999,
entity_category=EntityCategory.CONFIG,
set_value_fn=lambda lm, ticks, key: lm.set_dose(key=key, value=int(ticks)),
native_value_fn=lambda lm, key: lm.current_status[f"dose_k{key}"],
supported_fn=lambda coordinator: coordinator.lm.model_name
== LaMarzoccoModel.GS3_AV,
),
)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ConfigEntry,
@ -103,6 +214,17 @@ async def async_setup_entry(
if description.supported_fn(coordinator) if description.supported_fn(coordinator)
) )
entities: list[LaMarzoccoKeyNumberEntity] = []
for description in KEY_ENTITIES:
if description.supported_fn(coordinator):
num_keys = KEYS_PER_MODEL[coordinator.lm.model_name]
for key in range(min(num_keys, 1), num_keys + 1):
entities.append(
LaMarzoccoKeyNumberEntity(coordinator, description, key)
)
async_add_entities(entities)
class LaMarzoccoNumberEntity(LaMarzoccoEntity, NumberEntity): class LaMarzoccoNumberEntity(LaMarzoccoEntity, NumberEntity):
"""La Marzocco number entity.""" """La Marzocco number entity."""
@ -118,3 +240,42 @@ class LaMarzoccoNumberEntity(LaMarzoccoEntity, NumberEntity):
"""Set the value.""" """Set the value."""
await self.entity_description.set_value_fn(self.coordinator, value) await self.entity_description.set_value_fn(self.coordinator, value)
self.async_write_ha_state() self.async_write_ha_state()
class LaMarzoccoKeyNumberEntity(LaMarzoccoEntity, NumberEntity):
"""Number representing espresso machine with key support."""
entity_description: LaMarzoccoKeyNumberEntityDescription
def __init__(
self,
coordinator: LaMarzoccoUpdateCoordinator,
description: LaMarzoccoKeyNumberEntityDescription,
pyhsical_key: int,
) -> None:
"""Initialize the entity."""
super().__init__(coordinator, description)
# Physical Key on the machine the entity represents.
if pyhsical_key == 0:
pyhsical_key = 1
else:
self._attr_translation_key = f"{description.translation_key}_key"
self._attr_translation_placeholders = {"key": str(pyhsical_key)}
self._attr_unique_id = f"{super()._attr_unique_id}_key{pyhsical_key}"
self._attr_entity_registry_enabled_default = False
self.pyhsical_key = pyhsical_key
@property
def native_value(self) -> float:
"""Return the current value."""
return self.entity_description.native_value_fn(
self.coordinator.lm, self.pyhsical_key
)
async def async_set_native_value(self, value: float) -> None:
"""Set the value."""
await self.entity_description.set_value_fn(
self.coordinator.lm, value, self.pyhsical_key
)
self.async_write_ha_state()

View File

@ -60,6 +60,27 @@
"coffee_temp": { "coffee_temp": {
"name": "Coffee target temperature" "name": "Coffee target temperature"
}, },
"dose_key": {
"name": "Dose Key {key}"
},
"prebrew_on": {
"name": "Prebrew on time"
},
"prebrew_on_key": {
"name": "Prebrew on time Key {key}"
},
"prebrew_off": {
"name": "Prebrew off time"
},
"prebrew_off_key": {
"name": "Prebrew off time Key {key}"
},
"preinfusion_off": {
"name": "Preinfusion time"
},
"preinfusion_off_key": {
"name": "Preinfusion time Key {key}"
},
"steam_temp": { "steam_temp": {
"name": "Steam target temperature" "name": "Steam target temperature"
}, },

View File

@ -269,3 +269,611 @@
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>, 'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}) })
# --- # ---
# name: test_pre_brew_infusion_key_numbers[dose-6-set_dose-kwargs3-GS3 AV][GS01234_dose_key_1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'GS01234 Dose Key 1',
'max': 999,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
'unit_of_measurement': 'ticks',
}),
'context': <ANY>,
'entity_id': 'number.gs01234_dose_key_1',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '1023',
})
# ---
# name: test_pre_brew_infusion_key_numbers[dose-6-set_dose-kwargs3-GS3 AV][GS01234_dose_key_2-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'GS01234 Dose Key 2',
'max': 999,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
'unit_of_measurement': 'ticks',
}),
'context': <ANY>,
'entity_id': 'number.gs01234_dose_key_2',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '1023',
})
# ---
# name: test_pre_brew_infusion_key_numbers[dose-6-set_dose-kwargs3-GS3 AV][GS01234_dose_key_3-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'GS01234 Dose Key 3',
'max': 999,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
'unit_of_measurement': 'ticks',
}),
'context': <ANY>,
'entity_id': 'number.gs01234_dose_key_3',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '1023',
})
# ---
# name: test_pre_brew_infusion_key_numbers[dose-6-set_dose-kwargs3-GS3 AV][GS01234_dose_key_4-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'GS01234 Dose Key 4',
'max': 999,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
'unit_of_measurement': 'ticks',
}),
'context': <ANY>,
'entity_id': 'number.gs01234_dose_key_4',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '1023',
})
# ---
# name: test_pre_brew_infusion_key_numbers[prebrew_off_time-6-configure_prebrew-kwargs0-GS3 AV][GS01234_prebrew_off_time_key_1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Prebrew off time Key 1',
'max': 10,
'min': 1,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_prebrew_off_time_key_1',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '3',
})
# ---
# name: test_pre_brew_infusion_key_numbers[prebrew_off_time-6-configure_prebrew-kwargs0-GS3 AV][GS01234_prebrew_off_time_key_2-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Prebrew off time Key 2',
'max': 10,
'min': 1,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_prebrew_off_time_key_2',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '3',
})
# ---
# name: test_pre_brew_infusion_key_numbers[prebrew_off_time-6-configure_prebrew-kwargs0-GS3 AV][GS01234_prebrew_off_time_key_3-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Prebrew off time Key 3',
'max': 10,
'min': 1,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_prebrew_off_time_key_3',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '3',
})
# ---
# name: test_pre_brew_infusion_key_numbers[prebrew_off_time-6-configure_prebrew-kwargs0-GS3 AV][GS01234_prebrew_off_time_key_4-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Prebrew off time Key 4',
'max': 10,
'min': 1,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_prebrew_off_time_key_4',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '3',
})
# ---
# name: test_pre_brew_infusion_key_numbers[prebrew_on_time-6-configure_prebrew-kwargs1-GS3 AV][GS01234_prebrew_on_time_key_1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Prebrew on time Key 1',
'max': 10,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_prebrew_on_time_key_1',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '5',
})
# ---
# name: test_pre_brew_infusion_key_numbers[prebrew_on_time-6-configure_prebrew-kwargs1-GS3 AV][GS01234_prebrew_on_time_key_2-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Prebrew on time Key 2',
'max': 10,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_prebrew_on_time_key_2',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '5',
})
# ---
# name: test_pre_brew_infusion_key_numbers[prebrew_on_time-6-configure_prebrew-kwargs1-GS3 AV][GS01234_prebrew_on_time_key_3-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Prebrew on time Key 3',
'max': 10,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_prebrew_on_time_key_3',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '5',
})
# ---
# name: test_pre_brew_infusion_key_numbers[prebrew_on_time-6-configure_prebrew-kwargs1-GS3 AV][GS01234_prebrew_on_time_key_4-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Prebrew on time Key 4',
'max': 10,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_prebrew_on_time_key_4',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '5',
})
# ---
# name: test_pre_brew_infusion_key_numbers[preinfusion_time-7-configure_prebrew-kwargs2-GS3 AV][GS01234_preinfusion_time_key_1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Preinfusion time Key 1',
'max': 29,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_preinfusion_time_key_1',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
})
# ---
# name: test_pre_brew_infusion_key_numbers[preinfusion_time-7-configure_prebrew-kwargs2-GS3 AV][GS01234_preinfusion_time_key_2-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Preinfusion time Key 2',
'max': 29,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_preinfusion_time_key_2',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
})
# ---
# name: test_pre_brew_infusion_key_numbers[preinfusion_time-7-configure_prebrew-kwargs2-GS3 AV][GS01234_preinfusion_time_key_3-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Preinfusion time Key 3',
'max': 29,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_preinfusion_time_key_3',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
})
# ---
# name: test_pre_brew_infusion_key_numbers[preinfusion_time-7-configure_prebrew-kwargs2-GS3 AV][GS01234_preinfusion_time_key_4-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'GS01234 Preinfusion time Key 4',
'max': 29,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.gs01234_preinfusion_time_key_4',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
})
# ---
# name: test_pre_brew_infusion_numbers[prebrew_off_time-6-kwargs0-Linea Mini]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'LM01234 Prebrew off time',
'max': 10,
'min': 1,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.lm01234_prebrew_off_time',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '3',
})
# ---
# name: test_pre_brew_infusion_numbers[prebrew_off_time-6-kwargs0-Linea Mini].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 10,
'min': 1,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.lm01234_prebrew_off_time',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': <NumberDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Prebrew off time',
'platform': 'lamarzocco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'prebrew_off',
'unique_id': 'LM01234_prebrew_off',
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
})
# ---
# name: test_pre_brew_infusion_numbers[prebrew_off_time-6-kwargs0-Micra]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'MR01234 Prebrew off time',
'max': 10,
'min': 1,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.mr01234_prebrew_off_time',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '3',
})
# ---
# name: test_pre_brew_infusion_numbers[prebrew_off_time-6-kwargs0-Micra].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 10,
'min': 1,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.mr01234_prebrew_off_time',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': <NumberDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Prebrew off time',
'platform': 'lamarzocco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'prebrew_off',
'unique_id': 'MR01234_prebrew_off',
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
})
# ---
# name: test_pre_brew_infusion_numbers[prebrew_on_time-6-kwargs1-Linea Mini]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'LM01234 Prebrew on time',
'max': 10,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.lm01234_prebrew_on_time',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '5',
})
# ---
# name: test_pre_brew_infusion_numbers[prebrew_on_time-6-kwargs1-Linea Mini].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 10,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.lm01234_prebrew_on_time',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': <NumberDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Prebrew on time',
'platform': 'lamarzocco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'prebrew_on',
'unique_id': 'LM01234_prebrew_on',
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
})
# ---
# name: test_pre_brew_infusion_numbers[prebrew_on_time-6-kwargs1-Micra]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'MR01234 Prebrew on time',
'max': 10,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.mr01234_prebrew_on_time',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '5',
})
# ---
# name: test_pre_brew_infusion_numbers[prebrew_on_time-6-kwargs1-Micra].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 10,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.mr01234_prebrew_on_time',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': <NumberDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Prebrew on time',
'platform': 'lamarzocco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'prebrew_on',
'unique_id': 'MR01234_prebrew_on',
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
})
# ---
# name: test_pre_brew_infusion_numbers[preinfusion_time-7-kwargs2-Linea Mini]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'LM01234 Preinfusion time',
'max': 29,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.lm01234_preinfusion_time',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
})
# ---
# name: test_pre_brew_infusion_numbers[preinfusion_time-7-kwargs2-Linea Mini].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 29,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.lm01234_preinfusion_time',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': <NumberDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Preinfusion time',
'platform': 'lamarzocco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'preinfusion_off',
'unique_id': 'LM01234_preinfusion_off',
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
})
# ---
# name: test_pre_brew_infusion_numbers[preinfusion_time-7-kwargs2-Micra]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'MR01234 Preinfusion time',
'max': 29,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.mr01234_preinfusion_time',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
})
# ---
# name: test_pre_brew_infusion_numbers[preinfusion_time-7-kwargs2-Micra].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 29,
'min': 2,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.mr01234_preinfusion_time',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': <NumberDeviceClass.DURATION: 'duration'>,
'original_icon': None,
'original_name': 'Preinfusion time',
'platform': 'lamarzocco',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'preinfusion_off',
'unique_id': 'MR01234_preinfusion_off',
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
})
# ---

View File

@ -1,9 +1,8 @@
"""Tests for the La Marzocco number entities.""" """Tests for the La Marzocco number entities."""
from unittest.mock import MagicMock from unittest.mock import MagicMock
from lmcloud.const import LaMarzoccoModel from lmcloud.const import KEYS_PER_MODEL, LaMarzoccoModel
import pytest import pytest
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
@ -12,7 +11,7 @@ from homeassistant.components.number import (
DOMAIN as NUMBER_DOMAIN, DOMAIN as NUMBER_DOMAIN,
SERVICE_SET_VALUE, SERVICE_SET_VALUE,
) )
from homeassistant.const import ATTR_ENTITY_ID from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
@ -126,3 +125,202 @@ async def test_gs3_exclusive_none(
for entity in ENTITIES: for entity in ENTITIES:
state = hass.states.get(f"number.{serial_number}_{entity}") state = hass.states.get(f"number.{serial_number}_{entity}")
assert state is None assert state is None
@pytest.mark.parametrize(
"device_fixture", [LaMarzoccoModel.LINEA_MICRA, LaMarzoccoModel.LINEA_MINI]
)
@pytest.mark.parametrize(
("entity_name", "value", "kwargs"),
[
("prebrew_off_time", 6, {"on_time": 3000, "off_time": 6000, "key": 1}),
("prebrew_on_time", 6, {"on_time": 6000, "off_time": 5000, "key": 1}),
("preinfusion_time", 7, {"off_time": 7000, "key": 1}),
],
)
async def test_pre_brew_infusion_numbers(
hass: HomeAssistant,
mock_lamarzocco: MagicMock,
entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry,
snapshot: SnapshotAssertion,
entity_name: str,
value: float,
kwargs: dict[str, float],
) -> None:
"""Test the La Marzocco prebrew/-infusion sensors."""
mock_lamarzocco.current_status["enable_preinfusion"] = True
serial_number = mock_lamarzocco.serial_number
state = hass.states.get(f"number.{serial_number}_{entity_name}")
assert state
assert state == snapshot
entry = entity_registry.async_get(state.entity_id)
assert entry
assert entry.device_id
assert entry == snapshot
device = device_registry.async_get(entry.device_id)
assert device
# service call
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: f"number.{serial_number}_{entity_name}",
ATTR_VALUE: value,
},
blocking=True,
)
assert len(mock_lamarzocco.configure_prebrew.mock_calls) == 1
mock_lamarzocco.configure_prebrew.assert_called_once_with(**kwargs)
@pytest.mark.parametrize("device_fixture", [LaMarzoccoModel.GS3_AV])
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize(
("entity_name", "value", "function_name", "kwargs"),
[
(
"prebrew_off_time",
6,
"configure_prebrew",
{"on_time": 3000, "off_time": 6000},
),
(
"prebrew_on_time",
6,
"configure_prebrew",
{"on_time": 6000, "off_time": 5000},
),
("preinfusion_time", 7, "configure_prebrew", {"off_time": 7000}),
("dose", 6, "set_dose", {"value": 6}),
],
)
async def test_pre_brew_infusion_key_numbers(
hass: HomeAssistant,
mock_lamarzocco: MagicMock,
snapshot: SnapshotAssertion,
entity_name: str,
value: float,
function_name: str,
kwargs: dict[str, float],
) -> None:
"""Test the La Marzocco number sensors for GS3AV model."""
mock_lamarzocco.current_status["enable_preinfusion"] = True
serial_number = mock_lamarzocco.serial_number
func = getattr(mock_lamarzocco, function_name)
state = hass.states.get(f"number.{serial_number}_{entity_name}")
assert state is None
for key in range(1, KEYS_PER_MODEL[mock_lamarzocco.model_name] + 1):
state = hass.states.get(f"number.{serial_number}_{entity_name}_key_{key}")
assert state
assert state == snapshot(name=f"{serial_number}_{entity_name}_key_{key}-state")
# service call
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: f"number.{serial_number}_{entity_name}_key_{key}",
ATTR_VALUE: value,
},
blocking=True,
)
kwargs["key"] = key
assert len(func.mock_calls) == key
func.assert_called_with(**kwargs)
@pytest.mark.parametrize("device_fixture", [LaMarzoccoModel.GS3_AV])
async def test_disabled_entites(
hass: HomeAssistant,
mock_lamarzocco: MagicMock,
) -> None:
"""Test the La Marzocco prebrew/-infusion sensors for GS3AV model."""
ENTITIES = (
"prebrew_off_time",
"prebrew_on_time",
"preinfusion_time",
"set_dose",
)
serial_number = mock_lamarzocco.serial_number
for entity_name in ENTITIES:
for key in range(1, KEYS_PER_MODEL[mock_lamarzocco.model_name] + 1):
state = hass.states.get(f"number.{serial_number}_{entity_name}_key_{key}")
assert state is None
@pytest.mark.parametrize(
"device_fixture",
[LaMarzoccoModel.GS3_MP, LaMarzoccoModel.LINEA_MICRA, LaMarzoccoModel.LINEA_MINI],
)
async def test_not_existing_key_entites(
hass: HomeAssistant,
mock_lamarzocco: MagicMock,
) -> None:
"""Assert not existing key entities."""
serial_number = mock_lamarzocco.serial_number
for entity in (
"prebrew_off_time",
"prebrew_on_time",
"preinfusion_time",
"set_dose",
):
for key in range(1, KEYS_PER_MODEL[LaMarzoccoModel.GS3_AV] + 1):
state = hass.states.get(f"number.{serial_number}_{entity}_key_{key}")
assert state is None
@pytest.mark.parametrize(
"device_fixture",
[LaMarzoccoModel.GS3_MP],
)
async def test_not_existing_entites(
hass: HomeAssistant,
mock_lamarzocco: MagicMock,
) -> None:
"""Assert not existing entities."""
serial_number = mock_lamarzocco.serial_number
for entity in (
"prebrew_off_time",
"prebrew_on_time",
"preinfusion_time",
"set_dose",
):
state = hass.states.get(f"number.{serial_number}_{entity}")
assert state is None
@pytest.mark.parametrize("device_fixture", [LaMarzoccoModel.LINEA_MICRA])
async def test_not_settable_entites(
hass: HomeAssistant,
mock_lamarzocco: MagicMock,
) -> None:
"""Assert not settable causes error."""
serial_number = mock_lamarzocco.serial_number
state = hass.states.get(f"number.{serial_number}_preinfusion_time")
assert state
assert state.state == STATE_UNAVAILABLE