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