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,371 @@
1
+ import os
2
+ import sys
3
+ import time
4
+ from datetime import datetime
5
+ import subprocess
6
+ import platform
7
+ import threading
8
+ try:
9
+ from .micrOSClient import micrOSClient, color
10
+ except:
11
+ from micrOSClient import micrOSClient, color
12
+ try:
13
+ import curses
14
+ except Exception as e:
15
+ print(f"Missing dependency for curses: {e}")
16
+ curses = None
17
+
18
+ DRY_RUN = False
19
+
20
+ #####################################
21
+ # FILE SELECTION TUI #
22
+ #####################################
23
+
24
+ def select_menu(directory):
25
+ if not os.path.isdir(directory):
26
+ print("Not a directory.")
27
+ return None
28
+ files = [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f)) and f.endswith(".macro")]
29
+ if not files:
30
+ print("No macro found in the directory.")
31
+ return None
32
+
33
+ def macro_preview(path):
34
+ with open(path, 'r') as file:
35
+ # Read the first line
36
+ first_line = file.readline().strip()
37
+ preview = ' '.join(first_line.split()[1:])
38
+ macro_name_len = len(os.path.basename(path))
39
+ column_idx = 30
40
+ spacer = " " * (column_idx - macro_name_len)
41
+ return f"{spacer}{preview}"
42
+
43
+ def display_menu(stdscr):
44
+ if curses is None:
45
+ return None
46
+ curses.curs_set(0) # Hide the cursor
47
+ current_row = 0
48
+ while True:
49
+ stdscr.clear()
50
+ stdscr.addstr(0, 0, "Please select a macro:")
51
+ # Display files with navigation
52
+ for idx, file in enumerate(files):
53
+ if idx == current_row:
54
+ stdscr.addstr(idx + 1, 2, f"> {file} {macro_preview(os.path.join(directory, file))}", curses.A_REVERSE) # Highlight the current selection
55
+ else:
56
+ stdscr.addstr(idx + 1, 2, f" {file}")
57
+ key = stdscr.getch()
58
+ if key == curses.KEY_UP and current_row > 0:
59
+ current_row -= 1
60
+ elif key == curses.KEY_DOWN and current_row < len(files) - 1:
61
+ current_row += 1
62
+ elif key == curses.KEY_ENTER or key in [10, 13]:
63
+ stdscr.clear()
64
+ #stdscr.addstr(0, 0, f"Macro selected: {files[current_row]}")
65
+ #stdscr.refresh()
66
+ #stdscr.getch() # Wait for another key press before exiting
67
+ return os.path.join(directory, files[current_row])
68
+ return curses.wrapper(display_menu)
69
+
70
+
71
+ #####################################
72
+ # HELPER FUNCTIONS #
73
+ #####################################
74
+
75
+ def _elapsed_time(start, stop):
76
+ # Calculate the difference (delta) between the end and start times
77
+ delta = stop - start
78
+
79
+ # Total seconds in the delta
80
+ total_seconds = int(delta.total_seconds())
81
+
82
+ # Calculate days, hours, minutes, and seconds
83
+ days = total_seconds // 86400
84
+ hours = (total_seconds % 86400) // 3600
85
+ minutes = (total_seconds % 3600) // 60
86
+ seconds = total_seconds % 60
87
+
88
+ # Formatting the delta time
89
+ formatted_delta = f"{days}d {hours}h {minutes}m {seconds}s"
90
+ return formatted_delta
91
+
92
+
93
+ def action_console(action, msg):
94
+ if action == "SKIP":
95
+ action = f"{color.BOLD}--- {action}{color.NC}"
96
+ if action == "SEND":
97
+ action = f"{color.WARN}==> {action}{color.NC}"
98
+ if action == "SHELL":
99
+ action = f"{color.HEADER}*** {action}{color.NC}"
100
+ if action == "WAIT":
101
+ action = f"{color.OKBLUE}=== {action}{color.NC}"
102
+ if action == "MACRO":
103
+ action = f"{color.OKGREEN}=== {action}{color.NC}"
104
+ if action == "ERR":
105
+ action = f"{color.ERR}=== {action}{color.NC}"
106
+ if action == "RECURSION":
107
+ action = f"{color.WARN}{color.BOLD} &&& {action}{color.NC}"
108
+ if action == "INIT":
109
+ print(msg)
110
+ return
111
+ dr = "DRYRUN" if DRY_RUN else ""
112
+ message = f"{dr}| {action} {msg}"
113
+ print(message)
114
+
115
+
116
+ #####################################
117
+ # MACRO EXECUTOR #
118
+ #####################################
119
+
120
+ class Executor:
121
+ RECURSION_COUNTER = {}
122
+
123
+ def __init__(self, host=None, pwd=None, verbose=None, safe=None, parallel=None, max_recursion=5):
124
+ self.com = None
125
+ self.host = host
126
+ self.pwd = pwd
127
+ self.verbose = verbose
128
+ self.safe_mode = safe
129
+ self.safe_modules = None
130
+ self.workdir = None
131
+ self.macro_name = None
132
+ self.max_recursion = max_recursion
133
+ self.parallel = parallel
134
+
135
+ def init_com(self):
136
+ try:
137
+ self.com = micrOSClient(host=self.host, port=9008, pwd=self.pwd, dbg=self.verbose)
138
+ if self.safe_mode:
139
+ self.safe_modules = self.com.send_cmd_retry("modules")[0]
140
+ except Exception as e:
141
+ print(f"Connection error, cannot get dhcp(?): {e}")
142
+ if DRY_RUN:
143
+ self.com.close = lambda: print("DRY_RUN close")
144
+ self.safe_modules = []
145
+ return
146
+ raise e
147
+
148
+ def run(self, cmd, force_close=True):
149
+ if self.com is None:
150
+ self.init_com()
151
+ if self.safe_mode and cmd.split()[0] not in self.safe_modules:
152
+ action_console("SKIP", f"{cmd.split()[0]} execution (not loaded) - safe mode: {self.safe_modules}")
153
+ return True, "Skipped..."
154
+ # [2] Test functions for command send function
155
+ action_console("SEND", cmd)
156
+ if DRY_RUN:
157
+ out = f"dryrun"
158
+ else:
159
+ out = self.com.send_cmd_retry(cmd)
160
+ action_console(" "*8, f"{cmd}: {out}")
161
+ if force_close: self.com.close()
162
+ return True if len(out) > 0 else False, out
163
+
164
+ @staticmethod
165
+ def _run_embedded_macro(macro_name, workdir, verbose=None, safe=None, background=None):
166
+ def run():
167
+ nonlocal macro_name, workdir, verbose, safe, background
168
+ macro_name = f"{macro_name}.macro"
169
+ macro_path = os.path.join(workdir, macro_name)
170
+ print(f"| - {macro_path}{' - background' if background else ''}")
171
+ macro_exec = Executor(verbose=verbose, safe=safe)
172
+ macro_exec.run_micro_script(macro_path)
173
+
174
+ if background:
175
+ thread = threading.Thread(target=run)
176
+ thread.start()
177
+ else:
178
+ run()
179
+
180
+ def run_embedded_macro(self, macro_name):
181
+ run_macro = True
182
+ if self.macro_name == macro_name:
183
+ if Executor.RECURSION_COUNTER.get(self.macro_name, None) is None:
184
+ Executor.RECURSION_COUNTER[self.macro_name] = 1
185
+ counter = Executor.RECURSION_COUNTER[self.macro_name]
186
+ if counter >= self.max_recursion:
187
+ action_console("RECURSION", f"{self.macro_name} macro limit exceeded: {self.max_recursion}")
188
+ Executor.RECURSION_COUNTER[self.macro_name] = 1
189
+ run_macro = False
190
+ else:
191
+ action_console("RECURSION", f"{self.macro_name} macro: {counter}/{self.max_recursion}")
192
+ Executor.RECURSION_COUNTER[self.macro_name] += 1
193
+ if run_macro:
194
+ self._run_embedded_macro(macro_name, self.workdir, verbose=self.verbose, safe=self.safe_mode, background=self.parallel)
195
+
196
+ @staticmethod
197
+ def _run_shell_command(command):
198
+ """Run a command in the detected shell."""
199
+
200
+ def detect_shell():
201
+ """Detect the current shell type."""
202
+ if platform.system() == "Windows":
203
+ return os.getenv('ComSpec') # Typically cmd.exe on Windows
204
+ else:
205
+ return os.getenv('SHELL') # Typically /bin/bash, /bin/zsh, etc. on Unix-like systems
206
+
207
+ shell = detect_shell()
208
+ if not shell:
209
+ raise EnvironmentError("Could not detect the shell type.")
210
+ # Execute the command in the detected shell
211
+ result = subprocess.run(command, shell=True, executable=shell, capture_output=True, text=True)
212
+ return shell, result
213
+
214
+ def filter_commands(self, cmd):
215
+ cmd = cmd.strip()
216
+ # HANDLE wait COMMAND
217
+ if cmd.startswith("WAIT"):
218
+ wait_cmd = cmd.split()
219
+ wait = int(wait_cmd[1]) if len(wait_cmd) > 1 else 1
220
+ action_console("WAIT", cmd)
221
+ if DRY_RUN:
222
+ return None
223
+ time.sleep(wait)
224
+ return None
225
+ # HANDLE wait COMMAND
226
+ if cmd.startswith("MACRO"):
227
+ action_console("MACRO", cmd)
228
+ macro_name = cmd.split()[1].strip()
229
+ self.run_embedded_macro(macro_name)
230
+ return None
231
+ if cmd.startswith("SHELL"):
232
+ action_console("SHELL", cmd)
233
+ cmd = ' '.join(cmd.split()[1:])
234
+ try:
235
+ shell, result = self._run_shell_command(cmd)
236
+ stdout = result.stdout.strip()
237
+ stderr = "\n" + result.stderr.strip()
238
+ action_console(" " * 8, f"[{result.returncode}] {shell} {cmd}\n{stdout}{stderr}")
239
+ except Exception as e:
240
+ action_console("ERR", f"NoShell: {e}")
241
+ return None
242
+ return cmd
243
+
244
+ @staticmethod
245
+ def validate_conf(conf):
246
+ is_valid = False
247
+ parsed_config = {'dbg': False, 'device': None, 'pwd': "ADmin123", 'safe': False, 'parallel': False}
248
+ conf_list = conf.strip().split()
249
+ if len(conf_list) < 2:
250
+ print(f"Not enough parameters:\n#macroscript devName.local/IPaddress\n\t{conf}")
251
+ sys.exit(3)
252
+ if conf_list[0] == "#macroscript":
253
+ is_valid = True
254
+ # FLAG: safe
255
+ parsed_config['safe'] = "safe" in conf_list
256
+ if parsed_config['safe']:
257
+ conf_list.remove("safe")
258
+ # FLAG: debug
259
+ parsed_config['dbg'] = "debug" in conf_list
260
+ if parsed_config['dbg']:
261
+ conf_list.remove("debug")
262
+ # FLAG: parallel
263
+ parsed_config['parallel'] = "parallel" in conf_list
264
+ if parsed_config['parallel']:
265
+ conf_list.remove("parallel")
266
+ # PARAM: device
267
+ parsed_config['device'] = conf_list[1]
268
+ # PARAM: pwd
269
+ if len(conf_list) > 2:
270
+ parsed_config['pwd'] = conf_list[2]
271
+ return is_valid, parsed_config
272
+
273
+ @staticmethod
274
+ def create_template(path):
275
+ default_conf = """#macroscript 127.0.0.1 safe
276
+ #
277
+ # HINTS:
278
+ # SHEBANG LINE: #macroscript <device> <password> <opts>
279
+ # <device> : host name or IP address
280
+ # <password> : optional parameter, defualt: ADmin123
281
+ # <opts> : additional optional params:
282
+ # debug : show verbose output
283
+ # safe : only run loaded modules
284
+ # parallel: enable parallel macro execution
285
+ #
286
+ # You can list any load module function call to be remotely executed
287
+ #
288
+ # Additional script commands:
289
+ # WAIT <s> - seconds to wait before execute next command
290
+ # MACRO <name> - run a macro by name (from same dir as parent macro)
291
+ # SHELL <command> - run shell oneliners
292
+ # # - line comment
293
+
294
+ system top
295
+ system clock
296
+ WAIT 1
297
+ system clock
298
+
299
+ """
300
+
301
+ print(f"{color.OKGREEN}Create template for{color.NC} {path}")
302
+ with open(path, 'w') as f:
303
+ f.write(default_conf)
304
+
305
+ def run_micro_script(self, path):
306
+ try:
307
+ self._run_micro_script(path)
308
+ except KeyboardInterrupt:
309
+ print("Exiting...")
310
+
311
+ def _run_micro_script(self, path):
312
+ if os.path.isdir(path):
313
+ path = select_menu(path)
314
+ if path is None:
315
+ action_console("ERR", "Cannot find macro for selection menu")
316
+ sys.exit(5)
317
+ if not (os.path.isfile(path) and path.endswith(".macro")):
318
+ print(f"No file was found: {path} or extension is not .macro")
319
+ self.create_template(path)
320
+ return
321
+ with open(path, 'r') as f:
322
+ lines = [ l for l in f.read().strip().splitlines() if len(l.strip()) > 0 ]
323
+ conf = lines.pop(0)
324
+ lines = [ l for l in lines if not l.strip().startswith("#") ]
325
+ conf_is_valid, conf = self.validate_conf(conf)
326
+ if not conf_is_valid:
327
+ print("Invalid initial line, must starts with #macroscript")
328
+ sys.exit(2)
329
+
330
+ if self.com is None:
331
+ # Inject params from macro if was not set by constructor
332
+ self.safe_mode = conf['safe'] if self.safe_mode is None else self.safe_mode
333
+ self.verbose = conf['dbg'] if self.verbose is None else self.verbose
334
+ self.parallel = conf['parallel'] if self.parallel is None else self.parallel
335
+ self.host = conf['device'] if self.host is None else self.host
336
+ self.pwd = conf['pwd'] if self.pwd is None else self.pwd
337
+ self.workdir = os.path.dirname(path)
338
+ self.macro_name = os.path.basename(path).split(".")[0].strip()
339
+ action_console("INIT", f"{color.BOLD}{color.OK}\nmacroSCRIPT{color.NC}: {self.macro_name} {color.BOLD}{self.host}{color.NC}")
340
+ if DRY_RUN:
341
+ print(f"conf:{conf}\ncommands:{lines}")
342
+ start_time = datetime.now()
343
+ cmd_cnt = 0
344
+ try:
345
+ for cmd in lines:
346
+ cmd = self.filter_commands(cmd)
347
+ if cmd is None:
348
+ continue
349
+ cmd_cnt += 1
350
+ state, out = self.run(cmd, force_close=False)
351
+ if not state:
352
+ print("Communication was broken...")
353
+ break
354
+ except Exception as e:
355
+ print(f"Connection error {self.macro_name}: {e}")
356
+ finally:
357
+ if self.com is not None:
358
+ self.com.close()
359
+ end_time = datetime.now()
360
+ elapsed = _elapsed_time(start_time, end_time)
361
+ print(f"[{color.BOLD}{color.OK}{self.macro_name}{color.NC}] Elapsed time: {elapsed} / {cmd_cnt} command(s).")
362
+
363
+
364
+
365
+ if __name__ == "__main__":
366
+ if len(sys.argv) == 1:
367
+ print("Missing file input")
368
+ sys.exit(1)
369
+ MICROSCRIPT_PATH = sys.argv[1]
370
+ executor = Executor()
371
+ executor.run_micro_script(MICROSCRIPT_PATH)
@@ -1,6 +1,4 @@
1
1
  import socket
