def thread_handler(arg=None): try: if arg is None: handler() else: handler(arg) except Exception as e: log_message("{} Thread Failed: {}".format(name, e)) time.sleep(5) reset()
def clear_device(*args, **kwargs): log_message("Processing halted. Clearing device") time.sleep(5) # Remove stored configuration, resetting device to freshly # installed state os.remove("medusa.json") reset_handler()
def remove_dir(directory, raise_exceptions=False): try: for filename in os.listdir(directory): path = "{}/{}".format(directory, filename) remove_dir(path) os.remove(directory) return True except Exception as e: log_message("Failed to remove {}".format(directory)) if raise_exceptions: raise return False
def main(self): try: """ Operational Function for Medusa Device """ log_message("Medusa!") # Start thread to reset device after 10 monitoring cycles self.create_thread(name="reset", handler=self.lte_reset_handler, s=self.monitor_delay * 10 + 5, periodic=True) # Flash light blue to show Medusa is configuring itself (may indicate WiFi AP is up) self.FLASHING_LIGHT.colors = [LED_BLUE, LED_OFF] try: log_message("Attempting WiFi connection") with open("networks.json", "r") as f: networks = json.loads(f.read()) known_ssids = list(networks.keys()) self.wlan = bringup_wlan() # List of visible network tuples (SSID, bssid, sec, channel, rssi) visible_networks = self.wlan.scan() visible_known = [ net for net in visible_networks if net[0] in known_ssids ] for network in visible_known: try: ssid = network[0] pw = networks[ssid] simple_connect(ssid, pw, wlan=self.wlan) except Exception: log_message("Failed to connect to {}".format(network)) if self.wlan.isconnected() is not True: raise Exception("Failed to find network") except Exception: log_message("Failed to find WiFi network.") self.receive_instructions() self.monitor() except Exception as e: log_message("Main thread failed: {}".format(e)) raise
def monitor(self): enable_ntp() # Flash light green and blue to show device has connected to network and is registering with Mobius self.FLASHING_LIGHT.colors = [LED_BLUE, LED_GREEN] + [LED_OFF] * 4 # Check for firmware updates OTA(self.FLASHING_LIGHT) # Build websocket log_message("Connecting WebSocket") self.websocket = connect_websocket(MedusaWebSocket) # Flash light green slowly to show Medusa is connected, configured and operational. Green will pulse throughout operation self.FLASHING_LIGHT.colors = [LED_GREEN] + [LED_OFF] * 4 self.FLASHING_LIGHT.set_milliseconds(500) # Start monitoring loop while True: sleep(1)
def receive_instructions(self): log_message("Starting cellular connection") self.lte = connect_lte() enable_ntp() log_message("Connecting WebSocket") self.websocket = connect_websocket(MedusaWebSocket) log_message("Sending heartbeat") heartbeat = self.heartbeat("Cellular") self.upload_memories([heartbeat]) log_message("Making instructions request") request = { "action": "request", "REQUEST_TREE": "medusa_networks", "Limit": 1, "ScanIndexForward": False } self.websocket.send(json.dumps(request)) try: with open("networks.json", "r") as f: known_networks = json.loads(f.read()) except Exception: known_networks = {} log_message("Waiting for response") resp = {"NEW_NETWORKS": []} while not resp["NEW_NETWORKS"]: resp = self.websocket.recv() log_message("Received: {}".format(resp)) log_message(type(resp)) resp = json.loads(resp)[0] resp['NETWORKS'] = json.loads(resp['NETWORKS']) resp['NEW_NETWORKS'] = [ ssid for ssid in resp['NETWORKS'].keys() if ssid not in known_networks ] with open("networks.json", "w") as f: f.write(json.dumps(resp['NETWORKS'])) log_message("Received updated network list. Resetting device") self.lte_reset_handler()
def OTA(flashing_light): log_message("Checking for GoldenFight updates") # Open current summary with open("goldenfight.json", "r") as f: current_summary = json.loads(f.read()) log_message("Current version is {}".format(current_summary['version'])) # Download latest version summary try: resp = get(GoldenFightAPI + "/ota") if resp.status_code != 200: raise Exception("Unacceptable Status Code: " + str(resp.status_code)) except Exception as e: log_message("Failed to download GoldenFight Version Summary") print_exception(e) raise Exception("Failed Summary Retrieval") latest_summary = resp.json() resp.close() # If newer version available, update files if current_summary['version'] < latest_summary['version']: log_message("Update needed. Latest Version: {}".format( latest_summary['version'])) flashing_light.colors = [LED_PURPLE, LED_OFF, LED_TEAL] + [LED_OFF] * 4 # Download all files into directory named after version try: os.mkdir(latest_summary['version']) except Exception as e: log_message("Failed to create directory.") remove_dir(latest_summary['version']) filenames = [ fn for fn in latest_summary['files'] if fn not in latest_summary['ignore_files'] ] for filename in filenames: chunk_index = 0 chunks_remaining = True while chunks_remaining: log_message("Retrieving {} Chunk {}".format( filename, chunk_index)) get_file = get( GoldenFightAPI + "/ota/{}?chunk={}".format(filename, chunk_index)) if get_file.status_code != 200: msg = "Failed to retrieve {}".format(filename) log_message(msg) raise Exception(msg) get_file_resp = get_file.json() get_file.close() log_message("Storing {} chunk {} locally".format( filename, chunk_index)) with open("{}/{}".format(latest_summary['version'], filename), "a") as f: f.write(get_file_resp['content_chunk']) chunk_index += 1 chunks_remaining = int( get_file_resp['total_chunks']) - chunk_index if chunks_remaining > 0: log_message( "Chunks remaining: {}".format(chunks_remaining)) chunks_remaining = True log_message("File downloads complete. Installing new firmware...") # Copy new files into place flashing_light.colors = [ LED_YELLOW, LED_OFF, LED_OFF, LED_TEAL, LED_OFF, LED_OFF ] for filename in filenames: log_message("Installing {}".format(filename)) orig_path = "{}/{}".format(latest_summary['version'], filename) try: log_message("Removing existing version") remove_dir(filename, raise_exceptions=True) except Exception as e: log_message("Failed removing the existing version") print_exception(e) os.rename(orig_path, filename) log_message("Deleting unneeded files") # Delete any files not in filenames or latest_summary['save_files'] current_files = os.listdir() for cf in current_files: if cf not in filenames + latest_summary['save_files'] + [ latest_summary['version'] ]: log_message("Removing {}".format(cf)) try: remove_dir(cf) except Exception as e: log_message("Failure to remove {}".format(cf)) print_exception(e) log_message("GoldenFight updated to {}".format( latest_summary['version'])) reset_handler() else: log_message("Current version is up to date")