@@ -896,8 +896,8 @@ async def import_test_node(self, dump: str) -> None:
896
896
self ._nodes [node .node_id ] = node
897
897
self .server .signal_event (EventType .NODE_ADDED , node )
898
898
899
- @api_command (APICommand .UPDATE_NODE )
900
- async def update_node (self , node_id : int ) -> dict | None :
899
+ @api_command (APICommand .CHECK_NODE_UPDATE )
900
+ async def check_node_update (self , node_id : int ) -> dict | None :
901
901
"""
902
902
Check if there is an update for a particular node.
903
903
@@ -906,8 +906,27 @@ async def update_node(self, node_id: int) -> dict | None:
906
906
information of the latest update available.
907
907
"""
908
908
909
- node_logger = LOGGER .getChild (f"node_{ node_id } " )
910
- node = self ._nodes [node_id ]
909
+ return await self ._check_node_update (node_id )
910
+
911
+ @api_command (APICommand .UPDATE_NODE )
912
+ async def update_node (self , node_id : int , software_version : int ) -> dict | None :
913
+ """
914
+ Update a node to a new software version.
915
+
916
+ This command checks if the requested software version is indeed still available
917
+ and if so, it will start the update process. The update process will be handled
918
+ by the built-in OTA provider. The OTA provider will download the update and
919
+ notify the node about the new update.
920
+ """
921
+
922
+ update = await self ._check_node_update (node_id , software_version )
923
+ if update is None :
924
+ logging .error (
925
+ "Software version %d is not available for node %d" ,
926
+ software_version ,
927
+ node_id ,
928
+ )
929
+ return None
911
930
912
931
if self .chip_controller is None :
913
932
raise RuntimeError ("Device Controller not initialized." )
@@ -916,34 +935,7 @@ async def update_node(self, node_id: int) -> dict | None:
916
935
LOGGER .warning ("No OTA provider found, updates not possible." )
917
936
return None
918
937
919
- node_logger .debug ("Check for updates." )
920
- vid = cast (int , node .attributes .get (BASIC_INFORMATION_VENDOR_ID_ATTRIBUTE_PATH ))
921
- pid = cast (
922
- int , node .attributes .get (BASIC_INFORMATION_PRODUCT_ID_ATTRIBUTE_PATH )
923
- )
924
- software_version = cast (
925
- int , node .attributes .get (BASIC_INFORMATION_SOFTWARE_VERSION_ATTRIBUTE_PATH )
926
- )
927
- software_version_string = node .attributes .get (
928
- BASIC_INFORMATION_SOFTWARE_VERSION_STRING_ATTRIBUTE_PATH
929
- )
930
-
931
- update = await check_for_update (vid , pid , software_version )
932
- if not update :
933
- node_logger .info ("No new update found." )
934
- return None
935
-
936
- if "otaUrl" not in update :
937
- node_logger .warning ("Update found, but no OTA URL provided." )
938
- return None
939
-
940
- node_logger .info (
941
- "New software update found: %s (current %s). Preparing updates..." ,
942
- update ["softwareVersionString" ],
943
- software_version_string ,
944
- )
945
-
946
- # Add to OTA provider
938
+ # Add update to the OTA provider
947
939
await self ._ota_provider .download_update (update )
948
940
949
941
ota_provider_node_id = self ._ota_provider .get_node_id ()
@@ -1042,6 +1034,44 @@ async def update_node(self, node_id: int) -> dict | None:
1042
1034
1043
1035
return update
1044
1036
1037
+ async def _check_node_update (
1038
+ self ,
1039
+ node_id : int ,
1040
+ requested_software_version : int | None = None ,
1041
+ ) -> dict | None :
1042
+ node_logger = LOGGER .getChild (f"node_{ node_id } " )
1043
+ node = self ._nodes [node_id ]
1044
+
1045
+ node_logger .debug ("Check for updates." )
1046
+ vid = cast (int , node .attributes .get (BASIC_INFORMATION_VENDOR_ID_ATTRIBUTE_PATH ))
1047
+ pid = cast (
1048
+ int , node .attributes .get (BASIC_INFORMATION_PRODUCT_ID_ATTRIBUTE_PATH )
1049
+ )
1050
+ software_version = cast (
1051
+ int , node .attributes .get (BASIC_INFORMATION_SOFTWARE_VERSION_ATTRIBUTE_PATH )
1052
+ )
1053
+ software_version_string = node .attributes .get (
1054
+ BASIC_INFORMATION_SOFTWARE_VERSION_STRING_ATTRIBUTE_PATH
1055
+ )
1056
+
1057
+ update = await check_for_update (
1058
+ vid , pid , software_version , requested_software_version
1059
+ )
1060
+ if not update :
1061
+ node_logger .info ("No new update found." )
1062
+ return None
1063
+
1064
+ if "otaUrl" not in update :
1065
+ node_logger .warning ("Update found, but no OTA URL provided." )
1066
+ return None
1067
+
1068
+ node_logger .info (
1069
+ "New software update found: %s (current %s)." ,
1070
+ update ["softwareVersionString" ],
1071
+ software_version_string ,
1072
+ )
1073
+ return update
1074
+
1045
1075
async def _subscribe_node (self , node_id : int ) -> None :
1046
1076
"""
1047
1077
Subscribe to all node state changes/events for an individual node.
0 commit comments