diff --git a/eufy_sync/eufy_client.py b/eufy_sync/eufy_client.py index 5199e3e..7d31f6a 100644 --- a/eufy_sync/eufy_client.py +++ b/eufy_sync/eufy_client.py @@ -312,7 +312,11 @@ def _fetch_raw_measurements(self, after_timestamp: int | None) -> list[EufyMeasu logger.warning("Raw Wi-Fi fallback: fetch failed for %s: %s", device_id, e) try: - measurements = self._parse_all(records) + measurements = [] + for record in records: + m = self._parse_raw_wifi_record(record) + if m is not None: + measurements.append(m) raw_count = len(measurements) if self.config.customer_id: measurements = [m for m in measurements if m.customer_id == self.config.customer_id] @@ -332,6 +336,35 @@ def _fetch_raw_measurements(self, after_timestamp: int | None) -> list[EufyMeasu ) return measurements + def _parse_raw_wifi_record(self, record: dict) -> EufyMeasurement | None: + raw_weight = record.get("weight") + raw_timestamp = record.get("timestamp") + if raw_weight is None or raw_timestamp is None: + logger.warning("Raw Wi-Fi record missing weight or timestamp: %s", record) + return None + + try: + weight_kg = float(raw_weight) + update_time = int(raw_timestamp) + except (TypeError, ValueError): + logger.warning("Raw Wi-Fi record has invalid weight or timestamp: %s", record) + return None + + if weight_kg <= 0: + logger.warning("Raw Wi-Fi record has invalid weight: %s", raw_weight) + return None + + customer_id = record.get("customer_id", "unknown") + measurement_id = f"{customer_id}_{update_time}" + + return EufyMeasurement( + measurement_id=measurement_id, + customer_id=customer_id, + device_id=record.get("device_id", "unknown"), + timestamp=datetime.fromtimestamp(update_time, tz=timezone.utc), + weight_kg=weight_kg, + ) + def _parse_record(self, record: dict) -> EufyMeasurement | None: scale_data = record.get("scale_data") if not scale_data: diff --git a/tests/test_eufy_client.py b/tests/test_eufy_client.py index 6e79e08..80056c9 100644 --- a/tests/test_eufy_client.py +++ b/tests/test_eufy_client.py @@ -73,6 +73,20 @@ def _record(customer_id, weight_dg, update_time): } +def _raw_wifi_record(customer_id, weight_kg, timestamp): + return { + "id": "raw-record-id", + "weight": f"{weight_kg:.2f}", + "impedance": "112566", + "timestamp": str(timestamp), + "heart_rate": "0", + "customer_id": customer_id, + "device_id": "raw-device-id", + "user_id": "raw-user-id", + "product_code": "", + } + + # --------------------------------------------------------------------------- # Task 2: list_profiles # --------------------------------------------------------------------------- @@ -190,7 +204,7 @@ def test_get_raw_records_handles_null_list_500_and_bad_code(): def test_fetch_falls_back_to_raw_when_normal_empty(): c = _client(customer_id="a") - raw = _record("a", 800, 2_000_000_000) # year 2033, passes the window + raw = _raw_wifi_record("a", 80.0, 2_000_000_000) # year 2033, passes the window with patch.object(c, "_get_records", return_value=[]), \ patch.object(c, "_list_device_ids", return_value=["dev1"]), \ patch.object(c, "_get_raw_records", return_value=[raw]): @@ -198,6 +212,7 @@ def test_fetch_falls_back_to_raw_when_normal_empty(): assert len(measurements) == 1 assert measurements[0].weight_kg == 80.0 assert measurements[0].customer_id == "a" + assert measurements[0].body_fat_pct is None def test_fetch_does_not_use_raw_when_normal_has_data(): @@ -212,7 +227,10 @@ def test_fetch_does_not_use_raw_when_normal_has_data(): def test_raw_fallback_drops_other_profiles(): c = _client(customer_id="a") - raws = [_record("a", 800, 2_000_000_000), _record("b", 600, 2_000_000_000)] + raws = [ + _raw_wifi_record("a", 80.0, 2_000_000_000), + _raw_wifi_record("b", 60.0, 2_000_000_000), + ] with patch.object(c, "_get_records", return_value=[]), \ patch.object(c, "_list_device_ids", return_value=["dev1"]), \ patch.object(c, "_get_raw_records", return_value=raws): @@ -230,7 +248,7 @@ def test_raw_fallback_degrades_when_device_list_errors(): def test_raw_fallback_degrades_on_malformed_record(): c = _client(customer_id="a") - bad = {"customer_id": "a", "update_time": 100, "scale_data": {"weight": "heavy"}} + bad = {"customer_id": "a", "timestamp": "2_000_000_000", "weight": "heavy"} with patch.object(c, "_get_records", return_value=[]), \ patch.object(c, "_list_device_ids", return_value=["dev1"]), \ patch.object(c, "_get_raw_records", return_value=[bad]):