]> git.basschouten.com Git - openhab-addons.git/blob
e95294d4e34793fd8e7507faecc98bdb240fde87
[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.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 /**
21  * This is a utility class for parsing or formatting bluetooth characteristic data.
22  *
23  * @author Connor Petty - Initial Contribution
24  *
25  */
26 public class BluetoothUtils {
27
28     public static final Logger logger = LoggerFactory.getLogger(BluetoothUtils.class);
29
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;
38
39     /**
40      * Converts a byte array to an int array
41      *
42      * @param value
43      * @return
44      */
45     public static int[] toIntArray(byte[] value) {
46         if (value == null) {
47             return null;
48         }
49         int[] ret = new int[value.length];
50         for (int i = 0; i < value.length; i++) {
51             ret[i] = value[i];
52         }
53         return ret;
54     }
55
56     public static byte[] toByteArray(int[] value) {
57         if (value == null) {
58             return null;
59         }
60         byte[] ret = new byte[value.length];
61         for (int i = 0; i < value.length; i++) {
62             ret[i] = (byte) (value[i] & 0xFF);
63         }
64         return ret;
65     }
66
67     /**
68      * Return the stored value of this characteristic.
69      *
70      */
71     public static Integer getIntegerValue(byte[] value, int formatType, int offset) {
72         if ((offset + getTypeLen(formatType)) > value.length) {
73             return null;
74         }
75
76         switch (formatType) {
77             case FORMAT_UINT8:
78                 return unsignedByteToInt(value[offset]);
79
80             case FORMAT_UINT16:
81                 return unsignedBytesToInt(value[offset], value[offset + 1]);
82
83             case FORMAT_UINT32:
84                 return unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
85
86             case FORMAT_SINT8:
87                 return unsignedToSigned(unsignedByteToInt(value[offset]), 8);
88
89             case FORMAT_SINT16:
90                 return unsignedToSigned(unsignedBytesToInt(value[offset], value[offset + 1]), 16);
91
92             case FORMAT_SINT32:
93                 return unsignedToSigned(
94                         unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]), 32);
95             default:
96                 logger.error("Unknown format type {} - no int value can be provided for it.", formatType);
97         }
98
99         return null;
100     }
101
102     /**
103      * Return the stored value of this characteristic. This doesn't read the remote data.
104      *
105      */
106     public static Float getFloatValue(byte[] value, int formatType, int offset) {
107         if ((offset + getTypeLen(formatType)) > value.length) {
108             return null;
109         }
110
111         switch (formatType) {
112             case FORMAT_SFLOAT:
113                 return bytesToFloat(value[offset], value[offset + 1]);
114             case FORMAT_FLOAT:
115                 return bytesToFloat(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
116             default:
117                 logger.error("Unknown format type {} - no float value can be provided for it.", formatType);
118         }
119
120         return null;
121     }
122
123     /**
124      * Return the stored value of this characteristic. This doesn't read the remote data.
125      *
126      */
127     public static String getStringValue(byte[] value, int offset) {
128         if (value == null || offset > value.length) {
129             return null;
130         }
131         byte[] strBytes = new byte[value.length - offset];
132         for (int i = 0; i < (value.length - offset); ++i) {
133             strBytes[i] = value[offset + i];
134         }
135         return new String(strBytes, StandardCharsets.UTF_8);
136     }
137
138     /**
139      * Set the local value of this characteristic.
140      *
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
145      */
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) {
149             return false;
150         }
151         int val = value;
152         switch (formatType) {
153             case FORMAT_SINT8:
154                 val = intToSignedBits(value, 8);
155                 // Fall-through intended
156             case FORMAT_UINT8:
157                 dest[offset] = (byte) (val & 0xFF);
158                 break;
159
160             case FORMAT_SINT16:
161                 val = intToSignedBits(value, 16);
162                 // Fall-through intended
163             case FORMAT_UINT16:
164                 dest[offset] = (byte) (val & 0xFF);
165                 dest[offset + 1] = (byte) ((val >> 8) & 0xFF);
166                 break;
167
168             case FORMAT_SINT32:
169                 val = intToSignedBits(value, 32);
170                 // Fall-through intended
171             case FORMAT_UINT32:
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);
176                 break;
177
178             default:
179                 return false;
180         }
181         return true;
182     }
183
184     /**
185      * Set the local value of this characteristic.
186      *
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
192      *
193      */
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) {
197             return false;
198         }
199
200         switch (formatType) {
201             case FORMAT_SFLOAT:
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);
207                 break;
208
209             case FORMAT_FLOAT:
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);
216                 break;
217
218             default:
219                 return false;
220         }
221
222         return true;
223     }
224
225     /**
226      * Returns the size of the requested value type.
227      */
228     private static int getTypeLen(int formatType) {
229         return formatType & 0xF;
230     }
231
232     /**
233      * Convert a signed byte to an unsigned int.
234      */
235     private static int unsignedByteToInt(int value) {
236         return value & 0xFF;
237     }
238
239     /**
240      * Convert signed bytes to a 16-bit unsigned int.
241      */
242     private static int unsignedBytesToInt(int value1, int value2) {
243         return value1 + (value2 << 8);
244     }
245
246     /**
247      * Convert signed bytes to a 32-bit unsigned int.
248      */
249     private static int unsignedBytesToInt(int value1, int value2, int value3, int value4) {
250         return value1 + (value2 << 8) + (value3 << 16) + (value4 << 24);
251     }
252
253     /**
254      * Convert signed bytes to a 16-bit short float value.
255      */
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));
260     }
261
262     /**
263      * Convert signed bytes to a 32-bit short float value.
264      */
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));
269     }
270
271     /**
272      * Convert an unsigned integer to a two's-complement signed value.
273      */
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)));
277         } else {
278             return unsigned;
279         }
280     }
281
282     /**
283      * Convert an integer into the signed bits of the specified length.
284      */
285     private static int intToSignedBits(int i, int size) {
286         if (i < 0) {
287             return (1 << size - 1) + (i & ((1 << size - 1) - 1));
288         } else {
289             return i;
290         }
291     }
292 }