micrOSDevToolKit 2.20.0__py3-none-any.whl → 2.21.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of micrOSDevToolKit might be problematic. Click here for more details.
- micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +19 -15
- micrOS/source/Config.py +5 -2
- micrOS/source/Espnow.py +99 -40
- micrOS/source/Files.py +50 -23
- micrOS/source/InterConnect.py +9 -3
- micrOS/source/Pacman.py +141 -0
- micrOS/source/Shell.py +1 -1
- micrOS/source/Tasks.py +4 -1
- micrOS/source/modules/LM_oled_ui.py +39 -41
- micrOS/source/modules/LM_oledui.py +56 -85
- micrOS/source/modules/LM_pacman.py +36 -44
- micrOS/source/urequests.py +1 -1
- {microsdevtoolkit-2.20.0.dist-info → microsdevtoolkit-2.21.0.dist-info}/METADATA +9 -6
- {microsdevtoolkit-2.20.0.dist-info → microsdevtoolkit-2.21.0.dist-info}/RECORD +34 -32
- toolkit/DevEnvOTA.py +2 -2
- toolkit/DevEnvUSB.py +1 -1
- toolkit/lib/micrOSClient.py +37 -15
- toolkit/lib/micrOSClientHistory.py +35 -1
- toolkit/simulator_lib/__pycache__/uos.cpython-312.pyc +0 -0
- toolkit/simulator_lib/uos.py +1 -0
- toolkit/workspace/precompiled/Config.mpy +0 -0
- toolkit/workspace/precompiled/Espnow.mpy +0 -0
- toolkit/workspace/precompiled/Files.mpy +0 -0
- toolkit/workspace/precompiled/InterConnect.mpy +0 -0
- toolkit/workspace/precompiled/Pacman.mpy +0 -0
- toolkit/workspace/precompiled/Shell.mpy +0 -0
- toolkit/workspace/precompiled/Tasks.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_oled_ui.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_oledui.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_pacman.mpy +0 -0
- {microsdevtoolkit-2.20.0.data → microsdevtoolkit-2.21.0.data}/scripts/devToolKit.py +0 -0
- {microsdevtoolkit-2.20.0.dist-info → microsdevtoolkit-2.21.0.dist-info}/WHEEL +0 -0
- {microsdevtoolkit-2.20.0.dist-info → microsdevtoolkit-2.21.0.dist-info}/licenses/LICENSE +0 -0
- {microsdevtoolkit-2.20.0.dist-info → microsdevtoolkit-2.21.0.dist-info}/top_level.txt +0 -0
micrOS/source/Pacman.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from mip import install
|
|
2
|
+
from Files import OSPath, path_join, is_file
|
|
3
|
+
from Debug import syslog
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# ---------------------------------------------------------------------
|
|
7
|
+
# Utility helpers
|
|
8
|
+
# ---------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
def _normalize_source(ref):
|
|
11
|
+
"""
|
|
12
|
+
Normalize GitHub URLs or shorthand for mip compatibility.
|
|
13
|
+
Converts:
|
|
14
|
+
- https://github.com/user/repo/blob/branch/path/file.py → https://raw.githubusercontent.com/user/repo/branch/path/file.py
|
|
15
|
+
- https://github.com/user/repo/tree/branch/path → github:user/repo/path
|
|
16
|
+
Returns (normalized_ref, branch)
|
|
17
|
+
"""
|
|
18
|
+
try:
|
|
19
|
+
ref = ref.strip().rstrip("/")
|
|
20
|
+
# Already in github: shorthand
|
|
21
|
+
if ref.startswith("github:"):
|
|
22
|
+
return ref, None
|
|
23
|
+
|
|
24
|
+
if ref.startswith("https://"):
|
|
25
|
+
ref = ref.replace("https://", "")
|
|
26
|
+
if ref.startswith("github.com"):
|
|
27
|
+
# Folder (tree) case → github:user/repo/path
|
|
28
|
+
if "/tree/" in ref:
|
|
29
|
+
print("[mip-normalize] detected GitHub tree folder link")
|
|
30
|
+
parts = ref.split("/")
|
|
31
|
+
user, repo = parts[1], parts[2]
|
|
32
|
+
branch = parts[4]
|
|
33
|
+
path = "/".join(parts[5:])
|
|
34
|
+
github_ref = f"github:{user}/{repo}/{path}".rstrip("/")
|
|
35
|
+
return github_ref, branch
|
|
36
|
+
|
|
37
|
+
# File (blob) case → raw.githubusercontent.com
|
|
38
|
+
if "/blob/" in ref:
|
|
39
|
+
print("[mip-normalize] detected GitHub blob file link")
|
|
40
|
+
url_base = "https://raw.githubusercontent.com/"
|
|
41
|
+
ref = ref.replace("github.com/", url_base).replace("/blob", "")
|
|
42
|
+
return ref, None
|
|
43
|
+
|
|
44
|
+
# Direct GitHub file (no blob/tree) → github:user/repo/path
|
|
45
|
+
if ref.count("/") >= 2:
|
|
46
|
+
print("[mip-normalize] direct GitHub path (no blob/tree)")
|
|
47
|
+
parts = ref.split("/")
|
|
48
|
+
user, repo = parts[1], parts[2]
|
|
49
|
+
path = "/".join(parts[3:])
|
|
50
|
+
github_ref = f"github:{user}/{repo}/{path}".rstrip("/")
|
|
51
|
+
return github_ref, None
|
|
52
|
+
|
|
53
|
+
print("[mip-normalize] unchanged")
|
|
54
|
+
return ref, None
|
|
55
|
+
|
|
56
|
+
except Exception as e:
|
|
57
|
+
syslog(f"[ERR][pacman] normalize failed: {ref}: {e}")
|
|
58
|
+
print(f"[normalize][ERROR] {e}")
|
|
59
|
+
return str(ref), None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ---------------------------------------------------------------------
|
|
63
|
+
# Core installer
|
|
64
|
+
# ---------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
def _install_any(ref, target=None):
|
|
67
|
+
"""Internal wrapper with consistent error handling and debug output."""
|
|
68
|
+
verdict = f"[mip] Installing: {ref}\n"
|
|
69
|
+
try:
|
|
70
|
+
ref, branch = _normalize_source(ref)
|
|
71
|
+
kwargs = {}
|
|
72
|
+
if branch:
|
|
73
|
+
kwargs["version"] = branch
|
|
74
|
+
kwargs["target"] = target or OSPath.LIB
|
|
75
|
+
verdict = f"[mip] Installing: {ref} {kwargs}\n"
|
|
76
|
+
# MIP Install
|
|
77
|
+
install(ref, **kwargs)
|
|
78
|
+
verdict += f" ✓ Installed successfully under {kwargs["target"]}"
|
|
79
|
+
except Exception as e:
|
|
80
|
+
err = f" ✗ Failed to install '{ref}': {e}"
|
|
81
|
+
syslog(f"[ERR][pacman] {err}")
|
|
82
|
+
verdict += err
|
|
83
|
+
return verdict
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ---------------------------------------------------------------------
|
|
87
|
+
# Public install functions
|
|
88
|
+
# ---------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
def install_requirements(source="requirements.txt"):
|
|
91
|
+
"""Install from a requirements.txt file under /config."""
|
|
92
|
+
verdict = f"[mip] Installing from requirements file: {source}\n"
|
|
93
|
+
try:
|
|
94
|
+
source_path = path_join(OSPath.CONFIG, source)
|
|
95
|
+
verdict = f"[mip] Installing from requirements file: {source_path}\n"
|
|
96
|
+
if is_file(source_path):
|
|
97
|
+
install(source_path)
|
|
98
|
+
verdict += " ✓ All listed packages processed"
|
|
99
|
+
else:
|
|
100
|
+
err = f" ✗ {source_path} not exists"
|
|
101
|
+
syslog(f"[ERR][pacman] {err}")
|
|
102
|
+
verdict += err
|
|
103
|
+
except Exception as e:
|
|
104
|
+
err = f" ✗ Failed to process {source}: {e}"
|
|
105
|
+
syslog(f"[ERR][pacman] {err}")
|
|
106
|
+
verdict += err
|
|
107
|
+
return verdict
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------------------
|
|
111
|
+
# Unified entry point
|
|
112
|
+
# ---------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
def download(ref):
|
|
115
|
+
"""
|
|
116
|
+
Unified mip-based downloader for micrOS.
|
|
117
|
+
Automatically detects:
|
|
118
|
+
- requirements.txt files (local or remote)
|
|
119
|
+
- Single-file load modules (LM_/IO_ names or URLs)
|
|
120
|
+
- GitHub or raw URLs (tree/blob/github:)
|
|
121
|
+
- Official MicroPython packages
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
if not ref:
|
|
125
|
+
return "[mip] Nothing to download (empty input)"
|
|
126
|
+
|
|
127
|
+
# 1. requirements.txt
|
|
128
|
+
if ref == "requirements.txt":
|
|
129
|
+
return install_requirements(ref)
|
|
130
|
+
|
|
131
|
+
if "github" in ref:
|
|
132
|
+
# 2. LM_/IO_ load modules → /modules
|
|
133
|
+
if ref.endswith("py") and ("LM_" in ref or "IO_" in ref):
|
|
134
|
+
return _install_any(ref, target=OSPath.MODULES)
|
|
135
|
+
|
|
136
|
+
# 3. GitHub or raw URLs → /lib
|
|
137
|
+
if ref.startswith("http") or ref.startswith("github"):
|
|
138
|
+
return _install_any(ref, target=OSPath.LIB)
|
|
139
|
+
|
|
140
|
+
# 4. Fallback: official micropython package → /lib
|
|
141
|
+
return _install_any(ref, target=OSPath.LIB)
|
micrOS/source/Shell.py
CHANGED
micrOS/source/Tasks.py
CHANGED
|
@@ -10,6 +10,7 @@ Designed by Marcell Ban aka BxNxM
|
|
|
10
10
|
#################################################################
|
|
11
11
|
from sys import modules
|
|
12
12
|
from json import dumps
|
|
13
|
+
from re import match
|
|
13
14
|
import uasyncio as asyncio
|
|
14
15
|
from micropython import schedule
|
|
15
16
|
from utime import ticks_ms, ticks_diff
|
|
@@ -458,8 +459,10 @@ def exec_builtins(func):
|
|
|
458
459
|
# ... >json - command output format option
|
|
459
460
|
# ... >>node01.local - intercon: command execution on remote device by hostname/IP address
|
|
460
461
|
arg_list, json_flag = (arg_list[:-1], True) if arg_list[-1] == '>json' else (arg_list, False)
|
|
461
|
-
arg_list, intercon_target = (arg_list[:-1], arg_list[-1].replace(">>", "")) if arg_list[-1].startswith('>>') else (arg_list, None)
|
|
462
462
|
json_flag = jsonify if isinstance(jsonify, bool) else json_flag
|
|
463
|
+
arg_list, intercon_target = ((arg_list[:-1], arg_list[-1].replace(">>", ""))
|
|
464
|
+
if match(r'^>>[A-Za-z0-9._-]+$', arg_list[-1])
|
|
465
|
+
else (arg_list, None))
|
|
463
466
|
|
|
464
467
|
# INTERCONNECT
|
|
465
468
|
if intercon_target:
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
"""
|
|
2
|
+
micrOS simple OLED UI (irq or single task based refresh)
|
|
3
|
+
- with page generation
|
|
4
|
+
Designed by Marcell Ban aka BxNxM
|
|
5
|
+
"""
|
|
6
|
+
|
|
1
7
|
from Config import cfgget
|
|
2
8
|
from utime import localtime
|
|
3
9
|
from network import WLAN, STA_IF
|
|
@@ -236,8 +242,8 @@ class PageUI:
|
|
|
236
242
|
else:
|
|
237
243
|
self.page_callback_list[self.active_page]() # <== Execute page functions
|
|
238
244
|
except Exception as e:
|
|
239
|
-
PageUI.PAGE_UI_OBJ.show_msg = f"Err: {e}" # Show page error in msgbox
|
|
240
245
|
syslog(f"oled_ui render error: {e}")
|
|
246
|
+
PageUI.PAGE_UI_OBJ.show_msg = f"Err: {e}" # Show page error in msgbox
|
|
241
247
|
PageUI.DISPLAY.show()
|
|
242
248
|
self.__power_save()
|
|
243
249
|
else:
|
|
@@ -332,16 +338,17 @@ class PageUI:
|
|
|
332
338
|
#####################################
|
|
333
339
|
# PAGE GENERATORS #
|
|
334
340
|
#####################################
|
|
335
|
-
def intercon_page(self,
|
|
341
|
+
def intercon_page(self, cmd:str, run=False):
|
|
336
342
|
"""Generic interconnect page core - create multiple page with it"""
|
|
337
343
|
posx, posy = 5, 12
|
|
338
344
|
|
|
339
345
|
def _button():
|
|
346
|
+
nonlocal host, _cmd
|
|
340
347
|
# BUTTON CALLBACK - INTERCONNECT execution
|
|
341
348
|
self.open_intercons.append(host)
|
|
342
349
|
try:
|
|
343
350
|
# Send CMD to other device & show result
|
|
344
|
-
state, data_meta = exec_cmd(
|
|
351
|
+
state, data_meta = exec_cmd(_cmd, jsonify=True)
|
|
345
352
|
if state:
|
|
346
353
|
self.cmd_task_tag = list(data_meta.keys())[0]
|
|
347
354
|
verdict = list(data_meta.values())[0]
|
|
@@ -354,21 +361,24 @@ class PageUI:
|
|
|
354
361
|
self.open_intercons.remove(host)
|
|
355
362
|
|
|
356
363
|
# Check open host connection
|
|
357
|
-
|
|
358
|
-
|
|
364
|
+
_cmd = cmd.strip().split()
|
|
365
|
+
host = _cmd[-1].replace(">", "").replace("&", "")
|
|
359
366
|
# Draw host + cmd details
|
|
360
|
-
PageUI.DISPLAY.text(
|
|
361
|
-
PageUI.DISPLAY.text(
|
|
367
|
+
PageUI.DISPLAY.text(' '.join(_cmd[0:-1]), 0, posy)
|
|
368
|
+
PageUI.DISPLAY.text(_cmd[-1], posx, posy+10)
|
|
369
|
+
self._cmd_text(posx, posy + 10)
|
|
370
|
+
|
|
362
371
|
# Update display output with retrieved task result (by TaskID)
|
|
363
372
|
if self.cmd_task_tag is not None:
|
|
364
373
|
task_buffer = manage_task(self.cmd_task_tag, 'show').replace(' ', '')
|
|
365
374
|
if task_buffer is not None and len(task_buffer) > 0:
|
|
366
375
|
# Set display out to task buffered data
|
|
367
376
|
self.cmd_out = task_buffer
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
377
|
+
if not manage_task(self.cmd_task_tag, 'isbusy'):
|
|
378
|
+
# Kill task - clean
|
|
379
|
+
manage_task(self.cmd_task_tag, 'kill')
|
|
380
|
+
# data gathered - remove tag - skip re-read
|
|
381
|
+
self.cmd_task_tag = None
|
|
372
382
|
# Show self.cmd_out value on display
|
|
373
383
|
self._cmd_text(posx, posy+10)
|
|
374
384
|
# Run button event at page init
|
|
@@ -378,7 +388,7 @@ class PageUI:
|
|
|
378
388
|
# Set button press callback (+draw button)
|
|
379
389
|
self.set_press_callback(_button)
|
|
380
390
|
|
|
381
|
-
def cmd_call_page(self, cmd, run=False):
|
|
391
|
+
def cmd_call_page(self, cmd:str, run=False):
|
|
382
392
|
"""Generic LoadModule execution page core - create multiple page with it"""
|
|
383
393
|
posx, posy = 5, 12
|
|
384
394
|
|
|
@@ -429,8 +439,14 @@ def _intercon_cache(line_limit=3):
|
|
|
429
439
|
cache = hosts()["intercon"]
|
|
430
440
|
if sum([1 for _ in cache]) > 0:
|
|
431
441
|
for key, val in cache.items():
|
|
432
|
-
|
|
433
|
-
|
|
442
|
+
if '.' in key:
|
|
443
|
+
# IP splitting
|
|
444
|
+
key = key.split('.')[0]
|
|
445
|
+
val = '.'.join(val.split('.')[-2:])
|
|
446
|
+
else:
|
|
447
|
+
# MAC splitting
|
|
448
|
+
key = key.split(':')[0]
|
|
449
|
+
val = ':'.join(val.split(':')[-2:])
|
|
434
450
|
PageUI.DISPLAY.text(f" {val} {key}", 0, line_start+(line_cnt*10))
|
|
435
451
|
line_cnt += 1
|
|
436
452
|
if line_cnt > line_limit:
|
|
@@ -514,8 +530,7 @@ def msgbox(msg='micrOS msg'):
|
|
|
514
530
|
PageUI.PAGE_UI_OBJ.render_page()
|
|
515
531
|
return 'Show msg'
|
|
516
532
|
|
|
517
|
-
|
|
518
|
-
def intercon_genpage(cmd:str=None, run=False):
|
|
533
|
+
def genpage(cmd:str=None, run=False):
|
|
519
534
|
"""
|
|
520
535
|
Create intercon pages dynamically :)
|
|
521
536
|
- based on cmd value.
|
|
@@ -523,31 +538,15 @@ def intercon_genpage(cmd:str=None, run=False):
|
|
|
523
538
|
:param run: run button event at page init: True/False
|
|
524
539
|
:return: page creation verdict
|
|
525
540
|
"""
|
|
526
|
-
raw = cmd.split()
|
|
527
|
-
host = raw[0]
|
|
528
|
-
cmd = raw[1:]
|
|
529
|
-
try:
|
|
530
|
-
# Create page for intercon command
|
|
531
|
-
PageUI.PAGE_UI_OBJ.add_page(lambda: PageUI.PAGE_UI_OBJ.intercon_page(host, cmd, run=run))
|
|
532
|
-
except Exception as e:
|
|
533
|
-
syslog(f'[ERR] intercon_genpage: {e}')
|
|
534
|
-
return str(e)
|
|
535
|
-
return True
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
def cmd_genpage(cmd:str=None, run=False):
|
|
539
|
-
"""
|
|
540
|
-
Create load module execution pages dynamically :)
|
|
541
|
-
- based on cmd value: load_module function (args)
|
|
542
|
-
:param cmd: 'load_module function (args)' string
|
|
543
|
-
:param run: run button event at page init: True/False
|
|
544
|
-
:return: page creation verdict
|
|
545
|
-
"""
|
|
546
541
|
try:
|
|
547
|
-
|
|
548
|
-
|
|
542
|
+
if ">>" in cmd or "&" in cmd:
|
|
543
|
+
# Create page for intercon/task background command
|
|
544
|
+
PageUI.PAGE_UI_OBJ.add_page(lambda: PageUI.PAGE_UI_OBJ.intercon_page(cmd, run=run))
|
|
545
|
+
else:
|
|
546
|
+
# Create page for realtime command
|
|
547
|
+
PageUI.PAGE_UI_OBJ.add_page(lambda: PageUI.PAGE_UI_OBJ.cmd_call_page(cmd, run=run))
|
|
549
548
|
except Exception as e:
|
|
550
|
-
syslog(f'[ERR]
|
|
549
|
+
syslog(f'[ERR] genpage: {e}')
|
|
551
550
|
return str(e)
|
|
552
551
|
return True
|
|
553
552
|
|
|
@@ -598,6 +597,5 @@ def help(widgets=False):
|
|
|
598
597
|
'draw',
|
|
599
598
|
'BUTTON control cmd=<prev,press,next,on,off>',
|
|
600
599
|
'msgbox "msg"',
|
|
601
|
-
'
|
|
602
|
-
'cmd_genpage "cmd" run=False',
|
|
600
|
+
'genpage "cmd" run=False',
|
|
603
601
|
'pinmap'), widgets=widgets)
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"""
|
|
2
|
+
micrOS multitask OLED UI
|
|
3
|
+
- with page generation
|
|
4
|
+
Designed by Marcell Ban aka BxNxM
|
|
5
|
+
"""
|
|
1
6
|
from utime import localtime, ticks_ms, ticks_diff, sleep_ms
|
|
2
7
|
from Common import syslog, micro_task, manage_task, exec_cmd
|
|
3
8
|
from Types import resolve
|
|
@@ -738,79 +743,59 @@ class PageUI:
|
|
|
738
743
|
:param y: frame y
|
|
739
744
|
"""
|
|
740
745
|
x, y = x+2, y+4
|
|
741
|
-
def _execute(display, w, h, x, y):
|
|
742
|
-
nonlocal cmd
|
|
743
|
-
try:
|
|
744
|
-
cmd_list = cmd.strip().split()
|
|
745
|
-
# Send CMD to other device & show result
|
|
746
|
-
state, out = exec_cmd(cmd_list)
|
|
747
|
-
cmd_out = out.strip()
|
|
748
|
-
except Exception as e:
|
|
749
|
-
cmd_out = str(e)
|
|
750
|
-
self.app_frame.press_output = cmd_out
|
|
751
|
-
PageUI.write_lines(cmd_out, display, x, y + 15)
|
|
752
|
-
|
|
753
|
-
display.text(cmd, x, y)
|
|
754
|
-
if run:
|
|
755
|
-
_execute(display, w, h, x, y)
|
|
756
|
-
else:
|
|
757
|
-
self._press_indicator(display, w, h, x, y)
|
|
758
|
-
PageUI.write_lines(self.app_frame.press_output, display, x, y + 15)
|
|
759
|
-
# Return "press" callback, mandatory input parameters: display, w, h, x, y
|
|
760
|
-
return {"press": _execute}
|
|
761
|
-
return
|
|
762
746
|
|
|
747
|
+
def _display_output():
|
|
748
|
+
nonlocal x, y, display
|
|
749
|
+
if self._cmd_task_tag is None:
|
|
750
|
+
# Display cached data
|
|
751
|
+
PageUI.write_lines(self.app_frame.press_output, display, x, y + 20)
|
|
752
|
+
return
|
|
753
|
+
task_buffer = manage_task(self._cmd_task_tag, 'show').replace(' ', '')
|
|
754
|
+
if task_buffer is not None and len(task_buffer) > 0:
|
|
755
|
+
# Update display out to task buffered data
|
|
756
|
+
self.app_frame.press_output = task_buffer
|
|
757
|
+
if not manage_task(self._cmd_task_tag, 'isbusy'):
|
|
758
|
+
# data gathered - remove tag - skip re-read
|
|
759
|
+
self._cmd_task_tag = None
|
|
760
|
+
# Display task cached data
|
|
761
|
+
PageUI.write_lines(self.app_frame.press_output, display, x, y + 20)
|
|
763
762
|
|
|
764
|
-
def intercon_exec_page(self, host, cmd, run, display, w, h, x, y):
|
|
765
|
-
"""
|
|
766
|
-
:param host: hostname or IP address of a device
|
|
767
|
-
:param cmd: load module string command
|
|
768
|
-
:param run: auto-run command (every page refresh)
|
|
769
|
-
:param display: display instance
|
|
770
|
-
:param h: frame h
|
|
771
|
-
:param w: frame w
|
|
772
|
-
:param x: frame x
|
|
773
|
-
:param y: frame y
|
|
774
|
-
"""
|
|
775
|
-
x, y = x+2, y+4
|
|
776
763
|
def _execute(display, w, h, x, y):
|
|
777
|
-
nonlocal
|
|
778
|
-
# Check open host connection
|
|
764
|
+
nonlocal cmd, run
|
|
779
765
|
try:
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
if
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
if
|
|
786
|
-
self.
|
|
766
|
+
cmd_list = cmd.strip().split()
|
|
767
|
+
# TASK mode: background execution, intercon: >> OR task: &
|
|
768
|
+
if '>>' in cmd_list[-1] or '&' in cmd_list[-1]:
|
|
769
|
+
# BACKGROUND: EXECUTE COMMAND
|
|
770
|
+
state, out = exec_cmd(cmd_list, jsonify=True) if self._cmd_task_tag is None else (False, "skip...")
|
|
771
|
+
if state:
|
|
772
|
+
self._cmd_task_tag = list(out.keys())[0]
|
|
773
|
+
buffer = manage_task(self._cmd_task_tag, 'show').replace(' ', '')
|
|
774
|
+
if buffer is not None and len(buffer) > 0:
|
|
775
|
+
self.app_frame.press_output = buffer
|
|
787
776
|
else:
|
|
788
|
-
|
|
777
|
+
# REALTIME mode: get command execution result
|
|
778
|
+
state, out = exec_cmd(cmd_list, jsonify=True)
|
|
779
|
+
self.app_frame.press_output = str(out)
|
|
789
780
|
except Exception as e:
|
|
790
781
|
self.app_frame.press_output = str(e)
|
|
782
|
+
# Print and cache output to display
|
|
783
|
+
PageUI.write_lines(self.app_frame.press_output, display, x, y+20)
|
|
791
784
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
if task_buffer is not None and len(task_buffer) > 0:
|
|
797
|
-
# Set display out to task buffered data
|
|
798
|
-
self.app_frame.press_output = task_buffer
|
|
799
|
-
# data gathered - remove tag - skip re-read
|
|
800
|
-
self._cmd_task_tag = None
|
|
801
|
-
PageUI.write_lines(self.app_frame.press_output, display, x, y + 20, line_limit=2)
|
|
802
|
-
|
|
803
|
-
PageUI.write_lines(f"{host.split(".")[0]}:{' '.join(cmd)}", display, x, y, line_limit=2)
|
|
785
|
+
# Write command header line and buffered output
|
|
786
|
+
PageUI.write_lines(cmd, display, x, y, line_limit=2)
|
|
787
|
+
_display_output()
|
|
788
|
+
# RUN command
|
|
804
789
|
if run:
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
_read_buffer()
|
|
790
|
+
# Automatic Execution Mode (in page refresh time)
|
|
791
|
+
_execute(display, w, h, x, y)
|
|
792
|
+
return None
|
|
793
|
+
# Button Press Execution Mode (callback)
|
|
810
794
|
self._press_indicator(display, w, h, x, y)
|
|
811
795
|
# Return "press" callback, mandatory input parameters: display, w, h, x, y
|
|
812
796
|
return {"press": _execute}
|
|
813
797
|
|
|
798
|
+
|
|
814
799
|
#################################################################################
|
|
815
800
|
# Page function #
|
|
816
801
|
#################################################################################
|
|
@@ -833,8 +818,14 @@ def _intercon_nodes_page(display, w, h, x, y):
|
|
|
833
818
|
cache = hosts()["intercon"]
|
|
834
819
|
if sum([1 for _ in cache]) > 0:
|
|
835
820
|
for key, val in cache.items():
|
|
836
|
-
|
|
837
|
-
|
|
821
|
+
if '.' in key:
|
|
822
|
+
# IP splitting
|
|
823
|
+
key = key.split('.')[0]
|
|
824
|
+
val = '.'.join(val.split('.')[-2:])
|
|
825
|
+
else:
|
|
826
|
+
# MAC splitting
|
|
827
|
+
key = key.split(':')[0]
|
|
828
|
+
val = ':'.join(val.split(':')[-2:])
|
|
838
829
|
display.text(f" {val} {key}", x, line_start + (line_cnt * 10))
|
|
839
830
|
line_cnt += 1
|
|
840
831
|
if line_cnt > line_limit:
|
|
@@ -900,7 +891,7 @@ def cursor(x, y):
|
|
|
900
891
|
return "Set cursor position"
|
|
901
892
|
|
|
902
893
|
|
|
903
|
-
def
|
|
894
|
+
def genpage(cmd=None, run=False):
|
|
904
895
|
"""
|
|
905
896
|
Create load module execution pages dynamically :)
|
|
906
897
|
- based on cmd value: load_module function (args)
|
|
@@ -915,30 +906,11 @@ def cmd_genpage(cmd=None, run=False):
|
|
|
915
906
|
# Create page for intercon command
|
|
916
907
|
PageUI.INSTANCE.add_page(lambda display, w, h, x, y: PageUI.INSTANCE.lm_exec_page(cmd, run, display, w, h, x, y))
|
|
917
908
|
except Exception as e:
|
|
918
|
-
syslog(f'[ERR]
|
|
909
|
+
syslog(f'[ERR] genpage: {e}')
|
|
919
910
|
return str(e)
|
|
920
911
|
return True
|
|
921
912
|
|
|
922
913
|
|
|
923
|
-
def intercon_genpage(cmd=None, run=False):
|
|
924
|
-
"""
|
|
925
|
-
Create intercon pages dynamically :)
|
|
926
|
-
- based on cmd value.
|
|
927
|
-
:param cmd: 'host hello' or 'host system clock'
|
|
928
|
-
:param run: run button event at page init: True/False
|
|
929
|
-
:return: page creation verdict
|
|
930
|
-
"""
|
|
931
|
-
raw = cmd.split()
|
|
932
|
-
host = raw[0]
|
|
933
|
-
cmd = raw[1:]
|
|
934
|
-
try:
|
|
935
|
-
# Create page for intercon command
|
|
936
|
-
PageUI.INSTANCE.add_page(lambda display, w, h, x, y: PageUI.INSTANCE.intercon_exec_page(host, cmd, run, display, w, h, x, y))
|
|
937
|
-
except Exception as e:
|
|
938
|
-
syslog(f'[ERR] intercon_genpage: {e}')
|
|
939
|
-
return str(e)
|
|
940
|
-
return True
|
|
941
|
-
|
|
942
914
|
def add_page(page_callback):
|
|
943
915
|
"""
|
|
944
916
|
[LM] Create page from load module with callback function
|
|
@@ -963,6 +935,5 @@ def help(widgets=False):
|
|
|
963
935
|
"BUTTON control cmd=<prev,press,next,on,off>",
|
|
964
936
|
"BUTTON debug", "cursor x y",
|
|
965
937
|
"popup msg='text'", "cancel_popup",
|
|
966
|
-
"
|
|
967
|
-
"intercon_genpage 'host cmd' run=False"),
|
|
938
|
+
"genpage cmd='system clock'"),
|
|
968
939
|
widgets=widgets)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from sys import modules
|
|
2
2
|
from Common import socket_stream
|
|
3
|
-
from Files import is_protected, list_fs, ilist_fs,
|
|
3
|
+
from Files import is_protected, list_fs, ilist_fs, remove_file, remove_dir, OSPath, path_join
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
#############################################
|
|
@@ -31,13 +31,22 @@ def ls(path="/", content='*', raw=False, select='*', core=False):
|
|
|
31
31
|
return lines
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
def rm(path,
|
|
34
|
+
def rm(path, force=False):
|
|
35
35
|
"""
|
|
36
36
|
Linux like rm command - delete app resources and folders
|
|
37
37
|
:param path: app resource name/path, ex.: LM_robustness.py
|
|
38
|
-
:param
|
|
38
|
+
:param force: bypasses protection check - sudo mode
|
|
39
39
|
"""
|
|
40
|
-
return
|
|
40
|
+
return remove_file(path, force)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def rmdir(path, force=False):
|
|
44
|
+
"""
|
|
45
|
+
Linux like rmdir command for directory deletion
|
|
46
|
+
:param path: app resource folder path, ex.: /lib/myapp
|
|
47
|
+
:param force: bypasses protection check - sudo mode
|
|
48
|
+
"""
|
|
49
|
+
return remove_dir(path, force)
|
|
41
50
|
|
|
42
51
|
|
|
43
52
|
def dirtree(path="/", raw=False, core=False):
|
|
@@ -64,44 +73,26 @@ def cat(path):
|
|
|
64
73
|
return content
|
|
65
74
|
|
|
66
75
|
|
|
67
|
-
def download(
|
|
76
|
+
def download(ref=None):
|
|
68
77
|
"""
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
Unified mip-based downloader for micrOS.
|
|
79
|
+
Automatically detects:
|
|
80
|
+
1. Official MicroPython packages (from https://micropython.org/pi/v2)
|
|
81
|
+
Example: pacman download "umqtt.simple"
|
|
82
|
+
2. Single-file load modules (LM_/IO_ names or URLs)
|
|
83
|
+
Example: pacman download "https://github.com/BxNxM/micrOS/blob/master/toolkit/workspace/precompiled/modules/LM_rgb.mpy"
|
|
84
|
+
pacman download "github.com/BxNxM/micrOS/blob/master/toolkit/workspace/precompiled/modules/LM_rgb.mpy"
|
|
85
|
+
3. GitHub packages (folders via tree/blob URLs or github: form)
|
|
86
|
+
Example: pacman download "github:peterhinch/micropython-mqtt"
|
|
87
|
+
pacman download "https://github.com/peterhinch/micropython-mqtt/tree/master"
|
|
88
|
+
pacman download "https://github.com/peterhinch/micropython-mqtt/blob/master/package.json"
|
|
89
|
+
pacman download "https://github.com/peterhinch/micropython-mqtt"
|
|
90
|
+
[NOK] pacman download "https://github.com/basanovase/sim7600/tree/main/sim7600" -> Package not found: github:basanovase/sim7600/package.json
|
|
91
|
+
4. Install from local /config/requirements.txt file
|
|
92
|
+
Example: pacman download "requirements.txt"
|
|
72
93
|
"""
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
try:
|
|
76
|
-
verdict += f"Install {url}\n"
|
|
77
|
-
if target is None:
|
|
78
|
-
install(url) # Default download: /lib
|
|
79
|
-
else:
|
|
80
|
-
install(url, target=target) # Custom target
|
|
81
|
-
verdict += "\n|- Done"
|
|
82
|
-
except Exception as e:
|
|
83
|
-
verdict += f"|- Cannot install: {url}\n{e}"
|
|
84
|
-
return verdict
|
|
85
|
-
|
|
86
|
-
from mip import install
|
|
87
|
-
verdict = ""
|
|
88
|
-
if url is None and package is None:
|
|
89
|
-
return "Nothing to download, url=None package=None"
|
|
90
|
-
if package is None:
|
|
91
|
-
verdict += "Install from GitHub URL"
|
|
92
|
-
base_url = "https://raw.githubusercontent.com/"
|
|
93
|
-
file_name = url.split("/")[-1]
|
|
94
|
-
if not(file_name.endswith("py") and file_name.startswith("LM_")):
|
|
95
|
-
return "Invalid file name in url ending, hint: /LM_*.mpy or /LM_*.py"
|
|
96
|
-
# Convert GitHub URL to raw content URL
|
|
97
|
-
if "github.com" in url and "blob" in url:
|
|
98
|
-
url = url.replace("https://github.com/", base_url).replace("/blob", "")
|
|
99
|
-
else:
|
|
100
|
-
url = f"{base_url}{url}"
|
|
101
|
-
return _install(target=OSPath.MODULES) # Install module from Github URL
|
|
102
|
-
url = package
|
|
103
|
-
return _install() # Install official package
|
|
104
|
-
|
|
94
|
+
from Pacman import download as pm_download
|
|
95
|
+
return pm_download(ref)
|
|
105
96
|
|
|
106
97
|
def del_duplicates(migrate=True):
|
|
107
98
|
"""
|
|
@@ -119,7 +110,7 @@ def del_duplicates(migrate=True):
|
|
|
119
110
|
if m in py and m != 'main':
|
|
120
111
|
to_delete = f'{m}.py'
|
|
121
112
|
try:
|
|
122
|
-
verdict =
|
|
113
|
+
verdict = remove_file(path_join(modules_path, to_delete))
|
|
123
114
|
except:
|
|
124
115
|
verdict = "n/a"
|
|
125
116
|
state = False
|
|
@@ -130,7 +121,7 @@ def del_duplicates(migrate=True):
|
|
|
130
121
|
def _migrate_from_root(_rf):
|
|
131
122
|
nonlocal _deleted, files
|
|
132
123
|
if _rf in files:
|
|
133
|
-
|
|
124
|
+
remove_file(path_join(OSPath._ROOT, _rf))
|
|
134
125
|
if _rf in ("LM_pacman.mpy", "LM_system.mpy"):
|
|
135
126
|
# Delete protected LMs from root
|
|
136
127
|
remove(path_join(OSPath._ROOT, _rf))
|
|
@@ -188,7 +179,7 @@ def cachedump(delete=None, msgobj=None):
|
|
|
188
179
|
# Remove given cache file
|
|
189
180
|
try:
|
|
190
181
|
delete_cache = path_join(data_dir, f"{delete}.cache")
|
|
191
|
-
verdict =
|
|
182
|
+
verdict = remove_file(delete_cache)
|
|
192
183
|
return f'{delete_cache} delete done.: {verdict}'
|
|
193
184
|
except:
|
|
194
185
|
return f'{delete}.cache not exists'
|
|
@@ -252,7 +243,7 @@ def delmod(mod):
|
|
|
252
243
|
else:
|
|
253
244
|
return f'Invalid {mod}, must ends with .py or .mpy'
|
|
254
245
|
try:
|
|
255
|
-
return
|
|
246
|
+
return remove_file(path_join(OSPath.MODULES, to_remove))
|
|
256
247
|
except Exception as e:
|
|
257
248
|
return f'Cannot delete: {mod}: {e}'
|
|
258
249
|
|
|
@@ -286,5 +277,6 @@ def help(widgets=False):
|
|
|
286
277
|
'micros_checksum',
|
|
287
278
|
'ls path="/" content="*/f/d" select="*/LM/IO"',
|
|
288
279
|
'rm <path>',
|
|
280
|
+
'rmdir <path>',
|
|
289
281
|
'dirtree path="/"',
|
|
290
282
|
'makedir <path>')
|
micrOS/source/urequests.py
CHANGED
|
@@ -282,7 +282,7 @@ async def apost(url, data=None, json=None, headers:dict=None, sock_size=256, jso
|
|
|
282
282
|
return await arequest('POST', url, data=data, json=json, headers=headers, sock_size=sock_size, jsonify=jsonify)
|
|
283
283
|
|
|
284
284
|
|
|
285
|
-
def host_cache():
|
|
285
|
+
def host_cache() -> dict:
|
|
286
286
|
"""
|
|
287
287
|
Return address cache
|
|
288
288
|
"""
|