2
- import sys
3
-
4
2
  import select
5
3
  import re
6
4
  import time
@@ -10,6 +8,25 @@ except:
10
8
  from TerminalColors import Colors as color
11
9
 
12
10
 
11
+ ANSI_ESCAPE_RE = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]")
12
+
13
+ def load_command_history(prompt_getter):
14
+ """Optional command history feature"""
15
+ try:
16
+ try:
17
+ from .micrOSClientHistory import CommandInterface
18
+ except:
19
+ from micrOSClientHistory import CommandInterface
20
+ except Exception as e:
21
+ print(f"Command history - disabled (readline module error): {e}")
22
+ return None
23
+ try:
24
+ return CommandInterface(prompt=prompt_getter)
25
+ except Exception as e:
26
+ print(f"Command history error: {e}")
27
+ return None
28
+
29
+
13
30
  class micrOSClient:
14
31
  CONN_MAP = {}
15
32
 
@@ -35,6 +52,10 @@ class micrOSClient:
35
52
  # Validate and resolve host (IP/Hostname)
36
53
  self.__address_manager()
37
54
 
55
+ @property
56
+ def telnet_prompt(self):
57
+ return f"{color.BOLD}{self.preprompt}{self.prompt}{color.NC} "
58
+
38
59
  def __address_manager(self):
