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.
Files changed (408) hide show
  1. package/README.md +66 -105
  2. package/bin/robopark.js +9 -2
  3. package/conversation/elevenlabs_agent.py +1617 -1617
  4. package/conversation/supervisor_store.py +189 -189
  5. package/dist/{kernel/config-schema.js → config-schema.js} +1 -0
  6. package/dist/config-schema.js.map +1 -0
  7. package/dist/robopark/add-robot.js +1 -0
  8. package/dist/robopark/add-robot.js.map +1 -0
  9. package/dist/robopark/agent-ctl.js +1 -0
  10. package/dist/robopark/agent-ctl.js.map +1 -0
  11. package/dist/robopark/auto-start.js +51 -73
  12. package/dist/robopark/auto-start.js.map +1 -0
  13. package/dist/robopark/conversation.js +1 -0
  14. package/dist/robopark/conversation.js.map +1 -0
  15. package/dist/robopark/discovery.js +1 -0
  16. package/dist/robopark/discovery.js.map +1 -0
  17. package/dist/robopark/doctor.js +1 -0
  18. package/dist/robopark/doctor.js.map +1 -0
  19. package/dist/robopark/enroll.js +1 -0
  20. package/dist/robopark/enroll.js.map +1 -0
  21. package/dist/robopark/llm-set.js +1 -0
  22. package/dist/robopark/llm-set.js.map +1 -0
  23. package/dist/robopark/motor-control.js +1 -0
  24. package/dist/robopark/motor-control.js.map +1 -0
  25. package/dist/robopark/preview-agent-launcher.js +1 -0
  26. package/dist/robopark/preview-agent-launcher.js.map +1 -0
  27. package/dist/robopark/probe.js +16 -15
  28. package/dist/robopark/probe.js.map +1 -0
  29. package/dist/robopark/profile.js +1 -0
  30. package/dist/robopark/profile.js.map +1 -0
  31. package/dist/robopark/python-env.js +1 -7
  32. package/dist/robopark/python-env.js.map +1 -0
  33. package/dist/robopark/robot-runtime.js +1 -0
  34. package/dist/robopark/robot-runtime.js.map +1 -0
  35. package/dist/robopark/scan.js +1 -0
  36. package/dist/robopark/scan.js.map +1 -0
  37. package/dist/robopark/screen-control.js +1 -0
  38. package/dist/robopark/screen-control.js.map +1 -0
  39. package/dist/robopark/secrets.js +1 -0
  40. package/dist/robopark/secrets.js.map +1 -0
  41. package/dist/robopark/serve.js +6 -8
  42. package/dist/robopark/serve.js.map +1 -0
  43. package/dist/robopark/server-add.js +1 -0
  44. package/dist/robopark/server-add.js.map +1 -0
  45. package/dist/robopark/setup-livekit.js +53 -52
  46. package/dist/robopark/setup-livekit.js.map +1 -0
  47. package/dist/robopark/setup.js +1 -0
  48. package/dist/robopark/setup.js.map +1 -0
  49. package/dist/robopark/stop-all.js +1 -0
  50. package/dist/robopark/stop-all.js.map +1 -0
  51. package/dist/robopark/verify.js +1 -0
  52. package/dist/robopark/verify.js.map +1 -0
  53. package/dist/robopark/vision-agent-launcher.js +1 -0
  54. package/dist/robopark/vision-agent-launcher.js.map +1 -0
  55. package/dist/robopark/vision-control.js +1 -0
  56. package/dist/robopark/vision-control.js.map +1 -0
  57. package/dist/robopark-cli.js +7 -125
  58. package/dist/robopark-cli.js.map +1 -0
  59. package/package.json +16 -56
  60. package/pi-client/_install_steps.sh +29 -29
  61. package/pi-client/client.py +76 -76
  62. package/pi-client/install.sh +40 -40
  63. package/pi-client/join_convo.sh +54 -54
  64. package/pi-client/livekit_bridge.py +24 -24
  65. package/pi-client/motor_bridge.py +10 -10
  66. package/scheduler/__pycache__/main.cpython-312.pyc +0 -0
  67. package/scheduler/main.py +196 -17
  68. package/scheduler/media_lock.py +57 -57
  69. package/scheduler/preview_agent.py +1122 -1122
  70. package/scheduler/production_config.json +73 -113
  71. package/scheduler/scripts/install-robot-supervisor-linux.sh +33 -33
  72. package/{ui/standalone/public → scheduler/static}/voice-join.html +101 -13
  73. package/screen/screen_runtime.py +75 -75
  74. package/vision/app_pi_clean.py +297 -297
  75. package/vision/audio_server_pi.py +24 -24
  76. package/vision/install.sh +34 -34
  77. package/vision/motor_server.py +275 -275
  78. package/vision/requirements_camera.txt +6 -6
  79. package/vision/requirements_motor.txt +4 -4
  80. package/vision/requirements_pi_unified.txt +2 -2
  81. package/vision/requirements_vision_agent.txt +19 -19
  82. package/vision/run.sh +244 -244
  83. package/vision/services/services.sh +12 -12
  84. package/dist/kernel/types.js +0 -7
  85. package/dist/robopark/access.js +0 -99
  86. package/dist/robopark/control-center-html.js +0 -9191
  87. package/dist/robopark/control-server.js +0 -148
  88. package/dist/robopark/deployment-commands.js +0 -47
  89. package/dist/robopark/frontend-service.js +0 -80
  90. package/dist/robopark/standalone.js +0 -584
  91. package/dist/robopark/voice-join-html.js +0 -57
  92. package/scripts/windows-shim-fix.cjs +0 -58
  93. package/static/robopark/barracuda.png +0 -0
  94. package/static/robopark/bmw.png +0 -0
  95. package/static/robopark/jaguar.png +0 -0
  96. package/static/robopark/magnus.jpg +0 -0
  97. package/static/robopark/panda.png +0 -0
  98. package/static/robopark/tesla.png +0 -0
  99. package/static/robopark/titan.jpg +0 -0
  100. package/static/robopark/volt.png +0 -0
  101. package/static/vendor/elevenlabs-client.LICENSE +0 -21
  102. package/static/vendor/elevenlabs-client.iife.js +0 -24200
  103. package/ui/ROBOVOICE_BUILD.json +0 -9
  104. package/ui/standalone/.next/BUILD_ID +0 -1
  105. package/ui/standalone/.next/app-build-manifest.json +0 -391
  106. package/ui/standalone/.next/app-path-routes-manifest.json +0 -55
  107. package/ui/standalone/.next/build-manifest.json +0 -33
  108. package/ui/standalone/.next/package.json +0 -1
  109. package/ui/standalone/.next/prerender-manifest.json +0 -41
  110. package/ui/standalone/.next/react-loadable-manifest.json +0 -14
  111. package/ui/standalone/.next/required-server-files.json +0 -362
  112. package/ui/standalone/.next/routes-manifest.json +0 -246
  113. package/ui/standalone/.next/server/app/(app)/opengraph-image-xg4ifa/route.js +0 -1
  114. package/ui/standalone/.next/server/app/(app)/opengraph-image-xg4ifa/route.js.nft.json +0 -1
  115. package/ui/standalone/.next/server/app/(app)/opengraph-image-xg4ifa/route_client-reference-manifest.js +0 -1
  116. package/ui/standalone/.next/server/app/(app)/page.js +0 -2
  117. package/ui/standalone/.next/server/app/(app)/page.js.nft.json +0 -1
  118. package/ui/standalone/.next/server/app/(app)/page_client-reference-manifest.js +0 -1
  119. package/ui/standalone/.next/server/app/(app)/voice/page.js +0 -12
  120. package/ui/standalone/.next/server/app/(app)/voice/page.js.nft.json +0 -1
  121. package/ui/standalone/.next/server/app/(app)/voice/page_client-reference-manifest.js +0 -1
  122. package/ui/standalone/.next/server/app/_not-found/page.js +0 -12
  123. package/ui/standalone/.next/server/app/_not-found/page.js.nft.json +0 -1
  124. package/ui/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  125. package/ui/standalone/.next/server/app/api/connection-details/route.js +0 -1
  126. package/ui/standalone/.next/server/app/api/connection-details/route.js.nft.json +0 -1
  127. package/ui/standalone/.next/server/app/api/connection-details/route_client-reference-manifest.js +0 -1
  128. package/ui/standalone/.next/server/app/api/device-character/route.js +0 -1
  129. package/ui/standalone/.next/server/app/api/device-character/route.js.nft.json +0 -1
  130. package/ui/standalone/.next/server/app/api/device-character/route_client-reference-manifest.js +0 -1
  131. package/ui/standalone/.next/server/app/api/download-piper-model/route.js +0 -1
  132. package/ui/standalone/.next/server/app/api/download-piper-model/route.js.nft.json +0 -1
  133. package/ui/standalone/.next/server/app/api/download-piper-model/route_client-reference-manifest.js +0 -1
  134. package/ui/standalone/.next/server/app/api/model-status/route.js +0 -1
  135. package/ui/standalone/.next/server/app/api/model-status/route.js.nft.json +0 -1
  136. package/ui/standalone/.next/server/app/api/model-status/route_client-reference-manifest.js +0 -1
  137. package/ui/standalone/.next/server/app/api/models/route.js +0 -1
  138. package/ui/standalone/.next/server/app/api/models/route.js.nft.json +0 -1
  139. package/ui/standalone/.next/server/app/api/models/route_client-reference-manifest.js +0 -1
  140. package/ui/standalone/.next/server/app/api/prewarm/route.js +0 -1
  141. package/ui/standalone/.next/server/app/api/prewarm/route.js.nft.json +0 -1
  142. package/ui/standalone/.next/server/app/api/prewarm/route_client-reference-manifest.js +0 -1
  143. package/ui/standalone/.next/server/app/api/prompt/route.js +0 -1
  144. package/ui/standalone/.next/server/app/api/prompt/route.js.nft.json +0 -1
  145. package/ui/standalone/.next/server/app/api/prompt/route_client-reference-manifest.js +0 -1
  146. package/ui/standalone/.next/server/app/api/reload-tools/route.js +0 -1
  147. package/ui/standalone/.next/server/app/api/reload-tools/route.js.nft.json +0 -1
  148. package/ui/standalone/.next/server/app/api/reload-tools/route_client-reference-manifest.js +0 -1
  149. package/ui/standalone/.next/server/app/api/robopark/camera-token/route.js +0 -1
  150. package/ui/standalone/.next/server/app/api/robopark/camera-token/route.js.nft.json +0 -1
  151. package/ui/standalone/.next/server/app/api/robopark/camera-token/route_client-reference-manifest.js +0 -1
  152. package/ui/standalone/.next/server/app/api/robopark/character-ai/route.js +0 -1
  153. package/ui/standalone/.next/server/app/api/robopark/character-ai/route.js.nft.json +0 -1
  154. package/ui/standalone/.next/server/app/api/robopark/character-ai/route_client-reference-manifest.js +0 -1
  155. package/ui/standalone/.next/server/app/api/robopark/device-access/route.js +0 -1
  156. package/ui/standalone/.next/server/app/api/robopark/device-access/route.js.nft.json +0 -1
  157. package/ui/standalone/.next/server/app/api/robopark/device-access/route_client-reference-manifest.js +0 -1
  158. package/ui/standalone/.next/server/app/api/robopark/devices/[deviceId]/control/route.js +0 -1
  159. package/ui/standalone/.next/server/app/api/robopark/devices/[deviceId]/control/route.js.nft.json +0 -1
  160. package/ui/standalone/.next/server/app/api/robopark/devices/[deviceId]/control/route_client-reference-manifest.js +0 -1
  161. package/ui/standalone/.next/server/app/api/robopark/live-rooms/route.js +0 -1
  162. package/ui/standalone/.next/server/app/api/robopark/live-rooms/route.js.nft.json +0 -1
  163. package/ui/standalone/.next/server/app/api/robopark/live-rooms/route_client-reference-manifest.js +0 -1
  164. package/ui/standalone/.next/server/app/api/robopark/monitor-token/route.js +0 -1
  165. package/ui/standalone/.next/server/app/api/robopark/monitor-token/route.js.nft.json +0 -1
  166. package/ui/standalone/.next/server/app/api/robopark/monitor-token/route_client-reference-manifest.js +0 -1
  167. package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/operations/route.js +0 -1
  168. package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/operations/route.js.nft.json +0 -1
  169. package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/operations/route_client-reference-manifest.js +0 -1
  170. package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/voice-engine/route.js +0 -1
  171. package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/voice-engine/route.js.nft.json +0 -1
  172. package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/voice-engine/route_client-reference-manifest.js +0 -1
  173. package/ui/standalone/.next/server/app/api/robopark/robots/route.js +0 -1
  174. package/ui/standalone/.next/server/app/api/robopark/robots/route.js.nft.json +0 -1
  175. package/ui/standalone/.next/server/app/api/robopark/robots/route_client-reference-manifest.js +0 -1
  176. package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/metrics/route.js +0 -1
  177. package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/metrics/route.js.nft.json +0 -1
  178. package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/metrics/route_client-reference-manifest.js +0 -1
  179. package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/route.js +0 -1
  180. package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/route.js.nft.json +0 -1
  181. package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/route_client-reference-manifest.js +0 -1
  182. package/ui/standalone/.next/server/app/api/robopark/servers/route.js +0 -1
  183. package/ui/standalone/.next/server/app/api/robopark/servers/route.js.nft.json +0 -1
  184. package/ui/standalone/.next/server/app/api/robopark/servers/route_client-reference-manifest.js +0 -1
  185. package/ui/standalone/.next/server/app/api/robopark/sessions/[sessionId]/end/route.js +0 -1
  186. package/ui/standalone/.next/server/app/api/robopark/sessions/[sessionId]/end/route.js.nft.json +0 -1
  187. package/ui/standalone/.next/server/app/api/robopark/sessions/[sessionId]/end/route_client-reference-manifest.js +0 -1
  188. package/ui/standalone/.next/server/app/api/robopark/sessions/route.js +0 -1
  189. package/ui/standalone/.next/server/app/api/robopark/sessions/route.js.nft.json +0 -1
  190. package/ui/standalone/.next/server/app/api/robopark/sessions/route_client-reference-manifest.js +0 -1
  191. package/ui/standalone/.next/server/app/api/robopark/transcripts/[sessionId]/route.js +0 -1
  192. package/ui/standalone/.next/server/app/api/robopark/transcripts/[sessionId]/route.js.nft.json +0 -1
  193. package/ui/standalone/.next/server/app/api/robopark/transcripts/[sessionId]/route_client-reference-manifest.js +0 -1
  194. package/ui/standalone/.next/server/app/api/robopark/transcripts/route.js +0 -1
  195. package/ui/standalone/.next/server/app/api/robopark/transcripts/route.js.nft.json +0 -1
  196. package/ui/standalone/.next/server/app/api/robopark/transcripts/route_client-reference-manifest.js +0 -1
  197. package/ui/standalone/.next/server/app/api/robovoice/robots/[robotId]/media-health/route.js +0 -1
  198. package/ui/standalone/.next/server/app/api/robovoice/robots/[robotId]/media-health/route.js.nft.json +0 -1
  199. package/ui/standalone/.next/server/app/api/robovoice/robots/[robotId]/media-health/route_client-reference-manifest.js +0 -1
  200. package/ui/standalone/.next/server/app/api/settings/route.js +0 -1
  201. package/ui/standalone/.next/server/app/api/settings/route.js.nft.json +0 -1
  202. package/ui/standalone/.next/server/app/api/settings/route_client-reference-manifest.js +0 -1
  203. package/ui/standalone/.next/server/app/api/setup/complete/route.js +0 -1
  204. package/ui/standalone/.next/server/app/api/setup/complete/route.js.nft.json +0 -1
  205. package/ui/standalone/.next/server/app/api/setup/complete/route_client-reference-manifest.js +0 -1
  206. package/ui/standalone/.next/server/app/api/setup/status/route.js +0 -1
  207. package/ui/standalone/.next/server/app/api/setup/status/route.js.nft.json +0 -1
  208. package/ui/standalone/.next/server/app/api/setup/status/route_client-reference-manifest.js +0 -1
  209. package/ui/standalone/.next/server/app/api/setup/test-groq/route.js +0 -1
  210. package/ui/standalone/.next/server/app/api/setup/test-groq/route.js.nft.json +0 -1
  211. package/ui/standalone/.next/server/app/api/setup/test-groq/route_client-reference-manifest.js +0 -1
  212. package/ui/standalone/.next/server/app/api/setup/test-hass/route.js +0 -1
  213. package/ui/standalone/.next/server/app/api/setup/test-hass/route.js.nft.json +0 -1
  214. package/ui/standalone/.next/server/app/api/setup/test-hass/route_client-reference-manifest.js +0 -1
  215. package/ui/standalone/.next/server/app/api/setup/test-n8n/route.js +0 -1
  216. package/ui/standalone/.next/server/app/api/setup/test-n8n/route.js.nft.json +0 -1
  217. package/ui/standalone/.next/server/app/api/setup/test-n8n/route_client-reference-manifest.js +0 -1
  218. package/ui/standalone/.next/server/app/api/setup/test-ollama/route.js +0 -1
  219. package/ui/standalone/.next/server/app/api/setup/test-ollama/route.js.nft.json +0 -1
  220. package/ui/standalone/.next/server/app/api/setup/test-ollama/route_client-reference-manifest.js +0 -1
  221. package/ui/standalone/.next/server/app/api/test-tts/route.js +0 -1
  222. package/ui/standalone/.next/server/app/api/test-tts/route.js.nft.json +0 -1
  223. package/ui/standalone/.next/server/app/api/test-tts/route_client-reference-manifest.js +0 -1
  224. package/ui/standalone/.next/server/app/api/turn-credentials/route.js +0 -1
  225. package/ui/standalone/.next/server/app/api/turn-credentials/route.js.nft.json +0 -1
  226. package/ui/standalone/.next/server/app/api/turn-credentials/route_client-reference-manifest.js +0 -1
  227. package/ui/standalone/.next/server/app/api/voices/route.js +0 -1
  228. package/ui/standalone/.next/server/app/api/voices/route.js.nft.json +0 -1
  229. package/ui/standalone/.next/server/app/api/voices/route_client-reference-manifest.js +0 -1
  230. package/ui/standalone/.next/server/app/api/wake/route.js +0 -1
  231. package/ui/standalone/.next/server/app/api/wake/route.js.nft.json +0 -1
  232. package/ui/standalone/.next/server/app/api/wake/route_client-reference-manifest.js +0 -1
  233. package/ui/standalone/.next/server/app/api/wake-word/models/route.js +0 -1
  234. package/ui/standalone/.next/server/app/api/wake-word/models/route.js.nft.json +0 -1
  235. package/ui/standalone/.next/server/app/api/wake-word/models/route_client-reference-manifest.js +0 -1
  236. package/ui/standalone/.next/server/app/api/wake-word/status/route.js +0 -1
  237. package/ui/standalone/.next/server/app/api/wake-word/status/route.js.nft.json +0 -1
  238. package/ui/standalone/.next/server/app/api/wake-word/status/route_client-reference-manifest.js +0 -1
  239. package/ui/standalone/.next/server/app/api/wake-word/upload/route.js +0 -1
  240. package/ui/standalone/.next/server/app/api/wake-word/upload/route.js.nft.json +0 -1
  241. package/ui/standalone/.next/server/app/api/wake-word/upload/route_client-reference-manifest.js +0 -1
  242. package/ui/standalone/.next/server/app/favicon.ico/route.js +0 -1
  243. package/ui/standalone/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  244. package/ui/standalone/.next/server/app/favicon.ico.body +0 -0
  245. package/ui/standalone/.next/server/app/favicon.ico.meta +0 -1
  246. package/ui/standalone/.next/server/app/fed/[...path]/route.js +0 -4
  247. package/ui/standalone/.next/server/app/fed/[...path]/route.js.nft.json +0 -1
  248. package/ui/standalone/.next/server/app/fed/[...path]/route_client-reference-manifest.js +0 -1
  249. package/ui/standalone/.next/server/app/fed/media/robots/[robotId]/video_feed/route.js +0 -1
  250. package/ui/standalone/.next/server/app/fed/media/robots/[robotId]/video_feed/route.js.nft.json +0 -1
  251. package/ui/standalone/.next/server/app/fed/media/robots/[robotId]/video_feed/route_client-reference-manifest.js +0 -1
  252. package/ui/standalone/.next/server/app/robopark/[...path]/route.js +0 -1
  253. package/ui/standalone/.next/server/app/robopark/[...path]/route.js.nft.json +0 -1
  254. package/ui/standalone/.next/server/app/robopark/[...path]/route_client-reference-manifest.js +0 -1
  255. package/ui/standalone/.next/server/app/robopark/api/[...path]/route.js +0 -1
  256. package/ui/standalone/.next/server/app/robopark/api/[...path]/route.js.nft.json +0 -1
  257. package/ui/standalone/.next/server/app/robopark/api/[...path]/route_client-reference-manifest.js +0 -1
  258. package/ui/standalone/.next/server/app/robopark/api/robots/[robotId]/stream/route.js +0 -1
  259. package/ui/standalone/.next/server/app/robopark/api/robots/[robotId]/stream/route.js.nft.json +0 -1
  260. package/ui/standalone/.next/server/app/robopark/api/robots/[robotId]/stream/route_client-reference-manifest.js +0 -1
  261. package/ui/standalone/.next/server/app/robopark/page.js +0 -2
  262. package/ui/standalone/.next/server/app/robopark/page.js.nft.json +0 -1
  263. package/ui/standalone/.next/server/app/robopark/page_client-reference-manifest.js +0 -1
  264. package/ui/standalone/.next/server/app/tailnet/route.js +0 -1
  265. package/ui/standalone/.next/server/app/tailnet/route.js.nft.json +0 -1
  266. package/ui/standalone/.next/server/app/tailnet/route_client-reference-manifest.js +0 -1
  267. package/ui/standalone/.next/server/app/vendor/livekit-client.umd.js/route.js +0 -1
  268. package/ui/standalone/.next/server/app/vendor/livekit-client.umd.js/route.js.nft.json +0 -1
  269. package/ui/standalone/.next/server/app/vendor/livekit-client.umd.js/route_client-reference-manifest.js +0 -1
  270. package/ui/standalone/.next/server/app/voice-tester/page.js +0 -2
  271. package/ui/standalone/.next/server/app/voice-tester/page.js.nft.json +0 -1
  272. package/ui/standalone/.next/server/app/voice-tester/page_client-reference-manifest.js +0 -1
  273. package/ui/standalone/.next/server/app-paths-manifest.json +0 -55
  274. package/ui/standalone/.next/server/chunks/1428.js +0 -1
  275. package/ui/standalone/.next/server/chunks/1692.js +0 -1
  276. package/ui/standalone/.next/server/chunks/37.js +0 -1
  277. package/ui/standalone/.next/server/chunks/4876.js +0 -1
  278. package/ui/standalone/.next/server/chunks/5232.js +0 -9
  279. package/ui/standalone/.next/server/chunks/5455.js +0 -11
  280. package/ui/standalone/.next/server/chunks/5611.js +0 -6
  281. package/ui/standalone/.next/server/chunks/5873.js +0 -22
  282. package/ui/standalone/.next/server/chunks/6018.js +0 -1
  283. package/ui/standalone/.next/server/chunks/8755.js +0 -1
  284. package/ui/standalone/.next/server/chunks/9077.js +0 -11
  285. package/ui/standalone/.next/server/functions-config-manifest.json +0 -9
  286. package/ui/standalone/.next/server/middleware-build-manifest.js +0 -1
  287. package/ui/standalone/.next/server/middleware-manifest.json +0 -6
  288. package/ui/standalone/.next/server/middleware-react-loadable-manifest.js +0 -1
  289. package/ui/standalone/.next/server/next-font-manifest.js +0 -1
  290. package/ui/standalone/.next/server/next-font-manifest.json +0 -1
  291. package/ui/standalone/.next/server/pages/500.html +0 -1
  292. package/ui/standalone/.next/server/pages/_app.js +0 -1
  293. package/ui/standalone/.next/server/pages/_app.js.nft.json +0 -1
  294. package/ui/standalone/.next/server/pages/_document.js +0 -1
  295. package/ui/standalone/.next/server/pages/_document.js.nft.json +0 -1
  296. package/ui/standalone/.next/server/pages/_error.js +0 -19
  297. package/ui/standalone/.next/server/pages/_error.js.nft.json +0 -1
  298. package/ui/standalone/.next/server/pages-manifest.json +0 -5
  299. package/ui/standalone/.next/server/server-reference-manifest.js +0 -1
  300. package/ui/standalone/.next/server/server-reference-manifest.json +0 -1
  301. package/ui/standalone/.next/server/webpack-runtime.js +0 -1
  302. package/ui/standalone/.next/static/T3eGaQUuZNb6rhEStcouc/_buildManifest.js +0 -1
  303. package/ui/standalone/.next/static/T3eGaQUuZNb6rhEStcouc/_ssgManifest.js +0 -1
  304. package/ui/standalone/.next/static/chunks/1255-404bb8de49c90395.js +0 -1
  305. package/ui/standalone/.next/static/chunks/144d3bae-bd5d150c0cab972e.js +0 -1
  306. package/ui/standalone/.next/static/chunks/1646.9123ee47220ed70b.js +0 -1
  307. package/ui/standalone/.next/static/chunks/2333-43c39acb531f7d4e.js +0 -3
  308. package/ui/standalone/.next/static/chunks/4bd1b696-f785427dddbba9fb.js +0 -1
  309. package/ui/standalone/.next/static/chunks/5139.c5e46d26064a85db.js +0 -1
  310. package/ui/standalone/.next/static/chunks/76e8f37e-2ae0e3fe7b44d874.js +0 -1
  311. package/ui/standalone/.next/static/chunks/9880-53262785284464cc.js +0 -1
  312. package/ui/standalone/.next/static/chunks/adfb71a7-625c1b5ebd039b6a.js +0 -1
  313. package/ui/standalone/.next/static/chunks/app/(app)/layout-5f312b128a0caf75.js +0 -1
  314. package/ui/standalone/.next/static/chunks/app/(app)/opengraph-image-xg4ifa/route-5f312b128a0caf75.js +0 -1
  315. package/ui/standalone/.next/static/chunks/app/(app)/page-5f312b128a0caf75.js +0 -1
  316. package/ui/standalone/.next/static/chunks/app/(app)/voice/page-72d72d9a70d9c1d4.js +0 -1
  317. package/ui/standalone/.next/static/chunks/app/_not-found/page-cfd7daa82d71540c.js +0 -1
  318. package/ui/standalone/.next/static/chunks/app/api/connection-details/route-5f312b128a0caf75.js +0 -1
  319. package/ui/standalone/.next/static/chunks/app/api/device-character/route-5f312b128a0caf75.js +0 -1
  320. package/ui/standalone/.next/static/chunks/app/api/download-piper-model/route-5f312b128a0caf75.js +0 -1
  321. package/ui/standalone/.next/static/chunks/app/api/model-status/route-5f312b128a0caf75.js +0 -1
  322. package/ui/standalone/.next/static/chunks/app/api/models/route-5f312b128a0caf75.js +0 -1
  323. package/ui/standalone/.next/static/chunks/app/api/prewarm/route-5f312b128a0caf75.js +0 -1
  324. package/ui/standalone/.next/static/chunks/app/api/prompt/route-5f312b128a0caf75.js +0 -1
  325. package/ui/standalone/.next/static/chunks/app/api/reload-tools/route-5f312b128a0caf75.js +0 -1
  326. package/ui/standalone/.next/static/chunks/app/api/robopark/camera-token/route-5f312b128a0caf75.js +0 -1
  327. package/ui/standalone/.next/static/chunks/app/api/robopark/character-ai/route-5f312b128a0caf75.js +0 -1
  328. package/ui/standalone/.next/static/chunks/app/api/robopark/device-access/route-5f312b128a0caf75.js +0 -1
  329. package/ui/standalone/.next/static/chunks/app/api/robopark/devices/[deviceId]/control/route-5f312b128a0caf75.js +0 -1
  330. package/ui/standalone/.next/static/chunks/app/api/robopark/live-rooms/route-5f312b128a0caf75.js +0 -1
  331. package/ui/standalone/.next/static/chunks/app/api/robopark/monitor-token/route-5f312b128a0caf75.js +0 -1
  332. package/ui/standalone/.next/static/chunks/app/api/robopark/robots/[robotId]/operations/route-5f312b128a0caf75.js +0 -1
  333. package/ui/standalone/.next/static/chunks/app/api/robopark/robots/[robotId]/voice-engine/route-5f312b128a0caf75.js +0 -1
  334. package/ui/standalone/.next/static/chunks/app/api/robopark/robots/route-5f312b128a0caf75.js +0 -1
  335. package/ui/standalone/.next/static/chunks/app/api/robopark/servers/[serverId]/metrics/route-5f312b128a0caf75.js +0 -1
  336. package/ui/standalone/.next/static/chunks/app/api/robopark/servers/[serverId]/route-5f312b128a0caf75.js +0 -1
  337. package/ui/standalone/.next/static/chunks/app/api/robopark/servers/route-5f312b128a0caf75.js +0 -1
  338. package/ui/standalone/.next/static/chunks/app/api/robopark/sessions/[sessionId]/end/route-5f312b128a0caf75.js +0 -1
  339. package/ui/standalone/.next/static/chunks/app/api/robopark/sessions/route-5f312b128a0caf75.js +0 -1
  340. package/ui/standalone/.next/static/chunks/app/api/robopark/transcripts/[sessionId]/route-5f312b128a0caf75.js +0 -1
  341. package/ui/standalone/.next/static/chunks/app/api/robopark/transcripts/route-5f312b128a0caf75.js +0 -1
  342. package/ui/standalone/.next/static/chunks/app/api/robovoice/robots/[robotId]/media-health/route-5f312b128a0caf75.js +0 -1
  343. package/ui/standalone/.next/static/chunks/app/api/settings/route-5f312b128a0caf75.js +0 -1
  344. package/ui/standalone/.next/static/chunks/app/api/setup/complete/route-5f312b128a0caf75.js +0 -1
  345. package/ui/standalone/.next/static/chunks/app/api/setup/status/route-5f312b128a0caf75.js +0 -1
  346. package/ui/standalone/.next/static/chunks/app/api/setup/test-groq/route-5f312b128a0caf75.js +0 -1
  347. package/ui/standalone/.next/static/chunks/app/api/setup/test-hass/route-5f312b128a0caf75.js +0 -1
  348. package/ui/standalone/.next/static/chunks/app/api/setup/test-n8n/route-5f312b128a0caf75.js +0 -1
  349. package/ui/standalone/.next/static/chunks/app/api/setup/test-ollama/route-5f312b128a0caf75.js +0 -1
  350. package/ui/standalone/.next/static/chunks/app/api/test-tts/route-5f312b128a0caf75.js +0 -1
  351. package/ui/standalone/.next/static/chunks/app/api/turn-credentials/route-5f312b128a0caf75.js +0 -1
  352. package/ui/standalone/.next/static/chunks/app/api/voices/route-5f312b128a0caf75.js +0 -1
  353. package/ui/standalone/.next/static/chunks/app/api/wake/route-5f312b128a0caf75.js +0 -1
  354. package/ui/standalone/.next/static/chunks/app/api/wake-word/models/route-5f312b128a0caf75.js +0 -1
  355. package/ui/standalone/.next/static/chunks/app/api/wake-word/status/route-5f312b128a0caf75.js +0 -1
  356. package/ui/standalone/.next/static/chunks/app/api/wake-word/upload/route-5f312b128a0caf75.js +0 -1
  357. package/ui/standalone/.next/static/chunks/app/fed/[...path]/route-5f312b128a0caf75.js +0 -1
  358. package/ui/standalone/.next/static/chunks/app/fed/media/robots/[robotId]/video_feed/route-5f312b128a0caf75.js +0 -1
  359. package/ui/standalone/.next/static/chunks/app/layout-fcce963da1d8220f.js +0 -1
  360. package/ui/standalone/.next/static/chunks/app/robopark/[...path]/route-5f312b128a0caf75.js +0 -1
  361. package/ui/standalone/.next/static/chunks/app/robopark/api/[...path]/route-5f312b128a0caf75.js +0 -1
  362. package/ui/standalone/.next/static/chunks/app/robopark/api/robots/[robotId]/stream/route-5f312b128a0caf75.js +0 -1
  363. package/ui/standalone/.next/static/chunks/app/robopark/page-5f312b128a0caf75.js +0 -1
  364. package/ui/standalone/.next/static/chunks/app/tailnet/route-5f312b128a0caf75.js +0 -1
  365. package/ui/standalone/.next/static/chunks/app/vendor/livekit-client.umd.js/route-5f312b128a0caf75.js +0 -1
  366. package/ui/standalone/.next/static/chunks/app/voice-tester/page-ae699e048e0eb834.js +0 -1
  367. package/ui/standalone/.next/static/chunks/framework-e60c938074ff7136.js +0 -1
  368. package/ui/standalone/.next/static/chunks/main-9e24e8708ff075fc.js +0 -1
  369. package/ui/standalone/.next/static/chunks/main-app-a1f59168303c7f84.js +0 -1
  370. package/ui/standalone/.next/static/chunks/pages/_app-131c90850aef965b.js +0 -1
  371. package/ui/standalone/.next/static/chunks/pages/_error-e4ba546eb376bdf4.js +0 -1
  372. package/ui/standalone/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  373. package/ui/standalone/.next/static/chunks/webpack-5252e5254da079b5.js +0 -1
  374. package/ui/standalone/.next/static/css/3f478a573f9be82e.css +0 -1
  375. package/ui/standalone/.next/static/css/51560c4af1d5a21f.css +0 -1
  376. package/ui/standalone/.next/static/media/27fe93087559d480-s.p.otf +0 -0
  377. package/ui/standalone/.next/static/media/4d3869dd8a56345a-s.p.otf +0 -0
  378. package/ui/standalone/.next/static/media/63f3bb9ce29e22d5-s.p.otf +0 -0
  379. package/ui/standalone/.next/static/media/79e1c5d0f2fe9f68-s.p.otf +0 -0
  380. package/ui/standalone/.next/static/media/8bf7b2ceda89477b-s.p.woff2 +0 -0
  381. package/ui/standalone/.next/static/media/98baa4a7809fbf72-s.woff2 +0 -0
  382. package/ui/standalone/.next/static/media/f7650dfaa438c37c-s.woff2 +0 -0
  383. package/ui/standalone/package.json +0 -57
  384. package/ui/standalone/public/caal-logo.svg +0 -3
  385. package/ui/standalone/public/commit-mono-400-regular.woff +0 -0
  386. package/ui/standalone/public/cwl-logo-round.png +0 -0
  387. package/ui/standalone/public/everett-light.woff +0 -0
  388. package/ui/standalone/public/hey_cal.ppn +0 -0
  389. package/ui/standalone/public/install-robot-kiosk.sh +0 -242
  390. package/ui/standalone/public/lk-logo-dark.svg +0 -18
  391. package/ui/standalone/public/lk-logo.svg +0 -18
  392. package/ui/standalone/public/lk-wordmark.svg +0 -12
  393. package/ui/standalone/public/opengraph-image-bg.png +0 -0
  394. package/ui/standalone/public/porcupine_params.pv +0 -0
  395. package/ui/standalone/public/robopark-control.html +0 -9200
  396. package/ui/standalone/public/static/robopark/barracuda.png +0 -0
  397. package/ui/standalone/public/static/robopark/bmw.png +0 -0
  398. package/ui/standalone/public/static/robopark/jaguar.png +0 -0
  399. package/ui/standalone/public/static/robopark/magnus.jpg +0 -0
  400. package/ui/standalone/public/static/robopark/panda.png +0 -0
  401. package/ui/standalone/public/static/robopark/tesla.png +0 -0
  402. package/ui/standalone/public/static/robopark/titan.jpg +0 -0
  403. package/ui/standalone/public/static/robopark/volt.png +0 -0
  404. package/ui/standalone/public/tailnet/index.html +0 -8995
  405. package/ui/standalone/public/transcript-analytics.html +0 -72
  406. package/ui/standalone/public/vendor/livekit-client.umd.js +0 -2
  407. package/ui/standalone/server.js +0 -38
  408. /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"
@@ -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()
@@ -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"
@@ -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}"