44
55import ssl
66import wifi
7+ import time
78import socketpool
89from gc import collect
910from errno import EAGAIN
10- from binascii import b2a_base64
11+ from binascii import b2a_base64 , a2b_base64
1112from json import loads
13+ from microcontroller import reset
1214
1315# Helpers:
16+ def _replace_code (new_code : bytes , do_reset = True ):
17+ """Replaces the contents of code.py with a new bytes
18+ """
19+ with open ("/code.py" , "wb" ) as fp :
20+ fp .write (new_code )
21+ if do_reset :
22+ reset ()
23+
1424def enum (** enums ):
1525 """Fake enum-maker"""
1626 return type ('Enum' , (), enums )
@@ -157,12 +167,21 @@ def __init__(
157167 self .context .load_verify_locations (cadata = server_cert )
158168 self .connect_wifi ()
159169
160- def connect_wifi (self ) -> None :
170+ def connect_wifi (self , attempts = 10 ) -> None :
161171 """Creates the wifi connection to the access point"""
162172 wifi .radio .enabled = True
163173 if self .v :
164174 print ("Connecting to the access point..." )
165- wifi .radio .connect (self .conf .AP_SSID )
175+ connected = False
176+ while not connected and attempts > 0 :
177+ try :
178+ wifi .radio .connect (self .conf .AP_SSID )
179+ continue
180+ except ConnectionError as e :
181+ if self .v :
182+ print (e .with_traceback )
183+ attempts -= 1
184+ time .sleep (1 )
166185 if self .v :
167186 print ("Initializing socket pool..." )
168187 self .pool = socketpool .SocketPool (wifi .radio )
@@ -356,11 +375,19 @@ def request(
356375 raise last_error
357376
358377 def get_status (self ) -> GameStatus :
359- resp = self .request ('GET' , '/status' )
378+ if self .v :
379+ print ("Getting status..." )
380+ resp = self .request ('GET' , '/status' ,
381+ headers = ['User-Agent: CircuitPython, dude!' ]
382+ )
360383 resp_json = loads (resp [1 ])
384+ if self .v :
385+ print (f"It is { resp_json ['unix_time' ]} seconds since the epoch." )
361386 return GameStatus (resp_json ['unix_time' ], resp_json ['status' ]['score' ], resp_json ['status' ]['strikes' ])
362387
363388 def post (self , point : DataPoint ) -> bool :
389+ if self .v :
390+ print ("Posting datapoint!" )
364391 return self .request (
365392 'POST' ,
366393 '/data' ,
@@ -370,6 +397,8 @@ def post(self, point: DataPoint) -> bool:
370397 ).code == 201
371398
372399 def email (self , msg : Email ) -> bool :
400+ if self .v :
401+ print (f"Sending email { msg .subject } ..." )
373402 return self .request (
374403 'POST' ,
375404 '/email' ,
@@ -378,6 +407,44 @@ def email(self, msg: Email) -> bool:
378407 headers = ['User-Agent: CircuitPython, dude!' ]
379408 ).code == 201
380409
410+ def code_update (self , reset = True ) -> bool :
411+ """Checks for code updates from the server (uploaded by team)
412+ call like code_update(reset=False) to download the update but not reset
413+ (Must reset to run the new code)
414+ Otherwise, run like code_update()
415+ Returns True if reset is False and the update is successful.
416+ Reset the microcontroller in this case.
417+ Otherwise, returning False, the update is stale or invalid.
418+ """
419+ if self .v :
420+ print ("Checking for updates..." )
421+ response = self .request (
422+ 'GET' ,
423+ '/update' ,
424+ headers = ['User-Agent: CircuitPython, dude!' ]
425+ )
426+ if response .code != 200 :
427+ if self .v :
428+ print (f"Bad response code { response .code } " )
429+ return False
430+ print (f"Response: { response [1 ]} " )
431+ resp_json = loads (response [1 ])
432+ if resp_json ['new' ]:
433+ if self .v :
434+ print ("New Update!" )
435+ _replace_code (
436+ a2b_base64 (
437+ resp_json ['code' ]
438+ ),
439+ reset = reset
440+ )
441+ if self .v :
442+ print ("code.py replaced!" )
443+ return True
444+ if self .v :
445+ print ("Stale update." )
446+ return False
447+
381448 def __exit__ (self ):
382449 if self .v :
383450 print ("Closing the server connection-" )
0 commit comments