2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.bluetooth;
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.List;
19 import java.util.UUID;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
25 * The {@link BluetoothCharacteristic} class defines the Bluetooth characteristic.
27 * Characteristics are defined attribute types that contain a single logical value.
29 * https://www.bluetooth.com/specifications/gatt/characteristics
31 * @author Chris Jackson - Initial contribution
32 * @author Kai Kreuzer - Cleaned up code
33 * @author Peter Rosenberg - Improve properties support
36 public class BluetoothCharacteristic {
37 public static final int PROPERTY_BROADCAST = 0x01;
38 public static final int PROPERTY_READ = 0x02;
39 public static final int PROPERTY_WRITE_NO_RESPONSE = 0x04;
40 public static final int PROPERTY_WRITE = 0x08;
41 public static final int PROPERTY_NOTIFY = 0x10;
42 public static final int PROPERTY_INDICATE = 0x20;
43 public static final int PROPERTY_SIGNED_WRITE = 0x40;
44 public static final int PROPERTY_EXTENDED_PROPS = 0x80;
46 public static final int PERMISSION_READ = 0x01;
47 public static final int PERMISSION_READ_ENCRYPTED = 0x02;
48 public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04;
49 public static final int PERMISSION_WRITE = 0x10;
50 public static final int PERMISSION_WRITE_ENCRYPTED = 0x20;
51 public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40;
52 public static final int PERMISSION_WRITE_SIGNED = 0x80;
53 public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100;
55 public static final int WRITE_TYPE_DEFAULT = 0x02;
56 public static final int WRITE_TYPE_NO_RESPONSE = 0x01;
57 public static final int WRITE_TYPE_SIGNED = 0x04;
60 * The {@link UUID} for this characteristic
65 * The handle for this characteristic
70 * A map of {@link BluetoothDescriptor}s applicable to this characteristic
72 protected Map<UUID, BluetoothDescriptor> gattDescriptors = new HashMap<>();
73 protected int instance;
74 protected int properties;
75 protected int permissions;
76 protected int writeType;
79 * The {@link BluetoothService} to which this characteristic belongs
81 protected @Nullable BluetoothService service;
84 * Create a new BluetoothCharacteristic.
86 * @param uuid the {@link UUID} of the new characteristic
89 public BluetoothCharacteristic(UUID uuid, int handle) {
95 * Adds a descriptor to this characteristic.
97 * @param descriptor {@link BluetoothDescriptor} to be added to this characteristic.
98 * @return true, if the descriptor was added to the characteristic
100 public boolean addDescriptor(BluetoothDescriptor descriptor) {
101 if (gattDescriptors.get(descriptor.getUuid()) != null) {
105 gattDescriptors.put(descriptor.getUuid(), descriptor);
110 * Returns the {@link UUID} of this characteristic
112 * @return UUID of this characteristic
114 public UUID getUuid() {
119 * Returns the instance ID for this characteristic.
121 * If a remote device offers multiple characteristics with the same UUID, the instance ID is used to distinguish
122 * between characteristics.
124 * @return Instance ID of this characteristic
126 public int getInstanceId() {
131 * Set the raw properties. The individual properties are represented as bits inside
134 * @param properties of this Characteristic
136 public void setProperties(int properties) {
137 this.properties = properties;
141 * Returns the properties of this characteristic.
143 * The properties contain a bit mask of property flags indicating the features of this characteristic.
146 public int getProperties() {
151 * Returns if the given characteristics property is enabled or not.
153 * @param property one of the Constants BluetoothCharacteristic.PROPERTY_XX
154 * @return true if this characteristic has the given property enabled, false if properties not set or
155 * the given property is not enabled.
157 public boolean hasPropertyEnabled(int property) {
158 return (properties & property) != 0;
162 * Returns if notifications can be enabled on this characteristic.
164 * @return true if notifications can be enabled, false if notifications are not supported, characteristic is missing
165 * on device or notifications are not supported.
167 public boolean canNotify() {
168 return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY);
172 * Returns if the value can be read on this characteristic.
174 * @return true if the value can be read, false otherwise.
176 public boolean canRead() {
177 return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ);
181 * Returns if the value can be written on this characteristic.
183 * @return true if the value can be written with of without a response, false otherwise.
185 public boolean canWrite() {
186 return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE)
187 || hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
191 * Returns the permissions for this characteristic.
193 public int getPermissions() {
198 * Gets the write type for this characteristic.
201 public int getWriteType() {
206 * Set the write type for this characteristic
210 public void setWriteType(int writeType) {
211 this.writeType = writeType;
215 * Get the service to which this characteristic belongs
217 * @return the {@link BluetoothService}
219 public @Nullable BluetoothService getService() {
224 * Returns the handle for this characteristic
226 * @return the handle for the characteristic
228 public int getHandle() {
233 * Get the service to which this characteristic belongs
235 * @return the {@link BluetoothService}
237 public void setService(BluetoothService service) {
238 this.service = service;
242 * Returns a list of descriptors for this characteristic.
245 public List<BluetoothDescriptor> getDescriptors() {
246 return new ArrayList<>(gattDescriptors.values());
250 * Returns a descriptor with a given UUID out of the list of
251 * descriptors for this characteristic.
253 * @return the {@link BluetoothDescriptor}
255 public @Nullable BluetoothDescriptor getDescriptor(UUID uuid) {
256 return gattDescriptors.get(uuid);
260 public int hashCode() {
261 BluetoothService btService = service;
262 final int prime = 31;
264 result = prime * result + instance;
265 result = prime * result + ((btService == null) ? 0 : btService.hashCode());
266 result = prime * result + uuid.hashCode();
271 public boolean equals(@Nullable Object obj) {
278 if (getClass() != obj.getClass()) {
281 BluetoothCharacteristic other = (BluetoothCharacteristic) obj;
282 if (instance != other.instance) {
285 BluetoothService btService = service;
286 if (btService == null) {
287 if (other.service != null) {
290 } else if (!btService.equals(other.service)) {
294 return uuid.equals(other.uuid);
297 public @Nullable GattCharacteristic getGattCharacteristic() {
298 return GattCharacteristic.getCharacteristic(uuid);
301 public enum GattCharacteristic {
303 ALERT_CATEGORY_ID(0x2A43),
304 ALERT_CATEGORY_ID_BIT_MASK(0x2A42),
306 ALERT_NOTIFICATION_CONTROL_POINT(0x2A44),
307 ALERT_STATUS(0x2A3F),
309 BATTERY_LEVEL(0x2A19),
310 BLOOD_PRESSURE_FEATURE(0x2A49),
311 BLOOD_PRESSURE_MEASUREMENT(0x2A35),
312 BODY_SENSOR_LOCATION(0x2A38),
313 BOOT_KEYOBARD_INPUT_REPORT(0x2A22),
314 BOOT_KEYOBARD_OUTPUT_REPORT(0x2A32),
315 BOOT_MOUSE_INPUT_REPORT(0x2A33),
317 CSC_MEASUREMENT(0x2A5B),
318 CURRENT_TIME(0x2A2B),
319 CYCLING_POWER_CONTROL_POINT(0x2A66),
320 CYCLING_POWER_FEATURE(0x2A65),
321 CYCLING_POWER_MEASUREMENT(0x2A63),
322 CYCLING_POWER_VECTOR(0x2A64),
324 DAY_DATE_TIME(0x2A0A),
328 EXACT_TIME_256(0x2A0C),
329 FIRMWARE_REVISION_STRING(0x2A26),
330 GLUCOSE_FEATURE(0x2A51),
331 GLUCOSE_MEASUREMENT(0x2A18),
332 GLUCOSE_MEASUREMENT_CONTROL(0x2A34),
333 HARDWARE_REVISION_STRING(0x2A27),
334 HEART_RATE_CONTROL_POINT(0x2A39),
335 HEART_RATE_MEASUREMENT(0x2A37),
336 HID_CONTROL_POINT(0x2A4C),
337 HID_INFORMATION(0x2A4A),
338 IEEE11073_20601_REGULATORY_CERTIFICATION_DATA_LIST(0x2A2A),
339 INTERMEDIATE_CUFF_PRESSURE(0x2A36),
340 INTERMEDIATE_TEMPERATURE(0x2A1E),
341 LN_CONTROL_POINT(0x2A6B),
343 LOCAL_TIME_INFORMATION(0x2A0F),
344 LOCATION_AND_SPEED(0x2A67),
345 MANUFACTURER_NAME_STRING(0x2A29),
346 MEASUREMENT_INTERVAL(0x2A21),
347 MODEL_NUMBER_STRING(0x2A24),
350 PERIPERAL_PREFFERED_CONNECTION_PARAMETERS(0x2A04),
351 PERIPHERAL_PRIVACY_FLAG(0x2A02),
353 POSITION_QUALITY(0x2A69),
354 PROTOCOL_MODE(0x2A4E),
355 RECONNECTION_ADDRESS(0x2A03),
356 RECORD_ACCESS_CONTROL_POINT(0x2A52),
357 REFERENCE_TIME_INFORMATION(0x2A14),
360 RINGER_CONTROL_POINT(0x2A40),
361 RINGER_SETTING(0x2A41),
363 RSC_MEASUREMENT(0x2A53),
364 SC_CONTROL_POINT(0x2A55),
365 SCAN_INTERVAL_WINDOW(0x2A4F),
366 SCAN_REFRESH(0x2A31),
367 SENSOR_LOCATION(0x2A5D),
368 SERIAL_NUMBER_STRING(0x2A25),
369 SERVICE_CHANGED(0x2A05),
370 SOFTWARE_REVISION_STRING(0x2A28),
371 SUPPORTED_NEW_ALERT_CATEGORY(0x2A47),
372 SUPPORTED_UNREAD_ALERT_CATEGORY(0x2A48),
374 TEMPERATURE_MEASUREMENT(0x2A1C),
375 TEMPERATURE_TYPE(0x2A1D),
376 TIME_ACCURACY(0x2A12),
378 TIME_UPDATE_CONTROL_POINT(0x2A16),
379 TIME_UPDATE_STATE(0x2A17),
380 TIME_WITH_DST(0x2A11),
382 TX_POWER_LEVEL(0x2A07),
383 UNREAD_ALERT_STATUS(0x2A45),
384 AGGREGATE_INPUT(0x2A5A),
385 ANALOG_INPUT(0x2A58),
386 ANALOG_OUTPUT(0x2A59),
387 DIGITAL_INPUT(0x2A56),
388 DIGITAL_OUTPUT(0x2A57),
389 EXACT_TIME_100(0x2A0B),
390 NETWORK_AVAILABILITY(0x2A3E),
391 SCIENTIFIC_TEMPERATURE_IN_CELSIUS(0x2A3C),
392 SECONDARY_TIME_ZONE(0x2A10),
394 TEMPERATURE_IN_CELSIUS(0x2A1F),
395 TEMPERATURE_IN_FAHRENHEIT(0x2A20),
396 TIME_BROADCAST(0x2A15),
397 BATTERY_LEVEL_STATE(0x2A1B),
398 BATTERY_POWER_STATE(0x2A1A),
399 PULSE_OXIMETRY_CONTINUOUS_MEASUREMENT(0x2A5F),
400 PULSE_OXIMETRY_CONTROL_POINT(0x2A62),
401 PULSE_OXIMETRY_FEATURES(0x2A61),
402 PULSE_OXIMETRY_PULSATILE_EVENT(0x2A60),
403 PULSE_OXIMETRY_SPOT_CHECK_MEASUREMENT(0x2A5E),
404 RECORD_ACCESS_CONTROL_POINT_TESTVERSION(0x2A52),
406 SERVICE_REQUIRED(0x2A3B);
408 private static @Nullable Map<UUID, GattCharacteristic> uuidToServiceMapping;
412 private GattCharacteristic(long key) {
413 this.uuid = BluetoothBindingConstants.createBluetoothUUID(key);
416 public static @Nullable GattCharacteristic getCharacteristic(UUID uuid) {
417 Map<UUID, GattCharacteristic> localServiceMapping = uuidToServiceMapping;
418 if (localServiceMapping == null) {
419 localServiceMapping = new HashMap<>();
420 for (GattCharacteristic s : values()) {
421 localServiceMapping.put(s.uuid, s);
423 uuidToServiceMapping = localServiceMapping;
425 return localServiceMapping.get(uuid);
431 public UUID getUUID() {