forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathota_update.py
More file actions
147 lines (127 loc) · 6.09 KB
/
Copy pathota_update.py
File metadata and controls
147 lines (127 loc) · 6.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import asyncio
import argparse
import logging
import sys
import time
import subprocess # Added for running external commands
from pathlib import Path
from bleak import BleakClient, BleakScanner
from bleak.exc import BleakError
# --- Constants ---
DFU_SERVICE_UUID = "0000fe59-0000-1000-8000-00805f9b34fb" # Nordic Secure DFU Service
# --- Logging Setup ---
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Set nordicsemi logger level
logging.getLogger('nordicsemi').setLevel(logging.WARNING)
# --- Main DFU Logic (using adafruit-nrfutil) ---
async def perform_dfu(device_address: str, zip_file_path: str):
logger.info(f"Starting DFU process for {device_address} with {zip_file_path} using adafruit-nrfutil")
# Construct the command for adafruit-nrfutil
# We assume nRF53 family based on the initial request.
# Adjust '-ic' if your specific NRF53 variant needs a different identifier
# or if adafruit-nrfutil handles NRF53 differently.
# We use '-a' for the address. The tool should handle the connection.
command = [
"adafruit-nrfutil",
"dfu",
"ble",
"-ic", "NRF53", # Specify chip family
"-pkg", zip_file_path,
"-a", device_address,
# Add '-v' for verbose nrfutil output if script's verbose flag is set
# We'll handle this based on the logger level later
]
# Add verbose flags to nrfutil if script is run with -v
if logger.getEffectiveLevel() <= logging.DEBUG:
command.append("-v") # nrfutil's own verbose flag
command.append("-v") # Often requires -v -v or -v -v -v for max detail
command.append("-v")
logger.info(f"Executing command: {' '.join(command)}")
try:
# Run the command
# Using subprocess.run which is simpler for commands that finish
# We capture output to log it.
result = subprocess.run(command, capture_output=True, text=True, check=False) # check=False allows us to handle errors
# Log stdout and stderr
if result.stdout:
logger.info("adafruit-nrfutil stdout:")
for line in result.stdout.strip().split('\n'):
logger.info(f" {line}")
if result.stderr:
# nrfutil often prints progress to stderr, so log as INFO unless error code is non-zero
log_level = logging.ERROR if result.returncode != 0 else logging.INFO
logger.log(log_level, "adafruit-nrfutil stderr:")
for line in result.stderr.strip().split('\n'):
logger.log(log_level, f" {line}")
# Check return code
if result.returncode == 0:
logger.info("adafruit-nrfutil DFU process completed successfully.")
else:
logger.error(f"adafruit-nrfutil failed with exit code {result.returncode}")
except FileNotFoundError:
logger.error("Error: 'adafruit-nrfutil' command not found.")
logger.error("Please ensure it's installed and in your system's PATH or virtual environment.")
except Exception as e:
logger.error(f"An unexpected error occurred while running adafruit-nrfutil: {e}")
logger.exception("Detailed traceback:")
# Remove the old implementation based on nordicsemi library
# backend = None
# scanner = BleakScanner()
# try:
# logger.info(f"Scanning for device {device_address}...")
# device = await scanner.find_device_by_address(device_address, timeout=20.0)
# if not device:
# logger.error(f"Device with address {device_address} not found after 20 seconds.")
# return
#
# logger.info(f"Found device: {device.name} ({device.address})")
#
# # Nordic DFU library expects the backend instance
# backend = DfuTransportBle(target_device_identifier=device.address)
#
# # Configure DFU options
# # Note: nordicsemi-dfu logs progress internally
# dfu_proc = Dfu(zip_file_path=zip_file_path, dfu_transport=backend)
#
# logger.info("Initiating DFU...")
# dfu_proc.dfu_send_images()
# logger.info("DFU process completed successfully.")
#
# except BleakError as e:
# logger.error(f"BLE Error: {e}")
# except Exception as e:
# logger.error(f"An unexpected error occurred during DFU: {e}")
# logger.exception("Detailed traceback:") # Log full traceback for debugging
# finally:
# if backend:
# logger.debug("Closing DFU transport backend.")
# # The nordicsemi library handles BLE disconnection internally within DfuTransportBle
# # No explicit client.disconnect() needed here unless we manage the client separately
# pass # backend.close() might be needed in some backend implementations or future versions
pass # Replace with actual DFU implementation
# --- Argument Parser ---
def parse_args():
parser = argparse.ArgumentParser(description="Perform Nordic Secure DFU over BLE.")
parser.add_argument("zip_file", help="Path to the DFU package (.zip file).", type=Path)
parser.add_argument("-a", "--address", help="MAC address of the target BLE device.", required=True)
# parser.add_argument("-n", "--name", help="Name of the target BLE device (alternative to address).") # TODO: Add name-based discovery
parser.add_argument("-v", "--verbose", help="Enable debug logging.", action="store_true")
return parser.parse_args()
# --- Main Execution ---
async def main():
args = parse_args()
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
logger.setLevel(logging.DEBUG)
logging.getLogger('nordicsemi').setLevel(logging.INFO) # More verbose DFU logs
if not args.zip_file.is_file():
logger.error(f"Error: DFU file not found at {args.zip_file}")
sys.exit(1)
# TODO: Validate address format?
start_time = time.monotonic()
await perform_dfu(args.address, str(args.zip_file))
end_time = time.monotonic()
logger.info(f"DFU process finished in {end_time - start_time:.2f} seconds.")
if __name__ == "__main__":
asyncio.run(main())