Skip to content

Skin temp: fix Fahrenheit (deviation must not get the +32 offset) (#111)#115

Merged
ryanbr merged 1 commit into
mainfrom
fix-skintemp-fahrenheit
Jul 8, 2026
Merged

Skin temp: fix Fahrenheit (deviation must not get the +32 offset) (#111)#115
ryanbr merged 1 commit into
mainfrom
fix-skintemp-fahrenheit

Conversation

@ryanbr

@ryanbr ryanbr commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Fixes #111 — skin temp shows nonsense in Fahrenheit on a WHOOP MG (reported "24.4 °F"), with a broken graph axis/scale.

Root cause

The skin-temp metric's stored value is either an absolute reading (WHOOP export gives absolute °C) or a signed deviation from your baseline (±°C, the live/computed pipeline). Its Fahrenheit conversion always used the absolute formula F = C×9/5 + 32. Adding +32 to a deviation is wrong — and only shows in Fahrenheit (Celsius appends the raw deviation, so it looked fine). The math confirms the report: 24.4 °F = (−4.2) × 1.8 + 32 — a deviation run through the absolute formula. The graph's y-axis inherited the same corruption (the "scale on the side" that didn't make sense).

Fix

MetricDescriptor.format now picks the formula per value via VitalBands.isAbsoluteSkinTemp(v) (v >= 20 °C):

  • deviation (< 20 °C) → temperatureDeltaFromCelsius (×9/5, no +32)
  • absolute (>= 20 °C, e.g. an imported WHOOP reading) → full C→F (×9/5 + 32)

This mirrors the Android HealthScreen per-value branch (which was already correct), so iOS/macOS now agree with Android. Because the hero value, the chart readout, and the min/max/avg stat tiles all format through format, one change fixes all of them. Celsius output is unchanged.

Re-review note

My first pass just forced skin temp to always-delta — but that would have broken the absolute-import case (34 °C → "61.2 °F" instead of 93.2). Checking Android surfaced the per-value isAbsoluteSkinTemp split; the fix now matches it.

Testing

Test added: −4.2 → "-7.6 °F", 0.6 → "1.1 °F" (delta) and 34.0 → "93.2 °F" (absolute), Celsius unchanged. Can't build the app target / run StrandTests on WSL (Xcode + GRDB), so a testing build verifies the Swift compiles.

Caveat (separate from this fix)

The reporter's underlying value works out to ~−4.2 °C deviation on a live WHOOP MG, which is implausibly large — possibly "no personal baseline yet / calibrating," or an MG decode/baseline issue. The +32 corruption is the definite, reproducible bug (fixed here); I'll ask them for a debug export to sanity-check the raw MG reading. Also noted but out of scope: FullDayChartView shows skin temp only in °C (ignores the °F preference) — a pre-existing, separate gap.

File: Strand/Data/MetricCatalog.swift (+ test).

…111)

The skin-temp metric formatted its Celsius value with the ABSOLUTE C→F formula
(×9/5 + 32) even when the value is a signed DEVIATION from baseline. Adding +32 to
a deviation is nonsense, and only shows in Fahrenheit (Celsius appends the raw
deviation, so it looked fine) — a −4.2 °C deviation rendered as "24.4 °F" (the
report), and the graph axis inherited the same corruption.

Fix: MetricDescriptor.format picks per value via VitalBands.isAbsoluteSkinTemp
(v >= 20 °C) — a DEVIATION uses temperatureDeltaFromCelsius (×9/5, no +32), an
ABSOLUTE reading (WHOOP export gives absolute °C) keeps the full C→F. This mirrors
the Android HealthScreen per-value branch, so the two platforms now agree. Fixes
the hero value, chart readout, and the min/max/avg stat tiles (all route through
`format`). Celsius output is unchanged.

Test: skin_temp descriptor formats −4.2 → "-7.6 °F", 0.6 → "1.1 °F" (delta), and
34.0 → "93.2 °F" (absolute); Celsius unchanged.

NB the reporter's underlying ~−4.2 °C deviation on a live WHOOP MG is itself large
(possibly no baseline yet / calibrating) — the reply asks for a debug export to
sanity-check the raw MG reading; the +32 corruption is fixed regardless.
@ryanbr ryanbr merged commit 87b2340 into main Jul 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Skin temp reads inaccurately in Fahrenheit

1 participant