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 System.arraycopy(value, 0, ret, 0, value.length);
54 public static byte[] toByteArray(int[] value) {
55 byte[] ret = new byte[value.length];
56 for (int i = 0; i < value.length; i++) {
57 ret[i] = (byte) (value[i] & 0xFF);
63 * Return the stored value of this characteristic.
66 public static @Nullable Integer getIntegerValue(byte[] value, int formatType, int offset) {
67 if ((offset + getTypeLen(formatType)) > value.length) {
73 return unsignedByteToInt(value[offset]);
76 return unsignedBytesToInt(value[offset], value[offset + 1]);
79 return unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
82 return unsignedToSigned(unsignedByteToInt(value[offset]), 8);
85 return unsignedToSigned(unsignedBytesToInt(value[offset], value[offset + 1]), 16);
88 return unsignedToSigned(
89 unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]), 32);
91 LOGGER.error("Unknown format type {} - no int value can be provided for it.", formatType);
98 * Return the stored value of this characteristic. This doesn't read the remote data.
101 public static @Nullable Float getFloatValue(byte[] value, int formatType, int offset) {
102 if ((offset + getTypeLen(formatType)) > value.length) {
106 switch (formatType) {
108 return bytesToFloat(value[offset], value[offset + 1]);
110 return bytesToFloat(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
112 LOGGER.error("Unknown format type {} - no float value can be provided for it.", formatType);
119 * Return the stored value of this characteristic. This doesn't read the remote data.
122 public static @Nullable String getStringValue(byte[] value, int offset) {
123 if (offset > value.length) {
126 byte[] strBytes = new byte[value.length - offset];
127 for (int i = 0; i < (value.length - offset); ++i) {
128 strBytes[i] = value[offset + i];
130 return new String(strBytes, StandardCharsets.UTF_8);
134 * Set the local value of this characteristic.
136 * @param value the value to set
137 * @param formatType the format of the value (as one of the FORMAT_* constants in this class)
138 * @param offset the offset to use when interpreting the value
139 * @return true, if it has been set successfully
141 public static boolean setValue(byte[] dest, int value, int formatType, int offset) {
142 int len = offset + getTypeLen(formatType);
143 if (len > dest.length) {
147 switch (formatType) {
149 val = intToSignedBits(value, 8);
150 // Fall-through intended
152 dest[offset] = (byte) (val & 0xFF);
156 val = intToSignedBits(value, 16);
157 // Fall-through intended
159 dest[offset] = (byte) (val & 0xFF);
160 dest[offset + 1] = (byte) ((val >> 8) & 0xFF);
164 val = intToSignedBits(value, 32);
165 // Fall-through intended
167 dest[offset] = (byte) (val & 0xFF);
168 dest[offset + 1] = (byte) ((val >> 8) & 0xFF);
169 dest[offset + 2] = (byte) ((val >> 16) & 0xFF);
170 dest[offset + 2] = (byte) ((val >> 24) & 0xFF);
180 * Set the local value of this characteristic.
182 * @param mantissa the mantissa of the value
183 * @param exponent the exponent of the value
184 * @param formatType the format of the value (as one of the FORMAT_* constants in this class)
185 * @param offset the offset to use when interpreting the value
186 * @return true, if it has been set successfully
189 public static boolean setValue(byte[] dest, int mantissa, int exponent, int formatType, int offset) {
190 int len = offset + getTypeLen(formatType);
191 if (len > dest.length) {
195 switch (formatType) {
197 int m = intToSignedBits(mantissa, 12);
198 int exp = intToSignedBits(exponent, 4);
199 dest[offset] = (byte) (m & 0xFF);
200 dest[offset + 1] = (byte) ((m >> 8) & 0x0F);
201 dest[offset + 1] += (byte) ((exp & 0x0F) << 4);
205 m = intToSignedBits(mantissa, 24);
206 exp = intToSignedBits(exponent, 8);
207 dest[offset] = (byte) (m & 0xFF);
208 dest[offset + 1] = (byte) ((m >> 8) & 0xFF);
209 dest[offset + 2] = (byte) ((m >> 16) & 0xFF);
210 dest[offset + 2] += (byte) (exp & 0xFF);
221 * Returns the size of the requested value type.
223 private static int getTypeLen(int formatType) {
224 return formatType & 0xF;
228 * Convert a signed byte to an unsigned int.
230 private static int unsignedByteToInt(int value) {
235 * Convert signed bytes to a 16-bit unsigned int.
237 private static int unsignedBytesToInt(int value1, int value2) {
238 return value1 + (value2 << 8);
242 * Convert signed bytes to a 32-bit unsigned int.
244 private static int unsignedBytesToInt(int value1, int value2, int value3, int value4) {
245 return value1 + (value2 << 8) + (value3 << 16) + (value4 << 24);
249 * Convert signed bytes to a 16-bit short float value.
251 private static float bytesToFloat(int value1, int value2) {
252 int mantissa = unsignedToSigned(unsignedByteToInt(value1) + ((unsignedByteToInt(value2) & 0x0F) << 8), 12);
253 int exponent = unsignedToSigned(unsignedByteToInt(value2) >> 4, 4);
254 return (float) (mantissa * Math.pow(10, exponent));
258 * Convert signed bytes to a 32-bit short float value.
260 private static float bytesToFloat(int value1, int value2, int value3, int value4) {
261 int mantissa = unsignedToSigned(
262 unsignedByteToInt(value1) + (unsignedByteToInt(value2) << 8) + (unsignedByteToInt(value3) << 16), 24);
263 return (float) (mantissa * Math.pow(10, value4));
267 * Convert an unsigned integer to a two's-complement signed value.
269 private static int unsignedToSigned(int unsigned, int size) {
270 if ((unsigned & (1 << size - 1)) != 0) {
271 return -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1)));
278 * Convert an integer into the signed bits of the specified length.
280 private static int intToSignedBits(int i, int size) {
282 return (1 << size - 1) + (i & ((1 << size - 1) - 1));