""" Entry point: ``python -m backend`` Binds 0.0.0.0 and prints every address the dashboard is reachable on, so you can open it from a phone or tablet on the same Wi-Fi without knowing this machine's IP in advance. """ from __future__ import annotations import argparse import sys from . import netinfo, settings def main() -> int: parser = argparse.ArgumentParser(prog="python -m backend", description="AGIBOT X2 dashboard server") parser.add_argument("--port", type=int, help="HTTP port (default from config.json)") parser.add_argument("--host", default=None, help="Bind address (default 0.0.0.0 - all interfaces)") parser.add_argument("--robot", default=None, help="Robot host or IP; saved to config.json") parser.add_argument("--domain", type=int, default=None, help="ROS_DOMAIN_ID") parser.add_argument("--mode", choices=["auto", "ros2", "mock"], default=None, help="Bridge mode: auto (default), ros2 (require rclpy), mock (simulate)") parser.add_argument("--reload", action="store_true", help="Auto-reload on source changes") args = parser.parse_args() updates = {} if args.port is not None: updates["port"] = args.port if args.robot is not None: updates["robot_host"] = args.robot if args.domain is not None: updates["ros_domain_id"] = args.domain if args.mode is not None: updates["bridge_mode"] = args.mode if updates: settings.save(updates) config = settings.load() host = args.host or config["host"] port = config["port"] try: import uvicorn except ImportError: print("uvicorn is not installed. Run: pip install -r requirements.txt", file=sys.stderr) return 1 name = config.get("dashboard_name") if config.get("advertise_name") else None detail = netinfo.address_detail() print() print(" AGIBOT X2 Dashboard") print(" " + "-" * 52) if detail["ip"]: print(" Open this from a phone, tablet or laptop:") print(f" http://{detail['ip']}:{port}") print(f" (live {detail['kind'] or 'network'} address on '{detail['adapter']}')") else: print(" No usable network address right now:") print(f" {detail['reason_text'] or detail['reason']}") print(" The dashboard is still running; it will pick the address up") print(" automatically when the network comes back.") others = [u for u in netinfo.dashboard_urls(port, name) if u != f"http://{detail['ip']}:{port}"] if others: print() print(" Also reachable at:") for url in others: print(f" {url}") print(" " + "-" * 52) print(f" robot host {config['robot_host'] or '(not set - use the Settings tab)'}") print(f" bridge mode {config['bridge_mode']}") print(f" ROS_DOMAIN_ID {config['ros_domain_id']}") print(" Open any address above from any device on this network.") print(flush=True) uvicorn.run( "backend.server:app", host=host, port=port, reload=args.reload, log_level="info", access_log=False, # websockets 16.x crashes uvicorn's default WS impl (starlette/anyio # "cancel scope" error) -> stuck "Connecting". wsproto is a stable, # isolated WS engine that avoids it without touching the shared # `websockets` package the voice pipeline depends on. ws="wsproto", ) return 0 if __name__ == "__main__": raise SystemExit(main())