@stoprocent/noble 1.19.0 → 2.0.0

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 (97) hide show
  1. package/README.md +393 -650
  2. package/examples/advertisement-discovery.js +57 -48
  3. package/examples/connect-address.js +59 -34
  4. package/examples/echo.js +59 -69
  5. package/examples/enter-exit.js +55 -49
  6. package/examples/multiple-bindings.js +53 -0
  7. package/examples/peripheral-explorer-async.js +39 -21
  8. package/examples/peripheral-explorer.ts +52 -0
  9. package/index.d.ts +249 -209
  10. package/index.js +4 -1
  11. package/jest.config.js +4 -0
  12. package/lib/characteristic.js +153 -127
  13. package/lib/{win/src/callbacks.h → common/include/Emit.h} +17 -14
  14. package/lib/common/include/Peripheral.h +31 -0
  15. package/lib/common/include/ThreadSafeCallback.h +95 -0
  16. package/lib/{win/src/callbacks.cc → common/src/Emit.cc} +111 -68
  17. package/lib/descriptor.js +57 -54
  18. package/lib/hci-socket/acl-stream.js +2 -4
  19. package/lib/hci-socket/bindings.js +96 -73
  20. package/lib/hci-socket/gap.js +2 -3
  21. package/lib/hci-socket/gatt.js +2 -5
  22. package/lib/hci-socket/hci.js +19 -7
  23. package/lib/hci-socket/signaling.js +2 -3
  24. package/lib/hci-socket/smp.js +2 -3
  25. package/lib/hci-socket/vs.js +1 -0
  26. package/lib/mac/binding.gyp +5 -7
  27. package/lib/mac/bindings.js +1 -3
  28. package/lib/mac/src/ble_manager.h +1 -8
  29. package/lib/mac/src/ble_manager.mm +87 -44
  30. package/lib/mac/src/napi_objc.h +1 -0
  31. package/lib/mac/src/napi_objc.mm +0 -6
  32. package/lib/mac/src/noble_mac.h +5 -3
  33. package/lib/mac/src/noble_mac.mm +99 -57
  34. package/lib/mac/src/objc_cpp.h +3 -2
  35. package/lib/mac/src/objc_cpp.mm +0 -6
  36. package/lib/noble.js +579 -488
  37. package/lib/peripheral.js +171 -174
  38. package/lib/resolve-bindings.js +37 -30
  39. package/lib/service.js +58 -55
  40. package/lib/win/binding.gyp +4 -11
  41. package/lib/win/bindings.js +1 -3
  42. package/lib/win/src/ble_manager.cc +291 -166
  43. package/lib/win/src/ble_manager.h +11 -13
  44. package/lib/win/src/napi_winrt.cc +1 -7
  45. package/lib/win/src/napi_winrt.h +1 -1
  46. package/lib/win/src/noble_winrt.cc +88 -61
  47. package/lib/win/src/noble_winrt.h +5 -3
  48. package/lib/win/src/notify_map.cc +0 -7
  49. package/lib/win/src/notify_map.h +1 -8
  50. package/lib/win/src/peripheral_winrt.cc +29 -11
  51. package/lib/win/src/peripheral_winrt.h +1 -1
  52. package/lib/win/src/radio_watcher.cc +79 -69
  53. package/lib/win/src/radio_watcher.h +30 -11
  54. package/lib/win/src/winrt_cpp.cc +1 -1
  55. package/lib/win/src/winrt_cpp.h +3 -0
  56. package/package.json +14 -17
  57. package/prebuilds/darwin-x64+arm64/@stoprocent+noble.node +0 -0
  58. package/prebuilds/win32-ia32/@stoprocent+noble.node +0 -0
  59. package/prebuilds/win32-x64/@stoprocent+noble.node +0 -0
  60. package/test/lib/characteristic.test.js +202 -322
  61. package/test/lib/descriptor.test.js +62 -95
  62. package/test/lib/hci-socket/acl-stream.test.js +112 -108
  63. package/test/lib/hci-socket/bindings.test.js +576 -365
  64. package/test/lib/hci-socket/hci.test.js +442 -473
  65. package/test/lib/hci-socket/signaling.test.js +45 -48
  66. package/test/lib/hci-socket/smp.test.js +144 -142
  67. package/test/lib/hci-socket/vs.test.js +193 -18
  68. package/test/lib/peripheral.test.js +492 -322
  69. package/test/lib/resolve-bindings.test.js +207 -82
  70. package/test/lib/service.test.js +79 -88
  71. package/test/noble.test.js +381 -1085
  72. package/.editorconfig +0 -11
  73. package/.nycrc.json +0 -4
  74. package/codecov.yml +0 -5
  75. package/examples/cache-gatt-discovery.js +0 -198
  76. package/examples/cache-gatt-reconnect.js +0 -164
  77. package/examples/ext-advertisement-discovery.js +0 -65
  78. package/examples/peripheral-explorer.js +0 -225
  79. package/examples/pizza/central.js +0 -194
  80. package/examples/pizza/pizza.js +0 -60
  81. package/examples/test/test.custom.js +0 -131
  82. package/examples/uart-bind-params.js +0 -28
  83. package/lib/distributed/bindings.js +0 -326
  84. package/lib/mac/src/callbacks.cc +0 -222
  85. package/lib/mac/src/callbacks.h +0 -84
  86. package/lib/mac/src/peripheral.h +0 -23
  87. package/lib/resolve-bindings-web.js +0 -9
  88. package/lib/webbluetooth/bindings.js +0 -368
  89. package/lib/websocket/bindings.js +0 -321
  90. package/lib/win/src/peripheral.h +0 -23
  91. package/test/lib/distributed/bindings.test.js +0 -918
  92. package/test/lib/webbluetooth/bindings.test.js +0 -190
  93. package/test/lib/websocket/bindings.test.js +0 -456
  94. package/test/mocha.setup.js +0 -0
  95. package/with-bindings.js +0 -5
  96. package/with-custom-binding.js +0 -6
  97. package/ws-slave.js +0 -404
