reachy-mini 1.2.5rc1__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.
- reachy_mini/__init__.py +4 -0
- reachy_mini/apps/__init__.py +25 -0
- reachy_mini/apps/app.py +258 -0
- reachy_mini/apps/assistant.py +787 -0
- reachy_mini/apps/manager.py +312 -0
- reachy_mini/apps/sources/__init__.py +4 -0
- reachy_mini/apps/sources/hf_space.py +106 -0
- reachy_mini/apps/sources/local_common_venv.py +567 -0
- reachy_mini/apps/templates/README.md.j2 +12 -0
- reachy_mini/apps/templates/gitignore.j2 +3 -0
- reachy_mini/apps/templates/index.html.j2 +40 -0
- reachy_mini/apps/templates/main.py.j2 +74 -0
- reachy_mini/apps/templates/pyproject.toml.j2 +28 -0
- reachy_mini/apps/templates/static/index.html.j2 +27 -0
- reachy_mini/apps/templates/static/main.js.j2 +47 -0
- reachy_mini/apps/templates/static/style.css.j2 +25 -0
- reachy_mini/apps/templates/style.css.j2 +411 -0
- reachy_mini/apps/utils.py +30 -0
- reachy_mini/assets/config/hardware_config.yaml +164 -0
- reachy_mini/assets/confused1.wav +0 -0
- reachy_mini/assets/count.wav +0 -0
- reachy_mini/assets/dance1.wav +0 -0
- reachy_mini/assets/firmware/CHANGELOG.md +15 -0
- reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.2.bin +0 -0
- reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.3.bin +0 -0
- reachy_mini/assets/firmware/update.sh +9 -0
- reachy_mini/assets/go_sleep.wav +0 -0
- reachy_mini/assets/impatient1.wav +0 -0
- reachy_mini/assets/kinematics_data.json +253 -0
- reachy_mini/assets/models/fknetwork.dynamic.onnx +0 -0
- reachy_mini/assets/models/fknetwork.onnx +0 -0
- reachy_mini/assets/models/fknetwork_int8.onnx +0 -0
- reachy_mini/assets/models/iknetwork.onnx +0 -0
- reachy_mini/assets/wake_up.wav +0 -0
- reachy_mini/daemon/__init__.py +1 -0
- reachy_mini/daemon/app/__init__.py +1 -0
- reachy_mini/daemon/app/bg_job_register.py +142 -0
- reachy_mini/daemon/app/dashboard/static/assets/KO-cartoon-static.svg +40 -0
- reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon-static.svg +42 -0
- reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/go-to-sleep-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon-static.svg +50 -0
- reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-awake.svg +18 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-connection-lost-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-go-to-sleep-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko.svg +22 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping-static.svg +41 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping.svg +18 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-wake-up-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/css/app.css +1433 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Archivo-Italic-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Archivo-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Asap-Italic-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Asap-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/js/3rdparty/gstwebrtc-api-2.0.0.min.js +5 -0
- reachy_mini/daemon/app/dashboard/static/js/apps.js +323 -0
- reachy_mini/daemon/app/dashboard/static/js/appstore.js +167 -0
- reachy_mini/daemon/app/dashboard/static/js/daemon.js +169 -0
- reachy_mini/daemon/app/dashboard/static/js/health_check.js +9 -0
- reachy_mini/daemon/app/dashboard/static/js/move_player.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/notification.js +41 -0
- reachy_mini/daemon/app/dashboard/static/js/update.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/volume_control.js +252 -0
- reachy_mini/daemon/app/dashboard/static/js/wifi.js +176 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/README.md +15 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/package-lock.json +1127 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/package.json +6 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/pnpm-lock.yaml +608 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/postcss.config.js +6 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/styles/app.css +185 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/tailwind.config.js +11 -0
- reachy_mini/daemon/app/dashboard/templates/base.html +21 -0
- reachy_mini/daemon/app/dashboard/templates/index.html +20 -0
- reachy_mini/daemon/app/dashboard/templates/sections/apps.html +8 -0
- reachy_mini/daemon/app/dashboard/templates/sections/appstore.html +35 -0
- reachy_mini/daemon/app/dashboard/templates/sections/daemon.html +45 -0
- reachy_mini/daemon/app/dashboard/templates/sections/move_player.html +121 -0
- reachy_mini/daemon/app/dashboard/templates/sections/notification.html +19 -0
- reachy_mini/daemon/app/dashboard/templates/sections/update.html +54 -0
- reachy_mini/daemon/app/dashboard/templates/sections/wifi.html +46 -0
- reachy_mini/daemon/app/dashboard/templates/settings.html +14 -0
- reachy_mini/daemon/app/dependencies.py +41 -0
- reachy_mini/daemon/app/main.py +469 -0
- reachy_mini/daemon/app/models.py +149 -0
- reachy_mini/daemon/app/routers/apps.py +114 -0
- reachy_mini/daemon/app/routers/daemon.py +85 -0
- reachy_mini/daemon/app/routers/kinematics.py +57 -0
- reachy_mini/daemon/app/routers/motors.py +41 -0
- reachy_mini/daemon/app/routers/move.py +265 -0
- reachy_mini/daemon/app/routers/state.py +146 -0
- reachy_mini/daemon/app/routers/update.py +86 -0
- reachy_mini/daemon/app/routers/volume.py +423 -0
- reachy_mini/daemon/app/routers/wifi_config.py +219 -0
- reachy_mini/daemon/app/services/__init__.py +1 -0
- reachy_mini/daemon/app/services/bluetooth/bluetooth_service.py +796 -0
- reachy_mini/daemon/app/services/bluetooth/commands/HOTSPOT.sh +7 -0
- reachy_mini/daemon/app/services/bluetooth/commands/RESTART_DAEMON.sh +4 -0
- reachy_mini/daemon/app/services/bluetooth/commands/SOFTWARE_RESET.sh +7 -0
- reachy_mini/daemon/app/services/bluetooth/install_service_bluetooth.sh +39 -0
- reachy_mini/daemon/app/services/gpio_shutdown/__init__.py +1 -0
- reachy_mini/daemon/app/services/gpio_shutdown/install_service.sh +33 -0
- reachy_mini/daemon/app/services/gpio_shutdown/launcher.sh +3 -0
- reachy_mini/daemon/app/services/gpio_shutdown/shutdown_monitor.py +28 -0
- reachy_mini/daemon/app/services/wireless/generate_asoundrc.sh +70 -0
- reachy_mini/daemon/app/services/wireless/install_service.sh +33 -0
- reachy_mini/daemon/app/services/wireless/launcher.sh +5 -0
- reachy_mini/daemon/backend/__init__.py +1 -0
- reachy_mini/daemon/backend/abstract.py +815 -0
- reachy_mini/daemon/backend/mujoco/__init__.py +36 -0
- reachy_mini/daemon/backend/mujoco/backend.py +395 -0
- reachy_mini/daemon/backend/mujoco/utils.py +59 -0
- reachy_mini/daemon/backend/mujoco/video_udp.py +57 -0
- reachy_mini/daemon/backend/robot/__init__.py +8 -0
- reachy_mini/daemon/backend/robot/backend.py +597 -0
- reachy_mini/daemon/daemon.py +651 -0
- reachy_mini/daemon/utils.py +116 -0
- reachy_mini/descriptions/reachy_mini/mjcf/additional.xml +6 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/README.m +1 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/croissant_1k.obj +5021 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/textures/croissant_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/food_apple_01_1k.obj +18045 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/textures/food_apple_01_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/rubber_duck_toy_1k.obj +8940 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/textures/rubber_duck_toy_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/textures/wooden_table_02_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/wooden_table_02_1k.obj +485 -0
- reachy_mini/descriptions/reachy_mini/mjcf/config.json +21 -0
- reachy_mini/descriptions/reachy_mini/mjcf/joints_properties.xml +29 -0
- reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml +642 -0
- reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml.bak +442 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scene.xml +24 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scenes/empty.xml +27 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scenes/minimal.xml +132 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/README.m +1 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/config.json +18 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf +3281 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf.bak +3282 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot_no_collision.urdf +2316 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot_simple_collision.urdf +2399 -0
- reachy_mini/io/__init__.py +15 -0
- reachy_mini/io/abstract.py +70 -0
- reachy_mini/io/audio_ws.py +242 -0
- reachy_mini/io/protocol.py +44 -0
- reachy_mini/io/video_ws.py +135 -0
- reachy_mini/io/ws_controller.py +120 -0
- reachy_mini/io/zenoh_client.py +273 -0
- reachy_mini/io/zenoh_server.py +230 -0
- reachy_mini/kinematics/__init__.py +68 -0
- reachy_mini/kinematics/analytical_kinematics.py +151 -0
- reachy_mini/kinematics/nn_kinematics.py +109 -0
- reachy_mini/kinematics/placo_kinematics.py +656 -0
- reachy_mini/media/__init__.py +1 -0
- reachy_mini/media/audio_base.py +111 -0
- reachy_mini/media/audio_control_utils.py +455 -0
- reachy_mini/media/audio_gstreamer.py +272 -0
- reachy_mini/media/audio_sounddevice.py +321 -0
- reachy_mini/media/audio_utils.py +114 -0
- reachy_mini/media/camera_base.py +100 -0
- reachy_mini/media/camera_constants.py +152 -0
- reachy_mini/media/camera_gstreamer.py +231 -0
- reachy_mini/media/camera_opencv.py +89 -0
- reachy_mini/media/camera_utils.py +111 -0
- reachy_mini/media/media_manager.py +281 -0
- reachy_mini/media/webrtc_client_gstreamer.py +288 -0
- reachy_mini/media/webrtc_daemon.py +334 -0
- reachy_mini/motion/__init__.py +4 -0
- reachy_mini/motion/goto.py +71 -0
- reachy_mini/motion/move.py +43 -0
- reachy_mini/motion/recorded_move.py +146 -0
- reachy_mini/reachy_mini.py +753 -0
- reachy_mini/tools/reflash_motors.py +116 -0
- reachy_mini/tools/setup_motor.py +422 -0
- reachy_mini/tools/setup_motor_rpi.py +108 -0
- reachy_mini/utils/__init__.py +46 -0
- reachy_mini/utils/constants.py +9 -0
- reachy_mini/utils/hardware_config/__init__.py +1 -0
- reachy_mini/utils/hardware_config/parser.py +65 -0
- reachy_mini/utils/interpolation.py +227 -0
- reachy_mini/utils/parse_urdf_for_kinematics.py +110 -0
- reachy_mini/utils/rerun.py +320 -0
- reachy_mini/utils/wireless_version/__init__.py +1 -0
- reachy_mini/utils/wireless_version/startup_check.py +144 -0
- reachy_mini/utils/wireless_version/update.py +20 -0
- reachy_mini/utils/wireless_version/update_available.py +61 -0
- reachy_mini/utils/wireless_version/utils.py +39 -0
- reachy_mini-1.2.5rc1.dist-info/METADATA +161 -0
- reachy_mini-1.2.5rc1.dist-info/RECORD +479 -0
- reachy_mini-1.2.5rc1.dist-info/WHEEL +5 -0
- reachy_mini-1.2.5rc1.dist-info/entry_points.txt +4 -0
- reachy_mini-1.2.5rc1.dist-info/licenses/LICENSE +201 -0
- reachy_mini-1.2.5rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Bluetooth service for Reachy Mini using direct DBus API.
|
|
3
|
+
|
|
4
|
+
Includes a fixed NoInputNoOutput agent for automatic Just Works pairing.
|
|
5
|
+
"""
|
|
6
|
+
# mypy: ignore-errors
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
from typing import Callable
|
|
12
|
+
|
|
13
|
+
import dbus
|
|
14
|
+
import dbus.mainloop.glib
|
|
15
|
+
import dbus.service
|
|
16
|
+
from gi.repository import GLib
|
|
17
|
+
|
|
18
|
+
logging.basicConfig(level=logging.INFO)
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
# Service and Characteristic UUIDs
|
|
22
|
+
SERVICE_UUID = "12345678-1234-5678-1234-56789abcdef0"
|
|
23
|
+
COMMAND_CHAR_UUID = "12345678-1234-5678-1234-56789abcdef1"
|
|
24
|
+
RESPONSE_CHAR_UUID = "12345678-1234-5678-1234-56789abcdef2"
|
|
25
|
+
|
|
26
|
+
# Device Information Service UUIDs (standard BLE service)
|
|
27
|
+
DEVICE_INFO_SERVICE_UUID = "0000180a-0000-1000-8000-00805f9b34fb"
|
|
28
|
+
MANUFACTURER_NAME_UUID = "00002a29-0000-1000-8000-00805f9b34fb"
|
|
29
|
+
MODEL_NUMBER_UUID = "00002a24-0000-1000-8000-00805f9b34fb"
|
|
30
|
+
FIRMWARE_REVISION_UUID = "00002a26-0000-1000-8000-00805f9b34fb"
|
|
31
|
+
|
|
32
|
+
# Custom Reachy Status Service UUIDs
|
|
33
|
+
REACHY_STATUS_SERVICE_UUID = "12345678-1234-5678-1234-56789abcdef3"
|
|
34
|
+
NETWORK_STATUS_UUID = "12345678-1234-5678-1234-56789abcdef4"
|
|
35
|
+
SYSTEM_STATUS_UUID = "12345678-1234-5678-1234-56789abcdef5"
|
|
36
|
+
AVAILABLE_COMMANDS_UUID = "12345678-1234-5678-1234-56789abcdef6"
|
|
37
|
+
|
|
38
|
+
BLUEZ_SERVICE_NAME = "org.bluez"
|
|
39
|
+
GATT_MANAGER_IFACE = "org.bluez.GattManager1"
|
|
40
|
+
DBUS_OM_IFACE = "org.freedesktop.DBus.ObjectManager"
|
|
41
|
+
DBUS_PROP_IFACE = "org.freedesktop.DBus.Properties"
|
|
42
|
+
GATT_SERVICE_IFACE = "org.bluez.GattService1"
|
|
43
|
+
GATT_CHRC_IFACE = "org.bluez.GattCharacteristic1"
|
|
44
|
+
GATT_DESC_IFACE = "org.bluez.GattDescriptor1"
|
|
45
|
+
LE_ADVERTISING_MANAGER_IFACE = "org.bluez.LEAdvertisingManager1"
|
|
46
|
+
LE_ADVERTISEMENT_IFACE = "org.bluez.LEAdvertisement1"
|
|
47
|
+
AGENT_PATH = "/org/bluez/agent"
|
|
48
|
+
|
|
49
|
+
# Descriptor UUIDs
|
|
50
|
+
USER_DESCRIPTION_UUID = "00002901-0000-1000-8000-00805f9b34fb"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# =======================
|
|
54
|
+
# BLE Agent for Just Works
|
|
55
|
+
# =======================
|
|
56
|
+
class NoInputAgent(dbus.service.Object):
|
|
57
|
+
"""BLE Agent for Just Works pairing."""
|
|
58
|
+
|
|
59
|
+
@dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
|
|
60
|
+
def Release(self, *args):
|
|
61
|
+
"""Handle release of the agent."""
|
|
62
|
+
logger.info("Agent released")
|
|
63
|
+
|
|
64
|
+
@dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="s")
|
|
65
|
+
def RequestPinCode(self, *args):
|
|
66
|
+
"""Automatically provide an empty pin code for Just Works pairing."""
|
|
67
|
+
logger.info(f"RequestPinCode called with args: {args}, returning empty")
|
|
68
|
+
return ""
|
|
69
|
+
|
|
70
|
+
@dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="u")
|
|
71
|
+
def RequestPasskey(self, *args):
|
|
72
|
+
"""Automatically provide a passkey of 0 for Just Works pairing."""
|
|
73
|
+
logger.info(f"RequestPasskey called with args: {args}, returning 0")
|
|
74
|
+
return dbus.UInt32(0)
|
|
75
|
+
|
|
76
|
+
@dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
|
|
77
|
+
def RequestConfirmation(self, *args):
|
|
78
|
+
"""Automatically confirm the pairing request."""
|
|
79
|
+
logger.info(
|
|
80
|
+
f"RequestConfirmation called with args: {args}, accepting automatically"
|
|
81
|
+
)
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
@dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
|
|
85
|
+
def DisplayPinCode(self, *args):
|
|
86
|
+
"""Handle displaying the pin code (not used in Just Works)."""
|
|
87
|
+
logger.info(f"DisplayPinCode called with args: {args}")
|
|
88
|
+
|
|
89
|
+
@dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
|
|
90
|
+
def DisplayPasskey(self, *args):
|
|
91
|
+
"""Handle displaying the passkey (not used in Just Works)."""
|
|
92
|
+
logger.info(f"DisplayPasskey called with args: {args}")
|
|
93
|
+
|
|
94
|
+
@dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
|
|
95
|
+
def AuthorizeService(self, *args):
|
|
96
|
+
"""Handle service authorization requests."""
|
|
97
|
+
logger.info(f"AuthorizeService called with args: {args}")
|
|
98
|
+
|
|
99
|
+
@dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="")
|
|
100
|
+
def Cancel(self, *args):
|
|
101
|
+
"""Handle cancellation of the agent request."""
|
|
102
|
+
logger.info("Agent request canceled")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# =======================
|
|
106
|
+
# BLE Advertisement
|
|
107
|
+
# =======================
|
|
108
|
+
class Advertisement(dbus.service.Object):
|
|
109
|
+
"""BLE Advertisement."""
|
|
110
|
+
|
|
111
|
+
PATH_BASE = "/org/bluez/advertisement"
|
|
112
|
+
|
|
113
|
+
def __init__(self, bus, index, advertising_type, local_name):
|
|
114
|
+
"""Initialize the Advertisement."""
|
|
115
|
+
self.path = self.PATH_BASE + str(index)
|
|
116
|
+
self.bus = bus
|
|
117
|
+
self.ad_type = advertising_type
|
|
118
|
+
self.local_name = local_name
|
|
119
|
+
self.service_uuids = None
|
|
120
|
+
self.include_tx_power = False
|
|
121
|
+
dbus.service.Object.__init__(self, bus, self.path)
|
|
122
|
+
|
|
123
|
+
def get_properties(self):
|
|
124
|
+
"""Return the properties of the advertisement."""
|
|
125
|
+
props = {"Type": self.ad_type}
|
|
126
|
+
if self.local_name:
|
|
127
|
+
props["LocalName"] = dbus.String(self.local_name)
|
|
128
|
+
if self.service_uuids:
|
|
129
|
+
props["ServiceUUIDs"] = dbus.Array(self.service_uuids, signature="s")
|
|
130
|
+
props["Appearance"] = dbus.UInt16(0x0000)
|
|
131
|
+
props["Duration"] = dbus.UInt16(0)
|
|
132
|
+
props["Timeout"] = dbus.UInt16(0)
|
|
133
|
+
return {LE_ADVERTISEMENT_IFACE: props}
|
|
134
|
+
|
|
135
|
+
def get_path(self):
|
|
136
|
+
"""Return the object path."""
|
|
137
|
+
return dbus.ObjectPath(self.path)
|
|
138
|
+
|
|
139
|
+
@dbus.service.method(DBUS_PROP_IFACE, in_signature="s", out_signature="a{sv}")
|
|
140
|
+
def GetAll(self, interface):
|
|
141
|
+
"""Return all properties of the advertisement."""
|
|
142
|
+
if interface != LE_ADVERTISEMENT_IFACE:
|
|
143
|
+
raise dbus.exceptions.DBusException(
|
|
144
|
+
"org.freedesktop.DBus.Error.InvalidArgs",
|
|
145
|
+
"Unknown interface " + interface,
|
|
146
|
+
)
|
|
147
|
+
return self.get_properties()[LE_ADVERTISEMENT_IFACE]
|
|
148
|
+
|
|
149
|
+
@dbus.service.method(LE_ADVERTISEMENT_IFACE, in_signature="", out_signature="")
|
|
150
|
+
def Release(self):
|
|
151
|
+
"""Handle release of the advertisement."""
|
|
152
|
+
logger.info("Advertisement released")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# =======================
|
|
156
|
+
# BLE Characteristics & Service
|
|
157
|
+
# =======================
|
|
158
|
+
class Descriptor(dbus.service.Object):
|
|
159
|
+
"""GATT Descriptor."""
|
|
160
|
+
|
|
161
|
+
def __init__(self, bus, index, uuid, flags, characteristic):
|
|
162
|
+
"""Initialize the Descriptor."""
|
|
163
|
+
self.path = characteristic.path + "/desc" + str(index)
|
|
164
|
+
self.bus = bus
|
|
165
|
+
self.uuid = uuid
|
|
166
|
+
self.flags = flags
|
|
167
|
+
self.characteristic = characteristic
|
|
168
|
+
self.value = []
|
|
169
|
+
dbus.service.Object.__init__(self, bus, self.path)
|
|
170
|
+
|
|
171
|
+
def get_properties(self):
|
|
172
|
+
"""Return the properties of the descriptor."""
|
|
173
|
+
return {
|
|
174
|
+
GATT_DESC_IFACE: {
|
|
175
|
+
"Characteristic": self.characteristic.get_path(),
|
|
176
|
+
"UUID": self.uuid,
|
|
177
|
+
"Flags": self.flags,
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
def get_path(self):
|
|
182
|
+
"""Return the object path."""
|
|
183
|
+
return dbus.ObjectPath(self.path)
|
|
184
|
+
|
|
185
|
+
@dbus.service.method(DBUS_PROP_IFACE, in_signature="s", out_signature="a{sv}")
|
|
186
|
+
def GetAll(self, interface):
|
|
187
|
+
"""Return all properties of the descriptor."""
|
|
188
|
+
if interface != GATT_DESC_IFACE:
|
|
189
|
+
raise dbus.exceptions.DBusException(
|
|
190
|
+
"org.freedesktop.DBus.Error.InvalidArgs", "Unknown interface"
|
|
191
|
+
)
|
|
192
|
+
return self.get_properties()[GATT_DESC_IFACE]
|
|
193
|
+
|
|
194
|
+
@dbus.service.method(GATT_DESC_IFACE, in_signature="a{sv}", out_signature="ay")
|
|
195
|
+
def ReadValue(self, options):
|
|
196
|
+
"""Handle read from the descriptor."""
|
|
197
|
+
return dbus.Array(self.value, signature="y")
|
|
198
|
+
|
|
199
|
+
@dbus.service.method(GATT_DESC_IFACE, in_signature="aya{sv}")
|
|
200
|
+
def WriteValue(self, value, options):
|
|
201
|
+
"""Handle write to the descriptor."""
|
|
202
|
+
self.value = value
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class Characteristic(dbus.service.Object):
|
|
206
|
+
"""GATT Characteristic."""
|
|
207
|
+
|
|
208
|
+
def __init__(self, bus, index, uuid, flags, service):
|
|
209
|
+
"""Initialize the Characteristic."""
|
|
210
|
+
self.path = service.path + "/char" + str(index)
|
|
211
|
+
self.bus = bus
|
|
212
|
+
self.uuid = uuid
|
|
213
|
+
self.service = service
|
|
214
|
+
self.flags = flags
|
|
215
|
+
self.value = []
|
|
216
|
+
self.descriptors = []
|
|
217
|
+
dbus.service.Object.__init__(self, bus, self.path)
|
|
218
|
+
|
|
219
|
+
def get_properties(self):
|
|
220
|
+
"""Return the properties of the characteristic."""
|
|
221
|
+
props = {
|
|
222
|
+
GATT_CHRC_IFACE: {
|
|
223
|
+
"Service": self.service.get_path(),
|
|
224
|
+
"UUID": self.uuid,
|
|
225
|
+
"Flags": self.flags,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if self.descriptors:
|
|
229
|
+
props[GATT_CHRC_IFACE]["Descriptors"] = [
|
|
230
|
+
d.get_path() for d in self.descriptors
|
|
231
|
+
]
|
|
232
|
+
return props
|
|
233
|
+
|
|
234
|
+
def get_path(self):
|
|
235
|
+
"""Return the object path."""
|
|
236
|
+
return dbus.ObjectPath(self.path)
|
|
237
|
+
|
|
238
|
+
def add_descriptor(self, descriptor):
|
|
239
|
+
"""Add a descriptor to this characteristic."""
|
|
240
|
+
self.descriptors.append(descriptor)
|
|
241
|
+
|
|
242
|
+
@dbus.service.method(DBUS_PROP_IFACE, in_signature="s", out_signature="a{sv}")
|
|
243
|
+
def GetAll(self, interface):
|
|
244
|
+
"""Return all properties of the characteristic."""
|
|
245
|
+
if interface != GATT_CHRC_IFACE:
|
|
246
|
+
raise dbus.exceptions.DBusException(
|
|
247
|
+
"org.freedesktop.DBus.Error.InvalidArgs", "Unknown interface"
|
|
248
|
+
)
|
|
249
|
+
return self.get_properties()[GATT_CHRC_IFACE]
|
|
250
|
+
|
|
251
|
+
@dbus.service.method(GATT_CHRC_IFACE, in_signature="a{sv}", out_signature="ay")
|
|
252
|
+
def ReadValue(self, options):
|
|
253
|
+
"""Handle read from the characteristic."""
|
|
254
|
+
return dbus.Array(self.value, signature="y")
|
|
255
|
+
|
|
256
|
+
@dbus.service.method(GATT_CHRC_IFACE, in_signature="aya{sv}")
|
|
257
|
+
def WriteValue(self, value, options):
|
|
258
|
+
"""Handle write to the characteristic."""
|
|
259
|
+
self.value = value
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class CommandCharacteristic(Characteristic):
|
|
263
|
+
"""Command Characteristic."""
|
|
264
|
+
|
|
265
|
+
def __init__(self, bus, index, service, command_handler: Callable[[bytes], str]):
|
|
266
|
+
"""Initialize the Command Characteristic."""
|
|
267
|
+
super().__init__(bus, index, COMMAND_CHAR_UUID, ["write"], service)
|
|
268
|
+
self.command_handler = command_handler
|
|
269
|
+
|
|
270
|
+
def WriteValue(self, value, options):
|
|
271
|
+
"""Handle write to the Command Characteristic."""
|
|
272
|
+
command_bytes = bytes(value)
|
|
273
|
+
response = self.command_handler(command_bytes)
|
|
274
|
+
self.service.response_char.value = [
|
|
275
|
+
dbus.Byte(b) for b in response.encode("utf-8")
|
|
276
|
+
]
|
|
277
|
+
logger.info(f"Command received: {response}")
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class ResponseCharacteristic(Characteristic):
|
|
281
|
+
"""Response Characteristic.""" ""
|
|
282
|
+
|
|
283
|
+
def __init__(self, bus, index, service):
|
|
284
|
+
"""Initialize the Response Characteristic."""
|
|
285
|
+
super().__init__(bus, index, RESPONSE_CHAR_UUID, ["read", "notify"], service)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class Service(dbus.service.Object):
|
|
289
|
+
"""GATT Service."""
|
|
290
|
+
|
|
291
|
+
PATH_BASE = "/org/bluez/service"
|
|
292
|
+
|
|
293
|
+
def __init__(
|
|
294
|
+
self, bus, index, uuid, primary, command_handler: Callable[[bytes], str]
|
|
295
|
+
):
|
|
296
|
+
"""Initialize the GATT Service."""
|
|
297
|
+
self.path = self.PATH_BASE + str(index)
|
|
298
|
+
self.bus = bus
|
|
299
|
+
self.uuid = uuid
|
|
300
|
+
self.primary = primary
|
|
301
|
+
self.characteristics = []
|
|
302
|
+
dbus.service.Object.__init__(self, bus, self.path)
|
|
303
|
+
# Response characteristic first
|
|
304
|
+
self.response_char = ResponseCharacteristic(bus, 1, self)
|
|
305
|
+
self.add_characteristic(self.response_char)
|
|
306
|
+
# Command characteristic
|
|
307
|
+
self.add_characteristic(CommandCharacteristic(bus, 0, self, command_handler))
|
|
308
|
+
|
|
309
|
+
def get_properties(self):
|
|
310
|
+
"""Return the properties of the service."""
|
|
311
|
+
return {
|
|
312
|
+
GATT_SERVICE_IFACE: {
|
|
313
|
+
"UUID": self.uuid,
|
|
314
|
+
"Primary": self.primary,
|
|
315
|
+
"Characteristics": [ch.get_path() for ch in self.characteristics],
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
def get_path(self):
|
|
320
|
+
"""Return the object path."""
|
|
321
|
+
return dbus.ObjectPath(self.path)
|
|
322
|
+
|
|
323
|
+
def add_characteristic(self, ch):
|
|
324
|
+
"""Add a characteristic to the service."""
|
|
325
|
+
self.characteristics.append(ch)
|
|
326
|
+
|
|
327
|
+
@dbus.service.method(DBUS_PROP_IFACE, in_signature="s", out_signature="a{sv}")
|
|
328
|
+
def GetAll(self, interface):
|
|
329
|
+
"""Return all properties of the service."""
|
|
330
|
+
if interface != GATT_SERVICE_IFACE:
|
|
331
|
+
raise dbus.exceptions.DBusException(
|
|
332
|
+
"org.freedesktop.DBus.Error.InvalidArgs", "Unknown interface"
|
|
333
|
+
)
|
|
334
|
+
return self.get_properties()[GATT_SERVICE_IFACE]
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
class StaticCharacteristic(Characteristic):
|
|
338
|
+
"""Read-only characteristic with static value."""
|
|
339
|
+
|
|
340
|
+
def __init__(
|
|
341
|
+
self, bus, index, uuid, service, value_str: str, description: str = None
|
|
342
|
+
):
|
|
343
|
+
"""Initialize the Static Characteristic."""
|
|
344
|
+
super().__init__(bus, index, uuid, ["read"], service)
|
|
345
|
+
self.value = [dbus.Byte(b) for b in value_str.encode("utf-8")]
|
|
346
|
+
|
|
347
|
+
# Add user description descriptor if provided
|
|
348
|
+
if description:
|
|
349
|
+
desc = Descriptor(bus, 0, USER_DESCRIPTION_UUID, ["read"], self)
|
|
350
|
+
desc.value = [dbus.Byte(b) for b in description.encode("utf-8")]
|
|
351
|
+
self.add_descriptor(desc)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class DynamicCharacteristic(Characteristic):
|
|
355
|
+
"""Read-only characteristic with dynamically updatable value."""
|
|
356
|
+
|
|
357
|
+
def __init__(
|
|
358
|
+
self,
|
|
359
|
+
bus,
|
|
360
|
+
index,
|
|
361
|
+
uuid,
|
|
362
|
+
service,
|
|
363
|
+
value_getter: Callable[[], str],
|
|
364
|
+
description: str = None,
|
|
365
|
+
):
|
|
366
|
+
"""Initialize the Dynamic Characteristic."""
|
|
367
|
+
super().__init__(bus, index, uuid, ["read"], service)
|
|
368
|
+
self.value_getter = value_getter
|
|
369
|
+
self.update_value()
|
|
370
|
+
|
|
371
|
+
# Add user description descriptor if provided
|
|
372
|
+
if description:
|
|
373
|
+
desc = Descriptor(bus, 0, USER_DESCRIPTION_UUID, ["read"], self)
|
|
374
|
+
desc.value = [dbus.Byte(b) for b in description.encode("utf-8")]
|
|
375
|
+
self.add_descriptor(desc)
|
|
376
|
+
|
|
377
|
+
def update_value(self):
|
|
378
|
+
"""Update the characteristic value from the getter function."""
|
|
379
|
+
value_str = self.value_getter()
|
|
380
|
+
self.value = [dbus.Byte(b) for b in value_str.encode("utf-8")]
|
|
381
|
+
return True # Keep periodic callback alive
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
class DeviceInfoService(dbus.service.Object):
|
|
385
|
+
"""Device Information Service."""
|
|
386
|
+
|
|
387
|
+
PATH_BASE = "/org/bluez/device_info"
|
|
388
|
+
|
|
389
|
+
def __init__(self, bus, index):
|
|
390
|
+
"""Initialize the Device Information Service."""
|
|
391
|
+
self.path = self.PATH_BASE + str(index)
|
|
392
|
+
self.bus = bus
|
|
393
|
+
self.uuid = DEVICE_INFO_SERVICE_UUID
|
|
394
|
+
self.primary = True
|
|
395
|
+
self.characteristics = []
|
|
396
|
+
dbus.service.Object.__init__(self, bus, self.path)
|
|
397
|
+
|
|
398
|
+
# Get hotspot IP and format it
|
|
399
|
+
hotspot_ip = get_hotspot_ip()
|
|
400
|
+
firmware_value = f"[HOTSPOT]:{hotspot_ip}"
|
|
401
|
+
|
|
402
|
+
# Add standard Device Info characteristics
|
|
403
|
+
self.add_characteristic(
|
|
404
|
+
StaticCharacteristic(
|
|
405
|
+
bus, 0, MANUFACTURER_NAME_UUID, self, "Pollen Robotics"
|
|
406
|
+
)
|
|
407
|
+
)
|
|
408
|
+
self.add_characteristic(
|
|
409
|
+
StaticCharacteristic(bus, 1, MODEL_NUMBER_UUID, self, "Reachy Mini")
|
|
410
|
+
)
|
|
411
|
+
self.add_characteristic(
|
|
412
|
+
StaticCharacteristic(bus, 2, FIRMWARE_REVISION_UUID, self, firmware_value)
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
def get_properties(self):
|
|
416
|
+
"""Return the properties of the service."""
|
|
417
|
+
return {
|
|
418
|
+
GATT_SERVICE_IFACE: {
|
|
419
|
+
"UUID": self.uuid,
|
|
420
|
+
"Primary": self.primary,
|
|
421
|
+
"Characteristics": [ch.get_path() for ch in self.characteristics],
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
def get_path(self):
|
|
426
|
+
"""Return the object path."""
|
|
427
|
+
return dbus.ObjectPath(self.path)
|
|
428
|
+
|
|
429
|
+
def add_characteristic(self, ch):
|
|
430
|
+
"""Add a characteristic to the service."""
|
|
431
|
+
self.characteristics.append(ch)
|
|
432
|
+
|
|
433
|
+
@dbus.service.method(DBUS_PROP_IFACE, in_signature="s", out_signature="a{sv}")
|
|
434
|
+
def GetAll(self, interface):
|
|
435
|
+
"""Return all properties of the service."""
|
|
436
|
+
if interface != GATT_SERVICE_IFACE:
|
|
437
|
+
raise dbus.exceptions.DBusException(
|
|
438
|
+
"org.freedesktop.DBus.Error.InvalidArgs", "Unknown interface"
|
|
439
|
+
)
|
|
440
|
+
return self.get_properties()[GATT_SERVICE_IFACE]
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
class ReachyStatusService(dbus.service.Object):
|
|
444
|
+
"""Custom Reachy Status Service with network and system info."""
|
|
445
|
+
|
|
446
|
+
PATH_BASE = "/org/bluez/reachy_status"
|
|
447
|
+
|
|
448
|
+
def __init__(self, bus, index):
|
|
449
|
+
"""Initialize the Reachy Status Service."""
|
|
450
|
+
self.path = self.PATH_BASE + str(index)
|
|
451
|
+
self.bus = bus
|
|
452
|
+
self.uuid = REACHY_STATUS_SERVICE_UUID
|
|
453
|
+
self.primary = True
|
|
454
|
+
self.characteristics = []
|
|
455
|
+
dbus.service.Object.__init__(self, bus, self.path)
|
|
456
|
+
|
|
457
|
+
# Get available commands (static)
|
|
458
|
+
import os
|
|
459
|
+
|
|
460
|
+
commands_dir = "commands"
|
|
461
|
+
available_cmds = []
|
|
462
|
+
if os.path.isdir(commands_dir):
|
|
463
|
+
for f in os.listdir(commands_dir):
|
|
464
|
+
if f.endswith(".sh"):
|
|
465
|
+
available_cmds.append(f.replace(".sh", ""))
|
|
466
|
+
commands_value = ", ".join(available_cmds) if available_cmds else "None"
|
|
467
|
+
|
|
468
|
+
# Add dynamic network status characteristic that auto-updates
|
|
469
|
+
self.network_char = DynamicCharacteristic(
|
|
470
|
+
bus, 0, NETWORK_STATUS_UUID, self, get_network_status, "Network Status"
|
|
471
|
+
)
|
|
472
|
+
self.add_characteristic(self.network_char)
|
|
473
|
+
|
|
474
|
+
# Add static characteristics
|
|
475
|
+
self.add_characteristic(
|
|
476
|
+
StaticCharacteristic(
|
|
477
|
+
bus, 1, SYSTEM_STATUS_UUID, self, "Online", "System Status"
|
|
478
|
+
)
|
|
479
|
+
)
|
|
480
|
+
self.add_characteristic(
|
|
481
|
+
StaticCharacteristic(
|
|
482
|
+
bus,
|
|
483
|
+
2,
|
|
484
|
+
AVAILABLE_COMMANDS_UUID,
|
|
485
|
+
self,
|
|
486
|
+
commands_value,
|
|
487
|
+
"Available Commands",
|
|
488
|
+
)
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
def update_network_status(self):
|
|
492
|
+
"""Update the network status characteristic value."""
|
|
493
|
+
if hasattr(self, "network_char"):
|
|
494
|
+
self.network_char.update_value()
|
|
495
|
+
return True # Keep periodic callback alive
|
|
496
|
+
|
|
497
|
+
def get_properties(self):
|
|
498
|
+
"""Return the properties of the service."""
|
|
499
|
+
return {
|
|
500
|
+
GATT_SERVICE_IFACE: {
|
|
501
|
+
"UUID": self.uuid,
|
|
502
|
+
"Primary": self.primary,
|
|
503
|
+
"Characteristics": [ch.get_path() for ch in self.characteristics],
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
def get_path(self):
|
|
508
|
+
"""Return the object path."""
|
|
509
|
+
return dbus.ObjectPath(self.path)
|
|
510
|
+
|
|
511
|
+
def add_characteristic(self, ch):
|
|
512
|
+
"""Add a characteristic to the service."""
|
|
513
|
+
self.characteristics.append(ch)
|
|
514
|
+
|
|
515
|
+
@dbus.service.method(DBUS_PROP_IFACE, in_signature="s", out_signature="a{sv}")
|
|
516
|
+
def GetAll(self, interface):
|
|
517
|
+
"""Return all properties of the service."""
|
|
518
|
+
if interface != GATT_SERVICE_IFACE:
|
|
519
|
+
raise dbus.exceptions.DBusException(
|
|
520
|
+
"org.freedesktop.DBus.Error.InvalidArgs", "Unknown interface"
|
|
521
|
+
)
|
|
522
|
+
return self.get_properties()[GATT_SERVICE_IFACE]
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class Application(dbus.service.Object):
|
|
526
|
+
"""GATT Application."""
|
|
527
|
+
|
|
528
|
+
def __init__(self, bus, command_handler: Callable[[bytes], str]):
|
|
529
|
+
"""Initialize the GATT Application."""
|
|
530
|
+
self.path = "/"
|
|
531
|
+
self.services = []
|
|
532
|
+
dbus.service.Object.__init__(self, bus, self.path)
|
|
533
|
+
# Add command service
|
|
534
|
+
self.services.append(Service(bus, 0, SERVICE_UUID, True, command_handler))
|
|
535
|
+
# Add Device Information Service
|
|
536
|
+
self.services.append(DeviceInfoService(bus, 1))
|
|
537
|
+
# Add Custom Reachy Status Service
|
|
538
|
+
self.reachy_status = ReachyStatusService(bus, 2)
|
|
539
|
+
self.services.append(self.reachy_status)
|
|
540
|
+
|
|
541
|
+
def get_path(self):
|
|
542
|
+
"""Return the object path."""
|
|
543
|
+
return dbus.ObjectPath(self.path)
|
|
544
|
+
|
|
545
|
+
@dbus.service.method(DBUS_OM_IFACE, out_signature="a{oa{sa{sv}}}")
|
|
546
|
+
def GetManagedObjects(self):
|
|
547
|
+
"""Return a dictionary of all managed objects."""
|
|
548
|
+
resp = {}
|
|
549
|
+
for service in self.services:
|
|
550
|
+
resp[service.get_path()] = service.get_properties()
|
|
551
|
+
for ch in service.characteristics:
|
|
552
|
+
resp[ch.get_path()] = ch.get_properties()
|
|
553
|
+
# Include descriptors
|
|
554
|
+
for desc in ch.descriptors:
|
|
555
|
+
resp[desc.get_path()] = desc.get_properties()
|
|
556
|
+
return resp
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
# =======================
|
|
560
|
+
# Bluetooth Command Server
|
|
561
|
+
# =======================
|
|
562
|
+
class BluetoothCommandService:
|
|
563
|
+
"""Bluetooth Command Service."""
|
|
564
|
+
|
|
565
|
+
def __init__(self, device_name="ReachyMini", pin_code="00000"):
|
|
566
|
+
"""Initialize the Bluetooth Command Service."""
|
|
567
|
+
self.device_name = device_name
|
|
568
|
+
self.pin_code = pin_code
|
|
569
|
+
self.connected = False
|
|
570
|
+
self.bus = None
|
|
571
|
+
self.app = None
|
|
572
|
+
self.adv = None
|
|
573
|
+
self.mainloop = None
|
|
574
|
+
|
|
575
|
+
def _handle_command(self, value: bytes) -> str:
|
|
576
|
+
command_str = value.decode("utf-8").strip()
|
|
577
|
+
logger.info(f"Received command: {command_str}")
|
|
578
|
+
# Custom command handling
|
|
579
|
+
if command_str.upper() == "PING":
|
|
580
|
+
return "PONG"
|
|
581
|
+
elif command_str.upper() == "STATUS":
|
|
582
|
+
# exec a "sudo ls" command and print the result
|
|
583
|
+
try:
|
|
584
|
+
result = subprocess.run(["sudo", "ls"], capture_output=True, text=True)
|
|
585
|
+
logger.info(f"Command output: {result.stdout}")
|
|
586
|
+
except Exception as e:
|
|
587
|
+
logger.error(f"Error executing command: {e}")
|
|
588
|
+
return "OK: System running"
|
|
589
|
+
elif command_str.startswith("PIN_"):
|
|
590
|
+
pin = command_str[4:].strip()
|
|
591
|
+
if pin == self.pin_code:
|
|
592
|
+
self.connected = True
|
|
593
|
+
return "OK: Connected"
|
|
594
|
+
else:
|
|
595
|
+
return "ERROR: Incorrect PIN"
|
|
596
|
+
|
|
597
|
+
# else if command starts with "CMD_xxxxx" check if commands directory contains the said named script command xxxx.sh and run its, show output or/and send to read
|
|
598
|
+
elif command_str.startswith("CMD_"):
|
|
599
|
+
if not self.connected:
|
|
600
|
+
return "ERROR: Not connected. Please authenticate first."
|
|
601
|
+
try:
|
|
602
|
+
script_name = command_str[4:].strip() + ".sh"
|
|
603
|
+
script_path = os.path.join("commands", script_name)
|
|
604
|
+
if os.path.isfile(script_path):
|
|
605
|
+
try:
|
|
606
|
+
result = subprocess.run(
|
|
607
|
+
["sudo", script_path], capture_output=True, text=True
|
|
608
|
+
)
|
|
609
|
+
logger.info(f"Command output: {result.stdout}")
|
|
610
|
+
except Exception as e:
|
|
611
|
+
logger.error(f"Error executing command: {e}")
|
|
612
|
+
else:
|
|
613
|
+
return f"ERROR: Command '{script_name}' not found"
|
|
614
|
+
except Exception as e:
|
|
615
|
+
logger.error(f"Error processing command: {e}")
|
|
616
|
+
return "ERROR: Command execution failed"
|
|
617
|
+
finally:
|
|
618
|
+
self.connected = False # reset connection after command
|
|
619
|
+
else:
|
|
620
|
+
return f"ECHO: {command_str}"
|
|
621
|
+
|
|
622
|
+
def start(self):
|
|
623
|
+
"""Start the Bluetooth Command Service."""
|
|
624
|
+
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
|
625
|
+
self.bus = dbus.SystemBus()
|
|
626
|
+
|
|
627
|
+
# BLE Agent registration
|
|
628
|
+
agent_manager = dbus.Interface(
|
|
629
|
+
self.bus.get_object("org.bluez", "/org/bluez"), "org.bluez.AgentManager1"
|
|
630
|
+
)
|
|
631
|
+
self.agent = NoInputAgent(self.bus, AGENT_PATH)
|
|
632
|
+
agent_manager.RegisterAgent(AGENT_PATH, "NoInputNoOutput")
|
|
633
|
+
agent_manager.RequestDefaultAgent(AGENT_PATH)
|
|
634
|
+
logger.info("BLE Agent registered for Just Works pairing")
|
|
635
|
+
|
|
636
|
+
# Find adapter
|
|
637
|
+
adapter = self._find_adapter()
|
|
638
|
+
if not adapter:
|
|
639
|
+
raise Exception("Bluetooth adapter not found")
|
|
640
|
+
|
|
641
|
+
adapter_props = dbus.Interface(adapter, DBUS_PROP_IFACE)
|
|
642
|
+
adapter_props.Set("org.bluez.Adapter1", "Powered", dbus.Boolean(True))
|
|
643
|
+
adapter_props.Set("org.bluez.Adapter1", "Discoverable", dbus.Boolean(True))
|
|
644
|
+
adapter_props.Set("org.bluez.Adapter1", "DiscoverableTimeout", dbus.UInt32(0))
|
|
645
|
+
adapter_props.Set("org.bluez.Adapter1", "Pairable", dbus.Boolean(True))
|
|
646
|
+
|
|
647
|
+
# Register GATT application
|
|
648
|
+
service_manager = dbus.Interface(adapter, GATT_MANAGER_IFACE)
|
|
649
|
+
self.app = Application(self.bus, self._handle_command)
|
|
650
|
+
service_manager.RegisterApplication(
|
|
651
|
+
self.app.get_path(),
|
|
652
|
+
{},
|
|
653
|
+
reply_handler=lambda: logger.info("GATT app registered"),
|
|
654
|
+
error_handler=lambda e: logger.error(f"Failed to register GATT app: {e}"),
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
# Register advertisement
|
|
658
|
+
ad_manager = dbus.Interface(adapter, LE_ADVERTISING_MANAGER_IFACE)
|
|
659
|
+
self.adv = Advertisement(self.bus, 0, "peripheral", self.device_name)
|
|
660
|
+
# Only advertise main service UUID to avoid advertisement size limits
|
|
661
|
+
# All services are still available when connected
|
|
662
|
+
self.adv.service_uuids = [REACHY_STATUS_SERVICE_UUID]
|
|
663
|
+
ad_manager.RegisterAdvertisement(
|
|
664
|
+
self.adv.get_path(),
|
|
665
|
+
{},
|
|
666
|
+
reply_handler=lambda: logger.info("Advertisement registered"),
|
|
667
|
+
error_handler=lambda e: logger.error(
|
|
668
|
+
f"Failed to register advertisement: {e}"
|
|
669
|
+
),
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
# Setup periodic network status updates (every 10 seconds)
|
|
673
|
+
GLib.timeout_add_seconds(10, self.app.reachy_status.update_network_status)
|
|
674
|
+
|
|
675
|
+
logger.info(f"✓ Bluetooth service started as '{self.device_name}'")
|
|
676
|
+
|
|
677
|
+
def _find_adapter(self):
|
|
678
|
+
remote_om = dbus.Interface(
|
|
679
|
+
self.bus.get_object(BLUEZ_SERVICE_NAME, "/"), DBUS_OM_IFACE
|
|
680
|
+
)
|
|
681
|
+
objects = remote_om.GetManagedObjects()
|
|
682
|
+
for path, props in objects.items():
|
|
683
|
+
if GATT_MANAGER_IFACE in props and LE_ADVERTISING_MANAGER_IFACE in props:
|
|
684
|
+
return self.bus.get_object(BLUEZ_SERVICE_NAME, path)
|
|
685
|
+
return None
|
|
686
|
+
|
|
687
|
+
def run(self):
|
|
688
|
+
"""Run the Bluetooth Command Service."""
|
|
689
|
+
self.start()
|
|
690
|
+
self.mainloop = GLib.MainLoop()
|
|
691
|
+
try:
|
|
692
|
+
logger.info("Running. Press Ctrl+C to exit...")
|
|
693
|
+
self.mainloop.run()
|
|
694
|
+
except KeyboardInterrupt:
|
|
695
|
+
logger.info("Shutting down...")
|
|
696
|
+
self.mainloop.quit()
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
def get_pin() -> str:
|
|
700
|
+
"""Extract the last 5 digits of the serial number from dfu-util -l output."""
|
|
701
|
+
default_pin = "46879"
|
|
702
|
+
try:
|
|
703
|
+
result = subprocess.run(["dfu-util", "-l"], capture_output=True, text=True)
|
|
704
|
+
lines = result.stdout.splitlines()
|
|
705
|
+
for line in lines:
|
|
706
|
+
if "serial=" in line:
|
|
707
|
+
# Extract serial number
|
|
708
|
+
serial_part = line.split("serial=")[-1].strip().strip('"')
|
|
709
|
+
if len(serial_part) >= 5:
|
|
710
|
+
return serial_part[-5:]
|
|
711
|
+
return default_pin # fallback if not found
|
|
712
|
+
except Exception as e:
|
|
713
|
+
logger.error(f"Error getting pin from serial: {e}")
|
|
714
|
+
return default_pin
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
def get_network_status() -> str:
|
|
718
|
+
"""Get comprehensive network status with mode detection.
|
|
719
|
+
|
|
720
|
+
Returns formatted string: {MODE} [interface] address ; [interface] address
|
|
721
|
+
MODE: HOTSPOT (wlan0 is 10.0.0.x), CONNECTED (has IPs), OFFLINE (no IPs)
|
|
722
|
+
"""
|
|
723
|
+
try:
|
|
724
|
+
# Get network interfaces and IPs using ifconfig
|
|
725
|
+
result = subprocess.run(
|
|
726
|
+
["ip", "-4", "addr", "show"], capture_output=True, text=True
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
interfaces = {}
|
|
730
|
+
current_interface = None
|
|
731
|
+
|
|
732
|
+
for line in result.stdout.splitlines():
|
|
733
|
+
line = line.strip()
|
|
734
|
+
# Detect interface name (e.g., "2: wlan0: <BROADCAST...")
|
|
735
|
+
if line and not line.startswith("inet"):
|
|
736
|
+
parts = line.split(":")
|
|
737
|
+
if len(parts) >= 2 and parts[1].strip():
|
|
738
|
+
# Extract interface name (skip loopback)
|
|
739
|
+
iface = parts[1].strip()
|
|
740
|
+
if iface != "lo":
|
|
741
|
+
current_interface = iface
|
|
742
|
+
# Extract IP address
|
|
743
|
+
elif line.startswith("inet ") and current_interface:
|
|
744
|
+
inet_parts = line.split()
|
|
745
|
+
if len(inet_parts) >= 2:
|
|
746
|
+
ip_with_mask = inet_parts[1]
|
|
747
|
+
ip_addr = ip_with_mask.split("/")[0]
|
|
748
|
+
interfaces[current_interface] = ip_addr
|
|
749
|
+
|
|
750
|
+
# Determine mode
|
|
751
|
+
mode = "OFFLINE"
|
|
752
|
+
if interfaces:
|
|
753
|
+
# Check if wlan0 has 10.42.0.1 address (hotspot mode)
|
|
754
|
+
wlan0_ip = interfaces.get("wlan0", "")
|
|
755
|
+
if wlan0_ip.startswith("10.42.0.1"):
|
|
756
|
+
mode = "HOTSPOT"
|
|
757
|
+
else:
|
|
758
|
+
mode = "CONNECTED"
|
|
759
|
+
|
|
760
|
+
# Format output: {MODE} [interface] address ; [interface] address
|
|
761
|
+
if not interfaces:
|
|
762
|
+
return "OFFLINE"
|
|
763
|
+
|
|
764
|
+
interface_strings = [f"[{iface}] {ip}" for iface, ip in interfaces.items()]
|
|
765
|
+
return f"{mode} {' ; '.join(interface_strings)}"
|
|
766
|
+
|
|
767
|
+
except Exception as e:
|
|
768
|
+
logger.error(f"Error getting network status: {e}")
|
|
769
|
+
return "ERROR"
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def get_hotspot_ip() -> str:
|
|
773
|
+
"""Get the hotspot IP address from network interfaces (legacy function)."""
|
|
774
|
+
status = get_network_status()
|
|
775
|
+
# Extract first IP for backwards compatibility
|
|
776
|
+
if "[" in status and "]" in status:
|
|
777
|
+
try:
|
|
778
|
+
return status.split("]")[1].split(";")[0].strip()
|
|
779
|
+
except (IndexError, AttributeError):
|
|
780
|
+
return "0.0.0.0"
|
|
781
|
+
return "0.0.0.0"
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
# =======================
|
|
785
|
+
# Main
|
|
786
|
+
# =======================
|
|
787
|
+
def main():
|
|
788
|
+
"""Run the Bluetooth Command Service."""
|
|
789
|
+
pin = get_pin()
|
|
790
|
+
|
|
791
|
+
bt_service = BluetoothCommandService(device_name="ReachyMini", pin_code=pin)
|
|
792
|
+
bt_service.run()
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
if __name__ == "__main__":
|
|
796
|
+
main()
|