Skip to content

feat(teleop): replace Deno bridge with embedded FastAPI WebSocket server#1385

Merged
ruthwikdasyam merged 15 commits intodevfrom
ruthwik_teleop_fastapi
Mar 1, 2026
Merged

feat(teleop): replace Deno bridge with embedded FastAPI WebSocket server#1385
ruthwikdasyam merged 15 commits intodevfrom
ruthwik_teleop_fastapi

Conversation

@ruthwikdasyam
Copy link
Contributor

@ruthwikdasyam ruthwikdasyam commented Feb 28, 2026

Problem

TypeScript/Deno server collects raw pose/button data from Quest/Phone browser, encodes as full LCM packets (with topic headers), publishes directly to LCM UDP and Python DimOS modules subscribe to LCM topics, transform data, publish outputs
This meant a hard dependency on LCM as the transport between TS and Python, blueprints couldn't understand the topics used, and the Deno runtime was an external dependency

#1222
Closes DIM-536
Closes DIM-552

Solution

Embed a FastAPI/uvicorn HTTPS server directly inside each teleop module (following the WebInput + RobotWebInterface pattern). The JS client sends msg.encode() - raw LCM-encoded bytes without topic headers - over WebSocket. Python side decodes and processes:

Breaking Changes

None

How to Test

# Phone teleop
dimos run simple-phone-teleop
# Open https://<host-ip>:8444/teleop on phone, accept cert, start sensors, connect, hold to drive

# Quest teleop
dimos run arm-teleop
# Open https://<host-ip>:8443/teleop on Quest browser, accept cert, tap Connect

Contributor License Agreement

  • I have read and approved the CLA.

@ruthwikdasyam ruthwikdasyam marked this pull request as ready for review February 28, 2026 22:12
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 28, 2026

Greptile Summary

This PR eliminates the Deno bridge subprocess and embeds a FastAPI/uvicorn HTTPS server directly into each teleop module. The client now sends raw LCM-encoded bytes (without topic headers) over WebSocket, and Python uses fingerprint-based dispatch to decode messages.

Key improvements:

  • Removes external Deno runtime dependency and subprocess lifecycle management
  • Eliminates LCM UDP transport between TypeScript and Python
  • Simplifies architecture: browser → WebSocket → Python module (direct)
  • Adds SSL certificate auto-generation for HTTPS (required by mobile sensor APIs)

Minor issues:

  • Synchronous decoders called from async WebSocket handlers may cause event loop blocking under lock contention
  • Quest module's frame_id routing silently defaults to RIGHT for non-"left" values without validation

Confidence Score: 4/5

  • Safe to merge with minor style improvements recommended
  • The architectural change is sound and eliminates external dependencies. The async/sync mixing in WebSocket handlers is a performance consideration rather than a correctness issue, and frame_id routing works correctly for normal WebXR handedness values. No breaking changes or security issues identified.
  • Pay attention to dimos/teleop/quest/quest_teleop_module.py and dimos/teleop/phone/phone_teleop_module.py for potential event loop blocking if lock contention becomes an issue in production

Important Files Changed

Filename Overview
dimos/teleop/phone/phone_teleop_module.py Replaces Deno bridge with embedded FastAPI WebSocket server. Synchronous decoders called from async context may block event loop under lock contention.
dimos/teleop/quest/quest_teleop_module.py Replaces Deno bridge with embedded FastAPI server. Frame ID routing defaults to RIGHT for non-"left" values. Async/sync mixing in WebSocket handlers.
dimos/web/dimos_interface/api/server.py Adds SSL support and certificate auto-generation using openssl. Clean implementation with proper error handling.
dimos/teleop/phone/web/static/index.html Removes LCM topic encoding, now sends raw msg.encode() bytes. Updates WebSocket URL to include /ws path.
dimos/teleop/quest/web/static/index.html Removes LCM topic encoding, uses frame_id for routing instead. Sends raw msg.encode() bytes over WebSocket.

Sequence Diagram

sequenceDiagram
    participant Browser as Quest/Phone Browser
    participant WS as WebSocket (/ws)
    participant Decoder as Fingerprint Decoder
    participant Module as Teleop Module
    participant Out as Output Streams

    Browser->>WS: Connect via WSS
    WS-->>Browser: Accept connection
    
    loop Teleoperation Active
        Browser->>Browser: Read sensors/controllers
        Browser->>Browser: msg.encode() (raw LCM bytes)
        Browser->>WS: Send binary data
        WS->>Decoder: Extract fingerprint (first 8 bytes)
        
        alt Known fingerprint
            Decoder->>Module: Call decoder (acquire lock)
            Module->>Module: Update state
        else Unknown fingerprint
            Decoder->>Decoder: Log warning
        end
        
        Module->>Module: Control loop (50Hz)
        Module->>Out: Publish PoseStamped/TwistStamped
    end
    
    Browser->>WS: Disconnect
    WS-->>Module: WebSocketDisconnect
Loading

Last reviewed commit: 338734f

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

@ruthwikdasyam ruthwikdasyam merged commit c8f853f into dev Mar 1, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants