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.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
21 * This is a utility class for parsing or formatting bluetooth characteristic data.
23 * @author Connor Petty - Initial Contribution
26 public class BluetoothUtils {
28 public static final Logger logger = LoggerFactory.getLogger(BluetoothUtils.class);
30 public static final int FORMAT_UINT8 = 0x11;
31 public static final int FORMAT_UINT16 = 0x12;
32 public static final int FORMAT_UINT32 = 0x14;
33 public static final int FORMAT_SINT8 = 0x21;
34 public static final int FORMAT_SINT16 = 0x22;
35 public static final int FORMAT_SINT32 = 0x24;
36 public static final int FORMAT_SFLOAT = 0x32;
37 public static final int FORMAT_FLOAT = 0x34;
40 * Converts a byte array to an int array
45 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) {
60 byte[] ret = new byte[value.length];
61 for (int i = 0; i < value.length; i++) {
62 ret[i] = (byte) (value[i] & 0xFF);
68 * Return the stored value of this characteristic.
71 public static Integer getIntegerValue(byte[] value, int formatType, int offset) {
72 if ((offset + getTypeLen(formatType)) > value.length) {
78 return unsignedByteToInt(value[offset]);
81 return unsignedBytesToInt(value[offset], value[offset + 1]);
84 return unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
87 return unsignedToSigned(unsignedByteToInt(value[offset]), 8);
90 return unsignedToSigned(unsignedBytesToInt(value[offset], value[offset + 1]), 16);
93 return unsignedToSigned(
94 unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]), 32);
96 logger.error("Unknown format type {} - no int value can be provided for it.", formatType);
103 * Return the stored value of this characteristic. This doesn't read the remote data.
106 public static Float getFloatValue(byte[] value, int formatType, int offset) {
107 if ((offset + getTypeLen(formatType)) > value.length) {
111 switch (formatType) {
113 return bytesToFloat(value[offset], value[offset + 1]);
115 return bytesToFloat(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
117 logger.error("Unknown format type {} - no float value can be provided for it.", formatType);
124 * Return the stored value of this characteristic. This doesn't read the remote data.
127 public static String getStringValue(byte[] value, int offset) {
128 if (value == null || offset > value.length) {
131 byte[] strBytes = new byte[value.length - offset];
132 for (int i = 0; i < (value.length - offset); ++i) {
133 strBytes[i] = value[offset + i];
135 return new String(strBytes, StandardCharsets.UTF_8);
139 * Set the local value of this characteristic.
141 * @param value the value to set
142 * @param formatType the format of the value (as one of the FORMAT_* constants in this class)
143 * @param offset the offset to use when interpreting the value
144 * @return true, if it has been set successfully
146 public static boolean setValue(byte[] dest, int value, int formatType, int offset) {
147 int len = offset + getTypeLen(formatType);
148 if (dest == null || len > dest.length) {
152 switch (formatType) {
154 val = intToSignedBits(value, 8);
155 // Fall-through intended
157 dest[offset] = (byte) (val & 0xFF);
161 val = intToSignedBits(value, 16);
162 // Fall-through intended
164 dest[offset] = (byte) (val & 0xFF);
165 dest[offset + 1] = (byte) ((val >> 8) & 0xFF);
169 val = intToSignedBits(value, 32);
170 // Fall-through intended
172 dest[offset] = (byte) (val & 0xFF);
173 dest[offset + 1] = (byte) ((val >> 8) & 0xFF);
174 dest[offset + 2] = (byte) ((val >> 16) & 0xFF);
175 dest[offset + 2] = (byte) ((val >> 24) & 0xFF);
185 * Set the local value of this characteristic.
187 * @param mantissa the mantissa of the value
188 * @param exponent the exponent of the value
189 * @param formatType the format of the value (as one of the FORMAT_* constants in this class)
190 * @param offset the offset to use when interpreting the value
191 * @return true, if it has been set successfully
194 public static boolean setValue(byte[] dest, int mantissa, int exponent, int formatType, int offset) {
195 int len = offset + getTypeLen(formatType);
196 if (dest == null || len > dest.length) {
200 switch (formatType) {
202 int m = intToSignedBits(mantissa, 12);
203 int exp = intToSignedBits(exponent, 4);
204 dest[offset] = (byte) (m & 0xFF);
205 dest[offset + 1] = (byte) ((m >> 8) & 0x0F);
206 dest[offset + 1] += (byte) ((exp & 0x0F) << 4);
210 m = intToSignedBits(mantissa, 24);
211 exp = intToSignedBits(exponent, 8);
212 dest[offset] = (byte) (m & 0xFF);
213 dest[offset + 1] = (byte) ((m >> 8) & 0xFF);
214 dest[offset + 2] = (byte) ((m >> 16) & 0xFF);
215 dest[offset + 2] += (byte) (exp & 0xFF);
226 * Returns the size of the requested value type.
228 private static int getTypeLen(int formatType) {
229 return formatType & 0xF;
233 * Convert a signed byte to an unsigned int.
235 private static int unsignedByteToInt(int value) {
240 * Convert signed bytes to a 16-bit unsigned int.
242 private static int unsignedBytesToInt(int value1, int value2) {
243 return value1 + (value2 << 8);
247 * Convert signed bytes to a 32-bit unsigned int.
249 private static int unsignedBytesToInt(int value1, int value2, int value3, int value4) {
250 return value1 + (value2 << 8) + (value3 << 16) + (value4 << 24);
254 * Convert signed bytes to a 16-bit short float value.
256 private static float bytesToFloat(int value1, int value2) {
257 int mantissa = unsignedToSigned(unsignedByteToInt(value1) + ((unsignedByteToInt(value2) & 0x0F) << 8), 12);
258 int exponent = unsignedToSigned(unsignedByteToInt(value2) >> 4, 4);
259 return (float) (mantissa * Math.pow(10, exponent));
263 * Convert signed bytes to a 32-bit short float value.
265 private static float bytesToFloat(int value1, int value2, int value3, int value4) {
266 int mantissa = unsignedToSigned(
267 unsignedByteToInt(value1) + (unsignedByteToInt(value2) << 8) + (unsignedByteToInt(value3) << 16), 24);
268 return (float) (mantissa * Math.pow(10, value4));
272 * Convert an unsigned integer to a two's-complement signed value.
274 private static int unsignedToSigned(int unsigned, int size) {
275 if ((unsigned & (1 << size - 1)) != 0) {
276 return -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1)));
283 * Convert an integer into the signed bits of the specified length.
285 private static int intToSignedBits(int i, int size) {
287 return (1 << size - 1) + (i & ((1 << size - 1) - 1));