🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

bare-bluetooth

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bare-bluetooth - npm Package Compare versions

Comparing version
0.0.0
to
0.1.0
+8
index.js
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
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
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
> [!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"
}
}