diff --git a/homeassistant/components/lamarzocco/icons.json b/homeassistant/components/lamarzocco/icons.json index 79267b4abd4..2be882fafea 100644 --- a/homeassistant/components/lamarzocco/icons.json +++ b/homeassistant/components/lamarzocco/icons.json @@ -95,6 +95,9 @@ "drink_stats_flushing": { "default": "mdi:chart-line" }, + "drink_stats_coffee_key": { + "default": "mdi:chart-scatter-plot" + }, "shot_timer": { "default": "mdi:timer" }, diff --git a/homeassistant/components/lamarzocco/sensor.py b/homeassistant/components/lamarzocco/sensor.py index 406e8e40e92..a2d6143daa5 100644 --- a/homeassistant/components/lamarzocco/sensor.py +++ b/homeassistant/components/lamarzocco/sensor.py @@ -3,7 +3,7 @@ from collections.abc import Callable from dataclasses import dataclass -from pylamarzocco.const import BoilerType, MachineModel +from pylamarzocco.const import KEYS_PER_MODEL, BoilerType, MachineModel, PhysicalKey from pylamarzocco.devices.machine import LaMarzoccoMachine from homeassistant.components.sensor import ( @@ -21,7 +21,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .coordinator import LaMarzoccoConfigEntry +from .coordinator import LaMarzoccoConfigEntry, LaMarzoccoUpdateCoordinator from .entity import LaMarzoccoEntity, LaMarzoccoEntityDescription, LaMarzoccScaleEntity # Coordinator is used to centralize the data updates @@ -37,6 +37,15 @@ class LaMarzoccoSensorEntityDescription( value_fn: Callable[[LaMarzoccoMachine], float | int] +@dataclass(frozen=True, kw_only=True) +class LaMarzoccoKeySensorEntityDescription( + LaMarzoccoEntityDescription, SensorEntityDescription +): + """Description of a keyed La Marzocco sensor.""" + + value_fn: Callable[[LaMarzoccoMachine, PhysicalKey], int | None] + + ENTITIES: tuple[LaMarzoccoSensorEntityDescription, ...] = ( LaMarzoccoSensorEntityDescription( key="shot_timer", @@ -79,7 +88,6 @@ STATISTIC_ENTITIES: tuple[LaMarzoccoSensorEntityDescription, ...] = ( LaMarzoccoSensorEntityDescription( key="drink_stats_coffee", translation_key="drink_stats_coffee", - native_unit_of_measurement="drinks", state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda device: device.statistics.total_coffee, available_fn=lambda device: len(device.statistics.drink_stats) > 0, @@ -88,7 +96,6 @@ STATISTIC_ENTITIES: tuple[LaMarzoccoSensorEntityDescription, ...] = ( LaMarzoccoSensorEntityDescription( key="drink_stats_flushing", translation_key="drink_stats_flushing", - native_unit_of_measurement="drinks", state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda device: device.statistics.total_flushes, available_fn=lambda device: len(device.statistics.drink_stats) > 0, @@ -96,6 +103,18 @@ STATISTIC_ENTITIES: tuple[LaMarzoccoSensorEntityDescription, ...] = ( ), ) +KEY_STATISTIC_ENTITIES: tuple[LaMarzoccoKeySensorEntityDescription, ...] = ( + LaMarzoccoKeySensorEntityDescription( + key="drink_stats_coffee_key", + translation_key="drink_stats_coffee_key", + state_class=SensorStateClass.TOTAL_INCREASING, + value_fn=lambda device, key: device.statistics.drink_stats.get(key), + available_fn=lambda device: len(device.statistics.drink_stats) > 0, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), +) + SCALE_ENTITIES: tuple[LaMarzoccoSensorEntityDescription, ...] = ( LaMarzoccoSensorEntityDescription( key="scale_battery", @@ -120,6 +139,8 @@ async def async_setup_entry( """Set up sensor entities.""" config_coordinator = entry.runtime_data.config_coordinator + entities: list[LaMarzoccoSensorEntity | LaMarzoccoKeySensorEntity] = [] + entities = [ LaMarzoccoSensorEntity(config_coordinator, description) for description in ENTITIES @@ -142,6 +163,14 @@ async def async_setup_entry( if description.supported_fn(statistics_coordinator) ) + num_keys = KEYS_PER_MODEL[MachineModel(config_coordinator.device.model)] + if num_keys > 0: + entities.extend( + LaMarzoccoKeySensorEntity(statistics_coordinator, description, key) + for description in KEY_STATISTIC_ENTITIES + for key in range(1, num_keys + 1) + ) + def _async_add_new_scale() -> None: async_add_entities( LaMarzoccoScaleSensorEntity(config_coordinator, description) @@ -159,11 +188,36 @@ class LaMarzoccoSensorEntity(LaMarzoccoEntity, SensorEntity): entity_description: LaMarzoccoSensorEntityDescription @property - def native_value(self) -> int | float: + def native_value(self) -> int | float | None: """State of the sensor.""" return self.entity_description.value_fn(self.coordinator.device) +class LaMarzoccoKeySensorEntity(LaMarzoccoEntity, SensorEntity): + """Sensor for a La Marzocco key.""" + + entity_description: LaMarzoccoKeySensorEntityDescription + + def __init__( + self, + coordinator: LaMarzoccoUpdateCoordinator, + description: LaMarzoccoKeySensorEntityDescription, + key: int, + ) -> None: + """Initialize the sensor.""" + super().__init__(coordinator, description) + self.key = key + self._attr_translation_placeholders = {"key": str(key)} + self._attr_unique_id = f"{super()._attr_unique_id}_key{key}" + + @property + def native_value(self) -> int | None: + """State of the sensor.""" + return self.entity_description.value_fn( + self.coordinator.device, PhysicalKey(self.key) + ) + + class LaMarzoccoScaleSensorEntity(LaMarzoccoSensorEntity, LaMarzoccScaleEntity): """Sensor for a La Marzocco scale.""" diff --git a/homeassistant/components/lamarzocco/strings.json b/homeassistant/components/lamarzocco/strings.json index cc96e4615dc..62050685c27 100644 --- a/homeassistant/components/lamarzocco/strings.json +++ b/homeassistant/components/lamarzocco/strings.json @@ -175,10 +175,16 @@ "name": "Current steam temperature" }, "drink_stats_coffee": { - "name": "Total coffees made" + "name": "Total coffees made", + "unit_of_measurement": "coffees" + }, + "drink_stats_coffee_key": { + "name": "Coffees made Key {key}", + "unit_of_measurement": "coffees" }, "drink_stats_flushing": { - "name": "Total flushes made" + "name": "Total flushes made", + "unit_of_measurement": "flushes" }, "shot_timer": { "name": "Shot timer" diff --git a/tests/components/lamarzocco/snapshots/test_sensor.ambr b/tests/components/lamarzocco/snapshots/test_sensor.ambr index 9e2eae482d2..be2b1672cb9 100644 --- a/tests/components/lamarzocco/snapshots/test_sensor.ambr +++ b/tests/components/lamarzocco/snapshots/test_sensor.ambr @@ -50,6 +50,206 @@ 'unit_of_measurement': '%', }) # --- +# name: test_sensors[sensor.gs012345_coffees_made_key_1-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.gs012345_coffees_made_key_1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Coffees made Key 1', + 'platform': 'lamarzocco', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'drink_stats_coffee_key', + 'unique_id': 'GS012345_drink_stats_coffee_key_key1', + 'unit_of_measurement': 'coffees', + }) +# --- +# name: test_sensors[sensor.gs012345_coffees_made_key_1-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'GS012345 Coffees made Key 1', + 'state_class': , + 'unit_of_measurement': 'coffees', + }), + 'context': , + 'entity_id': 'sensor.gs012345_coffees_made_key_1', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '1047', + }) +# --- +# name: test_sensors[sensor.gs012345_coffees_made_key_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.gs012345_coffees_made_key_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Coffees made Key 2', + 'platform': 'lamarzocco', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'drink_stats_coffee_key', + 'unique_id': 'GS012345_drink_stats_coffee_key_key2', + 'unit_of_measurement': 'coffees', + }) +# --- +# name: test_sensors[sensor.gs012345_coffees_made_key_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'GS012345 Coffees made Key 2', + 'state_class': , + 'unit_of_measurement': 'coffees', + }), + 'context': , + 'entity_id': 'sensor.gs012345_coffees_made_key_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '560', + }) +# --- +# name: test_sensors[sensor.gs012345_coffees_made_key_3-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.gs012345_coffees_made_key_3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Coffees made Key 3', + 'platform': 'lamarzocco', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'drink_stats_coffee_key', + 'unique_id': 'GS012345_drink_stats_coffee_key_key3', + 'unit_of_measurement': 'coffees', + }) +# --- +# name: test_sensors[sensor.gs012345_coffees_made_key_3-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'GS012345 Coffees made Key 3', + 'state_class': , + 'unit_of_measurement': 'coffees', + }), + 'context': , + 'entity_id': 'sensor.gs012345_coffees_made_key_3', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '468', + }) +# --- +# name: test_sensors[sensor.gs012345_coffees_made_key_4-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.gs012345_coffees_made_key_4', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Coffees made Key 4', + 'platform': 'lamarzocco', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'drink_stats_coffee_key', + 'unique_id': 'GS012345_drink_stats_coffee_key_key4', + 'unit_of_measurement': 'coffees', + }) +# --- +# name: test_sensors[sensor.gs012345_coffees_made_key_4-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'GS012345 Coffees made Key 4', + 'state_class': , + 'unit_of_measurement': 'coffees', + }), + 'context': , + 'entity_id': 'sensor.gs012345_coffees_made_key_4', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '312', + }) +# --- # name: test_sensors[sensor.gs012345_current_coffee_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -241,7 +441,7 @@ 'supported_features': 0, 'translation_key': 'drink_stats_coffee', 'unique_id': 'GS012345_drink_stats_coffee', - 'unit_of_measurement': 'drinks', + 'unit_of_measurement': 'coffees', }) # --- # name: test_sensors[sensor.gs012345_total_coffees_made-state] @@ -249,7 +449,7 @@ 'attributes': ReadOnlyDict({ 'friendly_name': 'GS012345 Total coffees made', 'state_class': , - 'unit_of_measurement': 'drinks', + 'unit_of_measurement': 'coffees', }), 'context': , 'entity_id': 'sensor.gs012345_total_coffees_made', @@ -291,7 +491,7 @@ 'supported_features': 0, 'translation_key': 'drink_stats_flushing', 'unique_id': 'GS012345_drink_stats_flushing', - 'unit_of_measurement': 'drinks', + 'unit_of_measurement': 'flushes', }) # --- # name: test_sensors[sensor.gs012345_total_flushes_made-state] @@ -299,7 +499,7 @@ 'attributes': ReadOnlyDict({ 'friendly_name': 'GS012345 Total flushes made', 'state_class': , - 'unit_of_measurement': 'drinks', + 'unit_of_measurement': 'flushes', }), 'context': , 'entity_id': 'sensor.gs012345_total_flushes_made', diff --git a/tests/components/lamarzocco/test_sensor.py b/tests/components/lamarzocco/test_sensor.py index 3385e2b3891..43a0826d551 100644 --- a/tests/components/lamarzocco/test_sensor.py +++ b/tests/components/lamarzocco/test_sensor.py @@ -18,6 +18,7 @@ from . import async_init_integration from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform +@pytest.mark.usefixtures("entity_registry_enabled_by_default") async def test_sensors( hass: HomeAssistant, mock_lamarzocco: MagicMock,