public void removeNotify(
      UUID serviceUUID, UUID characteristicUUID, Callback success, Callback fail) {

    Log.d(LOG_TAG, "removeNotify");

    if (gatt == null) {
      fail.invoke("BluetoothGatt is null");
      return;
    }

    BluetoothGattService service = gatt.getService(serviceUUID);
    BluetoothGattCharacteristic characteristic =
        findNotifyCharacteristic(service, characteristicUUID);
    // String key = generateHashKey(serviceUUID, characteristic);

    if (characteristic != null) {

      if (gatt.setCharacteristicNotification(characteristic, false)) {
        success.invoke();
      } else {
        // TODO we can probably ignore and return success anyway since we removed the notification
        // callback
        fail.invoke("Failed to stop notification for " + characteristicUUID);
      }

    } else {
      fail.invoke("Characteristic " + characteristicUUID + " not found");
    }
  }
  public JSONObject asJSONObject(BluetoothGatt gatt) {

    JSONObject json = asJSONObject();

    try {
      JSONArray servicesArray = new JSONArray();
      JSONArray characteristicsArray = new JSONArray();
      json.put("services", servicesArray);
      json.put("characteristics", characteristicsArray);

      if (connected && gatt != null) {
        for (BluetoothGattService service : gatt.getServices()) {
          servicesArray.put(UUIDHelper.uuidToString(service.getUuid()));

          for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
            JSONObject characteristicsJSON = new JSONObject();
            characteristicsArray.put(characteristicsJSON);

            characteristicsJSON.put("service", UUIDHelper.uuidToString(service.getUuid()));
            characteristicsJSON.put(
                "characteristic", UUIDHelper.uuidToString(characteristic.getUuid()));
            // characteristicsJSON.put("instanceId", characteristic.getInstanceId());

            characteristicsJSON.put("properties", Helper.decodeProperties(characteristic));
            // characteristicsJSON.put("propertiesValue", characteristic.getProperties());

            if (characteristic.getPermissions() > 0) {
              characteristicsJSON.put("permissions", Helper.decodePermissions(characteristic));
              // characteristicsJSON.put("permissionsValue", characteristic.getPermissions());
            }

            JSONArray descriptorsArray = new JSONArray();

            for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
              JSONObject descriptorJSON = new JSONObject();
              descriptorJSON.put("uuid", UUIDHelper.uuidToString(descriptor.getUuid()));
              descriptorJSON.put("value", descriptor.getValue()); // always blank

              if (descriptor.getPermissions() > 0) {
                descriptorJSON.put("permissions", Helper.decodePermissions(descriptor));
                // descriptorJSON.put("permissionsValue", descriptor.getPermissions());
              }
              descriptorsArray.put(descriptorJSON);
            }
            if (descriptorsArray.length() > 0) {
              characteristicsJSON.put("descriptors", descriptorsArray);
            }
          }
        }
      }
    } catch (JSONException e) { // TODO better error handling
      e.printStackTrace();
    }

    return json;
  }
  @Override
  public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {

    Log.d(
        LOG_TAG,
        "onConnectionStateChange da "
            + status
            + " a "
            + newState
            + " peripheral:"
            + device.getAddress());

    this.gatt = gatt;

    if (newState == BluetoothGatt.STATE_CONNECTED) {

      connected = true;
      gatt.discoverServices();

    } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {

      if (connected) {
        connected = false;

        if (gatt != null) {
          gatt.close();
          gatt = null;
        }

        WritableMap map = Arguments.createMap();
        map.putString("peripheral", device.getAddress());
        sendEvent("BleManagerDisconnectPeripheral", map);
        Log.d(LOG_TAG, "BleManagerDisconnectPeripheral peripheral:" + device.getAddress());
      }
      if (connectFailCallback != null) {
        connectFailCallback.invoke();
        connectFailCallback = null;
        connectCallback = null;
      }
    }
  }
 public void disconnect() {
   connectCallback = null;
   connected = false;
   if (gatt != null) {
     gatt.close();
     gatt = null;
     Log.d(LOG_TAG, "Disconnect");
     WritableMap map = Arguments.createMap();
     map.putString("peripheral", device.getAddress());
     sendEvent("BleManagerDisconnectPeripheral", map);
   } else Log.d(LOG_TAG, "GATT is null");
 }
  public void doWrite(BluetoothGattCharacteristic characteristic, byte[] data) {

    // Log.d(LOG_TAG, "doWrite");

    characteristic.setValue(data);

    if (gatt.writeCharacteristic(characteristic)) {
      // Log.d(LOG_TAG, "doWrite completato");
    } else {
      Log.d(LOG_TAG, "errore doWrite");
    }
  }
  public void read(
      UUID serviceUUID, UUID characteristicUUID, Callback successCallback, Callback failCallback) {

    if (gatt == null) {
      failCallback.invoke("BluetoothGatt is null");
      return;
    }

    BluetoothGattService service = gatt.getService(serviceUUID);
    BluetoothGattCharacteristic characteristic =
        findReadableCharacteristic(service, characteristicUUID);

    if (characteristic == null) {
      failCallback.invoke("Characteristic " + characteristicUUID + " not found.");
    } else {
      readCallback = successCallback;
      readFailCallback = failCallback;
      if (!gatt.readCharacteristic(characteristic)) {
        readCallback = null;
        failCallback.invoke("Read failed");
      }
    }
  }
  public void write(
      UUID serviceUUID,
      UUID characteristicUUID,
      byte[] data,
      Callback successCallback,
      Callback failCallback,
      int writeType) {

    Log.d(LOG_TAG, "write interno peripheral");

    if (gatt == null) {
      failCallback.invoke("BluetoothGatt is null");

    } else {

      BluetoothGattService service = gatt.getService(serviceUUID);
      BluetoothGattCharacteristic characteristic =
          findWritableCharacteristic(service, characteristicUUID, writeType);
      characteristic.setWriteType(writeType);

      if (characteristic == null) {
        failCallback.invoke("Characteristic " + characteristicUUID + " not found.");
      } else {

        if (writeQueue.size() > 0) {
          failCallback.invoke("Scrittura con byte ancora in coda");
        }

        if (writeCallback != null) {
          failCallback.invoke("Altra scrittura in corso");
        }

        if (writeQueue.size() == 0 && writeCallback == null) {

          if (BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT == writeType) {
            writeCallback = successCallback;
            writeFailCallback = failCallback;
          } else successCallback.invoke();

          if (data.length > 20) {
            int dataLength = data.length;
            int count = 0;
            byte[] firstMessage = null;
            while (count < dataLength && (dataLength - count > 20)) {
              if (count == 0) {
                firstMessage = Arrays.copyOfRange(data, count, count + 20);
              } else {
                byte[] splitMessage = Arrays.copyOfRange(data, count, count + 20);
                writeQueue.add(splitMessage);
              }
              count += 20;
            }
            if (count < dataLength) {
              // Rimangono byte in coda
              byte[] splitMessage = Arrays.copyOfRange(data, count, data.length);
              Log.d(LOG_TAG, "Lunghezza ultimo messaggio: " + splitMessage.length);
              writeQueue.add(splitMessage);
            }

            Log.d(LOG_TAG, "Messaggi in coda: " + writeQueue.size());
            doWrite(characteristic, firstMessage);
          } else {
            characteristic.setValue(data);

            if (gatt.writeCharacteristic(characteristic)) {
              Log.d(LOG_TAG, "write completato");
            } else {
              writeCallback = null;
              failCallback.invoke("Write failed");
            }
          }
        }
      }
    }
  }
  // This seems way too complicated
  public void registerNotify(
      UUID serviceUUID, UUID characteristicUUID, Callback success, Callback fail) {

    Log.d(LOG_TAG, "registerNotify");

    if (gatt == null) {
      fail.invoke("BluetoothGatt is null");
      return;
    }

    BluetoothGattService service = gatt.getService(serviceUUID);
    BluetoothGattCharacteristic characteristic =
        findNotifyCharacteristic(service, characteristicUUID);
    // String key = generateHashKey(serviceUUID, characteristic);

    if (characteristic != null) {
      Log.d(LOG_TAG, "characteristic ok");

      if (gatt.setCharacteristicNotification(characteristic, true)) {

        BluetoothGattDescriptor descriptor =
            characteristic.getDescriptor(UUID.fromString(CHARACTERISTIC_NOTIFICATION_CONFIG));
        if (descriptor != null) {
          Log.d(LOG_TAG, "trovato descriptor");

          // prefer notify over indicate
          if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
            Log.d(LOG_TAG, "Characteristic " + characteristicUUID + " set NOTIFY");
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
          } else if ((characteristic.getProperties()
                  & BluetoothGattCharacteristic.PROPERTY_INDICATE)
              != 0) {
            Log.d(LOG_TAG, "Characteristic " + characteristicUUID + " set INDICATE");
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
          } else {
            Log.d(
                LOG_TAG,
                "Characteristic "
                    + characteristicUUID
                    + " does not have NOTIFY or INDICATE property set");
          }

          if (gatt.writeDescriptor(descriptor)) {
            // Tutto ok
            Log.d(LOG_TAG, "registerNotify completato");
            success.invoke();
          } else {
            fail.invoke(
                "Failed to set client characteristic notification for " + characteristicUUID);
          }

        } else {
          fail.invoke("Set notification failed for " + characteristicUUID);
        }

      } else {
        fail.invoke("Failed to register notification for " + characteristicUUID);
      }

    } else {
      fail.invoke("Characteristic " + characteristicUUID + " not found");
    }
  }