39
60
  self.dbg_print("[INIT] micrOSClient")
40
61
  # Host is valid IP address - self.host is ip address OK
@@ -50,7 +71,7 @@ class micrOSClient:
50
71
  if "__simulator__" in self.host:
51
72
  # Simulator hack - due to no dhcp available
52
73
  self.host = '127.0.0.1'
53
- self.hostname = 'node01'
74
+ self.hostname = 'simulator' # HARDCODE MATCHING HOSTNAME FOR __simulator__
54
75
  else:
55
76
  # * Set self.host to ip address OK
56
77
  self.host = socket.getaddrinfo(self.host, self.port)[-1][4][0]
@@ -67,10 +88,22 @@ class micrOSClient:
67
88
 
68
89
  @staticmethod
69
90
  def validate_ipv4(str_in):
70
- pattern = "^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"
71
- if bool(re.match(pattern, str_in)):
72
- return True
73
- return False
91
+ parts = str_in.split(".")
92
+ # An IPv4 address must have exactly 4 parts
93
+ if len(parts) != 4:
94
+ return False
95
+ for part in parts:
96
+ # Each part must be a number and not empty
97
+ if not part.isdigit():
98
+ return False
99
+ num = int(part)
100
+ # Each number must be in the valid range (0-255)
101
+ if num < 0 or num > 255:
102
+ return False
103
+ # Prevents leading zeros (e.g., "01" is invalid)
104
+ if part != str(num):
105
+ return False
106
+ return True
74
107
 
