micrOSDevToolKit 2.9.1__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 (368) hide show
  1. env/driver_cp210x/macOS_VCP_Driver/SiLabsUSBDriverDisk.dmg +0 -0
  2. env/driver_cp210x/macOS_VCP_Driver/macOS_VCP_Driver_Release_Notes.txt +17 -1
  3. micrOS/micropython/esp32-20251209-v1.27.0.bin +0 -0
  4. micrOS/micropython/esp32c3-20251209-v1.27.0.bin +0 -0
  5. micrOS/micropython/esp32c6-20251209-v1.27.0.bin +0 -0
  6. micrOS/micropython/esp32s2-20251209-v1.27.0.bin +0 -0
  7. micrOS/micropython/esp32s2-LOLIN_MINI-20251209-v1.27.0.bin +0 -0
  8. micrOS/micropython/{esp32s3-20241129-v1.24.1.bin → esp32s3-4MBflash-20241129-v1.24.1.bin} +0 -0
  9. micrOS/micropython/esp32s3-8MBflash-20251209-v1.27.0.bin +0 -0
  10. micrOS/micropython/esp32s3_spiram_oct-20251209-v1.27.0.bin +0 -0
  11. micrOS/micropython/rpi-pico-w-20251209-v1.27.0.uf2 +0 -0
  12. micrOS/micropython/tinypico-20251209-v1.27.0.bin +0 -0
  13. micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +167 -163
  14. micrOS/source/Auth.py +37 -0
  15. micrOS/source/Common.py +361 -116
  16. micrOS/source/Config.py +32 -22
  17. micrOS/source/Debug.py +50 -94
  18. micrOS/source/Espnow.py +377 -100
  19. micrOS/source/Files.py +207 -0
  20. micrOS/source/Hooks.py +48 -20
  21. micrOS/source/InterConnect.py +126 -42
  22. micrOS/source/Interrupts.py +6 -6
  23. micrOS/source/Logger.py +63 -26
  24. micrOS/source/Network.py +41 -21
  25. micrOS/source/Notify.py +48 -22
  26. micrOS/source/Pacman.py +326 -0
  27. micrOS/source/Scheduler.py +14 -54
  28. micrOS/source/Server.py +67 -69
  29. micrOS/source/Shell.py +99 -91
  30. micrOS/source/Tasks.py +141 -95
  31. micrOS/source/Time.py +19 -18
  32. micrOS/source/Types.py +53 -9
  33. micrOS/source/Web.py +381 -76
  34. micrOS/source/__pycache__/Common.cpython-312.pyc +0 -0
  35. micrOS/source/__pycache__/Debug.cpython-312.pyc +0 -0
  36. micrOS/source/__pycache__/Files.cpython-312.pyc +0 -0
  37. micrOS/source/__pycache__/Logger.cpython-312.pyc +0 -0
  38. micrOS/source/__pycache__/Scheduler.cpython-312.pyc +0 -0
  39. micrOS/source/__pycache__/Server.cpython-312.pyc +0 -0
  40. micrOS/source/__pycache__/Shell.cpython-312.pyc +0 -0
  41. micrOS/source/__pycache__/replhelper.cpython-312.pyc +0 -0
  42. micrOS/source/config/_git.keep +0 -0
  43. micrOS/source/helpers.py +132 -0
  44. micrOS/source/micrOS.py +17 -7
  45. micrOS/source/micrOSloader.py +5 -12
  46. micrOS/source/microIO.py +44 -20
  47. micrOS/source/modules/IO_esp32c6.py +38 -0
  48. micrOS/source/{IO_esp32s3.py → modules/IO_esp32s3.py} +37 -1
  49. micrOS/source/{IO_m5stamp.py → modules/IO_m5stamp.py} +35 -1
  50. micrOS/source/{IO_qtpy.py → modules/IO_qtpy.py} +22 -17
  51. micrOS/source/{IO_tinypico.py → modules/IO_tinypico.py} +38 -0
  52. micrOS/source/modules/LM_L298N.py +161 -0
  53. {toolkit/workspace/precompiled → micrOS/source/modules}/LM_L9110_DCmotor.py +3 -3
  54. micrOS/source/{LM_OV2640.py → modules/LM_OV2640.py} +45 -27
  55. micrOS/source/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +3 -3
  56. micrOS/source/{LM_aht10.py → modules/LM_aht10.py} +2 -2
  57. micrOS/source/{LM_bme280.py → modules/LM_bme280.py} +3 -3
  58. micrOS/source/{LM_buzzer.py → modules/LM_buzzer.py} +18 -25
  59. micrOS/source/{LM_cct.py → modules/LM_cct.py} +17 -21
  60. micrOS/source/modules/LM_cluster.py +255 -0
  61. micrOS/source/{LM_co2.py → modules/LM_co2.py} +3 -3
  62. micrOS/source/{LM_dht11.py → modules/LM_dht11.py} +2 -2
  63. micrOS/source/{LM_dht22.py → modules/LM_dht22.py} +2 -2
  64. micrOS/source/{LM_dimmer.py → modules/LM_dimmer.py} +9 -9
  65. micrOS/source/{LM_distance.py → modules/LM_distance.py} +4 -6
  66. micrOS/source/{LM_ds18.py → modules/LM_ds18.py} +2 -2
  67. micrOS/source/{LM_esp32.py → modules/LM_esp32.py} +5 -0
  68. micrOS/source/modules/LM_espnow.py +53 -0
  69. micrOS/source/modules/LM_fileserver.py +265 -0
  70. micrOS/source/{LM_genIO.py → modules/LM_genIO.py} +52 -37
  71. micrOS/source/{LM_haptic.py → modules/LM_haptic.py} +2 -2
  72. {toolkit/workspace/precompiled → micrOS/source/modules}/LM_i2c.py +5 -4
  73. micrOS/source/{LM_i2s_mic.py → modules/LM_i2s_mic.py} +6 -7
  74. micrOS/source/{LM_ld2410.py → modules/LM_ld2410.py} +2 -2
  75. micrOS/source/{LM_light_sensor.py → modules/LM_light_sensor.py} +10 -21
  76. micrOS/source/modules/LM_mh_z19c.py +198 -0
  77. micrOS/source/modules/LM_neoeffects.py +284 -0
  78. micrOS/source/{LM_neopixel.py → modules/LM_neopixel.py} +19 -23
  79. micrOS/source/{LM_oled.py → modules/LM_oled.py} +2 -2
  80. micrOS/source/{LM_oled_sh1106.py → modules/LM_oled_sh1106.py} +3 -3
  81. micrOS/source/{LM_oled_ui.py → modules/LM_oled_ui.py} +72 -64
  82. micrOS/source/modules/LM_pacman.py +320 -0
  83. micrOS/source/{LM_presence.py → modules/LM_presence.py} +11 -15
  84. micrOS/source/modules/LM_qmi8658.py +204 -0
  85. micrOS/source/{LM_rencoder.py → modules/LM_rencoder.py} +2 -2
  86. micrOS/source/{LM_rest.py → modules/LM_rest.py} +4 -6
  87. micrOS/source/{LM_rgb.py → modules/LM_rgb.py} +21 -29
  88. micrOS/source/{LM_roboarm.py → modules/LM_roboarm.py} +8 -8
  89. micrOS/source/modules/LM_robustness.py +137 -0
  90. micrOS/source/{LM_servo.py → modules/LM_servo.py} +3 -3
  91. micrOS/source/{LM_stepper.py → modules/LM_stepper.py} +5 -5
  92. micrOS/source/{LM_switch.py → modules/LM_switch.py} +11 -9
  93. micrOS/source/{LM_system.py → modules/LM_system.py} +38 -32
  94. micrOS/source/modules/LM_tcs3472.py +187 -0
  95. micrOS/source/{LM_telegram.py → modules/LM_telegram.py} +164 -116
  96. micrOS/source/{LM_trackball.py → modules/LM_trackball.py} +3 -3
  97. micrOS/source/{LM_veml7700.py → modules/LM_veml7700.py} +2 -2
  98. micrOS/source/modules/LM_web.py +38 -0
  99. micrOS/source/urequests.py +39 -15
  100. {toolkit/workspace/precompiled → micrOS/source/web}/dashboard.html +4 -0
  101. micrOS/source/web/editor.js +440 -0
  102. micrOS/source/web/filesui.html +178 -0
  103. micrOS/source/web/filesui.js +338 -0
  104. {toolkit/workspace/precompiled → micrOS/source/web}/index.html +44 -2
  105. micrOS/source/{uapi.js → web/uapi.js} +48 -7
  106. micrOS/source/{ustyle.css → web/ustyle.css} +6 -3
  107. micrOS/utests/__init__.py +0 -0
  108. micrOS/utests/test_scheduler.py +435 -0
  109. {micrOSDevToolKit-2.9.1.data → microsdevtoolkit-2.26.1.data}/scripts/devToolKit.py +33 -3
  110. {micrOSDevToolKit-2.9.1.dist-info → microsdevtoolkit-2.26.1.dist-info}/METADATA +327 -268
  111. microsdevtoolkit-2.26.1.dist-info/RECORD +396 -0
  112. {micrOSDevToolKit-2.9.1.dist-info → microsdevtoolkit-2.26.1.dist-info}/WHEEL +1 -1
  113. toolkit/DevEnvCompile.py +63 -33
  114. toolkit/DevEnvOTA.py +58 -22
  115. toolkit/DevEnvUSB.py +110 -55
  116. toolkit/Gateway.py +6 -6
  117. toolkit/LM_to_compile.dat +6 -4
  118. toolkit/MicrOSDevEnv.py +127 -57
  119. toolkit/WebRepl.py +73 -0
  120. toolkit/dashboard_apps/BackupRestore.py +20 -35
  121. toolkit/dashboard_apps/CCTDemo.py +12 -17
  122. toolkit/dashboard_apps/CCTTest.py +20 -24
  123. toolkit/dashboard_apps/CamStream.py +2 -6
  124. toolkit/dashboard_apps/CatGame.py +14 -16
  125. toolkit/dashboard_apps/Dimmer.py +11 -21
  126. toolkit/dashboard_apps/GetVersion.py +11 -19
  127. toolkit/dashboard_apps/MicrophoneTest.py +1 -6
  128. toolkit/dashboard_apps/NeoEffectsDemo.py +22 -35
  129. toolkit/dashboard_apps/NeopixelTest.py +20 -25
  130. toolkit/dashboard_apps/PresenceTest.py +2 -8
  131. toolkit/dashboard_apps/QMI8685_GYRO.py +68 -0
  132. toolkit/dashboard_apps/RGBTest.py +20 -24
  133. toolkit/dashboard_apps/RoboArm.py +24 -32
  134. toolkit/dashboard_apps/SED_test.py +10 -14
  135. toolkit/dashboard_apps/SensorsTest.py +61 -0
  136. toolkit/dashboard_apps/SystemTest.py +202 -105
  137. toolkit/dashboard_apps/Template_app.py +11 -23
  138. toolkit/dashboard_apps/_app_base.py +34 -0
  139. toolkit/dashboard_apps/_gyro_visualizer.py +78 -0
  140. toolkit/dashboard_apps/uLightDemo.py +15 -24
  141. toolkit/index.html +4 -4
  142. toolkit/lib/LocalMachine.py +6 -1
  143. toolkit/lib/MicrosFiles.py +46 -0
  144. toolkit/lib/Repository.py +64 -0
  145. toolkit/lib/TerminalColors.py +4 -0
  146. toolkit/lib/macroScript.py +6 -0
  147. toolkit/lib/micrOSClient.py +123 -50
  148. toolkit/lib/micrOSClientHistory.py +156 -0
  149. toolkit/lib/pip_package_installer.py +5 -2
  150. toolkit/micrOSdashboard.py +12 -17
  151. toolkit/micrOSlint.py +20 -8
  152. toolkit/simulator_lib/__pycache__/IO_darwin.cpython-312.pyc +0 -0
  153. toolkit/simulator_lib/__pycache__/aioespnow.cpython-312.pyc +0 -0
  154. toolkit/simulator_lib/__pycache__/framebuf.cpython-312.pyc +0 -0
  155. toolkit/simulator_lib/__pycache__/machine.cpython-312.pyc +0 -0
  156. toolkit/simulator_lib/__pycache__/micropython.cpython-312.pyc +0 -0
  157. toolkit/simulator_lib/__pycache__/mip.cpython-312.pyc +0 -0
  158. toolkit/simulator_lib/__pycache__/neopixel.cpython-312.pyc +0 -0
  159. toolkit/simulator_lib/__pycache__/network.cpython-312.pyc +0 -0
  160. toolkit/simulator_lib/__pycache__/sim_common.cpython-312.pyc +0 -0
  161. toolkit/simulator_lib/__pycache__/simgc.cpython-312.pyc +0 -0
  162. toolkit/simulator_lib/__pycache__/simulator.cpython-312.pyc +0 -0
  163. toolkit/simulator_lib/__pycache__/uasyncio.cpython-312.pyc +0 -0
  164. toolkit/simulator_lib/__pycache__/uos.cpython-312.pyc +0 -0
  165. toolkit/simulator_lib/__pycache__/urandom.cpython-312.pyc +0 -0
  166. toolkit/simulator_lib/__pycache__/usocket.cpython-312.pyc +0 -0
  167. toolkit/simulator_lib/__pycache__/ussl.cpython-312.pyc +0 -0
  168. toolkit/simulator_lib/aioespnow.py +28 -0
  169. toolkit/simulator_lib/dht.py +1 -1
  170. toolkit/simulator_lib/framebuf.py +49 -1
  171. toolkit/simulator_lib/machine.py +17 -2
  172. toolkit/simulator_lib/micropython.py +3 -3
  173. toolkit/simulator_lib/mip.py +165 -0
  174. toolkit/simulator_lib/neopixel.py +3 -2
  175. toolkit/simulator_lib/network.py +2 -1
  176. toolkit/simulator_lib/node_config.json +2 -3
  177. toolkit/simulator_lib/ntptime.py +1 -1
  178. toolkit/simulator_lib/{sim_console.py → sim_common.py} +2 -3
  179. toolkit/simulator_lib/simgc.py +6 -2
  180. toolkit/simulator_lib/simulator.py +137 -59
  181. toolkit/simulator_lib/uasyncio.py +33 -2
  182. toolkit/simulator_lib/uos.py +147 -0
  183. toolkit/simulator_lib/urandom.py +4 -0
  184. toolkit/socketClient.py +43 -23
  185. toolkit/user_data/webhooks/generic.py +1 -1
  186. toolkit/user_data/webhooks/macro.py +1 -1
  187. toolkit/user_data/webhooks/template.py +1 -1
  188. toolkit/workspace/precompiled/Auth.mpy +0 -0
  189. toolkit/workspace/precompiled/Common.mpy +0 -0
  190. toolkit/workspace/precompiled/Config.mpy +0 -0
  191. toolkit/workspace/precompiled/Debug.mpy +0 -0
  192. toolkit/workspace/precompiled/Espnow.mpy +0 -0
  193. toolkit/workspace/precompiled/Files.mpy +0 -0
  194. toolkit/workspace/precompiled/Hooks.mpy +0 -0
  195. toolkit/workspace/precompiled/InterConnect.mpy +0 -0
  196. toolkit/workspace/precompiled/Interrupts.mpy +0 -0
  197. toolkit/workspace/precompiled/Logger.mpy +0 -0
  198. toolkit/workspace/precompiled/Network.mpy +0 -0
  199. toolkit/workspace/precompiled/Notify.mpy +0 -0
  200. toolkit/workspace/precompiled/Pacman.mpy +0 -0
  201. toolkit/workspace/precompiled/Scheduler.mpy +0 -0
  202. toolkit/workspace/precompiled/Server.mpy +0 -0
  203. toolkit/workspace/precompiled/Shell.mpy +0 -0
  204. toolkit/workspace/precompiled/Tasks.mpy +0 -0
  205. toolkit/workspace/precompiled/Time.mpy +0 -0
  206. toolkit/workspace/precompiled/Types.mpy +0 -0
  207. toolkit/workspace/precompiled/Web.mpy +0 -0
  208. toolkit/workspace/precompiled/_mpy.version +1 -1
  209. toolkit/workspace/precompiled/config/_git.keep +0 -0
  210. toolkit/workspace/precompiled/helpers.mpy +0 -0
  211. toolkit/workspace/precompiled/micrOS.mpy +0 -0
  212. toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
  213. toolkit/workspace/precompiled/microIO.mpy +0 -0
  214. toolkit/workspace/precompiled/{IO_esp32.mpy → modules/IO_esp32.mpy} +0 -0
  215. toolkit/workspace/precompiled/{IO_esp32c3.mpy → modules/IO_esp32c3.mpy} +0 -0
  216. toolkit/workspace/precompiled/modules/IO_esp32c6.mpy +0 -0
  217. toolkit/workspace/precompiled/{IO_esp32s2.mpy → modules/IO_esp32s2.mpy} +0 -0
  218. toolkit/workspace/precompiled/modules/IO_esp32s3.mpy +0 -0
  219. toolkit/workspace/precompiled/modules/IO_m5stamp.mpy +0 -0
  220. toolkit/workspace/precompiled/modules/IO_qtpy.mpy +0 -0
  221. toolkit/workspace/precompiled/modules/IO_rp2.mpy +0 -0
  222. toolkit/workspace/precompiled/modules/IO_tinypico.mpy +0 -0
  223. toolkit/workspace/precompiled/modules/LM_L298N.mpy +0 -0
  224. {micrOS/source → toolkit/workspace/precompiled/modules}/LM_L9110_DCmotor.py +3 -3
  225. toolkit/workspace/precompiled/modules/LM_OV2640.mpy +0 -0
  226. toolkit/workspace/precompiled/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +3 -3
  227. toolkit/workspace/precompiled/{LM_aht10.mpy → modules/LM_aht10.mpy} +0 -0
  228. toolkit/workspace/precompiled/{LM_bme280.mpy → modules/LM_bme280.mpy} +0 -0
  229. toolkit/workspace/precompiled/{LM_buzzer.mpy → modules/LM_buzzer.mpy} +0 -0
  230. toolkit/workspace/precompiled/modules/LM_cct.mpy +0 -0
  231. toolkit/workspace/precompiled/modules/LM_cluster.mpy +0 -0
  232. toolkit/workspace/precompiled/{LM_co2.mpy → modules/LM_co2.mpy} +0 -0
  233. toolkit/workspace/precompiled/{LM_dht11.mpy → modules/LM_dht11.mpy} +0 -0
  234. toolkit/workspace/precompiled/{LM_dht22.mpy → modules/LM_dht22.mpy} +0 -0
  235. toolkit/workspace/precompiled/modules/LM_dimmer.mpy +0 -0
  236. toolkit/workspace/precompiled/modules/LM_distance.mpy +0 -0
  237. toolkit/workspace/precompiled/{LM_ds18.mpy → modules/LM_ds18.mpy} +0 -0
  238. toolkit/workspace/precompiled/{LM_esp32.py → modules/LM_esp32.py} +5 -0
  239. toolkit/workspace/precompiled/modules/LM_espnow.py +53 -0
  240. toolkit/workspace/precompiled/modules/LM_fileserver.mpy +0 -0
  241. toolkit/workspace/precompiled/{LM_gameOfLife.mpy → modules/LM_gameOfLife.mpy} +0 -0
  242. toolkit/workspace/precompiled/modules/LM_genIO.mpy +0 -0
  243. toolkit/workspace/precompiled/{LM_haptic.mpy → modules/LM_haptic.mpy} +0 -0
  244. {micrOS/source → toolkit/workspace/precompiled/modules}/LM_i2c.py +5 -4
  245. toolkit/workspace/precompiled/modules/LM_i2s_mic.mpy +0 -0
  246. toolkit/workspace/precompiled/{LM_ld2410.mpy → modules/LM_ld2410.mpy} +0 -0
  247. toolkit/workspace/precompiled/modules/LM_light_sensor.mpy +0 -0
  248. toolkit/workspace/precompiled/modules/LM_mh_z19c.py +198 -0
  249. toolkit/workspace/precompiled/modules/LM_neoeffects.mpy +0 -0
  250. toolkit/workspace/precompiled/modules/LM_neopixel.mpy +0 -0
  251. toolkit/workspace/precompiled/{LM_oled.mpy → modules/LM_oled.mpy} +0 -0
  252. toolkit/workspace/precompiled/{LM_oled_sh1106.mpy → modules/LM_oled_sh1106.mpy} +0 -0
  253. toolkit/workspace/precompiled/modules/LM_oled_ui.mpy +0 -0
  254. toolkit/workspace/precompiled/modules/LM_pacman.mpy +0 -0
  255. toolkit/workspace/precompiled/modules/LM_presence.mpy +0 -0
  256. toolkit/workspace/precompiled/modules/LM_qmi8658.py +204 -0
  257. toolkit/workspace/precompiled/{LM_rencoder.py → modules/LM_rencoder.py} +2 -2
  258. toolkit/workspace/precompiled/modules/LM_rest.mpy +0 -0
  259. toolkit/workspace/precompiled/modules/LM_rgb.mpy +0 -0
  260. toolkit/workspace/precompiled/{LM_rgbcct.mpy → modules/LM_rgbcct.mpy} +0 -0
  261. toolkit/workspace/precompiled/modules/LM_roboarm.mpy +0 -0
  262. toolkit/workspace/precompiled/modules/LM_robustness.py +137 -0
  263. toolkit/workspace/precompiled/{LM_servo.mpy → modules/LM_servo.mpy} +0 -0
  264. toolkit/workspace/precompiled/{LM_sound_event.mpy → modules/LM_sound_event.mpy} +0 -0
  265. toolkit/workspace/precompiled/{LM_stepper.mpy → modules/LM_stepper.mpy} +0 -0
  266. toolkit/workspace/precompiled/modules/LM_switch.mpy +0 -0
  267. toolkit/workspace/precompiled/modules/LM_system.mpy +0 -0
  268. toolkit/workspace/precompiled/modules/LM_tcs3472.py +187 -0
  269. toolkit/workspace/precompiled/modules/LM_telegram.mpy +0 -0
  270. toolkit/workspace/precompiled/{LM_tinyrgb.mpy → modules/LM_tinyrgb.mpy} +0 -0
  271. toolkit/workspace/precompiled/{LM_trackball.mpy → modules/LM_trackball.mpy} +0 -0
  272. toolkit/workspace/precompiled/{LM_veml7700.mpy → modules/LM_veml7700.mpy} +0 -0
  273. toolkit/workspace/precompiled/modules/LM_web.mpy +0 -0
  274. toolkit/workspace/precompiled/urequests.mpy +0 -0
  275. {micrOS/source → toolkit/workspace/precompiled/web}/dashboard.html +4 -0
  276. toolkit/workspace/precompiled/web/editor.js +440 -0
  277. toolkit/workspace/precompiled/web/filesui.html +178 -0
  278. toolkit/workspace/precompiled/web/filesui.js +338 -0
  279. {micrOS/source → toolkit/workspace/precompiled/web}/index.html +44 -2
  280. toolkit/workspace/precompiled/{uapi.js → web/uapi.js} +48 -7
  281. toolkit/workspace/precompiled/{ustyle.css → web/ustyle.css} +6 -3
  282. micrOS/micropython/esp32-20241129-v1.24.1.bin +0 -0
  283. micrOS/micropython/esp32c3-20240222-v1.22.2.bin +0 -0
  284. micrOS/micropython/esp32s2-20240602-v1.23.0.bin +0 -0
  285. micrOS/micropython/esp32s2-LOLIN_MINI-20220618-v1.19.1.bin +0 -0
  286. micrOS/micropython/esp32s2-LOLIN_MINI-20240602-v1.23.0.bin +0 -0
  287. micrOS/micropython/esp32s3-20240105-v1.22.1.bin +0 -0
  288. micrOS/micropython/esp32s3_spiram_oct-20231005-v1.21.0.bin +0 -0
  289. micrOS/micropython/esp32s3_spiram_oct-20241129-v1.24.1.bin +0 -0
  290. micrOS/micropython/rpi-pico-w-20241129-v1.24.1.uf2 +0 -0
  291. micrOS/micropython/tinypico-20241129-v1.24.1.bin +0 -0
  292. micrOS/source/LM_L298N_DCmotor.py +0 -86
  293. micrOS/source/LM_catgame.py +0 -75
  294. micrOS/source/LM_dashboard_be.py +0 -37
  295. micrOS/source/LM_demo.py +0 -97
  296. micrOS/source/LM_espnow.py +0 -23
  297. micrOS/source/LM_intercon.py +0 -57
  298. micrOS/source/LM_keychain.py +0 -322
  299. micrOS/source/LM_lmpacman.py +0 -126
  300. micrOS/source/LM_neoeffects.py +0 -331
  301. micrOS/source/LM_oledui.py +0 -972
  302. micrOS/source/LM_pet_feeder.py +0 -78
  303. micrOS/source/LM_ph_sensor.py +0 -51
  304. micrOS/source/LM_robustness.py +0 -74
  305. micrOS/source/reset.py +0 -11
  306. micrOSDevToolKit-2.9.1.dist-info/RECORD +0 -365
  307. toolkit/dashboard_apps/AirQualityBME280.py +0 -36
  308. toolkit/dashboard_apps/AirQualityDHT22_CO2.py +0 -36
  309. toolkit/lib/file_extensions.py +0 -16
  310. toolkit/simulator_lib/__pycache__/sim_console.cpython-312.pyc +0 -0
  311. toolkit/simulator_lib/__pycache__/sim_console.cpython-38.pyc +0 -0
  312. toolkit/simulator_lib/__pycache__/sim_console.cpython-39.pyc +0 -0
  313. toolkit/workspace/precompiled/IO_esp32s3.mpy +0 -0
  314. toolkit/workspace/precompiled/IO_m5stamp.mpy +0 -0
  315. toolkit/workspace/precompiled/IO_qtpy.mpy +0 -0
  316. toolkit/workspace/precompiled/IO_rp2.mpy +0 -0
  317. toolkit/workspace/precompiled/IO_tinypico.mpy +0 -0
  318. toolkit/workspace/precompiled/LM_L298N_DCmotor.mpy +0 -0
  319. toolkit/workspace/precompiled/LM_OV2640.mpy +0 -0
  320. toolkit/workspace/precompiled/LM_catgame.py +0 -75
  321. toolkit/workspace/precompiled/LM_cct.mpy +0 -0
  322. toolkit/workspace/precompiled/LM_dashboard_be.py +0 -37
  323. toolkit/workspace/precompiled/LM_demo.py +0 -97
  324. toolkit/workspace/precompiled/LM_dimmer.mpy +0 -0
  325. toolkit/workspace/precompiled/LM_distance.mpy +0 -0
  326. toolkit/workspace/precompiled/LM_espnow.py +0 -23
  327. toolkit/workspace/precompiled/LM_genIO.mpy +0 -0
  328. toolkit/workspace/precompiled/LM_i2s_mic.mpy +0 -0
  329. toolkit/workspace/precompiled/LM_intercon.mpy +0 -0
  330. toolkit/workspace/precompiled/LM_keychain.mpy +0 -0
  331. toolkit/workspace/precompiled/LM_light_sensor.mpy +0 -0
  332. toolkit/workspace/precompiled/LM_lmpacman.mpy +0 -0
  333. toolkit/workspace/precompiled/LM_neoeffects.mpy +0 -0
  334. toolkit/workspace/precompiled/LM_neopixel.mpy +0 -0
  335. toolkit/workspace/precompiled/LM_oled_ui.mpy +0 -0
  336. toolkit/workspace/precompiled/LM_oledui.mpy +0 -0
  337. toolkit/workspace/precompiled/LM_pet_feeder.py +0 -78
  338. toolkit/workspace/precompiled/LM_ph_sensor.py +0 -51
  339. toolkit/workspace/precompiled/LM_presence.mpy +0 -0
  340. toolkit/workspace/precompiled/LM_rest.mpy +0 -0
  341. toolkit/workspace/precompiled/LM_rgb.mpy +0 -0
  342. toolkit/workspace/precompiled/LM_roboarm.mpy +0 -0
  343. toolkit/workspace/precompiled/LM_robustness.py +0 -74
  344. toolkit/workspace/precompiled/LM_switch.mpy +0 -0
  345. toolkit/workspace/precompiled/LM_system.mpy +0 -0
  346. toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
  347. toolkit/workspace/precompiled/node_config.json +0 -1
  348. toolkit/workspace/precompiled/reset.mpy +0 -0
  349. /micrOS/source/{IO_esp32.py → modules/IO_esp32.py} +0 -0
  350. /micrOS/source/{IO_esp32c3.py → modules/IO_esp32c3.py} +0 -0
  351. /micrOS/source/{IO_esp32s2.py → modules/IO_esp32s2.py} +0 -0
  352. /micrOS/source/{IO_rp2.py → modules/IO_rp2.py} +0 -0
  353. /micrOS/source/{LM_gameOfLife.py → modules/LM_gameOfLife.py} +0 -0
  354. /micrOS/source/{LM_rgbcct.py → modules/LM_rgbcct.py} +0 -0
  355. /micrOS/source/{LM_rp2w.py → modules/LM_rp2w.py} +0 -0
  356. /micrOS/source/{LM_sdcard.py → modules/LM_sdcard.py} +0 -0
  357. /micrOS/source/{LM_sound_event.py → modules/LM_sound_event.py} +0 -0
  358. /micrOS/source/{LM_tinyrgb.py → modules/LM_tinyrgb.py} +0 -0
  359. /micrOS/source/{udashboard.js → web/udashboard.js} +0 -0
  360. /micrOS/source/{uwidgets.js → web/uwidgets.js} +0 -0
  361. /micrOS/source/{uwidgets_pro.js → web/uwidgets_pro.js} +0 -0
  362. {micrOSDevToolKit-2.9.1.dist-info → microsdevtoolkit-2.26.1.dist-info/licenses}/LICENSE +0 -0
  363. {micrOSDevToolKit-2.9.1.dist-info → microsdevtoolkit-2.26.1.dist-info}/top_level.txt +0 -0
  364. /toolkit/workspace/precompiled/{LM_rp2w.py → modules/LM_rp2w.py} +0 -0
  365. /toolkit/workspace/precompiled/{LM_sdcard.py → modules/LM_sdcard.py} +0 -0
  366. /toolkit/workspace/precompiled/{udashboard.js → web/udashboard.js} +0 -0
  367. /toolkit/workspace/precompiled/{uwidgets.js → web/uwidgets.js} +0 -0
  368. /toolkit/workspace/precompiled/{uwidgets_pro.js → web/uwidgets_pro.js} +0 -0