@@ -1,29 +1,52 @@
1
- //
2
- // radio_watcher.cc
3
- // noble-winrt-native
4
- //
5
- // Created by Georg Vienna on 07.09.18.
6
- //
1
+ // Standard library includes
2
+ #include <future>
3
+ #include <string>
4
+ #include <sstream>
5
+ #include <iomanip>
6
+ #include <cstdint>
7
7
 
8
- #pragma once
8
+ // Windows Runtime includes
9
+ #include <winrt/Windows.Foundation.Collections.h>
10
+ #include <winrt/Windows.Devices.Bluetooth.h>
11
+ #include <winrt/Windows.Devices.Enumeration.h>
12
+ #include <winrt/Windows.Devices.Radios.h>
9
13
 
14
+ // Project includes
10
15
  #include "radio_watcher.h"
11
16
  #include "winrt_cpp.h"
12
- #include <winrt/Windows.Foundation.Collections.h>
13
17
 
14
- using winrt::Windows::Devices::Radios::RadioKind;
15
- using winrt::Windows::Foundation::AsyncStatus;
18
+ using namespace winrt::Windows::Devices::Enumeration;
19
+ using namespace winrt::Windows::Devices::Bluetooth;
20
+ using namespace winrt::Windows::Devices::Bluetooth::GenericAttributeProfile;
21
+ using winrt::Windows::Devices::Radios::RadioState;
16
22
 
17
23
  template <typename O, typename M, class... Types> auto bind2(O* object, M method, Types&... args)
18
24
  {
19
25
  return std::bind(method, object, std::placeholders::_1, std::placeholders::_2, args...);
20
26
  }
21
27
 
22
- #define RADIO_INTERFACE_CLASS_GUID \
23
- L"System.Devices.InterfaceClassGuid:=\"{A8804298-2D5F-42E3-9531-9C8C39EB29CE}\""
28
+ const char* adapterStateToString(AdapterState state)
29
+ {
30
+ switch (state)
31
+ {
32
+ case AdapterState::Unsupported:
33
+ return "unsupported";
34
+ case AdapterState::On:
35
+ return "poweredOn";
36
+ break;
37
+ case AdapterState::Off:
38
+ return "poweredOff";
39
+ break;
40
+ case AdapterState::Disabled:
41
+ return "poweredOff";
42
+ break;
43
+ default:
44
+ return "unknown";
45
+ }
46
+ }
24
47
 
25
48
  RadioWatcher::RadioWatcher()
26
- : mRadio(nullptr), watcher(DeviceInformation::CreateWatcher(RADIO_INTERFACE_CLASS_GUID))
49
+ : mRadio(nullptr), watcher(DeviceInformation::CreateWatcher(BluetoothAdapter::GetDeviceSelector()))
27
50
  {
28
51
  mAddedRevoker = watcher.Added(winrt::auto_revoke, bind2(this, &RadioWatcher::OnAdded));
29
52
  mUpdatedRevoker = watcher.Updated(winrt::auto_revoke, bind2(this, &RadioWatcher::OnUpdated));
@@ -32,80 +55,68 @@ RadioWatcher::RadioWatcher()
32
55
  mCompletedRevoker = watcher.EnumerationCompleted(winrt::auto_revoke, completed);
33
56
  }
