Some checks failed
release / build-release (push) Has been cancelled
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C2GA94E1f3cmrvdQLQhYdg
132 lines
7.2 KiB
Markdown
132 lines
7.2 KiB
Markdown
# 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
|
||
(Mifflin–St 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` på
|
||
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.
|
||
|
||
**Viktigt beteende (verifierat 2026-08-23):** vågen skickar **vikten först**
|
||
och sammansättningen i en **senare ram** — väg-flödet måste hålla anslutningen
|
||
öppen och merga ramarna (fixat i väg-skärmen, 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).
|