Files
FitnessDroid/doc/openscale-integration.md
claude 051e5463a9
All checks were successful
release / build-release (push) Successful in 5m45s
Väg-skärm: tydlig varning (errorContainer) när kroppsdata saknas + doc-rättelse
Första testets uteblivna sammansättning berodde på att kroppsdatan inte var
ifylld — varningen var för diskret. Nu röd/tydlig med hänvisning till
Profil → Kroppsdata. Doc-noten rättad så orsaken inte påstås vara ramtiming
(merge-logiken finns kvar som robusthet).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C2GA94E1f3cmrvdQLQhYdg
2026-08-23 16:08:56 +02:00

134 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# openScale BT-vågar — integrationsplan
Bygga in [openScale](https://github.com/oliexdev/openScale):s Bluetooth-
drivrutiner i FitnessDroid så att vägningar (vikt + %muskler/fett/vatten)
kan hämtas direkt från en BT-våg in i appens kroppsmätningar.
## Beslut (2026-07-26)
- **Licens: FitnessDroid relicensas till GPL-3.0.** openScale är GPL-3.0;
att bädda in deras kod kräver att hela appen blir GPL-3.0. LICENSE byts,
och källfilerna som kommer från openScale behåller sina copyright-headers.
- **Alla drivrutiner portas** (hela `core/bluetooth`-paketet, ~55 handlers +
adaptrar), inte bara en modell. `ScaleFactory` auto-detekterar vågen vid
skanning precis som i openScale.
- **Attribution:** en **About**-vy under Profil som anger att appen använder
openScales drivrutiner, med länk till deras GitHub och GPL-3.0-licensen,
plus en lista på öppen källkod som används (openScale, Blessed-Kotlin).
## Teknik (bekräftat från repot)
- openScale använder **Blessed-Kotlin** (coroutine-BLE-wrapper) — läggs till
som Gradle-beroende; vi portar drivrutinerna som ligger ovanpå, inte
BLE-plumbingen.
- `ScaleDeviceHandler.supportFor(device)``DeviceSupport` (displayName,
linkMode, tuningProfile). `ScaleFactory` returnerar första matchande handler.
- Tre kopplingslägen: `CONNECT_GATT`, `BROADCAST_ONLY`, `CLASSIC_SPP` med var
sin adapter (Gatt/Broadcast/Spp).
- `ScaleMeasurement` bär weight, fat, water, muscle, visceralFat, bone, lbm,
bmr, protein, impedance m.m. → vi mappar weight + muscle/fat/water till vår
`BodyMeasurement` (resten kan tas in senare).
- minSdk 31 matchar vår app (BLE-permissions `BLUETOOTH_SCAN`/`_CONNECT`).
## Fallgrop: impedansvågar behöver användarprofil
Vissa vågar skickar bara **rå impedans**; openScale räknar då själv ut
fett/muskler/vatten med en formel som kräver **längd, ålder, kön** (ScaleUser).
Vi måste därför:
- Lägga till längd/födelseår/kön i profilen (se API-ändring nedan).
- Porta openScales BIA-beräkning för de vågar som kräver den.
Vågar som räknar ombord och skickar färdiga procent behöver inte detta.
## API-ändring: spara användarens längd (+ ålder/kön)
Längden ska sparas **på servern** (gym-API:t) så att den är gemensam för app
och webb, överlever ominstallation, och kan användas för mer korrekta
beräkningar:
- **BIA/kroppssammansättning:** openScales formler för impedansvågar kräver
längd (+ ålder + kön) för att ge korrekta %fett/muskler/vatten.
- **Energiförbrukning:** med längd (+ ålder + kön) kan kaloriberäkningen gå
från ren MET × vikt × tid till en BMR-baserad uppskattning
(MifflinSt Jeor: `10·vikt + 6.25·längd 5·ålder + könskonstant`), vilket
ger rimligare siffror per pass.
**Backend (brasse-pc.eu-v2, gym-api):**
- Utöka `User` med `HeightCm` (double?), och för full BIA även `BirthDate`
(eller `BirthYear`) och `Sex` (enum/sträng). Nullable + bakåtkompatibla.
- Migration (körs automatiskt vid uppstart, samma mönster som
`BodyMeasurement`).
- Exponera i `myProfile`-query och en `updateProfile`-mutation (eller utöka
`updateBodyWeight``updateProfile` med längd/ålder/kön).
- Deploya om gym-api-prod + -test (serialiserat bygge, se
[[pi5-ci-concurrency]]).
**App:** profil-fält för längd (och ålder/kön) som läser/skriver mot API:t;
cachas lokalt (som kroppsvikten) för offline och för BIA-beräkningen.
**Webb (brasse-pc.eu-v2):** samma fält på profil-fliken via `updateProfile`.
## Användarens våg (identifierad 2026-08-23)
**Biltema 84-1002 "Bluetooth Analyser Scale", modell PT-727** (Ningbo Putian).
Annonserar som BLE-namnet **"VScale"** (verifierat med `bluetoothctl`
brasse-linux01: `Device C4:BE:84:79:D0:6C VScale`) → openScales
**`ExingtechY1Handler`** matchar (exakt namnmatch `vscale`).
Protokoll (GATT): appen skriver `[0x10, userId, kön, ålder, längd_cm]` till
vågen; vågen räknar ut kroppssammansättningen **ombord** och notifierar en
20-bytes-ram (vikt, fett-%, vatten-%, muskel-%, benmassa kg, bukfettsindex).
Ingen BIA-formel behövs i appen — men längd/födelseår/kön måste finnas.
**Lärdom från första testet (2026-08-23):** utan ifylld kroppsdata
(längd/födelseår/kön) skickar vågen bara vikt — sammansättningen uteblir
tyst. Väg-skärmen varnar numera tydligt när kroppsdata saknas, och håller
dessutom anslutningen öppen och mergar ramar (vågen kan skicka vikten först
och sammansättningen i en senare ram; max 20 s väntan).
Hårdvarudetaljer från GATT-dump (openScales debug-läge): tillverkarsträng
**"VTrump"**, modellnummer **V300B1000001** — dvs. en VTrump-OEM. Notify-
tecknet `1a2ea400…` ligger i service `78667579-7b48…` (inte i `f433bd80…` som
klassiska Y1), och `f433bd80…` har ett extra notify-tecken `23b4fec0…`.
Fungerar ändå med `ExingtechY1Handler` — bra att veta vid framtida felsökning.
## Milstolpar (byggbara steg)
1. **Grund + licens + About** *(klar 2026-08-23)* — LICENSE → GPL-3.0;
`blessed-kotlin` 3.0.12 via JitPack (krävde Kotlin 2.0.21→2.2.0, KSP2,
Room 2.7.2); BLE-permissions (`BLUETOOTH_SCAN` neverForLocation +
`BLUETOOTH_CONNECT`); About-vy under Profil med GPL-attribution.
2. **Porta bluetooth-paketet (grund)** *(klar 2026-08-23)* — vendrat
`com.health.openscale.core.bluetooth` med copyright-headers kvar:
ScaleDeviceHandler, Gatt/Broadcast/Spp-adaptrar, ScaleFactory (utan Hilt),
ScaleCommunicator, BleScanner, ConverterUtils, ScaleMeasurement/ScaleUser,
samt **ExingtechY1Handler**. Shims i samma paketnamn för openScales
interna beroenden (facades → DataStore, slimmade enums, LogManager →
logcat), så fler drivrutiner kan portas nästan rakt av.
*Kvar: resterande ~59 handlers + libs/ portas allteftersom.*
3. **Skanna + koppla** *(klar 2026-08-23)* — Inställningar → Bluetooth-våg:
BLE-skanning, vågar med drivrutinsstöd överst (drivrutinsnamn visas),
spara/ta bort vald våg. Runtime-permission-flöde.
4. **Väg dig-flöde** *(klar och verifierad mot Biltema-vågen 2026-08-23)* — knapp i
profilen → väg-skärm med livestatus, resultat och spara via
`addBodyMeasurement`. Kroppsdata (längd/födelseår/kön) i profilen skickas
till vågen. Vikt + muskel/fett/vatten-% bekräftade end-to-end.
5. **API: längd (+ ålder/kön)** — utöka gym-API:ts `User` med `HeightCm`
(+ `BirthDate`/`Sex`), migration, `myProfile` + `updateProfile`, deploy.
App + webb får profilfält; appens lokala kroppsdata börjar synka mot
API:t. Se "API-ändring" ovan.
6. **Fler drivrutiner + impedansvågar + bättre kalorier** — porta resterande
handlers + `libs/` (BIA-beräkningar för vågar som skickar rå impedans),
och BMR-baserad energiförbrukning.
7. **Polering + docs** — felhantering, timeouts, ominställning av våg;
uppdatera README/wiki + infra-Doc (GPL-relicens noterad).
## Öppna frågor / risker
- ~~Vilken våg du har~~ → Biltema PT-727 = Exingtech Y1 ("VScale"), räknar
ombord — milstolpe 6:s BIA behövs inte för den.
- ~~Blessed-Kotlins koordinater~~ → `com.github.weliem:blessed-kotlin:3.0.12`
(JitPack). Byggd med Kotlin 2.2 → tvingade upp projektets Kotlin/KSP/Room.
- APK växer (drivrutiner + BLE-lib) — troligen någon MB, oproblematiskt.
- GPL-relicens är permanent för koden; medvetet val.
- ~~Väg-flödet är obekräftat~~ → verifierat mot Biltema-vågen 2026-08-23 (vikt + sammansättning).