16
16
import time
17
17
from typing import TYPE_CHECKING , Any , cast
18
18
19
- from chip .clusters import Attribute , Objects as Clusters , Types
19
+ from chip .clusters import Attribute , Objects as Clusters
20
20
from chip .clusters .Attribute import ValueDecodeFailure
21
21
from chip .clusters .ClusterObjects import ALL_ATTRIBUTES , ALL_CLUSTERS , Cluster
22
22
from chip .discovery import DiscoveryType
23
23
from chip .exceptions import ChipStackError
24
- from chip .interaction_model import Status
25
24
from zeroconf import BadTypeInNameException , IPVersion , ServiceStateChange , Zeroconf
26
25
from zeroconf .asyncio import AsyncServiceBrowser , AsyncServiceInfo , AsyncZeroconf
27
26
112
111
0 , Clusters .BasicInformation .Attributes .SoftwareVersionString
113
112
)
114
113
)
114
+ OTA_SOFTWARE_UPDATE_REQUESTOR_UPDATE_STATE_ATTRIBUTE_PATH = (
115
+ create_attribute_path_from_attribute (
116
+ 0 , Clusters .OtaSoftwareUpdateRequestor .Attributes .UpdateState
117
+ )
118
+ )
119
+
115
120
116
121
# pylint: disable=too-many-lines,too-many-instance-attributes,too-many-public-methods
117
122
@@ -891,83 +896,6 @@ async def check_node_update(self, node_id: int) -> dict | None:
891
896
892
897
return await self ._check_node_update (node_id )
893
898
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
-
971
899
@api_command (APICommand .UPDATE_NODE )
972
900
async def update_node (self , node_id : int , software_version : int ) -> dict | None :
973
901
"""
@@ -989,48 +917,21 @@ async def update_node(self, node_id: int, software_version: int) -> dict | None:
989
917
raise RuntimeError ("Device Controller not initialized." )
990
918
991
919
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
+ )
993
926
994
927
# Add update to the OTA provider
995
928
await self ._ota_provider .download_update (update )
996
929
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
-
1008
930
# 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
+ )
1034
935
1035
936
return update
1036
937
@@ -1125,6 +1026,16 @@ def attribute_updated(
1125
1026
# schedule a full interview of the node if the software version changed
1126
1027
loop .create_task (self .interview_node (node_id ))
1127
1028
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
+
1128
1039
# store updated value in node attributes
1129
1040
node .attributes [str (path )] = new_value
1130
1041
0 commit comments