|
1 | 1 | """MicroPython tool""" |
2 | 2 |
|
3 | 3 | import argparse as _argparse |
| 4 | +import datetime as _datetime |
4 | 5 | import fnmatch as _fnmatch |
5 | 6 | import importlib.metadata as _metadata |
6 | 7 | import os as _os |
|
31 | 32 | _CMD_ORDER = [ |
32 | 33 | 'ls', 'tree', 'cat', 'cp', 'mv', 'mkdir', 'rm', 'pwd', 'cd', 'path', |
33 | 34 | 'stop', 'reset', 'monitor', 'repl', 'exec', 'run', 'edit', 'info', |
34 | | - 'flash', 'mount', 'ln', 'speedtest', 'sleep', |
| 35 | + 'rtc', 'flash', 'mount', 'ln', 'speedtest', 'sleep', |
35 | 36 | ] |
36 | 37 |
|
37 | 38 |
|
@@ -147,12 +148,12 @@ def _make_parser(method): |
147 | 148 | for group_opts in getattr(method, '_cmd_groups', []): |
148 | 149 | group = parser.add_mutually_exclusive_group() |
149 | 150 | for opt_args, opt_kwargs in getattr(method, '_cmd_options', []): |
150 | | - # Match by first option string (e.g., '--machine') |
151 | | - if opt_args and opt_args[0] in group_opts: |
| 151 | + # Match any option string (e.g., '-s' or '--set') |
| 152 | + if opt_args and any(opt in group_opts for opt in opt_args): |
152 | 153 | group.add_argument(*opt_args, **opt_kwargs) |
153 | 154 | # Add non-grouped options |
154 | 155 | for opt_args, opt_kwargs in getattr(method, '_cmd_options', []): |
155 | | - if not opt_args or opt_args[0] not in grouped_opts: |
| 156 | + if not opt_args or not any(opt in grouped_opts for opt in opt_args): |
156 | 157 | parser.add_argument(*opt_args, **opt_kwargs) |
157 | 158 | # Add positional arguments |
158 | 159 | for args, kwargs in getattr(method, '_cmd_args', []): |
@@ -957,6 +958,63 @@ def _dispatch_edit(self, commands, is_last_group): |
957 | 958 | path = _parse_device_path(args.path, 'edit') |
958 | 959 | self.cmd_edit(path, editor=args.editor) |
959 | 960 |
|
| 961 | + @command('rtc', 'Get or set device RTC.') |
| 962 | + @mutually_exclusive('--set', '--local', '--utc') |
| 963 | + @option('-s', '--set', action='store_true', |
| 964 | + help='set to local PC time') |
| 965 | + @option('-l', '--local', action='store_true', |
| 966 | + help='set to local PC time') |
| 967 | + @option('-u', '--utc', action='store_true', |
| 968 | + help='set to UTC time') |
| 969 | + @argument('datetime', nargs='?', metavar='DATETIME', |
| 970 | + help='datetime string (YYYY-MM-DD HH:MM:SS)') |
| 971 | + def _dispatch_rtc(self, commands, is_last_group): |
| 972 | + args = _make_parser(self._dispatch_rtc).parse_args(commands) |
| 973 | + commands.clear() |
| 974 | + if args.datetime: |
| 975 | + if args.set or args.local or args.utc: |
| 976 | + raise ParamsError( |
| 977 | + 'datetime argument is incompatible with --set/--local/--utc') |
| 978 | + try: |
| 979 | + parsed = _datetime.datetime.strptime( |
| 980 | + args.datetime, '%Y-%m-%d %H:%M:%S') |
| 981 | + except ValueError: |
| 982 | + raise ParamsError( |
| 983 | + 'invalid datetime format (use YYYY-MM-DD HH:MM:SS)') |
| 984 | + self._set_rtc(parsed) |
| 985 | + self.verbose( |
| 986 | + f"RTC set to {parsed.strftime('%Y-%m-%d %H:%M:%S')}", 1) |
| 987 | + elif args.set or args.local: |
| 988 | + now = _datetime.datetime.now() |
| 989 | + self._set_rtc(now) |
| 990 | + self.verbose( |
| 991 | + f"RTC set to {now.strftime('%Y-%m-%d %H:%M:%S')} (local)", 1) |
| 992 | + elif args.utc: |
| 993 | + now = _datetime.datetime.now(_datetime.timezone.utc) |
| 994 | + self._set_rtc(now) |
| 995 | + self.verbose( |
| 996 | + f"RTC set to {now.strftime('%Y-%m-%d %H:%M:%S')} (UTC)", 1) |
| 997 | + else: |
| 998 | + rtc = self._get_rtc() |
| 999 | + print( |
| 1000 | + f"{rtc[0]:04d}-{rtc[1]:02d}-{rtc[2]:02d} " |
| 1001 | + f"{rtc[4]:02d}:{rtc[5]:02d}:{rtc[6]:02d}") |
| 1002 | + |
| 1003 | + def _set_rtc(self, dt_obj): |
| 1004 | + """Set device RTC from datetime object""" |
| 1005 | + timetuple = ( |
| 1006 | + dt_obj.year, dt_obj.month, dt_obj.day, dt_obj.weekday(), |
| 1007 | + dt_obj.hour, dt_obj.minute, dt_obj.second, dt_obj.microsecond) |
| 1008 | + self.mpy.comm.exec( |
| 1009 | + f"__import__('machine').RTC().datetime({timetuple})") |
| 1010 | + |
| 1011 | + def _get_rtc(self): |
| 1012 | + """Get device RTC as tuple""" |
| 1013 | + import ast |
| 1014 | + result = self.mpy.comm.exec( |
| 1015 | + "print(__import__('machine').RTC().datetime())") |
| 1016 | + return ast.literal_eval(result.decode().strip()) |
| 1017 | + |
960 | 1018 | @command('info', 'Show device info (platform, memory, filesystem).') |
961 | 1019 | def _dispatch_info(self, commands, is_last_group): |
962 | 1020 | _, commands[:] = _make_parser(self._dispatch_info).parse_known_args( |
@@ -1274,7 +1332,7 @@ def _dispatch_args(self, commands, is_last_group): |
1274 | 1332 | _COMMANDS = frozenset({ |
1275 | 1333 | 'ls', 'tree', 'cat', 'mkdir', 'rm', 'pwd', 'cd', 'path', |
1276 | 1334 | 'reset', 'stop', 'monitor', 'repl', 'exec', 'run', 'edit', 'info', |
1277 | | - 'flash', 'sleep', 'cp', 'mv', 'mount', 'ln', 'speedtest', |
| 1335 | + 'rtc', 'flash', 'sleep', 'cp', 'mv', 'mount', 'ln', 'speedtest', |
1278 | 1336 | '_paths', '_ports', '_commands', '_options', '_args', |
1279 | 1337 | }) |
1280 | 1338 |
|
|
0 commit comments