Overview
2026-03-18This integration allows any device controlled by api.rforssen.net to be controlled via Google Home and Google Assistant voice commands. No Home Assistant, no Nabu Casa, no third-party services — just a Flask OAuth2 server and a Google Smart Home Action.
Architecture
Key facts
OAuth2 concepts
2026-03-18For normal Google login (rforssen.net), your app is the OAuth2 client — Google issues tokens to you. For Google Home, the roles are reversed: your app is the OAuth2 server — you issue tokens to Google.
Account linking flow
Token types
Access token — short-lived (1 hour). Google sends this with every EXECUTE/QUERY request as a Bearer token. Your API validates it against the access_tokens table.
Refresh token — long-lived (90 days). Google uses this to get new access tokens without re-linking. Your API handles this in the /token endpoint with grant_type=refresh_token.
Auth code — one-time use (10 minutes). Generated when user clicks Allow, exchanged immediately for tokens, then deleted.
Client credentials
The Client ID and Client Secret in the Google Developer Console are credentials YOU invent and give to Google. Google sends them back when calling your /token endpoint so your API can verify the request is genuinely from Google.
Files and endpoints
2026-03-18- api/wled/__init__.py — Blueprint registration + auto-provisioning on startup
- api/wled/db.py — MySQL connection + table provisioning (auth_codes, access_tokens, refresh_tokens)
- api/wled/oauth_server.py — OAuth2 server: /wled/oauth/authorize + /wled/oauth/token
- api/wled/smarthome.py — Smart Home fulfillment: SYNC / QUERY / EXECUTE intents
- api/wled/wled_control.py — Device-specific control endpoints (WLED JSON API wrapper)
MySQL tables (wled_oauth)
Tables are auto-created on pod startup via db.provision() called from __init__.py. The MySQL database itself must be created manually by root — the app user only has table-level privileges.
OAuth2 server implementation
2026-03-18GET /wled/oauth/authorize
Called by Google Home app when user initiates account linking. If the user is not logged in, redirects to /auth/login/google preserving all query parameters. Once logged in, shows the "Allow Google Home" consent page.
POST /wled/oauth/authorize
User clicked Allow. Validates client ID, generates a secrets.token_urlsafe(32) auth code, stores it in auth_codes table, redirects to Google's redirect URI with the code and state.
# Google's redirect URI format
https://oauth-redirect.googleusercontent.com/r/<project-id>
POST /wled/oauth/token
Called by Google to exchange an auth code for tokens, or to refresh an expired access token.
# authorization_code grant grant_type=authorization_code code=<auth_code> redirect_uri=<redirect_uri> client_id=<your_client_id> client_secret=<your_client_secret> # refresh_token grant grant_type=refresh_token refresh_token=<refresh_token> client_id=<your_client_id> client_secret=<your_client_secret>
Returns:
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "...",
"scope": "..."
}
Smart Home fulfillment
2026-03-18All Google Assistant commands arrive at POST /wled/smarthome as JSON with a Bearer token. The endpoint validates the token against access_tokens table, then handles three intents:
SYNC — device discovery
Called when Google needs to know what devices exist. Returns a device descriptor. To add a new device, add an entry to the devices array.
{
"id": "wled-1",
"type": "action.devices.types.LIGHT",
"traits": [
"action.devices.traits.OnOff",
"action.devices.traits.Brightness"
],
"name": {
"name": "LED Strip",
"nicknames": ["lights", "the lights", "leds"]
}
}
QUERY — current state
Called when Google needs to know the current state of a device (e.g. "Hey Google, are the lights on?"). Returns on/off status and brightness.
EXECUTE — commands
Called when user issues a voice command. Supported commands:
Token validation
# Every request to /smarthome must include: Authorization: Bearer <access_token> # Validated against MySQL: SELECT user_sub FROM access_tokens WHERE token = %s AND expires_at > UTC_TIMESTAMP()
Google Developer Console setup
2026-03-18One-time setup at console.home.google.com. Create a project → Add cloud-to-cloud integration → Develop.
Account linking fields
The Client ID and Secret you enter here must match GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in oauth_server.py. Google sends them when calling your /token endpoint.
After saving
Go to Test tab → the integration appears as "Ready". Account linking must be done from the Google Home app before testing.
Account linking
2026-03-18Account linking connects your Google account to your Smart Home Action. Must be done once from the Google Home app.
Steps
Open Google Home app → tap + → Set up device → Works with Google → search for [test] wled-proj → tap it → the OAuth2 flow starts → you see the "Authorize Google Home" page → click Allow → linking complete.
After linking
Google issues a SYNC request to discover devices. Your devices appear in Google Home. Voice commands work immediately.
Re-syncing devices
If you add or rename devices in smarthome.py, say "Hey Google, sync my devices" to force a re-sync.
Troubleshooting
If account linking fails with "Not Found" — check that the user is logged in to rforssen.net before the authorize page is reached. The authorize endpoint redirects to /auth/login/google if no session exists, preserving all query parameters in the redirect URL.
If linking fails with unauthorized_client — the Client ID in oauth_server.py doesn't match what's in the Google Developer Console.
Adding new devices
2026-03-18To add a new device (e.g. a custom ESP32 animation controller, a second WLED strip, a relay):
1. Add the device to SYNC
In smarthome.py, add an entry to the devices array in the SYNC handler. Give it a unique id, appropriate type and traits, and friendly name/nicknames.
2. Handle it in EXECUTE
In the EXECUTE handler, route by device ID:
if device_id == "wled-1": # call WLED API elif device_id == "esp32-custom": # call your custom ESP32 API
3. Handle it in QUERY
Return the current state for each device ID.
4. Sync Google
Say "Hey Google, sync my devices" — the new device appears in Google Home immediately.