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,388 @@
1
+ from sys import modules
2
+ import urequests
3
+ from Notify import Notify
4
+ from Common import micro_task, syslog, console_write, data_dir, conf_dir
5
+ from LM_system import ifconfig
6
+ from utime import localtime
7
+
8
+
9
+ def _timestamp():
10
+ _time = [str(k) for k in localtime()[3:6]]
11
+ return ':'.join(_time)
12
+
13
+
14
+ class Telegram(Notify):
15
+ INSTANCE = None
16
+ # Telegram bot token and chat ID
17
+ # https://core.telegram.org/bots/api
18
+ _TOKEN = None # Telegram token
19
+ _CHAT_IDS = set() # Telegram bot chat IDs - multi group support - persistent caching
20
+ _API_PARAMS = "?offset=-1&limit=1&timeout=2" # Generic API params - optimization
21
+ _IN_MSG_ID = None
22
+ _FILE_CACHE = data_dir('telegram.cache')
23
+
24
+ def __init__(self, token:str):
25
+ """
26
+ :param token: bot token
27
+ """
28
+ # Subscribe to the notification system - provide send_msg method (over self)
29
+ super().add_subscriber(self)
30
+ Telegram._TOKEN = token
31
+ Telegram.INSTANCE = self
32
+
33
+ @staticmethod
34
+ def __id_cache(mode:str):
35
+ """
36
+ File cache
37
+ modes:
38
+ r - recover, s - save
39
+ """
40
+ if mode == 's':
41
+ # SAVE CACHE
42
+ console_write("[NTFY] Save chatIDs cache...")
43
+ with open(Telegram._FILE_CACHE, 'w') as f:
44
+ f.write(','.join([str(k) for k in Telegram._CHAT_IDS]))
45
+ return
46
+ try:
47
+ # RESTORE CACHE
48
+ console_write("[NTFY] Restore chatIDs cache...")
49
+ with open(Telegram._FILE_CACHE, 'r') as f:
50
+ # set() comprehension
51
+ Telegram._CHAT_IDS = {int(k) for k in f.read().strip().split(',')}
52
+ except:
53
+ pass
54
+
55
+ @staticmethod
56
+ def __bot_token():
57
+ """Get bot token"""
58
+ if Telegram._TOKEN is None:
59
+ return None
60
+ return Telegram._TOKEN
61
+
62
+ @staticmethod
63
+ def send_msg(text:str, *args, **kwargs):
64
+ """
65
+ Send a message to the Telegram chat by chat_id
66
+ :param text: text to send
67
+ :param reply_to: reply to specific message, if None, simple reply
68
+ :param chat_id: chat_id to reply on, if None, reply to all known
69
+ RETURN None when telegram bot token is missing
70
+ """
71
+ reply_to = kwargs.get("reply_to", args[0] if len(args) > 0 else None)
72
+ chat_id = kwargs.get("chat_id", args[1] if len(args) > 1 else None)
73
+
74
+ def _send(chid):
75
+ """Send message to chat_id (chid)"""
76
+ data = {"chat_id": chid, "text": f"{Telegram._DEVFID}⚙️\n{text}"}
77
+ if isinstance(reply_to, int):
78
+ data['reply_to_message_id'] = reply_to
79
+ Telegram._IN_MSG_ID = reply_to
80
+ _, _resp = urequests.post(url, headers={"Content-Type": "application/json"}, json=data, jsonify=True,
81
+ sock_size=128)
82
+ console_write(f"\tSend message:\n{data}\nresponse:\n{_resp}")
83
+ return _resp
84
+
85
+ def _get_chat_ids():
86
+ """Return chat ID or None (in case of no token or cannot get ID)"""
87
+ if len(Telegram._CHAT_IDS) == 0:
88
+ Telegram.get_msg() # It will update the Telegram.CHAT_IDS
89
+ console_write(f"\tGet chatIDs: {Telegram._CHAT_IDS}")
90
+ return Telegram._CHAT_IDS
91
+
92
+ # --------------------- FUNCTION MAIN ------------------------ #
93
+ console_write("[NTFY] SEND MESSAGE")
94
+ # Check bot token
95
+ bot_token = Telegram.__bot_token()
96
+ if bot_token is None:
97
+ return None
98
+ url = f"https://api.telegram.org/bot{bot_token}/sendMessage{Telegram._API_PARAMS}"
99
+
100
+ verdict = ""
101
+ # Reply to ALL (notification) - chat_id was not provided
102
+ if chat_id is None:
103
+ console_write("\tREPLY ALL")
104
+ for _chat_id in _get_chat_ids():
105
+ resp_json = _send(chid=_chat_id)
106
+ verdict += f'Sent{_chat_id};' if resp_json['ok'] else str(resp_json)
107
+ else:
108
+ console_write(f"\tREPLY TO {chat_id}")
109
+ # Direct reply to chat_id
110
+ resp_json = _send(chid=chat_id)
111
+ verdict = f'Sent{chat_id}' if resp_json['ok'] else str(resp_json)
112
+ return verdict
113
+
114
+ @staticmethod
115
+ def __update_chat_ids(resp_json:dict):
116
+ """
117
+ Update known chat_id-s and cache them
118
+ - return active chat_id frm resp_json
119
+ """
120
+ _cid = None
121
+ if resp_json.get("ok", None) and len(resp_json["result"]) > 0:
122
+ _cid = resp_json["result"][-1]["message"]["chat"]["id"]
123
+ # LIMIT Telegram._CHAT_IDS NOTIFICATION CACHE TO 3 IDs
124
+ if len(Telegram._CHAT_IDS) < 4 and _cid not in Telegram._CHAT_IDS:
125
+ console_write("[NTFY GET] update chatIDs")
126
+ _ids = len(Telegram._CHAT_IDS)
127
+ Telegram._CHAT_IDS.add(_cid)
128
+ if len(Telegram._CHAT_IDS) - _ids > 0: # optimized save (slow storage access)
129
+ Telegram.__id_cache('s')
130
+ else:
131
+ Telegram.__id_cache('r')
132
+ if len(Telegram._CHAT_IDS) == 0:
133
+ error_message = resp_json.get("description", "Unknown error")
134
+ raise Exception(f"Error retrieving chat ID: {error_message}")
135
+ return _cid
136
+
137
+ @staticmethod
138
+ def get_msg():
139
+ """
140
+ Get the last message from the Telegram chat.
141
+ RETURN None when telegram bot token is missing
142
+ """
143
+ console_write("[NTFY] GET MESSAGE")
144
+ bot_token = Telegram.__bot_token()
145
+ if bot_token is None:
146
+ return None
147
+ response = {'sender': None, 'text': None, 'm_id': -1, 'c_id': None}
148
+ url = f"https://api.telegram.org/bot{bot_token}/getUpdates{Telegram._API_PARAMS}"
149
+ console_write(f"\t[GET] request: {url}")
150
+
151
+ _, resp_json = urequests.get(url, jsonify=True, sock_size=128)
152
+
153
+ if len(resp_json["result"]) > 0:
154
+ response['c_id'] = Telegram.__update_chat_ids(resp_json)
155
+ resp = resp_json["result"][-1]["message"]
156
+ response['sender'] = f"{resp['chat']['first_name']}{resp['chat']['last_name']}" if resp['chat'].get(
157
+ 'username', None) is None else resp['chat']['username']
158
+ response['text'], response['m_id'] = resp['text'], resp['message_id']
159
+ console_write(f"\t\t[GET] response: {response}")
160
+ return response
161
+
162
+ @staticmethod
163
+ async def aget_msg():
164
+ """
165
+ Async: Get the last message from the Telegram chat.
166
+ RETURN None when telegram bot token is missing
167
+ """
168
+ console_write("[NTFY] GET MESSAGE")
169
+ bot_token = Telegram.__bot_token()
170
+ if bot_token is None:
171
+ return None
172
+ response = {'sender': None, 'text': None, 'm_id': -1, 'c_id': None}
173
+ url = f"https://api.telegram.org/bot{bot_token}/getUpdates{Telegram._API_PARAMS}"
174
+ console_write(f"\t[aGET] request: {url}")
175
+
176
+ _, resp_json = await urequests.aget(url, jsonify=True, sock_size=128)
177
+
178
+ if len(resp_json["result"]) > 0:
179
+ response['c_id'] = Telegram.__update_chat_ids(resp_json)
180
+ resp = resp_json["result"][-1]["message"]
181
+ response['sender'] = f"{resp['chat']['first_name']}{resp['chat']['last_name']}" if resp['chat'].get(
182
+ 'username', None) is None else resp['chat']['username']
183
+ response['text'], response['m_id'] = resp['text'], resp['message_id']
184
+ console_write(f"\t\t[aGET] response: {response}")
185
+ return response
186
+
187
+ @staticmethod
188
+ async def receive_eval():
189
+ """
190
+ READ - VALIDATE - EXECUTE - REPLY LOOP
191
+ - can be used in async loop
192
+ RETURN None when telegram bot token is missing
193
+ """
194
+ console_write("[NTFY] EVAL sequence")
195
+ verdict = None
196
+
197
+ def _lm_execute(cmd_args):
198
+ nonlocal verdict, m_id
199
+ status, output = Telegram.lm_execute(cmd_args)
200
+ access = "NotAllowed" not in str(output)
201
+ status = "OK" if status else "NOK"
202
+ if access:
203
+ verdict = f'{_timestamp()} [UP][{status}] Exec: {" ".join(cmd_args[0])}'
204
+ Telegram.send_msg(output, reply_to=m_id)
205
+ else:
206
+ verdict = f'{_timestamp()} [UP][{status}] NoAccess: {cmd_args[0]}'
207
+ Telegram._IN_MSG_ID = m_id
208
+
209
+ # -------------------------- FUNCTION MAIN -------------------------- #
210
+ # Async Poll telegram chat
211
+ data = await Telegram.aget_msg()
212
+ if data is None:
213
+ return data
214
+ # Get msg, msg_id, chat_id as main input data source
215
+ msg_in, m_id, c_id = data['text'], data['m_id'], data['c_id']
216
+ if msg_in is not None and m_id != Telegram._IN_MSG_ID:
217
+ # replace single/double quotation to apostrophe (str syntax for repl interpretation)
218
+ msg_in = msg_in.replace('‘', "'").replace('’', "'").replace('“', '"').replace('”', '"')
219
+ if msg_in.startswith('/ping'):
220
+ # Parse loaded modules
221
+ _loaded_mods = [lm.replace('LM_', '') for lm in modules if lm.startswith('LM_')] + ['task']
222
+ Telegram.send_msg(', '.join(_loaded_mods), reply_to=m_id, chat_id=c_id)
223
+ elif msg_in.startswith('/cmd_select'):
224
+ cmd_lm = msg_in.strip().split()[1:]
225
+ # [Compare] cmd selected device param with DEVFID (device/prompt name)
226
+ if cmd_lm[0] in Telegram._DEVFID:
227
+ _lm_execute(cmd_lm[1:])
228
+ else:
229
+ verdict = f'{_timestamp()} [UP] NoSelected: {cmd_lm[0]}'
230
+ elif msg_in.startswith('/cmd'):
231
+ cmd_lm = msg_in.strip().split()[1:]
232
+ _lm_execute(cmd_lm)
233
+ elif msg_in.startswith('/notify'):
234
+ param = msg_in.strip().split()[1:]
235
+ if len(param) > 0:
236
+ verdict = Telegram.notifications(not param[0].strip().lower() in ("disable", "off", 'false'))
237
+ else:
238
+ verdict = Telegram.notifications()
239
+ # Send is still synchronous (OK)
240
+ Telegram.send_msg(verdict, reply_to=m_id)
241
+ else:
242
+ verdict = f"{_timestamp()} [UP] NoExec"
243
+ return verdict
244
+
245
+ @staticmethod
246
+ async def server_bot(tag:str, period:int=3):
247
+ """
248
+ BOT - ReceiveEvalPrintLoop
249
+ :param tag: task tag (access)
250
+ :param period: polling period in sec, default: 3
251
+ """
252
+ cancel_cnt = 0
253
+ period = period if period > 0 else 1
254
+ period_ms = period * 1000
255
+ with micro_task(tag=tag) as my_task:
256
+ my_task.out = f"{_timestamp()} [UP] Running"
257
+ while True:
258
+ # Normal task period
259
+ await my_task.feed(sleep_ms=period_ms)
260
+ try:
261
+ # await asyncio.wait_for(Telegram.receive_eval(), 5) # 5 sec timeout???
262
+ v = await Telegram.receive_eval()
263
+ my_task.out = "Missing bot token" if v is None else f"{v} ({period}s)"
264
+ cancel_cnt = 0
265
+ except Exception as e:
266
+ my_task.out = str(e)
267
+ # Auto scale - blocking nature - in case of serial failures (5) - hibernate task (increase async sleep)
268
+ cancel_cnt += 1
269
+ if cancel_cnt > 5:
270
+ my_task.out = f"{_timestamp()} [DOWN] {e} (wait 1min)"
271
+ cancel_cnt = 5
272
+ # SLOW DOWN - hibernate task
273
+ await my_task.feed(sleep_ms=60_000)
274
+
275
+ @staticmethod
276
+ def set_commands():
277
+ """
278
+ Set Custom Commands to the Telegram chat.
279
+ RETURN None when telegram bot token is missing
280
+ """
281
+
282
+ console_write("[NTFY] SET DEFAULT COMMANDS")
283
+ bot_token = Telegram.__bot_token()
284
+ if bot_token is None:
285
+ return None
286
+ url = f"https://api.telegram.org/bot{bot_token}/setMyCommands{Telegram._API_PARAMS}"
287
+ data = {"commands": [{"command": "ping", "description": "Ping All endpoints: list of active modules."},
288
+ {"command": "notify", "description": "Enable/Disable notifications: on or off"},
289
+ {"command": "cmd", "description": "Run active module function on all devices."},
290
+ {"command": "cmd_select",
291
+ "description": "Same as cmd, only first param must be device name."},
292
+ ]}
293
+ _, resp_json = urequests.post(url, headers={"Content-Type": "application/json"}, json=data, jsonify=True,
294
+ sock_size=128)
295
+ return 'Custom commands was set' if resp_json['ok'] else str(resp_json)
296
+
297
+ #########################################
298
+ # micrOS Notifications #
299
+ #########################################
300
+
301
+ def __init(token:str=None):
302
+ token_cache = conf_dir("telegram.token")
303
+ token_refresh = True
304
+ if Telegram.INSTANCE is None:
305
+ # ENABLE TELEGRAM IF NW IS STA - CONNECTED TO THE WEB
306
+ if ifconfig()[0] == "STA":
307
+ if token is None:
308
+ token_refresh = False
309
+ # Attempt to load token from config folder
310
+ try:
311
+ with open(token_cache, "r") as f:
312
+ token = f.read()
313
+ except Exception as e:
314
+ err = f"Telegram: cannot load {token_cache}: {e}"
315
+ syslog(err)
316
+ raise Exception(err)
317
+ # Initialize telegram with token
318
+ Telegram(token)
319
+ if token_refresh:
320
+ # Save token
321
+ with open(token_cache, "w") as f:
322
+ f.write(token)
323
+ else:
324
+ syslog("No STA: cannot init telegram")
325
+ return Telegram.INSTANCE
326
+
327
+
328
+ def load(token:str=None):
329
+ """
330
+ Set custom chat commands for Telegram
331
+ - /ping
332
+ - /cmd module function (params)
333
+ :param token: telegram bot token
334
+ """
335
+ if __init(token) is None:
336
+ return "Network unavailable."
337
+ verdict = Telegram.set_commands()
338
+ return "Missing telegram bot token" if verdict is None else verdict
339
+
340
+
341
+ def send(text:str):
342
+ """
343
+ Send Telegram message - micrOS notification
344
+ :param text: text to send
345
+ return verdict
346
+ """
347
+ if Telegram.INSTANCE is None:
348
+ return "Network unavailable."
349
+ verdict = Telegram.send_msg(text)
350
+ return "Missing telegram bot token" if verdict is None else verdict
351
+
352
+
353
+ def receive():
354
+ """
355
+ Receive Telegram message
356
+ - if all value None, then no incoming messages
357
+ One successful msg receive is necessary to get chat_id for msg send as well!
358
+ """
359
+ if Telegram.INSTANCE is None:
360
+ return "Network unavailable."
361
+ verdict = Telegram.get_msg()
362
+ return "Missing telegram bot token" if verdict is None else verdict
363
+
364
+
365
+ def receiver_loop(period=3):
366
+ """
367
+ Telegram BOT (repl) - ReadEvalPrintLoop for Load Modules
368
+ - Only executes module (function) if the module is already loaded
369
+ :param period: polling period in sec, default: 3
370
+ """
371
+ if Telegram.INSTANCE is None:
372
+ return "Network unavailable or Telegram uninitialized"
373
+ tag = 'telegram.server_bot'
374
+ return micro_task(tag=tag, task=Telegram.server_bot(tag=tag, period=period))
375
+
376
+
377
+ def help(widgets=False):
378
+ """
379
+ [i] micrOS LM naming convention - built-in help message
380
+ :return tuple:
381
+ (widgets=False) list of functions implemented by this application
382
+ (widgets=True) list of widget json for UI generation
383
+ """
384
+ return ('send "text"',
385
+ 'receive',
386
+ 'receiver_loop period=3',
387
+ 'load token=<your-bot-token>',
388
+ 'INFO: Send & Receive messages with Telegram bot')
@@ -0,0 +1,287 @@
1
+ """
2
+ trackball.py - MicroPython module Pimoroni trackball breadkout.
3
+ Based on https://github.com/mchobby/esp8266-upy/blob/master/trackball/lib/trackball.py
4
+ * MicroPython standard API
5
+ https://github.com/mchobby/esp8266-upy/blob/master/trackball/examples/test_colorcontrol.py
6
+ """
7
+
8
+ from utime import sleep, ticks_ms, ticks_diff
9
+ from struct import unpack
10
+ from machine import SoftI2C, Pin
11
+ from microIO import bind_pin, pinmap_search
12
+ from Common import syslog
13
+ from micropython import schedule
14
+
15
+ TRACKBALL = None
16
+
17
+ #############################
18
+ # Trackball core #
19
+ #############################
20
+
21
+ CHIP_ID = 0xBA11
22
+
23
+ REG_LED_RED = 0x00
24
+ REG_LED_GRN = 0x01
25
+ REG_LED_BLU = 0x02
26
+ REG_LED_WHT = 0x03
27
+
28
+ REG_LEFT = 0x04
29
+ REG_RIGHT = 0x05
30
+ REG_UP = 0x06
31
+ REG_DOWN = 0x07
32
+ REG_SWITCH = 0x08
33
+ MSK_SWITCH_STATE = 0b10000000
34
+
35
+ REG_USER_FLASH = 0xD0
36
+ REG_FLASH_PAGE = 0xF0
37
+ REG_INT = 0xF9
38
+ MSK_INT_TRIGGERED = 0b00000001
39
+ MSK_INT_OUT_EN = 0b00000010
40
+ REG_CHIP_ID_L = 0xFA
41
+ RED_CHIP_ID_H = 0xFB
42
+ REG_VERSION = 0xFC
43
+ REG_I2C_ADDR = 0xFD
44
+ REG_CTRL = 0xFE
45
+ MSK_CTRL_SLEEP = 0b00000001
46
+ MSK_CTRL_RESET = 0b00000010
47
+ MSK_CTRL_FREAD = 0b00000100
48
+ MSK_CTRL_FWRITE = 0b00001000
49
+
50
+
51
+ class Trackball:
52
+ EVENT_LISTENERS = []
53
+
54
+ def __init__(self, i2c, address=0x0A, max_x=100, max_y=100, irq_sampling=1, sensitivity=2):
55
+ """
56
+ :param address: i2c device address
57
+ :param max_x: maximum value of x
58
+ :param max_y: maximum value of y
59
+ :param irq_sampling: max irq callback trigger sampling in time [ms]
60
+ :param sensitivity: xy window for irq trigger sampling in pixel, default: 2
61
+ """
62
+ self.address = address
63
+ self.i2c = i2c
64
+ self._max_x = max_x
65
+ self._max_y = max_y
66
+ self.posx = 0
67
+ self.posy = 0
68
+ self.toggle = False
69
+ self.action = None
70
+ self.irq_sampling = irq_sampling
71
+ self.sensitivity = sensitivity
72
+ self._last_event = ticks_ms()
73
+ self._last_event_coords = (self.posx, self.posy)
74
+
75
+ data = self.i2c.readfrom_mem( self.address, REG_CHIP_ID_L, 2 )
76
+ chip_id = unpack("<H", data)[0]
77
+ if chip_id != CHIP_ID:
78
+ raise Exception("Invalid chip ID: 0x{:04X}, expected 0x{:04X}".format(chip_id, CHIP_ID))
79
+ self.enable_interrupt()
80
+
81
+ def enable_interrupt(self, interrupt=True):
82
+ """ Enable/disable GPIO interrupt pin on the breakout."""
83
+ value = self.i2c.readfrom_mem( self.address, REG_INT, 1)[0]
84
+ value = value & (0xFF ^ MSK_INT_OUT_EN)
85
+ if interrupt:
86
+ value = value | MSK_INT_OUT_EN
87
+ self.i2c.writeto_mem( self.address, REG_INT, bytes([value]) )
88
+
89
+ def get_interrupt(self):
90
+ """Get the trackball interrupt status."""
91
+ # Only support the software version
92
+ data = self.i2c.readfrom_mem( self.address, REG_INT, 1)
93
+ return (data[0] & MSK_INT_TRIGGERED)==MSK_INT_TRIGGERED
94
+
95
+ def set_rgbw(self, r, g, b, w):
96
+ """Set all LED brightness as RGBW."""
97
+ self.i2c.writeto_mem( self.address, REG_LED_RED, bytes([r, g, b, w]) )
98
+
99
+ def set_red(self, value):
100
+ """Set brightness of trackball red LED."""
101
+ self.i2c.writeto_mem( self.address, REG_LED_RED, bytes([value & 0xff]) )
102
+
103
+ def set_green(self, value):
104
+ """Set brightness of trackball green LED."""
105
+ self.i2c.writeto_mem( self.address, REG_LED_GRN, bytes([value & 0xff]) )
106
+
107
+ def set_blue(self, value):
108
+ """Set brightness of trackball blue LED."""
109
+ self.i2c.writeto_mem( self.address, REG_LED_BLU, bytes([value & 0xff]) )
110
+
111
+ def set_white(self, value):
112
+ """Set brightness of trackball white LED."""
113
+ self.i2c.writeto_mem( self.address, REG_LED_WHT, bytes([value & 0xff]) )
114
+
115
+ def auto_color(self):
116
+ if self.action == "press":
117
+ w_br, x_br, y_br = 50, 0, 0
118
+ else:
119
+ w_br = 0
120
+ x_br = int(100 * (self.posx / self._max_x))
121
+ y_br = int(100 * (self.posy / self._max_y))
122
+ self.set_rgbw(r=y_br, g=3, b=x_br, w=w_br)
123
+
124
+ def _detection_block(self):
125
+ window = self.sensitivity
126
+ # Experimental - dymaic sampling time sensitivity...
127
+ last_x, last_y = self._last_event_coords
128
+ if abs(self.posx - last_x) > window or abs(self.posy - last_y) > window:
129
+ # Out of window - detect
130
+ return True
131
+ # Inside window - ignore
132
+ return False
133
+
134
+ def _update_states(self, up, down, left, right, state):
135
+ # Update cursor positions
136
+ change_x = (right - left)
137
+ change_y = (up - down)
138
+ posx = self.posx + change_x
139
+ posy = self.posy + change_y
140
+ self.posx = max(0, min(posx, self._max_x))
141
+ self.posy = max(0, min(posy, self._max_y))
142
+ # [1] Compose action verdict - press button
143
+ if state != self.toggle:
144
+ self.toggle = not self.toggle
145
+ if state:
146
+ # Detect only Raising edge
147
+ self.action = "press" if state else None
148
+ return True # [trigger]
149
+ # [2] Compose action verdict - directions
150
+ if abs(change_x) + abs(change_y) > 0:
151
+ directions = {'up': up, 'down': down, 'left': left, 'right': right}
152
+ self.action = max(directions, key=directions.get)
153
+ # [!!!] timebox and sensitivity block check
154
+ delta_t = ticks_diff(ticks_ms(), self._last_event)
155
+ if delta_t > self.irq_sampling or self._detection_block():
156
+ self._last_event = ticks_ms()
157
+ self._last_event_coords = self.posx, self.posy
158
+ return True # [trigger]
159
+ return False # [NO trigger]
160
+
161
+ def read(self):
162
+ """Read up, down, left, right and switch data from trackball."""
163
+ data = self.i2c.readfrom_mem( self.address, REG_LEFT, 5 )
164
+ left, right, up, down, switch = data[0], data[1], data[2], data[3], data[4]
165
+ switch, switch_state = switch & (0xFF ^ MSK_SWITCH_STATE), (switch & MSK_SWITCH_STATE)==MSK_SWITCH_STATE
166
+ trigger = self._update_states(up, down, left, right, switch_state)
167
+ return up, down, left, right, switch, switch_state, trigger
168
+
169
+
170
+
171
+ def load(width=100, height=100, irq_sampling=50, sensitivity=5, reload=False):
172
+ """
173
+ Load Pimoroni trackball
174
+ :param width: canvas pixel width
175
+ :param height: canvas pixel height
176
+ :param irq_sampling: trackball irq_sampling in ms
177
+ :param sensitivity: xy window for irq trigger sampling in pixel
178
+ :param reload: recreate trackball instance
179
+ """
180
+ global TRACKBALL
181
+ if TRACKBALL is None or reload:
182
+ i2c = SoftI2C(scl=Pin(bind_pin('i2c_scl')), sda=Pin(bind_pin('i2c_sda')))
183
+ TRACKBALL = Trackball(i2c, max_x=width, max_y=height, irq_sampling=irq_sampling, sensitivity=sensitivity)
184
+ _craft_event_interrupt()
185
+ ready_color()
186
+ return TRACKBALL
187
+
188
+ def ready_color():
189
+ if TRACKBALL is not None:
190
+ TRACKBALL.set_green(20)
191
+ return True
192
+ return False
193
+
194
+ def settings(irq_sampling=None, sensitivity=None):
195
+ """
196
+ Get simple trackball settings
197
+ :param irq_sampling: optional param, change irq_sampling ms
198
+ """
199
+ tb = load()
200
+ if isinstance(irq_sampling, int):
201
+ tb.irq_sampling = irq_sampling
202
+ if isinstance(sensitivity, int):
203
+ tb.sensitivity = sensitivity
204
+ return {"width": tb._max_x, "height": tb._max_y, "irq": tb.get_interrupt(),
205
+ "addr": hex(tb.address), "irq_sampling": tb.irq_sampling, "sensitivity": tb.sensitivity}
206
+
207
+
208
+ def read():
209
+ """
210
+ Read trackball data over i2c
211
+ """
212
+ trackball = load()
213
+ up, down, left, right, switch, switch_state, trigger = trackball.read()
214
+ if trigger:
215
+ trackball.auto_color()
216
+ return {"X": trackball.posx, "Y": trackball.posy,
217
+ "S": trackball.toggle, "action": trackball.action}
218
+
219
+
220
+ def get():
221
+ """
222
+ Get up-to-date (by irq) data
223
+ """
224
+ trackball = load()
225
+ return {"X": trackball.posx, "Y": trackball.posy,
226
+ "S": trackball.toggle, "action": trackball.action}
227
+
228
+
229
+ def _craft_event_interrupt():
230
+ """
231
+ Handle hange events from trackball
232
+ """
233
+
234
+ def _inner_callback(trackball):
235
+ trackball.auto_color()
236
+ # Execute callbacks
237
+ for clbk in Trackball.EVENT_LISTENERS:
238
+ if callable(clbk):
239
+ try:
240
+ clbk(get())
241
+ except Exception as e:
242
+ syslog(f"[ERR] Trackball clbk irq:{pin}:{e}")
243
+
244
+ def _callback(pin):
245
+ # Update trackball data - handle event!
246
+ trackball = load()
247
+ up, down, left, right, switch, switch_state, trigger = trackball.read()
248
+ # Schedule user callbacks
249
+ try:
250
+ if trigger:
251
+ schedule(_inner_callback, trackball)
252
+ except Exception as e:
253
+ syslog(f"[ERR] Trackball user callback: {e}")
254
+
255
+ try:
256
+ pin = bind_pin("trackball_int")
257
+ except Exception as e:
258
+ pin = None
259
+ syslog(f'[ERR] trackball_int IRQ: {e}')
260
+ if pin:
261
+ pin_obj = Pin(pin, Pin.IN, Pin.PULL_UP)
262
+ pin_obj.irq(trigger=Pin.IRQ_FALLING, handler=_callback)
263
+
264
+
265
+ def subscribe_event(func):
266
+ """
267
+ [LM] Add callback function to trackball events
268
+ """
269
+ if func not in Trackball.EVENT_LISTENERS:
270
+ Trackball.EVENT_LISTENERS.append(func)
271
+
272
+
273
+ def pinmap():
274
+ """
275
+ [i] micrOS LM naming convention
276
+ Shows logical pins - pin number(s) used by this Load module
277
+ - info which pins to use for this application
278
+ :return dict: pin name (str) - pin value (int) pairs
279
+ """
280
+ return pinmap_search(['i2c_scl', 'i2c_sda', 'trackball_int'])
281
+
282
+
283
+ def help():
284
+ return ("load width=100 height=100 irq_sampling=50 sensitivity=5",
285
+ "read", "get",
286
+ "settings irq_sampling=None sensitivity=None",
287
+ "pinmap")