micrOSDevToolKit 2.19.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.

Files changed (69) hide show
  1. micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +35 -35
  2. micrOS/source/Common.py +5 -13
  3. micrOS/source/Config.py +5 -2
  4. micrOS/source/Espnow.py +101 -45
  5. micrOS/source/Files.py +50 -23
  6. micrOS/source/InterConnect.py +10 -5
  7. micrOS/source/Pacman.py +141 -0
  8. micrOS/source/Shell.py +1 -1
  9. micrOS/source/Tasks.py +59 -54
  10. micrOS/source/modules/LM_buzzer.py +1 -4
  11. micrOS/source/modules/LM_cct.py +2 -4
  12. micrOS/source/modules/LM_dimmer.py +1 -2
  13. micrOS/source/modules/LM_distance.py +1 -3
  14. micrOS/source/modules/LM_i2s_mic.py +1 -2
  15. micrOS/source/modules/LM_keychain.py +1 -2
  16. micrOS/source/modules/LM_light_sensor.py +1 -4
  17. micrOS/source/modules/LM_mqtt_client.py +13 -10
  18. micrOS/source/modules/LM_neopixel.py +1 -2
  19. micrOS/source/modules/LM_oled_ui.py +39 -41
  20. micrOS/source/modules/LM_oledui.py +58 -89
  21. micrOS/source/modules/LM_pacman.py +36 -44
  22. micrOS/source/modules/LM_presence.py +1 -2
  23. micrOS/source/modules/LM_rest.py +1 -2
  24. micrOS/source/modules/LM_rgb.py +1 -2
  25. micrOS/source/modules/LM_roboarm.py +3 -4
  26. micrOS/source/modules/LM_robustness.py +1 -2
  27. micrOS/source/modules/LM_telegram.py +1 -2
  28. micrOS/source/urequests.py +1 -1
  29. {microsdevtoolkit-2.19.0.dist-info → microsdevtoolkit-2.21.0.dist-info}/METADATA +153 -214
  30. {microsdevtoolkit-2.19.0.dist-info → microsdevtoolkit-2.21.0.dist-info}/RECORD +67 -67
  31. toolkit/DevEnvOTA.py +2 -2
  32. toolkit/DevEnvUSB.py +1 -1
  33. toolkit/dashboard_apps/SystemTest.py +17 -14
  34. toolkit/lib/micrOSClient.py +37 -15
  35. toolkit/lib/micrOSClientHistory.py +35 -1
  36. toolkit/simulator_lib/__pycache__/uos.cpython-312.pyc +0 -0
  37. toolkit/simulator_lib/uos.py +1 -0
  38. toolkit/workspace/precompiled/Common.mpy +0 -0
  39. toolkit/workspace/precompiled/Config.mpy +0 -0
  40. toolkit/workspace/precompiled/Espnow.mpy +0 -0
  41. toolkit/workspace/precompiled/Files.mpy +0 -0
  42. toolkit/workspace/precompiled/InterConnect.mpy +0 -0
  43. toolkit/workspace/precompiled/Pacman.mpy +0 -0
  44. toolkit/workspace/precompiled/Shell.mpy +0 -0
  45. toolkit/workspace/precompiled/Tasks.mpy +0 -0
  46. toolkit/workspace/precompiled/modules/LM_buzzer.mpy +0 -0
  47. toolkit/workspace/precompiled/modules/LM_cct.mpy +0 -0
  48. toolkit/workspace/precompiled/modules/LM_dimmer.mpy +0 -0
  49. toolkit/workspace/precompiled/modules/LM_distance.mpy +0 -0
  50. toolkit/workspace/precompiled/modules/LM_i2s_mic.mpy +0 -0
  51. toolkit/workspace/precompiled/modules/LM_keychain.mpy +0 -0
  52. toolkit/workspace/precompiled/modules/LM_light_sensor.mpy +0 -0
  53. toolkit/workspace/precompiled/modules/LM_mqtt_client.mpy +0 -0
  54. toolkit/workspace/precompiled/modules/LM_neopixel.mpy +0 -0
  55. toolkit/workspace/precompiled/modules/LM_oled_ui.mpy +0 -0
  56. toolkit/workspace/precompiled/modules/LM_oledui.mpy +0 -0
  57. toolkit/workspace/precompiled/modules/LM_pacman.mpy +0 -0
  58. toolkit/workspace/precompiled/modules/LM_presence.mpy +0 -0
  59. toolkit/workspace/precompiled/modules/LM_rest.mpy +0 -0
  60. toolkit/workspace/precompiled/modules/LM_rgb.mpy +0 -0
  61. toolkit/workspace/precompiled/modules/LM_roboarm.mpy +0 -0
  62. toolkit/workspace/precompiled/modules/LM_robustness.py +1 -2
  63. toolkit/workspace/precompiled/modules/LM_telegram.mpy +0 -0
  64. micrOS/source/modules/LM_pet_feeder.py +0 -78
  65. toolkit/workspace/precompiled/modules/LM_pet_feeder.py +0 -78
  66. {microsdevtoolkit-2.19.0.data → microsdevtoolkit-2.21.0.data}/scripts/devToolKit.py +0 -0
  67. {microsdevtoolkit-2.19.0.dist-info → microsdevtoolkit-2.21.0.dist-info}/WHEEL +0 -0
  68. {microsdevtoolkit-2.19.0.dist-info → microsdevtoolkit-2.21.0.dist-info}/licenses/LICENSE +0 -0
  69. {microsdevtoolkit-2.19.0.dist-info → microsdevtoolkit-2.21.0.dist-info}/top_level.txt +0 -0
