From d006f91531121b8af47f4b92ae430a39c2be3423 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Sat, 27 Jan 2018 23:22:42 +0100 Subject: [PATCH 1/3] Initial contribution of the generic Bluetooth binding with BlueZ support Also-By: Chris Jackson Signed-off-by: Kai Kreuzer --- .../.classpath | 8 + .../.project | 33 + .../ESH-INF/thing/bluez.xml | 27 + .../META-INF/MANIFEST.MF | 34 + .../NOTICE | 52 + .../OSGI-INF/.gitignore | 1 + .../README.md | 29 + .../build.properties | 8 + .../lib/armv6hf/libjavatinyb.so | Bin 0 -> 484032 bytes .../lib/armv6hf/libtinyb.so | Bin 0 -> 1218076 bytes .../lib/tinyb-0.5.1.jar | Bin 0 -> 15143 bytes .../pom.xml | 18 + .../bluez/BlueZAdapterConstants.java | 35 + .../bluetooth/bluez/BlueZBluetoothDevice.java | 378 +++++ .../bluez/handler/BlueZBridgeHandler.java | 275 ++++ .../bluez/internal/BlueZHandlerFactory.java | 83 + .../discovery/BlueZDiscoveryService.java | 92 ++ .../.classpath | 7 + .../.project | 28 + .../META-INF/MANIFEST.MF | 21 + .../NOTICE | 19 + .../build.properties | 6 + ...se.smarthome.binding.bluetooth.test.launch | 143 ++ .../pom.xml | 119 ++ .../bluetooth/BluetoothAddressTest.java | 33 + .../.classpath | 7 + .../.project | 33 + .../ARCHITECTURE.md | 68 + .../ESH-INF/binding/binding.xml | 8 + .../ESH-INF/thing/channels.xml | 14 + .../ESH-INF/thing/thing-types.xml | 46 + .../META-INF/MANIFEST.MF | 28 + .../NOTICE | 19 + .../OSGI-INF/.gitignore | 1 + .../README.md | 72 + .../build.properties | 7 + .../pom.xml | 18 + .../bluetooth/BeaconBluetoothHandler.java | 154 ++ .../binding/bluetooth/BluetoothAdapter.java | 79 + .../binding/bluetooth/BluetoothAddress.java | 91 + .../bluetooth/BluetoothBindingConstants.java | 56 + .../bluetooth/BluetoothCharacteristic.java | 658 ++++++++ .../binding/bluetooth/BluetoothClass.java | 166 ++ .../BluetoothCompanyIdentifiers.java | 1466 +++++++++++++++++ .../bluetooth/BluetoothCompletionStatus.java | 24 + .../bluetooth/BluetoothDescriptor.java | 140 ++ .../binding/bluetooth/BluetoothDevice.java | 552 +++++++ .../bluetooth/BluetoothDeviceListener.java | 80 + .../bluetooth/BluetoothDiscoveryListener.java | 30 + .../binding/bluetooth/BluetoothService.java | 286 ++++ .../bluetooth/ConnectedBluetoothHandler.java | 245 +++ .../BluetoothDiscoveryParticipant.java | 61 + .../internal/BluetoothDiscoveryService.java | 182 ++ .../internal/BluetoothHandlerFactory.java | 60 + ...BluetoothConnectionStatusNotification.java | 37 + .../notification/BluetoothNotification.java | 31 + .../BluetoothScanNotification.java | 144 ++ extensions/binding/pom.xml | 4 +- .../esh-ext/src/main/feature/feature.xml | 11 + .../feature.xml | 16 +- 60 files changed, 6341 insertions(+), 2 deletions(-) create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/.classpath create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/.project create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/ESH-INF/thing/bluez.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/META-INF/MANIFEST.MF create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/NOTICE create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/OSGI-INF/.gitignore create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/README.md create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/build.properties create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/lib/armv6hf/libjavatinyb.so create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/lib/armv6hf/libtinyb.so create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/lib/tinyb-0.5.1.jar create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/pom.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/BlueZAdapterConstants.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/BlueZBluetoothDevice.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/internal/BlueZHandlerFactory.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/internal/discovery/BlueZDiscoveryService.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/.classpath create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/.project create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/META-INF/MANIFEST.MF create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/NOTICE create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/build.properties create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/org.eclipse.smarthome.binding.bluetooth.test.launch create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/pom.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/src/test/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAddressTest.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/.classpath create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/.project create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/ARCHITECTURE.md create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/binding/binding.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/thing/channels.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/thing/thing-types.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/META-INF/MANIFEST.MF create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/NOTICE create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/OSGI-INF/.gitignore create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/README.md create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/build.properties create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/pom.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BeaconBluetoothHandler.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAdapter.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAddress.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothBindingConstants.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCharacteristic.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothClass.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCompanyIdentifiers.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCompletionStatus.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDescriptor.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDevice.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDeviceListener.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDiscoveryListener.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothService.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/ConnectedBluetoothHandler.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/discovery/BluetoothDiscoveryParticipant.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/internal/BluetoothHandlerFactory.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothConnectionStatusNotification.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothNotification.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothScanNotification.java diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/.classpath b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/.classpath new file mode 100644 index 00000000000..fdbb371ce27 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/.project b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/.project new file mode 100644 index 00000000000..692d80e9e5a --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/.project @@ -0,0 +1,33 @@ + + + org.eclipse.smarthome.binding.bluetooth.bluez + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/ESH-INF/thing/bluez.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/ESH-INF/thing/bluez.xml new file mode 100644 index 00000000000..1e0e9fb4954 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/ESH-INF/thing/bluez.xml @@ -0,0 +1,27 @@ + + + + + + Linux built-in Bluetooth support + + address + + + + + The Bluetooth address of the adapter in format XX:XX:XX:XX:XX:XX + + + + Whether this adapter actively participates in Bluetooth device discovery + true + true + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..03be622458b --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/META-INF/MANIFEST.MF @@ -0,0 +1,34 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: BlueZ Bluetooth Adapter +Bundle-SymbolicName: org.eclipse.smarthome.binding.bluetooth.bluez;singleton:=true +Bundle-Vendor: Eclipse.org/SmartHome +Bundle-Version: 0.10.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ClassPath: ., + lib/tinyb-0.5.1.jar +Bundle-NativeCode: + lib/armv6hf/libjavatinyb.so;lib/armv6hf/libtinyb.so;processor=arm;osname=linux, + * +Import-Package: org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.binding.bluetooth, + org.eclipse.smarthome.binding.bluetooth.bluez, + org.eclipse.smarthome.binding.bluetooth.bluez.handler, + org.eclipse.smarthome.binding.bluetooth.discovery, + org.eclipse.smarthome.binding.bluetooth.notification, + org.eclipse.smarthome.config.core, + org.eclipse.smarthome.config.discovery, + org.eclipse.smarthome.core.common, + org.eclipse.smarthome.core.common.registry, + org.eclipse.smarthome.core.library.types, + org.eclipse.smarthome.core.thing, + org.eclipse.smarthome.core.thing.binding, + org.eclipse.smarthome.core.thing.binding.builder, + org.eclipse.smarthome.core.thing.type, + org.eclipse.smarthome.core.types, + org.osgi.framework, + org.slf4j +Service-Component: OSGI-INF/*.xml +Export-Package: org.eclipse.smarthome.binding.bluetooth.bluez, + org.eclipse.smarthome.binding.bluetooth.bluez.handler +Bundle-ActivationPolicy: lazy diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/NOTICE b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/NOTICE new file mode 100644 index 00000000000..b5cad373c26 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/NOTICE @@ -0,0 +1,52 @@ +This content is produced and maintained by the Eclipse SmartHome project. + +* Project home: https://eclipse.org/smarthome/ + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/eclipse/smarthome + +== Copyright Holders + +See the NOTICE file distributed with the source code at +https://github.com/eclipse/smarthome/blob/master/NOTICE +for detailed information regarding copyright ownership. + +== Third-party Content + +TinyB Version: 0.5.1 +* License: MIT License +* Project: https://github.com/intel-iot-devkit/tinyb +* Source: https://github.com/intel-iot-devkit/tinyb/tree/v0.5.1 + +== Third-party license(s) + +=== MIT License + +The MIT License (MIT) +Copyright © 2015-2016 Intel Corporation + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/OSGI-INF/.gitignore b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/OSGI-INF/.gitignore new file mode 100644 index 00000000000..b878e882aca --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/OSGI-INF/.gitignore @@ -0,0 +1 @@ +/*.xml diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/README.md b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/README.md new file mode 100644 index 00000000000..8d7825bb9a0 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/README.md @@ -0,0 +1,29 @@ +## Bluetooth BlueZ Adapter + +This extension supports Bluetooth access via BlueZ on Linux (ARMv6hf). + +## Supported Things + +It defines the following bridge type: + +| Bridge Type ID | Description | +|----------------|---------------------------------------------------------------------------| +| bluez | A Bluetooth adapter that is supported by BlueZ | + + +## Discovery + +If BlueZ is enabled and can be accessed, all available adapters are automatically discovered. + +## Bridge Configuration + +The bluez bridge requires the configuration parameter `address`, which corresponds to the Bluetooth address of the adapter (in format "XX:XX:XX:XX:XX:XX"). +Additionally, the parameter `discovery` can be set to true/false.When set to true, any Bluetooth device of which broadcasts are received is added to the Inbox. + +## Example + +This is how an BlueZ adapter can be configured textually in a *.things file: + +``` +Bridge bluetooth:bluez:hci0 [ address="12:34:56:78:90:AB", discovery=false ] +``` diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/build.properties b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/build.properties new file mode 100644 index 00000000000..2846a025d2f --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/build.properties @@ -0,0 +1,8 @@ +source..=src/main/java/ +output..=target/classes +bin.includes = META-INF/,\ + .,\ + OSGI-INF/,\ + ESH-INF/,\ + NOTICE,\ + lib/ diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/lib/armv6hf/libjavatinyb.so b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/lib/armv6hf/libjavatinyb.so new file mode 100644 index 0000000000000000000000000000000000000000..b1b5da17fc90b69d90570979d8382ffe90d91c03 GIT binary patch literal 484032 zcmcd!2Urxx+uwszP*JgW1;yTIDn^Y5f`Xu;pkh7RAt+5auq$?~vG;<#cTKQk*S|Fy zdvCFu#1doFk#0lIMwAhp9OR_eyab0X51K5NpE)ezRt(dC13Ksf6blD4Bsf z0FR;^7YfKvpIhPRyG#wp$Ac{@BtV|CHcH}}ffMc%V9#uD&PnPgm$m@*yyIhiB)O;j zk^1ccq+CgWHK0xV&fKcGoxfD9>O5sehnm$--%7PUpIQ2g4GOKpbJCf@IHaY`YHghs z=)kop$Z6D}-kgJN+C1lKUWbl4l(gm4nhY;4>z8M)&fD&Y_Hj-aaB2PY$z9bys*Yj!#P@&Ri7M%`b*k7YPhXjpw*h%vmCMli+O4^ z>7+LN(nqO0H*xDTZM`|pnG04G7`fd}U8wd2$7yL8xI^ByhipNV5IbEb2X%Xm!}MvI zQZwxwxr|E=zGb4QQu9ht_}v%fI1 z6Suls!+>gF#bRDumGm*bS!s6N?CmvU9ImK+G%cJwIQ#Ts4jNngL!pbRy4rB6Vn9X3 z*#m3Os-r^>yB5@Vb2a({HwRm1`v_H8wc3VT4pNNK9M^Emi?7W{Qf=4NbO_vEkaIXW zbDEvX+f(CKE1x=M!qk!WZRf)bR225{9!?VhgS9)yQ+t+ylvA0i*x^T zp0X{}VRXUL^Ej^fn6lgtz7brcEymG-bMp4$mXC4Jv~_jQ@335_w(|16kX74SRjWYx zNj4?bV+yM?{A*WjG}bzTd(5q{pKhDxpg9`KS*1Cojk%x>1j;zKpJ+8oW$T^K)zM)k zPhwxST4P(V2zS(WEN8EtUe1A(vwezDxAvY^V3M6X7pgv~PJ>)d^YRQW?quhn4hXeB zzAT+fQ(NbE$mdkdw~&?XGDnrYRk33Bb;7x8;O~y3f1b&TG_|s`ohVs4wSD#F#5?9ht7y)TypkaRFX-B{?haQRO(UR7gSZ zdfc>(0xC@b`;m?Xt82>J7r4h2?-r^FE2nXow^mh3oyHXl<$iQ>sO!1VSvAKu-^Nz; ze)SzQhnuFc8l?%g=emqJGOCukEa%19?zH7<7q9&(-Ku&T(FN-P8vvUC?6(;wTL9bW zjqNz@pyxl~xSO7n!(PAv$@M{;9|9bv_m1Ou0&o&=3UC&19`G~Z65toW6#)5N6%K~K z*9>PIcOB<90JrG9+c@3@+^5$MaC}J5$>A{o!_K_`yaK!i{01PuKZFDK7RNsU9{`^K zp8@{@!2kS@m>6q%jxpi#1Bgy9K<|-41tsT&aBe5L7HWa6mE0?W^P&KI$vsl8IIc?o zhRdTcqdkK}x=z%sycz-qu6z*@k10Q+r_oNuJ(n{eC= z*aFxJ*ap}R*a6rH*ai3zkO|lg*aKj{eK_AwuMbG>AH?|~z!7@yD2~Sf#{nnkJyOR> zoS&lCr*S+(&(Gp`9&mwPU!=!NI9{gbW5bo9_sIRS^t>F76#x|hmFT_7^yo;B zRdK9F&&j*wR~^?i=)Iaa)}`kSaBK*01-R3Djd1j!=S^{J253RAzsIp9z>{8k%{VW_0 z)AJ)Z9t9i&90!~NoB^B%TmbwGxCkJ>UvRuk&xJdXe_UUc+`ESJ>wsI5d!+1bdVL4S zdw_?M=a1<5W68ZI^!%CR-gA2X0>_ttSAf@m-vECA-T~eNq`!~2{sj0;?|;GZUwTf; zsSvbT1M&gLFF%g9^t=F$h3GkXhM0pZO0Vs4EG~Ig0_SA_WdY>?k&gaX0= zIzS%)`9;&Ca0Q)!>o`CHy`P9gz!iFZl^(Ct;|+Qw_20z#Ex>KbJ#t_9 zyNBoZ0S^EV0gnOX_bWX@qh%$J@$+p`{P+MeV#&(sW=V-458Po>~LJC0Y(Bw z(PyLSaSV=Q={YGs4(HGa-2947;&(CcY9P6x~a%m&N>%mvH?ECi6>5*(KTG62iz zy_Gnwrsr#M+yK}}uZ6cD+qm8$xwn;`Z^LmrUD#=Kqg=h;I~m;7u+t+-gM{6 zko!w79(+-)_IGPMgCG4gPv3XPrH^5DCA@}Z76@6YEq-U#vr+Y`C9W*q?RKdJ%fBBJ z*d%kqtc91Bj4x33lz+3p?n~=Tn)hbcvV}p%Z*5WKyOC~p<=3SJ?;cr|wPkf{yP=~- zIdu)2=YRd(k@3?C@6g6IpINW#p~vT{F7sMY^x&O??ysYJ{87ns*z2acW{^50@5r0`vxLfmc-S6HH?f>@$nh$-I}xv>DaWpn`gVR zeWz|Nd%JMu2R=Inj1GD9)BAQamTbIw{n-C@T#YGz_KICXrK$BkerP)V&6E}A7L_}) zXN7aK&x;-FTt0E6g4faf4!5j#ZCJMd`?t{xR@9kPYRi1v1{sqk)nD+a*}=1O?4Qiv zUUJJw_2{By8Z>Q^mh^VZHk)#n&NeP_J}ct~+j-TWHfm|9KdMGA$p{-${B7y>$4+j@e{5UPGJj6E;qlMyQ3rNU`J>3=1-m1=-TM9F;wI?p zlU|X%e!gONe)o4?Q;v2xcW8fydl%oI@*GmWYo{XVUm|{cxgs`h$)jdJSQq{^_DtDh zH5`2kNBq%j>txs0S!XMy4!ZnOSHJ(BZdOYwCk+1d@%dVHk?IXQX|Miz-s{S&A6)-v zAG*He%{|Q@tXExH9e1PWhA!$ITYgv?U;5zEwh!jMz0_b`kD*Q>Z_hm}>VLA^NT;KX zGZ(j8ykPaXo|B>ueiwK-%|?H?v2*&DwKF?hKlqzd;>*y)^8*8meZT%-;bNz@dn~Zaao0T|I@B;VCPwv3*7dc z-gv~wGK-@;8{Dc`?(-7u^xZ=?)qgv|q0q}K$FE1E54cpUbKNRy@43|-`|-r*KU!4# z;JKIy~V+jsrnKcCZhru~JhYo;e}cs^bq z_ay&6ah}&pHrV{QLxmULc}yJXdaGiov8QgXT$Z$J<;Iax!7khr| z=%(m@!%~WEoxHtTshuS&Wd3sQ>C?v{rCeKV-g{;9O`Bb_Ba_#xKIWs1%G^6;*rT6z zAKtTJN{O_aEmeWZmCDpPb*oCtx8G0rX{lFi{FtE+r>=7SdD_SCidK)?(yzyNpAv#D zFZ|#9+n4_>rP@BVe9Wto<=mU4Yew8@y7AZJ%?`ILfB5z(*H5V~@1y6bwzxliHoyCY z&em~vTHczxtn8o7Dz`k*8br(5Cvs~;VB zSk?X0opp;(tb88r_N?%nDUKh#uJ;eAXqVvJ_fF+f8r+=9-TDJ|1A7xgRWX_DLIZ+WWywI176wW+mh>g_)lTQxY?^>B>a!~03W zRh=`QoC|;F|EyP&yCX7!V!HoZcUnVD)3~Hso2xHr@z=}zZ>MzbGP`Eu0;%`T3`^Hf zEIjq%+00Ikb^q?4X4C2AkbI9qFZH_O@v>Rn^#g`Z-Ip=!*rEl?_78tkY~A!PURO7r zOzFOU@P;Fcj%&A_u26T@@Dr_{Uf9xLYoAAF{hgLaxbGTNAnKWQ!9DMHotjmrTiAgY z_0LxS=ryiV!&M&rB1<+dHT6y(O~-j%{_SD4edi%3n>`I`+w4u5d0^L#*A2FJt6Z#e zvvG53hYsGfq;uwgL9a@dTyn9+(|L~u9JC+yZd1aN_CpJf8??05&O1Bf<6pc#zWY(_ zOX2TaQXBegt}y3Czi~}MCcf&`Bx~4>al5yydRd_F!8v;$&KP=p-I=)Uh3@ohGfmyT zaPa2G_Wc~YKCCusYVBFc^TsCEx!kv5zu3ur>{>Pr>~rwsiIP|Uo%{aGla~86o^BZ> zFWJnAKd?X2cWA@)176IiJ9_>7Gc#UX`mIRk(pQJZR@z?gMUnhAk-PTpdQ|gjozl-H zbZ}WwH2q+UFs&wd?v6@s%VYmoJZJI!WBZ>5mv($rVO1snP2D!%a9jM`HsQx^m5+Vt zad}|(HSc;|FVap`YS_C0!OdsgX|warlQuph>eP3VRi|fGC^RIv&%uyQcg_|#Q}XJge>~G4 zHu!v^#Ne>^{~U_+9ci_3-kzP;3%7Dv>3n_Zp>rcI{NARlUHQRnwmVf_)M46@@m}HY zikv$+rRhKI?#%q!!-QkKU$xkEsMPL5T}roX zK2E#9-*-pZ^{vBFda3*z{PyOv>UlY{_nLRZrakzes_@h1*QLMwcCgZ@f)l-aPS&ii zn&t0(#_QAJN*6PKE;rP3m)8V`B4JCKI_X1_s{XI^gO0`W=j%{zty`w{w)U@GWuBaS zx}xFTA+y|WZy0y)WAk?%mu{?EBmbL!1~>(jIh%j+?KNJV|IN7c&&RRFd|y88J=XX7 z@XB+F{2g2;A%4iF&huxCyR`CjW$nxwmx`S$xUxpA{Yind6E1pg_8va@Wl~z|f;RqB z&N!S)I#g}tz#fAGPqYqy*YV=XDK=AwyB7GnaY(^Z17=J=FtE^?a|NqBX|GWgeP=bi zRLGpFVRt>!|2O6Nn|<-?%6yqKY4V}fCmxQ7)8Cj=zpL8ihdm#>dO0>sT~({CQBGkb`2%gL$|+xypRws>LTJ_XIQz6>g|S9>pP%Dd)w|BTey4k%f7j8?sKO`YXO4}E|BOxx$hA71_EDLFZ9Cv5D z+u%{<&vg!H>ehZhrv2`!CDLwGPPnu1=E+sDO`q1i;1J^x9(COB;^JKeTkK1`RAo=Q zSzqQJnee|Jg=4&zt5rR#9xtl7(kp2G#36e>47uAZ;>sFzdYk>rzF+G&C8S|=3D+@q zd%3k8wlrW>pM#t3TU)Q3vbV#4;33u(R4v}#Jk`QwTT+i3R-4Xr{Ocd5O78vVer_@L z&k`pzN1eLQ9ry5E=v}Xg$0qJPTxPTGLzA(u4zIZXXMewu(R1h7wc0i2O;Xv@mAgjF zUhC%gLG@|mzLBM0_S|s1^H3MJu$lv^Z~JGC)0V5F&vU_3W4lHE{O0&MMM9 zplMj2aoV8$RZG0S@VnzOyOovCxEA?oqdxx$f6sl1J5SH9YSX{>stvx&)g8UB^s3Um z=gqvf z^a!c?G4!tgylK5Ztt^}{cI|_c3l@|fS9kcRxN^>i&ZVy|r(OTM&7ewU{Jgu>KfQZ< z#nY8KPhR9I!_ z_vdFly_;6md-Hg>QW_a&@v0+ou^j_z`lr0hX{GH99>eK&zv_)4dbJ@g-H-9)?>vPcL z26gVaH0oad_xUyZ?5%P|*J_jYr(%O%AMBp>qGx&EcP)ZjP9GU^v(1#IZ~yk_J~QIQ z;hu{tbq%RCF}~!mfh$K%uDES*%*+8Tn!gVJYtW0HqgGG(W8L}{#hz4t^?2TmrMgwt zBi#S&_Qv0Por6Q-y7Px`Ot9`zqIZSkpR4bRS&=Z~W7LGVYl55S*VtTkj;|fD$oG|7 z>zj`IyZttI(2Qa?7v(!NKL3uBd#}CNxi|64yu;tuj7k46b>-&+qx-}hn&RoOYQ?^~ z7he_p@N!1Yj9D|^dL4byH_~@-mNreb_V|UF(;CznS2C)DW8+5MNB0T8l3;T@a^Cl; z^L*?d6@C`%^5?kvE(PKiZt7mOC`J>B;mZPg`wzmO1a*v4_!n;+ro^@9*eyb78%y(??ug zy?4^wmj8Q@eD(FlS}x&bJD==)3Y zCI_}(pO92%Wl86yl{!T|eSfic=ZbAwH499P_L$w-WB!j5}L z?fH+(*Z+Ib`_EUyrp~{AD`a-Wry?V2xVv6-%Pd=P)6z}dw|=TwsL?N~nz}9dFPCYN zcKT?All_~W_Wfb;fQl`G-CsAYa<5K!k59v0n!nxogM0S_Cm#m<(e7Cnzdsi}Jdkpw z)&CxJc|WG#yJv~NTie!1-d4W)qXYGQN1Y08)UrrwpGWx)uMI2M^ib#bzn%CnKIu)( z_F0Xla0}Gk&rhv*?#8>UDyqU)$5vjUFL3Y73HMX!H3sxNcksob*sG@p{k2Sg_xGR1 zuUPV_x$aND0Y7x@+jiHjv#ossnq-b|(=2dK^Z7qICP#j#^*DOqn$VEFr}vzFVwDiJ ze|w3*z+!j&-u>L}=CMvsrndg2?3#WR+Isvta`d#qW8X~)Kh-5}=KlLO`&xfD+2PH< zH4EJx<5zd}j{KJw?R9^>t$mA$h0BbM9CqIKhk1WQ?%6T0|KFo->UZ89QnAJQg5%q4 zOS>P?<(HpsTwY(S)%u$=+rPZ?XLx<$Rcs>n1kx{;dg=Uk8ZAd3r6( zFModDPAowBSL&Yp`e()XCf1a{Pv%M4kG#JFD|pzCT)QQ(D}i1!9Oc(NAIh)e`pd5? zCCabQhsv)@iC%dcIW*d zqW>?j=92x$^P7t2HVS!Nq*AfsygZ+dGhFi5<%Y?xt;57?!m1Y)@>Zgv_?)!gNx@I8 z75vc8O5Xk|g}iU%S8fS)lJ;E7$m`#%!0)VL{E931WvZh8j;Zqcsuhx7{~9H~j#kKD z{aE?)lYQjZrxg8tr@();VtijI$|q{&_4Su9AffL;h5X-E=!^LZc`f!>USAW1zDZEj zS3@B$qZH#YThX8AW##o(R>;>_MSc6k;=H}Nu6X`Q!Ji)$@^)20k5dwQk*+-n6&oP* zYNxPA2NdJ|zO%gic7?vLuHdHz3VECEDCg&?5_xez)!`N7^>22RUyoPR7vdnVuZelIa z{jC-Dy24uGBU)n>IMz;bg}83(Fjw2onscJpr>|h%I>ws+j(|MB)&T2Pu$ZSFeg3|c zip#)6ExT^B8S6T*Ac4I<0Qz!c0c(COU~#;!*@HRz-sB4fp}o0Sv}#Wl5`fO+c}YFy zmYZ7}>ic*MYuS2OV?nV{zHl$BL4tow$l}PA;%=e;Aq}iJvL2GW?^qP?WAfLRUjNw; zYbCIlD3rcGqb1fWjJGnh|5J7N)L5{@@R`~XbMjb76hh1Y;(__+QR4d-!M`UZ_*Db{ zS3G8A=-&=3{@iFI(&J?%6?Y30_KY4KKVwdDiAe90=zs7`^cVON`i4Kpnma6D(9rfP zbiK-uD#izg1bq{qn)u(0=R|tn=w2UiW~0NyFj@Mt{$Xpf47XF#ImS zo_SnEd5kaV-`~@)4#ZDfi-Cn;&ndg zBVR1!V()MARBLjM_lTi=2o=ZpLug~hj_TLh(K&z0jI zLEj|)D)Lw5@9{AJ#+SWsy%auVYik3)RDXu`M$<)lHwS&2W3e2g|IkhF0XtiB4zxd? zAGMUkRa`pg!|HE-AM~Fl zUZ1l^eUkQD{)F*`JTd&iT3iSg_l45>?!eyN8g9isqSv?IVLj*~vHX0zm^oVPj~DR1 zg@uBQ{+F=${X$o<{I6#6S8PY6E>|rhtFkMS9I{ zhxUqy<7@8@ez6wic|{oNbFt=tCeP1_kk5NqP{_vP3C7#0wD3NWr{S5PZ%>iGM~y*D zfCXO7>H8Nus5o^=)KBU29`s2=eH!}QKL~5J=8N>LQcbRpp4Y{A4;AU@H3;<4i2YfE zMe26>)rRp6KL>pqWzA*K`a_`TqQL}g{5(oyyu3ty4{wY9Vo_x%E&u#aw1-7itpAk| z@6E!3pn(+sTgyOCiTuv720gHFl+mXK?16`^HK(WLha>)-1$zRv;^}#M1Lz6+!^ZCc z=(W#Fl!w)ckbf*XBx{ceeOHx-JYr!Ke<{Gj3HpJ_+xfb3 zdF^-}`T_jJ#>-nFPcv>oKKqIN^M4HchJ|{}9!!IL+nu-OR$@Gfedy#?w?n6=jnAMQBB3iXrm?hAUnJ0$jJ@if?H*h@B^GrmBdEEVn5a`>;o|BCe7 zQULr4{l~_8Rtxa=I1#^}b>P>gV)<@&U=Mx}$(2voS{&z5blQDih z>9y^A^w(eXSG50w{?Ur~j@kzL!oOtnFVGPEmB`~_SI8q4bhG{)g?)3HBg&6Yuv}iB zPK5u}Myzk!ew2rPXZ*MJJC5_7V#WDV{Fanbakke){2uqhnp7?hN7+hEHFGJ@1JJ0|#3f`riojc?bSsuqrX9qmFQ1JTK}@!@P{S(sv-27E%aL?eO~5gxjn5NhVsyd5POd3 z!@AH<%~L@yTK*`;!(J_pPdVsosr&>g^xl3YGc_lG`iF8U`? zMd1%!5!?R-@rCyy(ckjf3VJ~wx>NktsG&dbQH?jfeg%0?M0~);BM9*qcSDR9ZaYA~ zf*&=seEIx{GJARd zd+2Liv+-+Uui^$?7U@^@9P|bBB^%Ey=!2n%ZyCR+_JMv9`?4DTaT?^qf#Sca0qBYP z+55+u$>p&?bHtPIr&#}7YomXVf1t_p|M}(e@fiVosfIIhhW2O9ao{8I=OgYxU+FR4 zl>T^vA9IB7YVQ zfc=1dbfEb5QN*h?pbygFUow03o1Epn@%4`bf8yh$G}_D|I0w&C$T4yE1_TEzcKzB0e*K168U>cLGb@T zk)K=5lk;ZNx!MSXFW#0P6derpf^@6lsx1AnflBbS#UPf#E70Sw#llCGW>n)boSAx7HiSnH95BRqd{c;xl+Syl( zF9KxoQaP>y#zTYgaH9S1^%4BFP^^E}THp))$oR#}4gE#@&G22?8uUhd#q`wy^!M`^ zQJ;9?IvDzbmDhkjqBn^2tOkE@AM_1-J_Y()DxcS4p)W3r_Wa8%xqq>^ii*qp3;YEB zCHC@Sd*qvns<|}uhse*I(>MvW=El(em4iKcDzTULu)m?z)x1AM%Fo&e{)B(W^hFf> z%UW$j{e2kiILC_gT=5+8GhOtzmc)ZU@c|!;e`-#HydmEeN!$M$`cWFcpD2U)2>v3Y zKlzA%>jkkt+IARk=oh9xW}JsT?<3l;sQZw=LEt}XUq)*|pE}sP0qqg{{@EM(RM?9U z+J2-RO^CbN73;YdtCpABQgg>s2 zf=OPR7zPEUN^Cjd9@hU4% zzF!&&{{_Rs%kQb1;9vN!?EO14fuAIv2!gy`s3wkw6Y_N>@v#}xmoq^ZseDfJL;eu) z7Nf_fP&9-en&nDk#K5I*ngN7?r6M~!M=3g4}Bx?mwchmcA|f*{f9N-55oU(r}Yo?hJOuyeoC)Tpue`r z2M~IZ@eJJ#eGU0x@0UmY()c@O1?rRN_i!kZofvNot*<`#xnyIpKhLH>pCg`S{TZf^ z_wF|EFH4L3U2r1eg zgMHJWJu<%58|C@WCb*Wy=XYR#YTbc-wi3o;^xv>Au%Gql`>mrf9($||`BkSz=x>@x zpHd}ZA3Q{T-1Z*ieU|873|NZyJBa?{nkI62z1bT4jrkQe{z*$9zZehJpD4`ttUM+9 z4_y`G5u)%13M%BSMhg|^ZX?$JyMo_!u>Vr|`706eK0Z8$3&8W|>9%Me@g1XQH#@of z3gm-!}wzS7(Z1|$ip@t*xw0OoErTj{Lvov=yMg(9<8ej{}A?& zwO2|(?{~H;?(?AXSyKY1b$}xFi3?LVNW0i{QEZ0-`=Q?wOeu=%`RSfp{7m=S&EJk~X z&zL-%hW*hei1u}o3*t}k6XWlYeBfV+e=`K*EuG&#vQi$e-2lH$RtfWC1pfkmL4GCr zu2*%r|8Stbid&2PJZoH7flS?-@i`SsAk z|H{b6l|^~7-e9r^`ma_S-mi5P{fQCf*SjC~hX{p?#^m-< z_=CUL^BSQ4TI2&_S%1QzZOtexEE- zKD;sV*N`99eiHPNR9{EIo|erJ{WI^TSWjas+UNZk{|l1&qr>Qb8vIG7A8#q_*?@8? zt|#J0hR?qcbgBGquAt)XNuJMqiuR#z8Gb7i>pdKxuch*M_A}_cM2zQF*&}|1zR^(n zUhXEBx1E!54gZ1R<8cS|#R>f-^!Ygq{<^DZKVmk5e~};XrS<*29{Nn8Zv#8a<>xWt zk3_^Dv9$b=zR)lK6UXx?{P_^fU#s!F7}mFJ#drq7J_X~Nw7(4WOkW}L%l7G*4@JI` zjsJdY73aNEjQ>{-#EYM;I4oh{-``PDE^n6|VE;ag{i&^kzpz=p{m z#EZ~3$UiXt?@=56EaaQ@@5WHbLtzo0^Q&RMB>BGhUqNr=zu5b4pwBB}KB5xPCi#k+ zPAYEbGtpmNuFyAO$d_EZCi)`_HX|PZ{mAO88v*_90e*q}5&IMl`ZV7v#>+8PRa_SO z&-DA&R%j3Y412#J?5A`*=hg$h@aGtRTquh93CVmy0@jo5ye{$&cT7It6^HSRE~e%( zC_eo$-eVB2GJ9j)8vKp;lU;{rVZ7l#vGJV%`{;}Qvi2Ln&@6&;9SVAp{&`|NBOTP- zT3i!;IS$4NK|YlArzeMeBIK9w6DfbXsC<5{)fd={r2@Z`=jlC>Kalu4uFXIXw8!d; z!2ISTslVZgd=~Q848OtX$UaGYa~A$?BAgJm9_1MH=@{rE)?cz|q&?`z>i^OL@+R@0 zdi%oPl*Bik5g%ts@)M_#kC5sMo9AeMo~SSE5dS!x6#e73O~9`sMSOQ)J}35r=!Z_8rUrlu)<;O+6fG_e*ti3t#w_}@#`lhae|7T~QK1n>}$Y|W2H37kmd$(N9?M95b(ef|pm0id9LFDfs7|9p>&G@OHV*T5e z(vVL$ooN_9!Y{oQ^7q0G{w3s(jpz9+RE+T+L+ft|`zn>U3h*bS^7YnUzP_>QD*1Yw zZtxFoA^$}BOWF_Zf$;;sFQWCQoWu$IAw~}mg}iOD1OLO{XY|t@skL=K%LoJd1TKH%4nv2)v!9U*M9}R79H|*U}*gIBV zA^2zBupg|ytE(gadnM{8JG7TITD%T|d}cx)N7DL!+=qBrVjr%o0J%Gg{qg9H_!RQY z+AD(i|CW_V&&FL*KjLQxTA$_;`U`q6{e!n{@fDBr=z_*|L`mL z;~xAiRKx45v|#XKMm~N$3W?vmVBhtUcW&F_Os#`AZ?$2-3l^?i&p{6*w1y(zxGBi@nfmnnaM zUnTZskV2nqg+J^J`DO3-3`4ww{@S8G(%%Ou(6@hy^vdTCd4s>t=sRm33bqpIRb>q1 z0oSa*)1hw@;ZL#hF5tJG;5T-?Y$EKRB%k}~6~=FYs87ydJ~qo*Z0{lDSvo&h7VFWa z>t_mk!T&@4D1g%AH|PtgehJoN{E;tX^e^=W{4AkQE!fjX$bV?)`%W_Iere+w8%ns4l`kf)yC$@4E2?xOxgasDwG`un9sf0z3N{et+} zo7TSy`co=Tx(g_ed=sPRpRUk5iQ1!rp_*e5I z5r1s3GWbuW;V;A{TN&~dNs9TqQyoEXiGG`e@e4rzSbv_MgS|yQmyK@^f4O{(!F*CA z{29j2r!l|bjd+vcQvm$D5&jf;PWqehJ=`@({F5L3cSrx(`opt^!tHK*t<+Ck^Y-HBmO`>m$kpL5#E=? z=WxflfPNx9TMdT3(TMVYq!8>e=DQhv&x6sW@%`EYpoir7A8~Sd4uyV@&R;DDKXR}S z44?8N;J?BCv+MU?pihvGVDvwD2>cKxKDTRyd?x0@S^4@3{OmA4?}7M&y`NHF9)JB+ z2kY};FIj({IbweDA{~T)J;vq<=)c5Xb_>M&$Oi@EeWI_LzJ)$sB#!sr z$ML+U7|%8D1bij=ZyN~4*7Kr%dj)>2D2Znm7nHB3ae=*%eowG_v|PTOcjCD;AJGo^ zD&*IFDSppdV|;;cD7`)d`AL_^&pz;XI{2IE$3k!bJno73ZP*0=R+3LBaT)w9k#{c# zR4kGAE8tIWiGLk&U0#0nJ=iPwvuwP&wFf@~UnWmI752Z%Kk#p0@0k1#06l|2Pj)?9 zA-^8bztZ{Cd=ZeRm12J?VthOz#QxqbkNj9?D?|P(p$+PvDe~7kKlmqT55vxnH>TIP zOvoc^{|)X-<#Uf6#-oPV-Y4+eN=bg&>4n^%@3LFYFV)JzKD875)$qEY7yNT(kDh;r z{(%3%Mm#=t4?v$GqQ6iH`Xm(kgyHuBjOroLFODmq&k&C?`e(TUe~EwaEAZ3I7U|ur zmVAEi{Z)B9Is*BL0Ll8`mlYu|BOpH%zdpz>-uhYex2($`zVs2-Q@Ft1NxzTIhj>q# z51oeiY*BMjzx`;4ecn~;Ul#gG$(0|O2 zgwpaV@R#&^W-am^Qu*{l{ehDFaE;3758?qv&!;CLpNRJu{Xc_o2F|lGe2-FkIP@>% zgFWvw8t=niGksU$2+`LKkFyv-`+l#%eQ_e>;?RN zMjx`*_~!(1{co+7^6$TAe3HvM*)*eKJy9O2!5&DzZ~kNrenWi>|8c%@{qXiSo=fHr z{eMUQCGk>A#25O%M0;9i8RXGd#J48+@fX+!HePdo!}!h@%UAme`b`oKgknBDRx+Qy zxv_k`z){8b)JGNm)}@}{Pf7d`UIg-r{2Aj9Kjagn>-pmc!2Y%r@#z8jg+O02dITU} zEB#*NCxv_u06tRrePWOLB<;P0-BnBc%~i19QhEM@_N3oCb6RbmxP*9ZvOX+Huhnwe zsF?T|PU{!kP8+F9()Ec+*6Wgj+qH^Kh}Q*&g~#fIXU3msBL;?%3c_Mz2BToxRBb0( zL#x=ZsG>x@dNq>l2#&P15<<0!|v^yqSNsyz;<90tu`)2uNz3~GxkJmUF#V?NE;cZ z4{NGzrwxmU&?P4enE3-WlMjRRF7DdaDe)1c!`kq$WF2eF$P+_j%6GhZN@@EH9H>py zB_$^i8tVsXQ=NrQXDd!CYNU^e9~AD=$SXEQr%y=GM|tc0!T|h!aSZpUhc3|*JT zt-ww~#X@;W`)2BILSFMqNFW5TaZQm$z&(B*%FjvqwNS_oF-=}kMYhmcbL3L?@+6+%yMTaFN^HnDEF9b$QYjSnz zgz*HEn=ViILQ=WAVQ4**lEMc0#E1iIloecEf^>Re$b=VSb#c0Qy-%Qv8{-`QevE{Z z+7fQ(ML}zj1kcE@MCdkcAD!NCFL+QQ^j3El58Tv-GnvuqaXXOljKJ4K)A2G8rjGyt zc$o-?vWwS72zR=>x+jIjBP*o?XN<+JD&XQNe=8e4Ao(i^y0d*gy zQ;E2`dPpitN{QFU#OaK;@8V{7oRpN1M38Wmyc-jr3Pl5@o7Bg|`;8?}QZhTn!G1ZAgKxlK7C3X<5J3zby5Qp0M;EV4iiv=h!yQ8ljSU2+ z{Np2)F2*B7&6sP2x3=Cf$q@;ux}-s*ITvu8Up(%|N0@IcSZiP(34=qRC`V*N(4~=J z+apH7y*3p_ zAt6btPhfIx7!{>`Gdd`cw#Gsggo~V0;$!-!=(LIYB)?!SF~yO(sIZh+(l{|lenB2u zA0ODiATJzwp(nnJi5eJ@vLRxuVpm#mBOG~?YHm1^ba7x33FY!02iGvYPhT)$d-yRj zx>(dJ7xV@~qh~Hra-#@%l}W;-$>DYr`bS6H2OfvuBqj^K29Hx-)7(87?r9EEfR1=Z zMkc||f8S1{~E(5Q)LdW~ot;g-2>G)0xPX?{G-wGJ!|8kP4O%~nkM#GKX~Y7I;n zfcPcSTy?zC)g}rBbEcA5qpOQRr*OdoB!nWTydz|qRR~pBW8nlZNSmuxn+$(S7s=}( z7gxB^ZDJF`c~>|<5{;lBJmh|OKQ<;}kdF%#hY&iEO56>VL?po0`{nSW7-M`x>tZF& zCOaU-(OfB7mrEldh-j4%uMdlX@r@*MK-JRMqsnTL6PMm~aZT2RB}GIFTxHy{y13CY zaePD}7BU1Ph%4N+{E%krL!zJ$O|>KV!r#OQh$bOrLR@1pGa$~x%dUJ-^le!16&bKF zGu#5cF04s&CHXpYS+YYAE9enLgKfXoso_#VQ9dAiXHJXg;z`1TihcV8eN0pgoO?v^ zMDZf4KzLKmRU~Yd1udYd*$(;zITP<9n5Tv*)#2(PV8XPAHeQO0Pmo^GG2lgV9cKFm zugy&7Oj;<{ru0PYI6%LMq9$DRgN}bUpJeN$DI03)(gJbrV%At2^8*u>|r< zt5@m*W7M<4ROS$nka9xYM^~y;q=P(HI6JvP?+fb2TQ}*IhZ32*vrg zEE`fZ#ka)A+Q^9@W2BcvwL}^rZrQ>{LB&C2`SDXVL_t8j5ckBJ;gd#*sn{2+pAojdz^=Nyrx zq{Kvuvj&F9Hkl+El6`zwXv*udY)=h=I-j67#L8k)f>pzUXK8*hH~OeuLL?tDnq?7Q zAH~s<;16y0Aib_X&?nxfGKqpdg#%G}ULjZ;47bNSo;0I;X0jUUNfR9}&E1$RpJRW8 zJdatyfvhop8q_)_7U^PwvY3HkR>aK^!xApwvqmHx0@K3>Fx1w_OA%j@x&LgWbEvOr zVoGu}rjR1~5wD&1cU>Fvp=eCJo$s!CUt+)}3sIpbuHV-NW1_;54afsH%Vg{1O*mS)l z!g9@sjr*S1xXPo(sTVWb(w1^g2l2h`;IQhrW~ekdnwiDNi!gL3#rwUo*g#Vbazd zCZroum26TLObv)6m-S8c2njtwuhYacF)vC+G>M;2M66GsA&>BleG+s(@ivVlm^x+0 zflhA=`cN^+Wr$(;5Jx^`M|C0}FtZsXe%e<|R4GytOj&=+u*v2yMCIzL?IKLl@R~8N z8gL;CE{N_4P861Zgkc_tdLABHZyi$734{CszqvU~$fRN}sEM`{+%Y0~bTIy#UQw=h zX+nAyuN#0#R?L6LV#SSNy>nnPS5mXw%a)tz2bkXo0g#7B@e;l)Wy?W~YSN0mFoZ#m;&o zC?D!v!pTn@M6HB7nMC&^KBk#;yiW&0sK#_g8ty435Xo{3xMYHl+Cda*Gu5CA;$*}( z%<%E`7-m%5Q{uyt25Aj*8{w%wD8Pr8K-$<-8hpY}bkVj88x*d?GCWcRmQaLaji$7+ zR6k6Vh}Grdg|?Qyz_(xtN_-237YtU)km3+KuQ1Q7L~gx?WF%gQ*>}mo?6Z2Aw<@)r z>{*V+j}mk}G4uN|{W6NP|NI8_Bdesi19O?kf;9gE9yVN;VqpA@kz{ zd9ozfbI&o@3raQ`)mhwDTTIwW14~0F1uxQ69!t@iuQU)?R-_Ex@~TA>MnGthw*ith zPbM2>>`ioKZp!B}=z@^((G{N}C4NP&Dv!Vnu3-&8uHF!rAc-y~3$~18rua0SDOHl| z_n3Cd;ROk4-EbCK@myb_VECoccmlB~TNC5*LH6QQb zh*%ifAXt3*I4V&amFTQZPK<~(;7bC!s6^5iB%55dm~{6}?0{fTz$BML$Pa}MDZ8); z9iNHShNTSjGhmX6Duu~>OSsRFca@kF6SO7s1f&a+i2@=_dBjfA6-V&f__4{&xVSU{ z@sp9miqLEEkpu5|89HStdhkQ~;$`Xsn&t+U49-2#8u2T%4GJZfLyj5zNa{wqBIJTj zjE~XBV4h?!63C>hTHq*tQUY3F>L@8LELIzXHIg|f*@Arw+iz=O`)v{G7;%s=KBklY zMn+E1&vG~LYaJ(fVC@3W)#MQ75hkV_DjpNk4Y}II4XKM_5q8pis#qe2Hg5GSTrRm21s6}dYurxuNXL)CubmqeXS15LV zjgl5?owNy2G?N+?r^jS5|B)NvZ3}g{U!Z)MqK^;nqaZT?dq$UG(0mU+o2-wHNjA*$ zNlja}RmiC@)YD5SGZhd(>`z3pPmW@Nu-n2M#yq z>l)@Zg|>}-A_~s{Au^c&)yBj}CBQQc)-p@2P%bQUATqX6J0vqF-H2*3@wQ0;HI&aL zVfc@t{FJ?jhal4I4WkmmGA#7fT=)`*7nM$fAuFgzViuR7eo>91_u#RozEtzHcD-5nANhQl+fl=5Z?3gIOkmMw! z29SZlt_gmA?csO0cwq54OK&mfELh8UUYMZr@o9wbwNvn|gkh}^5?z>*B$+NR7IRyM zvpnb477m{fN_=z9E%$jFkgc7tm_Qq=i}&ekST`vO3-5!N7%>CKV;+pHAJOrP2jBt2 zrIcrM$V{O&CJH$m;mdVocMO51Qf4unG#@8qY8q*g;OvC;YG1`2O(N+^7M{YXG)YN6 zI!MCDXY|trF=>=jhsnB`Y{^Q)5K5DkE|xKcTN7!vkAANwB*P3$iVvm4kIN+aF*km# zi}3vha$_d?kVkq=SUF|z{VdW-$mD_Tl62%V6MX8P6xK)J8jE#0kPtJP5=wk!rcmHI zk})7Fj3JPGHVuZxL(a)0H~C*=CZ%af#^_(OsFt$3kdKv+MZqBG*jJ}j_;jiv>yZ~3 z6wyu^m6&fqmLE|c7iURAqZ1aN%99#2>%dPt@YBQ~9Mc=$v;qz@Gx(8-qNYrJSr%?% z>4)(=1CbYKMsd|O*>gcrG>26s2(tvGDosBn9NmF!@cnMP1N!~}~?xkF2N@MdK7nZ%5 z9uQM6OWh1terk^whg4yj4(63<8q2zw&EYawKQj(jTQd$9bN6%YaOK|3@rVl>sHJ;T zkoDcJ&MX%QucwbbTB{>l^a#!mJQtMq>kIV{iA66BCeAWG@G#^~iMv5kJ3>}4F07wU zo6QvxC$vlqBkB;W17DHJ=`wQ;6}(HqnR3^{?Nv;BS=Kp({Xquh$U9YL23_#7;d4`M zD*D*emp0*C8*&Vm;nvhJW9modI#WqbmS}q+ZE9BHc^90BgXK~NeSx>>Y9KR>NWQ$c zyb+0tHR!Wk`R9hxRQmx{fvqJtyS91ZOTiAp43V9G z$Sf~83e4`K4e~aqIH|4V31{9qQvZhRqUd7jf=Q-TEp1X_MJ;{R`M-DgD0ULI$!nid zWh#=? zZJe8LoF!qTC2xJb697K>N_EEGv|Mgn;Bm7Ei}~DrG2hOACNE{Z+$t4T8DldHim-IU zu+=EvM(ndJKLf&#Gt|seCE<}b!(lU|3u;k)Ze2GIQ z5fpD4R_YqQj#cE_%`LV-rcVgTb1m|1jUd5qF#GM!2&nLFl}JLUZ>)%V+uw{4h80t7 zG1y&#GRe1R^@J*gIS)Y{d^?up<5RLggM9=Jr$Ms5h8XT|Zi9te#J#sX+~RkTN=!-8 zX;Wj8^eJJnz$rNBn~@^5bZHNYqW*!{ay`|B1h&aB!p4UDb|AtEBfdFii%o1F&EsQv z*Ro*%*Eu!LI0}S#7ML1g;5=dyl6iCH?uRAVf+e6k%wvvbU<|(v8N6~?F+r5H`q*Ti zG00}Ega*D%`o_m#@f%Um3d#}d%I`9&B%8%JV`pm7g+Q(R{~91mxPya?KfpGi(U@?G z)b`U2qEk<#>%w;yuFi&SX4-`5^+CEM6MOl?xz%*!|f1Uthsk09*k%AS#b{eT(3Kn!!=aCS2$ zCeT03=$1R){FcvThzy-1`@@(ko6}z1*;jUpn==|du_wteW1E>#eE7X?;^T#XM__!H zZX?a)v`M$}6~t&{PaVD;XhJji&_W_pWc%L^O)RM=Few3h_~^`3=R&sMCjG~MZs5z3 z1Y)u|6veLOqIM6lbgmm9yTh5m*quB#PetJqUo&-J{|$cMZP*baW9Dm%Ae*+wlD+5T zyQ^gzWp|+@znP!uz12iXpb{?KRV-Vb{BB&yf+`R;7R$wk>Bcn)GLnX!LJabOZ>HII zGEjfgiCD}#WdBs-Yj#aHfsL`+N#x8DsJvn~BPviOo>2DkExfbQFBdy_ivyzAz*|{a zPWyL@6|o(+nITrzl;iH*v?^@Jm19+8%PxLbw;Ux`s(h83aT~?Z++N(0&L$>e4;&&S z<|K~aB{a-X)V#<6Q$Oe>GxhOf5rZ;0Rzw6>wo$mUt1gCZ9d+bGFf%MCZojFEG_@0= zvUb-dlD#+aMOl0foBNt+9=eq5VkmSd*XH%gf@`}BVs8AhFzA8qma{>cwukc zez_QvaG_j|W0ITt$e6JDaxo^fN{h#YHjxKoVkriOF)@xpPGb_F>k}3+C>Krwuef6Q z(z$jNIt2y!<)jOfl9OXF(tHFg-7p*rRtyWIWIKtI2P92rM&Ss8LR!!DKsKVnMbI%j zifG5MdPwx7^*tpqRFyi}2oDaa~a;xFf<>jXL&s?99{-JB@_4b7OP zBDQJC1r;sF!iAC26dF0vQ@LHP(rWM?5Zf)+3=4wdMs}@L*5;zzwAOr$^nW{<5h=l_ zg$e&dOb{!7Q@f@%;saHokf}0a!Vl|u68-S+)|l5F4SzQpUa#Ig7Ot$Hp7($Ce30Yk zuNUUnBm&F=e^P~^kg_uJ@ix){{0I9k$=FN*3lZcEkd+DFh?8M0n`7!?+wM{Y048~-KJ4EMP5QAeEQ z1^)j^Qa<9>c3I2=k{uzwt^(=q2wzu&VW$LsS7&UaNOMqzWn<=s$A`|>k#`jXBLD7jLhUsYocJSb>U2^;2GfwKP%BxOs5(lKsWEb2z?)u+imnD>^02lD?T z%Z$GxxZisG?`rbT_8@uYE2MZNchV&%#HN}Jxdcy&5-=J2*9vc#Z$dU=hE{StY|QRu zYt`)7OT`$FgV7j zC<({&rZ4pS7n%S3pYVbB@3QhjF6I9?0i;mwxt(lwM{e@e5NNq!=~GTSawGqZpo;~a zIls`>(ii;53zne7w_tcd3}e4N(})kP47nom?~w2bGQatgK^KyMT2hECY7#^2?8#Dm zTxEC_JGIfe^B^TE!GV0ECL;QukmbMWg9nyKN;X0zU$NvJfk@@p^b~7E$;as8unHpv zHAbQYsFb{HQ>IDsvSN97o|iQnVs;S8P%jo}Vz&wMZ=eY6#M_1?e1vvJsLE1J#^iwP z0FoLOi!bh(Dq*UY&MOFs9U;q3vY>`J;X(7|LPO^-VY)i<0$s*Y?7!w!Bs~X!I znC112HwdiAjv4f;Z!!VGd>O6^BLK+0az8g4{zhj@5Mp^% zbTnPU?@4x9P<-2L6gg1^D8`LGAd*Bs*7qaT6E8vjj|{Stge<%j5}afs1`H8C!7)t2 zWHW1)=WF>)j9Fv&^>rHbY;H{GS2km*;r;(@|M0<-T&t; z88rS^wPXqP@F|>NTfVI&=tt?l#WlJ&ZVss?<(|K6B2k&4wcLH12$c=vDB%> zHS^wPF7^t}p<>JT5ti@UpDly^HG^XL9>a1ZBzp{-?=Fazo8)=kB#kBiLBN6(nUNHF zPUx6V8k3;w+q1^fe|w||bH2HLX;x6Io48^I--GwSzP%Tctc}++QR#Tmuvr!+<_RrnKANU;n9GnSRkII#kWl!F|0MgBgrfhDPqMdvvboRA z4byjo0OB{EE{eSaKo3)%nz3w+$p2=>RMqVLfmE#&v4QdN}OT9KBNW+freGHbzdiu`?Xi;k~MMw$87fUVey2tlP- znCl`&k8X!7>|ZOPuYzsOC}kt@k2G0iRx6u$*TBW1jwB_&IniXXX3mpVEmh8W(r8}l zXQQK(7Yf>F#ZnC|aV@?f+_q4Fj9_ItpOa07co#t$(&xDTNwEmbu-B+!jg#=Ni@!1$ z6jy=y|F_G)X!W2%-mU{fGQ*;O*3#JgzXr;2P>dxsFiQl@MnYGMkoiy6gUM0IDV%2i z@6Ma}$No52gqvoPFXw2b?d zL@!`kw10|@_@8V~JqshRZ2PR|!B+PcprG9E{;T{2BVm>FyE)P1e|se6N@i`r+S>Lc z^fGK2$)@#94wOalr;oTvWn<_E{0|Zj@{f}9KFEW}BX+^RI3$6jx#&nbjq>e8B&H)N zAwh4Tn#G}Wy- z3c4VdXijaEIcD2;96`DuR-nPsR@`ySJmcv-q>Xcn~McV@cbtFHgf8ZP%(!I_S=eCSHS``km^YjdWkK_efv z72{N)1~$r+;jY_;HKZSbO}_JL=V6uqJ5?^$OAm7W4{*KlfKN}(Sh%<=${o-Q_$@~7 ziO{CXfoo<}3wTmk)<rmzf=5weK?!UTlem+3hP3)$$RD zBYQoBJ0KLql!vv0Ts~t-?gXROuk;8HF?yI2# z*@jXbHzgU{9|lr%)(9`Hkf6QlVck!ro$WmXh5_i-=>WQ$M@dBy`htulR zcZS%A23X16L%#tQo@Lu<|Mkd{^Rt*!OS$bPt<)}cm3DnUVIW(j7XYNsUe5Az$H3>> zb&hkETthD&1%};R(EEQm(8*ZnzC}90;`$xxWD{#$OV(H?!~Ge89dh@3S}h%^n|JCp z-WizOxqOs4t%i>QH9?(~D434D;#^$;iY zP#-8g+(?hFko99pW8ie+Tc5&V_r4icKXvXrLUyUuG9$I0qwCWoQoq-f-j{_LWR@)= zSUbz~m85EX&#A}k6G3v|v^pSoc40(tdkL`x(fgn-q7r_67_Y!&4R(SvJBrWi$J;MB zEULmj$|@W_KW(O~{87hjidk*Oal$@+>1B?l13s$N&waaI=;fU|YoD-YnD4Nu4s6b{ zFO7M2O=!NHEyG!|?2{j%_wN~j4488b7XSaecMUG~0J_)U4Aj5&!7~L54YNBsjBCn} zTVH$=MQyT4np;H=;hqMnJXnjFzi>h78&w%sWc(g#Kr#>Sqy{A0^zPX#eo7aVeE|tV zhi)I8^v1catw3__M(FXm+qRM-x2qUK`Hp; zn>VIR^=Q|_gy$))D`(fny5P1mKz0chzNriooLhjW|I#g^ ze$+Q{3_B?LrkQ=Rd0hVhTZ`>> z*m_#Y-<U{6y)WyCJj+a&k7L_HLT#b*?;yZnd<}I$BH#@g4j|be> z%U8(9Oz2kq?wR7~&7NOe&h$m{fV}ys_~RJ=K`((HC+{uyUOrTw_4d(8?@RTm__H=O zC-*Itc!#P4wcvodoV^I233HV>5|4en4W4!??vp!t^t95-C3u^L=a#~Uq_#@h_LfcI zqepw$@JL()&A8LQxMS<#`4HOUCF}Xn^gWUl=Y}nt{ugG`LXZCoZ!A48C1^T&yxhS7r0P< zLz3+-2g7?`RpsX-;c0TT1~RZ;W63d()W;U=_g7LL&3+Ry_zi@f--Qh3^InI_z0jUv zaxAj9eV|v`g|yaJ?pDfr2vNyEpE5t}x9q9Tcz>-A;*=(+e^d#doR% zM@X+~*p@%@teW{%<=(c#?uSZb6c$u3Z-JF-3Dp=;ryiZtu@e2y042$CutY}r{|Da~ z%2<}HM@wMZFmRt4%2T>|_XJ+NOI+SDa-Q*+-z7ui2H6uMAdhevdGFnw|l z>_#C!Ih3)yIec{}^{0oIYc5Z9Aiero0X#Z~3C=hQIVXLI&{fkuW4yIZ!m!y+EGB(a}8oIEw^#3HsInxKI6~+wj@dv@i&0n;GbgGJm{- z8@~|y)Gk_h&NMzdADH^}>swW!x^$2_WlA?-HNqA|J_zl0a{Q)h7QKD6DwKlgk9&N# zs(bjA^*rc|nc-uPqjNk>J1AP|wew&FU81qR9o3z=@I8g2srVzYJ}TzRJ+M1-9XG+5 z-5E23_Wo~RV6A?VQd5$iee6-M_k# zi$a28ls+Di$vr+(YGzUap6^7T4PTzDt19*^it^d`+WD!|y}z|2tq%FyqzEXN~5!|T6mbtUslWBo2X z$19#Q(CwA|fjBrcRCN*A;2+m!yf^Y;6_~86ZQzkx)!c=PFsIJaY9{nx=i^9swtEuC zKlP*S;lq{V5$w%bct&~U?AcX|7pDv7V!FWN5Z}o;byV5hVwemRo;ZORN^2u(u=`@@~#I;8~h)8DvmDzUb zW8~|K`u$=L_leE)P&55NF86I2a!+qtGBRp9>~;*vv-hnSS#s@d8%CyEeOoYcP-pjD z7QOAz>3z3E4q5hmP@kzfxjdv7l|yj%?KE^(y;aG}V|KT#KdB{t_ozPG_K|=m_Sx98 zJ|XR15&uK`Oe)IhfxU>%VM|G-tTxql5-FYC2;x;f(~g&n#p~lX63xD9epRjaFeQf_ zB);uxnHs*`GLpOBayRtmw36NSk=eTfcMW9QK<08wcgo;>W0`X6%~J*5mrK8vf=#Jf zGH+hE74xm0ey0?wdSl+e<mN$6XacGG}sxV`U7=vOvj_9pZzqu%x> zgci=hu0MMevSv6y`xLV0(Q`Wiy?M8l_!+0+YtsRJ+)Y=*W&lzCaXRaMh3xXb+&v4~ z%d!dfEo99wz$jf$eg=Ge)=!Rq8KufTK4;p$(5I56_b_;EGlvarp|jBuEBz=ux2OG~ zGxK6_=e++fsuc36f9MK!(`J94{P!z&8{_2i^uJH}gxUenrySfX5qa*alOvnYSE~+CkA<$ubP0eg3#-=wj#iz&^Cx)DV{*XU1nZNp8TV zo15gpeducY9^Qw4z#Tfr{(Xe2Z`t+dL3ijJ8B3(Ti8G#W=IKa|emHRL$35^)t-Z@C z{!!jbmcB8YavZ4TqR9wkhgQ(1$3u|2bJ#}SMkb4*q3jD-v49y+gT{v3Uhynp#=IHFN@lzEG1dU`3}Tt1hB5_$>=>aTrx zB;60eD1m=@JH3s+r`)WQbXmle89H+4wm{FV@*yVZJ`#hwli4k_3HNk3d@dwKymV<#fphZQB=&QgK+kS4Fk1rx@?l?pBnR|` zKF=4rKem!^J@EC~O*dxC9r|$ML~a%7rm24JHwm9(?t4sfmmOwbN$yhnx0fV*KC*wL zs}W>5z@@`O$tv+yxH+y8&v&jDkOR+r*@#U?>_RKq@%{Ik^ zBRTc((w@&Xviapc%zZM1Q+72r)5>M;nn^ERVzGMeg0sqJ*VT>g`TdeUuhr(ZeJZzs zGZl@c2Z0bppTR`Sy3{KqAW_# z{C203*rgM#Kd9v)tvm6U+QU_a4J@c%}`Mp#vbxUbwZ8i7o zx&48o#~fL8_F?lEo>Dpg=&DntklmQZP4Nzv%FKuF9v*i@`RM(pRMl3_uAY3>BAgyt zJF}W+$fY*@O`tBpGG@Z0+8!*E7Eh_HI%DC2simb$`02YPXPCo}oK}9+f*A`dxuyTe z(c|!Y>VhRW(66>~-h!${Cvtb(VOOdnZS1w*`KY!I_ zIDS$xWz)&L?8~x?jo*~?Uf`HErJF3fld_9%0{D)p#qQ48M^x2TFT}eYS=q;TvF9M% z`>* z^XHvXJ8!``r|h$M;XcWI#_f|FHKu6P+{)SG=j@j(;?>2qb7t?oH!qLp&$+W_moMh8 z(PsXfQS;|5IIC_{<)SmHPi5MEro3uSWo@M?FFyq*je2{Gl`h9CLEe?B1#>*wB7m|_ zLrc$6n0?P$yl7vr?R(miGxjC#sG?B|@!ih(Rr{W{sLH#7k{pm5+rG2s&qGf;VBhM6 zXH@N5GjHF;i)QcZWxnqzXU&^GhrbUnhfSVbzTZCM%waR89WuFm%syky^@_X`qQ|7c_t4e|5E>=j__fcBTX3-^SGEYs2hK* zo7B`DdS8k;osyE{M7VT^F5RJWr+CtwX)0zmvp29b?jCo2bELSm6dN^>DtGEPuaY}` zuQByssFCjTb>48>{nHSVY;N|zb;>;Z#rq*|keOhAzkL7c z2+U$;iv3;F`6GlhQFE02UAxycxIfRFjNjhBhCyaW5HXAS7Kd1jTO4Y!z~XR=35z2v z-fr<8iw|0S%;IYn->~?O#SbigW-)e4s$4@X4z-xDIMU+S`%`>labMnp#uO1Ri<*hV z(x@pWmPO1I;uTR-Lfk9Q%p^XVXX-3g5KlIy+TtAIUNKWcJkywSEk0$jmiU!Cvy@m6 zH5U<&G-e3OvpsMbf1hv6<-~&`W(9F|o@pQ+X3R>9jTW0Mwpd(kvDIRS#SIoaEgI;7 z^71XlEe^L>XtA06-!Z0uzkwqxCM}M&SY&ac#bS$7EGAe#+`o$Qu8NrFh`)=PSBO(% zrscjtW*fAp)xho15?>=e7&WcL?TvYZxPvhr#J!AphnN>L8;IkL`G7dVm`>v2sQHX| zAOtesL(oov+Z>!{@`2j|<4j)`F++*@QBy#iYs_%seAJV8t}%tgYmFI6yxEu}@fKrd z-8#tZuyw>#5En(w9AZ_}R1;$nGoM%zF*U@W#Y`>n1GF3B9x-z+5mTt8#3r-@Vr|qc zBVs~vIq|-TsV5#1HOq-}(H@Am#>|bx84=S!9G7QqC(etRmBe%M%ss?kMolB}S26P- z5!0L|;=-tTjMx-2&BW^><|*RRh*?EED9=1cJP>vv{uy>6-j`=q6TcTTuMvL`F|EYC zBjyd_6*1F6Y>b+Bh>t|f1|nR^2gD~~AL8hk`HZ*$3b+lpIqXB+3HBj=J7R_qSH?`7 z2+uQ=_zCPp{2lB>d?wE%h?_;s2;$6`DI{(Idl9#Uy@>xZW-M`^m?rj$6un3=@mVy27;Pdkgaf7DbEw=!l9aclSi;@GH}Ph1)` zHN;=$nOY(oZyoXUJaaDbZxOSUcx=R6L_`NzM%*G|E+<|M`x95h%yQxnVrB(#-#l|8 zF(39PJ{~o<6K{=}mBg{IKk<(d(@5Obm`#0kVhV_dN6m2JSrL;U?u7nI{8iKx5_gQ5 zk;Lm_CP}Nor(Eo|M7&D)kFs6ohPSn&APm7v5;uR5dE^$}%f8rm||A`;L{=`%A z%;m)GV1MFn#w;i9Zp;ee`LI86gfR`ouS1Q*Jup5H(ocFwe9SC!qfmH$+Sa@h;e( zcr@%!TpKkX5I;lzC;k-nCw__d|)_z&J$wex4al zyd-K8#0dI7aS8fA@%V@tN!%O#pZE;=Kk;Vtf8t2kpEy3x6cabX{>1&!|A`Z$W(IMT zF{Q-YVSnP&d8Ul`i-?&;{4?xN+z0)ixG(xYu{O`lCnjNk;%(^v#IHq69q~%opZGcK zPaJK`Ma0AN%rfF#QFA$Qj4}1ZWqD>f@%vG;g7}^>Hxg^2rh(WLF}D++j+&K3#4+~} zzc8keSPJ_SKaZLw;>w75jQB>>G!r-GnWu>RVSFZzh5d=UL`@6vF=JjK9v3mIiRYpJ z6W@USiErnbH;CgfJ`?wZ{fS?|{=`e*|A}WsO($_%*q_*e{{J)JFR{W#OrZZ0%k#_- z;v$UG#H}z+6F-FiCl^v@sfy{LEIny-$VF+;=vJ9M!Yi5%p#tF@t=4A>`#0*&r}n?gYlo( zoM&o?*TDY7Z^Hi*-;0`aiF2c7DX{|mpEv^JKk)|if8x(!f8wREKk)_hf8vsexsmvL z_qtR^munAeC08`Da>C2HOv&PV?zZVCGne+~N+Z;P1^i1Q+*leiA?8Syd9 zCGG^?kCjv6U!o?Tm>)Akh_A!`#G7JfDDmg;|HOM^W;pRh^nYS8{6FzJ_e}-;J4x#L31K6JJFCC!U*UN{Dx1{3m`CHKoKl#Qnt4QBy`d680xf zLH{S_=b1Ug@4^2QqZt2*uOt2=-WD;n#3SJUiT9xY6Q`p86W7E3#6#i#iG?w9Iq|SO zQ%`&j_9xCn|0kB9{}W%!GY!OP@c+c4^2|zN8~Q(S``%Nhh5-xsDaL=|^{_v&6#bvLXVk=rKSKW}&Vc_X9)bQ(d=>E@@iX*) z;!UtW@ga==#D^m$NxTO3Cl;Xp6aR{N9`St4^N2@d-b?%#<3Dk;JTrs%6vltzQRx4~ zPS~G#f7Hw({ucHpmZJX?zXktKEQI}uN5lUUzk~6gcsJsI;xU*P5--L)kobG}e_|#2 zKd}b!AMq*JpLhoBPn?PVPdqAWZX|9KGY!N)!T!XbBmO7O!uU_T4e>v5C-{Hj+lc>( z@4)`Ve{=_R` zf8yvovw?U#;y>cm@c+bS_ycy#^aR%al;(@R~ackJ0xJT3sC6>Yd#DmfQ zi6_AS6OW9V5yUq5f8vRV|A{A|{}T^J{71Ye&lC}-!2c6xq5l)Vjs8zOB4SF2-$(pM z+y(Y0j*pp{#KDOFh*6CH#Ooubg7^sRPb`Q1i9<2|6Tgmi0HT5aCsx4!6DwhV;wk9= z#M!Vv@p+8@#5tJ%5epIjdDkOmIk5`%Cmsv?6X&7-6FV^fBi@YhpSTA7pLi+yKk*^- zf8t#Df8r0}|A{BV{=~KD|HMBa{v(cr{fW=P{>1fp<`v?pn4c39=>NpOV*Dpw2m2GJ z!2c5s>`(jv{-0PCF&_{c5&siUMgJ#uV*I}w_>ZWG5v#EtL7a#9pLh$_s)+aInW4m8 zG5!-@K>SDiAYu~4)8PM!&tkq#JRSBYz8y75;u(nlh~*JeMEoK8Kk<*SKk>I1|A`l4 z{3kBJ`0pX&KjJa4Kk)-&%82L2%q-#`(Eo`~!~Vn*Vy2q75b-~8cldu|jWM;v@1Xw^ zUxxoDE`t4u6Jq8f;$rxJ;&}9b;@ueki9bRACzfOWN4yE~KXEDgKXDbte_|W_Kk?hJ zKk=13(@1;?_9uRb{!gsM{EzrO_1OY{}X?K_>Z{Mm=@w!F#Z$w zhW{syM*K%CjhR;BTJ(S7y|6!VhlqKH_#4=tcrENt+z$PpI0W`5{s-~jJ-{ar{}KOz z`5*BD^nc=onEw-p!T!Wr*q?YG=KsWtV1MFeF*AaAJN!R!9r{1<--!Q-@8_AZ#3#}J ziGw3%BJoCy|HO;o|B07i{P%vt{=}2u|B2O@{}F$H{!e@W@gK1!W-5rw(Eo`?ApR%* z1@S*|B;tSKM8to@VVM6Be~9%z;wr5F5HE%QC!P=c6EB1RCr*R^C%%dPPy8YLKXF_1 zf8yns{}UgF{fW0={3q^?{!h%q_)i>y_@DSmo@pXZ!uU^2p#KxEFy<-Zm9Rf?CD#9l zWr+WXS0Vl<{uuEeaU$0Lh+n|}6Ssiwm=m!v4fL82^cvWBre~ z72>~pf%O>wi3cM7BaXuSpEw`$fA2cx|HOA-f8zD%|HMOMCPCZ=^MB$2us`u?*q=BE z{-5|$#DBzV5dRbRgZ+tj$4oJCORWD9+u{F-tKt8N+r~^O@s|-ZlURxQAMtIB|HPNk z|A}v6{fGE#*q^vB;y>ajjQ_;t=>No*(f^6pq5pf=5&scS!TKNZdia0hUFiSBdFcPd zA7TB6_%Q5GEQ0-sf5P}rTmb)1d>{RvctXV7LtKIRKk-)he_|5-pST45pLhV)e~FzK z|A`mI%qrqHVSi#V{6Fz<_Nn{_G_-)vq*a7A4|Z$NpOBmN`)3G;vAO1M?xU6}t9pM(D=egplV zxLM3RMqG{gKd}<=Kk*^-f8smv|HMC{KNgRAd)5|aeg4wlbi`UmwZ~d-Zr`f)s@D7s zkGI!6jPd*TSFQQxvzM=_`S`JRM^_77A#|D0r9zhoT`Y8w&`F^Sg-!@vAaq>le4&ld zogbykyg}#=p<9JsEp&^}tAuVAx=H9pp;rpsAoL2M>xEt>^irYggsu^~TIdR)%Y-f! zx4wrLN^G#Lg;#-mkGU8=sKZmgsv94Lg+G~ONA~Gx>)EUp_4)v z3Y`$TKDggmC(&X zHwoP+^h%){gkB+Zz0k{qUMh5*&^1C=3tb^}nb4&|mk3=fbdk_Wp$mmh2wfm_TUJY zdV|m%LbnRNTId#`R|(xLbd%7HLa!9MLFg4i*9*N&=%qr}30)&}wa^tpmkC`ebcxW# zLKg|06uMC8gwO>-$A!)p+6dkGh3H@C4xw9xUM+Ns(5r-Q7P?93Mxj><-5~S|q3eZS zCiGIF>x8Zmx?1Q8q059W6}m*|VxfzKP6}NpbVBF?q2og53vGn%{9N=ebcfKbLa!FO zMd(#RHw)b)bfeHKg>Ddfh0ygvFB5vH&~-xB2wg37h0tX}mkM1Xbg|GyLMMeT6gnYv zfzWZG^My7-cYY@N7rH~}R-so5-6HfVp__$n61q|7l|nZNy+Y`Ep_d80ROmXPYlN;A zx5xQ9DBB7H)7YdyaxDggmC(&XHwoP+ z^h%){gkB+Zz0k{qUMh5*&^1C=3tb^}nb4&|mk3=fbdk_Wp$mmh2wfm_Tcdz3cXtB z7NJ)O-7IvI(2YW`6uLp^6++hwy-et(Le~jhBXqUU6+)K@T`F{m(8WR*37r(WQ0Rov z1wzM#&KKGU-D%gNz4%|~4xw9xUM+Ns(5r-Q7P?93Mxj><-5~S|q3eZSCiGIF>x8Zm zx?1Q8q059W6}m*|VxfzKP6}NpbVBF?q2og53vGn%v}@6x{)O%kx>e}aLbnLLO6X>x zn}lu@dZo|}Laz|IUg%{)FBQ5@=o+D`g{}~~Oz2XfON1^Kx=84x(1k)Lgf0*|E_A-o zM(9qv7VYU@=nkPCw?zC&sp8kdI5V}?9)k3!jy-Mh2p__zm6ndr5 z4MMLFx?bpILN67%PUsq;tA(x*x=iR&p-Y4=7P?62q|k*zCxk8#Ixcj+&_?J^yB6)~ zU+4~@TZLXNbc@idgl-nPN$5tQR|?%A^a`Qtg0js$p<9Js zEp&^}tAuVAx=H9pp;rpsAoL2M>xEt>^irYggsu^~TIdR)%Y-f!x5xQ9DBB7H)7Ydyax&!LS8#n~}n!z*Hq(Yl#AW^Dr3crWg` zx&U(Wl9X|bCu6&f`?fuW_hp*#_Y~gCxE->J;y1S!K~^MwRV!sh;uWnUA!E`u4Q-Tj z%P!}){x;sxStAngs{GAJV_dp)^j+Nm4lCC;HZt~@JmI3?83Rw1N@*1y9coTHX zLjE>4zg5$@as8@|8`lrV??|FyE%P>s-?eiO+6c%mg8UVb-}zI#Z;U^dnR&6yCQ;wc zGE-N)4?7HZ$}j#+#~(IsT&HFJ@5YT=?gt%wjPyplud)hzt(QY)tZ`JkX}n_1Sn4Z* z`;*8&-q7Zi&6brijZHr@`ETt0Y-lqr)7QirfBFV=)3yw6Fav!r6b>9wjbTRbG-`F9%Wxg2(-3@EM+wx)8;X0T0OjlY% zTMRZZ%YM@ll*h6xc|cxQ9=C%JZLv%KQ~2$dn?TtU(BBB?FM+a68J+59Y$rQ^ma5A% z$Y2{e3|Rj8#`T|kym9@vft|pUfFA)*0Db^G9{3-iNi?+4_L1b|H8IpZ`rcLbsQ)_t zeFc9z;BOcF72@wr@aNff@(}K){%Av%Cm*^x{L_u=Cn5cHv~S0EkL4R;`KZ%RKiRl` z0@7a=zFRF{-15c2*9Li`kPdx!$y;IhhFZR%;Clx0c1QXX!gr#-^;>NDRKI6J-XFoI`Wo=Qj%TWebPxh~-4^zLreu+LpUpO6YkL_C9_npY+5a{a;U@`DE;6cD! zfD?f?0}lX((f>Z+cl!SV_p@B8*K44MOOdYn|G@I8UMGX^`$$*)ud{rr*S%2pvyiU( zZ?$}?*KHv0bfl~PU$uP0ZTlXMb~+9EuS9wn{cnV9)&CP8#W*SlFh>XUqI&u4<~Uhq9DdTVX4A|L!Qi zlYJx78H+_w2A12Ec`w!xGI=d(j%Ms|A?$mti=7bj?&NsCX7v$88C}|C(5tr5lLOl5 zdB_{_ZdcoN<@*%&bLFe`@i_U5AYa?}&iKtSWf<@w;8%eU0(S)d0=PY}5y(2_8VB7D ze%HQhaX;HP+b!Dx+igC|Nx%BEw6oJJAKNYY*lvFfyL;2pqXU>N(21;4Xj%<6}ImgQ!DT>(8_1-a^AN&^k{fK&IzYG{(oQx~MqxL%x(sZ?>_=h1wI3u1N;N<6yVdq3Sb!f%>=)*-w3N8wOcKj##ZzR&Q`z1{r3g7jj*qq@pW5mR`2GWsK3w={JMyWm-hjL$ z(zg{p+Kzl`tLGqZ52WV{A8kiIwN(sy9L{u;j^nb;M;}H0l)-h4eAwcb)@F>OkiPbC zb@06;d?l8T{f&Ga?|y(fzYIRCe|FjMAj?M^l8^p!A>=&+zQ=`c ztmUH($;a{TPNdOiK1e?3JsMB1wPe%%ET8TZ)EnyF+giZw$m?M6-U>Vr*Z}+{@Fw8? zz#D-RfMIN~H~5_mKE?fh8(allT!M6+i@s<1)CR@iI~VCX7p3hePi?RV>b?l+Iv1tw z$fq{g3i7Ivu5(e^j(lo^Lt%rHk?x<1I{ni}6(YtP86VuvdX7p?Z!d)X7$1IuJSdNS zU{vxX#Q6a{-ttkmtPk7wKQX2aL%RAN+K%$5Tk^4eKMi@? zAYFYAZAU)pmV9jA_d?$0NLSxO+mVkxihPXc+L87-^#=X9wZ=Hs0`jpIFw*LubB?{B z)7d*4?|$mKih3$$+zcJ#_h;4~q#xxq+!F&${dZWqP%lS|jG0!~;DxWcqMiMaI@5K8 zTDzuDXZtX9Hc4bWYU{*&dGCcA(4XY`H*HyXeWY8TK$#8zAInJHjuQTT1Ik3bf8afn z!E>ZUL)$x+K1Te?aFKC6%LKl?&<52ncyTG}%6d}f@Win8hZvWj9JI+`k#Vuqi@Zmm zyivJ+9&`6QBvCI5}}^x)%f3}P3&@TM<)u8Rg(e`Gb z?UkbM&%}LYh)+tDGJBYub2e!2`T)!7GXV|$P*A^~8*|^@oG?(vQ z(D@Xkaecw1T{hTw^LDOpuxvkrZrnWndg#`zMf?WOJ)HazknODvRA63SnkvJ3=y0<{ zL)##fk$u^%Z8=#NLYA%@IJuY)cgfufay4e!9(8JbZ{zyyfcF8n0p0`L8hAJGE5Mb& zFn;Ss@aLh7wBLN(Po2_#P|x%q273J%WmUg*isfTlB_G?&1JLEW;8VXF@(PnGp`Bu>Lkp;Hh zxR_}P`UuB_$06ec=;ATp@xVub#{!#x#{eG!ehU~zw^PCIbW7V&w;CJWg*rAsuIhHM z<iMg>;RHR#-lbiJpPH-I1;_(Uq1@Vm!*74wXe#={jgibXk;=JY;kgI-cXWNf8CYlevmEcpqwVmbDnCNKO?I!T4-`c|R zX-sqgv#e1u>>3lzK^;$mT-ELG@tbwjad~_2vF)jDAG3TqE`JKX!;r4J zrR`ilaWTbwa$`T%^o zMpth6ROdHA-a7E<8r`v$Ph+7=Ag>jCx<)tM@~O@rN7}343tFRdK5;DOiEh8b2P3Ap zjX31y_E_;%t$aQ|5&pZz+JX7Mg6rJ>h`pBBr)KWXhp#-(UXPZhpHcJKfj*;tW!Kc6 zM?Tx8{TLU~_YFc@h$8KFl+pRZkD+t-EVvG`$+N;+cUb-wdFvewIpcBP6!?1QU-x@w z*{i+jQEbax)^XF0TA zZ-cVtBVFe>i!Gn_>nEYRFHjGi(artn*lrG2ZGM*LsbyIR~c< z>SK42@l#tb7h_kg$NH^Y-`SSMA7c*(ALUVRTMPd-0cFY*V~?|CqE5fqF|C_AmI;0! zml%5m$8VH_ee+$BF<0uv{P#t9+vU1vchUd%Y`&Jb?G-$gu27=LB>bN$lA%`QHk0-xk)|CsD}gJSHn(JuV=x$z{^y5m34ww>J= zW4}fHK@Tp*z6vrm#;!)${4w@m+~be2TQ=(+V{eQ5T#Q`@-MVo_W9-eqhroK^`@k!K?*T6d{u_81FpTf|9{8Q_I@0z} zwqvgAaU59#9sLz@bsU*u`83A97JM&&Psb6)$+VHi*yq4*PlHd#k)-A0IO5hFDkP!nAS8-V*ZWAYFA!+mTPlg+rj*6PXTQ;_uJdo&|mk`IJQ+yJvyFvU>Ngd)*0M>c1HI z{2hJfAmCqt6M=sL9sv9^un71vFpU2H0DhML;o&7y6XQz%g1qpeDuvvLf%$LSN)%3`KVX&u}|F%c`>A`{uf$4_9^nw53fNQ z*JFe9AJn#Q5WVL^Mw1)!62WtvwUGTi=%@{NE|6;l-vPc3tONcXxC9tR=cj?+>72Hs z&b7_|33Y!Oa&?ZAw0zp;zYo4g!KZVaJuIKLd9E!y06v}L47Yq53mpM@cY;snINMr2 zZSxl+?Wf@D&m3ohwFC1Hn&V8d*Q2HBInLh5H`^TNMA%a2IH#kGZY+8nI(KuBgCRTH z9OrXf*E!A-=p|^5a~EU=&2jd$@}~iHjzhn?9&yEyz_)=jfNud02et!CfNOza+R|UZ z@7fY=$F@XUaSp+L^i7m~4AQm#(stz2InF!KT_Mu7|8mYqKAq#d1bJUWy7u2oEg#1w zH^+Gd@`fT^`|tNHAIBy)$KkV*EtwAg>7I$jPQo{Y+H#xY+=242jBc!&XZv$+bDVc! zn`O}5df+9%w}BS{86#Z)YzHm{hOtF0_?<1L*t%-JUX8N70J%EHanJO%UtbEor@^Ol zoTQbf{dytl{wVl#j#vEHOH^&*-f%j?T z`WwsxdHd%$Yu`$xQ*W;d|LTA;Wt!uBCOUmuWE{+Nj4!#&D#p?vI{4~mR^q+ZPb zBb4_Zx$fCr^gq@13)VB{)4!YJTmTtA5gFS{9voX=$8|TydF?gqslZ++q7W?Z!FIebgWH;O02TL8i`eI5+gqab7~2 ze~yFCIeBxM0{GEryrFF}bmQhY2Sc}RT+uns^U$+@j4Untj23*IK{Br<>1qCgzJ!$$8p5XaS9>t0;KCW@~-9MIO66w709EG>1gk6 z9C7V4C=T4wwi`EYEJZ$yvFAcY9&}MfS-{!AFJQw;;Ag;c;3vQ^y8RdUoo;D6)=|fW zqmlPCq^oWpw0t@)M8U_lr@CdFOnEvkybZo_NLSs`cI4A>;YGCjy^*fErR~V4eNT_09IkAx?{Kd0G4$$w?}RcGV9ylqsUMSm z_xk&9rpr-~9OhkTKji*1$XwfO%Sk`UePSH%@m}>#^Z~!J`9@2zr-|(Wc@W2X@>6Tt zBORS+XgfdE7QFY!c19a;W#jy(k=MXeg0*w{Qm&^jg+1NxMRwd^`B}abZMoUj`7RgJ z*)O7yF#^BeMp@jwUxgm@U9dRn#=7$TFT@B3B#QAqy3O}UPybkFFHXIKcr424_GvkJ zgCI|RiMy9=M*YWqsM9pm;U3^&z`KD{fh&QNfj$k2+Y|F(`+Bk+IxD7+Otr_qacpN> zum;0>88W#kk+~dzPDh{;RM< z9O?S*$#*RueG>T?*F1}|ZiaMy_vB2=N1sGK&TASW?=$F8-#wXY`REhK$9}g0^8N!p zefQ+smXCdue0+A(g0v3s`QJUsHile+GO(WB7?Mo+AiPhqC1V)AUpmyjXUcU3zF&G6 z?DPWiI1Rdb9#{?hBk)w4>49bahQOPP-L?jK+3(9dcx zKGensx7T4$?YXd74fI(Ho9$us&$2PCRmP8n@P*8W<>589t*6}eL_^zSmL5+(XUpUG zZWq2AEzNlZ`PjC|R|k0be!<;45_0FDeEzY7;|<4>e{9_NNWS?= zdx4cd3-V_`9?R?GtwtW_f`4&xPudqXZ+vq`eEOQji6`Ei1wQVf;(N|RP}jN0 z%f-~|pl4U_w?N~w)?MS0<7xAaOOF5VNY`;GALY9Z_1X-03or(}8A!kSQ=kFa-FpF0o8ohjtzQF>la+`c10o;doHcV-aE{Vv)g+YWVf2J)kwe_>Mda*k13!KcNG z;m48w2KIcjjEJ+mcK87JcyEzCV`&J^7~=1d_IJMh&2xTS9_?s9d%-Tu&&@A4;HVgU zs=8nGcvR}a=M*gOpL49oD}AWPFHw(=(bu&e=b{cx{jA5OQjaZcJ@9#LPw&0iN*74xO<7 z@a#0#hhD%lB)_cTQicS^J5EQ(Wn~BHwAhw|I!%(!pwlC9O?6s9-Tgs%wYd1sVsqq5yUaOaDv8FW>W0cvF zx_b`KJBS8$cI_ow*}U@stqtC0%v_wd!4~k1VQhd;vWK?8Ep2_X!M`znhq1vr*N&_W z?(@}kAZ^fyJz%uK&1he_*x)?r2g`irIv?+Bgt5URuAW#|aO(}(+Bf?_aUX4PWk?%b z>&j+raD=a}18IXo=#4fwC`TI{DK^;CSFRKAE_4_h%y#t*VS|6R_N^a`3TcChu58u@ z@8Evz^8;xE1HI7(ucLjreh}n`@5rJ3v7P=TN4~3bD&&%Z*$9d+MHa<@%cJI)mzVAS8>YQV|Q;**xy=$rG2_qa|0`qr_15PhBMNzYH zBhF)<8a0LZYrq6on>X|5qS941a-`Z-{AaxbGt^& z-}!N*C)oZ{&z7R7FV~8lUH$Qki$n6UCdWMAWLpDGKjzk8+&dpTU_R*h^5FXz+qvIg zz(1@)d**#EkINtJ`Apx7lV~55wSeUX{}7a!Wn>)5GPwKwb=G@l`R;Z13^6$C8|zX$ zN7~hmF;;%^hikZo&9=zAYn!k2=F@zZ!1YPCyNR|8J;uY5-*n`+T(O4dcdKl7Zg(8> zX|5T2?Fh0jf^1hWK7*qVaI&?2xt8_vK9)6C`75%>AB1&>LH2AJPsgH*T;;FIB0rn1 zv&rXu&X%1%`LqM|?JsX8+c~=}AKl$I zS@iXszj@`xZ`S!)_*^H;>Bm1_y6ZZQIlHOODerB&=11De>%j}3y$xgEUi`Jo_bA4W z8F-zKb}_;Uv?10?T^-%{#&KmF zR!c%IpU3QJ>&<6u&aV{M=dH}!^+EO#`g7h#fAH}?Qg!2VLDbRPXUJ=OJ{WgrEwRj& z-kfuK=z`BHQ{Pg+Gx&oW+BPJf_s;X=eS8jhCiFl)-H(FzHFx5=z&6fCrF>498n-*4 zgF-xm<8SKaMlS}#w?RJgC1BO>df80 zkJXuzai5jZ0$k$LSz;8vKl00-x? zBEX$k_XViC+v9iue21gQ+x|m2Y?s^9o{+)*;ds_UA4v217Ww#Wp-%c%3FMEq{)qjB zyzIvZB2Si=duCRP54s(C8G-V9&$Cf(*Z-VsK38{h!$Qa@gnVsFqfvjBFZ%4jNqhflgL98f9IF>qRIJ%SvBC zy|aABSi8D&z}=Y5=k?fAdH`*}?}Rv=UWq(kgYI1!F2is3VONeVZ8=z;m%yiec=_H@ zQ}RH>jKH7kzd>V8jAIna*X6I1vs)*RhTfo`Y-5oZOQC+87d(q{xmdcuwhi9z#-^Zr zdy~)dkmnKGUR7SuSXTtydiIuld+=GATZiDi3A&|Rj_ujX;9^ViP$zY0zpf71^x)+7 zrfbGM{&*-;ym2Dhc`kXgteUTX?ZDeBP@LKWaHjQBTmzyHcvSq7TQlgBozaF`VGnSD(67-gXho(T|X$a?MB;7P`-W}WNNdwqL203zM;$A zterI9OfpXo=&O#`AM@=+8^IsbPOjZ?O@ndQ=g@Ib%r}pI!E3j{F`vuJ#kbC{i1xP5$^Sd9GY)YwI7izCzgb@Yy8y0z zQ76OgoYllz*Z$FtEnEjiY>Ee*4cobIZ|o>@IQQ)k_HE8FC?_Fuew{_mAdxe~&Q+Wo zXN#aVg1UFDjj~Sar-S(YJpR7LJZl4dQV`$aIr^IE(08vs3A$q0p0;x((yreAyxH!( zaSryN-aH!Z+3l$vV)gI8zspzQdI$O={gms6_t|!Yaw0ap!0S8N%0NABMH#l-{=Bp; zx$)TRpXdX;hrTglV<^gUb33+YC$rf4MJ-1do3g&FBlX2~P3FUxV<_yAsl3j&Ud}$4 zfY0#i=Y4bLTIl9N*Y4@lDenTyH`I%3Q_nt_$6bF*=P~7HJ@+6w-_`XULB^8F3%qA1 z+J=K{PyNuBxn}}yeTn%#hW5kjnnvEsZMy2xFPrzdejkZ9tbG&xAe)|oVl9{NgV2jt zj)1kOsql5Fd%O2tmsc1YceOLv;yu`vdG=PvFzs==w8v}y+Ead*vSwabZy}U z*v-!y6vOkoX1cGW6aJ0k_G!?ai{1V9p$`~|>y+=U5y*EvwQPOTn{CT^IqKvL`l1|l z@}perVe>!R^13-#rnX953=QZbJ#=Q}o&mYeMj2&-H*0?#gYg9Uy741n{SI}1VnA8K z*dbGY{WawJ%j@T5ynm-{qbwhNGh@GIwyBox?+F%YYzf{7eD7u)Z< z@A&Sc%$IB)Zk;)fxXmj=1{w2wGN_LORwFh=UA%k|#}iX=yp4YmCm6;Fs7C}gb$0$4 z>P}vkgX17+t`ji^_ST|Zy%QI-dNRp_xcDL5_bsbWwvk=YUKj(h@8UhPo%miM^>?0M ze;AwT3pkIs7}pqMQ!ZufXY0kirCgt5y<)7Fz0S2QzE^$@^1dEz$E{BmH}v$w-k6Cx zQwC}Fxjb8c=9NoqKt8s&6EV)R?YZ_ArtPcm&Zqygb#by>pZ2$V$45P!&+!=fvn*4f zkJL3jXHV&E5z_tzeXzVia=X@pp#wf69fCDUKBLbAKkYCK@>Ans_WL>43%Y-BFZXNg z=si!gbzwW(KF7H<+m+637$4E5)C=cGuHMcz`ps%@-PxAiYoq8NUK-jme{+t@wx6v{ zFrUXm*w2g0P-eUq;60mjZHaXrg?8`C>+J8^iHlQcYsT=7FKC=i)g=}C72+AtGU~Dz zHiTX2*OuXakEb|QPt51ntwP#tv=ukL?T5bPAFqE1Ih64|w5!MP$FjR|du!~aa5{GS ziu?9KxVlhhMSn@>h?3_m#sXn+4AVTm~_ydA?n93%io`~wTFkH zWB>h5huR)?52%|zAC8xQw|$Cwb>n5N-0Rwse_uWQ?r-UXi!s)L-U>QkJBcM`w+{Dw zjW>VW4{5u*`5T_)Y)LKm%> z@ty?FZJ}>Fx($8)GRpl4%O&Y}=KT$(U&nNm(V%Uo?n$>*e>33)7&fW)?Hav*T;3AQ&8J%K|iNVmc`AdN$1*M zf@IF2&S3MDT(>7LwMQc@_uAC{K*TB0#0B2|MegOxRIjc!fH6SjW~wV=O7|WkZO-?P zxc`ZEr;nw59~axYeEl{nil_HiCcWo?>HU>#Z{D28u7xD5E?FiguX?Mfxe*TBFPApI z7JZMnZNPnD?CSR=t}SxR8p$!i#@qffP^XOJ$p1U*Gg$UnY?Ejc9IF{OxIHoK*YD$7 z!%m*-U+MQ0@SW}A^uAfjYKQ+~|1R`&*L@!;W5N-3EGMrUtBSr7HJ3wY1Em9#qYmEv zbGom8)!L*t9r$%hUGljS^{4H?@ny5sACSM(t-tQHmGHepOi`S)~etaWv|3-UGeRbNQk&7?nPk07~zoA_hh7W4-<1~~cI$~0HT z?5$)EEBnb1uE)YgvG`BgKSlrdX!cL+#T;Veu}pfSp0)h0|M}wqZ!U;_%lAL&?`qht zi__0#o8_7E{rPoIef%j)KgecB*FM+>Zx9=}yt=NV%q+`vgZ zpEg7MnOocaJ?g@Gj}7o?5wyqQ)~At91o*e={O)5ykPfaG03GC`54(E#+mx%fwkM~j zpz_R<@;JUOeL~*`Yc`YVW6-XsTbHln^~;DYai5NqUb-yuZ#mPY2$F?2x5p~a|UEBBP zM_bSry63Sj4r3kFK0){8!jtl|-DSD)gYDdnAAAS&5$d-BzM?cecFdujihGV7|75J| z8#}m<;@4;^nZ^$4mbSkeJpQ)guiw^VT6?^Q@7j~!*4z`tSY&v=_%S z@HoFlos;H0jFY@IGsHLlhEL-)`n2`%i(b&$Unf7%ZHg`~wwWTa?nKYGrDI)JmxEJdVYj&V6zIWScm4j{Xex$t z_EKLl2W6m*W(M5n_+NzX^%>4bG6wTqsD`bhJ>FrWeMg`#YK(Iz?&CPOKl8#k%Wr|Y zJi5w+`!tT<06n>S`0eHS2E_fD`dw;FxT=-&4UU(E-gD7w-m>y{LYw!}&$Rnhw0{@n zMNLaU+{--q43j#3Wb4iUb@MLQw&j`gTkBuA5b@lswV$BPG97t(K2l`{mA{9q`1!3@ zZ%tXHU9y5?=scRbreE>;4{5tL$GMz$4db3`6G5`*cij3S`MRD1N&&(mQT+On+v|% zz^CUByLBh;83^Rj?;e4A-2gs4hd5^CdCzme$NBg;$h!)BdJgd?_{}kb&v#fpK1VnM z_g;*2|2f1fFU2gvzANU}GtY4Qy&d;ezl$MfE9m1);1J+K;FiEMfce1Fftvxt==T%k z|aUy+xb-b!s%#x^nI%T?U2uX(APG=dw^R5?*@Ja zxDq%R_%mP_{r?#JPXDLje(GQK`UUjxA=;$szrymVUhfCrdhn_KkF$KL*XyAFHt?zb zXIMVf>v@p(7x1b653zh)k7WDiUie=j?RoHp(f>X^{XdeU{(p&lj)cA*0L}p34?G;m zHOUg-&w+;m!|4A2@H_o)Y3rx@zX^J{4suoh#`3BDj{@Hh!KeCnF@Wlye&c-bss7)= zZ@=Hz8S>6Xy6S(8xxO(Z7l5e{EMkPSqJwn zO%mxviDB(OM&3;0Ts#_YXuI3;cJ+^9t|O+->2&wrK)u_v7}{{q*`4*^83fx_FD zoBa*8KM=SY_)Xxe!2N-*04D%n0*0~Y@4@fvc|Go@Ua5c9n|}KnsN1fTFK4J;Zu!{$ z$w$Bacf@=^U(9dwx9!dk**6^N%R^snGv3z&XG_08arv4XgnE4tO#! zj6IJAzq99VR$pq*hoIMcAy@5LVENRZbHR5T_|%?TSw6Mr5zy5Q;8S~YZvfko+H)M_ zT?Ib1=O_40KD8(N=EX?&+cVqv^kz#+4##1RPtHGd^}h_`)9J`(Gw5p`Fa~7IlLxE< z8sKc;7qoqd`0GF5clv*la!^0@kHK}h>`P4tMZ+;!=e*c(F|Cjjm|GfR(oBjtQpPxZrTL5ndZVtQ^ zI0)DPi~?^0hSC2u;CK2z1^4^){}J@?F50Z>zs&Nf{_h0eTJWjLa51;-=(-)!L^``%yBcDT|FFqrl0=yG=2=ESI zG4M9vLBKHj9|L};|2$hiZU5Ip4_830>c11eSzguuVc@$E>8k&KSw7YODCqwjq^thl zuzafj?ICX=(pCR|vwW(5_RT7!`}MEmNfFlgC&p9Xv6+am>h~|C%kSRV)FA3BZ?ti>v99d-fI6jb%^Ss+2k85H&j#OK|Ld(Z9mH&>k_rd2MM;g8x>3V;;P+~pC z9gOpi5nH-gjkeqY`MTe=mWTJyuCr*%;`F)fyRpw?u%VOjRq&_{SD~!4q4VDZD}he| z%YjbJ7-YY-wqI$SPWv@O zmeYOAXTNuz?{2?eP|v;C?=JAD{mM|z_aT?#`+LBdz<&d|9{Vn^6u2H3#(w_wcp<$Zx5ua{jRorYQHBTZ#dG`em}5$ zYQImAwk`g`*l%$L`_(~?v)^*dlZx4v@&{GhYzE6^9dp|Fnc z21dl6{t4;Y*Um$k3Xt|);10lZfJ1?21Gfb-)`|ne*or>E+3FSC@9%5XD0ey1^{jZ> z&Tp%)g6~+Q>sj%%9r@H&8^Jdn>3UW?ZAU(})mzZdB&73sl`9`@M?SUHF0j=ErW?#R z@Ejy5&q0FLOK)JGfebfi+S1xg*Y9?RyvLEgEATPkF2F~CI|G}5!+;L~!|3^L@Vk0^ z2lulK8Xs;2y$nLS#)mbQPvgTUpqEe4-ZehtS~}&ik8!r##xtt=~P3wAaDsj}NoWj}G$bnt4$!zqjCxGnk()PCoJGVX!gRu`8jY zr-0?aCxPDvHUm!rJ`Owq7)IwufZyqyw)5-!0hEX9vHH!??JS?_{1our1U~)d=oXew zbzTCUF9)A~b95tqGhfwt67nuXx_)!?L(8W+uSOnAk^Z3lK975Lmn{xxdoGoqKMvRd zc{+b%;tgx7P*#@9&6N0og8NxN9e2h-2P2WLtO$jp|Agsy)zH9Ys&uqt*S%@p$kC}+$MA(s3s_c6oQbT5ClPxHnbq-xrNYXK}-=t znS&ta(1s9GNE34qI-?<`nA3>7pYOSARjr)5b?e^9?|pvH`@DbLXWg^UI%}`}U2Cts z_CEWZ^BHs~^b_b=(3#LPq0^yfKuf{@82BCkt*m`9#=Qd$6OkKZ+$P2s```&(b+-3MQcaVr>K>~9xSb{l-@7+1*}d0W=VyE1R8Vh*f( zl54T&w%y#EkExF=F07J1!Q6;rt>?)9^#4Ge<9{puS5($d^ZQC_^W2{s`F@e@vh+#f zqP{&-4!?5vIDbCg=F+Q#`15IyaiFzJ_3GNS=&Sz;=RH2?d{>{Bqtnz^a(W22w*vn= zd34g;=m&o;8hyXC|Njk`J{M$kS2=ved<(R{@)xjD|9K$DSSPeg^?#4PcZKtb-D`92 z8B*J{p5yqJrSSJ}fu4zsYlDoXLmir%f53U)TX7gK+aJKME5BC!GWywl*|^_Ab+={Q z@jU__v(voEexWS)?5lakk!$1^o8x=w>jqOtCBD}O->bv-`d0GICGOAEG0yiz$G<5S zgO}`vf6cf@_<@6btsm!iM`j=LPU)rhYjs7p^tV7?r!4&~(2e;OG}IYH&vC%s24 zUmpCP^>-gPGF-bIb{GhqkIj_x%!3Yq{tDe6`d?^&=+Dqnu>Asl$5yse8{@dJ3vJYT zY{XW%nfT(kkbzIW7qPw5_~N+m34B_Qjo8X|k{8E?*YNu)$|JV2o%rIopnD~&s9b%B zIfnLWd4JA_?T{3<%J(F*r7^iReDz>e0lg7g2E86SA78l+IuCj+v=r*dL9&3Gl@+xtqz0*vr1p zz?UAA9e>TmH70khT%6}=>!+>DxLifwQJ+{P*kN6JKky335(e!wep9CVW)+q7(iVfC*-)-M{vQ+4_n>B{fr>vXSJ7h6_XP! zZ~2RCGAPK7rCAj{S3CAyTqL6Tx6$nqCV(XC?}eUtd?@3 zqmdhPq7%VsDQv2HG=G8~3ta*_7`iz0C}=xqDRFBadV1XYH`mK|}9(&_V=LMEy#^I z(GtcNvF{JxweZE9sKWRn_FIGfrSQd^=)WANv0oc`=TIJVqHl~Z=0vjZsgy5FPW1ju zxq9W}o)f)|e$pkK6Fp0ek^gHw_807@_1L*kt;hZZ)q3m?(7mAFK})g0C-A!slC^Z| zk2%qfV6hc)qyLZQSbD~sNOO}-D3AUx+c~zL6KUOJUCN{X%XZ?6Ig#?f)hUnuFWZSP z=0xh7E2x~<;&X!La-uW8&t0!E^eo}TSX5P=TPNyd*z4STI!8S|CbzPBh09Sv#$dx5 z-WJ!1t_=RTUy$*h$yU9}iEet4{uIvdWxD8m%!&4dPdX}Q>=5{S=h3OToJjWvB}2H^ z1Q`!Xr)+UK(VH5}%qD*cGBi(7x%5{3hokSR;rs}luWiJCEPmnqC#CTBoao0FSicQ2 z&JT4cC#s_k&xvaJNMjAZR{W;yU6z^5&*Ne`C;A6*D4pA+dGqB&58yND>src*J`8rl zzh?XyKaV-l1Z2jX=oWNK=S16rc{(SmtXwcBS{6B;6FmsF`EsIKWT$hYHpHbuInhnX zYAGjL8@Vwj`YXCjMDF^~SD@=cUxuy&eG$4A^m%A0aceC69=FctdiP_`i8QZShVmG< z{$YGEC;B&7wxc}8t>cX^=0p!6Z$530aqI8K7jvSK$omn#7`Fx(U(AW#qU;O!ip`03 zGQaVhNIGjw_y8FvfW>6!anN_6$3WkP9u0jHdL*Ww*3PvqSQU&K~8xa~bB(teC9;frH}_vd^$ z(NDw`$#86a|3FJQ(P`AN99W$K?Fc;yx-?WdRVDN|=#tPqNj~7Vm}YQ9Vm}E(Zj|UvFCxdFM3cObE3PAFXlwD??#k2mlNf~ z|LPR}yIcIvcYk^nWIs+{=nQ=fx}xfWc7i?xT@LyHv=q#5hu<;xd^q}hGG9Y(ygz*e z$FfbtTyvA3@a1@a`at80nAd~(m+-~=(|Z|T#Qc2ZO@}YupWey%BIe^Ln+#w2{&c=P z=!p$_Put?#obDQ%dkCvA4!?_TYM0JkrE~oEJm@*<@HTf1GS)M^Ik!L_beP}Q%JPjE z`@fs@SK+Zzka3l@OZ6%b+V6SdcR0VS>5}9VF1L~#6h7%B+}Z^Gc6oGC9x^rDFPm)t zH6BTZF#Tyx?!W38ES=E(mksvidDvVYqwno#?M3_=M`j29fuRkmt1G%q59c*ciCCW& zu-+_%wdXuz$It)bjUc0Us6#o=GSuNYPtC6EE#;Tx_jX-b<}-dC_tH7fR>YZf?vm!s zm-8Ht52UYaDd)K{*ho2#*8O76qqXsv^ZX6n(m79Cu1V)St(ROd=jly7p7YF<47*Pj z*MTZ1Pv<DYamwex-k9-9Xl$^9LxSL5#)&++_5IKPqUBE8}|L^t@PlW_Y> z;9o0`PR*@D>|{C#)8&E;zrRz=y_qpKek#tj4l>kdrJwXx{oAAOyssO}61xZduMYU{ zk;31{VX-s}R*iB>deBlQU^0j`n;Z9`6F?k5OrN`vexh6d(@77_#G5I~@_?WD5C*PR7 z3bNB<@+|Z#G$x;ctd_>)w#bcR@}JPK5jfp`!yzyMaJ&@e-yea^kL{O&jaa`C8Z2w7l#8$QwUmO=!L*D6>M{H#~ zx4nf*e0S5}(%lv~*3c4Q`rM zQO!3|D(k1cIj*sOy{k&+Rj&C8GG{DfxX9;pEPPhRZZ20bzoT@`)=%qYd9&UHKNEZV z@w=v<%YNGGlz-}-JStcGt3Zanm#8%>zxE7lDW3SvLFuV?elo6aruV|TF7lc9{L>!j z>Ca@iyu*QooAGsn z?{wt73*VT)x0T7OF}@o3)Hk&rdk1_P7yWsS>Yjz4P3&s+m+w@WkH~MXVx072?P>S1 z*mFt7l?C>+FK0Tb?yXE_HEor@t|gzU6`r|0j$6^D=nMJYQ=z#)wE0!Etr{B*hiWWV z%7IE>%E^!Z}m})k0((c_q0zpKE)34DgXE@d|Hoeng0{nhd`;i7kw%I|nD)r#aYGu}W)=~Ype z`?jstWb|#0TyA{jr_w98mz)2{zX9AOV^_r-bd3Jy?V5?tMt}P+*IY^2&(JHNKSD2q zehrSR_8}TUFnD1HM-SUw7kE zEE1pg=xJ@?CHS5Vd>a^_Vv+b1iw;KKQ}8_&_^OOgu}FN1MI$NGdhEUM#TfJUBjuUy zJ<2k3w=By{?U2cg;paX$1RorN51v!WJJfsT-t#;JdFLQ+iQt0+f)5IF@yywzbcMzjXfF0{`{)|IfiM#dp5*;7fC!&eiSXtbANN>Z=2t zkGlKYe~m}-PubSn#ek5Mxej`l_{qwh;FE2|C);&J?)LI4%8$3>r_A3^=D0gPe7s?` zvBkT=cC4}eNo=HJfA8#4U@2LuSN&phlO_8{zBa~JQO6o-xPEih!S&u|`3pYJd(rvc zC9=kBvTq_=SahLH+gO|AH`Ue3Q*v0zZ`H56Q8o@drH^Dyw*T_4clhsZnSs5%o$b-> zc-|>|GJ2KM&()4A4Nt`d#?Te3kQ-yf%XVJvQEXTpy+of(;!rL|Jna9m-IcVZs&=IY zd+jBEQ@miD)4P&lzT!GQ0Z+_V+}6D)kFn$vWS@nv&xD={oen(%Iu&{v^ke8L&{AT_ zk??yg*}`mB1Lne2`M?{r_hsb9d}Tx9>u!AA;X50?r{RnF%38)(Ykalv9R{8c!x!_F zRgJHw@%4nS4taOO7xNWg=kH~Fz2G~aYwO`l=PPv&m1kB`i%bXF1)cT<4NLA8tZke@o;vK z@ug&Bo9>bA*v(=S=Q`Mb-5->Uj>vdF$k^R*QoYKpR>IHU23({Z$6bP zR}6b0@V}HtCylokhgfuu{r|%1)_D7Hknw0UGU|eiUiSY3lcD%`Ymjk!&^znbq4#a3 zx8mP5fqz7vIyLTn=5rpfoNE6y9tji0#(xGG8I{)(Yw>f<)5Rkjs{M*Dt6RKMEIm0~ z_np~S7>oA_bnIVE41!Pj)NE@@A3Gjk@uQC8eTW0TZt}kMQ=hXa<{p4f`-QsRQ(fpN z9*@KKn>_co_f2=j$(_Ts>a&^`#cS(5mZfCAZ9c3RtJtO(s@SY}wGY?S1i24NZnl2f z*lxM|Uc$W!yxz01MzN!7;J?H86|W_~9Qn#AvRqR`*)HIh4gA-MpL&I_^f-v~ox=Gm z!gt~=36s78|>28wX|k9nzqPpiZS3c<2(67ZR48Z zb+QMx5T4r4eH^-_*9@2An)I6CNo^KfGh9PFCTB8zG~b%xQpirP8BRvOLTiREAS;f+ zE;l~o_A9n&okf4zxj1$!r%g9tvkY`3^mlw_1oSuPHPBz6SLr<0=y}v)-jRC&{5~G< zja*@>m|I6|(cJGV+CLMyab0T{<5OG}pK_=h;rjr-xURLW@hP5)Pd<1K?Vbc*T-Vy% z_!Kk6r<`jL@?L~5u50<+N-LBHF=xd{ODw^JDuxZAWnH3`dQymJ5HrsJfp*y`_jL1ylMy6B=d0bs67pH z8T@&OT{FvYRKHL24{*8g_p|@HKj`gpU5=3*!Px83oMsyvljPIllh5iKnoC++2HElF zV5e&bbG#*fGsxQax~`|*!|Yh&>&4hZ$3B)$;n?d{p0t=9%kLxKn^s;?my_q$FJ|L{ z#x?1#enOqeoW|=?yc=dZi%0dGWEiX8B=ZfsUj0_NhsKr*;BlEZ+kf@18~N{T+MWO2 zUd6XTjCYz-XpB`ow{u?Y(|9+Q@h-joak*iqc4^#Kjw(9F=QGF{<^Qww)Aj=gt;M?C z#tLIFC(bvS+qjNvqeG0Ve(hac8)NDH$lj4QXg#(URO_+ZLA4&+6RP#tt)Zp#_l@EA z{=S&?C*^6vRddGc(BX3A#<|UJ91An$Q{qz&(i^_>D35cS?~G6Rj`);|{1rTPZ#2$r zJ~KYemBgpH(hA5sj`BFS`Ple0R}!D*O1f8aIOXZNP4yk+nNHPZnTOYBt%TncesA+j z&u8k03I06#OP}W2C4ZbCf2^&aR#%;y&kS%s;M}UhnD%?F)5r8u{ChOW7%Lh01iTB} zSI!qBrWp2$DYpk1t6I5qQat+f3C7cK{wC`;I$z_l1fRSSKIy2Kb9vw&o<}Fe*Nk6_ zzH@C}@R-%Dv2{q0adtB@o)2@2C+)xP4@wWksH207&xKiS{j|Q=akGG@bW#8AU~xk+ z?Z9x|hlaJg2b0%MLYLjs71G=^@C*QBVz1>*cPxK;yfObg4PQ~d+AGA|UZ$gB?sdf6&x729t$p6_&Nh6LwwkYdTQ$ef zeX?h$Z*q{aThL8yT#@=Thj9H~gMTi#`5a=vI;?l_JD%TfYnNqep_TmF@=MPJJm=pE zoB4mwGt#{I<_*VV&-8UI%^PB0rtLFkDi^?qghM~tr?v4oZ}=O!rRNQ8xh6et7};vU zdBbAh8$hpuRzpj{whR1@t!$^*(SdrEmuuc|K5f)`Y{Yi9@oC%? zpT@UM;X8ryi0xG4i}Qw6!S)EsBet@g>eZO%^M*FaJCO2-t!yVg+1}?3J-~J^m4j`% zKj+>5M+}h+$MzfH#c{qp_gf)%3byD0{Q$ZJbTV{v=)2HOp>IP=!TV+S9q((oUUf&j zyVCB}DUW;ZFEzf1_eb<+`7rOF!u)XRQoA*d5MSC82l9T$WE%CH4qpF(o(la4dNTAw=!wwxp~pi@ z!T&J$9se3@U&MbRx{XIJdovpLCahYt6i_2GPC_u5AM6*Hxa z+Bw(S?D+eh%M+0?EXZgV>d>6}4bJ;0Svn88sC>aZsGWFh zUa%q9dcG3#pl4}!IuAM@{R-tlS!A`82i=3**4f59=s~X837d|A_J-aE-2plpx;^x6 z=(f;OV%Mhdd+gF$y8E&646UKv0IpXeH;&cwITl~cgLa1RLdxS<{iE^4Jg6HnX$a+U ztY&~nK!Y;QEaI4*pIytOEg z*vfX^PdpEL0eP!Z9PjK#bIy1n|gzCYNJZ+Ir( zA;h;foWK6G?ZJH>{%|cg{R+Jr`d{dk(4V1KK!1c@1}z2mbKrN}`&zpq?w`^A{~$Nw zzN7I)+(*Fo4tx=Jt)&a6i2Iq~{wjPC_l=D&j>m^0?^*aF?#j*5xZgzCWAH`X)8B^l zcp1OfSZVL{)b|=IS=;t+-+uaNZojC;ghP?7@86zUmPzWBjh4hlm9?x{Q$BNkP9GmL zPJyoy`_&}h7#%jXgMA~ps~xv(_+~QSuZB-`{D-);IyTUMA4e(|%VhfS`y=gmB(!5A zYsU>mw&VJRXve*@<6C?>w&Qx*F!>L*V`ykcg|%Z`k?nY9A=+^o?RX~Dj;c19%s_sB z#2(sj;5L7Q|MDM_@1p!_`a-lr>rR)X+VL=L_~8$>W7S}fo2(se$OF_bTk0Q+!B=Yk zSe|z5n`%c-g2E7ff5aYlA=mA3q_t!1BHOXnLbPKRxx-qic1)rT-P--JcH9=)vAMNl zuOi#A=R&mOA=>dXJ`nr)Ike%kKiG~79%-~kJ8Q=|MYdz;LbT&7+VMiF9X0JUndAAT z{Yvv=wKw1Rp>GH4xrok{{OHpM{GJ;VI`O}vl3XBLKkZKRYl!WsaYt)#OYv-8CHxPn zy|u}4vX|iuFiHAV^;^o@llyM_vgac$t8>sHn`Lb#8C!DS%Uy}QA@}{>%pqBJYxE^bPAYO<-xS~7mEhdPan?ND<7tJnI6J)g#I^!>0_LW zm`)IH9!#%4_m5$^LLN+?p)R*sIzNh-z9O9hrf*rhGr>rb3-^_!n@Ji#5umAG` zjRkzQDvz%|$a^l*vK9u@PakT&|4%eMgz1y|-wdW7a4ur{rFipT`tVtQ4AcAbV9Mr> zCOC9qZ1MLi>pt<<(g#1)GXT>cq`g@^Ly+5hr+qLdGOp^_{q^Y|a>p7!?*SjR&H3-- zcqaB(6xg3%0DJkVbQbo>c+d>?Q{ao(Cv8P}M8%{hWaP667vv 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 literal 0 HcmV?d00001 diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/lib/armv6hf/libtinyb.so b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/lib/armv6hf/libtinyb.so new file mode 100644 index 0000000000000000000000000000000000000000..bf444a7f101d53698da78b9628d025c53276cbad GIT binary patch literal 1218076 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 z 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 )ROU{hJz?C)5oG@;Eck+NN%ieCKH8#jFbXqO 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 z z4TJwDGhUJ(mF*VaFq6G?t+k?a7+YodO?F(rg|;Dn?qWjOhWtIYAw!)c+fYxRnaUlP zK1kcXZ6Md*Z`eV(Z->P?i=7za?&Y(ZcfW0Ev%5b4ykA_A!`>llYM1T=B?F->ZriMxND*yjfUu19&eiY8AW)1CA;K6tOc_g;n9{yOOV z7Vk?==Hio+PXnRJfPf~7i$s(7xkr=yJv12@&_s1alL63VATTdud}C`SB!jW;xO3F=x?|_6 zOLumKChqZ$b9tqw%-+Ry2GD|o2gC>fJTY#q-Scmg}F#lznhwy(e{}mJK zzaJAHBVBBQXTkF$^c;U=V^iTYL~W$R#Q8XhIu#3)|VKll|^w2_UCty zK7waGi8~kYY&EgbFv?1v6&t&44-Z8DI`}F2Z3A89AL_x;_hrq{RD2J|Zt;3=>f6|@ zhsPIx?Dp*P^~WyC4-ciibuQOq_1|)`FFWCn-5w3SjqW)zy589B)9!gVcFVZu;n;0&p4-@M8sG2L*lpad=o@-A4gDJnuJh4H8@u^) z{M)&4y6oI$ZcH`I8`rehHGJ3sexp~5w}QuqyRnUAIv>NLYuZDlcfR)u?`FwC24kR% zCrdU48jMZIXpaIg8~truqVtKEHcOOGAV~U&G-y$AH%-Lu1i+S7$Yc80Mz+! zG3~&!A88DW+>P?;9VmMS4mKt<6!dL4z$vHN!u=phIBxfIsl|RXy%nt#zv}xJb{6Iwch<1QDjHYSY77jW&Vx2n zU0jkx+ldy#K-&wtpzX2HHaCCHnn>Os64%YGk37TW4FPFVf&nSz=tJDzxP_kS%PzJNPtb+e7*tLcCmqkt3%IW;?gFhR~|AE+*X8sSL zn7j-?Ueu4jcY1O*cQ*C)vJKF}_NR|?@JlDE@WWa=YdWMo`SQ&=liGZPzUi%8z0Rf{ z?CR>==xUzT@PD9tR?BtHKeR2Y;~Khh9#-x<-&*U^lka@%Jjhd_xzSE z0cP<==K?f2e`RMLjCJ+td4n?#d^(4EIor{Be@AE0MRB&-GtpT(D>|Ee`E(xd=qEbY z@}B5C+M{zW-_*8Dv=&W8=W=ApzMXD7`6n`HvSv8yj48>t#iz$7;Ap()_#U!`VeLaK z(UY#_a+Wr{H*(}ChdE9PbUl6!7_GK+Kc&$R0mQ#w!+2@^{-Qs9w za`ryHLNt{=o1FFWGv%!GzRB4NSAS#W?1zrd|0_8=CPB`wayXit-J5`;@nY0? z)^lw~oVpuvAf=5WK1|seyO-VP)rwZuyQTb%JpaNg&ff2+;g_8|@}i4I4m;`W3(h@f zB=3e#PCO`nbN_>aACBQ&;)&Eh{nm8s%nWZbime&YX^GemC zRihM)ShZ@^AXO?92v8tEeJxNRK-J(|wfYvnpncg4`(Ag>InDE2JI&LXh41^`-*57x zT)ChB_1w>Wp7WfOlaovq&+i-`9~}?eKa@bi`vZ774GhKx z$B;NMa(*H)7>^Id0_b^uG!YmdIyZX%`H`PS%e@Z;CLWqd3_XA@)`OVl(eanQwExxp zLl2C_QBeQAgXiAZKX_r%^5*JAym6Q*na{I{TVL3JcC7#0=ml&|tbZ&qex}#!6g=p3 z=YuHl&hXtY>F>Y){DuB=lasgJ)*l|lAY-HPk#i5-dF!2b9*osl>o$3AXe==@dcL|} zFxHFIvF;t57&>Ffi9WaXzxqOAXcF6Gwp&VtFYfPuKsr{(mU+6=WAH5#555&(Z1l}T zU$A=~^#1s9AhsK7^T;AADp4)DFRG$7390r%4>}&$LUOo8Gy+i#tP_@Fs_uh&P zx}1rT;tqLuYi@jEVni(G@po?=2a~)065|(eXveDSPFBac(evkVh*uX1P8k<)%1K-p z9~zGhCI+Sd;9K5QUBeIIx%#+6v=yToZxpOEo7I^dUNPNgSnVnCJ3|7y7gU*41Gu~0wZ0&WzDMP0ANNe^{9q&1t zc)xN_N z&^i-`mYLA1Og!cy&n7On#)&JM68G@O>(1ql_n1yM@;Z0)^Ue2ie_*jbwy~@8Ts@mS zwmjAO`yS+ZY{!rkP(9D`xxel?`+K`Xz6awU?3C|8u7e%(J;?Q#cTTm2%YF^~KJ1^z zUW2}m9(z80KRxz*k9l9=P`c#f;Cl3~!9?P$b0Hu`x+c}=<-LB#suv0JhUEya-?w#K zsvw*QW!StwWZK?i_;lXiS3k{8rOe99mU26NtQRn*QzWXMIa1niZ z^Wa`(9ql^iwij#jpqD1+#iLx))K7vXMVB>K^djBqUAG+lHSo-@Zf_(fx!Sy!XvT3eWT+K49b0mTRJ-9lc#R! z2tMlnZ@n!(DICP}LF=98BjXRr4Y9;1?rsgn0=R#bcu2;Rll~yy(BAdB!SRv7^NGO3 z@aTm&ZhT2Scte~R9667BWTW>52G0iu$HxaBlIQn6lo*<@hrq?AHZJ2{R~%ckB`W0Eeorss&~W( zt2eUnJv8f_`@C1V@8;cu^S0T$|K?4J7ppun^1#sOg@ks`?o`5U#5ldmy*u?jp4aa6 z_n6w(8%OT-c{Rv$?;f3O)P)NpG1s5NA6!qIc5j>E`&91T4?MH1S}X3e zV!Phc05k{S&xX5)H zxmT`kpmV(8p8CNgym=%s9FVfyW6`@e=FRijHx9+|@kO=Hwu$O~7@WWa7`ET4O$Dy& z+?%>Tad0+yL!jT=>9|EdlIX{atz)XLvFhX;oCm5)&26&oaAwXsj@^lL?i0&gxE#ad zz{Fg+$E@p=b9bM6$D@T0cMk5YcGJC8^}g#V_r|OH)~afC-wAlgicD6| zY*u3ORQ0qb+wA%$?osZ)y8R|jDK}+nhoy5KmlK6^=DyYPTH3HE-RIb$)l-A%-0%{q z;R?b1z-qluCG9`cTL z=Q;!*I7*vbgSfcHasA<9Z%uojEm&SHP9Nud;CP+quc*E<;QGVW+PjGfta0zH5ASDv zWyt=m?r(i@NON_+v%78Zq3Uayynf>O!K-WAPdY$$8ujs(h~b!e(UqDZ}w-;RLyeUIHdd?JBXTD+w ztBg-j-WL`ebvEUeiI?Jn2}cvIx#WTa|9?T=2?d^Y%fz$fEl2gbzItgja=v#OJ zFTM4)TVE`bD(4}5A}q`EsP9YA_qJQ5?_K?8&h~e|{IzHM?|9AKci(wO+;gTk@){cZ zi0|7lKQDNZ%+JyN!HyTe@5}+N1mp`KcT~TZ;eGyEy?S+K4A&dDa>a+la>X?HQ1v?X zlzeJ#)vj9kJz?#-zrWh4`Wf@Vu0OaubQmt)#pNuE+O^|^dv)6HoXYzjs9p*8pTpNg zQ2XIN`8fL^T^*k`tan{>YyZKpdY*%t54hgny9{tPN$;yOyrRn7pQb+ArX8`5R$BF%MBdpcw^0v0b}qTS2b*z;yz0x=Ve|KB zkCw=pJJobB;3W?*v;R_aRxiI+jx%4EwCj1Rb9G6dp3i$VlT&j4`Jp%Cl<0mZuXaf5 zJs!=aTgT}ap6)nxTjwdf)qGLx1s(WjZ;x&}{V3O?TaMPxYJC60Q>m`sYFJ)go_^$! zhQ>}iZ1vgr9&GN??XbQL)|6~tK|FU<`@RExukV;?NFB3Qj-k7y`VM=Zmi{lV_WxP* ze^%UHJZ>fJ+2ht?gFIbnurtT4a>Gmmg7xE)_DFeydhJ@DW}V#>L)LVIz1m={H^>0{ zf1z#nO-VG)H`wb9)@GI8Ufuq;rMQ+gy`MH_R%gC;M7;@bS4?Cijo>NC~Nu$px5$v!*PV9h#A-Nv0i*XF}g8B(LZpRLxX zwyX6W);_D}Jy>^>hqMnI(%vKO7zFG0ZL)s5++o|iUjBRh=xoywk`8D<$o?l~Uud|BR+UPseh`lDirK{x) z1nu%MYuMg7W=+boxrTNu@x6vv>bNz1Jb-_@q5Gc_wDBVk!}dx8{CGCkU~e{98x4NR ztVT_O=qsPiVgPGHP-a8rt&q`(lLAI8moX!y2jc&Hq!u{vhCEh)`}g( z^QCL-*=w!BHPUVU+Uj=fE^FIyGG;F|Sb1lSZ0PKX!C2;)RcNr&$E?&b8_%=V(}1;j zMBnrgecCoF=OT5#9_4wHKHg1T_B{TPgFS4~y+75qJ=4&#gO6@zkJ;NkXVY@W z?26AS9DxNyQQ!FU2Nf5M)<(b_y=Pv2-wpS1VTe6rgAdVAqUE9H~^K6~{>Yt3gb z-DvIjO6X7PLf!s0*$yw8%HVR_W{!}=c9uJ--E{kioA zSTooN?{#8tQI8MXzV##e7LMqfJfg2xeAqG7>pq;DnmW7Y8XBq>u!{|i_=jZf`JwHI zKI@3St=~V|xQETxVeRS~JIeE3wT?#`p4~o;4c=}@?loGo$CKNQxEe`rG+KMt^sO~o z(~Ux{7U@&l#|PKk_95-N?^Cw*kp^E&z_OzD7~Z1c-{|S=`gB9@J}wTc7kxQm*ei?p z)w1e!)x6Gpe&lM$kGph1lfr8CS#gPL=dq`B2IbVhc3j@4RF30HG**!Jh1JjHyTt3g zzC6-!==JDP=D+-V+IBy!Q)X|g!JfO;${ow%dFA-@vFx?h!nM^)ax1UwXVd-Dn<{RXF`>>tF%POAWaag~jwC`oVf4S$rkGa)tYrn#A%D=1a zf4}U{gk8jce5t`MG~k^Z;tlcC=Kf~!^vQdOxQ&yK^BzL2WkLLCaL=#W`)#CooA*9i zea|1)`#fIm8+6J0^j!FIe| z!n;#mYdP`NwpGimZfjciX>&5Bv&IfPWc6SqwVuPCtJ{6p^Cop$j(9yIf$Z{NY|;_kb(?LKJVly+}B>2+~MXV(`RdRLBF`RdKf46%&{8#fio)z{2veJpEC zV(bLW#5Fb-=ZzY{l1Nxm1_+-+gi2FalUfqv2XA473P?fezjxklCQlHJ@+N|Ux)5#@4&U= zLW8}GW3i#Ba74t)JeNuX9Vnj##(4Pt~~?R&zYkpj~qx)+cxceXJ|! zQ}{Jy`>3{MVVFSo9NO4t$MDt&pQTEB?Hs6G&nJa>*F4zIPmniN_#kVcp`rSz13~y&mnn*RHe6dJnZX zr){T}cT;DVm3Ln6tL07V?6UH9>3y}ltvb8ByvqBs;;(9RqODzZA7AboRQrCfo_Bno zYTf!3%z<}K!+W30eGfcO{nhutNn)3qJ4F4}+yP>jn|u40_3c)3=ZRfz?iBS`b01|r z9Jb!tB`*@V=V0x%=_q+s&tYSzuR%w7uI5b7qCRv&eeaO(58)$4ylHgaFpdfH&Zl+m z@2mMjb@HkAGmi3n@15HIKGNK+N*@&D&A0m6vOr9&JEgOO`wa0z+9T5L%?;gu=+s$b z*u4)Op!QyS822>syL$Q<*8VRccWs}z_uMzC_uSJqzJ-z~9=Eofd+r7CDs`y$@|TIJ z_r&Lf)y^T8_1;XKx3He)(0d!L;#2mEU+;J5zTN%rdOvmS!;S~lqds4dT;4Vx@qVM) z|FGxk8dE)owX2?^Jl{zlJ_l<5HSu|C;fTkpTH~}n&J)@O@ZV@_=$tt&UnknfyY^#| z>h*n}?mIYEx(;b?Q|qE*#iz5A(O&$xE^f-nKzqNaY|kSNCkE_E?^d_9DGVF$9p`!7 ztKENF6n4bpDXHge>X^f|F1}N>)qqd-P?b90z2f!O(<2S;JqX;7PvT}Y9;){!&n?;p z^>{7)f;QG=9j}XO-`Qt2JV{<0d+g-#xQ%ZvWgEH@)~++-4R2lRYtZVBOJ8qIJ<@Q= zdqF*F{yLxg1;kg|XWI3ldMvB2VY@%C^||DG>vi38=zHJ=-Dk~8T-$%zcB?h0$4FG~ zqrDgG5>}flEnh2*t*!@k+bc8L_G)vqt+Ok>hE~Tu?0G`Q^5*8K=SsED6ONNeM|rMZzx2FA+h^K-S91l#=dF(`I6rT{T^r}n^Ye=CJ2*ct9MV1` z?Ot7Ueva!5=jV0&u)cG)=+&8aeh%tmS*;Qu@f^JWb9z1MHBC_%Ce=Of*F8Tki`QGj zk2Kuqd^V3S65xJDz4P-Vjjx`Eqk7KEIu8fbzJ~K~=h+7ByXU2pw%wOJ531`~UGt05 zw>A&jIV-K_)y_9#I=ig%O;8{6vfrbYe^#s6r=APc?NpD$HR*!}tc`c#z0B+Ial-rw zd|kwzIpN%A+&^xwov>zXd-;TXQ`oZBud#PdSXimZH`ewQxfk^u&j_MR(#{`~;{bQr^O9n_A&ie+wvIKS@1*X3awm9~J&XV7fqd}+?-23S z{U;3i{TJz1+urIv587Rp+$vVrW3%qF8uj>+>r6e5s`I9{f4`qsa6czfcw}k-_p)Np zJTw6NzupL_x{{fNmmQ_yZ@N{G*4bHW05trF{=7 z`Q*Fb5*MKoXa3yr+4%PI;i+|K{Ii$`Xn6zM2hII0<^dY|!oyQtpL=Ag{fkcC@RyJWacb+~sW^1? z%MVYbq2;e&|3L@7iv0{-hUTEY;=@xb&|&B*G!K>fSHJGGm!Q%<{|%?Tcl+U~0^)`L ziFNsTY|oC<-m;5zi8u_E{yWgP#Q)~RyZ_6Hd-t3;_$?=%hl)S>A5OdtmAF`T;*M`S zao|6lI0dcNx9|F)5^sFRi7QZvryD9$!x+B=mAL6zCk{a+9&4;j#lRP!61SbGObtlA zP>GkWcj67GOR)XY4=UT4go?l5 zw(q*_D=&50x1r*1`ytFDGzy)BhCq zUN}^^|4yg93o7ly&cRNDKXIcOTX0$qZNZ{2P8hbvR7Xzzqdd&G^G-M9#qe*RZGyaOuv zB5pk9#sxRthswAecRT(tRO%dal;kbft z9x8Fq>nl_Hh$o>ExAj%I*?$96)k5u6vGlh7^bKC}pp{ZwUY8=8Lr z=Yju${c*lB)dI~xJD`oDm8ouM2-*XULBqnKz0i&^)B){-#-T~*7<3DofHu7e#~ri_ znuKmb=b#nnJTx$l{Q~WQE+soMcG}mW(!K+og|_?(_8+u)&haflR}eS83;P2agl<45p)yY2uQ}~msI=R^ z?zE?&MZ`JiHnajQK|AIvQ#;V6->6LOLVKZ-Ck2)EO}D-5wzvPb(_Vs#Z~u3kJgx6` z+QU%EGY#E?E5E%RjgBJ3fci}{Tm#&&}nEdbQc&I~(g!VxDpwrMO)c*;b zcc3BYBy<3pg!X+B+Xc1PaXy5mK85v0-Zx#cH`MEIq}Aph$k{E}?y{==heQtBCEdIdSS6IQ|juLnV)Y8}o~J30lnyT}K@HdnfLL z<`M5fx1r&G!1)=v3>AL?x`(*yADuV~-9=o6Hf~`)O4vWp98~-p&}PKl-*n;us2}k@ zv;!LXC!Cj|D^T$_|If-)7vd09;wUsE+>N)OorvunhsU8o#IsOoUvb+r|Lnx`|AKXg z_5yT3_^#8h1Wh4s{5L0VgH9t(xp5YnM7-g~o&S#YiZ}|D_7qh5FGHoy$hVySz5n6F zp>JdVf#;yYTmB2{1Mv)0;w*Fyad_W}i_itck^k+)V^HZ|bmM*KJmQ7#IDGzL?0>{f z6(??oO8J5cE#dBlmwpwhqDx>)6t&?UsXZrs;!aVm#6 z16_rdppqwc?Bdi4;@0C%+-YB&+C*Hw?&8!Iw5{>tR1vxl6@Tjqr@apYmSX%9h5hzFoM(0S-C)aP^jlh88aRcHlj-{7>Tp!EZwoq&xbZ0$r&7=^=nQlZDtzUs zPJ3_5#i@C;FF>Vz6}o_U7n+5(KJDVvB6JffeB97P#G`$Y$q;3Gl=b0Ctik1eMM-s{T9dH36=bPP#Jd;x&&Q-N?!kS zoc178+G9{@Pq}g6xlY^#mHuN;@vXb)n#4?1=9K!w}wPJIK= zW%$z29CQOJeEF2qUV%z`^t96+eE!9$HOULzgswujpjL;&15jy?L#4gowr6j}dKTYp zSf9{#Xazb3mGNfX_M+P!dV$mK4_=&V{0e-~7H9?XMWi6cm0SHH{Xuq68-{I@@4OEwy&?-i3?DfkG4CVIP!{% zQ_YC?pyD66%jxg$apE~>NI0|$nm*&STd%x06_yw({SvRjagMkGmADkfagI3lY9|ig z?es4|r9JQ(r#%FfIO)doP#I_4jT?W|8NUZAd2`TC@Wy-G7%FkujSFX;{jvuYe=y?2 zeNc&apfaD~*E-`YLwgYiU+3^%XcTcC8i)40{^C>;nuALJq8r;kj^hIDVQ3Dz0$qo8 zMzKDjV^Hg>xPE+t({JTo%roL5Gyv^5hy4bPLM3k!+KPC=ZQpm>{V~Va2^C)iD!wf@ z?jCaTC85%uflA&DXgjoUztgX27~6@s6B>a|L;IjPXcRg(g7YtQ6Ds30{*=?+2bK0o zsI;fw=(MjurF|P3gZkq*Uqge?IJ6Hc{)8KExN!wq^*!M51*r6ooX2?@nu1Ed;3&@P zh!aqW=b%Z%oo~YV2bzH{L9KDTzCu$_@hw2t5zkEEbrIU0xHz>3EkcFc7o0c@wTsCA zX3Q6KU=r&A8h!}t5xNJBLfd{C+X;cj(cc|18csuqV;sjLU^bC$q#MaNderU7!p+RWVFW@*6KUDlV zXgA_5Xc*f5i`c)=KB(}_FJZqU_P^7Kle3O*2P)kDWhdVfRAS$+IQ>_k5^uS21=@!^ zkzaM%$Dk6=x$!bo#tqInya<(X6Ys*jLGw__w+)RWZu+&0Q)AE`Xac$q6<+#vr@e9B zi4#!qFG0m$fQsMx4O|C6E5GUZdNNoSh_ldT=msfz0V?CA-i_BK zv@b#P&@yx#+O}|UssL?#&&8gcC{*$dFFNfzP-*x5fz#dwl{n?b%TTFf$BmPJ=;ZDABV6C1eE=%?nwD_ghqxCi zvG0%Z`iXe?Pq5yj!8Vx&)p50Ok?823?2lLJQEo6&w%Ht-r$f-+=zLlXw3^Sl5W-A9msy zXb0lJniKmzhI$aEpi<{<9>*!-zK=U`0vbZx@(Cx7LVFOGp;2h}C!O{IXbkZzbQro0 zjYB8b9q#*-(>@GMqJ0iJ4c&suxGSG_+U?Ic?J=mdR|*%W((o;R){UVvh?Adl;u184 zxOWr#0h)sDK(k-Kc>$XHBIaWo>mMrl*1m-E5#pXNW4@s!XcRj9Rpf#Ci;l1NYfhd` z=seonzK-<|O+qElB6Jb)9&`!X@(rweXdIe{u0X}N16@Phxb3u0LZy8kD($;&d(+=L z?GdQ7C!msN3Azs5bb0h2oc1)dS_f48CAZ!FN5|g-6)unB^0k(A5r6N#&T6Y%XC;5N z(du4lwDR9>w3=@|VRbg2uoB{NACKUX_`*pqtZK@Qg=L5>DJ+lY1%+>feAWKaZv*Wm zHzhZe9R7Ck*Q)qC!MpJ1ZGX4xmm0#vA_|MT z?6?eqM@(UHJWt@y%Q>mAX*{37pVu#~usLE`h2`*Ea(eyN6}FA%YR+AS?c=%HuklOH zyvg>p;JNhk>TFY35YI#S^H`U{dhlGGvxv(gnAb7Onb3tJ$zsIV10 zm+kQ$eLqsGZ4E5$#=6bEOoXM3%PLsxkC6$;BN_k z>-a0-Pae|m|Lt`gL0tp*^VUbqty5|l!*g|gOe$;|&($?Iqp(?*d24)LVGG0-6}Id$ zua-51ZQ%J9{ybJxSP9S7TJ{unCF@_dy@DKaPV>gIzgj!qe0UzfpU2u17Lw=6vDl-q zK0H^qVL)MVJXg12+GQcC(T-E^c=P5VUaZYg0?*a?lh+7& z$lNBu)NM^E{nB`z!Jn6N!DZ{Hvm~|N;j>!s=jB>+w5!rjNR!q(8fh&b=I6yT6)ga90#*#NctlfRrI!D*m?cSH@q{owa>G58oKt-}d>T9ZF#o?Dt~W7w!T z)aTO7v10wcJ-w1K)jo$k_l_O;((+-R%h@P3hu2SSJIZsf-%(V}=lRvP|35wVYGL*2 zK8kvMkK( zuC9TrYvAe{xVi?eu7Rs-;OZKKeGZ2ClAwt83uu8o0U!uC9TrYvAe{ zxVi?eu7Rs-;OZKKeGZ2ClAw|9jVf>3afKxibTmIKGGa&nK-m{0-pm zt~>5{X`t<%!HKbZM+VOaf~Q|{`nFTIc0A8{X5D@o-3EsS?;WukJsrtOo1!#6u4x2JeL?#vL8@o}C{}44uCJ{Dsr^ zUKokTPL0H@YBW4JF>IZVJ#-#D9Zifod<_aOipjSVISt<$&P^Xk)RmWrIGBe6;A^w4nsedB`<3^_gf2gk<;A98wn&wuJ1 z29gxj(Z)xsl?^^Hat_suViYI)y%Q7G>2sqGJTP<~Z8E@MA~8O4?}fzB#32i=?6x0w zzr88nv608|>NjZc1mCfd$8q;xC&+hioLIgkV_EVY8F?J9elrK(xxsg6oY?tyB=TJw zC%&$#@*Nx}cD`{W-vM%B=ljKS&U0etyM(zv^Nkzxf11Yrog>lX)o%tl-#Kz(`Gyew-7~&}B#-0OZwlex z>m0=L%_97JbNBl`$E_z+WBKlpJdRsWtg5VUCzfv@;rqD{c(MHYXzg*l`VA;Ge#*i3 zX{FV+zV*l}q27F0_{P(#mG~IqA4k05&X;iUd-H2qC#tc0*UEYLtT(vNM7;cq?)-cH z4#odI^8XoPZ+;puKQi8yGX95@*sITf@b8P9`dXFv@8I`h&mU8`Y@auu@{KMTulDbi zuDAY4iM{^oO8*MD%)hMQ2CKO`U-BI@=ka*!dH^qOz&FGsfA!x?RSnl$w}E@Hm%ruD zob@U5E%nKF*qq1XEcxD;7dK#i3HSCt{w1LG1}VgS$ovcU*0Y6g#=Qw#VzD(K#(xyV z>QrK>&&uiJN8R;qVZFZx{*dDT6U1FgyoR`2iT@69j}lA%PRc(+aZDLc{Idq$M)NgG zv3LAhIDTb5yyI84NAk~+zoNv|`S|nNd`P@jryj|_=YEe_zA5-kZ2v5_U(ReY-#&ba zu=OsV8~d%7$(IV$aq?)mr_R+P;v14;Xmbz^%WjKYq}_uERen1Rm|w?15d zJlnv7b-0{w=9Tp<=aY;Q%l?no=`VcNz;gy(BHs5eCy`Yi&iR4&PjF42k0m3q3 zUyl5Xk~RJ5>FWT zBJmv6!}(I5)W<6+<5kb!e^ujB{}RQ^)PGm$FaDx|_paCS%Xr*>miRu!?5_|X_*DJ; zG2&}9Uz|Tj{sQ^SwEer}uMm%XTCdN3y7PX-yS`Z^-ujvP{x$>eGw?+NZ!gpze};IG z=7+a$hy0<>>iK;%A6>-##N!5@`JA4g`)?X}#_g!B+dfza9+<_X#b|j-%5Oucsua|@c@m-{_g*w*B7LGyTscm zUx@s@f2Zf`B!4&gbL8(Pf0+EciGs}eD2>${siSu6HijUB>CIFq>rB>K21DLe8a#aTY7%(pEK}%18@6s{ruC! z=V?B;p4qSH`Q~VQbHp>mO9tNbRXsoV?=tWh@hoi*=bK|c)mtE*qkK!`w~KoHYvj+7 ze}Mc=U(@>+sQ)VUZzg}9c!+p`c-Fwz4ZQv9R4?W4HSnB)7Y*G0M*Z=l#J6ZZc|PXI zze(G(O1wzCM7%`fu|Kq}*S|;p66GHt|1R+v;$`AXb+}xwFXR2H^SGgUf36Mp$DD_A z|7;1|yMHGBP4agte&Jn8Ece?acKZ9Rcga8IbsygSwz85>?vF|gIk?}}a#5eJ+%xp+ z>rG|*(WZ*;lq-?$1<|SnfYqAF0jH8e&_C<^DsX63cjtb;c9E zVc`1)9{8?4zr25X41A3E*uQ)0(Ybz9^F194shRHgOYA(FtM`)^C|~3ey}l&nU#0aF zCI1S=dGfCjFA(1|@D|JY9L@W@Aw%_XzQlF9zd~{Nf9dnLsnjFKL*GB@^HEXGKe8U8 z#Pdold|inpUq`8yPk2y?g|92I@C_vv-t|qrzul;BUs0)F`nM4;5f2-9!oafzzG>i% zC-nNczMz2*7WLG# z1N!yTtbwl@xOJmGKIab^_`HGd8hH4o`s0roc*el<2EIo;LHmp6Bif|b>!bZKP25jB zYvAh!UN-QS$LaO)_&vl^w7u-lk-vquXN!0%@rr>59{`frxo;C1&1MhoA{qa)zYyrhVyh%XxWrhzv!Ct?j(F>H^?I217j&%CH)r6R2HxIYKmP#nW}0uFuQd7F$iGVSyF>m?^5@Cla!RkaoBRdx_mDqA z{!Q{{$sZ$sk^EcaA0vN>{Ptl7E2wv*a(6e~$bW;;Y2%XX^KdwuxtGJoX2l zuWyf!@=X%=6VDiU-oSSa+~1+s$K!Vy__TrN4SdJIn{KUNU#Ec&7z!@SqP69%3!@VtTV8hG=I>(}3B;AsQT5zo{9;Q87n ze~9M0xl^yVn|POj$B1uGJ)Cck{9zie?In6W5#qfDK4aj^2EJq9O)srqU(mp#2A(qT zB?I3w@a7+?Uw_oV=L~$6c#+lv@6YCtUQd+wH}M$poPpaf)AMux4g()1zC+u?`7-2> zQ@wfOWy+TzfAEL(`jf=xiKmFK5x2jkzrSMtF8Q0tpQilbAJOZZBR)nvK>0G{&yYV$ ze2sWJ*acVyTsdw$0=W#{1Ng`lD|U!9Qo~M>F-~*h|f?yANlP&^zHEzZzDcO`C7=IBY!LL zE#mFO?QXpu=IsXFXW%mio+rLQ+spMt?$qlI()LUe4-wBAc)`H;4ZQ6YdVM^8*uWtchm9J_)5M0F!3(p5#lie&lva)@olP?$7_9+UT^EG^>~;1N2%U6@(2Dy zzkZ34f0_Jo;sxRf;(G=jxm&NF`!5>!j)Awlrhfh|1CJT_x`F$9>yIBI-c9?1=Oan} zUh*et{J{7Lfr z$iG8=Kk=rY(CeF_d@bY;lYfrh0z_52IuZzq3}{6XR?#Fr>vi2O}&(8pgPe>eF% z$v;bRnEdm^BgEGXe8<3jKdINBr}}xk5cxOAAEo-z$X_P^GWlo8Um^eekX}!Q{C2DU`ms%ZAMuv^^n8ny&rkjY`E%rN zA%BMaYvgYwzjeP}Pdo8m;z8mw23|67->^PD=kGA^UIR}U__~2xBlYX+Azq;Q=Iu$6 ze~bJfn%^bzm&o5u{vGn~kv~lSrk~QcFG4&-e1YO9`4;DHD9?dASm z20mxt1>)_ry*!?EUazN<{At?04)S-Ce~$cd^7oQIL;gAPXNj*6Un0J3;P$A#z1+Xm zz$XoSnRt}8m&YrSf0+C^+CKl7UVno8tK{z`|1|mYmh2bHvj$9{a~0(#OkFz6|jK@ipQ( z8jt?5VxPBzkXB5w-trGVW-!<^i&*=5|s2Ctfp6B~@_QFO%I|Bv5^;t6ebf5(@c09Dxa1#J z@=H8T@fyX}N4@pr{C?Or@i6gS;&I~p3jaEOe`Kt3tu>1}yx)h}LF_!Pul~M&1;2OY z{GQ1T)!%=bt+RbHU+V^5uES+M7O38)9`E<3{MFx2+aTVea2elHeoyO0`A?}JZh?5) z*Q{_EFRuLeC*=3kRxm%#_WG^coc|OHT+V;*L;O3+`0{&d63fqq$$$Sq#&3CBZGFh^ zrAh4l-kJ0tP`Jdreufn;|9t|9)&D-hiW1B3ne8g$%kP=(DY5*XSy_qK5brCo{GM4w ziL3R$yH>x{xBZ=y_%!_rxT29`5hsQ2)HrU&fz(yIxO7 zIe$pKE5rlusQHDrDzW5CE3xodB^EyS^LqcR5({7a1)VP|v2Z)BbDt6mpM9s!GfFJH zZ%*ga6zA*Amu$~o9WMD>eqGPss>H(g=5@ZW#KLEOQ|GfvT&+K&^8t!m7HVAl>i(Ac zy9|8Tz~>Eo-M}jb-u9mQ^+ybR(!etYo+rLb$0yHc?7e!u1?Bjvu7Bd2#B;@~PVBkdqZ~f!?^@k07(ZF{MeBe*&kDn(#PsckS zFWrBt=L^#DJWM=9JZ<312EJ+F`v%^+tk=)=4H)>EftL)t@z3gy-)`U$1D`bTtbzOg zyncP11|Bu=X#-z2@V39GU*7=nEUjnW-<#y`ruDi{Jd@MMU#9$F>c2?-2=S)(>-nO@ zJBb%)Jod-PzeWBS<4&_Ub-~X5T_9TgS5-(G}6#4VyPZQrEK1ba50lglp zqF=wWe~J7pj@O6!8M_ z9Rm;kwO$YRj}q^rdfA^Le~kQ_R9~L_W8^QA-}<0luk|;2oTUCG>Ti>Omv|@fGVulC z%QPPQ?GNeo706$q{GH_ABES7S{ryIY{66AK#Qnre1|IydUO)Gb6E9J{>|Z1Q9{F3S zzP2@e{8r*|;_bxe419&S@1lPG;e0#fZzg|`#_#+H)ldEq`4i;tAb&UcqaW4thsYl$ zf0q0a;@iY~C|{KPogdT3kB~n`{sHpGiKmDsi0>G9Q(mu!`v(m?Zs0isFB^Ev$LrVE zW8iTE&l-5a!1oP2@QM2MhYdVo;0weDXg%@%9QdSOPmk z_(|e%+FtfAlYf%@Y1+PR@~6l@NB)*i>GjT%KSTa*@@I)Bh%XV(82Gw@+n?6!<@`Yd z?<1a}?d5#C3 z8Cdyz?=Ja#8nO2Iw!&q+G5UOa0QtPnx8Hb)&$pw>`11L-#E^r}w+qVr%IDh>tDkQ- zeZDq7GQLllf2qG&;qv*m#OmkU!%8flZx1Nr%jerMC6>>(hm}}9-;OJ>e7-%V#PWG` zFMS>@vGZ^~kM6GM_0Lhfc-&i0&gWk(?OuG7CEJ%Fe=G5I;_bxC1|GPm*T?6S!a8z z`@_KV23|Js*6-HplX`i7^ci@n4wroUrCNU39{Zbmyj-Wh_=|P8G=anEWGu9>wLwKzhvMYN=^!@wg3K1qC;_7~50nfw*nAI&%D^{!Gr`;>RSbFR-<$nPUw zB)(1g{N(SuQ6ImBc#`-o%T0Q|rfaPW~YIcgY_j-qfUz-%0tp z$)6^FnD{dBZps%SfA8b;@rTJDC4Y|mDe}k2A9%c;KTf=x_$=j1kbjN*N#Z5q8OoO; zf9htvzBTfv$-hJXBKhaYAMxw?GsI_zXNj*8-=Xo?zeD~d%GdM+y`D1V%aK1p{#D`` z;?}kL@slV22KfubE5tX6cRf+B-$&!IKTiH;@)s$8hWsVsCE~lpTb`uX$GpqHV+KA$ zyp6Vp$4fWs^+d>Drux^(zw#8l{{Z!`Q2$l(+o!$t;JhB}5uc-cKJvFbRUh9^yqov} zvWuDFe?N_>O`5p03x&;|C3Vz`#=mUNZ2OXVkARV&G{5Unagy`;+G*7trg8(f-;Z z9w%-+Q}55b&A@vNeA2*I47_aM&CjY|f5^aN2A($Xf`NBGyMBF(#C_L!=TrB70`0H1 zRy|*Wc$j#S_?Usu8Tg8U?-=;NEqeW2-#qbVnh*B-o}=eW(e`!_-=w&e@~5f)Hu>j> z+t1a<&k%1j@LmI-B;G~!@_75?&r&@tZF;?7%C|)RS@P$I=ZN<`PtV^+`B$lbjQn}x z1>yzbdj{_R0lgmXA2RS5@dRxT=PQwalj?2!LA{=7%2yW<=lZlvJV*I_cpTd04C{H?^Z#EX=#o&13gef%KtZsH;0aRZ+tUZ#3D-!}Q}MsI&OuW#Kne#fnP z{bAz6#3RIK419%n6V=1{rf<{hiPCsk;xXc70}sDI&(Hlc#6wgs`v+dE=j$PVoa)Pv zKSKTl`4gRb{v`25;scZ~Mg9``CGP-Jc;ICY~ieMm$C1v45TX zv*cf*{CnijkUvNMmLJl$XOaA?JZ{C?s|;w{7%3_MS~o9f|wv2MMd0rI!f_^ae^C%#R5lJW(~-+ZS& zev14d@(+-|=@okaS?b?S{hP@jCLSW5p?neY7s_I8thj{G6=XNbp$XNj*6@1^nBzfJxq`IjhvbDzFFIpST!Ek$91KiMajadVS2>iO*6!>>nooE{&HaUM5~5 zo}uyBpN{JFS14bOxP6;hH_y@jJ;MC4Y?kG2&_B-IOm*{%!L2l0QNIo^yJ= zN#beZDdKAe-WJpI$EhC9mnQ!V`O`c;`RB+#M}FUsUQdR2koW@S%aVVF{7d9tBL5!w zbHs!9>GiD=A2#p};_Flo=L_Dik5?jpp2sKu9{CI8&yjzVc#(L8@)gP7F|60yc!PfY zmdHO({$1il;$`C2h(2B`jmQ2r@^_HGLizj1Z@<9X9_RHkNjyaPeB@sye-HWnZ zefuKhZy|pt`CEyP5pO5HKzxA4V}F7CLCUvBe2nsi$lv=$eS5o!r-&yhUzq%P@<)i5 zh)0RralIbq?FQaUe1_`fe3Rs#BY%wQTO@y+c!Br=NXr4&qtja|XU@ z;AI03y-BZ+$4?q~*1$Imylmj@<9dA?G~ZnBF8P;eKAR@=d^zF?1J4-vhJpJM^~dir z@L>a=Gw=fOBF!hy$IJ!2-c_2fCE|JFO%Lk*nRgm^+`tzNylCL|o9oxtVc^5WcW6F& zds`><@d`9w5#pP~Ck=doc$wa(sEkgE8u*xjFA$%k?cwn{ zruBM)RPO-s5b+rU&l&ihfd}5E*O#L0;d&Oy-%a&y5f2l${+m7?^L7ItHt>1kv$Q=t zUf^f-dNSmXQ2j~rM~N>GU!r_5^6!v8PTZH$>sz6G3G#Q7KS?}EJVkuL!1D%PHt^=3 z)3=AmA2#qb@jT53=i4NInznbJ_#E-T+x2>x_YmKpdf1;Oe}={@5YH0dH}Jl9==E+> zJ)CcW{7W?6I`JIwvVr%_==E^_Y2v$7FZ-9sze@FN63-LgCtjiP*x&l|di@2;H%5Gu z_+us`A|4=KCf-gw@o7DOC-E}zZsJ{^ z(fjujPZN(4Zz|~hhl$S-A0u8OK1n?ISv~)>!e5D1(x&X6Df~X2#Om+cbtzodSAu@u zZUg(@`+d9jUgG!dR+aJP_w6KxRDa)2es69~iRJg^@=7efH@B|D@_TayC6?ct+fZWp zy}1be-kikF!}-0rRXUzpe?dP!8*lQiubuY~d_1<1e^!ZQKIfEJ=4<5_Yx#w*DY5YG zw9dmyTUZ8BtQvU9!2NUe^LHEgq=9GaaM_-|U#^WW+aFhA z;oHBW^Nl+Bq<_W0yWUmHcd7YI8u*rhxBpuG{AmMUHt<~o@A>um<7W)KXyEp|o`1N> zTaWJN!?Zp-$UjN`2>H|GpCNyg{A=Wo5#J#`NBQF9_y2~zeGBAIkUv5GB=LFT%akug z{yh29#CM3VQocFzCw^1ko(%Cd;#uN5#Mfy&_P70(KHetzmneTP`M1fRBY&Fw>EG7J z+ok@i)PI)zdEzU?3&e}W1CP_!Kl^>Zqu19?{!PjsC4VRRi{zgte>eF{IX$v;ef`$gV-I?ey$)6>Ekoo}+x-9eV0`D2l0MHV)gyZ zoWf=NNqRrChI+jBGyiyr_cPNN&w1Qn$@>|JA=URYGXB7dKEGLtBQN&m>n2Oy|D=@u z^8RN=iRJxIT8ZWT&#V&5`=2=_miIq>^!`U;=i$8nS*lZy)E{`CUjN$f>#>jacUsw> z(!W6d;9{-6@J=O``Cm|C;aMdX9{fYSf433~-%(=ezo*32{C}kPU!-`;Fdr2IZ~d!U zei={Qzfw=cz*7dkV&J7ZTpZB$!WTcF^I=1Ma|XU;;QkNR#*^`Re~%e>-oRT|>*wz=@G;_X zzjwZI&d*$L)8FXhMV0fD?2i!f81VrEPZ6J?dN|)U`QtR6|3iAc3F4gwo-ptY18@Cs z{qbW4o;L6m11}o5wWilMNAu0|l_Yks-}F&E ze};G;@mGiG>?;)Ni9yjn=179)lZ3A!2>-BSe z9R@yT;7bO+Vc=y0Z~1ur`nwH0W#AP9ANYhmelx8Xp3iOa7ic}$pVaeh5^po`uz`;m z_=15quh*}ymv}492X9ZF{6*T{9pdRv>Em}${u1?{C;u*S>(hF^Fy$+gKS%xw@gngk z<+D4z^NDkRqwzEP_`~G)k$;%{e&T83la#N8{43;dC0-<+qI~V-A1>(I6C}PtJVd-~ z;LV@a^UqQ}oNtBvOXTmS@tZ!U=MNJP63IjW-ynaK{7dAIeqOJqNd05fKSusI z@fqR?;=2akx}n#@{riaTP`&I=kv~cGY!FWo-zQ$C@z~$_w|f276TRcnxqePl{sHp) z$UjH^4EZyEr;pc6{WH{mf&5wG8^o80mkqpcQ?G~nrwzPJJWSie`TD+~k2g&I9MzvD ze}eq0h$i=O_OT`S-}*LjI<&>h-n~?34P(*~Y1@NENc{#yO| zx(qyS;PVEaCtjib&GR+=b-mso?axi(A>#W69{7fypZj+kc+9|841AAxlHonsPyvtwx@5AOkP|GKuCrYe-p4h%pk;us{+t_W*!joxIew4-u4T2>-rANe6Kj*%h=mYB2s0sMLI@#*MhGE<5JG5^2_X!PFvLO# zAq*je5JCt;=KDOo?yGa|*W>f}?*4`E~1%cdtX9x<>2sf3?4I z9rE^d$OqOTPx$NqUw+0q8ux^c(&T*1sZ5 z-X12e3X>14LmvN6F#quM8S9W2u0!6q4tei7pJAA|Nj5$ zR~shp3hVE1d&jR0=2sKepXp)px-fbEI^-4WkT8}pcZwk}T8yBokPFVWhu=E9C`u$<@+A#TG zn0!r`JU^_waQ%X)VExL%^oPUpuMN|$4AUP8)9($_kJ`ocx4*twFIawDm^^bG@|tzX zJHq5OVf76!FL8XZKJ{Vx31RiC4AV~vlXry4Q^Mo}>yYO}2kR4_zHA-xhIPn0*C8KX zhdenZSijaVdxh7xH%vb@%pP$Qg88L~$#q#c&oueT`YOZh*Mzg*jg{cY z71|$`8@a6i(#?8M_M5_Xzq#DVWxtt4es;gv6_$UM$zRG(eJ@ykZJ4|^Ox|GRQojyU z{(Df__@!?GXrR;yh0gHeWM zKNw?J_JeVTWj|OLwjUG@U7_b)`@;G+JFI`J)(`qqm+=>|S3{V5O}y%h+?%W{_KKUL zEb>Id;t$oZ^#^aN>We=V(LU!7qv?(OTI`(&+x|*3aw)&c^p|jx>Hj$P=fbZ4mj569 zS4{b#EA)J`Fx49EKdDb%qSjaPuM6V_V_(s42$T1RvDFvAVN z^oPUrTf_7R!}Lp%g7qB^lh=jG+r#o33DX}6)9(t?kIHoZ6IzcYZ5%8=E=-;kCQk^H z7l+Av!paNRuMN}h57SQy%fCBJe<(~pB~0JjB-ozRFnL;-JUvWa8zvtKD=%EXGfY1- zEWg1pdCbPn{-OAj9j2c)Gg$wGF#Vh`{roWfT;5h0jJO$2~HNh+Q6{>L0o$c`3*{kS9~1J5OJ|ndZM8@`A5oz4b{D z;jbs_$LgQp?Souc+8f~|cqMSF;Tz#@IKh{{1oy*oVj%i|!h=SCBLp$Ks(v1v1Uvm3 z_#*{&+?07Zuozg zI@j>FaEakuxZ3byxCu`8?Q;{{W8@3@_kPnq(w|XtD0K9iIC?)R#<0lyHV?|<=PHYQ z!0Puez^KH0FyTMbM4m4-#0vt2NK zu3?e)8PC9yp>|beED zv`a93sbP_i7?$)=nW``HI>REbH$0mEuBtEcQp2P98y?MnH`O1_-|%SuhDY7Jyu|RAvFsO2d=a^D=t}g~A}=v=DX-L|--xA# za0&k|@kplfghM8HEj~+r-9}&1_ZnV=zHp||PuL?^|3t%*U-q6Vzku|sP5o|%+YX-K z<@@TfUdqCte!AhM=nL1I_H-J#h~JCvtA!6Q2nI;v3_KH&LJOJuS@<>N2`23a^Y?>-r9~)`6A?9q~F}6Uv1>4 zAQ$d3>8lGheJ%1~ll~rI)~~6f`GuZ$YBc)KAs0?TF7e`9xC0h_7e5A#{;0hSPi12= z1b3MFG#sz>5&c%fd!aAf>gRtdTzxX-jSH1`DO_sedm~(CxE(G&LF0EnTz>8ZZ_w1Y z6M5(g`Ri)K5%Jg$w)0P1QBXhLu=qpfi7KB<`W{oC{osK!wEcx}(Rso7r_iLo2)S^& zu~*GWn!nhq&Tu{Y!o&Q#>yL8m6}l#RPa#k0kM(Bq56<7jo=z_HuQ^qDv_A~LPWo0j z!LQHvaOY_gygXCCxYL8_lMIiWO!=_&#~$qA%AbuqGpv4MFDKs~`SMcTA0G;*ES}(1 zpdjr%18y{Y4V-kA%I|~g4R^o^XRG{MxYqELDV)ETseBu_+HgJ`b&kp}fGZ8(3Xd2T z`)1SMu03L3>3_!}&nj0Id8y$i$S-+!-M_vMXBz$$&W967C;RnHr?TFF*;bEoK3rz> zFM?~0{tCFs=zj+k?I}Gmz_ZdDK9x+@2C+?y3yBkh7{5+g%_;a|#@CaOOIB6R8GQ11iZny~UHCzp^ zG29F%?5Xv62Tp@?{r>s|t~7G-k8a}+;vcIGi@f>5p#KaT7I{~N$|p>xekML`0rw+M zL`BAvE zxH*_U(Xhx{4NLl!hDDxJ7fhdPSmZs1N9%7`#fC+mS|5~W7#4Yl;Zge;7J0#vVERJCqxmll$}OfBJVXU@;<{NPr56te8Z#V8y+p+@M!slcW1sTH}lt#aE0N~`WgLm zkvGC6)I;XCTj8Rog6C1*-NE|B8GaOfVY?s5HgYL%fnlj{**%)yyQJ?k_4`foqx^io zKI0SVpOx&7{q?luS8vkKL@w;=FZp{-n!o5r8QuYXVY@$%yI1uOMqa>rdn3}h{Xnvj z7a7~3f!>^(*Y|o?e z9t!Fg8U7x9;d=6M`=d2RF8Plbj@mH9R=;+6F#kHk8=)_3_2)hu)K4|M9s0s`rao;( zF7;_Qyg&NFHvhawg8AngE=FJ2>JJ*Z_eJURf=3iyF9(`e}pU@K2Pc-}( z`odPf*~q0n%MEv-FKqQQR|NCVGW;X@!VSirJw`71_Zp6x5n`Kv$z#F%OARNZFKowC z`Qt(T8p9dr3tRgqw+8i73?GWVu+?8_6M!Tx;69;E7=VIfidUU)bvR82PAu z47Z@)3tM}ZJsHfu-0&Od3tM}}5Z^*qXg?wG&he4*2l39alrQnmvB(RbRu=hk!y+$w zCMd5kEb{(mgYpr>B5!RA%DW7UJpK8gJjd|QGq5LY&-c1sRQ&`3YaQ`!GxBlqwa>^m zM=os7_v$+|{Q=0^NN>*vTa3IAxv)J?DSAcIUx~cmq+bFzu%DFkIyoN@dsZ0z$B+xV z^Sw{tMp*P+yzDgklD^CE_@ofq^S$y;t&ix}8r~j#;Z{HYqu`=9^?YwJTxsUZ8{jI# zkHFcl>3sMOT-O&o-)ll1xS#=fRL>2T-j z30}2-KV>0Y`32+I%;%XV{Yl7$?fG7Dm*y|!mm01_UwD{*cm0uvy+YSSZyEBquVcMU z_y^~2VoxWR`WJUAkM@V*6{N3)-T7WO-1JWHd~e|0VEQ$N2hbO`{@jW^T>0avSZY}P z#9mImA@bZ0^?Yv!xD&2GLE4)OXZI+d2Dcl&63+Z+f>-J5-v_tBE`2+k@v(9*+-mqQ zIPDXaPvf9;x#6ARxxEv-DnI``k;5+kvqk=?^7U|&;pK4hXDWXUZZJFqCw{K-DarUV z?8-}n6Y+0Xp7duS{uR1H{w@CISma$_DocMSex-bPGWIp+OXtJ=hVOvAz4UzQML5au z&u|8ufSSaU4Q3G!ja4%K|3gp6WKk*@4{6}y2?Keg%gAKwmhMd|f=MGjb`f z-muiS;|I-e=4|S3>X!w_Q+~dm-w|-)pISf3Z@{E4M=tE@FZt&VYW||1XZRNMh3)#S z;78Se5_t~siDr$(&r&0Q4Y_a!a@XFppESLcmu~n+^o5;&O8rWIR{j5N3Wt?fXXLYx z3)}rr`!AYa%Ih$^8~Va_Kh*SFP`}mi$>BORV<}(!#j(iC42!(Vu*fslsJ_Va42!(& zub_OjVUgGT9h5g27J1xAP@ZD=2ihxa*N;tJEUDz0=uJzZ|M8D4Avd3|H1c`Kh3$M> z8mZ|IL*7pM(0VU)f4SPoi;)X=MyNk$MrrywYm1{pg9k?u7HFY5gR>_z9Z+1?0kZ{g@f6a?#H=`~~{LcKtXPdxfrv-g-PpkoZlg z|D3;xJ)Kq6d6t=y|7z9?h2Q$9~-zo+!h~9-(~nH^o6ZIS7Q%X-X+L0 z!|EsYa`KyyS5A!`dw%*ccn$2BA;Z$X4D9XlTgV?`f5#$kH7xQD!?Wk& zUyKj89y$hYhTZ+w+u;@?{{ZeV@`QP`A5J1)Sx@Z&4;cLlc*OA2aC~;07w7B$2`9lW z{bpOx9>aNXp5e>jQrOk^VYtf3KY$wzM-qtI45z|9hL41Y4OhYOdux4K;Z(z4zzYnE zeN(2#@?W_^{x#RI$mb>o(fp zCI8xugYr7VBJVXU>H7?eyke7J`bxth?=&pwy9|rGcxEttiD8kq8kY1c4U0S{Iha1z zu*e4uOZp+hBF~r=OrL33{64jK_L2J{Q9c zh9!NH(O-gGxP*U~`SBHaC^I(jJaL85m-N+!2hkU{^L4wCi++b;sqfHyt#8s+%qOOP zyTbL9@AfN4!;QPfj;(hkzl1bRe>rktSAWUB!pKFx((wK03)}rl^){N{+sF%<51B^B z)`ty7{v~qZ4&-kARj{q5m+}e?Pue=fcE8fFo$7CoygIDBb|YVaT-fec2DjJrQr?i^ z67+@bex-ZIpnjj>2K0sP{-7))s9$cl9erV|AGcFbKi=@S=nH3=^>FsiLH%6Aar0U4 z!ga>JeMY{JfA2TEJ^I47KL>XS=I`N8p^NzlePLVwIwP0-#s9|i`J)E^b1ZW4N5|rC zNxLbFJl(L!2MvonYIoHac}bSC$g2#CJadnrJkPMm+x85~R~x>9_6pnmP~N_({}l2T z{BK+GaqH7!BYz#auw6eU@2BbiMcz;P?M(U%BacsGeuM3K`rtxMzZdevZ#DfQxQO)& zVTJmH_Crzot9}J?VYeT844w;%z8jCZMt>pyo@e-b^o8wy$U8vu7yU%TbG8X_tDpZ7 z@ZiC^AF6;GP5inaZZiBX+(mvaK1E{a{G)^Wq0|F|RV=5{Gst6 z&F>h}kC^&g0_Prq{rvHGH=KQpjyK7#!=!%$xv;%k4Q|H}d1*b|Y_qJ7Krqc^U3E z`Xlff!`tt`dCWe#Upf;`gkAb2aJrGV!&yfDHC$jgVMpw5cxSi{j`!Ph0^DTeVvmet z)jnd6Ov57YHZ1a9!y<1j)bv&4@13FVf8GmsAs_L_i=-D0UH<;&cx91y8kYQvP6*12 z4U4?Ru%vG_Eb`obiFk!P0#)8`u&d4pj|-)LCmy@p?+zne|G{1#qr zSkm_y{fQYNcJVzOE;&EAe$F~QSic;@i_jOg@x98(rMzmxQs34yG{4J8A7%XY9yo#W z^U;%dvkrK-Of@+RV^%+GE;QE22D z$c63tIeD?Bm-12!AB(Sr2`+?nwRTm5b$m-?(WoQl41mT7+s{TsSM=Q+~fj-~%% z=x@i8pY*?Dkrx{ld6{96r(US~BF{D~^5qpld52+uj-@X*@^0k9cD zd5%dxWf$g8<|A2uy7g+d(N9M%?AEI%!NsuXJAbJ)`V0B@I>SrR7q;uwtgE#?qMvX0 zUG#-p{rn>cEUDM)dNmntG4X6)xYh6(aNIQ-50=91w*}X$CCEcp$p6X>uR>p#|Bbb; z#mJ?;t%i?;`>xgeej;IviD&EY%6jIOSg+mBZ)ZoS$Am)sOwueRJA zOy6$!FZ6}2KUZK6SAOzt#GkPGiM^bBJLI{y>Uwn%+zD5pApUnboK62amiE+OUzdI% ze~7&ui+qh?kw+~F%F~uA-%tK=rawF31;|}{B)xFx3azJlZ&wz1VuP}jUu#(8b%sSA zcZceWJl?R#s|}01#<0kvmIc$t7#4Y@VM$+Qcr^W;Vd)KzmT!2pe8VD-YYe83H!Sj6 z!;-$vu*ehc3Z_pq+>8B%^U;(3{~j)dlYEZd9ecppK1+U`CjBeFrX zPeLy2))!a6z4yg>y(a&(yMy)3G%V#;-J|k5NFQ(f>3O&rxvRgFFC4lidfmu7;CBAO z#oO{Gm5Y9r;qTEG&W}|6HtHL?Lh(-O?^x>BaKEzTx6-i4V;)d>R2J)5#_MGM-K~$3 zjeI(CVb`9W;Y`@}N4e22Lf&rde<|EySn5-0^zTG2?E2#cxV|;mAALq&(hnMz_M|_g z?dc_bg6WTc;e6z-{*qoebn!i=Jy>tRUE|fh-78c+A9)}4*_Z^*K3R{cd{5-Uwtvd0 zzsr9ie@J^AOMUa7P!@TaVUfo@sq$mV-}{bu&Hr5aiAFB^g@z@+BEuqIZdl|khDE-{ zu*gRYi#+=&t&hkT7#4Z0;j^f3r|Iuo;4Z_GzRu{cL@w<5zYmUoTKh}#A2#}u-dm~l z5qXc{h&_oXrvGQd!^my_3x}>yd{1~*c~|t4O!`CNWW!Q^fzg-pliD=B$TJL!y!pAH zyu+}_tDXE5mIa{v2j5yE-v4IY#nkGhh4&=S0Me`j2q*Uc{RnqDSwS z2;T@N9vfQkPJvgz0}G=^*MlNIoBq|b2f|i3jFk`iicezcvs<`j?b953c)s{8;<)jeIupa}A6BO|C7%cCzN`1us@4@o>K&gItvx@MCHqHM8xPKG1 z$3^f+oyPyK;pBhSzB`^s{+DaKTLL!|ejr7DP*WMvGK0ao2 ze=qf4|8(?uUpLFI|7LK~E;@ha!BsEDjJ5Ykk>8{J{Wd(jui9_>Gnfy~*Z#g2?t4Yc zPd=0QOM6}W%iy9dbiC|XN<8h;{%?Tg_lGL{_T90V`fRTDk2s6*va|a0;c!~Bmfr|h zo}luCv&qQ6Wc;PU2}wHt-3X`WXnp?^`IDMoIs>m^oW}cnxNCyS-+@d13E4yZVfs0Y z7yQAs_hGoENY@9qoJ%E6(f00l9`j>YwAaQzOaI&jFQ~@Mk#YB=pYjd#Dm#jx{-Z!e?&H&gjO zm%G%GpXZ$nFW+D5^93xwujb;*8CAjc-$!ufzvIW&55K|rjCZGh*cHT!XO!=U>krlb zSPQ2yHe$pN|+l*6&nfmwLaQE5je}BV${9d5= zzw}S~wTy>Pwf<5n&2&Gxu5LvTMZAys(;U_Wxu(J=65mNPyPjd z{$Ihph1&joZbaZ;Qs3j@VfJrsJl+H+-m3HWGopW-mbdjyE_KNN_l0v7ssGi(3Di&e zQ`#4MGvjdsjZY`Ta$cQ@KO#&2Uk=u7AFPM~ENJzjvr3KF(18xB+&Tq`wnh zevZb=8MjcM6FBcjCgr8W>2r0yzXX=w1I$7$^?eskd_(IWb1VHteO&qH!E;%^x%T}A z#}mJuzi)RN?Jd&&m|Bm0UJRY5iNB@88T>wL9OcP+eI;!7H*4Vm=7&_jKBp}qUbDY< z<-Gtmp0DerZ{VQ^V@CHk(!R+{$?x4*@0ij2CwXVX@g17}CRl#os>sj(qT8v@KiWSp z!SZ`8iGF+-g3E8x_&TA1_J1^fY`<3xm#)_F^9CI6pMOpE61|ULcS-;J2^XIf;}!F_ z*!#3Q*sn(G`mzJ=BOa#v@@lET(B}%c@eHl+^gF3f>;$iDy-@$|0JkweW&8R! z!BN=D+3z{H{%Gybh(^XovC0n>j??|!?ZV7Y`F?)y!DWeBpH1!}Uh@8DCvvfW7A(KN zSLy421G`Jw|2N!pgvO_wyNQpRsDGXgckbFNBd(bZ0GBrV0TGBD?fm&G>@F#P%KgNzsp_BS!3)g(U~)6|ni+b3U+l3Xyyi;v|HnnXSnWCa0md`q zD~7yfd^Nx;A0IzzKe1OkykIZ&uPG0@+DQNLC_Ow(`E&VO%4>z?d-o}@r0;^|``JA{ z{|L+T_}LL5`P7GGylVWP1G`J|-w&4GtCjpD|106vULEi4a5w7%SN=D!{C-=muYbgH z=CiwWJ^wwNzDn0q`#;S5vrzX>&%kq6gw`8U{{gs={cVw--;s|{|9Lu}E`{a!s2t>? z-wF?y^?2l?!FVzY9(Y*Km+pqk_fh+8(L#JURqJ~y>@Km#Ww88Ss+JS!MVIIo#)r@CD_|T^WX6q>UV9-*!j(|@CfH8ET zx%k=O{_PgH=}q<5$KeXj4_tdDwGpq){JIQIZ`bv1JKXiM+WYwD=&xU*<#2t}zE{IV z(=>j+14pqRaQP3xxt+S7sCpj%*;LmHzr*tWP91F#d(BwI`QQv)Pu&ie)Bn!@df+DZ zd(Pemw=+J>eA)!BuGaQ-!tN4#z2DAy_aPlG2`>cCTXW$I+T+Um7+x?@*W2@7q<);= zxb__b_b|Vdkhl2f2XNB*+P>f5R@MVqzI^IS{NZ0xpB>?p`cVH!`%i|8S)Udm6Rv@W z%>MgdxO9S+e`*K)#rct|?=5g#jjku3fCo%}uZ7coXTR4ZBXD;cux=+G_e|zQ*G{;e=AH-+OS^soI_$-zWWuw*OW*yHn%q({TBI8ei7? zfc9`dD#I`DM0oCAT7Iwa0v#X!z)cTodAoi{d|IXB{aLtx{h7;e2zHmWFQ&(l_%pEx zZd^Nl?0h8)&VE_ze@~eHQ*i4KF{96`i2r>CCvYB}MqXmCjX&ZK&c8Bz-VvUgqV>B4 zuGvQ8-3M^HoCigQ#@CJ?Q^(7qy_TpD-vKw=rS^IYc9+!uOSpv}y>j<=vOeJ-+ckdd z-HZRcsq2$!*xo-{?^DL(_1eGF;atueUA(vxZr)DIPx;K19w>G%+J3SBi3byOtMg4M~rS_?Yt8dl&0iVKA zn`?R7eog%4`3l$Hhr^YeXJld<@t47d-1?fXKPG+4evtV+#ZP|~ zEZ-}wgT+4|gbQEN@iK9McxdK_BVcL%Mt?z4a8|P5t@Jh*zvfoqyf~+xto5e?{>O_1|mZ zLhctk`)>0aa{rR{9S+O)w{!Sg@;?Q(&jWl2=N_cvbIPzw9U8xL;5DoVo&ENK?fUXn zxQ6%}Lo!+K&-k7A#Cpr=9}Bl7X!<8$@3GK+QR>q#>|avf4gO&L$@g8l`CH`M!$UuX zo<|Tq4wl~!sqy6(!uEc~w{Xh=(WB23N&oHlC+%ZCa{W1V4fgm!$KOeC*I&9m_*vw| zI^IwJi}ClP%2&copXz#TgTILg;O5T zc)HsN{vXBn=xCqlPxu#m^1Nr2-`;k(> z{ArQMzhFJb-(t@dusqM#1dIO5aMXF~pPSQB_IaVF;4aRm%Kh}ez;^$8;(8HY&n?>j zFT-~I^)p<`ey7n-zwvmU;@V8(_t|h=TFmJ9l=}Sy&z+_A-6=Z4>&uNEyT9}}+!Q%s z?0le(jwvtw)fZWfk%k%ZvNWv ze-Yjq?jICV7qQpPa7(n-Cu;o&FaP!E(fLi}d%*U5dscj~Jv+iSzAhDBU;Qt2azx<% z|Fv)u>pxfjVc0%zP%s5%y;0=X?@`!YV!!8MdB4TQuK~FBCCzV}sS$zug{$DQB=yf2 zUJOXRRrgQF!i76(f4u__Wa|1db%O}6mHW%tetlNJ1KdAwS%z^ECt_^mVwD%pjhWpV4e)*{!)Ytr{_Wb~^@8CL)<12UO)R4=g(EJeV*wZIO~=1 zWAS(5OyWz5j>k&4j{7qietF-+-GA!-d{Hv>o2Ki7o8WZLN1XoCa9pX@f5Tby2mQIg z&u>0#&tt0K#N##otT!9~IL+7-ZahTSD<8n=1)ATUn+E&4050OZb8LQr?fLbz6!J6o zgLZ*iUswNs3?6<@*H5p)#a+6di`pz$-V9j2f0IM|#9wxY?fSP6Ufr+b;Z3+NTlw-i z5#Gu-wS7Hsx7p8}ygB9ZJo2!g|CMlKyTkB*;H;g)07ULV5l zlJW5=T-c}Q=gC_{c=A0M=`U%|esCfCd1;@-vqCuP54G15aKX;%e>-eR|HbNfx?Ffk z=sZ-?N2C(pvZF`mci~dF1pjgVaVI?VrS8Apg6;Zv%2v!b9U5`T zUtsxtRo6eUThkw$SLFNcSp>`PcR2Z(a4qY7H=aI%`%HXHn@{{V_fwX`RoLJ8TTB}5 zyHn%KZgAuM8h?&}S8t=`mB1+<#drgLeLjOnhzBkn9<@!dJtxCi%=d17Y=!fCwftUq z4bL|wlAo-H6SgIO-=gc;ZQup}ssGi(-iX?7{C4=u(dzGez%g6u`R`A#U4I;(j{kB$ z)5X_S@LbN5QvLR9wmt17K4$q`1otl2{=OCVp4Iim+pxUvEc)VqzlX`U-huh>AiZC+ z3?8Y_{g1aJ{&}SK--)oh#2>DKllIToBQ8|lqLRs;LiBxUz+}D*j-Yex8br^)Lx5siSWufuW|N!7Pk2% zWn#a5G+yMviJR(pzZJI6qy7bZJP#rBf!K57T}gkk&JS+G}T?m>UL368H&|9lG0<9?-UPka{s!~S4@>LdGuW8psR?Z)pac)|O+ zzlq-i{SVc@)8U$}V@ALCB=-FoUh}Btzu}(DXPjSUlfTH%ge#l0z8!E8@z;(25xAy6 z`>$}XVE>#B%kM$D{a`iR$o)Fko@v>vhv(~gRuw!vOZ)q4I2U`0zSwiiy@?lp=>BgN zT=_lr_T^v0x$JK${rW$&5B;@D^P90R<7vYQW8*CYt~ck8i$p$K+y6Km_qC4q=?mx& za~{1vT(U~nza4Nl@zwRm4*Rh_eoNN_7r+&aUngGzS96~3=Knw7R$BJWXsxZ{^l`y{{F2Qof6FRS$BkHR@`t9^ch zS5DCLn;j0KzhBh#Q$5_hzsC1r*q(RonTtP~_XQq=*Ic3X`x&Ktq z8s=v=AMJAp{`HxT_j0)8Tpge9!704IBv(AM#wT#Yi1x=Y+_Q`N!@h^&Pwb~$|KAPg zmT3JB&%+;B54m_#0hbeBD|~wu9ftjvgysXWXD!@br0a|Q7LlHp?FxPUPB@wQCD-SP zhZEm9&vX8k4%c6%`|F3`)?}^kAnY#jm+ASW-&*J6bK!L2p|jUi=;i@!tR>Q9}|wkf3}VtyI-{xTobGHyAW=8P3`>wY}Z5U z6%fzN`eYNh3V(Cs=>#~P`{_lLCI0$2Y@cUKJeu~-)A6=HoWuEzi{GEXDeMe$ zj2|^{+3T^R{x0!4{#eT2TecL{8v)Q{KWdU(U-pnXE2|*^4G#~ zoL{>A*V)IhzS~vLhi`@LdF|uEl<)j6;&|$}RQ;y{c9-<`jc`$l+V^YN?stwnLE@jz zH|N4dv(=wpgnL;3y7I;q5nnFQ`WC|W`P2(wdmiuuoW8BLH|Iq5w@DgrUxw}a?MK+g zpX`&!FG8PJx& zA{^DG@wy4FZ`A&L6>j7Gs1m<^$DYA@>cJRqIV|a~hnGLC_b1^EDAazb&q6qP3-yN^;coV? z&R!jGG4U;jv|{hd#rO;Ji?jdavx4it8{xKHbbNmcyGzQCIGgnv_qUw?EP@j!YWr@4 z>vz-Vy`F}L&Hb_8;PmIz{`1NhU#urn{rc6y_Whc7;rI#=5@R=zPleG`?lP zNioc~BoljI2zRkxbnW{P9{fA>JgD?v>;;VXae6*f2)j$lI}DP#XUOSPrZovov7on6K-d}ltg+Nzuv{n zSKI0Sbv7)&|0@0^?b!it#Q$9U*ayxzQ{QKdyd>DZP2g^`-n$s?NYncF!Z{DCKhCQp zy*ZD)2bS+s*7)VW2DjX*>znvX@#jg}{t~$6jacvU(e_O8euM4#*0GmGc*&RQdg48} zo%0OmFS9Nu{*Kq@fh%C^Ur)meI3ITV;a#gZU-(z!<&*FT`=chxm;N~93f50|>i*(v zxQq2vx-ajB?fr+Ru4KHK_ib0hg|Fy(^x~^X&-$g!PrtYte=zqCUxMxPwx?W8|39Pt z`7$iuJ4S7^KZoG#DqSB;x(4QbN*Qm`-GIZ+?gCIDd5VE3PA6h?jNbC-!_x_`c||_0sg4@F&(gnSOak!1DdfT)%%RVY}XaL74Ml*Pcl? z;}7gt-FVE0?f&I!xO)Tb|IO;K-`yHt?t}X?^nB`VxPkcM{OiP9V9x)0{QCU`w-o97 znZ>u_-zHvM12?cf$o2J?!8NA7Q*R^vy?VdC3T~aN`~9eT#vAJm7tc^kd3WPK<~*n$ zw(l#@Mk_3*e|E9beDEBb+Bl{?fkx;6igh>eTzN{|qhvdAODRiS)npZx38?jLNUM zpY?%xKkGd>gZ;g$|NQ3Q{O}Ph-(RZo%l`vzoviWd_y_P`a~>J>Aokf^<>$k7+z)d8 z`x3n1R^8A41ZN+i_XnSQi1qkiy8k$6Iq{tL-JE~b!zpL#c--h=>`i$!etWNn*E}ykA^!FW4vLKiGSPz+vn+egm2XK*&ndS`n8a}MZfF`{I^d1 z^Lf}^QvUaF&1&6G?fE3@_dT`$pMsNEKf3v5;#1^zm-@>*crMQex&H2h?fa3LE3wCP zeLf}WY1aF!cU^utaPh9XzquK9m(=e*crNpMJb#P5-++6c)%#iR!S;QR^k;(U7YKi# z@u3+W=DcI!XnvEtKj31sKiu+J;`i}-|Kc_{y+0J+#6Mq$-6i$=ER2WXT+Sbhkcs_w zZ)3f?MEmo3c=!a3k2^lcc;bD_9$$YkJi_-_ihaHgw&x2!!TAT9@%KFQWt~1BH~`D{ z-`x2B7tUlq?EGo7RqRinR)07bw(IHVV0-@C3%g6&I}F?BlS|ua59dv8e*6GVW`B|A z*MEx_g8REFxR~>NSD)AyDen}u#|`ix{$JpyUjy6o+wERrzAsV#X@V2JAMX)nM&s?Q z4w&;Nm;a$~CiB0m-$QWOEamlHrv3{xUW|K%_)??eX%pC8(*Avg&GSf)!d2#dl*zBs zALeyHrQR0`QtS>{Ta32hp;^_n)N#KUxoI^#qiMX`hA9F@IYs*cOiMndSc@@I4>~Y z)7S&H=g$prop~R!4{m-)*GC(7(Vhibp9kTF+jRZ?H(byC{u0U)``q*<^UDGqPanYc z`!avP@t#nVe*`+br(VS7Jd$9J*k7iy1MIMx4N z*c5g}x5633Kj%-A-XnfQ>-awdE?TJN{S4dhbsV{xc)LjZEB<}v3v>UX9Jb$2*!~0j zuRi2I5-+cWJNVv>huMYig|k18@shE<^k>9}Fz-J{`SQuIyQF_oVEew(Ua)2+phn=hO5o}{WWkB z@yM;;F8-MHmU%w^UfAP2%&pg^eS-Z`_55Hl-0^~*mpusg@_eQ%|KMKY752}<7GkeX zxWMc`XMf6gDA#y*9X#|k@jfbK?{8rFJ?(DNi~ZNYwm%>FOwQxgK3~B0dp6sBPCRZ2 zjYmnp1WtcK`5icu{f4XGqCWg#bG6^yu*Z3It6%>CIK}LjlfEEcj933X8;;^UztGoT z1y}E+<1Oh+)|=aCeaqmCLS4V?{uSfLy#IU*T-O!r9Y}r>uUcUHzRP<3?2k9n_2m_? zeP3xAJji~*%}0@6(_gGtO8xrf!uEXbYmpx12)j%C+53j|Y?Ic1F>Ie7-sM}` zYt}3G!$}8e|3(h5-rrBpBNxN=dB6_Xj?dKZu!lMSSOMGfhTmZOy{<*yQ{Pjx|6+e& zzAc~NC65dF&m6esNWH&)4P422UN%h=|LlR?CG8!63*t0>?lDOJoBNRmz>Qhj-;cm0 zJRh*q&+mKK&KH~i$oRch=jUDE8qQnX_`Dak&o7R^MZ|Nr9ysGC=7(wOPrt$oI6rjq zLw*j{|0uZWF@0a_M%dn88b8E%XMSN#`ZC|Uc;@{XwC~1n7V%}dZ?76S ziuWIKe0~&8xJ&PE@3bbEejj**=WiO(7yo-Uf0Zr1PRetwhwX1&Y)TJjhD z>)|ZUCtdx1f=e&a{paTY;7=cpA6w7<1Kanrju|0dnDfdScqRVm{3-fh`iJ=1>6iCB zyqf1N+PZZv+BePMWFl`W3i8U;X8O<0HLh&I??Bp8_Xy zo>5QvlHU`syQKba!}fW~+~`Pe&9NE}9)<1mSDVH}dUFqm_NqxM_9=&x)3m=|hwc6M z-p`+ zaDrKn>>n5DwQ*kQ`r|sdlk@&m>Lcg#Pr-Q!x*mTQZZXeqeht?U-(39;orM3G=T%Bz z`@OXnVY{DP@IU%5M*HJrxN$GFS1WA45A!*^rb+epS|5Kh-w!E<-6j2Z8C-BZ>oMP7 z9){!CFXbYW{)vyLKX{(U@iwr#B>moSI_E{%zW&W{HS0%buOlXt-&1;j;AXh&2wh*k z1>5iM&7Xojm*{%?WVmvBT`#@@Cz$Qu(V(K_E=3J-UvKmQE(ao+C6-);$!UftGO zziN13K+i{?7diKrTzh735EX z$!30;I-T)no;P>~w&xWe!Etl6zt2s?zN{Brym|s2cuCu{&xXV=)*sIPPr&^rE6R8{^-cXSnpM;4<1H<4ycAd6US%^M6Z(dB3s{xs3PE;AV3_Z1b7; zEBlQcKmF}+zBw=d5pFm8>!r!~v)M1b0r#8lv3?dNUw>9`Jj{m6rf7XvzymxVmhYGU zHeAp7qZ?0q&!)V!+8^)2_I~5cP3dpuH>tn0FAdHtQ-8Z2j(<+$`>Sv*`wh1qOHU!* z+z|TSsiZ#v9y(F|`}oahzj@!W3~o71$IFLsUbD6*VUCP1?VsIYcS$@r6iznh@xQ}K zthZeM&D=cFD`LOw=CgZY`+oFRb7>Ff-OfM%gOfPVbiDDrV0pX4-F4djJK-MIPfq{v zE$}bCf8y+SK3sX6&KLV{85vl=FM`|IuekdC04MW2fUAE^D)Yf29WU`)MFzf4uq|BJ zpz-T6*q(0;!uEXr!mWuPd=K8$uMxKI<9`X;c)a)g$iVl&--hk`k}J|8z2$?U{*(Ux z1g`v2?UBC?s{?RsnGw$!&f);o>9l>O!P@F4d;YhW3F55o1E>G|HQ?Sl4s z5w_o3*)E;&@VxFf?}1wxk8XVb2S;()2kOW9qtnlT?f0mb!YMp& z;pD5~l?D2~#1T7U-!@HuJKV+nJlCJ|GMLZtFSmX@4la95^LrAu-}6n~iTTHT|7;i7 zK0o*voc}+y_rW{U9&`WpUbyIHU9Ua^*KnR)=G%A5F4*tg@m{mfhr{lY@l_6|pQP)J z0oc9|o1RH~cs_B!Pk$#|x?9ZX?}tnOW$cQ6O03uH&riq0?vnh^g|n{>-Cq*_c>qrA zRDVg>jrr+(ZQnvTmGd8Gzh&^?>uTRO;TYEU*?xVd?N0pRyx;X#DV*~t_k)m2c~8SD zBQ*a>S)}LtpKg52hUZegOMf8TJX_1V2Od5{$7}W;_}7`yUb0{Q9dIA>f1b~=dlG-l z{%jr`&w9e;e+OK-o${Zseg5Wzy~v;Q5f^X&fmh$E&qqwpj`Y&_K8p0Ov~O>?|7oq? z61d#><8N@^ep=qby|K4h?_CVH?xo}9CD?usbIv}@hd0J}S$=tk!HGN{=j`7Er@j~K zoq$}%_r!gvFZOftVmOoY7&jh&fOAuIf04C-_<((!ya`UekNvLiPm}ix`qNx^g!$g- zm%;sT}seV(Ha z?(R~1CLe@-Szo&NvjR@%{KSo)X}MY-xxbMP4|@7M=~7sJpWcm!RdD}p(PRDnGi>9{ zUI#NjZKC(riedZxwjnsrUw_JeX8l8mf1J0^pls=ntzdVFeGU{k_cz@7^Io_lN!NSt z!oxg|u>f6(HxY-Dzj?lP1K2*#^(>sq`pBI}F3t;{4gKx<<2Be_(%w(u+N(nM_r>2P9Zr96Kcv`i-#oa3 z^^xRrQpS|JamJJ zv<$9#S>wkR#|HDygcCTQb>nXtY~NS>0d9Fz>$hDY^DF-5{N)C?koAy@2S344uWJ8X zavbCPL|spAbv*Oy6!qtWP9XkqUgZ4YcG#ZRL=@ql=6RBt@W7QiAEv?9pZA01_jr1- zo%ri%aNU-n_#or)DcC-*=bc16GyB(r;VSkMZaiKLx7UR1Df!(EyG#1#NjTwI)t_}T z@itlK*X!X0Tj_psK;$MKpHj^HK>rkxm(=eY*nZC@<`m+MxgWm_w!cTo^s*-zVvS?f3NhVRuRUhvAexG=3~9;XI;2 z@5dFxgY$Jf{3`Mnbw3izaPSO|8U@0jHioK{|RB@fg4|0XH!4c^RE5N;4I>cv)AM@{QXmXe((p_ zzHd0;9Q^Yx9e+2&6}*q^{5$eo{BNcD%TaI|^Oy6-JK&W4HGWS!kM?t(=gO~v?f3B7 z;kcJ$y@UMzm|9N!y-VA(7n}#X^yk7O=KSYdxHm%UbKv>G{yH1ZAbvah+#>wEu0OvL z=6e(Fydm-e{QU;~9?+I>{1+O(ZiTbi@3{GVEo|qn*%z{2I8)a9lQ6aT@TYvP&56cdLC~fzx_hlJRg8T<}(`H~BXHVrdLErr zjep*!=jl7a_I=Ku;F?wH-@9DR_%-JP=fI^`YyB^Sy;;i7z)eT!d>(rZ>+54Req08Z z^FH`K)K%hb7i{nMZgnl~e@xqZ9c-T;zYA{WdnUc47k~Hy9v)GDomr#y5qsOo>J`{N?>Og1^k3Eed?swae-LpK@$~`yzUrRv8vHfO&%X`sKTP}gQ`qiz3vVWV zP1p6xOR(MlZ(N6eFn_!FyA^D|uXI0L@S@(YTn$(Ay%J~7_*>K-v#C=WY}ZS77`gQS z3OMl?osTZRmHM0KKi-FC!m18--&db;+13v8bsNov4e6SY52hFcdZ?{f$9%j-J+ABEF))%>ED(O>g)J#!@7 z!+DD9&nMs_^Lr07?qqxtuU-Fj!!`Hleto@0^zYU0ng0aa`(Leh5uabu@v#PWm-KJ! z-N?;(&{1&uw;GSnf;*;af2@S<_mF;tQ`cyGI_Mt!xrXsUG1C7Jz)75^xcEP@iScr- z_RmSMyQDt%2y@=z+B^AP;>8^0v*1B~&(ZCdn&H;7HC}9dAMuU+o&0pTiuJh5uNN+S zJoJ38#J{}zSx-F_@>k(n*rxA;3z@H-KP_nv*7teXKL6PjCjSJs_ZLohfcUUN>$@Db z@4uxzi2b<#U4kv7KBvL-UJfQ~Q6j<>Zgt+2cSsF+=Bvv*F@f^!e%A;rdLSA0i(n zet)2RKAb>%Tz}QU_W90M*xrwP54PW5OL`>O{#3Z)@Cjq*xjC?XUg}=hK7aE*9L4V~ zxcO}jEWa0CLHosj6CWi$p8`5iCXaf&DMh@qYpw z$M-Lsf3Ae>?@!+T7~^dtegF71*nU6lJJ{ae-t=+STg*2up6m-35KpuG{yZNpY1i_v zg6;blgK!JaZx;IcnXN(lE`s~N3)w^LQwiJs(!?i-M^p6oEat;@eyD=&_wf^-46e_1 zgxw|nd;q-C+^;?vw%=p;6JE`EVKTZB-#2=S^%du(&Oc9p^NG*Sz6Y+vf0%#s(3kOW zE!=E=4`T&v-#0zrY3j%QSeO1ZIOYd^zUo%E^G+?#dj@~mP|vf^hwXY}#>Pl4Sf^}7g8 zPuKBrGu+L7z}a_;cH+-AI$o}TSAVARA-HK<9e?v)V0^J3cJ`}=ZM@#&Mf{KW@Ajkb z!ozi1zrWy0-cQb^ZsHH0yo7)9ynMzrOob`r}m1Z(3)tyv^Zao;P*% zuMs)>CD+~_IQO0yFN?Z~J&t<~`_5DSL3pr8pMQ*go%pkf#>XPKbWeT1^&dE!-;27E z{A7Gy@kX#dH^c4R&vEPjJ~;n8wbv0{w7*o(^Q&R|J;I0KsBJ8ajBJHN^N$N7qD z?}f0t#Qt}|%_nI2zryzSUn}0CJagXt2<$FNzY0$JDP%tx&)>l@yw7nuGHLITZ&M$$ zo;n*|!+DddPdnUr(D>0Tr*NO;6}Y8b*MeV!6HXNubIci8?u^HuM0{$$Rh z$F1f(Xv^5K?-kF3`_1_H0CtzO_iNZbe|Y%&*l%;S-xF{p`?s<6hAe^|j`tv~_V?XjQ@bhnghly`4UVRAL@AVw=3HIc9PS>8tVf*~l5S%+% z_iKCgVh{6t>kaT4;;Bm?^J%bt+rWiGI{%yxXTG859nEkr>uZ;O;%CA1)8K|_+P#3iI{!}Z<9r1F zsPy%lVEg;&AHk*gqdWgQ>;mRQ0M1YVS7I9eaZU#0*#*qu#G1V z!<~GeUfM7IHQ_7l!SA_=e~3NizysXBbL*9ZVS66&E?m>4{`4Q*cC*_5;C||3z87#B z+%`|2KVJe5@;pb8U;cA&)86Vodw(7Dw@TPu;;&0!`+ex|VH;oe{|5iy{();>1?(=;V}Hi~OnnZ5Yxo{uqhH^v;JB-GeYOO4m)LW;;bgcSj`}(jA4cat z*gmhd-Vpv_&Yv!W^VaC|)OW#yyXk&o+%MF3oIW3T1Z?jQya3nCRC|y6mG(TR`@Pw4 zRHU9~p93eorvCE}+cc_{H;XuKLyS|MR^6Byg>8Y`*+6oC!z5v z?LQChsL=iOQrQ09!T3Lj*T=+qS=dhW=fd`W%MGyo-r~Qoo&V1Klk$o+-n;{sq^bYz zwFdj#sqZTv4cqrQUi>TApP#{nn`rwI{$~7Lsr$!Lc-X9enqYgsb^U*W=R=3V?h=1F z4{j~j@1IN>!C%e(dJb%#zb}WAw$k<7yKo!7msaebC$EL=`NZshIiI*#+kYNx?fDs; zY@XNNZ!P1|d>?KpY~MHOgxw|g2fl$jxL?-D-x6=e{YQK-_k)+f882x3PWQ&SJ9=Q}TET;TiB6|g<;{S|ICzb83u!nnZmndNZ7=j@k# z|G5ozm-J5)T>gc|#~+1R&$;&I#*Xt6CuuyXpGbM!Ps$>#_`^!re%~xTZk(6N{SKLL zWPC4#3wgdW)z`lYw%>n$1Rjpl@pr%^^k-}NkHGf#_U8N#dyYN-Io;bHw!hbMEu3xQ z!CKh3T zWqQA7FgIz7HW(LpUio|2zOQ%kG|Fdx;re&N z^l^dbGtYs`7|(9}?vjW-%zCjAPF)d-4^rQ6;4I!BbnS`Q5PNT+^HUjIwnXhY43~0$ zz07a#%o*dn{P#6}`~=(c=W{p0pKj6fuMgnX1)Bc|>@Mk#2}$I?v+hR@fUUp32HWqs zjo+C1^E`yJ$K!Ch`Tp`pu)QC@`zGVO1$$CUa#D0B#=j<(Do{x9q zuNt=BkKTMs#`ib+ym2kuxTWq77NiF2a}k_=iQ4Zg*na272Y6@^h$#z93zMa2=y63(LqJU5gi=A-+PvO z&b{~CTUF`C`M$3ndF+4gc9!?N`+1MGhaOad|6xzs_PrJC&(|-(4_tSkGMBAkOmw<1pIr;Y|u%91Y8)SUSZv1Wn+n@CB2jB;y zZ`=Mncm@4=ha10_gZ=$y2M@tdi3iB|3Vo~v-;DfT1(x>w4cMRWJHV%&G<5$67|J$vHaqt!FhqK@N zH?Xg7{u+FT$G`hlC;7$Cz}I{EH!Ig1kU0Nv0(_V!k8T0``E|dS`^i^3i?#|q>{yGu z_=&T}F9Q4Ze{KgKOnl*VbN}Xb$^K4)Pk7g%J<~?tI}1E@oEyJw!^n@Toj!aH_)6j- z*1zyiV81@Yp(ny0HoNh89{617*N*>fV81?9c|HC6{ekg*;03V#$$Y#5?4QT`UT_ur z((<3jpG5nwa{TfNuz#M$A!Wvs{J2w1|6d4Rg}>51|4#4`Cp-E3b+G-Z^$Yg%jj9{a zA8v8_(XYU_zSxb&){V@M7q2=W{K3D2e$4oO4t$Om5Bo0IKi}>-n`kfj#aEj4uG&n0 z9^mF<5q#4TuDut6ht72Ma*x@Pl!t!{KJ4g2;{2Dlf_;C=cfo%C@rtdi|AXB5TepF) zC!f*M`{PEEaLFrC+&EC41DW#Za?D#BJ``f5A3-$yVKY#7~539j`eTp}N@A2{zz6ieN3O8Q|)EGZceyj%DpR}(6UNz(9=X>Cz zC!Bw~IthR9>PP<=e8git`q{zwmYu$F5%?(df1wBI?-#&_QUBWN1HT0O`*|0p;CDy3 z@BadP)Pl3GPp{KHPaeMmY=6@J8^8l!cKv5IpK90dnpxK8sLTKVG}y2AbPd?AxA|M}or7*YoYa7R zh-cXMy%p^5r#Na3{g`-#y}lRh*I)St*!TB7=M4DKhuwI51nk#OzpTmldH&MRgC9YE zs*J7B%K`J~M_+UKsZR%Qr+=%=^-I84*v}`t0DPNQ-}e1rKOXv&7b8zUdQcos`#bQt zXS@0O9oYA;pLQ1OJ96LqEZF{JJ^csx1h2l|ufV?l;E{g=f86Bc^LgN_-{#Jf_!#(R z{8_etr=1NwJ>B6Sf#^{?cpv%q$dgNS`=!4X@VXbf^H5$6_Ukj;1-{bDPde=)_?PFe*$o~c9&7dCTflxj zjHkQ-`uLr*XP*o9<;5c?e~0JOVPg??)r)EL?0qw$m-XZgY8eg|7GyH+nxRX zeXyTD^uTvvKi}f=an1qz`pkR4#}Mzb_RQLMqYty6OU6_36<-X#X2$XFtHDY+JDuyopI`6~ z^p|=j7n=KL!G}N5(cc%rem?uZfH$H4+4&!QFZ6Sk<2SDdpZXFf?|u#T>sQWS&ia3k z>+i$g2S3^6?7OvKzrNUy!AG2Zu(E%oy-$2U^6@11{!f5!^z^64et`A2&5i$Sz}Fw< z`01~~E$jzNkI()f_A~YKM$C9z0KSNPC#&Cm8GPmU-1->!5cIs$ok#RSu? z_zTy*d%^yBEl>Iw{MO^|H-T?H*4=;n$CK-C4cPZjZ2|lFU+06bCH{A*>EGwUe!l1uLAq_-v_{c{$T%A%$Mnp8^KpsTzq=)nk2n!1>cE2 z`at?8@@Wq2*T?&A>i#>y_aeVfH}n&IKKZ^gz=xGwe}50Y)AI*Ue1Y|Ql$*bQ1N;2q zp=}#tp)r0RW1g%-s{HqW8e#Jb?^HH z_;!zf{?(Ug5B}VB=KC)N`~KTA{~3PrEA77)diW98uQ#~%y5x9Ff&KW=E#QmKa{Bp- z>)}7|cl`f#;JeRpVvQTT9O}pAMEE(?)W<2|Bki~Wq#fWKB?~Xu>)>mz2D!- zr+)<>=B@V!{0sV#w?ASLY=6?8SAaKN;PP4C2fp^LPQSeWzoMV*b^6w^VE;V6zXc!j zId?w772xZshjJ0`miFEN_RoWO+P^{HUOwzB*ss6$Mevd5JALKf!MFX^$+w4ngY`r{ z*h#!!=3_PZgwMF^F9+X2e9X%4kAbiJs*4vL{O{OnYutLD1)uKKhdB>?C;Fr1H-Gg_ zu$M3U5^&E3_r9NlPk)c&_b>bw>yP;HC8q!X2tJtgI&Io}1NcJDM-cui?XTUOoR71> ze!kXiV86cYmTxmY_zx|Ay$kHun>q6q)+hVh?Dbo~e*OPve+T}G|IhNDSAqR{IX8e; zdHUF&z?|bnENIVEdDNmn*=&yg%@VjGt#;p9j7M|IjM#mHE9Ld=vcN>X+x3cjBF4%@yb|0pSso8UXJFYJ8%0_>k(u~T)t`#jjM5Aw*L(I5D|eSZ_Y z3H{o(|8rnJKl>T~$@+YyULQhFr+}}SaNqw5*pGJ|awq)vtB#)^E%*)E-zCqFg8h8C zuY(`)Oh-R=g0CcgbiEn>cm5purGKXz{8ztVetX^goecKn)n~xI|NddWwTeE&VP?@O*dmw+$%Yxlm}z*n%J`3XEL>;D12XMK6+1I>Va{`oK9 zo@-qG$2b3gy?umh@5BE)>5qy8-|N1A19;tQ9RIupJcs^d*T=K&g?@f9DhVx6}`~5_~E4%1vOQ|69O*{>xwf5&B?%=ha-7`TGdiudn##|H1$JOh->Y z2m9w?KJ`z?qb+W|?*;EEyYc%H*gt>h*q&tx{_rmFG3<}J(6sLc!KXNV@7ecTmdFQv zBiOGu_$jdcN&mkAzIooo!&cvaSt8%>yN_$7|spMAixo?Bnw#`~?{v0*14 zz6bXG#aosw>p6n{T7LF@u>HyV{|h{Yz3~YCE%wHvACSD>pTZO15%N!~+%NNS8MyZ; zE}!wJ1D7TA;fuije#@VM{rZ0g9JH+GwsFTVzXtaC^V)-#^&Ce1m~&P89@O&{@MT|f z^Y?S`ly_dj{SR5zb3OSlN167$1bn2oU+`kE|NW1E@A#DC58s#jIS?o`di)kDDMDI9pJuqE!eNu zcKjojCH&)W17GCTqxv-XHqT!Fi$^kEXSnfP1zz@9*WcfOefpky6!g8*t->p1~^)PDbR@TnI&`{MqO<$I5D@xFQR zm{-5xJn+TDcdY)rcm(|;Uqj@JeD4e3BiFn4AN5zz_w{c4{{!rwZ++V1mL<;L_!!tf zANX;PXFmShjmH__RfoItl3ynnec~v-FZ}PJM=neFU(N;JvC^%-Z-bXT)wSoiCoo>f z^F8MItH2jM%iVw26PNY0eyH_f>E8hO$ltmCeh%!P-}W29pK|hc^lV1NJS_raGx$+ds>Y48J2 zpM4E@>RnEr9{6xJ@OSUFyk{1_Rk0T64>`w zJo$L&)mzV1@EwD0e9r{?=QIB&*w5!Uxp!I5J;)Ece$N5>=NXks%M$eopAGi)&3A!M z{f_gm-LH@K5zn7C?H>T!pYWqH_^!=PKUz#(e?7Q&)gj70mG$u{u)lsD($9DzAFcj* zEco(%x4u3KzUU*mo~=B88~ASIh2<~LA6V8ijsIg6ZISCQ1-CqZ@i)PjA@A+_fAaEW zJ(m&>JD&SR|K1Mv&jN2jUHWs#5c-jq|F<2y?y+uuz6L&w{P(lW`+mMM z`TZw96aB*TcYh7+*Gv2BXF>n$r)04)uVo|6=L{_znJ_ zb>{h-!2bCT2mJN2o^N1}Tm4`ZY=1I7yTDgG$?0F$fc^Zs--0iBrL$iSc{UjNZ`bFs zV834KB>3{n9Q|JouHvs;#kXX;^Bxzjd`~ z?_<_7UeN0%u+VEAeCt)`LK58IxPf^X#fB90MN^mO)VT;odNdu z3;qju5Ak2S9+#iEEK$GiQ{W4ThuiUc`g+C>`^b*(<=|Cc@y-he`~3RMlW4COANwNM z{)8TG0{ivM&o0AX{?hUPZ-I~W{1MAHz|S~OPxc?lc)tLA4DztbyJWuq72G=LAY~5< zK4v5Q*E>(O0bccJC+|N3K8pFU<2SGg{o3=leFA*WLtMVrO<=$N{+7+W|M8Cg-U+rp z@#kF&u5NYw^4DO$9@SH}!0(>o=;1}+!%uYWdq3EZKRk9T@)7@nosV(wcI2I<-)q5F zl7DaaBise{&qv)j!g^pnPc`H95wL&W=F^_b_wnzInClzBN0SdC{7lAs0qobm`YG7Y zcN^S>esH?u=Pv`_`g6zMJ_tUXdMT^S`#uk@KF!&ew}H=pgrm38DC-mdfn85mgD=1y zjpu8O{(k+So(~ctkoLV9?9=z}<@$4-{5a`(*f*P8JohT_%^%SHmFIs3wm+G_BTsht zNbm_@|NPI-fc^Z;ZwdDNFFyyL^X&u4QPcGK?&tHpV;#NRe>?o`P$#d3zPt4;rVvehKWKM{)cF>&wdzI~zRZ<$rw=?91PS zsu zb9X!XK4=H{scyV?floigUB4B4jpsjp+7$ZQ7u@*SbFlr%d>*zliJu0(^u4aV>%e!8J9>Q=c!2#2R^K~t7w`Y@K|Sa2E}_SZ zz`i~7Tkyp{bNMq5n`XSOb^d`DfG_a!R}PqQ?Gb+TG_aq)_$IJ_zT7dh(DS2R`)0tK zu5S!1gEO`_N_*KL>nA-SPKj^XMa9eSxQg{qrZT0sG^-rImbN1YU)_J&JG1_$g35H}c)Hr_TW2MLw71 zM?VH%`w+Lj_v}fw|Fz(C=>OYI`#%Qu_Y*vFFY6n9*Y{%_cNci|Hr==dVRCww{HXc`Fg(w`~KEvoCSZPp0iyK zw}7X=>hd-2_czG98(jZS1s{QZZRN|^U_Za}%V7Wf;-{Vszw+we?FIY$-`)bg`5EnxeT_um2b?ZM@*U_CK@c78qvz6|@WmwRP>{T%F{ z-+kFDq1Uo&&r{E%y`H~r27J~c>Yz$llQps|327159Xv-LqCT)d-7G_Wq;+?!^gmWe&KJxK7YFM0`zV4$z$8t^hN9{G0g`S1rjzW)yX z#!DPOeCF%17mjlC`}g3Zz4Oh#1U`cO5l8WEp^s<&J^YUMTl)D5_*DFLcKtl+BHlOa z?mruRIR2n@=J}h!RrGy3o|A8Yo<8CBvs?^5=cA4u4|yZ&i}|qQy&b&WtAE!3&%tl( z`~Dt$*-xFn;h(^MKHy)x33W zI{)Ap*#2Zbrc?MFupdvp7VMvw@VnIghr9*)V7x6qI}3a;{t!DK8{P_io#@(k8TeG} zPdgvq1|M~s^9M{`!gzh!*;`)#`~LGszYTix_TSXOAN1(u2Jj<#-S>`oJM!Gq_g)Y7 z_3!V2{q_8wcQ9V)8rpP6=Ny%w--G07MCyfX7CZ`YkHIQ zaU7lSXrpJ1>5C)oGrZhRNx@4BVz6I7;cZ~QzSoz$=Y_t$1-|4t z?)w|x1N~p<_9wjLt58n*-_jmpbJn%V3pBpbnp8mm&?}cFdJCc9A1AGPf`<7k~d>{1a z<)1u3@O7?#&j#O4zQEgU*bQ({&}s#A4FagZ?^XEcChd7|04L97rFjF^Fz=d`G$7< zE(IUy`8)3SVd5iy~Y4^GF{3QS?FTk>1VoLazhhBY&yy7d!_ZA^vCE^98W|9mzj#1^e-^V?V}t zdHIw#fX`p&q@{J$-X~x^zU-{QyKR;j`eD3dDzS5as|2*Pv zfzP?e>BCRI8hgcy53C3K=Z(G-eD5ROdjCGy_a8s$Gh9E-t*4KGFX4XMpFe_oUgr9L z;b&RTUVXWL_#FIU$hG$duwS3x4zT^n{2qJ__xHK++X225|F~T*9|zyK)2+wDKac&g z!^!U(z*lq9*03yl98oqzZO@ZFcX{Ze;=Z-2U*pL4#5{6JpJnfba3?AKR* z!L{fk>|eTw>q1X210Rh1xAbrw_z3btTju#^e+mAv$N7iHz~|!+v;692@X_pVJ>1B@ z`~5Tg=!I@Q-U{~jxAt7e{NC&0`R9WlafG}7r{L|kAE@H}(!Rs5haWDw_FV{W9qs7- zJK)nlry&d>9#uxgq{eKPEKVR!+u>A@D|2=qWw~K%M^6TiIRp-ARzscP% z`p+xCXVu;M`aXEOmml*B@TJ!{|Nis;75adi9>uf5PZq$v*c(>9-tXUNpO=r-2evXmoPaVI%3Vg)PZoVG; zZNB$=*Pg9l|9r3agKs6ia}?i_@qExN=nL#OvGQam_`-9X{J9Q%`mn2a^}6psPkS9d zyb5f8(*A2x_@^oS2XJ+r+aJ+)EBxqjPQTa$_Ujq`1iTD=^>p4X?d|_A*H=6JWh2=B z{Emr<<15XXW2fu03ww^;F}qN!jMt|oj~_U0{(1K-@8%Qpt%b?S<0g8dXv5lV(Wdg~SQPbCYt7n@ z`h2U_j9N3%#B^h}Hs2HYQxsJj(T?fHcx5`Owi?a(sIsu9XQDAPH(hJhs>iMD?H%CF zft#Yq`fNR_G@F&ZQEj%>+}ktRtjyG+>cY&-ULNsK#GS3gt!t(iYOO}2HMMnoXKkW& z+{D~mPjt#?Yg1|ET)j3?+g+cpMH7wL`BrmbqGc}iGl-3e3a#W_JMpNx}&6>HR zw_2N|$K18c{G~+0qE^HIQ*Wi(m}}X;Y*`saOldT+XV0d-5N}rsYaEFR(-J+%`Fc1^=ALtYWBqa8(w5=O!vaC;N_eAx6CZGYI{`c zMoP=)%m?)!Wgs^7^|>$VKQGKmhjZ>8aCbAF)u>gUsa1DZ>aCH|fPK5ZkbS|(#v!?V zey#>}o#Ky_3`T9w#8hQ=M=h%G+Df%LJaTfqp1QAZdG@|&zCOESx;8wLy?aIA?rMF0 zuF{&AQuoK>y16o2*->knkxev>@qtEW>Jxd5Dlz5mj)LYu4&HrX2Ncp=jA~~rRHl0cE>@cL%4{p~7id>wnEr{++p>17ymdXpuXe`!{9C>#&=GxxWtj+Ka*tX1mRNfM; z-nQPLoYw}Qm}4C?_w$`RlXK5J3pwr*(s5dZuW67*OL(wNyF%a0`AXnT@ocL}frU3~ z^Zp1Vug@agy>}-s!aXYak4{Wi3SK8pJ77t!v;uNh;Rv>tpxt<-OwbW;8QETrUf2u~& zVq+R!K3$VlxT3cw9tNx3D4`YqmBHDwH=3(7(N)kDWv*76WjmrTHS3G;i>NU<3B|eB z=?B#lv;=yeRk9@S1_Wj5n}5%4q1+Y*XxxSxDZ%`^Sf%8 zK&U?`xz*{Km0x1Xr6zQ+x4+L@{OJb^2y3u+`9Q&w1w=I1%d&RtE&k?WD>()6h3fdi zyk4VfW$1+m?}!JZ=+185>Ik;|yC@0$?K~OwkzrDC#|rpV z<{qDK#235fR)eL|K%gH=O?CW)dh&T%|OpKTJ_1j zftD9hWND~B@Psrj!<`aMPco>)r4)}!TuZU3#I+2kN?f%}B4?#Yi~U=&{3h`>%a(Hb zZZEfai~V4NaV5T!;$4Yrj(ur8IH_%`chAn^XX!DTaq>Fmf7^>9>ri524O>i)!ndU# za9T{LiKYqmq41ksKkR*0z0Pk<{xii5%03rvlT2^;%7o}N(`Z%orcw?=-OjclRwAO0}zJbaS5m&e7C zm4A7|ldWsc&*QHMf8Wqq{wIvC9)3Ez%#vR=vNZ4G_;wZ$zO98OhGDhR zs^r}`uYDPX_q6uloy2Zx^N#rlo31gvnBU_p4R&E&4EFc-<7*b%yTp)d*QpY3_K%EE z+O}Ay?;i=77Rkr55<7eyX`Q^#y`&x-7)re(&<#&QC%+d<=j5M@B=g{4`h&i36_X7U!7`QX70XYuS5mAbdxPR4**`l5lD(#RNA~YA+sIzB93y)r%Pg|@B=|)3a?B#K zm&}tP?hq>$?d_hG;^4C{Pd`^g`zr@9nDQPhBJ`EZhXM~8^Ca`aJe#MJ8@qU?TT<~N zj0Ypt1Y_{n1|-yQ`^Wg%gt@a$GtJQ)YbWz?KI@F%Z*TFqXMC+|2D+jb$4sUb2_721 zJitcdYZ*=&UnwA{6}^4SJ-P^5IhnQ@BPa9s0{UCgyP}lsq%_Q$IGMM}#RSt#^e4$T z6PJ>#GjS=vJrmax3^Z{i#X}R<(rh$wG0jO67ju|t;+_OQO<)rQk@7n7F)TH|^bd@twDTXc<@x{KOa6^0^C#q)Xz5YEZg>yxJ@ABX?vDz7W zVNm%D-U!cBOCj%~Sp|8Q?E=WV-iG$$72H+>S~0L(!lH@wntr-ydInaY2NgeFGz$aF zZrg+2S&b$Z}!|IqeE?k@!QNYL)YJ4ljw{P(-iGm!oE--Wyc5Quo+#H!4@o z)gn?K>@DRDhcr0AEfO6HGK|C>VV;q=CCoOGe@%K;RkGM(MKUBzA^@3Zq|{h(+ugG% zDcs@1Tc0jae(+ z+9?|S`4%}Z9pBxi)rRF}mCJMsk~bRVG^caPdyD2Pd8h7_a)qn)U)+Yt>B^40Rg!!! znQA7{s}Qeh)~Q55UP;jj3&rq$$?eJjEQqNhKp zEr(y9uQcb}pOA+6_t-_Aw-%vJ89%+g+uY=qcAFdZs%~>r`vo0K<+cj?P%p$7=DO*Z z6i~}hsbux^^nj+{Qa}wur9P{!=f1_)s&j7&XyZB!OqBO-o?7bH3UP*N@R zHTrZrvxu~8D}#T-d4yc6QfhW zD--v%VV8+}3pi%tzAV#pf9cL9>HN8rCe^`%AzP%2$GZNYVf}fZ@HBaApZWT17mp=v zk1ihUuuEP`+w`g6g-N~H^ELbJP2QAT8G%nHZ_gXO_Yn<>L<5O@d&^9S>WvQkiA|c2L@K; zHp=JMu|}PJyMJ3sn;sfK%H(u8sYz#Fo3BL&?oG&s@b%QX$ZJ)sKx-A$e^<=C+v~7{ z16GGEdO^{=3=UfTwdf5+V?W5u7IebbB!izwX@Z*Gt;4y~CzkRR!+K&}wUcl8da8ea zR!8-(#xt7HPuDsAGHR8akZu}$TL)Yw{<6I{#AxE1y5}`3*#3|q@%VjVcH=Im?D=3P z^O;Wko+A1t4ilozeBCqH2w(3E-cYnaR;)BSXYfgBywf*xZ)wL?5`$sXO-Z}U+50mz zg#V$Tc33Bq^UU8&hYixgBQnSn6v^vsHj=FbZEj1ffvu_uBcXi1#Jer|I`>uZvQT$X zuUC?jn0Qc8XyPx4nM_FfoY9y`Pi~C}mz!FtjUsm-pHG%@$}jqSa+TT3}2z>pj+M%?-)TX=Y$j zZ?T=vkXFupZ3by&FQM1)m-JA{em41+cAy>Chre+}&vc`)YhjKJMe|eYFGD@-ew`;C zq^dWZxNY_3;b?fvI?6ZbS`PN#)%k|mh13(Rd%>2~o6BptV{G$Ud$-wRz4gQsM~BCv zvDIrf4fBpuwrm`24NWf0QmvpdTRw4d+h}WWk!=8tW_fC~Ql1*-H?cTAESuER_s(-^ zdKi~FRT=u3x$>r+rQr=Db|d@pc$e~4wYY~zTcrW3d{<@XRINN(iuk~m(U$DDuU5%K zo-Bh({jCM6OGMl>!vvSNk3~GaT>Y`UVcpzVuRJy&dl~KjZ5v%3WhGF`SfCHQe9#q% zXf-KwFkhyp{JnQ!wtfb2xVcud+*K#~q&34U$x_lg;>)AUqYeDWAJ>gdOqcqiQCF#A zaxR*j>y75;CZ<GzKuz=SzLaGB@&XDV~$?9ekS(`=J2 zw}usA#Z%O;=%h<>FXxc^NoLgTs@TeMT-#UPFi&BKVaR7pD!k~dd*Q-*%Y#gleZVf% zzTU*JkLsNT`KxtpsXPir4@7*2uWW^QvkOma3bVD{hBQiMZOQHpAp}*HWOz{5C4ts< zSRrX1IT^mxA8lTcbs3mOnU785%;&_kyv;C%EQ!dv+(K&y+2TOS7ATLFmm=7S;U3%g zP$}9ry2jEl7ZvNNQ{G0=d)bat$uYEbYG4b7s4vJcx7ih!dDFPia?EGxbC^dl#YtvT z9))@FS4C5`S=p`@)hEl_=bNk+xCLcb$}rSYB-L1A{=~wamSr*)S>iIRba=Va7@;8j zhi#+1(FhDlxY3II5t2odU4*%v%~_f<1Q76Oe^lPK5$-9RP-qz%6LN;iESHJq@#ez> zCmIVZ&P`rv7e;3j6lSaPKp^XUPPU1{9h#HToTfnOjQWZA+o)Wbp+ ziaDm0D_LX805ZOFSj$2T{HWUB1*%2KPJAU zG%AIbV77Bc|Ii#m*jF#UToy%Ne>~kHaMr~(xUdajpo-x7kfW8o<663~$9ra+y;;UV z3G?Y}V7nI2zD6S>4O@^YZn&oK){jf~*0ywLh8KLZ~B9q@$?$2mtN>B}% zch-h?j%*bDZ?qLFES4u0i)&dcpXn2T19{EW19-?2;U)SDKXn zy8LAaGlJtIN_h8xyc>OoLTs#DTWJdcr+Mxq*Pt3dXc|?HDKa9>J)i=tE8&HQg z$Z`^!bkoj0=mWENs}rq8tL7_xQF*pXJw2mDZl9l1ykOYS%D}=bJ0+snMjl_(){*hp z)S69-4O0E8DfI*cR*+LbhE#ROLUlv*pOPrnP(@Nhfl-=TBTyHvumtBuBxZZ&YiZE)9I{fiR0jQa-Vv$J^GMRUvdnM*=zkay^GOwHFE|KNcy+YHSShqJSzF^mbq> zrkf%{t_yF65;1}CcS>x^D$1jtxfWVs)pIuK>K~_!i9i)+)3P0by&RvUlW75dVfaHr#7^_$9T)^6V(^&dx#Fu(e4sUxG%;}6Ct3C*x>Y%DDX zC#IT>*+zeKa$l)$elN9JRPpGEUH!9--NTECMjcmL8FTy1s%WuxBNuvX9AB}1y#PNe zXs=RW8R$F)ZTYk2bJnqxXZ`d^NXqzhaINMCDPisg~$yYqnNiERU;2oAAh@zNuDoKpzgzol#7R&8Hj%F6CTh@3HA6hc z*KCzkdwPY$#H6sHtrlnmLag;U{k2<@z*#%}Xp|~`pdCqgi^i!h?7Nf}gSxz1)6WfGWaug(J|#s&=@wn_QN^ebxiJ;QLGgIySTBRpk(vA9 zGGv2yrxZBuBO>T-M+IpP??M+S+i0eYO^!aduGf-p2h*~_8ZX5>B5Q~l_MmLL@ItXu z(z&gx)LNHyArVqT1EKZZwGF34m2yPG(%oWI`Z|9Ie4|6b;@jx%y=sxi#E>RqVI|oa zERcg%=WpY~NE?cM%Z)=*vLp1K>N-(0*?te@B_eweq;^Xy>x(fmcF;_nP-2v|hA_mV zq3VM6-<9`3uIiuVO?&W1?Xq|XbUc!%tnw^uTsF848X*K9r2Eh=L=gduJlkQ^<@$8Tz8)bD}lMRLN|ZEH1;2 zhT?$punOovX$VF{on+C8PHTb?mimhsPpAQQC2V6UKv6*>H98Rr`>{8t?DtD1c-x!N@#;sf3x;r9LQ)&bkh62L7D)KtP9omjS z=SgC*#gP*TmIxRG6Lb`ABZ8p$MxP4G+C(3f55zf3=pI`%9W%h8W|PV`N;6X^p5ik zMy7V^+hb)qD0sS?K41Pg^~0Y%d<2DzZXXw;<7!rBZ|s_2J697FwGPcx_HbOpnOawO z4#ptS7W0^}ax>iCOBo0Ax-ob1r&btwtS>Cp^k2d%WVHmOoGild5>zV?18u|TMsZGU z#yKTxQkHC4R*G>>v0l;%FWHes0cLSREH*V-+*U^R%PRI$6(y5J;&09!*M?W+4|TR7 zqR%dg9Z5PGU7)v;_`6@&(TLBEwq)4R7R8R9XLaMm7)g|INzuMO7=9lq-58Qupe7O$ zQ{O1I=bfyz!DNWTu?Ri^3-n|ygO^cju%`<$s~}Bd?JAWbCzUkFSv%8lgrm}ClRvG4 zzF37SZyoQnlr5i@L`zN?lB`I<#8Ko-6cvt7C(u~->V4d3`Z`6%c=C(cl$Wm;hn&k| zp&C0gV64gl?jW440zwi5*dhV!IL|Jv6JeSc%t^d%%^0Le2D03?v86biHtju@9WuT0 z*KUYh)O@vP*h6Z~?2!r5D>+NL)2OWj7h4*O*78erp4fm_yD$7go_8tRl?- z6Imx>^WdFCJ+W^Vy(LyqP@kN2RElJ4g%_XL*DVSjjiyxG&P9t#RuhiOT!lmnOol6g zFfamybw?LAE74(6P?a+1VoQd=@{)x56ZshM^n5>ze2evA_eqo`9b3tx#B^W?n>=%p zfL4+aA;A>2vBGkDX2)oic9L9r`4G83dLI&w9?nS(7-)(K$c*c2$}KIjC}Xu@vJ1gz z!)q{#{Ug>-l+R~Ei^iCaI*M4#d&GJdlTV8V8&LFI!eL&Ul?0)7$l|pr4o(3|%6=u+ zcsM{!xrRv&jcL!7xR>Eb3}IdpeujbNs0a4@+0GU}%1IBDvqv z?(h>f-%y*?j6KdW^nKNgaljGQhB^WQ+@DnU3Gs=W($g7^ft9D+@}k0-RI}JQpT~sv8x7r8n+{d*JTG+Axh<-OE_eHg*>aVa*4UwY zS@G%EJJYF)Z)#Y4gIZL0p-kkRzuB?F?IlJNaNA2n42hwq_5&q1?3Yx2GN;?dH9VvF zy|WS-GzkkBUtM}?mBrs~38~FImKi%YcpI0xo;#mh**c6aYcnUTpT@q}Ieq20EhV>P ze)`!*(M8y$Y{M>DZA-b}owLS%|32Z%k}yaTnCu``X@zp^J!ZYy*1Kped5l}z2ot~Z z@({Oedi?}rB)fQN1PtK zr`}q%iiF11YswU)-W-*V>p!kvO|MCj93dbt8FTS|Z9B!Cn_oI#qyq=$hK}Ytm(V4N zD#aQKj6OtVc%+DH$2&5`la^Yicn>{ce#3z zTVrjccX3ps`;}ujqvxz#YnF(oxfdsV#|a=tN(6PuQm=?fEvl^A@c)D3rd^ulb7eF= z*Y7qFfn8Zx1s+K1Uie<&OrW`|MQfcSh@ni38TP!NJ+MQ&YI||}%*Q zj}>Xj9P^+oIo&EL#y5;%k+Y5F3?)P24e}#X8TLP{Ed<#grSj46U$A zku@+?3+=unoX$44ze4q+ow1Fh8v18;44UA#S$5gMT|P10n5}6RX@WQzr>pWV8rP>Q zjTKjGlLc!MVo3pVr%A#Z`02w%crk*CEz8jAwe_ zbRBI^l$NIwc7;7#Uu{QzP^A$n#|&W#b1F#5JI7!N)^KK+l`a=2E2NxC-dMQa8NRLq zp4NYzE0W_;f(|qGfXkfBwu}2StVYXnJ^aD_pUx8KdRLjAv>=C5%A}r*M72nvwU{(> zEqnNI)%7ZV_)sV8qbd_N|SPS zk?VqK!tdDxAC1>`)MuHj%xaApmJ|!yp{-MDL2@L$nb<&=yBtVDX}r&FcF^f|w(L|< z1&88dRBEHFqP_jBA}h<;R=AFI5lOXp2IhBDqOKFw(yUdh8FuBZibL9q+pvu{2kOCz z!%>%$*Et~BZO7gUYh%}$CNhhJg&N}J)L{i~FCsi_xzt|D9WM$e5Y+0uV>sJ*+>u&F}4Q(<9T`=A*)~(^b znffI=UZ%+@+nL$E0hpwsVJ- zr5H|8F;?b|(m&hM)nAQ!#>`o6Ut(jXk}1StrdVCJGq!9h!^)o1!MjD2GMWbZ@(3Q(b8jDncWE9HqPv zN3o@D_p{Puf&-9|C2EFopVbMUb$PL1TazaI2~RI5u$hdevQ;K5r|^R;nYd_3To2hw zBxi7%uc+uzz~_?nqDk1_dd5;#r!JyMgvC{Y2(Bqnvhp`U7i?V3j*beJs4TS(60-3# zDu(W9GNziJwz`(0%*3&kLhHCI=RNYqGVlNqX2*c34jS`L&+2TA+MJ4xDYU(Mt|(NQ zZH7&jA}ZW8n{_g%z0+2dP%m~Ob}J#n^j{Dzm+&e(jyK@$#g#M+H=I#)=nCNqNOlPg@(ge}EeWNS;|JRvGw z%Qwt>@`pG?VM@s^%IRw8!$d@U8hL4&p41dgwhTnSld;b1^v`lEtHu??xENPI#+7os zb*L^{&Zx?ZLm6teR5q76hFU2GN+r-8D<zxnNO72^7;xD2LC|d~g zKbbZi`{86sh{mWmOY_9OV_)Qik7P2 zh4g}v4rFif3NEeZHp%5ujN7!Zvk;-TehNgsdN1^$ql3n|@xPw!C>^z-GN<+GZV)B2 zB1dMwNTddMw5SU>3$vLSxUE?qoJCZb;dGbUL?i{lY85FmrsgNT-_!0#$yp2`eOQu~ zRe0N2dyyttPd69uWx_jkQ?f>`*uRCy&Oj zk)40y@a6nxZA|Lpl`7Sp3-eNLr@lzOU(}eKHar3ul?HPgUvnTtkUYt8%hg zXGM0SWlSTfk_akyVYV_;i&gT>``fr%^Bb**A)FEE_PxO2*dZ(h=Ceh>U5*K_3YZGr zb3$daX0}u*FNDgG*ArwJ5MM2@F|(niBDvyvEK9LVsosVwg{8VRv+}q@X+Wge&dOqi zkdhZI>`bV3*L&F}j*aMVwDJ0q99cZEq}9`qN+UB>q$7HN!>?WSELaw>Bb)*DllrfKETV_ zpvp0)%F1y*fD$dSz6HYzS^W9-U6wN_H9*-Q@61G0{cUhX-VM zqpt6%8AM(k0eNj#Wlc7TD79rxHbluX^;V{Jn%l+_T zR~i&46qWj{q<)d+kJ>h?D+_DWC~+Jbteu_&mS}7w+VW_f$?`0Z2o01{Pw<;bVu+zu zFTtfF!JN+o3uH^jIkqO&RGMryW}-!1+_Ke(c8u?B)#epjvk?a4_R?ZlqF4Il1sQwP z;#jkbeR^R7L}ew1Ef=vulwUO=jIh}T=W*3Tw(PJB|9cijj|WIVD0znCa2)~HQ? zvyVD{+~qmmRo<)^iN(&OrI0ec3#g@ST)#3_g)aP%e%*NA)aS%C17Z^CR)@(53qAB9bRT?jOl!g~{lW^0$9+IjP zB5WFR%{weeC);#aCq(Digc+L+?;#oRkC=Nh9s3S`; zug;p)$HF|RBaLBy$R*J}Q#zd_R;r^d)bjKksm0n-K%p#Xpv=BXN9f3-YE|!)N7Zb= zFrj4U#M1&urSmEWQ_N=qtlHxx1CY>QD@dWwLGLc+%9n);Selw-Xen+HFv76)!FilWx z^QuDpz^##xB9_%lEEQ^jr&2O}ezXKy**|>8268MD4>ZIyUD#{zM0my%Y{AJ8=p{H?kWuABuQFxN2Ua%yuZVTBas+hCm(oOLT$r+;e6AeH$1O_Ad$r zCa;*4&WX+yIvleuSYEZ>Upqzq!UUlTLXY){lFYeg@`Z+tGR7vS zD?7~I%4}>^%j>+@YVL-hJZn6#bcMshfD&gHYXztM5i&}Q%8?n8YSYenL6K8Vj8hKV z<^o3tnKSFhQl+Tl#hI*jx=F>A*dnKwW!N!;#};oScg$=Qx;T zZelu)0t#E7oKuk<$s*Evoh;u>OjxMI?a8y$U37+%c4R{Tom4MJEG6}EX<1jKF)%s6}zCb`eIS_ z0$7o|)oy%-I#$s%5UDd{17GAiX7 z;OT{y|8~9La14o}A}QehpgS_@OxaN!8Ag!f7w`@4?9zw%#|}uCZ(?_H(Aq5&*KL(C zMQmn=b8T2k7c9zoK(o~IS*TA}vFE7hsYj!~ed2O_jG8xpS|1RzrF*+M-XCV&B*qMN z^RT%xTiH=-YEyzpifg};8ea0gjmruHF5HuD8r*hG^!X)2;rbE`88+oYsR?4NL~<2SgXzIh$>HQ?m8SAa0P}&NqyPQnDiszo9z{vT#{BDFH-q{ zS)`Jwqr>oMPLddh`jm{-Y>~gVoAY)|#>EUQrz-43mi%*c29->a$c&786Krx9^Y5En zr6O8opSrca0~4&*enDB=*gqY3*m?|-{<8!F?8N90=3&Rh*lIFxT{zT2pY$flOutEO zq2{q;BbE*7q9g9_qGaGVDGka18MYKC1HdwHJ0ZJU2t{_B6qq9xj3R)lq!)uUO~S$A zRy91_H7!@|bA+8)rdC?!(45s#%}z$EpxDz*%!c|{KL}?mdUIgdt}e^fUUOKfWV8x< zru_1jqVm+`;o*ttxCTfY)p|hkvWG`jM4Cd>i6rsCeq-!|;YIT({=}^!uP=t4yzzIW zv8B-^(Dwae$iO>PwAGvM z)z!)z4k0rCg7#O{D~g~ud}z_0yxbLQD7ibPo69@Y~crsLF1rw;EtX{%7 z$Fzk#Kw&vw$ozR%&|FI&5ov0N(xl>?x$Kul5CnsrfTj0Wu~Xy{$cbCninqR9Pj{~av}@E0rX-TUmiDN^#p zFO=u^jkC@FoJS1D=Vcj3U7!R>{t?4z7NoO<(?n@U%K3zhkVkj3_JJRoD;VtTD@6ZL zI>-dIl=`V4V>J+|R4GxhzK9ZK+c15Y)WTiy-NhR92{!3?BY`+uEf=Z@7??R6CvQ>6 z;e{1V@1~IVF|f5`l`T@CSXAgVE2^M5KZl95GZW@Db)H8SRx!1|pDwls5PZ@;>I1 znR~a;}}+HriKU;fWyiX)wQ5nc4)Rm$qT6 zrp^!>?CR)_N+N2fzS%@B+`bFB=+T>DC3*Cg4G}HpSWo4n5qXn;vR!U@I4gKGzYGL@ zQsv>KYe|c*NF(ujW^_Sa#!O5+lgRDe;`pfcgZKtEi?**cSFcUfcGu@?Uihk2WH7UW z;{9II@{;k9O*bZXu?Ci_gCRWMWZ$4U=w^4bG8fI%X2$Uqnu7|tKSVFNMrfXW&*G>y zRB0p0NkYI|4ODGLPL{-uCMnl4FQ@RtCTGSa9TRR=bgRYC-s-f4);qAaoR**SHD!wB zXwdfo*YN@u+Usr@Noqeda=A+bZ#*^I2xH-Ldf${HCI9r|#L2u%R2aRk*ceP&A!%E2 zK36UlDRIi(LWwCac+dLtv+1(!PzgK|7aW}rZKO&0wIB*!uXL9EUSMIV6(kLzI2Xq7 zo{8FA3k$~!RRoe9yZ84iF-}rsT#jXF4AgQ*mG0{ii!G)$qz@RaBl^!oP);=F_R4}w zrI6+A-t1gmRNY}6`2KfoVWe~Q$+cQjfms`IDGc||m3&Urn!X2A6{Y>F^31y4Jc^lj z8q-EjJ?FVLRU1Rg0rlLQ6Y?0TX51pqbjCu(9vq!-2;|EUQ9D>VCb^gnMU?MCCaIcT z2;E!`=TwK!DZ@s@pFOaX zp$-@o0pATbrp+*wm@upk{M24kxhINCKgj@fOMh8aWJzj!TuoXiDO=1?8qSpvZR!J( z+Z7glZd(%d&CkGhw2@g7m$9tL)&f;ArDo3(Oq8W1hlcd1{K4;>!@J0;>@Lw|H*>6{ z{*pZq+_<(fwuN$=_#+vZOKUI{I<}NSA2jM2@|2{^8u>w8V@a7QDiI)bL;@4Pp?x-&9`1!GBTzaRaeFFEJ}+hYK@|329_eFFyiZEI&zc%PzsrDL!6*Xl&GcFpeP; z9nQ->%)VY+SE8O!AB{wJsoSEAT3b0nu`54m!&N^t&c%R8w2?NCLW~UTn4iX2Z$_KezbY&U}ESsU?DJ|@&xk}#l2d5jI(AYaQ#PPZ=C z?0n@&QWNt=b24=H^1PSQSk#F`>7NCaNqVKjE3YZ6(`M+tPA|A@*^$nhTUGFZOZbDpz7CK2UZ% zZNZ2@A;a5*xueD$8_~q9C80vr+@PtA7b~V^XjnVRwvJj&I?ejJpjXYv^Y;&?;;Nm- zSgiX{1Xb)i%xPAj`UCVCGh#`;FW8<`z>3MkvPId%vm76ND$J;Cnab;L!V550z3Mgn+lr%3>*^8GM zMuY<_?HSrflPlly&FaR=|JJQZrO#>@Fv-Wpl`AH-XZH6rOtiFC+m$N%oEfTE^UiO1 zh()MX;T#0wYfC5@akRM`ZOP_3I3{I$L^jhw&zGrhBS(AeS3MenpsNA}V3Fyw%-{ee&Wb%}8}MQj7;Jm+hp;OGz#$+5sd&-|SdbG8&`E+rP&* z0?w}DhK1Ut6v7eN@n=rj7SrJ*sa>I*f}3_ODIkYXl@$kj*9*6s_~jD&klF6G9FlAo z%iNzwT@|E?Q&~c|i`sJ*8$;sV)NBPq*%h+X6#`}_h)P!GRmCEtOUT7lJWgSOM^-0G zm8lj>#>lC-ZnYJBH~xL8Xh>_-Ro~<^{>6B#dS$2~paqmovaw;VC+U z8A6*Akakrpp)iWmK)o*-HWjRiV)W@)$`?~YXw_S_T8LpFN(gp_GC^$tr=m-UZg(fL z6XnQiCn?V2_^H=#qnRrQXF>X<$6K5#zVAMfl6cEaKmpUSD!I#0mR0uEpA=Wo3#Gf_ zG+r<5>{=g;8)QBg;OhaOWN2E2nOl=W?ML&s->9I8N$)UQaP(^be?#dmDuAlOYyl%R zmz^&1TfkTZC_Y!8&i9!x0pceKZsdw4 z_Ehu{x`@@*#MG))_}5pjDMzK_`k|7*eDq4j%@V`&-A~RsPt}71x3R1;CYo((=PGq< z6VUa*QV{!G4N7(kw?0CLRNakIqG2HbBRELfY9|>>$>H(-z_ADwQfj9EtOAx*f+H zr&#V_uCU>FHH=b7%P`@<8uu7&!5oRMf|qU!ZleOGAO`xGip~xDu>4A`Q#~+8CTk{@@Z>zavXEXjMIJU_fJ&>`SYfGx{ZN$!(frcVQuZs ziA4Ew`|vFilGQ40oOR}z9Q)#O>(1!NIUC6xjg(?3;yJb6q%K`B&cyPx?G>3*mGMj@ zG40st2V|tz;(xLWg548y=823cvqmViiuUa>{;4^f)hTny0dONXXAd!DYxRkE9*Sg z0AjBotgCy4M>&v&cGdPWGc%^BFmn}P_e(QV87gzcE5q)1`M%qFI6)$s&E8|0n``2^ zFYXuVL5#9${lKP}HMORq>Vl4{DW8w7Y)Xc4!ND5I1CkMboA#83<-g%QB8`+pV%M6u zkji_MFAlCKCnclDn{$(u+S@OVNK3PM5qKR@ibH(yLAr$c+|Jec-Bf(+I-PuR8<{4m z#OVGo8ak5|O6e;y79?FmErhI^YMt!q_<~vmc2#F0vs$|`1t){DsE`APl&ye2Sy*I| z6RbcU#f3^+Q;Czp&yoXI(-&>wL;dnN$9 zJ5hskXDrWX`9Au#JceRicKeVDi2E~kE{b!(Ws1uXOM%fHP4p2JmZ!Ql{QT-;|tBV;3C^uP35$a2o-4L&eb}i^x#V3&TlIgRtCgH#}wSNSUS>6ss)aV zL5N8y2kA7JlzGwT24rnxwpy2bC{=BQ-SkeprG>^mrx2m9iVop_5tY&U(#NBtvwgV^O*kT*!7;X9afaOsv+dmYoSY zZPk1g{vqY}lz$4XPF7xdTfYA42FnNnSz z8^a-}A1^uooc^l{3s&a$&Q6d4P;aSBRw>e9H-TfN8iTRaE2UfYMq=EeQGaxDU#V|? zFWH_mY$9hfvTRQ+FdmUXb z9w@x6wY*xJsO)t~8}jb1LpO`LxY!AFGFQ%Jv=on{$!^gq ziZFIlp3`b(PJepd^UCX#ybvN9m}f6`tk=Z+OeN~k`H)OkJ{A|OWmgK3FO6xHm_GAU_lJXf-rN2f(q|K4gt9Ye0fY7YTkwGsH z4|q}1L_SBfRYTNJDw6FY6(c*M`&1DvCl^DuE~TwUJIH|+_~95!Oks*BCd|NBMFn&k zOR`96Ae(4u$Fw79Hb)cC0FM%9L*!)(bTNYm=&f-y4Xtlq<7?$@Xs;{C01w>Ei6?Q zFgsB4J?kr`Q=!*oT|0{SmHmk+V{@cqt-QmR(3UPkm2rF}OvME*AD#IJU*2l9v6S=; z=aAavpHU9gn8n*KvjY@NeqWk`p$__OR_1HUO3?a*t!pE31d}V~Ec^ncx(n0K=PE{) zFzTJwR$_lBY*qX02O_-=W$~v*y^p*hp?7hivb8c02#Fb`<*Rwg;gaoZa>KK!%%pcH zReg7pk@JtX?eO(wl}wySJ-@GPnE1WosBr_|8{w~~f)EE)VLApi6i1}S&&z#jMO>-G z+9RGhDb#KiptIIBxiO%52pQ&;XkeIPV@!NZ&H9e1)=Xnw6+2al%+_ZZ#YR#ppLxvY zba;!(SY+^WuM#57$d_c^!)7p)!+e|2V0PoTq}?UTBX;8j?s(Y$ti=mdPRi!W-f?|` ze6u#cFs(hZWy!GVl+o`4kM>nXDWsyvwu4h86$ObBYvxHzXq{r(;c=%SnjNN|rR3dN zowy@z=@C(fZgHazc1ooM%0W>vPe-jTFI9|;@kV31R+&}H#FX!b!4|I*XHZ%%i>&V) zHILQPMZDcsR5eR0-Y{x|o#_Zt7s5%a?6HM&U(IIxfzGrdo2|YzR*7UM#(h-}F@M|mIBJLPP;hkDyg>Kbgo|zoBgTbeha%@;#YDDBFO6LgAEGt!J zk}gxjG`8OYA8sF`6;$KAQtk;STdvU}s&;$F*^sr6Vog*T6t2=4^4KP@CHUe#IjhhP zV*AmL>UbQ@YPD;MA~`4JMshZwQZNAVuAZ2xlI-C;6TaXf>Y0GLIu9}zT&}BwJRb;7Fnl;_y^n?AR z`%55Zx;AN5f1E&5DoP@|W@4Y1R|_pJsTRj8sqh>RLZ$xuP@C6ryyE)dOiP#wJL>bgQkj#%nnX-j|-nq8RW z*yx2OrFEs~p#(PL^Q_D{3VCm_Cls`MNs3HiCf!>ZDy}a4toR6sA(6Lr!m3uz#ysZ# zW<3U9YQYB8j*W{#M|d~puyv9+5wO$B0C%=vW|11^nPRiWsf1Y(Y8-9-LFZ7XHNA7) zp&>@1Sn8bF4rFLO)2_Z=@#tp`q&9h7ZKy2sHoc#c#snN1!s59Su*k$~k;)%kim$kk zOjwzz*-!b4m*&%4K3>~Vj~8XyE7*_s)bk!V7Q9~^Q}MSm#!p5ddCvZH8<9`lb1f5^ z^K=Yt3(GdHkdje2H6hv+MPq_fgiJZGHguR-vPyC+i45bUw9Jv@o_sP+lp@y#BNRL0 zJVrNO^h{ifihyr?+B}slcGWZsN+_v?%5gHHk+=3seg4`XmTJhT8IDT#gQ6fg1=DiS z4?dj*w%z5VD3Um~%~MmOXm_nb5R2`lwwtN+?>4(jb+rtnD(qcYj;--3Zybv@;f9mQ zm~>Yix|>d#EGm)Uoh`HfNFDe1i0QwctX#FcZ_uP~srY|Nt4S*)X_jaD>awX3%k;Tz7R&Lb zgB-DqA!XU^eHjp@`#K_AJZ&DkU8nL-{OO8ouNTgI;1%MVRQ}1-X?#m7uBJ+nqSz5T z_D}H>TP>w6wmau1&{SR$(3Mn_QMO~-sCxEG`?LonrUB@R)F>B_qpA~1mG5d~$lRBJxD?IW^79}1vCM5gf1@d(nmpznXcIFT2R`UcTR)dK zYi;5xo{LMUBkUyYwyhkzhf6h4?1h&XoEf*W+hbIRq7ljIRtjd)n_gnSywpy##Tq1e z9_Jlgrz*4VPgKlz!fIMhTu00W;gq38pe2b6Wd8BU^4b!q$aYU>D3cj`L=v5AvkNoj z(M{rkGKt0JFuR>b>yIc^#gj;3EvgC?SwpX9R1f zCcr!Lqd~EY$CRxcj*PFTFti=-j?EH?&2r*->Q?BZOGuL2(VB{C&1R!Xu3Yl`5~bI- zQE<9**~i+AlNbAaqso#7dtZ-gYExHSm8lzq{C8KHoE0-4rH)$4Zf3J|k*-!@q+FbR zM`Z}8G4=zpaPox2eo;@e@wmZ6JnlXW*t#8rWOeEwf-!o6G+4?ehR+!PWDu8x z&S;byY?gg0-K=WTwZ*tkr)!HqU9!DZtyz&4GLZtA0P?Z)_IvT{EGLVHbEB=vD?%do z?9DD-tJClDs*ka6Z$iE#dQ8-6LNN2?3CYo!7*@h$To>hGOa3rh^OZV-dXe0f;%zR< zH3&uL4E8!3)Dc0Y^$VrxBnCkaqDht?7kww4bIhOeE#H9TFv3qXs{x}^r^miHfi!D7 zBJ#2Faj9go2bL0h&gh6%3Utm{V{OPLlqSYCaj>g5LIn!EkZcicF)_aSz|t50lk>79 z!ob-9EOJ(=^Fl%@F!tYYA_^Rk3Z3_k_Nv;BK?W@4%5w`l#{Pv)_b)P^RueH{7-P~x zlKQZkHhzKZ04Dr6-H6Q&d5a39Bu_b)z3(GrN9}Y`V3l7lJXht|v;A=|)#|1>+EaUI zQk}-SrNhc}ZX<;{a5no=NVr5Y;X&AjKFY@Ts1~OJ@l1w?$uL;G&wV1!?%jdm)2z%y z^K-QcZ7$48>HN_ajMsW`vvU{vkgPY#T%_8qZG&}^6)1g7E$U*f<+zF+P0h6IiRIaL z1@qoRdoN>=*m0>xZ&@0PPY1f)MyJf!TUTab9L1Uis#r&+U{jC?yrvZJKSlpm_&ddr zbPsjfy7L>l(19381zArrwb3YfBonQYB-AQ4m&m4*QzJarO(M;}x_!E&f?|q?#e3l_ zD;(|Cn1FUzFko6>n8SREKPz%mc)4UWjYVDo5#xf*vP+@dVUtLlS|S~h3D0DfwltV& zbPAa-&#eC@-;theWbCH2=Z`VpY6EG!r=t@Ozi=C|lVoC~>_EKMmbm!MauaFp?ADH$ zWv;dt8qIu0X|w(E97uiEd1rk{Z2~;WL;3Ir;OwkT)|xdbD6+d*nd7K9eTYm)q#=~0 z4KguZR?qHLsWbjQb5E;D=scE2Jci=)HbyC+rb;}BD*-Pz(v?XOS*yhGdOv0&QYrHM^rY=L5lgOUW{3KY8n#@3M^s8-yG7(upL)5eZ|Dtn_e zSld&ZSYQXE7)|39s%5dEE%JniQLc)X*c5t!jeYU&McH^c!uVE=`zxCSl{l$*zZEj? zMPxS2ag+8?WYg||+2!@I4LfV6D=^F>28?^cWMx@~fRJbD~UaJ>~#OH`N5BGmkg)rF6!6cw8@L9>}5^b@dhm_oahE_{q4 z0u5wqC>x$F}{*Dp8J;$9n{Fy+eL#X6`4_ zi&y8czyZucZ7+>7LI|H_w64ZR?8e^QdUoBZD`Lejl9NQ8P&7Ligzayinrk+w&thSxHTXBI(-4=8p$ zWH(q_5=gQqIZ8Uj<*X```A|yt3o8U+r7yu3+kCi7mOWSISeyYA{Aau@&R>&6u%8QS5v}3Jp3TEt_`sL453T-s-1F ziRjfvS$Vcv+hfzOw$E!-UF+&%8o-hhmcenQ0mY)@Z9Rb*FIWY{W;!jolT_jhxAEta zqxQ{sy3jzbDJOZZ0TWdRV5Ed+DMGtu2DD}*S!Oz6TjHxJ)*V!@yU^fd#l#U}2^$EN zjm^1yY|#oTEh-3-gTwCJT9a}*^@)-=E%)q=By~ICqe%{>mwXfELngTeRZtpYay#i| zH=HO-*oFn(*ZD17`BP)hg*8r7UGx?3Tp|ms0A*pL7O5dDOME6kHNFy+ANR2qQRjI( z1$nXv)sv(+v5FoFcL=oIuHLM1qgRtL`iM&xACuOtgGTMcv4zh&u~lSrv$l$evdk>T zTRA+kERZG2f@1bYiVtd)3YRMAoSEI2)#fDSn=#HGq8x{r;zqfpHIb!-CTw>*IDCtB zg*u$5)mNIU*CuMa>qI`t8P(P2A}G^N7t#XGHH>8>GRN1~s>YkF{5KO7;;Pds!Jm`A ze9uju64G7M6NhUI{U7SywYiNW$r|2YG$D|>`PAzY1fWE-~NS)klp%0 zC~So?F34!3kv4oRxh0At)NLH+E4=CnS|xsDJ0lxb`6uLAzfap4+fSP4vByp0?PM_i zqhEovwIqB5ZL#O$aJa>c^B&#$St;K3*BPs(i;Y?w9pc0F%*p8FQsf<#_LTI6dr1MY)E{`3a6+`Gu!4Cp>12}Y` ziak~b$5$qm42x0{rr@&1XHQPE>`5-8_$Gsy9vN$-9pJ}e@>{*`L+h2|=+_8FJzqBk zEvjSu110)@En_j$;sH^_o$&>}cbR4jdi$`!6^{iP6iTc`JR|N^Ff6(2BUbVlj=mqs zk{rg1*;bR=vtnKYs#&njH4pQPVC=ZQR2ol>riV2c2fHxP%kf-?~nk=^J5-NV{MXAoz~){ zq~zFQPNcJl!Q~6GLpkTVNVjiSR0prQ4#LO(2-3^ zpqLjPpTACs6cY#pyq>hwL30~uu-VkA!{cNmi{TuS=k%@>a$96AeGKgQ@4SaVIfAGz zJ)e|L2pNm)Tb=~kf`>PZ067;DFlSs3+nAa%a^V2Aa~c=l59a>%3Mcz0K*ak=9jG^+ zI1GE?2YZ~m8+xzSk6$tT8{Ot8XY_xExE_CqGaUvr5?S&xZ^;tVs8e($frfrDM`gp0 zbB7Bd&j=^n zHRk2T1RGAxSM%M;Ge~~qyGw9(l01nBV+@;Nm~`u!M7U!yAnQwGw3kv0Gp|0Oz9cv;d)@|7jYP^{s(j)_&ept1P1c(o1xDN0YK)AT4+PBJ~8Kv(O zz~L-*{5o%+B2S=&c=lo7)|mMKJGuJwfKj}+sQ9>BZnjQMA0E(hh3fAeYsJ0Q7-%x< z6oz#YqLVMl*TWNE*Gh8zS!ZozmZA;99*EO(E6%?7=T&+B#mZ|imb zI>}Flp&S4dZrN0)o*tQZa8pr)n3{aTD51$~|Eihoueq1eArzDF@Lc#7cFzZw(QzpK zNw@vDCg`ydDfg5?`}63UHVoR)c7_?;pG(CCVxX^gt8Qs3pqYf?ILe?_Y{d#US?}}d zNd~-Jt~Zz~^5Y+WeE-92KKa|Jf7@^4{Gv}~yKD4b>Wl3AQ72v%wZ@@Ipn)6}nOIsJ zhLJLd#=qyQe2SnUdVz3wrtC^VYJb?(0NpfyhlE{w(V&OrRL}}A%O_|_#2n|NO{5kg zu7^ID@^k4DJ#mOeMl(UlnhaG2QujKwKpqcAw?N2^?Ay?#2aY00SJDDp4e2Nrcpx%L z&~ohFxE6Hp;f{x_R<}tQL}1U0yQigTnl!ks6ype#Ph%AogW**hK?a=aGxk4Hq$M~= zt)Sl<+|2YElcFNxi4ZPS&PtKo@@Bz>Vm_jEeQ5D#mGtg-1-}5J^b31d5Jz6dp|h(0 zJ9(o$TXN%e(^ls?Yj}^1lp`smP@WU6q#NeyCyv4%UV4&I-?>&M7VhZ!+Tw@%^tE?T z9P!&^cq?`c_})phG!%t2XI{c zD&uhmA=L5#jnk82$^n!vel?fUkp8P;Q?q2V=ymW`crqc~_{eec%WLm$>h<3vORv(f z{q=rjV|xVo7_iH%+Xe7{S(xLOl=}GX;%0doXZHdTAWDt{wj@XYOE-$TwS#VD^>#~CtR9yaw?oz_8j8_Qe}wEs+2 z45XPG<}v5skHv1Km0lr(G@}LhX4AOj;>^(GDi(^ZA7hBK(T63fR0CG7j@;`nO8{MsBw8U4N;W7 zTR+{ULyQ@nOE^6a%J_&W!Ng&>amA^&CQiXVxu_C>=e7BNRz`Cfe!X`W#fHXw{xo4w{}v z5t0v1vf8;g(S7@MyIbD7iSDaT9_Me9iF;=&)Za&ebzPJW>pQaNKr&_i*YfN1oV;)@ z%f5P{&Kp%@hrYuUKkxS8glzmJNa_GI4n+ZmBRGUncqLUJ+^)chUF%KP-M{A|4q60l zS_!_hN*6KOtcV-t6PqdOc=JF~9x@o13LjjX-|6Puvjl^6U~{z%w@98bM)>E`lI8(b zVtqY1EWQMI`$?N}@ST_}a*5I1%Vk_J%VH0FSd=R1@9F-1n$gJOVr^EBn6#UE!E5e6 zwo(i+bBJJPh5mOe25#}=tS=XeAA0!w=edW(YF>B-<)9+PKCL^ZR16;;kgaG^!96yY>C*$~CC|$?ld4&~FCV?ZbuSS{>mv1|gb677!vU zQ#a@M0qC^@sP+*!kWCgesZy}Gr!x}m+8xY zEjMc=8PuW%z}7~7hQdoMng~Ia{IrGC^YBTHwK(@YFgyi?;_~XT80GQ_g-j@EN{6sy z)-R1GqxtGF1^6Dm^%3?>ndQoxB zG7?uW?O{YAnh-Ld=(JVCvfp0YL|x3m1no%Kz_0_?2O2&wIh_U|Xz)Kpg2Q8hmzaKW zjY_8ZBnCYni3d=Y3cQZCeq%-|B7*+$e}1GZZ=^wYZIdEu040ts!f&J-97e{qW4AaY zrXxX0nuM>)MYI>J+H-dF=*eOwmP%2AzTaVv&MaT?6<8%(`$^j2Z>)sq3@XI!j&8{It0Gs`Co`!JKZM9ykriF$)focW34JA1w4XMx0~yRt#{Br5v6l zuSdEz2q{^!- zrV_E;&IPfYQAB6Khr+doNE-#TSDWSi@?lp=d33lRcQ@^veQBRsg-Lj(iWEXV87sX< z0>z6&rc^qjIG&3w>H7t!5-c)6wI&9U34D55UDyBC^`&D@hkN#vE8~VHG~CDX+D=DC zfj*}#R%cjC#>d}(gF2(K^vmLM^*n*dW1#tBi~WHx?~s6-^|UA_0`-OtkspCa91rwt;eVmJ0JGhE*-X5r2rTZ5)zx zM9+!y<~0YezlW_V{fqwA`JMixV7fnjLau? z8Zb2ZJ)ah8i(8)fqb76Gk zCUyYV!S02W^nBv5H(x(_L+;kaAV^ae7H?EJMCs)$NtLIX#<7@?OzEy*2WQXt>wyTA z-#`}H8hssUWhr&Q0~!)P=6m21NUg`u6}nn88ZyLDcwKea6JO-e*_PUH7>Y-d9^Z zddNuh^wg>);rEo$ACA-$2jTH!s5_HhkoYytA)$-p6_O%+N^eE+E~*dUKDK z%j2*sL=Ul;09K3WLdx@;M|bj;Jiwq?!u1deCtCPlXYU0Oz%4Z$+3p-NEu=#E2wywfCvhE@I!76+3?$R3{noCPB;3=0=Ej;b?_)BE9 zx{^{efZk4TB0?)==TWG^9a9K&RtRooy6g7q!<9gy=zQ_6*7;Pgqk1;NR{6vLqeJPn zilq6DL}(mZoO&(~0Zt4SF^v_z?4V0nnpZLy3nqIk>l3io0&a^R{g`NIS;!Y?a^4KUgRC(1k zXxViq>5<MmcEU3X99a3!e6`I$=MoqISHKnY}~t6#V`L$ons7O)Y1H9wvI zelj`nwNKdjhSgyB(}8Q&aBQ#TTZFH^^9Y7Y*tT~1f{-a0w(ds2Fz-(&r*0ig{*mN7^96w6f;7PI)T6`NbaOQ-7VF2 z6n@vNyr4ZZUZm>0rmpBDNe)Qm$!~}3U+|b3Phw-G7--7b)uJoc$>N;Vc5K2Y5Lsbr zx<}ZiEwSZ&0I ziCFGMQ!4imY_Gu9CnHRZ4=T*}f)Lk!dk#PHWw1xTD5GCq@+SwZOj^3$m#-QT4UQ~=<}$Qqxy%qoPduf`-* zd(t~F1CSHhNS_J|S*IC*R=pqyhu>e{8QYdANyqM`Z{=L-iBpM>>tkeos*4rM6C;30 z^6&rs-?MkPpYxv?xcI8YUl(65T}3DMr?7jRv4*t5b%P0WP$|}^O(Pop zy+S&;B9099!=i)^ayy9OHIzM;qk#tv5dflFR4K3n7yO;~#`$8qvx64fg*5gzin@pM zc6&`Wi8|H+NTvJm%B8Na47oL>nHRK(b@jwC{K`Kx%W$+^%E3<$BI)bUra^r;GC|4u zLr{&L-zZu+=A$DVf{3TvZTctkI;)=$D@QY`b!wxkRHCDv zoif+H3<{`nvsqz)DuN}n$L>~3Kj6`UpGwIMTTkHsYF)<0jf1N<+KdeYINZr0*1cOU zWPl=O?JZUh^Sd>uEtGFL{99gnNR|!%lC-3pwR7`J6s0V&Bvf_#+jK}o@}$RBbx=IY zRbP?^ld?pKNk%opIMqibgbIhy#G5Dd*>M);fRu3E;#gi=c#c|T*bt5JNr5bQ7z;!O zvR;w}dJ}vwl{S=yf*Yz8lq5pvZ?RcRk`U5KAqmd40~pIXk|cF$Tte0RiS?{yFcDz* z3??=24m4zp4Vt&)h#fzsL#fB^8}zKgEKjYA#f>|&_iv72gZ2Z41xfunlGJ9=ULdTJ z?KSG@jp)bY6ysKDpCv-6ML%7S+T0gcFb00o?%?nz@xyx#0E@^ig97s4M$v(=APmrW z4sN=p3wyda0w=czVd~7J<4Q+cl;Sk4+p8NC>n(z0lxYf0Gf@mW2T=wdrCwQ2<_zlI zz2K(QbN`Z*s(O0oFs7twevX;}=p9KoYqKeG81Ndt)Merper&kMQCQS5D5jSP(WQrW zW%{hP@enfb+D~5w`Kaq;k;D$MAj-R;VEBVmMMNEc#EKjXb`5=C!<<(hHdNQLvfmHE zaz{9}8e9XH;elU>T5O9#?Ze*~h6?T5lJ}ZnRt+M?Us1-&KOrkQOSk5ofS5Fko98TW zlVuOob@k?@C2%>_PxI@`r|o?0ro|E7kwV_wU^<1s{sA2PD5hVAY-Lmz<74Tow#wFd zcb&rYu?DuS{+3A-W1H5Eyhip~BKc%3c^DTTc2ae4d68FaE#hf_pr<8I{nz~@Ug@;JUO-^qhW zz&*tKi}R{2Il~TYhMt$@(uHa=@jD9o-Rg3)*nFKM-rb_MRsk)HjQ5|- zF0)D&%bkz6J|e;5>XTfqD?a%hl?2EjKh0dpiTn^AL>BCZW}Ww7Yy}-~rw(EZXEn6| z0mq7FC@DaIhuTUNLL-&WkE+!vpziw%Y~hU6;`l!lj=O*OJBIwSoe!( z%&dTSe3e(D>=ZW52WdW`a5eTi=C* zj)4%~4=Gy@pLb8_R-1+H*5u23QG9h^0<=#Dzm1-{hJC#mG+NYow{QV6PS|gOf zyA-)hM(Q+MVpPNDCT_z54P&K6iGkF2(QFw`D7kJgYt~bf7;sa{Q!bup*$Y^dEFXP5 zE_S!J^fw11Hq_6Y|8iLM07eSDnAdW$rU>J33WM^k@}=4V?EHBc;)Y?6MnIxN?6|jw zp;v=>=qwo3@XeNvHb$}}C6#aQc=2Nf%fLc{8qJV|g$QgGcc_ZON~X)Fo15hZ;GltK zu;BgI`Qy%2FZ7fq?3HF#FyU=kW}9AqMhPE7wc#@>aZfH4p6bg|gyEywPY@kecy;jTsAI|*s>96RE}6-rd>
;yYM0A4bmSPxu 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 literal 0 HcmV?d00001 diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/lib/tinyb-0.5.1.jar b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/lib/tinyb-0.5.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..dca6d7ad8bd83da890fa05649b9d975c0135aa30 GIT binary patch literal 15143 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 cL;4H3SCj#NP1mo7t6u*;y(Tbg_8 + + + 4.0.0 + + + org.eclipse.smarthome.binding + pom + 0.10.0-SNAPSHOT + + + org.eclipse.smarthome.binding.bluetooth.bluez + + Eclipse SmartHome BlueZ Bluetooth Adapter + eclipse-plugin + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/BlueZAdapterConstants.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/BlueZAdapterConstants.java new file mode 100644 index 00000000000..d45222c081c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/BlueZAdapterConstants.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluez; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.binding.bluetooth.BluetoothBindingConstants; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link BlueZAdapterConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Kai Kreuzer - Initial contribution and API + */ +@NonNullByDefault +public class BlueZAdapterConstants { + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_BLUEZ = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID, "bluez"); + + // Properties + public static final String PROPERTY_ADDRESS = "address"; + public static final String PROPERTY_DISCOVERY = "discovery"; + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/BlueZBluetoothDevice.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/BlueZBluetoothDevice.java new file mode 100644 index 00000000000..7cc09c34202 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/BlueZBluetoothDevice.java @@ -0,0 +1,378 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluez; + +import java.util.Collection; +import java.util.UUID; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.binding.bluetooth.BluetoothAddress; +import org.eclipse.smarthome.binding.bluetooth.BluetoothCharacteristic; +import org.eclipse.smarthome.binding.bluetooth.BluetoothCompletionStatus; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDescriptor; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDevice; +import org.eclipse.smarthome.binding.bluetooth.BluetoothService; +import org.eclipse.smarthome.binding.bluetooth.bluez.handler.BlueZBridgeHandler; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothConnectionStatusNotification; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothScanNotification; +import org.eclipse.smarthome.core.common.ThreadPoolManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import tinyb.BluetoothException; +import tinyb.BluetoothGattCharacteristic; +import tinyb.BluetoothGattDescriptor; +import tinyb.BluetoothGattService; + +/** + * Implementation of BluetoothDevice for BlueZ via TinyB + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public class BlueZBluetoothDevice extends BluetoothDevice { + + private tinyb.BluetoothDevice device; + + private final Logger logger = LoggerFactory.getLogger(BlueZBluetoothDevice.class); + + private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("bluetooth"); + + /** + * Constructor + * + * @param adapter the bridge handler through which this device is connected + * @param address the Bluetooth address of the device + * @param name the name of the device + */ + public BlueZBluetoothDevice(BlueZBridgeHandler adapter, BluetoothAddress address, String name) { + super(adapter, address); + this.name = name; + logger.debug("Creating BlueZ device with address '{}'", address); + } + + /** + * Constructor + * + * @param adapter the bridge handler through which this device is connected + * @param tinybDevice the tinyB device to use internally (which already contains address and name information) + */ + public BlueZBluetoothDevice(BlueZBridgeHandler adapter, tinyb.BluetoothDevice tinybDevice) { + super(adapter, new BluetoothAddress(tinybDevice.getAddress())); + this.name = tinybDevice.getName(); + this.device = tinybDevice; + } + + /** + * Initializes a newly created instance of this class. + * It tries to set the internal tinyB device, if it isn't yet available, which is done asynchronously as + * BlueZ can take a while (it seems to do an active scan for the physical device). + * + * This method should always be called directly after creating a new object instance. + */ + public synchronized void initialize() { + scheduler.submit(() -> { + if (this.device == null) { + tinyb.BluetoothDevice tinybDevice = findTinybDevice(address.toString()); + if (tinybDevice != null) { + device = tinybDevice; + enableNotifications(); + } + } else { + enableNotifications(); + } + }); + } + + /** + * Updates the internally used tinyB device instance. It replaces any previous instance, disables notifications on + * it and enables notifications on the new instance. + * + * @param tinybDevice the new device instance to use for communication + */ + public synchronized void updateTinybDevice(tinyb.BluetoothDevice tinybDevice) { + if (device != null && !tinybDevice.equals(device)) { + // we need to replace the instance - let's deactivate notifications on the old one + disableNotifications(); + } + if (this.device == null || !tinybDevice.equals(device)) { + this.device = tinybDevice; + enableNotifications(); + } + tinyb.BluetoothDevice dev = this.device; + if (dev != null) { + this.rssi = (int) dev.getRSSI(); + this.txPower = (int) dev.getTxPower(); + if (dev.getConnected()) { + this.connectionState = ConnectionState.CONNECTED; + } + } + refreshServices(); + } + + private tinyb.@Nullable BluetoothDevice findTinybDevice(String address) { + Collection deviceList = ((BlueZBridgeHandler) getAdapter()).getTinyBDevices(); + logger.trace("Searching for '{}' in {} devices.", address, deviceList.size()); + return deviceList.stream().filter(d -> d.getAddress().equals(address)).findFirst().orElse(null); + } + + private void enableNotifications() { + logger.debug("Enabling notifications for device '{}'", device.getAddress()); + device.enableRSSINotifications(n -> { + rssi = (int) n; + BluetoothScanNotification notification = new BluetoothScanNotification(); + notification.setRssi(n); + notifyListeners(BluetoothEventType.SCAN_RECORD, notification); + }); + device.enableConnectedNotifications(connected -> { + connectionState = connected ? ConnectionState.CONNECTED : ConnectionState.DISCONNECTED; + logger.debug("Connection state of '{}' changed to {}", address, connectionState); + notifyListeners(BluetoothEventType.CONNECTION_STATE, + new BluetoothConnectionStatusNotification(connectionState)); + }); + device.enableServicesResolvedNotifications(resolved -> { + logger.debug("Received services resolved event for '{}': {}", address, resolved); + if (resolved) { + refreshServices(); + notifyListeners(BluetoothEventType.SERVICES_DISCOVERED); + } + }); + device.enableServiceDataNotifications(data -> { + logger.debug("Received service data for '{}': {}", address, data); + }); + } + + private void disableNotifications() { + logger.debug("Disabling notifications for device '{}'", device.getAddress()); + device.disableBlockedNotifications(); + device.disableManufacturerDataNotifications(); + device.disablePairedNotifications(); + device.disableRSSINotifications(); + device.disableServiceDataNotifications(); + device.disableTrustedNotifications(); + } + + protected void refreshServices() { + if (device.getServices().size() > getServices().size()) { + for (BluetoothGattService tinybService : device.getServices()) { + BluetoothService service = new BluetoothService(UUID.fromString(tinybService.getUUID()), + tinybService.getPrimary()); + for (BluetoothGattCharacteristic tinybCharacteristic : tinybService.getCharacteristics()) { + BluetoothCharacteristic characteristic = new BluetoothCharacteristic( + UUID.fromString(tinybCharacteristic.getUUID()), 0); + for (BluetoothGattDescriptor tinybDescriptor : tinybCharacteristic.getDescriptors()) { + BluetoothDescriptor descriptor = new BluetoothDescriptor(characteristic, + UUID.fromString(tinybDescriptor.getUUID())); + characteristic.addDescriptor(descriptor); + } + service.addCharacteristic(characteristic); + } + addService(service); + } + notifyListeners(BluetoothEventType.SERVICES_DISCOVERED); + } + } + + @Override + public boolean connect() { + if (device != null && !device.getConnected()) { + try { + return device.connect(); + } catch (BluetoothException e) { + if ("Timeout was reached".equals(e.getMessage())) { + notifyListeners(BluetoothEventType.CONNECTION_STATE, + new BluetoothConnectionStatusNotification(ConnectionState.DISCONNECTED)); + } else if (e.getMessage() != null && e.getMessage().contains("Protocol not available")) { + // this device does not seem to be connectable at all - let's log a warning and ignore it. + logger.warn("Bluetooth device '{}' does not allow a connection.", device.getAddress()); + } else { + logger.debug("Exception occurred when trying to connect device '{}': {}", device.getAddress(), + e.getMessage()); + } + } + } + return false; + } + + @Override + public boolean disconnect() { + if (device != null && device.getConnected()) { + logger.debug("Disconnecting '{}'", address); + try { + return device.disconnect(); + } catch (BluetoothException e) { + logger.debug("Exception occurred when trying to disconnect device '{}': {}", device.getAddress(), + e.getMessage()); + } + } + return false; + } + + @Override + public boolean readCharacteristic(BluetoothCharacteristic characteristic) { + if (device == null) { + throw new IllegalStateException("TinyB device is not yet set"); + } + BluetoothGattCharacteristic c = getTinybCharacteristicByUUID(characteristic.getUuid().toString()); + scheduler.submit(() -> { + try { + byte[] value = c.readValue(); + characteristic.setValue(value); + notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic, + BluetoothCompletionStatus.SUCCESS); + } catch (BluetoothException e) { + logger.debug("Exception occurred when trying to read characteristic '{}': {}", characteristic.getUuid(), + e.getMessage()); + notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic, + BluetoothCompletionStatus.ERROR); + } + }); + return true; + } + + @Override + public boolean writeCharacteristic(BluetoothCharacteristic characteristic) { + if (device == null) { + throw new IllegalStateException("TinyB device is not yet set"); + } + BluetoothGattCharacteristic c = getTinybCharacteristicByUUID(characteristic.getUuid().toString()); + scheduler.submit(() -> { + try { + BluetoothCompletionStatus successStatus = c.writeValue(characteristic.getByteValue()) + ? BluetoothCompletionStatus.SUCCESS + : BluetoothCompletionStatus.ERROR; + notifyListeners(BluetoothEventType.CHARACTERISTIC_WRITE_COMPLETE, characteristic, successStatus); + } catch (BluetoothException e) { + logger.debug("Exception occurred when trying to read characteristic '{}': {}", characteristic.getUuid(), + e.getMessage()); + notifyListeners(BluetoothEventType.CHARACTERISTIC_WRITE_COMPLETE, characteristic, + BluetoothCompletionStatus.ERROR); + } + }); + return true; + } + + @Override + public boolean enableNotifications(BluetoothCharacteristic characteristic) { + if (device == null || !device.getConnected()) { + throw new IllegalStateException("TinyB device is not set or not connected"); + } + BluetoothGattCharacteristic c = getTinybCharacteristicByUUID(characteristic.getUuid().toString()); + if (c != null) { + try { + c.enableValueNotifications(value -> { + logger.debug("Received new value '{}' for characteristic '{}' of device '{}'", value, + characteristic.getUuid(), address); + characteristic.setValue(value); + notifyListeners(BluetoothEventType.CHARACTERISTIC_UPDATED, characteristic); + }); + } catch (BluetoothException e) { + if (e.getMessage().contains("Already notifying")) { + return false; + } else if (e.getMessage().contains("In Progress")) { + // let's retry in 10 seconds + scheduler.schedule(() -> enableNotifications(characteristic), 10, TimeUnit.SECONDS); + } else { + logger.warn("Exception occurred while activating notifications on '{}'", address, e); + } + } + return true; + } else { + logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address); + return false; + } + } + + @Override + public boolean disableNotifications(BluetoothCharacteristic characteristic) { + if (device == null || !device.getConnected()) { + throw new IllegalStateException("TinyB device is not set or not connected"); + } + BluetoothGattCharacteristic c = getTinybCharacteristicByUUID(characteristic.getUuid().toString()); + if (c != null) { + c.disableValueNotifications(); + return true; + } else { + logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address); + return false; + } + } + + @Override + public boolean enableNotifications(BluetoothDescriptor descriptor) { + if (device == null || !device.getConnected()) { + throw new IllegalStateException("TinyB device is not set or not connected"); + } + BluetoothGattDescriptor d = getTinybDescriptorByUUID(descriptor.getUuid().toString()); + if (d != null) { + d.enableValueNotifications(value -> { + logger.debug("Received new value '{}' for descriptor '{}' of device '{}'", value, descriptor.getUuid(), + address); + descriptor.setValue(value); + notifyListeners(BluetoothEventType.DESCRIPTOR_UPDATED, descriptor); + }); + return true; + } else { + logger.warn("Descriptor '{}' is missing on device '{}'.", descriptor.getUuid(), address); + return false; + } + } + + @Override + public boolean disableNotifications(BluetoothDescriptor descriptor) { + if (device == null) { + throw new IllegalStateException("TinyB device is not yet set"); + } + BluetoothGattDescriptor d = getTinybDescriptorByUUID(descriptor.getUuid().toString()); + if (d != null) { + d.disableValueNotifications(); + return true; + } else { + logger.warn("Descriptor '{}' is missing on device '{}'.", descriptor.getUuid(), address); + return false; + } + } + + private BluetoothGattCharacteristic getTinybCharacteristicByUUID(String uuid) { + for (BluetoothGattService service : device.getServices()) { + for (BluetoothGattCharacteristic c : service.getCharacteristics()) { + if (c.getUUID().equals(uuid)) { + return c; + } + } + } + return null; + } + + private BluetoothGattDescriptor getTinybDescriptorByUUID(String uuid) { + for (BluetoothGattService service : device.getServices()) { + for (BluetoothGattCharacteristic c : service.getCharacteristics()) { + for (BluetoothGattDescriptor d : c.getDescriptors()) { + if (d.getUUID().equals(uuid)) { + return d; + } + } + } + } + return null; + } + + /** + * Clean up and release memory. + */ + public void dispose() { + disableNotifications(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java new file mode 100644 index 00000000000..027e2c93f16 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java @@ -0,0 +1,275 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluez.handler; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.binding.bluetooth.BluetoothAdapter; +import org.eclipse.smarthome.binding.bluetooth.BluetoothAddress; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDevice; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDiscoveryListener; +import org.eclipse.smarthome.binding.bluetooth.bluez.BlueZAdapterConstants; +import org.eclipse.smarthome.binding.bluetooth.bluez.BlueZBluetoothDevice; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.eclipse.smarthome.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import tinyb.BluetoothException; +import tinyb.BluetoothManager; + +/** + * The {@link BlueZBridgeHandler} is responsible for talking to the BlueZ stack. + * It provides a private interface for {@link BlueZBluetoothDevice}s to access the stack and provides top + * level adaptor functionality for scanning and arbitration. + * + * @author Kai Kreuzer - Initial contribution and API + */ +public class BlueZBridgeHandler extends BaseBridgeHandler implements BluetoothAdapter { + + private static final Logger logger = LoggerFactory.getLogger(BlueZBridgeHandler.class); + + private tinyb.BluetoothAdapter adapter; + + private final Map tinybDeviceCache = new ConcurrentHashMap<>(); + + // Our BT address + private BluetoothAddress address; + + // internal flag for the discovery configuration + private boolean discoveryActive = true; + + // Map of Bluetooth devices known to this bridge. + // This is all devices we have heard on the network - not just things bound to the bridge + private final Map devices = new ConcurrentHashMap<>(); + + // Set of discovery listeners + protected final Set discoveryListeners = new CopyOnWriteArraySet<>(); + + private ScheduledFuture discoveryJob; + + /** + * Constructor + * + * @param bridge the bridge definition for this handler + */ + public BlueZBridgeHandler(Bridge bridge) { + super(bridge); + } + + @Override + public void initialize() { + try { + BluetoothManager.getBluetoothManager(); + } catch (UnsatisfiedLinkError e) { + throw new IllegalStateException("BlueZ JNI connection cannot be established.", e); + } + + Object cfgAddress = getConfig().get(BlueZAdapterConstants.PROPERTY_ADDRESS); + if (cfgAddress != null) { + address = new BluetoothAddress(cfgAddress.toString()); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "address not set"); + return; + } + + Object discovery = getConfig().get(BlueZAdapterConstants.PROPERTY_DISCOVERY); + if (discovery != null && discovery.toString().equalsIgnoreCase(Boolean.FALSE.toString())) { + discoveryActive = false; + logger.debug("Deactivated discovery participation."); + } + + logger.debug("Creating BlueZ adapter with address '{}'", address); + for (tinyb.BluetoothAdapter a : BluetoothManager.getBluetoothManager().getAdapters()) { + if (a.getAddress().equals(address.toString())) { + adapter = a; + updateStatus(ThingStatus.ONLINE); + if (discoveryActive) { + adapter.startDiscovery(); + } + discoveryJob = scheduler.scheduleWithFixedDelay(() -> { + checkForNewDevices(); + }, 0, 10, TimeUnit.SECONDS); + return; + } + } + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No adapter for this address found."); + } + + @Override + public ThingUID getUID() { + return getThing().getUID(); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + } + + @Override + public void addDiscoveryListener(BluetoothDiscoveryListener listener) { + discoveryListeners.add(listener); + } + + @Override + public void removeDiscoveryListener(@Nullable BluetoothDiscoveryListener listener) { + discoveryListeners.remove(listener); + } + + @Override + public void scanStart() { + adapter.setRssiDiscoveryFilter(-96); + adapter.startDiscovery(); + for (tinyb.BluetoothDevice tinybDevice : adapter.getDevices()) { + synchronized (devices) { + logger.debug("Device {} has RSSI {}", tinybDevice.getAddress(), tinybDevice.getRSSI()); + BluetoothDevice device = devices.get(tinybDevice.getAddress()); + if (device == null) { + createAndRegisterBlueZDevice(tinybDevice); + } else { + // let's update the rssi and txpower values + device.setRssi(tinybDevice.getRSSI()); + device.setTxPower(tinybDevice.getTxPower()); + // The Bluetooth discovery expects a complete list on every scan, + // so we also have to report the already known devices. + notifyDiscoveryListeners(device); + } + } + } + } + + @Override + public void scanStop() { + if (adapter != null && adapter.getDiscovering()) { + try { + adapter.stopDiscovery(); + } catch (BluetoothException e) { + // tinyb often throws the exception + // tinyb.BluetoothException: GDBus.Error:org.bluez.Error.Failed: No discovery started + // here, although we first check whether discovery is active or not. + // As a workaround, we simply ignore this exception. + } + } + } + + @Override + public BluetoothAddress getAddress() { + return address; + } + + @Override + public BluetoothDevice getDevice(BluetoothAddress address) { + if (devices.containsKey(address.toString())) { + return devices.get(address.toString()); + } else { + synchronized (devices) { + if (devices.containsKey(address.toString())) { + return devices.get(address.toString()); + } else { + BlueZBluetoothDevice device = new BlueZBluetoothDevice(this, address, ""); + device.initialize(); + devices.put(address.toString(), device); + return device; + } + } + } + } + + @Override + public void dispose() { + if (discoveryJob != null) { + discoveryJob.cancel(true); + discoveryJob = null; + } + for (BluetoothDevice device : devices.values()) { + ((BlueZBluetoothDevice) device).dispose(); + } + devices.clear(); + } + + public Collection getTinyBDevices() { + synchronized (tinybDeviceCache) { + return Collections.unmodifiableCollection(tinybDeviceCache.values()); + } + } + + private void checkForNewDevices() { + logger.debug("Refreshing Bluetooth device list..."); + Set newAddresses = new HashSet<>(); + List tinybDevices = adapter.getDevices(); + logger.debug("Found {} Bluetooth devices.", tinybDevices.size()); + synchronized (tinybDeviceCache) { + tinybDeviceCache.clear(); + tinybDevices.stream().forEach(d -> tinybDeviceCache.put(d.getAddress(), d)); + } + for (tinyb.BluetoothDevice tinybDevice : tinybDevices) { + synchronized (devices) { + newAddresses.add(tinybDevice.getAddress()); + BlueZBluetoothDevice device = (BlueZBluetoothDevice) devices.get(tinybDevice.getAddress()); + if (device == null) { + createAndRegisterBlueZDevice(tinybDevice); + } else { + device.updateTinybDevice(tinybDevice); + notifyDiscoveryListeners(device); + } + } + } + // clean up orphaned entries + synchronized (devices) { + Set oldAdresses = devices.keySet(); + for (String address : oldAdresses) { + if (!newAddresses.contains(address)) { + devices.remove(address); + } + } + } + } + + private BlueZBluetoothDevice createAndRegisterBlueZDevice(tinyb.BluetoothDevice tinybDevice) { + BlueZBluetoothDevice device = new BlueZBluetoothDevice(this, tinybDevice); + device.initialize(); + devices.put(tinybDevice.getAddress(), device); + notifyDiscoveryListeners(device); + return device; + } + + private void notifyDiscoveryListeners(BluetoothDevice device) { + if (discoveryActive && deviceReachable(device)) { + for (BluetoothDiscoveryListener listener : discoveryListeners) { + listener.deviceDiscovered(device); + } + } else { + logger.trace("Not notifying listeners for device '{}', because it is not reachable.", device.getAddress()); + } + } + + private boolean deviceReachable(BluetoothDevice device) { + Integer rssi = device.getRssi(); + return rssi != null && rssi != 0; + } + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/internal/BlueZHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/internal/BlueZHandlerFactory.java new file mode 100644 index 00000000000..d86237ca96a --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/internal/BlueZHandlerFactory.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluez.internal; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; + +import org.eclipse.smarthome.binding.bluetooth.BluetoothAdapter; +import org.eclipse.smarthome.binding.bluetooth.bluez.BlueZAdapterConstants; +import org.eclipse.smarthome.binding.bluetooth.bluez.handler.BlueZBridgeHandler; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.UID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; + +/** + * The {@link BlueZHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Kai Kreuzer - Initial contribution and API + */ +@Component(service = ThingHandlerFactory.class, immediate = true, configurationPid = "binding.bluetooth.bluez", configurationPolicy = ConfigurationPolicy.OPTIONAL) +public class BlueZHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .singleton(BlueZAdapterConstants.THING_TYPE_BLUEZ); + + private final Map> serviceRegs = new HashMap<>(); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(BlueZAdapterConstants.THING_TYPE_BLUEZ)) { + BlueZBridgeHandler handler = new BlueZBridgeHandler((Bridge) thing); + registerBluetoothAdapter(handler); + return handler; + } else { + return null; + } + } + + private synchronized void registerBluetoothAdapter(BluetoothAdapter adapter) { + this.serviceRegs.put(adapter.getUID(), bundleContext.registerService(BluetoothAdapter.class.getName(), adapter, + new Hashtable())); + } + + @Override + protected synchronized void removeHandler(ThingHandler thingHandler) { + if (thingHandler instanceof BluetoothAdapter) { + UID uid = ((BluetoothAdapter) thingHandler).getUID(); + ServiceRegistration serviceReg = this.serviceRegs.remove(uid); + if (serviceReg != null) { + serviceReg.unregister(); + } + } + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/internal/discovery/BlueZDiscoveryService.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/internal/discovery/BlueZDiscoveryService.java new file mode 100644 index 00000000000..3d4bb801956 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluez/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluez/internal/discovery/BlueZDiscoveryService.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluez.internal.discovery; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.binding.bluetooth.bluez.BlueZAdapterConstants; +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; + +import tinyb.BluetoothAdapter; +import tinyb.BluetoothManager; + +/** + * This is a discovery service, which checks whether we are running on a Linux with a BlueZ stack. + * If this is the case, we create a bridge handler that provides Bluetooth access through BlueZ. + * + * @author Kai Kreuzer - Initial Contribution and API + * + */ +@Component(immediate = true, service = DiscoveryService.class, configurationPid = "discovery.bluetooth.bluez") +public class BlueZDiscoveryService extends AbstractDiscoveryService { + + private BluetoothManager manager; + + public BlueZDiscoveryService() { + super(Collections.singleton(BlueZAdapterConstants.THING_TYPE_BLUEZ), 1, true); + } + + @Override + @Activate + protected void activate(@Nullable Map<@NonNull String, @Nullable Object> configProperties) { + modified(configProperties); + } + + @Override + @Modified + protected void modified(@Nullable Map<@NonNull String, @Nullable Object> configProperties) { + super.modified(configProperties); + if (isBackgroundDiscoveryEnabled()) { + startScan(); + } + } + + @Override + @Deactivate + protected void deactivate() { + super.deactivate(); + } + + @Override + protected void startScan() { + try { + manager = BluetoothManager.getBluetoothManager(); + manager.getAdapters().stream().map(this::createDiscoveryResult).forEach(this::thingDiscovered); + } catch (UnsatisfiedLinkError e) { + // we cannot initialize the BlueZ stack + return; + } + } + + private DiscoveryResult createDiscoveryResult(BluetoothAdapter adapter) { + return DiscoveryResultBuilder.create(new ThingUID(BlueZAdapterConstants.THING_TYPE_BLUEZ, getId(adapter))) + .withLabel("Bluetooth Interface " + adapter.getName()) + .withProperty(BlueZAdapterConstants.PROPERTY_ADDRESS, adapter.getAddress()) + .withRepresentationProperty(BlueZAdapterConstants.PROPERTY_ADDRESS).build(); + } + + private String getId(BluetoothAdapter adapter) { + return adapter.getInterfaceName().replaceAll("[^a-zA-Z0-9_]", ""); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/.classpath b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/.classpath new file mode 100644 index 00000000000..64777f25c82 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/.project b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/.project new file mode 100644 index 00000000000..1085ce79bc0 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/.project @@ -0,0 +1,28 @@ + + + org.eclipse.smarthome.binding.bluetooth.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..2b614bac445 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/META-INF/MANIFEST.MF @@ -0,0 +1,21 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Bluetooth Binding Tests +Bundle-SymbolicName: org.eclipse.smarthome.binding.bluetooth.test;singleton:=true +Bundle-Vendor: Eclipse.org/SmartHome +Bundle-Version: 0.10.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Fragment-Host: org.eclipse.smarthome.binding.bluetooth +Import-Package: org.eclipse.smarthome.binding.bluetooth, + org.eclipse.smarthome.core.events, + org.eclipse.smarthome.core.thing.util, + org.eclipse.smarthome.test.java, + org.eclipse.smarthome.test.storage, + org.hamcrest;core=split, + org.junit;version="4.0.0", + org.mockito, + org.objenesis, + org.osgi.framework, + org.osgi.service.device, + org.slf4j +Require-Bundle: org.junit,org.mockito,org.hamcrest diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/NOTICE b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/NOTICE new file mode 100644 index 00000000000..b8675cd02e8 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/NOTICE @@ -0,0 +1,19 @@ +This content is produced and maintained by the Eclipse SmartHome project. + +* Project home: https://eclipse.org/smarthome/ + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/eclipse/smarthome + +== Copyright Holders + +See the NOTICE file distributed with the source code at +https://github.com/eclipse/smarthome/blob/master/NOTICE +for detailed information regarding copyright ownership. diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/build.properties b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/build.properties new file mode 100644 index 00000000000..271c737ba03 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/build.properties @@ -0,0 +1,6 @@ +source.. = src/test/java/ +output.. = target/classes +bin.includes = META-INF/,\ + .,\ + NOTICE + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/org.eclipse.smarthome.binding.bluetooth.test.launch b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/org.eclipse.smarthome.binding.bluetooth.test.launch new file mode 100644 index 00000000000..b21dae0f3da --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/org.eclipse.smarthome.binding.bluetooth.test.launch @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/pom.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/pom.xml new file mode 100644 index 00000000000..f9542698dcb --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/pom.xml @@ -0,0 +1,119 @@ + + + 4.0.0 + + + org.eclipse.smarthome.binding + pom + 0.10.0-SNAPSHOT + + + org.eclipse.smarthome.binding.bluetooth.test + + eclipse-test-plugin + + Eclipse SmartHome Bluetooth Binding Tests + + + + + org.eclipse.tycho + target-platform-configuration + + + + + + eclipse-plugin + org.eclipse.equinox.event + 0.0.0 + + + eclipse-plugin + org.eclipse.equinox.ds + 0.0.0 + + + eclipse-plugin + org.eclipse.smarthome.config.xml + 0.0.0 + + + eclipse-plugin + org.eclipse.smarthome.core.thing.xml + 0.0.0 + + + eclipse-plugin + org.eclipse.smarthome.core.binding.xml + 0.0.0 + + + + + + + org.eclipse.tycho + tycho-surefire-plugin + + + + eclipse-plugin + ch.qos.logback.classic + 0.0.0 + + + eclipse-plugin + ch.qos.logback.core + 0.0.0 + + + eclipse-plugin + ch.qos.logback.slf4j + 0.0.0 + + + + + org.eclipse.equinox.ds + 1 + true + + + org.eclipse.equinox.event + 2 + true + + + org.eclipse.smarthome.core + 4 + true + + + org.eclipse.smarthome.core.thing + 4 + true + + + org.eclipse.smarthome.config.xml + 4 + true + + + org.eclipse.smarthome.core.thing.xml + 4 + true + + + org.eclipse.smarthome.core.binding.xml + 4 + true + + + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/src/test/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAddressTest.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/src/test/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAddressTest.java new file mode 100644 index 00000000000..6b23972797a --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.test/src/test/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAddressTest.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import org.junit.Test; + +public class BluetoothAddressTest { + + @Test(expected = IllegalArgumentException.class) + public void testConstructorWithNullParam() { + new BluetoothAddress(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testConstructorWithoutColons() { + new BluetoothAddress("123456789ABC"); + } + + @Test + public void testConstructorCorrect() { + new BluetoothAddress("12:34:56:78:9A:BC"); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/.classpath b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/.classpath new file mode 100644 index 00000000000..7f457fa4138 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/.project b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/.project new file mode 100644 index 00000000000..3684cd6a366 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/.project @@ -0,0 +1,33 @@ + + + org.eclipse.smarthome.binding.bluetooth + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ARCHITECTURE.md b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ARCHITECTURE.md new file mode 100644 index 00000000000..bceb488708d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ARCHITECTURE.md @@ -0,0 +1,68 @@ +# Bluetooth Binding overview + +The Bluetooth binding is implemented to allow bundles to extend the main Bluetooth bundle (this one) in order to add new Bluetooth adapter as well as device support. +This architecture means that such extension bundles must utilise the binding name `bluetooth`. + +A base class structure is defined in the `org.eclipse.smarthome.binding.bluetooth` bundle. +This includes the main classes required to implement Bluetooth: + +* `BluetoothAdapter`. This interface defines the main functionality required to be implemented by a Bluetooth adapter, including device discovery. Typically, this interface is implemented by a BridgeHandler and then registered as an OSGi service +* `BluetoothDiscoveryParticipant`. An interface to be implemented by services that can identify specific Bluetooth devices. +* `BluetoothDevice`. This implements a Bluetooth device. It manages the notifications of device notifications, Bluetooth service and characteristic management, and provides the main interface to communicate to a Bluetooth device. +* `BluetoothService`. Implements the Bluetooth service. A service holds a number of characteristics. +* `BluetoothCharacteristic`. Implements the Bluetooth characteristic. This is the basic component for communicating data to and from a Bluetooth device. +* `BluetoothDescriptor`. Implements the Bluetooth descriptors for each characteristic. + +## Implementing a new Bluetooth Adapter bundle + +Bluetooth adapters are modelled as a bridge in ESH. +The bridge handler provides the link with the Bluetooth hardware (eg a dongle, or system Bluetooth API). +An adapter bundle needs to implement two main classes: the `BridgeHandler` which should implement `BluetoothAdapter` (any be registered as a service), and a `ThingFactory`, which is required to instantiate the handler. + +The bridge handler must implement any functionality required to interface to the Bluetooth layer. +It is responsible for managing the Bluetooth scanning, device discovery (i.e. the device interrogation to get the list of services and characteristics) and reading and writing of characteristics. +The bridge needs to manage any interaction between the interface with any things it provides – this needs to account for any constraints that a interface may impose such that things do not need to worry about any peculiarities imposed by a specific interface. + +Classes such as `BluetoothCharacteristic` or `BluetoothService` may be extended to provide additional functionality to interface to a specific library if needed. + +## Implementing specific Bluetooth device support + +A specific Bluetooth thing handler provides the functionality required to interact with a specific Bluetooth device. +The new thing bundle needs to implement three main classes – a `BluetoothDiscoveryParticipant`, a `ThingHandler` and a `ThingFactory`, which is required to instantiate the handler. + +Two fundamental communications methods can be employed in Bluetooth: beacons and connected mode. A Bluetooth thing handler can implement one or both of these communications + In practice, a connected mode Thing implementation would normally handle the beacons in order to provide as a minimum the RSSI data. + +### Thing Naming + +To avoid naming conflicts with different Bluetooth bundles a strict naming policy for things and thing xml files is proposed. +This should use the bundle name and the thing name, separated with an underscore - e.g. for a Yeelight binding Blue2 thing, the thing type would be `yeelight_blue2`. + +### Connected Mode Implementation + +The connected mode `BluetoothThingHandler` needs to handle the following functionality +* Extend the connected bluetooth thing handler. This holds the `adapter` through which all communication is done. +* Call the `adapter.getDevice()` method to get the `BluetoothDevice` class for the requested device. The `getDevice()` method will return a `BluetoothDevice` class even if the device is not currently known. +* Implement the `BluetoothDeviceListener` methods. These provide callbacks for various notifications regarding device updates – e.g. when the connection state of a device changes, when the device discovery is complete, when a read and write completes, and when beacon messages are received. +* The parent class calls the `device.connect()` method to connect to the device. Once the device is connected, the `BluetoothDeviceListener.onConnectionStateChange()` callback will be called. +* The parent class calls the `device.discoverServices()` method to discover all the BluetoothServices and `BluetoothCharacteristic`s implemented by the device. Once this is complete, the `BluetoothDeviceListener.onServicesDiscovered()` callback will be called. +* Call the `readCharacteristic` or `writeCharacteristic` methods to interact with the device. The `BluetoothDeviceListener.onCharacteristicReadComplete()` and `BluetoothDeviceListener.onCharacteristicWriteComplete()` methods will be called on completion. +* Implement the `BluetoothDeviceListener.onCharacteristicUpdate()` method to process any read responses or unsolicited updates of a characteristic value. + +### Beacon Mode Implementation + +The beacon mode thing handler needs to handle the following functionality: + +* Extend the beacon Bluetooth thing handler. This holds the `adapter` through which all communication is done. +* Call the `adapter.getDevice()` method to get the `BluetoothDevice` class for the requested device. The `getDevice()` method will return a `BluetoothDevice` class even if the device is not currently known. +* Implement the `BluetoothDeviceListener.onScanRecordReceived()` method to process the beacons. The notification will provide the current receive signal strength (RSSI), the raw beacon data, and various elements of generally useful beacon data is provided separately. + +### Generic Bluetooth Device Support + +The core Bluetooth binding already includes generic "beacon" and "connected" Bluetooth thing types. +All devices for which no discovery participant defines a specific thing type are added to the inbox as a beacon device. +The corresponding handler implementation (`BeaconBluetoothHandler`) uses Beacon mode and merely defines a channel for RSSI for such devices. + +The "connected" thing type can be used by manually defining a thing. +The corresponding handler implementation (`ConnectedBluetoothHandler`) uses Connected mode and thus immediately connects to the device and reads its services. +Common services are added as channels (t.b.d.). diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/binding/binding.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/binding/binding.xml new file mode 100644 index 00000000000..89bd040b06a --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/binding/binding.xml @@ -0,0 +1,8 @@ + + + + Bluetooth Binding + This binding supports the Bluetooth protocol. + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/thing/channels.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/thing/channels.xml new file mode 100644 index 00000000000..a7824ffbb07 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/thing/channels.xml @@ -0,0 +1,14 @@ + + + + + Number + + Received signal strength indicator + QualityOfService + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/thing/thing-types.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/thing/thing-types.xml new file mode 100644 index 00000000000..f2c5f3e31e4 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/ESH-INF/thing/thing-types.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + A generic Bluetooth device in beacon-mode + + + + + + + + The unique Bluetooth address of the device + + + + + + + + + + + + A generic Bluetooth device in connected-mode + + + + + + + + The unique Bluetooth address of the device + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..df568a56b1a --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/META-INF/MANIFEST.MF @@ -0,0 +1,28 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Bluetooth Binding +Bundle-SymbolicName: org.eclipse.smarthome.binding.bluetooth;singleton:=true +Bundle-Vendor: Eclipse.org/SmartHome +Bundle-Version: 0.10.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ClassPath: . +Import-Package: + org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.binding.bluetooth, + org.eclipse.smarthome.binding.bluetooth.discovery, + org.eclipse.smarthome.binding.bluetooth.notification, + org.eclipse.smarthome.config.core, + org.eclipse.smarthome.config.discovery, + org.eclipse.smarthome.core.common.registry, + org.eclipse.smarthome.core.library.types, + org.eclipse.smarthome.core.thing, + org.eclipse.smarthome.core.thing.binding, + org.eclipse.smarthome.core.thing.binding.builder, + org.eclipse.smarthome.core.thing.type, + org.eclipse.smarthome.core.types, + org.slf4j +Service-Component: OSGI-INF/*.xml +Export-Package: org.eclipse.smarthome.binding.bluetooth, + org.eclipse.smarthome.binding.bluetooth.discovery, + org.eclipse.smarthome.binding.bluetooth.notification +Bundle-ActivationPolicy: lazy diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/NOTICE b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/NOTICE new file mode 100644 index 00000000000..b8675cd02e8 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/NOTICE @@ -0,0 +1,19 @@ +This content is produced and maintained by the Eclipse SmartHome project. + +* Project home: https://eclipse.org/smarthome/ + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/eclipse/smarthome + +== Copyright Holders + +See the NOTICE file distributed with the source code at +https://github.com/eclipse/smarthome/blob/master/NOTICE +for detailed information regarding copyright ownership. diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/OSGI-INF/.gitignore b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/OSGI-INF/.gitignore new file mode 100644 index 00000000000..b878e882aca --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/OSGI-INF/.gitignore @@ -0,0 +1 @@ +/*.xml diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/README.md b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/README.md new file mode 100644 index 00000000000..9f882d66982 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/README.md @@ -0,0 +1,72 @@ +# Bluetooth Binding + +This binding provides support for generic Bluetooth devices. + +## Bridges + +In order to function, this binding requires a Bluetoooth adapter to be present, which handles the wireless communication. +As there is no standard in Bluetooth for such dongles resp. chips, different adapters require a different implementation. +This is why the Bluetooth binding itself does not come with any bridge handlers for such adapters itself, but instead is extensible by additional bundles which can implement support for a specific adapter. + +For Linux, there exists a special bundle which provides a Bluetooth bridge that talks to BlueZ. +This should be the best choice for any Linux-based single board computers like e.g. the Raspberry Pi. + +## Supported Things + +Two thing types are supported by this binding: + +| Thing Type ID | Description | +|---------------|---------------------------------------------------------------------------------------------------------| +| beacon | A Bluetooth device that is not connected, but only broadcasts annoucements. | +| connected | A Bluetooth device that allows a direct connection and which provides specific services when connected. | + + +## Discovery + +Discovery is performed through the Bluetooth bridge. +Normally, any broadcasting Bluetooth device can be uniquely identified and thus a bridge can create an inbox result for it. +As this might lead to a huge list of devices, bridges usually also offer a way to deactivate this behavior. + +## Thing Configuration + +Both thing types only require a single configuration parameter `address`, which corresponds to the Bluetooth address of the device (in format "XX:XX:XX:XX:XX:XX"). + +## Channels + +Every Bluetooth thing has the following channel: + +| Channel ID | Item Type | Description | +|------------|-----------|-----------------------------------------------------------------------------------------------------| +| rssi | Number | The "Received Signal Strength Indicator", the [RSSI](https://blog.bluetooth.com/proximity-and-rssi) | + +`connected` Things are dynamically queried for their services and if they support certain standard GATT characteristics, the appropriate channels are automatically added as well: + +| Channel ID | Item Type | Description | +|---------------|-----------|-----------------------------------------------------------------| +| battery_level | Number | The device's battery level in percent | + + +## Full Example + +demo.things (assuming you have a Bluetooth bridge with the ID `bluetooth:bluez:hci0`): + +``` +bluetooth:beacon:hci0:b1 "BLE Beacon" (bluetooth:bluez:hci0) [ address="68:64:4C:14:FC:C4" ] +``` + +demo.items: + +``` +Number Beacon_RSSI "My Beacon [%.0f]" { channel="bluetooth:beacon:hci0:b1:rssi" } +``` + +demo.sitemap: + +``` +sitemap demo label="Main Menu" +{ + Frame { + Text item=Beacon_RSSI + } +} +``` diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/build.properties b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/build.properties new file mode 100644 index 00000000000..a6cfff567c9 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/build.properties @@ -0,0 +1,7 @@ +source..=src/main/java/ +output..=target/classes +bin.includes=META-INF/,\ + .,\ + OSGI-INF/,\ + ESH-INF/,\ + NOTICE diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/pom.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/pom.xml new file mode 100644 index 00000000000..80a89b8112f --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/pom.xml @@ -0,0 +1,18 @@ + + + + 4.0.0 + + + org.eclipse.smarthome.binding + pom + 0.10.0-SNAPSHOT + + + org.eclipse.smarthome.binding.bluetooth + + Eclipse SmartHome Bluetooth Binding + eclipse-plugin + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BeaconBluetoothHandler.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BeaconBluetoothHandler.java new file mode 100644 index 00000000000..484fac43d28 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BeaconBluetoothHandler.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothConnectionStatusNotification; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothScanNotification; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.thing.binding.BridgeHandler; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.eclipse.smarthome.core.types.UnDefType; + +/** + * This is a handler for generic Bluetooth devices in beacon-mode (i.e. not connected), which at the same time can be + * used as a base implementation for more specific thing handlers. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public class BeaconBluetoothHandler extends BaseThingHandler implements BluetoothDeviceListener { + + protected BluetoothAdapter adapter; + protected BluetoothAddress address; + protected BluetoothDevice device; + + public BeaconBluetoothHandler(@NonNull Thing thing) { + super(thing); + } + + @Override + public void initialize() { + try { + address = new BluetoothAddress(getConfig().get(BluetoothBindingConstants.CONFIGURATION_ADDRESS).toString()); + } catch (IllegalArgumentException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getLocalizedMessage()); + return; + } + + Bridge bridge = getBridge(); + if (bridge == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Not associated with any bridge"); + return; + } + + BridgeHandler bridgeHandler = bridge.getHandler(); + if (!(bridgeHandler instanceof BluetoothAdapter)) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Associated with an unsupported bridge"); + return; + } + + adapter = (BluetoothAdapter) bridgeHandler; + device = adapter.getDevice(address); + device.addListener(this); + + updateStatus(ThingStatus.UNKNOWN); + } + + @Override + public void dispose() { + if (device != null) { + device.removeListener(this); + device = null; + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command == RefreshType.REFRESH && channelUID.getId().equals(BluetoothBindingConstants.CHANNEL_TYPE_RSSI)) { + updateRSSI(); + } + } + + /** + * Updates the RSSI channel and the Thing status according to the new received rssi value + */ + protected void updateRSSI() { + if (device != null) { + Integer rssi = device.getRssi(); + if (rssi != null && rssi != 0) { + updateState(BluetoothBindingConstants.CHANNEL_TYPE_RSSI, new DecimalType(rssi)); + updateStatusBasedOnRssi(true); + } else { + updateState(BluetoothBindingConstants.CHANNEL_TYPE_RSSI, UnDefType.NULL); + updateStatusBasedOnRssi(false); + } + } + } + + /** + * This method sets the Thing status based on whether or not we can receive a signal from it. + * This is the best logic for beacons, but connected devices might want to deactivate this by overriding the method. + * + * @param receivedSignal true, if the device is in reach + */ + protected void updateStatusBasedOnRssi(boolean receivedSignal) { + if (receivedSignal) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + } + } + + @Override + public void onScanRecordReceived(BluetoothScanNotification scanNotification) { + int rssi = scanNotification.getRssi(); + device.setRssi(rssi); + updateRSSI(); + } + + @Override + public void onConnectionStateChange(@NonNull BluetoothConnectionStatusNotification connectionNotification) { + } + + @Override + public void onServicesDiscovered() { + } + + @Override + public void onCharacteristicReadComplete(@NonNull BluetoothCharacteristic characteristic, + @NonNull BluetoothCompletionStatus status) { + } + + @Override + public void onCharacteristicWriteComplete(@NonNull BluetoothCharacteristic characteristic, + @NonNull BluetoothCompletionStatus status) { + } + + @Override + public void onCharacteristicUpdate(@NonNull BluetoothCharacteristic characteristic) { + } + + @Override + public void onDescriptorUpdate(@NonNull BluetoothDescriptor bluetoothDescriptor) { + } + +} \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAdapter.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAdapter.java new file mode 100644 index 00000000000..3446ec5d1b0 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAdapter.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.common.registry.Identifiable; +import org.eclipse.smarthome.core.thing.ThingUID; + +/** + * The {@link BluetoothAdapter} class defines the standard adapter API that must be implemented by bridge handlers, + * which are then required to be registered as an BluetoothAdapter OSGi service. + *

+ * Scanning + * The API assumes that the adapter is "always" scanning to enable beacons to be received. + * The bridge must decide to enable and disable scanning as it needs. This design choice avoids interaction between + * higher layers where a binding may want to enable scanning while another needs to disable scanning for a specific + * function (e.g. to connect to a device). The bridge should disable scanning only for the period that is needed. + * + * @author Chris Jackson - Initial contribution + * @author Kai Kreuzer - renamed it, made it identifiable and added listener support + */ +@NonNullByDefault +public interface BluetoothAdapter extends Identifiable { + + /** + * Adds a {@link BluetoothDiscoveryListener} to the adapter + * + * @param listener the listener to add + */ + void addDiscoveryListener(BluetoothDiscoveryListener listener); + + /** + * Removes a {@link BluetoothDiscoveryListener} from the adapter + * + * @param listener the listener to remove + */ + void removeDiscoveryListener(@Nullable BluetoothDiscoveryListener listener); + + /** + * Starts an active scan on the Bluetooth interface. + */ + void scanStart(); + + /** + * Stops an active scan on the Bluetooth interface + */ + void scanStop(); + + /** + * Gets the {@link BluetoothAddress} of the adapter + * + * @return the {@link BluetoothAddress} of the adapter + * @throws IllegalStateException if the adapter is not initialized + */ + BluetoothAddress getAddress(); + + /** + * Gets the {@link BluetoothDevice} given the {@link BluetoothAddress}. + * A {@link BluetoothDevice} will always be returned for a valid hardware address, even if this adapter has never + * seen that device. + * + * @param address the {@link BluetoothAddress} to retrieve + * @return the {@link BluetoothDevice} + * @throws IllegalArgumentException if the address is no valid hardware address + */ + BluetoothDevice getDevice(BluetoothAddress address); + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAddress.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAddress.java new file mode 100644 index 00000000000..0bdb22f9704 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothAddress.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +/** + * The {@link BluetoothAddress} class defines a bluetooth address + * + * @author Chris Jackson - Initial contribution + */ +public class BluetoothAddress { + + public static final int BD_ADDRESS_LENGTH = 17; + + private final String address; + + /** + * The default constructor + * + * @param address the device address + */ + public BluetoothAddress(String address) { + if (address == null || address.length() != BD_ADDRESS_LENGTH) { + throw new IllegalArgumentException("BT Address cannot be null and must be in format XX:XX:XX:XX:XX:XX"); + } + for (int i = 0; i < BD_ADDRESS_LENGTH; i++) { + char c = address.charAt(i); + + // Check address - 2 bytes should be hex, and then a colon + switch (i % 3) { + case 0: // fall through + case 1: + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { + break; + } + throw new IllegalArgumentException("BT Address must contain upper case hex values only"); + case 2: + if (c == ':') { + break; + } + throw new IllegalArgumentException("BT Address bytes must be separated with colon"); + } + } + + this.address = address; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((address == null) ? 0 : address.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + BluetoothAddress other = (BluetoothAddress) obj; + if (address == null) { + if (other.address != null) { + return false; + } + } else if (!address.equals(other.address)) { + return false; + } + return true; + } + + @Override + public String toString() { + return address; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothBindingConstants.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothBindingConstants.java new file mode 100644 index 00000000000..d31dbc63720 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothBindingConstants.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import java.util.UUID; + +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link BluetoothBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Chris Jackson - Initial contribution + * @author Kai Kreuzer - refactoring and extension + */ +public class BluetoothBindingConstants { + + public static final String BINDING_ID = "bluetooth"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_CONNECTED = new ThingTypeUID(BINDING_ID, "connected"); + public static final ThingTypeUID THING_TYPE_BEACON = new ThingTypeUID(BINDING_ID, "beacon"); + + // List of all Channel Type IDs + public static final String CHANNEL_TYPE_RSSI = "rssi"; + + public static final String PROPERTY_TXPOWER = "txpower"; + public static final String PROPERTY_MAXCONNECTIONS = "maxconnections"; + + public static final String CONFIGURATION_ADDRESS = "address"; + + public static final long BLUETOOTH_BASE_UUID = 0x800000805f9b34fbL; + + // Bluetooth profile UUID definitions + public final static UUID PROFILE_GATT = UUID.fromString("00001801-0000-1000-8000-00805f9b34fb"); + public final static UUID PROFILE_A2DP_SOURCE = UUID.fromString("0000110a-0000-1000-8000-00805f9b34fb"); + public final static UUID PROFILE_A2DP_SINK = UUID.fromString("0000110b-0000-1000-8000-00805f9b34fb"); + public final static UUID PROFILE_A2DP = UUID.fromString("0000110d-0000-1000-8000-00805f9b34fb"); + public final static UUID PROFILE_AVRCP_REMOTE = UUID.fromString("0000110c-0000-1000-8000-00805f9b34fb"); + public final static UUID PROFILE_CORDLESS_TELEPHONE = UUID.fromString("00001109-0000-1000-8000-00805f9b34fb"); + public final static UUID PROFILE_DID_PNPINFO = UUID.fromString("00001200-0000-1000-8000-00805f9b34fb"); + public final static UUID PROFILE_HEADSET = UUID.fromString("00001108-0000-1000-8000-00805f9b34fb"); + public final static UUID PROFILE_HFP = UUID.fromString("0000111e-0000-1000-8000-00805f9b34fb"); + public final static UUID PROFILE_HFP_AUDIOGATEWAY = UUID.fromString("0000111f-0000-1000-8000-00805f9b34fb"); + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCharacteristic.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCharacteristic.java new file mode 100644 index 00000000000..c7888b02807 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCharacteristic.java @@ -0,0 +1,658 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link BluetoothCharacteristic} class defines the Bluetooth characteristic. + *

+ * Characteristics are defined attribute types that contain a single logical value. + *

+ * https://www.bluetooth.com/specifications/gatt/characteristics + * + * @author Chris Jackson - Initial contribution + * @author Kai Kreuzer - Cleaned up code + */ +public class BluetoothCharacteristic { + public static final int FORMAT_UINT8 = 0x11; + public static final int FORMAT_UINT16 = 0x12; + public static final int FORMAT_UINT32 = 0x14; + public static final int FORMAT_SINT8 = 0x21; + public static final int FORMAT_SINT16 = 0x22; + public static final int FORMAT_SINT32 = 0x24; + public static final int FORMAT_SFLOAT = 0x32; + public static final int FORMAT_FLOAT = 0x34; + + public static final int PROPERTY_BROADCAST = 0x01; + public static final int PROPERTY_READ = 0x02; + public static final int PROPERTY_WRITE_NO_RESPONSE = 0x04; + public static final int PROPERTY_WRITE = 0x08; + public static final int PROPERTY_NOTIFY = 0x10; + public static final int PROPERTY_INDICATE = 0x20; + public static final int PROPERTY_SIGNED_WRITE = 0x40; + public static final int PROPERTY_EXTENDED_PROPS = 0x80; + + public static final int PERMISSION_READ = 0x01; + public static final int PERMISSION_READ_ENCRYPTED = 0x02; + public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; + public static final int PERMISSION_WRITE = 0x10; + public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; + public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; + public static final int PERMISSION_WRITE_SIGNED = 0x80; + public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; + + public static final int WRITE_TYPE_DEFAULT = 0x02; + public static final int WRITE_TYPE_NO_RESPONSE = 0x01; + public static final int WRITE_TYPE_SIGNED = 0x04; + + private final Logger logger = LoggerFactory.getLogger(BluetoothCharacteristic.class); + + /** + * The {@link UUID} for this characteristic + */ + protected UUID uuid; + + /** + * The handle for this characteristic + */ + protected int handle; + + /** + * A map of {@link BluetoothDescriptor}s applicable to this characteristic + */ + protected Map gattDescriptors = new HashMap(); + protected int instance; + protected int properties; + protected int permissions; + protected int writeType; + + /** + * The raw data value for this characteristic + */ + protected int[] value = new int[0]; + + /** + * The {@link BluetoothService} to which this characteristic belongs + */ + protected BluetoothService service; + + /** + * Create a new BluetoothCharacteristic. + * + * @param uuid the {@link UUID} of the new characteristic + * @param handle + */ + public BluetoothCharacteristic(UUID uuid, int handle) { + this.uuid = uuid; + this.handle = handle; + } + + /** + * Adds a descriptor to this characteristic. + * + * @param descriptor {@link BluetoothDescriptor} to be added to this characteristic. + * @return true, if the descriptor was added to the characteristic + */ + public boolean addDescriptor(BluetoothDescriptor descriptor) { + if (gattDescriptors.get(descriptor.getUuid()) != null) { + return false; + } + + gattDescriptors.put(descriptor.getUuid(), descriptor); + return true; + } + + /** + * Returns the {@link UUID} of this characteristic + * + * @return UUID of this characteristic + */ + public UUID getUuid() { + return uuid; + } + + /** + * Returns the instance ID for this characteristic. + * + * If a remote device offers multiple characteristics with the same UUID, the instance ID is used to distinguish + * between characteristics. + * + * @return Instance ID of this characteristic + */ + public int getInstanceId() { + return instance; + } + + /** + * Returns the properties of this characteristic. + * + * The properties contain a bit mask of property flags indicating the features of this characteristic. + * + */ + public int getProperties() { + return properties; + } + + /** + * Returns the permissions for this characteristic. + */ + public int getPermissions() { + return permissions; + } + + /** + * Gets the write type for this characteristic. + * + */ + public int getWriteType() { + return writeType; + } + + /** + * Set the write type for this characteristic + * + * @param writeType + */ + public void setWriteType(int writeType) { + this.writeType = writeType; + } + + /** + * Get the service to which this characteristic belongs + * + * @return the {@link BluetoothService} + */ + public BluetoothService getService() { + return service; + } + + /** + * Returns the handle for this characteristic + * + * @return the handle for the characteristic + */ + public int getHandle() { + return handle; + } + + /** + * Get the service to which this characteristic belongs + * + * @return the {@link BluetoothService} + */ + public void setService(BluetoothService service) { + this.service = service; + } + + /** + * Returns a list of descriptors for this characteristic. + * + */ + public List getDescriptors() { + return new ArrayList(gattDescriptors.values()); + } + + /** + * Returns a descriptor with a given UUID out of the list of + * descriptors for this characteristic. + * + * @return the {@link BluetoothDescriptor} + */ + public BluetoothDescriptor getDescriptor(UUID uuid) { + return gattDescriptors.get(uuid); + } + + /** + * Get the stored value for this characteristic. + * + */ + public int[] getValue() { + return value; + } + + /** + * Get the stored value for this characteristic. + * + */ + public byte[] getByteValue() { + byte[] byteValue = new byte[value.length]; + for (int cnt = 0; cnt < value.length; cnt++) { + byteValue[cnt] = (byte) (value[cnt] & 0xFF); + } + return byteValue; + } + + /** + * Return the stored value of this characteristic. + * + */ + public Integer getIntValue(int formatType, int offset) { + if ((offset + getTypeLen(formatType)) > value.length) { + return null; + } + + switch (formatType) { + case FORMAT_UINT8: + return unsignedByteToInt(value[offset]); + + case FORMAT_UINT16: + return unsignedBytesToInt(value[offset], value[offset + 1]); + + case FORMAT_UINT32: + return unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]); + + case FORMAT_SINT8: + return unsignedToSigned(unsignedByteToInt(value[offset]), 8); + + case FORMAT_SINT16: + return unsignedToSigned(unsignedBytesToInt(value[offset], value[offset + 1]), 16); + + case FORMAT_SINT32: + return unsignedToSigned( + unsignedBytesToInt(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]), 32); + default: + logger.error("Unknown format type {} - no int value can be provided for it.", formatType); + } + + return null; + } + + /** + * Return the stored value of this characteristic. This doesn't read the remote data. + * + */ + public Float getFloatValue(int formatType, int offset) { + if ((offset + getTypeLen(formatType)) > value.length) { + return null; + } + + switch (formatType) { + case FORMAT_SFLOAT: + return bytesToFloat(value[offset], value[offset + 1]); + case FORMAT_FLOAT: + return bytesToFloat(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]); + default: + logger.error("Unknown format type {} - no float value can be provided for it.", formatType); + } + + return null; + } + + /** + * Return the stored value of this characteristic. This doesn't read the remote data. + * + */ + public String getStringValue(int offset) { + if (value == null || offset > value.length) { + return null; + } + byte[] strBytes = new byte[value.length - offset]; + for (int i = 0; i < (value.length - offset); ++i) { + strBytes[i] = (byte) value[offset + i]; + } + return new String(strBytes, StandardCharsets.UTF_8); + } + + /** + * Updates the locally stored value of this characteristic. + * + * @param value the value to set + * @return true, if it has been set successfully + */ + public boolean setValue(int[] value) { + this.value = value; + return true; + } + + /** + * Set the local value of this characteristic. + * + * @param value the value to set + * @param formatType the format of the value (as one of the FORMAT_* constants in this class) + * @param offset the offset to use when interpreting the value + * @return true, if it has been set successfully + */ + public boolean setValue(int value, int formatType, int offset) { + int len = offset + getTypeLen(formatType); + if (this.value == null) { + this.value = new int[len]; + } + if (len > this.value.length) { + return false; + } + int val = value; + switch (formatType) { + case FORMAT_SINT8: + val = intToSignedBits(value, 8); + // Fall-through intended + case FORMAT_UINT8: + this.value[offset] = (byte) (val & 0xFF); + break; + + case FORMAT_SINT16: + val = intToSignedBits(value, 16); + // Fall-through intended + case FORMAT_UINT16: + this.value[offset] = (byte) (val & 0xFF); + this.value[offset + 1] = (byte) ((val >> 8) & 0xFF); + break; + + case FORMAT_SINT32: + val = intToSignedBits(value, 32); + // Fall-through intended + case FORMAT_UINT32: + this.value[offset] = (byte) (val & 0xFF); + this.value[offset + 1] = (byte) ((val >> 8) & 0xFF); + this.value[offset + 2] = (byte) ((val >> 16) & 0xFF); + this.value[offset + 2] = (byte) ((val >> 24) & 0xFF); + break; + + default: + return false; + } + return true; + } + + /** + * Set the local value of this characteristic. + * + * @param mantissa the mantissa of the value + * @param exponent the exponent of the value + * @param formatType the format of the value (as one of the FORMAT_* constants in this class) + * @param offset the offset to use when interpreting the value + * @return true, if it has been set successfully + * + */ + public boolean setValue(int mantissa, int exponent, int formatType, int offset) { + int len = offset + getTypeLen(formatType); + if (value == null) { + value = new int[len]; + } + if (len > value.length) { + return false; + } + + switch (formatType) { + case FORMAT_SFLOAT: + int m = intToSignedBits(mantissa, 12); + int exp = intToSignedBits(exponent, 4); + value[offset] = (byte) (m & 0xFF); + value[offset + 1] = (byte) ((m >> 8) & 0x0F); + value[offset + 1] += (byte) ((exp & 0x0F) << 4); + break; + + case FORMAT_FLOAT: + m = intToSignedBits(mantissa, 24); + exp = intToSignedBits(exponent, 8); + value[offset] = (byte) (m & 0xFF); + value[offset + 1] = (byte) ((m >> 8) & 0xFF); + value[offset + 2] = (byte) ((m >> 16) & 0xFF); + value[offset + 2] += (byte) (exp & 0xFF); + break; + + default: + return false; + } + + return true; + } + + /** + * Set the local value of this characteristic. + * + * @param value the value to set + * @return true, if it has been set successfully + */ + public boolean setValue(byte[] value) { + this.value = new int[value.length]; + int cnt = 0; + for (byte val : value) { + this.value[cnt++] = val; + } + return true; + } + + /** + * Set the local value of this characteristic. + * + * @param value the value to set + * @return true, if it has been set successfully + */ + public boolean setValue(String value) { + this.value = new int[value.getBytes().length]; + int cnt = 0; + for (byte val : value.getBytes()) { + this.value[cnt++] = val; + } + return true; + } + + /** + * Returns the size of the requested value type. + */ + private int getTypeLen(int formatType) { + return formatType & 0xF; + } + + /** + * Convert a signed byte to an unsigned int. + */ + private int unsignedByteToInt(int value) { + return value & 0xFF; + } + + /** + * Convert signed bytes to a 16-bit unsigned int. + */ + private int unsignedBytesToInt(int value1, int value2) { + return value1 + (value2 << 8); + } + + /** + * Convert signed bytes to a 32-bit unsigned int. + */ + private int unsignedBytesToInt(int value1, int value2, int value3, int value4) { + return value1 + (value2 << 8) + (value3 << 16) + (value4 << 24); + } + + /** + * Convert signed bytes to a 16-bit short float value. + */ + private float bytesToFloat(int value1, int value2) { + int mantissa = unsignedToSigned(unsignedByteToInt(value1) + ((unsignedByteToInt(value2) & 0x0F) << 8), 12); + int exponent = unsignedToSigned(unsignedByteToInt(value2) >> 4, 4); + return (float) (mantissa * Math.pow(10, exponent)); + } + + /** + * Convert signed bytes to a 32-bit short float value. + */ + private float bytesToFloat(int value1, int value2, int value3, int value4) { + int mantissa = unsignedToSigned( + unsignedByteToInt(value1) + (unsignedByteToInt(value2) << 8) + (unsignedByteToInt(value3) << 16), 24); + return (float) (mantissa * Math.pow(10, value4)); + } + + /** + * Convert an unsigned integer to a two's-complement signed value. + */ + private int unsignedToSigned(int unsigned, int size) { + if ((unsigned & (1 << size - 1)) != 0) { + return -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1))); + } else { + return unsigned; + } + } + + /** + * Convert an integer into the signed bits of the specified length. + */ + private int intToSignedBits(int i, int size) { + if (i < 0) { + return (1 << size - 1) + (i & ((1 << size - 1) - 1)); + } else { + return i; + } + } + + public GattCharacteristic getGattCharacteristic() { + return GattCharacteristic.getCharacteristic(uuid); + } + + public enum GattCharacteristic { + // Characteristic + ALERT_CATEGORY_ID(0x2A43), + ALERT_CATEGORY_ID_BIT_MASK(0x2A42), + ALERT_LEVEL(0x2A06), + ALERT_NOTIFICATION_CONTROL_POINT(0x2A44), + ALERT_STATUS(0x2A3F), + APPEARANCE(0x2A01), + BATTERY_LEVEL(0x2A19), + BLOOD_PRESSURE_FEATURE(0x2A49), + BLOOD_PRESSURE_MEASUREMENT(0x2A35), + BODY_SENSOR_LOCATION(0x2A38), + BOOT_KEYOBARD_INPUT_REPORT(0x2A22), + BOOT_KEYOBARD_OUTPUT_REPORT(0x2A32), + BOOT_MOUSE_INPUT_REPORT(0x2A33), + CSC_FEATURE(0x2A5C), + CSC_MEASUREMENT(0x2A5B), + CURRENT_TIME(0x2A2B), + CYCLING_POWER_CONTROL_POINT(0x2A66), + CYCLING_POWER_FEATURE(0x2A65), + CYCLING_POWER_MEASUREMENT(0x2A63), + CYCLING_POWER_VECTOR(0x2A64), + DATE_TIME(0x2A08), + DAY_DATE_TIME(0x2A0A), + DAY_OF_WEEK(0x2A09), + DEVICE_NAME(0x2A00), + DST_OFFSET(0x2A0D), + EXACT_TIME_256(0x2A0C), + FIRMWARE_REVISION_STRING(0x2A26), + GLUCOSE_FEATURE(0x2A51), + GLUCOSE_MEASUREMENT(0x2A18), + GLUCOSE_MEASUREMENT_CONTROL(0x2A34), + HARDWARE_REVISION_STRING(0x2A27), + HEART_RATE_CONTROL_POINT(0x2A39), + HEART_RATE_MEASUREMENT(0x2A37), + HID_CONTROL_POINT(0x2A4C), + HID_INFORMATION(0x2A4A), + IEEE11073_20601_REGULATORY_CERTIFICATION_DATA_LIST(0x2A2A), + INTERMEDIATE_CUFF_PRESSURE(0x2A36), + INTERMEDIATE_TEMPERATURE(0x2A1E), + LN_CONTROL_POINT(0x2A6B), + LN_FEATURE(0x2A6A), + LOCAL_TIME_INFORMATION(0x2A0F), + LOCATION_AND_SPEED(0x2A67), + MANUFACTURER_NAME_STRING(0x2A29), + MEASUREMENT_INTERVAL(0x2A21), + MODEL_NUMBER_STRING(0x2A24), + NAVIGATION(0x2A68), + NEW_ALERT(0x2A46), + PERIPERAL_PREFFERED_CONNECTION_PARAMETERS(0x2A04), + PERIPHERAL_PRIVACY_FLAG(0x2A02), + PN_PID(0x2A50), + POSITION_QUALITY(0x2A69), + PROTOCOL_MODE(0x2A4E), + RECONNECTION_ADDRESS(0x2A03), + RECORD_ACCESS_CONTROL_POINT(0x2A52), + REFERENCE_TIME_INFORMATION(0x2A14), + REPORT(0x2A4D), + REPORT_MAP(0x2A4B), + RINGER_CONTROL_POINT(0x2A40), + RINGER_SETTING(0x2A41), + RSC_FEATURE(0x2A54), + RSC_MEASUREMENT(0x2A53), + SC_CONTROL_POINT(0x2A55), + SCAN_INTERVAL_WINDOW(0x2A4F), + SCAN_REFRESH(0x2A31), + SENSOR_LOCATION(0x2A5D), + SERIAL_NUMBER_STRING(0x2A25), + SERVICE_CHANGED(0x2A05), + SOFTWARE_REVISION_STRING(0x2A28), + SUPPORTED_NEW_ALERT_CATEGORY(0x2A47), + SUPPORTED_UNREAD_ALERT_CATEGORY(0x2A48), + SYSTEM_ID(0x2A23), + TEMPERATURE_MEASUREMENT(0x2A1C), + TEMPERATURE_TYPE(0x2A1D), + TIME_ACCURACY(0x2A12), + TIME_SOURCE(0x2A13), + TIME_UPDATE_CONTROL_POINT(0x2A16), + TIME_UPDATE_STATE(0x2A17), + TIME_WITH_DST(0x2A11), + TIME_ZONE(0x2A0E), + TX_POWER_LEVEL(0x2A07), + UNREAD_ALERT_STATUS(0x2A45), + AGGREGATE_INPUT(0x2A5A), + ANALOG_INPUT(0x2A58), + ANALOG_OUTPUT(0x2A59), + DIGITAL_INPUT(0x2A56), + DIGITAL_OUTPUT(0x2A57), + EXACT_TIME_100(0x2A0B), + NETWORK_AVAILABILITY(0x2A3E), + SCIENTIFIC_TEMPERATURE_IN_CELSIUS(0x2A3C), + SECONDARY_TIME_ZONE(0x2A10), + STRING(0x2A3D), + TEMPERATURE_IN_CELSIUS(0x2A1F), + TEMPERATURE_IN_FAHRENHEIT(0x2A20), + TIME_BROADCAST(0x2A15), + BATTERY_LEVEL_STATE(0x2A1B), + BATTERY_POWER_STATE(0x2A1A), + PULSE_OXIMETRY_CONTINUOUS_MEASUREMENT(0x2A5F), + PULSE_OXIMETRY_CONTROL_POINT(0x2A62), + PULSE_OXIMETRY_FEATURES(0x2A61), + PULSE_OXIMETRY_PULSATILE_EVENT(0x2A60), + PULSE_OXIMETRY_SPOT_CHECK_MEASUREMENT(0x2A5E), + RECORD_ACCESS_CONTROL_POINT_TESTVERSION(0x2A52), + REMOVABLE(0x2A3A), + SERVICE_REQUIRED(0x2A3B); + + private static Map uuidToServiceMapping; + + private UUID uuid; + + private GattCharacteristic(long key) { + this.uuid = new UUID((key << 32) | 0x1000, BluetoothBindingConstants.BLUETOOTH_BASE_UUID); + } + + private static void initMapping() { + uuidToServiceMapping = new HashMap(); + for (GattCharacteristic s : values()) { + uuidToServiceMapping.put(s.uuid, s); + } + } + + public static GattCharacteristic getCharacteristic(UUID uuid) { + if (uuidToServiceMapping == null) { + initMapping(); + } + return uuidToServiceMapping.get(uuid); + } + + /** + * @return the key + */ + public UUID getUUID() { + return uuid; + } + } + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothClass.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothClass.java new file mode 100644 index 00000000000..8b0c1bbe594 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothClass.java @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +/** + * Represents a Bluetooth class, which describes the general characteristics and capabilities of a device. + * + * @author Chris Jackson - Initial Contribution + * + */ +public class BluetoothClass { + private final int clazz; + + public static final class Service { + private static final int BITMASK = 0xFFE000; + + public static final int LIMITED_DISCOVERABILITY = 0x002000; + public static final int POSITIONING = 0x010000; + public static final int NETWORKING = 0x020000; + public static final int RENDER = 0x040000; + public static final int CAPTURE = 0x080000; + public static final int OBJECT_TRANSFER = 0x100000; + public static final int AUDIO = 0x200000; + public static final int TELEPHONY = 0x400000; + public static final int INFORMATION = 0x800000; + } + + public static class Device { + private static final int BITMASK = 0x1FFC; + + /** + * Defines the major device class constants. + * + */ + public static class Major { + private static final int BITMASK = 0x1F00; + + public static final int MISC = 0x0000; + public static final int COMPUTER = 0x0100; + public static final int PHONE = 0x0200; + public static final int NETWORKING = 0x0300; + public static final int AUDIO_VIDEO = 0x0400; + public static final int PERIPHERAL = 0x0500; + public static final int IMAGING = 0x0600; + public static final int WEARABLE = 0x0700; + public static final int TOY = 0x0800; + public static final int HEALTH = 0x0900; + public static final int UNCATEGORIZED = 0x1F00; + } + + // Devices in the COMPUTER major class + public static final int COMPUTER_UNCATEGORIZED = 0x0100; + public static final int COMPUTER_DESKTOP = 0x0104; + public static final int COMPUTER_SERVER = 0x0108; + public static final int COMPUTER_LAPTOP = 0x010C; + public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110; + public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114; + public static final int COMPUTER_WEARABLE = 0x0118; + + // Devices in the PHONE major class + public static final int PHONE_UNCATEGORIZED = 0x0200; + public static final int PHONE_CELLULAR = 0x0204; + public static final int PHONE_CORDLESS = 0x0208; + public static final int PHONE_SMART = 0x020C; + public static final int PHONE_MODEM_OR_GATEWAY = 0x0210; + public static final int PHONE_ISDN = 0x0214; + + // Minor classes for the AUDIO_VIDEO major class + public static final int AUDIO_VIDEO_UNCATEGORIZED = 0x0400; + public static final int AUDIO_VIDEO_WEARABLE_HEADSET = 0x0404; + public static final int AUDIO_VIDEO_HANDSFREE = 0x0408; + public static final int AUDIO_VIDEO_MICROPHONE = 0x0410; + public static final int AUDIO_VIDEO_LOUDSPEAKER = 0x0414; + public static final int AUDIO_VIDEO_HEADPHONES = 0x0418; + public static final int AUDIO_VIDEO_PORTABLE_AUDIO = 0x041C; + public static final int AUDIO_VIDEO_CAR_AUDIO = 0x0420; + public static final int AUDIO_VIDEO_SET_TOP_BOX = 0x0424; + public static final int AUDIO_VIDEO_HIFI_AUDIO = 0x0428; + public static final int AUDIO_VIDEO_VCR = 0x042C; + public static final int AUDIO_VIDEO_VIDEO_CAMERA = 0x0430; + public static final int AUDIO_VIDEO_CAMCORDER = 0x0434; + public static final int AUDIO_VIDEO_VIDEO_MONITOR = 0x0438; + public static final int AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x043C; + public static final int AUDIO_VIDEO_VIDEO_CONFERENCING = 0x0440; + public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448; + + // Devices in the WEARABLE major class + public static final int WEARABLE_UNCATEGORIZED = 0x0700; + public static final int WEARABLE_WRIST_WATCH = 0x0704; + public static final int WEARABLE_PAGER = 0x0708; + public static final int WEARABLE_JACKET = 0x070C; + public static final int WEARABLE_HELMET = 0x0710; + public static final int WEARABLE_GLASSES = 0x0714; + + // Devices in the TOY major class + public static final int TOY_UNCATEGORIZED = 0x0800; + public static final int TOY_ROBOT = 0x0804; + public static final int TOY_VEHICLE = 0x0808; + public static final int TOY_DOLL_ACTION_FIGURE = 0x080C; + public static final int TOY_CONTROLLER = 0x0810; + public static final int TOY_GAME = 0x0814; + + // Devices in the HEALTH major class + public static final int HEALTH_UNCATEGORIZED = 0x0900; + public static final int HEALTH_BLOOD_PRESSURE = 0x0904; + public static final int HEALTH_THERMOMETER = 0x0908; + public static final int HEALTH_WEIGHING = 0x090C; + public static final int HEALTH_GLUCOSE = 0x0910; + public static final int HEALTH_PULSE_OXIMETER = 0x0914; + public static final int HEALTH_PULSE_RATE = 0x0918; + public static final int HEALTH_DATA_DISPLAY = 0x091C; + + // Devices in PERIPHERAL major class + public static final int PERIPHERAL_NON_KEYBOARD_NON_POINTING = 0x0500; + public static final int PERIPHERAL_KEYBOARD = 0x0540; + public static final int PERIPHERAL_POINTING = 0x0580; + public static final int PERIPHERAL_KEYBOARD_POINTING = 0x05C0; + } + + /** + * Public constructor + * + * @param clazz the device class provided in the bluetooth descriptor + */ + public BluetoothClass(int clazz) { + this.clazz = clazz; + } + + /** + * Return the major and minor device class + * + * @return major and minor device class + */ + public int getDeviceClass() { + return (clazz & Device.BITMASK); + } + + /** + * Return the major device class + * + * @return the major device class + */ + public int getMajorDeviceClass() { + return (clazz & Device.Major.BITMASK); + } + + /** + * Return true if the specified service class is supported + * + * @param service the service id + * @return true, if the class supports the service + */ + public boolean hasService(int service) { + return ((clazz & Service.BITMASK & service) != 0); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCompanyIdentifiers.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCompanyIdentifiers.java new file mode 100644 index 00000000000..f4bb828a66c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCompanyIdentifiers.java @@ -0,0 +1,1466 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * See https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +@NonNullByDefault +public class BluetoothCompanyIdentifiers { + + private static final Map cic = new HashMap<>(); + + static { + cic.put(0, "Ericsson Technology Licensing"); + cic.put(1, "Nokia Mobile Phones"); + cic.put(2, "Intel Corp."); + cic.put(3, "IBM Corp."); + cic.put(4, "Toshiba Corp."); + cic.put(5, "3Com"); + cic.put(6, "Microsoft"); + cic.put(7, "Lucent"); + cic.put(8, "Motorola"); + cic.put(9, "Infineon Technologies AG"); + cic.put(10, "Cambridge Silicon Radio"); + cic.put(11, "Silicon Wave"); + cic.put(12, "Digianswer A/S"); + cic.put(13, "Texas Instruments Inc."); + cic.put(14, "Parthus Technologies Inc."); + cic.put(15, "Broadcom Corporation"); + cic.put(16, "Mitel Semiconductor"); + cic.put(17, "Widcomm, Inc."); + cic.put(18, "Zeevo, Inc."); + cic.put(19, "Atmel Corporation"); + cic.put(20, "Mitsubishi Electric Corporation"); + cic.put(21, "RTX Telecom A/S"); + cic.put(22, "KC Technology Inc."); + cic.put(23, "Newlogic"); + cic.put(24, "Transilica, Inc."); + cic.put(25, "Rohde & Schwarz GmbH & Co. KG"); + cic.put(26, "TTPCom Limited"); + cic.put(27, "Signia Technologies, Inc."); + cic.put(28, "Conexant Systems Inc."); + cic.put(29, "Qualcomm"); + cic.put(30, "Inventel"); + cic.put(31, "AVM Berlin"); + cic.put(32, "BandSpeed, Inc."); + cic.put(33, "Mansella Ltd"); + cic.put(34, "NEC Corporation"); + cic.put(35, "WavePlus Technology Co., Ltd."); + cic.put(36, "Alcatel"); + cic.put(37, "NXP Semiconductors (formerly Philips Semiconductors)"); + cic.put(38, "C Technologies"); + cic.put(39, "Open Interface"); + cic.put(40, "R F Micro Devices"); + cic.put(41, "Hitachi Ltd"); + cic.put(42, "Symbol Technologies, Inc."); + cic.put(43, "Tenovis"); + cic.put(44, "Macronix International Co. Ltd."); + cic.put(45, "GCT Semiconductor"); + cic.put(46, "Norwood Systems"); + cic.put(47, "MewTel Technology Inc."); + cic.put(48, "ST Microelectronics"); + cic.put(49, "Synopsys, Inc."); + cic.put(50, "Red-M (Communications) Ltd"); + cic.put(51, "Commil Ltd"); + cic.put(52, "Computer Access Technology Corporation (CATC)"); + cic.put(53, "Eclipse (HQ Espana) S.L."); + cic.put(54, "Renesas Electronics Corporation"); + cic.put(55, "Mobilian Corporation"); + cic.put(56, "Terax"); + cic.put(57, "Integrated System Solution Corp."); + cic.put(58, "Matsushita Electric Industrial Co., Ltd."); + cic.put(59, "Gennum Corporation"); + cic.put(60, "BlackBerry Limited (formerly Research In Motion)"); + cic.put(61, "IPextreme, Inc."); + cic.put(62, "Systems and Chips, Inc"); + cic.put(63, "Bluetooth SIG, Inc"); + cic.put(64, "Seiko Epson Corporation"); + cic.put(65, "Integrated Silicon Solution Taiwan, Inc."); + cic.put(66, "CONWISE Technology Corporation Ltd"); + cic.put(67, "PARROT AUTOMOTIVE SAS"); + cic.put(68, "Socket Mobile"); + cic.put(69, "Atheros Communications, Inc."); + cic.put(70, "MediaTek, Inc."); + cic.put(71, "Bluegiga"); + cic.put(72, "Marvell Technology Group Ltd."); + cic.put(73, "3DSP Corporation"); + cic.put(74, "Accel Semiconductor Ltd."); + cic.put(75, "Continental Automotive Systems"); + cic.put(76, "Apple, Inc."); + cic.put(77, "Staccato Communications, Inc."); + cic.put(78, "Avago Technologies"); + cic.put(79, "APT Ltd."); + cic.put(80, "SiRF Technology, Inc."); + cic.put(81, "Tzero Technologies, Inc."); + cic.put(82, "J&M Corporation"); + cic.put(83, "Free2move AB"); + cic.put(84, "3DiJoy Corporation"); + cic.put(85, "Plantronics, Inc."); + cic.put(86, "Sony Ericsson Mobile Communications"); + cic.put(87, "Harman International Industries, Inc."); + cic.put(88, "Vizio, Inc."); + cic.put(89, "Nordic Semiconductor ASA"); + cic.put(90, "EM Microelectronic-Marin SA"); + cic.put(91, "Ralink Technology Corporation"); + cic.put(92, "Belkin International, Inc."); + cic.put(93, "Realtek Semiconductor Corporation"); + cic.put(94, "Stonestreet One, LLC"); + cic.put(95, "Wicentric, Inc."); + cic.put(96, "RivieraWaves S.A.S"); + cic.put(97, "RDA Microelectronics"); + cic.put(98, "Gibson Guitars"); + cic.put(99, "MiCommand Inc."); + cic.put(100, "Band XI International, LLC"); + cic.put(101, "Hewlett-Packard Company"); + cic.put(102, "9Solutions Oy"); + cic.put(103, "GN Netcom A/S"); + cic.put(104, "General Motors"); + cic.put(105, "A&D Engineering, Inc."); + cic.put(106, "MindTree Ltd."); + cic.put(107, "Polar Electro OY"); + cic.put(108, "Beautiful Enterprise Co., Ltd."); + cic.put(109, "BriarTek, Inc"); + cic.put(110, "Summit Data Communications, Inc."); + cic.put(111, "Sound ID"); + cic.put(112, "Monster, LLC"); + cic.put(113, "connectBlue AB"); + cic.put(114, "ShangHai Super Smart Electronics Co. Ltd."); + cic.put(115, "Group Sense Ltd."); + cic.put(116, "Zomm, LLC"); + cic.put(117, "Samsung Electronics Co. Ltd."); + cic.put(118, "Creative Technology Ltd."); + cic.put(119, "Laird Technologies"); + cic.put(120, "Nike, Inc."); + cic.put(121, "lesswire AG"); + cic.put(122, "MStar Semiconductor, Inc."); + cic.put(123, "Hanlynn Technologies"); + cic.put(124, "A & R Cambridge"); + cic.put(125, "Seers Technology Co., Ltd."); + cic.put(126, "Sports Tracking Technologies Ltd."); + cic.put(127, "Autonet Mobile"); + cic.put(128, "DeLorme Publishing Company, Inc."); + cic.put(129, "WuXi Vimicro"); + cic.put(130, "Sennheiser Communications A/S"); + cic.put(131, "TimeKeeping Systems, Inc."); + cic.put(132, "Ludus Helsinki Ltd."); + cic.put(133, "BlueRadios, Inc."); + cic.put(134, "Equinux AG"); + cic.put(135, "Garmin International, Inc."); + cic.put(136, "Ecotest"); + cic.put(137, "GN ReSound A/S"); + cic.put(138, "Jawbone"); + cic.put(139, "Topcon Positioning Systems, LLC"); + cic.put(140, "Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.)"); + cic.put(141, "Zscan Software"); + cic.put(142, "Quintic Corp"); + cic.put(143, "Telit Wireless Solutions GmbH (formerly Stollmann E+V GmbH)"); + cic.put(144, "Funai Electric Co., Ltd."); + cic.put(145, "Advanced PANMOBIL systems GmbH & Co. KG"); + cic.put(146, "ThinkOptics, Inc."); + cic.put(147, "Universal Electronics, Inc."); + cic.put(148, "Airoha Technology Corp."); + cic.put(149, "NEC Lighting, Ltd."); + cic.put(150, "ODM Technology, Inc."); + cic.put(151, "ConnecteDevice Ltd."); + cic.put(152, "zero1.tv GmbH"); + cic.put(153, "i.Tech Dynamic Global Distribution Ltd."); + cic.put(154, "Alpwise"); + cic.put(155, "Jiangsu Toppower Automotive Electronics Co., Ltd."); + cic.put(156, "Colorfy, Inc."); + cic.put(157, "Geoforce Inc."); + cic.put(158, "Bose Corporation"); + cic.put(159, "Suunto Oy"); + cic.put(160, "Kensington Computer Products Group"); + cic.put(161, "SR-Medizinelektronik"); + cic.put(162, "Vertu Corporation Limited"); + cic.put(163, "Meta Watch Ltd."); + cic.put(164, "LINAK A/S"); + cic.put(165, "OTL Dynamics LLC"); + cic.put(166, "Panda Ocean Inc."); + cic.put(167, "Visteon Corporation"); + cic.put(168, "ARP Devices Limited"); + cic.put(169, "Magneti Marelli S.p.A"); + cic.put(170, "CAEN RFID srl"); + cic.put(171, "Ingenieur-Systemgruppe Zahn GmbH"); + cic.put(172, "Green Throttle Games"); + cic.put(173, "Peter Systemtechnik GmbH"); + cic.put(174, "Omegawave Oy"); + cic.put(175, "Cinetix"); + cic.put(176, "Passif Semiconductor Corp"); + cic.put(177, "Saris Cycling Group, Inc"); + cic.put(178, "Bekey A/S"); + cic.put(179, "Clarinox Technologies Pty. Ltd."); + cic.put(180, "BDE Technology Co., Ltd."); + cic.put(181, "Swirl Networks"); + cic.put(182, "Meso international"); + cic.put(183, "TreLab Ltd"); + cic.put(184, "Qualcomm Innovation Center, Inc. (QuIC)"); + cic.put(185, "Johnson Controls, Inc."); + cic.put(186, "Starkey Laboratories Inc."); + cic.put(187, "S-Power Electronics Limited"); + cic.put(188, "Ace Sensor Inc"); + cic.put(189, "Aplix Corporation"); + cic.put(190, "AAMP of America"); + cic.put(191, "Stalmart Technology Limited"); + cic.put(192, "AMICCOM Electronics Corporation"); + cic.put(193, "Shenzhen Excelsecu Data Technology Co.,Ltd"); + cic.put(194, "Geneq Inc."); + cic.put(195, "adidas AG"); + cic.put(196, "LG Electronics"); + cic.put(197, "Onset Computer Corporation"); + cic.put(198, "Selfly BV"); + cic.put(199, "Quuppa Oy."); + cic.put(200, "GeLo Inc"); + cic.put(201, "Evluma"); + cic.put(202, "MC10"); + cic.put(203, "Binauric SE"); + cic.put(204, "Beats Electronics"); + cic.put(205, "Microchip Technology Inc."); + cic.put(206, "Elgato Systems GmbH"); + cic.put(207, "ARCHOS SA"); + cic.put(208, "Dexcom, Inc."); + cic.put(209, "Polar Electro Europe B.V."); + cic.put(210, "Dialog Semiconductor B.V."); + cic.put(211, "Taixingbang Technology (HK) Co,. LTD."); + cic.put(212, "Kawantech"); + cic.put(213, "Austco Communication Systems"); + cic.put(214, "Timex Group USA, Inc."); + cic.put(215, "Qualcomm Technologies, Inc."); + cic.put(216, "Qualcomm Connected Experiences, Inc."); + cic.put(217, "Voyetra Turtle Beach"); + cic.put(218, "txtr GmbH"); + cic.put(219, "Biosentronics"); + cic.put(220, "Procter & Gamble"); + cic.put(221, "Hosiden Corporation"); + cic.put(222, "Muzik LLC"); + cic.put(223, "Misfit Wearables Corp"); + cic.put(224, "Google"); + cic.put(225, "Danlers Ltd"); + cic.put(226, "Semilink Inc"); + cic.put(227, "inMusic Brands, Inc"); + cic.put(228, "L.S. Research Inc."); + cic.put(229, "Eden Software Consultants Ltd."); + cic.put(230, "Freshtemp"); + cic.put(231, "KS Technologies"); + cic.put(232, "ACTS Technologies"); + cic.put(233, "Vtrack Systems"); + cic.put(234, "Nielsen-Kellerman Company"); + cic.put(235, "Server Technology Inc."); + cic.put(236, "BioResearch Associates"); + cic.put(237, "Jolly Logic, LLC"); + cic.put(238, "Above Average Outcomes, Inc."); + cic.put(239, "Bitsplitters GmbH"); + cic.put(240, "PayPal, Inc."); + cic.put(241, "Witron Technology Limited"); + cic.put(242, "Morse Project Inc."); + cic.put(243, "Kent Displays Inc."); + cic.put(244, "Nautilus Inc."); + cic.put(245, "Smartifier Oy"); + cic.put(246, "Elcometer Limited"); + cic.put(247, "VSN Technologies, Inc."); + cic.put(248, "AceUni Corp., Ltd."); + cic.put(249, "StickNFind"); + cic.put(250, "Crystal Code AB"); + cic.put(251, "KOUKAAM a.s."); + cic.put(252, "Delphi Corporation"); + cic.put(253, "ValenceTech Limited"); + cic.put(254, "Stanley Black and Decker"); + cic.put(255, "Typo Products, LLC"); + cic.put(256, "TomTom International BV"); + cic.put(257, "Fugoo, Inc."); + cic.put(258, "Keiser Corporation"); + cic.put(259, "Bang & Olufsen A/S"); + cic.put(260, "PLUS Location Systems Pty Ltd"); + cic.put(261, "Ubiquitous Computing Technology Corporation"); + cic.put(262, "Innovative Yachtter Solutions"); + cic.put(263, "William Demant Holding A/S"); + cic.put(264, "Chicony Electronics Co., Ltd."); + cic.put(265, "Atus BV"); + cic.put(266, "Codegate Ltd"); + cic.put(267, "ERi, Inc"); + cic.put(268, "Transducers Direct, LLC"); + cic.put(269, "Fujitsu Ten LImited"); + cic.put(270, "Audi AG"); + cic.put(271, "HiSilicon Technologies Col, Ltd."); + cic.put(272, "Nippon Seiki Co., Ltd."); + cic.put(273, "Steelseries ApS"); + cic.put(274, "Visybl Inc."); + cic.put(275, "Openbrain Technologies, Co., Ltd."); + cic.put(276, "Xensr"); + cic.put(277, "e.solutions"); + cic.put(278, "10AK Technologies"); + cic.put(279, "Wimoto Technologies Inc"); + cic.put(280, "Radius Networks, Inc."); + cic.put(281, "Wize Technology Co., Ltd."); + cic.put(282, "Qualcomm Labs, Inc."); + cic.put(283, "Aruba Networks"); + cic.put(284, "Baidu"); + cic.put(285, "Arendi AG"); + cic.put(286, "Skoda Auto a.s."); + cic.put(287, "Volkswagen AG"); + cic.put(288, "Porsche AG"); + cic.put(289, "Sino Wealth Electronic Ltd."); + cic.put(290, "AirTurn, Inc."); + cic.put(291, "Kinsa, Inc"); + cic.put(292, "HID Global"); + cic.put(293, "SEAT es"); + cic.put(294, "Promethean Ltd."); + cic.put(295, "Salutica Allied Solutions"); + cic.put(296, "GPSI Group Pty Ltd"); + cic.put(297, "Nimble Devices Oy"); + cic.put(298, "Changzhou Yongse Infotech Co., Ltd."); + cic.put(299, "SportIQ"); + cic.put(300, "TEMEC Instruments B.V."); + cic.put(301, "Sony Corporation"); + cic.put(302, "ASSA ABLOY"); + cic.put(303, "Clarion Co. Inc."); + cic.put(304, "Warehouse Innovations"); + cic.put(305, "Cypress Semiconductor"); + cic.put(306, "MADS Inc"); + cic.put(307, "Blue Maestro Limited"); + cic.put(308, "Resolution Products, Ltd."); + cic.put(309, "Aireware LLC"); + cic.put(310, "Silvair, Inc."); + cic.put(311, "Prestigio Plaza Ltd."); + cic.put(312, "NTEO Inc."); + cic.put(313, "Focus Systems Corporation"); + cic.put(314, "Tencent Holdings Ltd."); + cic.put(315, "Allegion"); + cic.put(316, "Murata Manufacturing Co., Ltd."); + cic.put(317, "WirelessWERX"); + cic.put(318, "Nod, Inc."); + cic.put(319, "B&B Manufacturing Company"); + cic.put(320, "Alpine Electronics (China) Co., Ltd"); + cic.put(321, "FedEx Services"); + cic.put(322, "Grape Systems Inc."); + cic.put(323, "Bkon Connect"); + cic.put(324, "Lintech GmbH"); + cic.put(325, "Novatel Wireless"); + cic.put(326, "Ciright"); + cic.put(327, "Mighty Cast, Inc."); + cic.put(328, "Ambimat Electronics"); + cic.put(329, "Perytons Ltd."); + cic.put(330, "Tivoli Audio, LLC"); + cic.put(331, "Master Lock"); + cic.put(332, "Mesh-Net Ltd"); + cic.put(333, "HUIZHOU DESAY SV AUTOMOTIVE CO., LTD."); + cic.put(334, "Tangerine, Inc."); + cic.put(335, "B&W Group Ltd."); + cic.put(336, "Pioneer Corporation"); + cic.put(337, "OnBeep"); + cic.put(338, "Vernier Software & Technology"); + cic.put(339, "ROL Ergo"); + cic.put(340, "Pebble Technology"); + cic.put(341, "NETATMO"); + cic.put(342, "Accumulate AB"); + cic.put(343, "Anhui Huami Information Technology Co., Ltd."); + cic.put(344, "Inmite s.r.o."); + cic.put(345, "ChefSteps, Inc."); + cic.put(346, "micas AG"); + cic.put(347, "Biomedical Research Ltd."); + cic.put(348, "Pitius Tec S.L."); + cic.put(349, "Estimote, Inc."); + cic.put(350, "Unikey Technologies, Inc."); + cic.put(351, "Timer Cap Co."); + cic.put(352, "AwoX"); + cic.put(353, "yikes"); + cic.put(354, "MADSGlobalNZ Ltd."); + cic.put(355, "PCH International"); + cic.put(356, "Qingdao Yeelink Information Technology Co., Ltd."); + cic.put(357, "Milwaukee Tool (Formally Milwaukee Electric Tools)"); + cic.put(358, "MISHIK Pte Ltd"); + cic.put(359, "Ascensia Diabetes Care US Inc."); + cic.put(360, "Spicebox LLC"); + cic.put(361, "emberlight"); + cic.put(362, "Cooper-Atkins Corporation"); + cic.put(363, "Qblinks"); + cic.put(364, "MYSPHERA"); + cic.put(365, "LifeScan Inc"); + cic.put(366, "Volantic AB"); + cic.put(367, "Podo Labs, Inc"); + cic.put(368, "Roche Diabetes Care AG"); + cic.put(369, "Amazon Fulfillment Service"); + cic.put(370, "Connovate Technology Private Limited"); + cic.put(371, "Kocomojo, LLC"); + cic.put(372, "Everykey Inc."); + cic.put(373, "Dynamic Controls"); + cic.put(374, "SentriLock"); + cic.put(375, "I-SYST inc."); + cic.put(376, "CASIO COMPUTER CO., LTD."); + cic.put(377, "LAPIS Semiconductor Co., Ltd."); + cic.put(378, "Telemonitor, Inc."); + cic.put(379, "taskit GmbH"); + cic.put(380, "Daimler AG"); + cic.put(381, "BatAndCat"); + cic.put(382, "BluDotz Ltd"); + cic.put(383, "XTel Wireless ApS"); + cic.put(384, "Gigaset Communications GmbH"); + cic.put(385, "Gecko Health Innovations, Inc."); + cic.put(386, "HOP Ubiquitous"); + cic.put(387, "Walt Disney"); + cic.put(388, "Nectar"); + cic.put(389, "bel'apps LLC"); + cic.put(390, "CORE Lighting Ltd"); + cic.put(391, "Seraphim Sense Ltd"); + cic.put(392, "Unico RBC"); + cic.put(393, "Physical Enterprises Inc."); + cic.put(394, "Able Trend Technology Limited"); + cic.put(395, "Konica Minolta, Inc."); + cic.put(396, "Wilo SE"); + cic.put(397, "Extron Design Services"); + cic.put(398, "Fitbit, Inc."); + cic.put(399, "Fireflies Systems"); + cic.put(400, "Intelletto Technologies Inc."); + cic.put(401, "FDK CORPORATION"); + cic.put(402, "Cloudleaf, Inc"); + cic.put(403, "Maveric Automation LLC"); + cic.put(404, "Acoustic Stream Corporation"); + cic.put(405, "Zuli"); + cic.put(406, "Paxton Access Ltd"); + cic.put(407, "WiSilica Inc."); + cic.put(408, "VENGIT Korlatolt Felelossegu Tarsasag"); + cic.put(409, "SALTO SYSTEMS S.L."); + cic.put(410, "TRON Forum (formerly T-Engine Forum)"); + cic.put(411, "CUBETECH s.r.o."); + cic.put(412, "Cokiya Incorporated"); + cic.put(413, "CVS Health"); + cic.put(414, "Ceruus"); + cic.put(415, "Strainstall Ltd"); + cic.put(416, "Channel Enterprises (HK) Ltd."); + cic.put(417, "FIAMM"); + cic.put(418, "GIGALANE.CO.,LTD"); + cic.put(419, "EROAD"); + cic.put(420, "Mine Safety Appliances"); + cic.put(421, "Icon Health and Fitness"); + cic.put(422, "Asandoo GmbH"); + cic.put(423, "ENERGOUS CORPORATION"); + cic.put(424, "Taobao"); + cic.put(425, "Canon Inc."); + cic.put(426, "Geophysical Technology Inc."); + cic.put(427, "Facebook, Inc."); + cic.put(428, "Nipro Diagnostics, Inc."); + cic.put(429, "FlightSafety International"); + cic.put(430, "Earlens Corporation"); + cic.put(431, "Sunrise Micro Devices, Inc."); + cic.put(432, "Star Micronics Co., Ltd."); + cic.put(433, "Netizens Sp. z o.o."); + cic.put(434, "Nymi Inc."); + cic.put(435, "Nytec, Inc."); + cic.put(436, "Trineo Sp. z o.o."); + cic.put(437, "Nest Labs Inc."); + cic.put(438, "LM Technologies Ltd"); + cic.put(439, "General Electric Company"); + cic.put(440, "i+D3 S.L."); + cic.put(441, "HANA Micron"); + cic.put(442, "Stages Cycling LLC"); + cic.put(443, "Cochlear Bone Anchored Solutions AB"); + cic.put(444, "SenionLab AB"); + cic.put(445, "Syszone Co., Ltd"); + cic.put(446, "Pulsate Mobile Ltd."); + cic.put(447, "Hong Kong HunterSun Electronic Limited"); + cic.put(448, "pironex GmbH"); + cic.put(449, "BRADATECH Corp."); + cic.put(450, "Transenergooil AG"); + cic.put(451, "Bunch"); + cic.put(452, "DME Microelectronics"); + cic.put(453, "Bitcraze AB"); + cic.put(454, "HASWARE Inc."); + cic.put(455, "Abiogenix Inc."); + cic.put(456, "Poly-Control ApS"); + cic.put(457, "Avi-on"); + cic.put(458, "Laerdal Medical AS"); + cic.put(459, "Fetch My Pet"); + cic.put(460, "Sam Labs Ltd."); + cic.put(461, "Chengdu Synwing Technology Ltd"); + cic.put(462, "HOUWA SYSTEM DESIGN, k.k."); + cic.put(463, "BSH"); + cic.put(464, "Primus Inter Pares Ltd"); + cic.put(465, "August Home, Inc"); + cic.put(466, "Gill Electronics"); + cic.put(467, "Sky Wave Design"); + cic.put(468, "Newlab S.r.l."); + cic.put(469, "ELAD srl"); + cic.put(470, "G-wearables inc."); + cic.put(471, "Squadrone Systems Inc."); + cic.put(472, "Code Corporation"); + cic.put(473, "Savant Systems LLC"); + cic.put(474, "Logitech International SA"); + cic.put(475, "Innblue Consulting"); + cic.put(476, "iParking Ltd."); + cic.put(477, "Koninklijke Philips Electronics N.V."); + cic.put(478, "Minelab Electronics Pty Limited"); + cic.put(479, "Bison Group Ltd."); + cic.put(480, "Widex A/S"); + cic.put(481, "Jolla Ltd"); + cic.put(482, "Lectronix, Inc."); + cic.put(483, "Caterpillar Inc"); + cic.put(484, "Freedom Innovations"); + cic.put(485, "Dynamic Devices Ltd"); + cic.put(486, "Technology Solutions (UK) Ltd"); + cic.put(487, "IPS Group Inc."); + cic.put(488, "STIR"); + cic.put(489, "Sano, Inc."); + cic.put(490, "Advanced Application Design, Inc."); + cic.put(491, "AutoMap LLC"); + cic.put(492, "Spreadtrum Communications Shanghai Ltd"); + cic.put(493, "CuteCircuit LTD"); + cic.put(494, "Valeo Service"); + cic.put(495, "Fullpower Technologies, Inc."); + cic.put(496, "KloudNation"); + cic.put(497, "Zebra Technologies Corporation"); + cic.put(498, "Itron, Inc."); + cic.put(499, "The University of Tokyo"); + cic.put(500, "UTC Fire and Security"); + cic.put(501, "Cool Webthings Limited"); + cic.put(502, "DJO Global"); + cic.put(503, "Gelliner Limited"); + cic.put(504, "Anyka (Guangzhou) Microelectronics Technology Co, LTD"); + cic.put(505, "Medtronic Inc."); + cic.put(506, "Gozio Inc."); + cic.put(507, "Form Lifting, LLC"); + cic.put(508, "Wahoo Fitness, LLC"); + cic.put(509, "Kontakt Micro-Location Sp. z o.o."); + cic.put(510, "Radio Systems Corporation"); + cic.put(511, "Freescale Semiconductor, Inc."); + cic.put(512, "Verifone Systems Pte Ltd. Taiwan Branch"); + cic.put(513, "AR Timing"); + cic.put(514, "Rigado LLC"); + cic.put(515, "Kemppi Oy"); + cic.put(516, "Tapcentive Inc."); + cic.put(517, "Smartbotics Inc."); + cic.put(518, "Otter Products, LLC"); + cic.put(519, "STEMP Inc."); + cic.put(520, "LumiGeek LLC"); + cic.put(521, "InvisionHeart Inc."); + cic.put(522, "Macnica Inc."); + cic.put(523, "Jaguar Land Rover Limited"); + cic.put(524, "CoroWare Technologies, Inc"); + cic.put(525, "Simplo Technology Co., LTD"); + cic.put(526, "Omron Healthcare Co., LTD"); + cic.put(527, "Comodule GMBH"); + cic.put(528, "ikeGPS"); + cic.put(529, "Telink Semiconductor Co. Ltd"); + cic.put(530, "Interplan Co., Ltd"); + cic.put(531, "Wyler AG"); + cic.put(532, "IK Multimedia Production srl"); + cic.put(533, "Lukoton Experience Oy"); + cic.put(534, "MTI Ltd"); + cic.put(535, "Tech4home, Lda"); + cic.put(536, "Hiotech AB"); + cic.put(537, "DOTT Limited"); + cic.put(538, "Blue Speck Labs, LLC"); + cic.put(539, "Cisco Systems, Inc"); + cic.put(540, "Mobicomm Inc"); + cic.put(541, "Edamic"); + cic.put(542, "Goodnet, Ltd"); + cic.put(543, "Luster Leaf Products Inc"); + cic.put(544, "Manus Machina BV"); + cic.put(545, "Mobiquity Networks Inc"); + cic.put(546, "Praxis Dynamics"); + cic.put(547, "Philip Morris Products S.A."); + cic.put(548, "Comarch SA"); + cic.put(549, "Nestl Nespresso S.A."); + cic.put(550, "Merlinia A/S"); + cic.put(551, "LifeBEAM Technologies"); + cic.put(552, "Twocanoes Labs, LLC"); + cic.put(553, "Muoverti Limited"); + cic.put(554, "Stamer Musikanlagen GMBH"); + cic.put(555, "Tesla Motors"); + cic.put(556, "Pharynks Corporation"); + cic.put(557, "Lupine"); + cic.put(558, "Siemens AG"); + cic.put(559, "Huami (Shanghai) Culture Communication CO., LTD"); + cic.put(560, "Foster Electric Company, Ltd"); + cic.put(561, "ETA SA"); + cic.put(562, "x-Senso Solutions Kft"); + cic.put(563, "Shenzhen SuLong Communication Ltd"); + cic.put(564, "FengFan (BeiJing) Technology Co, Ltd"); + cic.put(565, "Qrio Inc"); + cic.put(566, "Pitpatpet Ltd"); + cic.put(567, "MSHeli s.r.l."); + cic.put(568, "Trakm8 Ltd"); + cic.put(569, "JIN CO, Ltd"); + cic.put(570, "Alatech Tehnology"); + cic.put(571, "Beijing CarePulse Electronic Technology Co, Ltd"); + cic.put(572, "Awarepoint"); + cic.put(573, "ViCentra B.V."); + cic.put(574, "Raven Industries"); + cic.put(575, "WaveWare Technologies Inc."); + cic.put(576, "Argenox Technologies"); + cic.put(577, "Bragi GmbH"); + cic.put(578, "16Lab Inc"); + cic.put(579, "Masimo Corp"); + cic.put(580, "Iotera Inc"); + cic.put(581, "Endress+Hauser"); + cic.put(582, "ACKme Networks, Inc."); + cic.put(583, "FiftyThree Inc."); + cic.put(584, "Parker Hannifin Corp"); + cic.put(585, "Transcranial Ltd"); + cic.put(586, "Uwatec AG"); + cic.put(587, "Orlan LLC"); + cic.put(588, "Blue Clover Devices"); + cic.put(589, "M-Way Solutions GmbH"); + cic.put(590, "Microtronics Engineering GmbH"); + cic.put(591, "Schneider Schreibgerte GmbH"); + cic.put(592, "Sapphire Circuits LLC"); + cic.put(593, "Lumo Bodytech Inc."); + cic.put(594, "UKC Technosolution"); + cic.put(595, "Xicato Inc."); + cic.put(596, "Playbrush"); + cic.put(597, "Dai Nippon Printing Co., Ltd."); + cic.put(598, "G24 Power Limited"); + cic.put(599, "AdBabble Local Commerce Inc."); + cic.put(600, "Devialet SA"); + cic.put(601, "ALTYOR"); + cic.put(602, "University of Applied Sciences Valais/Haute Ecole Valaisanne"); + cic.put(603, "Five Interactive, LLC dba Zendo"); + cic.put(604, "NetEaseHangzhouNetwork co.Ltd."); + cic.put(605, "Lexmark International Inc."); + cic.put(606, "Fluke Corporation"); + cic.put(607, "Yardarm Technologies"); + cic.put(608, "SensaRx"); + cic.put(609, "SECVRE GmbH"); + cic.put(610, "Glacial Ridge Technologies"); + cic.put(611, "Identiv, Inc."); + cic.put(612, "DDS, Inc."); + cic.put(613, "SMK Corporation"); + cic.put(614, "Schawbel Technologies LLC"); + cic.put(615, "XMI Systems SA"); + cic.put(616, "Cerevo"); + cic.put(617, "Torrox GmbH & Co KG"); + cic.put(618, "Gemalto"); + cic.put(619, "DEKA Research & Development Corp."); + cic.put(620, "Domster Tadeusz Szydlowski"); + cic.put(621, "Technogym SPA"); + cic.put(622, "FLEURBAEY BVBA"); + cic.put(623, "Aptcode Solutions"); + cic.put(624, "LSI ADL Technology"); + cic.put(625, "Animas Corp"); + cic.put(626, "Alps Electric Co., Ltd."); + cic.put(627, "OCEASOFT"); + cic.put(628, "Motsai Research"); + cic.put(629, "Geotab"); + cic.put(630, "E.G.O. Elektro-Gertebau GmbH"); + cic.put(631, "bewhere inc"); + cic.put(632, "Johnson Outdoors Inc"); + cic.put(633, "steute Schaltgerate GmbH & Co. KG"); + cic.put(634, "Ekomini inc."); + cic.put(635, "DEFA AS"); + cic.put(636, "Aseptika Ltd"); + cic.put(637, "HUAWEI Technologies Co., Ltd. ( )"); + cic.put(638, "HabitAware, LLC"); + cic.put(639, "ruwido austria gmbh"); + cic.put(640, "ITEC corporation"); + cic.put(641, "StoneL"); + cic.put(642, "Sonova AG"); + cic.put(643, "Maven Machines, Inc."); + cic.put(644, "Synapse Electronics"); + cic.put(645, "Standard Innovation Inc."); + cic.put(646, "RF Code, Inc."); + cic.put(647, "Wally Ventures S.L."); + cic.put(648, "Willowbank Electronics Ltd"); + cic.put(649, "SK Telecom"); + cic.put(650, "Jetro AS"); + cic.put(651, "Code Gears LTD"); + cic.put(652, "NANOLINK APS"); + cic.put(653, "IF, LLC"); + cic.put(654, "RF Digital Corp"); + cic.put(655, "Church & Dwight Co., Inc"); + cic.put(656, "Multibit Oy"); + cic.put(657, "CliniCloud Inc"); + cic.put(658, "SwiftSensors"); + cic.put(659, "Blue Bite"); + cic.put(660, "ELIAS GmbH"); + cic.put(661, "Sivantos GmbH"); + cic.put(662, "Petzl"); + cic.put(663, "storm power ltd"); + cic.put(664, "EISST Ltd"); + cic.put(665, "Inexess Technology Simma KG"); + cic.put(666, "Currant, Inc."); + cic.put(667, "C2 Development, Inc."); + cic.put(668, "Blue Sky Scientific, LLC"); + cic.put(669, "ALOTTAZS LABS, LLC"); + cic.put(670, "Kupson spol. s r.o."); + cic.put(671, "Areus Engineering GmbH"); + cic.put(672, "Impossible Camera GmbH"); + cic.put(673, "InventureTrack Systems"); + cic.put(674, "LockedUp"); + cic.put(675, "Itude"); + cic.put(676, "Pacific Lock Company"); + cic.put(677, "Tendyron Corporation ( )"); + cic.put(678, "Robert Bosch GmbH"); + cic.put(679, "Illuxtron international B.V."); + cic.put(680, "miSport Ltd."); + cic.put(681, "Chargelib"); + cic.put(682, "Doppler Lab"); + cic.put(683, "BBPOS Limited"); + cic.put(684, "RTB Elektronik GmbH & Co. KG"); + cic.put(685, "Rx Networks, Inc."); + cic.put(686, "WeatherFlow, Inc."); + cic.put(687, "Technicolor USA Inc."); + cic.put(688, "Bestechnic(Shanghai),Ltd"); + cic.put(689, "Raden Inc"); + cic.put(690, "JouZen Oy"); + cic.put(691, "CLABER S.P.A."); + cic.put(692, "Hyginex, Inc."); + cic.put(693, "HANSHIN ELECTRIC RAILWAY CO.,LTD."); + cic.put(694, "Schneider Electric"); + cic.put(695, "Oort Technologies LLC"); + cic.put(696, "Chrono Therapeutics"); + cic.put(697, "Rinnai Corporation"); + cic.put(698, "Swissprime Technologies AG"); + cic.put(699, "Koha.,Co.Ltd"); + cic.put(700, "Genevac Ltd"); + cic.put(701, "Chemtronics"); + cic.put(702, "Seguro Technology Sp. z o.o."); + cic.put(703, "Redbird Flight Simulations"); + cic.put(704, "Dash Robotics"); + cic.put(705, "LINE Corporation"); + cic.put(706, "Guillemot Corporation"); + cic.put(707, "Techtronic Power Tools Technology Limited"); + cic.put(708, "Wilson Sporting Goods"); + cic.put(709, "Lenovo (Singapore) Pte Ltd. ( )"); + cic.put(710, "Ayatan Sensors"); + cic.put(711, "Electronics Tomorrow Limited"); + cic.put(712, "VASCO Data Security International, Inc."); + cic.put(713, "PayRange Inc."); + cic.put(714, "ABOV Semiconductor"); + cic.put(715, "AINA-Wireless Inc."); + cic.put(716, "Eijkelkamp Soil & Water"); + cic.put(717, "BMA ergonomics b.v."); + cic.put(718, "Teva Branded Pharmaceutical Products R&D, Inc."); + cic.put(719, "Anima"); + cic.put(720, "3M"); + cic.put(721, "Empatica Srl"); + cic.put(722, "Afero, Inc."); + cic.put(723, "Powercast Corporation"); + cic.put(724, "Secuyou ApS"); + cic.put(725, "OMRON Corporation"); + cic.put(726, "Send Solutions"); + cic.put(727, "NIPPON SYSTEMWARE CO.,LTD."); + cic.put(728, "Neosfar"); + cic.put(729, "Fliegl Agrartechnik GmbH"); + cic.put(730, "Gilvader"); + cic.put(731, "Digi International Inc (R)"); + cic.put(732, "DeWalch Technologies, Inc."); + cic.put(733, "Flint Rehabilitation Devices, LLC"); + cic.put(734, "Samsung SDS Co., Ltd."); + cic.put(735, "Blur Product Development"); + cic.put(736, "University of Michigan"); + cic.put(737, "Victron Energy BV"); + cic.put(738, "NTT docomo"); + cic.put(739, "Carmanah Technologies Corp."); + cic.put(740, "Bytestorm Ltd."); + cic.put(741, "Espressif Incorporated ( () )"); + cic.put(742, "Unwire"); + cic.put(743, "Connected Yard, Inc."); + cic.put(744, "American Music Environments"); + cic.put(745, "Sensogram Technologies, Inc."); + cic.put(746, "Fujitsu Limited"); + cic.put(747, "Ardic Technology"); + cic.put(748, "Delta Systems, Inc"); + cic.put(749, "HTC Corporation"); + cic.put(750, "Citizen Holdings Co., Ltd."); + cic.put(751, "SMART-INNOVATION.inc"); + cic.put(752, "Blackrat Software"); + cic.put(753, "The Idea Cave, LLC"); + cic.put(754, "GoPro, Inc."); + cic.put(755, "AuthAir, Inc"); + cic.put(756, "Vensi, Inc."); + cic.put(757, "Indagem Tech LLC"); + cic.put(758, "Intemo Technologies"); + cic.put(759, "DreamVisions co., Ltd."); + cic.put(760, "Runteq Oy Ltd"); + cic.put(761, "IMAGINATION TECHNOLOGIES LTD"); + cic.put(762, "CoSTAR TEchnologies"); + cic.put(763, "Clarius Mobile Health Corp."); + cic.put(764, "Shanghai Frequen Microelectronics Co., Ltd."); + cic.put(765, "Uwanna, Inc."); + cic.put(766, "Lierda Science & Technology Group Co., Ltd."); + cic.put(767, "Silicon Laboratories"); + cic.put(768, "World Moto Inc."); + cic.put(769, "Giatec Scientific Inc."); + cic.put(770, "Loop Devices, Inc"); + cic.put(771, "IACA electronique"); + cic.put(772, "Martians Inc"); + cic.put(773, "Swipp ApS"); + cic.put(774, "Life Laboratory Inc."); + cic.put(775, "FUJI INDUSTRIAL CO.,LTD."); + cic.put(776, "Surefire, LLC"); + cic.put(777, "Dolby Labs"); + cic.put(778, "Ellisys"); + cic.put(779, "Magnitude Lighting Converters"); + cic.put(780, "Hilti AG"); + cic.put(781, "Devdata S.r.l."); + cic.put(782, "Deviceworx"); + cic.put(783, "Shortcut Labs"); + cic.put(784, "SGL Italia S.r.l."); + cic.put(785, "PEEQ DATA"); + cic.put(786, "Ducere Technologies Pvt Ltd"); + cic.put(787, "DiveNav, Inc."); + cic.put(788, "RIIG AI Sp. z o.o."); + cic.put(789, "Thermo Fisher Scientific"); + cic.put(790, "AG Measurematics Pvt. Ltd."); + cic.put(791, "CHUO Electronics CO., LTD."); + cic.put(792, "Aspenta International"); + cic.put(793, "Eugster Frismag AG"); + cic.put(794, "Amber wireless GmbH"); + cic.put(795, "HQ Inc"); + cic.put(796, "Lab Sensor Solutions"); + cic.put(797, "Enterlab ApS"); + cic.put(798, "Eyefi, Inc."); + cic.put(799, "MetaSystem S.p.A."); + cic.put(800, "SONO ELECTRONICS. CO., LTD"); + cic.put(801, "Jewelbots"); + cic.put(802, "Compumedics Limited"); + cic.put(803, "Rotor Bike Components"); + cic.put(804, "Astro, Inc."); + cic.put(805, "Amotus Solutions"); + cic.put(806, "Healthwear Technologies (Changzhou)Ltd"); + cic.put(807, "Essex Electronics"); + cic.put(808, "Grundfos A/S"); + cic.put(809, "Eargo, Inc."); + cic.put(810, "Electronic Design Lab"); + cic.put(811, "ESYLUX"); + cic.put(812, "NIPPON SMT.CO.,Ltd"); + cic.put(813, "BM innovations GmbH"); + cic.put(814, "indoormap"); + cic.put(815, "OttoQ Inc"); + cic.put(816, "North Pole Engineering"); + cic.put(817, "3flares Technologies Inc."); + cic.put(818, "Electrocompaniet A.S."); + cic.put(819, "Mul-T-Lock"); + cic.put(820, "Corentium AS"); + cic.put(821, "Enlighted Inc"); + cic.put(822, "GISTIC"); + cic.put(823, "AJP2 Holdings, LLC"); + cic.put(824, "COBI GmbH"); + cic.put(825, "Blue Sky Scientific, LLC"); + cic.put(826, "Appception, Inc."); + cic.put(827, "Courtney Thorne Limited"); + cic.put(828, "Virtuosys"); + cic.put(829, "TPV Technology Limited"); + cic.put(830, "Monitra SA"); + cic.put(831, "Automation Components, Inc."); + cic.put(832, "Letsense s.r.l."); + cic.put(833, "Etesian Technologies LLC"); + cic.put(834, "GERTEC BRASIL LTDA."); + cic.put(835, "Drekker Development Pty. Ltd."); + cic.put(836, "Whirl Inc"); + cic.put(837, "Locus Positioning"); + cic.put(838, "Acuity Brands Lighting, Inc"); + cic.put(839, "Prevent Biometrics"); + cic.put(840, "Arioneo"); + cic.put(841, "VersaMe"); + cic.put(842, "Vaddio"); + cic.put(843, "Libratone A/S"); + cic.put(844, "HM Electronics, Inc."); + cic.put(845, "TASER International, Inc."); + cic.put(846, "SafeTrust Inc."); + cic.put(847, "Heartland Payment Systems"); + cic.put(848, "Bitstrata Systems Inc."); + cic.put(849, "Pieps GmbH"); + cic.put(850, "iRiding(Xiamen)Technology Co.,Ltd."); + cic.put(851, "Alpha Audiotronics, Inc."); + cic.put(852, "TOPPAN FORMS CO.,LTD."); + cic.put(853, "Sigma Designs, Inc."); + cic.put(854, "Spectrum Brands, Inc."); + cic.put(855, "Polymap Wireless"); + cic.put(856, "MagniWare Ltd."); + cic.put(857, "Novotec Medical GmbH"); + cic.put(858, "Medicom Innovation Partner a/s"); + cic.put(859, "Matrix Inc."); + cic.put(860, "Eaton Corporation"); + cic.put(861, "KYS"); + cic.put(862, "Naya Health, Inc."); + cic.put(863, "Acromag"); + cic.put(864, "Insulet Corporation"); + cic.put(865, "Wellinks Inc."); + cic.put(866, "ON Semiconductor"); + cic.put(867, "FREELAP SA"); + cic.put(868, "Favero Electronics Srl"); + cic.put(869, "BioMech Sensor LLC"); + cic.put(870, "BOLTT Sports technologies Private limited"); + cic.put(871, "Saphe International"); + cic.put(872, "Metormote AB"); + cic.put(873, "littleBits"); + cic.put(874, "SetPoint Medical"); + cic.put(875, "BRControls Products BV"); + cic.put(876, "Zipcar"); + cic.put(877, "AirBolt Pty Ltd"); + cic.put(878, "KeepTruckin Inc"); + cic.put(879, "Motiv, Inc."); + cic.put(880, "Wazombi Labs O"); + cic.put(881, "ORBCOMM"); + cic.put(882, "Nixie Labs, Inc."); + cic.put(883, "AppNearMe Ltd"); + cic.put(884, "Holman Industries"); + cic.put(885, "Expain AS"); + cic.put(886, "Electronic Temperature Instruments Ltd"); + cic.put(887, "Plejd AB"); + cic.put(888, "Propeller Health"); + cic.put(889, "Shenzhen iMCO Electronic Technology Co.,Ltd"); + cic.put(890, "Algoria"); + cic.put(891, "Apption Labs Inc."); + cic.put(892, "Cronologics Corporation"); + cic.put(893, "MICRODIA Ltd."); + cic.put(894, "lulabytes S.L."); + cic.put(895, "Nestec S.A."); + cic.put(896, "LLC \"MEGA-F service\""); + cic.put(897, "Sharp Corporation"); + cic.put(898, "Precision Outcomes Ltd"); + cic.put(899, "Kronos Incorporated"); + cic.put(900, "OCOSMOS Co., Ltd."); + cic.put(901, "Embedded Electronic Solutions Ltd. dba e2Solutions"); + cic.put(902, "Aterica Inc."); + cic.put(903, "BluStor PMC, Inc."); + cic.put(904, "Kapsch TrafficCom AB"); + cic.put(905, "ActiveBlu Corporation"); + cic.put(906, "Kohler Mira Limited"); + cic.put(907, "Noke"); + cic.put(908, "Appion Inc."); + cic.put(909, "Resmed Ltd"); + cic.put(910, "Crownstone B.V."); + cic.put(911, "Xiaomi Inc."); + cic.put(912, "INFOTECH s.r.o."); + cic.put(913, "Thingsquare AB"); + cic.put(914, "T&D"); + cic.put(915, "LAVAZZA S.p.A."); + cic.put(916, "Netclearance Systems, Inc."); + cic.put(917, "SDATAWAY"); + cic.put(918, "BLOKS GmbH"); + cic.put(919, "LEGO System A/S"); + cic.put(920, "Thetatronics Ltd"); + cic.put(921, "Nikon Corporation"); + cic.put(922, "NeST"); + cic.put(923, "South Silicon Valley Microelectronics"); + cic.put(924, "ALE International"); + cic.put(925, "CareView Communications, Inc."); + cic.put(926, "SchoolBoard Limited"); + cic.put(927, "Molex Corporation"); + cic.put(928, "IVT Wireless Limited"); + cic.put(929, "Alpine Labs LLC"); + cic.put(930, "Candura Instruments"); + cic.put(931, "SmartMovt Technology Co., Ltd"); + cic.put(932, "Token Zero Ltd"); + cic.put(933, "ACE CAD Enterprise Co., Ltd. (ACECAD)"); + cic.put(934, "Medela, Inc"); + cic.put(935, "AeroScout"); + cic.put(936, "Esrille Inc."); + cic.put(937, "THINKERLY SRL"); + cic.put(938, "Exon Sp. z o.o."); + cic.put(939, "Meizu Technology Co., Ltd."); + cic.put(940, "Smablo LTD"); + cic.put(941, "XiQ"); + cic.put(942, "Allswell Inc."); + cic.put(943, "Comm-N-Sense Corp DBA Verigo"); + cic.put(944, "VIBRADORM GmbH"); + cic.put(945, "Otodata Wireless Network Inc."); + cic.put(946, "Propagation Systems Limited"); + cic.put(947, "Midwest Instruments & Controls"); + cic.put(948, "Alpha Nodus, inc."); + cic.put(949, "petPOMM, Inc"); + cic.put(950, "Mattel"); + cic.put(951, "Airbly Inc."); + cic.put(952, "A-Safe Limited"); + cic.put(953, "FREDERIQUE CONSTANT SA"); + cic.put(954, "Maxscend Microelectronics Company Limited"); + cic.put(955, "Abbott Diabetes Care"); + cic.put(956, "ASB Bank Ltd"); + cic.put(957, "amadas"); + cic.put(958, "Applied Science, Inc."); + cic.put(959, "iLumi Solutions Inc."); + cic.put(960, "Arch Systems Inc."); + cic.put(961, "Ember Technologies, Inc."); + cic.put(962, "Snapchat Inc"); + cic.put(963, "Casambi Technologies Oy"); + cic.put(964, "Pico Technology Inc."); + cic.put(965, "St. Jude Medical, Inc."); + cic.put(966, "Intricon"); + cic.put(967, "Structural Health Systems, Inc."); + cic.put(968, "Avvel International"); + cic.put(969, "Gallagher Group"); + cic.put(970, "In2things Automation Pvt. Ltd."); + cic.put(971, "SYSDEV Srl"); + cic.put(972, "Vonkil Technologies Ltd"); + cic.put(973, "Wynd Technologies, Inc."); + cic.put(974, "CONTRINEX S.A."); + cic.put(975, "MIRA, Inc."); + cic.put(976, "Watteam Ltd"); + cic.put(977, "Density Inc."); + cic.put(978, "IOT Pot India Private Limited"); + cic.put(979, "Sigma Connectivity AB"); + cic.put(980, "PEG PEREGO SPA"); + cic.put(981, "Wyzelink Systems Inc."); + cic.put(982, "Yota Devices LTD"); + cic.put(983, "FINSECUR"); + cic.put(984, "Zen-Me Labs Ltd"); + cic.put(985, "3IWare Co., Ltd."); + cic.put(986, "EnOcean GmbH"); + cic.put(987, "Instabeat, Inc"); + cic.put(988, "Nima Labs"); + cic.put(989, "Andreas Stihl AG & Co. KG"); + cic.put(990, "Nathan Rhoades LLC"); + cic.put(991, "Grob Technologies, LLC"); + cic.put(992, "Actions (Zhuhai) Technology Co., Limited"); + cic.put(993, "SPD Development Company Ltd"); + cic.put(994, "Sensoan Oy"); + cic.put(995, "Qualcomm Life Inc"); + cic.put(996, "Chip-ing AG"); + cic.put(997, "ffly4u"); + cic.put(998, "IoT Instruments Oy"); + cic.put(999, "TRUE Fitness Technology"); + cic.put(1000, "Reiner Kartengeraete GmbH & Co. KG."); + cic.put(1001, "SHENZHEN LEMONJOY TECHNOLOGY CO., LTD."); + cic.put(1002, "Hello Inc."); + cic.put(1003, "Evollve Inc."); + cic.put(1004, "Jigowatts Inc."); + cic.put(1005, "BASIC MICRO.COM,INC."); + cic.put(1006, "CUBE TECHNOLOGIES"); + cic.put(1007, "foolography GmbH"); + cic.put(1008, "CLINK"); + cic.put(1009, "Hestan Smart Cooking Inc."); + cic.put(1010, "WindowMaster A/S"); + cic.put(1011, "Flowscape AB"); + cic.put(1012, "PAL Technologies Ltd"); + cic.put(1013, "WHERE, Inc."); + cic.put(1014, "Iton Technology Corp."); + cic.put(1015, "Owl Labs Inc."); + cic.put(1016, "Rockford Corp."); + cic.put(1017, "Becon Technologies Co.,Ltd."); + cic.put(1018, "Vyassoft Technologies Inc"); + cic.put(1019, "Nox Medical"); + cic.put(1020, "Kimberly-Clark"); + cic.put(1021, "Trimble Navigation Ltd."); + cic.put(1022, "Littelfuse"); + cic.put(1023, "Withings"); + cic.put(1024, "i-developer IT Beratung UG"); + cic.put(1025, ""); + cic.put(1026, "Sears Holdings Corporation"); + cic.put(1027, "Gantner Electronic GmbH"); + cic.put(1028, "Authomate Inc"); + cic.put(1029, "Vertex International, Inc."); + cic.put(1030, "Airtago"); + cic.put(1031, "Swiss Audio SA"); + cic.put(1032, "ToGetHome Inc."); + cic.put(1033, "AXIS"); + cic.put(1034, "Openmatics"); + cic.put(1035, "Jana Care Inc."); + cic.put(1036, "Senix Corporation"); + cic.put(1037, "NorthStar Battery Company, LLC"); + cic.put(1038, "SKF (U.K.) Limited"); + cic.put(1039, "CO-AX Technology, Inc."); + cic.put(1040, "Fender Musical Instruments"); + cic.put(1041, "Luidia Inc"); + cic.put(1042, "SEFAM"); + cic.put(1043, "Wireless Cables Inc"); + cic.put(1044, "Lightning Protection International Pty Ltd"); + cic.put(1045, "Uber Technologies Inc"); + cic.put(1046, "SODA GmbH"); + cic.put(1047, "Fatigue Science"); + cic.put(1048, "Alpine Electronics Inc."); + cic.put(1049, "Novalogy LTD"); + cic.put(1050, "Friday Labs Limited"); + cic.put(1051, "OrthoAccel Technologies"); + cic.put(1052, "WaterGuru, Inc."); + cic.put(1053, "Benning Elektrotechnik und Elektronik GmbH & Co. KG"); + cic.put(1054, "Dell Computer Corporation"); + cic.put(1055, "Kopin Corporation"); + cic.put(1056, "TecBakery GmbH"); + cic.put(1057, "Backbone Labs, Inc."); + cic.put(1058, "DELSEY SA"); + cic.put(1059, "Chargifi Limited"); + cic.put(1060, "Trainesense Ltd."); + cic.put(1061, "Unify Software and Solutions GmbH & Co. KG"); + cic.put(1062, "Husqvarna AB"); + cic.put(1063, "Focus fleet and fuel management inc"); + cic.put(1064, "SmallLoop, LLC"); + cic.put(1065, "Prolon Inc."); + cic.put(1066, "BD Medical"); + cic.put(1067, "iMicroMed Incorporated"); + cic.put(1068, "Ticto N.V."); + cic.put(1069, "Meshtech AS"); + cic.put(1070, "MemCachier Inc."); + cic.put(1071, "Danfoss A/S"); + cic.put(1072, "SnapStyk Inc."); + cic.put(1073, "Amway Corporation"); + cic.put(1074, "Silk Labs, Inc."); + cic.put(1075, "Pillsy Inc."); + cic.put(1076, "Hatch Baby, Inc."); + cic.put(1077, "Blocks Wearables Ltd."); + cic.put(1078, "Drayson Technologies (Europe) Limited"); + cic.put(1079, "eBest IOT Inc."); + cic.put(1080, "Helvar Ltd"); + cic.put(1081, "Radiance Technologies"); + cic.put(1082, "Nuheara Limited"); + cic.put(1083, "Appside co., ltd."); + cic.put(1084, "DeLaval"); + cic.put(1085, "Coiler Corporation"); + cic.put(1086, "Thermomedics, Inc."); + cic.put(1087, "Tentacle Sync GmbH"); + cic.put(1088, "Valencell, Inc."); + cic.put(1089, "iProtoXi Oy"); + cic.put(1090, "SECOM CO., LTD."); + cic.put(1091, "Tucker International LLC"); + cic.put(1092, "Metanate Limited"); + cic.put(1093, "Kobian Canada Inc."); + cic.put(1094, "NETGEAR, Inc."); + cic.put(1095, "Fabtronics Australia Pty Ltd"); + cic.put(1096, "Grand Centrix GmbH"); + cic.put(1097, "1UP USA.com llc"); + cic.put(1098, "SHIMANO INC."); + cic.put(1099, "Nain Inc."); + cic.put(1100, "LifeStyle Lock, LLC"); + cic.put(1101, "VEGA Grieshaber KG"); + cic.put(1102, "Xtrava Inc."); + cic.put(1103, "TTS Tooltechnic Systems AG & Co. KG"); + cic.put(1104, "Teenage Engineering AB"); + cic.put(1105, "Tunstall Nordic AB"); + cic.put(1106, "Svep Design Center AB"); + cic.put(1107, "GreenPeak Technologies BV"); + cic.put(1108, "Sphinx Electronics GmbH & Co KG"); + cic.put(1109, "Atomation"); + cic.put(1110, "Nemik Consulting Inc"); + cic.put(1111, "RF INNOVATION"); + cic.put(1112, "Mini Solution Co., Ltd."); + cic.put(1113, "Lumenetix, Inc"); + cic.put(1114, "2048450 Ontario Inc"); + cic.put(1115, "SPACEEK LTD"); + cic.put(1116, "Delta T Corporation"); + cic.put(1117, "Boston Scientific Corporation"); + cic.put(1118, "Nuviz, Inc."); + cic.put(1119, "Real Time Automation, Inc."); + cic.put(1120, "Kolibree"); + cic.put(1121, "vhf elektronik GmbH"); + cic.put(1122, "Bonsai Systems GmbH"); + cic.put(1123, "Fathom Systems Inc."); + cic.put(1124, "Bellman & Symfon"); + cic.put(1125, "International Forte Group LLC"); + cic.put(1126, "CycleLabs Solutions inc."); + cic.put(1127, "Codenex Oy"); + cic.put(1128, "Kynesim Ltd"); + cic.put(1129, "Palago AB"); + cic.put(1130, "INSIGMA INC."); + cic.put(1131, "PMD Solutions"); + cic.put(1132, "Qingdao Realtime Technology Co., Ltd."); + cic.put(1133, "BEGA Gantenbrink-Leuchten KG"); + cic.put(1134, "Pambor Ltd."); + cic.put(1135, "Develco Products A/S"); + cic.put(1136, "iDesign s.r.l."); + cic.put(1137, "TiVo Corp"); + cic.put(1138, "Control-J Pty Ltd"); + cic.put(1139, "Steelcase, Inc."); + cic.put(1140, "iApartment co., ltd."); + cic.put(1141, "Icom inc."); + cic.put(1142, "Oxstren Wearable Technologies Private Limited"); + cic.put(1143, "Blue Spark Technologies"); + cic.put(1144, "FarSite Communications Limited"); + cic.put(1145, "mywerk system GmbH"); + cic.put(1146, "Sinosun Technology Co., Ltd."); + cic.put(1147, "MIYOSHI ELECTRONICS CORPORATION"); + cic.put(1148, "POWERMAT LTD"); + cic.put(1149, "Occly LLC"); + cic.put(1150, "OurHub Dev IvS"); + cic.put(1151, "Pro-Mark, Inc."); + cic.put(1152, "Dynometrics Inc."); + cic.put(1153, "Quintrax Limited"); + cic.put(1154, "POS Tuning Udo Vosshenrich GmbH & Co. KG"); + cic.put(1155, "Multi Care Systems B.V."); + cic.put(1156, "Revol Technologies Inc"); + cic.put(1157, "SKIDATA AG"); + cic.put(1158, "DEV TECNOLOGIA INDUSTRIA, COMERCIO E MANUTENCAO DE EQUIPAMENTOS LTDA. - ME"); + cic.put(1159, "Centrica Connected Home"); + cic.put(1160, "Automotive Data Solutions Inc"); + cic.put(1161, "Igarashi Engineering"); + cic.put(1162, "Taelek Oy"); + cic.put(1163, "CP Electronics Limited"); + cic.put(1164, "Vectronix AG"); + cic.put(1165, "S-Labs Sp. z o.o."); + cic.put(1166, "Companion Medical, Inc."); + cic.put(1167, "BlueKitchen GmbH"); + cic.put(1168, "Matting AB"); + cic.put(1169, "SOREX - Wireless Solutions GmbH"); + cic.put(1170, "ADC Technology, Inc."); + cic.put(1171, "Lynxemi Pte Ltd"); + cic.put(1172, "SENNHEISER electronic GmbH & Co. KG"); + cic.put(1173, "LMT Mercer Group, Inc"); + cic.put(1174, "Polymorphic Labs LLC"); + cic.put(1175, "Cochlear Limited"); + cic.put(1176, "METER Group, Inc. USA"); + cic.put(1177, "Ruuvi Innovations Ltd."); + cic.put(1178, "Situne AS"); + cic.put(1179, "nVisti, LLC"); + cic.put(1180, "DyOcean"); + cic.put(1181, "Uhlmann & Zacher GmbH"); + cic.put(1182, "AND!XOR LLC"); + cic.put(1183, "tictote AB"); + cic.put(1184, "Vypin, LLC"); + cic.put(1185, "PNI Sensor Corporation"); + cic.put(1186, "ovrEngineered, LLC"); + cic.put(1187, "GT-tronics HK Ltd"); + cic.put(1188, "Herbert Waldmann GmbH & Co. KG"); + cic.put(1189, "Guangzhou FiiO Electronics Technology Co.,Ltd"); + cic.put(1190, "Vinetech Co., Ltd"); + cic.put(1191, "Dallas Logic Corporation"); + cic.put(1192, "BioTex, Inc."); + cic.put(1193, "DISCOVERY SOUND TECHNOLOGY, LLC"); + cic.put(1194, "LINKIO SAS"); + cic.put(1195, "Harbortronics, Inc."); + cic.put(1196, "Undagrid B.V."); + cic.put(1197, "Shure Inc"); + cic.put(1198, "ERM Electronic Systems LTD"); + cic.put(1199, "BIOROWER Handelsagentur GmbH"); + cic.put(1200, "Weba Sport und Med. Artikel GmbH"); + cic.put(1201, "Kartographers Technologies Pvt. Ltd."); + cic.put(1202, "The Shadow on the Moon"); + cic.put(1203, "mobike (Hong Kong) Limited"); + cic.put(1204, "Inuheat Group AB"); + cic.put(1205, "Swiftronix AB"); + cic.put(1206, "Diagnoptics Technologies"); + cic.put(1207, "Analog Devices, Inc."); + cic.put(1208, "Soraa Inc."); + cic.put(1209, "CSR Building Products Limited"); + cic.put(1210, "Crestron Electronics, Inc."); + cic.put(1211, "Neatebox Ltd"); + cic.put(1212, "Draegerwerk AG & Co. KGaA"); + cic.put(1213, "AlbynMedical"); + cic.put(1214, "Averos FZCO"); + cic.put(1215, "VIT Initiative, LLC"); + cic.put(1216, "Statsports International"); + cic.put(1217, "Sospitas, s.r.o."); + cic.put(1218, "Dmet Products Corp."); + cic.put(1219, "Mantracourt Electronics Limited"); + cic.put(1220, "TeAM Hutchins AB"); + cic.put(1221, "Seibert Williams Glass, LLC"); + cic.put(1222, "Insta GmbH"); + cic.put(1223, "Svantek Sp. z o.o."); + cic.put(1224, "Shanghai Flyco Electrical Appliance Co., Ltd."); + cic.put(1225, "Thornwave Labs Inc"); + cic.put(1226, "Steiner-Optik GmbH"); + cic.put(1227, "Novo Nordisk A/S"); + cic.put(1228, "Enflux Inc."); + cic.put(1229, "Safetech Products LLC"); + cic.put(1230, "GOOOLED S.R.L."); + cic.put(1231, "DOM Sicherheitstechnik GmbH & Co. KG"); + cic.put(1232, "Olympus Corporation"); + cic.put(1233, "KTS GmbH"); + cic.put(1234, "Anloq Technologies Inc."); + cic.put(1235, "Queercon, Inc"); + cic.put(1236, "5th Element Ltd"); + cic.put(1237, "Gooee Limited"); + cic.put(1238, "LUGLOC LLC"); + cic.put(1239, "Blincam, Inc."); + cic.put(1240, "FUJIFILM Corporation"); + cic.put(1241, "RandMcNally"); + cic.put(1242, "Franceschi Marina snc"); + cic.put(1243, "Engineered Audio, LLC."); + cic.put(1244, "IOTTIVE (OPC) PRIVATE LIMITED"); + cic.put(1245, "4MOD Technology"); + cic.put(1246, "Lutron Electronics Co., Inc."); + cic.put(1247, "Emerson"); + cic.put(1248, "Guardtec, Inc."); + cic.put(1249, "REACTEC LIMITED"); + cic.put(1250, "EllieGrid"); + cic.put(1251, "Under Armour"); + cic.put(1252, "Woodenshark"); + cic.put(1253, "Avack Oy"); + cic.put(1254, "Smart Solution Technology, Inc."); + cic.put(1255, "REHABTRONICS INC."); + cic.put(1256, "STABILO International"); + cic.put(1257, "Busch Jaeger Elektro GmbH"); + cic.put(1258, "Pacific Bioscience Laboratories, Inc"); + cic.put(1259, "Bird Home Automation GmbH"); + cic.put(1260, "Motorola Solutions"); + cic.put(1261, "R9 Technology, Inc."); + cic.put(1262, "Auxivia"); + cic.put(1263, "DaisyWorks, Inc"); + cic.put(1264, "Kosi Limited"); + cic.put(1265, "Theben AG"); + cic.put(1266, "InDreamer Techsol Private Limited"); + cic.put(1267, "Cerevast Medical"); + cic.put(1268, "ZanCompute Inc."); + cic.put(1269, "Pirelli Tyre S.P.A."); + cic.put(1270, "McLear Limited"); + cic.put(1271, "Shenzhen Huiding Technology Co.,Ltd."); + cic.put(1272, "Convergence Systems Limited"); + cic.put(1273, "Interactio"); + cic.put(1274, "Androtec GmbH"); + cic.put(1275, "Benchmark Drives GmbH & Co. KG"); + cic.put(1276, "SwingLync L. L. C."); + cic.put(1277, "Tapkey GmbH"); + cic.put(1278, "Woosim Systems Inc."); + cic.put(1279, "Microsemi Corporation"); + cic.put(1280, "Wiliot LTD."); + cic.put(1281, "Polaris IND"); + cic.put(1282, "Specifi-Kali LLC"); + cic.put(1283, "Locoroll, Inc"); + cic.put(1284, "PHYPLUS Inc"); + cic.put(1285, "Inplay Technologies LLC"); + cic.put(1286, "Hager"); + cic.put(1287, "Yellowcog"); + cic.put(1288, "Axes System sp. z o. o."); + cic.put(1289, "myLIFTER Inc."); + cic.put(1290, "Shake-on B.V."); + cic.put(1291, "Vibrissa Inc."); + cic.put(1292, "OSRAM GmbH"); + cic.put(1293, "TRSystems GmbH"); + cic.put(1294, "Yichip Microelectronics (Hangzhou) Co.,Ltd."); + cic.put(1295, "Foundation Engineering LLC"); + cic.put(1296, "UNI-ELECTRONICS, INC."); + cic.put(1297, "Brookfield Equinox LLC"); + cic.put(1298, "Soprod SA"); + cic.put(1299, "9974091 Canada Inc."); + cic.put(1300, "FIBRO GmbH"); + cic.put(1301, "RB Controls Co., Ltd."); + cic.put(1302, "Footmarks"); + cic.put(1303, "Amcore AB"); + cic.put(1304, "MAMORIO.inc"); + cic.put(1305, "Tyto Life LLC"); + cic.put(1306, "Leica Camera AG"); + cic.put(1307, "Angee Technologies Ltd."); + cic.put(1308, "EDPS"); + cic.put(1309, "OFF Line Co., Ltd."); + cic.put(1310, "Detect Blue Limited"); + cic.put(1311, "Setec Pty Ltd"); + cic.put(1312, "Target Corporation"); + cic.put(1313, "IAI Corporation"); + cic.put(1314, "NS Tech, Inc."); + cic.put(1315, "MTG Co., Ltd."); + cic.put(1316, "Hangzhou iMagic Technology Co., Ltd"); + cic.put(1317, "HONGKONG NANO IC TECHNOLOGIES CO., LIMITED"); + cic.put(1318, "Honeywell International Inc."); + cic.put(1319, "Albrecht JUNG"); + cic.put(1320, "Lunera Lighting Inc."); + cic.put(1321, "Lumen UAB"); + cic.put(1322, "Keynes Controls Ltd"); + cic.put(1323, "Novartis AG"); + cic.put(1324, "Geosatis SA"); + cic.put(1325, "EXFO, Inc."); + cic.put(1326, "LEDVANCE GmbH"); + cic.put(1327, "Center ID Corp."); + cic.put(1328, "Adolene, Inc."); + cic.put(1329, "D&M Holdings Inc."); + cic.put(1330, "CRESCO Wireless, Inc."); + cic.put(1331, "Nura Operations Pty Ltd"); + cic.put(1332, "Frontiergadget, Inc."); + cic.put(1333, "Smart Component Technologies Limited"); + cic.put(1334, "ZTR Control Systems LLC"); + cic.put(1335, "MetaLogics Corporation"); + cic.put(1336, "Medela AG"); + cic.put(1337, "OPPLE Lighting Co., Ltd"); + cic.put(1338, "Savitech Corp.,"); + cic.put(1339, "prodigy"); + cic.put(1340, "Screenovate Technologies Ltd"); + cic.put(1341, "TESA SA"); + cic.put(1342, "CLIM8 LIMITED"); + cic.put(1343, "Silergy Corp"); + cic.put(1344, "SilverPlus, Inc"); + cic.put(1345, "Sharknet srl"); + cic.put(1346, "Mist Systems, Inc."); + cic.put(1347, "MIWA LOCK CO.,Ltd"); + cic.put(1348, "OrthoSensor, Inc."); + cic.put(1349, "Candy Hoover Group s.r.l"); + cic.put(1350, "Apexar Technologies S.A."); + cic.put(1351, "LOGICDATA d.o.o."); + cic.put(1352, "Knick Elektronische Messgeraete GmbH & Co. KG"); + cic.put(1353, "Smart Technologies and Investment Limited"); + cic.put(1354, "Linough Inc."); + cic.put(1355, "Advanced Electronic Designs, Inc."); + cic.put(1356, "Carefree Scott Fetzer Co Inc"); + cic.put(1357, "Sensome"); + cic.put(1358, "FORTRONIK storitve d.o.o."); + cic.put(1359, "Sinnoz"); + cic.put(1360, "Versa Networks, Inc."); + cic.put(1361, "Sylero"); + cic.put(1362, "Avempace SARL"); + cic.put(1363, "Nintendo Co., Ltd."); + cic.put(1364, "National Instruments"); + cic.put(1365, "KROHNE Messtechnik GmbH"); + cic.put(1366, "Otodynamics Ltd"); + cic.put(1367, "Arwin Technology Limited"); + cic.put(1368, "benegear, inc."); + cic.put(1369, "Newcon Optik"); + cic.put(1370, "CANDY HOUSE, Inc."); + cic.put(1371, "FRANKLIN TECHNOLOGY INC"); + cic.put(1372, "Lely"); + cic.put(1373, "Valve Corporation"); + cic.put(1374, "Hekatron Vertriebs GmbH"); + cic.put(1375, "PROTECH S.A.S. DI GIRARDI ANDREA & C."); + cic.put(1376, "Sarita CareTech IVS"); + cic.put(1377, "Finder S.p.A."); + cic.put(1378, "Thalmic Labs Inc."); + cic.put(1379, "Steinel Vertrieb GmbH"); + cic.put(1380, "Beghelli Spa"); + cic.put(1381, "Beijing Smartspace Technologies Inc."); + cic.put(1382, "CORE TRANSPORT TECHNOLOGIES NZ LIMITED"); + cic.put(1383, "Xiamen Everesports Goods Co., Ltd"); + cic.put(1384, "Bodyport Inc."); + cic.put(1385, "Audionics System, INC."); + cic.put(1386, "Flipnavi Co.,Ltd."); + cic.put(1387, "Rion Co., Ltd."); + cic.put(1388, "Long Range Systems, LLC"); + cic.put(1389, "Redmond Industrial Group LLC"); + cic.put(1390, "VIZPIN INC."); + cic.put(1391, "BikeFinder AS"); + cic.put(1392, "Consumer Sleep Solutions LLC"); + cic.put(1393, "PSIKICK, INC."); + cic.put(1394, "AntTail.com"); + cic.put(1395, "Lighting Science Group Corp."); + cic.put(1396, "AFFORDABLE ELECTRONICS INC"); + cic.put(1397, "Integral Memroy Plc"); + cic.put(1398, "Globalstar, Inc."); + cic.put(1399, "True Wearables, Inc."); + cic.put(1400, "Wellington Drive Technologies Ltd"); + cic.put(1401, "Ensemble Tech Private Limited"); + cic.put(1402, "OMNI Remotes"); + cic.put(1403, "Duracell U.S. Operations Inc."); + cic.put(1404, "Toor Technologies LLC"); + cic.put(1405, "Instinct Performance"); + cic.put(1406, "Beco, Inc"); + cic.put(1407, "Scuf Gaming International, LLC"); + cic.put(1408, "ARANZ Medical Limited"); + cic.put(1409, "LYS TECHNOLOGIES LTD"); + cic.put(1410, "Breakwall Analytics, LLC"); + cic.put(1411, "Code Blue Communications"); + cic.put(1412, "Gira Giersiepen GmbH & Co. KG"); + cic.put(1413, "Hearing Lab Technology"); + cic.put(1414, "LEGRAND"); + cic.put(1415, "Derichs GmbH"); + cic.put(1416, "ALT-TEKNIK LLC"); + cic.put(1417, "Star Technologies"); + } + + /** + * Returns the company name as a String + * + * @param id the Bluetooth company identifier + * @return The company name + */ + public static @Nullable String get(@Nullable Integer id) { + if (id != null) { + return cic.get(id); + } else { + return null; + } + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCompletionStatus.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCompletionStatus.java new file mode 100644 index 00000000000..35ef1cf9d6d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothCompletionStatus.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +/** + * An enumeration of transaction completion status values + * + * @author Chris Jackson - Initial Implementation + * + */ +public enum BluetoothCompletionStatus { + SUCCESS, + ERROR +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDescriptor.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDescriptor.java new file mode 100644 index 00000000000..ba03fb460c1 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDescriptor.java @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * The {@link BluetoothDescriptor} class defines the Bluetooth descriptor. + *

+ * Descriptors are defined attributes that describe a characteristic value. + *

+ * https://www.bluetooth.com/specifications/gatt/descriptors + * + * @author Chris Jackson - Initial contribution + * @author Kai Kreuzer - added constructor and fixed setValue method + */ +public class BluetoothDescriptor { + + protected final BluetoothCharacteristic characteristic; + protected final UUID uuid; + protected byte[] value; + + /** + * The main constructor + * + * @param characteristic the characteristic that this class describes + * @param uuid the uuid of the descriptor + */ + public BluetoothDescriptor(BluetoothCharacteristic characteristic, UUID uuid) { + this.characteristic = characteristic; + this.uuid = uuid; + } + + /** + * Returns the characteristic this descriptor belongs to. + * + * @return + */ + BluetoothCharacteristic getCharacteristic() { + return characteristic; + + } + + /** + * Returns the permissions for this descriptor. + * + * @return the permissions + */ + public int getPermissions() { + return 0; + } + + /** + * Returns the UUID of this descriptor. + * + * @return the UUID + */ + public UUID getUuid() { + return uuid; + + } + + /** + * Returns the stored value for this descriptor. It doesn't read remote data. + * + * @return the value of the descriptor + */ + public byte[] getValue() { + return value; + } + + /** + * Sets the stored value for this descriptor. It doesn't update remote data. + * + * @param value the value for this descriptor instance + */ + public void setValue(byte[] value) { + this.value = value; + } + + public GattDescriptor getDescriptor() { + return GattDescriptor.getDescriptor(uuid); + } + + public enum GattDescriptor { + // Descriptors + CHARACTERISTIC_EXTENDED_PROPERTIES(0x2900), + CHARACTERISTIC_USER_DESCRIPTION(0x2901), + CLIENT_CHARACTERISTIC_CONFIGURATION(0x2902), + SERVER_CHARACTERISTIC_CONFIGURATION(0x2903), + CHARACTERISTIC_PRESENTATION_FORMAT(0x2904), + CHARACTERISTIC_AGGREGATE_FORMAT(0x2905), + VALID_RANGE(0x2906), + EXTERNAL_REPORT_REFERENCE(0x2907), + REPORT_REFERENCE(0x2908), + NUMBER_OF_DIGITALS(0x2909), + TRIGGER_SETTING(0x290A); + + private static Map uuidToServiceMapping; + + private final UUID uuid; + + private GattDescriptor(long key) { + this.uuid = new UUID((key << 32) | 0x1000, BluetoothBindingConstants.BLUETOOTH_BASE_UUID); + } + + private static void initMapping() { + uuidToServiceMapping = new HashMap(); + for (GattDescriptor s : values()) { + uuidToServiceMapping.put(s.uuid, s); + } + } + + public static GattDescriptor getDescriptor(UUID uuid) { + if (uuidToServiceMapping == null) { + initMapping(); + } + return uuidToServiceMapping.get(uuid); + } + + /** + * @return the key + */ + public UUID getUUID() { + return uuid; + } + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDevice.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDevice.java new file mode 100644 index 00000000000..c29e39878e3 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDevice.java @@ -0,0 +1,552 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothConnectionStatusNotification; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothScanNotification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link BluetoothDevice} class provides a base implementation of a Bluetooth Low Energy device + * + * @author Chris Jackson - Initial contribution + * @author Kai Kreuzer - Refactored class to use Integer instead of int, fixed bugs, diverse improvements + */ +public abstract class BluetoothDevice { + + private final Logger logger = LoggerFactory.getLogger(BluetoothDevice.class); + + /** + * Enumeration of Bluetooth connection states + * + */ + public enum ConnectionState { + /** + * Device is still being discovered and is not available for use. + */ + DISCOVERING, + /** + * Device has been discovered. This is used for the initial notification that the device is available. + */ + DISCOVERED, + /** + * Device is disconnected. + */ + DISCONNECTED, + /** + * A connection is in progress. + */ + CONNECTING, + /** + * The device is connected. + */ + CONNECTED, + /** + * A disconnection is in progress. + */ + DISCONNECTING + } + + protected enum BluetoothEventType { + CONNECTION_STATE, + SCAN_RECORD, + CHARACTERISTIC_READ_COMPLETE, + CHARACTERISTIC_WRITE_COMPLETE, + CHARACTERISTIC_UPDATED, + DESCRIPTOR_UPDATED, + SERVICES_DISCOVERED + } + + /** + * Current connection state + */ + protected ConnectionState connectionState = ConnectionState.DISCOVERING; + + /** + * The adapter the device is accessed through + */ + protected final BluetoothAdapter adapter; + + /** + * Devices Bluetooth address + */ + protected final BluetoothAddress address; + + /** + * Manufacturer id + */ + protected Integer manufacturer = null; + + /** + * Device name. + *

+ * Uses the devices long name if known, otherwise the short name if known + */ + protected String name; + + /** + * List of supported services + */ + protected final Map supportedServices = new HashMap(); + + /** + * Last known RSSI + */ + protected Integer rssi = null; + + /** + * Last reported transmitter power + */ + protected Integer txPower = null; + + /** + * The event listeners will be notified of device updates + */ + private final List eventListeners = new CopyOnWriteArrayList(); + + /** + * Construct a Bluetooth device taking the Bluetooth address + * + * @param adapter + * + * @param sender + */ + public BluetoothDevice(BluetoothAdapter adapter, BluetoothAddress address) { + this.address = address; + this.adapter = adapter; + } + + /** + * Returns the the name of the Bluetooth device. + * + * @return The devices name + */ + public String getName() { + return name; + } + + /** + * Returns the physical address of the device. + * + * @return The physical address of the device + */ + public BluetoothAddress getAddress() { + return address; + } + + /** + * Returns the adapter through which the device is accessed + * + * @return The adapter through which the device is accessed + */ + public BluetoothAdapter getAdapter() { + return adapter; + } + + /** + * Sets the manufacturer id for the device + * + * @param manufacturer the manufacturer id + */ + public void setManufacturerId(int manufacturer) { + this.manufacturer = manufacturer; + } + + /** + * Returns the manufacturer ID of the device + * + * @return an integer with manufacturer ID of the device, or null if not known + */ + public Integer getManufacturerId() { + return manufacturer; + } + + /** + * Returns a {@link BluetoothService} if the requested service is supported + * + * @return the {@link BluetoothService} or null if the service is not supported. + */ + public BluetoothService getServices(UUID uuid) { + return supportedServices.get(uuid); + } + + /** + * Returns a list of supported service UUIDs + * + * @return list of supported {@link BluetoothService}s. + */ + public Collection getServices() { + return supportedServices.values(); + } + + /** + * Sets the device transmit power + * + * @param power the current transmitter power in dBm + */ + public void setTxPower(int txPower) { + this.txPower = txPower; + } + + /** + * Returns the last Transmit Power value or null if no transmit power has been received + * + * @return the last reported transmitter power value in dBm + */ + public Integer getTxPower() { + return txPower; + } + + /** + * Sets the current Receive Signal Strength Indicator (RSSI) value + * + * @param rssi the current RSSI value in dBm + * @return true if the RSSI has changed, false if it was the same as previous + */ + public boolean setRssi(int rssi) { + boolean changed = (this.rssi == null || this.rssi != rssi); + this.rssi = rssi; + + return changed; + } + + /** + * Returns the last Receive Signal Strength Indicator (RSSI) value or null if no RSSI has been received + * + * @return the last RSSI value in dBm + */ + public Integer getRssi() { + return rssi; + } + + /** + * Set the name of the device + * + * @param name a {@link String} defining the device name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Check if the device supports the specified service + * + * @param uuid the service {@link UUID} + * @return true if the service is supported + */ + public boolean supportsService(UUID uuid) { + return supportedServices.containsKey(uuid); + } + + /** + * Get the current connection state for this device + * + * @return the current {@link ConnectionState} + */ + public ConnectionState getConnectionState() { + return connectionState; + } + + /** + * Connects to a device. This is an asynchronous method. Once the connection state is updated, the + * {@link BluetoothDeviceListener.onConnectionState} method will be called with the connection state. + *

+ * If the device is already connected, this will return false. + * + * @return true if the connection process is started successfully + */ + public boolean connect() { + return false; + } + + /** + * Disconnects from a device. Once the connection state is updated, the + * {@link BluetoothDeviceListener.onConnectionState} + * method will be called with the connection state. + *

+ * If the device is not currently connected, this will return false. + * + * @return true if the disconnection process is started successfully + */ + public boolean disconnect() { + return false; + } + + /** + * Starts a discovery on a device. This will iterate through all services and characteristics to build up a view of + * the device. + *

+ * This method should be called before attempting to read or write characteristics. + * + * @return true if the discovery process is started successfully + */ + public boolean discoverServices() { + return false; + } + + /** + * Gets a Bluetooth characteristic if it is known. + *

+ * Note that this method will not search for a characteristic in the remote device if it is not known. + * You must have previously connected to the device so that the device services and characteristics can + * be retrieved. + * + * @param uuid the {@link UUID} of the characteristic to return + * @return the {@link BluetoothCharacteristic} or null if the characteristic is not found in the device + */ + public BluetoothCharacteristic getCharacteristic(UUID uuid) { + for (BluetoothService service : supportedServices.values()) { + if (service.providesCharacteristic(uuid)) { + return service.getCharacteristic(uuid); + } + } + return null; + } + + /** + * Reads a characteristic. Only a single read or write operation can be requested at once. Attempting to perform an + * operation when one is already in progress will result in subsequent calls returning false. + *

+ * This is an asynchronous method. Once the read is complete + * {@link BluetoothDeviceListener.onCharacteristicReadComplete} + * method will be called with the completion state. + *

+ * Note that {@link BluetoothDeviceListener.onCharacteristicUpdate} will be called when the read value is received. + * + * @param characteristic the {@link BluetoothCharacteristic} to read. + * @return true if the characteristic read is started successfully + */ + public boolean readCharacteristic(BluetoothCharacteristic characteristic) { + return false; + } + + /** + * Writes a characteristic. Only a single read or write operation can be requested at once. Attempting to perform an + * operation when one is already in progress will result in subsequent calls returning false. + *

+ * This is an asynchronous method. Once the write is complete + * {@link BluetoothDeviceListener.onCharacteristicWriteComplete} method will be called with the completion state. + * + * @param characteristic the {@link BluetoothCharacteristic} to read. + * @return true if the characteristic write is started successfully + */ + public boolean writeCharacteristic(BluetoothCharacteristic characteristic) { + return false; + } + + /** + * Enables notifications for a characteristic. Only a single read or write operation can be requested at once. + * Attempting to perform an operation when one is already in progress will result in subsequent calls returning + * false. + *

+ * Notifications result in CHARACTERISTIC_UPDATED events to the listeners. + * + * @param characteristic the {@link BluetoothCharacteristic} to receive notifications for. + * @return true if the characteristic notification is started successfully + */ + public boolean enableNotifications(BluetoothCharacteristic characteristic) { + return false; + } + + /** + * Disables notifications for a characteristic. Only a single read or write operation can be requested at once. + * Attempting to perform an operation when one is already in progress will result in subsequent calls returning + * false. + * + * @param characteristic the {@link BluetoothCharacteristic} to disable notifications for. + * @return true if the characteristic notification is stopped successfully + */ + public boolean disableNotifications(BluetoothCharacteristic characteristic) { + return false; + } + + /** + * Enables notifications for a descriptor. Only a single read or write operation can be requested at once. + * Attempting to perform an operation when one is already in progress will result in subsequent calls returning + * false. + *

+ * Notifications result in DESCRIPTOR_UPDATED events to the listeners. + * + * @param descriptor the {@link BluetoothDescriptor} to receive notifications for. + * @return true if the descriptor notification is started successfully + */ + public boolean enableNotifications(BluetoothDescriptor descriptor) { + return false; + } + + /** + * Disables notifications for a descriptor. Only a single read or write operation can be requested at once. + * Attempting to perform an operation when one is already in progress will result in subsequent calls returning + * false. + * + * @param descriptor the {@link BluetoothDescriptor} to disable notifications for. + * @return true if the descriptor notification is stopped successfully + */ + public boolean disableNotifications(BluetoothDescriptor descriptor) { + return false; + } + + /** + * Adds a service to the device. + * + * @param service the new {@link BluetoothService} to add + * @return true if the service was added or false if the service was already supported + */ + protected boolean addService(BluetoothService service) { + if (supportedServices.containsKey(service.getUuid())) { + return false; + } + logger.trace("Adding new service to device {}: {}", address, service); + supportedServices.put(service.getUuid(), service); + return true; + } + + /** + * Adds a list of services to the device + * + * @param uuids + */ + protected void addServices(List uuids) { + for (UUID uuid : uuids) { + // Check if we already know about this service + if (supportsService(uuid)) { + continue; + } + + // Create a new service and add it to the device + addService(new BluetoothService(uuid)); + } + } + + /** + * Gets a service based on the handle. + * This will return a service if the handle falls within the start and end handles for the service. + * + * @param handle the handle for the service + * @return the {@link BluetoothService} or null if the service was not found + */ + protected BluetoothService getServiceByHandle(int handle) { + synchronized (supportedServices) { + for (BluetoothService service : supportedServices.values()) { + if (service.getHandleStart() <= handle && service.getHandleEnd() >= handle) { + return service; + } + } + } + return null; + } + + /** + * Gets a characteristic based on the handle. + * + * @param handle the handle for the characteristic + * @return the {@link BluetoothCharacteristic} or null if the characteristic was not found + */ + protected BluetoothCharacteristic getCharacteristicByHandle(int handle) { + BluetoothService service = getServiceByHandle(handle); + if (service != null) { + return service.getCharacteristicByHandle(handle); + } + + return null; + } + + /** + * Adds a device listener + * + * @param listener the {@link BluetoothDeviceListener} to add + */ + public void addListener(BluetoothDeviceListener listener) { + if (listener == null) { + return; + } + eventListeners.add(listener); + } + + /** + * Removes a device listener + * + * @param listener the {@link BluetoothDeviceListener} to remove + */ + public void removeListener(BluetoothDeviceListener listener) { + eventListeners.remove(listener); + } + + /** + * Notify the listeners of an event + * + * @param event the {@link BluetoothEventType} of this event + * @param args an array of arguments to pass to the callback + */ + protected void notifyListeners(BluetoothEventType event, Object... args) { + for (BluetoothDeviceListener listener : eventListeners) { + try { + switch (event) { + case SCAN_RECORD: + listener.onScanRecordReceived((BluetoothScanNotification) args[0]); + break; + case CONNECTION_STATE: + listener.onConnectionStateChange((BluetoothConnectionStatusNotification) args[0]); + break; + case SERVICES_DISCOVERED: + listener.onServicesDiscovered(); + break; + case CHARACTERISTIC_READ_COMPLETE: + listener.onCharacteristicReadComplete((BluetoothCharacteristic) args[0], + (BluetoothCompletionStatus) args[1]); + break; + case CHARACTERISTIC_WRITE_COMPLETE: + listener.onCharacteristicWriteComplete((BluetoothCharacteristic) args[0], + (BluetoothCompletionStatus) args[1]); + break; + case CHARACTERISTIC_UPDATED: + listener.onCharacteristicUpdate((BluetoothCharacteristic) args[0]); + break; + case DESCRIPTOR_UPDATED: + listener.onDescriptorUpdate((BluetoothDescriptor) args[0]); + break; + } + } catch (Exception e) { + logger.error("Failed to inform listener '{}': {}", listener, e.getMessage(), e); + } + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("BluetoothDevice [address="); + builder.append(address); + builder.append(", name="); + builder.append(name); + builder.append(", rssi="); + builder.append(rssi); + builder.append(", manufacturer="); + builder.append(manufacturer); + if (BluetoothCompanyIdentifiers.get(manufacturer) != null) { + builder.append(" ("); + builder.append(BluetoothCompanyIdentifiers.get(manufacturer)); + builder.append(')'); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDeviceListener.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDeviceListener.java new file mode 100644 index 00000000000..d191c38a8bf --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDeviceListener.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothConnectionStatusNotification; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothScanNotification; + +/** + * The {@link BluetoothDeviceListener} class defines the a callback interface where devices are notified of updates to a + * BLE device + * + * @author Chris Jackson - Initial contribution + * @author Kai Kreuzer - Added descriptor updates + */ +@NonNullByDefault +public interface BluetoothDeviceListener { + + /** + * Called when a scan record is received for the device + * + * @param scanNotification the {@link BluetoothScanNotification} providing the scan packet information + */ + void onScanRecordReceived(BluetoothScanNotification scanNotification); + + /** + * Called when the connection status changes + * + * @param connectionNotification the {@link BluetoothConnectionStatusNotification} providing the updated connection + * information + */ + void onConnectionStateChange(BluetoothConnectionStatusNotification connectionNotification); + + /** + * Called when a devices services and characteristics have been completely read + */ + void onServicesDiscovered(); + + /** + * Called when a read request completes + * + * @param characteristic the {@link BluetoothCharacteristic} that has completed the read request + * @param status the {@link BluetoothCompletionStatus} of the read request + */ + void onCharacteristicReadComplete(BluetoothCharacteristic characteristic, BluetoothCompletionStatus status); + + /** + * Called when a write request completes + * + * @param characteristic the {@link BluetoothCharacteristic} that has completed the write request + * @param status the {@link BluetoothCompletionStatus} of the write request + */ + void onCharacteristicWriteComplete(BluetoothCharacteristic characteristic, BluetoothCompletionStatus status); + + /** + * Called when a characteristic value is received. Implementations should call this whenever a value + * is received from the BLE device even if there is no change to the value. + * + * @param characteristic the updated {@link BluetoothCharacteristic} + */ + void onCharacteristicUpdate(BluetoothCharacteristic characteristic); + + /** + * Called when a descriptor value is received. Implementations should call this whenever a value + * is received from the BLE device even if there is no change to the value. + * + * @param characteristic the updated {@link BluetoothCharacteristic} + */ + void onDescriptorUpdate(BluetoothDescriptor bluetoothDescriptor); +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDiscoveryListener.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDiscoveryListener.java new file mode 100644 index 00000000000..89abbb4c6f0 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothDiscoveryListener.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +/** + * This is a listener interface that is e.g. used by {@link BluetoothAdapter}s after discovering new devices. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +public interface BluetoothDiscoveryListener { + + /** + * Reports the discovery of a new device. + * + * @param device the newly discovered {@link BluetoothDevice} + */ + void deviceDiscovered(BluetoothDevice device); + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothService.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothService.java new file mode 100644 index 00000000000..fe1ff281fb0 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/BluetoothService.java @@ -0,0 +1,286 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * The {@link BluetoothCharacteristic} class defines the BLE Service. + *

+ * Services are collections of characteristics and relationships to other services that encapsulate the behavior of part + * of a device. + *

+ * https://www.bluetooth.com/specifications/gatt/services + * + * @author Chris Jackson - Initial contribution + * @author Kai Kreuzer - Cleaned up code + */ +public class BluetoothService { + + // The service UUID + private final UUID uuid; + + /** + * The start handle for this service + */ + private final int handleStart; + + /** + * The end handle for this service + */ + private final int handleEnd; + + protected int instanceId; + + /** + * Indicates if this is a primary service (true) or secondary service (false) + */ + protected boolean primaryService; + + /** + * Map of {@link BluetoothCharacteristic}s supported in this service + */ + protected final Map supportedCharacteristics = new ConcurrentHashMap<>(); + + /** + * Constructor + * + * @param uuid the uuid of the service + */ + public BluetoothService(UUID uuid) { + this(uuid, true, 0, 0); + } + + /** + * Constructor + * + * @param uuid the uuid of the service + * @param primaryService true, if this service is a primary service + */ + public BluetoothService(UUID uuid, boolean primaryService) { + this(uuid, primaryService, 0, 0); + } + + /** + * Constructor + * + * @param uuid the uuid of the service + * @param primaryService true, if this service is a primary service + * @param handleStart id of the lowest handle + * @param handleEnd id of the highest handle + */ + public BluetoothService(UUID uuid, boolean primaryService, int handleStart, int handleEnd) { + this.uuid = uuid; + this.primaryService = primaryService; + this.handleStart = handleStart; + this.handleEnd = handleEnd; + } + + /** + * Get characteristic based on {@link UUID} + * + * @return the {@link BluetoothCharacteristic} with the requested {@link UUID} + */ + public BluetoothCharacteristic getCharacteristic(UUID uuid) { + return supportedCharacteristics.get(uuid); + } + + /** + * Get list of characteristics of the service + * + * @return the list of {@link BluetoothCharacteristic}s + */ + public List getCharacteristics() { + return new ArrayList(supportedCharacteristics.values()); + } + + /** + * Return the UUID of this service + * + * @return the {@link UUID} of the service + */ + public UUID getUuid() { + return uuid; + } + + /** + * Gets the starting handle for this service + * + * @return the start handle + */ + public int getHandleStart() { + return handleStart; + } + + /** + * Gets the end handle for this service + * + * @return the end handle + */ + public int getHandleEnd() { + return handleEnd; + } + + /** + * Get the type of this service (primary/secondary) + * + * @return true if this is a primary service + */ + public boolean isPrimary() { + return primaryService; + } + + /** + * Returns the instance ID for this service + * + * @return Instance ID of this service + */ + public int getInstanceId() { + return instanceId; + } + + /** + * Checks if the service provides a specific characteristic + * + * @return true if the characteristic is provided in this service + */ + public boolean providesCharacteristic(UUID uuid) { + return supportedCharacteristics.containsKey(uuid); + } + + /** + * Add a characteristic to this service + * + * @param characteristic The characteristics to be added + * @return true, if the characteristic was added to the service + */ + public boolean addCharacteristic(BluetoothCharacteristic characteristic) { + if (supportedCharacteristics.get(characteristic.getUuid()) != null) { + return false; + } + + supportedCharacteristics.put(characteristic.getUuid(), characteristic); + characteristic.setService(this); + return true; + } + + /** + * Gets a characteristic by the handle + * + * @param handle the handle of the characteristic to return + * @return return the {@link BluetoothCharacteristic} or null if not found + */ + public BluetoothCharacteristic getCharacteristicByHandle(int handle) { + synchronized (supportedCharacteristics) { + for (BluetoothCharacteristic characteristic : supportedCharacteristics.values()) { + if (characteristic.getHandle() == handle) { + return characteristic; + } + } + } + return null; + } + + /** + * Gets the {@link GattService} for this service. This is an enum defining the available GATT services. + * + * @return the {@link GattService} relating to this service + */ + public GattService getService() { + return GattService.getService(uuid); + } + + public enum GattService { + + // List of GATT Services + ALERT_NOTIFICATION_SERVICE(0x1811), + AUTOMATION_IO(0x1815), + BATTERY_SERVICE(0x180F), + BLOOD_PRESSURE(0x1810), + BODY_COMPOSITION(0x181B), + BOND_MANAGEMENT(0x181E), + CONTINUOUS_GLUCOSE_MONITORING(0x181F), + CURRENT_TIME_SERVICE(0x1805), + CYCLING_POWER(0x1818), + CYCLING_SPEED_AND_CADENCE(0x1816), + DEVICE_INFORMATION(0x180A), + ENVIRONMENTAL_SENSING(0x181A), + GENERIC_ACCESS(0x1800), + GENERIC_ATTRIBUTE(0x1801), + GLUCOSE(0x1808), + HEALTH_THERMOMETER(0x1809), + HEART_RATE(0x180D), + HTTP_PROXY(0x1823), + HUMAN_INTERFACE_DEVICE(0x1812), + IMMEDIATE_ALERT(0x1802), + INDOOR_POSITIONING(0x1821), + INTERNET_PROTOCOL_SUPPORT(0x1820), + LINK_LOSS(0x1803L), + LOCATION_AND_NAVIGATION(0x1819), + NEXT_DST_CHANGE_SERVICE(0x1807), + PHONE_ALERT_STATUS_SERVICE(0x180E), + REFERENCE_TIME_UPDATE_SERVICE(0x1806), + RUNNING_SPEED_AND_CADENCE(0x1814), + SCAN_PARAMETERS(0x1813), + TX_POWER(0x1804), + USER_DATA(0x181C), + WEIGHT_SCALE(0x181D); + + private static Map uuidToServiceMapping; + + private UUID uuid; + + private GattService(long key) { + this.uuid = new UUID((key << 32) | 0x1000, BluetoothBindingConstants.BLUETOOTH_BASE_UUID); + } + + private static void initMapping() { + uuidToServiceMapping = new HashMap(); + for (GattService s : values()) { + uuidToServiceMapping.put(s.uuid, s); + } + } + + public static GattService getService(UUID uuid) { + if (uuidToServiceMapping == null) { + initMapping(); + } + return uuidToServiceMapping.get(uuid); + } + + /** + * @return the key + */ + public UUID getUUID() { + return uuid; + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("BluetoothService [uuid="); + builder.append(uuid); + builder.append(", handleStart="); + builder.append(handleStart); + builder.append(", handleEnd="); + builder.append(handleEnd); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/ConnectedBluetoothHandler.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/ConnectedBluetoothHandler.java new file mode 100644 index 00000000000..6ded9b8b8b1 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/ConnectedBluetoothHandler.java @@ -0,0 +1,245 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth; + +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.DefaultLocation; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.binding.bluetooth.BluetoothCharacteristic.GattCharacteristic; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDevice.ConnectionState; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothConnectionStatusNotification; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.DefaultSystemChannelTypeProvider; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; +import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is a handler for generic Bluetooth devices in connected mode, which at the same time can be used + * as a base implementation for more specific thing handlers. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +@NonNullByDefault({ DefaultLocation.PARAMETER, DefaultLocation.RETURN_TYPE, DefaultLocation.ARRAY_CONTENTS, + DefaultLocation.TYPE_ARGUMENT, DefaultLocation.TYPE_BOUND, DefaultLocation.TYPE_PARAMETER }) +public class ConnectedBluetoothHandler extends BeaconBluetoothHandler { + + private final Logger logger = LoggerFactory.getLogger(ConnectedBluetoothHandler.class); + private ScheduledFuture connectionJob; + + // internal flag for the service resolution status + protected volatile Boolean resolved = false; + + protected final Set deviceCharacteristics = new CopyOnWriteArraySet<>(); + + public ConnectedBluetoothHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + super.initialize(); + + connectionJob = scheduler.scheduleWithFixedDelay(() -> { + if (device.getConnectionState() != ConnectionState.CONNECTED) { + device.connect(); + // we do not set the Thing status here, because we will anyhow receive a call to onConnectionStateChange + } + updateRSSI(); + }, 0, 30, TimeUnit.SECONDS); + } + + @Override + public void dispose() { + if (connectionJob != null) { + connectionJob.cancel(true); + } + if (device != null) { + scheduler.submit(() -> device.disconnect()); + } + super.dispose(); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + super.handleCommand(channelUID, command); + + // Handle REFRESH + if (command == RefreshType.REFRESH) { + for (BluetoothCharacteristic characteristic : deviceCharacteristics) { + if (characteristic.getGattCharacteristic() != null + && channelUID.getId().equals(characteristic.getGattCharacteristic().name())) { + device.readCharacteristic(characteristic); + break; + } + } + } + } + + @Override + public void channelLinked(ChannelUID channelUID) { + super.channelLinked(channelUID); + } + + @Override + protected void updateStatusBasedOnRssi(boolean receivedSignal) { + // if there is no signal, we can be sure we are OFFLINE, but if there is a signal, we also have to check whether + // we are connected. + if (receivedSignal) { + if (device.getConnectionState() == ConnectionState.CONNECTED) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Device is not connected."); + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + } + } + + @Override + public void onConnectionStateChange(BluetoothConnectionStatusNotification connectionNotification) { + switch (connectionNotification.getConnectionState()) { + case DISCOVERED: + // The device is now known on the Bluetooth network, so we can do something... + scheduler.submit(() -> { + synchronized (connectionJob) { + if (device.getConnectionState() != ConnectionState.CONNECTED) { + if (!device.connect()) { + logger.debug("Error connecting to device after discovery."); + } + } + } + }); + break; + case CONNECTED: + updateStatus(ThingStatus.ONLINE); + scheduler.submit(() -> { + synchronized (resolved) { + if (!resolved) { + if (!device.discoverServices()) { + logger.debug("Error while discovering services"); + } + } + } + }); + break; + case DISCONNECTED: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + break; + default: + break; + } + } + + @Override + public void onServicesDiscovered() { + if (!resolved) { + resolved = true; + logger.debug("Service discovery completed for '{}'", address); + for (BluetoothService service : device.getServices()) { + for (BluetoothCharacteristic characteristic : service.getCharacteristics()) { + if (characteristic.getGattCharacteristic() != null) { + if (characteristic.getGattCharacteristic().equals(GattCharacteristic.BATTERY_LEVEL)) { + activateChannel(characteristic, + DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_BATTERY_LEVEL.getUID()); + continue; + } + logger.debug("Added GATT characteristic '{}'", characteristic.getGattCharacteristic().name()); + } + } + } + } + } + + @Override + public void onCharacteristicReadComplete(BluetoothCharacteristic characteristic, BluetoothCompletionStatus status) { + if (status == BluetoothCompletionStatus.SUCCESS) { + if (GattCharacteristic.BATTERY_LEVEL.equals(characteristic.getGattCharacteristic())) { + updateBatteryLevel(characteristic); + } else { + logger.debug("Characteristic {} from {} has been read - value {}", characteristic.getUuid(), address, + characteristic.getValue()); + } + } else { + logger.debug("Characteristic {} from {} has been read - ERROR", characteristic.getUuid(), address); + return; + } + } + + @Override + public void onCharacteristicWriteComplete(BluetoothCharacteristic characteristic, + BluetoothCompletionStatus status) { + logger.debug("Wrote {} to characteristic {} of device {}: {}", characteristic.getByteValue(), + characteristic.getUuid(), address, status); + } + + @Override + public void onCharacteristicUpdate(BluetoothCharacteristic characteristic) { + if (GattCharacteristic.BATTERY_LEVEL.equals(characteristic.getGattCharacteristic())) { + updateBatteryLevel(characteristic); + } + } + + protected void updateBatteryLevel(BluetoothCharacteristic characteristic) { + // the byte has values from 0-255, which we need to map to 0-100 + Double level = characteristic.getValue()[0] / 2.55; + updateState(characteristic.getGattCharacteristic().name(), new DecimalType(level.intValue())); + } + + protected void activateChannel(@Nullable BluetoothCharacteristic characteristic, ChannelTypeUID channelTypeUID, + @Nullable String name) { + if (characteristic != null) { + String channelId = name != null ? name : characteristic.getGattCharacteristic().name(); + if (channelId == null) { + // use the type id as a fallback + channelId = channelTypeUID.getId(); + } + if (getThing().getChannel(channelId) == null) { + // the channel does not exist yet, so let's add it + ThingBuilder updatedThing = editThing(); + Channel channel = ChannelBuilder.create(new ChannelUID(getThing().getUID(), channelId), "Number") + .withType(channelTypeUID).build(); + updatedThing.withChannel(channel); + updateThing(updatedThing.build()); + logger.debug("Added channel '{}' to Thing '{}'", channelId, getThing().getUID()); + } + deviceCharacteristics.add(characteristic); + device.enableNotifications(characteristic); + if (isLinked(channelId)) { + device.readCharacteristic(characteristic); + } + } else { + logger.debug("Characteristic is null - not activating any channel."); + } + } + + protected void activateChannel(@Nullable BluetoothCharacteristic characteristic, ChannelTypeUID channelTypeUID) { + activateChannel(characteristic, channelTypeUID, null); + } + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/discovery/BluetoothDiscoveryParticipant.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/discovery/BluetoothDiscoveryParticipant.java new file mode 100644 index 00000000000..cb197b1e8ba --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/discovery/BluetoothDiscoveryParticipant.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.discovery; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDevice; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; + +/** + * A {@link BluetoothDiscoveryParticipant} that is registered as a service is picked up by the BluetoothDiscoveryService + * and can thus contribute {@link DiscoveryResult}s from Bluetooth scans. + * + * @author Kai Kreuzer - Initial contribution + * + */ +@NonNullByDefault +public interface BluetoothDiscoveryParticipant { + + /** + * Defines the list of thing types that this participant can identify + * + * @return a set of thing type UIDs for which results can be created + */ + public Set getSupportedThingTypeUIDs(); + + /** + * Creates a discovery result for a Bluetooth device + * + * @param device the Bluetooth device found on the network + * + * @return the according discovery result or null, if device is not + * supported by this participant + */ + @Nullable + public DiscoveryResult createResult(BluetoothDevice device); + + /** + * Returns the thing UID for a Bluetooth device + * + * @param device the Bluetooth device + * + * @return a thing UID or null, if the device is not supported by this participant + */ + @Nullable + public ThingUID getThingUID(BluetoothDevice device); +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java new file mode 100644 index 00000000000..b153ae78d7f --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.discovery.internal; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; + +import org.eclipse.smarthome.binding.bluetooth.BluetoothAdapter; +import org.eclipse.smarthome.binding.bluetooth.BluetoothBindingConstants; +import org.eclipse.smarthome.binding.bluetooth.BluetoothCompanyIdentifiers; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDevice; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDiscoveryListener; +import org.eclipse.smarthome.binding.bluetooth.discovery.BluetoothDiscoveryParticipant; +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.UID; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link BluetoothDiscoveryService} handles searching for BLE devices. + * + * @author Chris Jackson - Initial Contribution + * @author Kai Kreuzer - Introduced BluetoothAdapters and BluetoothDiscoveryParticipants + * + */ +@Component(immediate = true, service = DiscoveryService.class, configurationPid = "discovery.bluetooth") +public class BluetoothDiscoveryService extends AbstractDiscoveryService { + + private final Logger logger = LoggerFactory.getLogger(BluetoothDiscoveryService.class); + + private final static int SEARCH_TIME = 15; + + private final Set adapters = new CopyOnWriteArraySet<>(); + private final Set participants = new CopyOnWriteArraySet<>(); + private final Map registeredListeners = new ConcurrentHashMap<>(); + + private final Set supportedThingTypes = new CopyOnWriteArraySet<>(); + + public BluetoothDiscoveryService() { + super(SEARCH_TIME); + supportedThingTypes.add(BluetoothBindingConstants.THING_TYPE_BEACON); + } + + @Override + @Activate + protected void activate(Map configProperties) { + logger.debug("Activating Bluetooth discovery service"); + super.activate(configProperties); + startScan(); + } + + @Override + @Modified + protected void modified(Map configProperties) { + super.modified(configProperties); + } + + @Override + @Deactivate + public void deactivate() { + logger.debug("Deactivating Bluetooth discovery service"); + } + + @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) + protected void addBluetoothAdapter(BluetoothAdapter adapter) { + this.adapters.add(adapter); + BluetoothDiscoveryListener listener = new BluetoothDiscoveryListener() { + + @Override + public void deviceDiscovered(BluetoothDevice device) { + BluetoothDiscoveryService.this.deviceDiscovered(adapter, device); + + } + }; + adapter.addDiscoveryListener(listener); + registeredListeners.put(adapter.getUID(), listener); + } + + protected void removeBluetoothAdapter(BluetoothAdapter adapter) { + this.adapters.remove(adapter); + adapter.removeDiscoveryListener(registeredListeners.remove(adapter.getUID())); + } + + @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) + protected void addBluetoothDiscoveryParticipant(BluetoothDiscoveryParticipant participant) { + this.participants.add(participant); + supportedThingTypes.addAll(participant.getSupportedThingTypeUIDs()); + } + + protected void removeBluetoothDiscoveryParticipant(BluetoothDiscoveryParticipant participant) { + supportedThingTypes.removeAll(participant.getSupportedThingTypeUIDs()); + this.participants.remove(participant); + } + + @Override + public Set getSupportedThingTypes() { + return supportedThingTypes; + } + + @Override + public void startScan() { + for (BluetoothAdapter adapter : adapters) { + adapter.scanStart(); + } + } + + @Override + public void stopScan() { + for (BluetoothAdapter adapter : adapters) { + adapter.scanStop(); + } + removeOlderResults(getTimestampOfLastScan()); + } + + private void deviceDiscovered(BluetoothAdapter adapter, BluetoothDevice device) { + for (BluetoothDiscoveryParticipant participant : participants) { + try { + DiscoveryResult result = participant.createResult(device); + if (result != null) { + thingDiscovered(result); + return; + } + } catch (Exception e) { + logger.error("Participant '{}' threw an exception", participant.getClass().getName(), e); + } + } + + // We did not find a thing type for this device, so let's treat it as a generic one + String label = device.getName(); + if (label == null || label.length() == 0 || label.equals(device.getAddress().toString().replace(':', '-'))) { + label = "Bluetooth Device"; + } + + Map properties = new HashMap<>(); + properties.put(BluetoothBindingConstants.CONFIGURATION_ADDRESS, device.getAddress().toString()); + Integer txPower = device.getTxPower(); + if (txPower != null && txPower > 0) { + properties.put(BluetoothBindingConstants.PROPERTY_TXPOWER, Integer.toString(txPower)); + } + String manufacturer = BluetoothCompanyIdentifiers.get(device.getManufacturerId()); + if (manufacturer != null) { + properties.put(Thing.PROPERTY_VENDOR, manufacturer); + label += " (" + manufacturer + ")"; + } else { + label += " (" + device.getAddress() + ")"; + } + + ThingUID thingUID = new ThingUID(BluetoothBindingConstants.THING_TYPE_BEACON, adapter.getUID(), + device.getAddress().toString().toLowerCase().replace(":", "")); + + // Create the discovery result and add to the inbox + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) + .withBridge(adapter.getUID()).withLabel(label).build(); + thingDiscovered(discoveryResult); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/internal/BluetoothHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/internal/BluetoothHandlerFactory.java new file mode 100644 index 00000000000..abd18c059c2 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/internal/BluetoothHandlerFactory.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.internal; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.smarthome.binding.bluetooth.BeaconBluetoothHandler; +import org.eclipse.smarthome.binding.bluetooth.BluetoothBindingConstants; +import org.eclipse.smarthome.binding.bluetooth.ConnectedBluetoothHandler; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; + +/** + * The {@link BluetoothHandlerFactory} is responsible for creating things and thing handlers. + * + * @author Kai Kreuzer - Initial contribution and API + */ +@Component(service = ThingHandlerFactory.class, immediate = true, configurationPid = "binding.bluetooth", configurationPolicy = ConfigurationPolicy.OPTIONAL) +public class BluetoothHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = new HashSet<>(); + static { + SUPPORTED_THING_TYPES_UIDS.add(BluetoothBindingConstants.THING_TYPE_BEACON); + SUPPORTED_THING_TYPES_UIDS.add(BluetoothBindingConstants.THING_TYPE_CONNECTED); + } + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(BluetoothBindingConstants.THING_TYPE_BEACON)) { + return new BeaconBluetoothHandler(thing); + } else if (thingTypeUID.equals(BluetoothBindingConstants.THING_TYPE_CONNECTED)) { + return new ConnectedBluetoothHandler(thing); + } + + return null; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothConnectionStatusNotification.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothConnectionStatusNotification.java new file mode 100644 index 00000000000..fa6968d9e6b --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothConnectionStatusNotification.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.notification; + +import org.eclipse.smarthome.binding.bluetooth.BluetoothDevice.ConnectionState; + +/** + * The {@link BluetoothConnectionStatusNotification} provides a notification of a change in the device connection state. + * + * @author Chris Jackson - Initial contribution + */ +public class BluetoothConnectionStatusNotification extends BluetoothNotification { + private ConnectionState connectionState; + + public BluetoothConnectionStatusNotification(ConnectionState connectionState) { + this.connectionState = connectionState; + } + + /** + * Returns the connection state for this notification + * + * @return the {@link ConnectionState} + */ + public ConnectionState getConnectionState() { + return connectionState; + }; +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothNotification.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothNotification.java new file mode 100644 index 00000000000..195587e7462 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothNotification.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.notification; + +import org.eclipse.smarthome.binding.bluetooth.BluetoothAddress; + +/** + * The {@link BluetoothNotification} is the base class for Bluetooth device notifications + * + * @author Chris Jackson - Initial contribution + */ +public abstract class BluetoothNotification { + protected BluetoothAddress address; + + /** + * Returns the bluetooth address for this frame + */ + public BluetoothAddress getAddress() { + return address; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothScanNotification.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothScanNotification.java new file mode 100644 index 00000000000..d53f40dd618 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth/src/main/java/org/eclipse/smarthome/binding/bluetooth/notification/BluetoothScanNotification.java @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.notification; + +/** + * The {@link BluetoothScanNotification} provides a notification of a received scan packet + * + * @author Chris Jackson - Initial contribution + */ +public class BluetoothScanNotification extends BluetoothNotification { + /** + * The receive signal strength for this beacon packet + */ + private int rssi = Integer.MIN_VALUE; + + /** + * The raw data + */ + private byte[] data = null; + + /** + * The manufacturer specific data + */ + private byte[] manufacturerData = null; + + /** + * The beacon type + */ + private BluetoothBeaconType beaconType = BluetoothBeaconType.BEACON_UNKNOWN; + + /** + * The device name + */ + private String name = new String(); + + /** + * An enumeration of basic beacon types + */ + public enum BluetoothBeaconType { + BEACON_UNKNOWN, + BEACON_ADVERTISEMENT, + BEACON_SCANRESPONSE + } + + /** + * Sets the receive signal strength RSSI value for the scan + * + * param rssi the RSSI value for the scan packet in dBm + */ + public void setRssi(int rssi) { + this.rssi = rssi; + } + + /** + * Gets the receive signal strength RSSI value for the scan + * + * @return the RSSI value for the scan packet in dBm or Integer.MIN_VALUE if no RSSI is available. + */ + public int getRssi() { + return rssi; + } + + /** + * Sets the scan packet data + * + * @param data a byte array containing the raw packet data; + */ + public void setData(byte[] data) { + this.data = data; + } + + /** + * Gets the scan packet data + * + * @return a byte array containing the data or null if none is set + */ + public byte[] getData() { + return data; + } + + /** + * Sets the scan packet manufacturer specific data + * + * @param manufacturerData a byte array containing the manufacturer specific data + */ + public void setManufacturerData(byte[] manufacturerData) { + this.manufacturerData = manufacturerData; + } + + /** + * Gets the scan packet manufacturer specific data + * + * @return a byte array containing the manufacturer specific data or null if none is set + */ + public byte[] getManufacturerData() { + return manufacturerData; + } + + /** + * Sets the beacon type for this packet + * + * @beaconType the {@link BluetoothBeaconType} for this packet + */ + public void setBeaconType(BluetoothBeaconType beaconType) { + this.beaconType = beaconType; + } + + /** + * Gets the beacon type for this packet + * + * @return the {@link BluetoothBeaconType} for this packet + */ + public BluetoothBeaconType getBeaconType() { + return beaconType; + } + + /** + * Sets the device name + * + * @param name {@link String} containing the device name + */ + public void setDeviceName(String name) { + this.name = name; + } + + /** + * Gets the device name + * + * @return {@link String} containing the device name + */ + public String getDeviceName() { + return name; + } +} diff --git a/extensions/binding/pom.xml b/extensions/binding/pom.xml index c9045c9d8a6..0972cea6c17 100644 --- a/extensions/binding/pom.xml +++ b/extensions/binding/pom.xml @@ -18,10 +18,12 @@ org.eclipse.smarthome.binding.astro org.eclipse.smarthome.binding.astro.test + org.eclipse.smarthome.binding.bluetooth + org.eclipse.smarthome.binding.bluetooth.bluez + org.eclipse.smarthome.binding.bluetooth.test org.eclipse.smarthome.binding.digitalstrom org.eclipse.smarthome.binding.dmx org.eclipse.smarthome.binding.dmx.test - org.eclipse.smarthome.binding.hue org.eclipse.smarthome.binding.hue.test org.eclipse.smarthome.binding.fsinternetradio diff --git a/features/karaf/esh-ext/src/main/feature/feature.xml b/features/karaf/esh-ext/src/main/feature/feature.xml index 8ff55a941bf..e698964bd6d 100644 --- a/features/karaf/esh-ext/src/main/feature/feature.xml +++ b/features/karaf/esh-ext/src/main/feature/feature.xml @@ -20,6 +20,17 @@ mvn:org.eclipse.smarthome.binding/org.eclipse.smarthome.binding.astro/${project.version} + + esh-base + mvn:org.eclipse.smarthome.binding/org.eclipse.smarthome.binding.bluetooth/${project.version} + + + + esh-base + esh-binding-bluetooth + mvn:org.eclipse.smarthome.binding/org.eclipse.smarthome.binding.bluetooth.bluez/${project.version} + + esh-base esh-io-transport-mdns diff --git a/features/org.eclipse.smarthome.feature.runtime.binding/feature.xml b/features/org.eclipse.smarthome.feature.runtime.binding/feature.xml index b1ff5da14f4..967d6e758e7 100644 --- a/features/org.eclipse.smarthome.feature.runtime.binding/feature.xml +++ b/features/org.eclipse.smarthome.feature.runtime.binding/feature.xml @@ -24,7 +24,7 @@ - Copyright (c) 2014-2016 by the respective copyright holders. + Copyright (c) 2014-2018 by the respective copyright holders. @@ -38,6 +38,20 @@ version="0.0.0" unpack="false"/> + + + + Date: Thu, 1 Feb 2018 13:19:57 +0100 Subject: [PATCH 2/3] added BlueGiga, Blukii & Yeelight bundles Signed-off-by: Kai Kreuzer --- .../.classpath | 7 + .../.project | 33 + .../i18n/ble.bluegiga_xx_XX.properties | 13 + .../ESH-INF/thing/bluegiga.xml | 19 + .../META-INF/MANIFEST.MF | 30 + .../NOTICE | 19 + .../OSGI-INF/.gitignore | 1 + .../README.md | 52 ++ .../build.properties | 7 + .../cfg/ble.bluegiga.cfg | 1 + .../pom.xml | 18 + .../bluegiga/BlueGigaAdapterConstants.java | 33 + .../bluegiga/BlueGigaBluetoothDevice.java | 434 +++++++++++++ .../handler/BlueGigaBridgeHandler.java | 597 ++++++++++++++++++ .../bluegiga/internal/BlueGigaCommand.java | 147 +++++ .../internal/BlueGigaEventListener.java | 27 + .../internal/BlueGigaHandlerListener.java | 32 + .../bluegiga/internal/BlueGigaPacket.java | 23 + .../bluegiga/internal/BlueGigaResponse.java | 185 ++++++ .../internal/BlueGigaResponsePackets.java | 204 ++++++ .../internal/BlueGigaSerialHandler.java | 460 ++++++++++++++ .../BlueGigaAttributeValueEvent.java | 139 ++++ .../BlueGigaAttributeWriteCommand.java | 110 ++++ .../BlueGigaAttributeWriteResponse.java | 94 +++ .../BlueGigaExecuteWriteCommand.java | 86 +++ .../BlueGigaExecuteWriteResponse.java | 93 +++ .../BlueGigaFindByTypeValueCommand.java | 147 +++++ .../BlueGigaFindByTypeValueResponse.java | 93 +++ .../BlueGigaFindInformationCommand.java | 104 +++ .../BlueGigaFindInformationFoundEvent.java | 115 ++++ .../BlueGigaFindInformationResponse.java | 93 +++ .../BlueGigaGroupFoundEvent.java | 134 ++++ .../BlueGigaIndicateConfirmCommand.java | 71 +++ .../BlueGigaIndicateConfirmResponse.java | 96 +++ .../BlueGigaIndicatedEvent.java | 93 +++ .../BlueGigaPrepareWriteCommand.java | 130 ++++ .../BlueGigaPrepareWriteResponse.java | 96 +++ .../BlueGigaProcedureCompletedEvent.java | 115 ++++ .../BlueGigaReadByGroupTypeCommand.java | 126 ++++ .../BlueGigaReadByGroupTypeResponse.java | 95 +++ .../BlueGigaReadByHandleCommand.java | 86 +++ .../BlueGigaReadByHandleResponse.java | 93 +++ .../BlueGigaReadByTypeCommand.java | 125 ++++ .../BlueGigaReadByTypeResponse.java | 94 +++ .../BlueGigaReadLongCommand.java | 89 +++ .../BlueGigaReadLongResponse.java | 96 +++ .../BlueGigaReadMultipleCommand.java | 90 +++ .../BlueGigaReadMultipleResponse.java | 92 +++ .../BlueGigaReadMultipleResponseEvent.java | 98 +++ .../BlueGigaWriteCommandCommand.java | 109 ++++ .../BlueGigaWriteCommandResponse.java | 93 +++ .../BlueGigaAttributeStatusEvent.java | 94 +++ .../attributedb/BlueGigaReadCommand.java | 87 +++ .../attributedb/BlueGigaReadResponse.java | 139 ++++ .../attributedb/BlueGigaReadTypeCommand.java | 67 ++ .../attributedb/BlueGigaReadTypeResponse.java | 117 ++++ .../BlueGigaSendAttributesCommand.java | 116 ++++ .../BlueGigaSendAttributesResponse.java | 76 +++ .../BlueGigaUserReadRequestEvent.java | 135 ++++ .../BlueGigaUserReadResponseCommand.java | 117 ++++ .../BlueGigaUserReadResponseResponse.java | 56 ++ .../BlueGigaUserWriteResponseCommand.java | 93 +++ .../BlueGigaUserWriteResponseResponse.java | 55 ++ .../attributedb/BlueGigaValueEvent.java | 158 +++++ .../attributedb/BlueGigaWriteCommand.java | 108 ++++ .../attributedb/BlueGigaWriteResponse.java | 72 +++ .../BlueGigaChannelMapGetCommand.java | 67 ++ .../BlueGigaChannelMapGetResponse.java | 98 +++ .../BlueGigaConnectionStatusEvent.java | 217 +++++++ .../connection/BlueGigaDisconnectCommand.java | 68 ++ .../BlueGigaDisconnectResponse.java | 93 +++ .../connection/BlueGigaDisconnectedEvent.java | 92 +++ .../connection/BlueGigaFeatureIndEvent.java | 96 +++ .../connection/BlueGigaGetRssiCommand.java | 68 ++ .../connection/BlueGigaGetRssiResponse.java | 92 +++ .../connection/BlueGigaGetStatusCommand.java | 67 ++ .../connection/BlueGigaGetStatusResponse.java | 71 +++ .../connection/BlueGigaUpdateCommand.java | 145 +++++ .../connection/BlueGigaUpdateResponse.java | 98 +++ .../connection/BlueGigaVersionIndEvent.java | 131 ++++ .../gap/BlueGigaConnectDirectCommand.java | 190 ++++++ .../gap/BlueGigaConnectDirectResponse.java | 102 +++ .../gap/BlueGigaConnectSelectiveCommand.java | 150 +++++ .../gap/BlueGigaConnectSelectiveResponse.java | 99 +++ .../command/gap/BlueGigaDiscoverCommand.java | 71 +++ .../command/gap/BlueGigaDiscoverResponse.java | 75 +++ .../gap/BlueGigaEndProcedureCommand.java | 46 ++ .../gap/BlueGigaEndProcedureResponse.java | 73 +++ .../gap/BlueGigaScanResponseEvent.java | 181 ++++++ .../gap/BlueGigaSetAdvDataCommand.java | 95 +++ .../gap/BlueGigaSetAdvDataResponse.java | 77 +++ .../gap/BlueGigaSetAdvParametersCommand.java | 114 ++++ .../gap/BlueGigaSetAdvParametersResponse.java | 75 +++ .../command/gap/BlueGigaSetModeCommand.java | 89 +++ .../command/gap/BlueGigaSetModeResponse.java | 74 +++ .../gap/BlueGigaSetScanParametersCommand.java | 125 ++++ .../BlueGigaSetScanParametersResponse.java | 72 +++ .../security/BlueGigaBondStatusEvent.java | 131 ++++ .../security/BlueGigaBondingFailEvent.java | 94 +++ .../BlueGigaDeleteBondingCommand.java | 71 +++ .../BlueGigaDeleteBondingResponse.java | 74 +++ .../security/BlueGigaEncryptStartCommand.java | 89 +++ .../BlueGigaEncryptStartResponse.java | 92 +++ .../security/BlueGigaGetBondsCommand.java | 47 ++ .../security/BlueGigaGetBondsResponse.java | 73 +++ .../security/BlueGigaPassKeyCommand.java | 86 +++ .../security/BlueGigaPassKeyResponse.java | 73 +++ .../security/BlueGigaPasskeyDisplayEvent.java | 92 +++ .../security/BlueGigaPasskeyRequestEvent.java | 73 +++ .../BlueGigaSetBondableModeCommand.java | 68 ++ .../BlueGigaSetBondableModeResponse.java | 50 ++ .../BlueGigaSetParametersCommand.java | 108 ++++ .../BlueGigaSetParametersResponse.java | 49 ++ .../BlueGigaWhitelistBondsCommand.java | 47 ++ .../BlueGigaWhitelistBondsResponse.java | 94 +++ .../system/BlueGigaAddressGetCommand.java | 45 ++ .../system/BlueGigaAddressGetResponse.java | 71 +++ .../command/system/BlueGigaBootEvent.java | 192 ++++++ .../BlueGigaEndpointWatermarkRxEvent.java | 94 +++ .../BlueGigaEndpointWatermarkTxEvent.java | 94 +++ .../system/BlueGigaGetConnectionsCommand.java | 45 ++ .../BlueGigaGetConnectionsResponse.java | 71 +++ .../system/BlueGigaGetCountersCommand.java | 45 ++ .../system/BlueGigaGetCountersResponse.java | 151 +++++ .../system/BlueGigaGetInfoCommand.java | 45 ++ .../system/BlueGigaGetInfoResponse.java | 191 ++++++ .../command/system/BlueGigaHelloCommand.java | 46 ++ .../command/system/BlueGigaHelloResponse.java | 50 ++ .../system/BlueGigaNoLicenseKeyEvent.java | 51 ++ .../system/BlueGigaProtocolErrorEvent.java | 75 +++ .../command/system/BlueGigaResetCommand.java | 67 ++ .../command/system/BlueGigaResetResponse.java | 49 ++ .../BlueGigaWhitelistAppendCommand.java | 73 +++ .../BlueGigaWhitelistAppendResponse.java | 76 +++ .../system/BlueGigaWhitelistClearCommand.java | 46 ++ .../BlueGigaWhitelistClearResponse.java | 50 ++ .../BlueGigaWhitelistRemoveCommand.java | 68 ++ .../BlueGigaWhitelistRemoveResponse.java | 73 +++ .../bluegiga/internal/eir/EirDataType.java | 115 ++++ .../bluegiga/internal/eir/EirFlags.java | 89 +++ .../bluegiga/internal/eir/EirPacket.java | 74 +++ .../bluegiga/internal/eir/EirRecord.java | 189 ++++++ .../enumeration/AttributeChangeReason.java | 98 +++ .../enumeration/AttributeValueType.java | 110 ++++ .../internal/enumeration/BgApiResponse.java | 478 ++++++++++++++ .../enumeration/BluetoothAddressType.java | 89 +++ .../enumeration/ConnectionStatusFlag.java | 102 +++ .../enumeration/GapConnectableMode.java | 100 +++ .../internal/enumeration/GapDiscoverMode.java | 117 ++++ .../enumeration/GapDiscoverableMode.java | 117 ++++ .../enumeration/ScanResponseType.java | 99 +++ .../enumeration/SmpIoCapabilities.java | 104 +++ .../factory/BlueGigaHandlerFactory.java | 85 +++ .../.classpath | 7 + .../.project | 33 + .../ESH-INF/thing/blukii.xml | 50 ++ .../META-INF/MANIFEST.MF | 29 + .../NOTICE | 19 + .../OSGI-INF/.gitignore | 1 + .../README.md | 52 ++ .../build.properties | 7 + .../pom.xml | 18 + .../blukii/BlukiiBindingConstants.java | 52 ++ .../blukii/handler/BlukiiHandler.java | 84 +++ .../internal/BlukiiDiscoveryParticipant.java | 81 +++ .../blukii/internal/BlukiiHandlerFactory.java | 54 ++ .../.classpath | 7 + .../.project | 33 + .../ESH-INF/thing/yeelight_blue2.xml | 38 ++ .../META-INF/MANIFEST.MF | 29 + .../NOTICE | 19 + .../OSGI-INF/.gitignore | 1 + .../README.md | 52 ++ .../build.properties | 7 + .../pom.xml | 18 + .../YeelightBlueBindingConstants.java | 37 ++ .../handler/YeelightBlueHandler.java | 167 +++++ .../internal/YeelightBlueHandlerFactory.java | 55 ++ .../YeelightDiscoveryParticipant.java | 80 +++ 179 files changed, 16213 insertions(+) create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/.classpath create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/.project create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/i18n/ble.bluegiga_xx_XX.properties create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/thing/bluegiga.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/META-INF/MANIFEST.MF create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/NOTICE create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/OSGI-INF/.gitignore create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/README.md create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/build.properties create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/cfg/ble.bluegiga.cfg create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/pom.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaEventListener.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaHandlerListener.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaPacket.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaResponsePackets.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaSerialHandler.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeValueEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationFoundEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaGroupFoundEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicatedEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaProcedureCompletedEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponseEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaAttributeStatusEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadRequestEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaValueEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaConnectionStatusEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectedEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaFeatureIndEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaVersionIndEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaScanResponseEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondStatusEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondingFailEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyDisplayEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyRequestEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaBootEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkRxEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkTxEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaNoLicenseKeyEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaProtocolErrorEvent.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveCommand.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirDataType.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirFlags.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirPacket.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirRecord.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/AttributeChangeReason.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/AttributeValueType.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/BgApiResponse.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/BluetoothAddressType.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/ConnectionStatusFlag.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapConnectableMode.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverMode.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverableMode.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/ScanResponseType.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/SmpIoCapabilities.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/factory/BlueGigaHandlerFactory.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/.classpath create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/.project create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/ESH-INF/thing/blukii.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/META-INF/MANIFEST.MF create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/NOTICE create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/OSGI-INF/.gitignore create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/README.md create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/build.properties create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/pom.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/BlukiiBindingConstants.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/handler/BlukiiHandler.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/internal/BlukiiDiscoveryParticipant.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/internal/BlukiiHandlerFactory.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/.classpath create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/.project create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/ESH-INF/thing/yeelight_blue2.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/META-INF/MANIFEST.MF create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/NOTICE create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/OSGI-INF/.gitignore create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/README.md create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/build.properties create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/pom.xml create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/YeelightBlueBindingConstants.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/handler/YeelightBlueHandler.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/internal/YeelightBlueHandlerFactory.java create mode 100644 extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/internal/YeelightDiscoveryParticipant.java diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/.classpath b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/.classpath new file mode 100644 index 00000000000..7f457fa4138 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/.project b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/.project new file mode 100644 index 00000000000..196e42d373a --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/.project @@ -0,0 +1,33 @@ + + + org.eclipse.smarthome.binding.bluetooth.bluegiga + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/i18n/ble.bluegiga_xx_XX.properties b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/i18n/ble.bluegiga_xx_XX.properties new file mode 100644 index 00000000000..204a65a57b9 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/i18n/ble.bluegiga_xx_XX.properties @@ -0,0 +1,13 @@ +# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE +# FIXME: please do not add the file to the repo if you add or change no content +# binding +binding.ble.bluegiga.name = +binding.ble.bluegiga.description = + +# thing types +thing-type.ble.bluegiga.sample.label = +thing-type.ble.bluegiga.sample.description = + +# channel types +channel-type.ble.bluegiga.sample-channel.label = +channel-type.ble.bluegiga.sample-channel.description = \ No newline at end of file diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/thing/bluegiga.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/thing/bluegiga.xml new file mode 100644 index 00000000000..01af4cdc677 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/thing/bluegiga.xml @@ -0,0 +1,19 @@ + + + + + + Serial interface to the BlueGiga dongle + + + + + serial-port + Serial Port + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..5d51b453915 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/META-INF/MANIFEST.MF @@ -0,0 +1,30 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: BlueGiga Bluetooth Adapter +Bundle-SymbolicName: org.eclipse.smarthome.binding.bluetooth.bluegiga +Bundle-Vendor: Eclipse.org/SmartHome +Bundle-Version: 0.10.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Import-Package: gnu.io, + org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.binding.bluetooth, + org.eclipse.smarthome.binding.bluetooth.bluegiga, + org.eclipse.smarthome.binding.bluetooth.bluegiga.handler, + org.eclipse.smarthome.binding.bluetooth.discovery, + org.eclipse.smarthome.binding.bluetooth.notification, + org.eclipse.smarthome.config.core, + org.eclipse.smarthome.config.discovery, + org.eclipse.smarthome.core.common, + org.eclipse.smarthome.core.common.registry, + org.eclipse.smarthome.core.library.types, + org.eclipse.smarthome.core.thing, + org.eclipse.smarthome.core.thing.binding, + org.eclipse.smarthome.core.thing.binding.builder, + org.eclipse.smarthome.core.thing.type, + org.eclipse.smarthome.core.types, + org.osgi.framework, + org.slf4j +Service-Component: OSGI-INF/*.xml +Export-Package: org.eclipse.smarthome.binding.bluetooth.bluegiga, + org.eclipse.smarthome.binding.bluetooth.bluegiga.handler +Bundle-ActivationPolicy: lazy diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/NOTICE b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/NOTICE new file mode 100644 index 00000000000..b8675cd02e8 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/NOTICE @@ -0,0 +1,19 @@ +This content is produced and maintained by the Eclipse SmartHome project. + +* Project home: https://eclipse.org/smarthome/ + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/eclipse/smarthome + +== Copyright Holders + +See the NOTICE file distributed with the source code at +https://github.com/eclipse/smarthome/blob/master/NOTICE +for detailed information regarding copyright ownership. diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/OSGI-INF/.gitignore b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/OSGI-INF/.gitignore new file mode 100644 index 00000000000..b878e882aca --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/OSGI-INF/.gitignore @@ -0,0 +1 @@ +/*.xml diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/README.md b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/README.md new file mode 100644 index 00000000000..c5e10cab543 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/README.md @@ -0,0 +1,52 @@ +# Ble.BlueGiga Binding + +_Give some details about what this binding is meant for - a protocol, system, specific device._ + +_If possible, provide some resources like pictures, a YouTube video, etc. to give an impression of what can be done with this binding. You can place such resources into a `doc` folder next to this README.md._ + +## Supported Things + +_Please describe the different supported things / devices within this section._ +_Which different types are supported, which models were tested etc.?_ +_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._ + +## Discovery + +_Describe the available auto-discovery features here. Mention for what it works and what needs to be kept in mind when using it._ + +## Binding Configuration + +_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it. In this section, you should link to this file and provide some information about the options. The file could e.g. look like:_ + +``` +# Configuration for the Philips Hue Binding +# +# Default secret key for the pairing of the Philips Hue Bridge. +# It has to be between 10-40 (alphanumeric) characters +# This may be changed by the user for security reasons. +secret=EclipseSmartHome +``` + +_Note that it is planned to generate some part of this based on the information that is available within ```ESH-INF/binding``` of your binding._ + +_If your binding does not offer any generic configurations, you can remove this section completely._ + +## Thing Configuration + +_Describe what is needed to manually configure a thing, either through the (Paper) UI or via a thing-file. This should be mainly about its mandatory and optional configuration parameters. A short example entry for a thing file can help!_ + +_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._ + +## Channels + +_Here you should provide information about available channel types, what their meaning is and how they can be used._ + +_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._ + +## Full Example + +_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._ + +## Any custom content here! + +_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/build.properties b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/build.properties new file mode 100644 index 00000000000..d3ebb210cc8 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/build.properties @@ -0,0 +1,7 @@ +source..=src/main/java/ +output..=target/classes +bin.includes = META-INF/,\ + .,\ + OSGI-INF/,\ + ESH-INF/,\ + NOTICE diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/cfg/ble.bluegiga.cfg b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/cfg/ble.bluegiga.cfg new file mode 100644 index 00000000000..8ccc12ede7c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/cfg/ble.bluegiga.cfg @@ -0,0 +1 @@ +# Ble.BlueGiga Binding Default Configuration diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/pom.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/pom.xml new file mode 100644 index 00000000000..f5e7912eb97 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/pom.xml @@ -0,0 +1,18 @@ + + + + 4.0.0 + + + org.eclipse.smarthome.binding + pom + 0.10.0-SNAPSHOT + + + org.eclipse.smarthome.binding.bluetooth.bluegiga + + Eclipse SmartHome BlueGiga Bluetooth Adapter + eclipse-plugin + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java new file mode 100644 index 00000000000..4e3f2f96811 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga; + +import org.eclipse.smarthome.binding.bluetooth.BluetoothBindingConstants; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link BlueGigaAdapterConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Chris Jackson - Initial contribution + */ +public class BlueGigaAdapterConstants { + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_BLUEGIGA = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID, + "bluegiga"); + + public static final String CONFIGURATION_PORT = "port"; + public static final String PROPERTY_LINKLAYER = "linklayer"; + public static final String PROPERTY_PROTOCOL = "protocol"; +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java new file mode 100644 index 00000000000..d07bd1b7a8b --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java @@ -0,0 +1,434 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.smarthome.binding.bluetooth.BluetoothAddress; +import org.eclipse.smarthome.binding.bluetooth.BluetoothCharacteristic; +import org.eclipse.smarthome.binding.bluetooth.BluetoothCompletionStatus; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDevice; +import org.eclipse.smarthome.binding.bluetooth.BluetoothService; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.handler.BlueGigaBridgeHandler; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaEventListener; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaAttributeValueEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaFindInformationFoundEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaGroupFoundEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaProcedureCompletedEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaConnectionStatusEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaDisconnectedEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaScanResponseEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.eir.EirDataType; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.eir.EirPacket; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BluetoothAddressType; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.ConnectionStatusFlag; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.ScanResponseType; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothConnectionStatusNotification; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothScanNotification; +import org.eclipse.smarthome.binding.bluetooth.notification.BluetoothScanNotification.BluetoothBeaconType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An extended {@link BluetoothDevice} class to handle BlueGiga specific information + * + * @author Chris Jackson - Initial contribution + */ +public class BlueGigaBluetoothDevice extends BluetoothDevice implements BlueGigaEventListener { + private final Logger logger = LoggerFactory.getLogger(BlueGigaBluetoothDevice.class); + + // BlueGiga needs to know the address type when connecting + private BluetoothAddressType addressType; + + // Used to correlate the scans so we get as much information as possible before calling the device "discovered" + private final Set scanResponses = new HashSet(); + + // The dongle handler + private final BlueGigaBridgeHandler bgHandler; + + // An enum to use in the state machine for interacting with the device + private enum BlueGigaProcedure { + NONE, + GET_SERVICES, + GET_CHARACTERISTICS, + CHARACTERISTIC_READ, + CHARACTERISTIC_WRITE; + } + + private BlueGigaProcedure procedureProgress = BlueGigaProcedure.NONE; + + // Somewhere to remember what characteristic we're working on + private BluetoothCharacteristic procedureCharacteristic = null; + + // The connection handle if the device is connected + private int connection = -1; + + /** + * Creates a new {@link BlueGigaBluetoothDevice} which extends {@link BluetoothDevice} for the BlueGiga + * implementation + * + * @param bgHandler the {@link BlueGigaBridgeHandler} that provides the link to the dongle + * @param address the {@link BluetoothAddress} for this device + * @param addressType the {@link BluetoothAddressType} of this device + */ + public BlueGigaBluetoothDevice(BlueGigaBridgeHandler bgHandler, BluetoothAddress address, + BluetoothAddressType addressType) { + super(bgHandler, address); + + logger.debug("Creating new BlueGiga device {}", address); + + this.bgHandler = bgHandler; + this.addressType = addressType; + + bgHandler.addEventListener(this); + } + + @Override + public boolean connect() { + if (connection != -1) { + // We're already connected + return false; + } + + if (bgHandler.bgConnect(address, addressType)) { + connectionState = ConnectionState.CONNECTING; + return true; + } else { + connectionState = ConnectionState.DISCONNECTED; + return false; + } + } + + @Override + public boolean disconnect() { + if (connection == -1) { + // We're already disconnected + return false; + } + + return bgHandler.bgDisconnect(connection); + } + + @Override + public boolean discoverServices() { + // Start by requesting all the services + procedureProgress = BlueGigaProcedure.GET_SERVICES; + return bgHandler.bgFindPrimaryServices(connection); + } + + @Override + public boolean readCharacteristic(BluetoothCharacteristic characteristic) { + if (characteristic == null || characteristic.getHandle() == 0) { + return false; + } + + if (!bgHandler.bgReadCharacteristic(connection, characteristic.getHandle())) { + return false; + } + + if (procedureProgress != BlueGigaProcedure.NONE) { + return false; + } + + procedureProgress = BlueGigaProcedure.CHARACTERISTIC_READ; + procedureCharacteristic = characteristic; + + return true; + } + + @Override + public boolean writeCharacteristic(BluetoothCharacteristic characteristic) { + if (characteristic == null || characteristic.getHandle() == 0) { + return false; + } + + if (procedureProgress != BlueGigaProcedure.NONE) { + return false; + } + + if (!bgHandler.bgWriteCharacteristic(connection, characteristic.getHandle(), characteristic.getValue())) { + return false; + } + + procedureProgress = BlueGigaProcedure.CHARACTERISTIC_WRITE; + procedureCharacteristic = characteristic; + + return true; + } + + @Override + public void bluegigaEventReceived(BlueGigaResponse event) { + if (event instanceof BlueGigaScanResponseEvent) { + BlueGigaScanResponseEvent scanEvent = (BlueGigaScanResponseEvent) event; + + // Check if this is addressed to this device + if (!address.equals(new BluetoothAddress(scanEvent.getSender()))) { + return; + } + + // Set device properties + rssi = scanEvent.getRssi(); + addressType = scanEvent.getAddressType(); + + byte[] manufacturerData = null; + + // If the packet contains data, then process it and add anything relevant to the device... + if (scanEvent.getData() != null) { + EirPacket eir = new EirPacket(scanEvent.getData()); + for (EirDataType record : eir.getRecords().keySet()) { + switch (record) { + case EIR_FLAGS: + break; + case EIR_MANUFACTURER_SPECIFIC: + manufacturerData = (byte[]) eir.getRecord(EirDataType.EIR_MANUFACTURER_SPECIFIC); + if (manufacturerData.length > 2) { + int id = manufacturerData[0] + (manufacturerData[1] << 8); + manufacturer = id; + } + break; + case EIR_NAME_LONG: + case EIR_NAME_SHORT: + name = (String) eir.getRecord(record); + break; + case EIR_SLAVEINTERVALRANGE: + break; + case EIR_SVC_DATA_UUID128: + break; + case EIR_SVC_DATA_UUID16: + break; + case EIR_SVC_DATA_UUID32: + break; + case EIR_SVC_UUID128_INCOMPLETE: + case EIR_SVC_UUID16_COMPLETE: + case EIR_SVC_UUID16_INCOMPLETE: + case EIR_SVC_UUID32_COMPLETE: + case EIR_SVC_UUID32_INCOMPLETE: + case EIR_SVC_UUID128_COMPLETE: + // addServices((List) eir.getRecord(record)); + break; + case EIR_TXPOWER: + txPower = (int) eir.getRecord(EirDataType.EIR_TXPOWER); + break; + default: + break; + } + } + } + + if (connectionState == ConnectionState.DISCOVERING) { + // We want to wait for an advertisement and a scan response before we call this discovered. + // The intention is to gather a reasonable amount of data about the device given devices send + // different data in different packets... + // Note that this is possible a bit arbitrary and may be refined later. + scanResponses.add(scanEvent.getPacketType()); + + if ((scanResponses.contains(ScanResponseType.CONNECTABLE_ADVERTISEMENT) + || scanResponses.contains(ScanResponseType.DISCOVERABLE_ADVERTISEMENT) + || scanResponses.contains(ScanResponseType.NON_CONNECTABLE_ADVERTISEMENT)) + && scanResponses.contains(ScanResponseType.SCAN_RESPONSE)) { + // Set our state to disconnected + connectionState = ConnectionState.DISCONNECTED; + connection = -1; + + // But notify listeners that the state is now DISCOVERED + notifyListeners(BluetoothEventType.CONNECTION_STATE, + new BluetoothConnectionStatusNotification(ConnectionState.DISCOVERED)); + + // Notify the bridge - for inbox notifications + bgHandler.deviceDiscovered(this); + } + } + + // Notify listeners of all scan records - for RSSI, beacon processing (etc) + BluetoothScanNotification scanNotification = new BluetoothScanNotification(); + scanNotification.setRssi(scanEvent.getRssi()); + + switch (scanEvent.getPacketType()) { + case CONNECTABLE_ADVERTISEMENT: + case DISCOVERABLE_ADVERTISEMENT: + case NON_CONNECTABLE_ADVERTISEMENT: + scanNotification.setBeaconType(BluetoothBeaconType.BEACON_ADVERTISEMENT); + break; + case SCAN_RESPONSE: + scanNotification.setBeaconType(BluetoothBeaconType.BEACON_SCANRESPONSE); + break; + default: + break; + } + + if (manufacturerData != null) { + + scanNotification.setManufacturerData(manufacturerData); + } + + notifyListeners(BluetoothEventType.SCAN_RECORD, scanNotification); + + return; + } + + if (event instanceof BlueGigaGroupFoundEvent) { + // A Service has been discovered + BlueGigaGroupFoundEvent serviceEvent = (BlueGigaGroupFoundEvent) event; + + // If this is not our connection handle then ignore. + if (connection != serviceEvent.getConnection()) { + return; + } + + logger.trace("BlueGiga Group: {} svcs={}", this, supportedServices); + + BluetoothService service = new BluetoothService(serviceEvent.getUuid(), true, serviceEvent.getStart(), + serviceEvent.getEnd()); + addService(service); + + return; + } + + if (event instanceof BlueGigaFindInformationFoundEvent) { + // A Characteristic has been discovered + BlueGigaFindInformationFoundEvent infoEvent = (BlueGigaFindInformationFoundEvent) event; + + // If this is not our connection handle then ignore. + if (connection != infoEvent.getConnection()) { + return; + } + + logger.trace("BlueGiga FindInfo: {} svcs={}", this, supportedServices); + + BluetoothCharacteristic characteristic = new BluetoothCharacteristic(infoEvent.getUuid(), + infoEvent.getChrHandle()); + + BluetoothService service = getServiceByHandle(characteristic.getHandle()); + if (service == null) { + logger.debug("BlueGiga: Unable to find service for handle {}", characteristic.getHandle()); + return; + } + characteristic.setService(service); + service.addCharacteristic(characteristic); + + return; + } + + if (event instanceof BlueGigaProcedureCompletedEvent) { + BlueGigaProcedureCompletedEvent completedEvent = (BlueGigaProcedureCompletedEvent) event; + + // If this is not our connection handle then ignore. + if (connection != completedEvent.getConnection()) { + return; + } + + if (procedureProgress == null) { + logger.debug("BlueGiga procedure completed but procedure is null with connection {}, address {}", + connection, address); + return; + } + + // The current procedure is now complete - move on... + switch (procedureProgress) { + case GET_SERVICES: + // We've downloaded all services, now get the characteristics + procedureProgress = BlueGigaProcedure.GET_CHARACTERISTICS; + bgHandler.bgFindCharacteristics(connection); + break; + case GET_CHARACTERISTICS: + // We've downloaded all characteristics + procedureProgress = BlueGigaProcedure.NONE; + notifyListeners(BluetoothEventType.SERVICES_DISCOVERED); + break; + case CHARACTERISTIC_READ: + // The read failed + notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, procedureCharacteristic, + BluetoothCompletionStatus.ERROR); + procedureProgress = BlueGigaProcedure.NONE; + procedureCharacteristic = null; + break; + case CHARACTERISTIC_WRITE: + // The write completed - failure or success + BluetoothCompletionStatus result = completedEvent.getResult() == BgApiResponse.SUCCESS + ? BluetoothCompletionStatus.SUCCESS + : BluetoothCompletionStatus.ERROR; + notifyListeners(BluetoothEventType.CHARACTERISTIC_WRITE_COMPLETE, procedureCharacteristic, result); + procedureProgress = BlueGigaProcedure.NONE; + procedureCharacteristic = null; + break; + default: + break; + } + + return; + } + + if (event instanceof BlueGigaConnectionStatusEvent) { + BlueGigaConnectionStatusEvent connectionEvent = (BlueGigaConnectionStatusEvent) event; + + // Check if this is addressed to this device + if (!address.equals(new BluetoothAddress(connectionEvent.getAddress()))) { + return; + } + + // If we're connected, then remember the connection handle + if (connectionEvent.getFlags().contains(ConnectionStatusFlag.CONNECTION_CONNECTED)) { + connectionState = ConnectionState.CONNECTED; + connection = connectionEvent.getConnection(); + } + + if (connectionEvent.getFlags().contains(ConnectionStatusFlag.CONNECTION_CONNECTED)) { + notifyListeners(BluetoothEventType.CONNECTION_STATE, + new BluetoothConnectionStatusNotification(connectionState)); + } + + return; + } + + if (event instanceof BlueGigaDisconnectedEvent) { + BlueGigaDisconnectedEvent disconnectedEvent = (BlueGigaDisconnectedEvent) event; + + // If this is not our connection handle then ignore. + if (connection != disconnectedEvent.getConnection()) { + return; + } + + connectionState = ConnectionState.DISCONNECTED; + connection = -1; + notifyListeners(BluetoothEventType.CONNECTION_STATE, + new BluetoothConnectionStatusNotification(connectionState)); + + return; + } + + if (event instanceof BlueGigaAttributeValueEvent) { + // A read request has completed - update the characteristic + BlueGigaAttributeValueEvent valueEvent = (BlueGigaAttributeValueEvent) event; + + BluetoothCharacteristic characteristic = getCharacteristicByHandle(valueEvent.getAttHandle()); + if (characteristic == null) { + logger.debug("BlueGiga didn't find characteristic for event {}", event); + } else { + // If this is the characteristic we were reading, then send a read completion + if (procedureProgress == BlueGigaProcedure.CHARACTERISTIC_READ && procedureCharacteristic != null + && procedureCharacteristic.getHandle() == valueEvent.getAttHandle()) { + procedureProgress = BlueGigaProcedure.NONE; + procedureCharacteristic = null; + notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic, + BluetoothCompletionStatus.SUCCESS); + } + + // Notify the user of the updated value + notifyListeners(BluetoothEventType.CHARACTERISTIC_UPDATED, procedureCharacteristic); + } + } + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java new file mode 100644 index 00000000000..28c218b6cba --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java @@ -0,0 +1,597 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.handler; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; + +import org.eclipse.jdt.annotation.DefaultLocation; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.binding.bluetooth.BluetoothAdapter; +import org.eclipse.smarthome.binding.bluetooth.BluetoothAddress; +import org.eclipse.smarthome.binding.bluetooth.BluetoothBindingConstants; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDevice; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDeviceListener; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDiscoveryListener; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.BlueGigaAdapterConstants; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.BlueGigaBluetoothDevice; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaEventListener; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaHandlerListener; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaSerialHandler; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaAttributeWriteCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaAttributeWriteResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaFindInformationCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaFindInformationResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByGroupTypeCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByGroupTypeResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByHandleCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByHandleResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaConnectionStatusEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaDisconnectCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaDisconnectResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaDisconnectedEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaConnectDirectCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaConnectDirectResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaDiscoverCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaEndProcedureCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaEndProcedureResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaScanResponseEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetModeCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetModeResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetScanParametersCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaAddressGetCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaAddressGetResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetConnectionsCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetConnectionsResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetInfoCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetInfoResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BluetoothAddressType; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.GapConnectableMode; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.GapDiscoverMode; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.GapDiscoverableMode; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.eclipse.smarthome.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import gnu.io.CommPort; +import gnu.io.CommPortIdentifier; +import gnu.io.NoSuchPortException; +import gnu.io.PortInUseException; +import gnu.io.SerialPort; +import gnu.io.UnsupportedCommOperationException; + +/** + * The {@link BlueGigaBridgeHandler} is responsible for interfacing to the BlueGiga Bluetooth adapter. + * It provides a private interface for {@link BlueGigaBluetoothDevice}s to access the dongle and provides top + * level adaptor functionality for scanning and arbitration. + *

+ * The handler provides the serial interface to the dongle via the BlueGiga BG-API library. + *

+ * In the BlueGiga dongle, we leave scanning enabled most of the time. Normally, it's just passive scanning, and active + * scanning is enabled when we want to include new devices. Passive scanning is enough for us to receive beacons etc + * that are transmitted periodically, and active scanning will get more information which may be useful when we are + * including new devices. + * + * @author Chris Jackson - Initial contribution + * @author Kai Kreuzer - Made handler implement BlueGigaHandlerListener + */ +@NonNullByDefault({ DefaultLocation.PARAMETER, DefaultLocation.RETURN_TYPE, DefaultLocation.ARRAY_CONTENTS, + DefaultLocation.TYPE_ARGUMENT, DefaultLocation.TYPE_BOUND, DefaultLocation.TYPE_PARAMETER }) +public class BlueGigaBridgeHandler extends BaseBridgeHandler + implements BluetoothAdapter, BlueGigaEventListener, BlueGigaHandlerListener { + + private final Logger logger = LoggerFactory.getLogger(BlueGigaBridgeHandler.class); + + // The serial port. + private SerialPort serialPort; + + // The serial port input stream. + private InputStream inputStream; + + // The serial port output stream. + private OutputStream outputStream; + + // The BlueGiga API handler + private BlueGigaSerialHandler bgHandler; + + // The maximum number of connections this interface supports + private int maxConnections = 0; + + private final int passiveScanInterval = 0x40; + private final int passiveScanWindow = 0x08; + + private final int activeScanInterval = 0x40; + private final int activeScanWindow = 0x20; + + // Our BT address + private BluetoothAddress address; + + // Map of Bluetooth devices known to this bridge. + // This is all devices we have heard on the network - not just things bound to the bridge + private final Map devices = new ConcurrentHashMap<>(); + + // Map of open connections + private final Map connections = new ConcurrentHashMap<>(); + + // Set of discovery listeners + protected final Set discoveryListeners = new CopyOnWriteArraySet<>(); + + // List of device listeners + protected final ConcurrentHashMap deviceListeners = new ConcurrentHashMap<>(); + + public BlueGigaBridgeHandler(Bridge bridge) { + super(bridge); + } + + @Override + public ThingUID getUID() { + // being a BluetoothAdapter, we use the UID of our bridge + return getThing().getUID(); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // No commands supported for the bridge + } + + @Override + public void initialize() { + final String portId = (String) getConfig().get(BlueGigaAdapterConstants.CONFIGURATION_PORT); + + if (portId == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Serial port must be configured!"); + return; + } + openSerialPort(portId, 115200); + bgHandler = new BlueGigaSerialHandler(inputStream, outputStream); + // Create and send the reset command to the dongle + bgHandler.addEventListener(this); + bgHandler.addHandlerListener(this); + + updateStatus(ThingStatus.UNKNOWN); + + scheduler.submit(() -> { + // Stop any procedures that are running + bgStopProcedure(); + + // Close all transactions + BlueGigaCommand command; // = new BlueGigaResetCommand(); + command = new BlueGigaGetConnectionsCommand(); + BlueGigaGetConnectionsResponse connectionsResponse = (BlueGigaGetConnectionsResponse) bgHandler + .sendTransaction(command); + if (connectionsResponse != null) { + maxConnections = connectionsResponse.getMaxconn(); + } + + // Close all connections so we start from a known position + for (int connection = 0; connection < maxConnections; connection++) { + bgDisconnect(connection); + } + + // Get our Bluetooth address + command = new BlueGigaAddressGetCommand(); + BlueGigaAddressGetResponse addressResponse = (BlueGigaAddressGetResponse) bgHandler + .sendTransaction(command); + if (addressResponse != null) { + address = new BluetoothAddress(addressResponse.getAddress()); + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE); + } + + command = new BlueGigaGetInfoCommand(); + BlueGigaGetInfoResponse infoResponse = (BlueGigaGetInfoResponse) bgHandler.sendTransaction(command); + + // Set mode to non-discoverable etc. + // Not doing this will cause connection failures later + bgSetMode(); + + // Start passive scan + bgStartScanning(false, passiveScanInterval, passiveScanWindow); + + Map properties = editProperties(); + properties.put(BluetoothBindingConstants.PROPERTY_MAXCONNECTIONS, Integer.toString(maxConnections)); + properties.put(Thing.PROPERTY_FIRMWARE_VERSION, + String.format("%d.%d", infoResponse.getMajor(), infoResponse.getMinor())); + properties.put(Thing.PROPERTY_HARDWARE_VERSION, Integer.toString(infoResponse.getHardware())); + properties.put(BlueGigaAdapterConstants.PROPERTY_PROTOCOL, + Integer.toString(infoResponse.getProtocolVersion())); + properties.put(BlueGigaAdapterConstants.PROPERTY_LINKLAYER, Integer.toString(infoResponse.getLlVersion())); + updateProperties(properties); + }); + } + + @Override + public void dispose() { + if (bgHandler != null) { + bgHandler.removeEventListener(this); + bgHandler.removeHandlerListener(this); + } + closeSerialPort(); + } + + private void openSerialPort(final String serialPortName, int baudRate) { + logger.debug("Connecting to serial port '{}'", serialPortName); + try { + CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(serialPortName); + CommPort commPort = portIdentifier.open("org.openhab.binding.zigbee", 2000); + serialPort = (gnu.io.SerialPort) commPort; + serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, + gnu.io.SerialPort.PARITY_NONE); + serialPort.setFlowControlMode(gnu.io.SerialPort.FLOWCONTROL_RTSCTS_OUT); + + ((CommPort) serialPort).enableReceiveThreshold(1); + serialPort.enableReceiveTimeout(2000); + + // RXTX serial port library causes high CPU load + // Start event listener, which will just sleep and slow down event loop + serialPort.notifyOnDataAvailable(true); + + logger.info("Connected to serial port '{}'.", serialPortName); + } catch (NoSuchPortException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Port does not exist"); + return; + } catch (PortInUseException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, + "Serial Error: Port in use"); + return; + } catch (UnsupportedCommOperationException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, + "Serial Error: Unsupported operation"); + return; + } + + try { + inputStream = serialPort.getInputStream(); + outputStream = serialPort.getOutputStream(); + } catch (IOException e) { + logger.error("Error getting serial streams", e); + } + + return; + } + + private void closeSerialPort() { + try { + if (serialPort != null) { + serialPort.enableReceiveTimeout(1); + + inputStream.close(); + outputStream.flush(); + outputStream.close(); + + serialPort.close(); + + logger.debug("Closed serial port closed.", serialPort.getName()); + + serialPort = null; + inputStream = null; + outputStream = null; + } + } catch (Exception e) { + logger.error("Error closing serial port.", e); + } + } + + @SuppressWarnings({ "unused", "null" }) + @Override + public void bluegigaEventReceived(@Nullable BlueGigaResponse event) { + if (event instanceof BlueGigaScanResponseEvent) { + BlueGigaScanResponseEvent scanEvent = (BlueGigaScanResponseEvent) event; + + // We use the scan event to add any devices we hear to the devices list + // The device gets created, and then manages itself for discovery etc. + BluetoothAddress sender = new BluetoothAddress(scanEvent.getSender()); + BlueGigaBluetoothDevice device; + if (devices.get(sender) == null) { + logger.debug("BlueGiga adding new device to adaptor {}: {}", address, sender); + device = new BlueGigaBluetoothDevice(this, new BluetoothAddress(scanEvent.getSender()), + scanEvent.getAddressType()); + devices.put(sender, device); + deviceDiscovered(device); + } + + return; + } + + if (event instanceof BlueGigaConnectionStatusEvent) { + BlueGigaConnectionStatusEvent connectionEvent = (BlueGigaConnectionStatusEvent) event; + + connections.put(connectionEvent.getConnection(), new BluetoothAddress(connectionEvent.getAddress())); + } + + if (event instanceof BlueGigaDisconnectedEvent) { + BlueGigaDisconnectedEvent disconnectedEvent = (BlueGigaDisconnectedEvent) event; + + connections.remove(disconnectedEvent.getConnection()); + } + } + + @Override + public void scanStart() { + // Stop the passive scan + bgStopProcedure(); + + // Start a active scan + bgStartScanning(true, activeScanInterval, activeScanWindow); + + for (BluetoothDevice device : devices.values()) { + deviceDiscovered(device); + } + } + + @Override + public void scanStop() { + // Stop the active scan + bgStopProcedure(); + + // Start a passive scan + bgStartScanning(false, passiveScanInterval, passiveScanWindow); + } + + @Override + public BluetoothAddress getAddress() { + if (address != null) { + return address; + } else { + throw new IllegalStateException("Adapter has not been initialized yet!"); + } + } + + @SuppressWarnings({ "null", "unused" }) + @Override + public BluetoothDevice getDevice(BluetoothAddress address) { + BluetoothDevice device = devices.get(address); + if (device == null) { + // This method always needs to return a device, even if we don't currently know about it. + device = new BlueGigaBluetoothDevice(this, address, BluetoothAddressType.UNKNOWN); + devices.put(address, device); + } + return device; + } + + /* + * The following methods provide adaptor level functions for the BlueGiga interface. Typically these methods + * are used by the device but are provided in the adapter to allow common knowledge and to support conflict + * resolution. + */ + + /** + * Connects to a device. + *

+ * If the device is already connected, or the attempt to connect failed, then we return false. If we have reached + * the maximum number of connections supported by this dongle, then we return false. + * + * @param address the device {@link BluetoothAddress} to connect to + * @param addressType the {@link BluetoothAddressType} of the device + * @return true if the connection was started + */ + public boolean bgConnect(BluetoothAddress address, BluetoothAddressType addressType) { + // Check the connection to make sure we're not already connected to this device + if (connections.containsValue(address)) { + return false; + } + + // FIXME: When getting here, I always found all connections to be already taken and thus the code never + // proceeded. Relaxing this condition did not do any obvious harm, but now guaranteed that the services are + // queried from the device. + if (connections.size() == maxConnections + 1) { + logger.debug("BlueGiga: Attempt to connect to {} but no connections available.", address); + return false; + } + + bgSetMode(); + + // Connect... + int connIntervalMin = 60; + int connIntervalMax = 100; + int latency = 0; + int timeout = 100; + + BlueGigaConnectDirectCommand connect = new BlueGigaConnectDirectCommand(); + connect.setAddress(address.toString()); + connect.setAddrType(addressType); + connect.setConnIntervalMin(connIntervalMin); + connect.setConnIntervalMax(connIntervalMax); + connect.setLatency(latency); + connect.setTimeout(timeout); + BlueGigaConnectDirectResponse connectResponse = (BlueGigaConnectDirectResponse) bgHandler + .sendTransaction(connect); + if (connectResponse.getResult() != BgApiResponse.SUCCESS) { + return false; + } + + return true; + } + + /** + * Close a connection using {@link BlueGigaDisconnectCommand} + * + * @param connectionHandle + * @return + */ + public boolean bgDisconnect(int connectionHandle) { + BlueGigaDisconnectCommand command = new BlueGigaDisconnectCommand(); + command.setConnection(connectionHandle); + BlueGigaDisconnectResponse response = (BlueGigaDisconnectResponse) bgHandler.sendTransaction(command); + + return response.getResult() == BgApiResponse.SUCCESS; + } + + /** + * Device discovered. This simply passes the discover information to the discovery service for processing. + */ + public void deviceDiscovered(BluetoothDevice device) { + for (BluetoothDiscoveryListener listener : discoveryListeners) { + listener.deviceDiscovered(device); + } + } + + /** + * Start a read of all primary services using {@link BlueGigaReadByGroupTypeCommand} + * + * @param connectionHandle + * @return true if successful + */ + public boolean bgFindPrimaryServices(int connectionHandle) { + logger.debug("BlueGiga FindPrimary: connection {}", connectionHandle); + BlueGigaReadByGroupTypeCommand command = new BlueGigaReadByGroupTypeCommand(); + command.setConnection(connectionHandle); + command.setStart(1); + command.setEnd(65535); + command.setUuid(UUID.fromString("00002800-0000-0000-0000-000000000000")); + BlueGigaReadByGroupTypeResponse response = (BlueGigaReadByGroupTypeResponse) bgHandler.sendTransaction(command); + return response.getResult() == BgApiResponse.SUCCESS; + } + + /** + * Start a read of all characteristics using {@link BlueGigaFindInformationCommand} + * + * @param connectionHandle + * @return true if successful + */ + public boolean bgFindCharacteristics(int connectionHandle) { + logger.debug("BlueGiga Find: connection {}", connectionHandle); + BlueGigaFindInformationCommand command = new BlueGigaFindInformationCommand(); + command.setConnection(connectionHandle); + command.setStart(1); + command.setEnd(65535); + BlueGigaFindInformationResponse response = (BlueGigaFindInformationResponse) bgHandler.sendTransaction(command); + + return response.getResult() == BgApiResponse.SUCCESS; + } + + /** + * Read a characteristic using {@link BlueGigaReadByHandleCommand} + * + * @param connectionHandle + * @param handle + * @return true if successful + */ + public boolean bgReadCharacteristic(int connectionHandle, int handle) { + logger.debug("BlueGiga Read: connection {}, handle {}", connectionHandle, handle); + BlueGigaReadByHandleCommand command = new BlueGigaReadByHandleCommand(); + command.setConnection(connectionHandle); + command.setChrHandle(handle); + BlueGigaReadByHandleResponse response = (BlueGigaReadByHandleResponse) bgHandler.sendTransaction(command); + + return response.getResult() == BgApiResponse.SUCCESS; + } + + /** + * Write a characteristic using {@link BlueGigaAttributeWriteCommand} + * + * @param connectionHandle + * @param handle + * @param value + * @return true if successful + */ + public boolean bgWriteCharacteristic(int connectionHandle, int handle, int[] value) { + logger.debug("BlueGiga Write: connection {}, handle {}", connectionHandle, handle); + BlueGigaAttributeWriteCommand command = new BlueGigaAttributeWriteCommand(); + command.setConnection(connectionHandle); + command.setAttHandle(handle); + command.setData(value); + BlueGigaAttributeWriteResponse response = (BlueGigaAttributeWriteResponse) bgHandler.sendTransaction(command); + + return response.getResult() == BgApiResponse.SUCCESS; + } + + /* + * The following methods are private methods for handling the BlueGiga protocol + */ + private boolean bgStopProcedure() { + BlueGigaCommand command = new BlueGigaEndProcedureCommand(); + BlueGigaEndProcedureResponse response = (BlueGigaEndProcedureResponse) bgHandler.sendTransaction(command); + + return response.getResult() == BgApiResponse.SUCCESS; + } + + private boolean bgSetMode() { + BlueGigaSetModeCommand command = new BlueGigaSetModeCommand(); + command.setConnect(GapConnectableMode.GAP_NON_CONNECTABLE); + command.setDiscover(GapDiscoverableMode.GAP_NON_DISCOVERABLE); + BlueGigaSetModeResponse response = (BlueGigaSetModeResponse) bgHandler.sendTransaction(command); + + return response.getResult() == BgApiResponse.SUCCESS; + } + + /** + * Starts scanning on the dongle + * + * @param active true for active scanning + */ + private void bgStartScanning(boolean active, int interval, int window) { + BlueGigaSetScanParametersCommand scanCommand = new BlueGigaSetScanParametersCommand(); + scanCommand.setActiveScanning(active); + scanCommand.setScanInterval(interval); + scanCommand.setScanWindow(window); + bgHandler.sendTransaction(scanCommand); + + BlueGigaDiscoverCommand discoverCommand = new BlueGigaDiscoverCommand(); + discoverCommand.setMode(GapDiscoverMode.GAP_DISCOVER_OBSERVATION); + bgHandler.sendTransaction(discoverCommand); + } + + /** + * Add an event listener for the BlueGiga events + * + * @param listener the {@link BlueGigaEventListener} to add + */ + public void addEventListener(BlueGigaEventListener listener) { + bgHandler.addEventListener(listener); + } + + /** + * Remove an event listener for the BlueGiga events + * + * @param listener the {@link BlueGigaEventListener} to remove + */ + public void removeEventListener(BlueGigaEventListener listener) { + bgHandler.removeEventListener(listener); + } + + @Override + public void addDiscoveryListener(@NonNull BluetoothDiscoveryListener listener) { + discoveryListeners.add(listener); + } + + @Override + public void removeDiscoveryListener(@Nullable BluetoothDiscoveryListener listener) { + discoveryListeners.remove(listener); + } + + @Override + public void bluegigaClosed(Exception reason) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, reason.getMessage()); + } + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaCommand.java new file mode 100644 index 00000000000..75dc9c7654c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaCommand.java @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal; + +import java.util.Arrays; +import java.util.UUID; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BluetoothAddressType; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.GapConnectableMode; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.GapDiscoverMode; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.GapDiscoverableMode; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.SmpIoCapabilities; + +/** + * Abstract base class for all commands. This provides the serialisation methods for converting parameters from Java + * classes to wire data. + * + * @author Chris Jackson + * + */ +public abstract class BlueGigaCommand extends BlueGigaPacket { + protected int[] buffer = new int[131]; + protected int length = 0; + + protected void serializeHeader(int classId, int commandId) { + // Octet 0 7 1 bit Message Type (MT) 0: Command + // -------6:3 4 bits Technology Type (TT) 0000: Smart Bluetooth + // -------2:0 3 bits Length High (LH) Payload length (high bits) + buffer[length++] = 0; + + // Octet 1 7:0 8 bits Length Low (LL) Payload length (low bits) + buffer[length++] = 0; + + // Octet 2 7:0 8 bits Class ID (CID) Command class ID + buffer[length++] = classId; + + // Octet 3 7:0 8 bits Command ID (CMD) Command ID + buffer[length++] = commandId; + } + + /** + * Adds a uint8 into the output stream + * + * @param val + */ + protected void serializeUInt8(int val) { + buffer[length++] = val & 0xFF; + } + + protected void serializeBoolean(boolean val) { + buffer[length++] = val ? 1 : 0; + } + + /** + * Adds a uint16 into the output stream + * + * @param val + */ + protected void serializeUInt16(int val) { + buffer[length++] = val & 0xFF; + buffer[length++] = (val >> 8) & 0xFF; + } + + /** + * Adds a uint32 into the output stream + * + * @param passkey + */ + protected void serializeUInt32(long passkey) { + buffer[length++] = (int) (passkey & 0xFF); + buffer[length++] = (int) ((passkey >> 8) & 0xFF); + buffer[length++] = (int) ((passkey >> 16) & 0xFF); + buffer[length++] = (int) ((passkey >> 24) & 0xFF); + } + + protected void serializeUInt8Array(int[] array) { + serializeUInt8(array.length); + + for (int val : array) { + serializeUInt8(val); + } + } + + protected void serializeUuid(UUID uuid) { + // TODO this probably needs to support longer UUIDs? + buffer[length++] = 2; + long high = uuid.getMostSignificantBits(); + + buffer[length++] = (int) ((high >> 32) & 0xff); + buffer[length++] = (int) ((high >> 40) & 0xff); + } + + protected void serializeAddress(String address) { + String[] bytes = address.split(":"); + if (bytes.length == 0) { + serializeUInt8(0); + serializeUInt8(0); + serializeUInt8(0); + serializeUInt8(0); + serializeUInt8(0); + serializeUInt8(0); + + return; + } + + for (int cnt = 5; cnt >= 0; cnt--) { + serializeUInt8(Integer.parseInt(bytes[cnt], 16)); + } + } + + protected void serializeSmpIoCapabilities(SmpIoCapabilities capabilities) { + serializeUInt8(capabilities.getKey()); + } + + protected void serializeBluetoothAddressType(BluetoothAddressType addrType) { + serializeUInt8(addrType.getKey()); + } + + protected void serializeGapDiscoverableMode(GapDiscoverableMode mode) { + serializeUInt8(mode.getKey()); + } + + protected void serializeGapConnectableMode(GapConnectableMode mode) { + serializeUInt8(mode.getKey()); + } + + protected void serializeGapDiscoverMode(GapDiscoverMode mode) { + serializeUInt8(mode.getKey()); + } + + protected int[] getPayload() { + buffer[1] = length - 4; + return Arrays.copyOfRange(buffer, 0, length); + } + + public abstract int[] serialize(); +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaEventListener.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaEventListener.java new file mode 100644 index 00000000000..c570db4172d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaEventListener.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal; + +/** + * + * @author Chris Jackson + * + */ +public interface BlueGigaEventListener { + /** + * Called when an event is received + * + * @param event the {@link BlueGigaResponse} just received + */ + void bluegigaEventReceived(BlueGigaResponse event); +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaHandlerListener.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaHandlerListener.java new file mode 100644 index 00000000000..a3cc6ae1584 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaHandlerListener.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A listener to track {@link BlueGigaSerialHandler} life cycle events. + * + * @author Chris Jackson - Initial contribution and API + */ +@NonNullByDefault +public interface BlueGigaHandlerListener { + + /** + * Notifies when the handler gets closed because of the reason specified as an argument. + * + * @param reason a reason caused to be closed + */ + void bluegigaClosed(Exception reason); + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaPacket.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaPacket.java new file mode 100644 index 00000000000..b766471e6fe --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaPacket.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal; + +/** + * Base class from which both commands and responses (and events) are derived. + * + * @author Chris Jackson + * + */ +public class BlueGigaPacket { + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaResponse.java new file mode 100644 index 00000000000..0775a5ae0b8 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaResponse.java @@ -0,0 +1,185 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.AttributeChangeReason; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.AttributeValueType; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BluetoothAddressType; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.ConnectionStatusFlag; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.ScanResponseType; + +/** + * Abstract class for response and event packets. This provides the deserialization methods to convert wire data to Java + * classes. + * + * @author Chris Jackson + * + */ +public class BlueGigaResponse extends BlueGigaPacket { + private int[] buffer = new int[131]; + private int position = 0; + protected boolean event = false; + + protected BlueGigaResponse(int[] inputBuffer) { + // TODO Auto-generated constructor stub + buffer = inputBuffer; + position = 4; + } + + /** + * Returns true if this response is an event, or false if it is a response to a command + * + * @return true if this is an event + */ + public boolean isEvent() { + return event; + } + + /** + * Reads a int8 from the output stream + * + * @return value read from input + */ + protected int deserializeInt8() { + if (buffer[position] >= 128) { + return buffer[position++] - 256; + } else { + return buffer[position++]; + } + } + + /** + * Reads a uint8 from the output stream + * + * @return value read from input + */ + protected int deserializeUInt8() { + return buffer[position++]; + } + + protected boolean deserializeBoolean() { + return buffer[position++] != 0; + } + + /** + * Reads a uint16 from the output stream + * + * @return value read from input + */ + protected int deserializeUInt16() { + return buffer[position++] + (buffer[position++] << 8); + } + + protected UUID deserializeUuid() { + long low; + long high; + + // This is a uint8array type so first byte is the length + int length = buffer[position++]; + switch (length) { + case 2: + low = 0; + high = ((long) buffer[position++] << 32) + ((long) buffer[position++] << 40); + break; + case 4: + low = 0; + high = ((long) buffer[position++] << 32) + ((long) buffer[position++] << 40) + + ((long) buffer[position++] << 48) + ((long) buffer[position++] << 56); + break; + case 16: + low = (buffer[position++]) + ((long) buffer[position++] << 8) + ((long) buffer[position++] << 16) + + ((long) buffer[position++] << 24) + ((long) buffer[position++] << 32) + + ((long) buffer[position++] << 40) + ((long) buffer[position++] << 48) + + ((long) buffer[position++] << 56); + high = (buffer[position++]) + ((long) buffer[position++] << 8) + ((long) buffer[position++] << 16) + + ((long) buffer[position++] << 24) + ((long) buffer[position++] << 32) + + ((long) buffer[position++] << 40) + ((long) buffer[position++] << 48) + + ((long) buffer[position++] << 56); + break; + default: + low = 0; + high = 0; + position += length; + break; + } + return new UUID(high, low); + } + + protected BgApiResponse deserializeBgApiResponse() { + return BgApiResponse.getBgApiResponse(deserializeUInt16()); + } + + public Set deserializeConnectionStatusFlag() { + int val = deserializeUInt8(); + Set options = new HashSet(); + for (ConnectionStatusFlag option : ConnectionStatusFlag.values()) { + if (option == ConnectionStatusFlag.UNKNOWN) { + continue; + } + if ((option.getKey() & val) != 0) { + options.add(option); + } + } + return options; + } + + protected AttributeValueType deserializeAttributeValueType() { + return AttributeValueType.getAttributeValueType(deserializeUInt8()); + } + + protected BluetoothAddressType deserializeBluetoothAddressType() { + return BluetoothAddressType.getBluetoothAddressType(deserializeUInt8()); + } + + protected AttributeChangeReason deserializeAttributeChangeReason() { + return AttributeChangeReason.getAttributeChangeReason(deserializeUInt8()); + } + + protected ScanResponseType deserializeScanResponseType() { + return ScanResponseType.getScanResponseType(deserializeUInt8()); + } + + protected long deserializeUInt32() { + return buffer[position++] + (buffer[position++] << 8) + (buffer[position++] << 16) + (buffer[position++] << 24); + } + + protected int[] deserializeUInt8Array() { + int length = buffer[position++]; + int[] val = new int[length]; + + for (int cnt = 0; cnt < length; cnt++) { + val[cnt] = deserializeUInt8(); + } + + return val; + } + + protected String deserializeAddress() { + StringBuilder builder = new StringBuilder(); + + for (int cnt = 5; cnt >= 0; cnt--) { + if (cnt < 5) { + builder.append(':'); + } + builder.append(String.format("%02X", buffer[position + cnt])); + } + position += 6; + + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaResponsePackets.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaResponsePackets.java new file mode 100644 index 00000000000..7fdbf069284 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaResponsePackets.java @@ -0,0 +1,204 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaAttributeValueEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaAttributeWriteResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaExecuteWriteResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaFindByTypeValueResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaFindInformationFoundEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaFindInformationResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaGroupFoundEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaIndicateConfirmResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaIndicatedEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaPrepareWriteResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaProcedureCompletedEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByGroupTypeResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByHandleResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByTypeResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadLongResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadMultipleResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadMultipleResponseEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaWriteCommandResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb.BlueGigaAttributeStatusEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb.BlueGigaReadResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb.BlueGigaReadTypeResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb.BlueGigaSendAttributesResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb.BlueGigaUserReadRequestEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb.BlueGigaUserReadResponseResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb.BlueGigaUserWriteResponseResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb.BlueGigaValueEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb.BlueGigaWriteResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaChannelMapGetResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaConnectionStatusEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaDisconnectResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaDisconnectedEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaFeatureIndEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaGetRssiResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaGetStatusResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaUpdateResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaVersionIndEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaConnectDirectResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaConnectSelectiveResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaDiscoverResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaEndProcedureResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaScanResponseEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetAdvDataResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetAdvParametersResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetModeResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetScanParametersResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaBondStatusEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaBondingFailEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaDeleteBondingResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaEncryptStartResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaGetBondsResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaPassKeyResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaPasskeyDisplayEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaPasskeyRequestEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaSetBondableModeResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaSetParametersResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security.BlueGigaWhitelistBondsResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaAddressGetResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaBootEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaEndpointWatermarkRxEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaEndpointWatermarkTxEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetConnectionsResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetCountersResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetInfoResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaHelloResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaNoLicenseKeyEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaProtocolErrorEvent; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaResetResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaWhitelistAppendResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaWhitelistClearResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system.BlueGigaWhitelistRemoveResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Helper class to create BlueGiga BLE Response and Event packets (ie packets that we will receive). + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +class BlueGigaResponsePackets { + + private static Logger logger = LoggerFactory.getLogger(BlueGigaResponsePackets.class); + + final private static Map> packetMap = new HashMap>(); + + static { + packetMap.put(Objects.hash(0x00, 0x06, true), BlueGigaProtocolErrorEvent.class); + packetMap.put(Objects.hash(0x00, 0x02, true), BlueGigaEndpointWatermarkRxEvent.class); + packetMap.put(Objects.hash(0x00, 0x03, true), BlueGigaEndpointWatermarkTxEvent.class); + packetMap.put(Objects.hash(0x00, 0x05, true), BlueGigaNoLicenseKeyEvent.class); + packetMap.put(Objects.hash(0x04, 0x05, false), BlueGigaAttributeWriteResponse.class); + packetMap.put(Objects.hash(0x04, 0x0A, false), BlueGigaExecuteWriteResponse.class); + packetMap.put(Objects.hash(0x04, 0x00, false), BlueGigaFindByTypeValueResponse.class); + packetMap.put(Objects.hash(0x04, 0x03, false), BlueGigaFindInformationResponse.class); + packetMap.put(Objects.hash(0x04, 0x07, false), BlueGigaIndicateConfirmResponse.class); + packetMap.put(Objects.hash(0x04, 0x09, false), BlueGigaPrepareWriteResponse.class); + packetMap.put(Objects.hash(0x04, 0x01, false), BlueGigaReadByGroupTypeResponse.class); + packetMap.put(Objects.hash(0x04, 0x04, false), BlueGigaReadByHandleResponse.class); + packetMap.put(Objects.hash(0x04, 0x02, false), BlueGigaReadByTypeResponse.class); + packetMap.put(Objects.hash(0x04, 0x08, false), BlueGigaReadLongResponse.class); + packetMap.put(Objects.hash(0x04, 0x0B, false), BlueGigaReadMultipleResponse.class); + packetMap.put(Objects.hash(0x04, 0x06, false), BlueGigaWriteCommandResponse.class); + packetMap.put(Objects.hash(0x04, 0x01, true), BlueGigaProcedureCompletedEvent.class); + packetMap.put(Objects.hash(0x04, 0x05, true), BlueGigaAttributeValueEvent.class); + packetMap.put(Objects.hash(0x04, 0x04, true), BlueGigaFindInformationFoundEvent.class); + packetMap.put(Objects.hash(0x04, 0x02, true), BlueGigaGroupFoundEvent.class); + packetMap.put(Objects.hash(0x04, 0x00, true), BlueGigaIndicatedEvent.class); + packetMap.put(Objects.hash(0x04, 0x00, true), BlueGigaReadMultipleResponseEvent.class); + packetMap.put(Objects.hash(0x02, 0x01, false), BlueGigaReadResponse.class); + packetMap.put(Objects.hash(0x02, 0x02, false), BlueGigaReadTypeResponse.class); + packetMap.put(Objects.hash(0x02, 0x02, false), BlueGigaSendAttributesResponse.class); + packetMap.put(Objects.hash(0x02, 0x03, false), BlueGigaUserReadResponseResponse.class); + packetMap.put(Objects.hash(0x02, 0x04, false), BlueGigaUserWriteResponseResponse.class); + packetMap.put(Objects.hash(0x02, 0x00, false), BlueGigaWriteResponse.class); + packetMap.put(Objects.hash(0x02, 0x02, true), BlueGigaAttributeStatusEvent.class); + packetMap.put(Objects.hash(0x02, 0x01, true), BlueGigaUserReadRequestEvent.class); + packetMap.put(Objects.hash(0x02, 0x00, true), BlueGigaValueEvent.class); + packetMap.put(Objects.hash(0x03, 0x04, false), BlueGigaChannelMapGetResponse.class); + packetMap.put(Objects.hash(0x03, 0x00, false), BlueGigaDisconnectResponse.class); + packetMap.put(Objects.hash(0x03, 0x01, false), BlueGigaGetRssiResponse.class); + packetMap.put(Objects.hash(0x03, 0x07, false), BlueGigaGetStatusResponse.class); + packetMap.put(Objects.hash(0x03, 0x02, false), BlueGigaUpdateResponse.class); + packetMap.put(Objects.hash(0x03, 0x04, true), BlueGigaDisconnectedEvent.class); + packetMap.put(Objects.hash(0x03, 0x02, true), BlueGigaFeatureIndEvent.class); + packetMap.put(Objects.hash(0x03, 0x00, true), BlueGigaConnectionStatusEvent.class); + packetMap.put(Objects.hash(0x03, 0x01, true), BlueGigaVersionIndEvent.class); + packetMap.put(Objects.hash(0x06, 0x07, false), BlueGigaSetScanParametersResponse.class); + packetMap.put(Objects.hash(0x06, 0x03, false), BlueGigaConnectDirectResponse.class); + packetMap.put(Objects.hash(0x06, 0x05, false), BlueGigaConnectSelectiveResponse.class); + packetMap.put(Objects.hash(0x06, 0x02, false), BlueGigaDiscoverResponse.class); + packetMap.put(Objects.hash(0x06, 0x08, false), BlueGigaSetAdvParametersResponse.class); + packetMap.put(Objects.hash(0x06, 0x09, false), BlueGigaSetAdvDataResponse.class); + packetMap.put(Objects.hash(0x06, 0x04, false), BlueGigaEndProcedureResponse.class); + packetMap.put(Objects.hash(0x06, 0x01, false), BlueGigaSetModeResponse.class); + packetMap.put(Objects.hash(0x06, 0x00, true), BlueGigaScanResponseEvent.class); + packetMap.put(Objects.hash(0x05, 0x02, false), BlueGigaDeleteBondingResponse.class); + packetMap.put(Objects.hash(0x05, 0x00, false), BlueGigaEncryptStartResponse.class); + packetMap.put(Objects.hash(0x05, 0x05, false), BlueGigaGetBondsResponse.class); + packetMap.put(Objects.hash(0x05, 0x04, false), BlueGigaPassKeyResponse.class); + packetMap.put(Objects.hash(0x05, 0x01, false), BlueGigaSetBondableModeResponse.class); + packetMap.put(Objects.hash(0x05, 0x03, false), BlueGigaSetParametersResponse.class); + packetMap.put(Objects.hash(0x05, 0x07, false), BlueGigaWhitelistBondsResponse.class); + packetMap.put(Objects.hash(0x00, 0x0A, false), BlueGigaWhitelistAppendResponse.class); + packetMap.put(Objects.hash(0x00, 0x0B, false), BlueGigaWhitelistRemoveResponse.class); + packetMap.put(Objects.hash(0x00, 0x0C, false), BlueGigaWhitelistClearResponse.class); + packetMap.put(Objects.hash(0x05, 0x01, true), BlueGigaBondingFailEvent.class); + packetMap.put(Objects.hash(0x05, 0x04, true), BlueGigaBondStatusEvent.class); + packetMap.put(Objects.hash(0x05, 0x02, true), BlueGigaPasskeyDisplayEvent.class); + packetMap.put(Objects.hash(0x05, 0x03, true), BlueGigaPasskeyRequestEvent.class); + packetMap.put(Objects.hash(0x00, 0x02, false), BlueGigaAddressGetResponse.class); + packetMap.put(Objects.hash(0x00, 0x01, false), BlueGigaHelloResponse.class); + packetMap.put(Objects.hash(0x00, 0x00, false), BlueGigaResetResponse.class); + packetMap.put(Objects.hash(0x00, 0x06, false), BlueGigaGetConnectionsResponse.class); + packetMap.put(Objects.hash(0x00, 0x05, false), BlueGigaGetCountersResponse.class); + packetMap.put(Objects.hash(0x00, 0x08, false), BlueGigaGetInfoResponse.class); + packetMap.put(Objects.hash(0x00, 0x00, true), BlueGigaBootEvent.class); + } + + public static BlueGigaResponse getPacket(int[] data) { + int cmdClass = data[2]; + int cmdMethod = data[3]; + boolean isEvent = (data[0] & 0x80) != 0; + + Class bleClass = packetMap.get(Objects.hash(cmdClass, cmdMethod, isEvent)); + + if (bleClass == null) { + return null; + } + + Constructor ctor; + + try { + ctor = bleClass.getConstructor(int[].class); + BlueGigaResponse bleFrame = (BlueGigaResponse) ctor.newInstance(data); + return bleFrame; + } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + logger.error("Error instantiating BLE class", e); + } + + return null; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaSerialHandler.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaSerialHandler.java new file mode 100644 index 00000000000..192c0c16567 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/BlueGigaSerialHandler.java @@ -0,0 +1,460 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.Callable; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import org.eclipse.smarthome.core.common.ThreadPoolManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The main handler class for interacting with the BlueGiga serial API. This class provides transaction management and + * queuing of of data, and conversion of packets from the serial stream into command and response classes. + * + * @author Chris Jackson - Initial contribution and API + * + */ +public class BlueGigaSerialHandler { + + private static final int BLE_MAX_LENGTH = 64; + private static final int TRANSACTION_TIMEOUT_PERIOD = 50; + + private final Logger logger = LoggerFactory.getLogger(BlueGigaSerialHandler.class); + + /** + * The portName portName output stream. + */ + private final OutputStream outputStream; + private final Queue sendQueue = new LinkedList(); + private final Timer timer = new Timer(); + private TimerTask timerTask = null; + private Thread parserThread = null; + private final ExecutorService executor = ThreadPoolManager.getPool("bluegiga"); + + /** + * Transaction listeners are used internally to correlate the commands and responses + */ + private final List transactionListeners = new CopyOnWriteArrayList(); + + /** + * The event listeners will be notified of any asynchronous events + */ + private final Set eventListeners = new CopyOnWriteArraySet<>(); + + /** + * The event listeners will be notified of any life-cycle events of the handler. + */ + private final Set handlerListeners = new CopyOnWriteArraySet<>(); + + /** + * Flag reflecting that parser has been closed and parser parserThread + * should exit. + */ + private boolean close = false; + + public BlueGigaSerialHandler(final InputStream inputStream, final OutputStream outputStream) { + this.outputStream = outputStream; + + final int framecheckParams[] = new int[] { 0x00, 0x7F, 0xC0, 0xF8, 0xE0 }; + + parserThread = new Thread("BlueGigaBLEHandler") { + @Override + public void run() { + int exceptionCnt = 0; + logger.trace("BlueGiga BLE thread started"); + int[] inputBuffer = new int[BLE_MAX_LENGTH]; + int inputCount = 0; + int inputLength = 0; + + while (!close) { + try { + int val = inputStream.read(); + if (val == -1) { + continue; + } + // logger.debug("BLE RX: " + String.format("%02X", val)); + + inputBuffer[inputCount++] = val; + + if (inputCount < 4) { + // The BGAPI protocol has no packet framing, and no error detection, so we do a few + // sanity checks on the header to try and allow resyncronisation should there be an + // error. + // Byte 0: Check technology type is bluetooth and high length is 0 + // Byte 1: Check length is less than 64 bytes + // Byte 2: Check class ID is less than 8 + // Byte 3: Check command ID is less than 16 + if ((val & framecheckParams[inputCount]) != 0) { + logger.debug("BlueGiga framing error byte {} = {}", inputCount, val); + inputCount = 0; + continue; + } + } else if (inputCount == 4) { + // Process the header to get the length + inputLength = inputBuffer[1] + (inputBuffer[0] & 0x02 << 8) + 4; + if (inputLength > 64) { + logger.error("BLE length larger than 64 bytes ({})", inputLength); + } + } + if (inputCount == inputLength) { + // End of packet reached - process + BlueGigaResponse responsePacket = BlueGigaResponsePackets.getPacket(inputBuffer); + + logger.trace("BLE RX: {}", printHex(inputBuffer, inputLength)); + logger.trace("BLE RX: {}", responsePacket); + if (responsePacket != null) { + if (responsePacket.isEvent()) { + notifyEventListeners(responsePacket); + } else { + notifyTransactionComplete(responsePacket); + } + } + + inputCount = 0; + } + + } catch (final IOException e) { + logger.error("BlueGiga BLE IOException: ", e); + + if (exceptionCnt++ > 10) { + logger.error("BlueGiga BLE exception count exceeded"); + // if (!close) { + // frameHandler.error(e); + close = true; + notifyClosed(e); + } + } + } + logger.debug("BlueGiga BLE exited."); + } + }; + + parserThread.setDaemon(true); + parserThread.start(); + } + + /** + * Requests parser thread to shutdown. + */ + public void close() { + this.close = true; + try { + parserThread.interrupt(); + parserThread.join(); + } catch (InterruptedException e) { + logger.warn("Interrupted in packet parser thread shutdown join."); + } + } + + /** + * Checks if parser thread is alive. + * + * @return true if parser thread is alive. + */ + public boolean isAlive() { + return parserThread != null && parserThread.isAlive() && !close; + } + + // Synchronize this method to ensure a packet gets sent as a block + private synchronized void sendFrame(BlueGigaCommand bleFrame) { + // Send the data + try { + int[] payload = bleFrame.serialize(); + logger.trace("TX BLE frame: {}", printHex(payload, payload.length)); + for (int b : payload) { + outputStream.write(b); + } + } catch (IOException e) { + logger.debug(e.getMessage()); + } + + logger.trace("--> TX BLE frame: {}", bleFrame); + } + + // Synchronize this method so we can do the window check without interruption. + // Otherwise this method could be called twice from different threads that could end up with + // more than the TX_WINDOW number of frames sent. + private synchronized void sendNextFrame() { + BlueGigaCommand nextFrame = sendQueue.poll(); + if (nextFrame == null) { + // Nothing to send + return; + } + + sendFrame(nextFrame); + } + + /** + * Add a {@link BlueGigaCommand} frame to the send queue. The sendQueue is a + * FIFO queue. This method queues a {@link BlueGigaCommand} frame without + * waiting for a response. + * + * @param transaction + * {@link BlueGigaCommand} + */ + public void queueFrame(BlueGigaCommand request) { + logger.trace("TX BLE frame: {}", request); + checkIfAlive(); + sendQueue.add(request); + logger.trace("TX BLE queue: {}", sendQueue.size()); + sendNextFrame(); + } + + /** + * Notify any transaction listeners when we receive a response. + * + * @param response + * the response data received + * @return true if the response was processed + */ + private boolean notifyTransactionComplete(final BlueGigaResponse response) { + boolean processed = false; + + // logger.debug("NODE {}: notifyTransactionResponse {}", + // transaction.getNodeId(), transaction.getTransactionId()); + synchronized (transactionListeners) { + for (BluetoothListener listener : transactionListeners) { + if (listener.transactionEvent(response)) { + processed = true; + } + } + } + + return processed; + } + + private void addTransactionListener(BluetoothListener listener) { + synchronized (transactionListeners) { + if (transactionListeners.contains(listener)) { + return; + } + + transactionListeners.add(listener); + } + } + + private void removeTransactionListener(BluetoothListener listener) { + synchronized (transactionListeners) { + transactionListeners.remove(listener); + } + } + + /** + * Sends an BlueGiga request without waiting for the response. + * + * @param bleCommand + * Request {@link BlueGigaCommand} + * @return response {@link Future} {@link BlueGigaResponse} + */ + public Future sendBleRequestAsync(final BlueGigaCommand bleCommand) { + checkIfAlive(); + class TransactionWaiter implements Callable, BluetoothListener { + private boolean complete = false; + private BlueGigaResponse response = null; + + @Override + public BlueGigaResponse call() { + // Register a listener + addTransactionListener(this); + + // Send the transaction + queueFrame(bleCommand); + + // Wait for the transaction to complete + synchronized (this) { + while (!complete) { + try { + wait(); + } catch (InterruptedException e) { + complete = true; + } + } + } + + // Remove the listener + removeTransactionListener(this); + + return response; + } + + @Override + public boolean transactionEvent(BlueGigaResponse bleResponse) { + // Check if this response completes our transaction + if (bleCommand.hashCode() == bleResponse.hashCode()) { + return false; + } + + response = bleResponse; + complete = true; + synchronized (this) { + notify(); + } + + return true; + } + } + + Callable worker = new TransactionWaiter(); + return executor.submit(worker); + } + + /** + * Sends a {@link BlueGigaCommand} request to the NCP and waits for the response. The response is correlated with + * the request and the returned {@link BlueGigaResponse} contains the request and response data. + * + * @param bleRequest + * Request {@link BlueGigaCommand} + * @return response {@link BlueGigaResponse} + */ + public BlueGigaResponse sendTransaction(BlueGigaCommand bleCommand) { + checkIfAlive(); + Future futureResponse = sendBleRequestAsync(bleCommand); + if (futureResponse == null) { + logger.debug("Error sending BLE transaction: Future is null"); + return null; + } + + try { + return futureResponse.get(); + // return bleCommand; + } catch (InterruptedException | ExecutionException e) { + logger.debug("Error sending BLE transaction to listeners: ", e); + } + + return null; + } + + // TODO: Add a timeout mechanism + @SuppressWarnings("unused") + private synchronized void startTransactionTimer() { + // Stop any existing timer + resetTransactionTimer(); + + // Create the timer task + timerTask = new TransactionTimer(); + timer.schedule(timerTask, TRANSACTION_TIMEOUT_PERIOD); + } + + private synchronized void resetTransactionTimer() { + // Stop any existing timer + if (timerTask != null) { + timerTask.cancel(); + timerTask = null; + } + } + + private class TransactionTimer extends TimerTask { + // private final Logger logger = + // LoggerFactory.getLogger(ZWaveTransactionTimer.class); + + @Override + public void run() { + + } + } + + /** + * Notify any transaction listeners when we receive a response. + * This uses a separate thread to separate the processing of the event. + * + * @param response + * the response data received + * @return true if the response was processed + */ + private void notifyEventListeners(final BlueGigaResponse response) { + synchronized (this) { + // Notify the listeners + for (final BlueGigaEventListener listener : eventListeners) { + executor.submit(() -> listener.bluegigaEventReceived(response)); + } + } + } + + public void addEventListener(BlueGigaEventListener listener) { + synchronized (eventListeners) { + eventListeners.add(listener); + } + } + + /** + * Adds a handler listener. + * + * @param listener a new handler listener + */ + public void addHandlerListener(BlueGigaHandlerListener listener) { + handlerListeners.add(listener); + } + + public void removeEventListener(BlueGigaEventListener listener) { + eventListeners.remove(listener); + } + + public void removeHandlerListener(BlueGigaHandlerListener listener) { + handlerListeners.remove(listener); + } + + private String printHex(int[] data, int len) { + StringBuilder builder = new StringBuilder(); + + for (int cnt = 0; cnt < len; cnt++) { + builder.append(String.format("%02X ", data[cnt])); + } + + return builder.toString(); + } + + private void checkIfAlive() { + if (!isAlive()) { + throw new IllegalStateException("Bluegiga handler is dead. Most likely because of IO errors. " + + "Re-initialization of the BlueGigaSerialHandler is required."); + } + } + + /** + * Notify handler event listeners that the handler was bluegigaClosed due to an error specified as an argument. + * + * @param reason the reason to bluegigaClosed + */ + private void notifyClosed(Exception reason) { + // It should be safe enough not to use the NotificationService as this is a fatal error, no any further actions + // can be done with the handler, a new handler should be re-created + // There is another reason why NotificationService can't be used - the listeners should be notified immidiately + for (BlueGigaHandlerListener listener : handlerListeners) { + try { + listener.bluegigaClosed(reason); + } catch (Exception ex) { + logger.warn("Execution error of a BlueGigaHandlerListener listener.", ex); + } + } + } + + interface BluetoothListener { + boolean transactionEvent(BlueGigaResponse bleResponse); + } + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeValueEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeValueEvent.java new file mode 100644 index 00000000000..f2248933508 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeValueEvent.java @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.AttributeValueType; + +/** + * Class to implement the BlueGiga command attributeValueEvent. + *

+ * This event is produced at the GATT client side when an attribute value is passed from the GATT + * server to the GATT client. This event is for example produced after a successful Read by + * Handle operation or when an attribute is indicated or notified by the remote device. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaAttributeValueEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x05; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int attHandle; + + /** + * Attribute type + *

+ * BlueGiga API type is AttributeValueType - Java type is {@link AttributeValueType} + */ + private AttributeValueType type; + + /** + * Attribute value (data) + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] value; + + /** + * Event constructor + */ + public BlueGigaAttributeValueEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + attHandle = deserializeUInt16(); + type = deserializeAttributeValueType(); + value = deserializeUInt8Array(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current att_handle as {@link int} + */ + public int getAttHandle() { + return attHandle; + } + /** + * Attribute type + *

+ * BlueGiga API type is AttributeValueType - Java type is {@link AttributeValueType} + * + * @return the current type as {@link AttributeValueType} + */ + public AttributeValueType getType() { + return type; + } + /** + * Attribute value (data) + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + * + * @return the current value as {@link int[]} + */ + public int[] getValue() { + return value; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaAttributeValueEvent [connection="); + builder.append(connection); + builder.append(", attHandle="); + builder.append(attHandle); + builder.append(", type="); + builder.append(type); + builder.append(", value="); + for (int c = 0; c < value.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", value[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteCommand.java new file mode 100644 index 00000000000..3a13d3fa728 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteCommand.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command attributeWrite. + *

+ * This command can be used to write an attributes value on a remote device. In order to write the + * value of an attribute a connection must exists and you need to know the handle of the attribute + * you want to write + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaAttributeWriteCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x05; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Attribute handle to write to + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int attHandle; + + /** + * Attribute value + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] data; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * Attribute handle to write to + * + * @param attHandle the attHandle to set as {@link int} + */ + public void setAttHandle(int attHandle) { + this.attHandle = attHandle; + } + /** + * Attribute value + * + * @param data the data to set as {@link int[]} + */ + public void setData(int[] data) { + this.data = data; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(attHandle); + serializeUInt8Array(data); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaAttributeWriteCommand [connection="); + builder.append(connection); + builder.append(", attHandle="); + builder.append(attHandle); + builder.append(", data="); + for (int c = 0; c < data.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", data[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteResponse.java new file mode 100644 index 00000000000..7c4f9a53a11 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteResponse.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command attributeWrite. + *

+ * This command can be used to write an attributes value on a remote device. In order to write the + * value of an attribute a connection must exists and you need to know the handle of the attribute + * you want to write + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaAttributeWriteResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x05; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : write was successful. Otherwise error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaAttributeWriteResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : write was successful. Otherwise error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaAttributeWriteResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteCommand.java new file mode 100644 index 00000000000..4c737aac841 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteCommand.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command executeWrite. + *

+ * This command can be used to execute or cancel a previously queued prepare_write command on a + * remote device + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaExecuteWriteCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x0A; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 1: commits queued writes, 0: cancels queued writes + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int commit; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * 1: commits queued writes, 0: cancels queued writes + * + * @param commit the commit to set as {@link int} + */ + public void setCommit(int commit) { + this.commit = commit; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt8(commit); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaExecuteWriteCommand [connection="); + builder.append(connection); + builder.append(", commit="); + builder.append(commit); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteResponse.java new file mode 100644 index 00000000000..0e6f24d4820 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteResponse.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command executeWrite. + *

+ * This command can be used to execute or cancel a previously queued prepare_write command on a + * remote device + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaExecuteWriteResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x0A; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : write was successful. Otherwise error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaExecuteWriteResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : write was successful. Otherwise error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaExecuteWriteResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueCommand.java new file mode 100644 index 00000000000..5f7c9458ea1 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueCommand.java @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import java.util.UUID; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command findByTypeValue. + *

+ * This command can be used to find specific attributes on a remote device based on their 16-bit + * UUID value and value. The search can be limited by a starting and ending handle values. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaFindByTypeValueCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x00; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * First requested handle number + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int start; + + /** + * Last requested handle number + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int end; + + /** + * 2 octet UUID to find + *

+ * BlueGiga API type is uuid - Java type is {@link UUID} + */ + private UUID uuid; + + /** + * Attribute value to find + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] value; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * First requested handle number + * + * @param start the start to set as {@link int} + */ + public void setStart(int start) { + this.start = start; + } + /** + * Last requested handle number + * + * @param end the end to set as {@link int} + */ + public void setEnd(int end) { + this.end = end; + } + /** + * 2 octet UUID to find + * + * @param uuid the uuid to set as {@link UUID} + */ + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + /** + * Attribute value to find + * + * @param value the value to set as {@link int[]} + */ + public void setValue(int[] value) { + this.value = value; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(start); + serializeUInt16(end); + serializeUuid(uuid); + serializeUInt8Array(value); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaFindByTypeValueCommand [connection="); + builder.append(connection); + builder.append(", start="); + builder.append(start); + builder.append(", end="); + builder.append(end); + builder.append(", uuid="); + builder.append(uuid); + builder.append(", value="); + for (int c = 0; c < value.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", value[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueResponse.java new file mode 100644 index 00000000000..8e476308cd8 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueResponse.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command findByTypeValue. + *

+ * This command can be used to find specific attributes on a remote device based on their 16-bit + * UUID value and value. The search can be limited by a starting and ending handle values. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaFindByTypeValueResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x00; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : the operation was successful. Otherwise error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaFindByTypeValueResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : the operation was successful. Otherwise error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaFindByTypeValueResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationCommand.java new file mode 100644 index 00000000000..82fb3ee51b9 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationCommand.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command findInformation. + *

+ * This command can be used to find specific attributes on a remote device based on their 16-bit + * UUID value and value. The search can be limited by a starting and ending handle values. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaFindInformationCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x03; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * First attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int start; + + /** + * Last attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int end; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * First attribute handle + * + * @param start the start to set as {@link int} + */ + public void setStart(int start) { + this.start = start; + } + /** + * Last attribute handle + * + * @param end the end to set as {@link int} + */ + public void setEnd(int end) { + this.end = end; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(start); + serializeUInt16(end); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaFindInformationCommand [connection="); + builder.append(connection); + builder.append(", start="); + builder.append(start); + builder.append(", end="); + builder.append(end); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationFoundEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationFoundEvent.java new file mode 100644 index 00000000000..758b00ce02e --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationFoundEvent.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import java.util.UUID; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command findInformationFoundEvent. + *

+ * This event is generated when characteristics type mappings are found. This happens + * typically after Find Information command has been issued to discover all attributes of a + * service. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaFindInformationFoundEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x04; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Characteristics handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int chrHandle; + + /** + * Characteristics type (UUID) + *

+ * BlueGiga API type is uuid - Java type is {@link UUID} + */ + private UUID uuid; + + /** + * Event constructor + */ + public BlueGigaFindInformationFoundEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + chrHandle = deserializeUInt16(); + uuid = deserializeUuid(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Characteristics handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current chr_handle as {@link int} + */ + public int getChrHandle() { + return chrHandle; + } + /** + * Characteristics type (UUID) + *

+ * BlueGiga API type is uuid - Java type is {@link UUID} + * + * @return the current uuid as {@link UUID} + */ + public UUID getUuid() { + return uuid; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaFindInformationFoundEvent [connection="); + builder.append(connection); + builder.append(", chrHandle="); + builder.append(chrHandle); + builder.append(", uuid="); + builder.append(uuid); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationResponse.java new file mode 100644 index 00000000000..bdb298a0b45 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationResponse.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command findInformation. + *

+ * This command can be used to find specific attributes on a remote device based on their 16-bit + * UUID value and value. The search can be limited by a starting and ending handle values. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaFindInformationResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x03; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : the operation was successful. Otherwise error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaFindInformationResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : the operation was successful. Otherwise error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaFindInformationResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaGroupFoundEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaGroupFoundEvent.java new file mode 100644 index 00000000000..8379a3d2a36 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaGroupFoundEvent.java @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import java.util.UUID; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command groupFoundEvent. + *

+ * This event is produced when an attribute group (a service) is found. Typically this event is + * produced after Read by Group Type command. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGroupFoundEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x02; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Starting handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int start; + + /** + * Ending handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int end; + + /** + * UUID of a service. Length is 0 if no services are found. + *

+ * BlueGiga API type is uuid - Java type is {@link UUID} + */ + private UUID uuid; + + /** + * Event constructor + */ + public BlueGigaGroupFoundEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + start = deserializeUInt16(); + end = deserializeUInt16(); + uuid = deserializeUuid(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Starting handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current start as {@link int} + */ + public int getStart() { + return start; + } + /** + * Ending handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current end as {@link int} + */ + public int getEnd() { + return end; + } + /** + * UUID of a service. Length is 0 if no services are found. + *

+ * BlueGiga API type is uuid - Java type is {@link UUID} + * + * @return the current uuid as {@link UUID} + */ + public UUID getUuid() { + return uuid; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaGroupFoundEvent [connection="); + builder.append(connection); + builder.append(", start="); + builder.append(start); + builder.append(", end="); + builder.append(end); + builder.append(", uuid="); + builder.append(uuid); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmCommand.java new file mode 100644 index 00000000000..5cd83fd5c8e --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmCommand.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command indicateConfirm. + *

+ * This command can be used to send a acknowledge a received indication from a remote device. + * This function allows the application to manually confirm the indicated values instead of + * the smart stack Bluetooth automatically doing it. The benefit of this is extra reliability + * since the application can for example store the received value on the flash memory before + * confirming the indication to the remote device. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaIndicateConfirmCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x07; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaIndicateConfirmCommand [connection="); + builder.append(connection); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmResponse.java new file mode 100644 index 00000000000..474f49ba27b --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmResponse.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command indicateConfirm. + *

+ * This command can be used to send a acknowledge a received indication from a remote device. + * This function allows the application to manually confirm the indicated values instead of + * the smart stack Bluetooth automatically doing it. The benefit of this is extra reliability + * since the application can for example store the received value on the flash memory before + * confirming the indication to the remote device. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaIndicateConfirmResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x07; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Command result. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaIndicateConfirmResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Command result. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaIndicateConfirmResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicatedEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicatedEvent.java new file mode 100644 index 00000000000..ab6f088d383 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicatedEvent.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command indicatedEvent. + *

+ * This event is produced at the GATT server side when an attribute is successfully indicated to + * the GATT client. This means the event is only produced at the GATT server if the indication is + * acknowledged by the GATT client (the remote device). + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaIndicatedEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x00; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int attrhandle; + + /** + * Event constructor + */ + public BlueGigaIndicatedEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + attrhandle = deserializeUInt16(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current attrhandle as {@link int} + */ + public int getAttrhandle() { + return attrhandle; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaIndicatedEvent [connection="); + builder.append(connection); + builder.append(", attrhandle="); + builder.append(attrhandle); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteCommand.java new file mode 100644 index 00000000000..5165076a908 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteCommand.java @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command prepareWrite. + *

+ * This command will send a prepare write request to a remote device for queued writes. Queued + * writes can for example be used to write large attribute values by transmitting the data in + * chunks using prepare write command. Once the data has been transmitted with multiple + * prepare write commands the write must then be executed or canceled with Execute Write + * command, which if acknowledged by the remote device triggers a Procedure Completed event. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaPrepareWriteCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x09; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int attHandle; + + /** + * Offset to write to + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int offset; + + /** + * Data to write. Maximum amount of data that can be sent in single command is 18 bytes. + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] data; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * Attribute handle + * + * @param attHandle the attHandle to set as {@link int} + */ + public void setAttHandle(int attHandle) { + this.attHandle = attHandle; + } + /** + * Offset to write to + * + * @param offset the offset to set as {@link int} + */ + public void setOffset(int offset) { + this.offset = offset; + } + /** + * Data to write. Maximum amount of data that can be sent in single command is 18 bytes. + * + * @param data the data to set as {@link int[]} + */ + public void setData(int[] data) { + this.data = data; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(attHandle); + serializeUInt16(offset); + serializeUInt8Array(data); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaPrepareWriteCommand [connection="); + builder.append(connection); + builder.append(", attHandle="); + builder.append(attHandle); + builder.append(", offset="); + builder.append(offset); + builder.append(", data="); + for (int c = 0; c < data.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", data[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteResponse.java new file mode 100644 index 00000000000..5bcd7ee53ff --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteResponse.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command prepareWrite. + *

+ * This command will send a prepare write request to a remote device for queued writes. Queued + * writes can for example be used to write large attribute values by transmitting the data in + * chunks using prepare write command. Once the data has been transmitted with multiple + * prepare write commands the write must then be executed or canceled with Execute Write + * command, which if acknowledged by the remote device triggers a Procedure Completed event. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaPrepareWriteResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x09; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Command result. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaPrepareWriteResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Command result. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaPrepareWriteResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaProcedureCompletedEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaProcedureCompletedEvent.java new file mode 100644 index 00000000000..5b7bf5c3980 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaProcedureCompletedEvent.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command procedureCompletedEvent. + *

+ * This event is produced at the GATT client when an attribute protocol event is completed a and + * new operation can be issued + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaProcedureCompletedEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x01; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0: The operation was successful. Otherwise: attribute protocol error code returned by + * remote device + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Characteristic handle at which the event ended + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int chrHandle; + + /** + * Event constructor + */ + public BlueGigaProcedureCompletedEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + chrHandle = deserializeUInt16(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0: The operation was successful. Otherwise: attribute protocol error code returned by + * remote device + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + /** + * Characteristic handle at which the event ended + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current chr_handle as {@link int} + */ + public int getChrHandle() { + return chrHandle; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaProcedureCompletedEvent [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(", chrHandle="); + builder.append(chrHandle); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeCommand.java new file mode 100644 index 00000000000..6fe79b8a918 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeCommand.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import java.util.UUID; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command readByGroupType. + *

+ * This command reads the value of each attribute of a given type and in a given handle range. The + * command is typically used for primary (UUID: 0x2800) and secondary (UUID: 0x2801) service + * discovery. Discovered services are reported by Group Found event. Finally when the + * procedure is completed a Procedure Completed event is generated. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadByGroupTypeCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x01; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * First requested handle number + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int start; + + /** + * Last requested handle number + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int end; + + /** + * Group UUID to find + *

+ * BlueGiga API type is uuid - Java type is {@link UUID} + */ + private UUID uuid; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * First requested handle number + * + * @param start the start to set as {@link int} + */ + public void setStart(int start) { + this.start = start; + } + /** + * Last requested handle number + * + * @param end the end to set as {@link int} + */ + public void setEnd(int end) { + this.end = end; + } + /** + * Group UUID to find + * + * @param uuid the uuid to set as {@link UUID} + */ + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(start); + serializeUInt16(end); + serializeUuid(uuid); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadByGroupTypeCommand [connection="); + builder.append(connection); + builder.append(", start="); + builder.append(start); + builder.append(", end="); + builder.append(end); + builder.append(", uuid="); + builder.append(uuid); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeResponse.java new file mode 100644 index 00000000000..b80c7a92dff --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeResponse.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command readByGroupType. + *

+ * This command reads the value of each attribute of a given type and in a given handle range. The + * command is typically used for primary (UUID: 0x2800) and secondary (UUID: 0x2801) service + * discovery. Discovered services are reported by Group Found event. Finally when the + * procedure is completed a Procedure Completed event is generated. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadByGroupTypeResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x01; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Command result. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaReadByGroupTypeResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Command result. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadByGroupTypeResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleCommand.java new file mode 100644 index 00000000000..e111d2dbc29 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleCommand.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command readByHandle. + *

+ * This command reads a remote attribute's value with the given handle. Read by handle can be + * used to read attributes up to 22 bytes long. For longer attributes command must be used. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadByHandleCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x04; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int chrHandle; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * Attribute handle + * + * @param chrHandle the chrHandle to set as {@link int} + */ + public void setChrHandle(int chrHandle) { + this.chrHandle = chrHandle; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(chrHandle); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadByHandleCommand [connection="); + builder.append(connection); + builder.append(", chrHandle="); + builder.append(chrHandle); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleResponse.java new file mode 100644 index 00000000000..e7f93f86f0c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleResponse.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command readByHandle. + *

+ * This command reads a remote attribute's value with the given handle. Read by handle can be + * used to read attributes up to 22 bytes long. For longer attributes command must be used. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadByHandleResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x04; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaReadByHandleResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadByHandleResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeCommand.java new file mode 100644 index 00000000000..6e57ec2d976 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeCommand.java @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import java.util.UUID; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command readByType. + *

+ * The command reads the value of each attribute of a given type (UUID) and in a given attribute + * handle range. The command can for example be used to discover the characteristic + * declarations (UUID: 0x2803) within a service. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadByTypeCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x02; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * First attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int start; + + /** + * Last attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int end; + + /** + * Attribute type (UUID) + *

+ * BlueGiga API type is uuid - Java type is {@link UUID} + */ + private UUID uuid; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * First attribute handle + * + * @param start the start to set as {@link int} + */ + public void setStart(int start) { + this.start = start; + } + /** + * Last attribute handle + * + * @param end the end to set as {@link int} + */ + public void setEnd(int end) { + this.end = end; + } + /** + * Attribute type (UUID) + * + * @param uuid the uuid to set as {@link UUID} + */ + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(start); + serializeUInt16(end); + serializeUuid(uuid); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadByTypeCommand [connection="); + builder.append(connection); + builder.append(", start="); + builder.append(start); + builder.append(", end="); + builder.append(end); + builder.append(", uuid="); + builder.append(uuid); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeResponse.java new file mode 100644 index 00000000000..234537c64eb --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeResponse.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command readByType. + *

+ * The command reads the value of each attribute of a given type (UUID) and in a given attribute + * handle range. The command can for example be used to discover the characteristic + * declarations (UUID: 0x2803) within a service. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadByTypeResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x02; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaReadByTypeResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadByTypeResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongCommand.java new file mode 100644 index 00000000000..dd1ea3650b4 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongCommand.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command readLong. + *

+ * This command can be used to read long attribute values, which are longer than 22 bytes and + * cannot be read with a simple Read by Handle command. The command starts a procedure, where the + * client first sends a normal read command to the server and if the returned attribute value + * length is equal to MTU, the client will send further read long read requests until rest of the + * attribute is read. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadLongCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x08; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int chrHandle; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * Attribute handle + * + * @param chrHandle the chrHandle to set as {@link int} + */ + public void setChrHandle(int chrHandle) { + this.chrHandle = chrHandle; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(chrHandle); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadLongCommand [connection="); + builder.append(connection); + builder.append(", chrHandle="); + builder.append(chrHandle); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongResponse.java new file mode 100644 index 00000000000..47a5439557e --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongResponse.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command readLong. + *

+ * This command can be used to read long attribute values, which are longer than 22 bytes and + * cannot be read with a simple Read by Handle command. The command starts a procedure, where the + * client first sends a normal read command to the server and if the returned attribute value + * length is equal to MTU, the client will send further read long read requests until rest of the + * attribute is read. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadLongResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x08; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaReadLongResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadLongResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleCommand.java new file mode 100644 index 00000000000..01f4da12495 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleCommand.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command readMultiple. + *

+ * This command can be used to read multiple attributes from a server. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadMultipleCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x0B; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * List of attribute handles to read from the remote device + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] handles; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * List of attribute handles to read from the remote device + * + * @param handles the handles to set as {@link int[]} + */ + public void setHandles(int[] handles) { + this.handles = handles; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt8Array(handles); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadMultipleCommand [connection="); + builder.append(connection); + builder.append(", handles="); + for (int c = 0; c < handles.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", handles[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponse.java new file mode 100644 index 00000000000..6deaa68e75f --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponse.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command readMultiple. + *

+ * This command can be used to read multiple attributes from a server. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadMultipleResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x0B; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaReadMultipleResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadMultipleResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponseEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponseEvent.java new file mode 100644 index 00000000000..9f8e8c70299 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponseEvent.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command readMultipleResponseEvent. + *

+ * This event is a response to a Read Multiple request. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadMultipleResponseEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x00; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * This array contains the concatenated data from the multiple attributes that have been read, + * up to 22 bytes. + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] handles; + + /** + * Event constructor + */ + public BlueGigaReadMultipleResponseEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + handles = deserializeUInt8Array(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * This array contains the concatenated data from the multiple attributes that have been read, + * up to 22 bytes. + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + * + * @return the current handles as {@link int[]} + */ + public int[] getHandles() { + return handles; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadMultipleResponseEvent [connection="); + builder.append(connection); + builder.append(", handles="); + for (int c = 0; c < handles.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", handles[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandCommand.java new file mode 100644 index 00000000000..2c345e4c506 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandCommand.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command writeCommand. + *

+ * Writes the value of a remote devices attribute. The handle and the new value of the attribute + * are gives as parameters. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWriteCommandCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x06; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Attribute handle to write + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int attHandle; + + /** + * Value for the attribute + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] data; + + /** + * Connection handle + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * Attribute handle to write + * + * @param attHandle the attHandle to set as {@link int} + */ + public void setAttHandle(int attHandle) { + this.attHandle = attHandle; + } + /** + * Value for the attribute + * + * @param data the data to set as {@link int[]} + */ + public void setData(int[] data) { + this.data = data; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(attHandle); + serializeUInt8Array(data); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaWriteCommandCommand [connection="); + builder.append(connection); + builder.append(", attHandle="); + builder.append(attHandle); + builder.append(", data="); + for (int c = 0; c < data.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", data[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandResponse.java new file mode 100644 index 00000000000..f563d31d436 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandResponse.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributeclient; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command writeCommand. + *

+ * Writes the value of a remote devices attribute. The handle and the new value of the attribute + * are gives as parameters. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWriteCommandResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x04; + public static int COMMAND_METHOD = 0x06; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaWriteCommandResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaWriteCommandResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaAttributeStatusEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaAttributeStatusEvent.java new file mode 100644 index 00000000000..7030184d874 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaAttributeStatusEvent.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command attributeStatusEvent. + *

+ * This event indicates attribute status flags have changed. For example, this even is + * generated at the module acting as the GATT Server whenever the remote GATT Client changes the + * Client Characteristic Configuration to start or stop notification or indications from the + * Server. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaAttributeStatusEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x02; + + /** + * Attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int handle; + + /** + * Attribute status flags. See: Attribute Status Flags + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int flags; + + /** + * Event constructor + */ + public BlueGigaAttributeStatusEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + handle = deserializeUInt16(); + flags = deserializeUInt8(); + } + + /** + * Attribute handle + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current handle as {@link int} + */ + public int getHandle() { + return handle; + } + /** + * Attribute status flags. See: Attribute Status Flags + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current flags as {@link int} + */ + public int getFlags() { + return flags; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaAttributeStatusEvent [handle="); + builder.append(handle); + builder.append(", flags="); + builder.append(flags); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadCommand.java new file mode 100644 index 00000000000..e6f3d5fadad --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadCommand.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command read. + *

+ * The command reads the given attribute's value from the local database. There is a 32-byte + * limit in the amount of data that can be read at a time. In order to read larger values multiple + * read commands must be used with the offset properly used. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x01; + + /** + * Handle of the attribute to read + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int handle; + + /** + * Offset to read from. Maximum of 32 bytes can be read at a time. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int offset; + + /** + * Handle of the attribute to read + * + * @param handle the handle to set as {@link int} + */ + public void setHandle(int handle) { + this.handle = handle; + } + /** + * Offset to read from. Maximum of 32 bytes can be read at a time. + * + * @param offset the offset to set as {@link int} + */ + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt16(handle); + serializeUInt16(offset); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadCommand [handle="); + builder.append(handle); + builder.append(", offset="); + builder.append(offset); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadResponse.java new file mode 100644 index 00000000000..d0f45b87f10 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadResponse.java @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command read. + *

+ * The command reads the given attribute's value from the local database. There is a 32-byte + * limit in the amount of data that can be read at a time. In order to read larger values multiple + * read commands must be used with the offset properly used. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x01; + + /** + * Handle of the attribute which was read + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int handle; + + /** + * Offset read from + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int offset; + + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Value of the attribute + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] value; + + /** + * Response constructor + */ + public BlueGigaReadResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + handle = deserializeUInt16(); + offset = deserializeUInt16(); + result = deserializeBgApiResponse(); + value = deserializeUInt8Array(); + } + + /** + * Handle of the attribute which was read + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current handle as {@link int} + */ + public int getHandle() { + return handle; + } + /** + * Offset read from + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current offset as {@link int} + */ + public int getOffset() { + return offset; + } + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + /** + * Value of the attribute + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + * + * @return the current value as {@link int[]} + */ + public int[] getValue() { + return value; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadResponse [handle="); + builder.append(handle); + builder.append(", offset="); + builder.append(offset); + builder.append(", result="); + builder.append(result); + builder.append(", value="); + for (int c = 0; c < value.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", value[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeCommand.java new file mode 100644 index 00000000000..e0243ee4774 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeCommand.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command readType. + *

+ * This command reads the given attribute's type (UUID) from the local database. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadTypeCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x02; + + /** + * Handle of the attribute to read + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int handle; + + /** + * Handle of the attribute to read + * + * @param handle the handle to set as {@link int} + */ + public void setHandle(int handle) { + this.handle = handle; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt16(handle); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadTypeCommand [handle="); + builder.append(handle); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeResponse.java new file mode 100644 index 00000000000..2eee68493cf --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeResponse.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command readType. + *

+ * This command reads the given attribute's type (UUID) from the local database. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaReadTypeResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x02; + + /** + * Handle of the attribute which was read + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int handle; + + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Value of the attribute type (UUID) + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] value; + + /** + * Response constructor + */ + public BlueGigaReadTypeResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + handle = deserializeUInt16(); + result = deserializeBgApiResponse(); + value = deserializeUInt8Array(); + } + + /** + * Handle of the attribute which was read + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current handle as {@link int} + */ + public int getHandle() { + return handle; + } + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + /** + * Value of the attribute type (UUID) + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + * + * @return the current value as {@link int[]} + */ + public int[] getValue() { + return value; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaReadTypeResponse [handle="); + builder.append(handle); + builder.append(", result="); + builder.append(result); + builder.append(", value="); + for (int c = 0; c < value.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", value[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesCommand.java new file mode 100644 index 00000000000..1be271dc75d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesCommand.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command sendAttributes. + *

+ * This command will send an attribute value, identified by handle, via a notification or an + * indication to a remote device, but does not modify the current corresponding value in the + * local GATT database. If this attribute, identified by handle, does not have notification or + * indication property, or no remote device has registered for notifications or indications + * of this attribute, then an error will be returned. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSendAttributesCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x02; + + /** + * Connection handle to send to. Use 0xFF to send to all connected clients which have subscribed + * to receive the notifications or indications. An error is returned as soon as the first failed + * transmission occurs. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Attribute handle to send. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int handle; + + /** + * Data to send + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] value; + + /** + * Connection handle to send to. Use 0xFF to send to all connected clients which have subscribed + * to receive the notifications or indications. An error is returned as soon as the first failed + * transmission occurs. + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * Attribute handle to send. + * + * @param handle the handle to set as {@link int} + */ + public void setHandle(int handle) { + this.handle = handle; + } + /** + * Data to send + * + * @param value the value to set as {@link int[]} + */ + public void setValue(int[] value) { + this.value = value; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(handle); + serializeUInt8Array(value); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSendAttributesCommand [connection="); + builder.append(connection); + builder.append(", handle="); + builder.append(handle); + builder.append(", value="); + for (int c = 0; c < value.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", value[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesResponse.java new file mode 100644 index 00000000000..c80e2e951f1 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesResponse.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command sendAttributes. + *

+ * This command will send an attribute value, identified by handle, via a notification or an + * indication to a remote device, but does not modify the current corresponding value in the + * local GATT database. If this attribute, identified by handle, does not have notification or + * indication property, or no remote device has registered for notifications or indications + * of this attribute, then an error will be returned. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSendAttributesResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x02; + + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaSendAttributesResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * 0 : the command was successful. Otherwise an error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSendAttributesResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadRequestEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadRequestEvent.java new file mode 100644 index 00000000000..5c507936753 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadRequestEvent.java @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command userReadRequestEvent. + *

+ * This event is generated when a remote device tries to read an attribute which has the user + * property enabled. This event should be responded within 30 seconds with User Read Response + * command either containing the data or an error code. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaUserReadRequestEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x01; + + /** + * Connection ID which requested attribute + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Attribute handle requested + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int handle; + + /** + * Attribute offset to send data from + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int offset; + + /** + * Maximum data size to respond with. If more data is sent than indicated by this parameter, the + * extra bytes will be ignored. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int maxsize; + + /** + * Event constructor + */ + public BlueGigaUserReadRequestEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + handle = deserializeUInt16(); + offset = deserializeUInt16(); + maxsize = deserializeUInt8(); + } + + /** + * Connection ID which requested attribute + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Attribute handle requested + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current handle as {@link int} + */ + public int getHandle() { + return handle; + } + /** + * Attribute offset to send data from + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current offset as {@link int} + */ + public int getOffset() { + return offset; + } + /** + * Maximum data size to respond with. If more data is sent than indicated by this parameter, the + * extra bytes will be ignored. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current maxsize as {@link int} + */ + public int getMaxsize() { + return maxsize; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaUserReadRequestEvent [connection="); + builder.append(connection); + builder.append(", handle="); + builder.append(handle); + builder.append(", offset="); + builder.append(offset); + builder.append(", maxsize="); + builder.append(maxsize); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseCommand.java new file mode 100644 index 00000000000..ec314072f7c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseCommand.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command userReadResponse. + *

+ * This command is used to respond to an attribute Read request by a remote device, but only for + * attributes which have been configured with the user property. Attributes which have the + * user property enabled allow the attribute value to be requested from the application + * instead of the Smart stack automatically responding with Bluetooth the data in it's local + * GATT database. This command is normally used in response to a User Read Request event, which + * is generated when a remote device tries to read an attribute with a user property enabled. The + * response to User Read Request events must happen within 30 seconds or otherwise a timeout + * will occur. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaUserReadResponseCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x03; + + /** + * Connection handle to response to. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0: User Read Request is responded with data. In case of an error an application specific error + * code can be sent. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int attError; + + /** + * Data to send + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] value; + + /** + * Connection handle to response to. + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * 0: User Read Request is responded with data. In case of an error an application specific error + * code can be sent. + * + * @param attError the attError to set as {@link int} + */ + public void setAttError(int attError) { + this.attError = attError; + } + /** + * Data to send + * + * @param value the value to set as {@link int[]} + */ + public void setValue(int[] value) { + this.value = value; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt8(attError); + serializeUInt8Array(value); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaUserReadResponseCommand [connection="); + builder.append(connection); + builder.append(", attError="); + builder.append(attError); + builder.append(", value="); + for (int c = 0; c < value.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", value[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseResponse.java new file mode 100644 index 00000000000..0597f878786 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseResponse.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command userReadResponse. + *

+ * This command is used to respond to an attribute Read request by a remote device, but only for + * attributes which have been configured with the user property. Attributes which have the + * user property enabled allow the attribute value to be requested from the application + * instead of the Smart stack automatically responding with Bluetooth the data in it's local + * GATT database. This command is normally used in response to a User Read Request event, which + * is generated when a remote device tries to read an attribute with a user property enabled. The + * response to User Read Request events must happen within 30 seconds or otherwise a timeout + * will occur. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaUserReadResponseResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x03; + + /** + * Response constructor + */ + public BlueGigaUserReadResponseResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + } + + + @Override + public String toString() { + return "BlueGigaUserReadResponseResponse []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseCommand.java new file mode 100644 index 00000000000..f5c816a93dc --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseCommand.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command userWriteResponse. + *

+ * This command is used by the GATT server to acknowledge to the remote device that the + * attribute's value was written. This feature again allows the user application to + * acknowledged the attribute write operations instead of the Smart stack doing it + * automatically. Bluetooth The command should be used when a event is received where the + * reason why value has changed Value corresponds to + * attributes_attribute_change_reason_write_request_user. This response must be sent + * within 30 seconds or otherwise a timeout will occur. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaUserWriteResponseCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x04; + + /** + * Connection handle to response to. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0: User Read Request is responded with data. In case of an error an application specific error + * code can be sent. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int attError; + + /** + * Connection handle to response to. + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * 0: User Read Request is responded with data. In case of an error an application specific error + * code can be sent. + * + * @param attError the attError to set as {@link int} + */ + public void setAttError(int attError) { + this.attError = attError; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt8(attError); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaUserWriteResponseCommand [connection="); + builder.append(connection); + builder.append(", attError="); + builder.append(attError); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseResponse.java new file mode 100644 index 00000000000..72289f6228e --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseResponse.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command userWriteResponse. + *

+ * This command is used by the GATT server to acknowledge to the remote device that the + * attribute's value was written. This feature again allows the user application to + * acknowledged the attribute write operations instead of the Smart stack doing it + * automatically. Bluetooth The command should be used when a event is received where the + * reason why value has changed Value corresponds to + * attributes_attribute_change_reason_write_request_user. This response must be sent + * within 30 seconds or otherwise a timeout will occur. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaUserWriteResponseResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x04; + + /** + * Response constructor + */ + public BlueGigaUserWriteResponseResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + } + + + @Override + public String toString() { + return "BlueGigaUserWriteResponseResponse []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaValueEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaValueEvent.java new file mode 100644 index 00000000000..95d02396786 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaValueEvent.java @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.AttributeChangeReason; + +/** + * Class to implement the BlueGiga command valueEvent. + *

+ * This event is produced at the GATT server when a local attribute value was written by a remote + * device. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaValueEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x00; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Reason why value has changed see: enum Attribute Change Reason + *

+ * BlueGiga API type is AttributeChangeReason - Java type is {@link AttributeChangeReason} + */ + private AttributeChangeReason reason; + + /** + * Attribute handle, which was changed + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int handle; + + /** + * Offset into attribute value where data starts + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int offset; + + /** + * Attribute value + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] value; + + /** + * Event constructor + */ + public BlueGigaValueEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + reason = deserializeAttributeChangeReason(); + handle = deserializeUInt16(); + offset = deserializeUInt16(); + value = deserializeUInt8Array(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Reason why value has changed see: enum Attribute Change Reason + *

+ * BlueGiga API type is AttributeChangeReason - Java type is {@link AttributeChangeReason} + * + * @return the current reason as {@link AttributeChangeReason} + */ + public AttributeChangeReason getReason() { + return reason; + } + /** + * Attribute handle, which was changed + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current handle as {@link int} + */ + public int getHandle() { + return handle; + } + /** + * Offset into attribute value where data starts + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current offset as {@link int} + */ + public int getOffset() { + return offset; + } + /** + * Attribute value + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + * + * @return the current value as {@link int[]} + */ + public int[] getValue() { + return value; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaValueEvent [connection="); + builder.append(connection); + builder.append(", reason="); + builder.append(reason); + builder.append(", handle="); + builder.append(handle); + builder.append(", offset="); + builder.append(offset); + builder.append(", value="); + for (int c = 0; c < value.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", value[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteCommand.java new file mode 100644 index 00000000000..865211ce1d4 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteCommand.java @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command write. + *

+ * This command writes an attribute's value to the local database. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWriteCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x00; + + /** + * Handle of the attribute to write. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int handle; + + /** + * Attribute offset to write data + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int offset; + + /** + * Value of the attribute to write + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] value; + + /** + * Handle of the attribute to write. + * + * @param handle the handle to set as {@link int} + */ + public void setHandle(int handle) { + this.handle = handle; + } + /** + * Attribute offset to write data + * + * @param offset the offset to set as {@link int} + */ + public void setOffset(int offset) { + this.offset = offset; + } + /** + * Value of the attribute to write + * + * @param value the value to set as {@link int[]} + */ + public void setValue(int[] value) { + this.value = value; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt16(handle); + serializeUInt8(offset); + serializeUInt8Array(value); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaWriteCommand [handle="); + builder.append(handle); + builder.append(", offset="); + builder.append(offset); + builder.append(", value="); + for (int c = 0; c < value.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", value[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteResponse.java new file mode 100644 index 00000000000..719ac56d984 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteResponse.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.attributedb; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command write. + *

+ * This command writes an attribute's value to the local database. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWriteResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x02; + public static int COMMAND_METHOD = 0x00; + + /** + * 0: the write was successful. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaWriteResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * 0: the write was successful. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaWriteResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetCommand.java new file mode 100644 index 00000000000..aea6ab44252 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetCommand.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command channelMapGet. + *

+ * This command can be used to read the current Channel Map. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaChannelMapGetCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x04; + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Connection handle. + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaChannelMapGetCommand [connection="); + builder.append(connection); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetResponse.java new file mode 100644 index 00000000000..1f9c2462838 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetResponse.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command channelMapGet. + *

+ * This command can be used to read the current Channel Map. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaChannelMapGetResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x04; + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Current Channel Map. Each bit corresponds to one channel. 0-bit corresponds to 0 channel. + * Size of Channel Map is 5 bytes. Channel range: 0-36 + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] map; + + /** + * Response constructor + */ + public BlueGigaChannelMapGetResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + map = deserializeUInt8Array(); + } + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Current Channel Map. Each bit corresponds to one channel. 0-bit corresponds to 0 channel. + * Size of Channel Map is 5 bytes. Channel range: 0-36 + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + * + * @return the current map as {@link int[]} + */ + public int[] getMap() { + return map; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaChannelMapGetResponse [connection="); + builder.append(connection); + builder.append(", map="); + for (int c = 0; c < map.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", map[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaConnectionStatusEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaConnectionStatusEvent.java new file mode 100644 index 00000000000..029669d1cba --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaConnectionStatusEvent.java @@ -0,0 +1,217 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BluetoothAddressType; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.ConnectionStatusFlag; + +/** + * Class to implement the BlueGiga command connectionStatusEvent. + *

+ * This event indicates the connection status and parameters. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaConnectionStatusEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x00; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Connection status flags use connstatus-enumerator + *

+ * BlueGiga API type is ConnectionStatusFlag - Java type is {@link ConnectionStatusFlag} + * Parameter allows multiple options so implemented as a {@link Set}. + */ + private Set flags = new HashSet(); + + /** + * Remote devices Bluetooth address + *

+ * BlueGiga API type is bd_addr - Java type is {@link String} + */ + private String address; + + /** + * Remote address type see: Bluetooth Address Types--gap + *

+ * BlueGiga API type is BluetoothAddressType - Java type is {@link BluetoothAddressType} + */ + private BluetoothAddressType addressType; + + /** + * Current connection interval (units of 1.25ms) + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int connInterval; + + /** + * Current supervision timeout (units of 10ms) + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int timeout; + + /** + * Slave latency which tells how many connection intervals the slave may skip. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int latency; + + /** + * Bonding handle if the device has been bonded with. Otherwise: 0xFF + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int bonding; + + /** + * Event constructor + */ + public BlueGigaConnectionStatusEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + flags = deserializeConnectionStatusFlag(); + address = deserializeAddress(); + addressType = deserializeBluetoothAddressType(); + connInterval = deserializeUInt16(); + timeout = deserializeUInt16(); + latency = deserializeUInt16(); + bonding = deserializeUInt8(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Connection status flags use connstatus-enumerator + *

+ * BlueGiga API type is ConnectionStatusFlag - Java type is {@link ConnectionStatusFlag} + * + * @return the current flags as {@link Set} of {@link ConnectionStatusFlag} + */ + public Set getFlags() { + return flags; + } + /** + * Remote devices Bluetooth address + *

+ * BlueGiga API type is bd_addr - Java type is {@link String} + * + * @return the current address as {@link String} + */ + public String getAddress() { + return address; + } + /** + * Remote address type see: Bluetooth Address Types--gap + *

+ * BlueGiga API type is BluetoothAddressType - Java type is {@link BluetoothAddressType} + * + * @return the current address_type as {@link BluetoothAddressType} + */ + public BluetoothAddressType getAddressType() { + return addressType; + } + /** + * Current connection interval (units of 1.25ms) + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current conn_interval as {@link int} + */ + public int getConnInterval() { + return connInterval; + } + /** + * Current supervision timeout (units of 10ms) + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current timeout as {@link int} + */ + public int getTimeout() { + return timeout; + } + /** + * Slave latency which tells how many connection intervals the slave may skip. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current latency as {@link int} + */ + public int getLatency() { + return latency; + } + /** + * Bonding handle if the device has been bonded with. Otherwise: 0xFF + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current bonding as {@link int} + */ + public int getBonding() { + return bonding; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaConnectionStatusEvent [connection="); + builder.append(connection); + builder.append(", flags="); + builder.append(flags); + builder.append(", address="); + builder.append(address); + builder.append(", addressType="); + builder.append(addressType); + builder.append(", connInterval="); + builder.append(connInterval); + builder.append(", timeout="); + builder.append(timeout); + builder.append(", latency="); + builder.append(latency); + builder.append(", bonding="); + builder.append(bonding); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectCommand.java new file mode 100644 index 00000000000..3b5c2f9eb92 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectCommand.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command disconnect. + *

+ * This command disconnects an active connection. Bluetooth When link is disconnected a + * Disconnected event is produced. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaDisconnectCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x00; + + /** + * Connection handle to close. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Connection handle to close. + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaDisconnectCommand [connection="); + builder.append(connection); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectResponse.java new file mode 100644 index 00000000000..ea994dce85b --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectResponse.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command disconnect. + *

+ * This command disconnects an active connection. Bluetooth When link is disconnected a + * Disconnected event is produced. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaDisconnectResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x00; + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : the update was successful. Non-zero: An error occurred. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaDisconnectResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : the update was successful. Non-zero: An error occurred. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaDisconnectResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectedEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectedEvent.java new file mode 100644 index 00000000000..d8771b97d1e --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectedEvent.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command disconnectedEvent. + *

+ * This event is produced when a connection is disconnected. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaDisconnectedEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x04; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Disconnection reason code. 0 : disconnected by local user + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse reason; + + /** + * Event constructor + */ + public BlueGigaDisconnectedEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + reason = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Disconnection reason code. 0 : disconnected by local user + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current reason as {@link BgApiResponse} + */ + public BgApiResponse getReason() { + return reason; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaDisconnectedEvent [connection="); + builder.append(connection); + builder.append(", reason="); + builder.append(reason); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaFeatureIndEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaFeatureIndEvent.java new file mode 100644 index 00000000000..e56a5b4a2d5 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaFeatureIndEvent.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command featureIndEvent. + *

+ * This event indicates the remote devices features. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaFeatureIndEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x02; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * CtrData field from LL_FEATURE_RSP - packet + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] features; + + /** + * Event constructor + */ + public BlueGigaFeatureIndEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + features = deserializeUInt8Array(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * CtrData field from LL_FEATURE_RSP - packet + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + * + * @return the current features as {@link int[]} + */ + public int[] getFeatures() { + return features; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaFeatureIndEvent [connection="); + builder.append(connection); + builder.append(", features="); + for (int c = 0; c < features.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", features[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiCommand.java new file mode 100644 index 00000000000..c866f0c195d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiCommand.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command getRssi. + *

+ * This command disconnects an active connection. Bluetooth When link is disconnected a + * Disconnected event is produced. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetRssiCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x01; + + /** + * Connection handle to close. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Connection handle to close. + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaGetRssiCommand [connection="); + builder.append(connection); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiResponse.java new file mode 100644 index 00000000000..362cb8ba0ba --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiResponse.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command getRssi. + *

+ * This command disconnects an active connection. Bluetooth When link is disconnected a + * Disconnected event is produced. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetRssiResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x01; + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * RSSI value of the connection in dBm. Range: -103 to -38 + *

+ * BlueGiga API type is int8 - Java type is {@link int} + */ + private int rssi; + + /** + * Response constructor + */ + public BlueGigaGetRssiResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + rssi = deserializeInt8(); + } + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * RSSI value of the connection in dBm. Range: -103 to -38 + *

+ * BlueGiga API type is int8 - Java type is {@link int} + * + * @return the current rssi as {@link int} + */ + public int getRssi() { + return rssi; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaGetRssiResponse [connection="); + builder.append(connection); + builder.append(", rssi="); + builder.append(rssi); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusCommand.java new file mode 100644 index 00000000000..f62ac7f44db --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusCommand.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command getStatus. + *

+ * This command returns the status of the given connection. Status is returned in a event. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetStatusCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x07; + + /** + * Connection handle to close. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Connection handle to close. + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaGetStatusCommand [connection="); + builder.append(connection); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusResponse.java new file mode 100644 index 00000000000..03bac93da93 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusResponse.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command getStatus. + *

+ * This command returns the status of the given connection. Status is returned in a event. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetStatusResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x07; + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Response constructor + */ + public BlueGigaGetStatusResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + } + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaGetStatusResponse [connection="); + builder.append(connection); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateCommand.java new file mode 100644 index 00000000000..5adc9912d1c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateCommand.java @@ -0,0 +1,145 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command update. + *

+ * This command updates the connection parameters of a given connection. The parameters have + * the same meaning and follow the same rules as for the GAP class command: Connect Direct. If + * this command is issued at a master device, it will send parameter update request to the link + * layer. Bluetooth On the other hand if this command is issued at a slave device, it will send + * L2CAP connection parameter update request to the master, which may either accept or reject + * it. It will take an amount of time corresponding to at least six times the current connection + * interval before the new connection parameters will become active. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaUpdateCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x02; + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Minimum connection interval (units of 1.25ms) + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int intervalMin; + + /** + * Maximum connection interval (units of 1.25ms) + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int intervalMax; + + /** + * Slave latency which defines how many connections intervals a slave may skip. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int latency; + + /** + * Supervision timeout (units of 10ms) + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int timeout; + + /** + * Connection handle. + * + * @param connection the connection to set as {@link int} + */ + public void setConnection(int connection) { + this.connection = connection; + } + /** + * Minimum connection interval (units of 1.25ms) + * + * @param intervalMin the intervalMin to set as {@link int} + */ + public void setIntervalMin(int intervalMin) { + this.intervalMin = intervalMin; + } + /** + * Maximum connection interval (units of 1.25ms) + * + * @param intervalMax the intervalMax to set as {@link int} + */ + public void setIntervalMax(int intervalMax) { + this.intervalMax = intervalMax; + } + /** + * Slave latency which defines how many connections intervals a slave may skip. + * + * @param latency the latency to set as {@link int} + */ + public void setLatency(int latency) { + this.latency = latency; + } + /** + * Supervision timeout (units of 10ms) + * + * @param timeout the timeout to set as {@link int} + */ + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(connection); + serializeUInt16(intervalMin); + serializeUInt16(intervalMax); + serializeUInt16(latency); + serializeUInt16(timeout); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaUpdateCommand [connection="); + builder.append(connection); + builder.append(", intervalMin="); + builder.append(intervalMin); + builder.append(", intervalMax="); + builder.append(intervalMax); + builder.append(", latency="); + builder.append(latency); + builder.append(", timeout="); + builder.append(timeout); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateResponse.java new file mode 100644 index 00000000000..a5f38582c8c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateResponse.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command update. + *

+ * This command updates the connection parameters of a given connection. The parameters have + * the same meaning and follow the same rules as for the GAP class command: Connect Direct. If + * this command is issued at a master device, it will send parameter update request to the link + * layer. Bluetooth On the other hand if this command is issued at a slave device, it will send + * L2CAP connection parameter update request to the master, which may either accept or reject + * it. It will take an amount of time corresponding to at least six times the current connection + * interval before the new connection parameters will become active. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaUpdateResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x02; + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * 0 : the update was successful. Non-zero: An error occurred. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaUpdateResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle. + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * 0 : the update was successful. Non-zero: An error occurred. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaUpdateResponse [connection="); + builder.append(connection); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaVersionIndEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaVersionIndEvent.java new file mode 100644 index 00000000000..3b65257a73c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaVersionIndEvent.java @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.connection; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command versionIndEvent. + *

+ * This event indicates the remote devices version. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaVersionIndEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x03; + public static int COMMAND_METHOD = 0x01; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connection; + + /** + * Bluetooth controller specification version + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int versNr; + + /** + * Manufacturer of the controller + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int compId; + + /** + * Bluetooth controller version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int subVersNr; + + /** + * Event constructor + */ + public BlueGigaVersionIndEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + connection = deserializeUInt8(); + versNr = deserializeUInt8(); + compId = deserializeUInt16(); + subVersNr = deserializeUInt16(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection as {@link int} + */ + public int getConnection() { + return connection; + } + /** + * Bluetooth controller specification version + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current vers_nr as {@link int} + */ + public int getVersNr() { + return versNr; + } + /** + * Manufacturer of the controller + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current comp_id as {@link int} + */ + public int getCompId() { + return compId; + } + /** + * Bluetooth controller version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current sub_vers_nr as {@link int} + */ + public int getSubVersNr() { + return subVersNr; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaVersionIndEvent [connection="); + builder.append(connection); + builder.append(", versNr="); + builder.append(versNr); + builder.append(", compId="); + builder.append(compId); + builder.append(", subVersNr="); + builder.append(subVersNr); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectCommand.java new file mode 100644 index 00000000000..2bd80f17910 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectCommand.java @@ -0,0 +1,190 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BluetoothAddressType; + +/** + * Class to implement the BlueGiga command connectDirect. + *

+ * This command will start the GAP direct connection establishment procedure to a dedicated + * Smart Bluetooth device. The module will enter a state where it continuously scans for the + * connectable advertisement packets Bluetooth from the remote device which matches the + * Bluetooth address gives as a parameter. Upon receiving the advertisement packet, the + * module will send a connection request packet to the target device to imitate a Bluetooth + * connection. A successful connection will bi indicated by a event. Status If the device is + * configured to support more than one connection, the smallest connection interval which is + * divisible by maximum_connections * 2.5ms will be selected. Thus, it is important to provide + * minimum and maximum connection intervals so that such a connection interval is available + * within the range. The connection establishment procedure can be cancelled with End + * Procedure command. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaConnectDirectCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x03; + + /** + * Bluetooth address of the target device. + *

+ * BlueGiga API type is bd_addr - Java type is {@link String} + */ + private String address; + + /** + * see: Bluetooth Address Types. + *

+ * BlueGiga API type is BluetoothAddressType - Java type is {@link BluetoothAddressType} + */ + private BluetoothAddressType addrType; + + /** + * Minimum Connection Interval (in units of 1.25ms). Range: 6 - 3200 The lowest possible + * Connection Interval is 7.50ms and the largest is 4000ms. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int connIntervalMin; + + /** + * Maximum Connection Interval (in units of 1.25ms). Range: 6 - 3200 Must be equal or bigger than + * minimum Connection Interval. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int connIntervalMax; + + /** + * Supervision Timeout (in units of 10ms). The Supervision Timeout defines how long the + * devices can be out of range before the connection is closed. Range: 10 - 3200 Minimum time for + * the Supervision Timeout is 100ms and maximum value is 32000ms. According to the + * specification, the Supervision Timeout in milliseconds shall be larger than (1 + latency) * + * conn_interval_max * 2, where conn_interval_max is given in milliseconds. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int timeout; + + /** + * This parameter configures the slave latency. Slave latency defines how many connection + * intervals a slave device can skip. Increasing slave latency will decrease the energy + * consumption of the slave in scenarios where slave does not have data to send at every + * connection interval. Range: 0 - 500 0 : Slave latency is disabled. Example: Connection + * interval is 10ms and slave latency is 9: this means that the slave is allowed to communicate + * every 100ms, but it can communicate every 10ms if needed. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int latency; + + /** + * Bluetooth address of the target device. + * + * @param address the address to set as {@link String} + */ + public void setAddress(String address) { + this.address = address; + } + /** + * see: Bluetooth Address Types. + * + * @param addrType the addrType to set as {@link BluetoothAddressType} + */ + public void setAddrType(BluetoothAddressType addrType) { + this.addrType = addrType; + } + /** + * Minimum Connection Interval (in units of 1.25ms). Range: 6 - 3200 The lowest possible + * Connection Interval is 7.50ms and the largest is 4000ms. + * + * @param connIntervalMin the connIntervalMin to set as {@link int} + */ + public void setConnIntervalMin(int connIntervalMin) { + this.connIntervalMin = connIntervalMin; + } + /** + * Maximum Connection Interval (in units of 1.25ms). Range: 6 - 3200 Must be equal or bigger than + * minimum Connection Interval. + * + * @param connIntervalMax the connIntervalMax to set as {@link int} + */ + public void setConnIntervalMax(int connIntervalMax) { + this.connIntervalMax = connIntervalMax; + } + /** + * Supervision Timeout (in units of 10ms). The Supervision Timeout defines how long the + * devices can be out of range before the connection is closed. Range: 10 - 3200 Minimum time for + * the Supervision Timeout is 100ms and maximum value is 32000ms. According to the + * specification, the Supervision Timeout in milliseconds shall be larger than (1 + latency) * + * conn_interval_max * 2, where conn_interval_max is given in milliseconds. + * + * @param timeout the timeout to set as {@link int} + */ + public void setTimeout(int timeout) { + this.timeout = timeout; + } + /** + * This parameter configures the slave latency. Slave latency defines how many connection + * intervals a slave device can skip. Increasing slave latency will decrease the energy + * consumption of the slave in scenarios where slave does not have data to send at every + * connection interval. Range: 0 - 500 0 : Slave latency is disabled. Example: Connection + * interval is 10ms and slave latency is 9: this means that the slave is allowed to communicate + * every 100ms, but it can communicate every 10ms if needed. + * + * @param latency the latency to set as {@link int} + */ + public void setLatency(int latency) { + this.latency = latency; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeAddress(address); + serializeBluetoothAddressType(addrType); + serializeUInt16(connIntervalMin); + serializeUInt16(connIntervalMax); + serializeUInt16(timeout); + serializeUInt16(latency); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaConnectDirectCommand [address="); + builder.append(address); + builder.append(", addrType="); + builder.append(addrType); + builder.append(", connIntervalMin="); + builder.append(connIntervalMin); + builder.append(", connIntervalMax="); + builder.append(connIntervalMax); + builder.append(", timeout="); + builder.append(timeout); + builder.append(", latency="); + builder.append(latency); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectResponse.java new file mode 100644 index 00000000000..5189a20232c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectResponse.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command connectDirect. + *

+ * This command will start the GAP direct connection establishment procedure to a dedicated + * Smart Bluetooth device. The module will enter a state where it continuously scans for the + * connectable advertisement packets Bluetooth from the remote device which matches the + * Bluetooth address gives as a parameter. Upon receiving the advertisement packet, the + * module will send a connection request packet to the target device to imitate a Bluetooth + * connection. A successful connection will bi indicated by a event. Status If the device is + * configured to support more than one connection, the smallest connection interval which is + * divisible by maximum_connections * 2.5ms will be selected. Thus, it is important to provide + * minimum and maximum connection intervals so that such a connection interval is available + * within the range. The connection establishment procedure can be cancelled with End + * Procedure command. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaConnectDirectResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x03; + + /** + * 0 : procedure was successfully started Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Connection handle that is reserved for new connection + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connectionHandle; + + /** + * Response constructor + */ + public BlueGigaConnectDirectResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + connectionHandle = deserializeUInt8(); + } + + /** + * 0 : procedure was successfully started Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + /** + * Connection handle that is reserved for new connection + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection_handle as {@link int} + */ + public int getConnectionHandle() { + return connectionHandle; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaConnectDirectResponse [result="); + builder.append(result); + builder.append(", connectionHandle="); + builder.append(connectionHandle); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveCommand.java new file mode 100644 index 00000000000..2c99378dd0a --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveCommand.java @@ -0,0 +1,150 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command connectSelective. + *

+ * This command will start the GAP direct connection establishment procedure to a set of + * dedicated Bluetooth Smart devices. When this command is issued the the module will enter a + * state where it scans connectable Bluetooth advertisement packets from the remote devices + * which are registered in the local white list. Upon receiving an advertisement packet from + * one of the registered devices, the module will send a connection request to this device, and a + * successful connection will produce a connection status event. The connect selective + * command can be cancelled with End Procedure command. When in Initiating State there are no + * scan response events. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaConnectSelectiveCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x05; + + /** + * Minimum Connection Interval (in units of 1.25ms). Range: 6 - 3200 The lowest possible + * Connection Interval is 7.50ms and the largest is 4000ms. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int connIntervalMin; + + /** + * Maximum Connection Interval (in units of 1.25ms). Range: 6 - 3200 Must be equal or bigger than + * minimum Connection Interval. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int connIntervalMax; + + /** + * Supervision Timeout (in units of 10ms). The Supervision Timeout defines how long the + * devices can be out of range before the connection is closed. Range: 10 - 3200 Minimum time for + * the Supervision Timeout is 100ms and maximum value is 32000ms. According to the + * specification, the Supervision Timeout in milliseconds shall be larger than (1 + latency) * + * conn_interval_max * 2, where conn_interval_max is given in milliseconds. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int timeout; + + /** + * This parameter configures the slave latency. Slave latency defines how many connection + * intervals a slave device can skip. Increasing slave latency will decrease the energy + * consumption of the slave in scenarios where slave does not have data to send at every + * connection interval. Range: 0 - 500 0 : Slave latency is disabled. Example: Connection + * interval is 10ms and slave latency is 9: this means that the slave is allowed to communicate + * every 100ms, but it can communicate every 10ms if needed. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int latency; + + /** + * Minimum Connection Interval (in units of 1.25ms). Range: 6 - 3200 The lowest possible + * Connection Interval is 7.50ms and the largest is 4000ms. + * + * @param connIntervalMin the connIntervalMin to set as {@link int} + */ + public void setConnIntervalMin(int connIntervalMin) { + this.connIntervalMin = connIntervalMin; + } + /** + * Maximum Connection Interval (in units of 1.25ms). Range: 6 - 3200 Must be equal or bigger than + * minimum Connection Interval. + * + * @param connIntervalMax the connIntervalMax to set as {@link int} + */ + public void setConnIntervalMax(int connIntervalMax) { + this.connIntervalMax = connIntervalMax; + } + /** + * Supervision Timeout (in units of 10ms). The Supervision Timeout defines how long the + * devices can be out of range before the connection is closed. Range: 10 - 3200 Minimum time for + * the Supervision Timeout is 100ms and maximum value is 32000ms. According to the + * specification, the Supervision Timeout in milliseconds shall be larger than (1 + latency) * + * conn_interval_max * 2, where conn_interval_max is given in milliseconds. + * + * @param timeout the timeout to set as {@link int} + */ + public void setTimeout(int timeout) { + this.timeout = timeout; + } + /** + * This parameter configures the slave latency. Slave latency defines how many connection + * intervals a slave device can skip. Increasing slave latency will decrease the energy + * consumption of the slave in scenarios where slave does not have data to send at every + * connection interval. Range: 0 - 500 0 : Slave latency is disabled. Example: Connection + * interval is 10ms and slave latency is 9: this means that the slave is allowed to communicate + * every 100ms, but it can communicate every 10ms if needed. + * + * @param latency the latency to set as {@link int} + */ + public void setLatency(int latency) { + this.latency = latency; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt16(connIntervalMin); + serializeUInt16(connIntervalMax); + serializeUInt16(timeout); + serializeUInt16(latency); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaConnectSelectiveCommand [connIntervalMin="); + builder.append(connIntervalMin); + builder.append(", connIntervalMax="); + builder.append(connIntervalMax); + builder.append(", timeout="); + builder.append(timeout); + builder.append(", latency="); + builder.append(latency); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveResponse.java new file mode 100644 index 00000000000..6ab1282249e --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveResponse.java @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command connectSelective. + *

+ * This command will start the GAP direct connection establishment procedure to a set of + * dedicated Bluetooth Smart devices. When this command is issued the the module will enter a + * state where it scans connectable Bluetooth advertisement packets from the remote devices + * which are registered in the local white list. Upon receiving an advertisement packet from + * one of the registered devices, the module will send a connection request to this device, and a + * successful connection will produce a connection status event. The connect selective + * command can be cancelled with End Procedure command. When in Initiating State there are no + * scan response events. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaConnectSelectiveResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x05; + + /** + * 0 : procedure was successfully started Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Connection handle that is reserved for new connection + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int connectionHandle; + + /** + * Response constructor + */ + public BlueGigaConnectSelectiveResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + connectionHandle = deserializeUInt8(); + } + + /** + * 0 : procedure was successfully started Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + /** + * Connection handle that is reserved for new connection + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current connection_handle as {@link int} + */ + public int getConnectionHandle() { + return connectionHandle; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaConnectSelectiveResponse [result="); + builder.append(result); + builder.append(", connectionHandle="); + builder.append(connectionHandle); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverCommand.java new file mode 100644 index 00000000000..e3c44bb008d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverCommand.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.GapDiscoverMode; + +/** + * Class to implement the BlueGiga command discover. + *

+ * This command starts the GAP discovery procedure to scan for advertising devices i.e. to + * perform a device discovery. Scanning parameters can be configured with the Set Scan + * Parameters command before issuing this command. To cancel on an ongoing discovery process + * use the End Procedure command. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaDiscoverCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x02; + + /** + * see:GAP Discover Mode. + *

+ * BlueGiga API type is GapDiscoverMode - Java type is {@link GapDiscoverMode} + */ + private GapDiscoverMode mode; + + /** + * see:GAP Discover Mode. + * + * @param mode the mode to set as {@link GapDiscoverMode} + */ + public void setMode(GapDiscoverMode mode) { + this.mode = mode; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeGapDiscoverMode(mode); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaDiscoverCommand [mode="); + builder.append(mode); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverResponse.java new file mode 100644 index 00000000000..d6cc13af393 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverResponse.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command discover. + *

+ * This command starts the GAP discovery procedure to scan for advertising devices i.e. to + * perform a device discovery. Scanning parameters can be configured with the Set Scan + * Parameters command before issuing this command. To cancel on an ongoing discovery process + * use the End Procedure command. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaDiscoverResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x02; + + /** + * 0: Scan procedure was successfully started Non-zero: An error occurred. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaDiscoverResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * 0: Scan procedure was successfully started Non-zero: An error occurred. + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaDiscoverResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureCommand.java new file mode 100644 index 00000000000..29923350404 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureCommand.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command endProcedure. + *

+ * This command ends the current GAP discovery procedure and stop the scanning of advertising + * devices. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaEndProcedureCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x04; + + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + return getPayload(); + } + + @Override + public String toString() { + return "BlueGigaEndProcedureCommand []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureResponse.java new file mode 100644 index 00000000000..93f603c1ec1 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureResponse.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command endProcedure. + *

+ * This command ends the current GAP discovery procedure and stop the scanning of advertising + * devices. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaEndProcedureResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x04; + + /** + * 0: the command was successful. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaEndProcedureResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * 0: the command was successful. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaEndProcedureResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaScanResponseEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaScanResponseEvent.java new file mode 100644 index 00000000000..72b5607e6e3 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaScanResponseEvent.java @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BluetoothAddressType; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.ScanResponseType; + +/** + * Class to implement the BlueGiga command scanResponseEvent. + *

+ * This is a scan response event. This event is normally received by a Master which is scanning + * for advertisement and scan response packets from Slaves. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaScanResponseEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x00; + + /** + * RSSI value (dBm). Range: -103 to -38 + *

+ * BlueGiga API type is int8 - Java type is {@link int} + */ + private int rssi; + + /** + * Scan response header. 0: Connectable Advertisement packet. 2: Non Connectable + * Advertisement packet. 4: Scan response packet. 6: Discoverable advertisement packet + *

+ * BlueGiga API type is ScanResponseType - Java type is {@link ScanResponseType} + */ + private ScanResponseType packetType; + + /** + * Advertisers address + *

+ * BlueGiga API type is bd_addr - Java type is {@link String} + */ + private String sender; + + /** + * Advertiser address type. 1: random address. 0: public address + *

+ * BlueGiga API type is BluetoothAddressType - Java type is {@link BluetoothAddressType} + */ + private BluetoothAddressType addressType; + + /** + * Bond handle if there is known bond for this device, 0xff otherwise + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int bond; + + /** + * Scan response data + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] data; + + /** + * Event constructor + */ + public BlueGigaScanResponseEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + rssi = deserializeInt8(); + packetType = deserializeScanResponseType(); + sender = deserializeAddress(); + addressType = deserializeBluetoothAddressType(); + bond = deserializeUInt8(); + data = deserializeUInt8Array(); + } + + /** + * RSSI value (dBm). Range: -103 to -38 + *

+ * BlueGiga API type is int8 - Java type is {@link int} + * + * @return the current rssi as {@link int} + */ + public int getRssi() { + return rssi; + } + /** + * Scan response header. 0: Connectable Advertisement packet. 2: Non Connectable + * Advertisement packet. 4: Scan response packet. 6: Discoverable advertisement packet + *

+ * BlueGiga API type is ScanResponseType - Java type is {@link ScanResponseType} + * + * @return the current packet_type as {@link ScanResponseType} + */ + public ScanResponseType getPacketType() { + return packetType; + } + /** + * Advertisers address + *

+ * BlueGiga API type is bd_addr - Java type is {@link String} + * + * @return the current sender as {@link String} + */ + public String getSender() { + return sender; + } + /** + * Advertiser address type. 1: random address. 0: public address + *

+ * BlueGiga API type is BluetoothAddressType - Java type is {@link BluetoothAddressType} + * + * @return the current address_type as {@link BluetoothAddressType} + */ + public BluetoothAddressType getAddressType() { + return addressType; + } + /** + * Bond handle if there is known bond for this device, 0xff otherwise + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current bond as {@link int} + */ + public int getBond() { + return bond; + } + /** + * Scan response data + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + * + * @return the current data as {@link int[]} + */ + public int[] getData() { + return data; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaScanResponseEvent [rssi="); + builder.append(rssi); + builder.append(", packetType="); + builder.append(packetType); + builder.append(", sender="); + builder.append(sender); + builder.append(", addressType="); + builder.append(addressType); + builder.append(", bond="); + builder.append(bond); + builder.append(", data="); + for (int c = 0; c < data.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", data[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataCommand.java new file mode 100644 index 00000000000..96c2b066a65 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataCommand.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command setAdvData. + *

+ * This commands set advertisement or scan response data used in the advertisement and scan + * response packets. The command allows application specific data to be broadcasts either in + * advertisement or scan response packets. The data set with this command is only used when the + * GAP discoverable mode is set to gap_user_data. Notice that advertisement or scan response + * data must be formatted in accordance to the Bluetooth Core Specification. See BLUETOOTH + * SPECIFICATION Version 4.0 [Vol 3 - Part C - Chapter 11]. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetAdvDataCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x09; + + /** + * Advertisement data type. 0 : sets advertisement data. 1 : sets scan response data + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int setScanrsp; + + /** + * Advertisement data to send + *

+ * BlueGiga API type is uint8array - Java type is {@link int[]} + */ + private int[] advData; + + /** + * Advertisement data type. 0 : sets advertisement data. 1 : sets scan response data + * + * @param setScanrsp the setScanrsp to set as {@link int} + */ + public void setSetScanrsp(int setScanrsp) { + this.setScanrsp = setScanrsp; + } + /** + * Advertisement data to send + * + * @param advData the advData to set as {@link int[]} + */ + public void setAdvData(int[] advData) { + this.advData = advData; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(setScanrsp); + serializeUInt8Array(advData); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSetAdvDataCommand [setScanrsp="); + builder.append(setScanrsp); + builder.append(", advData="); + for (int c = 0; c < advData.length; c++) { + if (c > 0) { + builder.append(' '); + } + builder.append(String.format("%02X", advData[c])); + } + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataResponse.java new file mode 100644 index 00000000000..9476f807463 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataResponse.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command setAdvData. + *

+ * This commands set advertisement or scan response data used in the advertisement and scan + * response packets. The command allows application specific data to be broadcasts either in + * advertisement or scan response packets. The data set with this command is only used when the + * GAP discoverable mode is set to gap_user_data. Notice that advertisement or scan response + * data must be formatted in accordance to the Bluetooth Core Specification. See BLUETOOTH + * SPECIFICATION Version 4.0 [Vol 3 - Part C - Chapter 11]. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetAdvDataResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x09; + + /** + * 0: Command was successfully executed. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaSetAdvDataResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * 0: Command was successfully executed. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSetAdvDataResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersCommand.java new file mode 100644 index 00000000000..d01ee607dcd --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersCommand.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command setAdvParameters. + *

+ * This command is used to set the advertising parameters. Example: If the minimum + * advertisement interval is 40ms and the maximum advertisement interval is 100ms then the + * real advertisement interval will be mostly the middle value (70ms) plus a randomly added + * 20ms delay, which needs to be added according to the Bluetooth specification. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetAdvParametersCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x08; + + /** + * Minimum advertisement interval in units of 625us. Range: 0x20 to 0x4000. Default: 0x200 + * (320ms) Explanation: 0x200 = 512 512 * 625us = 320000us = 320ms + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int advIntervalMin; + + /** + * Maximum advertisement interval in units of 625us. Range: 0x20 to 0x4000. Default: 0x200 + * (320ms) + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int advIntervalMax; + + /** + * A bit mask to identify which of the three advertisement channels are used. Examples: 0x07: + * All three channels are used 0x03: Advertisement channels 37 and 38 are used. 0x04: Only + * advertisement channel 39 is used + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int advChannels; + + /** + * Minimum advertisement interval in units of 625us. Range: 0x20 to 0x4000. Default: 0x200 + * (320ms) Explanation: 0x200 = 512 512 * 625us = 320000us = 320ms + * + * @param advIntervalMin the advIntervalMin to set as {@link int} + */ + public void setAdvIntervalMin(int advIntervalMin) { + this.advIntervalMin = advIntervalMin; + } + /** + * Maximum advertisement interval in units of 625us. Range: 0x20 to 0x4000. Default: 0x200 + * (320ms) + * + * @param advIntervalMax the advIntervalMax to set as {@link int} + */ + public void setAdvIntervalMax(int advIntervalMax) { + this.advIntervalMax = advIntervalMax; + } + /** + * A bit mask to identify which of the three advertisement channels are used. Examples: 0x07: + * All three channels are used 0x03: Advertisement channels 37 and 38 are used. 0x04: Only + * advertisement channel 39 is used + * + * @param advChannels the advChannels to set as {@link int} + */ + public void setAdvChannels(int advChannels) { + this.advChannels = advChannels; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt16(advIntervalMin); + serializeUInt16(advIntervalMax); + serializeUInt8(advChannels); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSetAdvParametersCommand [advIntervalMin="); + builder.append(advIntervalMin); + builder.append(", advIntervalMax="); + builder.append(advIntervalMax); + builder.append(", advChannels="); + builder.append(advChannels); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersResponse.java new file mode 100644 index 00000000000..e02a3f5752a --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersResponse.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command setAdvParameters. + *

+ * This command is used to set the advertising parameters. Example: If the minimum + * advertisement interval is 40ms and the maximum advertisement interval is 100ms then the + * real advertisement interval will be mostly the middle value (70ms) plus a randomly added + * 20ms delay, which needs to be added according to the Bluetooth specification. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetAdvParametersResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x08; + + /** + * 0: Command was successfully executed. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaSetAdvParametersResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * 0: Command was successfully executed. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSetAdvParametersResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeCommand.java new file mode 100644 index 00000000000..accc3c3cf16 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeCommand.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.GapConnectableMode; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.GapDiscoverableMode; + +/** + * Class to implement the BlueGiga command setMode. + *

+ * This command configures the current GAP discoverability and connectability modes. It can + * be used to enable advertisements and/or allow connection. The command is also meant to fully + * stop advertising, when using gap_non_discoverable and gap_non_connectable. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetModeCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x01; + + /** + * see:GAP Discoverable Mode + *

+ * BlueGiga API type is GapDiscoverableMode - Java type is {@link GapDiscoverableMode} + */ + private GapDiscoverableMode discover; + + /** + * see:GAP Connectable Mode + *

+ * BlueGiga API type is GapConnectableMode - Java type is {@link GapConnectableMode} + */ + private GapConnectableMode connect; + + /** + * see:GAP Discoverable Mode + * + * @param discover the discover to set as {@link GapDiscoverableMode} + */ + public void setDiscover(GapDiscoverableMode discover) { + this.discover = discover; + } + /** + * see:GAP Connectable Mode + * + * @param connect the connect to set as {@link GapConnectableMode} + */ + public void setConnect(GapConnectableMode connect) { + this.connect = connect; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeGapDiscoverableMode(discover); + serializeGapConnectableMode(connect); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSetModeCommand [discover="); + builder.append(discover); + builder.append(", connect="); + builder.append(connect); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeResponse.java new file mode 100644 index 00000000000..82a27c32331 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeResponse.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command setMode. + *

+ * This command configures the current GAP discoverability and connectability modes. It can + * be used to enable advertisements and/or allow connection. The command is also meant to fully + * stop advertising, when using gap_non_discoverable and gap_non_connectable. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetModeResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x01; + + /** + * 0: the command was successful. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaSetModeResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * 0: the command was successful. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSetModeResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersCommand.java new file mode 100644 index 00000000000..7c000e680e8 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersCommand.java @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command setScanParameters. + *

+ * This command sets the scan parameters which affect how other Smart devices are discovered. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetScanParametersCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x07; + + /** + * Scan interval defines the interval when scanning is re-started in units of 625us. Range: 0x4 + * - 0x4000. Default: (46,875ms) 0x4B After every scan interval the scanner will change the + * frequency it operates at at it will cycle through all the three advertisements channels in a + * round robin fashion. According to the specification all three channels must be Bluetooth + * used by a scanner. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int scanInterval; + + /** + * Scan Window defines how long time the scanner will listen on a certain frequency and try to + * pick up advertisement packets. Scan window is defined as units of 625us. Range: 0x4 - 0x4000. + * Default: 0x32 (31,25 ms). Scan windows must be equal or smaller than scan interval If scan + * window is equal to the scan interval value, then the module Bluetooth will be scanning at a + * 100% duty cycle. If scan window is half of the scan interval value, then the module Bluetooth + * will be scanning at a 50% duty cycle. + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int scanWindow; + + /** + * 1: Active scanning is used. When an advertisement packet is received the Bluetooth stack + * will send a scan request packet to the advertiser to try and read the scan response data. 0: + * Passive scanning is used. No scan request is made. + *

+ * BlueGiga API type is boolean - Java type is {@link boolean} + */ + private boolean activeScanning; + + /** + * Scan interval defines the interval when scanning is re-started in units of 625us. Range: 0x4 + * - 0x4000. Default: (46,875ms) 0x4B After every scan interval the scanner will change the + * frequency it operates at at it will cycle through all the three advertisements channels in a + * round robin fashion. According to the specification all three channels must be Bluetooth + * used by a scanner. + * + * @param scanInterval the scanInterval to set as {@link int} + */ + public void setScanInterval(int scanInterval) { + this.scanInterval = scanInterval; + } + /** + * Scan Window defines how long time the scanner will listen on a certain frequency and try to + * pick up advertisement packets. Scan window is defined as units of 625us. Range: 0x4 - 0x4000. + * Default: 0x32 (31,25 ms). Scan windows must be equal or smaller than scan interval If scan + * window is equal to the scan interval value, then the module Bluetooth will be scanning at a + * 100% duty cycle. If scan window is half of the scan interval value, then the module Bluetooth + * will be scanning at a 50% duty cycle. + * + * @param scanWindow the scanWindow to set as {@link int} + */ + public void setScanWindow(int scanWindow) { + this.scanWindow = scanWindow; + } + /** + * 1: Active scanning is used. When an advertisement packet is received the Bluetooth stack + * will send a scan request packet to the advertiser to try and read the scan response data. 0: + * Passive scanning is used. No scan request is made. + * + * @param activeScanning the activeScanning to set as {@link boolean} + */ + public void setActiveScanning(boolean activeScanning) { + this.activeScanning = activeScanning; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt16(scanInterval); + serializeUInt16(scanWindow); + serializeBoolean(activeScanning); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSetScanParametersCommand [scanInterval="); + builder.append(scanInterval); + builder.append(", scanWindow="); + builder.append(scanWindow); + builder.append(", activeScanning="); + builder.append(activeScanning); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersResponse.java new file mode 100644 index 00000000000..016350c3eb2 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersResponse.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.gap; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command setScanParameters. + *

+ * This command sets the scan parameters which affect how other Smart devices are discovered. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetScanParametersResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x06; + public static int COMMAND_METHOD = 0x07; + + /** + * 0: The command was executed successfully. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaSetScanParametersResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * 0: The command was executed successfully. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSetScanParametersResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondStatusEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondStatusEvent.java new file mode 100644 index 00000000000..9ca01b967e0 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondStatusEvent.java @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command bondStatusEvent. + *

+ * This event outputs bonding status information. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaBondStatusEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x04; + + /** + * Bonding handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int bond; + + /** + * Encryption key size used in long-term key + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int keysize; + + /** + * Was Man-in-the-Middle mode was used in pairing. 0: No MITM used. 1: MITM was used + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int mitm; + + /** + * Keys stored for bonding. See: Bonding Keys + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int keys; + + /** + * Event constructor + */ + public BlueGigaBondStatusEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + bond = deserializeUInt8(); + keysize = deserializeUInt8(); + mitm = deserializeUInt8(); + keys = deserializeUInt8(); + } + + /** + * Bonding handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current bond as {@link int} + */ + public int getBond() { + return bond; + } + /** + * Encryption key size used in long-term key + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current keysize as {@link int} + */ + public int getKeysize() { + return keysize; + } + /** + * Was Man-in-the-Middle mode was used in pairing. 0: No MITM used. 1: MITM was used + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current mitm as {@link int} + */ + public int getMitm() { + return mitm; + } + /** + * Keys stored for bonding. See: Bonding Keys + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current keys as {@link int} + */ + public int getKeys() { + return keys; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaBondStatusEvent [bond="); + builder.append(bond); + builder.append(", keysize="); + builder.append(keysize); + builder.append(", mitm="); + builder.append(mitm); + builder.append(", keys="); + builder.append(keys); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondingFailEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondingFailEvent.java new file mode 100644 index 00000000000..e511edea45d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondingFailEvent.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command bondingFailEvent. + *

+ * This event indicates the bonding has failed for a connection. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaBondingFailEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x01; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int handle; + + /** + * Encryption status, describes error that occurred during bonding. See: Security Manager + * Protocol Errors + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Event constructor + */ + public BlueGigaBondingFailEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + handle = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current handle as {@link int} + */ + public int getHandle() { + return handle; + } + /** + * Encryption status, describes error that occurred during bonding. See: Security Manager + * Protocol Errors + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaBondingFailEvent [handle="); + builder.append(handle); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingCommand.java new file mode 100644 index 00000000000..2691ec9bb63 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingCommand.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command deleteBonding. + *

+ * This command deletes a bonding from the local security database. There can be a maximum of 8 + * bonded devices stored at the same time, and one of them must be deleted if you need bonding with + * a 9th device. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaDeleteBondingCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x02; + + /** + * Bonding handle of a device. This handle can be obtained for example from events like: Scan + * Response Status - If handle is 0xFF, all bondings will be deleted + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int handle; + + /** + * Bonding handle of a device. This handle can be obtained for example from events like: Scan + * Response Status - If handle is 0xFF, all bondings will be deleted + * + * @param handle the handle to set as {@link int} + */ + public void setHandle(int handle) { + this.handle = handle; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(handle); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaDeleteBondingCommand [handle="); + builder.append(handle); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingResponse.java new file mode 100644 index 00000000000..2c107f0d6ff --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingResponse.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command deleteBonding. + *

+ * This command deletes a bonding from the local security database. There can be a maximum of 8 + * bonded devices stored at the same time, and one of them must be deleted if you need bonding with + * a 9th device. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaDeleteBondingResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x02; + + /** + * 0: the command was successful. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaDeleteBondingResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * 0: the command was successful. Non-zero: An error occurred + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaDeleteBondingResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartCommand.java new file mode 100644 index 00000000000..31aad7abd75 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartCommand.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command encryptStart. + *

+ * This command starts the encryption for a given connection. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaEncryptStartCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x00; + + /** + * Bonding handle of a device. This handle can be obtained for example from events like: Scan + * Response Status - If handle is 0xFF, all bondings will be deleted + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int handle; + + /** + * Create bonding if devices are not already bonded. 0: Do not create bonding. 1: Creating + * bonding + *

+ * BlueGiga API type is boolean - Java type is {@link boolean} + */ + private boolean bonding; + + /** + * Bonding handle of a device. This handle can be obtained for example from events like: Scan + * Response Status - If handle is 0xFF, all bondings will be deleted + * + * @param handle the handle to set as {@link int} + */ + public void setHandle(int handle) { + this.handle = handle; + } + /** + * Create bonding if devices are not already bonded. 0: Do not create bonding. 1: Creating + * bonding + * + * @param bonding the bonding to set as {@link boolean} + */ + public void setBonding(boolean bonding) { + this.bonding = bonding; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(handle); + serializeBoolean(bonding); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaEncryptStartCommand [handle="); + builder.append(handle); + builder.append(", bonding="); + builder.append(bonding); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartResponse.java new file mode 100644 index 00000000000..3eb391e0b97 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartResponse.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command encryptStart. + *

+ * This command starts the encryption for a given connection. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaEncryptStartResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x00; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int handle; + + /** + * 0 : the encryption was started successfully + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaEncryptStartResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + handle = deserializeUInt8(); + result = deserializeBgApiResponse(); + } + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current handle as {@link int} + */ + public int getHandle() { + return handle; + } + /** + * 0 : the encryption was started successfully + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaEncryptStartResponse [handle="); + builder.append(handle); + builder.append(", result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsCommand.java new file mode 100644 index 00000000000..b1ed7d0efca --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsCommand.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command getBonds. + *

+ * This command lists all bonded devices. There can be a maximum of 8 bonded devices. The + * information related to the bonded devices is stored in the Flash memory, so it is persistent + * across resets and power-cycles. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetBondsCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x05; + + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + return getPayload(); + } + + @Override + public String toString() { + return "BlueGigaGetBondsCommand []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsResponse.java new file mode 100644 index 00000000000..671446afa04 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsResponse.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command getBonds. + *

+ * This command lists all bonded devices. There can be a maximum of 8 bonded devices. The + * information related to the bonded devices is stored in the Flash memory, so it is persistent + * across resets and power-cycles. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetBondsResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x05; + + /** + * Num of currently bonded devices + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int bonds; + + /** + * Response constructor + */ + public BlueGigaGetBondsResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + bonds = deserializeUInt8(); + } + + /** + * Num of currently bonded devices + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current bonds as {@link int} + */ + public int getBonds() { + return bonds; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaGetBondsResponse [bonds="); + builder.append(bonds); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyCommand.java new file mode 100644 index 00000000000..38890ddda48 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyCommand.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command passKey. + *

+ * This command is used to enter a passkey required for Man-in-the-Middle pairing. It should be + * sent as a response to Passkey Request event. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaPassKeyCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x04; + + /** + * Connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int handle; + + /** + * Passkey. Range: 000000-999999 + *

+ * BlueGiga API type is uint32 - Java type is {@link long} + */ + private long passkey; + + /** + * Connection handle + * + * @param handle the handle to set as {@link int} + */ + public void setHandle(int handle) { + this.handle = handle; + } + /** + * Passkey. Range: 000000-999999 + * + * @param passkey the passkey to set as {@link long} + */ + public void setPasskey(long passkey) { + this.passkey = passkey; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeUInt8(handle); + serializeUInt32(passkey); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaPassKeyCommand [handle="); + builder.append(handle); + builder.append(", passkey="); + builder.append(passkey); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyResponse.java new file mode 100644 index 00000000000..9ff065eb0dd --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyResponse.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command passKey. + *

+ * This command is used to enter a passkey required for Man-in-the-Middle pairing. It should be + * sent as a response to Passkey Request event. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaPassKeyResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x04; + + /** + * Command Result + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaPassKeyResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * Command Result + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaPassKeyResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyDisplayEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyDisplayEvent.java new file mode 100644 index 00000000000..83d6adfdcda --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyDisplayEvent.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command passkeyDisplayEvent. + *

+ * This event tells a passkey should be printed to the user for bonding. This passkey must be + * entered in the remote device for bonding to be successful. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaPasskeyDisplayEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x02; + + /** + * Bluetooth connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int handle; + + /** + * Passkey range: 000000-999999 + *

+ * BlueGiga API type is uint32 - Java type is {@link long} + */ + private long passkey; + + /** + * Event constructor + */ + public BlueGigaPasskeyDisplayEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + handle = deserializeUInt8(); + passkey = deserializeUInt32(); + } + + /** + * Bluetooth connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current handle as {@link int} + */ + public int getHandle() { + return handle; + } + /** + * Passkey range: 000000-999999 + *

+ * BlueGiga API type is uint32 - Java type is {@link long} + * + * @return the current passkey as {@link long} + */ + public long getPasskey() { + return passkey; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaPasskeyDisplayEvent [handle="); + builder.append(handle); + builder.append(", passkey="); + builder.append(passkey); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyRequestEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyRequestEvent.java new file mode 100644 index 00000000000..738c239c69c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyRequestEvent.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command passkeyRequestEvent. + *

+ * This event indicates the Security Manager requests the user to enter passkey. The passkey + * the user needs to enter is displayed by the remote device. Use Passkey Entry command to + * respond to request + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaPasskeyRequestEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x03; + + /** + * Bluetooth connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int handle; + + /** + * Event constructor + */ + public BlueGigaPasskeyRequestEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + handle = deserializeUInt8(); + } + + /** + * Bluetooth connection handle + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current handle as {@link int} + */ + public int getHandle() { + return handle; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaPasskeyRequestEvent [handle="); + builder.append(handle); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeCommand.java new file mode 100644 index 00000000000..b5493946fbd --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeCommand.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command setBondableMode. + *

+ * This command is used to enter a passkey required for Man-in-the-Middle pairing. It should be + * sent as a response to Passkey Request event. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetBondableModeCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x01; + + /** + * Enables or disables bonding mode. 0 : the device is not bondable. 1 : the device is bondable + *

+ * BlueGiga API type is boolean - Java type is {@link boolean} + */ + private boolean bondable; + + /** + * Enables or disables bonding mode. 0 : the device is not bondable. 1 : the device is bondable + * + * @param bondable the bondable to set as {@link boolean} + */ + public void setBondable(boolean bondable) { + this.bondable = bondable; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeBoolean(bondable); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSetBondableModeCommand [bondable="); + builder.append(bondable); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeResponse.java new file mode 100644 index 00000000000..c69cf475875 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeResponse.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command setBondableMode. + *

+ * This command is used to enter a passkey required for Man-in-the-Middle pairing. It should be + * sent as a response to Passkey Request event. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetBondableModeResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x01; + + /** + * Response constructor + */ + public BlueGigaSetBondableModeResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + } + + + @Override + public String toString() { + return "BlueGigaSetBondableModeResponse []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersCommand.java new file mode 100644 index 00000000000..8cb40799a04 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersCommand.java @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.SmpIoCapabilities; + +/** + * Class to implement the BlueGiga command setParameters. + *

+ * This command is used to configure the local Security Manager and its features + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetParametersCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x03; + + /** + * 1: Man-in-the-middle protection required. 0: No Man-in-the-middle protection. Default: + * 0 + *

+ * BlueGiga API type is boolean - Java type is {@link boolean} + */ + private boolean requireMitm; + + /** + * Minimum key size in Bytes. Range: 7-16. Default: 7 (56bits) + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int minKeySize; + + /** + * Configures the local devices I/O capabilities. See: SMP IO Capabilities for options. + * Default: No Input and No Output + *

+ * BlueGiga API type is SmpIoCapabilities - Java type is {@link SmpIoCapabilities} + */ + private SmpIoCapabilities ioCapabilities; + + /** + * 1: Man-in-the-middle protection required. 0: No Man-in-the-middle protection. Default: + * 0 + * + * @param requireMitm the requireMitm to set as {@link boolean} + */ + public void setRequireMitm(boolean requireMitm) { + this.requireMitm = requireMitm; + } + /** + * Minimum key size in Bytes. Range: 7-16. Default: 7 (56bits) + * + * @param minKeySize the minKeySize to set as {@link int} + */ + public void setMinKeySize(int minKeySize) { + this.minKeySize = minKeySize; + } + /** + * Configures the local devices I/O capabilities. See: SMP IO Capabilities for options. + * Default: No Input and No Output + * + * @param ioCapabilities the ioCapabilities to set as {@link SmpIoCapabilities} + */ + public void setIoCapabilities(SmpIoCapabilities ioCapabilities) { + this.ioCapabilities = ioCapabilities; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeBoolean(requireMitm); + serializeUInt8(minKeySize); + serializeSmpIoCapabilities(ioCapabilities); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaSetParametersCommand [requireMitm="); + builder.append(requireMitm); + builder.append(", minKeySize="); + builder.append(minKeySize); + builder.append(", ioCapabilities="); + builder.append(ioCapabilities); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersResponse.java new file mode 100644 index 00000000000..758718ed943 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersResponse.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command setParameters. + *

+ * This command is used to configure the local Security Manager and its features + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaSetParametersResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x03; + + /** + * Response constructor + */ + public BlueGigaSetParametersResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + } + + + @Override + public String toString() { + return "BlueGigaSetParametersResponse []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsCommand.java new file mode 100644 index 00000000000..777829b6d71 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsCommand.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command whitelistBonds. + *

+ * This command will add all bonded devices with a known public or static address to the local + * devices white list. Previous entries in the white list will be first cleared. This command + * can't be used while advertising, scanning or being connected. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWhitelistBondsCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x07; + + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + return getPayload(); + } + + @Override + public String toString() { + return "BlueGigaWhitelistBondsCommand []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsResponse.java new file mode 100644 index 00000000000..463adf6cbd5 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsResponse.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.security; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command whitelistBonds. + *

+ * This command will add all bonded devices with a known public or static address to the local + * devices white list. Previous entries in the white list will be first cleared. This command + * can't be used while advertising, scanning or being connected. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWhitelistBondsResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x05; + public static int COMMAND_METHOD = 0x07; + + /** + * Command result + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Number of whitelisted bonds + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int count; + + /** + * Response constructor + */ + public BlueGigaWhitelistBondsResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + count = deserializeUInt8(); + } + + /** + * Command result + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + /** + * Number of whitelisted bonds + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current count as {@link int} + */ + public int getCount() { + return count; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaWhitelistBondsResponse [result="); + builder.append(result); + builder.append(", count="); + builder.append(count); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetCommand.java new file mode 100644 index 00000000000..8035c75f402 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetCommand.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command addressGet. + *

+ * This command reads the local device's public Bluetooth address. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaAddressGetCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x02; + + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + return getPayload(); + } + + @Override + public String toString() { + return "BlueGigaAddressGetCommand []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetResponse.java new file mode 100644 index 00000000000..250f93fb762 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetResponse.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command addressGet. + *

+ * This command reads the local device's public Bluetooth address. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaAddressGetResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x02; + + /** + * Bluetooth address of the local device + *

+ * BlueGiga API type is bd_addr - Java type is {@link String} + */ + private String address; + + /** + * Response constructor + */ + public BlueGigaAddressGetResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + address = deserializeAddress(); + } + + /** + * Bluetooth address of the local device + *

+ * BlueGiga API type is bd_addr - Java type is {@link String} + * + * @return the current address as {@link String} + */ + public String getAddress() { + return address; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaAddressGetResponse [address="); + builder.append(address); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaBootEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaBootEvent.java new file mode 100644 index 00000000000..637ffcc6f38 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaBootEvent.java @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command bootEvent. + *

+ * This event is produced when the device boots up and is ready to receive commands. This event is + * not sent over USB interface. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaBootEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x00; + + /** + * Major software version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int major; + + /** + * Minor software version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int minor; + + /** + * Patch ID + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int patch; + + /** + * Build version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int build; + + /** + * Link layer version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int llVersion; + + /** + * Protocol version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int protocolVersion; + + /** + * Hardware version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int hardware; + + /** + * Event constructor + */ + public BlueGigaBootEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + major = deserializeUInt16(); + minor = deserializeUInt16(); + patch = deserializeUInt16(); + build = deserializeUInt16(); + llVersion = deserializeUInt16(); + protocolVersion = deserializeUInt16(); + hardware = deserializeUInt16(); + } + + /** + * Major software version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current major as {@link int} + */ + public int getMajor() { + return major; + } + /** + * Minor software version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current minor as {@link int} + */ + public int getMinor() { + return minor; + } + /** + * Patch ID + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current patch as {@link int} + */ + public int getPatch() { + return patch; + } + /** + * Build version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current build as {@link int} + */ + public int getBuild() { + return build; + } + /** + * Link layer version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current ll_version as {@link int} + */ + public int getLlVersion() { + return llVersion; + } + /** + * Protocol version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current protocol_version as {@link int} + */ + public int getProtocolVersion() { + return protocolVersion; + } + /** + * Hardware version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current hardware as {@link int} + */ + public int getHardware() { + return hardware; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaBootEvent [major="); + builder.append(major); + builder.append(", minor="); + builder.append(minor); + builder.append(", patch="); + builder.append(patch); + builder.append(", build="); + builder.append(build); + builder.append(", llVersion="); + builder.append(llVersion); + builder.append(", protocolVersion="); + builder.append(protocolVersion); + builder.append(", hardware="); + builder.append(hardware); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkRxEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkRxEvent.java new file mode 100644 index 00000000000..3a7b161eb8d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkRxEvent.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command endpointWatermarkRxEvent. + *

+ * This event is generated if the receive (incoming) buffer of the endpoint has been filled with + * a number of bytes equal or higher than the value defined by the command Endpoint Set + * Watermarks. Data from the receive buffer can then be read (and consequently cleared) with + * the command Endpoint Rx + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaEndpointWatermarkRxEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x02; + + /** + * Endpoint index where data was received + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int endpoint; + + /** + * Space available + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int data; + + /** + * Event constructor + */ + public BlueGigaEndpointWatermarkRxEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + endpoint = deserializeUInt8(); + data = deserializeUInt8(); + } + + /** + * Endpoint index where data was received + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current endpoint as {@link int} + */ + public int getEndpoint() { + return endpoint; + } + /** + * Space available + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current data as {@link int} + */ + public int getData() { + return data; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaEndpointWatermarkRxEvent [endpoint="); + builder.append(endpoint); + builder.append(", data="); + builder.append(data); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkTxEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkTxEvent.java new file mode 100644 index 00000000000..3320d0dd064 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkTxEvent.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command endpointWatermarkTxEvent. + *

+ * This event is generated when the transmit (outgoing) buffer of the endpoint has free space + * for a number of bytes equal or higher than the value defined by the command Endpoint Set + * Watermarks. When there is enough free space, data can be sent out of the endpoint by the + * command Endpoint Tx. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaEndpointWatermarkTxEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x03; + + /** + * Endpoint index where data was sent + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int endpoint; + + /** + * Space available + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int data; + + /** + * Event constructor + */ + public BlueGigaEndpointWatermarkTxEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + endpoint = deserializeUInt8(); + data = deserializeUInt8(); + } + + /** + * Endpoint index where data was sent + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current endpoint as {@link int} + */ + public int getEndpoint() { + return endpoint; + } + /** + * Space available + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current data as {@link int} + */ + public int getData() { + return data; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaEndpointWatermarkTxEvent [endpoint="); + builder.append(endpoint); + builder.append(", data="); + builder.append(data); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsCommand.java new file mode 100644 index 00000000000..e5f3c86c446 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsCommand.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command getConnections. + *

+ * This command reads the number of supported connections from the local device. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetConnectionsCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x06; + + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + return getPayload(); + } + + @Override + public String toString() { + return "BlueGigaGetConnectionsCommand []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsResponse.java new file mode 100644 index 00000000000..931e2d79ab2 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsResponse.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command getConnections. + *

+ * This command reads the number of supported connections from the local device. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetConnectionsResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x06; + + /** + * Max supported connections + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int maxconn; + + /** + * Response constructor + */ + public BlueGigaGetConnectionsResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + maxconn = deserializeUInt8(); + } + + /** + * Max supported connections + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current maxconn as {@link int} + */ + public int getMaxconn() { + return maxconn; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaGetConnectionsResponse [maxconn="); + builder.append(maxconn); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersCommand.java new file mode 100644 index 00000000000..43e3c12ed48 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersCommand.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command getCounters. + *

+ * Read packet counters and resets them, also returns available packet buffers. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetCountersCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x05; + + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + return getPayload(); + } + + @Override + public String toString() { + return "BlueGigaGetCountersCommand []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersResponse.java new file mode 100644 index 00000000000..627f24c47b0 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersResponse.java @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command getCounters. + *

+ * Read packet counters and resets them, also returns available packet buffers. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetCountersResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x05; + + /** + * Number of transmitted packets + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int txok; + + /** + * Number of retransmitted packets + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int txretry; + + /** + * Number of received packets where CRC was OK + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int rxok; + + /** + * Number of received packets with CRC error + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int rxfail; + + /** + * Number of available packet buffers + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + */ + private int mbuf; + + /** + * Response constructor + */ + public BlueGigaGetCountersResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + txok = deserializeUInt8(); + txretry = deserializeUInt8(); + rxok = deserializeUInt8(); + rxfail = deserializeUInt8(); + mbuf = deserializeUInt8(); + } + + /** + * Number of transmitted packets + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current txok as {@link int} + */ + public int getTxok() { + return txok; + } + /** + * Number of retransmitted packets + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current txretry as {@link int} + */ + public int getTxretry() { + return txretry; + } + /** + * Number of received packets where CRC was OK + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current rxok as {@link int} + */ + public int getRxok() { + return rxok; + } + /** + * Number of received packets with CRC error + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current rxfail as {@link int} + */ + public int getRxfail() { + return rxfail; + } + /** + * Number of available packet buffers + *

+ * BlueGiga API type is uint8 - Java type is {@link int} + * + * @return the current mbuf as {@link int} + */ + public int getMbuf() { + return mbuf; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaGetCountersResponse [txok="); + builder.append(txok); + builder.append(", txretry="); + builder.append(txretry); + builder.append(", rxok="); + builder.append(rxok); + builder.append(", rxfail="); + builder.append(rxfail); + builder.append(", mbuf="); + builder.append(mbuf); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoCommand.java new file mode 100644 index 00000000000..e6bd8600c99 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoCommand.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command getInfo. + *

+ * This command reads the local devices software and hardware versions. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetInfoCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x08; + + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + return getPayload(); + } + + @Override + public String toString() { + return "BlueGigaGetInfoCommand []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoResponse.java new file mode 100644 index 00000000000..068edde447d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoResponse.java @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command getInfo. + *

+ * This command reads the local devices software and hardware versions. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaGetInfoResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x08; + + /** + * Major software version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int major; + + /** + * Minor software version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int minor; + + /** + * Patch ID + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int patch; + + /** + * Build version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int build; + + /** + * Link layer version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int llVersion; + + /** + * Protocol version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int protocolVersion; + + /** + * Hardware version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + */ + private int hardware; + + /** + * Response constructor + */ + public BlueGigaGetInfoResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + major = deserializeUInt16(); + minor = deserializeUInt16(); + patch = deserializeUInt16(); + build = deserializeUInt16(); + llVersion = deserializeUInt16(); + protocolVersion = deserializeUInt16(); + hardware = deserializeUInt16(); + } + + /** + * Major software version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current major as {@link int} + */ + public int getMajor() { + return major; + } + /** + * Minor software version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current minor as {@link int} + */ + public int getMinor() { + return minor; + } + /** + * Patch ID + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current patch as {@link int} + */ + public int getPatch() { + return patch; + } + /** + * Build version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current build as {@link int} + */ + public int getBuild() { + return build; + } + /** + * Link layer version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current ll_version as {@link int} + */ + public int getLlVersion() { + return llVersion; + } + /** + * Protocol version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current protocol_version as {@link int} + */ + public int getProtocolVersion() { + return protocolVersion; + } + /** + * Hardware version + *

+ * BlueGiga API type is uint16 - Java type is {@link int} + * + * @return the current hardware as {@link int} + */ + public int getHardware() { + return hardware; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaGetInfoResponse [major="); + builder.append(major); + builder.append(", minor="); + builder.append(minor); + builder.append(", patch="); + builder.append(patch); + builder.append(", build="); + builder.append(build); + builder.append(", llVersion="); + builder.append(llVersion); + builder.append(", protocolVersion="); + builder.append(protocolVersion); + builder.append(", hardware="); + builder.append(hardware); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloCommand.java new file mode 100644 index 00000000000..e8b50911be0 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloCommand.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command hello. + *

+ * This command can be used to test if the local device is functional. Similar to a typical "AT" -> + * "OK" test. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaHelloCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x01; + + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + return getPayload(); + } + + @Override + public String toString() { + return "BlueGigaHelloCommand []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloResponse.java new file mode 100644 index 00000000000..b065cd0e04b --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloResponse.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command hello. + *

+ * This command can be used to test if the local device is functional. Similar to a typical "AT" -> + * "OK" test. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaHelloResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x01; + + /** + * Response constructor + */ + public BlueGigaHelloResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + } + + + @Override + public String toString() { + return "BlueGigaHelloResponse []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaNoLicenseKeyEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaNoLicenseKeyEvent.java new file mode 100644 index 00000000000..3fae3606e15 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaNoLicenseKeyEvent.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command noLicenseKeyEvent. + *

+ * This error is produced when no valid license key found form the Smart hardware. When there is + * no Bluetooth valid license key the Bluetooth radio will not be operational. A new license key + * can be requested from the Bluegiga Technical Support. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaNoLicenseKeyEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x05; + + /** + * Event constructor + */ + public BlueGigaNoLicenseKeyEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + } + + + @Override + public String toString() { + return "BlueGigaNoLicenseKeyEvent []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaProtocolErrorEvent.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaProtocolErrorEvent.java new file mode 100644 index 00000000000..7c7acca7e10 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaProtocolErrorEvent.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command protocolErrorEvent. + *

+ * A protocol error was detected in BGAPI command parser. This event is triggered if a BGAPI + * command from the host contains syntax error(s), or if a command is only partially sent. Then + * the BGAPI parser has a 1 second command timeout and if a valid command is not transmitted + * within this timeout an error is raised and the partial or wrong command will be ignored. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaProtocolErrorEvent extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x06; + + /** + * Reason for failure + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse reason; + + /** + * Event constructor + */ + public BlueGigaProtocolErrorEvent(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + reason = deserializeBgApiResponse(); + } + + /** + * Reason for failure + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current reason as {@link BgApiResponse} + */ + public BgApiResponse getReason() { + return reason; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaProtocolErrorEvent [reason="); + builder.append(reason); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetCommand.java new file mode 100644 index 00000000000..32ba6879a9b --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetCommand.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command reset. + *

+ * This command resets the local device immediately. The command does not have a response. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaResetCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x00; + + /** + * Selects the boot mode. 0 : boot to main program. 1 : boot to DFU + *

+ * BlueGiga API type is boolean - Java type is {@link boolean} + */ + private boolean bootInDfu = false; + + /** + * Selects the boot mode. 0 : boot to main program. 1 : boot to DFU + * + * @param bootInDfu the bootInDfu to set as {@link boolean} + */ + public void setBootInDfu(boolean bootInDfu) { + this.bootInDfu = bootInDfu; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeBoolean(bootInDfu); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaResetCommand [bootInDfu="); + builder.append(bootInDfu); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetResponse.java new file mode 100644 index 00000000000..8caccc5fb36 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetResponse.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command reset. + *

+ * This command resets the local device immediately. The command does not have a response. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaResetResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x00; + + /** + * Response constructor + */ + public BlueGigaResetResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + } + + + @Override + public String toString() { + return "BlueGigaResetResponse []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendCommand.java new file mode 100644 index 00000000000..fe2a56e9a1c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendCommand.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command whitelistAppend. + *

+ * Add an entry to the running white list. By the white list you can define for example the remote + * devices which are allowed to establish a connection. See also Set Filtering Connect + * Selective and (if the white list is empty they will not be active). Do not use this command + * while advertising, scanning, or while being connected. The current list is discarded upon + * reset or power-cycle. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWhitelistAppendCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x0A; + + /** + * Bluetooth device address to add to the running white list. Maximum of 8 can be stored before + * you must clear or remove entries. + *

+ * BlueGiga API type is bd_addr - Java type is {@link String} + */ + private String address; + + /** + * Bluetooth device address to add to the running white list. Maximum of 8 can be stored before + * you must clear or remove entries. + * + * @param address the address to set as {@link String} + */ + public void setAddress(String address) { + this.address = address; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeAddress(address); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaWhitelistAppendCommand [address="); + builder.append(address); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendResponse.java new file mode 100644 index 00000000000..48859101295 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendResponse.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command whitelistAppend. + *

+ * Add an entry to the running white list. By the white list you can define for example the remote + * devices which are allowed to establish a connection. See also Set Filtering Connect + * Selective and (if the white list is empty they will not be active). Do not use this command + * while advertising, scanning, or while being connected. The current list is discarded upon + * reset or power-cycle. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWhitelistAppendResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x0A; + + /** + * Command result + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaWhitelistAppendResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * Command result + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaWhitelistAppendResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearCommand.java new file mode 100644 index 00000000000..7c2a34b756d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearCommand.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command whitelistClear. + *

+ * Delete all entries on the white list at once. Do not use this command while advertising or + * while being connected. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWhitelistClearCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x0C; + + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + return getPayload(); + } + + @Override + public String toString() { + return "BlueGigaWhitelistClearCommand []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearResponse.java new file mode 100644 index 00000000000..eed25898c4a --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearResponse.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; + +/** + * Class to implement the BlueGiga command whitelistClear. + *

+ * Delete all entries on the white list at once. Do not use this command while advertising or + * while being connected. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWhitelistClearResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x0C; + + /** + * Response constructor + */ + public BlueGigaWhitelistClearResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + } + + + @Override + public String toString() { + return "BlueGigaWhitelistClearResponse []"; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveCommand.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveCommand.java new file mode 100644 index 00000000000..dc24cbb5eca --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveCommand.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaCommand; + +/** + * Class to implement the BlueGiga command whitelistRemove. + *

+ * Remove an entry from the running white list. Do not use this command while advertising or + * while being connected. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWhitelistRemoveCommand extends BlueGigaCommand { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x0B; + + /** + * Bluetooth device address to remove from the running white list. + *

+ * BlueGiga API type is bd_addr - Java type is {@link String} + */ + private String address; + + /** + * Bluetooth device address to remove from the running white list. + * + * @param address the address to set as {@link String} + */ + public void setAddress(String address) { + this.address = address; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(COMMAND_CLASS, COMMAND_METHOD); + + // Serialize the fields + serializeAddress(address); + + return getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaWhitelistRemoveCommand [address="); + builder.append(address); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveResponse.java new file mode 100644 index 00000000000..5424475d862 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveResponse.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.command.system; + +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.BlueGigaResponse; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse; + +/** + * Class to implement the BlueGiga command whitelistRemove. + *

+ * Remove an entry from the running white list. Do not use this command while advertising or + * while being connected. + *

+ * This class provides methods for processing BlueGiga API commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class BlueGigaWhitelistRemoveResponse extends BlueGigaResponse { + public static int COMMAND_CLASS = 0x00; + public static int COMMAND_METHOD = 0x0B; + + /** + * Command result + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + */ + private BgApiResponse result; + + /** + * Response constructor + */ + public BlueGigaWhitelistRemoveResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + event = (inputBuffer[0] & 0x80) != 0; + + // Deserialize the fields + result = deserializeBgApiResponse(); + } + + /** + * Command result + *

+ * BlueGiga API type is BgApiResponse - Java type is {@link BgApiResponse} + * + * @return the current result as {@link BgApiResponse} + */ + public BgApiResponse getResult() { + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("BlueGigaWhitelistRemoveResponse [result="); + builder.append(result); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirDataType.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirDataType.java new file mode 100644 index 00000000000..0d6cf9f6ef3 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirDataType.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.eir; + +import java.util.HashMap; +import java.util.Map; + +/** + * Assigned numbers are used in GAP for inquiry response, EIR data type values, manufacturer-specific data, advertising + * data, low energy UUIDs and appearance characteristics, and class of device. + * + * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile + * + * @author Chris Jackson + * + */ +public enum EirDataType { + /** + * Default unknown value + */ + UNKNOWN(-1), + NONE(0), + + EIR_FLAGS(0x01), + EIR_SVC_UUID16_INCOMPLETE(0x02), + EIR_SVC_UUID16_COMPLETE(0x03), + EIR_SVC_UUID32_INCOMPLETE(0x04), + EIR_SVC_UUID32_COMPLETE(0x05), + EIR_SVC_UUID128_INCOMPLETE(0x06), + EIR_SVC_UUID128_COMPLETE(0x07), + EIR_NAME_SHORT(0x08), + EIR_NAME_LONG(0x09), + EIR_TXPOWER(0x0A), + EIR_DEVICE_CLASS(0x0D), + EIR_SIMPLE_PAIRING_RANDOMIZER(0x0F), + EIR_SECMAN_TK_VALUE(0x10), + EIR_SECMAN_OOB_FLAGS(0x11), + EIR_SLAVEINTERVALRANGE(0x12), + EIR_SVC_SOLICIT_UUID16(0x14), + EIR_SVC_SOLICIT_UUID128(0x15), + EIR_SVC_DATA_UUID16(0x16), + EIR_PUBLIC_TARGET_ADDR(0x17), + EIR_RANDOM_TARGET_ADDR(0x18), + EIR_APPEARANCE(0x19), + EIR_ADVERTISING_INTERVAL(0x1A), + EIR_LE_DEVICE_ADDRESS(0x1B), + EIR_LE_ROLE(0x1C), + EIR_SIMPLE_PAIRING_HASH(0x1D), + EIR_SVC_SOLICIT_UUID32(0x1F), + EIR_SVC_DATA_UUID32(0x20), + EIR_SVC_DATA_UUID128(0x21), + EIR_LE_SEC_CONFIRMATION_VALUE(0x22), + EIR_LE_CONNECTION_RANDOM_VALUE(0x23), + EIR_URI(0x24), + EIR_INDOOR_POSITIONING(0x25), + EIR_LE_SUPPORTED_FEATURES(0x27), + EIR_MANUFACTURER_SPECIFIC(0xFF); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private EirDataType(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (EirDataType s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param bluetoothAddressType + * the code to lookup + * @return enumeration value. + */ + public static EirDataType getEirPacketType(int eirDataType) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(eirDataType) == null) { + return UNKNOWN; + } + + return codeMapping.get(eirDataType); + } + + /** + * Returns the Bluetooth protocol defined value for this enum + * + * @return the EIR Data type key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirFlags.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirFlags.java new file mode 100644 index 00000000000..9637432e3b7 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirFlags.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.eir; + +import java.util.HashMap; +import java.util.Map; + +/** + * Definition of the EIR Flags field + * + * @author Chris Jackson + * + */ +public enum EirFlags { + UNKNOWN(-1), + LE_LIMITED_DISCOVERABLE_MODE(0), + LE_GENERAL_DISCOVERABLE_MODE(1), + BR_EDR_NOT_SUPPORTED(2), + SIMULTANEOUS_LE_BDR_CONTROLLER(3), + SIMULTANEOUS_LE_BDR_HOST(4), + BIT5(5), + BIT6(6), + BIT7(7), + BIT8(8), + BIT9(9), + BIT10(10), + BIT11(11), + BIT12(12), + BIT13(13), + BIT14(14), + BIT15(15); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private EirFlags(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (EirFlags s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param bluetoothAddressType + * the code to lookup + * @return enumeration value. + */ + public static EirFlags getEirFlag(int eirFlag) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(eirFlag) == null) { + return UNKNOWN; + } + + return codeMapping.get(eirFlag); + } + + /** + * Returns the Bluetooth protocol defined value for this enum + * + * @return the EIR Data type key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirPacket.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirPacket.java new file mode 100644 index 00000000000..8732edb94e5 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirPacket.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.eir; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * This class processes the Extended Inquiry Response data used in the BLE advertisement frame + * + * @author Chris Jackson + * + */ +public class EirPacket { + private Map records = new HashMap(); + + public EirPacket(int[] data) { + if (data == null || data.length == 0) { + return; + } + + for (int cnt = 0; cnt < data.length;) { + if (data[cnt] == 0) { + break; + } + + int[] rawRecord = Arrays.copyOfRange(data, cnt + 1, cnt + data[cnt] + 1); + EirRecord record = new EirRecord(rawRecord); + + cnt += data[cnt] + 1; + + records.put(record.getType(), record.getRecord()); + } + } + + /** + * Returns a map of all records decoded in the packet + * + * @return {@link Map} of {@link EirDataType} to {@link Object} + */ + public Map getRecords() { + return records; + } + + /** + * Returns the specified record decoded in the packet or null if the record is not found + * + * @param recordType the requested {@link EirDataType} + * @return {@link Map} of to {@link Object} + */ + public Object getRecord(EirDataType recordType) { + return records.get(recordType); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("EirPacket [records="); + builder.append(records); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirRecord.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirRecord.java new file mode 100644 index 00000000000..f86539bce71 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/eir/EirRecord.java @@ -0,0 +1,189 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.eir; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Defines an EIR record used in the BLE advertisement packets. + * + * @author Chris Jackson + * + */ +public class EirRecord { + private EirDataType type; + private Object record; + + EirRecord(int[] data) { + if (data == null || data.length == 0) { + return; + } + + type = EirDataType.getEirPacketType(data[0]); + switch (type) { + case EIR_FLAGS: + record = processFlags(data); + break; + case EIR_MANUFACTURER_SPECIFIC: + record = processManufacturer(data); + break; + case EIR_SVC_UUID16_COMPLETE: + case EIR_SVC_UUID16_INCOMPLETE: + record = processUuid16(data); + break; + case EIR_SVC_UUID32_COMPLETE: + case EIR_SVC_UUID32_INCOMPLETE: + record = processUuid32(data); + break; + case EIR_SVC_UUID128_COMPLETE: + case EIR_SVC_UUID128_INCOMPLETE: + record = processUuid128(data); + break; + case EIR_NAME_LONG: + case EIR_NAME_SHORT: + record = processString(data); + break; + case EIR_TXPOWER: + record = processUInt8(data); + break; + case EIR_SLAVEINTERVALRANGE: + record = processUInt16List(data); + break; + case EIR_DEVICE_CLASS: + record = processUInt8(data); + break; + default: + record = processUnknown(data); + break; + } + } + + private byte[] processManufacturer(int[] data) { + // we have to drop the first byte/int + byte[] manufacturerData = new byte[data.length - 1]; + for (int i = 1; i < data.length; i++) { + manufacturerData[i - 1] = (byte) data[i]; + } + return manufacturerData; + } + + private List processUuid16(int[] data) { + List uuidList = new ArrayList(); + + for (int cnt = 1; cnt < data.length - 1; cnt += 2) { + long high = ((long) data[cnt] << 32) + ((long) data[cnt + 1] << 40); + uuidList.add(new UUID(high, 0)); + } + + return uuidList; + } + + private List processUuid32(int[] data) { + List uuidList = new ArrayList(); + + for (int cnt = 1; cnt < data.length - 1; cnt += 4) { + long high = ((long) data[cnt++] << 32) + ((long) data[cnt++] << 40) + ((long) data[cnt++] << 48) + + ((long) data[cnt++] << 56); + uuidList.add(new UUID(high, 0)); + } + + return uuidList; + } + + private List processUuid128(int[] data) { + List uuidList = new ArrayList(); + + for (int cnt = 1; cnt < data.length - 1; cnt += 16) { + long low = (data[cnt++]) + ((long) data[cnt++] << 8) + ((long) data[cnt++] << 16) + + ((long) data[cnt++] << 24) + ((long) data[cnt++] << 32) + ((long) data[cnt++] << 40) + + ((long) data[cnt++] << 48) + ((long) data[cnt++] << 56); + long high = (data[cnt++]) + ((long) data[cnt++] << 8) + ((long) data[cnt++] << 16) + + ((long) data[cnt++] << 24) + ((long) data[cnt++] << 32) + ((long) data[cnt++] << 40) + + ((long) data[cnt++] << 48) + ((long) data[cnt++] << 56); + + uuidList.add(new UUID(high, low)); + } + + return uuidList; + } + + private List processUInt16List(int[] data) { + List intList = new ArrayList(); + + for (int cnt = 1; cnt < data.length - 1; cnt += 2) { + intList.add(Integer.valueOf(data[cnt] + (data[cnt + 1] << 8))); + } + + return intList; + } + + private List processFlags(int[] data) { + List flags = new ArrayList(); + int flagBit = 0; + for (int cnt = 1; cnt < data.length; cnt++) { + for (int bitcnt = 0; bitcnt < 8; bitcnt++) { + if ((data[cnt] & (1 << bitcnt)) != 0) { + flags.add(EirFlags.getEirFlag(flagBit)); + } + flagBit++; + } + } + + return flags; + } + + private String processString(int[] data) { + StringBuilder builder = new StringBuilder(); + for (int cnt = 1; cnt < data.length; cnt++) { + builder.append((char) data[cnt]); + } + return builder.toString(); + } + + private int processUInt8(int[] data) { + if (data[1] > 127) { + return data[1] - 256; + } else { + return data[1]; + } + } + + private String processUnknown(int[] data) { + StringBuilder builder = new StringBuilder(); + for (int cnt = 0; cnt < data.length; cnt++) { + builder.append(String.format("%02X", data[cnt])); + } + return builder.toString(); + } + + public EirDataType getType() { + return type; + } + + public Object getRecord() { + return record; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("EirRecord [type="); + builder.append(type); + builder.append(", record="); + builder.append(record); + builder.append(']'); + return builder.toString(); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/AttributeChangeReason.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/AttributeChangeReason.java new file mode 100644 index 00000000000..bc9097a34aa --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/AttributeChangeReason.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to implement the BlueGiga Enumeration AttributeChangeReason. + *

+ * This enumeration contains the reason for an attribute value change. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public enum AttributeChangeReason { + /** + * Default unknown value + */ + UNKNOWN(-1), + + /** + * [0] Value was written by remote device using write request + */ + ATTRIBUTES_ATTRIBUTE_CHANGE_REASON_WRITE_REQUEST(0x0000), + + /** + * [1] Value was written by remote device using write command + */ + ATTRIBUTES_ATTRIBUTE_CHANGE_REASON_WRITE_COMMAND(0x0001), + + /** + * [2] Local attribute value was written by the remote device, but the Smart Bluetooth stack is + * waiting for the write to be confirmed by the application. User Write Response command should + * be used to send the confirmation. For this reason to appear the attribute in the GATT database + * must have the user property enabled. See Profile Toolkit Developer Guide for more + * information how to enable the user property for an attribute. + */ + ATTRIBUTES_ATTRIBUTE_CHANGE_REASON_WRITE_REQUEST_USER(0x0002); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private AttributeChangeReason(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (AttributeChangeReason s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param attributeChangeReason + * the code to lookup + * @return enumeration value. + */ + public static AttributeChangeReason getAttributeChangeReason(int attributeChangeReason) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(attributeChangeReason) == null) { + return UNKNOWN; + } + + return codeMapping.get(attributeChangeReason); + } + + /** + * Returns the BlueGiga protocol defined value for this enum + * + * @return the BGAPI enumeration key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/AttributeValueType.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/AttributeValueType.java new file mode 100644 index 00000000000..c29332bb07a --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/AttributeValueType.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to implement the BlueGiga Enumeration AttributeValueType. + *

+ * These enumerations are in the Attribute Client class + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public enum AttributeValueType { + /** + * Default unknown value + */ + UNKNOWN(-1), + + /** + * [0] Value was read + */ + ATTCLIENT_ATTRIBUTE_VALUE_TYPE_READ(0x0000), + + /** + * [1] Value was notified + */ + ATTCLIENT_ATTRIBUTE_VALUE_TYPE_NOTIFY(0x0001), + + /** + * [2] Value was indicated + */ + ATTCLIENT_ATTRIBUTE_VALUE_TYPE_INDICATE(0x0002), + + /** + * [3] Value was read + */ + ATTCLIENT_ATTRIBUTE_VALUE_TYPE_READ_BY_TYPE(0x0003), + + /** + * [4] Value was part of a long attribute + */ + ATTCLIENT_ATTRIBUTE_VALUE_TYPE_READ_BLOB(0x0004), + + /** + * [5] Value was indicated and the remote device is waiting for a confirmation. Indicate + * Confirm command can be used to send a confirmation. + */ + ATTCLIENT_ATTRIBUTE_VALUE_TYPE_INDICATE_RSP_REQ(0x0005); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private AttributeValueType(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (AttributeValueType s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param attributeValueType + * the code to lookup + * @return enumeration value. + */ + public static AttributeValueType getAttributeValueType(int attributeValueType) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(attributeValueType) == null) { + return UNKNOWN; + } + + return codeMapping.get(attributeValueType); + } + + /** + * Returns the BlueGiga protocol defined value for this enum + * + * @return the BGAPI enumeration key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/BgApiResponse.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/BgApiResponse.java new file mode 100644 index 00000000000..e7dc259407f --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/BgApiResponse.java @@ -0,0 +1,478 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to implement the BlueGiga Enumeration BgApiResponse. + *

+ * Response codes + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public enum BgApiResponse { + /** + * Default unknown value + */ + UNKNOWN(-1), + + /** + * [0] Completed successfully. + */ + SUCCESS(0x0000), + + /** + * [257] Invalid GATT connection handle. + */ + INVALID_CONN_HANDLE(0x0101), + + /** + * [258] Waiting response from GATT server to previous procedure. + */ + WAITING_RESPONSE(0x0102), + + /** + * [384] Command contained invalid parameter + */ + INVALID_PARAM(0x0180), + + /** + * [385] Device is in wrong state to receive command + */ + WRONG_STATE(0x0181), + + /** + * [386] Device has run out of memory + */ + OUT_OF_MEMORY(0x0182), + + /** + * [387] Feature is not implemented + */ + NOT_IMPLEMENTED(0x0183), + + /** + * [388] Command was not recognized + */ + INVALID_COMMAND(0x0184), + + /** + * [389] Command or Procedure failed due to timeout + */ + TIMEOUT(0x0185), + + /** + * [390] Connection handle passed is to command is not a valid handle + */ + NOT_CONNECTED(0x0186), + + /** + * [391] Command would cause either underflow or overflow error + */ + FLOW(0x0187), + + /** + * [392] User attribute was accessed through API which is not supported + */ + USER_ATTRIBUTE(0x0188), + + /** + * [393] No valid license key found + */ + INVALID_LICENSE_KEY(0x0189), + + /** + * [394] Command maximum length exceeded + */ + COMMAND_TOO_LONG(0x018A), + + /** + * [395] Bonding procedure can't be started because device has no space left for bond. + */ + OUT_OF_BONDS(0x018B), + + /** + * [396] Unspecified error + */ + UNSPECIFIED(0x018C), + + /** + * [397] Hardware failure + */ + HARDWARE(0x018D), + + /** + * [398] Command not accepted, because internal buffers are full + */ + BUFFERS_FULL(0x018E), + + /** + * [399] Command or Procedure failed due to disconnection + */ + DISCONNECTED(0x018F), + + /** + * [400] Too many Simultaneous Requests + */ + TOO_MANY_REQUESTS(0x0190), + + /** + * [401] Feature is not supported in this firmware build + */ + NOT_SUPPORTED(0x0191), + + /** + * [402] The bonding does not exist. + */ + NO_BONDING(0x0192), + + /** + * [403] Error using crypto functions + */ + CRYPTO(0x0193), + + /** + * [514] A command was sent from the Host that should identify a connection, but that connection + * does not exist. + */ + UNKNOWN_CONNECTION_IDENTIFIER(0x0202), + + /** + * [520] Link supervision timeout has expired. + */ + CONNECTION_TIMEOUT(0x0208), + + /** + * [521] Controller is at limit of connections it can support. + */ + CONNECTION_LIMIT_EXCEEDED(0x0209), + + /** + * [522] + */ + SYNCHRONOUS_CONNECTIONTION_LIMIT_EXCEEDED(0x020A), + + /** + * [523] The ACL Connection Already Exists error code indicates that an attempt to create a new + * ACL Connection to a device when there is already a connection to this device. + */ + ACL_CONNECTION_ALREADY_EXISTS(0x020B), + + /** + * [524] Command requested cannot be executed because the Controller is in a state where it + * cannot process this command at this time. + */ + COMMAND_DISALLOWED(0x020C), + + /** + * [525] The Connection Rejected Due To Limited Resources error code indicates that an + * incoming connection was rejected due to limited resources. + */ + CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES(0x020D), + + /** + * [526] The Connection Rejected Due To Security Reasons error code indicates that a + * connection was rejected due to security requirements not being fulfilled, like + * authentication or pairing. + */ + CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS(0x020E), + + /** + * [527] The Connection was rejected because this device does not accept the BD_ADDR. This may + * be because the device will only accept connections from specific BD_ADDRs. + */ + CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR(0x020F), + + /** + * [528] The Connection Accept Timeout has been exceeded for this connection attempt. + */ + CONNECTION_ACCEPT_TIMEOUT_EXCEEDED(0x0210), + + /** + * [529] A feature or parameter value in the HCI command is not supported. + */ + UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE(0x0211), + + /** + * [530] Command contained invalid parameters. + */ + INVALID_COMMAND_PARAMETERS(0x0212), + + /** + * [531] User on the remote device terminated the connection. + */ + REMOTE_USER_TERMINATED(0x0213), + + /** + * [532] The remote device terminated the connection because of low resources + */ + REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES(0x0214), + + /** + * [533] Remote Device Terminated Connection due to Power Off + */ + REMOTE_POWERING_OFF(0x0215), + + /** + * [534] Local device terminated the connection. + */ + CONNECTION_TERMINATED_BY_LOCAL_HOST(0x0216), + + /** + * [535] The Controller is disallowing an authentication or pairing procedure because too + * little time has elapsed since the last authentication or pairing attempt failed. + */ + REPEATED_ATTEMPTS(0x0217), + + /** + * [536] The device does not allow pairing. This can be for example, when a device only allows + * pairing during a certain time window after some user input allows pairing + */ + PAIRING_NOT_ALLOWED(0x0218), + + /** + * [537] The Controller has received an unknown LMP OpCode. + */ + UNKNOWN_LMP_PDU(0x0219), + + /** + * [538] The remote device does not support the feature associated with the issued command or + * LMP PDU. + */ + UNSUPPORTED_REMOTE_FEATURE(0x021A), + + /** + * [560] A parameter value requested is outside the mandatory range of parameters for the given + * HCI command or LMP PDU. + */ + PARAMETER_OUT_OF_MANDATORY_RANGE(0x0230), + + /** + * [569] The Controller could not calculate an appropriate value for the Channel selection + * operation. + */ + CONNECTION_REJECTED_NO_SUITABLE_CHANNEL(0x0239), + + /** + * [570] Operation was rejected because the controller is busy and unable to process the + * request. + */ + CONTROLLER_BUSY(0x023A), + + /** + * [571] Remote device terminated the connection because of an unacceptable connection + * interval. + */ + UNACCEPTABLE_CONNECTION_INTERVAL(0x023B), + + /** + * [573] Connection was terminated because the Message Integrity Check (MIC) failed on a + * received packet. + */ + CONNECTION_TERMINATED_DUE_TO_MIC_FAILURE(0x023D), + + /** + * [574] LL initiated a connection but the connection has failed to be established. Controller + * did not receive any packets from remote end. + */ + CONNECTION_FAILED_TO_BE_ESTABLISHED(0x023E), + + /** + * [575] The MAC of the 802.11 AMP was requested to connect to a peer, but the connection failed. + */ + MAC_CONNECTION_FAILED(0x023F), + + /** + * [576] The master, at this time, is unable to make a coarse adjustment to the piconet clock, + * using the supplied parameters. Instead the master will attempt to move the clock using clock + * dragging. + */ + COARSE_CLOCK_ADJUSTMENT_REJECTED(0x0240), + + /** + * [769] The user input of passkey failed, for example, the user cancelled the operation + */ + PASSKEY_ENTRY_FAILED(0x0301), + + /** + * [1025] The attribute handle given was not valid on this server + */ + INVALID_HANDLE(0x0401), + + /** + * [1026] The attribute cannot be read + */ + READ_NOT_PERMITTED(0x0402), + + /** + * [1027] The attribute cannot be written + */ + WRITE_NOT_PERMITTED(0x0403), + + /** + * [1028] The attribute PDU was invalid + */ + INVALID_PDU(0x0404), + + /** + * [1029] The attribute requires authentication before it can be read or written. + */ + INSUFFICIENT_AUTHENTICATION(0x0405), + + /** + * [1030] Attribute Server does not support the request received from the client. + */ + REQUEST_NOT_SUPPORTED(0x0406), + + /** + * [1031] Offset specified was past the end of the attribute + */ + INVALID_OFFSET(0x0407), + + /** + * [1032] The attribute requires authorization before it can be read or written. + */ + INSUFFICIENT_AUTHORIZATION(0x0408), + + /** + * [1033] Too many prepare writes have been queueud + */ + PREPARE_QUEUE_FULL(0x0409), + + /** + * [1034] No attribute found within the given attribute handle range. + */ + ATT_NOT_FOUND(0x040A), + + /** + * [1035] The attribute cannot be read or written using the Read Blob Request + */ + ATT_NOT_LONG(0x040B), + + /** + * [1037] The attribute value length is invalid for the operation + */ + INVALID_ATT_LENGTH(0x040D), + + /** + * [1040] The attribute type is not a supported grouping attribute as defined by a higher layer + * specification. + */ + UNSUPPORTED_GROUP_TYPE(0x0410), + + /** + * [1041] Insufficient Resources to complete the request + */ + INSUFFICIENT_RESOURCES(0x0411), + + /** + * [1152] Application error code defined by a higher layer specification. + */ + APPLICATION(0x0480), + + /** + * [1537] Service Record not found + */ + RECORD_NOT_FOUND(0x0601), + + /** + * [1538] Service Record with this handle already exist + */ + RECORD_ALREADY_EXIST(0x0602), + + /** + * [2305] File not found. + */ + FILE_NOT_FOUND(0x0901), + + /** + * [2561] File open failed. + */ + FILE_OPEN_FAILED(0x0A01), + + /** + * [2562] XML parsing failed. + */ + XML_PARSE_FAILED(0x0A02), + + /** + * [2563] Device connection failed. + */ + DEVICE_CONNECTION_FAILED(0x0A03), + + /** + * [2817] Device firmware signature verification failed. + */ + IMAGE_SIGNATURE_VERIFICATION_FAILED(0x0B01), + + /** + * [2818] File signature verification failed. + */ + FILE_SIGNATURE_VERIFICATION_FAILED(0x0B02), + + /** + * [2819] Device firmware checksum is not valid. + */ + IMAGE_CHECKSUM_ERROR(0x0B03); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private BgApiResponse(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (BgApiResponse s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param bgApiResponse + * the code to lookup + * @return enumeration value. + */ + public static BgApiResponse getBgApiResponse(int bgApiResponse) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(bgApiResponse) == null) { + return UNKNOWN; + } + + return codeMapping.get(bgApiResponse); + } + + /** + * Returns the BlueGiga protocol defined value for this enum + * + * @return the BGAPI enumeration key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/BluetoothAddressType.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/BluetoothAddressType.java new file mode 100644 index 00000000000..bee7245198c --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/BluetoothAddressType.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to implement the BlueGiga Enumeration BluetoothAddressType. + *

+ * Bluetooth address types + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public enum BluetoothAddressType { + /** + * Default unknown value + */ + UNKNOWN(-1), + + /** + * [0] Public Address + */ + GAP_ADDRESS_TYPE_PUBLIC(0x0000), + + /** + * [1] Random Address + */ + GAP_ADDRESS_TYPE_RANDOM(0x0001); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private BluetoothAddressType(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (BluetoothAddressType s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param bluetoothAddressType + * the code to lookup + * @return enumeration value. + */ + public static BluetoothAddressType getBluetoothAddressType(int bluetoothAddressType) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(bluetoothAddressType) == null) { + return UNKNOWN; + } + + return codeMapping.get(bluetoothAddressType); + } + + /** + * Returns the BlueGiga protocol defined value for this enum + * + * @return the BGAPI enumeration key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/ConnectionStatusFlag.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/ConnectionStatusFlag.java new file mode 100644 index 00000000000..5de4a9105af --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/ConnectionStatusFlag.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to implement the BlueGiga Enumeration ConnectionStatusFlag. + *

+ * The possible connection status flags are described in the table below. The flags field is a + * bit mask, so multiple flags can be set at a time. If the bit is 1 the flag is active and if the bit is + * 0 the flag is inactive. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public enum ConnectionStatusFlag { + /** + * Default unknown value + */ + UNKNOWN(-1), + + /** + * [1] This status flag tells the connection exists to a remote device. + */ + CONNECTION_CONNECTED(0x0001), + + /** + * [2] This flag tells the connection is encrypted. + */ + CONNECTION_ENCRYPTED(0x0002), + + /** + * [4] Connection completed flag, which is used to tell a new connection has been created. + */ + CONNECTION_COMPLETED(0x0004), + + /** + * [8] This flag tells that connection parameters have changed and. It is set when connection + * parameters have changed due to a link layer operation. + */ + CONNECTION_PARAMETERS_CHANGE(0x0008); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private ConnectionStatusFlag(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (ConnectionStatusFlag s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param connectionStatusFlag + * the code to lookup + * @return enumeration value. + */ + public static ConnectionStatusFlag getConnectionStatusFlag(int connectionStatusFlag) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(connectionStatusFlag) == null) { + return UNKNOWN; + } + + return codeMapping.get(connectionStatusFlag); + } + + /** + * Returns the BlueGiga protocol defined value for this enum + * + * @return the BGAPI enumeration key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapConnectableMode.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapConnectableMode.java new file mode 100644 index 00000000000..d7d8b58a3aa --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapConnectableMode.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to implement the BlueGiga Enumeration GapConnectableMode. + *

+ * GAP connectable modes + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public enum GapConnectableMode { + /** + * Default unknown value + */ + UNKNOWN(-1), + + /** + * [0] Not connectable + */ + GAP_NON_CONNECTABLE(0x0000), + + /** + * [1] Directed Connectable + */ + GAP_DIRECTED_CONNECTABLE(0x0001), + + /** + * [2] Undirected connectable + */ + GAP_UNDIRECTED_CONNECTABLE(0x0002), + + /** + * [3] Same as non-connectable, but also supports ADV_SCAN_IND packets. Device accepts scan + * requests (active scanning) but is not connectable. + */ + GAP_SCANNABLE_NON_CONNECTABLE(0x0003); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private GapConnectableMode(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (GapConnectableMode s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param gapConnectableMode + * the code to lookup + * @return enumeration value. + */ + public static GapConnectableMode getGapConnectableMode(int gapConnectableMode) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(gapConnectableMode) == null) { + return UNKNOWN; + } + + return codeMapping.get(gapConnectableMode); + } + + /** + * Returns the BlueGiga protocol defined value for this enum + * + * @return the BGAPI enumeration key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverMode.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverMode.java new file mode 100644 index 00000000000..06c784bdd23 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverMode.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to implement the BlueGiga Enumeration GapDiscoverMode. + *

+ * GAP discover modes + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public enum GapDiscoverMode { + /** + * Default unknown value + */ + UNKNOWN(-1), + + /** + * [0] Discover only limited discoverable devices, that is, Slaves which have the LE Limited + * Discoverable Mode bit set in the AD type of their Flags advertisement packets. + */ + GAP_DISCOVER_LIMITED(0x0000), + + /** + * [1] Discover limited and generic discoverable devices, that is, Slaves which have the LE + * Limited Discoverable Mode LE General or the Discoverable Mode bit set in the AD type of their + * advertisement Flags packets. + */ + GAP_DISCOVER_GENERIC(0x0001), + + /** + * [2] Discover all devices regardless of the AD type, so also devices in Flags + * non-discoverable mode will be reported to host. + */ + GAP_DISCOVER_OBSERVATION(0x0002), + + /** + * [3] Same as gap_non_discoverable. + */ + GAP_BROADCAST(0x0003), + + /** + * [4] In this advertisement the advertisement and scan response data defined by user will be + * used. The user is responsible of building the advertisement data so that it also contains the + * appropriate desired Flags AD type. + */ + GAP_USER_DATA(0x0004), + + /** + * [128] When turning the most highest bit on in GAP discoverable mode, the remote devices that + * send scan request packets to the advertiser are reported back to the application through + * Scan Response event. This is so called Enhanced Broadcasting mode. + */ + GAP_ENHANCED_BROADCASTING(0x0080); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private GapDiscoverMode(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (GapDiscoverMode s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param gapDiscoverMode + * the code to lookup + * @return enumeration value. + */ + public static GapDiscoverMode getGapDiscoverMode(int gapDiscoverMode) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(gapDiscoverMode) == null) { + return UNKNOWN; + } + + return codeMapping.get(gapDiscoverMode); + } + + /** + * Returns the BlueGiga protocol defined value for this enum + * + * @return the BGAPI enumeration key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverableMode.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverableMode.java new file mode 100644 index 00000000000..94f7e5ecfcf --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverableMode.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to implement the BlueGiga Enumeration GapDiscoverableMode. + *

+ * GAP discoverable modes + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public enum GapDiscoverableMode { + /** + * Default unknown value + */ + UNKNOWN(-1), + + /** + * [0] Non-discoverable mode: the LE Limited Discoverable Mode and the LE General + * Discoverable Mode bits are NOT set in the AD Flags type. A master can still connect to the + * advertising slave in this mode. + */ + GAP_NON_DISCOVERABLE(0x0000), + + /** + * [1] Discoverable using limited scanning mode: the advertisement packets will carry the LE + * Limited Discoverable Mode bit set in the Flags AD type. + */ + GAP_LIMITED_DISCOVERABLE(0x0001), + + /** + * [2] Discoverable using general scanning mode: the advertisement packets will carry the LE + * General Discoverable Mode bit set in the Flags AD type. + */ + GAP_GENERAL_DISCOVERABLE(0x0002), + + /** + * [3] Same as gap_non_discoverable. + */ + GAP_BROADCAST(0x0003), + + /** + * [4] In this advertisement the advertisement and scan response data defined by user will be + * used. The user is responsible of building the advertisement data so that it also contains the + * appropriate desired Flags AD type. + */ + GAP_USER_DATA(0x0004), + + /** + * [128] When turning the most highest bit on in GAP discoverable mode, the remote devices that + * send scan request packets to the advertiser are reported back to the application through + * Scan Response event. This is so called Enhanced Broadcasting mode. + */ + GAP_ENHANCED_BROADCASTING(0x0080); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private GapDiscoverableMode(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (GapDiscoverableMode s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param gapDiscoverableMode + * the code to lookup + * @return enumeration value. + */ + public static GapDiscoverableMode getGapDiscoverableMode(int gapDiscoverableMode) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(gapDiscoverableMode) == null) { + return UNKNOWN; + } + + return codeMapping.get(gapDiscoverableMode); + } + + /** + * Returns the BlueGiga protocol defined value for this enum + * + * @return the BGAPI enumeration key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/ScanResponseType.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/ScanResponseType.java new file mode 100644 index 00000000000..2e44310af43 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/ScanResponseType.java @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to implement the BlueGiga Enumeration ScanResponseType. + *

+ * Defines the packet types received during a scan response + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public enum ScanResponseType { + /** + * Default unknown value + */ + UNKNOWN(-1), + + /** + * [0] Connectable Advertisement packet + */ + CONNECTABLE_ADVERTISEMENT(0x0000), + + /** + * [2] Non Connectable Advertisement packet + */ + NON_CONNECTABLE_ADVERTISEMENT(0x0002), + + /** + * [4] Scan response packet + */ + SCAN_RESPONSE(0x0004), + + /** + * [6] Discoverable advertisement packet + */ + DISCOVERABLE_ADVERTISEMENT(0x0006); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private ScanResponseType(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (ScanResponseType s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param scanResponseType + * the code to lookup + * @return enumeration value. + */ + public static ScanResponseType getScanResponseType(int scanResponseType) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(scanResponseType) == null) { + return UNKNOWN; + } + + return codeMapping.get(scanResponseType); + } + + /** + * Returns the BlueGiga protocol defined value for this enum + * + * @return the BGAPI enumeration key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/SmpIoCapabilities.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/SmpIoCapabilities.java new file mode 100644 index 00000000000..391bb19fc99 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/enumeration/SmpIoCapabilities.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.enumeration; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to implement the BlueGiga Enumeration SmpIoCapabilities. + *

+ * Security Manager I/O Capabilities + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public enum SmpIoCapabilities { + /** + * Default unknown value + */ + UNKNOWN(-1), + + /** + * [0] Display Only + */ + SM_IO_CAPABILITY_DISPLAYONLY(0x0000), + + /** + * [1] Display with Yes/No-buttons + */ + SM_IO_CAPABILITY_DISPLAYYESNO(0x0001), + + /** + * [2] Keyboard Only + */ + SM_IO_CAPABILITY_KEYBOARDONLY(0x0002), + + /** + * [3] No Input and No Output + */ + SM_IO_CAPABILITY_NOINPUTNOOUTPUT(0x0003), + + /** + * [4] Display with Keyboard + */ + SM_IO_CAPABILITY_KEYBOARDDISPLAY(0x0004); + + /** + * A mapping between the integer code and its corresponding type to + * facilitate lookup by code. + */ + private static Map codeMapping; + + private int key; + + private SmpIoCapabilities(int key) { + this.key = key; + } + + private static void initMapping() { + codeMapping = new HashMap(); + for (SmpIoCapabilities s : values()) { + codeMapping.put(s.key, s); + } + } + + /** + * Lookup function based on the type code. Returns null if the code does not exist. + * + * @param smpIoCapabilities + * the code to lookup + * @return enumeration value. + */ + public static SmpIoCapabilities getSmpIoCapabilities(int smpIoCapabilities) { + if (codeMapping == null) { + initMapping(); + } + + if (codeMapping.get(smpIoCapabilities) == null) { + return UNKNOWN; + } + + return codeMapping.get(smpIoCapabilities); + } + + /** + * Returns the BlueGiga protocol defined value for this enum + * + * @return the BGAPI enumeration key + */ + public int getKey() { + return key; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/factory/BlueGigaHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/factory/BlueGigaHandlerFactory.java new file mode 100644 index 00000000000..49ff70fa048 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/internal/factory/BlueGigaHandlerFactory.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.bluegiga.internal.factory; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; + +import org.eclipse.smarthome.binding.bluetooth.BluetoothAdapter; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.BlueGigaAdapterConstants; +import org.eclipse.smarthome.binding.bluetooth.bluegiga.handler.BlueGigaBridgeHandler; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.UID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; + +/** + * The {@link BlueGigaHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Chris Jackson - Initial contribution + * @author Kai Kreuzer - added support for adapter service registration + */ +@Component(service = ThingHandlerFactory.class, immediate = true, configurationPid = "binding.bluegiga", configurationPolicy = ConfigurationPolicy.OPTIONAL) +public class BlueGigaHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .singleton(BlueGigaAdapterConstants.THING_TYPE_BLUEGIGA); + + private final Map> serviceRegs = new HashMap<>(); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(BlueGigaAdapterConstants.THING_TYPE_BLUEGIGA)) { + BlueGigaBridgeHandler handler = new BlueGigaBridgeHandler((Bridge) thing); + registerBluetoothAdapter(handler); + return handler; + } else { + return null; + } + } + + private synchronized void registerBluetoothAdapter(BluetoothAdapter adapter) { + this.serviceRegs.put(adapter.getUID(), bundleContext.registerService(BluetoothAdapter.class.getName(), adapter, + new Hashtable())); + } + + @Override + protected synchronized void removeHandler(ThingHandler thingHandler) { + if (thingHandler instanceof BluetoothAdapter) { + UID uid = ((BluetoothAdapter) thingHandler).getUID(); + ServiceRegistration serviceReg = this.serviceRegs.get(uid); + if (serviceReg != null) { + serviceReg.unregister(); + serviceRegs.remove(uid); + } + } + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/.classpath b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/.classpath new file mode 100644 index 00000000000..7f457fa4138 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/.project b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/.project new file mode 100644 index 00000000000..a58bd02b7d9 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/.project @@ -0,0 +1,33 @@ + + + org.eclipse.smarthome.binding.bluetooth.blukii + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/ESH-INF/thing/blukii.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/ESH-INF/thing/blukii.xml new file mode 100644 index 00000000000..e7c3777a349 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/ESH-INF/thing/blukii.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + A Blukii Button + + + + + + + + + + + + + + + + + + + + + Bluetooth address in XX:XX:XX:XX:XX:XX format + + + + + + + Switch + + + + Number + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..d83692ace38 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/META-INF/MANIFEST.MF @@ -0,0 +1,29 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Blukii Binding +Bundle-SymbolicName: org.eclipse.smarthome.binding.bluetooth.blukii +Bundle-Vendor: Eclipse.org/SmartHome +Bundle-Version: 0.10.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ClassPath: . +Import-Package: + org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.binding.bluetooth, + org.eclipse.smarthome.binding.bluetooth.discovery, + org.eclipse.smarthome.binding.bluetooth.notification, + org.eclipse.smarthome.config.core, + org.eclipse.smarthome.config.discovery, + org.eclipse.smarthome.core.common.registry, + org.eclipse.smarthome.core.library.types, + org.eclipse.smarthome.core.thing, + org.eclipse.smarthome.core.thing.binding, + org.eclipse.smarthome.core.thing.binding.builder, + org.eclipse.smarthome.core.thing.binding.firmware, + org.eclipse.smarthome.core.thing.type, + org.eclipse.smarthome.core.types, + org.slf4j +Service-Component: OSGI-INF/*.xml +Export-Package: + org.eclipse.smarthome.binding.bluetooth.blukii, + org.eclipse.smarthome.binding.bluetooth.blukii.handler +Bundle-ActivationPolicy: lazy diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/NOTICE b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/NOTICE new file mode 100644 index 00000000000..b8675cd02e8 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/NOTICE @@ -0,0 +1,19 @@ +This content is produced and maintained by the Eclipse SmartHome project. + +* Project home: https://eclipse.org/smarthome/ + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/eclipse/smarthome + +== Copyright Holders + +See the NOTICE file distributed with the source code at +https://github.com/eclipse/smarthome/blob/master/NOTICE +for detailed information regarding copyright ownership. diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/OSGI-INF/.gitignore b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/OSGI-INF/.gitignore new file mode 100644 index 00000000000..b878e882aca --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/OSGI-INF/.gitignore @@ -0,0 +1 @@ +/*.xml diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/README.md b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/README.md new file mode 100644 index 00000000000..7f9817a86ca --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/README.md @@ -0,0 +1,52 @@ +# YeeLightBlue Binding + +_Give some details about what this binding is meant for - a protocol, system, specific device._ + +_If possible, provide some resources like pictures, a YouTube video, etc. to give an impression of what can be done with this binding. You can place such resources into a `doc` folder next to this README.md._ + +## Supported Things + +_Please describe the different supported things / devices within this section._ +_Which different types are supported, which models were tested etc.?_ +_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._ + +## Discovery + +_Describe the available auto-discovery features here. Mention for what it works and what needs to be kept in mind when using it._ + +## Binding Configuration + +_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it. In this section, you should link to this file and provide some information about the options. The file could e.g. look like:_ + +``` +# Configuration for the Philips Hue Binding +# +# Default secret key for the pairing of the Philips Hue Bridge. +# It has to be between 10-40 (alphanumeric) characters +# This may be changed by the user for security reasons. +secret=EclipseSmartHome +``` + +_Note that it is planned to generate some part of this based on the information that is available within ```ESH-INF/binding``` of your binding._ + +_If your binding does not offer any generic configurations, you can remove this section completely._ + +## Thing Configuration + +_Describe what is needed to manually configure a thing, either through the (Paper) UI or via a thing-file. This should be mainly about its mandatory and optional configuration parameters. A short example entry for a thing file can help!_ + +_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._ + +## Channels + +_Here you should provide information about available channel types, what their meaning is and how they can be used._ + +_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._ + +## Full Example + +_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._ + +## Any custom content here! + +_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/build.properties b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/build.properties new file mode 100644 index 00000000000..a6cfff567c9 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/build.properties @@ -0,0 +1,7 @@ +source..=src/main/java/ +output..=target/classes +bin.includes=META-INF/,\ + .,\ + OSGI-INF/,\ + ESH-INF/,\ + NOTICE diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/pom.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/pom.xml new file mode 100644 index 00000000000..026de1da344 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/pom.xml @@ -0,0 +1,18 @@ + + + + 4.0.0 + + + org.eclipse.smarthome.binding + pom + 0.10.0-SNAPSHOT + + + org.eclipse.smarthome.binding.bluetooth.blukii + + Eclipse SmartHome Blukii Binding + eclipse-plugin + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/BlukiiBindingConstants.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/BlukiiBindingConstants.java new file mode 100644 index 00000000000..f65105e65ba --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/BlukiiBindingConstants.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.blukii; + +import java.util.UUID; + +import org.eclipse.smarthome.binding.bluetooth.BluetoothBindingConstants; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; + +/** + * The {@link BlukiiBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Kai Kreuzer - Initial contribution + */ +public class BlukiiBindingConstants { + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_BUTTON = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID, + "blukii_bts"); + + public final static String BLUKII_PREFIX = "blukii"; + + // Channel IDs + public final static String CHANNEL_ID_ACCELREPORT = "accelReport"; + public final static String CHANNEL_ID_X = "accelX"; + public final static String CHANNEL_ID_Y = "accelY"; + public final static String CHANNEL_ID_Z = "accelZ"; + + // Channel types UIDs + public final static ChannelTypeUID CHANNEL_TYPE_UID_ACCEL_REPORT = new ChannelTypeUID( + BluetoothBindingConstants.BINDING_ID, "blukii_accel_report"); + public final static ChannelTypeUID CHANNEL_TYPE_UID_ACCEL = new ChannelTypeUID(BluetoothBindingConstants.BINDING_ID, + "blukii_accel"); + + // Characteristics + public final static UUID CHAR_ACCEL_REPORT = UUID.fromString("0000feb1-0000-1000-8000-00805f9b34fb"); + public final static UUID CHAR_ACCEL_X = UUID.fromString("0000feb3-0000-1000-8000-00805f9b34fb"); + public final static UUID CHAR_ACCEL_Y = UUID.fromString("0000feb4-0000-1000-8000-00805f9b34fb"); + public final static UUID CHAR_ACCEL_Z = UUID.fromString("0000feb5-0000-1000-8000-00805f9b34fb"); +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/handler/BlukiiHandler.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/handler/BlukiiHandler.java new file mode 100644 index 00000000000..216270adc30 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/handler/BlukiiHandler.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.blukii.handler; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.smarthome.binding.bluetooth.BluetoothCharacteristic; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDeviceListener; +import org.eclipse.smarthome.binding.bluetooth.ConnectedBluetoothHandler; +import org.eclipse.smarthome.binding.bluetooth.blukii.BlukiiBindingConstants; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link BlukiiHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Kai Kreuzer - Initial contribution and API + */ +public class BlukiiHandler extends ConnectedBluetoothHandler implements BluetoothDeviceListener { + + private final Logger logger = LoggerFactory.getLogger(BlukiiHandler.class); + + public BlukiiHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) { + super.handleCommand(channelUID, command); + if (channelUID.getId().equals(BlukiiBindingConstants.CHANNEL_ID_ACCELREPORT) && command instanceof OnOffType) { + BluetoothCharacteristic characteristic = device.getCharacteristic(BlukiiBindingConstants.CHAR_ACCEL_REPORT); + int[] value = command == OnOffType.ON ? new int[] { 1 } : new int[] { 0 }; + characteristic.setValue(value); + device.writeCharacteristic(characteristic); + } + } + + @Override + public void onServicesDiscovered() { + super.onServicesDiscovered(); + BluetoothCharacteristic xAccel = device.getCharacteristic(BlukiiBindingConstants.CHAR_ACCEL_X); + BluetoothCharacteristic yAccel = device.getCharacteristic(BlukiiBindingConstants.CHAR_ACCEL_Y); + BluetoothCharacteristic zAccel = device.getCharacteristic(BlukiiBindingConstants.CHAR_ACCEL_Z); + activateChannel(xAccel, BlukiiBindingConstants.CHANNEL_TYPE_UID_ACCEL, BlukiiBindingConstants.CHANNEL_ID_X); + activateChannel(yAccel, BlukiiBindingConstants.CHANNEL_TYPE_UID_ACCEL, BlukiiBindingConstants.CHANNEL_ID_Y); + activateChannel(zAccel, BlukiiBindingConstants.CHANNEL_TYPE_UID_ACCEL, BlukiiBindingConstants.CHANNEL_ID_Z); + } + + @Override + public void onCharacteristicUpdate(@NonNull BluetoothCharacteristic characteristic) { + super.onCharacteristicUpdate(characteristic); + if (characteristic.getUuid().equals(BlukiiBindingConstants.CHAR_ACCEL_REPORT)) { + OnOffType enabled = characteristic.getValue()[0] == 1 ? OnOffType.ON : OnOffType.OFF; + updateState(BlukiiBindingConstants.CHANNEL_ID_ACCELREPORT, enabled); + } + if (characteristic.getUuid().equals(BlukiiBindingConstants.CHAR_ACCEL_X)) { + Integer x = characteristic.getIntValue(BluetoothCharacteristic.FORMAT_SINT16, 0); + updateState(BlukiiBindingConstants.CHANNEL_ID_X, new DecimalType(x)); + } + if (characteristic.getUuid().equals(BlukiiBindingConstants.CHAR_ACCEL_Y)) { + Integer y = characteristic.getIntValue(BluetoothCharacteristic.FORMAT_SINT16, 0); + updateState(BlukiiBindingConstants.CHANNEL_ID_Y, new DecimalType(y)); + } + if (characteristic.getUuid().equals(BlukiiBindingConstants.CHAR_ACCEL_Z)) { + Integer z = characteristic.getIntValue(BluetoothCharacteristic.FORMAT_SINT16, 0); + updateState(BlukiiBindingConstants.CHANNEL_ID_Z, new DecimalType(z)); + } + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/internal/BlukiiDiscoveryParticipant.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/internal/BlukiiDiscoveryParticipant.java new file mode 100644 index 00000000000..acb73a6bd1d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/internal/BlukiiDiscoveryParticipant.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.blukii.internal; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.binding.bluetooth.BluetoothBindingConstants; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDevice; +import org.eclipse.smarthome.binding.bluetooth.blukii.BlukiiBindingConstants; +import org.eclipse.smarthome.binding.bluetooth.discovery.BluetoothDiscoveryParticipant; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; + +/** + * This discovery participant is able to recognize blukii devices and create discovery results for them. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +@Component(immediate = true) +public class BlukiiDiscoveryParticipant implements BluetoothDiscoveryParticipant { + + @Override + public @NonNull Set<@NonNull ThingTypeUID> getSupportedThingTypeUIDs() { + return Collections.singleton(BlukiiBindingConstants.THING_TYPE_BUTTON); + } + + @Override + public @Nullable ThingUID getThingUID(@NonNull BluetoothDevice device) { + if (device.getName() != null && device.getName().startsWith(BlukiiBindingConstants.BLUKII_PREFIX)) { + return new ThingUID(BlukiiBindingConstants.THING_TYPE_BUTTON, device.getAdapter().getUID(), + device.getAddress().toString().toLowerCase().replace(":", "")); + } else { + return null; + } + } + + @Override + public DiscoveryResult createResult(@NonNull BluetoothDevice device) { + ThingUID thingUID = getThingUID(device); + + if (thingUID != null) { + String label = device.getName(); + + Map properties = new HashMap<>(); + properties.put(BluetoothBindingConstants.CONFIGURATION_ADDRESS, device.getAddress().toString()); + properties.put(Thing.PROPERTY_VENDOR, "Schneider Schreibgeräte GmbH"); + Integer txPower = device.getTxPower(); + if (txPower != null) { + properties.put(BluetoothBindingConstants.PROPERTY_TXPOWER, Integer.toString(txPower)); + } + + // Create the discovery result and add to the inbox + return DiscoveryResultBuilder.create(thingUID).withProperties(properties) + .withRepresentationProperty(BluetoothBindingConstants.CONFIGURATION_ADDRESS) + .withBridge(device.getAdapter().getUID()).withLabel(label).build(); + } else { + return null; + } + } + +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/internal/BlukiiHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/internal/BlukiiHandlerFactory.java new file mode 100644 index 00000000000..227f5a20b53 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.blukii/src/main/java/org/eclipse/smarthome/binding/bluetooth/blukii/internal/BlukiiHandlerFactory.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.blukii.internal; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.smarthome.binding.bluetooth.blukii.BlukiiBindingConstants; +import org.eclipse.smarthome.binding.bluetooth.blukii.handler.BlukiiHandler; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; + +/** + * The {@link BlukiiHandlerFactory} is responsible for creating things and thing handlers. + * + * @author Kai Kreuzer - Initial contribution + */ +@Component(service = ThingHandlerFactory.class, immediate = true, configurationPid = "binding.blukii", configurationPolicy = ConfigurationPolicy.OPTIONAL) +public class BlukiiHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .singleton(BlukiiBindingConstants.THING_TYPE_BUTTON); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(BlukiiBindingConstants.THING_TYPE_BUTTON)) { + return new BlukiiHandler(thing); + } + + return null; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/.classpath b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/.classpath new file mode 100644 index 00000000000..7f457fa4138 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/.project b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/.project new file mode 100644 index 00000000000..5ea9154ebf4 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/.project @@ -0,0 +1,33 @@ + + + org.eclipse.smarthome.binding.bluetooth.yeelightblue + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/ESH-INF/thing/yeelight_blue2.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/ESH-INF/thing/yeelight_blue2.xml new file mode 100644 index 00000000000..a3484f8a566 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/ESH-INF/thing/yeelight_blue2.xml @@ -0,0 +1,38 @@ + + + + + + + + + + A bluetooth enabled color bulb + + + + + + + + + + Bluetooth address in XX:XX:XX:XX:XX:XX format + + + + + + + + Color + + The color channel allows to control the color of a light. + It is also possible to dim values and switch the light on and off. + + ColorLight + + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..e62d9488704 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/META-INF/MANIFEST.MF @@ -0,0 +1,29 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: YeelightBlue Binding +Bundle-SymbolicName: org.eclipse.smarthome.binding.bluetooth.yeelightblue +Bundle-Vendor: Eclipse.org/SmartHome +Bundle-Version: 0.10.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ClassPath: . +Import-Package: + org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.binding.bluetooth, + org.eclipse.smarthome.binding.bluetooth.discovery, + org.eclipse.smarthome.binding.bluetooth.notification, + org.eclipse.smarthome.config.core, + org.eclipse.smarthome.config.discovery, + org.eclipse.smarthome.core.common.registry, + org.eclipse.smarthome.core.library.types, + org.eclipse.smarthome.core.thing, + org.eclipse.smarthome.core.thing.binding, + org.eclipse.smarthome.core.thing.binding.builder, + org.eclipse.smarthome.core.thing.binding.firmware, + org.eclipse.smarthome.core.thing.type, + org.eclipse.smarthome.core.types, + org.slf4j +Service-Component: OSGI-INF/*.xml +Export-Package: + org.eclipse.smarthome.binding.bluetooth.yeelightblue, + org.eclipse.smarthome.binding.bluetooth.yeelightblue.handler +Bundle-ActivationPolicy: lazy diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/NOTICE b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/NOTICE new file mode 100644 index 00000000000..b8675cd02e8 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/NOTICE @@ -0,0 +1,19 @@ +This content is produced and maintained by the Eclipse SmartHome project. + +* Project home: https://eclipse.org/smarthome/ + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/eclipse/smarthome + +== Copyright Holders + +See the NOTICE file distributed with the source code at +https://github.com/eclipse/smarthome/blob/master/NOTICE +for detailed information regarding copyright ownership. diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/OSGI-INF/.gitignore b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/OSGI-INF/.gitignore new file mode 100644 index 00000000000..b878e882aca --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/OSGI-INF/.gitignore @@ -0,0 +1 @@ +/*.xml diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/README.md b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/README.md new file mode 100644 index 00000000000..7f9817a86ca --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/README.md @@ -0,0 +1,52 @@ +# YeeLightBlue Binding + +_Give some details about what this binding is meant for - a protocol, system, specific device._ + +_If possible, provide some resources like pictures, a YouTube video, etc. to give an impression of what can be done with this binding. You can place such resources into a `doc` folder next to this README.md._ + +## Supported Things + +_Please describe the different supported things / devices within this section._ +_Which different types are supported, which models were tested etc.?_ +_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._ + +## Discovery + +_Describe the available auto-discovery features here. Mention for what it works and what needs to be kept in mind when using it._ + +## Binding Configuration + +_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it. In this section, you should link to this file and provide some information about the options. The file could e.g. look like:_ + +``` +# Configuration for the Philips Hue Binding +# +# Default secret key for the pairing of the Philips Hue Bridge. +# It has to be between 10-40 (alphanumeric) characters +# This may be changed by the user for security reasons. +secret=EclipseSmartHome +``` + +_Note that it is planned to generate some part of this based on the information that is available within ```ESH-INF/binding``` of your binding._ + +_If your binding does not offer any generic configurations, you can remove this section completely._ + +## Thing Configuration + +_Describe what is needed to manually configure a thing, either through the (Paper) UI or via a thing-file. This should be mainly about its mandatory and optional configuration parameters. A short example entry for a thing file can help!_ + +_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._ + +## Channels + +_Here you should provide information about available channel types, what their meaning is and how they can be used._ + +_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._ + +## Full Example + +_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._ + +## Any custom content here! + +_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/build.properties b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/build.properties new file mode 100644 index 00000000000..a6cfff567c9 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/build.properties @@ -0,0 +1,7 @@ +source..=src/main/java/ +output..=target/classes +bin.includes=META-INF/,\ + .,\ + OSGI-INF/,\ + ESH-INF/,\ + NOTICE diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/pom.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/pom.xml new file mode 100644 index 00000000000..f0d93d84f16 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/pom.xml @@ -0,0 +1,18 @@ + + + + 4.0.0 + + + org.eclipse.smarthome.binding + pom + 0.10.0-SNAPSHOT + + + org.eclipse.smarthome.binding.bluetooth.yeelightblue + + Eclipse SmartHome YeelightBlue Binding + eclipse-plugin + + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/YeelightBlueBindingConstants.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/YeelightBlueBindingConstants.java new file mode 100644 index 00000000000..46ab17a7d20 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/YeelightBlueBindingConstants.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.yeelightblue; + +import org.eclipse.smarthome.binding.bluetooth.BluetoothBindingConstants; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link YeelightBlueBindingConstants.YeeLightBlueBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Chris Jackson - Initial contribution + */ +public class YeelightBlueBindingConstants { + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_BLUE2 = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID, + "yeelight_blue2"); + + // List of all Channel ids + public final static String CHANNEL_SWITCH = "switch"; + public final static String CHANNEL_BRIGHTNESS = "brightness"; + public final static String CHANNEL_COLOR = "color"; + public final static String CHANNEL_RSSI = "rssi"; + + public final static String YEELIGHT_NAME = "Yeelight Blue II"; +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/handler/YeelightBlueHandler.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/handler/YeelightBlueHandler.java new file mode 100644 index 00000000000..e4a2cfd120d --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/handler/YeelightBlueHandler.java @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.yeelightblue.handler; + +import java.util.UUID; + +import org.eclipse.smarthome.binding.bluetooth.BluetoothCharacteristic; +import org.eclipse.smarthome.binding.bluetooth.BluetoothCompletionStatus; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDeviceListener; +import org.eclipse.smarthome.binding.bluetooth.ConnectedBluetoothHandler; +import org.eclipse.smarthome.binding.bluetooth.yeelightblue.YeelightBlueBindingConstants; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link YeelightBlueHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Chris Jackson - Initial contribution + * @author Kai Kreuzer - Refactored to extend {@link ConnectedBluetoothHandler}. + */ +public class YeelightBlueHandler extends ConnectedBluetoothHandler implements BluetoothDeviceListener { + + private final Logger logger = LoggerFactory.getLogger(YeelightBlueHandler.class); + + private final UUID UUID_YEELIGHT_CONTROL = UUID.fromString("0000fff1-0000-0000-0000-000000000000"); + private final UUID UUID_YEELIGHT_STATUS_REQUEST = UUID.fromString("0000fff5-0000-0000-0000-000000000000"); + private final UUID UUID_YEELIGHT_STATUS_RESPONSE = UUID.fromString("0000fff6-0000-0000-0000-000000000000"); + + // The characteristics we regularly use + private BluetoothCharacteristic characteristicControl = null; + private BluetoothCharacteristic characteristicRequest = null; + + public YeelightBlueHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + super.initialize(); + device.connect(); + device.discoverServices(); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + String value = null; + + if (command instanceof HSBType) { + HSBType hsb = (HSBType) command; + double r = hsb.getRed().doubleValue() * 2.55; + double g = hsb.getGreen().doubleValue() * 2.55; + double b = hsb.getBlue().doubleValue() * 2.55; + double a = hsb.getSaturation().doubleValue(); + value = String.format("%.0f,%.0f,%.0f,%.0f", r, g, b, a); + } + + else if (command instanceof PercentType) { + value = ",,," + ((PercentType) command).intValue() + ""; + } + + else if (command instanceof OnOffType) { + value = ",,," + ((OnOffType) command == OnOffType.ON ? 100 : 0) + ""; + } + + if (value == null) { + logger.debug("Unable to convert value!"); + return; + } + + if (characteristicControl == null) { + logger.debug("YeeLightBlue: Unable to find control characteristic!"); + return; + } + + // Terminate the value string with commas - up to 18 characters long + for (int cnt = value.length(); cnt < 18; cnt++) { + value += ","; + } + logger.debug("YeelightBlue conversion: {} to \"{}\"", command, value); + + characteristicControl.setValue(value); + device.writeCharacteristic(characteristicControl); + } + + @Override + public void onServicesDiscovered() { + // Everything is initialised now - get the characteristics we want to use + characteristicControl = device.getCharacteristic(UUID_YEELIGHT_CONTROL); + if (characteristicControl == null) { + logger.debug("YeeLightBlue control characteristic not known after service discovery!"); + } + characteristicRequest = device.getCharacteristic(UUID_YEELIGHT_STATUS_REQUEST); + if (characteristicRequest == null) { + logger.debug("YeeLightBlue status characteristic not known after service discovery!"); + } + + // Read the current value so we can update the UI + readStatus(); + } + + @Override + public void onCharacteristicWriteComplete(BluetoothCharacteristic characteristic, + BluetoothCompletionStatus status) { + // If this was a write to the control, then read back the state + if (characteristic.getUuid().equals(UUID_YEELIGHT_CONTROL)) { + readStatus(); + } + } + + @Override + public void onCharacteristicUpdate(BluetoothCharacteristic characteristic) { + if (characteristic.getUuid().equals(UUID_YEELIGHT_STATUS_RESPONSE)) { + String value = characteristic.getStringValue(0); + logger.debug("Yeelight status update is \"{}\"", value); + + String[] elements = value.split(","); + + int red, green, blue; + try { + red = Integer.parseInt(elements[0]); + } catch (NumberFormatException e) { + red = 0; + } + try { + green = Integer.parseInt(elements[1]); + } catch (NumberFormatException e) { + green = 0; + } + try { + blue = Integer.parseInt(elements[2]); + } catch (NumberFormatException e) { + blue = 0; + } + + HSBType hsbState = HSBType.fromRGB(red, green, blue); + + updateState(new ChannelUID(getThing().getUID(), YeelightBlueBindingConstants.CHANNEL_COLOR), hsbState); + } + } + + private void readStatus() { + if (characteristicRequest == null) { + logger.debug("YeeLightBlue status characteristic not known"); + return; + } + + characteristicRequest.setValue("S"); + device.writeCharacteristic(characteristicRequest); + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/internal/YeelightBlueHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/internal/YeelightBlueHandlerFactory.java new file mode 100644 index 00000000000..9f331a49993 --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/internal/YeelightBlueHandlerFactory.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.yeelightblue.internal; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.smarthome.binding.bluetooth.yeelightblue.YeelightBlueBindingConstants; +import org.eclipse.smarthome.binding.bluetooth.yeelightblue.handler.YeelightBlueHandler; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; + +/** + * The {@link YeelightBlueHandlerFactory.YeeLightBlueHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Chris Jackson - Initial contribution + */ +@Component(service = ThingHandlerFactory.class, immediate = true, configurationPid = "binding.yeelight", configurationPolicy = ConfigurationPolicy.OPTIONAL) +public class YeelightBlueHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .singleton(YeelightBlueBindingConstants.THING_TYPE_BLUE2); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(YeelightBlueBindingConstants.THING_TYPE_BLUE2)) { + return new YeelightBlueHandler(thing); + } + + return null; + } +} diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/internal/YeelightDiscoveryParticipant.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/internal/YeelightDiscoveryParticipant.java new file mode 100644 index 00000000000..50cf1461dda --- /dev/null +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.yeelightblue/src/main/java/org/eclipse/smarthome/binding/bluetooth/yeelightblue/internal/YeelightDiscoveryParticipant.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2014,2017 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.binding.bluetooth.yeelightblue.internal; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.binding.bluetooth.BluetoothBindingConstants; +import org.eclipse.smarthome.binding.bluetooth.BluetoothDevice; +import org.eclipse.smarthome.binding.bluetooth.discovery.BluetoothDiscoveryParticipant; +import org.eclipse.smarthome.binding.bluetooth.yeelightblue.YeelightBlueBindingConstants; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; + +/** + * This discovery participant is able to recognize Yeelight devices and create discovery results for them. + * + * @author Kai Kreuzer - Initial contribution and API + * + */ +@Component(immediate = true) +public class YeelightDiscoveryParticipant implements BluetoothDiscoveryParticipant { + + @Override + public @NonNull Set<@NonNull ThingTypeUID> getSupportedThingTypeUIDs() { + return Collections.singleton(YeelightBlueBindingConstants.THING_TYPE_BLUE2); + } + + @Override + public @Nullable ThingUID getThingUID(@NonNull BluetoothDevice device) { + if (YeelightBlueBindingConstants.YEELIGHT_NAME.equals(device.getName())) { + return new ThingUID(YeelightBlueBindingConstants.THING_TYPE_BLUE2, device.getAdapter().getUID(), + device.getAddress().toString().toLowerCase().replace(":", "")); + } else { + return null; + } + } + + @Override + public DiscoveryResult createResult(@NonNull BluetoothDevice device) { + ThingUID thingUID = getThingUID(device); + + if (thingUID != null) { + String label = device.getName(); + + Map properties = new HashMap<>(); + properties.put(BluetoothBindingConstants.CONFIGURATION_ADDRESS, device.getAddress().toString()); + properties.put(Thing.PROPERTY_VENDOR, "Yeelight"); + Integer txPower = device.getTxPower(); + if (txPower != null) { + properties.put(BluetoothBindingConstants.PROPERTY_TXPOWER, Integer.toString(txPower)); + } + + // Create the discovery result and add to the inbox + return DiscoveryResultBuilder.create(thingUID).withProperties(properties) + .withBridge(device.getAdapter().getUID()).withLabel(label).build(); + } else { + return null; + } + } + +} From 8fe95e42dc822ef6250bd87a920a34045bc00903 Mon Sep 17 00:00:00 2001 From: Kai Kreuzer Date: Fri, 2 Feb 2018 10:26:51 +0100 Subject: [PATCH 3/3] added discovery configuration parameter and fixed JVM crash upon handler disposal Signed-off-by: Kai Kreuzer --- .../ESH-INF/thing/bluegiga.xml | 6 + .../bluegiga/BlueGigaAdapterConstants.java | 1 + .../handler/BlueGigaBridgeHandler.java | 167 ++++++++++-------- 3 files changed, 98 insertions(+), 76 deletions(-) diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/thing/bluegiga.xml b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/thing/bluegiga.xml index 01af4cdc677..4b5b950b02f 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/thing/bluegiga.xml +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/ESH-INF/thing/bluegiga.xml @@ -13,6 +13,12 @@ serial-port Serial Port + + + Whether this adapter actively participates in Bluetooth device discovery + true + true + diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java index 4e3f2f96811..b00c724580d 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java @@ -30,4 +30,5 @@ public class BlueGigaAdapterConstants { public static final String CONFIGURATION_PORT = "port"; public static final String PROPERTY_LINKLAYER = "linklayer"; public static final String PROPERTY_PROTOCOL = "protocol"; + public static final String PROPERTY_DISCOVERY = "discovery"; } diff --git a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java index 28c218b6cba..ff4aac2e5a5 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java +++ b/extensions/binding/org.eclipse.smarthome.binding.bluetooth.bluegiga/src/main/java/org/eclipse/smarthome/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java @@ -12,6 +12,7 @@ */ package org.eclipse.smarthome.binding.bluetooth.bluegiga.handler; +import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -134,6 +135,9 @@ public class BlueGigaBridgeHandler extends BaseBridgeHandler // Our BT address private BluetoothAddress address; + // internal flag for the discovery configuration + private boolean discoveryActive = true; + // Map of Bluetooth devices known to this bridge. // This is all devices we have heard on the network - not just things bound to the bridge private final Map devices = new ConcurrentHashMap<>(); @@ -164,69 +168,77 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void initialize() { + Object discovery = getConfig().get(BlueGigaAdapterConstants.PROPERTY_DISCOVERY); + if (discovery != null && discovery.toString().equalsIgnoreCase(Boolean.FALSE.toString())) { + discoveryActive = false; + logger.debug("Deactivated discovery participation."); + } + final String portId = (String) getConfig().get(BlueGigaAdapterConstants.CONFIGURATION_PORT); if (portId == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Serial port must be configured!"); return; } - openSerialPort(portId, 115200); - bgHandler = new BlueGigaSerialHandler(inputStream, outputStream); - // Create and send the reset command to the dongle - bgHandler.addEventListener(this); - bgHandler.addHandlerListener(this); - - updateStatus(ThingStatus.UNKNOWN); - - scheduler.submit(() -> { - // Stop any procedures that are running - bgStopProcedure(); - - // Close all transactions - BlueGigaCommand command; // = new BlueGigaResetCommand(); - command = new BlueGigaGetConnectionsCommand(); - BlueGigaGetConnectionsResponse connectionsResponse = (BlueGigaGetConnectionsResponse) bgHandler - .sendTransaction(command); - if (connectionsResponse != null) { - maxConnections = connectionsResponse.getMaxconn(); - } - - // Close all connections so we start from a known position - for (int connection = 0; connection < maxConnections; connection++) { - bgDisconnect(connection); - } - - // Get our Bluetooth address - command = new BlueGigaAddressGetCommand(); - BlueGigaAddressGetResponse addressResponse = (BlueGigaAddressGetResponse) bgHandler - .sendTransaction(command); - if (addressResponse != null) { - address = new BluetoothAddress(addressResponse.getAddress()); - updateStatus(ThingStatus.ONLINE); - } else { - updateStatus(ThingStatus.OFFLINE); - } - - command = new BlueGigaGetInfoCommand(); - BlueGigaGetInfoResponse infoResponse = (BlueGigaGetInfoResponse) bgHandler.sendTransaction(command); - - // Set mode to non-discoverable etc. - // Not doing this will cause connection failures later - bgSetMode(); - - // Start passive scan - bgStartScanning(false, passiveScanInterval, passiveScanWindow); - - Map properties = editProperties(); - properties.put(BluetoothBindingConstants.PROPERTY_MAXCONNECTIONS, Integer.toString(maxConnections)); - properties.put(Thing.PROPERTY_FIRMWARE_VERSION, - String.format("%d.%d", infoResponse.getMajor(), infoResponse.getMinor())); - properties.put(Thing.PROPERTY_HARDWARE_VERSION, Integer.toString(infoResponse.getHardware())); - properties.put(BlueGigaAdapterConstants.PROPERTY_PROTOCOL, - Integer.toString(infoResponse.getProtocolVersion())); - properties.put(BlueGigaAdapterConstants.PROPERTY_LINKLAYER, Integer.toString(infoResponse.getLlVersion())); - updateProperties(properties); - }); + if (openSerialPort(portId, 115200)) { + bgHandler = new BlueGigaSerialHandler(inputStream, outputStream); + // Create and send the reset command to the dongle + bgHandler.addEventListener(this); + bgHandler.addHandlerListener(this); + + updateStatus(ThingStatus.UNKNOWN); + + scheduler.submit(() -> { + // Stop any procedures that are running + bgStopProcedure(); + + // Close all transactions + BlueGigaCommand command; // = new BlueGigaResetCommand(); + command = new BlueGigaGetConnectionsCommand(); + BlueGigaGetConnectionsResponse connectionsResponse = (BlueGigaGetConnectionsResponse) bgHandler + .sendTransaction(command); + if (connectionsResponse != null) { + maxConnections = connectionsResponse.getMaxconn(); + } + + // Close all connections so we start from a known position + for (int connection = 0; connection < maxConnections; connection++) { + bgDisconnect(connection); + } + + // Get our Bluetooth address + command = new BlueGigaAddressGetCommand(); + BlueGigaAddressGetResponse addressResponse = (BlueGigaAddressGetResponse) bgHandler + .sendTransaction(command); + if (addressResponse != null) { + address = new BluetoothAddress(addressResponse.getAddress()); + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE); + } + + command = new BlueGigaGetInfoCommand(); + BlueGigaGetInfoResponse infoResponse = (BlueGigaGetInfoResponse) bgHandler.sendTransaction(command); + + // Set mode to non-discoverable etc. + // Not doing this will cause connection failures later + bgSetMode(); + + // Start passive scan + bgStartScanning(false, passiveScanInterval, passiveScanWindow); + + Map properties = editProperties(); + properties.put(BluetoothBindingConstants.PROPERTY_MAXCONNECTIONS, Integer.toString(maxConnections)); + properties.put(Thing.PROPERTY_FIRMWARE_VERSION, + String.format("%d.%d", infoResponse.getMajor(), infoResponse.getMinor())); + properties.put(Thing.PROPERTY_HARDWARE_VERSION, Integer.toString(infoResponse.getHardware())); + properties.put(BlueGigaAdapterConstants.PROPERTY_PROTOCOL, + Integer.toString(infoResponse.getProtocolVersion())); + properties.put(BlueGigaAdapterConstants.PROPERTY_LINKLAYER, + Integer.toString(infoResponse.getLlVersion())); + updateProperties(properties); + }); + } } @Override @@ -234,11 +246,12 @@ public void dispose() { if (bgHandler != null) { bgHandler.removeEventListener(this); bgHandler.removeHandlerListener(this); + bgHandler.close(); } closeSerialPort(); } - private void openSerialPort(final String serialPortName, int baudRate) { + private boolean openSerialPort(final String serialPortName, int baudRate) { logger.debug("Connecting to serial port '{}'", serialPortName); try { CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(serialPortName); @@ -258,47 +271,47 @@ private void openSerialPort(final String serialPortName, int baudRate) { logger.info("Connected to serial port '{}'.", serialPortName); } catch (NoSuchPortException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Port does not exist"); - return; + return false; } catch (PortInUseException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, "Serial Error: Port in use"); - return; + return false; } catch (UnsupportedCommOperationException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Serial Error: Unsupported operation"); - return; + return false; } try { - inputStream = serialPort.getInputStream(); + inputStream = new BufferedInputStream(serialPort.getInputStream()); outputStream = serialPort.getOutputStream(); } catch (IOException e) { logger.error("Error getting serial streams", e); + return false; } - return; + return true; } private void closeSerialPort() { - try { - if (serialPort != null) { - serialPort.enableReceiveTimeout(1); - - inputStream.close(); + if (serialPort != null) { + try { + serialPort.disableReceiveTimeout(); + serialPort.removeEventListener(); outputStream.flush(); outputStream.close(); - + inputStream.close(); + } catch (Exception e) { + logger.error("Error closing serial port.", e); + } finally { serialPort.close(); - - logger.debug("Closed serial port closed.", serialPort.getName()); - + logger.debug("Closed serial port {}.", serialPort.getName()); serialPort = null; inputStream = null; outputStream = null; } - } catch (Exception e) { - logger.error("Error closing serial port.", e); } + } @SuppressWarnings({ "unused", "null" }) @@ -450,8 +463,10 @@ public boolean bgDisconnect(int connectionHandle) { * Device discovered. This simply passes the discover information to the discovery service for processing. */ public void deviceDiscovered(BluetoothDevice device) { - for (BluetoothDiscoveryListener listener : discoveryListeners) { - listener.deviceDiscovered(device); + if (discoveryActive) { + for (BluetoothDiscoveryListener listener : discoveryListeners) { + listener.deviceDiscovered(device); + } } }