micrOSDevToolKit 2.9.1__py3-none-any.whl → 2.9.6__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 -19
- micrOS/source/Common.py +1 -1
- micrOS/source/Config.py +6 -6
- micrOS/source/LM_pacman.py +248 -0
- micrOS/source/LM_rest.py +0 -1
- micrOS/source/LM_robustness.py +2 -2
- micrOS/source/LM_system.py +4 -16
- micrOS/source/LM_telegram.py +95 -61
- micrOS/source/Logger.py +51 -17
- micrOS/source/Notify.py +4 -3
- micrOS/source/Server.py +33 -39
- micrOS/source/Shell.py +72 -74
- micrOS/source/Web.py +22 -23
- micrOS/source/microIO.py +1 -2
- micrOS/source/reset.py +5 -1
- micrOS/source/urequests.py +12 -4
- {micrOSDevToolKit-2.9.1.data → micrOSDevToolKit-2.9.6.data}/scripts/devToolKit.py +3 -2
- {micrOSDevToolKit-2.9.1.dist-info → micrOSDevToolKit-2.9.6.dist-info}/METADATA +1 -1
- {micrOSDevToolKit-2.9.1.dist-info → micrOSDevToolKit-2.9.6.dist-info}/RECORD +57 -54
- toolkit/Gateway.py +2 -2
- toolkit/dashboard_apps/SystemTest.py +14 -12
- toolkit/index.html +4 -4
- toolkit/lib/TerminalColors.py +4 -0
- toolkit/lib/macroScript.py +6 -0
- toolkit/lib/micrOSClient.py +48 -21
- toolkit/lib/micrOSClientHistory.py +122 -0
- toolkit/micrOSlint.py +1 -1
- toolkit/simulator_lib/__pycache__/machine.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/uasyncio.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/uos.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/ussl.cpython-312.pyc +0 -0
- toolkit/simulator_lib/machine.py +4 -0
- toolkit/simulator_lib/node_config.json +1 -1
- toolkit/simulator_lib/uasyncio.py +7 -1
- toolkit/simulator_lib/uos.py +138 -0
- toolkit/socketClient.py +4 -4
- toolkit/workspace/precompiled/Common.mpy +0 -0
- toolkit/workspace/precompiled/Config.mpy +0 -0
- toolkit/workspace/precompiled/Espnow.mpy +0 -0
- toolkit/workspace/precompiled/LM_pacman.py +248 -0
- toolkit/workspace/precompiled/LM_rest.mpy +0 -0
- toolkit/workspace/precompiled/LM_robustness.py +2 -2
- toolkit/workspace/precompiled/LM_system.mpy +0 -0
- toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
- toolkit/workspace/precompiled/Logger.mpy +0 -0
- toolkit/workspace/precompiled/Notify.mpy +0 -0
- toolkit/workspace/precompiled/Server.mpy +0 -0
- toolkit/workspace/precompiled/Shell.mpy +0 -0
- toolkit/workspace/precompiled/Web.mpy +0 -0
- toolkit/workspace/precompiled/_mpy.version +1 -1
- toolkit/workspace/precompiled/microIO.mpy +0 -0
- toolkit/workspace/precompiled/node_config.json +1 -1
- toolkit/workspace/precompiled/reset.mpy +0 -0
- toolkit/workspace/precompiled/urequests.mpy +0 -0
- micrOS/source/LM_lmpacman.py +0 -126
- toolkit/workspace/precompiled/LM_lmpacman.mpy +0 -0
- {micrOSDevToolKit-2.9.1.dist-info → micrOSDevToolKit-2.9.6.dist-info}/LICENSE +0 -0
- {micrOSDevToolKit-2.9.1.dist-info → micrOSDevToolKit-2.9.6.dist-info}/WHEEL +0 -0
- {micrOSDevToolKit-2.9.1.dist-info → micrOSDevToolKit-2.9.6.dist-info}/top_level.txt +0 -0
micrOS/source/Shell.py
CHANGED
|
@@ -11,8 +11,8 @@ Designed by Marcell Ban aka BxNxM
|
|
|
11
11
|
#################################################################
|
|
12
12
|
# IMPORTS #
|
|
13
13
|
#################################################################
|
|
14
|
-
from os import listdir
|
|
15
14
|
from sys import modules
|
|
15
|
+
from uos import listdir
|
|
16
16
|
from machine import reset as hard_reset, soft_reset
|
|
17
17
|
from Config import cfgget, cfgput
|
|
18
18
|
from Tasks import lm_exec
|
|
@@ -25,7 +25,7 @@ from Debug import errlog_add
|
|
|
25
25
|
|
|
26
26
|
class Shell:
|
|
27
27
|
__slots__ = ['__devfid', '__auth_mode', '__hwuid', '__auth_ok', '__conf_mode']
|
|
28
|
-
MICROS_VERSION = '2.9.
|
|
28
|
+
MICROS_VERSION = '2.9.6-0'
|
|
29
29
|
|
|
30
30
|
def __init__(self):
|
|
31
31
|
"""
|
|
@@ -45,10 +45,8 @@ class Shell:
|
|
|
45
45
|
except Exception as e:
|
|
46
46
|
errlog_add(f"[ERR] micrOS version export failed (config): {e}")
|
|
47
47
|
|
|
48
|
-
def
|
|
49
|
-
"""
|
|
50
|
-
Must be defined by child class...
|
|
51
|
-
"""
|
|
48
|
+
async def a_send(self, msg):
|
|
49
|
+
""" Must be defined by parent class... """
|
|
52
50
|
pass
|
|
53
51
|
|
|
54
52
|
def reset(self):
|
|
@@ -56,44 +54,43 @@ class Shell:
|
|
|
56
54
|
self.__auth_ok = False
|
|
57
55
|
self.__conf_mode = False
|
|
58
56
|
|
|
59
|
-
def reboot(self, hard=False):
|
|
60
|
-
"""Reboot micropython VM"""
|
|
61
|
-
self.
|
|
62
|
-
self.send("Bye!")
|
|
57
|
+
async def reboot(self, hard=False):
|
|
58
|
+
""" Reboot micropython VM """
|
|
59
|
+
await self.a_send(f"{'[HARD] ' if hard else ''}Reboot micrOS system.\nBye!")
|
|
63
60
|
if hard:
|
|
64
61
|
hard_reset()
|
|
65
62
|
soft_reset()
|
|
66
63
|
|
|
67
64
|
def prompt(self):
|
|
68
|
-
"""Generate prompt"""
|
|
65
|
+
""" Generate prompt """
|
|
69
66
|
auth = "[password] " if self.__auth_mode and not self.__auth_ok else ""
|
|
70
67
|
mode = "[configure] " if self.__conf_mode else ""
|
|
71
68
|
return f"{auth}{mode}{self.__devfid} $ "
|
|
72
69
|
|
|
73
|
-
def __auth(self, msg_list):
|
|
74
|
-
"""Authorize user"""
|
|
70
|
+
async def __auth(self, msg_list):
|
|
71
|
+
""" Authorize user """
|
|
75
72
|
# Set user auth state
|
|
76
73
|
if self.__auth_mode and not self.__auth_ok:
|
|
77
74
|
# check password
|
|
78
75
|
usrpwd = cfgget('appwd')
|
|
79
76
|
if usrpwd == msg_list[0].strip():
|
|
80
77
|
self.__auth_ok = True
|
|
81
|
-
self.
|
|
78
|
+
await self.a_send("AuthOk")
|
|
82
79
|
return True, []
|
|
83
|
-
self.
|
|
80
|
+
await self.a_send("AuthFailed\nBye!")
|
|
84
81
|
return False, []
|
|
85
82
|
return True, msg_list
|
|
86
83
|
|
|
87
|
-
def shell(self, msg):
|
|
84
|
+
async def shell(self, msg):
|
|
88
85
|
"""
|
|
89
86
|
micrOS Shell main - input string handling
|
|
90
87
|
:param msg: incoming shell command (command or load module call)
|
|
91
88
|
"""
|
|
92
|
-
state = self.__shell(msg)
|
|
93
|
-
self.
|
|
89
|
+
state = await self.__shell(msg)
|
|
90
|
+
await self.a_send(self.prompt())
|
|
94
91
|
return state
|
|
95
92
|
|
|
96
|
-
def __shell(self, msg):
|
|
93
|
+
async def __shell(self, msg):
|
|
97
94
|
"""
|
|
98
95
|
Socket server - interpreter shell
|
|
99
96
|
:param msg: socket input string
|
|
@@ -118,11 +115,11 @@ class Shell:
|
|
|
118
115
|
# Hello message
|
|
119
116
|
if msg_list[0] == 'hello':
|
|
120
117
|
# For low level device identification - hello msg
|
|
121
|
-
self.
|
|
118
|
+
await self.a_send(f"hello:{self.__devfid}:{self.__hwuid}")
|
|
122
119
|
return True
|
|
123
120
|
|
|
124
121
|
# [!] AUTH
|
|
125
|
-
state, msg_list = self.__auth(msg_list)
|
|
122
|
+
state, msg_list = await self.__auth(msg_list)
|
|
126
123
|
if not state:
|
|
127
124
|
return False
|
|
128
125
|
if len(msg_list) == 0:
|
|
@@ -131,7 +128,7 @@ class Shell:
|
|
|
131
128
|
# Version handling
|
|
132
129
|
if msg_list[0] == 'version':
|
|
133
130
|
# For micrOS system version info
|
|
134
|
-
self.
|
|
131
|
+
await self.a_send(str(Shell.MICROS_VERSION))
|
|
135
132
|
return True
|
|
136
133
|
|
|
137
134
|
# Reboot micropython VM
|
|
@@ -140,12 +137,12 @@ class Shell:
|
|
|
140
137
|
if len(msg_list) >= 2 and "-h" in msg_list[1]:
|
|
141
138
|
# reboot / reboot -h
|
|
142
139
|
hard = True
|
|
143
|
-
self.reboot(hard)
|
|
140
|
+
await self.reboot(hard)
|
|
144
141
|
|
|
145
142
|
if msg_list[0].startswith('webrepl'):
|
|
146
143
|
if len(msg_list) == 2 and '-u' in msg_list[1]:
|
|
147
|
-
Shell.webrepl(msg_obj=self.
|
|
148
|
-
Shell.webrepl(msg_obj=self.
|
|
144
|
+
await Shell.webrepl(msg_obj=self.a_send, update=True)
|
|
145
|
+
return await Shell.webrepl(msg_obj=self.a_send)
|
|
149
146
|
|
|
150
147
|
# CONFIGURE MODE STATE: ACCESS FOR NODE_CONFIG.JSON
|
|
151
148
|
if msg_list[0].startswith('conf'):
|
|
@@ -157,34 +154,34 @@ class Shell:
|
|
|
157
154
|
|
|
158
155
|
# HELP MSG
|
|
159
156
|
if msg_list[0] == "help":
|
|
160
|
-
self.
|
|
161
|
-
self.
|
|
162
|
-
self.
|
|
163
|
-
self.
|
|
164
|
-
self.
|
|
165
|
-
self.
|
|
166
|
-
self.
|
|
167
|
-
self.
|
|
168
|
-
self.
|
|
169
|
-
self.
|
|
170
|
-
self.
|
|
171
|
-
self.
|
|
172
|
-
self.
|
|
173
|
-
self.
|
|
174
|
-
self.
|
|
175
|
-
self.
|
|
176
|
-
self.
|
|
177
|
-
self.
|
|
178
|
-
self.
|
|
157
|
+
await self.a_send("[MICROS] - built-in shell commands")
|
|
158
|
+
await self.a_send(" hello - hello msg - for device identification")
|
|
159
|
+
await self.a_send(" modules - show active Load Modules")
|
|
160
|
+
await self.a_send(" version - returns micrOS version")
|
|
161
|
+
await self.a_send(" exit - exit from shell socket prompt")
|
|
162
|
+
await self.a_send(" reboot - system soft reboot (vm), hard reboot (hw): reboot -h")
|
|
163
|
+
await self.a_send(" webrepl - start webrepl, for file transfers use with --update")
|
|
164
|
+
await self.a_send("[CONF] Configure mode - built-in shell commands")
|
|
165
|
+
await self.a_send(" conf - Enter conf mode")
|
|
166
|
+
await self.a_send(" dump - Dump all data")
|
|
167
|
+
await self.a_send(" key - Get value")
|
|
168
|
+
await self.a_send(" key value - Set value")
|
|
169
|
+
await self.a_send(" noconf - Exit conf mode")
|
|
170
|
+
await self.a_send("[TASK] postfix: &x - one-time, &&x - periodic, x: wait ms [x min: 20ms]")
|
|
171
|
+
await self.a_send(" task list - list tasks with <tag>s")
|
|
172
|
+
await self.a_send(" task kill <tag> - stop task")
|
|
173
|
+
await self.a_send(" task show <tag> - show task output")
|
|
174
|
+
await self.a_send("[EXEC] Command mode (LMs):")
|
|
175
|
+
await self.a_send(" help lm - list ALL LoadModules")
|
|
179
176
|
if "lm" in str(msg_list):
|
|
180
|
-
return Shell._show_lm_funcs(msg_obj=self.
|
|
181
|
-
return Shell._show_lm_funcs(msg_obj=self.
|
|
177
|
+
return await Shell._show_lm_funcs(msg_obj=self.a_send)
|
|
178
|
+
return await Shell._show_lm_funcs(msg_obj=self.a_send, active_only=True)
|
|
182
179
|
|
|
183
180
|
# [2] EXECUTE:
|
|
184
181
|
# @1 Configure mode
|
|
185
182
|
if self.__conf_mode and len(msg_list) > 0:
|
|
186
183
|
# Lock thread under config handling is threads available
|
|
187
|
-
return Shell._configure(self.
|
|
184
|
+
return await Shell._configure(self.a_send, msg_list)
|
|
188
185
|
# @2 Command mode
|
|
189
186
|
"""
|
|
190
187
|
INPUT MSG STRUCTURE
|
|
@@ -193,43 +190,43 @@ class Shell:
|
|
|
193
190
|
"""
|
|
194
191
|
try:
|
|
195
192
|
# Execute command via InterpreterCore
|
|
196
|
-
self.
|
|
193
|
+
await self.a_send(lm_exec(arg_list=msg_list)[1])
|
|
197
194
|
return True
|
|
198
195
|
except Exception as e:
|
|
199
|
-
self.
|
|
196
|
+
await self.a_send(f"[ERROR] shell.lm_exec internal error: {e}")
|
|
200
197
|
return False
|
|
201
198
|
|
|
202
199
|
#################################################################
|
|
203
200
|
# CONFIGURE MODE HANDLER #
|
|
204
201
|
#################################################################
|
|
205
202
|
@staticmethod
|
|
206
|
-
def _configure(msg_obj, msg_list):
|
|
203
|
+
async def _configure(msg_obj, msg_list):
|
|
207
204
|
"""
|
|
208
205
|
:param msg_obj: shell output stream function reference (write object)
|
|
209
206
|
:param msg_list: socket input param list
|
|
210
207
|
:return: execution status
|
|
211
208
|
"""
|
|
212
|
-
def dump():
|
|
209
|
+
async def dump():
|
|
213
210
|
nonlocal msg_obj, msg_list
|
|
214
211
|
if msg_list[0] == 'dump':
|
|
215
212
|
search = msg_list[1] if len(msg_list) == 2 else None
|
|
216
213
|
# DUMP DATA
|
|
217
214
|
for _key, value in cfgget().items():
|
|
218
215
|
if search is None or search in _key:
|
|
219
|
-
msg_obj(f" {_key}{' ' * (10 - len(_key))}:{' ' * 7} {value}")
|
|
216
|
+
await msg_obj(f" {_key}{' ' * (10 - len(_key))}:{' ' * 7} {value}")
|
|
220
217
|
return True
|
|
221
218
|
return False
|
|
222
219
|
|
|
223
220
|
# [CONFIG] Get value
|
|
224
221
|
if len(msg_list) == 1:
|
|
225
|
-
if dump(): # Simple dump without param filter
|
|
222
|
+
if await dump(): # Simple dump without param filter
|
|
226
223
|
return True
|
|
227
224
|
# GET SINGLE PARAMETER VALUE
|
|
228
|
-
msg_obj(cfgget(msg_list[0]))
|
|
225
|
+
await msg_obj(cfgget(msg_list[0]))
|
|
229
226
|
return True
|
|
230
227
|
# [CONFIG] Set value
|
|
231
228
|
if len(msg_list) >= 2:
|
|
232
|
-
if dump(): # Dump with search option
|
|
229
|
+
if await dump(): # Dump with search option
|
|
233
230
|
return True
|
|
234
231
|
# Deserialize params
|
|
235
232
|
key = msg_list[0]
|
|
@@ -237,28 +234,28 @@ class Shell:
|
|
|
237
234
|
try:
|
|
238
235
|
output = cfgput(key, " ".join(msg_list[1:]), type_check=True)
|
|
239
236
|
except Exception as e:
|
|
240
|
-
msg_obj(f"node_config write error: {e}")
|
|
237
|
+
await msg_obj(f"node_config write error: {e}")
|
|
241
238
|
output = False
|
|
242
239
|
# Evaluation and reply
|
|
243
|
-
msg_obj('Saved' if output else 'Invalid key' if cfgget(key) is None else 'Failed to save')
|
|
240
|
+
await msg_obj('Saved' if output else 'Invalid key' if cfgget(key) is None else 'Failed to save')
|
|
244
241
|
return True
|
|
245
242
|
|
|
246
243
|
#################################################################
|
|
247
244
|
# COMMAND MODE & LMS HANDLER #
|
|
248
245
|
#################################################################
|
|
249
246
|
@staticmethod
|
|
250
|
-
def _show_lm_funcs(msg_obj, active_only=False):
|
|
247
|
+
async def _show_lm_funcs(msg_obj, active_only=False):
|
|
251
248
|
"""
|
|
252
249
|
Dump LM modules with functions - in case of [py] files
|
|
253
250
|
Dump LM module with help function call - in case of [mpy] files
|
|
254
251
|
"""
|
|
255
|
-
def _help(mod):
|
|
252
|
+
async def _help(mod):
|
|
256
253
|
for lm_path in (i for i in mod if i.startswith('LM_') and (i.endswith('py'))):
|
|
257
254
|
lm_name = lm_path.replace('LM_', '').split('.')[0]
|
|
258
255
|
try:
|
|
259
|
-
msg_obj(f" {lm_name}")
|
|
256
|
+
await msg_obj(f" {lm_name}")
|
|
260
257
|
if lm_path.endswith('.mpy'):
|
|
261
|
-
msg_obj(f" {' ' * len(lm_path.replace('LM_', '').split('.')[0])}help")
|
|
258
|
+
await msg_obj(f" {' ' * len(lm_path.replace('LM_', '').split('.')[0])}help")
|
|
262
259
|
continue
|
|
263
260
|
with open(lm_path, 'r') as f:
|
|
264
261
|
line = "micrOSisTheBest"
|
|
@@ -266,9 +263,9 @@ class Shell:
|
|
|
266
263
|
line = f.readline()
|
|
267
264
|
ldata = line.strip()
|
|
268
265
|
if ldata.startswith('def ') and not ldata.split()[1].startswith("_") and 'self' not in ldata:
|
|
269
|
-
msg_obj(f" {' ' * len(lm_name)}{ldata.replace('def ', '').split('(')[0]}")
|
|
266
|
+
await msg_obj(f" {' ' * len(lm_name)}{ldata.replace('def ', '').split('(')[0]}")
|
|
270
267
|
except Exception as e:
|
|
271
|
-
msg_obj(f"[{lm_path}] SHOW LM PARSER WARNING: {e}")
|
|
268
|
+
await msg_obj(f"[{lm_path}] SHOW LM PARSER WARNING: {e}")
|
|
272
269
|
return False
|
|
273
270
|
return True
|
|
274
271
|
|
|
@@ -276,29 +273,30 @@ class Shell:
|
|
|
276
273
|
if active_only:
|
|
277
274
|
mod_keys = modules.keys()
|
|
278
275
|
active_modules = (dir_mod for dir_mod in listdir() if dir_mod.split('.')[0] in mod_keys)
|
|
279
|
-
return _help(active_modules)
|
|
276
|
+
return await _help(active_modules)
|
|
280
277
|
# [2] list all LMs on file system (ALL - help lm) - manual
|
|
281
|
-
return _help(listdir())
|
|
278
|
+
return await _help(listdir())
|
|
282
279
|
|
|
283
280
|
@staticmethod
|
|
284
|
-
def webrepl(msg_obj, update=False):
|
|
281
|
+
async def webrepl(msg_obj, update=False):
|
|
285
282
|
from Network import ifconfig
|
|
286
283
|
|
|
287
|
-
msg_obj(" Start micropython WEBREPL - file transfer and debugging")
|
|
288
|
-
msg_obj(" [i] restart machine shortcut: import reset")
|
|
289
|
-
msg_obj(f" Connect over http://micropython.org/webrepl/#{ifconfig()[1][0]}:8266/")
|
|
290
|
-
msg_obj(f" \t[!] webrepl password: {cfgget('appwd')}")
|
|
284
|
+
await msg_obj(" Start micropython WEBREPL - file transfer and debugging")
|
|
285
|
+
await msg_obj(" [i] restart machine shortcut: import reset")
|
|
286
|
+
await msg_obj(f" Connect over http://micropython.org/webrepl/#{ifconfig()[1][0]}:8266/")
|
|
287
|
+
await msg_obj(f" \t[!] webrepl password: {cfgget('appwd')}")
|
|
291
288
|
if update:
|
|
292
|
-
msg_obj(" Restart node then start webrepl...")
|
|
293
|
-
msg_obj(" Bye!")
|
|
289
|
+
await msg_obj(" Restart node then start webrepl...")
|
|
290
|
+
await msg_obj(" Bye!")
|
|
294
291
|
# Set .if_mode->webrepl (start webrepl after reboot and poll update status...)
|
|
295
292
|
with open('.if_mode', 'w') as f:
|
|
296
293
|
f.write('webrepl')
|
|
297
294
|
hard_reset()
|
|
298
295
|
try:
|
|
299
296
|
import webrepl
|
|
300
|
-
msg_obj(webrepl.start(password=cfgget('appwd')))
|
|
297
|
+
await msg_obj(webrepl.start(password=cfgget('appwd')))
|
|
301
298
|
except Exception as e:
|
|
302
299
|
_err_msg = f"[ERR] while starting webrepl: {e}"
|
|
303
|
-
msg_obj(_err_msg)
|
|
300
|
+
await msg_obj(_err_msg)
|
|
304
301
|
errlog_add(_err_msg)
|
|
302
|
+
return True
|
micrOS/source/Web.py
CHANGED
|
@@ -5,7 +5,7 @@ Built-in-function:
|
|
|
5
5
|
- response
|
|
6
6
|
- landing page: index.html
|
|
7
7
|
- rest/ - call load modules, e.x.: system/top
|
|
8
|
-
- file response (.html, .css, .js, .
|
|
8
|
+
- file response (.html, .css, .js, .jpeg) - generic file server feature
|
|
9
9
|
- "virtual" endpoints - to reply from script on a defined endpoint
|
|
10
10
|
- stream - stream data (jpeg) function
|
|
11
11
|
|
|
@@ -21,7 +21,7 @@ from Config import cfgget
|
|
|
21
21
|
|
|
22
22
|
class WebEngine:
|
|
23
23
|
__slots__ = ["client"]
|
|
24
|
-
|
|
24
|
+
ENDPOINTS = {}
|
|
25
25
|
AUTH = cfgget('auth')
|
|
26
26
|
VERSION = "n/a"
|
|
27
27
|
REQ200 = "HTTP/1.1 200 OK\r\nContent-Type: {dtype}\r\nContent-Length:{len}\r\n\r\n{data}"
|
|
@@ -37,7 +37,8 @@ class WebEngine:
|
|
|
37
37
|
"""File dynamic Content-Type handling"""
|
|
38
38
|
content_types = {".html": "text/html",
|
|
39
39
|
".css": "text/css",
|
|
40
|
-
".js": "application/javascript"
|
|
40
|
+
".js": "application/javascript",
|
|
41
|
+
".jpeg": "image/jpeg"}
|
|
41
42
|
# Extract the file extension
|
|
42
43
|
ext = path.rsplit('.', 1)[-1]
|
|
43
44
|
# Return the content type based on the file extension
|
|
@@ -49,7 +50,7 @@ class WebEngine:
|
|
|
49
50
|
_method, url, _version = request.split('\n')[0].split()
|
|
50
51
|
# Protocol validation
|
|
51
52
|
if _method != "GET" and _version.startswith('HTTP'):
|
|
52
|
-
_err = "Bad Request: not GET HTTP
|
|
53
|
+
_err = f"Bad Request: not GET HTTP but {_version}"
|
|
53
54
|
await self.client.a_send(self.REQ400.format(len=len(_err), data=_err))
|
|
54
55
|
return
|
|
55
56
|
|
|
@@ -109,46 +110,42 @@ class WebEngine:
|
|
|
109
110
|
else:
|
|
110
111
|
state, out = lm_exec(cmd, jsonify=True)
|
|
111
112
|
try:
|
|
112
|
-
resp_schema['result'] = loads(out) # Load again ... hack for embedded
|
|
113
|
+
resp_schema['result'] = loads(out) # Load again ... hack for embedded json converter...
|
|
113
114
|
except:
|
|
114
115
|
resp_schema['result'] = out
|
|
115
116
|
resp_schema['state'] = state
|
|
116
117
|
else:
|
|
117
118
|
resp_schema['result'] = {"micrOS": WebEngine.VERSION, 'node': cfgget('devfid'), 'auth': WebEngine.AUTH}
|
|
118
|
-
if len(tuple(WebEngine.
|
|
119
|
-
resp_schema['result']['usr_endpoints'] = tuple(WebEngine.
|
|
119
|
+
if len(tuple(WebEngine.ENDPOINTS.keys())) > 0:
|
|
120
|
+
resp_schema['result']['usr_endpoints'] = tuple(WebEngine.ENDPOINTS)
|
|
120
121
|
resp_schema['state'] = True
|
|
121
122
|
response = dumps(resp_schema)
|
|
122
123
|
return WebEngine.REQ200.format(dtype='text/html', len=len(response), data=response)
|
|
123
124
|
|
|
124
125
|
async def endpoints(self, url):
|
|
125
126
|
url = url[1:] # Cut first / char
|
|
126
|
-
if url in WebEngine.
|
|
127
|
+
if url in WebEngine.ENDPOINTS:
|
|
127
128
|
console_write(f"[WebCli] endpoint: {url}")
|
|
128
129
|
# Registered endpoint was found - exec callback
|
|
129
130
|
try:
|
|
130
131
|
# RESOLVE ENDPOINT CALLBACK
|
|
131
132
|
# dtype:
|
|
132
|
-
# one-shot:
|
|
133
|
-
# task: multipart/x-mixed-replace | multipart/form-data - data: dict
|
|
134
|
-
#
|
|
135
|
-
dtype, data = WebEngine.
|
|
133
|
+
# one-shot (simple MIME types): image/jpeg | text/html | text/plain - data: raw
|
|
134
|
+
# task (streaming MIME types): multipart/x-mixed-replace | multipart/form-data - data: dict{callback,content-type}
|
|
135
|
+
# content-type: image/jpeg | audio/l16;*
|
|
136
|
+
dtype, data = WebEngine.ENDPOINTS[url]()
|
|
136
137
|
if dtype == 'image/jpeg':
|
|
137
|
-
resp = f"HTTP/1.1 200 OK\r\nContent-Type: {dtype}\r\nContent-Length:{len(data)}\r\n\r\n".encode(
|
|
138
|
-
'utf8') + data
|
|
138
|
+
resp = f"HTTP/1.1 200 OK\r\nContent-Type: {dtype}\r\nContent-Length:{len(data)}\r\n\r\n".encode('utf8') + data
|
|
139
139
|
await self.client.a_send(resp, encode=None)
|
|
140
140
|
elif dtype in ('multipart/x-mixed-replace', 'multipart/form-data'):
|
|
141
|
-
headers =
|
|
142
|
-
|
|
143
|
-
'utf-8')
|
|
144
|
-
await self.client.a_send(headers, encode=None)
|
|
141
|
+
headers = f"HTTP/1.1 200 OK\r\nContent-Type: {dtype}; boundary=\"micrOS_boundary\"\r\n\r\n"
|
|
142
|
+
await self.client.a_send(headers)
|
|
145
143
|
# Start Native stream async task
|
|
146
144
|
task = NativeTask()
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
tag=f"web.stream_{self.client.client_id.replace('W', '')}"
|
|
146
|
+
task.create(callback=self.stream(data['callback'], task, data['content-type']), tag=tag)
|
|
149
147
|
else: # dtype: text/html or text/plain
|
|
150
|
-
await self.client.a_send(
|
|
151
|
-
f"HTTP/1.1 200 OK\r\nContent-Type: {dtype}\r\nContent-Length:{len(data)}\r\n\r\n{data}")
|
|
148
|
+
await self.client.a_send(WebEngine.REQ200.format(dtype=dtype, len=len(data), data=data))
|
|
152
149
|
except Exception as e:
|
|
153
150
|
await self.client.a_send(self.REQ404.format(len=len(str(e)), data=e))
|
|
154
151
|
errlog_add(f"[ERR] WebCli endpoints {url}: {e}")
|
|
@@ -159,6 +156,8 @@ class WebEngine:
|
|
|
159
156
|
"""
|
|
160
157
|
Async stream method
|
|
161
158
|
:param callback: sync or async function callback (auto-detect) WARNING: works for functions only (not methods!)
|
|
159
|
+
:param task: NativeTask instance (that the stream runs in)
|
|
160
|
+
:param content_type: image/jpeg | audio/l16;*
|
|
162
161
|
"""
|
|
163
162
|
is_coroutine = 'generator' in str(type(callback)) # async function callback auto-detect
|
|
164
163
|
with task:
|
|
@@ -175,6 +174,6 @@ class WebEngine:
|
|
|
175
174
|
# Gracefully terminate the stream
|
|
176
175
|
if self.client.connected:
|
|
177
176
|
closing_boundary = '\r\n--micrOS_boundary--\r\n'
|
|
178
|
-
await self.client.a_send(closing_boundary
|
|
177
|
+
await self.client.a_send(closing_boundary)
|
|
179
178
|
await self.client.close()
|
|
180
179
|
task.out = 'Finished stream'
|
micrOS/source/microIO.py
CHANGED
|
@@ -9,7 +9,7 @@ Designed by Marcell Ban aka BxNxM
|
|
|
9
9
|
# IMPORTS #
|
|
10
10
|
#################################################################
|
|
11
11
|
from sys import platform
|
|
12
|
-
from
|
|
12
|
+
from uos import listdir, uname
|
|
13
13
|
from Logger import syslog
|
|
14
14
|
|
|
15
15
|
#################################################################
|
|
@@ -29,7 +29,6 @@ def detect_platform():
|
|
|
29
29
|
Unified platform detection for micrOS
|
|
30
30
|
"""
|
|
31
31
|
if 'esp32' in platform:
|
|
32
|
-
from os import uname
|
|
33
32
|
board = str(uname()[-1]).lower()
|
|
34
33
|
if 'tinypico' in board:
|
|
35
34
|
return 'tinypico' # esp32 family - tinypico
|
micrOS/source/reset.py
CHANGED
micrOS/source/urequests.py
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module implements an optimized version of requests module
|
|
3
|
+
for async and sync http get and post requests.
|
|
4
|
+
|
|
5
|
+
Designed by Marcell Ban aka BxNxM GitHub
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from json import loads, dumps
|
|
1
9
|
from usocket import socket, getaddrinfo
|
|
2
10
|
try:
|
|
3
|
-
from ussl import wrap_socket # Legacy micropython ssl usage
|
|
11
|
+
from ussl import wrap_socket # Legacy micropython ssl usage (+simulator mode)
|
|
4
12
|
except ImportError:
|
|
5
13
|
from ssl import wrap_socket # From micropython 1.23...
|
|
6
|
-
from json import loads, dumps
|
|
7
14
|
from Debug import errlog_add
|
|
8
15
|
import uasyncio as asyncio
|
|
9
16
|
|
|
@@ -39,7 +46,7 @@ def _chunked(_body):
|
|
|
39
46
|
chunk_size_str = _body[:line_end]
|
|
40
47
|
try:
|
|
41
48
|
chunk_size = int(chunk_size_str, 16)
|
|
42
|
-
except ValueError
|
|
49
|
+
except ValueError:
|
|
43
50
|
chunk_size = 0
|
|
44
51
|
|
|
45
52
|
# Check chunk size
|
|
@@ -213,7 +220,8 @@ async def arequest(method, url, data=None, json=None, headers=None, sock_size=25
|
|
|
213
220
|
status_code, body = _parse_response(response)
|
|
214
221
|
except Exception as e:
|
|
215
222
|
status_code = 500
|
|
216
|
-
|
|
223
|
+
# https://github.com/micropython/micropython/blob/8785645a952c03315dbf93667b5f7c7eec49762f/cc3200/simplelink/include/device.h
|
|
224
|
+
body = "[WARN] arequest ASSOC_REJECT" if "-104" == str(e) else f"[ERR] arequest failed: {e}"
|
|
217
225
|
errlog_add(body)
|
|
218
226
|
finally:
|
|
219
227
|
if writer:
|
|
@@ -10,8 +10,9 @@ if len(sys.argv) > 1 and sys.argv[1] in ['light', '--light']:
|
|
|
10
10
|
else:
|
|
11
11
|
# INSTALL OPTIONAL DEPENDENCIES - PIP HACK
|
|
12
12
|
from toolkit.lib import pip_package_installer as pip_install
|
|
13
|
-
pip_install.install_optional_dependencies(['PyQt5', 'opencv-python', 'PyAudio', 'mpy-cross==1.
|
|
14
|
-
|
|
13
|
+
pip_install.install_optional_dependencies(['PyQt5', 'opencv-python', 'PyAudio', 'mpy-cross==1.24.1.post2', 'matplotlib'])
|
|
14
|
+
if sys.platform.startswith("win"):
|
|
15
|
+
pip_install.install_optional_dependencies(['pyreadline3'])
|
|
15
16
|
|
|
16
17
|
# NORMAL CODE ...
|
|
17
18
|
MYPATH = os.path.dirname(__file__)
|