diff --git a/docs/polar-ble-runbook-mac.md b/docs/polar-ble-runbook-mac.md index 72b97a9..b8c98b9 100644 --- a/docs/polar-ble-runbook-mac.md +++ b/docs/polar-ble-runbook-mac.md @@ -227,6 +227,30 @@ et garder le câble. **Le câble, lui, fonctionne intégralement** : `tools/polar/README.md` du dépôt `coach_sportif`. Rien de ce qui a été livré côté serveur n'est perdu. +## 🔑 La montre ne s'annonce pas : elle est déjà connectée + +Mesuré le 31/08 : **4020 advertisements vus** (HomePod, Furbo…), **aucune +Polar**. Le scan fonctionne parfaitement — la Vantage ne diffuse simplement pas. + +C'est le comportement normal du BLE : **un appareil connecté cesse d'émettre**. +La montre est liée à l'iPhone par l'app Flow, donc elle est invisible au scan, +par conception. L'attendre était sans espoir, et tous les correctifs de scan qui +ont précédé ne pouvaient rien y changer. + +`search()` prévoit ce cas, mais seulement quand on lui passe des UUID : + +```swift +foundPeripherals = self.manager.retrieveConnectedPeripherals(withServices: uuids!) +``` + +⇒ **Appeler `search([BlePsFtpClient.PSFTP_SERVICE], identifiers: nil, +fetchKnownDevices: true)`**, et non `search(nil, …)` : c'est cette branche qui +rend les périphériques déjà connectés exposant PsFTP et leur fabrique une +session. + +⚠️ Dédupliquer aussi les sessions vues : `AllowDuplicates` est armé côté SDK, le +même appareil revient à chaque advertisement. + ## Ce qui reste inconnu, et ne se lèvera qu'ici 1. **La V3 accepte-t-elle une connexion BLE tierce** alors qu'elle est appairée diff --git a/ios/App/App/CoachPolarBLE.swift b/ios/App/App/CoachPolarBLE.swift index 3f47323..59d63b6 100644 --- a/ios/App/App/CoachPolarBLE.swift +++ b/ios/App/App/CoachPolarBLE.swift @@ -304,7 +304,12 @@ final class PolarPsFtpWriter { // L'état, lui, est acquis avant d'arriver ici : `blePowered()` a été // sondé jusqu'à devenir vrai. do { + // `AllowDuplicates` est armé côté SDK : le même appareil revient à + // chaque advertisement (4020 émissions pour une poignée d'appareils + // réels). On déduplique avant de compter et d'ouvrir quoi que ce soit. + var dejaVues = Set() let toutes = try await sessionsVues(listener, fenetre: timeout) + .filter { dejaVues.insert(Self.etiquette($0)).inserted } vues = toutes.count noms = toutes.map { Self.etiquette($0) } @@ -372,7 +377,22 @@ final class PolarPsFtpWriter { rendu = true suite.resume(returning: vues) } - listener.search(nil, identifiers: nil, fetchKnownDevices: true) + // ⚠️ PASSER L'UUID DU SERVICE — c'est ce qui trouve une montre DÉJÀ + // CONNECTÉE. + // + // Mesuré le 2026-08-31 : 4020 advertisements vus (HomePod, Furbo…), + // aucune Polar. Normal — **un appareil BLE connecté cesse + // d'émettre**. La Vantage est liée à l'iPhone par l'app Flow : elle + // est donc invisible au scan, par conception, et l'attendre était + // sans espoir. + // + // Avec un `uuids` non nil, `search()` emprunte une autre branche : + // `manager.retrieveConnectedPeripherals(withServices: uuids!)`, qui + // rend les périphériques déjà connectés au système exposant ce + // service, et leur fabrique une session. Passer `nil` — ce que + // faisait ce code — n'exécutait jamais cette branche. + listener.search([BlePsFtpClient.PSFTP_SERVICE], + identifiers: nil, fetchKnownDevices: true) .sink(receiveCompletion: { [weak self] fin in // Distinguer « le publisher a terminé » de « il n'a // jamais rien dit » : dans le second cas, `flatMap`