docs(wiki): add full HTTP API reference
+453
@@ -0,0 +1,453 @@
|
||||
# HTTP API
|
||||
|
||||
`tvctl` exposes a loopback HTTP API for local automation and dashboards.
|
||||
|
||||
## Base URL
|
||||
|
||||
- Default: `http://127.0.0.1:7272/v1`
|
||||
- Configurable with `daemon.http_host` and `daemon.http_port`
|
||||
- API version prefix is always `/v1`
|
||||
|
||||
## Response Envelope
|
||||
|
||||
Successful responses:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
Error responses:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"error": {
|
||||
"code": "device_not_found",
|
||||
"message": "No known device matched 'living-room'.",
|
||||
"hint": "Run tvctl device list to see known devices."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `error.code` for automation logic. `message` and `hint` are human-facing.
|
||||
|
||||
## Device Identifiers
|
||||
|
||||
Anywhere `{id}` appears, you can use either:
|
||||
|
||||
- tvctl UUID
|
||||
- Friendly device name
|
||||
|
||||
Example:
|
||||
|
||||
- `GET /v1/devices/3e2f7e8b-2d5e-4eb5-aef3-b2c4f2b2f17a/state`
|
||||
- `GET /v1/devices/living-room/state`
|
||||
|
||||
## Endpoints
|
||||
|
||||
### `GET /v1/devices`
|
||||
|
||||
List known devices.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:7272/v1/devices
|
||||
```
|
||||
|
||||
Returns `data` as `Device[]`.
|
||||
|
||||
---
|
||||
|
||||
### `POST /v1/devices/discover`
|
||||
|
||||
Run discovery immediately and merge results into registry.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:7272/v1/devices/discover
|
||||
```
|
||||
|
||||
Returns `data`:
|
||||
|
||||
```json
|
||||
{
|
||||
"devices": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `GET /v1/devices/{id}`
|
||||
|
||||
Get one known device by UUID or friendly name.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:7272/v1/devices/living-room
|
||||
```
|
||||
|
||||
Returns `data` as `Device`.
|
||||
|
||||
---
|
||||
|
||||
### `DELETE /v1/devices/{id}`
|
||||
|
||||
Remove one device from the registry.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -X DELETE http://127.0.0.1:7272/v1/devices/bedroom
|
||||
```
|
||||
|
||||
Returns `data` as removed `Device`.
|
||||
|
||||
---
|
||||
|
||||
### `GET /v1/devices/{id}/state`
|
||||
|
||||
Fetch live state for one device.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:7272/v1/devices/living-room/state
|
||||
```
|
||||
|
||||
Returns `data`:
|
||||
|
||||
```json
|
||||
{
|
||||
"device": {},
|
||||
"state": {}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `GET /v1/devices/{id}/apps`
|
||||
|
||||
List installed apps for device platform from cache.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:7272/v1/devices/living-room/apps
|
||||
```
|
||||
|
||||
Returns `data`:
|
||||
|
||||
```json
|
||||
{
|
||||
"platform": "roku",
|
||||
"apps": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `POST /v1/devices/{id}/apps/launch`
|
||||
|
||||
Launch an app by normalized app id, app name, or platform id.
|
||||
|
||||
Request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"app": "jellyfin"
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:7272/v1/devices/living-room/apps/launch \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"app":"jellyfin"}'
|
||||
```
|
||||
|
||||
Returns action payload in `data`.
|
||||
|
||||
---
|
||||
|
||||
### `POST /v1/devices/{id}/apps/stop`
|
||||
|
||||
Stop the currently active app.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:7272/v1/devices/living-room/apps/stop
|
||||
```
|
||||
|
||||
Returns action payload in `data`.
|
||||
|
||||
---
|
||||
|
||||
### `POST /v1/devices/{id}/apps/refresh`
|
||||
|
||||
Refresh cached app catalog from live device.
|
||||
|
||||
Request body is optional.
|
||||
|
||||
- Without body: merge/refresh cache
|
||||
- With body:
|
||||
|
||||
```json
|
||||
{
|
||||
"clear": true
|
||||
}
|
||||
```
|
||||
|
||||
`clear=true` clears platform cache before refresh.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:7272/v1/devices/living-room/apps/refresh
|
||||
|
||||
curl -X POST http://127.0.0.1:7272/v1/devices/living-room/apps/refresh \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"clear":true}'
|
||||
```
|
||||
|
||||
Returns action payload in `data`.
|
||||
|
||||
---
|
||||
|
||||
### `POST /v1/devices/{id}/remote/key`
|
||||
|
||||
Send a single normalized key.
|
||||
|
||||
Request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "home"
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:7272/v1/devices/living-room/remote/key \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"key":"volume-up"}'
|
||||
```
|
||||
|
||||
Returns action payload in `data`.
|
||||
|
||||
---
|
||||
|
||||
### `POST /v1/devices/{id}/remote/sequence`
|
||||
|
||||
Send multiple normalized keys with per-key delay.
|
||||
|
||||
Request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"keys": ["home", "home", "up"],
|
||||
"delay_ms": 200
|
||||
}
|
||||
```
|
||||
|
||||
`delay_ms` defaults to `200` when omitted.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:7272/v1/devices/living-room/remote/sequence \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"keys":["home","home","home","up"],"delay_ms":200}'
|
||||
```
|
||||
|
||||
Returns action payload in `data`.
|
||||
|
||||
---
|
||||
|
||||
### `POST /v1/devices/{id}/dev/install`
|
||||
|
||||
Sideload a development package ZIP (Roku developer mode).
|
||||
|
||||
Content type: `multipart/form-data`
|
||||
|
||||
Form field:
|
||||
|
||||
- `archive`: zip file bytes
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:7272/v1/devices/living-room/dev/install \
|
||||
-F archive=@./channel.zip
|
||||
```
|
||||
|
||||
Returns action payload in `data`.
|
||||
|
||||
---
|
||||
|
||||
### `POST /v1/devices/{id}/dev/reload`
|
||||
|
||||
Reload currently sideloaded development package.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:7272/v1/devices/living-room/dev/reload
|
||||
```
|
||||
|
||||
Returns action payload in `data`.
|
||||
|
||||
---
|
||||
|
||||
### `GET /v1/devices/{id}/dev/logs`
|
||||
|
||||
Fetch recent device developer logs.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:7272/v1/devices/living-room/dev/logs
|
||||
```
|
||||
|
||||
Returns `data`:
|
||||
|
||||
```json
|
||||
{
|
||||
"device": {},
|
||||
"lines": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `GET /v1/daemon/status`
|
||||
|
||||
Get daemon health and runtime status.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:7272/v1/daemon/status
|
||||
```
|
||||
|
||||
Returns `data`:
|
||||
|
||||
```json
|
||||
{
|
||||
"pid": 12345,
|
||||
"socket": "/run/user/1000/tvctl.sock",
|
||||
"http_enabled": true,
|
||||
"http_host": "127.0.0.1",
|
||||
"http_port": 7272,
|
||||
"device_count": 1,
|
||||
"default_device": "living-room"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `GET /v1/config`
|
||||
|
||||
Read effective config from disk.
|
||||
|
||||
Notes:
|
||||
|
||||
- `dev.roku_password` is redacted in API responses.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:7272/v1/config
|
||||
```
|
||||
|
||||
Returns `data` as full config object.
|
||||
|
||||
---
|
||||
|
||||
### `PATCH /v1/config`
|
||||
|
||||
Patch config using flat key/value pairs.
|
||||
|
||||
Rules:
|
||||
|
||||
- Keys are dotted paths (for example `daemon.http_port`)
|
||||
- Values must be string, number, boolean, or `null`
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -X PATCH http://127.0.0.1:7272/v1/config \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"daemon.cors_enabled":true,"daemon.cors_allowed_origins":"http://127.0.0.1:8080"}'
|
||||
```
|
||||
|
||||
Returns same shape as config reload result in `data`.
|
||||
|
||||
---
|
||||
|
||||
### `POST /v1/config/reload`
|
||||
|
||||
Reload daemon config from disk.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:7272/v1/config/reload
|
||||
```
|
||||
|
||||
Returns `data`:
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {},
|
||||
"restart_required": []
|
||||
}
|
||||
```
|
||||
|
||||
## Key Names
|
||||
|
||||
Common key names:
|
||||
|
||||
- `home`, `back`, `up`, `down`, `left`, `right`, `select`
|
||||
- `play`, `pause`, `play-pause`, `stop`, `rewind`, `fast-forward`, `replay`, `skip`
|
||||
- `volume-up`, `volume-down`, `mute`
|
||||
- `power`, `power-on`, `power-off`
|
||||
- `channel-up`, `channel-down`
|
||||
- `input-hdmi1`, `input-hdmi2`, `input-hdmi3`, `input-hdmi4`, `input-av`, `input-tuner`
|
||||
- `search`, `info`, `options`
|
||||
- `literal:<text>` for raw adapter literal keys
|
||||
|
||||
## HTTP Status Mapping
|
||||
|
||||
Typical statuses:
|
||||
|
||||
- `200 OK` success
|
||||
- `400 Bad Request` invalid key, invalid payload, ambiguous app, unsupported patch values
|
||||
- `404 Not Found` unknown device/default device missing
|
||||
- `500 Internal Server Error` transport/adapter/internal failures
|
||||
|
||||
## CORS Notes
|
||||
|
||||
Browser clients require CORS config:
|
||||
|
||||
```toml
|
||||
[daemon]
|
||||
cors_enabled = true
|
||||
cors_allowed_origins = ["http://127.0.0.1:8080", "http://localhost:8080"]
|
||||
```
|
||||
|
||||
Then reload:
|
||||
|
||||
```bash
|
||||
tvctl config reload
|
||||
```
|
||||
|
||||
Origin must match exactly (`http://127.0.0.1:8080` is not `http://0.0.0.0:8080`).
|
||||
Reference in New Issue
Block a user