Coach DrépanoCoach DrépanoNight, oxygen and measurements
Algorithm transparency

Night, oxygen and measurements

Night oxygen and sleep, everyday measurements and the usual range of each value.

Each algorithm is described with what it reads, its steps, the values it uses (with their place in the code), what it shows and its limits.

Sensor and algorithm bias: what Trévor says and the measures offeredWeather and air quality at your home locationDaily watch measurements: steps, pulse, HRV, temperature, stress, and detected signalsFifteen days of "moments": summary of your hard moments and the environmentMy night: your usual range, lower nights, time below 90 percent and alertsNight oxygen: average, lowest point, time below threshold, desaturation indexNight quality, ignored nights and source choiceManual entry: night oxygen, fingertip oximeter, how you slept and bedtimeNight sleep: duration, efficiency, awakeningsOxygen estimate from the phone camera (experimental)Watch measurement quality: good, medium or insufficientAligned series of the "dashboard of your life"The low oxygen threshold (92 percent) and nearby thresholds: where they apply and how to set themUsual range of each measurement and value alerts

← All algorithms

Sensor and algorithm bias: what Trévor says and the measures offered

Trévor explains that oximeters, watches, rings and phone cameras can show oxygen higher than reality on dark skin, and that its own calculations inherit this bias. It offers five practical measures. This text is displayed and is not a calculation.

Where you see it: Bot: button "Sensor bias, possible measures" in "My oxygen" and the end of the "My night" screen; app (GET /api/phone/nuit).

What the algorithm reads

The calculation, step by step

Sensor biasAn oximeter estimates oxygen from light passing through skinMelanin absorbs some of it: on dark skin, the displayed oxygen ison average higher than reality and a real shortage can gounnoticedWatches, rings and cameras rely on the same principle; on thewrist, movement, a loose strap and tattoos add to itAlgorithm biasTrévor's calculations start from these measurements and fromreferences drawn from studies that do not represent allpopulationsThey inherit the same bias; the displayed risk is not validatedfor every skin color: a marker, never proofPossible measuresRead each value against your own usual range and follow the trendover several nightsMeasure several times, at rest, warm hands and no nail polish;compare two sites or two devicesTry a forehead oximeter (reflectance), which some studies say isless sensitive to skin color, but data are limited and it is notfree of biasHave a doubtful value confirmed by the care team (blood gas)Give priority to symptoms (shortness of breath, bluish lips orfingers, dizziness)Reminder on My night"Your watch or ring is neither a diagnosis nor a medical device.It makes mistakes more easily when you move. A normal figuretherefore does not mean everything is fine."
Flowchart: Sensor and algorithm bias: what Trévor says and the measures offered

What is shown

Three fixed messages ("Sensor bias", "Algorithm bias", "Possible measures") shown with the night reminder. No value is corrected according to skin color: Trévor applies no numeric bias correction, neither for the watch nor for the camera.

A worked example

none

What this algorithm cannot do

Validation. Text drafted from references listed in docs/NUIT_SOMMEIL_OXYGENE.md: Sjoding 2020 (PMID 33326721, figures not re-read), Wong 2021 (PMID 34730820), Fawzy 2022 (PMID 35639368), Gottlieb 2022 (PMID 35816344), Blaisdell 2000 (PMID 10980793) and Ortiz 1999 (PMID 9927356) on oximetry in sickle cell disease, and the FDA draft guidance of January 7, 2025. The statement that Trévor's calculations are not validated for every skin color is accurate according to the code: no validation by skin color appears there.

Code : data/i18n/fr.json (textBiasWhy, textBiasAlgo, textBiasMeasures, textBiasBtn, textNightDisclaimer), src/trevor/conversation/night_screen.py (blocks), src/trevor/conversation/spo2_screens.py (on_spo2), src/trevor/media/ppg_spo2.py (docstring)

Weather and air quality at your home location

Each day Trévor fetches the weather and air quality where you live and keeps a daily summary, to set it beside how you are doing. It invents nothing: with no location or no service response, the series stays empty.

Where you see it: Environment curves on the life dashboard, the fifteen-day moments summary, daily context.

What the algorithm reads

The calculation, step by step