micrOS/source/Common.py CHANGED
@@ -2,121 +2,62 @@
2
2
  micrOS Load Module programming Official API-s
3
3
  Designed by Marcell Ban aka BxNxM
4
4
  """
5
-
6
5
  from Server import Server, WebCli
7
- from Debug import errlog_add, console_write
6
+ from Debug import syslog as debug_syslog, console_write
8
7
  from Logger import logger, log_get
8
+ from Files import OSPath, path_join
9
9
  from microIO import resolve_pin
10
- from Tasks import TaskBase, Manager, lm_exec
10
+ from Tasks import TaskBase, Manager, lm_exec, lm_is_loaded
11
11
  from machine import Pin, ADC
12
12
  from Notify import Notify
13
13
 
14
- ################## Common LM features ##################
15
14
 
16
- def transition(from_val, to_val, step_ms, interval_sec):
15
+ #####################################################################################
16
+ # SYSTEM #
17
+ #####################################################################################
18
+ def micro_task(tag:str, task=None, _wrap=False):
17
19
  """
18
- [LM] Single Generator for color/value transition:
19
- :param from_val: from value - start from
20
- :param to_val: to value - target value
21
- :param step_ms: step to reach to_val - timirq_seq
22
- :param interval_sec: time of full interval
23
- """
24
- if interval_sec > 0:
25
- step_cnt = round((interval_sec*1000)/step_ms)
26
- delta = abs((from_val-to_val)/step_cnt)
27
- direc = -1 if from_val > to_val else 1
28
- for cnt in range(0, step_cnt+1):
29
- yield round(from_val + (cnt * delta) * direc)
30
- else:
31
- yield round(to_val)
32
-
20
+ [LM] Async task manager.
21
+ Modes:
22
+ A) GET:
23
+ micro_task("tag") -> existing task object or None
24
+ B) CREATE:
25
+ micro_task("tag", task=...) -> True | None | False
26
+ Creates a new async task with the given tag if not already running.
27
+ C) CREATE AS DECORATOR (shortcut):
28
+ @micro_task("main", _wrap=True)
29
+ async def mytask(tag, ...): ...
30
+ # Calling mytask(...) will create/start a new task under "main._mytask"
33
31
 
34
- def transition_gen(*args, interval_sec=1.0):
35
- """
36
- [LM] Multiple Generator for color/value transitions:
37
- - calculate minimum step count -> step_ms
38
- - autofill and use transition(from_val, to_val, step_ms, interval_sec)
39
- :param args: ch1_from, ch1_to, ch2_from, ch2_to, etc...
40
- :param interval_sec: interval in sec to calculate optimal fade/transition effect
41
- return: gen, step_ms OR gen list, step_ms
32
+ :param tag: Task tag string
33
+ :param task: Coroutine (or list of command arguments) to contract a task with
34
+ the given async task callback
35
+ :param _wrap: When True, return a decorator factory (for use as @micro_task(...))
36
+ :return: Task object (GET), bool|None|False (CREATE), or decorator (DECORATOR)
42
37
  """
43
- step_ms_min = 5 # min calculated step is 5 ms - good enough
44
- delta = max((abs(args[ch_from_i] - args[ch_from_i+1]) for ch_from_i in range(0, len(args)-1, 2)))
45
- step_ms = 0 if delta == 0 else int(interval_sec*1000 / delta)
46
- step_ms = step_ms_min if step_ms < step_ms_min else step_ms
47
- transitions = list((transition(args[ch_from_i], args[ch_from_i+1], step_ms, interval_sec) for ch_from_i in range(0, len(args)-1, 2)))
48
- if len(transitions) == 1:
49
- return transitions[0], step_ms
50
- return list(transitions), step_ms
38
+ # --- CREATE (original) ---
39
+ if task is not None:
40
+ return Manager().create_task(callback=task, tag=tag)
51
41
 
42
+ # --- CREATE WITH DECORATOR FACTORY (simplified) ---
43
+ if _wrap:
44
+ def _decorator(async_fn):
45
+ task_tag = f"{tag}._{async_fn.__name__}"
46
+ _launcher = (lambda *args, **kwargs: Manager().create_task(callback=async_fn(task_tag, *args, **kwargs),
47
+ tag=task_tag))
48
+ return _launcher
49
+ return _decorator
52
50
 
53
- class SmartADC:
54
- """
55
- [LM] General ADC implementation for auto scaled output: raw, percent, volt
56
- https://docs.micropython.org/en/latest/esp32/quickref.html#adc-analog-to-digital-conversion
57
- ADC.ATTN_0DB: 0 dB attenuation, resulting in a full-scale voltage range of 0-1.1V
58
- ADC.ATTN_2_5DB: 2.5 dB ... of 0-1.5V
59
- ADC.ATTN_6DB: 6 dB ... of 0-2.2V
60
- ADC.ATTN_11DB: 11 dB ... of 0-2450mV/
61
- Note that the absolute maximum voltage rating for input pins is 3.6V. Going near to this boundary risks damage to the IC!
62
- """
63
- OBJS = {}
51
+ # --- GET (_wrap=False): return task object or None if not existing ---
52
+ return TaskBase.TASKS.get(tag, None)
64
53
 
65
- def __init__(self, pin):
66
- self.adp_prop = (65535, 2450) # raw value, 2450mV (so 2,45V)
67
- self.adc = None
68
- if not isinstance(pin, int):
69
- pin = resolve_pin(pin)
70
- self.adc = ADC(Pin(pin))
71
- self.adc.atten(ADC.ATTN_11DB) # 2450mV measure range
72
-
73
- def get(self):
74
- raw = int((self.adc.read_u16() + self.adc.read_u16())/2) # 16-bit ADC value (0-65535)
75
- percent = raw / self.adp_prop[0]
76
- volt = round(percent * self.adp_prop[1] / 1000, 2) # devide with 1000 to get V from mV
77
- return {'raw': raw, 'percent': round(percent*100, 1), 'volt': volt}
78
54
 
79
- @staticmethod
80
- def get_instance(pin):
81
- if pin in SmartADC.OBJS.keys():
82
- return SmartADC.OBJS[pin]
83
- SmartADC.OBJS[pin] = SmartADC(pin)
84
- return SmartADC.OBJS[pin]
85
-
86
- ################# micrOS feature interfaces #################
87
-
88
- def micro_task(tag, task=None):
89
- """
90
- [LM] Async task creation
91
- :param tag:
92
- [1] tag=None: return task generator object
93
- [2] tag=taskID: return existing task object by tag
94
- :param task: coroutine to execute (with built-in overload protection and lcm)
95
- """
96
- if task is None:
97
- # [1] Task is None -> Get task mode by tag
98
- # RETURN task obj (access obj.out + obj.done (automatic - with keyword arg))
99
- async_task = TaskBase.TASKS.get(tag, None)
100
- return async_task
101
- if TaskBase.is_busy(tag):
102
- # [2] Shortcut: Check task state by tag
103
- # RETURN: None - if task is already running
104
- return None
105
- # [3] Create task (not running) + task coroutine was provided
106
- # RETURN task creation state - success (True) / fail (False)
107
- state = Manager().create_task(callback=task, tag=tag)
108
- return state
109
-
110
-
111
- def manage_task(tag, operation):
55
+ def manage_task(tag:str, operation:str):
112
56
  """
