micrOSDevToolKit 2.1.5__py3-none-any.whl → 2.26.1__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.
Files changed (400) hide show
  1. env/driver_cp210x/.DS_Store +0 -0
  2. env/driver_cp210x/macOS_VCP_Driver/SiLabsUSBDriverDisk.dmg +0 -0
  3. env/driver_cp210x/macOS_VCP_Driver/macOS_VCP_Driver_Release_Notes.txt +17 -1
  4. micrOS/micropython/esp32-20251209-v1.27.0.bin +0 -0
  5. micrOS/micropython/esp32c3-20251209-v1.27.0.bin +0 -0
  6. micrOS/micropython/esp32c6-20251209-v1.27.0.bin +0 -0
  7. micrOS/micropython/esp32s2-20251209-v1.27.0.bin +0 -0
  8. micrOS/micropython/esp32s2-LOLIN_MINI-20251209-v1.27.0.bin +0 -0
  9. micrOS/micropython/esp32s3-4MBflash-20241129-v1.24.1.bin +0 -0
  10. micrOS/micropython/esp32s3-8MBflash-20251209-v1.27.0.bin +0 -0
  11. micrOS/micropython/esp32s3_spiram_oct-20251209-v1.27.0.bin +0 -0
  12. micrOS/micropython/rpi-pico-w-20251209-v1.27.0.uf2 +0 -0
  13. micrOS/micropython/tinypico-20251209-v1.27.0.bin +0 -0
  14. micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +191 -151
  15. micrOS/source/Auth.py +37 -0
  16. micrOS/source/Common.py +376 -102
  17. micrOS/source/Config.py +55 -25
  18. micrOS/source/Debug.py +54 -193
  19. micrOS/source/Espnow.py +404 -0
  20. micrOS/source/Files.py +207 -0
  21. micrOS/source/Hooks.py +88 -16
  22. micrOS/source/InterConnect.py +130 -46
  23. micrOS/source/Interrupts.py +8 -8
  24. micrOS/source/Logger.py +131 -0
  25. micrOS/source/Network.py +41 -21
  26. micrOS/source/Notify.py +74 -198
  27. micrOS/source/Pacman.py +326 -0
  28. micrOS/source/Scheduler.py +18 -55
  29. micrOS/source/Server.py +84 -217
  30. micrOS/source/Shell.py +103 -93
  31. micrOS/source/Tasks.py +239 -173
  32. micrOS/source/Time.py +21 -22
  33. micrOS/source/Types.py +89 -54
  34. micrOS/source/Web.py +485 -0
  35. micrOS/source/__pycache__/Common.cpython-312.pyc +0 -0
  36. micrOS/source/__pycache__/Debug.cpython-312.pyc +0 -0
  37. micrOS/source/__pycache__/Files.cpython-312.pyc +0 -0
  38. micrOS/source/__pycache__/Logger.cpython-312.pyc +0 -0
  39. micrOS/source/__pycache__/Scheduler.cpython-312.pyc +0 -0
  40. micrOS/source/__pycache__/Server.cpython-312.pyc +0 -0
  41. micrOS/source/__pycache__/Shell.cpython-312.pyc +0 -0
  42. micrOS/source/__pycache__/replhelper.cpython-312.pyc +0 -0
  43. micrOS/source/helpers.py +132 -0
  44. micrOS/source/micrOS.py +25 -21
  45. micrOS/source/micrOSloader.py +14 -23
  46. micrOS/source/microIO.py +94 -57
  47. toolkit/simulator_lib/LP_darwin.py → micrOS/source/modules/IO_esp32.py +22 -11
  48. micrOS/source/{IO_esp32c3.py → modules/IO_esp32c3.py} +6 -1
  49. micrOS/source/modules/IO_esp32c6.py +38 -0
  50. micrOS/source/{IO_esp32s2.py → modules/IO_esp32s2.py} +6 -1
  51. micrOS/source/{IO_esp32s3.py → modules/IO_esp32s3.py} +43 -2
  52. micrOS/source/modules/IO_m5stamp.py +86 -0
  53. micrOS/source/{IO_qtpy.py → modules/IO_qtpy.py} +28 -18
  54. micrOS/source/{IO_tinypico.py → modules/IO_tinypico.py} +48 -3
  55. micrOS/source/modules/LM_L298N.py +161 -0
  56. {toolkit/workspace/precompiled → micrOS/source/modules}/LM_L9110_DCmotor.py +4 -4
  57. micrOS/source/{LM_OV2640.py → modules/LM_OV2640.py} +53 -42
  58. micrOS/source/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +5 -5
  59. micrOS/source/{LM_aht10.py → modules/LM_aht10.py} +12 -4
  60. micrOS/source/{LM_bme280.py → modules/LM_bme280.py} +13 -25
  61. micrOS/source/{LM_buzzer.py → modules/LM_buzzer.py} +42 -40
  62. micrOS/source/{LM_cct.py → modules/LM_cct.py} +22 -27
  63. micrOS/source/modules/LM_cluster.py +255 -0
  64. micrOS/source/{LM_co2.py → modules/LM_co2.py} +13 -6
  65. micrOS/source/{LM_dht11.py → modules/LM_dht11.py} +13 -29
  66. micrOS/source/{LM_dht22.py → modules/LM_dht22.py} +13 -28
  67. micrOS/source/{LM_dimmer.py → modules/LM_dimmer.py} +19 -16
  68. micrOS/source/modules/LM_distance.py +135 -0
  69. micrOS/source/{LM_ds18.py → modules/LM_ds18.py} +12 -4
  70. micrOS/source/{LM_esp32.py → modules/LM_esp32.py} +16 -4
  71. micrOS/source/modules/LM_espnow.py +53 -0
  72. micrOS/source/modules/LM_fileserver.py +265 -0
  73. micrOS/source/{LM_gameOfLife.py → modules/LM_gameOfLife.py} +5 -5
  74. micrOS/source/{LM_genIO.py → modules/LM_genIO.py} +49 -35
  75. micrOS/source/modules/LM_haptic.py +111 -0
  76. micrOS/source/modules/LM_i2c.py +61 -0
  77. micrOS/source/{LM_i2s_mic.py → modules/LM_i2s_mic.py} +20 -23
  78. micrOS/source/{LM_ld2410.py → modules/LM_ld2410.py} +3 -3
  79. micrOS/source/{LM_light_sensor.py → modules/LM_light_sensor.py} +22 -26
  80. micrOS/source/modules/LM_mh_z19c.py +198 -0
  81. micrOS/source/modules/LM_neoeffects.py +284 -0
  82. micrOS/source/{LM_neopixel.py → modules/LM_neopixel.py} +26 -31
  83. micrOS/source/{LM_oled.py → modules/LM_oled.py} +28 -20
  84. micrOS/source/{LM_oled_sh1106.py → modules/LM_oled_sh1106.py} +28 -24
  85. micrOS/source/{LM_oled_ui.py → modules/LM_oled_ui.py} +132 -174
  86. micrOS/source/modules/LM_pacman.py +320 -0
  87. micrOS/source/{LM_presence.py → modules/LM_presence.py} +24 -36
  88. micrOS/source/modules/LM_qmi8658.py +204 -0
  89. micrOS/source/{LM_rencoder.py → modules/LM_rencoder.py} +40 -11
  90. micrOS/source/modules/LM_rest.py +81 -0
  91. micrOS/source/{LM_rgb.py → modules/LM_rgb.py} +25 -34
  92. micrOS/source/{LM_rgbcct.py → modules/LM_rgbcct.py} +5 -5
  93. micrOS/source/{LM_roboarm.py → modules/LM_roboarm.py} +37 -45
  94. micrOS/source/modules/LM_robustness.py +137 -0
  95. micrOS/source/{LM_rp2w.py → modules/LM_rp2w.py} +3 -0
  96. micrOS/source/{LM_sdcard.py → modules/LM_sdcard.py} +3 -0
  97. micrOS/source/{LM_servo.py → modules/LM_servo.py} +4 -4
  98. micrOS/source/modules/LM_sound_event.py +751 -0
  99. micrOS/source/{LM_stepper.py → modules/LM_stepper.py} +8 -8
  100. micrOS/source/{LM_switch.py → modules/LM_switch.py} +21 -18
  101. micrOS/source/{LM_system.py → modules/LM_system.py} +96 -59
  102. micrOS/source/modules/LM_tcs3472.py +187 -0
  103. micrOS/source/modules/LM_telegram.py +388 -0
  104. micrOS/source/modules/LM_trackball.py +287 -0
  105. micrOS/source/modules/LM_veml7700.py +159 -0
  106. micrOS/source/modules/LM_web.py +38 -0
  107. micrOS/source/urequests.py +204 -91
  108. {toolkit/workspace/precompiled → micrOS/source/web}/dashboard.html +9 -4
  109. micrOS/source/web/editor.js +440 -0
  110. micrOS/source/web/filesui.html +178 -0
  111. micrOS/source/web/filesui.js +338 -0
  112. micrOS/source/{index.html → web/index.html} +44 -2
  113. micrOS/source/web/uapi.js +103 -0
  114. micrOS/source/web/udashboard.js +129 -0
  115. micrOS/source/web/ustyle.css +55 -0
  116. micrOS/source/web/uwidgets.js +172 -0
  117. micrOS/source/web/uwidgets_pro.js +99 -0
  118. micrOS/utests/__init__.py +0 -0
  119. micrOS/utests/test_scheduler.py +435 -0
  120. {micrOSDevToolKit-2.1.5.data → microsdevtoolkit-2.26.1.data}/scripts/devToolKit.py +47 -4
  121. {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info}/METADATA +392 -279
  122. microsdevtoolkit-2.26.1.dist-info/RECORD +396 -0
  123. {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info}/WHEEL +1 -1
  124. toolkit/DevEnvCompile.py +63 -33
  125. toolkit/DevEnvOTA.py +72 -22
  126. toolkit/DevEnvUSB.py +147 -77
  127. toolkit/Gateway.py +9 -9
  128. toolkit/LM_to_compile.dat +12 -4
  129. toolkit/MicrOSDevEnv.py +129 -51
  130. toolkit/WebRepl.py +73 -0
  131. toolkit/dashboard_apps/BackupRestore.py +171 -0
  132. toolkit/dashboard_apps/CCTDemo.py +12 -17
  133. toolkit/dashboard_apps/CCTTest.py +20 -24
  134. toolkit/dashboard_apps/CamStream.py +2 -6
  135. toolkit/dashboard_apps/CatGame.py +14 -16
  136. toolkit/dashboard_apps/Dimmer.py +11 -21
  137. toolkit/dashboard_apps/GetVersion.py +11 -19
  138. toolkit/dashboard_apps/MicrophoneTest.py +2 -7
  139. toolkit/dashboard_apps/NeoEffectsDemo.py +22 -35
  140. toolkit/dashboard_apps/NeopixelTest.py +20 -25
  141. toolkit/dashboard_apps/PresenceTest.py +2 -8
  142. toolkit/dashboard_apps/QMI8685_GYRO.py +68 -0
  143. toolkit/dashboard_apps/RGBTest.py +20 -24
  144. toolkit/dashboard_apps/RoboArm.py +24 -32
  145. toolkit/dashboard_apps/SED_test.py +10 -14
  146. toolkit/dashboard_apps/SensorsTest.py +61 -0
  147. toolkit/dashboard_apps/SystemTest.py +219 -117
  148. toolkit/dashboard_apps/Template_app.py +12 -19
  149. toolkit/dashboard_apps/_app_base.py +34 -0
  150. toolkit/dashboard_apps/_gyro_visualizer.py +78 -0
  151. toolkit/dashboard_apps/uLightDemo.py +15 -24
  152. toolkit/index.html +6 -5
  153. toolkit/lib/LocalMachine.py +6 -1
  154. toolkit/lib/MicrosFiles.py +46 -0
  155. toolkit/lib/Repository.py +64 -0
  156. toolkit/lib/TerminalColors.py +4 -0
  157. toolkit/lib/macroScript.py +371 -0
  158. toolkit/lib/micrOSClient.py +124 -51
  159. toolkit/lib/micrOSClientHistory.py +156 -0
  160. toolkit/lib/pip_package_installer.py +31 -4
  161. toolkit/micrOSdashboard.py +16 -21
  162. toolkit/micrOSlint.py +28 -10
  163. toolkit/simulator_lib/.DS_Store +0 -0
  164. micrOS/source/IO_esp32.py → toolkit/simulator_lib/IO_darwin.py +3 -0
  165. toolkit/simulator_lib/__pycache__/IO_darwin.cpython-312.pyc +0 -0
  166. toolkit/simulator_lib/__pycache__/aioespnow.cpython-312.pyc +0 -0
  167. toolkit/simulator_lib/__pycache__/camera.cpython-312.pyc +0 -0
  168. toolkit/simulator_lib/__pycache__/framebuf.cpython-312.pyc +0 -0
  169. toolkit/simulator_lib/__pycache__/machine.cpython-312.pyc +0 -0
  170. toolkit/simulator_lib/__pycache__/micropython.cpython-312.pyc +0 -0
  171. toolkit/simulator_lib/__pycache__/mip.cpython-312.pyc +0 -0
  172. toolkit/simulator_lib/__pycache__/neopixel.cpython-312.pyc +0 -0
  173. toolkit/simulator_lib/__pycache__/network.cpython-312.pyc +0 -0
  174. toolkit/simulator_lib/__pycache__/sim_common.cpython-312.pyc +0 -0
  175. toolkit/simulator_lib/__pycache__/simgc.cpython-312.pyc +0 -0
  176. toolkit/simulator_lib/__pycache__/simulator.cpython-312.pyc +0 -0
  177. toolkit/simulator_lib/__pycache__/uasyncio.cpython-312.pyc +0 -0
  178. toolkit/simulator_lib/__pycache__/uos.cpython-312.pyc +0 -0
  179. toolkit/simulator_lib/__pycache__/urandom.cpython-312.pyc +0 -0
  180. toolkit/simulator_lib/__pycache__/usocket.cpython-312.pyc +0 -0
  181. toolkit/simulator_lib/__pycache__/ussl.cpython-312.pyc +0 -0
  182. toolkit/simulator_lib/aioespnow.py +28 -0
  183. toolkit/simulator_lib/camera.py +84 -0
  184. toolkit/simulator_lib/dht.py +1 -1
  185. toolkit/simulator_lib/framebuf.py +49 -1
  186. toolkit/simulator_lib/machine.py +32 -2
  187. toolkit/simulator_lib/micropython.py +3 -3
  188. toolkit/simulator_lib/mip.py +165 -0
  189. toolkit/simulator_lib/neopixel.py +3 -2
  190. toolkit/simulator_lib/network.py +2 -1
  191. toolkit/simulator_lib/node_config.json +2 -3
  192. toolkit/simulator_lib/ntptime.py +1 -1
  193. toolkit/simulator_lib/{sim_console.py → sim_common.py} +2 -3
  194. toolkit/simulator_lib/simgc.py +6 -2
  195. toolkit/simulator_lib/simulator.py +138 -46
  196. toolkit/simulator_lib/uasyncio.py +34 -3
  197. toolkit/simulator_lib/uos.py +147 -0
  198. toolkit/simulator_lib/urandom.py +4 -0
  199. toolkit/simulator_lib/usocket.py +5 -1
  200. toolkit/simulator_lib/view01.jpg +0 -0
  201. toolkit/simulator_lib/view02.jpg +0 -0
  202. toolkit/socketClient.py +43 -23
  203. toolkit/user_data/webhooks/generic.py +1 -1
  204. toolkit/user_data/webhooks/macro.py +44 -0
  205. toolkit/user_data/webhooks/template.macro +20 -0
  206. toolkit/user_data/webhooks/template.py +1 -1
  207. toolkit/workspace/precompiled/Auth.mpy +0 -0
  208. toolkit/workspace/precompiled/Common.mpy +0 -0
  209. toolkit/workspace/precompiled/Config.mpy +0 -0
  210. toolkit/workspace/precompiled/Debug.mpy +0 -0
  211. toolkit/workspace/precompiled/Espnow.mpy +0 -0
  212. toolkit/workspace/precompiled/Files.mpy +0 -0
  213. toolkit/workspace/precompiled/Hooks.mpy +0 -0
  214. toolkit/workspace/precompiled/InterConnect.mpy +0 -0
  215. toolkit/workspace/precompiled/Interrupts.mpy +0 -0
  216. toolkit/workspace/precompiled/Logger.mpy +0 -0
  217. toolkit/workspace/precompiled/Network.mpy +0 -0
  218. toolkit/workspace/precompiled/Notify.mpy +0 -0
  219. toolkit/workspace/precompiled/Pacman.mpy +0 -0
  220. toolkit/workspace/precompiled/Scheduler.mpy +0 -0
  221. toolkit/workspace/precompiled/Server.mpy +0 -0
  222. toolkit/workspace/precompiled/Shell.mpy +0 -0
  223. toolkit/workspace/precompiled/Tasks.mpy +0 -0
  224. toolkit/workspace/precompiled/Time.mpy +0 -0
  225. toolkit/workspace/precompiled/Types.mpy +0 -0
  226. toolkit/workspace/precompiled/Web.mpy +0 -0
  227. toolkit/workspace/precompiled/_mpy.version +1 -1
  228. toolkit/workspace/precompiled/config/_git.keep +0 -0
  229. toolkit/workspace/precompiled/helpers.mpy +0 -0
  230. toolkit/workspace/precompiled/micrOS.mpy +0 -0
  231. toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
  232. toolkit/workspace/precompiled/microIO.mpy +0 -0
  233. toolkit/workspace/precompiled/modules/IO_esp32.mpy +0 -0
  234. toolkit/workspace/precompiled/modules/IO_esp32c3.mpy +0 -0
  235. toolkit/workspace/precompiled/modules/IO_esp32c6.mpy +0 -0
  236. toolkit/workspace/precompiled/modules/IO_esp32s2.mpy +0 -0
  237. toolkit/workspace/precompiled/modules/IO_esp32s3.mpy +0 -0
  238. toolkit/workspace/precompiled/modules/IO_m5stamp.mpy +0 -0
  239. toolkit/workspace/precompiled/modules/IO_qtpy.mpy +0 -0
  240. toolkit/workspace/precompiled/modules/IO_rp2.mpy +0 -0
  241. toolkit/workspace/precompiled/modules/IO_tinypico.mpy +0 -0
  242. toolkit/workspace/precompiled/modules/LM_L298N.mpy +0 -0
  243. {micrOS/source → toolkit/workspace/precompiled/modules}/LM_L9110_DCmotor.py +4 -4
  244. toolkit/workspace/precompiled/modules/LM_OV2640.mpy +0 -0
  245. toolkit/workspace/precompiled/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +5 -5
  246. toolkit/workspace/precompiled/modules/LM_aht10.mpy +0 -0
  247. toolkit/workspace/precompiled/modules/LM_bme280.mpy +0 -0
  248. toolkit/workspace/precompiled/{LM_buzzer.mpy → modules/LM_buzzer.mpy} +0 -0
  249. toolkit/workspace/precompiled/modules/LM_cct.mpy +0 -0
  250. toolkit/workspace/precompiled/modules/LM_cluster.mpy +0 -0
  251. toolkit/workspace/precompiled/modules/LM_co2.mpy +0 -0
  252. toolkit/workspace/precompiled/modules/LM_dht11.mpy +0 -0
  253. toolkit/workspace/precompiled/modules/LM_dht22.mpy +0 -0
  254. toolkit/workspace/precompiled/modules/LM_dimmer.mpy +0 -0
  255. toolkit/workspace/precompiled/modules/LM_distance.mpy +0 -0
  256. toolkit/workspace/precompiled/modules/LM_ds18.mpy +0 -0
  257. toolkit/workspace/precompiled/{LM_esp32.py → modules/LM_esp32.py} +16 -4
  258. toolkit/workspace/precompiled/modules/LM_espnow.py +53 -0
  259. toolkit/workspace/precompiled/modules/LM_fileserver.mpy +0 -0
  260. toolkit/workspace/precompiled/{LM_gameOfLife.mpy → modules/LM_gameOfLife.mpy} +0 -0
  261. toolkit/workspace/precompiled/modules/LM_genIO.mpy +0 -0
  262. toolkit/workspace/precompiled/modules/LM_haptic.mpy +0 -0
  263. toolkit/workspace/precompiled/modules/LM_i2c.py +61 -0
  264. toolkit/workspace/precompiled/modules/LM_i2s_mic.mpy +0 -0
  265. toolkit/workspace/precompiled/{LM_ld2410.mpy → modules/LM_ld2410.mpy} +0 -0
  266. toolkit/workspace/precompiled/modules/LM_light_sensor.mpy +0 -0
  267. toolkit/workspace/precompiled/modules/LM_mh_z19c.py +198 -0
  268. toolkit/workspace/precompiled/modules/LM_neoeffects.mpy +0 -0
  269. toolkit/workspace/precompiled/modules/LM_neopixel.mpy +0 -0
  270. toolkit/workspace/precompiled/modules/LM_oled.mpy +0 -0
  271. toolkit/workspace/precompiled/modules/LM_oled_sh1106.mpy +0 -0
  272. toolkit/workspace/precompiled/modules/LM_oled_ui.mpy +0 -0
  273. toolkit/workspace/precompiled/modules/LM_pacman.mpy +0 -0
  274. toolkit/workspace/precompiled/modules/LM_presence.mpy +0 -0
  275. toolkit/workspace/precompiled/modules/LM_qmi8658.py +204 -0
  276. toolkit/workspace/precompiled/{LM_rencoder.py → modules/LM_rencoder.py} +40 -11
  277. toolkit/workspace/precompiled/modules/LM_rest.mpy +0 -0
  278. toolkit/workspace/precompiled/modules/LM_rgb.mpy +0 -0
  279. toolkit/workspace/precompiled/{LM_rgbcct.mpy → modules/LM_rgbcct.mpy} +0 -0
  280. toolkit/workspace/precompiled/modules/LM_roboarm.mpy +0 -0
  281. toolkit/workspace/precompiled/modules/LM_robustness.py +137 -0
  282. toolkit/workspace/precompiled/{LM_rp2w.py → modules/LM_rp2w.py} +3 -0
  283. toolkit/workspace/precompiled/{LM_sdcard.py → modules/LM_sdcard.py} +3 -0
  284. toolkit/workspace/precompiled/{LM_servo.mpy → modules/LM_servo.mpy} +0 -0
  285. toolkit/workspace/precompiled/modules/LM_sound_event.mpy +0 -0
  286. toolkit/workspace/precompiled/{LM_stepper.mpy → modules/LM_stepper.mpy} +0 -0
  287. toolkit/workspace/precompiled/modules/LM_switch.mpy +0 -0
  288. toolkit/workspace/precompiled/modules/LM_system.mpy +0 -0
  289. toolkit/workspace/precompiled/modules/LM_tcs3472.py +187 -0
  290. toolkit/workspace/precompiled/modules/LM_telegram.mpy +0 -0
  291. toolkit/workspace/precompiled/{LM_tinyrgb.mpy → modules/LM_tinyrgb.mpy} +0 -0
  292. toolkit/workspace/precompiled/modules/LM_trackball.mpy +0 -0
  293. toolkit/workspace/precompiled/modules/LM_veml7700.mpy +0 -0
  294. toolkit/workspace/precompiled/modules/LM_web.mpy +0 -0
  295. toolkit/workspace/precompiled/urequests.mpy +0 -0
  296. {micrOS/source → toolkit/workspace/precompiled/web}/dashboard.html +9 -4
  297. toolkit/workspace/precompiled/web/editor.js +440 -0
  298. toolkit/workspace/precompiled/web/filesui.html +178 -0
  299. toolkit/workspace/precompiled/web/filesui.js +338 -0
  300. toolkit/workspace/precompiled/{index.html → web/index.html} +44 -2
  301. toolkit/workspace/precompiled/web/uapi.js +103 -0
  302. toolkit/workspace/precompiled/web/udashboard.js +129 -0
  303. toolkit/workspace/precompiled/web/ustyle.css +55 -0
  304. toolkit/workspace/precompiled/web/uwidgets.js +172 -0
  305. toolkit/workspace/precompiled/web/uwidgets_pro.js +99 -0
  306. env/driver_cp210x/CH34XSER_MAC/CH34X_DRV_INSTALL_INSTRUCTIONS.pdf +0 -0
  307. env/driver_cp210x/CH34XSER_MAC/CH34xVCPDriver.pkg +0 -0
  308. micrOS/micropython/esp32-20231005-v1.21.0.bin +0 -0
  309. micrOS/micropython/esp32c3-GENERIC-20240105-v1.22.1.bin +0 -0
  310. micrOS/micropython/esp32c3-GENERIC-20240222-v1.22.2.bin +0 -0
  311. micrOS/micropython/esp32s2-GENERIC-20240105-v1.22.1.bin +0 -0
  312. micrOS/micropython/esp32s2-LOLIN_MINI-20220618-v1.19.1.bin +0 -0
  313. micrOS/micropython/esp32s3-GENERIC-20240105-v1.22.1.bin +0 -0
  314. micrOS/micropython/esp32s3_spiram_oct-20231005-v1.21.0.bin +0 -0
  315. micrOS/micropython/rpi-pico-w-20231005-v1.21.0.uf2 +0 -0
  316. micrOS/micropython/tinypico-20231005-v1.21.0.bin +0 -0
  317. micrOS/micropython/tinypico-usbc-UM-20240105-v1.22.1.bin +0 -0
  318. micrOS/source/LM_L298N_DCmotor.py +0 -86
  319. micrOS/source/LM_catgame.py +0 -74
  320. micrOS/source/LM_dashboard_be.py +0 -37
  321. micrOS/source/LM_demo.py +0 -85
  322. micrOS/source/LM_distance.py +0 -88
  323. micrOS/source/LM_i2c.py +0 -44
  324. micrOS/source/LM_intercon.py +0 -57
  325. micrOS/source/LM_keychain.py +0 -318
  326. micrOS/source/LM_lmpacman.py +0 -126
  327. micrOS/source/LM_neoeffects.py +0 -327
  328. micrOS/source/LM_pet_feeder.py +0 -76
  329. micrOS/source/LM_ph_sensor.py +0 -51
  330. micrOS/source/LM_rest.py +0 -40
  331. micrOS/source/LM_robustness.py +0 -73
  332. micrOS/source/LM_telegram.py +0 -96
  333. micrOS/source/reset.py +0 -11
  334. micrOS/source/uapi.js +0 -76
  335. micrOS/source/udashboard.js +0 -137
  336. micrOS/source/ustyle.css +0 -28
  337. micrOS/source/uwidgets.js +0 -179
  338. micrOSDevToolKit-2.1.5.dist-info/RECORD +0 -337
  339. toolkit/dashboard_apps/AirQualityBME280.py +0 -36
  340. toolkit/dashboard_apps/AirQualityDHT22_CO2.py +0 -36
  341. toolkit/lib/file_extensions.py +0 -16
  342. toolkit/simulator_lib/__pycache__/LP_darwin.cpython-312.pyc +0 -0
  343. toolkit/simulator_lib/__pycache__/LP_darwin.cpython-38.pyc +0 -0
  344. toolkit/simulator_lib/__pycache__/LP_darwin.cpython-39.pyc +0 -0
  345. toolkit/simulator_lib/__pycache__/sim_console.cpython-312.pyc +0 -0
  346. toolkit/simulator_lib/__pycache__/sim_console.cpython-38.pyc +0 -0
  347. toolkit/simulator_lib/__pycache__/sim_console.cpython-39.pyc +0 -0
  348. toolkit/workspace/precompiled/IO_esp32.mpy +0 -0
  349. toolkit/workspace/precompiled/IO_esp32c3.mpy +0 -0
  350. toolkit/workspace/precompiled/IO_esp32s2.mpy +0 -0
  351. toolkit/workspace/precompiled/IO_esp32s3.mpy +0 -0
  352. toolkit/workspace/precompiled/IO_qtpy.mpy +0 -0
  353. toolkit/workspace/precompiled/IO_rp2.mpy +0 -0
  354. toolkit/workspace/precompiled/IO_tinypico.mpy +0 -0
  355. toolkit/workspace/precompiled/LM_L298N_DCmotor.mpy +0 -0
  356. toolkit/workspace/precompiled/LM_OV2640.mpy +0 -0
  357. toolkit/workspace/precompiled/LM_aht10.mpy +0 -0
  358. toolkit/workspace/precompiled/LM_bme280.mpy +0 -0
  359. toolkit/workspace/precompiled/LM_catgame.py +0 -74
  360. toolkit/workspace/precompiled/LM_cct.mpy +0 -0
  361. toolkit/workspace/precompiled/LM_co2.mpy +0 -0
  362. toolkit/workspace/precompiled/LM_dashboard_be.py +0 -37
  363. toolkit/workspace/precompiled/LM_demo.py +0 -85
  364. toolkit/workspace/precompiled/LM_dht11.mpy +0 -0
  365. toolkit/workspace/precompiled/LM_dht22.mpy +0 -0
  366. toolkit/workspace/precompiled/LM_dimmer.mpy +0 -0
  367. toolkit/workspace/precompiled/LM_distance.py +0 -88
  368. toolkit/workspace/precompiled/LM_ds18.mpy +0 -0
  369. toolkit/workspace/precompiled/LM_genIO.mpy +0 -0
  370. toolkit/workspace/precompiled/LM_i2c.py +0 -44
  371. toolkit/workspace/precompiled/LM_i2s_mic.mpy +0 -0
  372. toolkit/workspace/precompiled/LM_intercon.mpy +0 -0
  373. toolkit/workspace/precompiled/LM_keychain.mpy +0 -0
  374. toolkit/workspace/precompiled/LM_light_sensor.mpy +0 -0
  375. toolkit/workspace/precompiled/LM_lmpacman.mpy +0 -0
  376. toolkit/workspace/precompiled/LM_neoeffects.mpy +0 -0
  377. toolkit/workspace/precompiled/LM_neopixel.mpy +0 -0
  378. toolkit/workspace/precompiled/LM_oled.mpy +0 -0
  379. toolkit/workspace/precompiled/LM_oled_sh1106.mpy +0 -0
  380. toolkit/workspace/precompiled/LM_oled_ui.mpy +0 -0
  381. toolkit/workspace/precompiled/LM_pet_feeder.py +0 -76
  382. toolkit/workspace/precompiled/LM_ph_sensor.py +0 -51
  383. toolkit/workspace/precompiled/LM_presence.mpy +0 -0
  384. toolkit/workspace/precompiled/LM_rest.mpy +0 -0
  385. toolkit/workspace/precompiled/LM_rgb.mpy +0 -0
  386. toolkit/workspace/precompiled/LM_roboarm.mpy +0 -0
  387. toolkit/workspace/precompiled/LM_robustness.py +0 -73
  388. toolkit/workspace/precompiled/LM_switch.mpy +0 -0
  389. toolkit/workspace/precompiled/LM_system.mpy +0 -0
  390. toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
  391. toolkit/workspace/precompiled/reset.mpy +0 -0
  392. toolkit/workspace/precompiled/uapi.js +0 -76
  393. toolkit/workspace/precompiled/udashboard.js +0 -137
  394. toolkit/workspace/precompiled/ustyle.css +0 -28
  395. toolkit/workspace/precompiled/uwidgets.js +0 -179
  396. /toolkit/user_data/node_config_archive/.include → /micrOS/source/config/_git.keep +0 -0
  397. /micrOS/source/{IO_rp2.py → modules/IO_rp2.py} +0 -0
  398. /micrOS/source/{LM_tinyrgb.py → modules/LM_tinyrgb.py} +0 -0
  399. {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info/licenses}/LICENSE +0 -0
  400. {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,435 @@
1
+ """
2
+ Scheduler.py unit test (single 1-year execution, end-of-run metrics)
3
+
4
+ Run:
5
+ python3 -m unittest -v utests.test_scheduler
6
+
7
+ This:
8
+ - loads ../source/Scheduler.py via importlib (no package needed)
9
+ - stubs external imports so Scheduler.py runs untouched
10
+ - simulates exactly RUN_DAYS days with virtual ticks
11
+ - calls Scheduler.scheduler(EXEC_PERIOD_SEC) in sync with the virtual tick step
12
+ - validates selected tasks via counts + ΔT metrics (requested vs executed time)
13
+ - prints one summary at the end (no per-tick spam; progress is throttled)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import math
19
+ import os
20
+ import sys
21
+ import types
22
+ import unittest
23
+ import importlib.util
24
+ from dataclasses import dataclass
25
+ from pathlib import Path
26
+ from typing import Dict, Iterator, List, Optional, Tuple
27
+
28
+ # =============================================================================
29
+ # EDIT HERE: generic test tasks
30
+ # =============================================================================
31
+ # Each entry becomes a cron task: "<time_spec>!<lm_name>"
32
+ # time_spec formats:
33
+ # WD:H:M:S (WD can be '*', 0..6, or ranges like 0-4)
34
+ # sunrise / sunset / sunrise+30 / sunset-15 (uses SUN_TIME mapping)
35
+ #
36
+ # NOTE: Builtins are always present in Scheduler.scheduler():
37
+ # "*:3:0:0" -> suntime
38
+ # "*:3:5:0" -> ntp_time
39
+ #
40
+ TEST_TASKS = [
41
+ # Fires daily at noon
42
+ {"lm": "LM_NOON", "time": "*:12:0:0", "expect_count": 365},
43
+
44
+ # Weekday only at 08:30:00 (in a 365-day year starting WD=0, weekdays occur 261 times)
45
+ {"lm": "LM_WEEKDAY_0830", "time": "0-4:8:30:0", "expect_count": 261},
46
+
47
+ # Tag + offset example: daily at sunset-15
48
+ {"lm": "LM_SUNSET_MINUS_15", "time": "sunset-15", "expect_count": 365},
49
+
50
+ # Wildcard seconds example: every second during 12:00 minute (60 per day)
51
+ # {"lm": "LM_STAR_SEC", "time": "*:12:0:*", "expect_count": 365 * 60},
52
+ ]
53
+
54
+ # One-year run config
55
+ RUN_DAYS = 365
56
+
57
+ # Single knob used everywhere:
58
+ # - scheduler sampling/tolerance window (deltasec)
59
+ # - execution period (how frequently scheduler() is called)
60
+ # - virtual time tick step
61
+ EXEC_PERIOD_SEC = 5 # default 5 seconds
62
+
63
+ # Provide sunrise/sunset times for tag resolution (Sun.TIME)
64
+ SUN_TIME = {"sunrise": (6, 0, 0), "sunset": (18, 0, 0)}
65
+
66
+ # If set, prints extra details about each task summary
67
+ VERBOSE_SUMMARY = os.environ.get("SCHED_TEST_SUMMARY_VERBOSE", "0") == "1"
68
+
69
+
70
+ # =============================================================================
71
+ # Internal: stubs + module loader
72
+ # =============================================================================
73
+
74
+ def _install_import_stubs():
75
+ """Install minimal stub modules so Scheduler.py imports succeed unchanged."""
76
+ if "Tasks" not in sys.modules:
77
+ m = types.ModuleType("Tasks")
78
+ m.exec_lm_pipe_schedule = lambda *_a, **_k: True
79
+ sys.modules["Tasks"] = m
80
+
81
+ if "Debug" not in sys.modules:
82
+ m = types.ModuleType("Debug")
83
+ m.console_write = lambda *_a, **_k: None
84
+ m.syslog = lambda *_a, **_k: None
85
+ sys.modules["Debug"] = m
86
+
87
+ if "Time" not in sys.modules:
88
+ m = types.ModuleType("Time")
89
+
90
+ class _Sun:
91
+ TIME: Dict[str, Tuple[int, int, int]] = {}
92
+
93
+ m.Sun = _Sun
94
+ m.suntime = lambda: "stub_suntime"
95
+ m.ntp_time = lambda: "stub_ntp_time"
96
+ sys.modules["Time"] = m
97
+
98
+ if "Config" not in sys.modules:
99
+ m = types.ModuleType("Config")
100
+ m.cfgget = lambda _k: ""
101
+ sys.modules["Config"] = m
102
+
103
+
104
+ def _load_scheduler_module():
105
+ here = Path(__file__).resolve()
106
+ scheduler_path = (here.parent.parent / "source" / "Scheduler.py").resolve()
107
+ if not scheduler_path.exists():
108
+ raise FileNotFoundError(f"Scheduler.py not found at: {scheduler_path}")
109
+
110
+ _install_import_stubs()
111
+
112
+ module_name = "micros_scheduler_under_test_single"
113
+ spec = importlib.util.spec_from_file_location(module_name, str(scheduler_path))
114
+ if spec is None or spec.loader is None:
115
+ raise ImportError(f"Cannot load spec for {scheduler_path}")
116
+
117
+ mod = importlib.util.module_from_spec(spec)
118
+ sys.modules[module_name] = mod
119
+ spec.loader.exec_module(mod)
120
+ return mod
121
+
122
+
123
+ # =============================================================================
124
+ # Virtual time
125
+ # =============================================================================
126
+
127
+ def virtual_localtime_iter(
128
+ days: int,
129
+ step_sec: int,
130
+ start_year: int = 2024,
131
+ start_month: int = 1,
132
+ start_day: int = 1,
133
+ start_wd: int = 0, # 0=Monday ... 6=Sunday
134
+ ) -> Iterator[Tuple[int, int, int, int, int, int, int, int]]:
135
+ """
136
+ Generates valid localtime() tuples:
137
+ (Y, M, D, H, M, S, WD, YD)
138
+
139
+ step_sec controls tick rate (must match EXEC_PERIOD_SEC).
140
+ """
141
+ if step_sec <= 0:
142
+ raise ValueError("step_sec must be >= 1")
143
+
144
+ def is_leap(y: int) -> bool:
145
+ return (y % 4 == 0 and y % 100 != 0) or (y % 400 == 0)
146
+
147
+ mdays_common = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
148
+
149
+ def month_len(y: int, mo: int) -> int:
150
+ if mo == 2 and is_leap(y):
151
+ return 29
152
+ return mdays_common[mo - 1]
153
+
154
+ y = start_year
155
+ mo = start_month
156
+ d = start_day
157
+ wd = start_wd
158
+
159
+ yday = 0
160
+ for m in range(1, mo):
161
+ yday += month_len(y, m)
162
+ yday += (d - 1)
163
+
164
+ for _day_idx in range(days):
165
+ sec = 0
166
+ while sec < 86400:
167
+ s = sec % 60
168
+ mi = (sec // 60) % 60
169
+ h = (sec // 3600)
170
+ yield (y, mo, d, h, mi, s, wd, yday)
171
+ sec += step_sec
172
+
173
+ # Advance date by 1 day
174
+ yday += 1
175
+ wd = (wd + 1) % 7
176
+ d += 1
177
+ if d > month_len(y, mo):
178
+ d = 1
179
+ mo += 1
180
+ if mo > 12:
181
+ mo = 1
182
+ y += 1
183
+ yday = 0
184
+
185
+
186
+ def _hhmmss_to_sec(h: int, m: int, s: int) -> int:
187
+ return int(h) * 3600 + int(m) * 60 + int(s)
188
+
189
+
190
+ def _parse_tag_with_offset(tag: str) -> Tuple[str, int]:
191
+ tag = tag.strip()
192
+ if "+" in tag:
193
+ base, off = tag.split("+", 1)
194
+ return base.strip(), int(off.strip())
195
+ if "-" in tag:
196
+ base, off = tag.split("-", 1)
197
+ return base.strip(), -int(off.strip())
198
+ return tag, 0
199
+
200
+
201
+ def _requested_hms_for_spec(time_spec: str) -> Tuple[int, int, int]:
202
+ """
203
+ Convert 'sunset-15' or 'sunrise+30' to absolute HMS based on SUN_TIME.
204
+ For WD:H:M:S specs, returns H,M,S if fixed, else returns 0,0,0 (handled elsewhere).
205
+ """
206
+ time_spec = time_spec.strip()
207
+ if ":" in time_spec:
208
+ parts = [p.strip() for p in time_spec.split(":")]
209
+ h = parts[1] if len(parts) > 1 else "*"
210
+ m = parts[2] if len(parts) > 2 else "*"
211
+ s = parts[3] if len(parts) > 3 else "*"
212
+ if h.isdigit() and m.isdigit() and s.isdigit():
213
+ return int(h), int(m), int(s)
214
+ return 0, 0, 0
215
+
216
+ base, offset_min = _parse_tag_with_offset(time_spec)
217
+ h, m, s = SUN_TIME[base]
218
+ total_min = (h * 60 + m) + offset_min
219
+ total_min %= 1440
220
+ return int(total_min // 60), int(total_min % 60), int(s)
221
+
222
+
223
+ # =============================================================================
224
+ # Metrics aggregator (no per-event storage)
225
+ # =============================================================================
226
+
227
+ @dataclass
228
+ class Agg:
229
+ count: int = 0
230
+ pass_count: int = 0
231
+ min_dt: int = 0
232
+ max_dt: int = 0
233
+ sum_dt: int = 0
234
+ sumsq_dt: int = 0
235
+
236
+ def add(self, dt: int, tol: int):
237
+ if self.count == 0:
238
+ self.min_dt = dt
239
+ self.max_dt = dt
240
+ else:
241
+ self.min_dt = min(self.min_dt, dt)
242
+ self.max_dt = max(self.max_dt, dt)
243
+
244
+ self.count += 1
245
+ self.sum_dt += dt
246
+ self.sumsq_dt += dt * dt
247
+ if -tol <= dt <= tol:
248
+ self.pass_count += 1
249
+
250
+ def stats(self) -> Tuple[float, float]:
251
+ if self.count == 0:
252
+ return 0.0, 0.0
253
+ mean = self.sum_dt / self.count
254
+ var = (self.sumsq_dt / self.count) - (mean * mean)
255
+ std = math.sqrt(var) if var > 0 else 0.0
256
+ return mean, std
257
+
258
+
259
+ # =============================================================================
260
+ # The test
261
+ # =============================================================================
262
+
263
+ class TestSchedulerSingleYearMetrics(unittest.TestCase):
264
+
265
+ @classmethod
266
+ def setUpClass(cls):
267
+ cls.S = _load_scheduler_module()
268
+
269
+ def setUp(self):
270
+ self.S.LAST_CRON_TASKS.clear()
271
+
272
+ def test_one_year_metrics_and_counts(self):
273
+ S = self.S
274
+
275
+ # Build cron_data string from TEST_TASKS
276
+ cron_data = ";".join(f"{t['time']}!{t['lm']}" for t in TEST_TASKS)
277
+
278
+ aggs: Dict[str, Agg] = {
279
+ "BUILTIN:suntime": Agg(),
280
+ "BUILTIN:ntp_time": Agg(),
281
+ }
282
+ expected_counts: Dict[str, int] = {
283
+ "BUILTIN:suntime": RUN_DAYS,
284
+ "BUILTIN:ntp_time": RUN_DAYS,
285
+ }
286
+
287
+ time_spec_by_lm: Dict[str, str] = {}
288
+ for t in TEST_TASKS:
289
+ key = f"LM:{t['lm']}"
290
+ aggs[key] = Agg()
291
+ expected_counts[key] = int(t["expect_count"])
292
+ time_spec_by_lm[t["lm"]] = str(t["time"])
293
+
294
+ # Patch Scheduler dependencies in-module
295
+ S.console_write = lambda *_a, **_k: None
296
+ S.syslog = lambda *_a, **_k: None
297
+ S.cfgget = lambda k: cron_data if k == "crontasks" else ""
298
+
299
+ class _SunObj:
300
+ TIME = dict(SUN_TIME)
301
+
302
+ S.Sun = _SunObj
303
+
304
+ current_localtime: Optional[Tuple[int, int, int, int, int, int, int, int]] = None
305
+
306
+ def fake_localtime():
307
+ return current_localtime # type: ignore[return-value]
308
+
309
+ S.localtime = fake_localtime
310
+
311
+ def _record(key: str, req_h: int, req_m: int, req_s: int):
312
+ nonlocal current_localtime
313
+ assert current_localtime is not None
314
+ _, _, _day, ah, am, asec, _wd, _yd = current_localtime
315
+
316
+ act_sec = _hhmmss_to_sec(ah, am, asec)
317
+ req_sec = _hhmmss_to_sec(req_h, req_m, req_s)
318
+ dt = act_sec - req_sec
319
+ aggs[key].add(dt, tol=EXEC_PERIOD_SEC)
320
+
321
+ def fake_suntime():
322
+ _record("BUILTIN:suntime", 3, 0, 0)
323
+ return "sun ok"
324
+
325
+ def fake_ntp_time():
326
+ _record("BUILTIN:ntp_time", 3, 5, 0)
327
+ return "ntp ok"
328
+
329
+ S.suntime = fake_suntime
330
+ S.ntp_time = fake_ntp_time
331
+
332
+ def fake_exec_lm_pipe_schedule(cmd: str):
333
+ cmd = str(cmd).strip()
334
+ time_spec = time_spec_by_lm.get(cmd, "")
335
+
336
+ if ":" in time_spec:
337
+ parts = [p.strip() for p in time_spec.split(":")]
338
+ h_spec = parts[1] if len(parts) > 1 else "*"
339
+ m_spec = parts[2] if len(parts) > 2 else "*"
340
+ s_spec = parts[3] if len(parts) > 3 else "*"
341
+
342
+ assert current_localtime is not None
343
+ _, _, _day, ah, am, asec, _wd, _yd = current_localtime
344
+
345
+ req_h = ah if h_spec == "*" else int(h_spec)
346
+ req_m = am if m_spec == "*" else int(m_spec)
347
+ req_s = asec if s_spec == "*" else int(s_spec)
348
+ _record(f"LM:{cmd}", req_h, req_m, req_s)
349
+ else:
350
+ req_h, req_m, req_s = _requested_hms_for_spec(time_spec)
351
+ _record(f"LM:{cmd}", req_h, req_m, req_s)
352
+
353
+ return True
354
+
355
+ S.exec_lm_pipe_schedule = fake_exec_lm_pipe_schedule
356
+
357
+ # Run exactly one year, tick + scheduler call step = EXEC_PERIOD_SEC
358
+ t_iter = virtual_localtime_iter(days=RUN_DAYS, step_sec=EXEC_PERIOD_SEC)
359
+
360
+ # Progressbar throttling (avoid printing millions of times)
361
+ last_percent_int = -1
362
+
363
+ print(f"\n== RUN CRON-SCHEDULER TEST {RUN_DAYS} days")
364
+ for lt in t_iter:
365
+ # Progressbar (based on yday + fraction of day)
366
+ progressbar_width: int = 50
367
+ yd = lt[7] # 0-based day index
368
+ sec_in_day = (lt[3] * 3600) + (lt[4] * 60) + lt[5]
369
+ percent: float = (yd + (sec_in_day / 86400.0)) / float(RUN_DAYS)
370
+ if percent < 0.0:
371
+ percent = 0.0
372
+ elif percent > 1.0:
373
+ percent = 1.0
374
+
375
+ percent_int = int(percent * 100)
376
+ if percent_int != last_percent_int:
377
+ last_percent_int = percent_int
378
+ filled = int(progressbar_width * percent)
379
+ progressbar: str = f"{'|' * filled}{' ' * (progressbar_width - filled)}"
380
+ print(f"({EXEC_PERIOD_SEC} sec timer): {lt}\t|{progressbar}| {percent_int} %", end="\r")
381
+
382
+ # RUN:
383
+ current_localtime = lt
384
+ S.scheduler(EXEC_PERIOD_SEC)
385
+
386
+ # ---- Final verdict summary
387
+ calls = RUN_DAYS * (86400 // EXEC_PERIOD_SEC)
388
+
389
+ lines: List[str] = []
390
+ lines.append("=== Scheduler 1-year metrics ===")
391
+ lines.append(f"days={RUN_DAYS}, exec_period={EXEC_PERIOD_SEC}s, calls~={calls}, tol=±{EXEC_PERIOD_SEC}s")
392
+ lines.append(f"cron_data='{cron_data}'")
393
+ lines.append("")
394
+
395
+ any_fail = False
396
+
397
+ for key in sorted(aggs.keys()):
398
+ agg = aggs[key]
399
+ exp = expected_counts.get(key, None)
400
+ mean, std = agg.stats()
401
+ pass_rate = (agg.pass_count / agg.count * 100.0) if agg.count else 0.0
402
+
403
+ lines.append(
404
+ f"{key:22s} "
405
+ f"count={agg.count:6d} exp={exp if exp is not None else '-':6} "
406
+ f"ΔT[min/avg/max]={agg.min_dt:+4d}/{mean:+6.2f}/{agg.max_dt:+4d} s "
407
+ f"std={std:5.2f} "
408
+ f"pass={agg.pass_count:6d} ({pass_rate:6.2f}%)"
409
+ )
410
+
411
+ if exp is not None and agg.count != exp:
412
+ any_fail = True
413
+ if agg.count and agg.pass_count != agg.count:
414
+ any_fail = True
415
+
416
+ summary = "\n".join(lines)
417
+ print("\n" + summary)
418
+
419
+ if any_fail:
420
+ mismatches = []
421
+ for key in sorted(aggs.keys()):
422
+ agg = aggs[key]
423
+ exp = expected_counts.get(key, None)
424
+ if exp is not None and agg.count != exp:
425
+ mismatches.append(f"{key}: expected count {exp}, got {agg.count}")
426
+ if agg.count and agg.pass_count != agg.count:
427
+ mismatches.append(f"{key}: {agg.count - agg.pass_count} executions outside ±{EXEC_PERIOD_SEC}s tolerance")
428
+
429
+ self.fail("Scheduler validation failed:\n" + "\n".join(mismatches) + "\n\n" + summary)
430
+
431
+ self.assertTrue(True)
432
+
433
+
434
+ if __name__ == "__main__":
435
+ unittest.main(verbosity=2)
@@ -2,6 +2,7 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  import sys
4
4
  import os
5
+ import re
5
6
 
6
7
 
7
8
  if len(sys.argv) > 1 and sys.argv[1] in ['light', '--light']:
@@ -10,8 +11,9 @@ if len(sys.argv) > 1 and sys.argv[1] in ['light', '--light']:
10
11
  else:
11
12
  # INSTALL OPTIONAL DEPENDENCIES - PIP HACK
12
13
  from toolkit.lib import pip_package_installer as pip_install
13
- pip_install.install_optional_dependencies(['PyQt5', 'opencv-python', 'PyAudio', 'mpy-cross==1.20.0', 'matplotlib'])
14
-
14
+ pip_install.install_optional_dependencies(['PyQt5', 'opencv-python', 'PyAudio', 'mpy-cross==1.24.1.post2', 'matplotlib'])
15
+ if sys.platform.startswith("win"):
16
+ pip_install.install_optional_dependencies(['pyreadline3'])
15
17
 
16
18
  # NORMAL CODE ...
17
19
  MYPATH = os.path.dirname(__file__)
@@ -29,11 +31,21 @@ try:
29
31
  except Exception as e:
30
32
  print(f"micrOS linter import error: {e}")
31
33
  micrOSlint = None
34
+ try:
35
+ from toolkit.lib import macroScript
36
+ except Exception as e:
37
+ print(f"Optional dependency missing in macroScript: {e}")
38
+ macroScript = None
39
+ try:
40
+ from toolkit.WebRepl import open_webrepl
41
+ except Exception as e:
42
+ print(f"Optional dependency missing in WebRepl: {e}")
43
+ open_webrepl = None
32
44
 
33
45
 
34
46
  def arg_parse():
35
47
  parser = argparse.ArgumentParser(prog="micrOS dev toolkit - deploy, connect, update, etc.",
36
- description="CMDline wrapper for {}\n and for {}\n USED ENV VARS: API_AUTH=usr:pwd, GATEWAYIP=10.0.1.1".format(
48
+ description="CMDline wrapper for {}\n and for {}\n[HINTS] USED ENV VARS FOR GATEWAY:\n API_AUTH=usr:pwd, GATEWAYIP=10.0.1.1, MACRO_WORKDIR=~/macros".format(
37
49
  os.path.join(TOOLKIT_PATH, 'MicrOSDevEnv.py'),
38
50
  os.path.join(TOOLKIT_PATH, 'socketClient.py')))
39
51
 
@@ -46,6 +58,7 @@ def arg_parse():
46
58
  base_group.add_argument("-c", "--connect", action="store_true", help="Connect via socketclinet")
47
59
  base_group.add_argument("-p", "--connect_parameters", type=str, help="Parameters for connection in non-interactive mode. For more info: -p 'help'")
48
60
  base_group.add_argument("-a", "--applications", type=str, help="List/Execute frontend applications. [list]")
61
+ base_group.add_argument("-macro", "--macro", type=str, help="Run micrOS remote command sequence from .macro file. If file not exists creates a template")
49
62
  base_group.add_argument("-stat", "--node_status", action="store_true", help="Show all available micrOS devices status data.")
50
63
  base_group.add_argument("-cl", "--clean", action="store_true", help="Clean user connection data: device_conn_cache.json")
51
64
 
@@ -63,7 +76,10 @@ def arg_parse():
63
76
  dev_group.add_argument("-gw", "--gateway", action="store_true", help="Start micrOS Gateway rest-api server, Env. vars: API_AUTH='username:password' (optional), GATEWAYIP needed for container deployment only.")
64
77
  dev_group.add_argument("-v", "--version", action="store_true", help="Get micrOS version - repo + connected device.")
65
78
  dev_group.add_argument("-lint", "--linter", action="store_true", help="Run micrOS system linter (pylint+)")
79
+ dev_group.add_argument("-webrepl", "--open_webrepl", action="store_true", help="(beta) Open webrepl in default browser, micropython repl + file transfers (built-in)")
66
80
  dev_group.add_argument("--light", action="store_true", help="Skip optional dependency deployments (low level param: add this as first argument always)")
81
+ dev_group.add_argument("-ut", "--unittest", action="store_true", help="Run micrOS unit tests - for development")
82
+
67
83
 
68
84
  toolkit_group = parser.add_argument_group("Toolkit development")
69
85
  toolkit_group.add_argument("--dummy", action="store_true", help="Skip subshell executions - for API logic test.")
@@ -100,7 +116,13 @@ def install(api_obj):
100
116
 
101
117
  def connect(args=None):
102
118
  if args is not None and len(args) != 0:
103
- arg_list = args.split()
119
+ # REPARSE IINPUT DUE TO EMBEDDED INPUT PARSER... HACK
120
+ # extract quoted content (without quotes)
121
+ extracted = re.findall(r'(["\'])(.*?)\1', args)
122
+ extracted = [text for _, text in extracted]
123
+ # remove quoted parts from original string
124
+ cleaned = re.sub(r'(["\']).*?\1', '', args)
125
+ arg_list = cleaned.split() + extracted
104
126
  socketClient.run(arg_list=arg_list)
105
127
  else:
106
128
  socketClient.run(arg_list=[])
@@ -203,6 +225,13 @@ def update_pip_package():
203
225
  return True if out == 0 else False
204
226
 
205
227
 
228
+ def run_unit_tests():
229
+ """Run micrOS Unit tests under micrOS/utests/*"""
230
+ import unittest
231
+ suite = unittest.defaultTestLoader.discover("micrOS/utests")
232
+ unittest.TextTestRunner(verbosity=2).run(suite)
233
+
234
+
206
235
  if __name__ == "__main__":
207
236
  # Arg parse
208
237
  cmd_args = arg_parse()
@@ -286,4 +315,18 @@ if __name__ == "__main__":
286
315
  if micrOSlint is not None:
287
316
  sys.exit(micrOSlint.main(verbose=False))
288
317
 
318
+ if cmd_args.open_webrepl:
319
+ if open_webrepl is not None:
320
+ open_webrepl()
321
+
322
+ if cmd_args.macro:
323
+ if macroScript is None:
324
+ print("macroScript not available :(")
325
+ else:
326
+ executor = macroScript.Executor()
327
+ executor.run_micro_script(cmd_args.macro)
328
+
329
+ if cmd_args.unittest:
330
+ run_unit_tests()
331
+
289
332
  sys.exit(0)