Add informative header to ZHA update entity release notes (#130099)

This commit is contained in:
puddly 2024-11-21 04:17:44 -05:00 committed by GitHub
parent 27695095dd
commit 51e592f450
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 89 additions and 33 deletions

View File

@ -36,6 +36,18 @@ from .helpers import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
OTA_MESSAGE_BATTERY_POWERED = (
"Battery powered devices can sometimes take multiple hours to update and you may"
" need to wake the device for the update to begin."
)
ZHA_DOCS_NETWORK_RELIABILITY = "https://www.home-assistant.io/integrations/zha/#zigbee-interference-avoidance-and-network-rangecoverage-optimization"
OTA_MESSAGE_RELIABILITY = (
"If you are having issues updating a specific device, make sure that you've"
f" eliminated [common environmental issues]({ZHA_DOCS_NETWORK_RELIABILITY}) that"
" could be affecting network reliability. OTA updates require a reliable network."
)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
@ -149,7 +161,21 @@ class ZHAFirmwareUpdateEntity(
This is suitable for a long changelog that does not fit in the release_summary This is suitable for a long changelog that does not fit in the release_summary
property. The returned string can contain markdown. property. The returned string can contain markdown.
""" """
return self.entity_data.entity.release_notes
if self.entity_data.device_proxy.device.is_mains_powered:
header = (
"<ha-alert alert-type='info'>"
f"{OTA_MESSAGE_RELIABILITY}"
"</ha-alert>"
)
else:
header = (
"<ha-alert alert-type='info'>"
f"{OTA_MESSAGE_BATTERY_POWERED} {OTA_MESSAGE_RELIABILITY}"
"</ha-alert>"
)
return f"{header}\n\n{self.entity_data.entity.release_notes or ''}"
@property @property
def release_url(self) -> str | None: def release_url(self) -> str | None:

View File

@ -1,6 +1,6 @@
"""Test ZHA firmware updates.""" """Test ZHA firmware updates."""
from unittest.mock import AsyncMock, call, patch from unittest.mock import AsyncMock, PropertyMock, call, patch
import pytest import pytest
from zha.application.platforms.update import ( from zha.application.platforms.update import (
@ -14,6 +14,7 @@ from zigpy.profiles import zha
import zigpy.types as t import zigpy.types as t
from zigpy.zcl import foundation from zigpy.zcl import foundation
from zigpy.zcl.clusters import general from zigpy.zcl.clusters import general
import zigpy.zdo.types as zdo_t
from homeassistant.components.homeassistant import ( from homeassistant.components.homeassistant import (
DOMAIN as HA_DOMAIN, DOMAIN as HA_DOMAIN,
@ -33,6 +34,10 @@ from homeassistant.components.zha.helpers import (
get_zha_gateway, get_zha_gateway,
get_zha_gateway_proxy, get_zha_gateway_proxy,
) )
from homeassistant.components.zha.update import (
OTA_MESSAGE_BATTERY_POWERED,
OTA_MESSAGE_RELIABILITY,
)
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
STATE_OFF, STATE_OFF,
@ -84,7 +89,26 @@ async def setup_test_data(
SIG_EP_PROFILE: zha.PROFILE_ID, SIG_EP_PROFILE: zha.PROFILE_ID,
} }
}, },
node_descriptor=b"\x02@\x84_\x11\x7fd\x00\x00,d\x00\x00", node_descriptor=zdo_t.NodeDescriptor(
logical_type=zdo_t.LogicalType.Router,
complex_descriptor_available=0,
user_descriptor_available=0,
reserved=0,
aps_flags=0,
frequency_band=zdo_t.NodeDescriptor.FrequencyBand.Freq2400MHz,
mac_capability_flags=(
zdo_t.NodeDescriptor.MACCapabilityFlags.FullFunctionDevice
| zdo_t.NodeDescriptor.MACCapabilityFlags.MainsPowered
| zdo_t.NodeDescriptor.MACCapabilityFlags.RxOnWhenIdle
| zdo_t.NodeDescriptor.MACCapabilityFlags.AllocateAddress
),
manufacturer_code=4107,
maximum_buffer_size=82,
maximum_incoming_transfer_size=128,
server_mask=11264,
maximum_outgoing_transfer_size=128,
descriptor_capability_field=zdo_t.NodeDescriptor.DescriptorCapability.NONE,
).serialize(),
) )
gateway.get_or_create_device(zigpy_device) gateway.get_or_create_device(zigpy_device)
@ -568,27 +592,8 @@ async def test_update_release_notes(
) -> None: ) -> None:
"""Test ZHA update platform release notes.""" """Test ZHA update platform release notes."""
await setup_zha() await setup_zha()
zha_device, _, _, _ = await setup_test_data(hass, zigpy_device_mock)
gateway = get_zha_gateway(hass)
gateway_proxy: ZHAGatewayProxy = get_zha_gateway_proxy(hass)
zigpy_device = zigpy_device_mock(
{
1: {
SIG_EP_INPUT: [general.Basic.cluster_id, general.OnOff.cluster_id],
SIG_EP_OUTPUT: [general.Ota.cluster_id],
SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH,
SIG_EP_PROFILE: zha.PROFILE_ID,
}
},
node_descriptor=b"\x02@\x84_\x11\x7fd\x00\x00,d\x00\x00",
)
gateway.get_or_create_device(zigpy_device)
await gateway.async_device_initialized(zigpy_device)
await hass.async_block_till_done(wait_background_tasks=True)
zha_device: ZHADeviceProxy = gateway_proxy.get_device_proxy(zigpy_device.ieee)
zha_lib_entity = next( zha_lib_entity = next(
e e
for e in zha_device.device.platform_entities.values() for e in zha_device.device.platform_entities.values()
@ -602,14 +607,39 @@ async def test_update_release_notes(
assert entity_id is not None assert entity_id is not None
ws_client = await hass_ws_client(hass) ws_client = await hass_ws_client(hass)
await ws_client.send_json(
{
"id": 1,
"type": "update/release_notes",
"entity_id": entity_id,
}
)
result = await ws_client.receive_json() # Mains-powered devices
assert result["success"] is True with patch(
assert result["result"] == "Some lengthy release notes" "zha.zigbee.device.Device.is_mains_powered", PropertyMock(return_value=True)
):
await ws_client.send_json(
{
"id": 1,
"type": "update/release_notes",
"entity_id": entity_id,
}
)
result = await ws_client.receive_json()
assert result["success"] is True
assert "Some lengthy release notes" in result["result"]
assert OTA_MESSAGE_RELIABILITY in result["result"]
assert OTA_MESSAGE_BATTERY_POWERED not in result["result"]
# Battery-powered devices
with patch(
"zha.zigbee.device.Device.is_mains_powered", PropertyMock(return_value=False)
):
await ws_client.send_json(
{
"id": 2,
"type": "update/release_notes",
"entity_id": entity_id,
}
)
result = await ws_client.receive_json()
assert result["success"] is True
assert "Some lengthy release notes" in result["result"]
assert OTA_MESSAGE_RELIABILITY in result["result"]
assert OTA_MESSAGE_BATTERY_POWERED in result["result"]