@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
package/index.d.ts ADDED
@@ -0,0 +1,153 @@
1
+ // Type definitions for bleno 0.4
2
+ // Project: https://github.com/sandeepmistry/bleno
3
+ // Definitions by: Manuel Francisco Naranjo <naranjo.manuel@gmail.com>
4
+ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
5
+
6
+ /// <reference types="node" />
7
+
8
+ type State = 'poweredOn' | 'poweredOff' | 'unauthorized' | 'unsupported' | 'unknown' | 'resetting';
9
+
10
+ type Property = 'read' | 'write' | 'indicate' | 'notify' | 'writeWithoutResponse';
11
+
12
+ interface CharacteristicOptions {
13
+ uuid: string;
14
+ properties?: ReadonlyArray<Property> | null;
15
+ secure?: ReadonlyArray<Property> | null;
16
+ value?: Buffer | null;
17
+ descriptors?: ReadonlyArray<Descriptor> | null;
18
+ onIndicate?: (() => void) | null;
19
+ onNotify?: (() => void) | null;
20
+ onReadRequest?: ((
21
+ offset: number,
22
+ callback: (result: number, data?: Buffer) => void
23
+ ) => void) | null;
24
+ onSubscribe?: ((maxValueSize: number, updateValueCallback: (data: Buffer) => void) => void) | null;
25
+ onUnsubscribe?: (() => void) | null;
26
+ onWriteRequest?: ((
27
+ data: Buffer,
28
+ offset: number,
29
+ withoutResponse: boolean,
30
+ callback: (result: number) => void
31
+ ) => void) | null;
32
+ }
33
+
34
+ declare class Characteristic {
35
+ uuid: string;
36
+ properties: ReadonlyArray<Property>;
37
+ secure: ReadonlyArray<Property>;
38
+ value: Buffer | null;
39
+ descriptors: ReadonlyArray<Descriptor>;
40
+
41
+ constructor(options: CharacteristicOptions);
42
+
43
+ onIndicate(): void;
44
+
45
+ onNotify(): void;
46
+
47
+ onReadRequest(offset: number, callback: (result: number, data?: Buffer) => void): void;
48
+
49
+ onSubscribe(maxValueSize: number, updateValueCallback: (data: Buffer) => void): void;
50
+
51
+ onUnsubscribe(): void;
52
+
53
+ onWriteRequest(data: Buffer, offset: number, withoutResponse: boolean, callback: (result: number) => void): void;
54
+
55
+ toString(): string;
56
+
57
+ readonly RESULT_ATTR_NOT_LONG: number;
58
+
59
+ readonly RESULT_INVALID_ATTRIBUTE_LENGTH: number;
60
+
61
+ readonly RESULT_INVALID_OFFSET: number;
62
+
63
+ readonly RESULT_SUCCESS: number;
64
+
65
+ readonly RESULT_UNLIKELY_ERROR: number;
66
+
67
+ static readonly RESULT_ATTR_NOT_LONG: number;
68
+
69
+ static readonly RESULT_INVALID_ATTRIBUTE_LENGTH: number;
70
+
71
+ static readonly RESULT_INVALID_OFFSET: number;
72
+
73
+ static readonly RESULT_SUCCESS: number;
74
+
75
+ static readonly RESULT_UNLIKELY_ERROR: number;
76
+ }
77
+
78
+ interface DescriptorOptions {
79
+ uuid: string;
80
+ value?: Buffer | string | null;
81
+ }
82
+
83
+ declare class Descriptor {
84
+ uuid: string;
85
+ value: Buffer;
86
+
87
+ constructor(options: DescriptorOptions);
88
+
89
+ toString(): string;
90
+ }
91
+
92
+ interface PrimaryServiceOptions {
93
+ uuid: string;
94
+ characteristics?: ReadonlyArray<Characteristic> | null;
95
+ }
96
+
97
+ declare class PrimaryService {
98
+ uuid: string;
99
+ characteristics: ReadonlyArray<Characteristic>;
100
+
101
+ constructor(options: PrimaryServiceOptions);
102
+
103
+ toString(): string;
104
+ }
105
+
106
+ interface Bleno extends NodeJS.EventEmitter {
107
+ readonly Characteristic: typeof Characteristic;
108
+ readonly Descriptor: typeof Descriptor;
109
+ readonly PrimaryService: typeof PrimaryService;
110
+
111
+ readonly address: string;
112
+
113
+ readonly mtu: number;
114
+
115
+ readonly platform: string;
116
+
117
+ readonly rssi: number;
118
+
119
+ readonly state: State;
120
+
121
+ disconnect(): void;
122
+
123
+ setAddress(address: string): void;
124
+
125
+ setServices(services: ReadonlyArray<PrimaryService>, callback?: (arg: Error | undefined | null) => void): void;
126
+
127
+ startAdvertising(name: string, serviceUuids?: ReadonlyArray<string>, callback?: (arg: Error | undefined | null) => void): void;
128
+
129
+ startAdvertisingIBeacon(uuid: string, major: number, minor: number, measuredPower: number, callback?: (arg: Error | undefined | null) => void): void;
130
+
131
+ startAdvertisingWithEIRData(advertisementData: Buffer, callback?: (arg: Error | undefined | null) => void): void;
132
+ startAdvertisingWithEIRData(advertisementData: Buffer, scanData: Buffer, callback?: (arg: Error | undefined | null) => void): void;
133
+
134
+ stopAdvertising(callback?: () => void): void;
135
+
136
+ updateRssi(callback?: (err: null, rssi: number) => void): void;
137
+
138
+ on(event: 'stateChange', cb: (state: State) => void): this;
139
+ on(event: 'platform', cb: (platform: NodeJS.Platform) => void): this;
140
+ on(event: 'addressChange', cb: (address: string) => void): this;
141
+ on(event: 'accept', cb: (address: string) => void): this;
142
+ on(event: 'mtuChange', cb: (mtu: number) => void): this;
143
+ on(event: 'disconnect', cb: (clientAddress: string) => void): this;
144
+ on(event: 'advertisingStart', cb: (err?: Error | null) => void): this;
145
+ on(event: 'advertisingStartError', cb: (err: Error) => void): this;
146
+ on(event: 'advertisingStop', cb: () => void): this;
147
+ on(event: 'servicesSet', cb: (err?: Error | null) => void): this;
148
+ on(event: 'servicesSetError', cb: (err: Error) => void): this;
149
+ on(event: 'rssiUpdate', cb: (rssi: number) => void): this;
150
+ }
151
+
152
+ declare const bleno: Bleno;
153
+ export = bleno;
package/index.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./with-custom-binding')();
package/lib/bleno.js ADDED
@@ -0,0 +1,231 @@
1
+ const debug = require('debug')('bleno');
2
+ const UuidUtil = require('./uuid-util');
3
+
4
+ const { EventEmitter } = require('events');
5
+
6
+ const PrimaryService = require('./primary-service');
7
+ const Characteristic = require('./characteristic');
8
+ const Descriptor = require('./descriptor');
9
+
10
+ class Bleno extends EventEmitter {
11
+ constructor (bindings) {
12
+ super();
13
+ this.initialized = false;
14
+ this.platform = 'unknown';
15
+ this.state = 'unknown';
16
+ this.address = 'unknown';
17
+ this.rssi = 0;
18
+ this.mtu = 20;
19
+
20
+ this._bindings = bindings;
21
+
22
+ this._bindings.on('stateChange', this.onStateChange.bind(this));
23
+ this._bindings.on('platform', this.onPlatform.bind(this));
24
+ this._bindings.on('addressChange', this.onAddressChange.bind(this));
25
+ this._bindings.on('advertisingStart', this.onAdvertisingStart.bind(this));
26
+ this._bindings.on('advertisingStop', this.onAdvertisingStop.bind(this));
27
+ this._bindings.on('servicesSet', this.onServicesSet.bind(this));
28
+ this._bindings.on('accept', this.onAccept.bind(this));
29
+ this._bindings.on('mtuChange', this.onMtuChange.bind(this));
30
+ this._bindings.on('disconnect', this.onDisconnect.bind(this));
31
+ this._bindings.on('rssiUpdate', this.onRssiUpdate.bind(this));
32
+
33
+ this.on('newListener', (event) => {
34
+ if (event === 'stateChange' && !this.initialized) {
35
+ this._bindings.init();
36
+
37
+ this.initialized = true;
38
+ }
39
+ });
40
+ }
41
+
42
+ onPlatform (platform) {
43
+ debug('platform ' + platform);
44
+
45
+ this.platform = platform;
46
+ }
47
+
48
+ onStateChange (state) {
49
+ debug('stateChange ' + state);
50
+
51
+ this.state = state;
52
+
53
+ this.emit('stateChange', state);
54
+ }
55
+
56
+ onAddressChange (address) {
57
+ debug('addressChange ' + address);
58
+
59
+ this.address = address;
60
+ }
61
+
62
+ onAccept (clientAddress) {
63
+ debug('accept ' + clientAddress);
64
+ this.emit('accept', clientAddress);
65
+ }
66
+
67
+ onMtuChange (mtu) {
68
+ debug('mtu ' + mtu);
69
+
70
+ this.mtu = mtu;
71
+
72
+ this.emit('mtuChange', mtu);
73
+ }
74
+
75
+ onDisconnect (clientAddress) {
76
+ debug('disconnect ' + clientAddress);
77
+ this.emit('disconnect', clientAddress);
78
+ }
79
+
80
+ setAddress (address) {
81
+ if (this._bindings.setAddress) {
82
+ this._bindings.setAddress(address);
83
+ } else {
84
+ this.emit('warning', 'current binding does not implement setAddress method.');
85
+ }
86
+ }
87
+
88
+ startAdvertising (name, serviceUuids, callback) {
89
+ if (this.state !== 'poweredOn') {
90
+ const error = new Error('Could not start advertising, state is ' + this.state + ' (not poweredOn)');
91
+
92
+ if (typeof callback === 'function') {
93
+ callback(error);
94
+ } else {
95
+ throw error;
96
+ }
97
+ } else {
98
+ if (typeof callback === 'function') {
99
+ this.once('advertisingStart', callback);
100
+ }
101
+
102
+ const undashedServiceUuids = [];
103
+
104
+ if (serviceUuids && serviceUuids.length) {
105
+ for (let i = 0; i < serviceUuids.length; i++) {
106
+ undashedServiceUuids[i] = UuidUtil.removeDashes(serviceUuids[i]);
107
+ }
108
+ }
109
+
110
+ this._bindings.startAdvertising(name, undashedServiceUuids);
111
+ }
112
+ }
113
+
114
+ startAdvertisingIBeacon (uuid, major, minor, measuredPower, callback) {
115
+ if (this.state !== 'poweredOn') {
116
+ const error = new Error('Could not start advertising, state is ' + this.state + ' (not poweredOn)');
117
+
118
+ if (typeof callback === 'function') {
119
+ callback(error);
120
+ } else {
121
+ throw error;
122
+ }
123
+ } else {
124
+ const undashedUuid = UuidUtil.removeDashes(uuid);
125
+ const uuidData = Buffer.from(undashedUuid, 'hex');
126
+ const uuidDataLength = uuidData.length;
127
+ const iBeaconData = Buffer.alloc(uuidData.length + 5);
128
+
129
+ for (let i = 0; i < uuidDataLength; i++) {
130
+ iBeaconData[i] = uuidData[i];
131
+ }
132
+
133
+ iBeaconData.writeUInt16BE(major, uuidDataLength);
134
+ iBeaconData.writeUInt16BE(minor, uuidDataLength + 2);
135
+ iBeaconData.writeInt8(measuredPower, uuidDataLength + 4);
136
+
137
+ if (typeof callback === 'function') {
138
+ this.once('advertisingStart', callback);
139
+ }
140
+
141
+ debug('iBeacon data = ' + iBeaconData.toString('hex'));
142
+
143
+ this._bindings.startAdvertisingIBeacon(iBeaconData);
144
+ }
145
+ }
146
+
147
+ onAdvertisingStart (error) {
148
+ debug('advertisingStart: ' + error);
149
+
150
+ if (error) {
151
+ this.emit('advertisingStartError', error);
152
+ } else {
153
+ this.emit('advertisingStart', error);
154
+ }
155
+ }
156
+
157
+ startAdvertisingWithEIRData (advertisementData, scanData, callback) {
158
+ if (typeof scanData === 'function') {
159
+ callback = scanData;
160
+ scanData = null;
161
+ }
162
+
163
+ if (this.state !== 'poweredOn') {
164
+ const error = new Error('Could not advertising scanning, state is ' + this.state + ' (not poweredOn)');
165
+
166
+ if (typeof callback === 'function') {
167
+ callback(error);
168
+ } else {
169
+ throw error;
170
+ }
171
+ } else {
172
+ if (typeof callback === 'function') {
173
+ this.once('advertisingStart', callback);
174
+ }
175
+
176
+ this._bindings.startAdvertisingWithEIRData(advertisementData, scanData);
177
+ }
178
+ }
179
+
180
+ stopAdvertising (callback) {
181
+ if (typeof callback === 'function') {
182
+ this.once('advertisingStop', callback);
183
+ }
184
+ this._bindings.stopAdvertising();
185
+ }
186
+
187
+ onAdvertisingStop () {
188
+ debug('advertisingStop');
189
+ this.emit('advertisingStop');
190
+ }
191
+
192
+ setServices (services, callback) {
193
+ if (typeof callback === 'function') {
194
+ this.once('servicesSet', callback);
195
+ }
196
+ this._bindings.setServices(services);
197
+ }
198
+
199
+ onServicesSet (error) {
200
+ debug('servicesSet');
201
+
202
+ if (error) {
203
+ this.emit('servicesSetError', error);
204
+ } else {
205
+ this.emit('servicesSet', error);
206
+ }
207
+ }
208
+
209
+ disconnect () {
210
+ debug('disconnect');
211
+ this._bindings.disconnect();
212
+ }
213
+
214
+ updateRssi (callback) {
215
+ if (typeof callback === 'function') {
216
+ this.once('rssiUpdate', (rssi) => callback(null, rssi));
217
+ }
218
+
219
+ this._bindings.updateRssi();
220
+ }
221
+
222
+ onRssiUpdate (rssi) {
223
+ this.emit('rssiUpdate', rssi);
224
+ }
225
+ }
226
+
227
+ Bleno.prototype.PrimaryService = PrimaryService;
228
+ Bleno.prototype.Characteristic = Characteristic;
229
+ Bleno.prototype.Descriptor = Descriptor;
230
+
231
+ module.exports = Bleno;
@@ -0,0 +1,91 @@
1
+ const { EventEmitter } = require('events');
2
+ const UuidUtil = require('./uuid-util');
3
+
4
+ class Characteristic extends EventEmitter {
5
+ constructor (options) {
6
+ super();
7
+
8
+ this.uuid = UuidUtil.removeDashes(options.uuid);
9
+ this.properties = options.properties || [];
10
+ this.secure = options.secure || [];
11
+ this.value = options.value || null;
12
+ this.descriptors = options.descriptors || [];
13
+
14
+ if (this.value && (this.properties.length !== 1 || this.properties[0] !== 'read')) {
15
+ throw new Error('Characteristics with value can be read only!');
16
+ }
17
+
18
+ if (options.onReadRequest) {
19
+ this.onReadRequest = options.onReadRequest;
20
+ }
21
+
22
+ if (options.onWriteRequest) {
23
+ this.onWriteRequest = options.onWriteRequest;
24
+ }
25
+
26
+ if (options.onSubscribe) {
27
+ this.onSubscribe = options.onSubscribe;
28
+ }
29
+
30
+ if (options.onUnsubscribe) {
31
+ this.onUnsubscribe = options.onUnsubscribe;
32
+ }
33
+
34
+ if (options.onNotify) {
35
+ this.onNotify = options.onNotify;
36
+ }
37
+
38
+ if (options.onIndicate) {
39
+ this.onIndicate = options.onIndicate;
40
+ }
41
+
42
+ this.on('readRequest', this.onReadRequest.bind(this));
43
+ this.on('writeRequest', this.onWriteRequest.bind(this));
44
+ this.on('subscribe', this.onSubscribe.bind(this));
45
+ this.on('unsubscribe', this.onUnsubscribe.bind(this));
46
+ this.on('notify', this.onNotify.bind(this));
47
+ this.on('indicate', this.onIndicate.bind(this));
48
+ }
49
+
50
+ toString () {
51
+ return JSON.stringify({
52
+ uuid: this.uuid,
53
+ properties: this.properties,
54
+ secure: this.secure,
55
+ value: this.value,
56
+ descriptors: this.descriptors
57
+ });
58
+ }
59
+
60
+ onReadRequest (offset, callback) {
61
+ callback(this.RESULT_UNLIKELY_ERROR, null);
62
+ }
63
+
64
+ onWriteRequest (data, offset, withoutResponse, callback) {
65
+ callback(this.RESULT_UNLIKELY_ERROR);
66
+ }
67
+
68
+ onSubscribe (maxValueSize, updateValueCallback) {
69
+ this.maxValueSize = maxValueSize;
70
+ this.updateValueCallback = updateValueCallback;
71
+ }
72
+
73
+ onUnsubscribe () {
74
+ this.maxValueSize = null;
75
+ this.updateValueCallback = null;
76
+ }
77
+
78
+ onNotify () {
79
+ }
80
+
81
+ onIndicate () {
82
+ }
83
+ }
84
+
85
+ Characteristic.RESULT_SUCCESS = Characteristic.prototype.RESULT_SUCCESS = 0x00;
86
+ Characteristic.RESULT_INVALID_OFFSET = Characteristic.prototype.RESULT_INVALID_OFFSET = 0x07;
87
+ Characteristic.RESULT_ATTR_NOT_LONG = Characteristic.prototype.RESULT_ATTR_NOT_LONG = 0x0b;
88
+ Characteristic.RESULT_INVALID_ATTRIBUTE_LENGTH = Characteristic.prototype.RESULT_INVALID_ATTRIBUTE_LENGTH = 0x0d;
89
+ Characteristic.RESULT_UNLIKELY_ERROR = Characteristic.prototype.RESULT_UNLIKELY_ERROR = 0x0e;
90
+
91
+ module.exports = Characteristic;
@@ -0,0 +1,17 @@
1
+ const UuidUtil = require('./uuid-util');
2
+
3
+ class Descriptor {
4
+ constructor (options) {
5
+ this.uuid = UuidUtil.removeDashes(options.uuid);
6
+ this.value = options.value || Buffer.alloc(0);
7
+ }
8
+
9
+ toString () {
10
+ return JSON.stringify({
11
+ uuid: this.uuid,
12
+ value: Buffer.isBuffer(this.value) ? this.value.toString('hex') : this.value
13
+ });
14
+ }
15
+ }
16
+
17
+ module.exports = Descriptor;
@@ -0,0 +1,37 @@
1
+ const { EventEmitter } = require('events');
2
+ const Smp = require('./smp');
3
+
4
+ class AclStream extends EventEmitter {
5
+ constructor (hci, handle, localAddressType, localAddress, remoteAddressType, remoteAddress) {
6
+ super();
7
+ this._hci = hci;
8
+ this._handle = handle;
9
+ this.encypted = false;
10
+
11
+ this._smp = new Smp(this, localAddressType, localAddress, remoteAddressType, remoteAddress);
12
+ }
13
+
14
+ write (cid, data) {
15
+ this._hci.queueAclDataPkt(this._handle, cid, data);
16
+ }
17
+
18
+ push (cid, data) {
19
+ if (data) {
20
+ this.emit('data', cid, data);
21
+ } else {
22
+ this.emit('end');
23
+ }
24
+ }
25
+
26
+ pushEncrypt (encrypt) {
27
+ this.encrypted = !!encrypt;
28
+
29
+ this.emit('encryptChange', this.encrypted);
30
+ }
31
+
32
+ pushLtkNegReply () {
33
+ this.emit('ltkNegReply');
34
+ }
35
+ }
36
+
37
+ module.exports = AclStream;