113
57
  [LM] Async task management
114
58
  :param tag: task tag
115
59
  :param operation: kill / show / isbusy
116
60
  """
117
- if Manager is None:
118
- # RETURN: None - cannot utilize async task functionality
119
- return None
120
61
  if operation == "show":
121
62
  return str(Manager().show(tag=tag))
122
63
  if operation == "kill":
@@ -126,41 +67,48 @@ def manage_task(tag, operation):
126
67
  raise Exception(f"Invalid operation: {operation}")
127
68
 
128
69
 
129
- def exec_cmd(cmd, skip_check=False):
70
+ def exec_cmd(cmd:list, jsonify:bool=None, secure=False):
130
71
  """
131
72
  [LM] Single (sync) LM execution
132
- :param cmd: command string list
133
- :param skip_check: skip cmd type check, micropython bug
73
+ :param cmd: command string list, ex.: ['system', 'clock']
74
+ :param jsonify: request json output
75
+ :param secure: check LM is loaded, if NOT skip execution 'NotAllowed'
134
76
  return state, output
135
77
  """
136
- # [BUG] Solution with isinstance/type is not reliable... micropython 1.22
137
- # Invalid type, must be list: <class list>" ...
138
- if skip_check:
139
- return lm_exec(cmd)
140
- return lm_exec(cmd) if isinstance(cmd, list) else False, f"Invalid type, must be list: {type(cmd)}"
78
+ if secure and not lm_is_loaded(cmd[0]):
79
+ return False, f"NotAllowed {cmd[0]}"
80
+ return lm_exec(cmd, jsonify=jsonify)
141
81
 
142
82
 
143
- def notify(text=None) -> bool:
83
+ def notify(*args, **kwargs) -> bool:
144
84
  """
