Skip to content

Commit c6e19e2

Browse files
committed
Update implementation to work with latest refactoring
Make use of the new ChipDeviceControllerWrapper to communicate with the device directly. This avoids unnecessary node interviewing and showing up on the controller side.
1 parent 563a50b commit c6e19e2

File tree

2 files changed

+49
-60
lines changed

2 files changed

+49
-60
lines changed

matter_server/server/device_controller.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def __init__(
158158
self._polled_attributes: dict[int, set[str]] = {}
159159
self._custom_attribute_poller_timer: asyncio.TimerHandle | None = None
160160
self._custom_attribute_poller_task: asyncio.Task | None = None
161-
self._ota_provider = ExternalOtaProvider(ota_provider_dir)
161+
self._ota_provider = ExternalOtaProvider(server.vendor_id, ota_provider_dir)
162162

163163
async def initialize(self) -> None:
164164
"""Initialize the device controller."""
@@ -913,12 +913,6 @@ async def update_node(self, node_id: int, software_version: int) -> dict | None:
913913
f"Software version {software_version} is not available for node {node_id}."
914914
)
915915

916-
if self.chip_controller is None:
917-
raise RuntimeError("Device Controller not initialized.")
918-
919-
if not self._ota_provider:
920-
raise UpdateError("No OTA provider found, updates not possible")
921-
922916
if self._ota_provider.is_busy():
923917
raise UpdateError(
924918
"No OTA provider currently busy, updates currently not possible"
@@ -929,7 +923,7 @@ async def update_node(self, node_id: int, software_version: int) -> dict | None:
929923

930924
# Make sure any previous instances get stopped
931925
await self._ota_provider.start_update(
932-
self,
926+
self._chip_device_controller,
933927
node_id,
934928
)
935929

matter_server/server/ota/provider.py

+47-52
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,26 @@
1818
from aiohttp import ClientError, ClientSession
1919
from aiohttp.client_exceptions import InvalidURL
2020
from chip.clusters import Attribute, Objects as Clusters, Types
21+
from chip.discovery import FilterType
2122
from chip.exceptions import ChipStackError
2223
from chip.interaction_model import Status
2324

24-
from matter_server.common.errors import NodeCommissionFailed, NodeNotExists, UpdateError
25+
from matter_server.common.errors import UpdateError
2526
from matter_server.common.helpers.util import dataclass_from_dict
2627

27-
if TYPE_CHECKING:
28-
from matter_server.server.device_controller import MatterDeviceController
29-
3028
if TYPE_CHECKING:
3129
from asyncio.subprocess import Process
3230

31+
from chip.native import PyChipError
32+
33+
from matter_server.server.sdk import ChipDeviceControllerWrapper
34+
3335
LOGGER = logging.getLogger(__name__)
3436

3537
DEFAULT_UPDATES_PATH: Final[Path] = Path("updates")
3638

39+
DEFAULT_OTA_PROVIDER_NODE_ID = 999900
40+
3741
# From Matter SDK src/app/ota_image_tool.py
3842
CHECHKSUM_TYPES: Final[dict[int, str]] = {
3943
1: "sha256",
@@ -85,8 +89,9 @@ class ExternalOtaProvider:
8589

8690
ENDPOINT_ID: Final[int] = 0
8791

88-
def __init__(self, ota_provider_dir: Path) -> None:
92+
def __init__(self, vendor_id: int, ota_provider_dir: Path) -> None:
8993
"""Initialize the OTA provider."""
94+
self._vendor_id: int = vendor_id
9095
self._ota_provider_dir: Path = ota_provider_dir
9196
self._ota_provider_image_list_file: Path = ota_provider_dir / "updates.json"
9297
self._ota_provider_image_list: OtaProviderImageList | None = None
@@ -158,40 +163,45 @@ def get_node_id(self) -> int | None:
158163

159164
return self._get_ota_provider_image_list().otaProviderNodeId
160165

161-
async def _initialize(self, device_controller: MatterDeviceController) -> None:
166+
async def _initialize(
167+
self, chip_device_controller: ChipDeviceControllerWrapper
168+
) -> None:
162169
"""Commissions the OTA Provider."""
163170

164-
if device_controller.chip_controller is None:
165-
raise RuntimeError("Device Controller not initialized.")
166-
167171
# The OTA Provider has not been commissioned yet, let's do it now.
168172
LOGGER.info("Commissioning the built-in OTA Provider App.")
169-
try:
170-
ota_provider_node = await device_controller.commission_on_network(
171-
self.get_passcode(),
172-
# TODO: Filtering by long discriminator seems broken
173-
# filter_type=FilterType.LONG_DISCRIMINATOR,
174-
# filter=ota_provider.get_descriminator(),
173+
174+
res: PyChipError = await chip_device_controller.commission_on_network(
175+
DEFAULT_OTA_PROVIDER_NODE_ID,
176+
self.get_passcode(),
177+
# TODO: Filtering by long discriminator seems broken
178+
disc_filter_type=FilterType.LONG_DISCRIMINATOR,
179+
disc_filter=self.get_descriminator(),
180+
)
181+
if not res.is_success:
182+
await self.stop()
183+
raise UpdateError(
184+
f"Failed to commission OTA Provider App: SDK Error {res.code}"
175185
)
176-
ota_provider_node_id = ota_provider_node.node_id
177-
except NodeCommissionFailed:
178-
LOGGER.error("Failed to commission OTA Provider App!")
179-
return
180186

181187
LOGGER.info(
182188
"OTA Provider App commissioned with node id %d.",
183-
ota_provider_node_id,
189+
DEFAULT_OTA_PROVIDER_NODE_ID,
184190
)
185191

186192
# Adjust ACL of OTA Requestor such that Node peer-to-peer communication
187193
# is allowed.
188194
try:
189-
read_result = await device_controller.chip_controller.ReadAttribute(
190-
ota_provider_node_id, [(0, Clusters.AccessControl.Attributes.Acl)]
195+
read_result = cast(
196+
Attribute.AsyncReadTransaction.ReadResponse,
197+
await chip_device_controller.read_attribute(
198+
DEFAULT_OTA_PROVIDER_NODE_ID,
199+
[(0, Clusters.AccessControl.Attributes.Acl)],
200+
),
191201
)
192202
acl_list = cast(
193203
list,
194-
read_result[0][Clusters.AccessControl][
204+
read_result.attributes[0][Clusters.AccessControl][
195205
Clusters.AccessControl.Attributes.Acl
196206
],
197207
)
@@ -216,8 +226,8 @@ async def _initialize(self, device_controller: MatterDeviceController) -> None:
216226
# And write. This is persistent, so only need to be done after we commissioned
217227
# the OTA Provider App.
218228
write_result: Attribute.AttributeWriteResult = (
219-
await device_controller.chip_controller.WriteAttribute(
220-
ota_provider_node_id,
229+
await chip_device_controller.write_attribute(
230+
DEFAULT_OTA_PROVIDER_NODE_ID,
221231
[(0, Clusters.AccessControl.Attributes.Acl(acl_list))],
222232
)
223233
)
@@ -226,22 +236,17 @@ async def _initialize(self, device_controller: MatterDeviceController) -> None:
226236
"Failed writing adjusted OTA Provider App ACL: Status %s.",
227237
str(write_result[0].Status),
228238
)
229-
await device_controller.remove_node(ota_provider_node_id)
239+
await self.stop()
230240
raise UpdateError("Error while setting up OTA Provider.")
231241
except ChipStackError as ex:
232242
logging.exception("Failed adjusting OTA Provider App ACL.", exc_info=ex)
233-
await device_controller.remove_node(ota_provider_node_id)
243+
await self.stop()
234244
raise UpdateError("Error while setting up OTA Provider.") from ex
235245

236-
self.set_node_id(ota_provider_node_id)
237-
238246
async def start_update(
239-
self, device_controller: MatterDeviceController, node_id: int
247+
self, chip_device_controller: ChipDeviceControllerWrapper, node_id: int
240248
) -> None:
241-
"""Start the OTA Provider."""
242-
243-
if device_controller.chip_controller is None:
244-
raise RuntimeError("Device Controller not initialized.")
249+
"""Start the OTA Provider and trigger the update."""
245250

246251
self._ota_target_node_id = node_id
247252

@@ -291,39 +296,29 @@ def _write_ota_provider_image_list_json(
291296

292297
# Wait for OTA provider to be ready
293298
# TODO: Detect when OTA provider is ready
294-
await asyncio.sleep(2)
299+
await asyncio.sleep(3)
295300

296301
# Handle if user deleted the OTA Provider node.
297302
ota_provider_node_id = self.get_node_id()
298-
if ota_provider_node_id is not None:
299-
try:
300-
device_controller.get_node(ota_provider_node_id)
301-
except NodeNotExists:
302-
LOGGER.warning(
303-
"OTA Provider node id %d not known by device controller! Resetting...",
304-
ota_provider_node_id,
305-
)
306-
await self._reset()
307-
ota_provider_node_id = None
308303

309304
# Commission and prepare OTA Provider if not initialized yet.
310305
# Use "ota_provider_node_id" to indicate if OTA Provider is setup or not.
311306
try:
312307
if ota_provider_node_id is None:
313308
LOGGER.info("Initializing OTA Provider")
314-
await self._initialize(device_controller)
309+
await self._initialize(chip_device_controller)
315310
finally:
316311
self._ota_target_node_id = None
317312

318313
# Notify update node about the availability of the OTA Provider. It will query
319314
# the OTA provider and start the update.
320315
try:
321-
await device_controller.chip_controller.SendCommand(
322-
nodeid=node_id,
323-
endpoint=0,
324-
payload=Clusters.OtaSoftwareUpdateRequestor.Commands.AnnounceOTAProvider(
325-
providerNodeID=ota_provider_node_id,
326-
vendorID=device_controller.server.vendor_id,
316+
await chip_device_controller.send_command(
317+
node_id,
318+
endpoint_id=0,
319+
command=Clusters.OtaSoftwareUpdateRequestor.Commands.AnnounceOTAProvider(
320+
providerNodeID=DEFAULT_OTA_PROVIDER_NODE_ID,
321+
vendorID=self._vendor_id,
327322
announcementReason=Clusters.OtaSoftwareUpdateRequestor.Enums.AnnouncementReasonEnum.kUpdateAvailable,
328323
endpoint=ExternalOtaProvider.ENDPOINT_ID,
329324
),

0 commit comments

Comments
 (0)