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.
- env/driver_cp210x/.DS_Store +0 -0
- env/driver_cp210x/macOS_VCP_Driver/SiLabsUSBDriverDisk.dmg +0 -0
- env/driver_cp210x/macOS_VCP_Driver/macOS_VCP_Driver_Release_Notes.txt +17 -1
- micrOS/micropython/esp32-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32c3-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32c6-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32s2-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32s2-LOLIN_MINI-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32s3-4MBflash-20241129-v1.24.1.bin +0 -0
- micrOS/micropython/esp32s3-8MBflash-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32s3_spiram_oct-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/rpi-pico-w-20251209-v1.27.0.uf2 +0 -0
- micrOS/micropython/tinypico-20251209-v1.27.0.bin +0 -0
- micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +191 -151
- micrOS/source/Auth.py +37 -0
- micrOS/source/Common.py +376 -102
- micrOS/source/Config.py +55 -25
- micrOS/source/Debug.py +54 -193
- micrOS/source/Espnow.py +404 -0
- micrOS/source/Files.py +207 -0
- micrOS/source/Hooks.py +88 -16
- micrOS/source/InterConnect.py +130 -46
- micrOS/source/Interrupts.py +8 -8
- micrOS/source/Logger.py +131 -0
- micrOS/source/Network.py +41 -21
- micrOS/source/Notify.py +74 -198
- micrOS/source/Pacman.py +326 -0
- micrOS/source/Scheduler.py +18 -55
- micrOS/source/Server.py +84 -217
- micrOS/source/Shell.py +103 -93
- micrOS/source/Tasks.py +239 -173
- micrOS/source/Time.py +21 -22
- micrOS/source/Types.py +89 -54
- micrOS/source/Web.py +485 -0
- micrOS/source/__pycache__/Common.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Debug.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Files.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Logger.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Scheduler.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Server.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Shell.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/replhelper.cpython-312.pyc +0 -0
- micrOS/source/helpers.py +132 -0
- micrOS/source/micrOS.py +25 -21
- micrOS/source/micrOSloader.py +14 -23
- micrOS/source/microIO.py +94 -57
- toolkit/simulator_lib/LP_darwin.py → micrOS/source/modules/IO_esp32.py +22 -11
- micrOS/source/{IO_esp32c3.py → modules/IO_esp32c3.py} +6 -1
- micrOS/source/modules/IO_esp32c6.py +38 -0
- micrOS/source/{IO_esp32s2.py → modules/IO_esp32s2.py} +6 -1
- micrOS/source/{IO_esp32s3.py → modules/IO_esp32s3.py} +43 -2
- micrOS/source/modules/IO_m5stamp.py +86 -0
- micrOS/source/{IO_qtpy.py → modules/IO_qtpy.py} +28 -18
- micrOS/source/{IO_tinypico.py → modules/IO_tinypico.py} +48 -3
- micrOS/source/modules/LM_L298N.py +161 -0
- {toolkit/workspace/precompiled → micrOS/source/modules}/LM_L9110_DCmotor.py +4 -4
- micrOS/source/{LM_OV2640.py → modules/LM_OV2640.py} +53 -42
- micrOS/source/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +5 -5
- micrOS/source/{LM_aht10.py → modules/LM_aht10.py} +12 -4
- micrOS/source/{LM_bme280.py → modules/LM_bme280.py} +13 -25
- micrOS/source/{LM_buzzer.py → modules/LM_buzzer.py} +42 -40
- micrOS/source/{LM_cct.py → modules/LM_cct.py} +22 -27
- micrOS/source/modules/LM_cluster.py +255 -0
- micrOS/source/{LM_co2.py → modules/LM_co2.py} +13 -6
- micrOS/source/{LM_dht11.py → modules/LM_dht11.py} +13 -29
- micrOS/source/{LM_dht22.py → modules/LM_dht22.py} +13 -28
- micrOS/source/{LM_dimmer.py → modules/LM_dimmer.py} +19 -16
- micrOS/source/modules/LM_distance.py +135 -0
- micrOS/source/{LM_ds18.py → modules/LM_ds18.py} +12 -4
- micrOS/source/{LM_esp32.py → modules/LM_esp32.py} +16 -4
- micrOS/source/modules/LM_espnow.py +53 -0
- micrOS/source/modules/LM_fileserver.py +265 -0
- micrOS/source/{LM_gameOfLife.py → modules/LM_gameOfLife.py} +5 -5
- micrOS/source/{LM_genIO.py → modules/LM_genIO.py} +49 -35
- micrOS/source/modules/LM_haptic.py +111 -0
- micrOS/source/modules/LM_i2c.py +61 -0
- micrOS/source/{LM_i2s_mic.py → modules/LM_i2s_mic.py} +20 -23
- micrOS/source/{LM_ld2410.py → modules/LM_ld2410.py} +3 -3
- micrOS/source/{LM_light_sensor.py → modules/LM_light_sensor.py} +22 -26
- micrOS/source/modules/LM_mh_z19c.py +198 -0
- micrOS/source/modules/LM_neoeffects.py +284 -0
- micrOS/source/{LM_neopixel.py → modules/LM_neopixel.py} +26 -31
- micrOS/source/{LM_oled.py → modules/LM_oled.py} +28 -20
- micrOS/source/{LM_oled_sh1106.py → modules/LM_oled_sh1106.py} +28 -24
- micrOS/source/{LM_oled_ui.py → modules/LM_oled_ui.py} +132 -174
- micrOS/source/modules/LM_pacman.py +320 -0
- micrOS/source/{LM_presence.py → modules/LM_presence.py} +24 -36
- micrOS/source/modules/LM_qmi8658.py +204 -0
- micrOS/source/{LM_rencoder.py → modules/LM_rencoder.py} +40 -11
- micrOS/source/modules/LM_rest.py +81 -0
- micrOS/source/{LM_rgb.py → modules/LM_rgb.py} +25 -34
- micrOS/source/{LM_rgbcct.py → modules/LM_rgbcct.py} +5 -5
- micrOS/source/{LM_roboarm.py → modules/LM_roboarm.py} +37 -45
- micrOS/source/modules/LM_robustness.py +137 -0
- micrOS/source/{LM_rp2w.py → modules/LM_rp2w.py} +3 -0
- micrOS/source/{LM_sdcard.py → modules/LM_sdcard.py} +3 -0
- micrOS/source/{LM_servo.py → modules/LM_servo.py} +4 -4
- micrOS/source/modules/LM_sound_event.py +751 -0
- micrOS/source/{LM_stepper.py → modules/LM_stepper.py} +8 -8
- micrOS/source/{LM_switch.py → modules/LM_switch.py} +21 -18
- micrOS/source/{LM_system.py → modules/LM_system.py} +96 -59
- micrOS/source/modules/LM_tcs3472.py +187 -0
- micrOS/source/modules/LM_telegram.py +388 -0
- micrOS/source/modules/LM_trackball.py +287 -0
- micrOS/source/modules/LM_veml7700.py +159 -0
- micrOS/source/modules/LM_web.py +38 -0
- micrOS/source/urequests.py +204 -91
- {toolkit/workspace/precompiled → micrOS/source/web}/dashboard.html +9 -4
- micrOS/source/web/editor.js +440 -0
- micrOS/source/web/filesui.html +178 -0
- micrOS/source/web/filesui.js +338 -0
- micrOS/source/{index.html → web/index.html} +44 -2
- micrOS/source/web/uapi.js +103 -0
- micrOS/source/web/udashboard.js +129 -0
- micrOS/source/web/ustyle.css +55 -0
- micrOS/source/web/uwidgets.js +172 -0
- micrOS/source/web/uwidgets_pro.js +99 -0
- micrOS/utests/__init__.py +0 -0
- micrOS/utests/test_scheduler.py +435 -0
- {micrOSDevToolKit-2.1.5.data → microsdevtoolkit-2.26.1.data}/scripts/devToolKit.py +47 -4
- {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info}/METADATA +392 -279
- microsdevtoolkit-2.26.1.dist-info/RECORD +396 -0
- {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info}/WHEEL +1 -1
- toolkit/DevEnvCompile.py +63 -33
- toolkit/DevEnvOTA.py +72 -22
- toolkit/DevEnvUSB.py +147 -77
- toolkit/Gateway.py +9 -9
- toolkit/LM_to_compile.dat +12 -4
- toolkit/MicrOSDevEnv.py +129 -51
- toolkit/WebRepl.py +73 -0
- toolkit/dashboard_apps/BackupRestore.py +171 -0
- toolkit/dashboard_apps/CCTDemo.py +12 -17
- toolkit/dashboard_apps/CCTTest.py +20 -24
- toolkit/dashboard_apps/CamStream.py +2 -6
- toolkit/dashboard_apps/CatGame.py +14 -16
- toolkit/dashboard_apps/Dimmer.py +11 -21
- toolkit/dashboard_apps/GetVersion.py +11 -19
- toolkit/dashboard_apps/MicrophoneTest.py +2 -7
- toolkit/dashboard_apps/NeoEffectsDemo.py +22 -35
- toolkit/dashboard_apps/NeopixelTest.py +20 -25
- toolkit/dashboard_apps/PresenceTest.py +2 -8
- toolkit/dashboard_apps/QMI8685_GYRO.py +68 -0
- toolkit/dashboard_apps/RGBTest.py +20 -24
- toolkit/dashboard_apps/RoboArm.py +24 -32
- toolkit/dashboard_apps/SED_test.py +10 -14
- toolkit/dashboard_apps/SensorsTest.py +61 -0
- toolkit/dashboard_apps/SystemTest.py +219 -117
- toolkit/dashboard_apps/Template_app.py +12 -19
- toolkit/dashboard_apps/_app_base.py +34 -0
- toolkit/dashboard_apps/_gyro_visualizer.py +78 -0
- toolkit/dashboard_apps/uLightDemo.py +15 -24
- toolkit/index.html +6 -5
- toolkit/lib/LocalMachine.py +6 -1
- toolkit/lib/MicrosFiles.py +46 -0
- toolkit/lib/Repository.py +64 -0
- toolkit/lib/TerminalColors.py +4 -0
- toolkit/lib/macroScript.py +371 -0
- toolkit/lib/micrOSClient.py +124 -51
- toolkit/lib/micrOSClientHistory.py +156 -0
- toolkit/lib/pip_package_installer.py +31 -4
- toolkit/micrOSdashboard.py +16 -21
- toolkit/micrOSlint.py +28 -10
- toolkit/simulator_lib/.DS_Store +0 -0
- micrOS/source/IO_esp32.py → toolkit/simulator_lib/IO_darwin.py +3 -0
- toolkit/simulator_lib/__pycache__/IO_darwin.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/aioespnow.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/camera.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/framebuf.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/machine.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/micropython.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/mip.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/neopixel.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/network.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/sim_common.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/simgc.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/simulator.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/uasyncio.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/uos.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/urandom.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/usocket.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/ussl.cpython-312.pyc +0 -0
- toolkit/simulator_lib/aioespnow.py +28 -0
- toolkit/simulator_lib/camera.py +84 -0
- toolkit/simulator_lib/dht.py +1 -1
- toolkit/simulator_lib/framebuf.py +49 -1
- toolkit/simulator_lib/machine.py +32 -2
- toolkit/simulator_lib/micropython.py +3 -3
- toolkit/simulator_lib/mip.py +165 -0
- toolkit/simulator_lib/neopixel.py +3 -2
- toolkit/simulator_lib/network.py +2 -1
- toolkit/simulator_lib/node_config.json +2 -3
- toolkit/simulator_lib/ntptime.py +1 -1
- toolkit/simulator_lib/{sim_console.py → sim_common.py} +2 -3
- toolkit/simulator_lib/simgc.py +6 -2
- toolkit/simulator_lib/simulator.py +138 -46
- toolkit/simulator_lib/uasyncio.py +34 -3
- toolkit/simulator_lib/uos.py +147 -0
- toolkit/simulator_lib/urandom.py +4 -0
- toolkit/simulator_lib/usocket.py +5 -1
- toolkit/simulator_lib/view01.jpg +0 -0
- toolkit/simulator_lib/view02.jpg +0 -0
- toolkit/socketClient.py +43 -23
- toolkit/user_data/webhooks/generic.py +1 -1
- toolkit/user_data/webhooks/macro.py +44 -0
- toolkit/user_data/webhooks/template.macro +20 -0
- toolkit/user_data/webhooks/template.py +1 -1
- toolkit/workspace/precompiled/Auth.mpy +0 -0
- toolkit/workspace/precompiled/Common.mpy +0 -0
- toolkit/workspace/precompiled/Config.mpy +0 -0
- toolkit/workspace/precompiled/Debug.mpy +0 -0
- toolkit/workspace/precompiled/Espnow.mpy +0 -0
- toolkit/workspace/precompiled/Files.mpy +0 -0
- toolkit/workspace/precompiled/Hooks.mpy +0 -0
- toolkit/workspace/precompiled/InterConnect.mpy +0 -0
- toolkit/workspace/precompiled/Interrupts.mpy +0 -0
- toolkit/workspace/precompiled/Logger.mpy +0 -0
- toolkit/workspace/precompiled/Network.mpy +0 -0
- toolkit/workspace/precompiled/Notify.mpy +0 -0
- toolkit/workspace/precompiled/Pacman.mpy +0 -0
- toolkit/workspace/precompiled/Scheduler.mpy +0 -0
- toolkit/workspace/precompiled/Server.mpy +0 -0
- toolkit/workspace/precompiled/Shell.mpy +0 -0
- toolkit/workspace/precompiled/Tasks.mpy +0 -0
- toolkit/workspace/precompiled/Time.mpy +0 -0
- toolkit/workspace/precompiled/Types.mpy +0 -0
- toolkit/workspace/precompiled/Web.mpy +0 -0
- toolkit/workspace/precompiled/_mpy.version +1 -1
- toolkit/workspace/precompiled/config/_git.keep +0 -0
- toolkit/workspace/precompiled/helpers.mpy +0 -0
- toolkit/workspace/precompiled/micrOS.mpy +0 -0
- toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
- toolkit/workspace/precompiled/microIO.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_esp32.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_esp32c3.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_esp32c6.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_esp32s2.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_esp32s3.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_m5stamp.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_qtpy.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_rp2.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_tinypico.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_L298N.mpy +0 -0
- {micrOS/source → toolkit/workspace/precompiled/modules}/LM_L9110_DCmotor.py +4 -4
- toolkit/workspace/precompiled/modules/LM_OV2640.mpy +0 -0
- toolkit/workspace/precompiled/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +5 -5
- toolkit/workspace/precompiled/modules/LM_aht10.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_bme280.mpy +0 -0
- toolkit/workspace/precompiled/{LM_buzzer.mpy → modules/LM_buzzer.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_cct.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_cluster.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_co2.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_dht11.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_dht22.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_dimmer.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_distance.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_ds18.mpy +0 -0
- toolkit/workspace/precompiled/{LM_esp32.py → modules/LM_esp32.py} +16 -4
- toolkit/workspace/precompiled/modules/LM_espnow.py +53 -0
- toolkit/workspace/precompiled/modules/LM_fileserver.mpy +0 -0
- toolkit/workspace/precompiled/{LM_gameOfLife.mpy → modules/LM_gameOfLife.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_genIO.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_haptic.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_i2c.py +61 -0
- toolkit/workspace/precompiled/modules/LM_i2s_mic.mpy +0 -0
- toolkit/workspace/precompiled/{LM_ld2410.mpy → modules/LM_ld2410.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_light_sensor.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_mh_z19c.py +198 -0
- toolkit/workspace/precompiled/modules/LM_neoeffects.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_neopixel.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_oled.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_oled_sh1106.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_oled_ui.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_pacman.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_presence.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_qmi8658.py +204 -0
- toolkit/workspace/precompiled/{LM_rencoder.py → modules/LM_rencoder.py} +40 -11
- toolkit/workspace/precompiled/modules/LM_rest.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_rgb.mpy +0 -0
- toolkit/workspace/precompiled/{LM_rgbcct.mpy → modules/LM_rgbcct.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_roboarm.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_robustness.py +137 -0
- toolkit/workspace/precompiled/{LM_rp2w.py → modules/LM_rp2w.py} +3 -0
- toolkit/workspace/precompiled/{LM_sdcard.py → modules/LM_sdcard.py} +3 -0
- toolkit/workspace/precompiled/{LM_servo.mpy → modules/LM_servo.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_sound_event.mpy +0 -0
- toolkit/workspace/precompiled/{LM_stepper.mpy → modules/LM_stepper.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_switch.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_system.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_tcs3472.py +187 -0
- toolkit/workspace/precompiled/modules/LM_telegram.mpy +0 -0
- toolkit/workspace/precompiled/{LM_tinyrgb.mpy → modules/LM_tinyrgb.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_trackball.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_veml7700.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_web.mpy +0 -0
- toolkit/workspace/precompiled/urequests.mpy +0 -0
- {micrOS/source → toolkit/workspace/precompiled/web}/dashboard.html +9 -4
- toolkit/workspace/precompiled/web/editor.js +440 -0
- toolkit/workspace/precompiled/web/filesui.html +178 -0
- toolkit/workspace/precompiled/web/filesui.js +338 -0
- toolkit/workspace/precompiled/{index.html → web/index.html} +44 -2
- toolkit/workspace/precompiled/web/uapi.js +103 -0
- toolkit/workspace/precompiled/web/udashboard.js +129 -0
- toolkit/workspace/precompiled/web/ustyle.css +55 -0
- toolkit/workspace/precompiled/web/uwidgets.js +172 -0
- toolkit/workspace/precompiled/web/uwidgets_pro.js +99 -0
- env/driver_cp210x/CH34XSER_MAC/CH34X_DRV_INSTALL_INSTRUCTIONS.pdf +0 -0
- env/driver_cp210x/CH34XSER_MAC/CH34xVCPDriver.pkg +0 -0
- micrOS/micropython/esp32-20231005-v1.21.0.bin +0 -0
- micrOS/micropython/esp32c3-GENERIC-20240105-v1.22.1.bin +0 -0
- micrOS/micropython/esp32c3-GENERIC-20240222-v1.22.2.bin +0 -0
- micrOS/micropython/esp32s2-GENERIC-20240105-v1.22.1.bin +0 -0
- micrOS/micropython/esp32s2-LOLIN_MINI-20220618-v1.19.1.bin +0 -0
- micrOS/micropython/esp32s3-GENERIC-20240105-v1.22.1.bin +0 -0
- micrOS/micropython/esp32s3_spiram_oct-20231005-v1.21.0.bin +0 -0
- micrOS/micropython/rpi-pico-w-20231005-v1.21.0.uf2 +0 -0
- micrOS/micropython/tinypico-20231005-v1.21.0.bin +0 -0
- micrOS/micropython/tinypico-usbc-UM-20240105-v1.22.1.bin +0 -0
- micrOS/source/LM_L298N_DCmotor.py +0 -86
- micrOS/source/LM_catgame.py +0 -74
- micrOS/source/LM_dashboard_be.py +0 -37
- micrOS/source/LM_demo.py +0 -85
- micrOS/source/LM_distance.py +0 -88
- micrOS/source/LM_i2c.py +0 -44
- micrOS/source/LM_intercon.py +0 -57
- micrOS/source/LM_keychain.py +0 -318
- micrOS/source/LM_lmpacman.py +0 -126
- micrOS/source/LM_neoeffects.py +0 -327
- micrOS/source/LM_pet_feeder.py +0 -76
- micrOS/source/LM_ph_sensor.py +0 -51
- micrOS/source/LM_rest.py +0 -40
- micrOS/source/LM_robustness.py +0 -73
- micrOS/source/LM_telegram.py +0 -96
- micrOS/source/reset.py +0 -11
- micrOS/source/uapi.js +0 -76
- micrOS/source/udashboard.js +0 -137
- micrOS/source/ustyle.css +0 -28
- micrOS/source/uwidgets.js +0 -179
- micrOSDevToolKit-2.1.5.dist-info/RECORD +0 -337
- toolkit/dashboard_apps/AirQualityBME280.py +0 -36
- toolkit/dashboard_apps/AirQualityDHT22_CO2.py +0 -36
- toolkit/lib/file_extensions.py +0 -16
- toolkit/simulator_lib/__pycache__/LP_darwin.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/LP_darwin.cpython-38.pyc +0 -0
- toolkit/simulator_lib/__pycache__/LP_darwin.cpython-39.pyc +0 -0
- toolkit/simulator_lib/__pycache__/sim_console.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/sim_console.cpython-38.pyc +0 -0
- toolkit/simulator_lib/__pycache__/sim_console.cpython-39.pyc +0 -0
- toolkit/workspace/precompiled/IO_esp32.mpy +0 -0
- toolkit/workspace/precompiled/IO_esp32c3.mpy +0 -0
- toolkit/workspace/precompiled/IO_esp32s2.mpy +0 -0
- toolkit/workspace/precompiled/IO_esp32s3.mpy +0 -0
- toolkit/workspace/precompiled/IO_qtpy.mpy +0 -0
- toolkit/workspace/precompiled/IO_rp2.mpy +0 -0
- toolkit/workspace/precompiled/IO_tinypico.mpy +0 -0
- toolkit/workspace/precompiled/LM_L298N_DCmotor.mpy +0 -0
- toolkit/workspace/precompiled/LM_OV2640.mpy +0 -0
- toolkit/workspace/precompiled/LM_aht10.mpy +0 -0
- toolkit/workspace/precompiled/LM_bme280.mpy +0 -0
- toolkit/workspace/precompiled/LM_catgame.py +0 -74
- toolkit/workspace/precompiled/LM_cct.mpy +0 -0
- toolkit/workspace/precompiled/LM_co2.mpy +0 -0
- toolkit/workspace/precompiled/LM_dashboard_be.py +0 -37
- toolkit/workspace/precompiled/LM_demo.py +0 -85
- toolkit/workspace/precompiled/LM_dht11.mpy +0 -0
- toolkit/workspace/precompiled/LM_dht22.mpy +0 -0
- toolkit/workspace/precompiled/LM_dimmer.mpy +0 -0
- toolkit/workspace/precompiled/LM_distance.py +0 -88
- toolkit/workspace/precompiled/LM_ds18.mpy +0 -0
- toolkit/workspace/precompiled/LM_genIO.mpy +0 -0
- toolkit/workspace/precompiled/LM_i2c.py +0 -44
- toolkit/workspace/precompiled/LM_i2s_mic.mpy +0 -0
- toolkit/workspace/precompiled/LM_intercon.mpy +0 -0
- toolkit/workspace/precompiled/LM_keychain.mpy +0 -0
- toolkit/workspace/precompiled/LM_light_sensor.mpy +0 -0
- toolkit/workspace/precompiled/LM_lmpacman.mpy +0 -0
- toolkit/workspace/precompiled/LM_neoeffects.mpy +0 -0
- toolkit/workspace/precompiled/LM_neopixel.mpy +0 -0
- toolkit/workspace/precompiled/LM_oled.mpy +0 -0
- toolkit/workspace/precompiled/LM_oled_sh1106.mpy +0 -0
- toolkit/workspace/precompiled/LM_oled_ui.mpy +0 -0
- toolkit/workspace/precompiled/LM_pet_feeder.py +0 -76
- toolkit/workspace/precompiled/LM_ph_sensor.py +0 -51
- toolkit/workspace/precompiled/LM_presence.mpy +0 -0
- toolkit/workspace/precompiled/LM_rest.mpy +0 -0
- toolkit/workspace/precompiled/LM_rgb.mpy +0 -0
- toolkit/workspace/precompiled/LM_roboarm.mpy +0 -0
- toolkit/workspace/precompiled/LM_robustness.py +0 -73
- toolkit/workspace/precompiled/LM_switch.mpy +0 -0
- toolkit/workspace/precompiled/LM_system.mpy +0 -0
- toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
- toolkit/workspace/precompiled/reset.mpy +0 -0
- toolkit/workspace/precompiled/uapi.js +0 -76
- toolkit/workspace/precompiled/udashboard.js +0 -137
- toolkit/workspace/precompiled/ustyle.css +0 -28
- toolkit/workspace/precompiled/uwidgets.js +0 -179
- /toolkit/user_data/node_config_archive/.include → /micrOS/source/config/_git.keep +0 -0
- /micrOS/source/{IO_rp2.py → modules/IO_rp2.py} +0 -0
- /micrOS/source/{LM_tinyrgb.py → modules/LM_tinyrgb.py} +0 -0
- {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info/licenses}/LICENSE +0 -0
- {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")
|