@stoprocent/bleno 0.7.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.
- package/.jshintrc +5 -0
- package/LICENSE +20 -0
- package/README.md +409 -0
- package/binding.gyp +17 -0
- package/examples/battery-service/README.md +14 -0
- package/examples/battery-service/battery-level-characteristic.js +45 -0
- package/examples/battery-service/battery-service.js +16 -0
- package/examples/battery-service/main.js +28 -0
- package/examples/battery-service/package-lock.json +1314 -0
- package/examples/battery-service/package.json +20 -0
- package/examples/blink1/README.md +44 -0
- package/examples/blink1/blink1-fade-rgb-characteristic.js +42 -0
- package/examples/blink1/blink1-rgb-characteristic.js +38 -0
- package/examples/blink1/blink1-service.js +19 -0
- package/examples/blink1/device-information-service.js +19 -0
- package/examples/blink1/hardware-revision-characteristic.js +32 -0
- package/examples/blink1/main.js +32 -0
- package/examples/blink1/serial-number-characteristic.js +21 -0
- package/examples/echo/characteristic.js +45 -0
- package/examples/echo/main.js +33 -0
- package/examples/pizza/README.md +16 -0
- package/examples/pizza/peripheral.js +57 -0
- package/examples/pizza/pizza-bake-characteristic.js +40 -0
- package/examples/pizza/pizza-crust-characteristic.js +52 -0
- package/examples/pizza/pizza-service.js +20 -0
- package/examples/pizza/pizza-toppings-characteristic.js +41 -0
- package/examples/pizza/pizza.js +58 -0
- package/examples/uart/main.js +23 -0
- package/index.d.ts +153 -0
- package/index.js +1 -0
- package/lib/bleno.js +231 -0
- package/lib/characteristic.js +91 -0
- package/lib/descriptor.js +17 -0
- package/lib/hci-socket/acl-stream.js +37 -0
- package/lib/hci-socket/bindings.js +219 -0
- package/lib/hci-socket/crypto.js +74 -0
- package/lib/hci-socket/gap.js +212 -0
- package/lib/hci-socket/gatt.js +1028 -0
- package/lib/hci-socket/hci-status.json +67 -0
- package/lib/hci-socket/hci.js +796 -0
- package/lib/hci-socket/mgmt.js +89 -0
- package/lib/hci-socket/smp.js +160 -0
- package/lib/hci-socket/vs.js +156 -0
- package/lib/mac/binding.gyp +39 -0
- package/lib/mac/bindings.js +12 -0
- package/lib/mac/src/ble_peripheral_manager.h +32 -0
- package/lib/mac/src/ble_peripheral_manager.mm +241 -0
- package/lib/mac/src/bleno_mac.h +26 -0
- package/lib/mac/src/bleno_mac.mm +167 -0
- package/lib/mac/src/callbacks.h +76 -0
- package/lib/mac/src/callbacks.mm +124 -0
- package/lib/mac/src/napi_objc.h +30 -0
- package/lib/mac/src/napi_objc.mm +286 -0
- package/lib/mac/src/noble_mac.h +34 -0
- package/lib/mac/src/noble_mac.mm +260 -0
- package/lib/mac/src/objc_cpp.h +27 -0
- package/lib/mac/src/objc_cpp.mm +144 -0
- package/lib/mac/src/peripheral.h +23 -0
- package/lib/primary-service.js +19 -0
- package/lib/resolve-bindings.js +19 -0
- package/lib/uuid-util.js +7 -0
- package/package.json +77 -0
- package/prebuilds/android-arm/node.napi.armv7.node +0 -0
- package/prebuilds/android-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
- package/prebuilds/linux-arm/node.napi.armv6.node +0 -0
- package/prebuilds/linux-arm/node.napi.armv7.node +0 -0
- package/prebuilds/linux-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/node.napi.musl.node +0 -0
- package/prebuilds/win32-ia32/node.napi.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/test/characteristic.test.js +174 -0
- package/test/descriptor.test.js +46 -0
- package/test/mocha.setup.js +0 -0
- package/with-bindings.js +5 -0
- package/with-custom-binding.js +6 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const debug = require('debug')('mgmt');
|
|
2
|
+
|
|
3
|
+
const BluetoothHciSocket = require('@stoprocent/bluetooth-hci-socket');
|
|
4
|
+
|
|
5
|
+
const LTK_INFO_SIZE = 36;
|
|
6
|
+
|
|
7
|
+
const MGMT_OP_LOAD_LONG_TERM_KEYS = 0x0013;
|
|
8
|
+
|
|
9
|
+
class Mgmt {
|
|
10
|
+
constructor () {
|
|
11
|
+
this._socket = new BluetoothHciSocket();
|
|
12
|
+
this._ltkInfos = [];
|
|
13
|
+
|
|
14
|
+
this._socket.on('data', this.onSocketData.bind(this));
|
|
15
|
+
this._socket.on('error', this.onSocketError.bind(this));
|
|
16
|
+
|
|
17
|
+
this._socket.bindControl();
|
|
18
|
+
this._socket.start();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
onSocketData (data) {
|
|
22
|
+
debug('on data ->' + data.toString('hex'));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onSocketError (error) {
|
|
26
|
+
debug('on error ->' + error.message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
addLongTermKey (address, addressType, authenticated, master, ediv, rand, key) {
|
|
30
|
+
const ltkInfo = Buffer.alloc(LTK_INFO_SIZE);
|
|
31
|
+
|
|
32
|
+
address.copy(ltkInfo, 0);
|
|
33
|
+
ltkInfo.writeUInt8(addressType.readUInt8(0) + 1, 6); // BDADDR_LE_PUBLIC = 0x01, BDADDR_LE_RANDOM 0x02, so add one
|
|
34
|
+
|
|
35
|
+
ltkInfo.writeUInt8(authenticated, 7);
|
|
36
|
+
ltkInfo.writeUInt8(master, 8);
|
|
37
|
+
ltkInfo.writeUInt8(key.length, 9);
|
|
38
|
+
|
|
39
|
+
ediv.copy(ltkInfo, 10);
|
|
40
|
+
rand.copy(ltkInfo, 12);
|
|
41
|
+
key.copy(ltkInfo, 20);
|
|
42
|
+
|
|
43
|
+
this._ltkInfos.push(ltkInfo);
|
|
44
|
+
|
|
45
|
+
this.loadLongTermKeys();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
clearLongTermKeys () {
|
|
49
|
+
this._ltkInfos = [];
|
|
50
|
+
|
|
51
|
+
this.loadLongTermKeys();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
loadLongTermKeys () {
|
|
55
|
+
const numLongTermKeys = this._ltkInfos.length;
|
|
56
|
+
const op = Buffer.alloc(2 + numLongTermKeys * LTK_INFO_SIZE);
|
|
57
|
+
|
|
58
|
+
op.writeUInt16LE(numLongTermKeys, 0);
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < numLongTermKeys; i++) {
|
|
61
|
+
this._ltkInfos[i].copy(op, 2 + i * LTK_INFO_SIZE);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.write(MGMT_OP_LOAD_LONG_TERM_KEYS, 0, op);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
write (opcode, index, data) {
|
|
68
|
+
let length = 0;
|
|
69
|
+
|
|
70
|
+
if (data) {
|
|
71
|
+
length = data.length;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const pkt = Buffer.alloc(6 + length);
|
|
75
|
+
|
|
76
|
+
pkt.writeUInt16LE(opcode, 0);
|
|
77
|
+
pkt.writeUInt16LE(index, 2);
|
|
78
|
+
pkt.writeUInt16LE(length, 4);
|
|
79
|
+
|
|
80
|
+
if (length) {
|
|
81
|
+
data.copy(pkt, 6);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
debug('writing -> ' + pkt.toString('hex'));
|
|
85
|
+
this._socket.write(pkt);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = new Mgmt();
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
const { EventEmitter } = require('events');
|
|
2
|
+
const crypto = require('./crypto');
|
|
3
|
+
const mgmt = require('./mgmt');
|
|
4
|
+
|
|
5
|
+
const SMP_CID = 0x0006;
|
|
6
|
+
|
|
7
|
+
const SMP_PAIRING_REQUEST = 0x01;
|
|
8
|
+
const SMP_PAIRING_RESPONSE = 0x02;
|
|
9
|
+
const SMP_PAIRING_CONFIRM = 0x03;
|
|
10
|
+
const SMP_PAIRING_RANDOM = 0x04;
|
|
11
|
+
const SMP_PAIRING_FAILED = 0x05;
|
|
12
|
+
const SMP_ENCRYPT_INFO = 0x06;
|
|
13
|
+
const SMP_MASTER_IDENT = 0x07;
|
|
14
|
+
|
|
15
|
+
const SMP_UNSPECIFIED = 0x08;
|
|
16
|
+
|
|
17
|
+
class Smp extends EventEmitter {
|
|
18
|
+
constructor (aclStream, localAddressType, localAddress, remoteAddressType, remoteAddress) {
|
|
19
|
+
super();
|
|
20
|
+
|
|
21
|
+
this._aclStream = aclStream;
|
|
22
|
+
|
|
23
|
+
this._iat = Buffer.from([(remoteAddressType === 'random') ? 0x01 : 0x00]);
|
|
24
|
+
this._ia = Buffer.from(remoteAddress.split(':').reverse().join(''), 'hex');
|
|
25
|
+
this._rat = Buffer.from([(localAddressType === 'random') ? 0x01 : 0x00]);
|
|
26
|
+
this._ra = Buffer.from(localAddress.split(':').reverse().join(''), 'hex');
|
|
27
|
+
|
|
28
|
+
this._stk = null;
|
|
29
|
+
this._random = null;
|
|
30
|
+
this._diversifier = null;
|
|
31
|
+
|
|
32
|
+
this.onAclStreamDataBinded = this.onAclStreamData.bind(this);
|
|
33
|
+
this.onAclStreamEncryptChangeBinded = this.onAclStreamEncryptChange.bind(this);
|
|
34
|
+
this.onAclStreamLtkNegReplyBinded = this.onAclStreamLtkNegReply.bind(this);
|
|
35
|
+
this.onAclStreamEndBinded = this.onAclStreamEnd.bind(this);
|
|
36
|
+
|
|
37
|
+
this._aclStream.on('data', this.onAclStreamDataBinded);
|
|
38
|
+
this._aclStream.on('encryptChange', this.onAclStreamEncryptChangeBinded);
|
|
39
|
+
this._aclStream.on('ltkNegReply', this.onAclStreamLtkNegReplyBinded);
|
|
40
|
+
this._aclStream.on('end', this.onAclStreamEndBinded);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
onAclStreamData (cid, data) {
|
|
44
|
+
if (cid !== SMP_CID) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const code = data.readUInt8(0);
|
|
49
|
+
|
|
50
|
+
if (SMP_PAIRING_REQUEST === code) {
|
|
51
|
+
this.handlePairingRequest(data);
|
|
52
|
+
} else if (SMP_PAIRING_CONFIRM === code) {
|
|
53
|
+
this.handlePairingConfirm(data);
|
|
54
|
+
} else if (SMP_PAIRING_RANDOM === code) {
|
|
55
|
+
this.handlePairingRandom(data);
|
|
56
|
+
} else if (SMP_PAIRING_FAILED === code) {
|
|
57
|
+
this.handlePairingFailed(data);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
onAclStreamEncryptChange (encrypted) {
|
|
62
|
+
if (encrypted) {
|
|
63
|
+
if (this._stk && this._diversifier && this._random) {
|
|
64
|
+
this.write(Buffer.concat([
|
|
65
|
+
Buffer.from([SMP_ENCRYPT_INFO]),
|
|
66
|
+
this._stk
|
|
67
|
+
]));
|
|
68
|
+
|
|
69
|
+
this.write(Buffer.concat([
|
|
70
|
+
Buffer.from([SMP_MASTER_IDENT]),
|
|
71
|
+
this._diversifier,
|
|
72
|
+
this._random
|
|
73
|
+
]));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
onAclStreamLtkNegReply () {
|
|
79
|
+
this.write(Buffer.from([
|
|
80
|
+
SMP_PAIRING_FAILED,
|
|
81
|
+
SMP_UNSPECIFIED
|
|
82
|
+
]));
|
|
83
|
+
|
|
84
|
+
this.emit('fail');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
onAclStreamEnd () {
|
|
88
|
+
this._aclStream.removeListener('data', this.onAclStreamDataBinded);
|
|
89
|
+
this._aclStream.removeListener('encryptChange', this.onAclStreamEncryptChangeBinded);
|
|
90
|
+
this._aclStream.removeListener('ltkNegReply', this.onAclStreamLtkNegReplyBinded);
|
|
91
|
+
this._aclStream.removeListener('end', this.onAclStreamEndBinded);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
handlePairingRequest (data) {
|
|
95
|
+
this._preq = data;
|
|
96
|
+
|
|
97
|
+
this._pres = Buffer.from([
|
|
98
|
+
SMP_PAIRING_RESPONSE,
|
|
99
|
+
0x03, // IO capability: NoInputNoOutput
|
|
100
|
+
0x00, // OOB data: Authentication data not present
|
|
101
|
+
0x01, // Authentication requirement: Bonding - No MITM
|
|
102
|
+
0x10, // Max encryption key size
|
|
103
|
+
0x00, // Initiator key distribution: <none>
|
|
104
|
+
0x01 // Responder key distribution: EncKey
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
this.write(this._pres);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
handlePairingConfirm (data) {
|
|
111
|
+
this._pcnf = data;
|
|
112
|
+
|
|
113
|
+
this._tk = Buffer.from('00000000000000000000000000000000', 'hex');
|
|
114
|
+
this._r = crypto.r();
|
|
115
|
+
|
|
116
|
+
this.write(Buffer.concat([
|
|
117
|
+
Buffer.from([SMP_PAIRING_CONFIRM]),
|
|
118
|
+
crypto.c1(this._tk, this._r, this._pres, this._preq, this._iat, this._ia, this._rat, this._ra)
|
|
119
|
+
]));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
handlePairingRandom (data) {
|
|
123
|
+
const r = data.slice(1);
|
|
124
|
+
|
|
125
|
+
const pcnf = Buffer.concat([
|
|
126
|
+
Buffer.from([SMP_PAIRING_CONFIRM]),
|
|
127
|
+
crypto.c1(this._tk, r, this._pres, this._preq, this._iat, this._ia, this._rat, this._ra)
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
if (this._pcnf.toString('hex') === pcnf.toString('hex')) {
|
|
131
|
+
this._diversifier = Buffer.from('0000', 'hex');
|
|
132
|
+
this._random = Buffer.from('0000000000000000', 'hex');
|
|
133
|
+
this._stk = crypto.s1(this._tk, this._r, r);
|
|
134
|
+
|
|
135
|
+
mgmt.addLongTermKey(this._ia, this._iat, 0, 0, this._diversifier, this._random, this._stk);
|
|
136
|
+
|
|
137
|
+
this.write(Buffer.concat([
|
|
138
|
+
Buffer.from([SMP_PAIRING_RANDOM]),
|
|
139
|
+
this._r
|
|
140
|
+
]));
|
|
141
|
+
} else {
|
|
142
|
+
this.write(Buffer.from([
|
|
143
|
+
SMP_PAIRING_FAILED,
|
|
144
|
+
SMP_PAIRING_CONFIRM
|
|
145
|
+
]));
|
|
146
|
+
|
|
147
|
+
this.emit('fail');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
handlePairingFailed (data) {
|
|
152
|
+
this.emit('fail');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
write (data) {
|
|
156
|
+
this._aclStream.write(SMP_CID, data);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = Smp;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// This file is based on the bluez implementation
|
|
2
|
+
// https://github.com/bluez/bluez/blob/master/tools/bdaddr.c
|
|
3
|
+
|
|
4
|
+
const OGF_VENDOR_CMD = 0x3f;
|
|
5
|
+
|
|
6
|
+
const OCF_ERICSSON_WRITE_BD_ADDR = 0x000d;
|
|
7
|
+
const OCF_TI_WRITE_BD_ADDR = 0x0006;
|
|
8
|
+
const OCF_LINUX_FOUNDATION_WRITE_BD_ADDR = 0x0006;
|
|
9
|
+
const OCF_BCM_WRITE_BD_ADDR = 0x0001;
|
|
10
|
+
const OCF_ZEEVO_WRITE_BD_ADDR = 0x0001;
|
|
11
|
+
const OCF_MRVL_WRITE_BD_ADDR = 0x0022;
|
|
12
|
+
const OCF_ERICSSON_STORE_IN_FLASH = 0x0022;
|
|
13
|
+
const ERICSSON_STORE_IN_FLASH_CP_SIZE = 0xFF;
|
|
14
|
+
|
|
15
|
+
function parseAddress (address) {
|
|
16
|
+
// Parse MAC Address as in 00:00:00:00:00:00 into Buffer (needs to reverse byte order)
|
|
17
|
+
const macAddress = Buffer.from(address.split(':').reverse().join(''), 'hex');
|
|
18
|
+
|
|
19
|
+
if (Buffer.isBuffer(macAddress) && macAddress.byteLength !== 6) {
|
|
20
|
+
throw new Error('Invalid MAC Address. Should be formated as 00:00:00:00:00:00 string.');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return macAddress;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// eslint-disable-next-line camelcase
|
|
27
|
+
function csr_write_bd_addr (address) {
|
|
28
|
+
// Parse MAC Address
|
|
29
|
+
const macAddress = parseAddress(address);
|
|
30
|
+
|
|
31
|
+
if (macAddress === null) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Base command
|
|
36
|
+
const base = Buffer.from([
|
|
37
|
+
0x02, 0x00, 0x0c, 0x00, 0x11, 0x47, 0x03, 0x70,
|
|
38
|
+
0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
|
|
39
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
// Command
|
|
43
|
+
const cmd = Buffer.alloc(3 + base.byteLength);
|
|
44
|
+
|
|
45
|
+
cmd.writeUInt16LE(0x00 | OGF_VENDOR_CMD << 10, 0);
|
|
46
|
+
cmd.writeUInt8(0xC2, 2);
|
|
47
|
+
|
|
48
|
+
base.writeUint8(macAddress.readUInt8(2), 16);
|
|
49
|
+
base.writeUint8(0x00, 17);
|
|
50
|
+
base.writeUint8(macAddress.readUInt8(0), 18);
|
|
51
|
+
base.writeUint8(macAddress.readUInt8(1), 19);
|
|
52
|
+
base.writeUint8(macAddress.readUInt8(3), 20);
|
|
53
|
+
base.writeUint8(0x00, 21);
|
|
54
|
+
base.writeUint8(macAddress.readUInt8(4), 22);
|
|
55
|
+
base.writeUint8(macAddress.readUInt8(5), 23);
|
|
56
|
+
base.copy(cmd, 3);
|
|
57
|
+
|
|
58
|
+
return cmd;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// eslint-disable-next-line camelcase
|
|
62
|
+
function ericsson_store_in_flash (user_id, data) {
|
|
63
|
+
// Check Data
|
|
64
|
+
if (Buffer.isBuffer(data) === false || data.byteLength > OCF_ERICSSON_STORE_IN_FLASH - 2) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Command
|
|
69
|
+
const cmd = Buffer.alloc(3 + ERICSSON_STORE_IN_FLASH_CP_SIZE);
|
|
70
|
+
|
|
71
|
+
cmd.writeUInt16LE(OCF_ERICSSON_STORE_IN_FLASH | OGF_VENDOR_CMD << 10, 0);
|
|
72
|
+
cmd.writeUInt8(ERICSSON_STORE_IN_FLASH_CP_SIZE, 2);
|
|
73
|
+
cmd.writeUInt8(user_id, 3); // user_id
|
|
74
|
+
cmd.writeUInt8(data.byteLength, 4); // flash_length
|
|
75
|
+
data.copy(cmd, 5); // flash_data
|
|
76
|
+
|
|
77
|
+
return cmd;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// eslint-disable-next-line camelcase
|
|
81
|
+
function st_write_bd_addr (address) {
|
|
82
|
+
// Parse MAC Address
|
|
83
|
+
const macAddress = parseAddress(address);
|
|
84
|
+
|
|
85
|
+
if (macAddress === null) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return ericsson_store_in_flash(0xFE, macAddress);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// eslint-disable-next-line camelcase
|
|
93
|
+
function mrvl_write_bd_addr (address) {
|
|
94
|
+
// Parse MAC Address
|
|
95
|
+
const macAddress = parseAddress(address);
|
|
96
|
+
|
|
97
|
+
if (macAddress === null) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Command
|
|
102
|
+
const cmd = Buffer.alloc(11);
|
|
103
|
+
|
|
104
|
+
cmd.writeUInt16LE(OCF_MRVL_WRITE_BD_ADDR | OGF_VENDOR_CMD << 10, 0);
|
|
105
|
+
cmd.writeUInt8(0x08, 2);
|
|
106
|
+
cmd.writeUInt8(0xFE, 3); // parameter_id
|
|
107
|
+
cmd.writeUInt8(0x06, 4); // bdaddr_len
|
|
108
|
+
macAddress.copy(cmd, 5); // bdaddr
|
|
109
|
+
|
|
110
|
+
return cmd;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// eslint-disable-next-line camelcase
|
|
114
|
+
function write_common_bd_addr (OCF_VS_WRITE_BD_ADDR) {
|
|
115
|
+
// Return a function
|
|
116
|
+
return (address) => {
|
|
117
|
+
// Parse MAC Address
|
|
118
|
+
const macAddress = parseAddress(address);
|
|
119
|
+
|
|
120
|
+
if (macAddress === null) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Command
|
|
125
|
+
const cmd = Buffer.alloc(9);
|
|
126
|
+
|
|
127
|
+
cmd.writeUInt16LE(OCF_VS_WRITE_BD_ADDR | OGF_VENDOR_CMD << 10, 0);
|
|
128
|
+
cmd.writeUInt8(0x06, 2);
|
|
129
|
+
macAddress.copy(cmd, 3); // bdaddr
|
|
130
|
+
|
|
131
|
+
return cmd;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const vendors = new Map();
|
|
136
|
+
|
|
137
|
+
vendors.set(0, write_common_bd_addr(OCF_ERICSSON_WRITE_BD_ADDR));
|
|
138
|
+
vendors.set(10, csr_write_bd_addr);
|
|
139
|
+
vendors.set(13, write_common_bd_addr(OCF_TI_WRITE_BD_ADDR));
|
|
140
|
+
vendors.set(15, write_common_bd_addr(OCF_BCM_WRITE_BD_ADDR));
|
|
141
|
+
vendors.set(18, write_common_bd_addr(OCF_ZEEVO_WRITE_BD_ADDR));
|
|
142
|
+
vendors.set(48, st_write_bd_addr);
|
|
143
|
+
vendors.set(57, write_common_bd_addr(OCF_ERICSSON_WRITE_BD_ADDR));
|
|
144
|
+
vendors.set(72, mrvl_write_bd_addr);
|
|
145
|
+
vendors.set(1521, write_common_bd_addr(OCF_LINUX_FOUNDATION_WRITE_BD_ADDR));
|
|
146
|
+
|
|
147
|
+
module.exports = {
|
|
148
|
+
// Vendor Specific Set Address
|
|
149
|
+
setAddressCmd: (manufacturer, address) => {
|
|
150
|
+
const generateCommand = vendors.get(manufacturer);
|
|
151
|
+
if (typeof generateCommand === 'function') {
|
|
152
|
+
return generateCommand(address) || null;
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
'variables': {
|
|
3
|
+
'openssl_fips' : ''
|
|
4
|
+
},
|
|
5
|
+
'targets': [
|
|
6
|
+
{
|
|
7
|
+
'target_name': 'binding',
|
|
8
|
+
'sources': [
|
|
9
|
+
'src/bleno_mac.mm',
|
|
10
|
+
'src/napi_objc.mm',
|
|
11
|
+
'src/ble_peripheral_manager.mm',
|
|
12
|
+
'src/objc_cpp.mm',
|
|
13
|
+
'src/callbacks.mm'
|
|
14
|
+
],
|
|
15
|
+
'include_dirs': [
|
|
16
|
+
"<!(node -p \"require('node-addon-api').include_dir\")",
|
|
17
|
+
"<!@(node -p \"require('napi-thread-safe-callback').include\")"
|
|
18
|
+
],
|
|
19
|
+
'cflags!': [ '-fno-exceptions' ],
|
|
20
|
+
'cflags_cc!': [ '-fno-exceptions' ],
|
|
21
|
+
"defines": ["NAPI_CPP_EXCEPTIONS"],
|
|
22
|
+
'xcode_settings': {
|
|
23
|
+
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
|
|
24
|
+
'MACOSX_DEPLOYMENT_TARGET': '10.13',
|
|
25
|
+
'CLANG_CXX_LIBRARY': 'libc++',
|
|
26
|
+
'OTHER_CFLAGS': [
|
|
27
|
+
'-fobjc-arc',
|
|
28
|
+
'-arch x86_64',
|
|
29
|
+
'-arch arm64'
|
|
30
|
+
],
|
|
31
|
+
'OTHER_LDFLAGS': [
|
|
32
|
+
'-framework CoreBluetooth',
|
|
33
|
+
'-arch x86_64',
|
|
34
|
+
'-arch arm64'
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const events = require('events');
|
|
2
|
+
const util = require('util');
|
|
3
|
+
|
|
4
|
+
const { resolve } = require('path');
|
|
5
|
+
const dir = resolve(__dirname, '..', '..');
|
|
6
|
+
const binding = require('node-gyp-build')(dir);
|
|
7
|
+
|
|
8
|
+
const { BlenoMac } = binding;
|
|
9
|
+
|
|
10
|
+
util.inherits(BlenoMac, events.EventEmitter);
|
|
11
|
+
|
|
12
|
+
module.exports = BlenoMac;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ble_peripheral_manager.h
|
|
3
|
+
// bleno-mac-native
|
|
4
|
+
//
|
|
5
|
+
// Created by Georg Vienna on 28.08.18.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#import <CoreBluetooth/CoreBluetooth.h>
|
|
11
|
+
|
|
12
|
+
#import "callbacks.h"
|
|
13
|
+
|
|
14
|
+
#import <map>
|
|
15
|
+
|
|
16
|
+
@interface BLEPeripheralManager : NSObject {
|
|
17
|
+
@public Emit emit;
|
|
18
|
+
@public std::map<CBUUID *, EmitCharacteristic> emitters;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
- (nonnull instancetype)init NS_DESIGNATED_INITIALIZER;
|
|
22
|
+
|
|
23
|
+
- (void)start;
|
|
24
|
+
- (void)startAdvertising:(NSString * _Nonnull)name serviceUUIDs:(NSArray<CBUUID *> * _Nonnull)serviceUUIDs;
|
|
25
|
+
- (void)startAdvertisingIBeacon:(NSData * _Nullable)data;
|
|
26
|
+
- (void)startAdvertisingWithEIRData:(NSData * _Nullable)data;
|
|
27
|
+
- (void)stopAdvertising;
|
|
28
|
+
- (void)setServices:(NSArray<CBMutableService *> * _Nonnull)services;
|
|
29
|
+
- (void)disconnect;
|
|
30
|
+
- (void)updateRssi;
|
|
31
|
+
|
|
32
|
+
@end
|