145
- [LM] micrOS common notification handler (Telegram)
146
- :param text: notification text / None (return notification state)
85
+ [LM] micrOS common notification handler (Telegram, etc.)
86
+ text (0): notification text / None (return notification state)
87
+ :param channels (optional): select communication interface(s) by class name or an iterable of these.
88
+ If omitted or empty, sends over all available channels.
89
+ Examples (optional parameters):
90
+ Telegram params:
91
+ reply_to: message id to reply to (optional) - default: None
92
+ chat_id: chat identifier - default: None -> auto resolve in child class
93
+ MQTTClient params:
94
+ topic: mqtt topic to send the message - default: None -> auto resolve in child class
147
95
  return: verdict: True/False
148
96
  """
149
- # (1) Return notification state
150
- if text is None:
97
+ # (1) Return notification state (if no text input given)
98
+ if kwargs.get("text", args[0] if args else None) is None:
151
99
  return Notify.GLOBAL_NOTIFY
152
100
  # (2) Send notification
153
101
  try:
154
- out = Notify.notify(text)
102
+ out = Notify.notify(*args, **kwargs)
155
103
  except Exception as e:
156
- errlog_add(f"[ERR] Notify: {e}")
104
+ debug_syslog(f"[ERR] Notify: {e}")
157
105
  out = str(e)
158
106
  if out is not None and (out.startswith('Sent') or out.endswith('disabled')):
159
107
  return True
160
108
  return False
161
109
 
162
110
 
163
- def web_endpoint(endpoint, function) -> bool:
111
+ def web_endpoint(endpoint, function, method='GET') -> bool:
164
112
  """
