robopark 3.2.0 → 3.3.0
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.
- package/README.md +66 -105
- package/bin/robopark.js +9 -2
- package/conversation/elevenlabs_agent.py +1617 -1617
- package/conversation/supervisor_store.py +189 -189
- package/dist/{kernel/config-schema.js → config-schema.js} +1 -0
- package/dist/config-schema.js.map +1 -0
- package/dist/robopark/add-robot.js +1 -0
- package/dist/robopark/add-robot.js.map +1 -0
- package/dist/robopark/agent-ctl.js +1 -0
- package/dist/robopark/agent-ctl.js.map +1 -0
- package/dist/robopark/auto-start.js +51 -73
- package/dist/robopark/auto-start.js.map +1 -0
- package/dist/robopark/conversation.js +1 -0
- package/dist/robopark/conversation.js.map +1 -0
- package/dist/robopark/discovery.js +1 -0
- package/dist/robopark/discovery.js.map +1 -0
- package/dist/robopark/doctor.js +1 -0
- package/dist/robopark/doctor.js.map +1 -0
- package/dist/robopark/enroll.js +1 -0
- package/dist/robopark/enroll.js.map +1 -0
- package/dist/robopark/llm-set.js +1 -0
- package/dist/robopark/llm-set.js.map +1 -0
- package/dist/robopark/motor-control.js +1 -0
- package/dist/robopark/motor-control.js.map +1 -0
- package/dist/robopark/preview-agent-launcher.js +1 -0
- package/dist/robopark/preview-agent-launcher.js.map +1 -0
- package/dist/robopark/probe.js +16 -15
- package/dist/robopark/probe.js.map +1 -0
- package/dist/robopark/profile.js +1 -0
- package/dist/robopark/profile.js.map +1 -0
- package/dist/robopark/python-env.js +1 -7
- package/dist/robopark/python-env.js.map +1 -0
- package/dist/robopark/robot-runtime.js +1 -0
- package/dist/robopark/robot-runtime.js.map +1 -0
- package/dist/robopark/scan.js +1 -0
- package/dist/robopark/scan.js.map +1 -0
- package/dist/robopark/screen-control.js +1 -0
- package/dist/robopark/screen-control.js.map +1 -0
- package/dist/robopark/secrets.js +1 -0
- package/dist/robopark/secrets.js.map +1 -0
- package/dist/robopark/serve.js +6 -8
- package/dist/robopark/serve.js.map +1 -0
- package/dist/robopark/server-add.js +1 -0
- package/dist/robopark/server-add.js.map +1 -0
- package/dist/robopark/setup-livekit.js +53 -52
- package/dist/robopark/setup-livekit.js.map +1 -0
- package/dist/robopark/setup.js +1 -0
- package/dist/robopark/setup.js.map +1 -0
- package/dist/robopark/stop-all.js +1 -0
- package/dist/robopark/stop-all.js.map +1 -0
- package/dist/robopark/verify.js +1 -0
- package/dist/robopark/verify.js.map +1 -0
- package/dist/robopark/vision-agent-launcher.js +1 -0
- package/dist/robopark/vision-agent-launcher.js.map +1 -0
- package/dist/robopark/vision-control.js +1 -0
- package/dist/robopark/vision-control.js.map +1 -0
- package/dist/robopark-cli.js +7 -125
- package/dist/robopark-cli.js.map +1 -0
- package/package.json +16 -56
- package/pi-client/_install_steps.sh +29 -29
- package/pi-client/client.py +76 -76
- package/pi-client/install.sh +40 -40
- package/pi-client/join_convo.sh +54 -54
- package/pi-client/livekit_bridge.py +24 -24
- package/pi-client/motor_bridge.py +10 -10
- package/scheduler/__pycache__/main.cpython-312.pyc +0 -0
- package/scheduler/main.py +196 -17
- package/scheduler/media_lock.py +57 -57
- package/scheduler/preview_agent.py +1122 -1122
- package/scheduler/production_config.json +73 -113
- package/scheduler/scripts/install-robot-supervisor-linux.sh +33 -33
- package/{ui/standalone/public → scheduler/static}/voice-join.html +101 -13
- package/screen/screen_runtime.py +75 -75
- package/vision/app_pi_clean.py +297 -297
- package/vision/audio_server_pi.py +24 -24
- package/vision/install.sh +34 -34
- package/vision/motor_server.py +275 -275
- package/vision/requirements_camera.txt +6 -6
- package/vision/requirements_motor.txt +4 -4
- package/vision/requirements_pi_unified.txt +2 -2
- package/vision/requirements_vision_agent.txt +19 -19
- package/vision/run.sh +244 -244
- package/vision/services/services.sh +12 -12
- package/dist/kernel/types.js +0 -7
- package/dist/robopark/access.js +0 -99
- package/dist/robopark/control-center-html.js +0 -9191
- package/dist/robopark/control-server.js +0 -148
- package/dist/robopark/deployment-commands.js +0 -47
- package/dist/robopark/frontend-service.js +0 -80
- package/dist/robopark/standalone.js +0 -584
- package/dist/robopark/voice-join-html.js +0 -57
- package/scripts/windows-shim-fix.cjs +0 -58
- package/static/robopark/barracuda.png +0 -0
- package/static/robopark/bmw.png +0 -0
- package/static/robopark/jaguar.png +0 -0
- package/static/robopark/magnus.jpg +0 -0
- package/static/robopark/panda.png +0 -0
- package/static/robopark/tesla.png +0 -0
- package/static/robopark/titan.jpg +0 -0
- package/static/robopark/volt.png +0 -0
- package/static/vendor/elevenlabs-client.LICENSE +0 -21
- package/static/vendor/elevenlabs-client.iife.js +0 -24200
- package/ui/ROBOVOICE_BUILD.json +0 -9
- package/ui/standalone/.next/BUILD_ID +0 -1
- package/ui/standalone/.next/app-build-manifest.json +0 -391
- package/ui/standalone/.next/app-path-routes-manifest.json +0 -55
- package/ui/standalone/.next/build-manifest.json +0 -33
- package/ui/standalone/.next/package.json +0 -1
- package/ui/standalone/.next/prerender-manifest.json +0 -41
- package/ui/standalone/.next/react-loadable-manifest.json +0 -14
- package/ui/standalone/.next/required-server-files.json +0 -362
- package/ui/standalone/.next/routes-manifest.json +0 -246
- package/ui/standalone/.next/server/app/(app)/opengraph-image-xg4ifa/route.js +0 -1
- package/ui/standalone/.next/server/app/(app)/opengraph-image-xg4ifa/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/(app)/opengraph-image-xg4ifa/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/(app)/page.js +0 -2
- package/ui/standalone/.next/server/app/(app)/page.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/(app)/page_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/(app)/voice/page.js +0 -12
- package/ui/standalone/.next/server/app/(app)/voice/page.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/(app)/voice/page_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/_not-found/page.js +0 -12
- package/ui/standalone/.next/server/app/_not-found/page.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/connection-details/route.js +0 -1
- package/ui/standalone/.next/server/app/api/connection-details/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/connection-details/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/device-character/route.js +0 -1
- package/ui/standalone/.next/server/app/api/device-character/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/device-character/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/download-piper-model/route.js +0 -1
- package/ui/standalone/.next/server/app/api/download-piper-model/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/download-piper-model/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/model-status/route.js +0 -1
- package/ui/standalone/.next/server/app/api/model-status/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/model-status/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/models/route.js +0 -1
- package/ui/standalone/.next/server/app/api/models/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/models/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/prewarm/route.js +0 -1
- package/ui/standalone/.next/server/app/api/prewarm/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/prewarm/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/prompt/route.js +0 -1
- package/ui/standalone/.next/server/app/api/prompt/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/prompt/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/reload-tools/route.js +0 -1
- package/ui/standalone/.next/server/app/api/reload-tools/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/reload-tools/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/camera-token/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/camera-token/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/camera-token/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/character-ai/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/character-ai/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/character-ai/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/device-access/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/device-access/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/device-access/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/devices/[deviceId]/control/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/devices/[deviceId]/control/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/devices/[deviceId]/control/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/live-rooms/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/live-rooms/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/live-rooms/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/monitor-token/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/monitor-token/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/monitor-token/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/operations/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/operations/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/operations/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/voice-engine/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/voice-engine/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/voice-engine/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/metrics/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/metrics/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/metrics/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/[sessionId]/end/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/[sessionId]/end/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/[sessionId]/end/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/[sessionId]/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/[sessionId]/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/[sessionId]/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robovoice/robots/[robotId]/media-health/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robovoice/robots/[robotId]/media-health/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robovoice/robots/[robotId]/media-health/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/settings/route.js +0 -1
- package/ui/standalone/.next/server/app/api/settings/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/settings/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/complete/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/complete/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/complete/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/status/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/status/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/status/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-groq/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-groq/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-groq/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-hass/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-hass/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-hass/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-n8n/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-n8n/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-n8n/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-ollama/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-ollama/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-ollama/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/test-tts/route.js +0 -1
- package/ui/standalone/.next/server/app/api/test-tts/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/test-tts/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/turn-credentials/route.js +0 -1
- package/ui/standalone/.next/server/app/api/turn-credentials/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/turn-credentials/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/voices/route.js +0 -1
- package/ui/standalone/.next/server/app/api/voices/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/voices/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/wake/route.js +0 -1
- package/ui/standalone/.next/server/app/api/wake/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/wake/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/models/route.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/models/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/models/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/status/route.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/status/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/status/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/upload/route.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/upload/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/upload/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/favicon.ico/route.js +0 -1
- package/ui/standalone/.next/server/app/favicon.ico/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/favicon.ico.body +0 -0
- package/ui/standalone/.next/server/app/favicon.ico.meta +0 -1
- package/ui/standalone/.next/server/app/fed/[...path]/route.js +0 -4
- package/ui/standalone/.next/server/app/fed/[...path]/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/fed/[...path]/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/fed/media/robots/[robotId]/video_feed/route.js +0 -1
- package/ui/standalone/.next/server/app/fed/media/robots/[robotId]/video_feed/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/fed/media/robots/[robotId]/video_feed/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/robopark/[...path]/route.js +0 -1
- package/ui/standalone/.next/server/app/robopark/[...path]/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/robopark/[...path]/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/robopark/api/[...path]/route.js +0 -1
- package/ui/standalone/.next/server/app/robopark/api/[...path]/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/robopark/api/[...path]/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/robopark/api/robots/[robotId]/stream/route.js +0 -1
- package/ui/standalone/.next/server/app/robopark/api/robots/[robotId]/stream/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/robopark/api/robots/[robotId]/stream/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/robopark/page.js +0 -2
- package/ui/standalone/.next/server/app/robopark/page.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/robopark/page_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/tailnet/route.js +0 -1
- package/ui/standalone/.next/server/app/tailnet/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/tailnet/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/vendor/livekit-client.umd.js/route.js +0 -1
- package/ui/standalone/.next/server/app/vendor/livekit-client.umd.js/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/vendor/livekit-client.umd.js/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/voice-tester/page.js +0 -2
- package/ui/standalone/.next/server/app/voice-tester/page.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/voice-tester/page_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app-paths-manifest.json +0 -55
- package/ui/standalone/.next/server/chunks/1428.js +0 -1
- package/ui/standalone/.next/server/chunks/1692.js +0 -1
- package/ui/standalone/.next/server/chunks/37.js +0 -1
- package/ui/standalone/.next/server/chunks/4876.js +0 -1
- package/ui/standalone/.next/server/chunks/5232.js +0 -9
- package/ui/standalone/.next/server/chunks/5455.js +0 -11
- package/ui/standalone/.next/server/chunks/5611.js +0 -6
- package/ui/standalone/.next/server/chunks/5873.js +0 -22
- package/ui/standalone/.next/server/chunks/6018.js +0 -1
- package/ui/standalone/.next/server/chunks/8755.js +0 -1
- package/ui/standalone/.next/server/chunks/9077.js +0 -11
- package/ui/standalone/.next/server/functions-config-manifest.json +0 -9
- package/ui/standalone/.next/server/middleware-build-manifest.js +0 -1
- package/ui/standalone/.next/server/middleware-manifest.json +0 -6
- package/ui/standalone/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/ui/standalone/.next/server/next-font-manifest.js +0 -1
- package/ui/standalone/.next/server/next-font-manifest.json +0 -1
- package/ui/standalone/.next/server/pages/500.html +0 -1
- package/ui/standalone/.next/server/pages/_app.js +0 -1
- package/ui/standalone/.next/server/pages/_app.js.nft.json +0 -1
- package/ui/standalone/.next/server/pages/_document.js +0 -1
- package/ui/standalone/.next/server/pages/_document.js.nft.json +0 -1
- package/ui/standalone/.next/server/pages/_error.js +0 -19
- package/ui/standalone/.next/server/pages/_error.js.nft.json +0 -1
- package/ui/standalone/.next/server/pages-manifest.json +0 -5
- package/ui/standalone/.next/server/server-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/server-reference-manifest.json +0 -1
- package/ui/standalone/.next/server/webpack-runtime.js +0 -1
- package/ui/standalone/.next/static/T3eGaQUuZNb6rhEStcouc/_buildManifest.js +0 -1
- package/ui/standalone/.next/static/T3eGaQUuZNb6rhEStcouc/_ssgManifest.js +0 -1
- package/ui/standalone/.next/static/chunks/1255-404bb8de49c90395.js +0 -1
- package/ui/standalone/.next/static/chunks/144d3bae-bd5d150c0cab972e.js +0 -1
- package/ui/standalone/.next/static/chunks/1646.9123ee47220ed70b.js +0 -1
- package/ui/standalone/.next/static/chunks/2333-43c39acb531f7d4e.js +0 -3
- package/ui/standalone/.next/static/chunks/4bd1b696-f785427dddbba9fb.js +0 -1
- package/ui/standalone/.next/static/chunks/5139.c5e46d26064a85db.js +0 -1
- package/ui/standalone/.next/static/chunks/76e8f37e-2ae0e3fe7b44d874.js +0 -1
- package/ui/standalone/.next/static/chunks/9880-53262785284464cc.js +0 -1
- package/ui/standalone/.next/static/chunks/adfb71a7-625c1b5ebd039b6a.js +0 -1
- package/ui/standalone/.next/static/chunks/app/(app)/layout-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/(app)/opengraph-image-xg4ifa/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/(app)/page-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/(app)/voice/page-72d72d9a70d9c1d4.js +0 -1
- package/ui/standalone/.next/static/chunks/app/_not-found/page-cfd7daa82d71540c.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/connection-details/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/device-character/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/download-piper-model/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/model-status/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/models/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/prewarm/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/prompt/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/reload-tools/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/camera-token/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/character-ai/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/device-access/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/devices/[deviceId]/control/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/live-rooms/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/monitor-token/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/robots/[robotId]/operations/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/robots/[robotId]/voice-engine/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/robots/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/servers/[serverId]/metrics/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/servers/[serverId]/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/servers/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/sessions/[sessionId]/end/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/sessions/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/transcripts/[sessionId]/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/transcripts/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robovoice/robots/[robotId]/media-health/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/settings/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/complete/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/status/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/test-groq/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/test-hass/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/test-n8n/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/test-ollama/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/test-tts/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/turn-credentials/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/voices/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/wake/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/wake-word/models/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/wake-word/status/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/wake-word/upload/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/fed/[...path]/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/fed/media/robots/[robotId]/video_feed/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/layout-fcce963da1d8220f.js +0 -1
- package/ui/standalone/.next/static/chunks/app/robopark/[...path]/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/robopark/api/[...path]/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/robopark/api/robots/[robotId]/stream/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/robopark/page-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/tailnet/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/vendor/livekit-client.umd.js/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/voice-tester/page-ae699e048e0eb834.js +0 -1
- package/ui/standalone/.next/static/chunks/framework-e60c938074ff7136.js +0 -1
- package/ui/standalone/.next/static/chunks/main-9e24e8708ff075fc.js +0 -1
- package/ui/standalone/.next/static/chunks/main-app-a1f59168303c7f84.js +0 -1
- package/ui/standalone/.next/static/chunks/pages/_app-131c90850aef965b.js +0 -1
- package/ui/standalone/.next/static/chunks/pages/_error-e4ba546eb376bdf4.js +0 -1
- package/ui/standalone/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/ui/standalone/.next/static/chunks/webpack-5252e5254da079b5.js +0 -1
- package/ui/standalone/.next/static/css/3f478a573f9be82e.css +0 -1
- package/ui/standalone/.next/static/css/51560c4af1d5a21f.css +0 -1
- package/ui/standalone/.next/static/media/27fe93087559d480-s.p.otf +0 -0
- package/ui/standalone/.next/static/media/4d3869dd8a56345a-s.p.otf +0 -0
- package/ui/standalone/.next/static/media/63f3bb9ce29e22d5-s.p.otf +0 -0
- package/ui/standalone/.next/static/media/79e1c5d0f2fe9f68-s.p.otf +0 -0
- package/ui/standalone/.next/static/media/8bf7b2ceda89477b-s.p.woff2 +0 -0
- package/ui/standalone/.next/static/media/98baa4a7809fbf72-s.woff2 +0 -0
- package/ui/standalone/.next/static/media/f7650dfaa438c37c-s.woff2 +0 -0
- package/ui/standalone/package.json +0 -57
- package/ui/standalone/public/caal-logo.svg +0 -3
- package/ui/standalone/public/commit-mono-400-regular.woff +0 -0
- package/ui/standalone/public/cwl-logo-round.png +0 -0
- package/ui/standalone/public/everett-light.woff +0 -0
- package/ui/standalone/public/hey_cal.ppn +0 -0
- package/ui/standalone/public/install-robot-kiosk.sh +0 -242
- package/ui/standalone/public/lk-logo-dark.svg +0 -18
- package/ui/standalone/public/lk-logo.svg +0 -18
- package/ui/standalone/public/lk-wordmark.svg +0 -12
- package/ui/standalone/public/opengraph-image-bg.png +0 -0
- package/ui/standalone/public/porcupine_params.pv +0 -0
- package/ui/standalone/public/robopark-control.html +0 -9200
- package/ui/standalone/public/static/robopark/barracuda.png +0 -0
- package/ui/standalone/public/static/robopark/bmw.png +0 -0
- package/ui/standalone/public/static/robopark/jaguar.png +0 -0
- package/ui/standalone/public/static/robopark/magnus.jpg +0 -0
- package/ui/standalone/public/static/robopark/panda.png +0 -0
- package/ui/standalone/public/static/robopark/tesla.png +0 -0
- package/ui/standalone/public/static/robopark/titan.jpg +0 -0
- package/ui/standalone/public/static/robopark/volt.png +0 -0
- package/ui/standalone/public/tailnet/index.html +0 -8995
- package/ui/standalone/public/transcript-analytics.html +0 -72
- package/ui/standalone/public/vendor/livekit-client.umd.js +0 -2
- package/ui/standalone/server.js +0 -38
- /package/{ui/standalone/public → scheduler/static}/vendor/elevenlabs-client.iife.js +0 -0
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -e
|
|
3
|
-
echo "=== reset perms (cleanup from previous attempt) ==="
|
|
4
|
-
sudo rm -rf /opt/robopark-pi-client/.venv 2>/dev/null || true
|
|
5
|
-
sudo chown -R robopark:robopark /opt/robopark-pi-client
|
|
6
|
-
|
|
7
|
-
echo "=== apt deps (no signing strict) ==="
|
|
8
|
-
sudo apt-get -o Acquire::AllowInsecureRepositories=true update 2>&1 | tail -3
|
|
9
|
-
sudo apt-get install -y --no-install-recommends python3-venv python3-pip libatlas3-base libasound2t64 portaudio19-dev libjpeg-dev libpng-dev ffmpeg 2>&1 | tail -3
|
|
10
|
-
echo "apt-done"
|
|
11
|
-
|
|
12
|
-
echo "=== create venv as robopark ==="
|
|
13
|
-
sudo -u robopark python3 -m venv /opt/robopark-pi-client/.venv
|
|
14
|
-
sudo -u robopark /opt/robopark-pi-client/.venv/bin/pip install --quiet --upgrade pip
|
|
15
|
-
sudo -u robopark /opt/robopark-pi-client/.venv/bin/pip install --quiet httpx
|
|
16
|
-
echo "httpx-done"
|
|
17
|
-
|
|
18
|
-
echo "=== media stack (optional, may take a while) ==="
|
|
19
|
-
sudo -u robopark /opt/robopark-pi-client/.venv/bin/pip install --quiet livekit livekit-api numpy 2>&1 | tail -3
|
|
20
|
-
echo "lk-done"
|
|
21
|
-
sudo -u robopark /opt/robopark-pi-client/.venv/bin/pip install --quiet opencv-python-headless 2>&1 | tail -3
|
|
22
|
-
echo "cv-done"
|
|
23
|
-
sudo -u robopark /opt/robopark-pi-client/.venv/bin/pip install --quiet sounddevice 2>&1 | tail -3
|
|
24
|
-
echo "sd-done"
|
|
25
|
-
|
|
26
|
-
echo "=== final state ==="
|
|
27
|
-
ls -la /opt/robopark-pi-client
|
|
28
|
-
ls /opt/robopark-pi-client/.venv/bin | head -5
|
|
29
|
-
echo "all-done"
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
echo "=== reset perms (cleanup from previous attempt) ==="
|
|
4
|
+
sudo rm -rf /opt/robopark-pi-client/.venv 2>/dev/null || true
|
|
5
|
+
sudo chown -R robopark:robopark /opt/robopark-pi-client
|
|
6
|
+
|
|
7
|
+
echo "=== apt deps (no signing strict) ==="
|
|
8
|
+
sudo apt-get -o Acquire::AllowInsecureRepositories=true update 2>&1 | tail -3
|
|
9
|
+
sudo apt-get install -y --no-install-recommends python3-venv python3-pip libatlas3-base libasound2t64 portaudio19-dev libjpeg-dev libpng-dev ffmpeg 2>&1 | tail -3
|
|
10
|
+
echo "apt-done"
|
|
11
|
+
|
|
12
|
+
echo "=== create venv as robopark ==="
|
|
13
|
+
sudo -u robopark python3 -m venv /opt/robopark-pi-client/.venv
|
|
14
|
+
sudo -u robopark /opt/robopark-pi-client/.venv/bin/pip install --quiet --upgrade pip
|
|
15
|
+
sudo -u robopark /opt/robopark-pi-client/.venv/bin/pip install --quiet httpx
|
|
16
|
+
echo "httpx-done"
|
|
17
|
+
|
|
18
|
+
echo "=== media stack (optional, may take a while) ==="
|
|
19
|
+
sudo -u robopark /opt/robopark-pi-client/.venv/bin/pip install --quiet livekit livekit-api numpy 2>&1 | tail -3
|
|
20
|
+
echo "lk-done"
|
|
21
|
+
sudo -u robopark /opt/robopark-pi-client/.venv/bin/pip install --quiet opencv-python-headless 2>&1 | tail -3
|
|
22
|
+
echo "cv-done"
|
|
23
|
+
sudo -u robopark /opt/robopark-pi-client/.venv/bin/pip install --quiet sounddevice 2>&1 | tail -3
|
|
24
|
+
echo "sd-done"
|
|
25
|
+
|
|
26
|
+
echo "=== final state ==="
|
|
27
|
+
ls -la /opt/robopark-pi-client
|
|
28
|
+
ls /opt/robopark-pi-client/.venv/bin | head -5
|
|
29
|
+
echo "all-done"
|
package/pi-client/client.py
CHANGED
|
@@ -20,15 +20,15 @@
|
|
|
20
20
|
# Subsequent runs (token is in device.json):
|
|
21
21
|
# python client.py --scheduler-url http://100.64.1.5:8080
|
|
22
22
|
|
|
23
|
-
import argparse
|
|
24
|
-
import asyncio
|
|
25
|
-
import glob
|
|
26
|
-
import json
|
|
27
|
-
import logging
|
|
28
|
-
import os
|
|
29
|
-
import signal
|
|
30
|
-
import subprocess
|
|
31
|
-
import sys
|
|
23
|
+
import argparse
|
|
24
|
+
import asyncio
|
|
25
|
+
import glob
|
|
26
|
+
import json
|
|
27
|
+
import logging
|
|
28
|
+
import os
|
|
29
|
+
import signal
|
|
30
|
+
import subprocess
|
|
31
|
+
import sys
|
|
32
32
|
import time
|
|
33
33
|
from pathlib import Path
|
|
34
34
|
from typing import Optional
|
|
@@ -41,45 +41,45 @@ logging.basicConfig(
|
|
|
41
41
|
level=logging.INFO,
|
|
42
42
|
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
43
43
|
)
|
|
44
|
-
log = logging.getLogger("robopark-pi")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def get_device_inventory() -> dict:
|
|
48
|
-
"""Report real Linux media devices so the remote dashboard can select them."""
|
|
49
|
-
inventory = {
|
|
50
|
-
"video": [{"id": "auto", "name": "Auto detect"}, {"id": "none", "name": "Disable camera"}],
|
|
51
|
-
"audio_input": [{"id": "default", "name": "System default input"}],
|
|
52
|
-
"audio_output": [{"id": "default", "name": "System default output"}],
|
|
53
|
-
"platform": sys.platform,
|
|
54
|
-
}
|
|
55
|
-
for path in sorted(glob.glob("/dev/video*")):
|
|
56
|
-
inventory["video"].append({"id": path, "name": path, "backend": "v4l2"})
|
|
57
|
-
|
|
58
|
-
try:
|
|
59
|
-
import pyaudio
|
|
60
|
-
pa = pyaudio.PyAudio()
|
|
61
|
-
for index in range(pa.get_device_count()):
|
|
62
|
-
info = pa.get_device_info_by_index(index)
|
|
63
|
-
name = str(info.get("name", f"Audio device {index}"))
|
|
64
|
-
item = {"id": str(index), "name": name, "host_api": str(info.get("hostApi", ""))}
|
|
65
|
-
if info.get("maxInputChannels", 0) > 0:
|
|
66
|
-
inventory["audio_input"].append(item.copy())
|
|
67
|
-
if info.get("maxOutputChannels", 0) > 0:
|
|
68
|
-
inventory["audio_output"].append(item.copy())
|
|
69
|
-
pa.terminate()
|
|
70
|
-
except Exception as exc:
|
|
71
|
-
# Keep the device visible even when PortAudio is not installed; ALSA
|
|
72
|
-
# names are still useful for an operator diagnosing a Pi remotely.
|
|
73
|
-
log.debug("PyAudio inventory unavailable: %s", exc)
|
|
74
|
-
for command, key, kind in (("arecord", "audio_input", "input"), ("aplay", "audio_output", "output")):
|
|
75
|
-
try:
|
|
76
|
-
output = subprocess.run([command, "-L"], capture_output=True, text=True, timeout=3, check=False).stdout
|
|
77
|
-
for name in (line.strip() for line in output.splitlines()):
|
|
78
|
-
if name and not name.startswith("(") and name not in {x["id"] for x in inventory[key]}:
|
|
79
|
-
inventory[key].append({"id": name, "name": name, "backend": "alsa", "kind": kind})
|
|
80
|
-
except Exception:
|
|
81
|
-
pass
|
|
82
|
-
return inventory
|
|
44
|
+
log = logging.getLogger("robopark-pi")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_device_inventory() -> dict:
|
|
48
|
+
"""Report real Linux media devices so the remote dashboard can select them."""
|
|
49
|
+
inventory = {
|
|
50
|
+
"video": [{"id": "auto", "name": "Auto detect"}, {"id": "none", "name": "Disable camera"}],
|
|
51
|
+
"audio_input": [{"id": "default", "name": "System default input"}],
|
|
52
|
+
"audio_output": [{"id": "default", "name": "System default output"}],
|
|
53
|
+
"platform": sys.platform,
|
|
54
|
+
}
|
|
55
|
+
for path in sorted(glob.glob("/dev/video*")):
|
|
56
|
+
inventory["video"].append({"id": path, "name": path, "backend": "v4l2"})
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
import pyaudio
|
|
60
|
+
pa = pyaudio.PyAudio()
|
|
61
|
+
for index in range(pa.get_device_count()):
|
|
62
|
+
info = pa.get_device_info_by_index(index)
|
|
63
|
+
name = str(info.get("name", f"Audio device {index}"))
|
|
64
|
+
item = {"id": str(index), "name": name, "host_api": str(info.get("hostApi", ""))}
|
|
65
|
+
if info.get("maxInputChannels", 0) > 0:
|
|
66
|
+
inventory["audio_input"].append(item.copy())
|
|
67
|
+
if info.get("maxOutputChannels", 0) > 0:
|
|
68
|
+
inventory["audio_output"].append(item.copy())
|
|
69
|
+
pa.terminate()
|
|
70
|
+
except Exception as exc:
|
|
71
|
+
# Keep the device visible even when PortAudio is not installed; ALSA
|
|
72
|
+
# names are still useful for an operator diagnosing a Pi remotely.
|
|
73
|
+
log.debug("PyAudio inventory unavailable: %s", exc)
|
|
74
|
+
for command, key, kind in (("arecord", "audio_input", "input"), ("aplay", "audio_output", "output")):
|
|
75
|
+
try:
|
|
76
|
+
output = subprocess.run([command, "-L"], capture_output=True, text=True, timeout=3, check=False).stdout
|
|
77
|
+
for name in (line.strip() for line in output.splitlines()):
|
|
78
|
+
if name and not name.startswith("(") and name not in {x["id"] for x in inventory[key]}:
|
|
79
|
+
inventory[key].append({"id": name, "name": name, "backend": "alsa", "kind": kind})
|
|
80
|
+
except Exception:
|
|
81
|
+
pass
|
|
82
|
+
return inventory
|
|
83
83
|
|
|
84
84
|
|
|
85
85
|
class PiClient:
|
|
@@ -96,10 +96,10 @@ class PiClient:
|
|
|
96
96
|
self.heartbeat_interval = args.heartbeat_interval
|
|
97
97
|
self.config_poll_interval = args.config_poll_interval
|
|
98
98
|
self.join_room = args.join_room
|
|
99
|
-
self.room_name = args.room_name
|
|
100
|
-
self.video_device = args.video_device
|
|
101
|
-
self.audio_device = args.audio_device
|
|
102
|
-
self.audio_output_device = args.audio_output_device
|
|
99
|
+
self.room_name = args.room_name
|
|
100
|
+
self.video_device = args.video_device
|
|
101
|
+
self.audio_device = args.audio_device
|
|
102
|
+
self.audio_output_device = args.audio_output_device
|
|
103
103
|
|
|
104
104
|
self.device_id: Optional[str] = None
|
|
105
105
|
self.device_token: Optional[str] = None
|
|
@@ -172,12 +172,12 @@ class PiClient:
|
|
|
172
172
|
log.info(f"Enrolled as device_id={self.device_id}")
|
|
173
173
|
self._save_state()
|
|
174
174
|
|
|
175
|
-
async def heartbeat(self):
|
|
176
|
-
body = {
|
|
177
|
-
"status": "online",
|
|
178
|
-
"ip": self.tailscale_ip or self.lan_ip,
|
|
179
|
-
"device_inventory": get_device_inventory(),
|
|
180
|
-
}
|
|
175
|
+
async def heartbeat(self):
|
|
176
|
+
body = {
|
|
177
|
+
"status": "online",
|
|
178
|
+
"ip": self.tailscale_ip or self.lan_ip,
|
|
179
|
+
"device_inventory": get_device_inventory(),
|
|
180
|
+
}
|
|
181
181
|
async with httpx.AsyncClient(timeout=10.0) as c:
|
|
182
182
|
r = await c.post(
|
|
183
183
|
f"{self.scheduler_url}/api/devices/{self.device_id}/heartbeat",
|
|
@@ -233,10 +233,10 @@ class PiClient:
|
|
|
233
233
|
production_mode_provider=lambda: self.production_mode,
|
|
234
234
|
scheduler_url=self.scheduler_url,
|
|
235
235
|
device_id=self.device_id,
|
|
236
|
-
device_token=self.device_token,
|
|
237
|
-
video_device=self.video_device,
|
|
238
|
-
audio_device=self.audio_device,
|
|
239
|
-
audio_output_device=self.audio_output_device,
|
|
236
|
+
device_token=self.device_token,
|
|
237
|
+
video_device=self.video_device,
|
|
238
|
+
audio_device=self.audio_device,
|
|
239
|
+
audio_output_device=self.audio_output_device,
|
|
240
240
|
)
|
|
241
241
|
|
|
242
242
|
# ---- main loop ------------------------------------------------------
|
|
@@ -284,14 +284,14 @@ class PiClient:
|
|
|
284
284
|
if int(time.time()) % int(self.config_poll_interval) < int(self.heartbeat_interval):
|
|
285
285
|
cfg = await self.fetch_config()
|
|
286
286
|
self.production_mode = bool(cfg.get("production_mode", False))
|
|
287
|
-
if cfg.get("character_id"):
|
|
288
|
-
self.character_id = cfg["character_id"]
|
|
289
|
-
if cfg.get("video_device") is not None:
|
|
290
|
-
self.video_device = cfg["video_device"]
|
|
291
|
-
if cfg.get("audio_device") is not None:
|
|
292
|
-
self.audio_device = cfg["audio_device"]
|
|
293
|
-
if cfg.get("audio_output_device") is not None:
|
|
294
|
-
self.audio_output_device = cfg["audio_output_device"]
|
|
287
|
+
if cfg.get("character_id"):
|
|
288
|
+
self.character_id = cfg["character_id"]
|
|
289
|
+
if cfg.get("video_device") is not None:
|
|
290
|
+
self.video_device = cfg["video_device"]
|
|
291
|
+
if cfg.get("audio_device") is not None:
|
|
292
|
+
self.audio_device = cfg["audio_device"]
|
|
293
|
+
if cfg.get("audio_output_device") is not None:
|
|
294
|
+
self.audio_output_device = cfg["audio_output_device"]
|
|
295
295
|
except Exception as e:
|
|
296
296
|
log.debug(f"Config poll error: {e}")
|
|
297
297
|
|
|
@@ -323,11 +323,11 @@ def parse_args() -> argparse.Namespace:
|
|
|
323
323
|
help="Base URL of the local RoboVisionAI_PI motor server")
|
|
324
324
|
p.add_argument("--livekit-url", default=None, help="e.g. ws://100.64.1.5:7880")
|
|
325
325
|
p.add_argument("--character-id", default=None, help="Override character assignment")
|
|
326
|
-
p.add_argument("--room-name", default="robopark-pi-standby",
|
|
327
|
-
help="LiveKit room to join")
|
|
328
|
-
p.add_argument("--video-device", default="auto", help="Camera path/index, picamera, or auto")
|
|
329
|
-
p.add_argument("--audio-device", default="default", help="Microphone name/index or default")
|
|
330
|
-
p.add_argument("--audio-output-device", default="default", help="Speaker name/index or default")
|
|
326
|
+
p.add_argument("--room-name", default="robopark-pi-standby",
|
|
327
|
+
help="LiveKit room to join")
|
|
328
|
+
p.add_argument("--video-device", default="auto", help="Camera path/index, picamera, or auto")
|
|
329
|
+
p.add_argument("--audio-device", default="default", help="Microphone name/index or default")
|
|
330
|
+
p.add_argument("--audio-output-device", default="default", help="Speaker name/index or default")
|
|
331
331
|
p.add_argument("--heartbeat-interval", type=float, default=10.0)
|
|
332
332
|
p.add_argument("--config-poll-interval", type=float, default=30.0)
|
|
333
333
|
p.add_argument("--join-room", action="store_true",
|
|
@@ -361,4 +361,4 @@ def main():
|
|
|
361
361
|
|
|
362
362
|
|
|
363
363
|
if __name__ == "__main__":
|
|
364
|
-
main()
|
|
364
|
+
main()
|
package/pi-client/install.sh
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Install RoboPark Pi Client on a Raspberry Pi
|
|
3
|
-
# Usage:
|
|
4
|
-
# sudo ./install.sh --scheduler-url http://100.64.1.5:8080 \
|
|
5
|
-
# --enrollment-token <TOKEN>
|
|
6
|
-
set -euo pipefail
|
|
7
|
-
|
|
8
|
-
SCHEDULER_URL=""
|
|
9
|
-
ENROLLMENT_TOKEN=""
|
|
10
|
-
|
|
11
|
-
while [[ $# -gt 0 ]]; do
|
|
12
|
-
case "$1" in
|
|
13
|
-
--scheduler-url) SCHEDULER_URL="$2"; shift 2 ;;
|
|
14
|
-
--enrollment-token) ENROLLMENT_TOKEN="$2"; shift 2 ;;
|
|
15
|
-
*) echo "Unknown arg: $1"; exit 1 ;;
|
|
16
|
-
esac
|
|
17
|
-
done
|
|
18
|
-
|
|
19
|
-
if [[ -z "$SCHEDULER_URL" || -z "$ENROLLMENT_TOKEN" ]]; then
|
|
20
|
-
echo "Usage: $0 --scheduler-url URL --enrollment-token TOKEN"
|
|
21
|
-
exit 1
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
id "robopark" 2>/dev/null || useradd -r -s /bin/false robopark
|
|
25
|
-
install -d -m 755 -o robopark -g robopark /opt/robopark-pi-client
|
|
26
|
-
install -d -m 700 -o robopark -g robopark /etc/robopark
|
|
27
|
-
cp -r ./* /opt/robopark-pi-client/
|
|
28
|
-
chown -R robopark:robopark /opt/robopark-pi-client
|
|
29
|
-
|
|
30
|
-
sudo -u robopark python3 -m venv /opt/robopark-pi-client/.venv
|
|
31
|
-
/opt/robopark-pi-client/.venv/bin/pip install --upgrade pip
|
|
32
|
-
/opt/robopark-pi-client/.venv/bin/pip install -r /opt/robopark-pi-client/requirements.txt
|
|
33
|
-
# On Pi you typically want the full media stack:
|
|
34
|
-
/opt/robopark-pi-client/.venv/bin/pip install livekit livekit-api sounddevice numpy opencv-python av
|
|
35
|
-
|
|
36
|
-
cp /opt/robopark-pi-client/robopark-pi-client.service /etc/systemd/system/
|
|
37
|
-
systemctl daemon-reload
|
|
38
|
-
systemctl enable --now robopark-pi-client.service
|
|
39
|
-
|
|
40
|
-
echo "First-boot enrollment will run as part of the service start."
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install RoboPark Pi Client on a Raspberry Pi
|
|
3
|
+
# Usage:
|
|
4
|
+
# sudo ./install.sh --scheduler-url http://100.64.1.5:8080 \
|
|
5
|
+
# --enrollment-token <TOKEN>
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCHEDULER_URL=""
|
|
9
|
+
ENROLLMENT_TOKEN=""
|
|
10
|
+
|
|
11
|
+
while [[ $# -gt 0 ]]; do
|
|
12
|
+
case "$1" in
|
|
13
|
+
--scheduler-url) SCHEDULER_URL="$2"; shift 2 ;;
|
|
14
|
+
--enrollment-token) ENROLLMENT_TOKEN="$2"; shift 2 ;;
|
|
15
|
+
*) echo "Unknown arg: $1"; exit 1 ;;
|
|
16
|
+
esac
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
if [[ -z "$SCHEDULER_URL" || -z "$ENROLLMENT_TOKEN" ]]; then
|
|
20
|
+
echo "Usage: $0 --scheduler-url URL --enrollment-token TOKEN"
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
id "robopark" 2>/dev/null || useradd -r -s /bin/false robopark
|
|
25
|
+
install -d -m 755 -o robopark -g robopark /opt/robopark-pi-client
|
|
26
|
+
install -d -m 700 -o robopark -g robopark /etc/robopark
|
|
27
|
+
cp -r ./* /opt/robopark-pi-client/
|
|
28
|
+
chown -R robopark:robopark /opt/robopark-pi-client
|
|
29
|
+
|
|
30
|
+
sudo -u robopark python3 -m venv /opt/robopark-pi-client/.venv
|
|
31
|
+
/opt/robopark-pi-client/.venv/bin/pip install --upgrade pip
|
|
32
|
+
/opt/robopark-pi-client/.venv/bin/pip install -r /opt/robopark-pi-client/requirements.txt
|
|
33
|
+
# On Pi you typically want the full media stack:
|
|
34
|
+
/opt/robopark-pi-client/.venv/bin/pip install livekit livekit-api sounddevice numpy opencv-python av
|
|
35
|
+
|
|
36
|
+
cp /opt/robopark-pi-client/robopark-pi-client.service /etc/systemd/system/
|
|
37
|
+
systemctl daemon-reload
|
|
38
|
+
systemctl enable --now robopark-pi-client.service
|
|
39
|
+
|
|
40
|
+
echo "First-boot enrollment will run as part of the service start."
|
|
41
41
|
echo "Tail logs with: journalctl -u robopark-pi-client -f"
|
package/pi-client/join_convo.sh
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Join a LiveKit conversation from the Pi.
|
|
3
|
-
#
|
|
4
|
-
# Calls the local Pi UI's /api/join endpoint, which itself asks the scheduler
|
|
5
|
-
# for a short-lived LiveKit access token. The script prints the meet URL
|
|
6
|
-
# AND (if --open) opens it in the default browser on the Pi.
|
|
7
|
-
#
|
|
8
|
-
# Usage:
|
|
9
|
-
# ./join_convo.sh
|
|
10
|
-
# ./join_convo.sh --room robopark-pi-test --identity alice --open
|
|
11
|
-
# PI_UI=http://192.168.1.159:8081 ./join_convo.sh --open
|
|
12
|
-
|
|
13
|
-
set -euo pipefail
|
|
14
|
-
|
|
15
|
-
PI_UI="${PI_UI:-http://127.0.0.1:8081}"
|
|
16
|
-
ROOM="${ROOM:-robopark-pi-test}"
|
|
17
|
-
IDENTITY="${IDENTITY:-}"
|
|
18
|
-
OPEN=0
|
|
19
|
-
|
|
20
|
-
while [[ $# -gt 0 ]]; do
|
|
21
|
-
case "$1" in
|
|
22
|
-
--room) ROOM="$2"; shift 2 ;;
|
|
23
|
-
--identity) IDENTITY="$2"; shift 2 ;;
|
|
24
|
-
--open) OPEN=1; shift ;;
|
|
25
|
-
--pi-ui) PI_UI="$2"; shift 2 ;;
|
|
26
|
-
-h|--help)
|
|
27
|
-
sed -n '2,12p' "$0"; exit 0 ;;
|
|
28
|
-
*) echo "unknown arg: $1" >&2; exit 1 ;;
|
|
29
|
-
esac
|
|
30
|
-
done
|
|
31
|
-
|
|
32
|
-
BODY=$(jq -nc --arg room "$ROOM" --arg identity "$IDENTITY" \
|
|
33
|
-
'{room:$room, identity:$identity}')
|
|
34
|
-
|
|
35
|
-
echo "Requesting token from $PI_UI (room=$ROOM identity=$IDENTITY)..."
|
|
36
|
-
RESP=$(curl -fsS -X POST "$PI_UI/api/join" \
|
|
37
|
-
-H 'Content-Type: application/json' -d "$BODY")
|
|
38
|
-
|
|
39
|
-
URL=$(echo "$RESP" | jq -r '
|
|
40
|
-
"https://meet.livekit.io/custom?livekitUrl=" + (.url | @uri) +
|
|
41
|
-
"&token=" + (.token | @uri)
|
|
42
|
-
')
|
|
43
|
-
|
|
44
|
-
EXPIRES=$(echo "$RESP" | jq -r '.expires_at')
|
|
45
|
-
echo "Token expires: $EXPIRES"
|
|
46
|
-
echo "Meet URL:"
|
|
47
|
-
echo " $URL"
|
|
48
|
-
|
|
49
|
-
if [[ $OPEN -eq 1 ]]; then
|
|
50
|
-
if command -v xdg-open >/dev/null 2>&1; then xdg-open "$URL" >/dev/null 2>&1 || true
|
|
51
|
-
elif command -v sensible-browser >/dev/null 2>&1; then sensible-browser "$URL" >/dev/null 2>&1 || true
|
|
52
|
-
elif command -v open >/dev/null 2>&1; then open "$URL" >/dev/null 2>&1 || true
|
|
53
|
-
else echo "no browser opener found; copy the URL above" >&2; exit 2; fi
|
|
54
|
-
echo "(opened in browser)"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Join a LiveKit conversation from the Pi.
|
|
3
|
+
#
|
|
4
|
+
# Calls the local Pi UI's /api/join endpoint, which itself asks the scheduler
|
|
5
|
+
# for a short-lived LiveKit access token. The script prints the meet URL
|
|
6
|
+
# AND (if --open) opens it in the default browser on the Pi.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# ./join_convo.sh
|
|
10
|
+
# ./join_convo.sh --room robopark-pi-test --identity alice --open
|
|
11
|
+
# PI_UI=http://192.168.1.159:8081 ./join_convo.sh --open
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
PI_UI="${PI_UI:-http://127.0.0.1:8081}"
|
|
16
|
+
ROOM="${ROOM:-robopark-pi-test}"
|
|
17
|
+
IDENTITY="${IDENTITY:-}"
|
|
18
|
+
OPEN=0
|
|
19
|
+
|
|
20
|
+
while [[ $# -gt 0 ]]; do
|
|
21
|
+
case "$1" in
|
|
22
|
+
--room) ROOM="$2"; shift 2 ;;
|
|
23
|
+
--identity) IDENTITY="$2"; shift 2 ;;
|
|
24
|
+
--open) OPEN=1; shift ;;
|
|
25
|
+
--pi-ui) PI_UI="$2"; shift 2 ;;
|
|
26
|
+
-h|--help)
|
|
27
|
+
sed -n '2,12p' "$0"; exit 0 ;;
|
|
28
|
+
*) echo "unknown arg: $1" >&2; exit 1 ;;
|
|
29
|
+
esac
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
BODY=$(jq -nc --arg room "$ROOM" --arg identity "$IDENTITY" \
|
|
33
|
+
'{room:$room, identity:$identity}')
|
|
34
|
+
|
|
35
|
+
echo "Requesting token from $PI_UI (room=$ROOM identity=$IDENTITY)..."
|
|
36
|
+
RESP=$(curl -fsS -X POST "$PI_UI/api/join" \
|
|
37
|
+
-H 'Content-Type: application/json' -d "$BODY")
|
|
38
|
+
|
|
39
|
+
URL=$(echo "$RESP" | jq -r '
|
|
40
|
+
"https://meet.livekit.io/custom?livekitUrl=" + (.url | @uri) +
|
|
41
|
+
"&token=" + (.token | @uri)
|
|
42
|
+
')
|
|
43
|
+
|
|
44
|
+
EXPIRES=$(echo "$RESP" | jq -r '.expires_at')
|
|
45
|
+
echo "Token expires: $EXPIRES"
|
|
46
|
+
echo "Meet URL:"
|
|
47
|
+
echo " $URL"
|
|
48
|
+
|
|
49
|
+
if [[ $OPEN -eq 1 ]]; then
|
|
50
|
+
if command -v xdg-open >/dev/null 2>&1; then xdg-open "$URL" >/dev/null 2>&1 || true
|
|
51
|
+
elif command -v sensible-browser >/dev/null 2>&1; then sensible-browser "$URL" >/dev/null 2>&1 || true
|
|
52
|
+
elif command -v open >/dev/null 2>&1; then open "$URL" >/dev/null 2>&1 || true
|
|
53
|
+
else echo "no browser opener found; copy the URL above" >&2; exit 2; fi
|
|
54
|
+
echo "(opened in browser)"
|
|
55
55
|
fi
|
|
@@ -75,12 +75,12 @@ async def run_livekit(
|
|
|
75
75
|
on_motor_command: Callable[[str | bytes], Awaitable[str]],
|
|
76
76
|
stop_event: asyncio.Event,
|
|
77
77
|
production_mode_provider: Callable[[], bool],
|
|
78
|
-
scheduler_url: str = "",
|
|
79
|
-
device_id: str = "",
|
|
80
|
-
device_token: str = "",
|
|
81
|
-
video_device: str = "auto",
|
|
82
|
-
audio_device: str = "default",
|
|
83
|
-
audio_output_device: str = "default",
|
|
78
|
+
scheduler_url: str = "",
|
|
79
|
+
device_id: str = "",
|
|
80
|
+
device_token: str = "",
|
|
81
|
+
video_device: str = "auto",
|
|
82
|
+
audio_device: str = "default",
|
|
83
|
+
audio_output_device: str = "default",
|
|
84
84
|
):
|
|
85
85
|
"""Join a LiveKit room, publish mic + cam, subscribe to data channel.
|
|
86
86
|
|
|
@@ -143,8 +143,8 @@ async def run_livekit(
|
|
|
143
143
|
try:
|
|
144
144
|
stream = rtc.AudioStream(track=track)
|
|
145
145
|
# Open output stream at 48kHz mono (Pi speaker)
|
|
146
|
-
output = None if str(audio_output_device).lower() in ("", "default", "auto") else audio_output_device
|
|
147
|
-
out = sd.OutputStream(samplerate=OUT_RATE, channels=1, dtype="int16", blocksize=0, device=output)
|
|
146
|
+
output = None if str(audio_output_device).lower() in ("", "default", "auto") else audio_output_device
|
|
147
|
+
out = sd.OutputStream(samplerate=OUT_RATE, channels=1, dtype="int16", blocksize=0, device=output)
|
|
148
148
|
out.start()
|
|
149
149
|
_active_streams[sid] = out
|
|
150
150
|
|
|
@@ -207,16 +207,16 @@ async def run_livekit(
|
|
|
207
207
|
except Exception as e:
|
|
208
208
|
log.warning(f"sounddevice unavailable, skipping mic: {e}")
|
|
209
209
|
return
|
|
210
|
-
selected_mic = str(audio_device or "default")
|
|
211
|
-
MIC_DEVICE = None if selected_mic.lower() in ("", "default", "auto") else selected_mic
|
|
212
|
-
try:
|
|
213
|
-
if MIC_DEVICE is None:
|
|
214
|
-
log.info("mic: opening system default input")
|
|
215
|
-
else:
|
|
216
|
-
log.info(f"mic: opening device {MIC_DEVICE}")
|
|
217
|
-
except Exception:
|
|
218
|
-
log.warning(f"mic: device {MIC_DEVICE} not found, trying default")
|
|
219
|
-
MIC_DEVICE = None
|
|
210
|
+
selected_mic = str(audio_device or "default")
|
|
211
|
+
MIC_DEVICE = None if selected_mic.lower() in ("", "default", "auto") else selected_mic
|
|
212
|
+
try:
|
|
213
|
+
if MIC_DEVICE is None:
|
|
214
|
+
log.info("mic: opening system default input")
|
|
215
|
+
else:
|
|
216
|
+
log.info(f"mic: opening device {MIC_DEVICE}")
|
|
217
|
+
except Exception:
|
|
218
|
+
log.warning(f"mic: device {MIC_DEVICE} not found, trying default")
|
|
219
|
+
MIC_DEVICE = None
|
|
220
220
|
try:
|
|
221
221
|
source = rtc.AudioSource(sample_rate=16000, num_channels=1)
|
|
222
222
|
track = rtc.LocalAudioTrack.create_audio_track("mic", source)
|
|
@@ -248,11 +248,11 @@ async def run_livekit(
|
|
|
248
248
|
except Exception as e:
|
|
249
249
|
log.warning(f"opencv unavailable, skipping camera: {e}")
|
|
250
250
|
return
|
|
251
|
-
selected_camera = str(video_device or "auto")
|
|
252
|
-
camera_index = 0 if selected_camera.lower() in ("", "auto", "default", "first") else selected_camera
|
|
253
|
-
cap = cv2.VideoCapture(camera_index, cv2.CAP_V4L2)
|
|
254
|
-
if not cap.isOpened():
|
|
255
|
-
log.warning(f"camera {selected_camera} not available")
|
|
251
|
+
selected_camera = str(video_device or "auto")
|
|
252
|
+
camera_index = 0 if selected_camera.lower() in ("", "auto", "default", "first") else selected_camera
|
|
253
|
+
cap = cv2.VideoCapture(camera_index, cv2.CAP_V4L2)
|
|
254
|
+
if not cap.isOpened():
|
|
255
|
+
log.warning(f"camera {selected_camera} not available")
|
|
256
256
|
return
|
|
257
257
|
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
|
|
258
258
|
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
|
|
@@ -295,4 +295,4 @@ async def run_livekit(
|
|
|
295
295
|
except Exception: pass
|
|
296
296
|
try: await room.disconnect()
|
|
297
297
|
except Exception: pass
|
|
298
|
-
log.info("left room, will loop to reconnect")
|
|
298
|
+
log.info("left room, will loop to reconnect")
|
|
@@ -10,8 +10,8 @@ Command wire format (JSON on the data channel):
|
|
|
10
10
|
{"op": "stop"}
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
import asyncio
|
|
14
|
-
import os
|
|
13
|
+
import asyncio
|
|
14
|
+
import os
|
|
15
15
|
import json
|
|
16
16
|
import logging
|
|
17
17
|
from typing import Awaitable, Callable, Optional
|
|
@@ -21,12 +21,12 @@ import httpx
|
|
|
21
21
|
log = logging.getLogger("robopark-pi.motor")
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
class MotorBridge:
|
|
25
|
-
def __init__(self, motor_server_url: Optional[str], dry_run: bool = False):
|
|
24
|
+
class MotorBridge:
|
|
25
|
+
def __init__(self, motor_server_url: Optional[str], dry_run: bool = False):
|
|
26
26
|
self.motor_server_url = (motor_server_url or "").rstrip("/")
|
|
27
|
-
self.dry_run = dry_run
|
|
28
|
-
token = os.getenv("ROBOPARK_MOTOR_TOKEN", "").strip() or os.getenv("ROBOPARK_MESH_TOKEN", "").strip()
|
|
29
|
-
self.headers = {"X-RoboPark-Motor-Token": token} if token else {}
|
|
27
|
+
self.dry_run = dry_run
|
|
28
|
+
token = os.getenv("ROBOPARK_MOTOR_TOKEN", "").strip() or os.getenv("ROBOPARK_MESH_TOKEN", "").strip()
|
|
29
|
+
self.headers = {"X-RoboPark-Motor-Token": token} if token else {}
|
|
30
30
|
self._lock = asyncio.Lock() # the local motor server allows one motor at a time
|
|
31
31
|
|
|
32
32
|
async def handle_command(self, raw: str | bytes) -> str:
|
|
@@ -63,7 +63,7 @@ class MotorBridge:
|
|
|
63
63
|
async with httpx.AsyncClient(timeout=seconds + 5.0) as c:
|
|
64
64
|
r = await c.post(
|
|
65
65
|
f"{self.motor_server_url}/trigger-motor",
|
|
66
|
-
json={"motor_name": name, "seconds": seconds}, headers=self.headers,
|
|
66
|
+
json={"motor_name": name, "seconds": seconds}, headers=self.headers,
|
|
67
67
|
)
|
|
68
68
|
if r.status_code == 200:
|
|
69
69
|
return f"moved {name} for {seconds}s"
|
|
@@ -78,8 +78,8 @@ class MotorBridge:
|
|
|
78
78
|
return "dry-run: stop"
|
|
79
79
|
try:
|
|
80
80
|
async with httpx.AsyncClient(timeout=5.0) as c:
|
|
81
|
-
r = await c.post(f"{self.motor_server_url}/stop-motors", json={}, headers=self.headers)
|
|
81
|
+
r = await c.post(f"{self.motor_server_url}/stop-motors", json={}, headers=self.headers)
|
|
82
82
|
return f"stop {r.status_code}"
|
|
83
83
|
except Exception as e:
|
|
84
84
|
log.error(f"stop_all failed: {e}")
|
|
85
|
-
return f"error: {e}"
|
|
85
|
+
return f"error: {e}"
|
|
Binary file
|