@@ -49,6 +49,10 @@ class InvalidStateError(Exception):
49
49
_DEFAULT_PORT = (8883 , 8884 )
50
50
51
51
52
+ class DoneException (Exception ):
53
+ pass
54
+
55
+
52
56
def timestamp ():
53
57
return int (time .time ())
54
58
@@ -278,12 +282,16 @@ def mqtt_callback(self, topic, message):
278
282
logging .debug (f"mqtt topic: { topic [- 8 :]} ... message: { message [:8 ]} ..." )
279
283
self .senmlpack .clear ()
280
284
for record in self .records .values ():
285
+ # If the object is uninitialized, updates are always allowed even if it's a read-only
286
+ # object. Otherwise, for initialized objects, updates are only allowed if the object
287
+ # is writable (on_write function is set) and the value is received from the out topic.
281
288
if not record .initialized or (record .on_write is not None and b"shadow" not in topic ):
282
289
record .add_to_pack (self .senmlpack )
283
290
self .senmlpack .from_cbor (message )
284
291
self .senmlpack .clear ()
285
292
286
293
async def discovery_task (self , interval = 0.100 ):
294
+ self .mqtt .subscribe (self .device_topic , qos = 1 )
287
295
while self .thing_id is None :
288
296
self .mqtt .check_msg ()
289
297
if self .records .get ("thing_id" ).value is not None :
@@ -300,6 +308,25 @@ async def discovery_task(self, interval=0.100):
300
308
self .mqtt .publish (self .create_topic ("shadow" , "o" ), self .senmlpack .to_cbor (), qos = 1 )
301
309
logging .info ("Device configured via discovery protocol." )
302
310
await asyncio .sleep (interval )
311
+ raise DoneException ()
312
+
313
+ async def conn_task (self , interval = 1.0 , backoff = 1.2 ):
314
+ logging .info ("Connecting to Arduino IoT cloud..." )
315
+ while True :
316
+ try :
317
+ self .mqtt .connect ()
318
+ break
319
+ except Exception as e :
320
+ logging .warning (f"Connection failed { e } , retrying after { interval } s" )
321
+ await asyncio .sleep (interval )
322
+ interval = min (interval * backoff , 4.0 )
323
+
324
+ if self .thing_id is None :
325
+ self .create_task ("discovery" , self .discovery_task )
326
+ else :
327
+ self .mqtt .subscribe (self .create_topic ("e" , "i" ))
328
+ self .create_task ("mqtt_task" , self .mqtt_task )
329
+ raise DoneException ()
303
330
304
331
async def mqtt_task (self , interval = 0.100 ):
305
332
while True :
@@ -320,34 +347,33 @@ async def mqtt_task(self, interval=0.100):
320
347
self .last_ping = timestamp ()
321
348
logging .debug ("No records to push, sent a ping request." )
322
349
await asyncio .sleep (interval )
350
+ raise DoneException ()
323
351
324
352
async def run (self , user_main = None ):
325
- logging .info ("Connecting to Arduino IoT cloud..." )
326
- if not self .mqtt .connect ():
327
- logging .error ("Failed to connect Arduino IoT cloud." )
328
- return
329
-
330
- self .mqtt .subscribe (self .device_topic , qos = 1 )
353
+ self .create_task ("conn_task" , self .conn_task )
331
354
if user_main is not None :
332
355
self .create_task ("user_main" , user_main , self )
333
- self .create_task ("mqtt_task" , self .mqtt_task )
334
- self .create_task ("discovery" , self .discovery_task )
335
356
336
357
while True :
358
+ task_except = None
337
359
try :
338
360
await asyncio .gather (* self .tasks .values (), return_exceptions = False )
339
- logging .info ("All tasks finished!" )
340
- break
361
+ break # All tasks are done, not likely.
341
362
except Exception as e :
342
- except_msg = str ( e )
343
- pass # import traceback; traceback.print_exc()
363
+ task_except = e
364
+ pass # import traceback; traceback.print_exc()
344
365
345
366
for name in list (self .tasks ):
346
367
task = self .tasks [name ]
347
368
try :
348
369
if task .done ():
349
370
self .tasks .pop (name )
350
371
self .records .pop (name , None )
351
- logging .error (f"Removed task: { name } . Raised exception: { except_msg } ." )
372
+ if isinstance (task_except , DoneException ):
373
+ logging .error (f"task: { name } complete." )
374
+ elif task_except is not None :
375
+ logging .error (f"task: { name } raised exception: { str (task_except )} ." )
376
+ if name == "mqtt_task" :
377
+ self .create_task ("conn_task" , self .conn_task )
352
378
except (CancelledError , InvalidStateError ):
353
379
pass
0 commit comments