75
108
  def __connect(self, timeout):
76
109
  # Server connection - create socket
@@ -143,24 +176,35 @@ class micrOSClient:
143
176
 
144
177
  def __filter_preprompt(self, _data):
145
178
  if len(_data) == 0:
146
- return
147
- last_line = _data.strip().split('\n')[-1]
148
- # get pre-prompt: >[configure]< prompt $
179
+ return _data
180
+
181
+ has_trailing_newline = _data.endswith('\n')
182
+ working = _data.rstrip('\n')
183
+ lines = working.split('\n') if working else ['']
184
+ last_line = lines[-1]
185
+
149
186
  if self.prompt is not None:
150
- # Check prompt is in last line
151
- if self.prompt in last_line:
152
- # Check pre-prompt - remove prompt
153
- x = last_line.replace(self.prompt, '')
154
- # SET preprompt if preprompt exists
155
- self.preprompt = x if len(x) > 0 else self.preprompt
156
- # Pre-prompt remove and cancel preprompt modes
157
- if self.preprompt in last_line:
158
- _data = _data.replace(self.preprompt, "")
159
- else:
160
- self.preprompt = ""
161
- return _data
187
+ prompt_index = last_line.find(self.prompt)
188
+ if prompt_index != -1:
189
+ raw_prefix = last_line[:prompt_index]
190
+ plain_prefix = ANSI_ESCAPE_RE.sub('', raw_prefix)
191
+
192
+ plain_prefix = plain_prefix.rstrip()
193
+ if plain_prefix:
194
+ if not plain_prefix.endswith(' '):
195
+ plain_prefix += ' '
196
+ self.preprompt = plain_prefix
197
+ else:
198
+ self.preprompt = ""
199
+
200
+ lines[-1] = last_line[prompt_index:]
162
201
 
