mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 01:37:08 +00:00
Add update platform to La Marzocco (#108235)
* add update * requested changes * improve * docstring * docstring
This commit is contained in:
parent
3eb1283fa5
commit
ee44e9d4d6
@ -13,6 +13,7 @@ PLATFORMS = [
|
||||
Platform.SELECT,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
Platform.UPDATE,
|
||||
]
|
||||
|
||||
|
||||
|
@ -104,6 +104,14 @@
|
||||
"steam_boiler": {
|
||||
"name": "Steam boiler"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"machine_firmware": {
|
||||
"name": "Machine firmware"
|
||||
},
|
||||
"gateway_firmware": {
|
||||
"name": "Gateway firmware"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
105
homeassistant/components/lamarzocco/update.py
Normal file
105
homeassistant/components/lamarzocco/update.py
Normal file
@ -0,0 +1,105 @@
|
||||
"""Support for La Marzocco update entities."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from lmcloud import LMCloud as LaMarzoccoClient
|
||||
from lmcloud.const import LaMarzoccoUpdateableComponent
|
||||
|
||||
from homeassistant.components.update import (
|
||||
UpdateDeviceClass,
|
||||
UpdateEntity,
|
||||
UpdateEntityDescription,
|
||||
UpdateEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .entity import LaMarzoccoEntity, LaMarzoccoEntityDescription
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class LaMarzoccoUpdateEntityDescription(
|
||||
LaMarzoccoEntityDescription,
|
||||
UpdateEntityDescription,
|
||||
):
|
||||
"""Description of a La Marzocco update entities."""
|
||||
|
||||
current_fw_fn: Callable[[LaMarzoccoClient], str]
|
||||
latest_fw_fn: Callable[[LaMarzoccoClient], str]
|
||||
component: LaMarzoccoUpdateableComponent
|
||||
|
||||
|
||||
ENTITIES: tuple[LaMarzoccoUpdateEntityDescription, ...] = (
|
||||
LaMarzoccoUpdateEntityDescription(
|
||||
key="machine_firmware",
|
||||
translation_key="machine_firmware",
|
||||
device_class=UpdateDeviceClass.FIRMWARE,
|
||||
icon="mdi:cloud-download",
|
||||
current_fw_fn=lambda lm: lm.firmware_version,
|
||||
latest_fw_fn=lambda lm: lm.latest_firmware_version,
|
||||
component=LaMarzoccoUpdateableComponent.MACHINE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
LaMarzoccoUpdateEntityDescription(
|
||||
key="gateway_firmware",
|
||||
translation_key="gateway_firmware",
|
||||
device_class=UpdateDeviceClass.FIRMWARE,
|
||||
icon="mdi:cloud-download",
|
||||
current_fw_fn=lambda lm: lm.gateway_version,
|
||||
latest_fw_fn=lambda lm: lm.latest_gateway_version,
|
||||
component=LaMarzoccoUpdateableComponent.GATEWAY,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Create update entities."""
|
||||
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
async_add_entities(
|
||||
LaMarzoccoUpdateEntity(coordinator, description)
|
||||
for description in ENTITIES
|
||||
if description.supported_fn(coordinator)
|
||||
)
|
||||
|
||||
|
||||
class LaMarzoccoUpdateEntity(LaMarzoccoEntity, UpdateEntity):
|
||||
"""Entity representing the update state."""
|
||||
|
||||
entity_description: LaMarzoccoUpdateEntityDescription
|
||||
_attr_supported_features = UpdateEntityFeature.INSTALL
|
||||
|
||||
@property
|
||||
def installed_version(self) -> str | None:
|
||||
"""Return the current firmware version."""
|
||||
return self.entity_description.current_fw_fn(self.coordinator.lm)
|
||||
|
||||
@property
|
||||
def latest_version(self) -> str:
|
||||
"""Return the latest firmware version."""
|
||||
return self.entity_description.latest_fw_fn(self.coordinator.lm)
|
||||
|
||||
async def async_install(
|
||||
self, version: str | None, backup: bool, **kwargs: Any
|
||||
) -> None:
|
||||
"""Install an update."""
|
||||
self._attr_in_progress = True
|
||||
self.async_write_ha_state()
|
||||
success = await self.coordinator.lm.update_firmware(
|
||||
self.entity_description.component
|
||||
)
|
||||
if not success:
|
||||
raise HomeAssistantError("Update failed")
|
||||
self._attr_in_progress = False
|
||||
self.async_write_ha_state()
|
@ -87,9 +87,10 @@ def mock_lamarzocco(
|
||||
lamarzocco.serial_number = serial_number
|
||||
|
||||
lamarzocco.firmware_version = "1.1"
|
||||
lamarzocco.latest_firmware_version = "1.1"
|
||||
lamarzocco.latest_firmware_version = "1.2"
|
||||
lamarzocco.gateway_version = "v2.2-rc0"
|
||||
lamarzocco.latest_gateway_version = "v3.1-rc4"
|
||||
lamarzocco.update_firmware.return_value = True
|
||||
|
||||
lamarzocco.current_status = load_json_object_fixture(
|
||||
"current_status.json", DOMAIN
|
||||
|
111
tests/components/lamarzocco/snapshots/test_update.ambr
Normal file
111
tests/components/lamarzocco/snapshots/test_update.ambr
Normal file
@ -0,0 +1,111 @@
|
||||
# serializer version: 1
|
||||
# name: test_update_entites[gateway_firmware-gateway]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'auto_update': False,
|
||||
'device_class': 'firmware',
|
||||
'entity_picture': 'https://brands.home-assistant.io/_/lamarzocco/icon.png',
|
||||
'friendly_name': 'GS01234 Gateway firmware',
|
||||
'icon': 'mdi:cloud-download',
|
||||
'in_progress': False,
|
||||
'installed_version': 'v2.2-rc0',
|
||||
'latest_version': 'v3.1-rc4',
|
||||
'release_summary': None,
|
||||
'release_url': None,
|
||||
'skipped_version': None,
|
||||
'supported_features': <UpdateEntityFeature: 1>,
|
||||
'title': None,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'update.gs01234_gateway_firmware',
|
||||
'last_changed': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_update_entites[gateway_firmware-gateway].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'update',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'update.gs01234_gateway_firmware',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <UpdateDeviceClass.FIRMWARE: 'firmware'>,
|
||||
'original_icon': 'mdi:cloud-download',
|
||||
'original_name': 'Gateway firmware',
|
||||
'platform': 'lamarzocco',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <UpdateEntityFeature: 1>,
|
||||
'translation_key': 'gateway_firmware',
|
||||
'unique_id': 'GS01234_gateway_firmware',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_update_entites[machine_firmware-machine]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'auto_update': False,
|
||||
'device_class': 'firmware',
|
||||
'entity_picture': 'https://brands.home-assistant.io/_/lamarzocco/icon.png',
|
||||
'friendly_name': 'GS01234 Machine firmware',
|
||||
'icon': 'mdi:cloud-download',
|
||||
'in_progress': False,
|
||||
'installed_version': '1.1',
|
||||
'latest_version': '1.2',
|
||||
'release_summary': None,
|
||||
'release_url': None,
|
||||
'skipped_version': None,
|
||||
'supported_features': <UpdateEntityFeature: 1>,
|
||||
'title': None,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'update.gs01234_machine_firmware',
|
||||
'last_changed': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_update_entites[machine_firmware-machine].1
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'update',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'update.gs01234_machine_firmware',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <UpdateDeviceClass.FIRMWARE: 'firmware'>,
|
||||
'original_icon': 'mdi:cloud-download',
|
||||
'original_name': 'Machine firmware',
|
||||
'platform': 'lamarzocco',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <UpdateEntityFeature: 1>,
|
||||
'translation_key': 'machine_firmware',
|
||||
'unique_id': 'GS01234_machine_firmware',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
76
tests/components/lamarzocco/test_update.py
Normal file
76
tests/components/lamarzocco/test_update.py
Normal file
@ -0,0 +1,76 @@
|
||||
"""Tests for the La Marzocco Update Entities."""
|
||||
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from lmcloud.const import LaMarzoccoUpdateableComponent
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.update import DOMAIN as UPDATE_DOMAIN, SERVICE_INSTALL
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("init_integration")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity_name", "component"),
|
||||
[
|
||||
("machine_firmware", LaMarzoccoUpdateableComponent.MACHINE),
|
||||
("gateway_firmware", LaMarzoccoUpdateableComponent.GATEWAY),
|
||||
],
|
||||
)
|
||||
async def test_update_entites(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_name: str,
|
||||
component: LaMarzoccoUpdateableComponent,
|
||||
) -> None:
|
||||
"""Test the La Marzocco update entities."""
|
||||
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
|
||||
state = hass.states.get(f"update.{serial_number}_{entity_name}")
|
||||
assert state
|
||||
assert state == snapshot
|
||||
|
||||
entry = entity_registry.async_get(state.entity_id)
|
||||
assert entry
|
||||
assert entry == snapshot
|
||||
|
||||
await hass.services.async_call(
|
||||
UPDATE_DOMAIN,
|
||||
SERVICE_INSTALL,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"update.{serial_number}_{entity_name}",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_lamarzocco.update_firmware.assert_called_once_with(component)
|
||||
|
||||
|
||||
async def test_update_error(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
) -> None:
|
||||
"""Test error during update."""
|
||||
state = hass.states.get(f"update.{mock_lamarzocco.serial_number}_machine_firmware")
|
||||
assert state
|
||||
|
||||
mock_lamarzocco.update_firmware.return_value = False
|
||||
|
||||
with pytest.raises(HomeAssistantError, match="Update failed"):
|
||||
await hass.services.async_call(
|
||||
UPDATE_DOMAIN,
|
||||
SERVICE_INSTALL,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"update.{mock_lamarzocco.serial_number}_machine_firmware",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user