]> git.basschouten.com Git - openhab-addons.git/blob
33c9e8b32783e376ca3d8ad0ab3fc7c7a1b05c7e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.bluetooth;
14
15 import java.nio.charset.StandardCharsets;
16
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 /**
23  * This is a utility class for parsing or formatting bluetooth characteristic data.
24  *
25  * @author Connor Petty - Initial Contribution
26  *
27  */
28 @NonNullByDefault
29 public class BluetoothUtils {
30
31     public static final Logger LOGGER = LoggerFactory.getLogger(BluetoothUtils.class);
32
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;
41
42     /**
43      * Converts a byte array to an int array
44      *
45      * @param value
46      * @return
47      */
48     public static int[] toIntArray(byte[] value) {
49         int[] ret = new int[value.length];
50         System.arraycopy(value, 0, ret, 0, value.length);
51         return ret;
52     }
53
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);
58         }
59         return ret;
60     }
61
62     /**
63      * Return the stored value of this characteristic.
64      *
65      */
66     public static @Nullable Integer getIntegerValue(byte[] value, int formatType, int offset) {
67         if ((offset + getTypeLen(formatType)) > value.length) {
68             return null;
69         }
70
71         switch (formatType) {
72             case FORMAT_UINT8:
73                 return unsignedByteToInt(value[offset]);
74
75             case FORMAT_UINT16:
76                 return unsignedBytesToInt(value[offset], value[offset + 1]);
77
78             case FORMAT_UINT32:
79                 return unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
80
81             case FORMAT_SINT8:
82                 return unsignedToSigned(unsignedByteToInt(value[offset]), 8);
83
84             case FORMAT_SINT16:
85                 return unsignedToSigned(unsignedBytesToInt(value[offset], value[offset + 1]), 16);
86
87             case FORMAT_SINT32:
88                 return unsignedToSigned(
89                         unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]), 32);
90             default:
91                 LOGGER.error("Unknown format type {} - no int value can be provided for it.", formatType);
92         }
93
94         return null;
95     }
96
97     /**
98      * Return the stored value of this characteristic. This doesn't read the remote data.
99      *
100      */
101     public static @Nullable Float getFloatValue(byte[] value, int formatType, int offset) {
102         if ((offset + getTypeLen(formatType)) > value.length) {
103             return null;
104         }
105
106         switch (formatType) {
107             case FORMAT_SFLOAT:
108                 return bytesToFloat(value[offset], value[offset + 1]);
109             case FORMAT_FLOAT:
110                 return bytesToFloat(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
111             default:
112                 LOGGER.error("Unknown format type {} - no float value can be provided for it.", formatType);
113         }
114
115         return null;
116     }
117
118     /**
119      * Return the stored value of this characteristic. This doesn't read the remote data.
120      *
121      */
122     public static @Nullable String getStringValue(byte[] value, int offset) {
123         if (offset > value.length) {
124             return null;
125         }
126         byte[] strBytes = new byte[value.length - offset];
127         for (int i = 0; i < (value.length - offset); ++i) {
128             strBytes[i] = value[offset + i];
129         }
130         return new String(strBytes, StandardCharsets.UTF_8);
131     }
132
133     /**
134      * Set the local value of this characteristic.
135      *
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
140      */
141     public static boolean setValue(byte[] dest, int value, int formatType, int offset) {
142         int len = offset + getTypeLen(formatType);
143         if (len > dest.length) {
144             return false;
145         }
146         int val = value;
147         switch (formatType) {
148             case FORMAT_SINT8:
149                 val = intToSignedBits(value, 8);
150                 // Fall-through intended
151             case FORMAT_UINT8:
152                 dest[offset] = (byte) (val & 0xFF);
153                 break;
154
155             case FORMAT_SINT16:
156                 val = intToSignedBits(value, 16);
157                 // Fall-through intended
158             case FORMAT_UINT16:
159                 dest[offset] = (byte) (val & 0xFF);
160                 dest[offset + 1] = (byte) ((val >> 8) & 0xFF);
161                 break;
162
163             case FORMAT_SINT32:
164                 val = intToSignedBits(value, 32);
165                 // Fall-through intended
166             case FORMAT_UINT32:
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);
171                 break;
172
173             default:
174                 return false;
175         }
176         return true;
177     }
178
179     /**
180      * Set the local value of this characteristic.
181      *
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
187      *
188      */
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) {
192             return false;
193         }
194
195         switch (formatType) {
196             case FORMAT_SFLOAT:
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);
202                 break;
203
204             case FORMAT_FLOAT:
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);
211                 break;
212
213             default:
214                 return false;
215         }
216
217         return true;
218     }
219
220     /**
221      * Returns the size of the requested value type.
222      */
223     private static int getTypeLen(int formatType) {
224         return formatType & 0xF;
225     }
226
227     /**
228      * Convert a signed byte to an unsigned int.
229      */
230     private static int unsignedByteToInt(int value) {
231         return value & 0xFF;
232     }
233
234     /**
235      * Convert signed bytes to a 16-bit unsigned int.
236      */
237     private static int unsignedBytesToInt(int value1, int value2) {
238         return value1 + (value2 << 8);
239     }
240
241     /**
242      * Convert signed bytes to a 32-bit unsigned int.
243      */
244     private static int unsignedBytesToInt(int value1, int value2, int value3, int value4) {
245         return value1 + (value2 << 8) + (value3 << 16) + (value4 << 24);
246     }
247
248     /**
249      * Convert signed bytes to a 16-bit short float value.
250      */
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));
255     }
256
257     /**
258      * Convert signed bytes to a 32-bit short float value.
259      */
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));
264     }
265
266     /**
267      * Convert an unsigned integer to a two's-complement signed value.
268      */
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)));
272         } else {
273             return unsigned;
274         }
275     }
276
277     /**
278      * Convert an integer into the signed bits of the specified length.
279      */
280     private static int intToSignedBits(int i, int size) {
281         if (i < 0) {
282             return (1 << size - 1) + (i & ((1 << size - 1) - 1));
283         } else {
284             return i;
285         }
286     }
287 }