@@ -123,8 +123,8 @@ def publish(topic: str, message: str, retain: bool = False):
123
123
  :return: Status message string.
124
124
  """
125
125
  unique_tag = f'mqtt.publish.{topic}.{time.ticks_ms()}'
126
- state = micro_task(tag=unique_tag, task=_publish(unique_tag, message, topic, retain))
127
- return f"Message was sent {state}"
126
+ state:dict = micro_task(tag=unique_tag, task=_publish(unique_tag, message, topic, retain))
127
+ return f"Message was sent ({list(state.values())[0]})"
128
128
 
129
129
 
130
130
  ####################
@@ -142,8 +142,8 @@ async def _up():
142
142
  my_task.out = "Wait"
143
143
  await MQTT.CLIENT.up.wait()
144
144
  MQTT.CLIENT.up.clear()
145
- state = micro_task(tag=MQTT.SUB_TASK, task=_subscribe(MQTT.DEFAULT_TOPIC))
146
- my_task.out = f"Re-Subscription {state}"
145
+ state:dict = micro_task(tag=MQTT.SUB_TASK, task=_subscribe(MQTT.DEFAULT_TOPIC))
146
+ my_task.out = f"Re-Subscription ({list(state.values())[0]})"
147
147
  my_task.feed()
148
148
 
149
149
 
@@ -165,10 +165,14 @@ async def _init_client():
165
165
  MQTT.CLIENT.up.clear()
166
166
 
167
167
  # Initialize mqtt topics
168
- if not micro_task(tag=MQTT.SUB_TASK, task=_subscribe(MQTT.DEFAULT_TOPIC)):
169
- syslog(f"Failed start mqtt subscribe")
170
- if not micro_task(tag=MQTT.UP_TASK, task=_up()):
171
- syslog(f"Failed start mqtt up")
168
+ try:
169
+ micro_task(tag=MQTT.SUB_TASK, task=_subscribe(MQTT.DEFAULT_TOPIC))
170
+ except Exception as err:
171
+ syslog(f"Failed start mqtt subscribe: {err}")
172
+ try:
173
+ micro_task(tag=MQTT.UP_TASK, task=_up())
174
+ except Exception as err:
175
+ syslog(f"Failed start mqtt up: {err}")
172
176
  # Async listener loop
173
177
  await _receiver()
174
178
  my_task.out = "Receiver closed"
@@ -230,8 +234,7 @@ def load(username: str, password: str, server_ip: str, server_port: str='1883',
230
234
  MQTT.CLIENT = MQTTClient(_configure(username, password, server_ip, server_port))
231
235
  MQTT.QOS = qos
232
236
 
233
- state = micro_task(tag=MQTT.CLIENT_TASK, task=_init_client())
234
- return {MQTT.CLIENT_TASK: "Starting"} if state else {MQTT.CLIENT_TASK: "Already running"}
237
+ return micro_task(tag=MQTT.CLIENT_TASK, task=_init_client())
235
238
 
236
239
 
237
240
  def help(widgets=False):
@@ -278,8 +278,7 @@ def transition(r=None, g=None, b=None, sec=1.0, wake=False):
278
278
  # Create transition generator and calculate step_ms
279
279
  rgb_gen, step_ms = transition_gen(r_from, r_to, g_from, g_to, b_from, b_to, interval_sec=sec)
280
280
  # [!] ASYNC TASK CREATION [1*] with async task callback + taskID (TAG) handling
281
- state = micro_task(tag=Data.RGB_TASK_TAG, task=_task(ms_period=step_ms, iterable=rgb_gen))
282
- return "Starting transition" if state else "Transition already running"
281
+ return micro_task(tag=Data.RGB_TASK_TAG, task=_task(ms_period=step_ms, iterable=rgb_gen))
283
282
 
284
283
 
285
284
  def random(smooth=True, max_val=255):
@@ -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, host:str, cmd:list, run=False):
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(cmd + [f">>{host}"], jsonify=True)
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
- if host in self.open_intercons:
358
- return
364
+ _cmd = cmd.strip().split()
365
+ host = _cmd[-1].replace(">", "").replace("&", "")
359
366
  # Draw host + cmd details
360
- PageUI.DISPLAY.text(host, 0, posy)
361
- PageUI.DISPLAY.text(' '.join(cmd), posx, posy+10)
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
- # Kill task - clean
369
- manage_task(self.cmd_task_tag, 'kill')
370
- # data gathered - remove tag - skip re-read
371
- self.cmd_task_tag = None
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
- key = key.split('.')[0]
433
- val = '.'.join(val.split('.')[-2:])
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
- # Create page for intercon command
548
- PageUI.PAGE_UI_OBJ.add_page(lambda: PageUI.PAGE_UI_OBJ.cmd_call_page(cmd, run=run))
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] cmd_genpage: {e}')
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
- 'intercon_genpage "host cmd" run=False',
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
@@ -121,8 +126,7 @@ class Frame(BaseFrame):
121
126
  """