163
- def __receive_data(self, read_timeout=20):
202
+ rebuilt = '\n'.join(lines)
203
+ if has_trailing_newline:
204
+ rebuilt += '\n'
205
+ return rebuilt
206
+
207
+ def __receive_data(self, read_timeout=20, stream=False):
164
208
  """
165
209
  Client Receiver Loop
166
210
  - read_timeout - wait for server to reply (should be <15, avoid msg queue-ing)
@@ -173,7 +217,11 @@ class micrOSClient:
173
217
  # Collect answer data
174
218
  if select.select([self.conn], [], [], read_timeout)[0]:
175
219
  while True:
176
- data_buffer += self.conn.recv(4096).decode('utf-8')
220
+ incoming_data = self.conn.recv(4096).decode('utf-8')
221
+ if stream:
222
+ incoming_data = incoming_data.replace(self.prompt, f"{color.NC}{color.BOLD}{self.prompt}{color.NC}")
223
+ print(f"\r{color.LIGHT_GRAY}{incoming_data}{color.NC}", end="")
224
+ data_buffer += incoming_data
177
225
  # Last line from data_buffer (handle fragmented messages - prompt detection)
178
226
  last_line = data_buffer.strip().split("\n")[-1]
179
227
  # Wait for prompt or special cases (exit/prompt)
@@ -200,7 +248,7 @@ class micrOSClient:
200
248
  self.isconn = False
201
249
  self.spacer = 0
202
250
 
203
- def __run_command(self, cmd):
251
+ def __run_command(self, cmd, stream=False):
204
252
  """
