Skip to content

Commit 563a50b

Browse files
committed
Move almost all update logic into ExternalOtaProvider
Most update logic is related to the external OTA provider (like commissioning and configuring it). This commit moves most of the update logic into the ExternalOtaProvider class.
1 parent 00564c0 commit 563a50b

File tree

3 files changed

+217
-124
lines changed

3 files changed

+217
-124
lines changed

matter_server/server/device_controller.py

+27-116
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@
1616
import time
1717
from typing import TYPE_CHECKING, Any, cast
1818

19-
from chip.clusters import Attribute, Objects as Clusters, Types
19+
from chip.clusters import Attribute, Objects as Clusters
2020
from chip.clusters.Attribute import ValueDecodeFailure
2121
from chip.clusters.ClusterObjects import ALL_ATTRIBUTES, ALL_CLUSTERS, Cluster
2222
from chip.discovery import DiscoveryType
2323
from chip.exceptions import ChipStackError
24-
from chip.interaction_model import Status
2524
from zeroconf import BadTypeInNameException, IPVersion, ServiceStateChange, Zeroconf
2625
from zeroconf.asyncio import AsyncServiceBrowser, AsyncServiceInfo, AsyncZeroconf
2726

@@ -112,6 +111,12 @@
112111
0, Clusters.BasicInformation.Attributes.SoftwareVersionString
113112
)
114113
)
114+
OTA_SOFTWARE_UPDATE_REQUESTOR_UPDATE_STATE_ATTRIBUTE_PATH = (
115+
create_attribute_path_from_attribute(
116+
0, Clusters.OtaSoftwareUpdateRequestor.Attributes.UpdateState
117+
)
118+
)
119+
115120

116121
# pylint: disable=too-many-lines,too-many-instance-attributes,too-many-public-methods
117122

@@ -891,83 +896,6 @@ async def check_node_update(self, node_id: int) -> dict | None:
891896

892897
return await self._check_node_update(node_id)
893898

