Skip to content

Commit

Permalink
fixes #10 (Add support for 128 bit service and characteristic UUIDs)
Browse files Browse the repository at this point in the history
  • Loading branch information
vkolotov committed Nov 13, 2018
1 parent 82940cb commit 31506f4
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
*/
public class BluetoothGattParser {

private final static String BASE_UUID = "-0000-1000-8000-00805F9B34FB";

private final Logger logger = LoggerFactory.getLogger(GenericCharacteristicParser.class);

private BluetoothGattSpecificationReader specificationReader;
Expand All @@ -83,7 +85,7 @@ public class BluetoothGattParser {
* @return true if the parser has loaded definitions for that characteristic, false otherwise
*/
public boolean isKnownCharacteristic(String characteristicUUID) {
return specificationReader.getCharacteristicByUUID(getShortUUID(characteristicUUID)) != null;
return specificationReader.getCharacteristicByUUID(trim(characteristicUUID)) != null;
}

/**
Expand All @@ -92,7 +94,7 @@ public boolean isKnownCharacteristic(String characteristicUUID) {
* @return true if the parser has loaded definitions for that service, false otherwise
*/
public boolean isKnownService(String serviceUUID) {
return specificationReader.getService(getShortUUID(serviceUUID)) != null;
return specificationReader.getService(trim(serviceUUID)) != null;
}

/**
Expand All @@ -118,7 +120,7 @@ public GattResponse parse(String characteristicUUID, byte[] raw) throws Characte
* @return list of fields represented by {@link GattRequest} for a write operation
*/
public GattRequest prepare(String characteristicUUID) {
characteristicUUID = getShortUUID(characteristicUUID);
characteristicUUID = trim(characteristicUUID);
return new GattRequest(characteristicUUID,
specificationReader.getFields(specificationReader.getCharacteristicByUUID(characteristicUUID)));
}
Expand All @@ -135,7 +137,7 @@ public GattRequest prepare(String characteristicUUID) {
* @return list of fields represented by {@link GattRequest} for a write operation
*/
public GattRequest prepare(String characteristicUUID, byte[] initial) {
characteristicUUID = getShortUUID(characteristicUUID);
characteristicUUID = trim(characteristicUUID);
return new GattRequest(characteristicUUID, parseFields(characteristicUUID, initial));
}

Expand Down Expand Up @@ -170,7 +172,7 @@ public byte[] serialize(GattRequest gattRequest, boolean strict) {
throw new IllegalArgumentException("GATT request is not valid");
}
synchronized (customParsers) {
String characteristicUUID = getShortUUID(gattRequest.getCharacteristicUUID());
String characteristicUUID = trim(gattRequest.getCharacteristicUUID());
if (strict && !isValidForWrite(characteristicUUID)) {
throw new CharacteristicFormatException(
"Characteristic is not valid for write: " + characteristicUUID);
Expand All @@ -188,7 +190,7 @@ public byte[] serialize(GattRequest gattRequest, boolean strict) {
* @return a GATT service specification by its UUID
*/
public Service getService(String serviceUUID) {
return specificationReader.getService(getShortUUID(serviceUUID));
return specificationReader.getService(trim(serviceUUID));
}

/**
Expand All @@ -197,7 +199,7 @@ public Service getService(String serviceUUID) {
* @return a GATT characteristic specification by its UUID
*/
public Characteristic getCharacteristic(String characteristicUUID) {
return specificationReader.getCharacteristicByUUID(getShortUUID(characteristicUUID));
return specificationReader.getCharacteristicByUUID(trim(characteristicUUID));
}

/**
Expand All @@ -209,7 +211,7 @@ public Characteristic getCharacteristic(String characteristicUUID) {
* @return a list of field specifications for a given characteristic
*/
public List<Field> getFields(String characteristicUUID) {
return specificationReader.getFields(getCharacteristic(getShortUUID(characteristicUUID)));
return specificationReader.getFields(getCharacteristic(trim(characteristicUUID)));
}

/**
Expand All @@ -219,7 +221,7 @@ public List<Field> getFields(String characteristicUUID) {
*/
public void registerParser(String characteristicUUID, CharacteristicParser parser) {
synchronized (customParsers) {
customParsers.put(getShortUUID(characteristicUUID), parser);
customParsers.put(trim(characteristicUUID), parser);
}
}

Expand All @@ -233,7 +235,7 @@ public void registerParser(String characteristicUUID, CharacteristicParser parse
* @return true if a given characteristic is valid for read operation
*/
public boolean isValidForRead(String characteristicUUID) {
Characteristic characteristic = specificationReader.getCharacteristicByUUID(getShortUUID(characteristicUUID));
Characteristic characteristic = specificationReader.getCharacteristicByUUID(trim(characteristicUUID));
return characteristic != null && characteristic.isValidForRead();
}

Expand All @@ -247,7 +249,7 @@ public boolean isValidForRead(String characteristicUUID) {
* @return true if a given characteristic is valid for write operation
*/
public boolean isValidForWrite(String characteristicUUID) {
Characteristic characteristic = specificationReader.getCharacteristicByUUID(getShortUUID(characteristicUUID));
Characteristic characteristic = specificationReader.getCharacteristicByUUID(trim(characteristicUUID));
return characteristic != null && characteristic.isValidForWrite();
}

Expand Down Expand Up @@ -346,15 +348,20 @@ public byte[] serialize(String raw, int radix) {
return bytes;
}

private String getShortUUID(String uuid) {
if (uuid.length() < 8) {
return uuid.toUpperCase();
private String trim(String uuid) {
if (uuid.endsWith(BASE_UUID)) {
String shortUUID = uuid.substring(0, 8).toUpperCase();
if (shortUUID.startsWith("0000")) {
return shortUUID.substring(4, 8);
} else {
return shortUUID;
}
}
return Long.toHexString(Long.valueOf(uuid.substring(0, 8), 16)).toUpperCase();
return uuid.toUpperCase();
}

private LinkedHashMap<String, FieldHolder> parseFields(String characteristicUUID, byte[] raw) {
characteristicUUID = getShortUUID(characteristicUUID);
characteristicUUID = trim(characteristicUUID);
synchronized (customParsers) {
if (!isValidForRead(characteristicUUID)) {
throw new CharacteristicFormatException("Characteristic is not valid for read: " + characteristicUUID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@ public void loadExtensionsFromFolder(String path) {
readCharacteristics(getFilesFromFolder(characteristicsFolderName));
}

public void addCharacteristic(URL url) {
logger.info("Adding a characteristic: {}", url);
Characteristic characteristic = getCharacteristic(url);
if (characteristic != null) {
addCharacteristic(characteristic);
}
}

private static URL getSpecResourceURL(URL catalogURL, String characteristicType) throws MalformedURLException {
String catalogFilePath = catalogURL.getFile();
int lastSlashPos = catalogFilePath.lastIndexOf('/');
Expand Down Expand Up @@ -368,7 +376,7 @@ private Characteristic loadCharacteristic(String uuid) {
return getCharacteristic(url);
}

private void readServices(List<URL> files) {
void readServices(List<URL> files) {
for (URL file : files) {
Service service = getService(file);
if (service != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Characteristic xsi:noNamespaceSchemaLocation="http://schemas.bluetooth.org/Documents/characteristic.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Temperature and humidity"
type="com.oregonscientific.characteristic.temperature_and_humidity" uuid="74E78E10" last-modified="2018-01-24"
type="com.oregonscientific.characteristic.temperature_and_humidity" uuid="74E78E10-C6A4-11E2-B7A9-0002A5D5C51B" last-modified="2018-10-10"
approved="No">
<Value>
<Field name="Flags">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Service xsi:noNamespaceSchemaLocation="http://schemas.bluetooth.org/Documents/service.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Oregon Scientific BLE Weather Station temperature and humidity service"
type="com.oregonscientific.service.temperature_and_humidity" uuid="74E7FE00" last-modified="2018-01-24">
type="com.oregonscientific.service.temperature_and_humidity" uuid="74E7FE00-C6A4-11E2-B7A9-0002A5D5C51B" last-modified="2018-10-10">
<InformativeText>
<Abstract>
Oregon Scientific BLE Weather Station temperature and humidity service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@
*/

import org.junit.Test;
import org.sputnikdev.bluetooth.gattparser.spec.BluetoothGattSpecificationReader;
import org.sputnikdev.bluetooth.gattparser.spec.Enumeration;

import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -146,10 +149,10 @@ public void testOregonWeatherStation() {
byte[] actual = {0x01, 0x0d, 0x01, (byte) 0xec, 0x00, (byte) 0xff, 0x7f, (byte) 0xff, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, (byte) 0xff, (byte) 0xff, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f};
byte[] minMax = {(byte) 130, 0x7f, 0x7f, 0x7f, 0x21, 0x01, (byte) 0xf8, 0x00, 0x33, 0x01, (byte) 0xba, 0x00, (byte) 0xff, 0x7f, (byte) 0xff, 0x7f, (byte) 0xff, 0x7f, (byte) 0xff, 0x7f};

assertTrue(parser.isKnownService("74E7FE00"));
assertTrue(parser.isKnownCharacteristic("74E78E10"));
assertTrue(parser.isKnownService("74E7FE00-C6A4-11E2-B7A9-0002A5D5C51B"));
assertTrue(parser.isKnownCharacteristic("74E78E10-C6A4-11E2-B7A9-0002A5D5C51B"));

GattResponse response1 = parser.parse("74E78E10", actual);
GattResponse response1 = parser.parse("74E78E10-C6A4-11E2-B7A9-0002A5D5C51B", actual);
assertEquals(26.9, response1.get("Base temp").getDouble(), 0.1);
assertEquals(23.6, response1.get("Sensor 1 temp").getDouble(), 0.1);
assertEquals(3276.7, response1.get("Sensor 2 temp").getDouble(), 0.1);
Expand All @@ -167,7 +170,7 @@ public void testOregonWeatherStation() {
assertEquals(127, (int) response1.get("Sensor 2 humidity max").getInteger());


GattResponse response2 = parser.parse("74E78E10", minMax);
GattResponse response2 = parser.parse("74E78E10-C6A4-11E2-B7A9-0002A5D5C51B", minMax);
assertEquals(127, (int) response2.get("Sensor 2 humidity min").getInteger());
assertEquals(127, (int) response2.get("Sensor 3 humidity max").getInteger());
assertEquals(127, (int) response2.get("Sensor 3 humidity min").getInteger());
Expand Down Expand Up @@ -419,6 +422,22 @@ public void testMinewKeyfinderGetAuthStatusCode() {
assertArrayEquals(expected, parser.serialize(request, false));
}

@Test
public void testRooliSwitch() throws MalformedURLException {
ClassLoader classLoader = getClass().getClassLoader();
BluetoothGattParserFactory.getSpecificationReader()
.addCharacteristic(classLoader.getResource("gatt/characteristic/com.r00li.bluetooth.characteristic.rswitchv1.xml"));

assertTrue(parser.isKnownCharacteristic("9ED90C00-71D7-11E5-977A-0002A5D5C51B"));

GattRequest request = parser.prepare("9ED90C00-71D7-11E5-977A-0002A5D5C51B");
request.setField("Switch state", 255);

byte[] raw = parser.serialize(request);
assertEquals(1, raw.length);
assertEquals("11111111", Integer.toBinaryString(Byte.toUnsignedInt(raw[0])));
}

private void assertField(Integer expectedValue, String expectedEnum,
String characteristicUUID, byte[] data, String fieldName) {
FieldHolder fieldHolder = parser.parse(characteristicUUID, data).get(fieldName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?><!-- Copyright 2011 Bluetooth SIG, Inc. All rights reserved. -->
<Characteristic xsi:noNamespaceSchemaLocation="http://schemas.bluetooth.org/Documents/characteristic.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
type="com.r00li.bluetooth.characteristic.rswitchv1" name="RSwitch_V1"
uuid="9ED90C00-71D7-11E5-977A-0002A5D5C51B">
<InformativeText></InformativeText>
<Value>
<Field name="Switch state">
<Requirement>Mandatory</Requirement>
<Format>8bit</Format>
<Enumerations>
<Enumeration key="0" value="Off"/>
<Enumeration key="255" value="On"/>
<ReservedForFutureUse start="1" end="254"/>
</Enumerations>
</Field>
</Value>
</Characteristic>

0 comments on commit 31506f4

Please sign in to comment.