diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a1290d..95b8379 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## Unreleased + +### Fixes + +- **EU/NA device discovery**: `omron list` and setup device discovery no longer + return an empty list on the EU/NA v2 API. That API omits the `macAddress` + field, so every device was dropped by the "skip device without MAC address" + guard. The MAC is now derived from the BLE local name (e.g. + `blesmart_…` → `28:FF:B2:10:AA:A4`), then from `deviceSerialID`, matching the + value produced by Bluetooth discovery and manual `add --macaddr`. + ## v0.3.0 - **Garmin auth rewritten**: Migrated to garminconnect>=0.3.0 due to Garmin SSO/OAuth changes; replaces garth-based authentication. Re-login required. diff --git a/README.md b/README.md index f880d6e..1b29b36 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,23 @@ OMRAMIN_GARMIN_DEBUG=1 omramin sync # Garmin API only --- +### Troubleshooting + +**`omron list` shows no devices, but the device is in the OMRON app** + +- **Region mismatch**: the device only appears on the OMRON regional server your + country code maps to. Make sure the country you logged in with matches your + OMRON account's region. Some countries (e.g. `TR`) map to an offline region + with no cloud API. +- **Not synced to the cloud yet**: open the OMRON Connect phone app so it uploads + the latest measurements — `omramin` reads what's on OMRON's servers, not what is + only on the device over Bluetooth. +- **Inspect the raw response**: `omramin --debug omron list` logs the full + `init-user` response (credentials redacted) so you can see the returned + `deviceList`. + +--- + ## Commands | Command | Description | diff --git a/omronconnect.py b/omronconnect.py index e084cf1..b7eaf98 100644 --- a/omronconnect.py +++ b/omronconnect.py @@ -278,6 +278,21 @@ def serial_to_mac(serial: str) -> str: return ":".join(values[5:2:-1] + values[2::-1]) +def ble_local_name_to_mac(local_name: str) -> str: + # e.g. blesmart_0001020828ffb210aaa4 to 28:FF:B2:10:AA:A4 + # OMRON encodes the MAC as the last 12 hex chars of the BLE local name. + # Mirrors the BLE-scan derivation in omramin.omron_ble_scan. + name = (local_name or "").strip() + # Only OMRON BLE local names carry the MAC this way; guard against deriving a + # bogus MAC from an unrelated name that happens to end in 12 hex chars. + if not name.upper().startswith("BLESMART_"): + return "" + mac_hex = name[-12:].upper() + if len(mac_hex) != 12 or not all(c in "0123456789ABCDEF" for c in mac_hex): + return "" + return ":".join(mac_hex[i : i + 2] for i in range(0, 12, 2)) + + def convert_weight_to_kg(weight: T.Union[int, float], unit: int) -> float: if unit == WeightUnit.G: return weight / 1000 @@ -833,9 +848,20 @@ def get_registered_devices(self, days: T.Optional[int] = 30) -> T.Optional[list[ continue macAddress = attrs.get("macAddress", "").strip() + if not macAddress: + # EU/NA v2 responses omit macAddress; OMRON encodes the MAC in the + # BLE local name (blesmart_<...>) and falls back to deviceSerialID. + macAddress = ble_local_name_to_mac(attrs.get("deviceLocalName", "")) + if not macAddress: + serial = attrs.get("deviceSerialID", "") + if serial: + macAddress = serial_to_mac(serial) if not macAddress: L.debug(f"Skipping device without MAC address: {attrs.get('name', 'unknown')}") continue + # Normalize case so every discovery path (API macAddress, BLE local + # name, serial fallback) yields the same string for a given device. + macAddress = macAddress.upper() category = None deviceCategory = attrs.get("deviceCategory") @@ -855,7 +881,7 @@ def get_registered_devices(self, days: T.Optional[int] = 30) -> T.Optional[list[ continue else: - L.warning(f"Device with unknown category {device.get('deviceModel')}") + L.warning(f"Device with unknown category {deviceModel}") # Create OmronDevice deviceModel = attrs.get("deviceModel", attrs.get("identifier", "Unknown"))