feat(withings): ingest meastype 35 (legacy SpO2) into OXYGEN_SATURATION

Older Withings firmware and some partner SDK examples report SpO2
under meastype 35 instead of 54. Mapping both into OXYGEN_SATURATION
prevents a quiet drop on legacy devices and is compatible with mixed
fleets (the existing meastype 54 path stays in place).
This commit is contained in:
Marc-André Bombeck committed 2026-05-14 13:24:01 +02:00
1 parent fb23796d72
commit 4158a02966
2 files changed
+33

No files matched your search

+29
View File
@@ -60,6 +60,35 @@ describe("MEASURE_TYPE_MAP", () => {
it("maps Withings meastype 71 (current-gen Thermo) → BODY_TEMPERATURE", () => {
expect(MEASURE_TYPE_MAP[71]).toEqual({ type: "BODY_TEMPERATURE" });
});
it("maps Withings meastype 35 (legacy SpO2) → OXYGEN_SATURATION", () => {
expect(MEASURE_TYPE_MAP[35]).toEqual({ type: "OXYGEN_SATURATION" });
});
});
describe("fetchMeasurements — SpO2 alt code (meastype 35)", () => {
it("decodes a legacy-firmware SpO2 reading into OXYGEN_SATURATION %", async () => {
// 97% as Withings exponent encoding: value=97, unit=0.
installFetchMock(fakeGetmeasPayload([{ type: 35, value: 97, unit: 0 }]));
const out = await fetchMeasurements("token");
expect(out).toHaveLength(1);
expect(out[0]).toMatchObject({ type: "OXYGEN_SATURATION", value: 97 });
});
it("co-exists with meastype 54 in the same payload (mixed firmware)", async () => {
installFetchMock(
fakeGetmeasPayload([
{ type: 35, value: 96, unit: 0 },
{ type: 54, value: 98, unit: 0 },
]),
);
const out = await fetchMeasurements("token");
expect(out).toHaveLength(2);
expect(out.map((r) => r.type)).toEqual([
"OXYGEN_SATURATION",
"OXYGEN_SATURATION",
]);
});
});
describe("fetchMeasurements — body temperature (meastype 71)", () => {
+4
View File
@@ -138,6 +138,10 @@ export const MEASURE_TYPE_MAP: Record<number, { type: string; factor?: number }>
77: { type: "TOTAL_BODY_WATER" }, // Hydration / water mass (kg)
88: { type: "BONE_MASS" }, // Bone mass (kg)
54: { type: "OXYGEN_SATURATION" }, // SpO2 (% — only ScanWatch / pulse-ox products)
// v1.4.25 — older Withings firmware (and some SDK examples) report
// SpO2 under meastype 35 instead of 54. Both target OXYGEN_SATURATION
// in percent; the exponent decode handles 0.97 vs 97 transparently.
35: { type: "OXYGEN_SATURATION" },
};
export interface WithingsMeasure {