|
5 | 5 | from fastapi import Depends, FastAPI, HTTPException |
6 | 6 | from pydantic import BaseModel |
7 | 7 |
|
| 8 | +from robot_orchestrator.audio import volume as audio_volume |
| 9 | +from robot_orchestrator.network import info as network_info |
8 | 10 | from robot_orchestrator.secrets import store as secrets_store |
9 | 11 | from robot_orchestrator.secrets.ingest import ingest_payload |
10 | 12 | from robot_orchestrator.secrets.protocol import SecretsPayload |
11 | 13 | from robot_orchestrator.state import store |
12 | 14 | from robot_orchestrator.supervisor.logsink import parse_line |
13 | 15 | from robot_orchestrator.supervisor.service import ServiceState |
14 | 16 | from robot_orchestrator.web import auth |
| 17 | +from robot_orchestrator.web import voice_control |
15 | 18 | from robot_orchestrator.web.context import AdminContext |
16 | 19 |
|
17 | 20 |
|
| 21 | +class WifiJoinRequest(BaseModel): |
| 22 | + ssid: str |
| 23 | + psk: str | None = None |
| 24 | + |
| 25 | + |
| 26 | +class VolumeRequest(BaseModel): |
| 27 | + percent: int | None = None |
| 28 | + muted: bool | None = None |
| 29 | + |
| 30 | + |
| 31 | +class WakeWordRequest(BaseModel): |
| 32 | + enabled: bool |
| 33 | + |
| 34 | + |
18 | 35 | class ChangePasswordRequest(BaseModel): |
19 | 36 | current_password: str |
20 | 37 | new_password: str |
@@ -248,6 +265,64 @@ async def connect_wifi(claims: dict = Depends(require_auth)): |
248 | 265 | connected = await asyncio.to_thread(c.wifi_manager.try_known_networks, known) |
249 | 266 | return {"connected_ssid": connected} |
250 | 267 |
|
| 268 | + @app.get("/api/network") |
| 269 | + async def get_network(claims: dict = Depends(require_auth)): |
| 270 | + return await asyncio.to_thread(network_info.network_info) |
| 271 | + |
| 272 | + @app.get("/api/wifi/networks") |
| 273 | + async def get_wifi_networks(rescan: bool = False, claims: dict = Depends(require_auth)): |
| 274 | + c = _require(context) |
| 275 | + if c.wifi_manager is None: |
| 276 | + raise HTTPException(status_code=503, detail="wifi manager is not available") |
| 277 | + if rescan: |
| 278 | + await asyncio.to_thread(c.wifi_manager.rescan) |
| 279 | + networks = await asyncio.to_thread(c.wifi_manager.list_networks) |
| 280 | + saved = await asyncio.to_thread(c.wifi_manager.saved_profiles) |
| 281 | + return {"networks": [{**n, "saved": n["ssid"] in saved} for n in networks]} |
| 282 | + |
| 283 | + @app.post("/api/wifi/join") |
| 284 | + async def join_wifi(body: WifiJoinRequest, claims: dict = Depends(require_auth)): |
| 285 | + c = _require(context) |
| 286 | + if c.wifi_manager is None: |
| 287 | + raise HTTPException(status_code=503, detail="wifi manager is not available") |
| 288 | + if body.psk: |
| 289 | + connected = await asyncio.to_thread(c.wifi_manager.connect, body.ssid, body.psk) |
| 290 | + else: |
| 291 | + connected = await asyncio.to_thread(c.wifi_manager.connect_saved, body.ssid) |
| 292 | + return {"connected": connected, "ssid": body.ssid} |
| 293 | + |
| 294 | + @app.get("/api/audio/volume") |
| 295 | + async def get_volume(claims: dict = Depends(require_auth)): |
| 296 | + state = await asyncio.to_thread(audio_volume.get_volume) |
| 297 | + if state is None: |
| 298 | + raise HTTPException(status_code=503, detail="audio mixer is not available") |
| 299 | + return state |
| 300 | + |
| 301 | + @app.post("/api/audio/volume") |
| 302 | + async def set_volume(body: VolumeRequest, claims: dict = Depends(require_auth)): |
| 303 | + state = await asyncio.to_thread(audio_volume.set_volume, body.percent, body.muted) |
| 304 | + if state is None: |
| 305 | + raise HTTPException(status_code=503, detail="audio mixer is not available") |
| 306 | + return state |
| 307 | + |
| 308 | + @app.get("/api/voice/wake-word") |
| 309 | + async def get_wake_word(claims: dict = Depends(require_auth)): |
| 310 | + c = _require(context) |
| 311 | + try: |
| 312 | + status = await asyncio.to_thread(voice_control.voice_status, c) |
| 313 | + except voice_control.VoiceControlError as e: |
| 314 | + raise HTTPException(status_code=e.status, detail=str(e)) |
| 315 | + return {"connected": bool(status.get("connected")), "enabled": status.get("wake_word_enabled")} |
| 316 | + |
| 317 | + @app.post("/api/voice/wake-word") |
| 318 | + async def set_wake_word(body: WakeWordRequest, claims: dict = Depends(require_auth)): |
| 319 | + c = _require(context) |
| 320 | + try: |
| 321 | + status = await asyncio.to_thread(voice_control.set_wake_word, c, body.enabled) |
| 322 | + except voice_control.VoiceControlError as e: |
| 323 | + raise HTTPException(status_code=e.status, detail=str(e)) |
| 324 | + return {"connected": bool(status.get("connected")), "enabled": status.get("wake_word_enabled")} |
| 325 | + |
251 | 326 | @app.post("/api/settings/password") |
252 | 327 | def change_password(body: ChangePasswordRequest, claims: dict = Depends(require_auth)): |
253 | 328 | c = _require(context) |
|
0 commit comments