Tracker de vie auto-hébergé : suivi poids/calories/sport avec planning de pesées, sevrage tabac (vape) avec modèle de coût DIY et économies, et finances personnelles avec import de relevés bancaires. Architecture : FastAPI + SQLAlchemy 2.0 + PostgreSQL 16, React 18 + TS + Vite + Tailwind + ECharts, déploiement Docker Compose. Modules auto-découverts des deux côtés (pkgutil / import.meta.glob) et framework de connecteurs à deux voies (importeurs de fichiers + ingestion JSON) pour brancher de nouvelles sources sans toucher au noyau. Validé : 292 tests pytest, tsc + vite build, contrat API/web vérifié contre le schéma OpenAPI, et déploiement Docker réel sur PostgreSQL 16 (28 tables, SPA servie par nginx, wizard de premier démarrage). Documentation : README.md, docs/GUIDE.md, CONVENTIONS.md, et les documents de conception et de recherche dans docs/. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
274 lines
33 KiB
Markdown
274 lines
33 KiB
Markdown
# Recherche : intégrer les données Android Health Connect dans LifeTrack (auto-hébergé)
|
||
|
||
> Document de recherche — module Santé/Fitness de LifeTrack.
|
||
> Date : 2026-08-13. Public : agents d'implémentation (backend FastAPI, frontend React, app Android éventuelle).
|
||
> Langue : prose en français, identifiants de code en anglais (convention projet).
|
||
|
||
---
|
||
|
||
## 1. Résumé exécutif
|
||
|
||
**Constat central : Health Connect est un magasin de données strictement local au téléphone ("on-device"). Il n'existe aucune API cloud officielle permettant à un serveur (auto-hébergé ou non) d'interroger les données Health Connect d'un utilisateur.** Le seul moyen d'amener ces données sur le serveur LifeTrack est qu'une application Android installée sur le téléphone lise Health Connect localement puis pousse les données vers notre backend (ou exporte des fichiers que l'on importe).
|
||
|
||
**L'API REST Google Fit est morte** : inscriptions fermées depuis le 1er mai 2024, arrêt définitif courant 2026. Elle ne doit servir de base à rien dans LifeTrack.
|
||
|
||
**Recommandation v1** (détaillée en section 8) :
|
||
|
||
1. Construire côté LifeTrack un **endpoint d'ingestion REST générique et authentifié** (`POST /api/v1/ingest/...`), conçu pour recevoir des lots de mesures santé en JSON, avec stockage du payload brut + normalisation, idempotent (dédoublonnage par identifiant externe).
|
||
2. Utiliser comme pont v1 l'application open-source **health-connect-webhook** (Android, AGPL-3.0, disponible sur le Play Store), qui lit Health Connect en tâche de fond (WorkManager) et POSTe du JSON vers des URLs de webhook configurables — c'est exactement le modèle "push vers endpoint custom" voulu par le brief. Alternative plus lourde : **HCGateway** (serveur Flask+MongoDB à héberger en plus, moins bien aligné avec notre stack).
|
||
3. Prévoir en **fallback un import de fichiers CSV** : l'application payante **Health Sync** (licence unique ~4 €) exporte automatiquement les données Health Connect en CSV (et les séances en FIT/TCX/GPX) vers Google Drive ; LifeTrack fournit un importeur CSV correspondant via le framework de connecteurs.
|
||
4. **v2** : application companion Android minimale maison (Kotlin + Health Connect SDK + WorkManager) qui POSTe directement sur notre endpoint — fiabilité et contrôle maximum, effort modéré (3 à 10 jours). **v2/v3** : enregistrement direct des séances de tapis via **Web Bluetooth + FTMS** dans le frontend React (Chrome/Edge uniquement, HTTPS requis), pour s'affranchir de l'app FitShow.
|
||
|
||
---
|
||
|
||
## 2. Health Connect : fonctionnement, capacités, limites
|
||
|
||
### 2.1 Architecture : on-device uniquement, pas d'API cloud
|
||
|
||
- Health Connect (HC) est une base de données chiffrée **stockée sur le téléphone**. Les apps santé (Samsung Health, Fitbit, Gadgetbridge, Foodvisor via Google Fit, etc.) y écrivent ; d'autres apps y lisent, **uniquement depuis le device**.
|
||
- La documentation officielle Google est explicite : HC est prévu pour les données "on-device" Android ; la « Google Health API » (cloud) est un produit distinct, successeur de la **Fitbit Web API** uniquement (comptes Fitbit/Google, accès soumis à validation Google) — **inutilisable pour un projet personnel auto-hébergé** et hors périmètre.
|
||
- Conséquence d'architecture pour LifeTrack : **un intermédiaire Android est obligatoire**. Le backend ne pourra jamais "aller chercher" les données ; il doit **recevoir** (push HTTP) ou **importer** (fichiers).
|
||
- Depuis Android 14, HC est **intégré au système** (Réglages > Sécurité et confidentialité > Health Connect). Sur Android 9–13, c'est une app APK à installer depuis le Play Store. SDK minimum : API 28 (Android 9).
|
||
|
||
### 2.2 Types de données disponibles (Jetpack `androidx.health.connect.client.records`)
|
||
|
||
Tous les besoins du module Santé/Fitness de LifeTrack sont couverts par des types HC standards. Correspondance à utiliser telle quelle dans le modèle de données :
|
||
|
||
| Besoin LifeTrack | Record Health Connect | Permission Android |
|
||
|---|---|---|
|
||
| Pas quotidiens | `StepsRecord` | `android.permission.health.READ_STEPS` |
|
||
| Distance | `DistanceRecord` | `...READ_DISTANCE` |
|
||
| Calories actives brûlées | `ActiveCaloriesBurnedRecord` | `...READ_ACTIVE_CALORIES_BURNED` |
|
||
| Calories totales brûlées (TDEE observé) | `TotalCaloriesBurnedRecord` | `...READ_TOTAL_CALORIES_BURNED` |
|
||
| Métabolisme de base (BMR) | `BasalMetabolicRateRecord` | `...READ_BASAL_METABOLIC_RATE` |
|
||
| Séances de sport (tapis, etc.) | `ExerciseSessionRecord` (+ `SpeedRecord`, `PowerRecord`, `ElevationGainedRecord` associés) | `...READ_EXERCISE` (+ `READ_SPEED`, `READ_POWER`) |
|
||
| Poids | `WeightRecord` | `...READ_WEIGHT` |
|
||
| Masse grasse | `BodyFatRecord` | `...READ_BODY_FAT` |
|
||
| Masse maigre / osseuse / hydrique | `LeanBodyMassRecord`, `BoneMassRecord`, `BodyWaterMassRecord` | permissions dédiées |
|
||
| Taille | `HeightRecord` | `...READ_HEIGHT` |
|
||
| Fréquence cardiaque | `HeartRateRecord`, `RestingHeartRateRecord`, `HeartRateVariabilityRmssdRecord` | `...READ_HEART_RATE`, etc. |
|
||
| Sommeil | `SleepSessionRecord` (avec stages) | `...READ_SLEEP` |
|
||
| Apport calorique / macros (Foodvisor) | `NutritionRecord` | `...READ_NUTRITION` |
|
||
| Hydratation | `HydrationRecord` | `...READ_HYDRATION` |
|
||
| VO2 max, étages montés, etc. | `Vo2MaxRecord`, `FloorsClimbedRecord`, ... | permissions dédiées |
|
||
|
||
Note Foodvisor : Foodvisor (Android) se synchronise avec **Google Fit** (réglage « connexion aux données de santé ») ; Google Fit écrit/lit la nutrition via Health Connect. La chaîne Foodvisor → Google Fit → Health Connect → pont → LifeTrack est donc **possible en théorie pour les calories ingérées** (`NutritionRecord`), mais fragile (dépend du maintien de l'app Google Fit, en fin de vie côté API ; des utilisateurs signalent des synchronisations Foodvisor→Fit capricieuses). À tester en priorité une fois le pont en place ; sinon, saisie manuelle / export Foodvisor en fallback.
|
||
|
||
### 2.3 Permissions et restrictions importantes (impact direct sur la conception)
|
||
|
||
- **Fenêtre historique de 30 jours** : par défaut, une app ne peut lire que les données datant d'au plus **30 jours avant la première autorisation**. Pour lire plus ancien, permission additionnelle `android.permission.health.READ_HEALTH_DATA_HISTORY` (`PERMISSION_READ_HEALTH_DATA_HISTORY`), disponible depuis les mises à jour 2025 du SDK (Jetpack en bêta depuis mars 2025). Conséquence : installer/configurer le pont **tôt** ; l'historique antérieur profond passera plutôt par des exports CSV.
|
||
- **Lecture en arrière-plan** : permission dédiée `android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND`, indispensable pour un pont qui synchronise sans que l'app soit ouverte. Les apps pont citées la gèrent déjà ; une app maison doit la déclarer et la demander.
|
||
- **Rate limiting** : HC impose des quotas de lecture (par app, différents premier plan / arrière-plan). Conception à base de **sync incrémentale** obligatoire, pas de relecture complète à chaque cycle.
|
||
- **API de changements (differential changes)** : `getChanges(token)` fournit ajouts/modifications/suppressions depuis le dernier token — c'est le mécanisme officiel de sync incrémentale. Les tokens expirent (~30 jours) ; prévoir une resynchronisation de rattrapage si token expiré.
|
||
- **Distribution hors Play Store** : la validation Google (formulaire de déclaration des permissions santé) est une exigence **de publication sur le Play Store**, pas un prérequis technique de l'API. Une app companion **sideloadée** (APK debug/release signé localement) déclarant correctement ses permissions dans le manifest et l'intent de justification (`androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE` sur Android 13-, `android.intent.action.VIEW_PERMISSION_USAGE` + catégorie `android.intent.category.HEALTH_PERMISSIONS` sur 14+) fonctionne : l'utilisateur accorde les permissions dans l'UI Health Connect. Preuve empirique : HCGateway et d'autres ponts open-source se distribuent en APK GitHub et fonctionnent. Point de vigilance : certains guides tiers évoquent des contrôles d'app ID pour les apps Play ; pour un usage personnel sideloadé, aucun blocage connu, mais **à valider sur l'appareil cible dès le début du développement v2**.
|
||
|
||
### 2.4 Ce que ça implique pour LifeTrack
|
||
|
||
- Le backend doit exposer une **API d'ingestion push** (section 8) — jamais de « pull » possible.
|
||
- Les données arrivent par lots hétérogènes, avec doublons possibles (re-synchronisations) : l'ingestion doit être **idempotente**.
|
||
- Chaque record HC porte des métadonnées utiles à conserver : `metadata.id` (UUID unique HC), `clientRecordId`, `dataOrigin` (package de l'app source, ex. `com.sec.android.app.shealth`), horodatages début/fin avec offset de zone. LifeTrack stocke en UTC (convention projet) + `source_app`.
|
||
|
||
---
|
||
|
||
## 3. API REST Google Fit : statut (chemin non viable)
|
||
|
||
- **Dépréciation annoncée** avec Health Connect comme successeur ; **inscriptions fermées depuis le 1er mai 2024** (aucun nouveau projet ne peut obtenir l'accès OAuth aux scopes Fitness).
|
||
- **Arrêt de service courant 2026** ("supported until the end of 2026" selon la FAQ de migration ; la page officielle developers.google.com/fit dit "will be deprecated in 2026" sans jour précis).
|
||
- L'app mobile Google Fit elle-même est en fin de vie au profit de l'app Fitbit ; ne pas en faire une dépendance.
|
||
- **Verdict : à exclure totalement.** Même si un projet OAuth existant fonctionnait encore quelques mois, tout investissement serait perdu. Aucune ligne de code LifeTrack ne doit cibler l'API Google Fit.
|
||
|
||
---
|
||
|
||
## 4. Applications pont existantes (Health Connect → serveur/fichier)
|
||
|
||
### 4.1 health-connect-webhook (mcnaveen) — candidat recommandé v1
|
||
|
||
- **Dépôt** : https://github.com/mcnaveen/health-connect-webhook — Kotlin / Jetpack Compose / Material 3, licence **AGPL-3.0** (+ addendum commercial pour la redistribution en store). ~141 stars, ~161 commits, activement maintenu, distribué sur le **Play Store** (et App Store côté iOS).
|
||
- **Fonction** : lit Health Connect et **POSTe un JSON vers une ou plusieurs URLs de webhook** configurées par l'utilisateur. Exactement le modèle "push vers endpoint REST custom" du brief.
|
||
- **Mécanismes de sync** : (a) périodique via **WorkManager** (minimum 15 min, réglable), (b) horaires fixes via AlarmManager (défaut 08:00 et 21:00), (c) sync manuelle, (d) **serveur HTTP local optionnel** sur le téléphone (port 8787 par défaut) exposant un snapshot JSON en pull sur le LAN.
|
||
- **Payload** : `POST` avec `Content-Type: application/json; charset=utf-8`. Objet JSON unique : `timestamp` (génération du payload), `app_version`, puis un **tableau par type de données en snake_case** (ex. `steps`, `heart_rate`, `weight`, `exercise_sessions`, `sleep_sessions`, `nutrition`, `active_calories_burned`, `total_calories_burned`, `distance`...), tableaux omis si vides. Schémas de champs détaillés dans les fichiers `docs/webhook.md` et `docs/local-http.md` du dépôt (les récupérer au moment de l'implémentation de l'adaptateur pour figer le mapping exact).
|
||
- **Couverture** : **31 types de données**, incluant tout ce dont LifeTrack a besoin (steps, distance, calories actives/totales, exercise sessions, poids, masse grasse, BMR, FC, sommeil avec stages, nutrition, hydratation, VO2 max).
|
||
- **Fenêtre de lecture** : fenêtre glissante de **48 h** ; les syncs en arrière-plan sont incrémentales (données nouvelles depuis la dernière sync réussie). Retries : 3 tentatives avec backoff exponentiel, puis nouvelle tentative à la sync suivante. **Limite** : si le téléphone/app est inactif plus de 48 h, trou possible → couvert par le fallback CSV et par la réconciliation côté serveur.
|
||
- **Limite importante : pas d'en-tête d'authentification configurable.** La sécurité repose sur l'URL. Mitigation LifeTrack : jeton secret **dans l'URL** (ex. `https://lifetrack.example/api/v1/ingest/hc-webhook/<ingest_token>`), token révocable, endpoint accessible uniquement en HTTPS (et idéalement seulement via VPN/LAN — déploiement domestique). Vérifier à l'implémentation si des en-têtes custom ont été ajoutés depuis.
|
||
|
||
### 4.2 HCGateway (ShuchirJ) — alternative complète mais lourde
|
||
|
||
- **Dépôt** : https://github.com/ShuchirJ/HCGateway — licence **GPL-3.0**, ~414 stars, développement actif ("API stability not guaranteed").
|
||
- **Architecture** : app mobile **React Native** (Android 8+) + **serveur Python Flask + MongoDB** auto-hébergeable (Docker Compose fourni) + **Firebase** (notifications push, nécessaire seulement pour déclencher des écritures serveur→téléphone ; il faut alors builder l'APK soi-même avec son `google-services.json`).
|
||
- **Fonction** : sync **bidirectionnelle** — l'app envoie ~34/35 types de données HC vers le serveur toutes les **2 h** (réglable, service foreground persistant, sync ~15 min) ; API REST (login/signup, fetch par type — méthodes nommées `steps`, `heartRate`, `sleepSession`, `activeCaloriesBurned`, `weight`, `exerciseSession`, etc. — et push vers HC). Docs API : https://hcgateway.shuchir.dev/.
|
||
- **Sécurité** : mots de passe Argon2 ; données chiffrées **Fernet** au repos dans MongoDB (clé dérivée du hash utilisateur), déchiffrées à la volée lors des requêtes API. Collections `hcgateway_[user_id]` avec données chiffrées, horodatages début/fin, package d'origine, id unique.
|
||
- **Historique** : limité aussi par la fenêtre HC de 30 jours (issue GitHub #38 ouverte à ce sujet).
|
||
- **Évaluation pour LifeTrack** : fonctionne, mais impose **un deuxième backend + MongoDB + éventuellement Firebase** à côté de notre stack Postgres/FastAPI, et LifeTrack devrait **poller** l'API HCGateway (au lieu de recevoir un push). Intéressant seulement si health-connect-webhook s'avère défaillant. Une instance publique existe (`https://api.hcgateway.shuchir.dev/`) mais envoie les données santé chez un tiers — contraire à l'esprit auto-hébergé.
|
||
|
||
### 4.3 Health Sync (appyhapps.nl) — le fallback fichiers de référence
|
||
|
||
- App Android commerciale mature (https://healthsync.app) : essai 1 semaine, puis **licence à vie en achat unique (~4 €)** ou abonnement 6 mois (Withings seul nécessite un abonnement dédié).
|
||
- Synchronise entre plateformes (sources : Health Connect, Samsung Health, Fitbit, Garmin, Polar, Suunto, Huawei, Oura, Strava, fatsecret... ; destinations : Health Connect, Strava, Google Drive, etc.).
|
||
- **Fonction clé pour LifeTrack : export automatique vers Google Drive** — données santé (pas, FC, poids...) en **CSV** (fichiers jour courant / 7 jours / mois / 30 jours glissants), séances d'activité en **FIT, TCX, GPX, KML, CSV**. Tourne en arrière-plan sans intervention.
|
||
- Usage LifeTrack : l'utilisateur dépose les CSV dans l'UI d'import (ou un dossier synchronisé sur le serveur) ; le framework d'importeurs LifeTrack fournit un parseur `health_sync_csv`. Sert aussi à **rattraper l'historique profond** (au-delà des 30 jours HC) et les trous de sync.
|
||
- Fermé/propriétaire : formats CSV à rétro-ingénierer sur échantillons réels (colonnes stables, une ligne par mesure horodatée ; prévoir l'importeur tolérant : détection d'en-têtes + mapping configurable).
|
||
|
||
### 4.4 Health Data Export (teqxnology / healthdataexport.com)
|
||
|
||
- App d'export manuel/planifié : Apple Health, Health Connect, Google Fit → **CSV, JSON, PDF, Excel**. Utile ponctuellement pour un dump massif initial ; moins adaptée à une sync continue. Second choix derrière Health Sync pour le fallback fichiers.
|
||
|
||
### 4.5 Home Assistant (app companion Android)
|
||
|
||
- Depuis la version **2025.5**, l'app companion Home Assistant expose des **capteurs Health Connect** (pas, calories actives/totales, distance, sommeil, poids, FC... ; liste élargie par vagues de 6 capteurs, cf. release notes github.com/home-assistant/android). Flux : app santé → HC → capteurs HA.
|
||
- Limites : capteurs = **valeurs instantanées/agrégats du moment**, pas des séries historiques propres ; il faudrait ensuite extraire du recorder HA vers LifeTrack (REST API HA + long-lived token). Convoluté, granularité pauvre (pas de séances détaillées, pas de nutrition complète). **Pertinent uniquement si l'utilisateur exploite déjà Home Assistant** et seulement pour des métriques simples (pas quotidiens). Non retenu comme chemin principal.
|
||
|
||
### 4.6 Gadgetbridge
|
||
|
||
- Gadgetbridge (app FLOSS pour montres/bracelets) est une **source** Health Connect, pas un exporteur : depuis la 0.89.0, Réglages > External Integrations > Health Connect permet de pousser **Steps et Heartbeat** (types supportés à ce jour) vers HC, localement, sans cloud constructeur.
|
||
- Intérêt LifeTrack : si l'utilisateur passe un jour à une montre supportée par Gadgetbridge, ses données rejoignent HC puis LifeTrack via le pont existant — **aucun travail supplémentaire côté LifeTrack**. Gadgetbridge offre aussi ses propres exports (base SQLite, auto-export) mais ce n'est pas le sujet v1.
|
||
|
||
### 4.7 Tableau comparatif des ponts
|
||
|
||
| Solution | Type | Push vers REST custom ? | Auth | Types couverts | Coût | Maintenance/risque |
|
||
|---|---|---|---|---|---|---|
|
||
| health-connect-webhook | App open-source (AGPL) | **Oui (webhooks POST JSON)** | Token dans URL seulement | 31 | Gratuit | Actif ; projet jeune |
|
||
| HCGateway | App + serveur open-source (GPL) | Non (LifeTrack pollerait son API) | Login + tokens | ~34 | Gratuit | Actif ; stack Flask/MongoDB/Firebase en plus |
|
||
| Health Sync | App commerciale | Non (fichiers vers Drive) | n/a | Large | ~4 € une fois | Très mature |
|
||
| Health Data Export | App commerciale | Non (fichiers) | n/a | Large | Freemium | OK |
|
||
| Home Assistant companion | App open-source | Indirect (via HA) | Token HA | Partiel (capteurs) | Gratuit | Actif |
|
||
| Gadgetbridge | App open-source | Non (c'est une source HC) | n/a | Steps, FC | Gratuit | Très actif |
|
||
|
||
---
|
||
|
||
## 5. FitShow (tapis de course) et la piste FTMS
|
||
|
||
### 5.1 L'app FitShow : capacités et limites
|
||
|
||
- FitShow (`com.fitshow` sur le Play Store) est l'app officielle des équipements embarquant le module Bluetooth **FitShow SmartBTM** (tapis, vélos, elliptiques, rameurs — marques low-cost/moyennes très répandues). Modes cartes, programmes, objectifs, podomètre.
|
||
- **iOS** : écrit pas et distance dans **Apple Health** (HealthKit) — sans objet pour nous.
|
||
- **Android** : **aucune intégration Health Connect ni Google Fit documentée**, **pas d'export Strava** (demandé par les utilisateurs, non implémenté), pas d'export de fichiers (TCX/GPX/FIT) connu. Compte cloud FitShow sans API publique.
|
||
- **Verdict : ne pas compter sur FitShow comme source de données.** Les séances tapis faites dans FitShow resteront enfermées. Trois contournements : (a) saisie manuelle de la séance dans LifeTrack (v1), (b) enregistrer la séance via une app qui écrit dans HC (ex. app de sport compatible FTMS, ou QZ ci-dessous), (c) **capter le tapis directement en BLE** (v2/v3, ci-dessous).
|
||
|
||
### 5.2 Protocoles BLE : FS (propriétaire) vs FTMS (standard)
|
||
|
||
- **FTMS** (Fitness Machine Service, Bluetooth SIG) est le standard BLE des machines de fitness : service `0x1826`, caractéristique **Treadmill Data `0x2ACD`** (notifications : vitesse instantanée en 0,01 km/h, distance totale, inclinaison en 0,1 %, calories, temps écoulé, FC si dispo — champs présents selon un bitfield de flags), Fitness Machine Control Point `0x2AD9` (contrôle vitesse/inclinaison), Fitness Machine Feature `0x2ACC`. Supporté par Zwift, Kinomap, Wahoo, Polar, etc.
|
||
- Les machines équipées FitShow parlent le **protocole propriétaire "FS"** ; **beaucoup de modèles récents exposent aussi FTMS** (les compatibilités Zwift/Kinomap l'attestent). À vérifier sur le tapis de l'utilisateur avec un scanner BLE (nRF Connect : chercher le service `0x1826`). Attention : le tapis n'accepte en général **qu'une connexion BLE à la fois** (FitShow app OU LifeTrack, pas les deux).
|
||
- **QZ (qdomyos-zwift)**, open-source (https://github.com/cagnulein/qdomyos-zwift), sait parler le protocole FS propriétaire de nombreux tapis et **re-exposer un device FTMS virtuel** ; il pousse aussi vers Strava/Peloton/Garmin. C'est le plan B si le tapis n'expose pas FTMS nativement.
|
||
|
||
### 5.3 Web Bluetooth dans le frontend LifeTrack : faisable
|
||
|
||
- **Oui, un frontend web peut enregistrer une séance de tapis en direct via Web Bluetooth + FTMS.** Des simulateurs de vélo open-source dans le navigateur le font déjà avec des trainers FTMS ; l'API `navigator.bluetooth.requestDevice({ filters: [{ services: [0x1826] }] })` puis abonnement aux notifications de `0x2ACD` suffit pour un tapis.
|
||
- **Contraintes** :
|
||
- Navigateurs : **Chrome/Edge (desktop et Android) uniquement** — pas Firefox ni Safari. Acceptable pour un outil personnel ; à documenter dans l'UI.
|
||
- **Contexte sécurisé requis** : HTTPS (ou `localhost`). Le déploiement domestique nginx devra servir en HTTPS (mkcert / CA locale / certificat Let's Encrypt si domaine) pour que le bouton "Connecter le tapis" fonctionne depuis un autre appareil que le serveur.
|
||
- Geste utilisateur requis pour l'appairage (pas de connexion silencieuse au chargement) ; reconnexion à gérer.
|
||
- **Design proposé (v2/v3)** : composant React `TreadmillRecorder` — connexion FTMS, échantillonnage ~1 Hz (vitesse, distance cumulée, inclinaison, kcal), graphe live ECharts, à l'arrêt POST de la séance vers `/api/v1/workouts` (durée, distance, vitesse moy/max, kcal, série de points). Valeur : remplace complètement FitShow pour le tapis et alimente directement le bilan énergétique. Effort : ~1-2 semaines avec l'UI.
|
||
|
||
---
|
||
|
||
## 6. App companion Android maison (chemin v2 privilégié)
|
||
|
||
### 6.1 Faisabilité et briques techniques
|
||
|
||
- **SDK** : Jetpack `androidx.health.connect:connect-client` (bêta depuis mars 2025, stable pour les usages courants). Kotlin, `minSdk 28` (HC APK requis sur Android 9-13 ; natif sur 14+).
|
||
- **Manifest** : déclarer chaque permission `android.permission.health.READ_*` nécessaire + `READ_HEALTH_DATA_IN_BACKGROUND` + `READ_HEALTH_DATA_HISTORY` ; intent de justification : activité gérant `androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE` (Android 13-) et alias avec `android.intent.action.VIEW_PERMISSION_USAGE` + catégorie `android.intent.category.HEALTH_PERMISSIONS` (Android 14+), affichant la politique de confidentialité (une page statique en français suffit pour un usage personnel).
|
||
- **Sync** : `PeriodicWorkRequest` WorkManager (minimum Android : **15 min** ; 30-60 min suffisent pour LifeTrack), avec contrainte réseau. Première exécution : lecture complète 30 jours (ou plus avec la permission history) par `readRecords` paginé ; ensuite **sync incrémentale via `getChangesToken` / `getChanges`** (gère ajouts/modifs/suppressions, économise le quota de rate limiting). Stocker le token ; si expiré (~30 jours), refaire un rattrapage borné.
|
||
- **Envoi** : POST JSON (OkHttp/Ktor) vers l'endpoint d'ingestion LifeTrack avec **Bearer token** (vraie authentification, contrairement à health-connect-webhook) ; file de retry persistée (Room ou fichier) pour tolérer serveur éteint / hors LAN ; option "sync seulement en Wi-Fi domestique".
|
||
- **Distribution** : APK signé localement, sideloadé — **pas de compte développeur Google ni de revue Play nécessaire** pour un usage personnel (cf. 2.3, à valider sur l'appareil cible en tout début de v2).
|
||
|
||
### 6.2 Estimation d'effort
|
||
|
||
| Poste | Estimation |
|
||
|---|---|
|
||
| Squelette app (Compose, 2 écrans : config serveur/token, état de sync) | 1 jour |
|
||
| Intégration HC : permissions + lecture des ~10 types utiles | 1-2 jours |
|
||
| WorkManager + changes API + file de retry | 1-2 jours |
|
||
| Client HTTP + mapping JSON vers le schéma d'ingestion | 0,5-1 jour |
|
||
| Tests sur device réel, edge cases (Doze, OEM battery killers), polish | 1-3 jours |
|
||
| **Total** | **~3-5 jours** (dev Android expérimenté) à **~2 semaines** (montée en compétence incluse) |
|
||
|
||
Risques spécifiques Android : optimisations batterie agressives des OEM (Xiaomi/Huawei/Samsung) pouvant tuer WorkManager → documenter l'exclusion de l'optimisation batterie ; quotas HC en arrière-plan → rester sur la changes API.
|
||
|
||
---
|
||
|
||
## 7. Classement des chemins d'intégration (effort vs valeur)
|
||
|
||
| # | Chemin | Effort | Valeur | Fiabilité | Version cible |
|
||
|---|---|---|---|---|---|
|
||
| 1 | **Endpoint d'ingestion REST générique LifeTrack** (prérequis de tout le reste) | Moyen (backend pur) | Très élevée | n/a | **v1** |
|
||
| 2 | **health-connect-webhook → endpoint LifeTrack** | Faible (adaptateur de payload) | Élevée (sync auto ~15 min, 31 types) | Moyenne+ (fenêtre 48 h, pas d'auth header) | **v1** |
|
||
| 3 | **Import CSV Health Sync (fallback + historique)** | Faible-moyen (parseurs CSV/TCX) | Élevée (rattrapage, robustesse) | Élevée | **v1** |
|
||
| 4 | Saisie manuelle poids/séances/nutrition | Déjà prévu (UI) | Élevée | Élevée | v1 |
|
||
| 5 | **App companion maison (HC SDK + WorkManager + Bearer)** | Moyen (3-10 j) | Très élevée (contrôle total, auth propre, >48 h, history) | Élevée | **v2** |
|
||
| 6 | **Web Bluetooth FTMS (enregistrement tapis dans le navigateur)** | Moyen (1-2 sem.) | Élevée (remplace FitShow, données riches) | Moyenne (Chrome/Edge + HTTPS + FTMS dispo sur le tapis) | **v2/v3** |
|
||
| 7 | HCGateway auto-hébergé | Moyen-élevé (2e stack serveur) | Moyenne (doublonne #2/#5) | Moyenne | plan B uniquement |
|
||
| 8 | Home Assistant companion → HA → LifeTrack | Moyen | Faible (granularité pauvre) | Moyenne | non retenu |
|
||
| 9 | Gadgetbridge (source HC si montre compatible) | Nul côté LifeTrack | Bonus | Élevée | opportuniste |
|
||
| 10 | Google Fit REST API | — | **Nulle (arrêt 2026, inscriptions fermées)** | — | **exclu** |
|
||
|
||
---
|
||
|
||
## 8. Recommandation v1 détaillée + contrat d'ingestion proposé
|
||
|
||
### 8.1 Périmètre v1
|
||
|
||
1. **API d'ingestion générique** (ci-dessous) + table de payloads bruts + normalisation vers les tables métier (`weight_measurements`, `daily_activity`, `workouts`, `nutrition_entries`, ...).
|
||
2. **Adaptateur health-connect-webhook** : endpoint dédié acceptant le format de cette app, token dans l'URL.
|
||
3. **Importeurs fichiers** via le framework de connecteurs : `health_sync_csv` (pas, poids, FC, calories), `tcx`/`gpx` (séances). Réutilisables pour tout autre export.
|
||
4. Documentation utilisateur (français) : installer health-connect-webhook depuis le Play Store, coller l'URL d'ingestion générée par LifeTrack (avec token), cocher les types de données, régler l'intervalle ; configurer Health Sync en secours.
|
||
|
||
### 8.2 Contrat API d'ingestion (à implémenter tel quel)
|
||
|
||
Principes : **push only, idempotent, tolérant, brut d'abord**.
|
||
|
||
- `POST /api/v1/ingest/health` — endpoint canonique (utilisé par la future app companion v2 et tout client "propre").
|
||
- Auth : `Authorization: Bearer <ingest_token>` (token d'ingestion par device, distinct du JWT de session, révocable, stocké haché).
|
||
- Corps : `{ "source": "companion-app", "device_id": "...", "records": [ { "type": "steps", "external_id": "<hc metadata.id>", "start_time": "...Z", "end_time": "...Z", "value": {...}, "unit": "...", "origin_app": "com.sec.android.app.shealth" }, ... ] }`.
|
||
- Réponse : `{ "accepted": n, "duplicates": m, "rejected": [...] }` ; `207`-like sémantique, jamais d'échec global pour un record invalide.
|
||
- `POST /api/v1/ingest/hc-webhook/{ingest_token}` — **adaptateur health-connect-webhook** (l'app ne sait pas poser d'en-tête d'auth → token dans le chemin, transmis en HTTPS uniquement). Accepte le payload natif de l'app (objet avec `timestamp`, `app_version`, tableaux snake_case par type), le stocke brut, puis mappe les types connus vers le pipeline canonique.
|
||
- **Stockage brut systématique** : table `raw_ingest_payloads` (id, token/device, received_at UTC, source, payload JSONB, processing_status, error). Permet de rejouer la normalisation quand le mapping s'affine — crucial car le schéma exact des ponts tiers peut évoluer.
|
||
- **Idempotence / dédoublonnage** : contrainte unique `(user_id, record_type, external_id)` quand un id externe existe (UUID `metadata.id` HC, transmis par les ponts) ; sinon clé de repli = hash SHA-256 de `(record_type, start_time, end_time, origin_app, valeur canonique)`. Les re-syncs (fenêtre 48 h de health-connect-webhook, ré-imports CSV) deviennent inoffensives.
|
||
- **Fuseaux** : entrées horodatées ISO-8601 avec offset ; conversion et stockage **UTC** ; agrégats journaliers calculés en `Europe/Paris` côté requêtes/vues.
|
||
- Sécurité déploiement domestique : HTTPS obligatoire (nginx), rate limit simple sur les endpoints d'ingestion, tokens révocables depuis l'UI (page « Sources de données »), logs d'ingestion visibles dans l'UI pour diagnostiquer les trous de sync.
|
||
|
||
### 8.3 Ordre de vérification à l'implémentation (sans nouvelle recherche produit)
|
||
|
||
1. Figer le mapping exact des champs de health-connect-webhook depuis `docs/webhook.md` du dépôt (et/ou capturer un payload réel avec l'app pointée vers un endpoint de debug).
|
||
2. Générer des exports Health Sync réels (CSV jour/semaine/mois + un TCX de séance) et figer les parseurs sur ces échantillons.
|
||
3. Tester la chaîne Foodvisor → Google Fit → Health Connect → pont pour `NutritionRecord` ; si KO, la saisie calories reste manuelle en v1.
|
||
4. Scanner le tapis avec nRF Connect pour confirmer la présence du service FTMS `0x1826` (décide la faisabilité du chemin Web Bluetooth v2/v3 sans QZ).
|
||
|
||
---
|
||
|
||
## 9. Risques et inconnues
|
||
|
||
- **health-connect-webhook** : projet jeune ; schéma de payload non contractuel (d'où le stockage brut + adaptateur isolé) ; absence d'auth par en-tête (token en URL + HTTPS/VPN en mitigation) ; fenêtre 48 h (trous possibles, couverts par CSV).
|
||
- **Comportement OEM Android** (Doze, battery killers) : peut espacer les syncs de n'importe quel pont ou de l'app maison ; documenter l'exclusion d'optimisation batterie.
|
||
- **Fenêtre historique HC de 30 jours** : l'historique profond ne viendra jamais de HC sans `READ_HEALTH_DATA_HISTORY` (et jamais au-delà de ce que les apps sources ont écrit dans HC) → import CSV pour le passé.
|
||
- **Sideload + permissions HC sur l'appareil cible** : à valider empiriquement en tout début de v2 (aucun blocage connu, mais politique Google mouvante en 2025-2026).
|
||
- **FitShow** : silo confirmé côté Android ; la valeur du chemin FTMS dépend du matériel réel de l'utilisateur (présence du service `0x1826`).
|
||
- **Foodvisor → HC** : chaîne indirecte via Google Fit, app Google Fit en fin de vie ; fiabilité incertaine.
|
||
- **Écosystème mouvant** : Google migre l'écosystème (Fit → Health Connect / Google Health API) ; revalider les politiques HC (permissions, quotas) au démarrage de la v2 companion.
|
||
|
||
---
|
||
|
||
## 10. Sources
|
||
|
||
- Health Connect — guide de comparaison (on-device vs cloud) : https://developer.android.com/health-and-fitness/health-connect/comparison-guide
|
||
- Health Connect — démarrage : https://developer.android.com/health-and-fitness/health-connect/get-started
|
||
- Health Connect — synchronisation (changes API) : https://developer.android.com/health-and-fitness/health-connect/sync-data
|
||
- Health Connect — types de données et permissions : https://developer.android.com/health-and-fitness/health-connect/data-types
|
||
- Health Connect — lecture de données (fenêtre 30 jours) : https://developer.android.com/health-and-fitness/guides/health-connect/develop/read-data
|
||
- Health Connect — FAQ : https://developer.android.com/health-and-fitness/guides/health-connect/frequently-asked-questions
|
||
- Health Connect — rate limiting : https://developer.android.com/health-and-fitness/health-connect/rate-limiting
|
||
- Blog Android Developers (mars 2025) — SDK Jetpack bêta, background reads, history : https://android-developers.googleblog.com/2025/03/health-connect-jetpack-sdk-now-in-beta.html
|
||
- Android Authority — historical/background reads : https://www.androidauthority.com/health-connect-historical-background-reads-3443726/
|
||
- Publication Play / déclaration santé : https://developer.android.com/health-and-fitness/health-connect/publish et https://support.google.com/googleplay/android-developer/answer/12991134
|
||
- Google Fit — page officielle (dépréciation) : https://developers.google.com/fit
|
||
- Google Fit — FAQ migration : https://developer.android.com/health-and-fitness/health-connect/migration/fit/faq
|
||
- HCGateway : https://github.com/ShuchirJ/HCGateway/ (docs API : https://hcgateway.shuchir.dev/ ; issue 30 jours : https://github.com/ShuchirJ/HCGateway/issues/38)
|
||
- health-connect-webhook : https://github.com/mcnaveen/health-connect-webhook
|
||
- Health Sync : https://healthsync.app/about/ et https://play.google.com/store/apps/details?id=nl.appyhapps.healthsync
|
||
- Health Data Export : https://healthdataexport.com/
|
||
- Home Assistant companion — capteurs Health Connect : https://github.com/home-assistant/android/releases/tag/2025.5.3 ; https://community.home-assistant.io/t/health-and-fitness-data-into-home-assistant-ha-companion-app-and-health-connect/905477
|
||
- Gadgetbridge — intégration Health Connect : https://gadgetbridge.org/basics/integrations/health-connect/ ; https://gadgetbridge.org/blog/release-0_89_00/
|
||
- FitShow (Play Store) : https://play.google.com/store/apps/details?id=com.fitshow ; (App Store, sync Apple Health) : https://apps.apple.com/us/app/fitshow-treadmill-workout/id1387360716
|
||
- FTMS — présentation Decathlon Digital : https://medium.com/decathlondigital/take-control-of-your-fitness-machines-6588439aeeda ; intégration apps : https://www.fitscope.com/blog/bluetooth-ftms-integration-for-fitness-apps
|
||
- QZ (qdomyos-zwift) : https://github.com/cagnulein/qdomyos-zwift
|
||
- Foodvisor (Play Store, sync Google Fit) : https://play.google.com/store/apps/details?id=io.foodvisor.foodvisor
|