@stoprocent/noble 1.9.2-16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/.editorconfig +11 -0
  2. package/.eslintrc.js +25 -0
  3. package/.github/FUNDING.yml +2 -0
  4. package/.github/workflows/fediverse-action.yml +16 -0
  5. package/.github/workflows/nodepackage.yml +77 -0
  6. package/.github/workflows/npm-publish.yml +26 -0
  7. package/.github/workflows/prebuild.yml +65 -0
  8. package/.nycrc.json +4 -0
  9. package/CHANGELOG.md +119 -0
  10. package/LICENSE +20 -0
  11. package/MAINTAINERS.md +1 -0
  12. package/README.md +833 -0
  13. package/assets/noble-logo.png +0 -0
  14. package/assets/noble-logo.svg +13 -0
  15. package/binding.gyp +19 -0
  16. package/codecov.yml +5 -0
  17. package/examples/advertisement-discovery.js +65 -0
  18. package/examples/cache-gatt-discovery.js +198 -0
  19. package/examples/cache-gatt-reconnect.js +164 -0
  20. package/examples/echo.js +104 -0
  21. package/examples/enter-exit.js +78 -0
  22. package/examples/peripheral-explorer-async.js +133 -0
  23. package/examples/peripheral-explorer.js +225 -0
  24. package/examples/pizza/README.md +15 -0
  25. package/examples/pizza/central.js +194 -0
  26. package/examples/pizza/pizza.js +60 -0
  27. package/index.d.ts +203 -0
  28. package/index.js +6 -0
  29. package/lib/characteristic.js +161 -0
  30. package/lib/characteristics.json +449 -0
  31. package/lib/descriptor.js +72 -0
  32. package/lib/descriptors.json +47 -0
  33. package/lib/distributed/bindings.js +326 -0
  34. package/lib/hci-socket/acl-stream.js +60 -0
  35. package/lib/hci-socket/bindings.js +788 -0
  36. package/lib/hci-socket/crypto.js +74 -0
  37. package/lib/hci-socket/gap.js +432 -0
  38. package/lib/hci-socket/gatt.js +809 -0
  39. package/lib/hci-socket/hci-status.json +71 -0
  40. package/lib/hci-socket/hci.js +1264 -0
  41. package/lib/hci-socket/signaling.js +76 -0
  42. package/lib/hci-socket/smp.js +140 -0
  43. package/lib/hci-uart/bindings.js +569 -0
  44. package/lib/hci-uart/hci-serial-parser.js +70 -0
  45. package/lib/hci-uart/hci.js +1336 -0
  46. package/lib/mac/binding.gyp +26 -0
  47. package/lib/mac/bindings.js +11 -0
  48. package/lib/mac/src/ble_manager.h +41 -0
  49. package/lib/mac/src/ble_manager.mm +435 -0
  50. package/lib/mac/src/callbacks.cc +222 -0
  51. package/lib/mac/src/callbacks.h +84 -0
  52. package/lib/mac/src/napi_objc.h +12 -0
  53. package/lib/mac/src/napi_objc.mm +50 -0
  54. package/lib/mac/src/noble_mac.h +34 -0
  55. package/lib/mac/src/noble_mac.mm +264 -0
  56. package/lib/mac/src/objc_cpp.h +26 -0
  57. package/lib/mac/src/objc_cpp.mm +126 -0
  58. package/lib/mac/src/peripheral.h +23 -0
  59. package/lib/manufacture.js +48 -0
  60. package/lib/noble.js +593 -0
  61. package/lib/peripheral.js +219 -0
  62. package/lib/resolve-bindings-web.js +9 -0
  63. package/lib/resolve-bindings.js +44 -0
  64. package/lib/service.js +72 -0
  65. package/lib/services.json +92 -0
  66. package/lib/webbluetooth/bindings.js +368 -0
  67. package/lib/websocket/bindings.js +321 -0
  68. package/lib/win/binding.gyp +23 -0
  69. package/lib/win/bindings.js +11 -0
  70. package/lib/win/src/ble_manager.cc +802 -0
  71. package/lib/win/src/ble_manager.h +77 -0
  72. package/lib/win/src/callbacks.cc +274 -0
  73. package/lib/win/src/callbacks.h +33 -0
  74. package/lib/win/src/napi_winrt.cc +76 -0
  75. package/lib/win/src/napi_winrt.h +12 -0
  76. package/lib/win/src/noble_winrt.cc +308 -0
  77. package/lib/win/src/noble_winrt.h +34 -0
  78. package/lib/win/src/notify_map.cc +62 -0
  79. package/lib/win/src/notify_map.h +50 -0
  80. package/lib/win/src/peripheral.h +23 -0
  81. package/lib/win/src/peripheral_winrt.cc +296 -0
  82. package/lib/win/src/peripheral_winrt.h +82 -0
  83. package/lib/win/src/radio_watcher.cc +125 -0
  84. package/lib/win/src/radio_watcher.h +61 -0
  85. package/lib/win/src/winrt_cpp.cc +82 -0
  86. package/lib/win/src/winrt_cpp.h +11 -0
  87. package/lib/win/src/winrt_guid.cc +12 -0
  88. package/lib/win/src/winrt_guid.h +13 -0
  89. package/misc/nrf52840dk.hex +6921 -0
  90. package/misc/prj.conf +43 -0
  91. package/package.json +96 -0
  92. package/test/lib/characteristic.test.js +791 -0
  93. package/test/lib/descriptor.test.js +249 -0
  94. package/test/lib/distributed/bindings.test.js +918 -0
  95. package/test/lib/hci-socket/acl-stream.test.js +188 -0
  96. package/test/lib/hci-socket/bindings.test.js +1756 -0
  97. package/test/lib/hci-socket/crypto.test.js +55 -0
  98. package/test/lib/hci-socket/gap.test.js +1089 -0
  99. package/test/lib/hci-socket/gatt.test.js +2392 -0
  100. package/test/lib/hci-socket/hci.test.js +1891 -0
  101. package/test/lib/hci-socket/signaling.test.js +94 -0
  102. package/test/lib/hci-socket/smp.test.js +268 -0
  103. package/test/lib/manufacture.test.js +77 -0
  104. package/test/lib/peripheral.test.js +623 -0
  105. package/test/lib/resolve-bindings.test.js +102 -0
  106. package/test/lib/service.test.js +195 -0
  107. package/test/lib/webbluetooth/bindings.test.js +190 -0
  108. package/test/lib/websocket/bindings.test.js +456 -0
  109. package/test/noble.test.js +1565 -0
  110. package/test.js +131 -0
  111. package/with-bindings.js +5 -0
  112. package/ws-slave.js +404 -0
