/* * Copyright (c) 2020, Gluon * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL GLUON BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.gluonhq.attach.ble; import java.util.Optional; import java.util.UUID; import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; /** * All BLE UUIDs are of the form: 0000XXXX-0000-1000-8000-00805f9b34fb * * These specs specify the unique XXXX code for each of the currently existing * Services, Characteristics and Descriptors * * For more info about BLE see: * https://www.bluetooth.com/specifications/generic-attributes-overview * */ public class BleSpecs { private static final Logger LOG = Logger.getLogger(BleSpecs.class.getName()); private static final String UUID_BASE_PREFIX = "0000"; private static final String UUID_BASE_SUFIX = "-0000-1000-8000-00805f9b34fb"; /** * https://www.bluetooth.com/specifications/gatt/services */ public enum GattServices { ALERT_NOTIFICATION_SERVICE("Alert Notification Service", "org.bluetooth.service.alert_notification", 0x1811), AUTOMATION_IO("Automation IO", "org.bluetooth.service.automation_io", 0x1815), BATTERY_SERVICE("Battery Service", "org.bluetooth.service.battery_service", 0x180F), BLOOD_PRESSURE("Blood Pressure", "org.bluetooth.service.blood_pressure", 0x1810), BODY_COMPOSITION("Body Composition", "org.bluetooth.service.body_composition", 0x181B), BOND_MANAGEMENT("Bond Management", "org.bluetooth.service.bond_management", 0x181E), CONTINUOUS_GLUCOSE_MONITORING("Continuous Glucose Monitoring", "org.bluetooth.service.continuous_glucose_monitoring", 0x181F), CURRENT_TIME_SERVICE("Current Time Service", "org.bluetooth.service.current_time", 0x1805), CUSTOM_SERVICE("Custom Service", "", 0x0000), CYCLING_POWER("Cycling Power", "org.bluetooth.service.cycling_power", 0x1818), CYCLING_SPEED_AND_CADENCE("Cycling Speed and Cadence", "org.bluetooth.service.cycling_speed_and_cadence", 0x1816), DEVICE_INFORMATION("Device Information", "org.bluetooth.service.device_information", 0x180A), ENVIRONMENTAL_SENSING("Environmental Sensing", "org.bluetooth.service.environmental_sensing", 0x181A), FITNESS_MACHINE("Fitness Machine", "org.bluetooth.service.fitness_machine", 0x1826), GENERIC_ACCESS("Generic Access", "org.bluetooth.service.generic_access", 0x1800), GENERIC_ATTRIBUTE("Generic Attribute", "org.bluetooth.service.generic_attribute", 0x1801), GLUCOSE("Glucose", "org.bluetooth.service.glucose", 0x1808), HEALTH_THERMOMETER("Health Thermometer", "org.bluetooth.service.health_thermometer", 0x1809), HEART_RATE("Heart Rate", "org.bluetooth.service.heart_rate", 0x180D), HTTP_PROXY("HTTP Proxy", "org.bluetooth.service.http_proxy", 0x1823), HUMAN_INTERFACE_DEVICE("Human Interface Device", "org.bluetooth.service.human_interface_device", 0x1812), IMMEDIATE_ALERT("Immediate Alert", "org.bluetooth.service.immediate_alert", 0x1802), INDOOR_POSITIONING("Indoor Positioning", "org.bluetooth.service.indoor_positioning", 0x1821), INTERNET_PROTOCOL_SUPPORT("Internet Protocol Support", "org.bluetooth.service.internet_protocol_support", 0x1820), LINK_LOSS("Link Loss", "org.bluetooth.service.link_loss", 0x1803), LOCATION_AND_NAVIGATION("Location and Navigation", "org.bluetooth.service.location_and_navigation", 0x1819), NEXT_DST_CHANGE_SERVICE("Next DST Change Service", "org.bluetooth.service.next_dst_change", 0x1807), OBJECT_TRANSFER("Object Transfer", "org.bluetooth.service.object_transfer", 0x1825), PHONE_ALERT_STATUS_SERVICE("Phone Alert Status Service", "org.bluetooth.service.phone_alert_status", 0x180E), PULSE_OXIMETER("Pulse Oximeter", "org.bluetooth.service.pulse_oximeter", 0x1822), REFERENCE_TIME_UPDATE_SERVICE("Reference Time Update Service", "org.bluetooth.service.reference_time_update", 0x1806), RUNNING_SPEED_AND_CADENCE("Running Speed and Cadence", "org.bluetooth.service.running_speed_and_cadence", 0x1814), SCAN_PARAMETERS("Scan Parameters", "org.bluetooth.service.scan_parameters", 0x1813), TRANSPORT_DISCOVERY("Transport Discovery", "org.bluetooth.service.transport_discovery", 0x1824), TX_POWER("Tx Power", "org.bluetooth.service.tx_power", 0x1804), USER_DATA("User Data", "org.bluetooth.service.user_data", 0x181C), WEIGHT_SCALE("Weight Scale", "org.bluetooth.service.weight_scale", 0x181D); private final String specificationName; private final String specificationType; private final long assignedNumber; GattServices(String specificationName, String specificationType, long assignedNumber) { this.specificationName = specificationName; this.specificationType = specificationType; this.assignedNumber = assignedNumber; } public String getSpecificationName() { return specificationName; } public String getSpecificationType() { return specificationType; } public long getAssignedNumber() { return assignedNumber; } public static GattServices ofAssignedNumber(long number) { for (GattServices service : values()) { if (service.getAssignedNumber() == number) { return service; } } return GattServices.CUSTOM_SERVICE; } public static GattServices ofAssignedName(String specificationName) { for (GattServices service : values()) { if (service.getSpecificationName().equals(specificationName) || service.getSpecificationName().equals(specificationName + " Service")) { return service; } } return GattServices.CUSTOM_SERVICE; } } /** * https://www.bluetooth.com/specifications/gatt/characteristics */ public enum GattCharacteristics { AEROBIC_HEART_RATE_LOWER_LIMIT("Aerobic Heart Rate Lower Limit", "org.bluetooth.characteristic.aerobic_heart_rate_lower_limit", 0x2A7E), AEROBIC_HEART_RATE_UPPER_LIMIT("Aerobic Heart Rate Upper Limit", "org.bluetooth.characteristic.aerobic_heart_rate_upper_limit", 0x2A84), AEROBIC_THRESHOLD("Aerobic Threshold", "org.bluetooth.characteristic.aerobic_threshold", 0x2A7F), AGE("Age", "org.bluetooth.characteristic.age", 0x2A80), AGGREGATE("Aggregate", "org.bluetooth.characteristic.aggregate", 0x2A5A), ALERT_CATEGORY_ID("Alert Category ID", "org.bluetooth.characteristic.alert_category_id", 0x2A43), ALERT_CATEGORY_ID_BIT_MASK("Alert Category ID Bit Mask", "org.bluetooth.characteristic.alert_category_id_bit_mask", 0x2A42), ALERT_LEVEL("Alert Level", "org.bluetooth.characteristic.alert_level", 0x2A06), ALERT_NOTIFICATION_CONTROL_POINT("Alert Notification Control Point", "org.bluetooth.characteristic.alert_notification_control_point", 0x2A44), ALERT_STATUS("Alert Status", "org.bluetooth.characteristic.alert_status", 0x2A3F), ALTITUDE("Altitude", "org.bluetooth.characteristic.altitude", 0x2AB3), ANAEROBIC_HEART_RATE_LOWER_LIMIT("Anaerobic Heart Rate Lower Limit", "org.bluetooth.characteristic.anaerobic_heart_rate_lower_limit", 0x2A81), ANAEROBIC_HEART_RATE_UPPER_LIMIT("Anaerobic Heart Rate Upper Limit", "org.bluetooth.characteristic.anaerobic_heart_rate_upper_limit", 0x2A82), ANAEROBIC_THRESHOLD("Anaerobic Threshold", "org.bluetooth.characteristic.anaerobic_threshold", 0x2A83), ANALOG("Analog", "org.bluetooth.characteristic.analog", 0x2A58), APPARENT_WIND_DIRECTION("Apparent Wind Direction", "org.bluetooth.characteristic.apparent_wind_direction", 0x2A73), APPARENT_WIND_SPEED("Apparent Wind Speed", "org.bluetooth.characteristic.apparent_wind_speed", 0x2A72), APPEARANCE("Appearance", "org.bluetooth.characteristic.gap.appearance", 0x2A01), BAROMETRIC_PRESSURE_TREND("Barometric Pressure Trend", "org.bluetooth.characteristic.barometric_pressure_trend", 0x2AA3), BATTERY_LEVEL("Battery Level", "org.bluetooth.characteristic.battery_level", 0x2A19), BLOOD_PRESSURE_FEATURE("Blood Pressure Feature", "org.bluetooth.characteristic.blood_pressure_feature", 0x2A49), BLOOD_PRESSURE_MEASUREMENT("Blood Pressure Measurement", "org.bluetooth.characteristic.blood_pressure_measurement", 0x2A35), BODY_COMPOSITION_FEATURE("Body Composition Feature", "org.bluetooth.characteristic.body_composition_feature", 0x2A9B), BODY_COMPOSITION_MEASUREMENT("Body Composition Measurement", "org.bluetooth.characteristic.body_composition_measurement", 0x2A9C), BODY_SENSOR_LOCATION("Body Sensor Location", "org.bluetooth.characteristic.body_sensor_location", 0x2A38), BOND_MANAGEMENT_CONTROL_POINT("Bond Management Control Point", "org.bluetooth.characteristic.bond_management_control_point", 0x2AA4), BOND_MANAGEMENT_FEATURE("Bond Management Feature", "org.bluetooth.characteristic.bond_management_feature", 0x2AA5), BOOT_KEYBOARD_INPUT_REPORT("Boot Keyboard Input Report", "org.bluetooth.characteristic.boot_keyboard_input_report", 0x2A22), BOOT_KEYBOARD_OUTPUT_REPORT("Boot Keyboard Output Report", "org.bluetooth.characteristic.boot_keyboard_output_report", 0x2A32), BOOT_MOUSE_INPUT_REPORT("Boot Mouse Input Report", "org.bluetooth.characteristic.boot_mouse_input_report", 0x2A33), CENTRAL_ADDRESS_RESOLUTION("Central Address Resolution", "org.bluetooth.characteristic.gap.central_address_resolution_support", 0x2AA6), CGM_FEATURE("CGM Feature", "org.bluetooth.characteristic.cgm_feature", 0x2AA8), CGM_MEASUREMENT("CGM Measurement", "org.bluetooth.characteristic.cgm_measurement", 0x2AA7), CGM_SESSION_RUN_TIME("CGM Session Run Time", "org.bluetooth.characteristic.cgm_session_run_time", 0x2AAB), CGM_SESSION_START_TIME("CGM Session Start Time", "org.bluetooth.characteristic.cgm_session_start_time", 0x2AAA), CGM_SPECIFIC_OPS_CONTROL_POINT("CGM Specific Ops Control Point", "org.bluetooth.characteristic.cgm_specific_ops_control_point", 0x2AAC), CGM_STATUS("CGM Status", "org.bluetooth.characteristic.cgm_status", 0x2AA9), CROSS_TRAINER_DATA("Cross Trainer Data", "org.bluetooth.characteristic.cross_trainer_data", 0x2ACE), CSC_FEATURE("CSC Feature", "org.bluetooth.characteristic.csc_feature", 0x2A5C), CSC_MEASUREMENT("CSC Measurement", "org.bluetooth.characteristic.csc_measurement", 0x2A5B), CURRENT_TIME("Current Time", "org.bluetooth.characteristic.current_time", 0x2A2B), CUSTOM_CHARACTERISTIC("Custom Characteristic","", 0x0000), CYCLING_POWER_CONTROL_POINT("Cycling Power Control Point", "org.bluetooth.characteristic.cycling_power_control_point", 0x2A66), CYCLING_POWER_FEATURE("Cycling Power Feature", "org.bluetooth.characteristic.cycling_power_feature", 0x2A65), CYCLING_POWER_MEASUREMENT("Cycling Power Measurement", "org.bluetooth.characteristic.cycling_power_measurement", 0x2A63), CYCLING_POWER_VECTOR("Cycling Power Vector", "org.bluetooth.characteristic.cycling_power_vector", 0x2A64), DATABASE_CHANGE_INCREMENT("Database Change Increment", "org.bluetooth.characteristic.database_change_increment", 0x2A99), DATE_OF_BIRTH("Date of Birth", "org.bluetooth.characteristic.date_of_birth", 0x2A85), DATE_OF_THRESHOLD_ASSESSMENT("Date of Threshold Assessment", "org.bluetooth.characteristic.date_of_threshold_assessment", 0x2A86), DATE_TIME("Date Time", "org.bluetooth.characteristic.date_time", 0x2A08), DAY_DATE_TIME("Day Date Time", "org.bluetooth.characteristic.day_date_time", 0x2A0A), DAY_OF_WEEK("Day of Week", "org.bluetooth.characteristic.day_of_week", 0x2A09), DESCRIPTOR_VALUE_CHANGED("Descriptor Value Changed", "org.bluetooth.characteristic.descriptor_value_changed", 0x2A7D), DEVICE_NAME("Device Name", "org.bluetooth.characteristic.gap.device_name", 0x2A00), DEW_POINT("Dew Point", "org.bluetooth.characteristic.dew_point", 0x2A7B), DIGITAL("Digital", "org.bluetooth.characteristic.digital", 0x2A56), DST_OFFSET("DST Offset", "org.bluetooth.characteristic.dst_offset", 0x2A0D), ELEVATION("Elevation", "org.bluetooth.characteristic.elevation", 0x2A6C), EMAIL_ADDRESS("Email Address", "org.bluetooth.characteristic.email_address", 0x2A87), EXACT_TIME_256("Exact Time 256", "org.bluetooth.characteristic.exact_time_256", 0x2A0C), FAT_BURN_HEART_RATE_LOWER_LIMIT("Fat Burn Heart Rate Lower Limit", "org.bluetooth.characteristic.fat_burn_heart_rate_lower_limit", 0x2A88), FAT_BURN_HEART_RATE_UPPER_LIMIT("Fat Burn Heart Rate Upper Limit", "org.bluetooth.characteristic.fat_burn_heart_rate_upper_limit", 0x2A89), FIRMWARE_REVISION_STRING("Firmware Revision String", "org.bluetooth.characteristic.firmware_revision_string", 0x2A26), FIRST_NAME("First Name", "org.bluetooth.characteristic.first_name", 0x2A8A), FITNESS_MACHINE_CONTROL_POINT("Fitness Machine Control Point", "org.bluetooth.characteristic.fitness_machine_control_point", 0x2AD9), FITNESS_MACHINE_FEATURE("Fitness Machine Feature", "org.bluetooth.characteristic.fitness_machine_feature", 0x2ACC), FITNESS_MACHINE_STATUS("Fitness Machine Status", "org.bluetooth.characteristic.fitness_machine_status", 0x2ADA), FIVE_ZONE_HEART_RATE_LIMITS("Five Zone Heart Rate Limits", "org.bluetooth.characteristic.five_zone_heart_rate_limits", 0x2A8B), FLOOR_NUMBER("Floor Number", "org.bluetooth.characteristic.floor_number", 0x2AB2), GENDER("Gender", "org.bluetooth.characteristic.gender", 0x2A8C), GLUCOSE_FEATURE("Glucose Feature", "org.bluetooth.characteristic.glucose_feature", 0x2A51), GLUCOSE_MEASUREMENT("Glucose Measurement", "org.bluetooth.characteristic.glucose_measurement", 0x2A18), GLUCOSE_MEASUREMENT_CONTEXT("Glucose Measurement Context", "org.bluetooth.characteristic.glucose_measurement_context", 0x2A34), GUST_FACTOR("Gust Factor", "org.bluetooth.characteristic.gust_factor", 0x2A74), HARDWARE_REVISION_STRING("Hardware Revision String", "org.bluetooth.characteristic.hardware_revision_string", 0x2A27), HEART_RATE_CONTROL_POINT("Heart Rate Control Point", "org.bluetooth.characteristic.heart_rate_control_point", 0x2A39), HEART_RATE_MAX("Heart Rate Max", "org.bluetooth.characteristic.heart_rate_max", 0x2A8D), HEART_RATE_MEASUREMENT("Heart Rate Measurement", "org.bluetooth.characteristic.heart_rate_measurement", 0x2A37), HEAT_INDEX("Heat Index", "org.bluetooth.characteristic.heat_index", 0x2A7A), HEIGHT("Height", "org.bluetooth.characteristic.height", 0x2A8E), HID_CONTROL_POINT("HID Control Point", "org.bluetooth.characteristic.hid_control_point", 0x2A4C), HID_INFORMATION("HID Information", "org.bluetooth.characteristic.hid_information", 0x2A4A), HIP_CIRCUMFERENCE("Hip Circumference", "org.bluetooth.characteristic.hip_circumference", 0x2A8F), HTTP_CONTROL_POINT("HTTP Control Point", "org.bluetooth.characteristic.http_control_point", 0x2ABA), HTTP_ENTITY_BODY("HTTP Entity Body", "org.bluetooth.characteristic.http_entity_body", 0x2AB9), HTTP_HEADERS("HTTP Headers", "org.bluetooth.characteristic.http_headers", 0x2AB7), HTTP_STATUS_CODE("HTTP Status Code", "org.bluetooth.characteristic.http_status_code", 0x2AB8), HTTPS_SECURITY("HTTPS Security", "org.bluetooth.characteristic.https_security", 0x2ABB), HUMIDITY("Humidity", "org.bluetooth.characteristic.humidity", 0x2A6F), IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST("IEEE 11073-20601 Regulatory Certification Data List", "org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list", 0x2A2A), INDOOR_BIKE_DATA("Indoor Bike Data", "org.bluetooth.characteristic.indoor_bike_data", 0x2AD2), INDOOR_POSITIONING_CONFIGURATION("Indoor Positioning Configuration", "org.bluetooth.characteristic.indoor_positioning_configuration", 0x2AAD), INTERMEDIATE_CUFF_PRESSURE("Intermediate Cuff Pressure", "org.bluetooth.characteristic.intermediate_cuff_pressure", 0x2A36), INTERMEDIATE_TEMPERATURE("Intermediate Temperature", "org.bluetooth.characteristic.intermediate_temperature", 0x2A1E), IRRADIANCE("Irradiance", "org.bluetooth.characteristic.irradiance", 0x2A77), LANGUAGE("Language", "org.bluetooth.characteristic.language", 0x2AA2), LAST_NAME("Last Name", "org.bluetooth.characteristic.last_name", 0x2A90), LATITUDE("Latitude", "org.bluetooth.characteristic.latitude", 0x2AAE), LN_CONTROL_POINT("LN Control Point", "org.bluetooth.characteristic.ln_control_point", 0x2A6B), LN_FEATURE("LN Feature", "org.bluetooth.characteristic.ln_feature", 0x2A6A), LOCAL_EAST_COORDINATE("Local East Coordinate", "org.bluetooth.characteristic.local_east_coordinate", 0x2AB1), LOCAL_NORTH_COORDINATE("Local North Coordinate", "org.bluetooth.characteristic.local_north_coordinate", 0x2AB0), LOCAL_TIME_INFORMATION("Local Time Information", "org.bluetooth.characteristic.local_time_information", 0x2A0F), LOCATION_AND_SPEED("Location and Speed", "org.bluetooth.characteristic.location_and_speed", 0x2A67), LOCATION_NAME("Location Name", "org.bluetooth.characteristic.location_name", 0x2AB5), LONGITUDE("Longitude", "org.bluetooth.characteristic.longitude", 0x2AAF), MAGNETIC_DECLINATION("Magnetic Declination", "org.bluetooth.characteristic.magnetic_declination", 0x2A2C), MAGNETIC_FLUX_DENSITY_2D("Magnetic Flux Density - 2D", "org.bluetooth.characteristic.magnetic_flux_density_2D", 0x2AA0), MAGNETIC_FLUX_DENSITY_3D("Magnetic Flux Density - 3D", "org.bluetooth.characteristic.magnetic_flux_density_3D", 0x2AA1), MANUFACTURER_NAME_STRING("Manufacturer Name String", "org.bluetooth.characteristic.manufacturer_name_string", 0x2A29), MAXIMUM_RECOMMENDED_HEART_RATE("Maximum Recommended Heart Rate", "org.bluetooth.characteristic.maximum_recommended_heart_rate", 0x2A91), MEASUREMENT_INTERVAL("Measurement Interval", "org.bluetooth.characteristic.measurement_interval", 0x2A21), MODEL_NUMBER_STRING("Model Number String", "org.bluetooth.characteristic.model_number_string", 0x2A24), NAVIGATION("Navigation", "org.bluetooth.characteristic.navigation", 0x2A68), NEW_ALERT("New Alert", "org.bluetooth.characteristic.new_alert", 0x2A46), OBJECT_ACTION_CONTROL_POINT("Object Action Control Point", "org.bluetooth.characteristic.object_action_control_point", 0x2AC5), OBJECT_CHANGED("Object Changed", "org.bluetooth.characteristic.object_changed", 0x2AC8), OBJECT_FIRST_CREATED("Object First-Created", "org.bluetooth.characteristic.object_first_created", 0x2AC1), OBJECT_ID("Object ID", "org.bluetooth.characteristic.object_id", 0x2AC3), OBJECT_LAST_MODIFIED("Object Last-Modified", "org.bluetooth.characteristic.object_last_modified", 0x2AC2), OBJECT_LIST_CONTROL_POINT("Object List Control Point", "org.bluetooth.characteristic.object_list_control_point", 0x2AC6), OBJECT_LIST_FILTER("Object List Filter", "org.bluetooth.characteristic.object_list_filter", 0x2AC7), OBJECT_NAME("Object Name", "org.bluetooth.characteristic.object_name", 0x2ABE), OBJECT_PROPERTIES("Object Properties", "org.bluetooth.characteristic.object_properties", 0x2AC4), OBJECT_SIZE("Object Size", "org.bluetooth.characteristic.object_size", 0x2AC0), OBJECT_TYPE("Object Type", "org.bluetooth.characteristic.object_type", 0x2ABF), OTS_FEATURE("OTS Feature", "org.bluetooth.characteristic.ots_feature", 0x2ABD), PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS("Peripheral Preferred Connection Parameters", "org.bluetooth.characteristic.gap.peripheral_preferred_connection_parameters", 0x2A04), PERIPHERAL_PRIVACY_FLAG("Peripheral Privacy Flag", "org.bluetooth.characteristic.gap.peripheral_privacy_flag", 0x2A02), PLX_CONTINUOUS_MEASUREMENT("PLX Continuous Measurement", "org.bluetooth.characteristic.plx_continuous_measurement", 0x2A5F), PLX_FEATURES("PLX Features", "org.bluetooth.characteristic.plx_features", 0x2A60), PLX_SPOT_CHECK_MEASUREMENT("PLX Spot-Check Measurement", "org.bluetooth.characteristic.plx_spot_check_measurement", 0x2A5E), PNP_ID("PnP ID", "org.bluetooth.characteristic.pnp_id", 0x2A50), POLLEN_CONCENTRATION("Pollen Concentration", "org.bluetooth.characteristic.pollen_concentration", 0x2A75), POSITION_QUALITY("Position Quality", "org.bluetooth.characteristic.position_quality", 0x2A69), PRESSURE("Pressure", "org.bluetooth.characteristic.pressure", 0x2A6D), PROTOCOL_MODE("Protocol Mode", "org.bluetooth.characteristic.protocol_mode", 0x2A4E), RAINFALL("Rainfall", "org.bluetooth.characteristic.rainfall", 0x2A78), RECONNECTION_ADDRESS("Reconnection Address", "org.bluetooth.characteristic.gap.reconnection_address", 0x2A03), RECORD_ACCESS_CONTROL_POINT("Record Access Control Point", "org.bluetooth.characteristic.record_access_control_point", 0x2A52), REFERENCE_TIME_INFORMATION("Reference Time Information", "org.bluetooth.characteristic.reference_time_information", 0x2A14), REPORT("Report", "org.bluetooth.characteristic.report", 0x2A4D), REPORT_MAP("Report Map", "org.bluetooth.characteristic.report_map", 0x2A4B), RESOLVABLE_PRIVATE_ADDRESS_ONLY("Resolvable Private Address Only", "org.bluetooth.characteristic.resolvable_private_address_only", 0x2AC9), RESTING_HEART_RATE("Resting Heart Rate", "org.bluetooth.characteristic.resting_heart_rate", 0x2A92), RINGER_CONTROL_POINT("Ringer Control Point", "org.bluetooth.characteristic.ringer_control_point", 0x2A40), RINGER_SETTING("Ringer Setting", "org.bluetooth.characteristic.ringer_setting", 0x2A41), ROWER_DATA("Rower Data", "org.bluetooth.characteristic.rower_data", 0x2AD1), RSC_FEATURE("RSC Feature", "org.bluetooth.characteristic.rsc_feature", 0x2A54), RSC_MEASUREMENT("RSC Measurement", "org.bluetooth.characteristic.rsc_measurement", 0x2A53), SC_CONTROL_POINT("SC Control Point", "org.bluetooth.characteristic.sc_control_point", 0x2A55), SCAN_INTERVAL_WINDOW("Scan Interval Window", "org.bluetooth.characteristic.scan_interval_window", 0x2A4F), SCAN_REFRESH("Scan Refresh", "org.bluetooth.characteristic.scan_refresh", 0x2A31), SENSOR_LOCATION("Sensor Location", "org.blueooth.characteristic.sensor_location", 0x2A5D), SERIAL_NUMBER_STRING("Serial Number String", "org.bluetooth.characteristic.serial_number_string", 0x2A25), SERVICE_CHANGED("Service Changed", "org.bluetooth.characteristic.gatt.service_changed", 0x2A05), SOFTWARE_REVISION_STRING("Software Revision String", "org.bluetooth.characteristic.software_revision_string", 0x2A28), SPORT_TYPE_FOR_AEROBIC_AND_ANAEROBIC_THRESHOLDS("Sport Type for Aerobic and Anaerobic Thresholds", "org.bluetooth.characteristic.sport_type_for_aerobic_and_anaerobic_thresholds", 0x2A93), STAIR_CLIMBER_DATA("Stair Climber Data", "org.bluetooth.characteristic.stair_climber_data", 0x2AD0), STEP_CLIMBER_DATA("Step Climber Data", "org.bluetooth.characteristic.step_climber_data", 0x2ACF), SUPPORTED_HEART_RATE_RANGE("Supported Heart Rate Range", "org.bluetooth.characteristic.supported_heart_rate_range", 0x2AD7), SUPPORTED_INCLINATION_RANGE("Supported Inclination Range", "org.bluetooth.characteristic.supported_inclination_range", 0x2AD5), SUPPORTED_NEW_ALERT_CATEGORY("Supported New Alert Category", "org.bluetooth.characteristic.supported_new_alert_category", 0x2A47), SUPPORTED_POWER_RANGE("Supported Power Range", "org.bluetooth.characteristic.supported_power_range", 0x2AD8), SUPPORTED_RESISTANCE_LEVEL_RANGE("Supported Resistance Level Range", "org.bluetooth.characteristic.supported_resistance_level_range", 0x2AD6), SUPPORTED_SPEED_RANGE("Supported Speed Range", "org.bluetooth.characteristic.supported_speed_range", 0x2AD4), SUPPORTED_UNREAD_ALERT_CATEGORY("Supported Unread Alert Category", "org.bluetooth.characteristic.supported_unread_alert_category", 0x2A48), SYSTEM_ID("System ID", "org.bluetooth.characteristic.system_id", 0x2A23), TDS_CONTROL_POINT("TDS Control Point", "org.bluetooth.characteristic.tds_control_point", 0x2ABC), TEMPERATURE("Temperature", "org.bluetooth.characteristic.temperature", 0x2A6E), TEMPERATURE_MEASUREMENT("Temperature Measurement", "org.bluetooth.characteristic.temperature_measurement", 0x2A1C), TEMPERATURE_TYPE("Temperature Type", "org.bluetooth.characteristic.temperature_type", 0x2A1D), THREE_ZONE_HEART_RATE_LIMITS("Three Zone Heart Rate Limits", "org.bluetooth.characteristic.three_zone_heart_rate_limits", 0x2A94), TIME_ACCURACY("Time Accuracy", "org.bluetooth.characteristic.time_accuracy", 0x2A12), TIME_SOURCE("Time Source", "org.bluetooth.characteristic.time_source", 0x2A13), TIME_UPDATE_CONTROL_POINT("Time Update Control Point", "org.bluetooth.characteristic.time_update_control_point", 0x2A16), TIME_UPDATE_STATE("Time Update State", "org.bluetooth.characteristic.time_update_state", 0x2A17), TIME_WITH_DST("Time with DST", "org.bluetooth.characteristic.time_with_dst", 0x2A11), TIME_ZONE("Time Zone", "org.bluetooth.characteristic.time_zone", 0x2A0E), TRAINING_STATUS("Training Status", "org.bluetooth.characteristic.training_status", 0x2AD3), TREADMILL_DATA("Treadmill Data", "org.bluetooth.characteristic.treadmill_data", 0x2ACD), TRUE_WIND_DIRECTION("True Wind Direction", "org.bluetooth.characteristic.true_wind_direction", 0x2A71), TRUE_WIND_SPEED("True Wind Speed", "org.bluetooth.characteristic.true_wind_speed", 0x2A70), TWO_ZONE_HEART_RATE_LIMIT("Two Zone Heart Rate Limit", "org.bluetooth.characteristic.two_zone_heart_rate_limit", 0x2A95), TX_POWER_LEVEL("Tx Power Level", "org.bluetooth.characteristic.tx_power_level", 0x2A07), UNCERTAINTY("Uncertainty", "org.bluetooth.characteristic.uncertainty", 0x2AB4), UNREAD_ALERT_STATUS("Unread Alert Status", "org.bluetooth.characteristic.unread_alert_status", 0x2A45), URI("URI", "org.bluetooth.characteristic.uri", 0x2AB6), USER_CONTROL_POINT("User Control Point", "org.bluetooth.characteristic.user_control_point", 0x2A9F), USER_INDEX("User Index", "org.bluetooth.characteristic.user_index", 0x2A9A), UV_INDEX("UV Index", "org.bluetooth.characteristic.uv_index", 0x2A76), VO2_MAX("VO2 Max", "org.bluetooth.characteristic.vo2_max", 0x2A96), WAIST_CIRCUMFERENCE("Waist Circumference", "org.bluetooth.characteristic.waist_circumference", 0x2A97), WEIGHT("Weight", "org.bluetooth.characteristic.weight", 0x2A98), WEIGHT_MEASUREMENT("Weight Measurement", "org.bluetooth.characteristic.weight_measurement", 0x2A9D), WEIGHT_SCALE_FEATURE("Weight Scale Feature", "org.bluetooth.characteristic.weight_scale_feature", 0x2A9E), WIND_CHILL("Wind Chill", "org.bluetooth.characteristic.wind_chill", 0x2A79); private final String specificationName; private final String specificationType; private final long assignedNumber; GattCharacteristics(String specificationName, String specificationType, long assignedNumber) { this.specificationName = specificationName; this.specificationType = specificationType; this.assignedNumber = assignedNumber; } public String getSpecificationName() { return specificationName; } public String getSpecificationType() { return specificationType; } public long getAssignedNumber() { return assignedNumber; } public static GattCharacteristics ofAssignedNumber(long number) { for (GattCharacteristics characteristic : values()) { if (characteristic.getAssignedNumber() == number) { return characteristic; } } return GattCharacteristics.CUSTOM_CHARACTERISTIC; } public static GattCharacteristics ofAssignedName(String specificationName) { for (GattCharacteristics characteristic : values()) { if (characteristic.getSpecificationName().equals(specificationName)) { return characteristic; } } return GattCharacteristics.CUSTOM_CHARACTERISTIC; } } /** * https://www.bluetooth.com/specifications/gatt/descriptors */ public enum GattDescriptors { CHARACTERISTIC_AGGREGATE_FORMAT("Characteristic Aggregate Format", "org.bluetooth.descriptor.gatt.characteristic_aggregate_format", 0x2905), CHARACTERISTIC_EXTENDED_PROPERTIES("Characteristic Extended Properties", "org.bluetooth.descriptor.gatt.characteristic_extended_properties", 0x2900), CHARACTERISTIC_PRESENTATION_FORMAT("Characteristic Presentation Format", "org.bluetooth.descriptor.gatt.characteristic_presentation_format", 0x2904), CHARACTERISTIC_USER_DESCRIPTION("Characteristic User Description", "org.bluetooth.descriptor.gatt.characteristic_user_description", 0x2901), CLIENT_CHARACTERISTIC_CONFIGURATION("Client Characteristic Configuration", "org.bluetooth.descriptor.gatt.client_characteristic_configuration", 0x2902), ENVIRONMENTAL_SENSING_CONFIGURATION("Environmental Sensing Configuration", "org.bluetooth.descriptor.es_configuration", 0x290B), ENVIRONMENTAL_SENSING_MEASUREMENT("Environmental Sensing Measurement", "org.bluetooth.descriptor.es_measurement", 0x290C), ENVIRONMENTAL_SENSING_TRIGGER_SETTING("Environmental Sensing Trigger Setting", "org.bluetooth.descriptor.es_trigger_setting", 0x290D), EXTERNAL_REPORT_REFERENCE("External Report Reference", "org.bluetooth.descriptor.external_report_reference", 0x2907), NUMBER_OF_DIGITALS("Number of Digitals", "org.bluetooth.descriptor.number_of_digitals", 0x2909), REPORT_REFERENCE("Report Reference", "org.bluetooth.descriptor.report_reference", 0x2908), SERVER_CHARACTERISTIC_CONFIGURATION("Server Characteristic Configuration", "org.bluetooth.descriptor.gatt.server_characteristic_configuration", 0x2903), TIME_TRIGGER_SETTING("Time Trigger Setting", "org.bluetooth.descriptor.time_trigger_setting", 0x290E), VALID_RANGE("Valid Range", "org.bluetooth.descriptor.valid_range", 0x2906), VALUE_TRIGGER_SETTING("Value Trigger Setting", "org.bluetooth.descriptor.value_trigger_setting", 0x290A); private final String specificationName; private final String specificationType; private final long assignedNumber; GattDescriptors(String specificationName, String specificationType, long assignedNumber) { this.specificationName = specificationName; this.specificationType = specificationType; this.assignedNumber = assignedNumber; } public String getSpecificationName() { return specificationName; } public String getSpecificationType() { return specificationType; } public long getAssignedNumber() { return assignedNumber; } public static GattDescriptors ofAssignedNumber(long number) { for (GattDescriptors descriptor : values()) { if (descriptor.getAssignedNumber() == number) { return descriptor; } } return GattDescriptors.CHARACTERISTIC_EXTENDED_PROPERTIES; } public static GattDescriptors ofAssignedName(String specificationName) { for (GattDescriptors descriptor : values()) { if (descriptor.getSpecificationName().equals(specificationName)) { return descriptor; } } return GattDescriptors.CHARACTERISTIC_EXTENDED_PROPERTIES; } } public static String getServiceToken(UUID serviceUuid) { final long assignedNumber = getAssignedNumber(serviceUuid); if (GattServices.ofAssignedNumber(assignedNumber).equals(GattServices.CUSTOM_SERVICE)) { return serviceUuid.toString(); } return formatToken(assignedNumber); } public static String getCharacteristicsToken(UUID characteristicsUuid) { final long assignedNumber = getAssignedNumber(characteristicsUuid); if (GattCharacteristics.ofAssignedNumber(assignedNumber).equals(GattCharacteristics.CUSTOM_CHARACTERISTIC)) { return characteristicsUuid.toString(); } return formatToken(assignedNumber); } public static UUID getUUIDFromServiceName(String serviceName) { GattServices gattService = GattServices.ofAssignedName(serviceName); if (gattService.equals(GattServices.CUSTOM_SERVICE)) { return getUUIDfromToken(serviceName); } final String serviceToken = formatToken(gattService.getAssignedNumber()); return getUUIDfromToken(serviceToken); } public static UUID getUUIDFromCharacteristicsName(String charName) { GattCharacteristics gattChars = GattCharacteristics.ofAssignedName(charName); if (gattChars.equals(GattCharacteristics.CUSTOM_CHARACTERISTIC)) { return getUUIDfromToken(charName); } final String charToken = formatToken(gattChars.getAssignedNumber()); return getUUIDfromToken(charToken); } public static UUID getUUIDFromDescriptorName(String descName) { GattDescriptors gattDescriptor = GattDescriptors.ofAssignedName(descName); final String descToken = formatToken(gattDescriptor.getAssignedNumber()); return getUUIDfromToken(descToken); } public static Optional<UUID> getUUIDfromTokenOrElse(String token, Function<String, UUID> functionName) { UUID uuid = getUUIDfromToken(token); if (uuid == null && functionName != null) { uuid = functionName.apply(token); } return uuid == null ? Optional.empty() : Optional.of(uuid); } public static String formatToken(long assignedNumber) { return String.format("%04x", assignedNumber); } public static long getAssignedNumber(UUID uuid) { return (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >> 32; } private static UUID getUUIDfromToken(String value) { if (value == null || value.isEmpty()) { LOG.log(Level.INFO, "Error retrieving UUID for " + value); return null; } try { if (value.length() == 4) { return UUID.fromString(BleSpecs.UUID_BASE_PREFIX + value + BleSpecs.UUID_BASE_SUFIX); } return UUID.fromString(value); } catch (IllegalArgumentException e) { LOG.log(Level.WARNING, "Error retrieving UUID for " + value + ": " + e.getMessage()); } return null; } }