894-
async def _initialize_ota_provider(self, ota_provider: ExternalOtaProvider) -> None:
895-
"""Commissions the OTA Provider."""
896-
897-
if self.chip_controller is None:
898-
raise RuntimeError("Device Controller not initialized.")
899-
900-
# The OTA Provider has not been commissioned yet, let's do it now.
901-
LOGGER.info("Commissioning the built-in OTA Provider App.")
902-
try:
903-
ota_provider_node = await self.commission_on_network(
904-
ota_provider.get_passcode(),
905-
# TODO: Filtering by long discriminator seems broken
906-
# filter_type=FilterType.LONG_DISCRIMINATOR,
907-
# filter=ota_provider.get_descriminator(),
908-
)
909-
ota_provider_node_id = ota_provider_node.node_id
910-
except NodeCommissionFailed:
911-
LOGGER.error("Failed to commission OTA Provider App!")
912-
return
913-
914-
LOGGER.info(
915-
"OTA Provider App commissioned with node id %d.",
916-
ota_provider_node_id,
917-
)
918-
919-
# Adjust ACL of OTA Requestor such that Node peer-to-peer communication
920-
# is allowed.
921-
try:
922-
read_result = await self.chip_controller.ReadAttribute(
923-
ota_provider_node_id, [(0, Clusters.AccessControl.Attributes.Acl)]
924-
)
925-
acl_list = cast(
926-
list,
927-
read_result[0][Clusters.AccessControl][
928-
Clusters.AccessControl.Attributes.Acl
929-
],
930-
)
931-
932-
# Add new ACL entry...
933-
acl_list.append(
934-
Clusters.AccessControl.Structs.AccessControlEntryStruct(
935-
fabricIndex=1,
936-
privilege=Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kOperate,
937-
authMode=Clusters.AccessControl.Enums.AccessControlEntryAuthModeEnum.kCase,
938-
subjects=Types.NullValue,
939-
targets=[
940-
Clusters.AccessControl.Structs.AccessControlTargetStruct(
941-
cluster=Clusters.OtaSoftwareUpdateProvider.id,
942-
endpoint=0,
943-
deviceType=Types.NullValue,
944-
)
945-
],
946-
)
947-
)
948-
949-
# And write. This is persistent, so only need to be done after we commissioned
950-
# the OTA Provider App.
951-
write_result: Attribute.AttributeWriteResult = (
952-
await self.chip_controller.WriteAttribute(
953-
ota_provider_node_id,
954-
[(0, Clusters.AccessControl.Attributes.Acl(acl_list))],
955-
)
956-
)
957-
if write_result[0].Status != Status.Success:
958-
logging.error(
959-
"Failed writing adjusted OTA Provider App ACL: Status %s.",
960-
str(write_result[0].Status),
961-
)
962-
await self.remove_node(ota_provider_node_id)
963-
raise UpdateError("Error while setting up OTA Provider.")
964-
except ChipStackError as ex:
965-
logging.exception("Failed adjusting OTA Provider App ACL.", exc_info=ex)
966-
await self.remove_node(ota_provider_node_id)
967-
raise UpdateError("Error while setting up OTA Provider.") from ex
968-
969-
ota_provider.set_node_id(ota_provider_node_id)
970-
971899
@api_command(APICommand.UPDATE_NODE)
972900
async def update_node(self, node_id: int, software_version: int) -> dict | None:
973901
"""
@@ -989,48 +917,21 @@ async def update_node(self, node_id: int, software_version: int) -> dict | None:
989917
raise RuntimeError("Device Controller not initialized.")
990918

991919
if not self._ota_provider:
992-
raise UpdateError("No OTA provider found, updates not possible.")
920+
raise UpdateError("No OTA provider found, updates not possible")
921+
922+
if self._ota_provider.is_busy():
923+
raise UpdateError(
924+
"No OTA provider currently busy, updates currently not possible"
925+
)
993926

994927
# Add update to the OTA provider
995928
await self._ota_provider.download_update(update)
996929

997-
ota_provider_node_id = self._ota_provider.get_node_id()
998-
if ota_provider_node_id is None:
999-
LOGGER.info("Initializing OTA Provider")
1000-
elif ota_provider_node_id not in self._nodes:
1001-
LOGGER.warning(
1002-
"OTA Provider node id %d no longer exists! Resetting...",
1003-
ota_provider_node_id,
1004-
)
1005-
await self._ota_provider.reset()
1006-
ota_provider_node_id = None
1007-
1008930
# Make sure any previous instances get stopped
1009-
await self._ota_provider.stop()
1010-
await self._ota_provider.start()
1011-
1012-
# Wait for OTA provider to be ready
1013-
# TODO: Detect when OTA provider is ready
1014-
await asyncio.sleep(2)
1015-
1016-
if not ota_provider_node_id:
1017-
await self._initialize_ota_provider(self._ota_provider)
1018-
1019-
# Notify update node about the availability of the OTA Provider. It will query
1020-
# the OTA provider and start the update.
1021-
try:
1022-
await self.chip_controller.SendCommand(
1023-
nodeid=node_id,
1024-
endpoint=0,
1025-
payload=Clusters.OtaSoftwareUpdateRequestor.Commands.AnnounceOTAProvider(
1026-
providerNodeID=ota_provider_node_id,
1027-
vendorID=self.server.vendor_id,
1028-
announcementReason=Clusters.OtaSoftwareUpdateRequestor.Enums.AnnouncementReasonEnum.kUpdateAvailable,
1029-
endpoint=ExternalOtaProvider.ENDPOINT_ID,
1030-
),
1031-
)
1032-
except ChipStackError as ex:
1033-
raise UpdateError("Error while announcing OTA Provider to node.") from ex
931+
await self._ota_provider.start_update(
932+
self,
933+
node_id,
934+
)
1034935

1035936
return update
1036937

@@ -1125,6 +1026,16 @@ def attribute_updated(
11251026
# schedule a full interview of the node if the software version changed
11261027
loop.create_task(self.interview_node(node_id))
11271028

1029+
# work out if update state changed
1030+
if (
1031+
str(path) == OTA_SOFTWARE_UPDATE_REQUESTOR_UPDATE_STATE_ATTRIBUTE_PATH
1032+
and new_value != old_value
1033+
):
1034+
if self._ota_provider:
1035+
loop.create_task(
1036+
self._ota_provider.check_update_state(node_id, new_value)
1037+
)
1038+
11281039
# store updated value in node attributes
11291040
node.attributes[str(path)] = new_value
11301041

matter_server/server/ota/__init__.py

+13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@
1818
"otaUrl": "https://github.com/agners/matter-linux-example-apps/releases/download/v1.3.0.0/chip-ota-requestor-app-x86-64.ota",
1919
"releaseNotesUrl": "https://github.com/agners/matter-linux-example-apps/releases/tag/v1.3.0.0",
2020
},
21+
(0x143D, 0x1001): {
22+
"vid": 0x143D,
23+
"pid": 0x1001,
24+
"softwareVersion": 10010011,
25+
"softwareVersionString": "1.1.11-c85ba1e-dirty",
26+
"cdVersionNumber": 1,
27+
"softwareVersionValid": True,
28+
"otaChecksum": "x2sK9xjVuGff0eefYa4cporDO+Z+WVxxw+JP5Ol+5og=",
29+
"otaChecksumType": 1,
30+
"minApplicableSoftwareVersion": 10010000,
31+
"maxApplicableSoftwareVersion": 10010011,
32+
"otaUrl": "https://raw.githubusercontent.com/ChampOnBon/Onvis/master/S4/debug.ota",
33+
},
2134
}
2235

2336

0 commit comments

Comments
 (0)