v3.0.4-experimental — changes since v3.0.3
New: Aerivue backend (aerivue.app)
- Full upload backend for Aerivue, on par with the
SleepHQ integration: per-day imports → multipart file posts →
process → poll to completion. - Uploads the complete file set:
STR.edf,Identification.json/.crc,
SETTINGS/,DATALOG/<day>/session groups, andOXYMETRY/<day>/
Wellue/O2 Ring recordings — oximetry imports verified server-side
(77 recordings, "imported" with reading counts). - Web portal: Aerivue card with enable toggle, masked API-key field,
"Test connection" button (validates the key viaGET /api/v1/me
without saving), and dirty-state tracking. Settings persist in NVS
(aer_en,aer_key); secrets are masked (***) in API responses. - New endpoint:
POST /api/uploads/test-aerivue. - Upload state tracking, retries, cooldowns and the progress-bar card
work identically to the existing backends — Aerivue appears as a
third row automatically. - Error mapping: 401/403 → permanent, 404 (expired import) / 5xx /
content_hash_mismatch→ transient with automatic recovery;
import-result details (sessions imported/skipped, nights imported,
per-recording oximetry results) are logged.
Modular upload architecture
- Extracted the shared TLS/HTTP/multipart plumbing (~560 lines) from
uploader_sleephq.cintoupload_http.c/.h— this is the seam
future backends (e.g. Breathing Room) plug into. uploader_sleephq.crefactored onto the shared layer; wire
behaviour unchanged.- New
supports_oxbackend capability flag — the scheduler skips
oximetry units for backends that can't take them (Breathing Room's
oximetry endpoint is token-gated today).
Resilience: keep-alive connection handling
- Long backfills could die mid-session when the remote edge closed the
keep-alive connection (observed at ~1,000 requests / ~12 min on one
socket — consistent with a Cloudflare edge request cap; the failure
cascaded across groups until the day rolled back). - The shared HTTP layer now detects
Connection: close, peer closes
and TLS transport errors, marks the socket dead, reconnects
transparently and retries the interrupted request once. s_import_idsurvives socket reconnects — a day that loses its
connection mid-flight continues into the same server-side import
(48 h TTL) instead of orphaning it.
Verified
- First production sync: 45/45 days (148 sessions) and 77/77 oximetry
recordings landed, none refused. - 16/16 host tests pass.