diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/WithingsBaseDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/WithingsBaseDeviceSupport.java index 1df0aa39ac..b0f6994b51 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/WithingsBaseDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/WithingsBaseDeviceSupport.java @@ -310,7 +310,9 @@ public abstract class WithingsBaseDeviceSupport extends AbstractBTLESingleDevice try { getDevice().setBusyTask(R.string.busy_task_syncing, getContext()); + getDevice().sendDeviceUpdateIntent(getContext()); syncInProgress = true; + logger.info("Starting Withings sync, trigger={}, shouldSync={}", triggerSource, shoudSync()); if (withingsEcgHandler == null) { withingsEcgHandler = createEcgHandler(); } @@ -388,6 +390,7 @@ public abstract class WithingsBaseDeviceSupport extends AbstractBTLESingleDevice conversationQueue.clear(); } finally { // This must be done in all cases or the watch won't respond anymore! + logger.debug("Queueing SYNC_OK terminator for trigger={}", triggerSource); addSimpleConversationToQueue(new WithingsMessage(WithingsMessageType.SYNC_OK), new SyncFinishedHandler(this)); } conversationQueue.send(); @@ -455,6 +458,17 @@ public abstract class WithingsBaseDeviceSupport extends AbstractBTLESingleDevice } } + @Override + public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { + super.onConnectionStateChange(gatt, status, newState); + + if (newState == BluetoothGatt.STATE_CONNECTED) { + clearStaleSyncState("connect"); + } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { + clearStaleSyncState("disconnect"); + } + } + @Override public void onNotification(NotificationSpec notificationSpec) { notificationProvider.notifyClient(notificationSpec); @@ -670,7 +684,28 @@ public abstract class WithingsBaseDeviceSupport extends AbstractBTLESingleDevice logger.debug("Finished initialization."); } + private void clearStaleSyncState(final String reason) { + if (withingsEcgHandler != null) { + withingsEcgHandler.reset(); + } else { + GB.updateTransferNotification(null, "", false, 100, getContext()); + } + + if (syncInProgress) { + logger.info("Clearing stale Withings sync state on {}", reason); + syncInProgress = false; + conversationQueue.clear(); + } + + final String syncingTask = getContext().getString(R.string.busy_task_syncing); + if (syncingTask.equals(getDevice().getBusyTask())) { + getDevice().unsetBusyTask(); + getDevice().sendDeviceUpdateIntent(getContext()); + } + } + public void finishSync() { + logger.info("Finishing Withings sync, busyTask={}, syncInProgress={}", getDevice().getBusyTask(), syncInProgress); syncInProgress = false; if (withingsEcgHandler != null) { withingsEcgHandler.onSyncFinished(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/communication/conversation/SyncFinishedHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/communication/conversation/SyncFinishedHandler.java index 146574af90..3ff2379451 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/communication/conversation/SyncFinishedHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/communication/conversation/SyncFinishedHandler.java @@ -16,11 +16,15 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication.conversation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.WithingsBaseDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication.message.Message; import nodomain.freeyourgadget.gadgetbridge.service.devices.withingssteelhr.communication.message.WithingsMessageType; public class SyncFinishedHandler extends AbstractResponseHandler { + private static final Logger logger = LoggerFactory.getLogger(SyncFinishedHandler.class); public SyncFinishedHandler(WithingsBaseDeviceSupport support) { super(support); @@ -30,6 +34,7 @@ public class SyncFinishedHandler extends AbstractResponseHandler { @Override public void handleResponse(Message response) { + logger.info("SyncFinishedHandler received response type={} (0x{})", response.getType(), Integer.toHexString(response.getType() & 0xffff)); if (response.getType() == WithingsMessageType.SYNC_OK) { support.finishSync(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/communication/conversation/WithingsEcgHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/communication/conversation/WithingsEcgHandler.java index b9544089fe..71f78a6315 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/communication/conversation/WithingsEcgHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/withingssteelhr/communication/conversation/WithingsEcgHandler.java @@ -106,6 +106,7 @@ public class WithingsEcgHandler implements ResponseHandler { discoveredEcgCount = 0; fetchedEcgCount = 0; deletedEcgCount = 0; + logger.info("Starting Withings ECG discovery"); updateEcgProgress("Scanning for ECG records", 0, true); queueDiscoveryProbe(true); } @@ -198,6 +199,8 @@ public class WithingsEcgHandler implements ResponseHandler { // Keep the discovery conversation active until its trailing MEASURE_STOP / TRANSFER_COMPLETE. // Otherwise that trailing completion marker can get mis-bound to the next queued 0x0147 // request, causing the actual ECG waveform stream to be routed into a stored-measure handler. + logger.debug("Queueing Withings ECG discovery probe, initial={}, discoveryPagesSeen={}, waveformFetchQueued={}", + initial, discoveryPagesSeen, waveformFetchQueued); final WithingsMessage message = new WithingsMessage(WithingsMessageType.MEASURE_START, ExpectedResponse.EOT); message.addDataStructure(new MeasureCategory(MeasureCategory.ECG)); message.addDataStructure(new MeasureLiveAppStatus(initial ? 1 : 0)); @@ -207,6 +210,7 @@ public class WithingsEcgHandler implements ResponseHandler { private void handleDiscoveryResponse(final Message response) { final List structures = response.getDataStructures(); if (structures == null || structures.isEmpty()) { + logger.warn("Withings ECG discovery response type={} had no structures", response.getType()); return; } @@ -226,6 +230,9 @@ public class WithingsEcgHandler implements ResponseHandler { } } + logger.debug("Withings ECG discovery response type={} hasEot={} hasEcgMarkers={} discovered={} fetched={} deleted={} structures={}", + response.getType(), hasEot, hasEcgMarkers, discoveredEcgCount, fetchedEcgCount, deletedEcgCount, describeStructures(structures)); + if (recordKey != null && isNewRecordKey(recordKey)) { seenRecordKeys.add(recordKey.getRawPayload()); final long timestampMs = recordKey.getTimestampMs(); @@ -257,8 +264,42 @@ public class WithingsEcgHandler implements ResponseHandler { } if (!waveformFetchQueued && hasEcgMarkers && discoveryPagesSeen < MAX_DISCOVERY_PAGES) { queueDiscoveryProbe(false); + } else if (!waveformFetchQueued && hasEcgMarkers) { + logger.warn("Withings ECG discovery stopped after {} pages without queueing waveform fetch", discoveryPagesSeen); + } + } else if (!hasEot) { + logger.debug("Withings ECG discovery awaiting more packets for response type={}", response.getType()); + } + } + + private String describeStructures(final List structures) { + final List descriptions = new ArrayList<>(structures.size()); + for (final WithingsStructure structure : structures) { + if (structure instanceof StoredMeasureMeta) { + final StoredMeasureMeta meta = (StoredMeasureMeta) structure; + descriptions.add("0116(type=" + meta.getMeasurementType() + ",ts=" + meta.getTimestampMs() + ")"); + } else if (structure instanceof StoredMeasureData) { + final StoredMeasureData data = (StoredMeasureData) structure; + descriptions.add("0117(type=" + data.getMeasurementType() + ",raw=" + data.getRawValue() + ")"); + } else if (structure instanceof StoredMeasureDataExtend) { + final StoredMeasureDataExtend dataExt = (StoredMeasureDataExtend) structure; + descriptions.add("0149(type=" + dataExt.getMeasurementType() + ",extra=" + dataExt.getExtraData() + ")"); + } else if (structure instanceof StoredSignalMeta) { + final StoredSignalMeta meta = (StoredSignalMeta) structure; + descriptions.add("0143(signalType=" + meta.getSignalType() + ",flags=" + meta.getSignalFlags() + ",cursor=" + meta.getCursor() + ")"); + } else if (structure instanceof StoredSignalMetaExtended) { + final byte[] rawPayload = ((StoredSignalMetaExtended) structure).getRawPayload(); + descriptions.add("0146(" + GB.hexdump(rawPayload) + ")"); + } else if (structure instanceof StoredSignalData) { + descriptions.add("0144(samples)"); + } else if (structure instanceof RawWithingsStructure) { + final RawWithingsStructure raw = (RawWithingsStructure) structure; + descriptions.add(String.format("0x%04x(raw=%s)", raw.getType() & 0xffff, GB.hexdump(raw.getRawData()))); + } else { + descriptions.add(String.format("0x%04x", structure.getType() & 0xffff)); } } + return descriptions.toString(); } private int computeEcgProgressPercent() { @@ -425,9 +466,19 @@ public class WithingsEcgHandler implements ResponseHandler { } if (!support.hasEndOfTransmission(response)) { + logger.debug("Withings ECG waveform response awaiting more packets for ts={} structures={}", + startTimestampMs > 0 ? startTimestampMs : requestedRecordKey.getTimestampMs(), + describeStructures(structures)); return; } + logger.debug("Withings ECG waveform response reached EOT for ts={} deleteKeyPresent={} verifyDeletion={} samples={} structures={}", + startTimestampMs > 0 ? startTimestampMs : requestedRecordKey.getTimestampMs(), + deleteKey != null, + verifyDeletion, + waveform.size(), + describeStructures(structures)); + if (startTimestampMs <= 0) { startTimestampMs = requestedRecordKey.getTimestampMs(); } @@ -508,35 +559,6 @@ public class WithingsEcgHandler implements ResponseHandler { } } - private String describeStructures(final List structures) { - final List descriptions = new ArrayList<>(structures.size()); - for (final WithingsStructure structure : structures) { - if (structure instanceof StoredMeasureMeta) { - final StoredMeasureMeta meta = (StoredMeasureMeta) structure; - descriptions.add("0116(type=" + meta.getMeasurementType() + ",ts=" + meta.getTimestampMs() + ")"); - } else if (structure instanceof StoredMeasureData) { - final StoredMeasureData data = (StoredMeasureData) structure; - descriptions.add("0117(type=" + data.getMeasurementType() + ",raw=" + data.getRawValue() + ")"); - } else if (structure instanceof StoredMeasureDataExtend) { - final StoredMeasureDataExtend dataExt = (StoredMeasureDataExtend) structure; - descriptions.add("0149(type=" + dataExt.getMeasurementType() + ",extra=" + dataExt.getExtraData() + ")"); - } else if (structure instanceof StoredSignalMeta) { - final StoredSignalMeta meta = (StoredSignalMeta) structure; - descriptions.add("0143(signalType=" + meta.getSignalType() + ",flags=" + meta.getSignalFlags() + ",cursor=" + meta.getCursor() + ")"); - } else if (structure instanceof StoredSignalMetaExtended) { - final byte[] rawPayload = ((StoredSignalMetaExtended) structure).getRawPayload(); - descriptions.add("0146(" + GB.hexdump(rawPayload) + ")"); - } else if (structure instanceof StoredSignalData) { - descriptions.add("0144(samples)"); - } else if (structure instanceof RawWithingsStructure) { - final RawWithingsStructure raw = (RawWithingsStructure) structure; - descriptions.add(String.format("0x%04x(raw=%s)", raw.getType() & 0xffff, GB.hexdump(raw.getRawData()))); - } else { - descriptions.add(String.format("0x%04x", structure.getType() & 0xffff)); - } - } - return descriptions.toString(); - } } private void storeWaveform(final long start,