]> git.basschouten.com Git - openhab-addons.git/blob
456c35e7369b1147fd4c49da5e9aa5a3340b66c3
[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 cannot be used as it throws ArrayStoreException
51         for (int i = 0; i < value.length; i++) {
52             ret[i] = value[i];
53         }
54         return ret;
55     }
56
57     public static byte[] toByteArray(int[] value) {
58         byte[] ret = new byte[value.length];
59         for (int i = 0; i < value.length; i++) {
60             ret[i] = (byte) (value[i] & 0xFF);
61         }
62         return ret;
63     }
64
65     /**
66      * Return the stored value of this characteristic.
67      *
68      */
69     public static @Nullable Integer getIntegerValue(byte[] value, int formatType, int offset) {
70         if ((offset + getTypeLen(formatType)) > value.length) {
71             return null;
72         }
73
74         switch (formatType) {
75             case FORMAT_UINT8:
76                 return unsignedByteToInt(value[offset]);
77
78             case FORMAT_UINT16:
79                 return unsignedBytesToInt(value[offset], value[offset + 1]);
80
81             case FORMAT_UINT32:
82                 return unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
83
84             case FORMAT_SINT8:
85                 return unsignedToSigned(unsignedByteToInt(value[offset]), 8);
86
87             case FORMAT_SINT16:
88                 return unsignedToSigned(unsignedBytesToInt(value[offset], value[offset + 1]), 16);
89
90             case FORMAT_SINT32:
91                 return unsignedToSigned(
92                         unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]), 32);
93             default:
94                 LOGGER.error("Unknown format type {} - no int value can be provided for it.", formatType);
95         }
96
97         return null;
98     }
99
100     /**
101      * Return the stored value of this characteristic. This doesn't read the remote data.
102      *
103      */
104     public static @Nullable Float getFloatValue(byte[] value, int formatType, int offset) {
105         if ((offset + getTypeLen(formatType)) > value.length) {
106             return null;
107         }
108
109         switch (formatType) {
110             case FORMAT_SFLOAT:
111                 return bytesToFloat(value[offset], value[offset + 1]);
112             case FORMAT_FLOAT:
113                 return bytesToFloat(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
114             default:
115                 LOGGER.error("Unknown format type {} - no float value can be provided for it.", formatType);
116         }
117
118         return null;
119     }
120
121     /**
122      * Return the stored value of this characteristic. This doesn't read the remote data.
123      *
124      */
125     public static @Nullable String getStringValue(byte[] value, int offset) {
126         if (offset > value.length) {
127             return null;
128         }
129         byte[] strBytes = new byte[value.length - offset];
130         for (int i = 0; i < (value.length - offset); ++i) {
131             strBytes[i] = value[offset + i];
132         }
133         return new String(strBytes, StandardCharsets.UTF_8);
134     }
135
136     /**
137      * Set the local value of this characteristic.
138      *
139      * @param value the value to set
140      * @param formatType the format of the value (as one of the FORMAT_* constants in this class)
141      * @param offset the offset to use when interpreting the value
142      * @return true, if it has been set successfully
143      */
144     public static boolean setValue(byte[] dest, int value, int formatType, int offset) {
145         int len = offset + getTypeLen(formatType);
146         if (len > dest.length) {
147             return false;
148         }
149         int val = value;
150         switch (formatType) {
151             case FORMAT_SINT8:
152                 val = intToSignedBits(value, 8);
153                 // Fall-through intended
154             case FORMAT_UINT8:
155                 dest[offset] = (byte) (val & 0xFF);
156                 break;
157
158             case FORMAT_SINT16:
159                 val = intToSignedBits(value, 16);
160                 // Fall-through intended
161             case FORMAT_UINT16:
162                 dest[offset] = (byte) (val & 0xFF);
163                 dest[offset + 1] = (byte) ((val >> 8) & 0xFF);
164                 break;
165
166             case FORMAT_SINT32:
167                 val = intToSignedBits(value, 32);
168                 // Fall-through intended
169             case FORMAT_UINT32:
170                 dest[offset] = (byte) (val & 0xFF);
171                 dest[offset + 1] = (byte) ((val >> 8) & 0xFF);
172                 dest[offset + 2] = (byte) ((val >> 16) & 0xFF);
173                 dest[offset + 2] = (byte) ((val >> 24) & 0xFF);
174                 break;
175
176             default:
177                 return false;
178         }
179         return true;
180     }
181
182     /**
183      * Set the local value of this characteristic.
184      *
185      * @param mantissa the mantissa of the value
186      * @param exponent the exponent of the value
187      * @param formatType the format of the value (as one of the FORMAT_* constants in this class)
188      * @param offset the offset to use when interpreting the value
189      * @return true, if it has been set successfully
190      *
191      */
192     public static boolean setValue(byte[] dest, int mantissa, int exponent, int formatType, int offset) {
193         int len = offset + getTypeLen(formatType);
194         if (len > dest.length) {
195             return false;
196         }
197
198         switch (formatType) {
199             case FORMAT_SFLOAT:
200                 int m = intToSignedBits(mantissa, 12);
201                 int exp = intToSignedBits(exponent, 4);
202                 dest[offset] = (byte) (m & 0xFF);
203                 dest[offset + 1] = (byte) ((m >> 8) & 0x0F);
204                 dest[offset + 1] += (byte) ((exp & 0x0F) << 4);
205                 break;
206
207             case FORMAT_FLOAT:
208                 m = intToSignedBits(mantissa, 24);
209                 exp = intToSignedBits(exponent, 8);
210                 dest[offset] = (byte) (m & 0xFF);
211                 dest[offset + 1] = (byte) ((m >> 8) & 0xFF);
212                 dest[offset + 2] = (byte) ((m >> 16) & 0xFF);
213                 dest[offset + 2] += (byte) (exp & 0xFF);
214                 break;
215
216             default:
217                 return false;
218         }
219
220         return true;
221     }
222
223     /**
224      * Returns the size of the requested value type.
225      */
226     private static int getTypeLen(int formatType) {
227         return formatType & 0xF;
228     }
229
230     /**
231      * Convert a signed byte to an unsigned int.
232      */
233     private static int unsignedByteToInt(int value) {
234         return value & 0xFF;
235     }
236
237     /**
238      * Convert signed bytes to a 16-bit unsigned int.
239      */
240     private static int unsignedBytesToInt(int value1, int value2) {
241         return value1 + (value2 << 8);
242     }
243
244     /**
245      * Convert signed bytes to a 32-bit unsigned int.
246      */
247     private static int unsignedBytesToInt(int value1, int value2, int value3, int value4) {
248         return value1 + (value2 << 8) + (value3 << 16) + (value4 << 24);
249     }
250
251     /**
252      * Convert signed bytes to a 16-bit short float value.
253      */
254     private static float bytesToFloat(int value1, int value2) {
255         int mantissa = unsignedToSigned(unsignedByteToInt(value1) + ((unsignedByteToInt(value2) & 0x0F) << 8), 12);
256         int exponent = unsignedToSigned(unsignedByteToInt(value2) >> 4, 4);
257         return (float) (mantissa * Math.pow(10, exponent));
258     }
259
260     /**
261      * Convert signed bytes to a 32-bit short float value.
262      */
263     private static float bytesToFloat(int value1, int value2, int value3, int value4) {
264         int mantissa = unsignedToSigned(
265                 unsignedByteToInt(value1) + (unsignedByteToInt(value2) << 8) + (unsignedByteToInt(value3) << 16), 24);
266         return (float) (mantissa * Math.pow(10, value4));
267     }
268
269     /**
270      * Convert an unsigned integer to a two's-complement signed value.
271      */
272     private static int unsignedToSigned(int unsigned, int size) {
273         if ((unsigned & (1 << size - 1)) != 0) {
274             return -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1)));
275         } else {
276             return unsigned;
277         }
278     }
279
280     /**
281      * Convert an integer into the signed bits of the specified length.
282      */
283     private static int intToSignedBits(int i, int size) {
284         if (i < 0) {
285             return (1 << size - 1) + (i & ((1 << size - 1) - 1));
286         } else {
287             return i;
288         }
289     }
290 }