165
113
  [LM] Add test endpoint <localhost.local>/endpoint from Load Modules
166
114
  :param endpoint: simple string, name of the endpoint
@@ -171,11 +119,27 @@ def web_endpoint(endpoint, function) -> bool:
171
119
  [2] Stream function return tuple (multipart_type, data):
172
120
  multipart/x-mixed-replace | multipart/form-data, <data>
173
121
  <data>: {'callback':<func>, 'content-type': image/jpeg | audio/l16;*}
122
+ [3] String as file reference from /web
123
+ Example: 'filesui.html'
124
+ :param method: HTTP method name
174
125
  """
175
- WebCli.register(endpoint=endpoint, callback=function)
126
+ WebCli.register(endpoint=endpoint, callback=function, method=method)
176
127
  return True
177
128
 
178
129
 
130
+ def web_mounts(*args, **kwargs):
131
+ """
132
+ Extend web engine shared root path list
133
+ :param modules: add /modules to web shared path
134
+ :param data: add /data to web shared path
135
+ """
136
+ try:
137
+ return WebCli.web_mounts(*args, **kwargs)
138
+ except Exception as e:
139
+ syslog(f"[ERR] web_mounts error: {e}")
140
+ return {}
141
+
142
+
179
143
  def socket_stream(func):
180
144
  """