@@ -0,0 +1,222 @@
1
+ //
2
+ // callbacks.cc
3
+ // noble-mac-native
4
+ //
5
+ // Created by Georg Vienna on 30.08.18.
6
+ //
7
+ #include "callbacks.h"
8
+
9
+ #define _s(val) Napi::String::New(env, val)
10
+ #define _b(val) Napi::Boolean::New(env, val)
11
+ #define _n(val) Napi::Number::New(env, val)
12
+ #define _u(str) toUuid(env, str)
13
+
14
+ Napi::String toUuid(Napi::Env& env, const std::string& uuid) {
15
+ std::string str(uuid);
16
+ str.erase(std::remove(str.begin(), str.end(), '-'), str.end());
17
+ std::transform(str.begin(), str.end(), str.begin(), ::tolower);
18
+ return _s(str);
19
+ }
20
+
21
+ Napi::String toAddressType(Napi::Env& env, const AddressType& type) {
22
+ if(type == PUBLIC) {
23
+ return _s("public");
24
+ } else if (type == RANDOM) {
25
+ return _s("random");
26
+ }
27
+ return _s("unknown");
28
+ }
29
+
30
+ Napi::Buffer<uint8_t> toBuffer(Napi::Env& env, const Data& data) {
31
+ if (data.empty()) {
32
+ return Napi::Buffer<uint8_t>::New(env, 0);
33
+ }
34
+ return Napi::Buffer<uint8_t>::Copy(env, &data[0], data.size());
35
+ }
36
+
37
+ Napi::Array toUuidArray(Napi::Env& env, const std::vector<std::string>& data) {
38
+ if (data.empty()) {
39
+ return Napi::Array::New(env);
40
+ }
41
+ auto arr = Napi::Array::New(env, data.size());
42
+ for (size_t i = 0; i < data.size(); i++) {
43
+ arr.Set(i, _u(data[i]));
44
+ }
45
+ return arr;
46
+ }
47
+
48
+ Napi::Array toArray(Napi::Env& env, const std::vector<std::string>& data) {
49
+ if (data.empty()) {
50
+ return Napi::Array::New(env);
51
+ }
52
+ auto arr = Napi::Array::New(env, data.size());
53
+ for (size_t i = 0; i < data.size(); i++) {
54
+ arr.Set(i, _s(data[i]));
55
+ }
56
+ return arr;
57
+ }
58
+
59
+ void Emit::Wrap(const Napi::Value& receiver, const Napi::Function& callback) {
60
+ mCallback = std::make_shared<ThreadSafeCallback>(receiver, callback);
61
+ }
62
+
63
+ void Emit::RadioState(const std::string& state) {
64
+ mCallback->call([state](Napi::Env env, std::vector<napi_value>& args) {
65
+ // emit('stateChange', state);
66
+ args = { _s("stateChange"), _s(state) };
67
+ });
68
+ }
69
+
70
+ void Emit::ScanState(bool start) {
71
+ mCallback->call([start](Napi::Env env, std::vector<napi_value>& args) {
72
+ // emit('scanStart') emit('scanStop')
73
+ args = { _s(start ? "scanStart" : "scanStop") };
74
+ });
75
+ }
76
+
77
+ void Emit::Scan(const std::string& uuid, int rssi, const Peripheral& peripheral) {
78
+ auto address = peripheral.address;
79
+ auto addressType = peripheral.addressType;
80
+ auto connectable = peripheral.connectable;
81
+ auto name = peripheral.name;
82
+ auto txPowerLevel = peripheral.txPowerLevel;
83
+ auto manufacturerData = peripheral.manufacturerData;
84
+ auto serviceData = peripheral.serviceData;
85
+ auto serviceUuids = peripheral.serviceUuids;
86
+ mCallback->call([uuid, rssi, address, addressType, connectable, name, txPowerLevel, manufacturerData, serviceData, serviceUuids](Napi::Env env, std::vector<napi_value>& args) {
87
+ Napi::Object advertisment = Napi::Object::New(env);
88
+ if (std::get<1>(name)) {
89
+ advertisment.Set(_s("localName"), _s(std::get<0>(name)));
90
+ }
91
+
92
+ if (std::get<1>(txPowerLevel)) {
93
+ advertisment.Set(_s("txPowerLevel"), std::get<0>(txPowerLevel));
94
+ }
95
+
96
+ if (std::get<1>(manufacturerData)) {
97
+ advertisment.Set(_s("manufacturerData"), toBuffer(env, std::get<0>(manufacturerData)));
98
+ }
99
+
100
+ if (std::get<1>(serviceData)) {
101
+ auto array = std::get<0>(serviceData).empty() ? Napi::Array::New(env) : Napi::Array::New(env, std::get<0>(serviceData).size());
102
+ for (size_t i = 0; i < std::get<0>(serviceData).size(); i++) {
103
+ Napi::Object data = Napi::Object::New(env);
104
+ data.Set(_s("uuid"), _u(std::get<0>(serviceData)[i].first));
105
+ data.Set(_s("data"), toBuffer(env, std::get<0>(serviceData)[i].second));
106
+ array.Set(i, data);
107
+ }
108
+ advertisment.Set(_s("serviceData"), array);
109
+ }
110
+
111
+ if (std::get<1>(serviceUuids)) {
112
+ advertisment.Set(_s("serviceUuids"), toUuidArray(env, std::get<0>(serviceUuids)));
113
+ }
114
+ // emit('discover', deviceUuid, address, addressType, connectable, advertisement, rssi);
115
+ args = { _s("discover"), _u(uuid), _s(address), toAddressType(env, addressType), _b(connectable), advertisment, _n(rssi) };
116
+ });
117
+ }
118
+
119
+ void Emit::Connected(const std::string& uuid, const std::string& error) {
120
+ mCallback->call([uuid, error](Napi::Env env, std::vector<napi_value>& args) {
121
+ // emit('connect', deviceUuid) error added here
122
+ args = { _s("connect"), _u(uuid), error.empty() ? env.Null() : _s(error) };
123
+ });
124
+ }
125
+
126
+ void Emit::Disconnected(const std::string& uuid) {
127
+ mCallback->call([uuid](Napi::Env env, std::vector<napi_value>& args) {
128
+ // emit('disconnect', deviceUuid);
129
+ args = { _s("disconnect"), _u(uuid) };
130
+ });
131
+ }
132
+
133
+ void Emit::RSSI(const std::string & uuid, int rssi) {
134
+ mCallback->call([uuid, rssi](Napi::Env env, std::vector<napi_value>& args) {
135
+ // emit('rssiUpdate', deviceUuid, rssi);
136
+ args = { _s("rssiUpdate"), _u(uuid), _n(rssi) };
137
+ });
138
+ }
139
+
140
+ void Emit::ServicesDiscovered(const std::string & uuid, const std::vector<std::string>& serviceUuids) {
141
+ mCallback->call([uuid, serviceUuids](Napi::Env env, std::vector<napi_value>& args) {
142
+ // emit('servicesDiscover', deviceUuid, serviceUuids)
143
+ args = { _s("servicesDiscover"), _u(uuid), toUuidArray(env, serviceUuids) };
144
+ });
145
+ }
146
+
147
+ void Emit::IncludedServicesDiscovered(const std::string & uuid, const std::string & serviceUuid, const std::vector<std::string>& serviceUuids) {
148
+ mCallback->call([uuid, serviceUuid, serviceUuids](Napi::Env env, std::vector<napi_value>& args) {
149
+ // emit('includedServicesDiscover', deviceUuid, serviceUuid, includedServiceUuids)
150
+ args = { _s("includedServicesDiscover"), _u(uuid), _u(serviceUuid), toUuidArray(env, serviceUuids) };
151
+ });
152
+ }
153
+
154
+ void Emit::CharacteristicsDiscovered(const std::string & uuid, const std::string & serviceUuid, const std::vector<std::pair<std::string, std::vector<std::string>>>& characteristics) {
155
+ mCallback->call([uuid, serviceUuid, characteristics](Napi::Env env, std::vector<napi_value>& args) {
156
+ auto arr = characteristics.empty() ? Napi::Array::New(env) : Napi::Array::New(env, characteristics.size());
157
+ for (size_t i = 0; i < characteristics.size(); i++) {
158
+ Napi::Object characteristic = Napi::Object::New(env);
159
+ characteristic.Set(_s("uuid"), _u(characteristics[i].first));
160
+ characteristic.Set(_s("properties"), toArray(env, characteristics[i].second));
161
+ arr.Set(i, characteristic);
162
+ }
163
+ // emit('characteristicsDiscover', deviceUuid, serviceUuid, { uuid, properties: ['broadcast', 'read', ...]})
164
+ args = { _s("characteristicsDiscover"), _u(uuid), _u(serviceUuid), arr };
165
+ });
166
+ }
167
+
168
+ void Emit::Read(const std::string & uuid, const std::string & serviceUuid, const std::string & characteristicUuid, const Data& data, bool isNotification) {
169
+ mCallback->call([uuid, serviceUuid, characteristicUuid, data, isNotification](Napi::Env env, std::vector<napi_value>& args) {
170
+ // emit('read', deviceUuid, serviceUuid, characteristicsUuid, data, isNotification);
171
+ args = { _s("read"), _u(uuid), _u(serviceUuid), _u(characteristicUuid), toBuffer(env, data), _b(isNotification) };
172
+ });
173
+ }
174
+
175
+ void Emit::Write(const std::string & uuid, const std::string & serviceUuid, const std::string & characteristicUuid) {
176
+ mCallback->call([uuid, serviceUuid, characteristicUuid](Napi::Env env, std::vector<napi_value>& args) {
177
+ // emit('write', deviceUuid, servicesUuid, characteristicsUuid)
178
+ args = { _s("write"), _u(uuid), _u(serviceUuid), _u(characteristicUuid) };
179
+ });
180
+ }
181
+
182
+ void Emit::Notify(const std::string & uuid, const std::string & serviceUuid, const std::string & characteristicUuid, bool state) {
183
+ mCallback->call([uuid, serviceUuid, characteristicUuid, state](Napi::Env env, std::vector<napi_value>& args) {
184
+ // emit('notify', deviceUuid, servicesUuid, characteristicsUuid, state)
185
+ args = { _s("notify"), _u(uuid), _u(serviceUuid), _u(characteristicUuid), _b(state) };
186
+ });
187
+ }
188
+
189
+ void Emit::DescriptorsDiscovered(const std::string & uuid, const std::string & serviceUuid, const std::string & characteristicUuid, const std::vector<std::string>& descriptorUuids) {
190
+ mCallback->call([uuid, serviceUuid, characteristicUuid, descriptorUuids](Napi::Env env, std::vector<napi_value>& args) {
191
+ // emit('descriptorsDiscover', deviceUuid, servicesUuid, characteristicsUuid, descriptors: [uuids])
192
+ args = { _s("descriptorsDiscover"), _u(uuid), _u(serviceUuid), _u(characteristicUuid), toUuidArray(env, descriptorUuids) };
193
+ });
194
+ }
195
+
196
+ void Emit::ReadValue(const std::string & uuid, const std::string & serviceUuid, const std::string & characteristicUuid, const std::string& descriptorUuid, const Data& data) {
197
+ mCallback->call([uuid, serviceUuid, characteristicUuid, descriptorUuid, data](Napi::Env env, std::vector<napi_value>& args) {
198
+ // emit('valueRead', deviceUuid, serviceUuid, characteristicUuid, descriptorUuid, data)
199
+ args = { _s("valueRead"), _u(uuid), _u(serviceUuid), _u(characteristicUuid), _u(descriptorUuid), toBuffer(env, data) };
200
+ });
201
+ }
202
+
203
+ void Emit::WriteValue(const std::string & uuid, const std::string & serviceUuid, const std::string & characteristicUuid, const std::string& descriptorUuid) {
204
+ mCallback->call([uuid, serviceUuid, characteristicUuid, descriptorUuid](Napi::Env env, std::vector<napi_value>& args) {
205
+ // emit('valueWrite', deviceUuid, serviceUuid, characteristicUuid, descriptorUuid);
206
+ args = { _s("valueWrite"), _u(uuid), _u(serviceUuid), _u(characteristicUuid), _u(descriptorUuid) };
207
+ });
208
+ }
209
+
210
+ void Emit::ReadHandle(const std::string & uuid, int descriptorHandle, const Data& data) {
211
+ mCallback->call([uuid, descriptorHandle, data](Napi::Env env, std::vector<napi_value>& args) {
212
+ // emit('handleRead', deviceUuid, descriptorHandle, data);
213
+ args = { _s("handleRead"), _u(uuid), _n(descriptorHandle), toBuffer(env, data) };
214
+ });
215
+ }
216
+
217
+ void Emit::WriteHandle(const std::string & uuid, int descriptorHandle) {
218
+ mCallback->call([uuid, descriptorHandle](Napi::Env env, std::vector<napi_value>& args) {
219
+ // emit('handleWrite', deviceUuid, descriptorHandle);
220
+ args = { _s("handleWrite"), _u(uuid), _n(descriptorHandle) };
221
+ });
222
+ }
@@ -0,0 +1,84 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include "peripheral.h"
5
+
6
+ class ThreadSafeCallback {
7
+ using arg_vector_t = std::vector<napi_value>;
8
+ using arg_func_t = std::function<void(napi_env, arg_vector_t &)>;
9
+
10
+ static void callJsCallback(Napi::Env env,
11
+ Napi::Function jsCallback,
12
+ Napi::Reference<Napi::Value> *context,
13
+ arg_func_t *argfn) {
14
+ if (argfn != nullptr) {
15
+ arg_vector_t args;
16
+
17
+ (*argfn)(env, args);
18
+ delete argfn;
19
+
20
+ if (env != nullptr && jsCallback != nullptr)
21
+ jsCallback.Call(context->Value(), args);
22
+ }
23
+ };
24
+ using tsfn_t = Napi::TypedThreadSafeFunction<
25
+ Napi::Reference<Napi::Value>, arg_func_t, callJsCallback>;
26
+
27
+ public:
28
+ ThreadSafeCallback(const Napi::Value &receiver,
29
+ const Napi::Function &jsCallback) {
30
+ if (!(receiver.IsObject() || receiver.IsFunction()))
31
+ throw Napi::Error::New(jsCallback.Env(),
32
+ "Callback receiver must be an object or function");
33
+ if (!jsCallback.IsFunction())
34
+ throw Napi::Error::New(jsCallback.Env(), "Callback must be a function");
35
+
36
+ receiver_ = Napi::Persistent(receiver);
37
+ tsfn_ = tsfn_t::New(jsCallback.Env(),
38
+ jsCallback,
39
+ "ThreadSafeCallback callback",
40
+ 0, 1, &receiver_);
41
+ }
42
+ ~ThreadSafeCallback() {
43
+ // No further interaction with the thread safe function allowed.
44
+ tsfn_.Abort();
45
+ }
46
+ void call(arg_func_t arg_function) {
47
+ arg_func_t *argfn = new arg_func_t(arg_function);
48
+ if (tsfn_.BlockingCall(argfn) != napi_ok)
49
+ delete argfn;
50
+ };
51
+
52
+ protected:
53
+ ThreadSafeCallback(const ThreadSafeCallback &) = delete;
54
+ ThreadSafeCallback& operator=(const ThreadSafeCallback &) = delete;
55
+ ThreadSafeCallback& operator=(ThreadSafeCallback &&) = delete;
56
+
57
+ private:
58
+ Napi::Reference<Napi::Value> receiver_;
59
+ tsfn_t tsfn_;
60
+ };
61
+
62
+ class Emit {
63
+ public:
64
+ void Wrap(const Napi::Value& receiver, const Napi::Function& callback);
65
+ void RadioState(const std::string& status);
66
+ void ScanState(bool start);
67
+ void Scan(const std::string& uuid, int rssi, const Peripheral& peripheral);
68
+ void Connected(const std::string& uuid, const std::string& error = "");
69
+ void Disconnected(const std::string& uuid);
70
+ void RSSI(const std::string& uuid, int rssi);
71
+ void ServicesDiscovered(const std::string& uuid, const std::vector<std::string>& serviceUuids);
72
+ void IncludedServicesDiscovered(const std::string& uuid, const std::string& serviceUuid, const std::vector<std::string>& serviceUuids);
73
+ void CharacteristicsDiscovered(const std::string& uuid, const std::string& serviceUuid, const std::vector<std::pair<std::string, std::vector<std::string>>>& characteristics);
74
+ void Read(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const Data& data, bool isNotification);
75
+ void Write(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid);
76
+ void Notify(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, bool state);
77
+ void DescriptorsDiscovered(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const std::vector<std::string>& descriptorUuids);
78
+ void ReadValue(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const std::string& descriptorUuid, const Data& data);
79
+ void WriteValue(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const std::string& descriptorUuid);
80
+ void ReadHandle(const std::string& uuid, int descriptorHandle, const std::vector<uint8_t>& data);
81
+ void WriteHandle(const std::string& uuid, int descriptorHandle);
82
+ protected:
83
+ std::shared_ptr<ThreadSafeCallback> mCallback;
84
+ };
@@ -0,0 +1,12 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #import <Foundation/Foundation.h>
5
+
6
+ NSArray* getUuidArray(const Napi::Value& value);
7
+ BOOL getBool(const Napi::Value& value, BOOL def);
8
+
9
+ NSString* napiToUuidString(Napi::String string);
10
+ NSArray* napiToUuidArray(Napi::Array array);
11
+ NSData* napiToData(Napi::Buffer<Byte> buffer);
12
+ NSNumber* napiToNumber(Napi::Number number);
@@ -0,0 +1,50 @@
1
+ //
2
+ // napi_objc.mm
3
+ // noble-mac-native
4
+ //
5
+ // Created by Georg Vienna on 30.08.18.
6
+ //
7
+ #include "napi_objc.h"
8
+
9
+ NSString* napiToUuidString(Napi::String string) {
10
+ std::string str = string.Utf8Value();
11
+ NSMutableString * uuid = [[NSMutableString alloc] initWithCString:str.c_str() encoding:NSASCIIStringEncoding];
12
+ if([uuid length] == 32) {
13
+ [uuid insertString: @"-" atIndex: 8];
14
+ [uuid insertString: @"-" atIndex: 13];
15
+ [uuid insertString: @"-" atIndex: 18];
16
+ [uuid insertString: @"-" atIndex: 23];
17
+ }
18
+ return [uuid uppercaseString];
19
+ }
20
+
21
+ NSArray* napiToUuidArray(Napi::Array array) {
22
+ NSMutableArray* serviceUuids = [NSMutableArray arrayWithCapacity:array.Length()];
23
+ for(size_t i = 0; i < array.Length(); i++) {
24
+ Napi::Value val = array[i];
25
+ [serviceUuids addObject:napiToUuidString(val.As<Napi::String>())];
26
+ }
27
+ return serviceUuids;
28
+ }
29
+
30
+ NSData* napiToData(Napi::Buffer<Byte> buffer) {
31
+ return [NSData dataWithBytes:buffer.Data() length:buffer.Length()];
32
+ }
33
+
34
+ NSNumber* napiToNumber(Napi::Number number) {
35
+ return [NSNumber numberWithInt:number.Int64Value()];
36
+ }
37
+
38
+ NSArray* getUuidArray(const Napi::Value& value) {
39
+ if (value.IsArray()) {
40
+ return napiToUuidArray(value.As<Napi::Array>());
41
+ }
42
+ return nil;
43
+ }
44
+
45
+ BOOL getBool(const Napi::Value& value, BOOL def) {
46
+ if (value.IsBoolean()) {
47
+ return value.As<Napi::Boolean>().Value();
48
+ }
49
+ return def;
50
+ }
@@ -0,0 +1,34 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+
5
+ #include "ble_manager.h"
6
+
7
+ class NobleMac : public Napi::ObjectWrap<NobleMac>
8
+ {
9
+ public:
10
+ NobleMac(const Napi::CallbackInfo&);
11
+ Napi::Value Init(const Napi::CallbackInfo&);
12
+ Napi::Value Scan(const Napi::CallbackInfo&);
13
+ Napi::Value StopScan(const Napi::CallbackInfo&);
14
+ Napi::Value Connect(const Napi::CallbackInfo&);
15
+ Napi::Value Disconnect(const Napi::CallbackInfo&);
16
+ Napi::Value UpdateRSSI(const Napi::CallbackInfo&);
17
+ Napi::Value DiscoverServices(const Napi::CallbackInfo&);
18
+ Napi::Value DiscoverIncludedServices(const Napi::CallbackInfo& info);
19
+ Napi::Value DiscoverCharacteristics(const Napi::CallbackInfo& info);
20
+ Napi::Value Read(const Napi::CallbackInfo& info);
21
+ Napi::Value Write(const Napi::CallbackInfo& info);
22
+ Napi::Value Notify(const Napi::CallbackInfo& info);
23
+ Napi::Value DiscoverDescriptors(const Napi::CallbackInfo& info);
24
+ Napi::Value ReadValue(const Napi::CallbackInfo& info);
25
+ Napi::Value WriteValue(const Napi::CallbackInfo& info);
26
+ Napi::Value ReadHandle(const Napi::CallbackInfo& info);
27
+ Napi::Value WriteHandle(const Napi::CallbackInfo& info);
28
+ Napi::Value Stop(const Napi::CallbackInfo&);
29
+
30
+ static Napi::Function GetClass(Napi::Env);
31
+
32
+ private:
33
+ BLEManager* manager;
34
+ };
@@ -0,0 +1,264 @@
1
+ //
2
+ // noble_mac.mm
3
+ // noble-mac-native
4
+ //
5
+ // Created by Georg Vienna on 28.08.18.
6
+ //
7
+ #include "noble_mac.h"
8
+
9
+ #include "napi_objc.h"
10
+
11
+ #define THROW(msg) \
12
+ Napi::TypeError::New(info.Env(), msg).ThrowAsJavaScriptException(); \
13
+ return Napi::Value();
14
+
15
+ #define ARG1(type1) \
16
+ if (!info[0].Is##type1()) { \
17
+ THROW("There should be one argument: (" #type1 ")") \
18
+ }
19
+
20
+ #define ARG2(type1, type2) \
21
+ if (!info[0].Is##type1() || !info[1].Is##type2()) { \
22
+ THROW("There should be 2 arguments: (" #type1 ", " #type2 ")"); \
23
+ }
24
+
25
+ #define ARG3(type1, type2, type3) \
26
+ if (!info[0].Is##type1() || !info[1].Is##type2() || !info[2].Is##type3()) { \
27
+ THROW("There should be 3 arguments: (" #type1 ", " #type2 ", " #type3 ")"); \
28
+ }
29
+
30
+ #define ARG4(type1, type2, type3, type4) \
31
+ if (!info[0].Is##type1() || !info[1].Is##type2() || !info[2].Is##type3() || !info[3].Is##type4()) { \
32
+ THROW("There should be 4 arguments: (" #type1 ", " #type2 ", " #type3 ", " #type4 ")"); \
33
+ }
34
+
35
+ #define ARG5(type1, type2, type3, type4, type5) \
36
+ if (!info[0].Is##type1() || !info[1].Is##type2() || !info[2].Is##type3() || !info[3].Is##type4() || !info[4].Is##type5()) { \
37
+ THROW("There should be 5 arguments: (" #type1 ", " #type2 ", " #type3 ", " #type4 ", " #type5 ")"); \
38
+ }
39
+
40
+ #define CHECK_MANAGER() \
41
+ if(!manager) { \
42
+ THROW("BLEManager has already been cleaned up"); \
43
+ }
44
+
45
+ NobleMac::NobleMac(const Napi::CallbackInfo& info) : ObjectWrap(info) {
46
+ }
47
+
48
+ Napi::Value NobleMac::Init(const Napi::CallbackInfo& info) {
49
+ Napi::Function emit = info.This().As<Napi::Object>().Get("emit").As<Napi::Function>();
50
+ manager = [[BLEManager alloc] init:info.This() with:emit];
51
+ return Napi::Value();
52
+ }
53
+
54
+ // startScanning(serviceUuids, allowDuplicates)
55
+ Napi::Value NobleMac::Scan(const Napi::CallbackInfo& info) {
56
+ CHECK_MANAGER()
57
+ NSArray* array = getUuidArray(info[0]);
58
+ // default value NO
59
+ auto duplicates = getBool(info[1], NO);
60
+ [manager scan:array allowDuplicates:duplicates];
61
+ return Napi::Value();
62
+ }
63
+
64
+ // stopScanning()
65
+ Napi::Value NobleMac::StopScan(const Napi::CallbackInfo& info) {
66
+ CHECK_MANAGER()
67
+ [manager stopScan];
68
+ return Napi::Value();
69
+ }
70
+
71
+ // connect(deviceUuid)
72
+ Napi::Value NobleMac::Connect(const Napi::CallbackInfo& info) {
73
+ CHECK_MANAGER()
74
+ ARG1(String)
75
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
76
+ [manager connect:uuid];
77
+ return Napi::Value();
78
+ }
79
+
80
+ // disconnect(deviceUuid)
81
+ Napi::Value NobleMac::Disconnect(const Napi::CallbackInfo& info) {
82
+ CHECK_MANAGER()
83
+ ARG1(String)
84
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
85
+ [manager disconnect:uuid];
86
+ return Napi::Value();
87
+ }
88
+
89
+ // updateRssi(deviceUuid)
90
+ Napi::Value NobleMac::UpdateRSSI(const Napi::CallbackInfo& info) {
91
+ CHECK_MANAGER()
92
+ ARG1(String)
93
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
94
+ [manager updateRSSI:uuid];
95
+ return Napi::Value();
96
+ }
97
+
98
+ // discoverServices(deviceUuid, uuids)
99
+ Napi::Value NobleMac::DiscoverServices(const Napi::CallbackInfo& info) {
100
+ CHECK_MANAGER()
101
+ ARG1(String)
102
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
103
+ NSArray* array = getUuidArray(info[1]);
104
+ [manager discoverServices:uuid serviceUuids:array];
105
+ return Napi::Value();
106
+ }
107
+
108
+ // discoverIncludedServices(deviceUuid, serviceUuid, serviceUuids)
109
+ Napi::Value NobleMac::DiscoverIncludedServices(const Napi::CallbackInfo& info) {
110
+ CHECK_MANAGER()
111
+ ARG2(String, String)
112
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
113
+ auto service = napiToUuidString(info[1].As<Napi::String>());
114
+ NSArray* serviceUuids = getUuidArray(info[2]);
115
+ [manager discoverIncludedServices:uuid forService:service services:serviceUuids];
116
+ return Napi::Value();
117
+ }
118
+
119
+ // discoverCharacteristics(deviceUuid, serviceUuid, characteristicUuids)
120
+ Napi::Value NobleMac::DiscoverCharacteristics(const Napi::CallbackInfo& info) {
121
+ CHECK_MANAGER()
122
+ ARG2(String, String)
123
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
124
+ auto service = napiToUuidString(info[1].As<Napi::String>());
125
+ NSArray* characteristics = getUuidArray(info[2]);
126
+ [manager discoverCharacteristics:uuid forService:service characteristics:characteristics];
127
+ return Napi::Value();
128
+ }
129
+
130
+ // read(deviceUuid, serviceUuid, characteristicUuid)
131
+ Napi::Value NobleMac::Read(const Napi::CallbackInfo& info) {
132
+ CHECK_MANAGER()
133
+ ARG3(String, String, String)
134
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
135
+ auto service = napiToUuidString(info[1].As<Napi::String>());
136
+ auto characteristic = napiToUuidString(info[2].As<Napi::String>());
137
+ [manager read:uuid service:service characteristic:characteristic];
138
+ return Napi::Value();
139
+ }
140
+
141
+ // write(deviceUuid, serviceUuid, characteristicUuid, data, withoutResponse)
142
+ Napi::Value NobleMac::Write(const Napi::CallbackInfo& info) {
143
+ CHECK_MANAGER()
144
+ ARG4(String, String, String, Buffer /*, Boolean */)
145
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
146
+ auto service = napiToUuidString(info[1].As<Napi::String>());
147
+ auto characteristic = napiToUuidString(info[2].As<Napi::String>());
148
+ auto data = napiToData(info[3].As<Napi::Buffer<Byte>>());
149
+
150
+ auto withoutResponse = false;
151
+ if(info[4] && !(info[4].IsNull() || info[4].IsUndefined())) {
152
+ withoutResponse = info[4].As<Napi::Boolean>().Value();
153
+ }
154
+
155
+ [manager write:uuid service:service characteristic:characteristic data:data withoutResponse:withoutResponse];
156
+ return Napi::Value();
157
+ }
158
+
159
+ // notify(deviceUuid, serviceUuid, characteristicUuid, notify)
160
+ Napi::Value NobleMac::Notify(const Napi::CallbackInfo& info) {
161
+ CHECK_MANAGER()
162
+ ARG4(String, String, String, Boolean)
163
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
164
+ auto service = napiToUuidString(info[1].As<Napi::String>());
165
+ auto characteristic = napiToUuidString(info[2].As<Napi::String>());
166
+ auto on = info[3].As<Napi::Boolean>().Value();
167
+ [manager notify:uuid service:service characteristic:characteristic on:on];
168
+ return Napi::Value();
169
+ }
170
+
171
+ // discoverDescriptors(deviceUuid, serviceUuid, characteristicUuid)
172
+ Napi::Value NobleMac::DiscoverDescriptors(const Napi::CallbackInfo& info) {
173
+ CHECK_MANAGER()
174
+ ARG3(String, String, String)
175
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
176
+ auto service = napiToUuidString(info[1].As<Napi::String>());
177
+ auto characteristic = napiToUuidString(info[2].As<Napi::String>());
178
+ [manager discoverDescriptors:uuid service:service characteristic:characteristic];
179
+ return Napi::Value();
180
+ }
181
+
182
+ // readValue(deviceUuid, serviceUuid, characteristicUuid, descriptorUuid)
183
+ Napi::Value NobleMac::ReadValue(const Napi::CallbackInfo& info) {
184
+ CHECK_MANAGER()
185
+ ARG4(String, String, String, String)
186
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
187
+ auto service = napiToUuidString(info[1].As<Napi::String>());
188
+ auto characteristic = napiToUuidString(info[2].As<Napi::String>());
189
+ auto descriptor = napiToUuidString(info[3].As<Napi::String>());
190
+ [manager readValue:uuid service:service characteristic:characteristic descriptor:descriptor];
191
+ return Napi::Value();
192
+ }
193
+
194
+ // writeValue(deviceUuid, serviceUuid, characteristicUuid, descriptorUuid, data)
195
+ Napi::Value NobleMac::WriteValue(const Napi::CallbackInfo& info) {
196
+ CHECK_MANAGER()
197
+ ARG5(String, String, String, String, Buffer)
198
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
199
+ auto service = napiToUuidString(info[1].As<Napi::String>());
200
+ auto characteristic = napiToUuidString(info[2].As<Napi::String>());
201
+ auto descriptor = napiToUuidString(info[3].As<Napi::String>());
202
+ auto data = napiToData(info[4].As<Napi::Buffer<Byte>>());
203
+ [manager writeValue:uuid service:service characteristic:characteristic descriptor:descriptor data: data];
204
+ return Napi::Value();
205
+ }
206
+
207
+ // readHandle(deviceUuid, handle)
208
+ Napi::Value NobleMac::ReadHandle(const Napi::CallbackInfo& info) {
209
+ CHECK_MANAGER()
210
+ ARG2(String, Number)
211
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
212
+ auto handle = napiToNumber(info[1].As<Napi::Number>());
213
+ [manager readHandle:uuid handle:handle];
214
+ return Napi::Value();
215
+ }
216
+
217
+ // writeHandle(deviceUuid, handle, data, (unused)withoutResponse)
218
+ Napi::Value NobleMac::WriteHandle(const Napi::CallbackInfo& info) {
219
+ CHECK_MANAGER()
220
+ ARG3(String, Number, Buffer)
221
+ auto uuid = napiToUuidString(info[0].As<Napi::String>());
222
+ auto handle = napiToNumber(info[1].As<Napi::Number>());
223
+ auto data = napiToData(info[2].As<Napi::Buffer<Byte>>());
224
+ [manager writeHandle:uuid handle:handle data: data];
225
+ return Napi::Value();
226
+ }
227
+
228
+ Napi::Value NobleMac::Stop(const Napi::CallbackInfo& info) {
229
+ CHECK_MANAGER()
230
+ CFRelease((__bridge CFTypeRef)manager);
231
+ manager = nil;
232
+ return Napi::Value();
233
+ }
234
+
235
+ Napi::Function NobleMac::GetClass(Napi::Env env) {
236
+ return DefineClass(env, "NobleMac", {
237
+ NobleMac::InstanceMethod("init", &NobleMac::Init),
238
+ NobleMac::InstanceMethod("startScanning", &NobleMac::Scan),
239
+ NobleMac::InstanceMethod("stopScanning", &NobleMac::StopScan),
240
+ NobleMac::InstanceMethod("connect", &NobleMac::Connect),
241
+ NobleMac::InstanceMethod("disconnect", &NobleMac::Disconnect),
242
+ NobleMac::InstanceMethod("updateRssi", &NobleMac::UpdateRSSI),
243
+ NobleMac::InstanceMethod("discoverServices", &NobleMac::DiscoverServices),
244
+ NobleMac::InstanceMethod("discoverIncludedServices", &NobleMac::DiscoverIncludedServices),
245
+ NobleMac::InstanceMethod("discoverCharacteristics", &NobleMac::DiscoverCharacteristics),
246
+ NobleMac::InstanceMethod("read", &NobleMac::Read),
247
+ NobleMac::InstanceMethod("write", &NobleMac::Write),
248
+ NobleMac::InstanceMethod("notify", &NobleMac::Notify),
249
+ NobleMac::InstanceMethod("discoverDescriptors", &NobleMac::DiscoverDescriptors),
250
+ NobleMac::InstanceMethod("readValue", &NobleMac::ReadValue),
251
+ NobleMac::InstanceMethod("writeValue", &NobleMac::WriteValue),
252
+ NobleMac::InstanceMethod("readHandle", &NobleMac::ReadHandle),
253
+ NobleMac::InstanceMethod("writeHandle", &NobleMac::WriteHandle),
254
+ NobleMac::InstanceMethod("stop", &NobleMac::Stop),
255
+ });
256
+ }
257
+
258
+ Napi::Object Init(Napi::Env env, Napi::Object exports) {
259
+ Napi::String name = Napi::String::New(env, "NobleMac");
260
+ exports.Set(name, NobleMac::GetClass(env));
261
+ return exports;
262
+ }
263
+
264
+ NODE_API_MODULE(addon, Init)