@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.
Files changed (77) hide show
  1. package/.jshintrc +5 -0
  2. package/LICENSE +20 -0
  3. package/README.md +409 -0
  4. package/binding.gyp +17 -0
  5. package/examples/battery-service/README.md +14 -0
  6. package/examples/battery-service/battery-level-characteristic.js +45 -0
  7. package/examples/battery-service/battery-service.js +16 -0
  8. package/examples/battery-service/main.js +28 -0
  9. package/examples/battery-service/package-lock.json +1314 -0
  10. package/examples/battery-service/package.json +20 -0
  11. package/examples/blink1/README.md +44 -0
  12. package/examples/blink1/blink1-fade-rgb-characteristic.js +42 -0
  13. package/examples/blink1/blink1-rgb-characteristic.js +38 -0
  14. package/examples/blink1/blink1-service.js +19 -0
  15. package/examples/blink1/device-information-service.js +19 -0
  16. package/examples/blink1/hardware-revision-characteristic.js +32 -0
  17. package/examples/blink1/main.js +32 -0
  18. package/examples/blink1/serial-number-characteristic.js +21 -0
  19. package/examples/echo/characteristic.js +45 -0
  20. package/examples/echo/main.js +33 -0
  21. package/examples/pizza/README.md +16 -0
  22. package/examples/pizza/peripheral.js +57 -0
  23. package/examples/pizza/pizza-bake-characteristic.js +40 -0
  24. package/examples/pizza/pizza-crust-characteristic.js +52 -0
  25. package/examples/pizza/pizza-service.js +20 -0
  26. package/examples/pizza/pizza-toppings-characteristic.js +41 -0
  27. package/examples/pizza/pizza.js +58 -0
  28. package/examples/uart/main.js +23 -0
  29. package/index.d.ts +153 -0
  30. package/index.js +1 -0
  31. package/lib/bleno.js +231 -0
  32. package/lib/characteristic.js +91 -0
  33. package/lib/descriptor.js +17 -0
  34. package/lib/hci-socket/acl-stream.js +37 -0
  35. package/lib/hci-socket/bindings.js +219 -0
  36. package/lib/hci-socket/crypto.js +74 -0
  37. package/lib/hci-socket/gap.js +212 -0
  38. package/lib/hci-socket/gatt.js +1028 -0
  39. package/lib/hci-socket/hci-status.json +67 -0
  40. package/lib/hci-socket/hci.js +796 -0
  41. package/lib/hci-socket/mgmt.js +89 -0
  42. package/lib/hci-socket/smp.js +160 -0
  43. package/lib/hci-socket/vs.js +156 -0
  44. package/lib/mac/binding.gyp +39 -0
  45. package/lib/mac/bindings.js +12 -0
  46. package/lib/mac/src/ble_peripheral_manager.h +32 -0
  47. package/lib/mac/src/ble_peripheral_manager.mm +241 -0
  48. package/lib/mac/src/bleno_mac.h +26 -0
  49. package/lib/mac/src/bleno_mac.mm +167 -0
  50. package/lib/mac/src/callbacks.h +76 -0
  51. package/lib/mac/src/callbacks.mm +124 -0
  52. package/lib/mac/src/napi_objc.h +30 -0
  53. package/lib/mac/src/napi_objc.mm +286 -0
  54. package/lib/mac/src/noble_mac.h +34 -0
  55. package/lib/mac/src/noble_mac.mm +260 -0
  56. package/lib/mac/src/objc_cpp.h +27 -0
  57. package/lib/mac/src/objc_cpp.mm +144 -0
  58. package/lib/mac/src/peripheral.h +23 -0
  59. package/lib/primary-service.js +19 -0
  60. package/lib/resolve-bindings.js +19 -0
  61. package/lib/uuid-util.js +7 -0
  62. package/package.json +77 -0
  63. package/prebuilds/android-arm/node.napi.armv7.node +0 -0
  64. package/prebuilds/android-arm64/node.napi.armv8.node +0 -0
  65. package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
  66. package/prebuilds/linux-arm/node.napi.armv6.node +0 -0
  67. package/prebuilds/linux-arm/node.napi.armv7.node +0 -0
  68. package/prebuilds/linux-arm64/node.napi.armv8.node +0 -0
  69. package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
  70. package/prebuilds/linux-x64/node.napi.musl.node +0 -0
  71. package/prebuilds/win32-ia32/node.napi.node +0 -0
  72. package/prebuilds/win32-x64/node.napi.node +0 -0
  73. package/test/characteristic.test.js +174 -0
  74. package/test/descriptor.test.js +46 -0
  75. package/test/mocha.setup.js +0 -0
  76. package/with-bindings.js +5 -0
  77. 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