mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 12:47:08 +00:00
Fix update platform for Shelly gen1 devices (#124798)
This commit is contained in:
parent
1fcfe9e135
commit
99a65d3098
@ -9,6 +9,7 @@ from typing import Any, Final, cast
|
|||||||
|
|
||||||
from aioshelly.const import RPC_GENERATIONS
|
from aioshelly.const import RPC_GENERATIONS
|
||||||
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError
|
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError
|
||||||
|
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy
|
||||||
|
|
||||||
from homeassistant.components.update import (
|
from homeassistant.components.update import (
|
||||||
ATTR_INSTALLED_VERSION,
|
ATTR_INSTALLED_VERSION,
|
||||||
@ -203,6 +204,22 @@ class RestUpdateEntity(ShellyRestAttributeEntity, UpdateEntity):
|
|||||||
else:
|
else:
|
||||||
LOGGER.debug("Result of OTA update call: %s", result)
|
LOGGER.debug("Result of OTA update call: %s", result)
|
||||||
|
|
||||||
|
def version_is_newer(self, latest_version: str, installed_version: str) -> bool:
|
||||||
|
"""Return True if available version is newer then installed version.
|
||||||
|
|
||||||
|
Default strategy generate an exception with Shelly firmware format
|
||||||
|
thus making the entity state always true.
|
||||||
|
"""
|
||||||
|
return AwesomeVersion(
|
||||||
|
latest_version,
|
||||||
|
find_first_match=True,
|
||||||
|
ensure_strategy=[AwesomeVersionStrategy.SEMVER],
|
||||||
|
) > AwesomeVersion(
|
||||||
|
installed_version,
|
||||||
|
find_first_match=True,
|
||||||
|
ensure_strategy=[AwesomeVersionStrategy.SEMVER],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RpcUpdateEntity(ShellyRpcAttributeEntity, UpdateEntity):
|
class RpcUpdateEntity(ShellyRpcAttributeEntity, UpdateEntity):
|
||||||
"""Represent a RPC update entity."""
|
"""Represent a RPC update entity."""
|
||||||
|
@ -226,9 +226,9 @@ MOCK_STATUS_COAP = {
|
|||||||
"update": {
|
"update": {
|
||||||
"status": "pending",
|
"status": "pending",
|
||||||
"has_update": True,
|
"has_update": True,
|
||||||
"beta_version": "some_beta_version",
|
"beta_version": "20231107-162609/v1.14.1-rc1-g0617c15",
|
||||||
"new_version": "some_new_version",
|
"new_version": "20230913-111730/v1.14.0-gcb84623",
|
||||||
"old_version": "some_old_version",
|
"old_version": "20230913-111730/v1.14.0-gcb84623",
|
||||||
},
|
},
|
||||||
"uptime": 5 * REST_SENSORS_UPDATE_INTERVAL,
|
"uptime": 5 * REST_SENSORS_UPDATE_INTERVAL,
|
||||||
"wifi_sta": {"rssi": -64},
|
"wifi_sta": {"rssi": -64},
|
||||||
|
@ -54,15 +54,15 @@ async def test_block_update(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test block device update entity."""
|
"""Test block device update entity."""
|
||||||
entity_id = "update.test_name_firmware_update"
|
entity_id = "update.test_name_firmware_update"
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "1")
|
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "1.0.0")
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "new_version", "2")
|
monkeypatch.setitem(mock_block_device.status["update"], "new_version", "2.0.0")
|
||||||
monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": False})
|
monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": False})
|
||||||
await init_integration(hass, 1)
|
await init_integration(hass, 1)
|
||||||
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert state.attributes[ATTR_INSTALLED_VERSION] == "1"
|
assert state.attributes[ATTR_INSTALLED_VERSION] == "1.0.0"
|
||||||
assert state.attributes[ATTR_LATEST_VERSION] == "2"
|
assert state.attributes[ATTR_LATEST_VERSION] == "2.0.0"
|
||||||
assert state.attributes[ATTR_IN_PROGRESS] is False
|
assert state.attributes[ATTR_IN_PROGRESS] is False
|
||||||
supported_feat = state.attributes[ATTR_SUPPORTED_FEATURES]
|
supported_feat = state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
assert supported_feat == UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS
|
assert supported_feat == UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS
|
||||||
@ -77,18 +77,18 @@ async def test_block_update(
|
|||||||
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert state.attributes[ATTR_INSTALLED_VERSION] == "1"
|
assert state.attributes[ATTR_INSTALLED_VERSION] == "1.0.0"
|
||||||
assert state.attributes[ATTR_LATEST_VERSION] == "2"
|
assert state.attributes[ATTR_LATEST_VERSION] == "2.0.0"
|
||||||
assert state.attributes[ATTR_IN_PROGRESS] is True
|
assert state.attributes[ATTR_IN_PROGRESS] is True
|
||||||
assert state.attributes[ATTR_RELEASE_URL] == GEN1_RELEASE_URL
|
assert state.attributes[ATTR_RELEASE_URL] == GEN1_RELEASE_URL
|
||||||
|
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "2")
|
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "2.0.0")
|
||||||
await mock_rest_update(hass, freezer)
|
await mock_rest_update(hass, freezer)
|
||||||
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
assert state.attributes[ATTR_INSTALLED_VERSION] == "2"
|
assert state.attributes[ATTR_INSTALLED_VERSION] == "2.0.0"
|
||||||
assert state.attributes[ATTR_LATEST_VERSION] == "2"
|
assert state.attributes[ATTR_LATEST_VERSION] == "2.0.0"
|
||||||
assert state.attributes[ATTR_IN_PROGRESS] is False
|
assert state.attributes[ATTR_IN_PROGRESS] is False
|
||||||
|
|
||||||
entry = entity_registry.async_get(entity_id)
|
entry = entity_registry.async_get(entity_id)
|
||||||
@ -106,25 +106,27 @@ async def test_block_beta_update(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test block device beta update entity."""
|
"""Test block device beta update entity."""
|
||||||
entity_id = "update.test_name_beta_firmware_update"
|
entity_id = "update.test_name_beta_firmware_update"
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "1")
|
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "1.0.0")
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "new_version", "2")
|
monkeypatch.setitem(mock_block_device.status["update"], "new_version", "2.0.0")
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "beta_version", "")
|
monkeypatch.setitem(mock_block_device.status["update"], "beta_version", "")
|
||||||
monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": False})
|
monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": False})
|
||||||
await init_integration(hass, 1)
|
await init_integration(hass, 1)
|
||||||
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
assert state.attributes[ATTR_INSTALLED_VERSION] == "1"
|
assert state.attributes[ATTR_INSTALLED_VERSION] == "1.0.0"
|
||||||
assert state.attributes[ATTR_LATEST_VERSION] == "1"
|
assert state.attributes[ATTR_LATEST_VERSION] == "1.0.0"
|
||||||
assert state.attributes[ATTR_IN_PROGRESS] is False
|
assert state.attributes[ATTR_IN_PROGRESS] is False
|
||||||
|
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "beta_version", "2b")
|
monkeypatch.setitem(
|
||||||
|
mock_block_device.status["update"], "beta_version", "2.0.0-beta"
|
||||||
|
)
|
||||||
await mock_rest_update(hass, freezer)
|
await mock_rest_update(hass, freezer)
|
||||||
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert state.attributes[ATTR_INSTALLED_VERSION] == "1"
|
assert state.attributes[ATTR_INSTALLED_VERSION] == "1.0.0"
|
||||||
assert state.attributes[ATTR_LATEST_VERSION] == "2b"
|
assert state.attributes[ATTR_LATEST_VERSION] == "2.0.0-beta"
|
||||||
assert state.attributes[ATTR_IN_PROGRESS] is False
|
assert state.attributes[ATTR_IN_PROGRESS] is False
|
||||||
assert state.attributes[ATTR_RELEASE_URL] is None
|
assert state.attributes[ATTR_RELEASE_URL] is None
|
||||||
|
|
||||||
@ -138,17 +140,17 @@ async def test_block_beta_update(
|
|||||||
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert state.attributes[ATTR_INSTALLED_VERSION] == "1"
|
assert state.attributes[ATTR_INSTALLED_VERSION] == "1.0.0"
|
||||||
assert state.attributes[ATTR_LATEST_VERSION] == "2b"
|
assert state.attributes[ATTR_LATEST_VERSION] == "2.0.0-beta"
|
||||||
assert state.attributes[ATTR_IN_PROGRESS] is True
|
assert state.attributes[ATTR_IN_PROGRESS] is True
|
||||||
|
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "2b")
|
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "2.0.0-beta")
|
||||||
await mock_rest_update(hass, freezer)
|
await mock_rest_update(hass, freezer)
|
||||||
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
assert state.attributes[ATTR_INSTALLED_VERSION] == "2b"
|
assert state.attributes[ATTR_INSTALLED_VERSION] == "2.0.0-beta"
|
||||||
assert state.attributes[ATTR_LATEST_VERSION] == "2b"
|
assert state.attributes[ATTR_LATEST_VERSION] == "2.0.0-beta"
|
||||||
assert state.attributes[ATTR_IN_PROGRESS] is False
|
assert state.attributes[ATTR_IN_PROGRESS] is False
|
||||||
|
|
||||||
entry = entity_registry.async_get(entity_id)
|
entry = entity_registry.async_get(entity_id)
|
||||||
@ -164,8 +166,8 @@ async def test_block_update_connection_error(
|
|||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test block device update connection error."""
|
"""Test block device update connection error."""
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "1")
|
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "1.0.0")
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "new_version", "2")
|
monkeypatch.setitem(mock_block_device.status["update"], "new_version", "2.0.0")
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
mock_block_device,
|
mock_block_device,
|
||||||
"trigger_ota_update",
|
"trigger_ota_update",
|
||||||
@ -190,8 +192,8 @@ async def test_block_update_auth_error(
|
|||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test block device update authentication error."""
|
"""Test block device update authentication error."""
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "1")
|
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "1.0.0")
|
||||||
monkeypatch.setitem(mock_block_device.status["update"], "new_version", "2")
|
monkeypatch.setitem(mock_block_device.status["update"], "new_version", "2.0.0")
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
mock_block_device,
|
mock_block_device,
|
||||||
"trigger_ota_update",
|
"trigger_ota_update",
|
||||||
@ -222,6 +224,51 @@ async def test_block_update_auth_error(
|
|||||||
assert flow["context"].get("entry_id") == entry.entry_id
|
assert flow["context"].get("entry_id") == entry.entry_id
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
|
async def test_block_version_compare(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
mock_block_device: Mock,
|
||||||
|
entity_registry: EntityRegistry,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
"""Test block device custom firmware version comparison."""
|
||||||
|
|
||||||
|
STABLE = "20230913-111730/v1.14.0-gcb84623"
|
||||||
|
BETA = "20231107-162609/v1.14.1-rc1-g0617c15"
|
||||||
|
|
||||||
|
entity_id_beta = "update.test_name_beta_firmware_update"
|
||||||
|
entity_id_latest = "update.test_name_firmware_update"
|
||||||
|
monkeypatch.setitem(mock_block_device.status["update"], "old_version", STABLE)
|
||||||
|
monkeypatch.setitem(mock_block_device.status["update"], "new_version", "")
|
||||||
|
monkeypatch.setitem(mock_block_device.status["update"], "beta_version", BETA)
|
||||||
|
monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": False})
|
||||||
|
await init_integration(hass, 1)
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id_latest)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
assert state.attributes[ATTR_INSTALLED_VERSION] == STABLE
|
||||||
|
assert state.attributes[ATTR_LATEST_VERSION] == STABLE
|
||||||
|
state = hass.states.get(entity_id_beta)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes[ATTR_INSTALLED_VERSION] == STABLE
|
||||||
|
assert state.attributes[ATTR_LATEST_VERSION] == BETA
|
||||||
|
|
||||||
|
monkeypatch.setitem(mock_block_device.status["update"], "old_version", BETA)
|
||||||
|
monkeypatch.setitem(mock_block_device.status["update"], "new_version", STABLE)
|
||||||
|
monkeypatch.setitem(mock_block_device.status["update"], "beta_version", BETA)
|
||||||
|
await mock_rest_update(hass, freezer)
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id_latest)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
assert state.attributes[ATTR_INSTALLED_VERSION] == BETA
|
||||||
|
assert state.attributes[ATTR_LATEST_VERSION] == STABLE
|
||||||
|
state = hass.states.get(entity_id_beta)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
assert state.attributes[ATTR_INSTALLED_VERSION] == BETA
|
||||||
|
assert state.attributes[ATTR_LATEST_VERSION] == BETA
|
||||||
|
|
||||||
|
|
||||||
async def test_rpc_update(
|
async def test_rpc_update(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_rpc_device: Mock,
|
mock_rpc_device: Mock,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user