class BluetoothPeripheral {

    public boolean setNotify(@NotNull final BluetoothGattCharacteristic characteristic, final boolean enable) {
        Objects.requireNonNull(characteristic, "no valid characteristic provided");

        // Check if gatt object is valid
        if (bluetoothGatt == null) {
            Timber.e("gatt is 'null', ignoring set notify request");
            return false;
        }

        // Get the Client Configuration Descriptor for the characteristic
        final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(CCC_DESCRIPTOR_UUID));
        if (descriptor == null) {
            Timber.e("could not get CCC descriptor for characteristic %s", characteristic.getUuid());
            return false;
        }

        // Check if characteristic has NOTIFY or INDICATE properties and set the correct byte value to be written
        byte[] value;
        int properties = characteristic.getProperties();
        if ((properties & PROPERTY_NOTIFY) > 0) {
            value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
        } else if ((properties & PROPERTY_INDICATE) > 0) {
            value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
        } else {
            Timber.e("characteristic %s does not have notify or indicate property", characteristic.getUuid());
            return false;
        }
        final byte[] finalValue = enable ? value : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;

        // Queue Runnable to turn on/off the notification now that all checks have been passed
        boolean result = commandQueue.add(new Runnable() {
            @Override
            public void run() {
                if (!isConnected()) {
                    completedCommand();
                    return;
                }

                // First set notification for Gatt object
                if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable)) {
                    Timber.e("setCharacteristicNotification failed for characteristic: %s", characteristic.getUuid());
                }

                // Then write to descriptor
                currentWriteBytes = finalValue;
                descriptor.setValue(finalValue);
                boolean result;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    result = bluetoothGatt.writeDescriptor(descriptor);
                } else {
                    // Up to Android 6 there is a bug where Android takes the writeType of the parent characteristic instead of always WRITE_TYPE_DEFAULT
                    // See: https://android.googlesource.com/platform/frameworks/base/+/942aebc95924ab1e7ea1e92aaf4e7fc45f695a6c%5E%21/#F0
                    final BluetoothGattCharacteristic parentCharacteristic = descriptor.getCharacteristic();
                    final int originalWriteType = parentCharacteristic.getWriteType();
                    parentCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
                    result = bluetoothGatt.writeDescriptor(descriptor);
                    parentCharacteristic.setWriteType(originalWriteType);
                }
                if (!result) {
                    Timber.e("writeDescriptor failed for descriptor: %s", descriptor.getUuid());
                    completedCommand();
                } else {
                    nrTries++;
                }
            }
        });

        if (result) {
            nextCommand();
        } else {
            Timber.e("could not enqueue setNotify command");
        }

        return result;
    }

      BluetoothPeripheral(@NotNull Context context, @NotNull BluetoothDevice device, @NotNull InternalCallback listener, BluetoothPeripheralCallback peripheralCallback, Handler callbackHandler);

}