122
127
  # [!] ASYNC TASK CREATION [1*] with async task callback + taskID (TAG) handling
123
128
  self._taskid = f"oledui.{tid}"
124
- state = micro_task(tag=self._taskid, task=self._task(period_ms=period_ms))
125
- return "Starting" if state else "Already running"
129
+ return micro_task(tag=self._taskid, task=self._task(period_ms=period_ms))
126
130
 
127
131
  def hover(self):
128
132
  """
@@ -486,8 +490,7 @@ class ScreenSaver(BaseFrame):
486
490
  def run(self, fps=10):
487
491
  # [!] ASYNC TASK CREATION [1*] with async task callback + taskID (TAG) handling
488
492
  period_ms = int(1000/fps)
489
- state = micro_task(tag="oledui.anim", task=self._task(period_ms))
490
- return "Starting" if state else "Already running"
493
+ return micro_task(tag="oledui.anim", task=self._task(period_ms))
491
494
 
492
495
  def cancel(self):
493
496
  if self.running:
@@ -740,79 +743,59 @@ class PageUI:
740
743
  :param y: frame y
741
744
  """
742
745
  x, y = x+2, y+4
743
- def _execute(display, w, h, x, y):
744
- nonlocal cmd
745
- try:
746
- cmd_list = cmd.strip().split()
747
- # Send CMD to other device & show result
748
- state, out = exec_cmd(cmd_list)
749
- cmd_out = out.strip()
750
- except Exception as e:
751
- cmd_out = str(e)
752
- self.app_frame.press_output = cmd_out
753
- PageUI.write_lines(cmd_out, display, x, y + 15)
754
-
755
- display.text(cmd, x, y)
756
- if run:
757
- _execute(display, w, h, x, y)
758
- else:
759
- self._press_indicator(display, w, h, x, y)
760
- PageUI.write_lines(self.app_frame.press_output, display, x, y + 15)
761
- # Return "press" callback, mandatory input parameters: display, w, h, x, y
762
- return {"press": _execute}
763
- return
764
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)
765
762
 
766
- def intercon_exec_page(self, host, cmd, run, display, w, h, x, y):
767
- """
768
- :param host: hostname or IP address of a device
769
- :param cmd: load module string command
770
- :param run: auto-run command (every page refresh)
771
- :param display: display instance
772
- :param h: frame h
773
- :param w: frame w
774
- :param x: frame x
775
- :param y: frame y
776
- """
777
- x, y = x+2, y+4
778
763
  def _execute(display, w, h, x, y):
779
- nonlocal host, cmd, run
780
- # Check open host connection
764
+ nonlocal cmd, run
781
765
  try:
782
- # Send CMD to other device & show result
783
- state, data_meta = exec_cmd(cmd + [f">>{host}"], jsonify=True)
784
- if state:
785
- self._cmd_task_tag = list(data_meta.keys())[0]
786
- verdict = list(data_meta.values())[0]
787
- if "Already running" in verdict and not run:
788
- self.app_frame.press_output = verdict # Otherwise the task start output not relevant on UI
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
789
776
  else:
790
- self.app_frame.press_output = f"Error: {data_meta}"
777
+ # REALTIME mode: get command execution result
778
+ state, out = exec_cmd(cmd_list, jsonify=True)
779
+ self.app_frame.press_output = str(out)
791
780
  except Exception as e:
792
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)
793
784
 
794
- def _read_buffer():
795
- # Read command output from async buffer
796
- if self._cmd_task_tag is not None:
797
- task_buffer = manage_task(self._cmd_task_tag, 'show').replace(' ', '')
798
- if task_buffer is not None and len(task_buffer) > 0:
799
- # Set display out to task buffered data
800
- self.app_frame.press_output = task_buffer
801
- # data gathered - remove tag - skip re-read
802
- self._cmd_task_tag = None
803
- PageUI.write_lines(self.app_frame.press_output, display, x, y + 20, line_limit=2)
804
-
805
- 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
806
789
  if run:
807
- if self._cmd_task_tag is None:
808
- _execute(display, w, h, x, y)
809
- _read_buffer()
810
- return
811
- _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)
812
794
  self._press_indicator(display, w, h, x, y)
813
795
  # Return "press" callback, mandatory input parameters: display, w, h, x, y
814
796
  return {"press": _execute}
815
797
 
798
+
816
799
  #################################################################################
817
800
  # Page function #
818
801
  #################################################################################
@@ -835,8 +818,14 @@ def _intercon_nodes_page(display, w, h, x, y):
835
818
  cache = hosts()["intercon"]
836
819
  if sum([1 for _ in cache]) > 0:
837
820
  for key, val in cache.items():
838
- key = key.split('.')[0]
839
- val = '.'.join(val.split('.')[-2:])
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:])
840
829
  display.text(f" {val} {key}", x, line_start + (line_cnt * 10))
841
830
  line_cnt += 1
842
831
  if line_cnt > line_limit:
@@ -902,7 +891,7 @@ def cursor(x, y):
902
891
  return "Set cursor position"
903
892
 
904
893
 
905
- def cmd_genpage(cmd=None, run=False):
894
+ def genpage(cmd=None, run=False):
906
895
  """
907
896
  Create load module execution pages dynamically :)
908
897
  - based on cmd value: load_module function (args)
@@ -917,30 +906,11 @@ def cmd_genpage(cmd=None, run=False):
917
906
  # Create page for intercon command
918
907
  PageUI.INSTANCE.add_page(lambda display, w, h, x, y: PageUI.INSTANCE.lm_exec_page(cmd, run, display, w, h, x, y))
919
908
  except Exception as e:
920
- syslog(f'[ERR] cmd_genpage: {e}')
909
+ syslog(f'[ERR] genpage: {e}')
921
910
  return str(e)
922
911
  return True
923
912
 
924
913
 
925
- def intercon_genpage(cmd=None, run=False):
926
- """
927
- Create intercon pages dynamically :)
928
- - based on cmd value.
929
- :param cmd: 'host hello' or 'host system clock'
930
- :param run: run button event at page init: True/False
931
- :return: page creation verdict
932
- """
933
- raw = cmd.split()
934
- host = raw[0]
935
- cmd = raw[1:]
936
- try:
937
- # Create page for intercon command
938
- PageUI.INSTANCE.add_page(lambda display, w, h, x, y: PageUI.INSTANCE.intercon_exec_page(host, cmd, run, display, w, h, x, y))
939
- except Exception as e:
940
- syslog(f'[ERR] intercon_genpage: {e}')
941
- return str(e)
942
- return True
943
-
944
914
  def add_page(page_callback):
945
915
  """
946
916
  [LM] Create page from load module with callback function
@@ -965,6 +935,5 @@ def help(widgets=False):
965
935
  "BUTTON control cmd=<prev,press,next,on,off>",
966
936
  "BUTTON debug", "cursor x y",
967
937
  "popup msg='text'", "cancel_popup",
968
- "cmd_genpage cmd='system clock'",
969
- "intercon_genpage 'host cmd' run=False"),
938
+ "genpage cmd='system clock'"),
970
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, remove_fs, OSPath, path_join
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, allow_dir=False):
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 allow_dir: enable directory deletion, default: False
38
+ :param force: bypasses protection check - sudo mode
39
39
  """
40
- return remove_fs(path, allow_dir)
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(url=None, package=None):
76
+ def download(ref=None):
68
77
  """
