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,