34
57
 
35
- void RadioWatcher::Start(std::function<void(Radio& radio)> on)
58
+ void RadioWatcher::Start(std::function<void(Radio& radio, const AdapterCapabilities& capabilities)> on)
36
59
  {
37
60
  radioStateChanged = on;
38
61
  inEnumeration = true;
39
- initialDone = false;
40
- initialCount = 0;
41
62
  watcher.Start();
42
63
  }
43
64
 
44
- IAsyncOperation<Radio> RadioWatcher::GetRadios(std::set<winrt::hstring> ids)
45
- {
46
- Radio bluetooth = nullptr;
47
- for (auto id : ids)
48
- {
49
- try
50
- {
51
- auto radio = co_await Radio::FromIdAsync(id);
52
- if (radio && radio.Kind() == RadioKind::Bluetooth)
53
- {
54
- auto state = radio.State();
55
- // we only get state changes for turned on/off adapter but not for disabled adapter
56
- if (state == RadioState::On || state == RadioState::Off)
57
- {
58
- bluetooth = radio;
59
- }
65
+ winrt::fire_and_forget RadioWatcher::OnRadioChanged() {
66
+ try {
67
+ auto adapter = co_await BluetoothAdapter::GetDefaultAsync();
68
+
69
+ if (adapter) {
70
+ auto radio = co_await adapter.GetRadioAsync();
71
+
72
+ AdapterCapabilities capabilities;
73
+ capabilities.bluetoothAddress = adapter.BluetoothAddress();
74
+ capabilities.classicSecureConnectionsSupported = adapter.AreClassicSecureConnectionsSupported();
75
+ capabilities.lowEnergySecureConnectionsSupported = adapter.AreLowEnergySecureConnectionsSupported();
76
+ capabilities.extendedAdvertisingSupported = adapter.IsExtendedAdvertisingSupported();
77
+ capabilities.lowEnergySupported = adapter.IsLowEnergySupported();
78
+ capabilities.maxAdvertisementDataLength = adapter.MaxAdvertisementDataLength();
79
+ capabilities.peripheralRoleSupported = adapter.IsPeripheralRoleSupported();
80
+ capabilities.centralRoleSupported = adapter.IsCentralRoleSupported();
81
+
82
+ Radio bluetooth = nullptr;
83
+ if (radio.State() == RadioState::On && adapter.IsPeripheralRoleSupported()) {
84
+ bluetooth = radio;
60
85
  }
61
- }
62
- catch (...)
63
- {
64
- // Radio::RadioFromAsync throws if the device is not available (unplugged)
65
- }
66
- }
67
- co_return bluetooth;
68
- }
69
86
 
70
- void RadioWatcher::OnRadioChanged()
71
- {
72
- GetRadios(radioIds).Completed([=](auto&& asyncOp, auto&& status) {
73
- if (status == AsyncStatus::Completed)
74
- {
75
- Radio radio = asyncOp.GetResults();
76
- // !radio: to handle if there is no radio
77
- if (!radio || radio != mRadio)
78
- {
79
- if (radio)
80
- {
87
+ if (!bluetooth || bluetooth != mRadio) {
88
+ if (bluetooth) {
81
89
  mRadioStateChangedRevoker.revoke();
82
- mRadioStateChangedRevoker = radio.StateChanged(
83
- winrt::auto_revoke, [=](Radio radio, auto&&) { radioStateChanged(radio); });
84
- }
85
- else
86
- {
90
+ mRadioStateChangedRevoker = bluetooth.StateChanged(
91
+ winrt::auto_revoke,
92
+ [this, capabilities](Radio radio, auto&&) {
93
+ radioStateChanged(radio, capabilities);
94
+ });
95
+ } else {
87
96
  mRadioStateChangedRevoker.revoke();
88
97
  }
89
- radioStateChanged(radio);
90
- mRadio = radio;
98
+
99
+ radioStateChanged(bluetooth, capabilities);
100
+ mRadio = bluetooth;
91
101
  }
92
- }
93
- else
94
- {
102
+ } else {
95
103
  mRadio = nullptr;
96
104
  mRadioStateChangedRevoker.revoke();
97
- radioStateChanged(mRadio);
105
+ AdapterCapabilities emptyCapabilities = {};
106
+ radioStateChanged(mRadio, emptyCapabilities);
98
107
  }
99
- });
108
+ } catch (const winrt::hresult_error&) {
109
+ mRadio = nullptr;
110
+ mRadioStateChangedRevoker.revoke();
111
+ AdapterCapabilities emptyCapabilities = {};
112
+ radioStateChanged(mRadio, emptyCapabilities);
113
+ }
100
114
  }
101
115
 
102
116
  void RadioWatcher::OnAdded(DeviceWatcher watcher, DeviceInformation info)
103
117
  {
104
- radioIds.insert(info.Id());
105
- if (!inEnumeration)
106
- {
107
- OnRadioChanged();
108
- }
118
+ if (inEnumeration) { return; }
119
+ OnRadioChanged();
109
120
  }
110
121
 
111
122
  void RadioWatcher::OnUpdated(DeviceWatcher watcher, DeviceInformationUpdate info)
@@ -115,7 +126,6 @@ void RadioWatcher::OnUpdated(DeviceWatcher watcher, DeviceInformationUpdate info
115
126
 
116
127
  void RadioWatcher::OnRemoved(DeviceWatcher watcher, DeviceInformationUpdate info)
117
128
  {
118
- radioIds.erase(info.Id());
119
129
  OnRadioChanged();
120
130
  }
121
131
 
@@ -7,19 +7,24 @@
7
7
 
8
8
  #pragma once
9
9
 
10
+ // Standard library includes
10
11
  #include <functional>
11
12
  #include <set>
13
+
14
+ // Windows Runtime includes
12
15
  #include <winrt/Windows.Devices.Enumeration.h>
13
16
  #include <winrt/Windows.Devices.Radios.h>
17
+ #include <winrt/Windows.Foundation.h>
14
18
 
19
+ // Forward declarations
15
20
  using namespace winrt::Windows::Devices::Enumeration;
16
-
17
21
  using winrt::Windows::Devices::Radios::IRadio;
18
22
  using winrt::Windows::Devices::Radios::Radio;
19
23
  using winrt::Windows::Devices::Radios::RadioState;
20
24
  using winrt::Windows::Foundation::IAsyncOperation;
21
25
  using winrt::Windows::Foundation::IInspectable;
22
26
 
27
+ // AdapterState enum
23
28
  enum class AdapterState : int32_t
24
29
  {
25
30
  Initial = -2,
@@ -30,32 +35,46 @@ enum class AdapterState : int32_t
30
35
  Disabled = (int32_t)RadioState::Disabled,
31
36
  };
32
37
 
38
+ // AdapterCapabilities struct
39
+ struct AdapterCapabilities {
40
+ uint64_t bluetoothAddress;
41
+ bool classicSecureConnectionsSupported;
42
+ bool lowEnergySecureConnectionsSupported;
43
+ bool extendedAdvertisingSupported;
44
+ bool lowEnergySupported;
45
+ uint32_t maxAdvertisementDataLength;
46
+ bool peripheralRoleSupported;
47
+ bool centralRoleSupported;
48
+ };
49
+
50
+ // Convert AdapterState to std:string
51
+ const char* adapterStateToString(AdapterState state);
52
+
53
+ // RadioWatcher class
33
54
  class RadioWatcher
34
55
  {
35
56
  public:
36
57
  RadioWatcher();
37
58
 
38
- void Start(std::function<void(Radio& radio)> on);
59
+ void Start(std::function<void(Radio& radio, const AdapterCapabilities& capabilities)> on);
39
60
 
40
- private:
41
- IAsyncOperation<Radio> GetRadios(std::set<winrt::hstring> ids);
61
+ winrt::fire_and_forget OnRadioChanged();
42
62
 
43
- void OnRadioChanged();
44
63
  void OnAdded(DeviceWatcher watcher, DeviceInformation info);
45
64
  void OnUpdated(DeviceWatcher watcher, DeviceInformationUpdate info);
46
65
  void OnRemoved(DeviceWatcher watcher, DeviceInformationUpdate info);
47
66
  void OnCompleted(DeviceWatcher watcher, IInspectable info);
48
67
 
68
+ private:
69
+ Radio mRadio;
49
70
  DeviceWatcher watcher;
71
+ bool inEnumeration;
72
+
73
+ std::function<void(Radio& radio, const AdapterCapabilities& capabilities)> radioStateChanged;
74
+
50
75
  winrt::event_revoker<IDeviceWatcher> mAddedRevoker;
51
76
  winrt::event_revoker<IDeviceWatcher> mUpdatedRevoker;
52
77
  winrt::event_revoker<IDeviceWatcher> mRemovedRevoker;
53
78
  winrt::event_revoker<IDeviceWatcher> mCompletedRevoker;
54
- bool inEnumeration;
55
- bool initialDone;
56
- int initialCount;
57
- std::set<winrt::hstring> radioIds;
58
- Radio mRadio;
59
79
  winrt::event_revoker<IRadio> mRadioStateChangedRevoker;
60
- std::function<void(Radio& radio)> radioStateChanged;
61
80
  };
@@ -3,7 +3,7 @@
3
3
  #include <sstream>
4
4
  #include <iomanip>
5
5
 
6
- #include <winrt\Windows.Devices.Bluetooth.h>
6
+ #include <winrt/Windows.Devices.Bluetooth.h>
7
7
  #include <winrt/Windows.Foundation.Collections.h>
8
8
 
9
9
  std::string ws2s(const wchar_t* wstr)
@@ -1,5 +1,8 @@
1
1
  #pragma once
2
2
 
3
+ #include <vector>
4
+ #include <string>
5
+
3
6
  #include <winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h>
4
7
 
5
8
  using winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties;
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "license": "MIT",
7
7
  "name": "@stoprocent/noble",
8
8
  "description": "A Node.js BLE (Bluetooth Low Energy) central library.",
9
- "version": "1.19.0",
9
+ "version": "2.0.0",
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "https://github.com/stoprocent/noble.git"
@@ -29,14 +29,14 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "debug": "^4.3.7",
32
- "napi-thread-safe-callback": "^0.0.6",
33
32
  "node-addon-api": "^8.1.0",
34
33
  "node-gyp-build": "^4.8.1"
35
34
  },
36
35
  "optionalDependencies": {
37
- "@stoprocent/bluetooth-hci-socket": "^1.5.1"
36
+ "@stoprocent/bluetooth-hci-socket": "^2.1.1"
38
37
  },
39
38
  "devDependencies": {
39
+ "@babel/eslint-parser": "^7.27.0",
40
40
  "@commitlint/cli": "^19.3.0",
41
41
  "@commitlint/config-conventional": "^19.2.2",
42
42
  "@semantic-release/changelog": "^6.0.3",
@@ -46,35 +46,32 @@
46
46
  "async": "^3.2.6",
47
47
  "cross-env": "^7.0.3",
48
48
  "eslint": "^8.57.1",
49
- "eslint-plugin-import": "^2.30.0",
49
+ "eslint-config-node": "^4.1.0",
50
+ "eslint-plugin-import": "^2.31.0",
51
+ "eslint-plugin-jest": "^28.11.0",
50
52
  "eslint-plugin-n": "^17.10.3",
53
+ "eslint-plugin-node": "^11.1.0",
51
54
  "eslint-plugin-promise": "^7.1.0",
52
- "mocha": "^10.7.0",
53
- "nyc": "^17.0.0",
55
+ "jest": "^29.7.0",
56
+ "jshint": "^2.13.6",
54
57
  "prebuildify": "^6.0.1",
55
58
  "prebuildify-cross": "^5.1.1",
56
- "semantic-release": "^24.1.1",
57
- "jshint": "^2.13.6",
58
59
  "prettier": "^3.3.3",
59
60
  "proxyquire": "^2.1.3",
61
+ "semantic-release": "^24.1.1",
60
62
  "should": "^13.2.3",
61
- "sinon": "^15.0.1",
62
- "ws": "^8.11.0"
63
+ "sinon": "^15.0.1"
63
64
  },
64
65
  "scripts": {
65
66
  "install": "node-gyp-build",
66
67
  "lint": "eslint \"**/*.js\"",
67
68
  "lint-fix": "eslint \"**/*.js\" --fix",
68
- "prebuildify": "prebuildify --napi --target 14.0.0 --force --strip --verbose",
69
- "prebuildify-cross": "prebuildify-cross --napi --target 14.0.0 --force --strip --verbose",
69
+ "prebuildify": "prebuildify --napi --target 17.0.0 --force --strip --verbose",
70
+ "prebuildify-cross": "prebuildify-cross --napi --target 17.0.0 --force --strip --verbose",
70
71
  "semantic-release": "semantic-release",
71
72
  "pretest": "npm run rebuild",
72
73
  "rebuild": "node-gyp rebuild",
73
- "coverage": "nyc npm test && nyc report --reporter=text-lcov > .nyc_output/lcov.info",
74
- "test": "cross-env NODE_ENV=test mocha --recursive \"test/*.test.js\" \"test/**/*.test.js\" --exit"
75
- },
76
- "browser": {
77
- "./lib/resolve-bindings.js": "./lib/resolve-bindings-web.js"
74
+ "test": "npx jest"
78
75
  },
79
76
  "publishConfig": {
80
77
  "access": "public"