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