@@ -67,6 +67,7 @@ def check_tb_paho_mqtt_installed():
6767FW_CHECKSUM_ALG_ATTR = "fw_checksum_algorithm"
6868FW_SIZE_ATTR = "fw_size"
6969FW_STATE_ATTR = "fw_state"
70+ FW_ERROR_ATTR = "fw_error"
7071
7172REQUIRED_SHARED_KEYS = f"{ FW_CHECKSUM_ATTR } ,{ FW_CHECKSUM_ALG_ATTR } ,{ FW_SIZE_ATTR } ,{ FW_TITLE_ATTR } ,{ FW_VERSION_ATTR } "
7273
@@ -106,6 +107,14 @@ class TBSendMethod(Enum):
106107 PUBLISH = 1
107108 UNSUBSCRIBE = 2
108109
110+ class TBFirmwareState (Enum ):
111+ IDLE = "IDLE"
112+ DOWNLOADING = "DOWNLOADING"
113+ DOWNLOADED = "DOWNLOADED"
114+ VERIFIED = "VERIFIED"
115+ UPDATING = "UPDATING"
116+ UPDATED = "UPDATED"
117+ FAILED = "FAILED"
109118
110119class TBPublishInfo :
111120 TB_ERR_AGAIN = - 1
@@ -474,7 +483,7 @@ class TBDeviceMqttClient:
474483 def __init__ (self , host , port = 1883 , username = None , password = None , quality_of_service = None , client_id = "" ,
475484 chunk_size = 0 , messages_rate_limit = "DEFAULT_MESSAGES_RATE_LIMIT" ,
476485 telemetry_rate_limit = "DEFAULT_TELEMETRY_RATE_LIMIT" ,
477- telemetry_dp_rate_limit = "DEFAULT_TELEMETRY_DP_RATE_LIMIT" , max_payload_size = 8196 , ** kwargs ):
486+ telemetry_dp_rate_limit = "DEFAULT_TELEMETRY_DP_RATE_LIMIT" , max_payload_size = 8196 , on_firmware_received = None , ** kwargs ):
478487 # Added for compatibility with old versions
479488 if kwargs .get ('rate_limit' ) is not None or kwargs .get ('dp_rate_limit' ) is not None :
480489 messages_rate_limit = messages_rate_limit if kwargs .get ('rate_limit' ) == "DEFAULT_RATE_LIMIT" else kwargs .get ('rate_limit' , messages_rate_limit ) # noqa
@@ -523,37 +532,50 @@ def __init__(self, host, port=1883, username=None, password=None, quality_of_ser
523532 self .current_firmware_info = {
524533 "current_" + FW_TITLE_ATTR : "Initial" ,
525534 "current_" + FW_VERSION_ATTR : "v0" ,
526- FW_STATE_ATTR : "IDLE"
535+ FW_STATE_ATTR : TBFirmwareState .IDLE .value ,
536+ FW_ERROR_ATTR : ""
527537 }
528538 self .__request_id = 0
529539 self .__firmware_request_id = 0
530540 self .__chunk_size = chunk_size
531541 self .firmware_received = False
542+ self .set_on_firmware_received_function (on_firmware_received )
532543 self .rate_limits_received = False
533544 self .__request_service_configuration_required = False
534545 self .__service_loop = Thread (target = self .__service_loop , name = "Service loop" , daemon = True )
535546 self .__service_loop .start ()
536547 self .__messages_limit_reached_set_time = (0 ,0 )
537548 self .__datapoints_limit_reached_set_time = (0 ,0 )
538549
550+ def update_firmware_info (self , title = None , version = None , state : TBFirmwareState = None , error = None ):
551+ if title is not None :
552+ self .current_firmware_info ["current_" + FW_TITLE_ATTR ] = title
553+
554+ if version is not None :
555+ self .current_firmware_info ["current_" + FW_VERSION_ATTR ] = version
556+
557+ if state is not None :
558+ self .current_firmware_info [FW_STATE_ATTR ] = state .value
559+ if state is TBFirmwareState .FAILED and error is not None :
560+ self .current_firmware_info [FW_ERROR_ATTR ] = error
561+ else :
562+ self .current_firmware_info [FW_ERROR_ATTR ] = ""
563+
564+ self .send_telemetry (self .current_firmware_info )
565+
566+ def set_on_firmware_received_function (self , on_firmware_received ):
567+ if on_firmware_received is not None :
568+ self .__on_firmware_received = on_firmware_received
569+ else :
570+ self .__on_firmware_received = self .__on_firmware_received_default
571+
539572 def __service_loop (self ):
540573 while not self .stopped :
541574 if self .__request_service_configuration_required :
542575 self .request_service_configuration (self .service_configuration_callback )
543576 self .__request_service_configuration_required = False
544577 elif self .firmware_received :
545- self .current_firmware_info [FW_STATE_ATTR ] = "UPDATING"
546- self .send_telemetry (self .current_firmware_info )
547- sleep (1 )
548-
549- self .__on_firmware_received (self .firmware_info .get (FW_VERSION_ATTR ))
550-
551- self .current_firmware_info = {
552- "current_" + FW_TITLE_ATTR : self .firmware_info .get (FW_TITLE_ATTR ),
553- "current_" + FW_VERSION_ATTR : self .firmware_info .get (FW_VERSION_ATTR ),
554- FW_STATE_ATTR : "UPDATED"
555- }
556- self .send_telemetry (self .current_firmware_info )
578+ self .__on_firmware_received (self , self .firmware_data , self .firmware_info .get (FW_VERSION_ATTR ))
557579 self .firmware_received = False
558580 sleep (0.05 )
559581
@@ -758,8 +780,7 @@ def _on_decoded_message(self, content, message):
758780 self .firmware_data = b''
759781 self .__current_chunk = 0
760782
761- self .current_firmware_info [FW_STATE_ATTR ] = "DOWNLOADING"
762- self .send_telemetry (self .current_firmware_info )
783+ self .update_firmware_info (state = TBFirmwareState .DOWNLOADING )
763784 sleep (1 )
764785
765786 self .__firmware_request_id = self .__firmware_request_id + 1
@@ -769,22 +790,19 @@ def _on_decoded_message(self, content, message):
769790 self .__get_firmware ()
770791
771792 def __process_firmware (self ):
772- self .current_firmware_info [FW_STATE_ATTR ] = "DOWNLOADED"
773- self .send_telemetry (self .current_firmware_info )
793+ self .update_firmware_info (state = TBFirmwareState .DOWNLOADED )
774794 sleep (1 )
775795
776796 verification_result = verify_checksum (self .firmware_data , self .firmware_info .get (FW_CHECKSUM_ALG_ATTR ),
777797 self .firmware_info .get (FW_CHECKSUM_ATTR ))
778798
779799 if verification_result :
780800 log .debug ('Checksum verified!' )
781- self .current_firmware_info [FW_STATE_ATTR ] = "VERIFIED"
782- self .send_telemetry (self .current_firmware_info )
801+ self .update_firmware_info (state = TBFirmwareState .VERIFIED )
783802 sleep (1 )
784803 else :
785804 log .debug ('Checksum verification failed!' )
786- self .current_firmware_info [FW_STATE_ATTR ] = "FAILED"
787- self .send_telemetry (self .current_firmware_info )
805+ self .update_firmware_info (state = TBFirmwareState .FAILED , error = "Checksum verification failed!" )
788806 self .__request_firmware_info ()
789807 return
790808 self .firmware_received = True
@@ -796,10 +814,19 @@ def __get_firmware(self):
796814 f"v2/fw/request/{ self .__firmware_request_id } /chunk/{ self .__current_chunk } " ,
797815 payload = payload , qos = 1 )
798816
799- def __on_firmware_received (self , version_to ):
817+ def __on_firmware_received_default (self , client , firmware_data , version_to ):
818+ self .update_firmware_info (state = TBFirmwareState .UPDATING )
819+ sleep (1 )
820+
800821 with open (self .firmware_info .get (FW_TITLE_ATTR ), "wb" ) as firmware_file :
801- firmware_file .write (self .firmware_data )
802- log .info ('Firmware is updated!\n Current firmware version is: %s' % version_to )
822+ firmware_file .write (firmware_data )
823+ log .warning (f"Firmware was received and stored under { self .firmware_info .get (FW_TITLE_ATTR )} ,"
824+ "but 'on_firmware_received' callback is not defined. No OTA update applied."
825+ "Call 'set_on_firmware_received_function' to handle properly firmware update." )
826+
827+ self .update_firmware_info (title = self .firmware_info .get (FW_TITLE_ATTR ),
828+ version = self .firmware_info .get (FW_VERSION_ATTR ),
829+ state = TBFirmwareState .UPDATED )
803830
804831 @staticmethod
805832 def _decode (message ):
0 commit comments