69
- [BETA] Load Module downloader with mip
70
- :param url: github url path, ex. BxNxM/micrOS/master/toolkit/workspace/precompiled/LM_robustness.py
71
- :param package: mip package name or raw url (hack)
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
- def _install(target=None):
74
- nonlocal url, verdict
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 = remove_fs(path_join(modules_path, to_delete))
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
- remove_fs(path_join(OSPath._ROOT, _rf))
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 = remove_fs(delete_cache)
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 remove_fs(path_join(OSPath.MODULES, to_remove))
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>')
@@ -213,8 +213,7 @@ def motion_trig(sample_ms=15, buff_size=10):
213
213
 
214
214
  # [3] Start mic sampling in async task
215
215
  # [!] ASYNC TASK CREATION [1*] with async task callback + taskID (TAG) handling
216
- state = micro_task(tag=Data.TASK_TAG, task=__task(ms_period=sample_ms, buff_size=buff_size))
217
- return "Starting" if state else "Already running"
216
+ return micro_task(tag=Data.TASK_TAG, task=__task(ms_period=sample_ms, buff_size=buff_size))
218
217
 
219
218
 
220
219
  def subscribe_intercon(on, off):
@@ -66,8 +66,7 @@ def aurl(subdomain):
66
66
  tag = "rest." + subdomain.replace("http://", '').replace("https://", '')
67
67
  if len(tag) > 50:
68
68
  tag = tag[0:50]
69
- state = micro_task(tag=tag, task=__task(subdomain, tag))
70
- return f"Starting" if state else f"Already running"
69
+ return micro_task(tag=tag, task=__task(subdomain, tag))
71
70
 
72
71
 
73
72
  def help(widgets=False):
@@ -246,8 +246,7 @@ def transition(r=None, g=None, b=None, sec=1.0, wake=False):
246
246
  # Create transition generator and calculate step_ms
247
247
  rgb_gen, step_ms = transition_gen(r_from, r, g_from, g, b_from, b, interval_sec=sec)
248
248
  # [!] ASYNC TASK CREATION [1*] with async task callback + taskID (TAG) handling
249
- state = micro_task(tag=Data.RGB_TASK_TAG, task=_task(ms_period=step_ms, iterable=rgb_gen))
250
- return "Starting transition" if state else "Transition already running"
249
+ return micro_task(tag=Data.RGB_TASK_TAG, task=_task(ms_period=step_ms, iterable=rgb_gen))
251
250
 
252
251
 
253
252
  def random(smooth=True, max_val=1000):
@@ -204,11 +204,10 @@ def play(*args, s=None, delay=None, deinit=True):
204
204
 
205
205
  # Start play - servo XY in async task
206
206
  # [!] ASYNC TASK CREATION [1*] with async task callback + taskID (TAG) handling
207
- state = micro_task(tag=RoboArm.PLAY_TAG, task=_play(args, deinit, delay))
208
- if state:
209
- return 'Play: {} steps'.format(int(len(args)/2))
210
- return 'Play - already running'
211
207
 
208
+ state:dict = micro_task(tag=RoboArm.PLAY_TAG, task=_play(args, deinit, delay))
209
+ state.update({"Play steps": int(len(args)/2)})
210
+ return state
212
211
 
213
212
  def record(clean=False, rec_limit=8):
214
213
  """
@@ -83,8 +83,7 @@ def create_task():
83
83
  """
84
84
  # [!] ASYNC TASK CREATION [1*] with async task callback + taskID (TAG) handling
85
85
  task_tag = "microtask.run"
86
- state = micro_task(tag=task_tag, task=__task(tag=task_tag, period_ms=5))
87
- return "Starting" if state else "Already running"
86
+ return micro_task(tag=task_tag, task=__task(tag=task_tag, period_ms=5))
88
87
 
89
88
 
90
89
  @micro_task("microtask", _wrap=True)
@@ -362,8 +362,7 @@ def receiver_loop(period=3):
362
362
  if Telegram.INSTANCE is None:
363
363
  return "Network unavailable."
364
364
  tag = 'telegram.server_bot'
365
- state = micro_task(tag=tag, task=Telegram.server_bot(tag=tag, period=period))
366
- return {tag: "Starting"} if state else {tag: "Already running"}
365
+ return micro_task(tag=tag, task=Telegram.server_bot(tag=tag, period=period))
367
366
 
368
367
 
369
368
  def help(widgets=False):
@@ -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
  """