bare-bluetooth
Advanced tools
+8
| const platform = require('#bluetooth') | ||
| exports.Central = platform.Central | ||
| exports.Peripheral = platform.Peripheral | ||
| exports.Server = platform.Server | ||
| exports.L2CAPChannel = platform.L2CAPChannel | ||
| exports.Service = platform.Service | ||
| exports.Characteristic = platform.Characteristic |
+510
| const bluetooth = require('bare-bluetooth-android') | ||
| const EventEmitter = require('bare-events') | ||
| const { Duplex } = require('bare-stream') | ||
| const { DiscoveredPeripheral, ReadRequest, WriteRequest } = require('./common') | ||
| const STATES = { | ||
| off: 'poweredOff', | ||
| turningOn: 'turningOn', | ||
| on: 'poweredOn', | ||
| turningOff: 'turningOff' | ||
| } | ||
| class Characteristic { | ||
| constructor(uuid, opts, handle = null) { | ||
| this._native = handle || new bluetooth.Characteristic(uuid, opts) | ||
| } | ||
| get uuid() { | ||
| return this._native.uuid | ||
| } | ||
| get properties() { | ||
| return this._native.properties | ||
| } | ||
| get permissions() { | ||
| return this._native.permissions | ||
| } | ||
| get value() { | ||
| return this._native.value | ||
| } | ||
| set value(v) { | ||
| this._native.value = v | ||
| } | ||
| static _from(handle) { | ||
| return new Characteristic(null, null, handle) | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { __proto__: { constructor: Characteristic }, uuid: this.uuid } | ||
| } | ||
| } | ||
| Characteristic.PROPERTY_READ = bluetooth.Characteristic.PROPERTY_READ | ||
| Characteristic.PROPERTY_WRITE_WITHOUT_RESPONSE = | ||
| bluetooth.Characteristic.PROPERTY_WRITE_WITHOUT_RESPONSE | ||
| Characteristic.PROPERTY_WRITE = bluetooth.Characteristic.PROPERTY_WRITE | ||
| Characteristic.PROPERTY_NOTIFY = bluetooth.Characteristic.PROPERTY_NOTIFY | ||
| Characteristic.PROPERTY_INDICATE = bluetooth.Characteristic.PROPERTY_INDICATE | ||
| class Service { | ||
| constructor(uuid, characteristics, opts, handle = null) { | ||
| if (handle) { | ||
| this._native = handle | ||
| this._characteristics = [] | ||
| return | ||
| } | ||
| characteristics = characteristics || [] | ||
| const nativeChars = characteristics.map((c) => c._native) | ||
| this._native = new bluetooth.Service(uuid, nativeChars, opts) | ||
| this._characteristics = characteristics | ||
| } | ||
| get uuid() { | ||
| return this._native.uuid | ||
| } | ||
| get characteristics() { | ||
| return this._characteristics | ||
| } | ||
| get primary() { | ||
| return this._native.primary | ||
| } | ||
| static _from(handle) { | ||
| return new Service(null, null, null, handle) | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { __proto__: { constructor: Service }, uuid: this.uuid } | ||
| } | ||
| } | ||
| class L2CAPChannel extends Duplex { | ||
| constructor(nativeChannel) { | ||
| super({ allowHalfOpen: false }) | ||
| this._native = nativeChannel | ||
| } | ||
| get psm() { | ||
| return this._native.psm | ||
| } | ||
| get peer() { | ||
| return this._native.peer | ||
| } | ||
| _open(cb) { | ||
| this._native | ||
| .on('data', this._ondata.bind(this)) | ||
| .on('end', this._onend.bind(this)) | ||
| .on('error', this._onerror.bind(this)) | ||
| .on('open', this._onopen.bind(this)) | ||
| cb(null) | ||
| } | ||
| _ondata(data) { | ||
| this.push(data) | ||
| } | ||
| _onend() { | ||
| this.push(null) | ||
| } | ||
| _onerror(err) { | ||
| this.destroy(err) | ||
| } | ||
| _onopen() { | ||
| this.emit('open') | ||
| } | ||
| _write(chunk, encoding, cb) { | ||
| this._native.write(chunk, encoding, cb) | ||
| } | ||
| _destroy(err, cb) { | ||
| this._native.destroy(err) | ||
| cb(err) | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { __proto__: { constructor: L2CAPChannel }, destroyed: this.destroyed } | ||
| } | ||
| } | ||
| class Peripheral extends EventEmitter { | ||
| constructor(nativePeripheral) { | ||
| super() | ||
| this._native = nativePeripheral | ||
| this._services = new WeakMap() | ||
| this._chars = new WeakMap() | ||
| this._native | ||
| .on('servicesDiscover', this._onservicesdiscover.bind(this)) | ||
| .on('characteristicsDiscover', this._oncharacteristicsdiscover.bind(this)) | ||
| .on('read', this._onread.bind(this)) | ||
| .on('write', this._onwrite.bind(this)) | ||
| .on('notify', this._onnotify.bind(this)) | ||
| .on('notifyState', this._onnotifystate.bind(this)) | ||
| .on('disconnect', this._ondisconnect.bind(this)) | ||
| .on('channelOpen', this._onchannelopen.bind(this)) | ||
| .on('mtuChanged', this._onmtuchanged.bind(this)) | ||
| .on('error', this._onerror.bind(this)) | ||
| } | ||
| get id() { | ||
| return this._native.id | ||
| } | ||
| get name() { | ||
| return this._native.name | ||
| } | ||
| get serviceData() { | ||
| return this._native.serviceData | ||
| } | ||
| discoverServices(serviceUUIDs) { | ||
| this._native.discoverServices(serviceUUIDs) | ||
| } | ||
| discoverCharacteristics(service, characteristicUUIDs) { | ||
| this._native.discoverCharacteristics(service._native, characteristicUUIDs) | ||
| } | ||
| read(characteristic) { | ||
| this._native.read(characteristic._native) | ||
| } | ||
| write(characteristic, data, withResponse) { | ||
| this._native.write(characteristic._native, data, withResponse) | ||
| } | ||
| subscribe(characteristic) { | ||
| this._native.subscribe(characteristic._native) | ||
| } | ||
| unsubscribe(characteristic) { | ||
| this._native.unsubscribe(characteristic._native) | ||
| } | ||
| openL2CAPChannel(psm) { | ||
| this._native.openL2CAPChannel(psm) | ||
| } | ||
| requestMtu(mtu) { | ||
| this._native.requestMtu(mtu) | ||
| } | ||
| destroy() { | ||
| this._native.destroy() | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { | ||
| __proto__: { constructor: Peripheral }, | ||
| id: this.id, | ||
| name: this.name, | ||
| serviceData: this.serviceData | ||
| } | ||
| } | ||
| _onservicesdiscover(services) { | ||
| this.emit( | ||
| 'servicesDiscover', | ||
| services.map((s) => this._fromService(s)) | ||
| ) | ||
| } | ||
| _oncharacteristicsdiscover(service, chars) { | ||
| const wrappedService = service ? this._fromService(service) : null | ||
| const wrappedChars = chars.map((c) => this._fromCharacteristic(c)) | ||
| if (wrappedService) wrappedService._characteristics = wrappedChars | ||
| this.emit('characteristicsDiscover', wrappedService, wrappedChars) | ||
| } | ||
| _onread(char, data) { | ||
| this.emit('read', this._fromCharacteristic(char), data) | ||
| } | ||
| _onwrite(char) { | ||
| this.emit('write', this._fromCharacteristic(char)) | ||
| } | ||
| _onnotify(char, data) { | ||
| this.emit('notify', this._fromCharacteristic(char), data) | ||
| } | ||
| _onnotifystate(char, isNotifying) { | ||
| this.emit('notifyState', this._fromCharacteristic(char), isNotifying) | ||
| } | ||
| _ondisconnect() { | ||
| this.emit('disconnect') | ||
| } | ||
| _onchannelopen(channel) { | ||
| this.emit('channelOpen', new L2CAPChannel(channel)) | ||
| } | ||
| _onmtuchanged(mtu) { | ||
| this.emit('mtuChanged', mtu) | ||
| } | ||
| _onerror(error) { | ||
| this.emit('error', error) | ||
| } | ||
| _fromService(native) { | ||
| let wrapped = this._services.get(native) | ||
| if (!wrapped) { | ||
| wrapped = Service._from(native) | ||
| this._services.set(native, wrapped) | ||
| } | ||
| return wrapped | ||
| } | ||
| _fromCharacteristic(native) { | ||
| let wrapped = this._chars.get(native) | ||
| if (!wrapped) { | ||
| wrapped = Characteristic._from(native) | ||
| this._chars.set(native, wrapped) | ||
| } | ||
| return wrapped | ||
| } | ||
| } | ||
| Peripheral.PROPERTY_READ = bluetooth.Peripheral.PROPERTY_READ | ||
| Peripheral.PROPERTY_WRITE_WITHOUT_RESPONSE = bluetooth.Peripheral.PROPERTY_WRITE_WITHOUT_RESPONSE | ||
| Peripheral.PROPERTY_WRITE = bluetooth.Peripheral.PROPERTY_WRITE | ||
| Peripheral.PROPERTY_NOTIFY = bluetooth.Peripheral.PROPERTY_NOTIFY | ||
| Peripheral.PROPERTY_INDICATE = bluetooth.Peripheral.PROPERTY_INDICATE | ||
| class Central extends EventEmitter { | ||
| constructor() { | ||
| super() | ||
| this._native = new bluetooth.Central() | ||
| this._state = 'unknown' | ||
| this._peripherals = new Map() | ||
| this._native | ||
| .on('stateChange', this._onstatechange.bind(this)) | ||
| .on('discover', this._ondiscover.bind(this)) | ||
| .on('connect', this._onconnect.bind(this)) | ||
| .on('disconnect', this._ondisconnect.bind(this)) | ||
| .on('error', this._onerror.bind(this)) | ||
| } | ||
| get state() { | ||
| return this._state | ||
| } | ||
| startScan(serviceUUIDs, opts) { | ||
| this._native.startScan(serviceUUIDs, opts) | ||
| } | ||
| stopScan() { | ||
| this._native.stopScan() | ||
| } | ||
| connect(discoveredPeripheral) { | ||
| this._native.connect(discoveredPeripheral._native) | ||
| } | ||
| disconnect(peripheral) { | ||
| this._native.disconnect(peripheral._native) | ||
| } | ||
| destroy() { | ||
| this._native.destroy() | ||
| } | ||
| [Symbol.dispose]() { | ||
| this.destroy() | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { __proto__: { constructor: Central }, state: this._state } | ||
| } | ||
| _onstatechange(state) { | ||
| this._state = STATES[state] || state | ||
| this.emit('stateChange', this._state) | ||
| } | ||
| _ondiscover(nativePeripheral) { | ||
| this.emit('discover', new DiscoveredPeripheral(nativePeripheral)) | ||
| } | ||
| _onconnect(nativePeripheral) { | ||
| const peripheral = new Peripheral(nativePeripheral) | ||
| this._peripherals.set(nativePeripheral.id, peripheral) | ||
| this.emit('connect', peripheral) | ||
| } | ||
| _ondisconnect(nativePeripheral) { | ||
| if (!nativePeripheral) { | ||
| this.emit('disconnect', null) | ||
| return | ||
| } | ||
| const peripheral = this._peripherals.get(nativePeripheral.id) || null | ||
| if (peripheral) this._peripherals.delete(nativePeripheral.id) | ||
| this.emit('disconnect', peripheral) | ||
| } | ||
| _onerror(error) { | ||
| this.emit('error', error) | ||
| } | ||
| } | ||
| Central.SCAN_MODE_OPPORTUNISTIC = bluetooth.Central.SCAN_MODE_OPPORTUNISTIC | ||
| Central.SCAN_MODE_LOW_POWER = bluetooth.Central.SCAN_MODE_LOW_POWER | ||
| Central.SCAN_MODE_BALANCED = bluetooth.Central.SCAN_MODE_BALANCED | ||
| Central.SCAN_MODE_LOW_LATENCY = bluetooth.Central.SCAN_MODE_LOW_LATENCY | ||
| class Server extends EventEmitter { | ||
| constructor() { | ||
| super() | ||
| this._native = new bluetooth.Server() | ||
| this._state = 'unknown' | ||
| this._native | ||
| .on('stateChange', this._onstatechange.bind(this)) | ||
| .on('serviceAdd', this._onserviceadd.bind(this)) | ||
| .on('readRequest', this._onreadrequest.bind(this)) | ||
| .on('writeRequest', this._onwriterequest.bind(this)) | ||
| .on('subscribe', this._onsubscribe.bind(this)) | ||
| .on('unsubscribe', this._onunsubscribe.bind(this)) | ||
| .on('error', this._onerror.bind(this)) | ||
| .on('channelPublish', this._onchannelpublish.bind(this)) | ||
| .on('channelOpen', this._onchannelopen.bind(this)) | ||
| .on('notifySent', this._onnotifysent.bind(this)) | ||
| } | ||
| get state() { | ||
| return this._state | ||
| } | ||
| addService(service) { | ||
| this._native.addService(service._native) | ||
| } | ||
| startAdvertising(opts) { | ||
| this._native.startAdvertising(opts) | ||
| } | ||
| stopAdvertising() { | ||
| this._native.stopAdvertising() | ||
| } | ||
| respondToRequest(request, result, data) { | ||
| this._native.respondToRequest(request._native, result, data) | ||
| } | ||
| updateValue(characteristic, data) { | ||
| return this._native.updateValue(characteristic._native, data) | ||
| } | ||
| publishChannel(opts) { | ||
| this._native.publishChannel(opts) | ||
| } | ||
| unpublishChannel(psm) { | ||
| this._native.unpublishChannel(psm) | ||
| } | ||
| destroy() { | ||
| this._native.destroy() | ||
| } | ||
| [Symbol.dispose]() { | ||
| this.destroy() | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { __proto__: { constructor: Server }, state: this._state } | ||
| } | ||
| _onstatechange(state) { | ||
| this._state = STATES[state] || state | ||
| this.emit('stateChange', this._state) | ||
| } | ||
| _onserviceadd(uuid) { | ||
| this.emit('serviceAdd', uuid) | ||
| } | ||
| _onreadrequest(request) { | ||
| this.emit('readRequest', new ReadRequest(request)) | ||
| } | ||
| _onwriterequest(requests) { | ||
| this.emit( | ||
| 'writeRequest', | ||
| requests.map((r) => new WriteRequest(r)) | ||
| ) | ||
| } | ||
| _onsubscribe(peer, characteristicUuid) { | ||
| this.emit('subscribe', peer, characteristicUuid) | ||
| } | ||
| _onunsubscribe(peer, characteristicUuid) { | ||
| this.emit('unsubscribe', peer, characteristicUuid) | ||
| } | ||
| _onerror(error) { | ||
| this.emit('error', error) | ||
| } | ||
| _onchannelpublish(psm) { | ||
| this.emit('channelPublish', psm) | ||
| } | ||
| _onchannelopen(channel) { | ||
| this.emit('channelOpen', new L2CAPChannel(channel)) | ||
| } | ||
| _onnotifysent(deviceAddress, status) { | ||
| this.emit('notifySent', deviceAddress, status) | ||
| } | ||
| } | ||
| Server.STATE_UNKNOWN = 0 | ||
| Server.STATE_RESETTING = 1 | ||
| Server.STATE_UNSUPPORTED = 2 | ||
| Server.STATE_UNAUTHORIZED = 3 | ||
| Server.STATE_POWERED_OFF = 4 | ||
| Server.STATE_POWERED_ON = 5 | ||
| Server.PROPERTY_READ = bluetooth.Server.PROPERTY_READ | ||
| Server.PROPERTY_WRITE_WITHOUT_RESPONSE = bluetooth.Server.PROPERTY_WRITE_WITHOUT_RESPONSE | ||
| Server.PROPERTY_WRITE = bluetooth.Server.PROPERTY_WRITE | ||
| Server.PROPERTY_NOTIFY = bluetooth.Server.PROPERTY_NOTIFY | ||
| Server.PROPERTY_INDICATE = bluetooth.Server.PROPERTY_INDICATE | ||
| Server.PERMISSION_READABLE = bluetooth.Server.PERMISSION_READABLE | ||
| Server.PERMISSION_WRITEABLE = bluetooth.Server.PERMISSION_WRITEABLE | ||
| Server.PERMISSION_READ_ENCRYPTED = bluetooth.Server.PERMISSION_READ_ENCRYPTED | ||
| Server.PERMISSION_WRITE_ENCRYPTED = bluetooth.Server.PERMISSION_WRITE_ENCRYPTED | ||
| Server.ATT_SUCCESS = bluetooth.Server.ATT_SUCCESS | ||
| Server.ATT_INVALID_HANDLE = bluetooth.Server.ATT_INVALID_HANDLE | ||
| Server.ATT_READ_NOT_PERMITTED = bluetooth.Server.ATT_READ_NOT_PERMITTED | ||
| Server.ATT_WRITE_NOT_PERMITTED = bluetooth.Server.ATT_WRITE_NOT_PERMITTED | ||
| Server.ATT_INSUFFICIENT_RESOURCES = bluetooth.Server.ATT_INSUFFICIENT_RESOURCES | ||
| Server.ATT_UNLIKELY_ERROR = bluetooth.Server.ATT_UNLIKELY_ERROR | ||
| exports.Central = Central | ||
| exports.Server = Server | ||
| exports.Peripheral = Peripheral | ||
| exports.L2CAPChannel = L2CAPChannel | ||
| exports.Service = Service | ||
| exports.Characteristic = Characteristic |
+491
| const bluetooth = require('bare-bluetooth-apple') | ||
| const EventEmitter = require('bare-events') | ||
| const { Duplex } = require('bare-stream') | ||
| const { DiscoveredPeripheral, ReadRequest, WriteRequest } = require('./common') | ||
| class Characteristic { | ||
| constructor(uuid, opts, handle = null) { | ||
| this._native = handle || new bluetooth.Characteristic(uuid, opts) | ||
| } | ||
| get uuid() { | ||
| return this._native.uuid | ||
| } | ||
| get properties() { | ||
| return this._native.properties | ||
| } | ||
| get permissions() { | ||
| return this._native.permissions | ||
| } | ||
| get value() { | ||
| return this._native.value | ||
| } | ||
| set value(v) { | ||
| this._native.value = v | ||
| } | ||
| static _from(handle) { | ||
| return new Characteristic(null, null, handle) | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { __proto__: { constructor: Characteristic }, uuid: this.uuid } | ||
| } | ||
| } | ||
| Characteristic.PROPERTY_READ = bluetooth.Characteristic.PROPERTY_READ | ||
| Characteristic.PROPERTY_WRITE_WITHOUT_RESPONSE = | ||
| bluetooth.Characteristic.PROPERTY_WRITE_WITHOUT_RESPONSE | ||
| Characteristic.PROPERTY_WRITE = bluetooth.Characteristic.PROPERTY_WRITE | ||
| Characteristic.PROPERTY_NOTIFY = bluetooth.Characteristic.PROPERTY_NOTIFY | ||
| Characteristic.PROPERTY_INDICATE = bluetooth.Characteristic.PROPERTY_INDICATE | ||
| class Service { | ||
| constructor(uuid, characteristics, opts, handle = null) { | ||
| if (handle) { | ||
| this._native = handle | ||
| this._characteristics = [] | ||
| return | ||
| } | ||
| characteristics = characteristics || [] | ||
| const nativeChars = characteristics.map((c) => c._native) | ||
| this._native = new bluetooth.Service(uuid, nativeChars, opts) | ||
| this._characteristics = characteristics | ||
| } | ||
| get uuid() { | ||
| return this._native.uuid | ||
| } | ||
| get characteristics() { | ||
| return this._characteristics | ||
| } | ||
| get primary() { | ||
| return this._native.primary | ||
| } | ||
| static _from(handle) { | ||
| return new Service(null, null, null, handle) | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { __proto__: { constructor: Service }, uuid: this.uuid } | ||
| } | ||
| } | ||
| class L2CAPChannel extends Duplex { | ||
| constructor(nativeChannel) { | ||
| super({ allowHalfOpen: false }) | ||
| this._native = nativeChannel | ||
| } | ||
| get psm() { | ||
| return this._native.psm | ||
| } | ||
| get peer() { | ||
| return this._native.peer | ||
| } | ||
| _open(cb) { | ||
| this._native | ||
| .on('data', this._ondata.bind(this)) | ||
| .on('end', this._onend.bind(this)) | ||
| .on('error', this._onerror.bind(this)) | ||
| .on('open', this._onopen.bind(this)) | ||
| cb(null) | ||
| } | ||
| _ondata(data) { | ||
| this.push(data) | ||
| } | ||
| _onend() { | ||
| this.push(null) | ||
| } | ||
| _onerror(err) { | ||
| this.destroy(err) | ||
| } | ||
| _onopen() { | ||
| this.emit('open') | ||
| } | ||
| _write(chunk, encoding, cb) { | ||
| this._native.write(chunk, encoding, cb) | ||
| } | ||
| _destroy(err, cb) { | ||
| this._native.destroy(err) | ||
| cb(err) | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { __proto__: { constructor: L2CAPChannel }, destroyed: this.destroyed } | ||
| } | ||
| } | ||
| class Peripheral extends EventEmitter { | ||
| constructor(nativePeripheral) { | ||
| super() | ||
| this._native = nativePeripheral | ||
| this._services = new WeakMap() | ||
| this._chars = new WeakMap() | ||
| this._native | ||
| .on('servicesDiscover', this._onservicesdiscover.bind(this)) | ||
| .on('characteristicsDiscover', this._oncharacteristicsdiscover.bind(this)) | ||
| .on('read', this._onread.bind(this)) | ||
| .on('write', this._onwrite.bind(this)) | ||
| .on('notify', this._onnotify.bind(this)) | ||
| .on('notifyState', this._onnotifystate.bind(this)) | ||
| .on('channelOpen', this._onchannelopen.bind(this)) | ||
| .on('error', this._onerror.bind(this)) | ||
| } | ||
| get id() { | ||
| return this._native.id | ||
| } | ||
| get name() { | ||
| return this._native.name | ||
| } | ||
| get serviceData() { | ||
| return this._native.serviceData | ||
| } | ||
| discoverServices(serviceUUIDs) { | ||
| this._native.discoverServices(serviceUUIDs) | ||
| } | ||
| discoverCharacteristics(service, characteristicUUIDs) { | ||
| this._native.discoverCharacteristics(service._native, characteristicUUIDs) | ||
| } | ||
| read(characteristic) { | ||
| this._native.read(characteristic._native) | ||
| } | ||
| write(characteristic, data, withResponse) { | ||
| this._native.write(characteristic._native, data, withResponse) | ||
| } | ||
| subscribe(characteristic) { | ||
| this._native.subscribe(characteristic._native) | ||
| } | ||
| unsubscribe(characteristic) { | ||
| this._native.unsubscribe(characteristic._native) | ||
| } | ||
| openL2CAPChannel(psm) { | ||
| this._native.openL2CAPChannel(psm) | ||
| } | ||
| requestMtu() {} | ||
| destroy() { | ||
| this._native.destroy() | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { | ||
| __proto__: { constructor: Peripheral }, | ||
| id: this.id, | ||
| name: this.name, | ||
| serviceData: this.serviceData | ||
| } | ||
| } | ||
| _onservicesdiscover(services) { | ||
| this.emit( | ||
| 'servicesDiscover', | ||
| services.map((s) => this._fromService(s)) | ||
| ) | ||
| } | ||
| _oncharacteristicsdiscover(service, chars) { | ||
| const wrappedService = service ? this._fromService(service) : null | ||
| const wrappedChars = chars.map((c) => this._fromCharacteristic(c)) | ||
| if (wrappedService) wrappedService._characteristics = wrappedChars | ||
| this.emit('characteristicsDiscover', wrappedService, wrappedChars) | ||
| } | ||
| _onread(char, data) { | ||
| this.emit('read', this._fromCharacteristic(char), data) | ||
| } | ||
| _onwrite(char) { | ||
| this.emit('write', this._fromCharacteristic(char)) | ||
| } | ||
| _onnotify(char, data) { | ||
| this.emit('notify', this._fromCharacteristic(char), data) | ||
| } | ||
| _onnotifystate(char, isNotifying) { | ||
| this.emit('notifyState', this._fromCharacteristic(char), isNotifying) | ||
| } | ||
| _onchannelopen(channel) { | ||
| this.emit('channelOpen', new L2CAPChannel(channel)) | ||
| } | ||
| _onerror(error) { | ||
| this.emit('error', error) | ||
| } | ||
| _fromService(native) { | ||
| let wrapped = this._services.get(native) | ||
| if (!wrapped) { | ||
| wrapped = Service._from(native) | ||
| this._services.set(native, wrapped) | ||
| } | ||
| return wrapped | ||
| } | ||
| _fromCharacteristic(native) { | ||
| let wrapped = this._chars.get(native) | ||
| if (!wrapped) { | ||
| wrapped = Characteristic._from(native) | ||
| this._chars.set(native, wrapped) | ||
| } | ||
| return wrapped | ||
| } | ||
| } | ||
| Peripheral.PROPERTY_READ = bluetooth.Peripheral.PROPERTY_READ | ||
| Peripheral.PROPERTY_WRITE_WITHOUT_RESPONSE = bluetooth.Peripheral.PROPERTY_WRITE_WITHOUT_RESPONSE | ||
| Peripheral.PROPERTY_WRITE = bluetooth.Peripheral.PROPERTY_WRITE | ||
| Peripheral.PROPERTY_NOTIFY = bluetooth.Peripheral.PROPERTY_NOTIFY | ||
| Peripheral.PROPERTY_INDICATE = bluetooth.Peripheral.PROPERTY_INDICATE | ||
| class Central extends EventEmitter { | ||
| constructor() { | ||
| super() | ||
| this._native = new bluetooth.Central() | ||
| this._state = 'unknown' | ||
| this._peripherals = new Map() | ||
| this._native | ||
| .on('stateChange', this._onstatechange.bind(this)) | ||
| .on('discover', this._ondiscover.bind(this)) | ||
| .on('connect', this._onconnect.bind(this)) | ||
| .on('disconnect', this._ondisconnect.bind(this)) | ||
| .on('error', this._onerror.bind(this)) | ||
| } | ||
| get state() { | ||
| return this._state | ||
| } | ||
| startScan(serviceUUIDs) { | ||
| this._native.startScan(serviceUUIDs) | ||
| } | ||
| stopScan() { | ||
| this._native.stopScan() | ||
| } | ||
| connect(discoveredPeripheral) { | ||
| this._native.connect(discoveredPeripheral._native) | ||
| } | ||
| disconnect(peripheral) { | ||
| this._native.disconnect(peripheral._native) | ||
| } | ||
| destroy() { | ||
| this._native.destroy() | ||
| } | ||
| [Symbol.dispose]() { | ||
| this.destroy() | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { __proto__: { constructor: Central }, state: this._state } | ||
| } | ||
| _onstatechange(state) { | ||
| this._state = state | ||
| this.emit('stateChange', this._state) | ||
| } | ||
| _ondiscover(discovered) { | ||
| this.emit('discover', new DiscoveredPeripheral(discovered)) | ||
| } | ||
| _onconnect(nativePeripheral) { | ||
| const peripheral = new Peripheral(nativePeripheral) | ||
| this._peripherals.set(nativePeripheral.id, peripheral) | ||
| this.emit('connect', peripheral) | ||
| } | ||
| _ondisconnect(nativePeripheral) { | ||
| if (!nativePeripheral) { | ||
| this.emit('disconnect', null) | ||
| return | ||
| } | ||
| const peripheral = this._peripherals.get(nativePeripheral.id) || null | ||
| if (peripheral) this._peripherals.delete(nativePeripheral.id) | ||
| this.emit('disconnect', peripheral) | ||
| } | ||
| _onerror(error) { | ||
| this.emit('error', error) | ||
| } | ||
| } | ||
| Central.SCAN_MODE_OPPORTUNISTIC = -1 | ||
| Central.SCAN_MODE_LOW_POWER = 0 | ||
| Central.SCAN_MODE_BALANCED = 1 | ||
| Central.SCAN_MODE_LOW_LATENCY = 2 | ||
| class Server extends EventEmitter { | ||
| constructor() { | ||
| super() | ||
| this._native = new bluetooth.PeripheralManager() | ||
| this._state = 'unknown' | ||
| this._native | ||
| .on('stateChange', this._onstatechange.bind(this)) | ||
| .on('serviceAdd', this._onserviceadd.bind(this)) | ||
| .on('readRequest', this._onreadrequest.bind(this)) | ||
| .on('writeRequest', this._onwriterequest.bind(this)) | ||
| .on('subscribe', this._onsubscribe.bind(this)) | ||
| .on('unsubscribe', this._onunsubscribe.bind(this)) | ||
| .on('error', this._onerror.bind(this)) | ||
| .on('readyToUpdate', this._onreadytoupdate.bind(this)) | ||
| .on('channelPublish', this._onchannelpublish.bind(this)) | ||
| .on('channelOpen', this._onchannelopen.bind(this)) | ||
| } | ||
| get state() { | ||
| return this._state | ||
| } | ||
| addService(service) { | ||
| this._native.addService(service._native) | ||
| } | ||
| startAdvertising(opts) { | ||
| this._native.startAdvertising(opts) | ||
| } | ||
| stopAdvertising() { | ||
| this._native.stopAdvertising() | ||
| } | ||
| respondToRequest(request, result, data) { | ||
| this._native.respondToRequest(request._native, result, data) | ||
| } | ||
| updateValue(characteristic, data) { | ||
| return this._native.updateValue(characteristic._native, data) | ||
| } | ||
| publishChannel(opts) { | ||
| this._native.publishChannel(opts) | ||
| } | ||
| unpublishChannel(psm) { | ||
| this._native.unpublishChannel(psm) | ||
| } | ||
| destroy() { | ||
| this._native.destroy() | ||
| } | ||
| [Symbol.dispose]() { | ||
| this.destroy() | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { __proto__: { constructor: Server }, state: this._state } | ||
| } | ||
| _onstatechange(state) { | ||
| this._state = state | ||
| this.emit('stateChange', this._state) | ||
| } | ||
| _onserviceadd(uuid) { | ||
| this.emit('serviceAdd', uuid) | ||
| } | ||
| _onreadrequest(request) { | ||
| this.emit('readRequest', new ReadRequest(request)) | ||
| } | ||
| _onwriterequest(requests) { | ||
| this.emit( | ||
| 'writeRequest', | ||
| requests.map((r) => new WriteRequest(r)) | ||
| ) | ||
| } | ||
| _onsubscribe(peer, characteristicUuid) { | ||
| this.emit('subscribe', peer, characteristicUuid) | ||
| } | ||
| _onunsubscribe(peer, characteristicUuid) { | ||
| this.emit('unsubscribe', peer, characteristicUuid) | ||
| } | ||
| _onerror(error) { | ||
| this.emit('error', error) | ||
| } | ||
| _onreadytoupdate() { | ||
| this.emit('readyToUpdate') | ||
| } | ||
| _onchannelpublish(psm) { | ||
| this.emit('channelPublish', psm) | ||
| } | ||
| _onchannelopen(channel) { | ||
| this.emit('channelOpen', new L2CAPChannel(channel)) | ||
| } | ||
| } | ||
| Server.STATE_UNKNOWN = bluetooth.PeripheralManager.STATE_UNKNOWN | ||
| Server.STATE_POWERED_ON = bluetooth.PeripheralManager.STATE_POWERED_ON | ||
| Server.STATE_POWERED_OFF = bluetooth.PeripheralManager.STATE_POWERED_OFF | ||
| Server.STATE_RESETTING = bluetooth.PeripheralManager.STATE_RESETTING | ||
| Server.STATE_UNAUTHORIZED = bluetooth.PeripheralManager.STATE_UNAUTHORIZED | ||
| Server.STATE_UNSUPPORTED = bluetooth.PeripheralManager.STATE_UNSUPPORTED | ||
| Server.PROPERTY_READ = bluetooth.PeripheralManager.PROPERTY_READ | ||
| Server.PROPERTY_WRITE_WITHOUT_RESPONSE = bluetooth.PeripheralManager.PROPERTY_WRITE_WITHOUT_RESPONSE | ||
| Server.PROPERTY_WRITE = bluetooth.PeripheralManager.PROPERTY_WRITE | ||
| Server.PROPERTY_NOTIFY = bluetooth.PeripheralManager.PROPERTY_NOTIFY | ||
| Server.PROPERTY_INDICATE = bluetooth.PeripheralManager.PROPERTY_INDICATE | ||
| Server.PERMISSION_READABLE = bluetooth.PeripheralManager.PERMISSION_READABLE | ||
| Server.PERMISSION_WRITEABLE = bluetooth.PeripheralManager.PERMISSION_WRITEABLE | ||
| Server.PERMISSION_READ_ENCRYPTED = bluetooth.PeripheralManager.PERMISSION_READ_ENCRYPTED | ||
| Server.PERMISSION_WRITE_ENCRYPTED = bluetooth.PeripheralManager.PERMISSION_WRITE_ENCRYPTED | ||
| Server.ATT_SUCCESS = bluetooth.PeripheralManager.ATT_SUCCESS | ||
| Server.ATT_INVALID_HANDLE = bluetooth.PeripheralManager.ATT_INVALID_HANDLE | ||
| Server.ATT_READ_NOT_PERMITTED = bluetooth.PeripheralManager.ATT_READ_NOT_PERMITTED | ||
| Server.ATT_WRITE_NOT_PERMITTED = bluetooth.PeripheralManager.ATT_WRITE_NOT_PERMITTED | ||
| Server.ATT_INSUFFICIENT_RESOURCES = bluetooth.PeripheralManager.ATT_INSUFFICIENT_RESOURCES | ||
| Server.ATT_UNLIKELY_ERROR = bluetooth.PeripheralManager.ATT_UNLIKELY_ERROR | ||
| exports.Central = Central | ||
| exports.Server = Server | ||
| exports.Peripheral = Peripheral | ||
| exports.L2CAPChannel = L2CAPChannel | ||
| exports.Service = Service | ||
| exports.Characteristic = Characteristic |
| class DiscoveredPeripheral { | ||
| constructor(native) { | ||
| this._native = native | ||
| } | ||
| get id() { | ||
| return this._native.id | ||
| } | ||
| get name() { | ||
| return this._native.name | ||
| } | ||
| get rssi() { | ||
| return this._native.rssi | ||
| } | ||
| get serviceData() { | ||
| return this._native.serviceData | ||
| } | ||
| [Symbol.for('bare.inspect')]() { | ||
| return { | ||
| __proto__: { constructor: DiscoveredPeripheral }, | ||
| id: this.id, | ||
| name: this.name, | ||
| rssi: this.rssi | ||
| } | ||
| } | ||
| } | ||
| class ReadRequest { | ||
| constructor(native) { | ||
| this._native = native | ||
| } | ||
| get characteristicUuid() { | ||
| return this._native.characteristicUuid | ||
| } | ||
| get offset() { | ||
| return this._native.offset | ||
| } | ||
| } | ||
| class WriteRequest { | ||
| constructor(native) { | ||
| this._native = native | ||
| } | ||
| get characteristicUuid() { | ||
| return this._native.characteristicUuid | ||
| } | ||
| get offset() { | ||
| return this._native.offset | ||
| } | ||
| get data() { | ||
| return this._native.data | ||
| } | ||
| get responseNeeded() { | ||
| return this._native.responseNeeded ?? true | ||
| } | ||
| } | ||
| exports.DiscoveredPeripheral = DiscoveredPeripheral | ||
| exports.ReadRequest = ReadRequest | ||
| exports.WriteRequest = WriteRequest |
+505
| > [!IMPORTANT] | ||
| > This module is experimental. The API is subject to change and may break at any time. | ||
| # bare-bluetooth | ||
| Bluetooth bindings for Bare. Provides BLE central and peripheral roles, GATT services and characteristics, and L2CAP channels across Apple and Android platforms. | ||
| The module normalizes API differences between platforms so consumer code does not need platform conditionals. State strings, class names, and constants are unified. | ||
| ``` | ||
| npm i bare-bluetooth | ||
| ``` | ||
| ## Usage | ||
| The example below shows a peripheral advertising a single writable, notifying characteristic and a central that scans, connects, subscribes, and exchanges data with it. | ||
| Peripheral: | ||
| ```js | ||
| const { TextEncoder, TextDecoder } = require('bare-encoding') | ||
| const { Server, Service, Characteristic } = require('bare-bluetooth') | ||
| const SERVICE_UUID = '01230000-0000-1000-8000-00805F9B34FB' | ||
| const CHAR_UUID = '01230001-0000-1000-8000-00805F9B34FB' | ||
| const server = new Server() | ||
| let pingChar = null | ||
| server.on('stateChange', (state) => { | ||
| if (state !== 'poweredOn') return | ||
| pingChar = new Characteristic(CHAR_UUID, { | ||
| write: true, | ||
| notify: true | ||
| }) | ||
| server.addService(new Service(SERVICE_UUID, [pingChar])) | ||
| }) | ||
| server.on('serviceAdd', (uuid) => { | ||
| server.startAdvertising({ | ||
| name: 'MyDevice', | ||
| serviceUUIDs: [SERVICE_UUID] | ||
| }) | ||
| }) | ||
| server.on('writeRequest', (requests) => { | ||
| const request = requests[0] | ||
| const message = new TextDecoder().decode(request.data) | ||
| server.respondToRequest(request, Server.ATT_SUCCESS, null) | ||
| server.updateValue(pingChar, new TextEncoder().encode('pong: ' + message)) | ||
| }) | ||
| ``` | ||
| Central: | ||
| ```js | ||
| const { TextEncoder, TextDecoder } = require('bare-encoding') | ||
| const { Central } = require('bare-bluetooth') | ||
| const SERVICE_UUID = '01230000-0000-1000-8000-00805F9B34FB' | ||
| const CHAR_UUID = '01230001-0000-1000-8000-00805F9B34FB' | ||
| const central = new Central() | ||
| central.on('stateChange', (state) => { | ||
| if (state !== 'poweredOn') return | ||
| central.startScan([SERVICE_UUID]) | ||
| }) | ||
| central.on('discover', (peripheral) => { | ||
| central.stopScan() | ||
| central.connect(peripheral) | ||
| }) | ||
| central.on('connect', (peripheral) => { | ||
| peripheral.on('servicesDiscover', (services) => { | ||
| for (const service of services) { | ||
| if (service.uuid === SERVICE_UUID) { | ||
| peripheral.discoverCharacteristics(service) | ||
| } | ||
| } | ||
| }) | ||
| peripheral.on('characteristicsDiscover', (service, characteristics) => { | ||
| for (const characteristic of characteristics) { | ||
| if (characteristic.uuid === CHAR_UUID) { | ||
| peripheral.subscribe(characteristic) | ||
| } | ||
| } | ||
| }) | ||
| peripheral.on('notifyState', (characteristic, isNotifying) => { | ||
| if (isNotifying) { | ||
| peripheral.write(characteristic, new TextEncoder().encode('ping')) | ||
| } | ||
| }) | ||
| peripheral.on('notify', (characteristic, data) => { | ||
| console.log('received:', new TextDecoder().decode(data)) | ||
| }) | ||
| peripheral.discoverServices([SERVICE_UUID]) | ||
| }) | ||
| ``` | ||
| The Apple and Android repositories include runnable variants of this flow under `examples/ping-pong/`. | ||
| ## Platforms | ||
| The package resolves to a platform-specific implementation: | ||
| - `android` resolves to [`bare-bluetooth-android`](https://github.com/holepunchto/bare-bluetooth-android) | ||
| - `darwin` and `ios` resolve to [`bare-bluetooth-apple`](https://github.com/holepunchto/bare-bluetooth-apple) | ||
| ## Types | ||
| ### `BluetoothState` | ||
| A string describing the current Bluetooth adapter state. | ||
| | Value | Platforms | | ||
| | ---------------- | -------------- | | ||
| | `'unknown'` | Apple | | ||
| | `'resetting'` | Apple | | ||
| | `'unsupported'` | Apple | | ||
| | `'unauthorized'` | Apple | | ||
| | `'poweredOff'` | Android, Apple | | ||
| | `'poweredOn'` | Android, Apple | | ||
| | `'turningOn'` | Android | | ||
| | `'turningOff'` | Android | | ||
| ## API | ||
| ## `Central` | ||
| ### `const central = new Central()` | ||
| Create a new BLE central manager. The central scans for and connects to peripherals. | ||
| ### Properties | ||
| | Property | Type | Description | | ||
| | -------- | ---------------- | ------------------------------- | | ||
| | `state` | `BluetoothState` | Current Bluetooth adapter state | | ||
| ### Methods | ||
| #### `central.startScan([serviceUUIDs[, options]])` | ||
| Start scanning for peripherals. If `serviceUUIDs` is provided, only peripherals advertising those services will be discovered. | ||
| ```js | ||
| options = { | ||
| scanMode: null // Android only | ||
| } | ||
| ``` | ||
| Set `scanMode` to one of `Central.SCAN_MODE_OPPORTUNISTIC`, `Central.SCAN_MODE_LOW_POWER`, `Central.SCAN_MODE_BALANCED`, or `Central.SCAN_MODE_LOW_LATENCY`. | ||
| #### `central.stopScan()` | ||
| Stop scanning for peripherals. | ||
| #### `central.connect(peripheral)` | ||
| Connect to a `DiscoveredPeripheral`. | ||
| #### `central.disconnect(peripheral)` | ||
| Disconnect from a connected `Peripheral`. | ||
| #### `central.destroy()` | ||
| Destroy the central manager and release all resources. | ||
| ### Events | ||
| | Event | Arguments | Description | | ||
| | ------------- | ---------------------------------- | -------------------------------------- | | ||
| | `stateChange` | `state: BluetoothState` | Bluetooth adapter state changed | | ||
| | `discover` | `peripheral: DiscoveredPeripheral` | A peripheral was found during scanning | | ||
| | `connect` | `peripheral: Peripheral` | Connection to a peripheral established | | ||
| | `disconnect` | `peripheral: Peripheral \| null` | A peripheral disconnected cleanly | | ||
| | `error` | `error: Error` | An error occurred | | ||
| ### Constants | ||
| | Constant | Description | | ||
| | --------------------------------- | -------------------------------------------------------------------- | | ||
| | `Central.SCAN_MODE_OPPORTUNISTIC` | Scan using highest duty cycle when other apps are scanning (Android) | | ||
| | `Central.SCAN_MODE_LOW_POWER` | Low power scan mode | | ||
| | `Central.SCAN_MODE_BALANCED` | Balanced scan mode | | ||
| | `Central.SCAN_MODE_LOW_LATENCY` | Low latency scan mode | | ||
| ## `DiscoveredPeripheral` | ||
| Represents a peripheral found during scanning. Emitted by the `discover` event on `Central`. Pass it directly to `central.connect()`. | ||
| ### Properties | ||
| | Property | Type | Description | | ||
| | ------------- | ---------------------------------------- | ------------------------------------------ | | ||
| | `id` | `string` | Unique identifier of the peripheral | | ||
| | `name` | `string \| null` | Advertised name, or `null` | | ||
| | `rssi` | `number` | Signal strength in dBm | | ||
| | `serviceData` | `{ [uuid: string]: Uint8Array } \| null` | Service data from advertisement, or `null` | | ||
| ## `Peripheral` | ||
| Represents a connected peripheral. Obtained from the `connect` event on `Central`. | ||
| ### Properties | ||
| | Property | Type | Description | | ||
| | ------------- | ---------------------------------------- | ----------------------------------- | | ||
| | `id` | `string` | Unique identifier of the peripheral | | ||
| | `name` | `string \| null` | Advertised name, or `null` | | ||
| | `serviceData` | `{ [uuid: string]: Uint8Array } \| null` | Service data, or `null` | | ||
| ### Methods | ||
| #### `peripheral.discoverServices([serviceUUIDs])` | ||
| Discover services on the peripheral. On Apple, an optional `serviceUUIDs` array restricts discovery to those services. Android always discovers all services. Results are emitted via `servicesDiscover`. | ||
| #### `peripheral.discoverCharacteristics(service[, characteristicUUIDs])` | ||
| Discover characteristics for a `Service`. On Apple, an optional `characteristicUUIDs` array restricts discovery. Android always discovers all characteristics. Results are emitted via `characteristicsDiscover`. | ||
| #### `peripheral.read(characteristic)` | ||
| Read the value of a `Characteristic`. The result is emitted via `read`. | ||
| #### `peripheral.write(characteristic, data[, withResponse])` | ||
| Write `data: Uint8Array` to a `Characteristic`. If `withResponse` is `true` (the default), the write is confirmed by the peripheral. | ||
| #### `peripheral.subscribe(characteristic)` | ||
| Subscribe to notifications for a `Characteristic`. | ||
| #### `peripheral.unsubscribe(characteristic)` | ||
| Unsubscribe from notifications for a `Characteristic`. | ||
| #### `peripheral.openL2CAPChannel(psm)` | ||
| Open an L2CAP channel to the peripheral using the given `psm: number`. The result is emitted via `channelOpen`. | ||
| #### `peripheral.requestMtu(mtu)` | ||
| Request a new MTU size (`mtu: number`). The result is emitted via `mtuChanged`. No-op on Apple. | ||
| #### `peripheral.destroy()` | ||
| Destroy the peripheral instance and release resources. | ||
| ### Events | ||
| | Event | Arguments | Description | | ||
| | ------------------------- | --------------------------------------------------------------- | ---------------------------------------- | | ||
| | `servicesDiscover` | `services: Service[]` | Services discovered | | ||
| | `characteristicsDiscover` | `service: Service \| null`, `characteristics: Characteristic[]` | Characteristics discovered for a service | | ||
| | `read` | `characteristic: Characteristic`, `data: Uint8Array` | Characteristic value read | | ||
| | `write` | `characteristic: Characteristic` | Characteristic write completed | | ||
| | `notify` | `characteristic: Characteristic`, `data: Uint8Array` | Notification received | | ||
| | `notifyState` | `characteristic: Characteristic`, `isNotifying: boolean` | Notification state changed | | ||
| | `channelOpen` | `channel: L2CAPChannel` | L2CAP channel opened | | ||
| | `error` | `error: Error` | An error occurred | | ||
| | Event | Arguments | Platform | | ||
| | ------------ | ------------- | -------- | | ||
| | `disconnect` | _(none)_ | Android | | ||
| | `mtuChanged` | `mtu: number` | Android | | ||
| ### Constants | ||
| | Constant | Value | | ||
| | -------------------------------------------- | ------ | | ||
| | `Peripheral.PROPERTY_READ` | `0x02` | | ||
| | `Peripheral.PROPERTY_WRITE_WITHOUT_RESPONSE` | `0x04` | | ||
| | `Peripheral.PROPERTY_WRITE` | `0x08` | | ||
| | `Peripheral.PROPERTY_NOTIFY` | `0x10` | | ||
| | `Peripheral.PROPERTY_INDICATE` | `0x20` | | ||
| ## `Server` | ||
| ### `const server = new Server()` | ||
| Create a new BLE peripheral manager (server). The server advertises services and handles read/write requests from centrals. | ||
| ### Properties | ||
| | Property | Type | Description | | ||
| | -------- | ---------------- | ------------------------------- | | ||
| | `state` | `BluetoothState` | Current Bluetooth adapter state | | ||
| ### Methods | ||
| #### `server.addService(service)` | ||
| Add a `Service` to the GATT server. The `serviceAdd` event is emitted when the service has been registered. | ||
| #### `server.startAdvertising([options])` | ||
| Start advertising the server. | ||
| ```js | ||
| options = { | ||
| name: null, | ||
| serviceUUIDs: null, | ||
| serviceData: null // Apple only | ||
| } | ||
| ``` | ||
| #### `server.stopAdvertising()` | ||
| Stop advertising. | ||
| #### `server.respondToRequest(request, result[, data])` | ||
| Respond to a `ReadRequest` or `WriteRequest` with the given ATT `result: number` code. Optionally include `data: Uint8Array` for read responses. Use the `Server.ATT_*` constants for `result`. | ||
| #### `server.updateValue(characteristic, data)` | ||
| Update the value of a `Characteristic` and notify subscribed centrals. `data` is a `Uint8Array`. Returns `true` if the update was sent successfully. | ||
| #### `server.publishChannel([options])` | ||
| Publish an L2CAP channel. The `channelPublish` event is emitted with the assigned PSM. | ||
| ```js | ||
| options = { | ||
| encrypted: false | ||
| } | ||
| ``` | ||
| #### `server.unpublishChannel(psm)` | ||
| Unpublish a previously published L2CAP channel identified by `psm: number`. | ||
| #### `server.destroy()` | ||
| Destroy the server and release all resources. | ||
| ### Events | ||
| | Event | Arguments | Description | | ||
| | ---------------- | --------------------------------------------- | --------------------------------------- | | ||
| | `stateChange` | `state: BluetoothState` | Bluetooth adapter state changed | | ||
| | `serviceAdd` | `uuid: string` | Service registered | | ||
| | `readRequest` | `request: ReadRequest` | Central read a characteristic | | ||
| | `writeRequest` | `requests: WriteRequest[]` | Central wrote to a characteristic | | ||
| | `subscribe` | `peer: unknown`, `characteristicUuid: string` | Central subscribed to notifications | | ||
| | `unsubscribe` | `peer: unknown`, `characteristicUuid: string` | Central unsubscribed from notifications | | ||
| | `error` | `error: Error` | An error occurred | | ||
| | `channelPublish` | `psm: number` | L2CAP channel published | | ||
| | `channelOpen` | `channel: L2CAPChannel` | L2CAP channel opened by a central | | ||
| | Event | Arguments | Platform | | ||
| | --------------- | ----------------------------------------- | -------- | | ||
| | `notifySent` | `deviceAddress: string`, `status: number` | Android | | ||
| | `readyToUpdate` | _(none)_ | Apple | | ||
| ### Constants | ||
| #### State | ||
| | Constant | Value | | ||
| | --------------------------- | ----- | | ||
| | `Server.STATE_UNKNOWN` | `0` | | ||
| | `Server.STATE_RESETTING` | `1` | | ||
| | `Server.STATE_UNSUPPORTED` | `2` | | ||
| | `Server.STATE_UNAUTHORIZED` | `3` | | ||
| | `Server.STATE_POWERED_OFF` | `4` | | ||
| | `Server.STATE_POWERED_ON` | `5` | | ||
| #### Properties | ||
| | Constant | Value | | ||
| | ---------------------------------------- | ------ | | ||
| | `Server.PROPERTY_READ` | `0x02` | | ||
| | `Server.PROPERTY_WRITE_WITHOUT_RESPONSE` | `0x04` | | ||
| | `Server.PROPERTY_WRITE` | `0x08` | | ||
| | `Server.PROPERTY_NOTIFY` | `0x10` | | ||
| | `Server.PROPERTY_INDICATE` | `0x20` | | ||
| #### Permissions | ||
| | Constant | Value | | ||
| | ----------------------------------- | ------ | | ||
| | `Server.PERMISSION_READABLE` | `0x01` | | ||
| | `Server.PERMISSION_WRITEABLE` | `0x02` | | ||
| | `Server.PERMISSION_READ_ENCRYPTED` | `0x04` | | ||
| | `Server.PERMISSION_WRITE_ENCRYPTED` | `0x08` | | ||
| #### ATT result codes | ||
| | Constant | Value | | ||
| | ----------------------------------- | ------ | | ||
| | `Server.ATT_SUCCESS` | `0x00` | | ||
| | `Server.ATT_INVALID_HANDLE` | `0x01` | | ||
| | `Server.ATT_READ_NOT_PERMITTED` | `0x02` | | ||
| | `Server.ATT_WRITE_NOT_PERMITTED` | `0x03` | | ||
| | `Server.ATT_INSUFFICIENT_RESOURCES` | `0x11` | | ||
| | `Server.ATT_UNLIKELY_ERROR` | `0x0E` | | ||
| ## `ReadRequest` | ||
| Represents a read request from a central. Emitted by the `readRequest` event on `Server`. Pass it directly to `server.respondToRequest()`. | ||
| ### Properties | ||
| | Property | Type | Description | | ||
| | -------------------- | -------- | ------------------------------------- | | ||
| | `characteristicUuid` | `string` | UUID of the characteristic being read | | ||
| | `offset` | `number` | Byte offset for the read | | ||
| ## `WriteRequest` | ||
| Represents a write request from a central. Emitted as an array by the `writeRequest` event on `Server`. Pass it directly to `server.respondToRequest()`. | ||
| ### Properties | ||
| | Property | Type | Description | | ||
| | -------------------- | ------------ | ---------------------------------------- | | ||
| | `characteristicUuid` | `string` | UUID of the characteristic being written | | ||
| | `offset` | `number` | Byte offset for the write | | ||
| | `data` | `Uint8Array` | Data being written | | ||
| | `responseNeeded` | `boolean` | Whether the central expects a response | | ||
| ## `L2CAPChannel` | ||
| An L2CAP connection-oriented channel. Obtained through the `channelOpen` event on `Server` or `Peripheral`. Extends `Duplex` from [`bare-stream`](https://github.com/holepunchto/bare-stream) and supports standard readable and writable stream operations. | ||
| ### Properties | ||
| | Property | Type | Description | | ||
| | -------- | ---------------- | ----------------------------------- | | ||
| | `psm` | `number` | Protocol/Service Multiplexer number | | ||
| | `peer` | `string \| null` | Peer identifier, or `null` | | ||
| ## `Service` | ||
| ### `const service = new Service(uuid[, characteristics[, options]])` | ||
| Create a GATT service definition. | ||
| | Parameter | Type | Default | Description | | ||
| | ----------------- | ------------------ | ------- | --------------------------------- | | ||
| | `uuid` | `string` | | Service UUID | | ||
| | `characteristics` | `Characteristic[]` | `[]` | Characteristics for this service | | ||
| | `options.primary` | `boolean` | `true` | Whether this is a primary service | | ||
| ### Properties | ||
| | Property | Type | Description | | ||
| | ----------------- | ------------------ | ---------------------------------------- | | ||
| | `uuid` | `string` | UUID of the service | | ||
| | `characteristics` | `Characteristic[]` | Characteristics belonging to the service | | ||
| | `primary` | `boolean` | Whether this is a primary service | | ||
| ## `Characteristic` | ||
| ### `const characteristic = new Characteristic(uuid[, options])` | ||
| Create a GATT characteristic definition. | ||
| | Parameter | Type | Default | Description | | ||
| | ------------------------------ | -------------------- | ------- | ---------------------------------------------------------------- | | ||
| | `uuid` | `string` | | Characteristic UUID | | ||
| | `options.read` | `boolean` | `false` | Enable read property | | ||
| | `options.write` | `boolean` | `false` | Enable write property | | ||
| | `options.writeWithoutResponse` | `boolean` | `false` | Enable write-without-response property | | ||
| | `options.notify` | `boolean` | `false` | Enable notify property | | ||
| | `options.indicate` | `boolean` | `false` | Enable indicate property | | ||
| | `options.permissions` | `number \| null` | `null` | Explicit permission bitmask. If `null`, inferred from properties | | ||
| | `options.value` | `Uint8Array \| null` | `null` | Static value | | ||
| ### Properties | ||
| | Property | Type | Description | | ||
| | ------------- | -------------------- | --------------------------------------------- | | ||
| | `uuid` | `string` | UUID of the characteristic | | ||
| | `properties` | `number` | Bitmask of characteristic properties | | ||
| | `permissions` | `number \| null` | Bitmask of permissions, or `null` if inferred | | ||
| | `value` | `Uint8Array \| null` | Static value (read/write) | | ||
| ### Constants | ||
| | Constant | Value | | ||
| | ------------------------------------------------ | ------ | | ||
| | `Characteristic.PROPERTY_READ` | `0x02` | | ||
| | `Characteristic.PROPERTY_WRITE_WITHOUT_RESPONSE` | `0x04` | | ||
| | `Characteristic.PROPERTY_WRITE` | `0x08` | | ||
| | `Characteristic.PROPERTY_NOTIFY` | `0x10` | | ||
| | `Characteristic.PROPERTY_INDICATE` | `0x20` | | ||
| ## License | ||
| Apache-2.0 |
+43
-1
@@ -1,1 +0,43 @@ | ||
| {"name":"bare-bluetooth","version":"0.0.0"} | ||
| { | ||
| "name": "bare-bluetooth", | ||
| "version": "0.1.0", | ||
| "description": "Bluetooth bindings for Bare", | ||
| "exports": { | ||
| "./package": "./package.json", | ||
| ".": "./index.js" | ||
| }, | ||
| "imports": { | ||
| "#bluetooth": { | ||
| "android": "./lib/android.js", | ||
| "darwin": "./lib/apple.js", | ||
| "ios": "./lib/apple.js" | ||
| } | ||
| }, | ||
| "files": [ | ||
| "index.js", | ||
| "lib" | ||
| ], | ||
| "scripts": { | ||
| "format": "prettier --write . && lunte --fix", | ||
| "lint": "prettier --check . && lunte" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/holepunchto/bare-bluetooth.git" | ||
| }, | ||
| "author": "Holepunch", | ||
| "license": "Apache-2.0", | ||
| "bugs": { | ||
| "url": "https://github.com/holepunchto/bare-bluetooth/issues" | ||
| }, | ||
| "homepage": "https://github.com/holepunchto/bare-bluetooth#readme", | ||
| "dependencies": { | ||
| "bare-bluetooth-android": "^0.4.0", | ||
| "bare-bluetooth-apple": "^0.3.0" | ||
| }, | ||
| "devDependencies": { | ||
| "lunte": "^1.7.0", | ||
| "prettier": "^3.9.1", | ||
| "prettier-config-holepunch": "^2.0.0" | ||
| } | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
No License Found
LicenseLicense information could not be found.
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
No website
QualityPackage does not have a website.
48428
112523.26%6
500%0
-100%867
Infinity%0
-100%1
-50%0
-100%506
Infinity%0
-100%2
Infinity%3
Infinity%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added