205
253
  Run command on server tcp/ip connection
206
254
  - prompt check - validate device ("hostname $" = "prompt")
@@ -218,14 +266,14 @@ class micrOSClient:
218
266
  # Workaround for reboot command - micrOS async server cannot send Bye! msg before reboot.
219
267
  if reboot_request:
220
268
  return 'Bye!'
221
- data = self.__receive_data()
269
+ data = self.__receive_data(stream=stream)
222
270
  return data
223
271
  # Skip command run: prompt and host not the same!
224
272
  print(f"[micrOSClient] {color.ERR}prompt mismatch{color.NC}, hostname: {check_hostname} prompt: {check_prompt} ")
225
273
  # Check UID?
226
274
  return None
227
275
 
228
- def send_cmd(self, cmd, timeout=3, retry=5):
276
+ def send_cmd(self, cmd, timeout=3, retry=5, stream=False):
229
277
  """
230
278
  Send command function - main usage for non interactive mode
231
279
  """
@@ -244,7 +292,7 @@ class micrOSClient:
244
292
 
245
293
  # @ Run command
246
294
  try:
247
- out = self.__run_command(cmd)
295
+ out = self.__run_command(cmd, stream=stream)
248
296
  except Exception as e:
249
297
  self.dbg_print("{}[ERR]{} send_cmd error: {}".format(color.ERR, color.NC, e))
250
298
  self.dbg_print("Auto deinit connection")
@@ -258,14 +306,14 @@ class micrOSClient:
258
306
  f_delta_t = "{}[{:.2f}]{}".format(color.OKGREEN, delta_time, color.NC)
259
307
  self.dbg_print("{}[⏰] {} {}reply: {}{}".format(f_delta_t, cmd, color.BOLD, out, color.NC))
260
308
 
261
- # return output list
309
+ # return output list or None
262
310
  return out
263
311
 
264
- def send_cmd_retry(self, cmd, timeout=6, retry=5):
312
+ def send_cmd_retry(self, cmd, timeout=6, retry=5, stream=False):
265
313
  out = None
266
314
  for cnt in range(0, retry):
267
315
  try:
268
- out = self.send_cmd(cmd, timeout)
316
+ out = self.send_cmd(cmd, timeout, stream=stream)
269
317
  if out is None or isinstance(out, list):
270
318
  break
271
319
  except OSError as e:
@@ -277,7 +325,7 @@ class micrOSClient:
277
325
  time.sleep(0.2)
278
326
  return out
279
327
 
280
- def telnet(self, timeout=4):
328
+ def telnet(self, timeout=5):
281
329
  """
282
330
  Implements interactive mode for socket communication.
283
331
  """
@@ -287,18 +335,41 @@ class micrOSClient:
287
335
  print("Telnet connect: {}".format(e))
288
336
  if "busy" in str(e) or "timed out" in str(e) or "No route to host" in str(e) or "Host is down" in str(e):
289
337
  return
338
+
339
+ history = load_command_history(self.telnet_prompt) # History: Beta feature
340
+ if history is not None:
341
+ history.prompt = self.telnet_prompt
342
+ print(self.telnet_prompt, end="")
343
+ is_empty = False # Empty input support
290
344
  while True:
291
- cmd = input("{}{} ".format(self.preprompt, self.prompt))
292
- # send command
293
- output = self.send_cmd(cmd)
294
- # Format output to human readable
295
- output = '\n'.join(output) if isinstance(output, list) else output
296
- # output to STDOUT
297
- if not (cmd.strip() == '' and output is None):
298
- print(output)
299
- # Close session
300
- if 'Bye!' in str(output):
345
+ try:
346
+ # INPUT HANDLING
347
+ if history is not None:
348
+ history.prompt = self.telnet_prompt
349
+ cmd = input(self.telnet_prompt if is_empty else '') # CANNOT contain prompt - it is coming back from response data
350
+ if len(cmd.strip()) == 0:
351
+ is_empty = True
352
+ if history is not None:
353
+ history.prompt = self.telnet_prompt
354
+ continue
355
+ is_empty = False
356
+ # SEND COMMAND
357
+ output = self.send_cmd(cmd, timeout=timeout, stream=True)
358
+ if history is not None:
359
+ history.prompt = self.telnet_prompt
360
+ if not (history is None or output is None) and "Shell: for hints type help." not in output: # History: Beta feature
361
+ history.add_history(cmd)
362
+ # OUTPUT HANDLING
363
+ if 'Bye!' in str(output):
364
+ break
365
+ if output is None:
366
+ print("Exiting... client disconnected")
367
+ break
368
+ except KeyboardInterrupt:
369
+ print("Exiting...")
301
370
  break