Location known?yesnoCalls both servicesNothing is written (neverGeneva's weather)Forecast (Open-Meteo)Per day: max and min temperature, min apparent temperature, meanhumidity, mean pressure, max wind, max gusts, rain, max UV2 past days by default, 60 on the first call, 92 at mostComputed variablesTemperature drop = max(0, yesterday's max temperature - today's)Pressure change = today's pressure - yesterday'sDaily range = max - minAltitude of the placeAir (Open-Meteo Air Quality)Daily mean: PM2.5, PM10, dustDaily maximum: nitrogen dioxide, ozone, European air qualityindex, pollen (sum of the maxima of six species)UV: the forecast value takes priority, the air value fills gapsSavingKeeps days up to today and at most 92 days backToday and yesterday are refreshed on each callA network failure writes nothing
Flowchart: Weather and air quality at your home location

The values used

Name in the codeValueRoleWhere
MAX_PAST_DAYS92Open-Meteo ceiling for past dayssrc/trevor/navigator/environment.py:23
BACKFILL_DAYS60History requested on activationsrc/trevor/navigator/environment.py:24
place_key2 decimalsPlace rounded to about 1 kmsrc/trevor/navigator/environment.py:54
timeout10 sTimeout of each callsrc/trevor/navigator/environment.py:127
units°C ; % ; hPa ; km/h ; mm ; µg/m³ ; grains/m³ ; mVariable unitssrc/trevor/navigator/environment.py:28-48
evidence levelmoderate, low, noneEvidence level noted for each variable from the October 7, 2026 research (min and max temperature, temperature drop, apparent temperature, wind, PM2.5: moderate; UV, dust, pollen: none)src/trevor/navigator/environment.py:27-48

What is shown

A day-by-day table of the variables, with the attribution "Open-Meteo, CAMS (Copernicus)". Nothing says these variables cause a crisis.

A worked example

Computed by running the code: on November 4, max 12.0 C, min 4.0 C, pressure 1015.0 hPa; on November 5, max 7.5 C, min 1.0 C, pressure 1008.2 hPa. For November 5: temperature drop 4.5 C, pressure change -6.8 hPa, daily range 6.5 C.

What this algorithm cannot do

Validation. The evidence levels per variable come from research of October 7, 2026 summarized in the code; no reference is cited in environment.py (see docs/NAVIGATEUR_PERSONNEL.md). The formulas (drop, change, range) are simple differences.

Code : src/trevor/navigator/environment.py (fetch_days, parse_forecast, parse_air, record, location_of)

Daily watch measurements: steps, pulse, HRV, temperature, stress, and detected signals

Trévor stores your watch or ring measurements as one value per day, drops impossible values, fills in the temperature deviation when the device gives only the temperature, then compares your last 7 days with the 30 days before. It also lists factual signals (low oxygen, high pulse, low HRV, temperature, breathing, short nights). This is not a validated medical device.

Where you see it: "Your measurements: average of the last 7 days" and "Detected signals" summary in the bot, journal curves, daily monitor, the watch "My health" screen, and coaching sentences without numbers.

What the algorithm reads

The calculation, step by step

Cleaning a dayKeeps only known measurements, as numbersDrops: oxygen outside 50 to 100; pulse outside 25 to 250; sleepoutside 0 to 1440 min; steps outside 0 to 200,000; HRV zero orabove 400 ms; respiratory rate outside 4 to 60; temperaturedeviation outside -5 to 5; skin temperature outside 25 to 42 CAn absolute temperature received as a deviation (25 to 42) isstored as a temperatureToday's day?yesnoMarked "partial": steps andspot pulse are left out ofcurves, averages and themodel; only nightmeasurements (sleep, night,HRV, temperature) countComplete dayMerging sourcesOne entry per day: for the same measurement, the last source winsOura and Fitbit come last, so they take priority over files andentered oximeter valuesA complete day received later replaces the "partial" versionTemperature deviationIf the watch gives only temperature: deviation = the night'stemperature minus the median of nights in the previous 30 days,with at least 3 nightsA deviation supplied by the device is kept as isAveragesAverage of the last 7 days and average of the previous 30 days forten measurements (steps, sleep, resting pulse, HRV, oxygen,readiness, temperature deviation, temperature, breathing, noise)A difference is written if it reaches 5 percent of the reference(or 1 if the reference is 0), except for temperature deviationDetected signals (if interpretation is on)A day's night oxygen below 92 percentResting pulse at least 10 bpm above the median of the previous 30days (at least 5 days)HRV at 75 percent or less of the median of the previous 30 days(at least 5 days)Temperature deviation of at least +0.5 CRespiratory rate of 20/min or moreUnder 5 h of sleep 3 nights in a row (nights of 2 h or more only)Number-free coaching sentencesUp to three sentences: sleep shorter or longer by 45 min or more,steps down or up by 20 percent, resting pulse 7 bpm or more aboveor below, readiness under 60 or HRV down 15 percent, recent oxygenunder 92 percent, temperature at least 0.5 C
Flowchart: Daily watch measurements: steps, pulse, HRV, temperature, stress, and detected signals

The values used

Name in the codeValueRoleWhere
SUMMARY_KEYSsteps, sleep_minutes, resting_hr, hrv, spo2, readiness, temp_deviation, skin_temp, respiratory_rate, noise_dbMeasurements summarizedsrc/trevor/wearables/service.py:49
NIGHT_KEYSnight, sleep_minutes, hrv, skin_temp, temp_deviationNight measurements kept in a partial daysrc/trevor/wearables/service.py:48
difference threshold5 %Difference displayed if it reaches 5 percent of the referencesrc/trevor/wearables/service.py:399
TEMP_BASELINE_DAYS30Window for the temperature mediansrc/trevor/wearables/models.py:112
TEMP_BASELINE_MIN3Minimum nights for a temperature deviationsrc/trevor/wearables/models.py:113
pulse signal+10 bpmResting pulse vs median of the previous 30 days (5 days minimum)src/trevor/wearables/service.py:488
HRV signal0.75 x referenceHRV at least 25 percent below the mediansrc/trevor/wearables/service.py:490
temperature signal0.5 °CSkin temperature deviationsrc/trevor/wearables/service.py:492
breathing signal20 /minRespiratory ratesrc/trevor/wearables/service.py:494
sleep signal300 min, 3 nights, 120 min minimumUnder 5 h three nights in a row; under 2 h the night does not countsrc/trevor/wearables/service.py:497-505
ambient noisedB(A)Sound exposure read by the watch; no sound is keptsrc/trevor/wearables/models.py:31
stress_score/100Watch stress score (daily average for Samsung); not in the summary, read by the risk modelsrc/trevor/wearables/files.py:314

What is shown

For each measurement: average value over the last 7 days, difference from the previous 30 days if large enough, and, if interpretation is on, a list of factual signals with the date. Example line: "Resting heart rate: 58 bpm (+6 vs previous 30 days)". Signals are presented as facts to discuss with your care team.

A worked example

Computed by running the code, as of November 6, 2026: 24 days at 60 bpm resting pulse, then on October 30 a pulse of 71 bpm and oxygen of 91 percent. Result: two signals on October 30, "Night SpO2 91 percent" and "Resting HR 71 bpm (reference 60)". Another example: 7 nights of skin temperature alternating 33.1 and 32.9, then 34.0: computed deviation +0.9 C (median of earlier nights: 33.1).

What this algorithm cannot do

Validation. The plausibility bounds, the 5 percent, 10 bpm, 25 percent HRV, 0.5 C and 5 h are Trévor choices. The code says 20 breaths per minute is a usual tachypnea threshold in adults, used in early warning scores (no reference cited), and that 92 percent is a common alert threshold for consumer oximeters. The 30-day temperature window is described in the code as "like Oura, Fitbit and Apple", with no reference. This is not a validated medical device and these signals are not learned from data of people with sickle cell disease.

Code : src/trevor/wearables/service.py (_day_rows, series, summary, summary_text, qualitative_summary, grounded_values, signals, handle_webhook), src/trevor/wearables/models.py (clean_day, derive_temp_deviation, merge_days), src/trevor/storage/database.py (upsert_wearable_days, wearable_days), src/trevor/wearables/files.py, src/trevor/wearables/day_view.py

Fifteen days of "moments": summary of your hard moments and the environment

If you turn it on, for fifteen days Trévor asks twice a day how you feel right now (good, medium, not good). At the end it compares hard days with other days on the environment and writes at most two conditions that differ, without speaking of cause.

Where you see it: Bot questions at 10-11 am and 4-5 pm; summary sent the day after the period ends.

What the algorithm reads

The calculation, step by step

Explicitly turned on?yesno15-day period starting todayNever by default: no questionSlotsMorning at 10 or 11, afternoon at 4 or 5 pm (Geneva time), at mosttwo questions per dayNo question in hospital, after 3 ignored reminders, for a blockedor non-patient accountAnswersGood = 2, medium = 1, not good = 0; at most 120 answers keptHardest slotWith at least 4 morning and 4 afternoon answers: if the share of"not good" differs by at least 25 points, the hardest slot isnamedHard days and other daysHard day: at least one "not good" answer; other day: no "not good"answerAt least 3 days of each kind are neededStandardized difference ofat least 0.5 for a variable?yesnoVariable kept (at most 2, thestrongest): mean on hard daysand on other daysNothing is cited
Flowchart: Fifteen days of "moments": summary of your hard moments and the environment

The values used

Name in the codeValueRoleWhere
BURST_DAYS14Length of the period (today + 14 days = 15 days)src/trevor/navigator/ema.py:20
SLOT_HOURSam 10-11, pm 16-17Geneva hours at which a slot can firesrc/trevor/navigator/ema.py:21
MAX_LOG120Answers keptsrc/trevor/navigator/ema.py:22
MIN_DAYS_PER_GROUP3Minimum days per groupsrc/trevor/navigator/ema.py:23
MIN_EFFECT0.5Minimum standardized difference (Cohen's d)src/trevor/navigator/ema.py:24
MAX_CONDITIONS2Conditions cited at mostsrc/trevor/navigator/ema.py:25
SCOREgood 2, mid 1, bad 0Answer valuessrc/trevor/navigator/ema.py:26
slot gap0.25Difference in share of "not good" to name a slotsrc/trevor/navigator/ema.py:122

What is shown

"Over N moments: X good, Y medium, Z not good", possibly the hardest slot, and at most two sentences like "On harder days, [condition]: mean A versus B". A last sentence recalls that these figures are fifteen-day markers and never indicate a cause. Answers are not used for crisis alerts.

A worked example

Computed by running the code: for three hard days with a maximum temperature of 2, 4 and 3 C and three other days at 8, 9 and 10 C, the standardized difference is -1.93; its absolute value exceeds 0.5, so this variable is kept (mean 3.0 versus 9.0).

What this algorithm cannot do

Validation. The ecological momentary assessment principle is cited in the code; 0.5 as an effect size, 3 days and 25 points are Trévor choices with no study cited. No statistical test.

Code : src/trevor/navigator/ema.py (due_slot, answer, summary, _group_stats, summary_lines), src/trevor/navigator/environment.py (VARIABLES)

My night: your usual range, lower nights, time below 90 percent and alerts

Trévor compares each night's average oxygen to your own usual range from the previous 30 nights and flags nights that are clearly lower. It also reports time below 90 percent, a run of low nights and a very low measured oxygen, in which case safety comes first. This is not a diagnosis.

Where you see it: "My night" screen in the bot and GET /api/phone/nuit; an optional soft alert (off by default) sent at most once a day.

What the algorithm reads

The calculation, step by step

Your usual rangeFor each night: median and robust spread of the averages from theprevious 30 nights (quality not low)Robust spread = 1.4826 x MAD, at least 0.7 pointAt least 7 nights, otherwise no usual rangeNight z <= -1.5?yesnoNight "lower than your usualrange" (⚠️); z = (average -median) / spread, clipped at±4Ordinary nightCountersLow nights over 14 daysCurrent run: low nights in a row up to the latest (today's night,if not yet arrived, is skipped)Nights with time below 90 percent: p90 above 0Largest share below 90 percentRun of 3 or more low nights?yesnoSentence "talk to your careteam in the next few days";soft alert if you turned itonNo repeat sentenceLatest usable night average(yesterday or today) <= 88percent, or oximeter <= 88percent?yesnoEmergency message at the topof the screen: call emergencyservices without waiting(number depends on yourcountry)General safety message at thetopNight sweatsSentence shown only with at least 10 nights (90 days) where usableoxygen and the sweat answer both exist, at least 3 low nights withsweats, and a share of low nights with sweats at least double theshare without sweats (at least 5 percent)
Flowchart: My night: your usual range, lower nights, time below 90 percent and alerts

The values used

Name in the codeValueRoleWhere
NIGHT_TOO_FEW7Measured nights needed to speak of "your usual range"src/trevor/wearables/service.py:1068
NIGHT_DROP_Z-1.5 (crisis.DROP_Z)A night is "low" at 1.5 deviations or more below the usual rangesrc/trevor/wearables/crisis.py:189
BASELINE_DAYS30Prior nights for the usual rangesrc/trevor/wearables/crisis.py:121
MIN_SD oxy_mean0.7Minimum spread of the oxygen usual range (points)src/trevor/wearables/crisis.py:166
night_spo2_very_low88 %Very low oxygen: emergency ("to be validated by HUG")data/emergency_thresholds.json:11
night_spo2_repeated3 nightsLow nights in a row before the repeat sentencedata/emergency_thresholds.json:12
sweat n_both10Minimum nights for the sweat sentencesrc/trevor/wearables/service.py:1136
sweat window90 daysPeriod for the sweat and low-night associationsrc/trevor/wearables/service.py:1155
days shown14Nights shown on screensrc/trevor/conversation/night_screen.py:102

What is shown

A two-message screen: safety first (emergency or reminder), your usual range ("average oxygen X percent, over N nights"), one line per night (date, average oxygen, sleep, flags ⚠️ 🔄 👍 👎 💦), then lower nights, time below 90 percent, the quality warning, watch-versus-answer divergence, sweats, the run of low nights, the sensor warning and general advice. The lower cutoff of your usual range is computed internally (low_cut = median - 1.5 x spread) and not written in the text.

A worked example

Computed by running the code: 8 nights at 95, 95.5, 96, 95, 96.5, 95.5, 95 and 96 percent give a median of 95.5 and a spread of 0.74. A night at 94.0 percent has z = (94 - 95.5) / 0.74 = -2.0, so it is flagged lower (lower cutoff 94.4 percent). A night at 95.0 percent has z = -0.7 and is not flagged.

What this algorithm cannot do

Validation. The median and robust spread (MAD) are standard statistical methods, but the thresholds (7 nights, 30 nights, -1.5 deviations, 3 nights in a row) are Trévor choices: the emergency file says of the 3 nights "no study sets this number". The 88 percent threshold is described as a usual hypoxemia threshold in pulmonology, not specific to sickle cell disease, with a non-validated consumer sensor; text and threshold are to be validated by HUG (M-OXY-1). The link between night hypoxemia and sickle cell complications is documented in docs/NUIT_SOMMEIL_OXYGENE.md (for example Hargrave 2003, PMID 12393400; Quinn 2005, PMID 16173973), but no Trévor numeric rule derives from it. This is not a validated medical device.

Code : src/trevor/wearables/service.py (night_view, _night_pairs, _recent_oximeter, night_alert_due), src/trevor/conversation/night_screen.py (blocks, row, payload), src/trevor/wearables/crisis.py (_robust, _z, DROP_Z), data/emergency_thresholds.json, src/trevor/conversation/reminders.py (alerte douce, vers 1565)

Night oxygen: average, lowest point, time below threshold, desaturation index

When your watch or ring sends a series of oxygen readings during the night, Trévor boils it down to a few numbers: average, lowest point, share of the night below 92, 90 and 88 percent, and drops per hour. These numbers feed the "My night" screen and your usual range. This is not a validated medical device.

Where you see it: "My night" screen in the bot (one line per night with the average oxygen), the app (GET /api/phone/nuit), and the "My landmarks" curves (average oxygen, lowest point, time below 90 percent, drops per hour).

What the algorithm reads

The calculation, step by step

Sort the readingsKeeps values from 50 to 100 percentSorts by time; at most 20,000 readingsTypical gap between readingsMedian of the gaps between successive readingsGap above 5 minutes?yesnoSeries too sparse: only theplain average and the lowestvalue, no time share and noindex; quality at best"medium"Continue with thetime-weighted calculationWeight of each readingWeight = time until the next reading if 5 minutes or less,otherwise 0Last reading: typical gapValid time = sum of weightsAverage and lowest pointTime-weighted averageLowest point = lowest value held on two consecutive readings (thehigher of the two, then the minimum)Time below thresholdPercent of valid time below 92, below 90 and below 88A reading at least everyminute and 2 valid hours?yesnoDesaturation index at 3points (odi3) and 4 points(odi4), in events per validhourNo indexCoverage, movement, qualityCoverage = valid time / night length (0 to 1)Quality: good, medium or low (see the night-quality algorithm)
Flowchart: Night oxygen: average, lowest point, time below threshold, desaturation index

The values used

Name in the codeValueRoleWhere
THRESHOLDS(92, 90, 88)Thresholds for cumulative time below saturationsrc/trevor/wearables/night.py:26
MAX_GAP_S300Beyond 5 minutes without a reading, the time is not counted as measuredsrc/trevor/wearables/night.py:27
ODI_MAX_STEP_S60Maximum typical gap to compute a desaturation indexsrc/trevor/wearables/night.py:28
ODI_MIN_VALID_MIN120Minimum valid minutes for an indexsrc/trevor/wearables/night.py:29
BASELINE_WINDOW_S120Reference level of an event: maximum of the previous 2 minutessrc/trevor/wearables/night.py:30
MAX_SAMPLES20000Maximum number of readings readsrc/trevor/wearables/night.py:35
drop in _count_events3 and 4 pointsAn event is a drop of at least 3 (odi3) or 4 points (odi4) below the reference, held for two readingssrc/trevor/wearables/night.py:93-94,110-123

What is shown

One summary per night: mean (1 decimal), min (lowest point), p92, p90, p88 (percent of valid time), odi3 and odi4 (per hour, only if the gap is 60 s or less and at least 2 hours are valid), valid_min, coverage, motion, quality. An event ends when saturation climbs back to within (threshold - 1) points of its reference; a single isolated low reading does not count.

A worked example

Computed by running the code: 480 readings, one per minute, at 96 percent, including 3 readings at 91 and 10 readings at 89, 480-minute night. Result: average 95.8; lowest 89; p92 2.7 percent; p90 2.1 percent; p88 0 percent; odi3 and odi4 0.2 per hour; coverage 1.0; quality "good".

What this algorithm cannot do

Validation. The desaturation index definitions (a drop of 3 or 4 points below the maximum of the previous 2 minutes) follow the practice described in docs/NUIT_SOMMEIL_OXYGENE.md (which cites Berry 2012, AASM rules, PMID 23066376). The quality cutoffs, the 5 minutes, the 2 minutes and the two-consecutive-readings rule are Trévor choices set without a study of their own. Consumer sensors are assessed in the literature cited in the repository (Walzel 2023, PMID 38005550) on young healthy people, not on sickle cell disease. This is not a validated medical device.

Code : src/trevor/wearables/night.py (summarize_oxygen, _count_events, oxygen_from_aggregate), src/trevor/wearables/files.py (night_from_item), src/trevor/wearables/oura.py, src/trevor/wearables/fitbit.py

Night quality, ignored nights and source choice

Trévor rates each measured night "good", "medium" or "low" based on measured time, coverage and movement. A "low" night stays visible but is left out of every calculation (your usual range, lower nights, curves). When several sources describe the same night, Trévor keeps the best one.

Where you see it: "My night" screen (a quality warning when a night is low), "My landmarks" curves, and the risk model (which also ignores low nights).

What the algorithm reads

The calculation, step by step

Assign the reading to a nightFrom 8 pm to midnight: the night ends the next dayMidnight to 10 am: same day10 am to 8 pm: reading ignoredSeries of readings?yesnoLow quality if under 120valid minutes, or coverageunder 50 percent, or movementabove 30 percentSummary only: low in the samecases, otherwise never betterthan "medium"Series: medium or goodMedium if coverage under 80 percent, or movement above 15 percent,or fewer than 30 readingsOtherwise goodSeveral sources for the same nightFor oxygen: series before summary, then better quality, then morevalid minutesFor sleep: device stages before no stages, then more fields, thenqualityOn a tie, the source that arrived last wins (Oura and Fitbit comeafter the others)Quality "low"?yesnoThe value becomes absent(None) in calculations: nousual range, no low night, nocurveThe night counts incalculations
Flowchart: Night quality, ignored nights and source choice

The values used

Name in the codeValueRoleWhere
MIN_VALID_MIN120Under 2 valid hours, low qualitysrc/trevor/wearables/night.py:31
MIN_COVERAGE0.5Under 50 percent of the night covered, low qualitysrc/trevor/wearables/night.py:32
MOTION_HIGH0.30More than 30 percent of the night in motion: low qualitysrc/trevor/wearables/night.py:33
MOTION_MID0.15More than 15 percent in motion: medium qualitysrc/trevor/wearables/night.py:34
average coverage0.8Series covering less than 80 percent: medium qualitysrc/trevor/wearables/night.py:131
number of measurements30Fewer than 30 readings: medium qualitysrc/trevor/wearables/night.py:131
night hours8 pm to 10 amWindow for assigning a reading to a nightsrc/trevor/wearables/night.py:50-58

What is shown

A quality label per night (good, medium, low). `oxy_usable` and `sleep_usable` return the night only if quality is not "low" (and, for oxygen, only if an average exists). A manual entry gets 480 valid minutes, so "medium".

A worked example

Computed by running the code: the 480-reading series from the previous example (coverage 1.0, 480 valid minutes, 480 readings) gives "good". A summary without a series (average and minimum only) gives "medium" at best.

What this algorithm cannot do

Validation. All thresholds (2 h, 50 percent, 30 percent, 15 percent, 80 percent, 30 readings) are Trévor choices, with no study cited in the code or in docs/NUIT_SOMMEIL_OXYGENE.md for these exact values. They come neither from a clinician nor from learning on data.

Code : src/trevor/wearables/night.py (assign_night, quality_of, oxy_usable, sleep_usable, scalars, by_date, _oxy_rank, _sleep_rank, clean_night), src/trevor/wearables/service.py (night_records)

Manual entry: night oxygen, fingertip oximeter, how you slept and bedtime

When your watch does not send the night's oxygen, you can type it yourself, along with a fingertip oximeter value, how you slept and an approximate bedtime. Trévor stores these entries as measurements without correcting them.

Where you see it: Bot: "My oxygen (SpO2)", "My night" (entry button), "My day" (night question).

What the algorithm reads

The calculation, step by step

Night oxygenReads up to three numbers of 2 or 3 digits (comma or point)Refuses if fewer than two numbers or if any is outside 50 to 100Sorting the numbersSorts the numbers: lowest is the lowest point, highest is thepeak, the middle one is the averageWith two numbers: the higher is the average, the lower is thelowest point, no peakSavingSaved for today (the wake-up day), source "manual", with 480 validminutesThe block goes through the same cleaning as other sources: quality"medium"The peak is displayed but weighs in no calculationFingertip oximeterFirst number in the text between 70 and 100Kept in the profile (60 values) and in daily measurements (source"oximeter")Below 92 percent: safety message; call emergency services (thenumber depends on your country) without waiting if the value staysunder 92 percent after two minutes of rest or if you have troublebreathingNight feel and bedtimeOne entry per day (the new one replaces the old one), 60 nights atmostThree "bad" nights in 7 days: one sentence in "My day", never anotificationIf the watch already measured the duration, the bot shows it andonly asks how it felt
Flowchart: Manual entry: night oxygen, fingertip oximeter, how you slept and bedtime

The values used

Name in the codeValueRoleWhere
night entry range50 to 100Each number in the night entrysrc/trevor/conversation/night_screen.py:90
manual valid_min480Valid minutes assigned to a manual entrysrc/trevor/conversation/night_screen.py:132
oximeter range70 to 100Accepted oximeter valuesrc/trevor/conversation/spo2_screens.py:41
LOW92.0 (ppg_spo2.LOW)Below this, safety message on the entered valuesrc/trevor/conversation/spo2_screens.py:24
KEEP (oximeter)60Oximeter values keptsrc/trevor/conversation/spo2_screens.py:25
KEEP_NIGHTS60Felt nights keptsrc/trevor/navigator/sleep.py:22
BAD_NIGHTS_ADVICE / ADVICE_WINDOW_DAYS3 / 7"Bad" nights in 7 days before the advicesrc/trevor/navigator/sleep.py:23-24
SELF_SLEEP_Zgood +1.0; average 0; bad -1.0Declared night seen as a deviation, on the watch measurement scale (used by the daily form)src/trevor/navigator/sleep.py:26

What is shown

A saved oxygen night (average, lowest, optional peak) visible in "My night", an oximeter value in the curves and value alerts, a felt night stored in the profile. Below 92 percent: "Redo the measurement after two minutes of rest, seated, hands warm. If the value stays below 92 percent, call now."

A worked example

Based on the parsing code: "94 88 97" gives average 94, lowest 88, peak 97; "93.5 90" gives average 93.5, lowest 90, no peak; "96" alone is refused.

What this algorithm cannot do

Validation. Ergonomics and plausibility rules set by Trévor, without a study. The "three bad nights" advice relies on the link between sleep and pain cited in navigator/sleep.py (NHLBI 2014; Moscou-Jackson et al. 2015, PMID 25842346), but the number three is a Trévor choice.

Code : src/trevor/conversation/night_screen.py (parse_night_values, on_night_enter), src/trevor/conversation/spo2_screens.py (parse_value, on_spo2_enter), src/trevor/navigator/sleep.py, src/trevor/conversation/daily_screen.py (screen_sleep_night, on_sleep_bed)

Night sleep: duration, efficiency, awakenings

Trévor takes what your device reports about your night (time asleep, time in bed, awakenings, stages) and computes only what can be computed without inventing anything, such as sleep efficiency. It then compares your night to your own usual range to spot a fragmented night.

Where you see it: "My night" screen (sleep duration for each night), "My landmarks" curves (efficiency, awakenings, deep sleep), and the daily form sentence.

What the algorithm reads

The calculation, step by step

Segments to summaryTime in bed = session length (or first to last segment)Time asleep = sum of sleeping segments (deep, light, REM, asleep)Latency = from session start to the first sleeping segment, onlyif the session starts awake or in bedAwakenings (WASO) = awake segments after falling asleep, only ifthe source provides "awake" segmentsPlausible boundsMinutes between 0 and 1440 (latency 600, awake time 1000, deep1000, REM 1000)Number of awakenings between 0 and 200Does the device giveefficiency?yesnoValue kept as is (0 to 100)Computed as 100 x asleep / inbed, only if both exist andtime in bed is at least timeasleepLabelsStages filled: "staged"Quality "medium" if staged, "good" if asleep and efficiency areknown, otherwise "medium"Screen markersEfficiency 85 percent or more: good; under 75 percent: poor; inbetween: medium (watch)Fragmented night: awake time at least 1.5 deviations above yourusual range or efficiency at least 1.5 deviations below
Flowchart: Night sleep: duration, efficiency, awakenings

The values used

Name in the codeValueRoleWhere
watch_sleep good85Efficiency from which the watch reading is called "good"src/trevor/wearables/service.py:1103
watch_sleep poor75Efficiency below which the watch reading is called "poor"src/trevor/wearables/service.py:1103
fragmentation threshold1.5Deviations from your usual range (awake time up or efficiency down)src/trevor/wearables/service.py:1106-1107,1109
MIN_SD sleep_eff / sleep_waso3.0 / 10.0Minimum standard deviations of the usual range for efficiency and awake timesrc/trevor/wearables/crisis.py:164-165
BASELINE_DAYS30Prior nights used for the usual rangesrc/trevor/wearables/crisis.py:121
MIN_BASELINE7Measured nights needed for a usual rangesrc/trevor/wearables/crisis.py:122

What is shown

Per night: durations in minutes, efficiency in percent, number of awakenings, stages if known. On screen: time asleep as "7 h 00", a fragmentation flag (used in the view) and, with your answer to "Did you sleep well?", a 🔄 sign when the watch and your answer disagree (answer "yes" with efficiency under 75 percent, or "no" with efficiency of at least 85 percent).

A worked example

Computed by running the code: 480 minutes in bed and 420 minutes asleep, with no efficiency given, give an efficiency of 88 percent (100 x 420 / 480) and quality "good". 88 percent is above 85, so the watch reads "good".

What this algorithm cannot do

Validation. Efficiency (asleep over in bed) is the usual definition. The 85 percent marker comes from the National Sleep Foundation report (Ohayon 2017, PMID 28346153); docs/NUIT_SOMMEIL_OXYGENE.md states that the 85 percent value was not re-read in that report (unverified). The 75 percent cutoff and the 1.5-deviation threshold are Trévor choices with no study cited. The accuracy of consumer watches for sleep is discussed in Chinoy 2021 (PMID 33378539) and Haghayegh 2019 (PMID 31778122), cited in that document.

Code : src/trevor/wearables/night.py (sleep_summary, sleep_from_segments, normalise_stage), src/trevor/wearables/service.py (night_view), src/trevor/conversation/night_screen.py (row), src/trevor/wearables/oura.py, src/trevor/wearables/fitbit.py

Oxygen estimate from the phone camera (experimental)

The phone has you rest your finger on the lens and flash, measures how the light pulses, and sends Trévor a ratio R, the signal quality and the pulse. Trévor converts R into a SpO2 estimate with a range, wider until you calibrate with a reference oximeter. This is experimental and not a validated medical device.

Where you see it: Coach Drépano app ("SpO2 by camera" card) and bot ("My oxygen": latest estimate, range and verdict sentence).

What the algorithm reads

The calculation, step by step

R between 0.2 and 2.5?yesnoContinueReading refused (reason"signal")Quality at least 0.5?yesnoContinueReading refused (reason"quality")Reference outside 70 to 100?yesnoReading refused (reason"reference")ContinueCalibration pairs(R, reference value) pairs already kept for the same color, plusthis reading's pair if it has a reference, a valid R andsufficient qualityAt least 3 usable pairs withvarying R?yesnoPersonal line SpO2 = A - B xR by least squares(decreasing slope required);margin = 2 x residual spread,at least 2 pointsUniversal line SpO2 = 110 -25 x R; margin of 8 pointsValue and rangeValue = A - B x R, clipped between 70 and 100Range = value ± margin, clipped between 70 and 100Value below 92?yesnoVerdict "low"Verdict "uncertain" if nopersonal calibration or ifthe bottom of the range isbelow 92, otherwise "normal"StorageKeeps the last 90 readings in the encrypted profile (date, R,quality, pulse, reference, color, value, bounds, calibrated,verdict)
Flowchart: Oxygen estimate from the phone camera (experimental)

The values used

Name in the codeValueRoleWhere
R_RANGE(0.2, 2.5)Plausible range of Rsrc/trevor/media/ppg_spo2.py:24
DEFAULT_A, DEFAULT_B110.0, 25.0Universal line: SpO2 = 110 - 25 x Rsrc/trevor/media/ppg_spo2.py:25
DEFAULT_MARGIN8.0Half-width of the range without calibration (SpO2 points)src/trevor/media/ppg_spo2.py:26
MIN_QUALITY0.5Minimum signal qualitysrc/trevor/media/ppg_spo2.py:27
MIN_PAIRS3(R, oximeter) pairs for a personal linesrc/trevor/media/ppg_spo2.py:28
LOW92.0Threshold for verdict "low"src/trevor/media/ppg_spo2.py:29
KEEP90Readings kept per personsrc/trevor/media/ppg_spo2.py:30
calibrated marginmax(2, 2 x residual deviation)Range with a personal line (residual spread at least 1)src/trevor/media/ppg_spo2.py:48,58
pair rangeR 0.2 to 2.5; SpO2 70 to 100Pairs kept for fittingsrc/trevor/media/ppg_spo2.py:35

What is shown

A rounded value, a range (low and high), whether it is calibrated, and a verdict. Bot sentences: "Low value.", "The range is too wide to conclude." or "No alert. This estimate does not replace a pulse oximeter." The "normal" verdict requires a personal line and the bottom of the range to stay at or above 92. Below 92 or if you feel unwell, a pulse oximeter then your care team or emergency services come first.

A worked example

Computed by running the code: R = 0.6 and quality 0.8 with no calibration give 95 percent (110 - 25 x 0.6), range 87 to 100 percent, verdict "uncertain". With four pairs (0.5, 98), (0.7, 93), (0.9, 88), (0.6, 95.5), the line becomes 110.5 - 25 x R with a residual spread of 1; R = 0.6 gives 96 percent between 94 and 98, verdict "normal"; R = 0.95 gives 87 percent between 85 and 89, verdict "low".

What this algorithm cannot do

Validation. The principle (ratio of ratios, decreasing line) is the photoplethysmography described in the code; the constants 110, 25 and the 8-point margin are Trévor values, and the universal line is declared not validated. The 92 threshold is aligned with value_alerts. No validation study is cited in the code or in docs/SPO2_CAMERA.md for this method. Oximetry gaps by skin color are documented by Sjoding 2020 (PMID 33326721; figures not re-read in the repository) and Wong 2021 (PMID 34730820). Experimental function, not a validated medical device.

Code : src/trevor/media/ppg_spo2.py (fit, estimate, add_reading, view), src/trevor/signals.py (spo2_cam_reading, last_oximeter), src/trevor/conversation/spo2_screens.py (screen_spo2), docs/SPO2_CAMERA.md

Watch measurement quality: good, medium or insufficient

Trévor counts over 14 days how many days have enough useful measurements, and from that decides whether your watch gives "good", "medium" or "insufficient" measurements. This label is used to silence a fragile conclusion and to show reliability on the wrist and in the bot.

Where you see it: "measure_quality" field on the wrist "My health" screen and in the app.

What the algorithm reads

The calculation, step by step

Usable dayAt least 2 useful measurements among: resting pulse, HRV, skintemperature, temperature deviation, oxygen, respirationAnd sleep, if known, of at least 120 minutesCountingNumber of usable days over 14 days, days with nothing, and whethera usable day exists today or yesterday10 or more usable days and arecent day?yesnoGood6 or more days and a recentday: medium; otherwiseinsufficient
Flowchart: Watch measurement quality: good, medium or insufficient

The values used

Name in the codeValueRoleWhere
WINDOW_DAYS14Windowsrc/trevor/wearables/mesure.py:16
MIN_NIGHT_COVERAGE_MIN120Minimum sleep for a usable daysrc/trevor/wearables/mesure.py:17
GOOD_DAYS10Days for "good"src/trevor/wearables/mesure.py:19
MEDIUM_DAYS6Days for "medium"src/trevor/wearables/mesure.py:20

What is shown

A label (good, medium, insufficient), the number of usable days, the gaps. The label says nothing about the accuracy of the values, only about their regularity.

A worked example

none

What this algorithm cannot do

Validation. Thresholds (14, 10, 6, 2 h) set by Trévor with no study cited.

Code : src/trevor/wearables/mesure.py (usable, assess), src/trevor/wearables/watch_summary.py (_measure_quality)

Aligned series of the "dashboard of your life"

Trévor puts on the same day axis what you experience (pain, mood, form, fatigue, crisis days), your body (watch, nights, lab results), the environment, your care and event markers. It computes no averages and no interpolation: a day without a measurement stays empty.

Where you see it: Health view (lifecourse/health_view.py, up to 366 days), navigator recommendations (90 days), findings.py associations and longitudinal research (180 days); the exact screens that display them were not all re-read.

What the algorithm reads

The calculation, step by step

Day axisBetween 14 and 180 days up to today (366 for the health view)OutcomesPain: the day's maximum; mood: latest value; fatigue: yes/no orlevelCrisis day: declared episodes, plus any day with a recorded painof 7 or more; a day without a crisis is 0 where you notedsomethingBodySteps, sleep, resting pulse, HRV, oxygen, respiration,temperature, noise: one value per day from the watch"My night": one value per night, only if quality is not lowDaily form: the watch readiness scoreLab and eye yellow: scattered points, never interpolatedEnvironment and careDaily weather and air variablesTreatment intake: taken = 1, missed = 0; missed doses with theirreason; "assumed" days marked separately and never counted asproofRescue pain medication: doses per day; self-care actionsEvents and weeklyMarkers: crisis start, transfusion, hospitalization,interventions, self-careWeekly questionnaire scores (PROMs, PREMs) and functioningtrajectory (ICF)
Flowchart: Aligned series of the "dashboard of your life"

The values used

Name in the codeValueRoleWhere
MAX_DAYS180Default maximum windowsrc/trevor/navigator/series.py:21
MIN_DAYS14Minimum windowsrc/trevor/navigator/series.py:22
pain threshold7A day with recorded pain of 7 or more counts as a crisis daysrc/trevor/navigator/series.py:157
MAX_MISSED_LISTED300Missed doses listedsrc/trevor/navigator/series.py:76
unitssteps; min; bpm; ms; %; /min; °C; dB(A); /hUnits of watch and night seriessrc/trevor/navigator/series.py:29-47

What is shown

For each series, values per day (measured days only), labels with unit and group, events, the attribution "Open-Meteo, CAMS (Copernicus)". No composite number.

A worked example

none

What this algorithm cannot do

Validation. Data assembly, no statistical model. The "pain of 7 or more = crisis day" rule is a Trévor choice with no study cited in the code.

Code : src/trevor/navigator/series.py (build), src/trevor/wearables/day_view.py (series, monitor), src/trevor/lifecourse/health_view.py (_nav), src/trevor/navigator/reco_flow.py (vers 243), src/trevor/research_longitudinal.py (vers 589)

The low oxygen threshold (92 percent) and nearby thresholds: where they apply and how to set them

Trévor uses several oxygen thresholds in different places: 92 percent for a single value, 90 and 88 percent for time spent at night, and 88 percent for emergencies. The 92 percent cannot be changed; you can only set your own usual range, which changes when an alert is sent.

Where you see it: Oximeter entry messages, camera estimate, value alerts, watch signals, "My night".

What the algorithm reads

The calculation, step by step

Entered oximeter valueBelow 92: safety message (redo the measurement after two minutesof rest; call emergency services if it stays below 92 or if youhave trouble breathing)Camera estimateValue below 92: verdict "low"; range whose bottom goes below 92:"uncertain"Value alertsA daily value below 92 is outside the norm; depending on yourusual range: alert, usual for you, or watchWatch signals"Night SpO2" listed if a day's value is under 92 (a bot sentence"your night oxygenation is sometimes low" appears if the recentaverage is under 92)My nightTime below 92, 90 and 88 percent computed for each series night;counter of nights with time below 90 percent; emergency if thelast night's average or the oximeter is 88 percent or less; riskmodel: night average below 92 percentDo you want to change thethreshold?yesnoYou can set a personal usualrange (low and high): itmoves neither the 92 percentnor the 88 percent, butdecides whether a value under92 is an alert or "usual foryou"The threshold stays 92percent everywhere
Flowchart: The low oxygen threshold (92 percent) and nearby thresholds: where they apply and how to set them

The values used

Name in the codeValueRoleWhere
LOW92.0Camera and oximeter message thresholdsrc/trevor/media/ppg_spo2.py:29
Metric spo2 low92General saturation norm in value alertssrc/trevor/value_alerts.py:68
spo2 signals< 92"Night SpO2" signalsrc/trevor/wearables/service.py:486
qualitative spo2< 92Trend sentence (recent average)src/trevor/wearables/service.py:444
THRESHOLDS(92, 90, 88)Cumulative time below these thresholds each nightsrc/trevor/wearables/night.py:26
ABS_LOW_NIGHT92.0Night average below which the risk model counts a low nightsrc/trevor/wearables/crisis.py:190
night_spo2_very_low88Emergency: night average or oximeter at 88 percent or lessdata/emergency_thresholds.json:11

What is shown

The same word "low" can come from different thresholds depending on the screen. No screen lets you change 92 or 88 percent. Your personal usual range (or your clinician's) only changes the level of the value alert.

A worked example

Computed by running the code: a value of 91 percent with no range set is an "alert"; with a lower limit set at 90 percent, the same value becomes "usual for you"; with a lower limit set at 94 percent, it stays an "alert". A value of 93 percent with a lower limit at 94 percent is "watch".

What this algorithm cannot do

Validation. The emergency file presents 88 percent as a usual pulmonology hypoxemia threshold, not specific to sickle cell disease, with a non-validated consumer sensor, to be validated by HUG (M-OXY-1). The code describes 92 percent as the "usual alert threshold of consumer oximeters" and as a safety marker. No study cited in the code sets these values for sickle cell disease, where steady-state desaturation is described (Quinn 2005, PMID 16173973, cited in docs/NUIT_SOMMEIL_OXYGENE.md): that is the purpose of the personal range.

Code : src/trevor/media/ppg_spo2.py, src/trevor/conversation/spo2_screens.py (on_spo2_enter), src/trevor/value_alerts.py, src/trevor/wearables/service.py (signals, qualitative_summary, night_view), src/trevor/wearables/night.py, src/trevor/wearables/crisis.py, data/emergency_thresholds.json

Usual range of each measurement and value alerts

Trévor reads your latest value against two references: the general adult norm and your usual range, learned from your own values or set by you or your clinician. Depending on whether the value is outside one, both or neither, it shows "alert", "watch", "usual for you" or "nothing to report". This is not a diagnosis.

Where you see it: "Value alerts" card on the dashboard (patient and clinician), bot push message for the "alert" level (once per measurement and date, can be turned off), dashboard alerts for fatigue and lab results.

What the algorithm reads

The calculation, step by step

Latest valueKeeps the latest value in the series if it is at most 3 days old(lab values: delay specific to each measurement)Watch values outside the plausible range are dropped (e.g. oxygenoutside 50 to 100)A limit set by you or yourclinician?yesnoIt serves as the usual rangeLearned range: median ± 2.5robust spreads of values fromthe previous 90 days, from 10valuesRobust spread1.4826 x MAD, never below a floor specific to each measurement(oxygen 0.7; pulse 3; HRV 5; steps 1,500; sleep 30 min)The lower bound does not go below zero, except for temperaturedeviationDirection of concernOnly the direction of concern triggers: oxygen, HRV, sleep, stepsdownward; temperature and noise upward; pulse and respiratory rateboth waysOutside the norm AND outsideyour usual range?yesno"Alert" level: to report tothe care teamSee the other three casesOther levelsInside the norm, outside the range: "watch"Outside the norm, inside the range: "usual for you" (information)Outside the norm with no known range: "watch", but "alert" fornight oxygen and fatigueNothing outside: "nothing to report"Fatigue and steps cross-checkFatigue at alert or watch AND steps down (alert or watch, lowdirection) within 2 days: combined "alert" levelMessageThe bot sends a message only for alerts not yet sent for thatmeasurement date, unless you turned the message off
Flowchart: Usual range of each measurement and value alerts

The values used

Name in the codeValueRoleWhere
LEARN_K2.5Width of the learned range in robust spreadssrc/trevor/value_alerts.py:41
MIN_WEAR, WEAR_DAYS10, 90Watch values needed to learn a range and window in dayssrc/trevor/value_alerts.py:42
spo2low norm 92; floor 0.7; freshness 3 d; alert if aloneNight saturation: under 92 percent is outside the norm; with no personal range the reading is already "alert"src/trevor/value_alerts.py:68
resting_hr50 to 100 bpm, both directions, floor 3Resting heart ratesrc/trevor/value_alerts.py:69
respiratory_rate12 to 20 /min, both directions, floor 1Respiratory ratesrc/trevor/value_alerts.py:70
hrvno norm, direction low, floor 5 msHeart rate variability: only the gap from your range countssrc/trevor/value_alerts.py:71
temp_deviationhigh 0.5 °C, direction high, floor 0.2Skin temperature deviationsrc/trevor/value_alerts.py:72
sleep_minuteslow 300 min, direction low, floor 30Sleep: under 5 hsrc/trevor/value_alerts.py:73
stepsno norm, direction low, floor 1,500Steps per daysrc/trevor/value_alerts.py:74
fatiguehigh 7/10, direction high, floor 1Declared fatigue: above 7/10 (marker to be validated)src/trevor/value_alerts.py:75
noise_dbhigh 70 dB(A), floor 3Daily average ambient noisesrc/trevor/value_alerts.py:76
PLAUSIBLEoxygen 50 to 100; pulse 25 to 250; sleep 0 to 960 min; steps 0 to 60,000Beyond these, the value does not enter the usual rangesrc/trevor/value_alerts.py:98-99

What is shown

One line per measurement with level (alert, watch, usual for you, nothing to report), direction (under or above), norm and usual range, sorted from most to least concerning. Example "alert" sentence: "Night oxygen saturation: 91 percent, below the norm (92 percent) and below your usual range (93.65 percent). Talk to your care team."

A worked example

Computed by running the code: 12 nights between 95.0 and 96.0 percent (median 95.5; learned range 93.65 to 97.35) then a value of 91 percent: "alert" level. If you set a lower limit of 90 percent yourself, the same 91 percent becomes "usual for you": it is below the 92 norm but above your limit, so no message is sent. With a single 91 percent value and no history: also "alert", because saturation is handled separately.

What this algorithm cannot do

Validation. The norms are described in the code as adult markers to be confirmed by the care team. Median with robust spread is a standard statistical method; the 2.5 coefficient, the 10 values, the 90 days and the floors are Trévor choices with no study cited. The fatigue marker at 7/10 and the noise marker at 70 dB(A) are marked "to be validated" in the code. The 92 percent threshold is a "safety marker" common to consumer oximeters according to value_alerts.py:16-17 and service.py:467, not a value specific to sickle cell disease. This is not a validated medical device.

Code : src/trevor/value_alerts.py (METRICS, learn, evaluate, set_limit, build, describe, unsent), src/trevor/dashboard/server.py (patient_limits, clinician_limits, vers 6939-6970), src/trevor/conversation/reminders.py (value_alerts)

Important. These calculations guide you; they make no diagnosis and do not replace the care team's advice. In case of severe pain, fever or shortness of breath, call 144 without waiting.