A production-ready fleet management API for autonomous robots. Built with FastAPI, PostgreSQL, Redis, Celery, and WebSockets.
Portfolio project demonstrating: async Python, real-time communication, background task processing, JWT auth with RBAC, and clean architecture patterns.
- π€ Robot Management β Full CRUD with status tracking (idle, active, charging, maintenance, offline)
- π Mission Control β Create, assign, and track missions through their lifecycle
- β‘ Real-time Updates β WebSocket connections for live robot/fleet status
- β° Background Tasks β Celery workers for async operations + scheduled health checks
- π Authentication β JWT-based auth with role-based access (viewer/operator/admin)
- π Auto Documentation β OpenAPI/Swagger UI at
/docs
| Layer | Technology |
|---|---|
| Framework | FastAPI (Python 3.11+) |
| Database | PostgreSQL + SQLAlchemy 2.0 (async) |
| Migrations | Alembic |
| Cache/Broker | Redis |
| Task Queue | Celery + Celery Beat |
| Validation | Pydantic v2 |
| Auth | JWT (python-jose) + bcrypt |
| Containers | Docker + docker compose |
Run the interactive demo to see all features in action:
# Start services first
docker compose up -d
docker compose exec api alembic upgrade head
# Run the demo
./scripts/run-demo.shThe demo walks through: authentication β robot management β mission lifecycle β background tasks β WebSocket info.
# Clone the repo
git clone https://github.com/ibrahimsel/openmotiv.git
cd openmotiv
# Copy environment file
cp .env.example .env
# Start all services (api, db, redis, celery-worker, celery-beat)
docker compose up -d
# Run database migrations
docker compose exec api alembic upgrade head
# API is now live!
# π Docs: http://localhost:8000/docs
# π API: http://localhost:8000/api/v1Using uv (fast, recommended)
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Create venv and install
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
# Start PostgreSQL and Redis (or use Docker for just those)
docker compose up -d db redis
# Run migrations
alembic upgrade head
# Start the API
uvicorn app.main:app --reloadUsing pip
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# Start PostgreSQL and Redis
docker compose up -d db redis
# Run migrations
alembic upgrade head
# Start the API
uvicorn app.main:app --reload# Register a new user
curl -X POST http://localhost:8000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "securepass123"}'
# Login and get JWT token
curl -X POST http://localhost:8000/api/v1/auth/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "[email protected]&password=securepass123"
# Response: {"access_token": "eyJ...", "token_type": "bearer"}
# Use the token in subsequent requests
export TOKEN="eyJ..."# Create a robot (requires operator/admin role)
curl -X POST http://localhost:8000/api/v1/robots \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Scout-01",
"serial_number": "RBT-2024-001",
"robot_type": "drone",
"status": "idle"
}'
# List all robots
curl http://localhost:8000/api/v1/robots \
-H "Authorization: Bearer $TOKEN"
# Get specific robot
curl http://localhost:8000/api/v1/robots/{robot_id} \
-H "Authorization: Bearer $TOKEN"
# Update robot status
curl -X PATCH http://localhost:8000/api/v1/robots/{robot_id}/status \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": "active"}'# Create a mission
curl -X POST http://localhost:8000/api/v1/missions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Warehouse Patrol",
"description": "Routine security sweep of sector A",
"priority": 2
}'
# Assign a robot to a mission
curl -X POST http://localhost:8000/api/v1/missions/{mission_id}/assign \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"robot_id": "{robot_id}"}'
# Start the mission
curl -X POST http://localhost:8000/api/v1/missions/{mission_id}/start \
-H "Authorization: Bearer $TOKEN"
# Complete the mission
curl -X POST http://localhost:8000/api/v1/missions/{mission_id}/complete \
-H "Authorization: Bearer $TOKEN"pending β assigned β in_progress β completed
β failed β
β cancelled
Connect to WebSockets for real-time updates:
// Connect to a specific robot's updates
const ws = new WebSocket('ws://localhost:8000/ws/robots/{robot_id}?token={jwt_token}');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Robot update:', data);
// { "robot_id": "...", "status": "active", "battery_level": 85, ... }
};// Connect to all fleet updates
const ws = new WebSocket('ws://localhost:8000/ws/fleet?token={jwt_token}');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Fleet update:', data);
// { "event": "status_change", "robot_id": "...", "data": {...} }
};import asyncio
import websockets
async def listen():
uri = "ws://localhost:8000/ws/fleet?token=YOUR_JWT_TOKEN"
async with websockets.connect(uri) as ws:
async for message in ws:
print(f"Received: {message}")
asyncio.run(listen())OpenMotiv uses Celery for background processing:
| Task | Description |
|---|---|
send_robot_command |
Queue commands to robots |
schedule_mission |
Process mission scheduling |
| Schedule | Task | Description |
|---|---|---|
| Every 60s | check_fleet_health |
Monitor all robots, flag issues |
# Via API endpoint
curl -X POST http://localhost:8000/api/v1/tasks/fleet-health-check \
-H "Authorization: Bearer $TOKEN"
# Response: {"task_id": "abc-123", "status": "queued"}| Role | Permissions |
|---|---|
viewer |
Read robots, missions |
operator |
+ Create/update robots, manage missions |
admin |
+ Delete robots, manage users |
New users are assigned viewer role by default.
openmotiv/
βββ app/
β βββ api/
β β βββ v1/
β β β βββ auth.py # Login, register
β β β βββ robots.py # Robot CRUD
β β β βββ missions.py # Mission management
β β β βββ tasks.py # Task triggers
β β β βββ websocket.py # WS endpoints
β β βββ deps.py # Auth & DB dependencies
β βββ core/
β β βββ config.py # Settings (pydantic-settings)
β β βββ security.py # JWT & password hashing
β β βββ websocket.py # Connection manager
β βββ models/ # SQLAlchemy models
β βββ schemas/ # Pydantic schemas
β βββ tasks/ # Celery task definitions
β βββ db/ # Database setup
β βββ worker.py # Celery app config
β βββ main.py # FastAPI app
βββ alembic/ # Database migrations
βββ tests/ # pytest test suite
βββ scripts/
β βββ ws_client.py # WebSocket test client
βββ docker-compose.yml
βββ Dockerfile
βββ pyproject.toml
# With Docker
docker compose exec api pytest
# Local (with uv)
uv run pytest -v
# Local (with pip)
pytest -v
# With coverage
pytest --cov=app --cov-report=term-missing| Service | Port | Description |
|---|---|---|
api |
8000 | FastAPI application |
db |
5432 | PostgreSQL database |
redis |
6379 | Redis (cache + Celery broker) |
celery-worker |
β | Background task processor |
celery-beat |
β | Scheduled task scheduler |
# View logs
docker compose logs -f api celery-worker
# Restart a service
docker compose restart api
# Stop everything
docker compose down
# Stop and remove volumes (fresh start)
docker compose down -v| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Required |
REDIS_URL |
Redis connection string | redis://localhost:6379 |
SECRET_KEY |
JWT signing key | Required |
ACCESS_TOKEN_EXPIRE_MINUTES |
Token expiry | 30 |
See .env.example for a complete template.
- Prometheus metrics endpoint
- Rate limiting
- API key authentication (for robot agents)
- Mission waypoints and path planning
- Fleet analytics dashboard
MIT
Built by Ibrahim Sel β’ View on GitHub