Base URL: http://localhost:8080 (configurable in ~/.cometblue/config.yaml)
Interactive API docs (Swagger UI): http://localhost:8080/docs
OpenAPI schema: http://localhost:8080/openapi.json
- Devices
- Temperatures
- Schedules
- Holidays
- Profiles
- Scenarios (Presets)
- Scheduler (Zeitplan)
- Discovery
- History
- Settings
- System
List all configured devices including last known RSSI and status.
Response:
[
{
"address": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"name": "Wohnzimmer",
"pin": 0,
"adapter": null,
"mac_address": "XX:XX:XX:XX:XX:XX",
"active": true,
"added_at": "2026-03-10T12:00:00",
"last_seen": "2026-03-16T08:00:00",
"rssi": -71
}
]Add a device. If the address is already known from a scan, the MAC address is filled in automatically.
Body:
{ "address": "XXXXXXXX-...", "name": "Wohnzimmer", "pin": 0, "adapter": null }Response: 201 Created — device object
Get a single device. Response: device object
Update device name, PIN, adapter or MAC address (all fields optional).
Body: { "name": "Küche", "pin": 31337, "adapter": null, "mac_address": "XX:XX:XX:XX:XX:XX" }
Remove device. History records are kept. Response: 204 No Content
Last cached poll status — instant, no BLE required.
Response:
{
"address": "XXXXXXXX-...",
"temp_current": 20.5,
"temp_manual": null,
"temp_comfort": 19.0,
"temp_eco": 18.0,
"temp_offset": 0.0,
"window_open": 0,
"window_minutes": null,
"battery": 78,
"rssi": -71,
"child_lock": 0,
"flags_raw": "000000",
"device_time": "2026-03-16T08:00:00",
"error": null,
"polled_at": "2026-03-16T08:05:00"
}Cached device metadata (no BLE). Useful for displaying battery and last seen.
Response:
{
"address": "XXXXXXXX-...",
"name": "Wohnzimmer",
"mac_address": "XX:XX:XX:XX:XX:XX",
"adapter": null,
"added_at": "2026-03-10T12:00:00",
"last_seen": "2026-03-16T08:00:00",
"battery": 78,
"device_time": "2026-03-16T08:00:00",
"polled_at": "2026-03-16T08:05:00",
"error": null
}Trigger an immediate BLE poll for this device.
Response: fresh status object (same as /status)
409 if a poll is already running.
Trigger an immediate BLE poll of all configured devices (runs in background).
Response: 202 Accepted — { "started": true }
Check if a background poll is in progress.
Response:
{
"running": true,
"current_device": "XXXXXXXX-...",
"started_at": "2026-03-16T08:10:00Z",
"completed_at": null
}Test a PIN via BLE without saving it.
Body: { "pin": 31337 } — omit to use the device's stored PIN
Response: { "address": "...", "pin": 31337, "valid": true }
Set device flags (currently: child lock).
Body: { "child_lock": true }
Response: { "status": "ok" }
Bulk set child lock on all or specific devices.
Body:
{ "enabled": true, "addresses": ["all"] }
// or specific devices:
{ "enabled": false, "addresses": ["XXXXXXXX-...", "YYYYYYYY-..."] }Re-scan for a device by its stored MAC address. Needed on macOS after battery replacement when CoreBluetooth assigns a new UUID.
Response:
{ "status": "updated", "old_address": "XXXXXXXX-...", "new_address": "YYYYYYYY-...", "mac": "XX:XX:XX:XX:XX:XX" }
// or if UUID unchanged:
{ "status": "unchanged", "address": "XXXXXXXX-...", "mac": "XX:XX:XX:XX:XX:XX" }Cached temperature setpoints from the last poll (no BLE).
Response:
{
"address": "XXXXXXXX-...",
"temperatures": {
"current": 20.5,
"manual": null,
"comfort": 19.0,
"eco": 18.0,
"offset": 0.0
},
"window_open": false,
"window_minutes": null,
"polled_at": "2026-03-16T08:05:00"
}Set temperature setpoints via BLE. Triggers an automatic poll afterwards.
Body (all fields optional):
{ "comfort": 22.0, "eco": 17.0, "offset": 0.0 }| Field | Range | Description |
|---|---|---|
comfort |
5.0–35.0 °C | Heating temperature (during schedule) |
eco |
5.0–35.0 °C | Setback temperature (outside schedule) |
offset |
-3.5–+3.5 °C | Calibration offset. Cached value is used if omitted. |
Write the current system time to the device.
Response: { "status": "ok", "address": "..." }
Up to 4 heating periods per day, in 10-minute steps.
Read the full weekly schedule via BLE.
Response:
[
{
"day": "monday",
"periods": [
{ "start": "07:00", "end": "12:00" },
{ "start": "12:10", "end": "19:00" },
{ "start": null, "end": null },
{ "start": null, "end": null }
]
},
{ "day": "tuesday", "periods": [ ... ] }
]Write the full weekly schedule. Only specified days are updated.
Body:
{
"monday": [{ "start": "07:00", "end": "12:00" }, { "start": "12:10", "end": "19:00" }],
"tuesday": [{ "start": "07:00", "end": "19:00" }],
"saturday": [{ "start": "08:00", "end": "22:00" }]
}Set schedule for a single day. day = monday–sunday.
Body: { "periods": [{ "start": "07:00", "end": "19:00" }] } — max 4 periods
curl -X PUT http://localhost:8080/api/devices/XXXXXXXX-.../schedules/monday \
-H "Content-Type: application/json" \
-d '{"periods": [{"start": "07:00", "end": "12:00"}, {"start": "12:10", "end": "19:00"}]}'8 holiday slots per device (slot 1–8).
Read all holiday slots via BLE.
Response:
[
{ "slot": 1, "active": true, "start": "2026-12-24T00:00:00", "end": "2027-01-02T23:59:00", "temperature": 15.0 },
{ "slot": 2, "active": false, "start": null, "end": null, "temperature": null }
]Set a holiday slot (slot 1–8).
Body:
{
"start": "2026-12-24T00:00:00",
"end": "2027-01-02T23:59:00",
"temperature": 15.0,
"active": true
}curl -X PUT http://localhost:8080/api/devices/XXXXXXXX-.../holidays/1 \
-H "Content-Type: application/json" \
-d '{"start": "2026-12-24T00:00:00", "end": "2027-01-02T23:59:00", "temperature": 15.0, "active": true}'Clear (deactivate) a holiday slot. Response: 204 No Content
Profiles are stored as YAML in ~/.cometblue/profiles/. Default profiles: winter, summer, spring, holiday, weekday, weekend, aus.
List all available profiles. Response: [{ "name": "winter" }, ...]
Get profile details including schedules and optional child lock.
Response:
{
"name": "winter",
"comfort_temp": 22.0,
"eco_temp": 17.0,
"child_lock": null,
"schedules": {
"monday": [{ "start": "07:00", "end": "12:00" }, { "start": "12:10", "end": "19:00" }],
"saturday": [{ "start": "08:00", "end": "22:00" }]
}
}Create or overwrite a profile.
Body:
{
"name": "winter",
"comfort_temp": 22.0,
"eco_temp": 17.0,
"child_lock": null,
"schedules": { "monday": [{ "start": "07:00", "end": "19:00" }] }
}Delete a profile. Response: { "status": "deleted", "name": "winter" }
Apply a profile to devices via BLE (temperatures + optional schedules).
Body:
{ "devices": ["all"], "apply_schedules": true }Response:
{ "status": "done", "profile": "winter", "results": { "XXXXXXXX-...": "ok" } }# Apply to all devices including schedules:
curl -X POST http://localhost:8080/api/profiles/winter/apply \
-H "Content-Type: application/json" \
-d '{"devices": ["all"], "apply_schedules": true}'
# Temperatures only on specific devices:
curl -X POST http://localhost:8080/api/profiles/holiday/apply \
-H "Content-Type: application/json" \
-d '{"devices": ["XXXXXXXX-..."], "apply_schedules": false}'A scenario assigns a different profile to each device. Applying it writes all profiles simultaneously with live SSE progress.
List all scenarios.
Response:
[{ "id": 1, "name": "Wohnung Frühling", "assignments": { "XXXXXXXX-...": "spring" } }]Create a scenario.
Body:
{
"name": "Wohnung Frühling",
"assignments": {
"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX": "spring",
"YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY": "summer"
}
}Response: 201 Created
Get a single scenario. Response: scenario object
Update scenario name and/or assignments.
Delete scenario. Response: 204 No Content
Apply scenario — returns a Server-Sent Events stream.
curl -N -X POST http://localhost:8080/api/presets/1/applySSE Events:
event: progress
data: {"address": "XXXXXXXX-...", "profile": "spring", "index": 0, "total": 3}
event: result
data: {"address": "XXXXXXXX-...", "status": "ok"}
event: done
data: {"preset": "Wohnung Frühling", "results": {"XXXXXXXX-...": "ok", ...}}
Automatically apply a scenario or profile at set times and days of the week.
List all triggers.
Response:
[{
"id": 1,
"name": "Morgens warm",
"type": "scenario",
"target_id": "1",
"days": ["mon", "tue", "wed", "thu", "fri"],
"time_hm": "07:00",
"enabled": true,
"last_run": "2026-03-16T07:00:00",
"next_run": "2026-03-17T07:00:00",
"created_at": "2026-03-15T10:00:00"
}]Create a trigger.
Body:
{
"name": "Morgens warm",
"type": "scenario",
"target_id": "1",
"days": ["mon", "tue", "wed", "thu", "fri"],
"time_hm": "07:00",
"enabled": true
}| Field | Values | Description |
|---|---|---|
type |
"scenario" / "profile" |
What to apply |
target_id |
scenario ID (string) or profile name | Target |
days |
["daily"] or ["mon","tue",...] |
Which days |
time_hm |
"HH:MM" |
Time of day |
Response: 201 Created
Update a trigger. Same body as POST.
Delete a trigger. Response: 204 No Content
Execute a trigger immediately (ignores schedule).
BLE scan — returns a Server-Sent Events stream of found devices. Ends after timeout seconds.
curl -N "http://localhost:8080/api/discovery/stream?timeout=15"SSE Events:
event: device
data: {"address": "XXXXXXXX-...", "name": "Comet Blue", "rssi": -65, "verified": true}
event: progress
data: {"elapsed": 3.5, "total": 15.0}
event: done
data: {"found": 7}
event: error
data: {"message": "scan_in_progress"}
Query parameters:
| Parameter | Default | Description |
|---|---|---|
timeout |
10.0 |
Scan duration in seconds (3–60) |
Continuous RSSI locator — streams sorted RSSI snapshots every second until the client disconnects. Useful for physically locating thermostats by walking towards the strongest signal.
Only one BLE scan can run at a time. Starting the locator while a regular scan is active (or vice versa) returns an
errorevent.
curl -N "http://localhost:8080/api/discovery/locator"SSE Events:
event: rssi
data: [
{"address": "XXXXXXXX-...", "name": "Comet Blue", "rssi": -65, "label": "Küche"},
{"address": "YYYYYYYY-...", "name": "Comet Blue", "rssi": -78, "label": null}
]
event: error
data: {"message": "scan_in_progress"}
Each rssi event contains all currently seen CometBlue devices, sorted strongest-first. label is the configured device name from the database (or null if not yet added).
Close the connection (Ctrl-C / disconnect) to stop the scan.
All devices ever found via scan (persisted in DB).
Response:
[{
"address": "XXXXXXXX-...",
"name": "Comet Blue",
"rssi": -65,
"mac_address": "XX:XX:XX:XX:XX:XX",
"last_seen": "2026-03-16T09:00:00",
"configured": true
}]Temperature and battery history for a device.
Query parameters:
| Parameter | Description |
|---|---|
hours=24 |
Relative time window (from now) |
from=2026-03-01T00:00:00 |
Absolute start (ISO 8601) |
to=2026-03-16T23:59:59 |
Absolute end (ISO 8601) |
limit=500 |
Max records (default 500) |
Response:
{
"address": "XXXXXXXX-...",
"count": 288,
"records": [
{
"recorded_at": "2026-03-16T08:00:00",
"temp_current": 20.5,
"temp_comfort": 19.0,
"temp_eco": 18.0,
"battery": 78
}
]
}Combined history for all devices (used by the Monitor chart).
Query parameters: hours=24, limit=1000
Response: [{ "address", "name", "records": [...] }]
Enable or disable automatic background polling.
Body: { "enabled": true }
Response: { "auto_poll": true }
Update the poll interval at runtime (minimum 60 seconds, recommended ≥ 900).
Body: { "poll_interval": 900 }
Response: { "poll_interval": 900, "next_poll": "2026-03-16T08:25:00" }
⚠️ Firmware warning: Polling too frequently freezes CometBlue devices. Recovery requires removing and reinserting the battery. Recommended minimum: 15 minutes (900 s).
Service status overview.
Response:
{
"status": "ok",
"devices": 7,
"poll_interval": 900,
"next_poll": "2026-03-16T08:25:00+01:00",
"auto_poll": false
}