diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java
new file mode 100644
index 000000000..76f0a839e
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java
@@ -0,0 +1,42 @@
+package nodomain.freeyourgadget.gadgetbridge.service.btle;
+
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+
+/**
+ * Base class for GattCallbacks wishing to just implement a few of the methods.
+ */
+public abstract class AbstractGattCallback implements GattCallback {
+    @Override
+    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+    }
+
+    @Override
+    public void onServicesDiscovered(BluetoothGatt gatt) {
+    }
+
+    @Override
+    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+    }
+
+    @Override
+    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+    }
+
+    @Override
+    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+    }
+
+    @Override
+    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+    }
+
+    @Override
+    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+    }
+
+    @Override
+    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+    }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java
index 6f2c49741..9564695db 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java
@@ -9,6 +9,7 @@ import android.bluetooth.BluetoothGattDescriptor;
 import android.bluetooth.BluetoothGattService;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.support.annotation.Nullable;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -19,9 +20,9 @@ import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 
-import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
 import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
 import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
+import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
 
 /**
  * One queue/thread per connectable device.
@@ -49,7 +50,7 @@ public final class BtLEQueue {
     private CountDownLatch mWaitForActionResultLatch;
     private CountDownLatch mConnectionLatch;
     private BluetoothGattCharacteristic mWaitCharacteristic;
-    private GattCallback mExternalGattCallback;
+    private final InternalGattCallback internalGattCallback;
 
     private Thread dispatchThread = new Thread("GadgetBridge GATT Dispatcher") {
 
@@ -60,6 +61,8 @@ public final class BtLEQueue {
             while (!mDisposed && !mCrashed) {
                 try {
                     Transaction transaction = mTransactions.take();
+                    internalGattCallback.setTransactionGattCallback(null);
+
                     if (!isConnected()) {
                         // TODO: request connection and initialization from the outside and wait until finished
 
@@ -73,6 +76,7 @@ public final class BtLEQueue {
                         mConnectionLatch = null;
                     }
 
+                    internalGattCallback.setTransactionGattCallback(transaction.getGattCallback());
                     mAbortTransaction = false;
                     // Run all actions of the transaction until one doesn't succeed
                     for (BtLEAction action : transaction.getActions()) {
@@ -106,6 +110,7 @@ public final class BtLEQueue {
                 } finally {
                     mWaitForActionResultLatch = null;
                     mWaitCharacteristic = null;
+                    internalGattCallback.reset();
                 }
             }
             LOG.info("Queue Dispatch Thread terminated.");
@@ -115,7 +120,7 @@ public final class BtLEQueue {
     public BtLEQueue(BluetoothAdapter bluetoothAdapter, GBDevice gbDevice, GattCallback externalGattCallback, Context context) {
         mBluetoothAdapter = bluetoothAdapter;
         mGbDevice = gbDevice;
-        mExternalGattCallback = externalGattCallback;
+        internalGattCallback = new InternalGattCallback(externalGattCallback);
         mContext = context;
 
         dispatchThread.start();
@@ -254,7 +259,25 @@ public final class BtLEQueue {
 
     // Implements callback methods for GATT events that the app cares about.  For example,
     // connection change and services discovered.
-    private final BluetoothGattCallback internalGattCallback = new BluetoothGattCallback() {
+    private final class InternalGattCallback extends BluetoothGattCallback {
+        private @Nullable GattCallback mTransactionGattCallback;
+        private GattCallback mExternalGattCallback;
+
+        public InternalGattCallback(GattCallback externalGattCallback) {
+            mExternalGattCallback = externalGattCallback;
+        }
+
+        public void setTransactionGattCallback(@Nullable GattCallback callback) {
+            mTransactionGattCallback = callback;
+        }
+
+        private GattCallback getCallbackToUse() {
+            if (mTransactionGattCallback != null) {
+                return mTransactionGattCallback;
+            }
+            return mExternalGattCallback;
+        }
+
         @Override
         public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
             LOG.debug("connection state change, newState: " + newState + getStatusString(status));
@@ -293,9 +316,9 @@ public final class BtLEQueue {
             }
 
             if (status == BluetoothGatt.GATT_SUCCESS) {
-                if (mExternalGattCallback != null) {
+                if (getCallbackToUse() != null) {
                     // only propagate the successful event
-                    mExternalGattCallback.onServicesDiscovered(gatt);
+                    getCallbackToUse().onServicesDiscovered(gatt);
                 }
             } else {
                 LOG.warn("onServicesDiscovered received: " + status);
@@ -308,8 +331,8 @@ public final class BtLEQueue {
             if (!checkCorrectGattInstance(gatt, "characteristic write")) {
                 return;
             }
-            if (mExternalGattCallback != null) {
-                mExternalGattCallback.onCharacteristicWrite(gatt, characteristic, status);
+            if (getCallbackToUse() != null) {
+                getCallbackToUse().onCharacteristicWrite(gatt, characteristic, status);
             }
             checkWaitingCharacteristic(characteristic, status);
         }
@@ -322,8 +345,8 @@ public final class BtLEQueue {
             if (!checkCorrectGattInstance(gatt, "characteristic read")) {
                 return;
             }
-            if (mExternalGattCallback != null) {
-                mExternalGattCallback.onCharacteristicRead(gatt, characteristic, status);
+            if (getCallbackToUse() != null) {
+                getCallbackToUse().onCharacteristicRead(gatt, characteristic, status);
             }
             checkWaitingCharacteristic(characteristic, status);
         }
@@ -334,8 +357,8 @@ public final class BtLEQueue {
             if (!checkCorrectGattInstance(gatt, "descriptor read")) {
                 return;
             }
-            if (mExternalGattCallback != null) {
-                mExternalGattCallback.onDescriptorRead(gatt, descriptor, status);
+            if (getCallbackToUse() != null) {
+                getCallbackToUse().onDescriptorRead(gatt, descriptor, status);
             }
             checkWaitingCharacteristic(descriptor.getCharacteristic(), status);
         }
@@ -346,8 +369,8 @@ public final class BtLEQueue {
             if (!checkCorrectGattInstance(gatt, "descriptor write")) {
                 return;
             }
-            if (mExternalGattCallback != null) {
-                mExternalGattCallback.onDescriptorWrite(gatt, descriptor, status);
+            if (getCallbackToUse() != null) {
+                getCallbackToUse().onDescriptorWrite(gatt, descriptor, status);
             }
             checkWaitingCharacteristic(descriptor.getCharacteristic(), status);
         }
@@ -363,8 +386,8 @@ public final class BtLEQueue {
                 LOG.info("Ignoring characteristic change event from wrong BluetoothGatt instance");
                 return;
             }
-            if (mExternalGattCallback != null) {
-                mExternalGattCallback.onCharacteristicChanged(gatt, characteristic);
+            if (getCallbackToUse() != null) {
+                getCallbackToUse().onCharacteristicChanged(gatt, characteristic);
             }
         }
 
@@ -378,8 +401,8 @@ public final class BtLEQueue {
                 LOG.info("Ignoring remote rssi event from wrong BluetoothGatt instance");
                 return;
             }
-            if (mExternalGattCallback != null) {
-                mExternalGattCallback.onReadRemoteRssi(gatt, rssi, status);
+            if (getCallbackToUse() != null) {
+                getCallbackToUse().onReadRemoteRssi(gatt, rssi, status);
             }
         }
 
@@ -398,9 +421,13 @@ public final class BtLEQueue {
                 }
             }
         }
-    };
 
-    private String getStatusString(int status) {
-        return status == BluetoothGatt.GATT_SUCCESS ? " (success)" : " (failed: " + status + ")";
-    }
+        private String getStatusString(int status) {
+            return status == BluetoothGatt.GATT_SUCCESS ? " (success)" : " (failed: " + status + ")";
+        }
+
+        public void reset() {
+            mTransactionGattCallback = null;
+        }
+    };
 }
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java
index ffa1d91a9..4006e1cd9 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java
@@ -1,5 +1,7 @@
 package nodomain.freeyourgadget.gadgetbridge.service.btle;
 
+import android.support.annotation.Nullable;
+
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -17,6 +19,7 @@ public class Transaction {
     private String mName;
     private List<BtLEAction> mActions = new ArrayList<>(4);
     private long creationTimestamp = System.currentTimeMillis();
+    private @Nullable GattCallback gattCallback;
 
     public Transaction(String taskName) {
         this.mName = taskName;
@@ -46,4 +49,15 @@ public class Transaction {
     public String toString() {
         return String.format(Locale.US, "%s: Transaction task: %s with %d actions", getCreationTime(), getTaskName(), mActions.size());
     }
+
+    public void setGattCallback(@Nullable GattCallback callback) {
+        gattCallback = callback;
+    }
+
+    /**
+     * Returns the GattCallback for this transaction, or null if none.
+     */
+    public @Nullable GattCallback getGattCallback() {
+        return gattCallback;
+    }
 }
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java
index 77d9a338b..85eb3da6d 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java
@@ -1,6 +1,7 @@
 package nodomain.freeyourgadget.gadgetbridge.service.btle;
 
 import android.bluetooth.BluetoothGattCharacteristic;
+import android.support.annotation.Nullable;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -60,6 +61,19 @@ public class TransactionBuilder {
         return this;
     }
 
+    /**
+     * Sets a GattCallback instance that will be called when the transaction is executed,
+     * resulting in GattCallback events.
+     * @param callback the callback to set, may be null
+     */
+    public void setGattCallback(@Nullable GattCallback callback) {
+        mTransaction.setGattCallback(callback);
+    }
+
+    public @Nullable GattCallback getGattCallback() {
+        return mTransaction.getGattCallback();
+    }
+
     /**
      * To be used as the final step to execute the transaction by the given queue.
      *