@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.
- package/.editorconfig +11 -0
- package/.eslintrc.js +25 -0
- package/.github/FUNDING.yml +2 -0
- package/.github/workflows/fediverse-action.yml +16 -0
- package/.github/workflows/nodepackage.yml +77 -0
- package/.github/workflows/npm-publish.yml +26 -0
- package/.github/workflows/prebuild.yml +65 -0
- package/.nycrc.json +4 -0
- package/CHANGELOG.md +119 -0
- package/LICENSE +20 -0
- package/MAINTAINERS.md +1 -0
- package/README.md +833 -0
- package/assets/noble-logo.png +0 -0
- package/assets/noble-logo.svg +13 -0
- package/binding.gyp +19 -0
- package/codecov.yml +5 -0
- package/examples/advertisement-discovery.js +65 -0
- package/examples/cache-gatt-discovery.js +198 -0
- package/examples/cache-gatt-reconnect.js +164 -0
- package/examples/echo.js +104 -0
- package/examples/enter-exit.js +78 -0
- package/examples/peripheral-explorer-async.js +133 -0
- package/examples/peripheral-explorer.js +225 -0
- package/examples/pizza/README.md +15 -0
- package/examples/pizza/central.js +194 -0
- package/examples/pizza/pizza.js +60 -0
- package/index.d.ts +203 -0
- package/index.js +6 -0
- package/lib/characteristic.js +161 -0
- package/lib/characteristics.json +449 -0
- package/lib/descriptor.js +72 -0
- package/lib/descriptors.json +47 -0
- package/lib/distributed/bindings.js +326 -0
- package/lib/hci-socket/acl-stream.js +60 -0
- package/lib/hci-socket/bindings.js +788 -0
- package/lib/hci-socket/crypto.js +74 -0
- package/lib/hci-socket/gap.js +432 -0
- package/lib/hci-socket/gatt.js +809 -0
- package/lib/hci-socket/hci-status.json +71 -0
- package/lib/hci-socket/hci.js +1264 -0
- package/lib/hci-socket/signaling.js +76 -0
- package/lib/hci-socket/smp.js +140 -0
- package/lib/hci-uart/bindings.js +569 -0
- package/lib/hci-uart/hci-serial-parser.js +70 -0
- package/lib/hci-uart/hci.js +1336 -0
- package/lib/mac/binding.gyp +26 -0
- package/lib/mac/bindings.js +11 -0
- package/lib/mac/src/ble_manager.h +41 -0
- package/lib/mac/src/ble_manager.mm +435 -0
- package/lib/mac/src/callbacks.cc +222 -0
- package/lib/mac/src/callbacks.h +84 -0
- package/lib/mac/src/napi_objc.h +12 -0
- package/lib/mac/src/napi_objc.mm +50 -0
- package/lib/mac/src/noble_mac.h +34 -0
- package/lib/mac/src/noble_mac.mm +264 -0
- package/lib/mac/src/objc_cpp.h +26 -0
- package/lib/mac/src/objc_cpp.mm +126 -0
- package/lib/mac/src/peripheral.h +23 -0
- package/lib/manufacture.js +48 -0
- package/lib/noble.js +593 -0
- package/lib/peripheral.js +219 -0
- package/lib/resolve-bindings-web.js +9 -0
- package/lib/resolve-bindings.js +44 -0
- package/lib/service.js +72 -0
- package/lib/services.json +92 -0
- package/lib/webbluetooth/bindings.js +368 -0
- package/lib/websocket/bindings.js +321 -0
- package/lib/win/binding.gyp +23 -0
- package/lib/win/bindings.js +11 -0
- package/lib/win/src/ble_manager.cc +802 -0
- package/lib/win/src/ble_manager.h +77 -0
- package/lib/win/src/callbacks.cc +274 -0
- package/lib/win/src/callbacks.h +33 -0
- package/lib/win/src/napi_winrt.cc +76 -0
- package/lib/win/src/napi_winrt.h +12 -0
- package/lib/win/src/noble_winrt.cc +308 -0
- package/lib/win/src/noble_winrt.h +34 -0
- package/lib/win/src/notify_map.cc +62 -0
- package/lib/win/src/notify_map.h +50 -0
- package/lib/win/src/peripheral.h +23 -0
- package/lib/win/src/peripheral_winrt.cc +296 -0
- package/lib/win/src/peripheral_winrt.h +82 -0
- package/lib/win/src/radio_watcher.cc +125 -0
- package/lib/win/src/radio_watcher.h +61 -0
- package/lib/win/src/winrt_cpp.cc +82 -0
- package/lib/win/src/winrt_cpp.h +11 -0
- package/lib/win/src/winrt_guid.cc +12 -0
- package/lib/win/src/winrt_guid.h +13 -0
- package/misc/nrf52840dk.hex +6921 -0
- package/misc/prj.conf +43 -0
- package/package.json +96 -0
- package/test/lib/characteristic.test.js +791 -0
- package/test/lib/descriptor.test.js +249 -0
- package/test/lib/distributed/bindings.test.js +918 -0
- package/test/lib/hci-socket/acl-stream.test.js +188 -0
- package/test/lib/hci-socket/bindings.test.js +1756 -0
- package/test/lib/hci-socket/crypto.test.js +55 -0
- package/test/lib/hci-socket/gap.test.js +1089 -0
- package/test/lib/hci-socket/gatt.test.js +2392 -0
- package/test/lib/hci-socket/hci.test.js +1891 -0
- package/test/lib/hci-socket/signaling.test.js +94 -0
- package/test/lib/hci-socket/smp.test.js +268 -0
- package/test/lib/manufacture.test.js +77 -0
- package/test/lib/peripheral.test.js +623 -0
- package/test/lib/resolve-bindings.test.js +102 -0
- package/test/lib/service.test.js +195 -0
- package/test/lib/webbluetooth/bindings.test.js +190 -0
- package/test/lib/websocket/bindings.test.js +456 -0
- package/test/noble.test.js +1565 -0
- package/test.js +131 -0
- package/with-bindings.js +5 -0
- package/ws-slave.js +404 -0
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg width="600px" height="266px" viewBox="0 0 600 266" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
|
3
|
+
<title>noble-logo-less-spacing3</title>
|
|
4
|
+
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
|
|
5
|
+
<defs></defs>
|
|
6
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
|
7
|
+
<g id="v2" sketch:type="MSLayerGroup" transform="translate(39.000000, 41.000000)">
|
|
8
|
+
<path d="M15.844,69.808 C16.169335,72.8986821 16.4133325,76.314648 16.576,80.056 C16.7386675,83.797352 16.82,86.8879878 16.82,89.328 L17.308,89.328 C20.5613496,82.6586333 25.8479634,77.2500207 33.168,73.102 C40.4880366,68.9539793 48.214626,66.88 56.348,66.88 C70.8254057,66.88 81.6832971,71.1906236 88.922,79.812 C96.1607029,88.4333764 99.78,99.8199292 99.78,113.972 L99.78,184 L83.188,184 L83.188,120.804 C83.188,114.785303 82.7000049,109.336024 81.724,104.456 C80.7479951,99.5759756 79.0806785,95.3873508 76.722,91.89 C74.3633215,88.3926492 71.1913533,85.6680098 67.206,83.716 C63.2206467,81.7639902 58.2186968,80.788 52.2,80.788 C47.807978,80.788 43.5380207,81.6826577 39.39,83.472 C35.2419793,85.2613423 31.5413496,87.9859817 28.288,91.646 C25.0346504,95.3060183 22.4320098,99.9419719 20.48,105.554 C18.5279902,111.166028 17.552,117.794628 17.552,125.44 L17.552,184 L0.96,184 L0.96,94.94 C0.96,91.8493179 0.87866748,87.8640244 0.716,82.984 C0.55333252,78.1039756 0.30933496,73.7120195 -0.016,69.808 L15.844,69.808 Z M215.616,126.904 C215.616,120.559968 214.599344,114.582028 212.566,108.97 C210.532657,103.357972 207.645352,98.4780207 203.904,94.33 C200.162648,90.1819793 195.608027,86.8880122 190.24,84.448 C184.871973,82.0079878 178.853367,80.788 172.184,80.788 C165.514633,80.788 159.536693,82.0079878 154.25,84.448 C148.963307,86.8880122 144.449352,90.1819793 140.708,94.33 C136.966648,98.4780207 134.079343,103.357972 132.046,108.97 C130.012656,114.582028 128.996,120.559968 128.996,126.904 C128.996,133.248032 130.012656,139.225972 132.046,144.838 C134.079343,150.450028 136.966648,155.329979 140.708,159.478 C144.449352,163.626021 148.963307,166.879322 154.25,169.238 C159.536693,171.596678 165.514633,172.776 172.184,172.776 C178.853367,172.776 184.871973,171.596678 190.24,169.238 C195.608027,166.879322 200.162648,163.626021 203.904,159.478 C207.645352,155.329979 210.532657,150.450028 212.566,144.838 C214.599344,139.225972 215.616,133.248032 215.616,126.904 Z M262.888,164.724 L262.888,184 L246.296,184 L246.296,-0.464 L262.888,-0.464 L262.888,89.572 L263.376,89.572 C268.256024,82.0892959 274.599961,76.4366858 282.408,72.614 C290.216039,68.7913142 298.267959,66.88 306.564,66.88 C315.510711,66.88 323.603297,68.4253179 330.842,71.516 C338.080703,74.6066821 344.261974,78.8359732 349.386,84.204 C354.510026,89.5720268 358.495319,95.9159634 361.342,103.236 C364.188681,110.556037 365.612,118.445291 365.612,126.904 C365.612,135.362709 364.188681,143.251963 361.342,150.572 C358.495319,157.892037 354.510026,164.235973 349.386,169.604 C344.261974,174.972027 338.080703,179.201318 330.842,182.292 C323.603297,185.382682 315.510711,186.928 306.564,186.928 C298.267959,186.928 290.216039,185.057352 282.408,181.316 C274.599961,177.574648 268.256024,172.044037 263.376,164.724 L262.888,164.724 Z M348.044,126.904 C348.044,120.559968 347.06801,114.582028 345.116,108.97 C343.16399,103.357972 340.358018,98.4780207 336.698,94.33 C333.037982,90.1819793 328.524027,86.8880122 323.156,84.448 C317.787973,82.0079878 311.688034,80.788 304.856,80.788 C298.511968,80.788 292.656027,81.9673215 287.288,84.326 C281.919973,86.6846785 277.28402,89.9379793 273.38,94.086 C269.47598,98.2340207 266.426011,103.113972 264.23,108.726 C262.033989,114.338028 260.936,120.397301 260.936,126.904 C260.936,133.410699 262.033989,139.469972 264.23,145.082 C266.426011,150.694028 269.47598,155.533313 273.38,159.6 C277.28402,163.666687 281.919973,166.879322 287.288,169.238 C292.656027,171.596678 298.511968,172.776 304.856,172.776 C311.688034,172.776 317.787973,171.596678 323.156,169.238 C328.524027,166.879322 333.037982,163.626021 336.698,159.478 C340.358018,155.329979 343.16399,150.450028 345.116,144.838 C347.06801,139.225972 348.044,133.248032 348.044,126.904 Z M395.316,184 L378.724,184 L378.724,-0.464 L395.316,-0.464 L395.316,184 Z M520.668,123.488 L520.668,127.148 C520.668,128.44934 520.586667,129.831993 520.424,131.296 L425.752,131.296 C425.914667,136.989362 427.053323,142.357308 429.168,147.4 C431.282677,152.442692 434.210648,156.834648 437.952,160.576 C441.693352,164.317352 446.085308,167.285989 451.128,169.482 C456.170692,171.678011 461.619971,172.776 467.476,172.776 C476.097376,172.776 483.539302,170.783353 489.802,166.798 C496.064698,162.812647 500.82265,158.217359 504.076,153.012 L516.52,162.772 C509.687966,171.393376 502.164708,177.574648 493.95,181.316 C485.735292,185.057352 476.910714,186.928 467.476,186.928 C459.017291,186.928 451.168703,185.423348 443.93,182.414 C436.691297,179.404652 430.469359,175.216027 425.264,169.848 C420.058641,164.479973 415.951348,158.136037 412.942,150.816 C409.932652,143.495963 408.428,135.525376 408.428,126.904 C408.428,118.282624 409.891985,110.312037 412.82,102.992 C415.748015,95.6719634 419.814641,89.3280268 425.02,83.96 C430.225359,78.5919732 436.325298,74.4033484 443.32,71.394 C450.314702,68.3846516 457.878626,66.88 466.012,66.88 C474.958711,66.88 482.847966,68.3846516 489.68,71.394 C496.512034,74.4033484 502.205311,78.4699744 506.76,83.594 C511.314689,88.7180256 514.771322,94.6959658 517.13,101.528 C519.488678,108.360034 520.668,115.679961 520.668,123.488 Z M503.832,118.12 C503.343998,107.383946 499.887365,98.4780354 493.462,91.402 C487.036635,84.3259646 477.886726,80.788 466.012,80.788 C460.481306,80.788 455.357357,81.8046565 450.64,83.838 C445.922643,85.8713435 441.774685,88.6366492 438.196,92.134 C434.617315,95.6313508 431.770677,99.6166443 429.656,104.09 C427.541323,108.563356 426.321335,113.239976 425.996,118.12 L503.832,118.12 Z" id="noble" fill="#1977E0" sketch:type="MSShapeGroup"></path>
|
|
9
|
+
<path d="M233.184,126.904 C233.184,135.525376 231.679348,143.495963 228.67,150.816 C225.660652,158.136037 221.472027,164.479973 216.104,169.848 C210.735973,175.216027 204.310704,179.404652 196.828,182.414 C189.345296,185.423348 181.130711,186.928 172.184,186.928 C163.399956,186.928 155.266704,185.423348 147.784,182.414 C140.301296,179.404652 133.876027,175.216027 128.508,169.848 C123.139973,164.479973 118.951348,158.136037 115.942,150.816 C112.932652,143.495963 111.428,135.525376 111.428,126.904 C111.428,118.282624 112.932652,110.312037 115.942,102.992 C118.951348,95.6719634 123.139973,89.3280268 128.508,83.96 C133.876027,78.5919732 140.301296,74.4033484 147.784,71.394 C155.266704,68.3846516 163.399956,66.88 172.184,66.88 C181.130711,66.88 189.345296,68.3846516 196.828,71.394 C204.310704,74.4033484 210.735973,78.5919732 216.104,83.96 C221.472027,89.3280268 225.660652,95.6719634 228.67,102.992 C231.679348,110.312037 233.184,118.282624 233.184,126.904 L233.184,126.904 Z" id="Path" fill="#1977E0" sketch:type="MSShapeGroup"></path>
|
|
10
|
+
<path d="M191.265672,141.408876 L174.98806,125.550988 L190.776119,110.181035 C191.265672,109.6931 191.510448,109.083181 191.510448,108.473262 C191.510448,107.863343 191.265672,107.131441 190.776119,106.765489 L173.274627,89.6877639 C172.540299,88.9558614 171.561194,88.8338776 170.58209,89.1998289 C169.725373,89.5657802 169.113433,90.4196664 169.113433,91.3955365 L169.113433,119.695767 L155.283582,106.277554 C154.304478,105.301684 152.713433,105.301684 151.856716,106.277554 C150.877612,107.253424 150.877612,108.839213 151.856716,109.6931 L168.01194,125.429004 L151.734328,141.164908 C150.755224,142.140778 150.755224,143.604583 151.734328,144.580453 C152.713433,145.556323 154.18209,145.556323 155.161194,144.580453 L168.991045,131.040256 L168.991045,160.560325 C168.991045,161.536195 169.602985,162.390081 170.459701,162.756032 C170.704478,162.878016 171.071642,163 171.438806,163 C172.050746,163 172.662687,162.756032 173.152239,162.268097 L191.143284,144.580453 C191.755224,144.336486 192,143.726567 192,143.116648 C192,142.506729 191.755224,141.774827 191.265672,141.408876 Z M174,97 L185,108.562162 L174,120 L174,97 L174,97 Z M174,155 L174,131 L186,143 L174,155 L174,155 Z" id="Shape" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
|
|
11
|
+
</g>
|
|
12
|
+
</g>
|
|
13
|
+
</svg>
|
package/binding.gyp
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
'targets': [
|
|
3
|
+
{
|
|
4
|
+
'target_name': 'noble',
|
|
5
|
+
'conditions': [
|
|
6
|
+
['OS=="mac"', {
|
|
7
|
+
'dependencies': [
|
|
8
|
+
'lib/mac/binding.gyp:binding',
|
|
9
|
+
],
|
|
10
|
+
}],
|
|
11
|
+
['OS=="win"', {
|
|
12
|
+
'dependencies': [
|
|
13
|
+
'lib/win/binding.gyp:binding',
|
|
14
|
+
],
|
|
15
|
+
}],
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
}
|
package/codecov.yml
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const noble = require('../index')({ extended: false });
|
|
2
|
+
|
|
3
|
+
noble.on('stateChange', function (state) {
|
|
4
|
+
if (state === 'poweredOn') {
|
|
5
|
+
noble.startScanning([], false);
|
|
6
|
+
} else {
|
|
7
|
+
noble.stopScanning();
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
noble.on('discover', function (peripheral) {
|
|
12
|
+
console.log(`${new Date()}`);
|
|
13
|
+
console.log(
|
|
14
|
+
`Peripheral discovered (${peripheral.id} with address <${peripheral.address}, ${peripheral.addressType}>, connectable: ${peripheral.connectable}, scannable: ${peripheral.scannable}, RSSI ${peripheral.rssi}:`
|
|
15
|
+
);
|
|
16
|
+
console.log('\thello my local name is:');
|
|
17
|
+
console.log(`\t\t${peripheral.advertisement.localName}`);
|
|
18
|
+
console.log(
|
|
19
|
+
'\tcan I interest you in any of the following advertised services:'
|
|
20
|
+
);
|
|
21
|
+
console.log(`\t\t${JSON.stringify(peripheral.advertisement.serviceUuids)}`);
|
|
22
|
+
const serviceData = peripheral.advertisement.serviceData;
|
|
23
|
+
|
|
24
|
+
if (serviceData && serviceData.length) {
|
|
25
|
+
console.log('\there is my service data:');
|
|
26
|
+
for (const i in serviceData) {
|
|
27
|
+
console.log(
|
|
28
|
+
`\t\t${JSON.stringify(serviceData[i].uuid)}: ${JSON.stringify(
|
|
29
|
+
serviceData[i].data.toString('hex')
|
|
30
|
+
)}`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (peripheral.advertisement.manufacturerData) {
|
|
36
|
+
console.log('\there is my manufacturer data:');
|
|
37
|
+
console.log(
|
|
38
|
+
`\t\t${JSON.stringify(
|
|
39
|
+
peripheral.advertisement.manufacturerData.toString('hex')
|
|
40
|
+
)}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (peripheral.advertisement.txPowerLevel !== undefined) {
|
|
45
|
+
console.log('\tmy TX power level is:');
|
|
46
|
+
console.log(`\t\t${peripheral.advertisement.txPowerLevel}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
process.on('SIGINT', function () {
|
|
53
|
+
console.log('Caught interrupt signal');
|
|
54
|
+
noble.stopScanning(() => process.exit());
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
process.on('SIGQUIT', function () {
|
|
58
|
+
console.log('Caught interrupt signal');
|
|
59
|
+
noble.stopScanning(() => process.exit());
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
process.on('SIGTERM', function () {
|
|
63
|
+
console.log('Caught interrupt signal');
|
|
64
|
+
noble.stopScanning(() => process.exit());
|
|
65
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/* eslint-disable handle-callback-err */
|
|
2
|
+
/** discover a device (here, the first one where the name was resolved),
|
|
3
|
+
* for the first device discover all services and characteristics,
|
|
4
|
+
* store the collected GATT information into a meta-data object and write to disk.
|
|
5
|
+
* Finds a temperature characteristic and registers for data.
|
|
6
|
+
* Prints timing information from discovered to connected to reading states.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const noble = require('../index')({ extended: false });
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
|
|
12
|
+
// the sensor value to scan for, number of bits and factor for displaying it
|
|
13
|
+
const CHANNEL = process.env.CHANNEL ? process.env.CHANNEL : 'Temperature';
|
|
14
|
+
const BITS = process.env.BITS ? 1 * process.env.BITS : 16;
|
|
15
|
+
const FACTOR = process.env.FACTOR ? 1.0 * process.env.FACTOR : 0.1;
|
|
16
|
+
|
|
17
|
+
const EXT = '.dump';
|
|
18
|
+
|
|
19
|
+
noble.on('stateChange', function (state) {
|
|
20
|
+
if (state === 'poweredOn') {
|
|
21
|
+
noble.startScanning([], false);
|
|
22
|
+
} else {
|
|
23
|
+
noble.stopScanning();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
let tDisco = 0; // time when device was discovered
|
|
28
|
+
let tConn = 0; // time when connection to device was established
|
|
29
|
+
let tRead = 0; // time when reading data starts.
|
|
30
|
+
|
|
31
|
+
// collect device meta-data into this object:
|
|
32
|
+
const meta = {
|
|
33
|
+
services: [], // stores an array of GATT service data objects
|
|
34
|
+
characteristics: {} // a map with key service-UUID, stores the array of characteristics
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
noble.on('discover', function (peripheral) {
|
|
38
|
+
console.log(
|
|
39
|
+
`peripheral discovered (${peripheral.id} with address <${peripheral.address}, ${peripheral.addressType}>, connectable ${peripheral.connectable}, RSSI ${peripheral.rssi}:`
|
|
40
|
+
);
|
|
41
|
+
console.log('\thello my local name is:');
|
|
42
|
+
console.log(`\t\t${peripheral.advertisement.localName}`);
|
|
43
|
+
console.log();
|
|
44
|
+
|
|
45
|
+
// connect to the first device with a valid name
|
|
46
|
+
if (peripheral.advertisement.localName) {
|
|
47
|
+
console.log(
|
|
48
|
+
`Connecting to ${peripheral.address} ${peripheral.advertisement.localName}`
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
tDisco = Date.now();
|
|
52
|
+
|
|
53
|
+
connectToDevice(peripheral);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const connectToDevice = function (peripheral) {
|
|
58
|
+
// BLE cannot scan and connect in parallel, so we stop scanning here:
|
|
59
|
+
noble.stopScanning();
|
|
60
|
+
|
|
61
|
+
peripheral.connect((error) => {
|
|
62
|
+
// noble.startScanning([], true)
|
|
63
|
+
if (error) {
|
|
64
|
+
console.log(`Connect error: ${error}`);
|
|
65
|
+
noble.startScanning([], true);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
tConn = Date.now();
|
|
69
|
+
console.log('Connected!');
|
|
70
|
+
|
|
71
|
+
findServices(noble, peripheral);
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
let servicesToRead = 0;
|
|
76
|
+
|
|
77
|
+
const findServices = function (noble, peripheral) {
|
|
78
|
+
meta.uuid = peripheral.uuid;
|
|
79
|
+
meta.address = peripheral.address;
|
|
80
|
+
meta.name = peripheral.advertisement.localName; // not needed but nice to have
|
|
81
|
+
|
|
82
|
+
meta.characteristics = {};
|
|
83
|
+
|
|
84
|
+
// callback triggers with GATT-relevant data
|
|
85
|
+
peripheral.on('servicesDiscovered', (peripheral, services) => {
|
|
86
|
+
console.log(`servicesDiscovered: Found ${services.length} services! `);
|
|
87
|
+
meta.services = services;
|
|
88
|
+
for (const i in services) {
|
|
89
|
+
const service = services[i];
|
|
90
|
+
console.log(`\tservice ${i} : ${JSON.stringify(service)}`);
|
|
91
|
+
// meta.services[ service.uuid ] = service
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
peripheral.discoverServices([], (error, services) => {
|
|
96
|
+
if (error) {
|
|
97
|
+
console.error(error);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let sensorCharacteristic;
|
|
102
|
+
|
|
103
|
+
servicesToRead = services.length;
|
|
104
|
+
// we found the list of services, now trigger characteristics lookup for each of them:
|
|
105
|
+
|
|
106
|
+
for (let i = 0; i < services.length; i++) {
|
|
107
|
+
const service = services[i];
|
|
108
|
+
|
|
109
|
+
service.on('characteristicsDiscovered', (characteristics) => {
|
|
110
|
+
// store the list of characteristics per service
|
|
111
|
+
meta.characteristics[service.uuid] = characteristics;
|
|
112
|
+
|
|
113
|
+
console.log(`SRV\t${service.uuid} characteristic GATT data: `);
|
|
114
|
+
for (let i = 0; i < characteristics.length; i++) {
|
|
115
|
+
console.log(
|
|
116
|
+
`\t${service.uuid} chara.\t ${i} ${JSON.stringify(
|
|
117
|
+
characteristics[i]
|
|
118
|
+
)}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
service.discoverCharacteristics([], function (error, characteristics) {
|
|
124
|
+
if (error) {
|
|
125
|
+
console.error(error);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log(`SRV\t${service.uuid} characteristic decoded data: `);
|
|
130
|
+
for (let j = 0; j < characteristics.length; j++) {
|
|
131
|
+
const ch = characteristics[j];
|
|
132
|
+
console.log(`\t${service.uuid} chara.\t ${j} ${ch}`);
|
|
133
|
+
|
|
134
|
+
if (ch.name === CHANNEL) {
|
|
135
|
+
console.log(`found ${CHANNEL} characteristic!`);
|
|
136
|
+
sensorCharacteristic = ch;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
servicesToRead--;
|
|
141
|
+
if (!servicesToRead) {
|
|
142
|
+
console.log('----------------- FINISHED');
|
|
143
|
+
console.log(JSON.stringify(meta, null, 4));
|
|
144
|
+
// write to file
|
|
145
|
+
fs.writeFile(
|
|
146
|
+
meta.uuid + EXT,
|
|
147
|
+
JSON.stringify(meta, null, 2),
|
|
148
|
+
function (err) {
|
|
149
|
+
if (err) {
|
|
150
|
+
return console.log(err);
|
|
151
|
+
}
|
|
152
|
+
console.log('The data was saved to ', meta.uuid + EXT);
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (sensorCharacteristic) {
|
|
157
|
+
console.log('Listening for temperature data...');
|
|
158
|
+
|
|
159
|
+
tRead = Date.now();
|
|
160
|
+
|
|
161
|
+
sensorCharacteristic.on('data', (data) => {
|
|
162
|
+
if (BITS === 16) {
|
|
163
|
+
console.log(` new ${CHANNEL} ${data.readUInt16LE() * FACTOR}`);
|
|
164
|
+
} else if (BITS === 32) {
|
|
165
|
+
console.log(` new ${CHANNEL} ${data.readUInt32LE() * FACTOR}`);
|
|
166
|
+
} else {
|
|
167
|
+
console.log(` Cannot cope with BITS value ${BITS}`);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
sensorCharacteristic.read();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.log(
|
|
174
|
+
`Timespan from discovery to connected: ${tConn - tDisco} ms`
|
|
175
|
+
);
|
|
176
|
+
console.log(
|
|
177
|
+
`Timespan from connected to reading : ${tRead - tConn} ms`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
process.on('SIGINT', function () {
|
|
186
|
+
console.log('Caught interrupt signal');
|
|
187
|
+
noble.stopScanning(() => process.exit());
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
process.on('SIGQUIT', function () {
|
|
191
|
+
console.log('Caught interrupt signal');
|
|
192
|
+
noble.stopScanning(() => process.exit());
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
process.on('SIGTERM', function () {
|
|
196
|
+
console.log('Caught interrupt signal');
|
|
197
|
+
noble.stopScanning(() => process.exit());
|
|
198
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* eslint-disable handle-callback-err */
|
|
2
|
+
/** reconnect to a device that has been discovered earlier on using cache-gatt-discovery:
|
|
3
|
+
* If a device is discovered and a dump file exists, load it and connect to it, re-initializing service
|
|
4
|
+
* and characteristic objects in the noble stack.
|
|
5
|
+
* Finds a temperature characteristic and registers for data.
|
|
6
|
+
* Prints timing information from discovered to connected to reading states.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const noble = require('../index')({ extended: false });
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
|
|
12
|
+
// the sensor value to scan for, number of bits and factor for displaying it
|
|
13
|
+
const CHANNEL = process.env.CHANNEL ? process.env.CHANNEL : 'Temperature';
|
|
14
|
+
const BITS = process.env.BITS ? 1 * process.env.BITS : 16;
|
|
15
|
+
const FACTOR = process.env.FACTOR ? 1.0 * process.env.FACTOR : 0.1;
|
|
16
|
+
|
|
17
|
+
const EXT = '.dump';
|
|
18
|
+
|
|
19
|
+
noble.on('stateChange', function (state) {
|
|
20
|
+
if (state === 'poweredOn') {
|
|
21
|
+
noble.startScanning([], false);
|
|
22
|
+
} else {
|
|
23
|
+
noble.stopScanning();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
let tDisco = 0; // time when device was discovered
|
|
28
|
+
let tConn = 0; // time when connection to device was established
|
|
29
|
+
let tRead = 0; // time when reading data starts.
|
|
30
|
+
|
|
31
|
+
// collect device meta-data into this object:
|
|
32
|
+
let meta = {
|
|
33
|
+
services: {}, // a map indexted by service-UUID -> contains service data
|
|
34
|
+
characteristics: {} // an map with key service-UUID, stores the array of characteristics
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
noble.on('discover', function (peripheral) {
|
|
38
|
+
console.log(
|
|
39
|
+
`peripheral discovered (${peripheral.id} with address <${peripheral.address}, ${peripheral.addressType}>, connectable ${peripheral.connectable}, RSSI ${peripheral.rssi}:`
|
|
40
|
+
);
|
|
41
|
+
console.log('\thello my local name is:');
|
|
42
|
+
console.log(`\t\t${peripheral.advertisement.localName}`);
|
|
43
|
+
console.log();
|
|
44
|
+
|
|
45
|
+
// Check if a dump exists in the current directory.
|
|
46
|
+
fs.access(peripheral.uuid + EXT, fs.constants.F_OK, (err) => {
|
|
47
|
+
if (!err) {
|
|
48
|
+
console.log(`found dump file for ${peripheral.uuid}`);
|
|
49
|
+
|
|
50
|
+
tDisco = Date.now();
|
|
51
|
+
|
|
52
|
+
quickConnect(peripheral);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const quickConnect = function (peripheral) {
|
|
58
|
+
// BLE cannot scan and connect in parallel, so we stop scanning here:
|
|
59
|
+
noble.stopScanning();
|
|
60
|
+
|
|
61
|
+
peripheral.connect((error) => {
|
|
62
|
+
if (error) {
|
|
63
|
+
console.log(`Connect error: ${error}`);
|
|
64
|
+
noble.startScanning([], true);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
tConn = Date.now();
|
|
68
|
+
console.log('Connected!');
|
|
69
|
+
|
|
70
|
+
// load stored data. This needs to be done when connected, as we need a handle at GATT level
|
|
71
|
+
meta = loadData(peripheral);
|
|
72
|
+
|
|
73
|
+
// initialize the service and charateristics objects in Noble; return a temperature characteristic, if found
|
|
74
|
+
const sensorCharacteristic = setData(peripheral, meta);
|
|
75
|
+
|
|
76
|
+
if (!sensorCharacteristic) {
|
|
77
|
+
console.log('Warning - no temperature characteristic found.');
|
|
78
|
+
} else {
|
|
79
|
+
console.log('Listening for temperature data...');
|
|
80
|
+
|
|
81
|
+
tRead = Date.now();
|
|
82
|
+
|
|
83
|
+
sensorCharacteristic.on('data', (data) => {
|
|
84
|
+
if (BITS === 16) {
|
|
85
|
+
console.log(` new ${CHANNEL} ${data.readUInt16LE() * FACTOR}`);
|
|
86
|
+
} else if (BITS === 32) {
|
|
87
|
+
console.log(` new ${CHANNEL} ${data.readUInt32LE() * FACTOR}`);
|
|
88
|
+
} else {
|
|
89
|
+
console.log(` Cannot cope with BITS value ${BITS}`);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
sensorCharacteristic.read();
|
|
93
|
+
|
|
94
|
+
console.log(`Timespan from discovery to connected: ${tConn - tDisco} ms`);
|
|
95
|
+
console.log(`Timespan from connected to reading : ${tRead - tConn} ms`);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const loadData = function (peripheral) {
|
|
101
|
+
const dump = fs.readFileSync(peripheral.uuid + EXT);
|
|
102
|
+
const data = JSON.parse(dump);
|
|
103
|
+
|
|
104
|
+
// verify data: console.log(JSON.stringify(data,null,2))
|
|
105
|
+
return data;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const setData = function (peripheral, meta) {
|
|
109
|
+
// first, create the service objects:
|
|
110
|
+
console.log('initializing services... ');
|
|
111
|
+
|
|
112
|
+
// addServices returns an array of initialized service objects
|
|
113
|
+
const services = noble.addServices(peripheral.uuid, meta.services);
|
|
114
|
+
|
|
115
|
+
console.log('initialized services: ');
|
|
116
|
+
for (const i in services) {
|
|
117
|
+
const service = services[i];
|
|
118
|
+
console.log(`\tservice ${i} ${service}`);
|
|
119
|
+
}
|
|
120
|
+
console.log();
|
|
121
|
+
|
|
122
|
+
let sensorCharacteristic;
|
|
123
|
+
|
|
124
|
+
console.log('initializing characteristics... ');
|
|
125
|
+
// now, for each service, set the characteristics:
|
|
126
|
+
for (const i in services) {
|
|
127
|
+
const service = services[i];
|
|
128
|
+
const charas = meta.characteristics[service.uuid];
|
|
129
|
+
console.log(`\tservice ${i} ${service} ${JSON.stringify(charas)}`);
|
|
130
|
+
|
|
131
|
+
const characteristics = noble.addCharacteristics(
|
|
132
|
+
peripheral.uuid,
|
|
133
|
+
service.uuid,
|
|
134
|
+
charas
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
for (const j in characteristics) {
|
|
138
|
+
const characteristic = characteristics[j];
|
|
139
|
+
console.log(
|
|
140
|
+
`\t\tcharac ${service.uuid} ${j} ${characteristic} ${characteristic.rawProps}`
|
|
141
|
+
);
|
|
142
|
+
if (characteristic.name === CHANNEL) {
|
|
143
|
+
console.log(`\t\t\t-->found ${CHANNEL} characteristic!`);
|
|
144
|
+
sensorCharacteristic = characteristic;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return sensorCharacteristic;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
process.on('SIGINT', function () {
|
|
152
|
+
console.log('Caught interrupt signal');
|
|
153
|
+
noble.stopScanning(() => process.exit());
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
process.on('SIGQUIT', function () {
|
|
157
|
+
console.log('Caught interrupt signal');
|
|
158
|
+
noble.stopScanning(() => process.exit());
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
process.on('SIGTERM', function () {
|
|
162
|
+
console.log('Caught interrupt signal');
|
|
163
|
+
noble.stopScanning(() => process.exit());
|
|
164
|
+
});
|
package/examples/echo.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* eslint-disable handle-callback-err */
|
|
2
|
+
// Connect to a peripheral running the echo service
|
|
3
|
+
// https://github.com/noble/bleno/blob/master/examples/echo
|
|
4
|
+
|
|
5
|
+
// subscribe to be notified when the value changes
|
|
6
|
+
// start an interval to write data to the characteristic
|
|
7
|
+
|
|
8
|
+
// const noble = require('noble');
|
|
9
|
+
const noble = require('..')({ extended: false });
|
|
10
|
+
|
|
11
|
+
const ECHO_SERVICE_UUID = 'ec00';
|
|
12
|
+
const ECHO_CHARACTERISTIC_UUID = 'ec0e';
|
|
13
|
+
|
|
14
|
+
noble.on('stateChange', (state) => {
|
|
15
|
+
if (state === 'poweredOn') {
|
|
16
|
+
console.log('Scanning');
|
|
17
|
+
noble.startScanning([ECHO_SERVICE_UUID], false);
|
|
18
|
+
} else {
|
|
19
|
+
noble.stopScanning();
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
noble.on('discover', (peripheral) => {
|
|
24
|
+
// connect to the first peripheral that is scanned
|
|
25
|
+
noble.stopScanning();
|
|
26
|
+
const name = peripheral.advertisement.localName;
|
|
27
|
+
console.log(`Connecting to '${name}' ${peripheral.id}`);
|
|
28
|
+
connectAndSetUp(peripheral);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
function connectAndSetUp (peripheral) {
|
|
32
|
+
peripheral.connect((error) => {
|
|
33
|
+
if (error) {
|
|
34
|
+
console.error(error);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log('Connected to', peripheral.id);
|
|
39
|
+
|
|
40
|
+
// specify the services and characteristics to discover
|
|
41
|
+
const serviceUUIDs = [ECHO_SERVICE_UUID];
|
|
42
|
+
const characteristicUUIDs = [ECHO_CHARACTERISTIC_UUID];
|
|
43
|
+
|
|
44
|
+
peripheral.discoverSomeServicesAndCharacteristics(
|
|
45
|
+
serviceUUIDs,
|
|
46
|
+
characteristicUUIDs,
|
|
47
|
+
onServicesAndCharacteristicsDiscovered
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
peripheral.on('disconnect', () => console.log('disconnected'));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function onServicesAndCharacteristicsDiscovered (
|
|
55
|
+
error,
|
|
56
|
+
services,
|
|
57
|
+
characteristics
|
|
58
|
+
) {
|
|
59
|
+
if (error) {
|
|
60
|
+
console.error(error);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log('Discovered services and characteristics');
|
|
65
|
+
const echoCharacteristic = characteristics[0];
|
|
66
|
+
|
|
67
|
+
// data callback receives notifications
|
|
68
|
+
echoCharacteristic.on('data', (data, isNotification) => {
|
|
69
|
+
console.log(`Received: "${data}"`);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// subscribe to be notified whenever the peripheral update the characteristic
|
|
73
|
+
echoCharacteristic.subscribe((error) => {
|
|
74
|
+
if (error) {
|
|
75
|
+
console.error('Error subscribing to echoCharacteristic');
|
|
76
|
+
} else {
|
|
77
|
+
console.log('Subscribed for echoCharacteristic notifications');
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// create an interval to send data to the service
|
|
82
|
+
let count = 0;
|
|
83
|
+
setInterval(() => {
|
|
84
|
+
count++;
|
|
85
|
+
const message = Buffer.from(`hello, ble ${count}`, 'utf-8');
|
|
86
|
+
console.log(`Sending: '${message}'`);
|
|
87
|
+
echoCharacteristic.write(message);
|
|
88
|
+
}, 2500);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
process.on('SIGINT', function () {
|
|
92
|
+
console.log('Caught interrupt signal');
|
|
93
|
+
noble.stopScanning(() => process.exit());
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
process.on('SIGQUIT', function () {
|
|
97
|
+
console.log('Caught interrupt signal');
|
|
98
|
+
noble.stopScanning(() => process.exit());
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
process.on('SIGTERM', function () {
|
|
102
|
+
console.log('Caught interrupt signal');
|
|
103
|
+
noble.stopScanning(() => process.exit());
|
|
104
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* eslint-disable handle-callback-err */
|
|
2
|
+
/*
|
|
3
|
+
Continuously scans for peripherals and prints out message when they enter/exit
|
|
4
|
+
|
|
5
|
+
In range criteria: RSSI < threshold
|
|
6
|
+
Out of range criteria: lastSeen > grace period
|
|
7
|
+
|
|
8
|
+
based on code provided by: Mattias Ask (http://www.dittlof.com)
|
|
9
|
+
*/
|
|
10
|
+
const noble = require('../index')({ extended: false });
|
|
11
|
+
|
|
12
|
+
const RSSI_THRESHOLD = -90;
|
|
13
|
+
const EXIT_GRACE_PERIOD = 2000; // milliseconds
|
|
14
|
+
|
|
15
|
+
const inRange = [];
|
|
16
|
+
|
|
17
|
+
noble.on('discover', function (peripheral) {
|
|
18
|
+
if (peripheral.rssi < RSSI_THRESHOLD) {
|
|
19
|
+
// ignore
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const id = peripheral.id;
|
|
24
|
+
const entered = !inRange[id];
|
|
25
|
+
|
|
26
|
+
if (entered) {
|
|
27
|
+
inRange[id] = {
|
|
28
|
+
peripheral: peripheral
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
console.log(
|
|
32
|
+
`"${peripheral.advertisement.localName}" entered (RSSI ${
|
|
33
|
+
peripheral.rssi
|
|
34
|
+
}) ${new Date()}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
inRange[id].lastSeen = Date.now();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
setInterval(function () {
|
|
42
|
+
for (const id in inRange) {
|
|
43
|
+
if (inRange[id].lastSeen < Date.now() - EXIT_GRACE_PERIOD) {
|
|
44
|
+
const peripheral = inRange[id].peripheral;
|
|
45
|
+
|
|
46
|
+
console.log(
|
|
47
|
+
`"${peripheral.advertisement.localName}" exited (RSSI ${
|
|
48
|
+
peripheral.rssi
|
|
49
|
+
}) ${new Date()}`
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
delete inRange[id];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}, EXIT_GRACE_PERIOD / 2);
|
|
56
|
+
|
|
57
|
+
noble.on('stateChange', function (state) {
|
|
58
|
+
if (state === 'poweredOn') {
|
|
59
|
+
noble.startScanning([], true);
|
|
60
|
+
} else {
|
|
61
|
+
noble.stopScanning();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
process.on('SIGINT', function () {
|
|
66
|
+
console.log('Caught interrupt signal');
|
|
67
|
+
noble.stopScanning(() => process.exit());
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
process.on('SIGQUIT', function () {
|
|
71
|
+
console.log('Caught interrupt signal');
|
|
72
|
+
noble.stopScanning(() => process.exit());
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
process.on('SIGTERM', function () {
|
|
76
|
+
console.log('Caught interrupt signal');
|
|
77
|
+
noble.stopScanning(() => process.exit());
|
|
78
|
+
});
|