github ilyakruchinin/SomnoTrace v1.3.0

4 hours ago

SomnoTrace — Changes since v1.2.2

Reliability: Fixed connection failures and battery drain

Stale connection handle (issue #167)

After a ring disconnects on its own (e.g. it powers off after a sync), the
BLE stack sometimes left a stale signal that made the firmware think it was
still connected. Every subsequent connection attempt would then fail
silently with "Legacy service not found" or error code 6, and the ring
would never sync again until a reboot. The firmware now drains any stale
signals before each connection attempt, so recovery from a remote
disconnect is automatic.

Fast-retry limit for failed pulls

When a sync attempt fails (e.g. the ring was picked up mid-sync), the
firmware used to retry aggressively every 15 seconds for as long as the
ring kept advertising. This burned battery on both devices. Now it allows
up to 3 quick retries while the ring's sync window is still open (~19
seconds), then applies a 130-second cooldown to let the ring power off
and conserve battery. The counter resets on a successful sync.

Accuracy: Fixed cross-generation ring misidentification

The core bug: Gen2 rings handled by the Gen1 driver

Users with both a Gen1 (O2Ring, KidsO2, etc.) and a Gen2 (O2Ring-S /
SHQO2Pro / S8-AW) ring nearby would see the Gen2 ring falsely detected as
a Gen1 device. This happened because the old detection logic matched any
device name containing "O2" — and "SHQO2Pro" contains "O2". The Gen1
driver would then waste its entire 15-second scan window trying to
connect to a Gen2 ring that doesn't speak the Gen1 protocol, reporting
"Legacy service not found" and potentially missing the real Gen1 ring's
sync window entirely.

New detection: advertisement data only, no name guessing

Ring generation is now determined purely from BLE advertisement data:

  • Gen1 rings are identified by their service UUID
    (14839ac4-…) in the advertisement. This is more reliable than name
    matching — some rings stop broadcasting their name after the first
    connection, but the service UUID is always present.

  • Gen2 rings are identified by their manufacturer ID (0xF34E) in
    the advertisement. The Gen2 ring's own service UUID is not advertised
    (it's only discoverable after connecting), so the manufacturer ID is
    the only reliable advertisement-level signal.

Device names are still captured and displayed in the UI — they're just no
longer used to decide which driver handles which ring. This eliminates the
entire class of name-based false positives across generations.

Scan result merge simplified

The workaround that tried to override misclassified Gen2 entries in the
merged scan list is no longer needed and has been removed. The scan
results shown in the web UI are now a clean union of both drivers'
findings, with a simple address-based deduplication as a safety net.

Accuracy: Connect to the right ring when multiple are nearby

Paired device selection from scan results

Previously, when multiple rings of the same generation were nearby, the
firmware would blindly connect to the first device found in the scan
results — even if it wasn't the paired one. Now it searches the scan
results for the address matching the paired ring and connects to that
specific device. If the paired address isn't found (e.g. the ring's MAC
changed after a factory reset), it falls back to the first device with a
warning log, and the serial number check during sync remains as the final
identity gate.

This also fixes a cosmetic issue where the paired address shown in the UI
could be temporarily overwritten by a nearby ring's address.

Usability: BLE device name in the UI

Show the real advertised name

The firmware now captures and stores the BLE advertised name (e.g.
"O2Ring", "SHQO2Pro", "KidsO2") at pairing time and displays it on both
the Status page and the Settings page, instead of constructing a generic
label from the serial number. For Gen1 rings that don't include a serial
in their advertised name, a display name is synthesized by appending the
last 4 digits of the serial (e.g. "O2Ring 1234").

Real-time state updates

Oximeter state changes (Idle → Scanning → Connecting → Syncing → Paired)
are now pushed to the web UI immediately via WebSocket, instead of waiting
for the next polling cycle. The "Syncing…" indicator appears and
disappears without the delay users previously saw.

Don't miss a new SomnoTrace release

NewReleases is sending notifications on new releases.