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.nio.charset.StandardCharsets;
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
23 * This is a utility class for parsing or formatting bluetooth characteristic data.
25 * @author Connor Petty - Initial Contribution
29 public class BluetoothUtils {
31 public static final Logger logger = LoggerFactory.getLogger(BluetoothUtils.class);
33 public static final int FORMAT_UINT8 = 0x11;
34 public static final int FORMAT_UINT16 = 0x12;
35 public static final int FORMAT_UINT32 = 0x14;
36 public static final int FORMAT_SINT8 = 0x21;
37 public static final int FORMAT_SINT16 = 0x22;
38 public static final int FORMAT_SINT32 = 0x24;
39 public static final int FORMAT_SFLOAT = 0x32;
40 public static final int FORMAT_FLOAT = 0x34;
43 * Converts a byte array to an int array
48 public static int[] toIntArray(byte[] value) {
49 int[] ret = new int[value.length];
50 for (int i = 0; i < value.length; i++) {
56 public static byte[] toByteArray(int[] value) {
57 byte[] ret = new byte[value.length];
58 for (int i = 0; i < value.length; i++) {
59 ret[i] = (byte) (value[i] & 0xFF);
65 * Return the stored value of this characteristic.
68 public static @Nullable Integer getIntegerValue(byte[] value, int formatType, int offset) {
69 if ((offset + getTypeLen(formatType)) > value.length) {
75 return unsignedByteToInt(value[offset]);
78 return unsignedBytesToInt(value[offset], value[offset + 1]);
81 return unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
84 return unsignedToSigned(unsignedByteToInt(value[offset]), 8);
87 return unsignedToSigned(unsignedBytesToInt(value[offset], value[offset + 1]), 16);
90 return unsignedToSigned(
91 unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]), 32);
93 logger.error("Unknown format type {} - no int value can be provided for it.", formatType);
100 * Return the stored value of this characteristic. This doesn't read the remote data.
103 public static @Nullable Float getFloatValue(byte[] value, int formatType, int offset) {
104 if ((offset + getTypeLen(formatType)) > value.length) {
108 switch (formatType) {
110 return bytesToFloat(value[offset], value[offset + 1]);
112 return bytesToFloat(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
114 logger.error("Unknown format type {} - no float value can be provided for it.", formatType);
121 * Return the stored value of this characteristic. This doesn't read the remote data.
124 public static @Nullable String getStringValue(byte[] value, int offset) {
125 if (offset > value.length) {
128 byte[] strBytes = new byte[value.length - offset];
129 for (int i = 0; i < (value.length - offset); ++i) {
130 strBytes[i] = value[offset + i];
132 return new String(strBytes, StandardCharsets.UTF_8);
136 * Set the local value of this characteristic.
138 * @param value the value to set
139 * @param formatType the format of the value (as one of the FORMAT_* constants in this class)
140 * @param offset the offset to use when interpreting the value
141 * @return true, if it has been set successfully
143 public static boolean setValue(byte[] dest, int value, int formatType, int offset) {
144 int len = offset + getTypeLen(formatType);
145 if (len > dest.length) {
149 switch (formatType) {
151 val = intToSignedBits(value, 8);
152 // Fall-through intended
154 dest[offset] = (byte) (val & 0xFF);
158 val = intToSignedBits(value, 16);
159 // Fall-through intended
161 dest[offset] = (byte) (val & 0xFF);
162 dest[offset + 1] = (byte) ((val >> 8) & 0xFF);
166 val = intToSignedBits(value, 32);
167 // Fall-through intended
169 dest[offset] = (byte) (val & 0xFF);
170 dest[offset + 1] = (byte) ((val >> 8) & 0xFF);
171 dest[offset + 2] = (byte) ((val >> 16) & 0xFF);
172 dest[offset + 2] = (byte) ((val >> 24) & 0xFF);
182 * Set the local value of this characteristic.
184 * @param mantissa the mantissa of the value
185 * @param exponent the exponent of the value
186 * @param formatType the format of the value (as one of the FORMAT_* constants in this class)
187 * @param offset the offset to use when interpreting the value
188 * @return true, if it has been set successfully
191 public static boolean setValue(byte[] dest, int mantissa, int exponent, int formatType, int offset) {
192 int len = offset + getTypeLen(formatType);
193 if (len > dest.length) {
197 switch (formatType) {
199 int m = intToSignedBits(mantissa, 12);
200 int exp = intToSignedBits(exponent, 4);
201 dest[offset] = (byte) (m & 0xFF);
202 dest[offset + 1] = (byte) ((m >> 8) & 0x0F);
203 dest[offset + 1] += (byte) ((exp & 0x0F) << 4);
207 m = intToSignedBits(mantissa, 24);
208 exp = intToSignedBits(exponent, 8);
209 dest[offset] = (byte) (m & 0xFF);
210 dest[offset + 1] = (byte) ((m >> 8) & 0xFF);
211 dest[offset + 2] = (byte) ((m >> 16) & 0xFF);
212 dest[offset + 2] += (byte) (exp & 0xFF);
223 * Returns the size of the requested value type.
225 private static int getTypeLen(int formatType) {
226 return formatType & 0xF;
230 * Convert a signed byte to an unsigned int.
232 private static int unsignedByteToInt(int value) {
237 * Convert signed bytes to a 16-bit unsigned int.
239 private static int unsignedBytesToInt(int value1, int value2) {
240 return value1 + (value2 << 8);
244 * Convert signed bytes to a 32-bit unsigned int.
246 private static int unsignedBytesToInt(int value1, int value2, int value3, int value4) {
247 return value1 + (value2 << 8) + (value3 << 16) + (value4 << 24);
251 * Convert signed bytes to a 16-bit short float value.
253 private static float bytesToFloat(int value1, int value2) {
254 int mantissa = unsignedToSigned(unsignedByteToInt(value1) + ((unsignedByteToInt(value2) & 0x0F) << 8), 12);
255 int exponent = unsignedToSigned(unsignedByteToInt(value2) >> 4, 4);
256 return (float) (mantissa * Math.pow(10, exponent));
260 * Convert signed bytes to a 32-bit short float value.
262 private static float bytesToFloat(int value1, int value2, int value3, int value4) {
263 int mantissa = unsignedToSigned(
264 unsignedByteToInt(value1) + (unsignedByteToInt(value2) << 8) + (unsignedByteToInt(value3) << 16), 24);
265 return (float) (mantissa * Math.pow(10, value4));
269 * Convert an unsigned integer to a two's-complement signed value.
271 private static int unsignedToSigned(int unsigned, int size) {
272 if ((unsigned & (1 << size - 1)) != 0) {
273 return -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1)));
280 * Convert an integer into the signed bits of the specified length.
282 private static int intToSignedBits(int i, int size) {
284 return (1 << size - 1) + (i & ((1 << size - 1) - 1));