Django REST backend for HomeSync — a virtual smart home dashboard where users can create rooms, add devices, toggle them on/off, and control them with natural-language commands.
- Django 6 + Django REST Framework
- SQLite (local development)
- Token Authentication (DRF's built-in)
- Google OAuth 2.0 for third-party sign-in
- Anthropic Claude (claude-haiku-4-5) for natural-language command parsing
- Open-Meteo for outdoor weather data (public API, no key required)
- Register/login with username and password, or sign in with Google
- Full CRUD for Rooms and Devices
- Per-user data isolation (you only see your own rooms and devices)
- LLM command endpoint: send "turn off the kitchen light" and the backend parses it, finds the matching device, and toggles it
- Outdoor weather endpoint proxying Open-Meteo for San Antonio, TX
homesync-api/
├── apps/
│ ├── accounts/ # User, auth views, Google OAuth
│ ├── rooms/ # Room model + CRUD
│ ├── devices/ # Device model + CRUD
│ └── assistant/ # LLM command parser + weather
├── config/ # Django settings, root URLs
├── requirements.txt
├── manage.py
└── .env.example
- Python 3.10+
- An Anthropic API key (https://console.anthropic.com)
- A Google Cloud OAuth 2.0 client (https://console.cloud.google.com)
python -m venv venv
venv\Scripts\Activate.ps1
pip install -r requirements.txt
Copy .env.example to .env and fill in your real keys:
ANTHROPIC_API_KEY=sk-ant-api03-...
GOOGLE_CLIENT_ID=...apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-...
python manage.py migrate
python manage.py runserver
Server runs at http://localhost:8000.
All endpoints except register, login, and google require an Authorization: Token <token> header.
- POST /api/auth/register/ — Create account, returns { token, user }
- POST /api/auth/login/ — Log in with username/password, returns { token, user }
- POST /api/auth/google/ — Log in with a Google ID token, returns { token, user }
- GET /api/auth/me/ — Get the current user's info
- GET /api/rooms/ — List your rooms
- POST /api/rooms/ — Create a room
- GET /api/rooms/{id}/ — Get one room
- PATCH /api/rooms/{id}/ — Rename a room
- DELETE /api/rooms/{id}/ — Delete a room
- GET /api/devices/ — List your devices
- POST /api/devices/ — Create a device
- GET /api/devices/{id}/ — Get one device
- PATCH /api/devices/{id}/ — Toggle state, rename, etc.
- DELETE /api/devices/{id}/ — Delete a device
- POST /api/assistant/command/ — Send a natural-language command. Body: { "message": "turn off the living room light" }. Returns the parsed action and the updated device.
- GET /api/weather/ — Current outdoor temperature (Open-Meteo)
- id — auto integer PK
- owner — FK to User
- name — string
- created_at — timestamp
- id — auto integer PK
- room — FK to Room
- name — string
- kind — one of: light, ac, fan, tv, lock
- is_on — boolean
- created_at — timestamp
- User-scoped querysets in every viewset (get_queryset filters by request.user) so users can never see each other's data.
- The LLM command endpoint gives Claude the user's device list as context, then parses the response as JSON and looks up the matching device case-insensitively.
- Markdown code fences are stripped from Claude's response before JSON parsing — models occasionally wrap JSON in code blocks despite instructions not to.
- Google OAuth uses the ID token flow appropriate for SPAs. The server verifies the token against Google using the GOOGLE_CLIENT_ID we registered, and the GOOGLE_CLIENT_SECRET is loaded from the environment.
- SQLite used for local dev — swap to Postgres for production
- Weather endpoint is hardcoded to San Antonio coordinates
- No pagination on list endpoints