181
145
  [LM] Decorator for Socket message stream - adds msgobj to the decorated function arg list.
@@ -193,7 +157,7 @@ def data_logger(f_name, data=None, limit=12, msgobj=None):
193
157
  [LM] micrOS Common Data logger solution
194
158
  - if data None => read mode
195
159
  - if data value => write mode
196
- :param f_name: log name (without extension, automatic: .dat)
160
+ :param f_name: log name (without extension, automatic: .dat, default folder: /data)
197
161
  :param data: data to append
198
162
  :param limit: line limit (max.: 12 with short lines: limited disk speed!)
199
163
  :param msgobj: socket stream object (set automatically!)
@@ -209,10 +173,291 @@ def data_logger(f_name, data=None, limit=12, msgobj=None):
209
173
 
210
174
 
211
175
  def syslog(msg):
212
- """ Wrapper of errlog_add """
213
- return errlog_add(f"[U] {msg}")
176
+ """ Wrapper of debug_syslog """
177
+ return debug_syslog(f"{msg}")
214
178
 
215
179
 
216
180
  def console(msg):
217
181
  """ Wrapper of console_write """
218
182
  return console_write(msg)
183
+
184
+ #####################################################################################
185
+ # SYSTEM DIRECTORY ACCESS #
186
+ #####################################################################################
187
+
188
+ def data_dir(f_name=None):
189
+ """
190
+ Access for data dir path
191
+ :param f_name: if given, returns full path, otherwise returns data dir root path
192
+ """
193
+ root_path = OSPath.DATA
194
+ if f_name is None:
195
+ return root_path
196
+ return path_join(root_path, f_name)
197
+
198
+ def web_dir(f_name=None):
199
+ """
200
+ Access for web dir path
201
+ :param f_name: if given, returns full path, otherwise returns web dir root path
202
+ """
203
+ root_path = OSPath.WEB
204
+ if f_name is None:
205
+ return root_path
206
+ return path_join(root_path, f_name)
207
+
208
+ def conf_dir(f_name):
209
+ """
210
+ Create application configs under config directory
211
+ - node_config.json and config dir read restriction/protection (basic)
212
+ :param f_name: creates custom config path with f_name
213
+ """
214
+ root_path = OSPath.CONFIG
215
+ if f_name.strip().lstrip("/") == "node_config.json":
216
+ raise Exception("Protected resource: config!")
217
+ return path_join(root_path, f_name)
218
+
219
+ #####################################################################################
220
+ # CHANNEL: SIGNAL GENERATORS #
221
+ #####################################################################################
222
+
223
+ def transition(from_val, to_val, step_ms, interval_sec):
224
+ """
225
+ [LM] Single Generator for color/value transition:
226
+ :param from_val: from value - start from
227
+ :param to_val: to value - target value
228
+ :param step_ms: step to reach to_val - timirq_seq
229
+ :param interval_sec: time of full interval
230
+ """
231
+ if interval_sec > 0:
232
+ step_cnt = round((interval_sec*1000)/step_ms)
233
+ delta = abs((from_val-to_val)/step_cnt)
234
+ direc = -1 if from_val > to_val else 1
235
+ for cnt in range(0, step_cnt+1):
236
+ yield round(from_val + (cnt * delta) * direc)
237
+ else:
238
+ yield round(to_val)
239
+
240
+
241
+ def transition_gen(*args, interval_sec=1.0):
242
+ """
243
+ [LM] Multiple Generator for color/value transitions:
244
+ - calculate minimum step count -> step_ms
245
+ - autofill and use transition(from_val, to_val, step_ms, interval_sec)
246
+ :param args: ch1_from, ch1_to, ch2_from, ch2_to, etc...
247
+ :param interval_sec: interval in sec to calculate optimal fade/transition effect
248
+ return: gen, step_ms OR gen list, step_ms
249
+ """
250
+ step_ms_min = 5 # min calculated step is 5 ms - good enough
251
+ delta = max((abs(args[ch_from_i] - args[ch_from_i+1]) for ch_from_i in range(0, len(args)-1, 2)))
252
+ step_ms = 0 if delta == 0 else int(interval_sec*1000 / delta)
253
+ step_ms = step_ms_min if step_ms < step_ms_min else step_ms
254
+ transitions = list((transition(args[ch_from_i], args[ch_from_i+1], step_ms, interval_sec) for ch_from_i in range(0, len(args)-1, 2)))
255
+ if len(transitions) == 1:
256
+ return transitions[0], step_ms
257
+ return list(transitions), step_ms
258
+
259
+ #####################################################################################
260
+ # EXTRAS #
261
+ #####################################################################################
262
+
263
+ class SmartADC:
264
+ """
265
+ [LM] General ADC implementation for auto scaled output: raw, percent, volt
266
+ https://docs.micropython.org/en/latest/esp32/quickref.html#adc-analog-to-digital-conversion
267
+ ADC.ATTN_0DB: 0 dB attenuation, resulting in a full-scale voltage range of 0-1.1V
268
+ ADC.ATTN_2_5DB: 2.5 dB ... of 0-1.5V
269
+ ADC.ATTN_6DB: 6 dB ... of 0-2.2V
270
+ ADC.ATTN_11DB: 11 dB ... of 0-2450mV/
271
+ Note that the absolute maximum voltage rating for input pins is 3.6V. Going near to this boundary risks damage to the IC!
272
+ """
273
+ OBJS = {}
274
+
275
+ def __init__(self, pin):
276
+ self.adp_prop = (65535, 2450) # raw value, 2450mV (so 2,45V)
277
+ self.adc = None
278
+ if not isinstance(pin, int):
279
+ pin = resolve_pin(pin)
280
+ self.adc = ADC(Pin(pin))
281
+ self.adc.atten(ADC.ATTN_11DB) # 2450mV measure range
282
+
283
+ def get(self):
284
+ raw = int((self.adc.read_u16() + self.adc.read_u16())/2) # 16-bit ADC value (0-65535)
285
+ percent = raw / self.adp_prop[0]
286
+ volt = round(percent * self.adp_prop[1] / 1000, 2) # devide with 1000 to get V from mV
287
+ return {'raw': raw, 'percent': round(percent*100, 1), 'volt': volt}
288
+
289
+ @staticmethod
290
+ def get_instance(pin):
291
+ if pin in SmartADC.OBJS.keys():
292
+ return SmartADC.OBJS[pin]
293
+ SmartADC.OBJS[pin] = SmartADC(pin)
294
+ return SmartADC.OBJS[pin]
295
+
296
+
297
+ class AnimationPlayer:
298
+ """
299
+ Generic async animation (generator) player.
300
+ """
301
+
302
+ def __init__(self, animation:callable=None, tag:str=None, batch_draw:bool=False, batch_size:int=None, loop:bool=True):
303
+ """
304
+ Initialize the AnimationPlayer with an optional animation.
305
+ :param animation: Function to GENERATE animation data
306
+ :param tag: Optional task tag for micro_task management.
307
+ :param batch_draw: If True - draw in batches
308
+ :param batch_size: Number of pixels per batch when drawing
309
+ :param loop: If True - loop the animation (default)
310
+ """
311
+ self.animation:callable = None
312
+ self.batch_draw:bool = batch_draw
313
+ self.__max_batch_size:int = 256 # MAX BATCH SIZE - ASYNC PROTECTION
314
+ self.__batch_size:int = 8 # Default batch size: 8
315
+ self.__loop:bool = loop # Loop the animation (generator)
316
+ self._set_batch_size(batch_size) # Set batch size from parameter
317
+ self._player_speed_ms:int = 10 # Default speed in ms between frames
318
+ main_tag:str = tag if tag else "animation"
319
+ self._task_tag:str = f"{main_tag}.player"
320
+ if animation is not None and not self._set_animation(animation):
321
+ raise Exception("Invalid animation function provided.")
322
+ self.__running:bool = True
323
+
324
+ def _set_animation(self, animation:callable) -> bool:
325
+ """
326
+ Setter to change/set current animation.
327
+ """
328
+ if callable(animation):
329
+ self.animation = animation
330
+ return True
331
+ return False
332
+
333
+ def _set_batch_size(self, batch_size:int) -> None:
334
+ """
335
+ Setter to change/set batch size.
336
+ - with max batch size check (due to async event loop feeding)
337
+ """
338
+ if batch_size is None:
339
+ return
340
+ self.__batch_size = max(0, min(batch_size, self.__max_batch_size))
341
+
342
+ async def _render(self, my_task):
343
+ # Cache methods for speed
344
+ clear = self.clear
345
+ update = self.update
346
+ draw = self.draw
347
+ # Cache the current animation for comparison
348
+ current_animation = self.animation
349
+ frame_counter = 0
350
+ # Clear the display before each frame
351
+ if not self.batch_draw:
352
+ clear()
353
+ for data in self.animation():
354
+ # Check if animation has changed under the loop
355
+ if not self.__running or self.animation != current_animation:
356
+ # Animation changed — break — clean and restart animation loop.
357
+ clear()
358
+ break
359
+ # Update data cache
360
+ update(*data)
361
+ if self.batch_draw:
362
+ # Batched draw mode
363
+ frame_counter += 1
364
+ if frame_counter >= self.__batch_size:
365
+ draw()
366
+ frame_counter = 0
367
+ await my_task.feed(sleep_ms=self._player_speed_ms)
368
+ else:
369
+ # Real-time draw mode
370
+ draw()
371
+ await my_task.feed(sleep_ms=self._player_speed_ms)
372
+
373
+ async def _player(self):
374
+ """
375
+ Async task to play the current animation.
376
+ """
377
+ with micro_task(tag=self._task_tag) as my_task:
378
+ while self.__running:
379
+ my_task.out = f"Play {self.animation.__name__} ({self._player_speed_ms}ms/frame)"
380
+ try:
381
+ await self._render(my_task)
382
+ except IndexError:
383
+ # Draw after generator exhausted and Restart animation if IndexError occurs
384
+ self.draw()
385
+ if not self.__loop:
386
+ break
387
+ await my_task.feed(sleep_ms=self._player_speed_ms)
388
+ my_task.out = "Restart animation"
389
+ except Exception as e:
390
+ my_task.out = f"Error: {e}"
391
+ break
392
+ my_task.out = f"Animation stopped...{my_task.out}"
393
+
394
+ def control(self, play_speed_ms:int, bt_draw:bool=None, bt_size:int=None, loop:bool=None):
395
+ """
396
+ Set/Get current play speed of the animation.
397
+ :param play_speed_ms: player loop speed in milliseconds.
398
+ :param bt_draw: batch drawing flag.
399
+ :param bt_size: batch drawing size.
400
+ :param loop: loop flag.
401
+ """
402
+ if isinstance(play_speed_ms, int):
403
+ self._player_speed_ms = max(0, min(10000, int(play_speed_ms)))
404
+ if isinstance(bt_draw, bool):
405
+ self.batch_draw = bt_draw
406
+ if isinstance(bt_size, int):
407
+ self._set_batch_size(bt_size)
408
+ if isinstance(loop, bool):
409
+ self.__loop = loop
410
+ return {"realtime": not self.batch_draw, "batched": self.batch_draw,
411
+ "size": self.__batch_size, "speed_ms": self._player_speed_ms,
412
+ "loop": self.__loop}
413
+
414
+
415
+ def play(self, animation=None, speed_ms=None, bt_draw=False, bt_size=None, loop=True):
416
+ """
417
+ Play animation via generator function.
418
+ :param animation: Animation generator function.
419
+ :param speed_ms: Speed of the animation in milliseconds. (min.: 3ms)
420
+ :param bt_draw: batch drawing flag.
421
+ :param bt_size: batch drawing size.
422
+ :param loop: Loop the animation.
423
+ :return: Player settings.
424
+ """
425
+
426
+ if animation is not None:
427
+ if not self._set_animation(animation):
428
+ return "Invalid animation"
429
+ if self.animation is None:
430
+ return "No animation to play"
431
+ # Handle player settings
432
+ settings = self.control(play_speed_ms=speed_ms, bt_draw=bt_draw, bt_size=bt_size, loop=loop)
433
+ # Ensure async loop set up correctly. (After stop operation, it is needed)
434
+ self.__running = True
435
+ # [!] ASYNC TASK CREATION
436
+ state:dict = micro_task(tag=self._task_tag, task=self._player())
437
+ settings["state"] = list(state.values())[0]
438
+ return settings
439
+
440
+ def stop(self):
441
+ """
442
+ Stop the animation.
443
+ """
444
+ self.__running = False
445
+ return "Stop animation player"
446
+
447
+ def update(self, *arg, **kwargs):
448
+ """
449
+ Child class must implement this method to handle drawing logic.
450
+ """
451
+ raise NotImplementedError("Child class must implement update method.")
452
+
453
+ def draw(self):
454
+ """
455
+ Draw the current frame.
456
+ """
457
+ raise NotImplementedError("Child class must implement draw method.")
458
+
459
+ def clear(self):
460
+ """
461
+ Clear the display.
462
+ """
463
+ raise NotImplementedError("Child class must implement clear method.")