371
+ if history is not None: # History: Beta feature
372
+ history.save_history()
302
373
  self.close()
303
374
 
304
375
  def dbg_print(self, msg, end='\n'):
@@ -325,8 +396,9 @@ class micrOSClient:
325
396
  verdict.append(console_msg)
326
397
  print(f"===>\t\t{console_msg}")
327
398
  self.close()
328
- verdict.append(f"SINGLE CONNECTION LOAD TEST X{cnt}, AVERAGE REPLY TIME: {round(delta_t_all/cnt, 3)} sec\n")
329
- return verdict
399
+ delta_t_result = round(delta_t_all / cnt, 3)
400
+ verdict.append(f"SINGLE CONNECTION LOAD TEST X{cnt}, AVERAGE REPLY TIME: {delta_t_result} sec\n")
401
+ return verdict, delta_t_result
330
402
 
331
403
  def __del__(self):
332
404
  if self.dbg and self.avg_reply[1] > 0:
@@ -355,9 +427,10 @@ def micros_connection_metrics(address):
355
427
  print(f"\t\t{_console_msg}")
356
428
  all_reply.append(_console_msg)
357
429
  success_rate = int(round(_success / cnt, 2) * 100)
358
- all_reply.append(f"MULTI CONNECTION LOAD TEST X{cnt}, AVERAGE REPLY TIME: {round(_all_delta_t/cnt, 3)}s, "
359
- f"SERVER AVAILABILITY: {success_rate}% ({round(_all_delta_t/_success, 3)}s)")
360
- return all_reply
430
+ delta_t_result = round(_all_delta_t/cnt, 3)
431
+ all_reply.append(f"MULTI CONNECTION LOAD TEST X{cnt}, AVERAGE REPLY TIME: {delta_t_result}s, "
432
+ f"SERVER AVAILABILITY: {success_rate}% ({int((_all_delta_t/_success)*1000)} ms)")
433
+ return all_reply, delta_t_result
361
434
 
362
435
  # ---------------------------------------------------- #
363
436
  high_level_verdict = []
@@ -365,12 +438,12 @@ def micros_connection_metrics(address):
365
438
  # [1] Create micrOSClient object + Run LOAD tests
366
439
  com_obj = micrOSClient(host=address, port=9008, pwd="ADmin123", dbg=True)
367
440
  # [1.1] Run load test in one connection
368
- verdict_list = com_obj.load_test()
441
+ verdict_list, delta_t_single_session = com_obj.load_test()
369
442
  com_obj.close()
370
443
  high_level_verdict.append(verdict_list[-1])
371
444
 
372
445
  # [2] Run multi connection load test - reconnect - raw connection (without retry)
373
- verdict_multi = multi_conn_load(address)
446
+ verdict_multi, delta_t_multi_session = multi_conn_load(address)
374
447
  high_level_verdict.append((verdict_multi[-1]))
375
448
 
376
449
  ############################################################################
@@ -382,7 +455,7 @@ def micros_connection_metrics(address):
382
455
  for k in verdict_multi:
383
456
  print(f"\t{k}")
384
457
 
385
- return high_level_verdict
458
+ return high_level_verdict, delta_t_single_session, delta_t_multi_session
386
459
 
387
460
 
388
461
  if __name__ == "__main__":
@@ -416,7 +489,7 @@ if __name__ == "__main__":
416
489
  print(f"noconf out: {noconf_mode}")
417
490
  if force_close: com_obj.close()
418
491
 
419
- verdict = micros_connection_metrics(address=address)
492
+ verdict, delta_t_single, delta_t_multi = micros_connection_metrics(address=address)
420
493
  for k in verdict:
421
494
  print(f"+\t\t{k}")
422
495