@abandonware/noble
Version:
A Node.js BLE (Bluetooth Low Energy) central library.
405 lines (330 loc) • 10.8 kB
JavaScript
/* jshint loopfunc: true */
const WebSocket = require('ws');
const noble = require('./index');
const serverMode = !process.argv[2];
const port = 0xB1e;
const host = process.argv[2];
let ws;
let wss;
if (serverMode) {
console.log('noble - ws slave - server mode');
wss = new WebSocket.Server({
port: 0xB1e
});
wss.on('connection', function (ws_) {
console.log('ws -> connection');
ws = ws_;
ws.on('message', onMessage);
ws.on('close', function () {
console.log('ws -> close');
noble.stopScanning();
});
noble.on('stateChange', function (state) {
sendEvent({
type: 'stateChange',
state
});
});
// Send poweredOn if already in this state.
if (noble.state === 'poweredOn') {
sendEvent({
type: 'stateChange',
state: 'poweredOn'
});
}
});
} else {
ws = new WebSocket('ws://' + host + ':' + port);
ws.on('open', function () {
console.log('ws -> open');
});
ws.on('message', function (message) {
onMessage(message);
});
ws.on('close', function () {
console.log('ws -> close');
noble.stopScanning();
});
}
const peripherals = {};
// TODO: open/close ws on state change
function sendEvent (event) {
const message = JSON.stringify(event);
console.log('ws -> send: ' + message);
const clients = serverMode ? wss.clients : [ws];
for (let i = 0; i < clients.length; i++) {
clients[i].send(message);
}
}
const onMessage = function (message) {
console.log('ws -> message: ' + message);
const command = JSON.parse(message);
const action = command.action;
const peripheralUuid = command.peripheralUuid;
const serviceUuids = command.serviceUuids;
const serviceUuid = command.serviceUuid;
const characteristicUuids = command.characteristicUuids;
const characteristicUuid = command.characteristicUuid;
const data = command.data ? Buffer.from(command.data, 'hex') : null;
const withoutResponse = command.withoutResponse;
const broadcast = command.broadcast;
const notify = command.notify;
const descriptorUuid = command.descriptorUuid;
let handle;
const peripheral = peripherals[peripheralUuid];
let service = null;
let characteristic = null;
let descriptor = null;
if (peripheral && serviceUuid) {
const services = peripheral.services;
for (const i in services) {
if (services[i].uuid === serviceUuid) {
service = services[i];
if (characteristicUuid) {
const characteristics = service.characteristics;
for (const j in characteristics) {
if (characteristics[j].uuid === characteristicUuid) {
characteristic = characteristics[j];
if (descriptorUuid) {
const descriptors = characteristic.descriptors;
for (const k in descriptors) {
if (descriptors[k].uuid === descriptorUuid) {
descriptor = descriptors[k];
break;
}
}
}
break;
}
}
}
break;
}
}
}
if (action === 'startScanning') {
noble.startScanning(serviceUuids, command.allowDuplicates);
} else if (action === 'stopScanning') {
noble.stopScanning();
} else if (action === 'connect') {
peripheral.connect();
} else if (action === 'disconnect') {
peripheral.disconnect();
} else if (action === 'updateRssi') {
peripheral.updateRssi();
} else if (action === 'discoverServices') {
peripheral.discoverServices(command.uuids);
} else if (action === 'discoverIncludedServices') {
service.discoverIncludedServices(serviceUuids);
} else if (action === 'discoverCharacteristics') {
service.discoverCharacteristics(characteristicUuids);
} else if (action === 'read') {
characteristic.read();
} else if (action === 'write') {
characteristic.write(data, withoutResponse);
} else if (action === 'broadcast') {
characteristic.broadcast(broadcast);
} else if (action === 'notify') {
characteristic.notify(notify);
} else if (action === 'discoverDescriptors') {
characteristic.discoverDescriptors();
} else if (action === 'readValue') {
descriptor.readValue();
} else if (action === 'writeValue') {
descriptor.writeValue(data);
} else if (action === 'readHandle') {
peripheral.readHandle(handle);
} else if (action === 'writeHandle') {
peripheral.writeHandle(handle, data, withoutResponse);
}
};
noble.on('discover', function (peripheral) {
peripherals[peripheral.uuid] = peripheral;
peripheral.on('connect', function () {
sendEvent({
type: 'connect',
peripheralUuid: this.uuid
});
});
peripheral.on('disconnect', function () {
sendEvent({
type: 'disconnect',
peripheralUuid: this.uuid
});
for (const i in this.services) {
for (const j in this.services[i].characteristics) {
for (const k in this.services[i].characteristics[j].descriptors) {
this.services[i].characteristics[j].descriptors[k].removeAllListeners();
}
this.services[i].characteristics[j].removeAllListeners();
}
this.services[i].removeAllListeners();
}
this.removeAllListeners();
});
peripheral.on('rssiUpdate', function (rssi) {
sendEvent({
type: 'rssiUpdate',
peripheralUuid: this.uuid,
rssi
});
});
peripheral.on('servicesDiscover', function (services) {
const peripheral = this;
const serviceUuids = [];
const includedServicesDiscover = function (includedServiceUuids) {
sendEvent({
type: 'includedServicesDiscover',
peripheralUuid: peripheral.uuid,
serviceUuid: this.uuid,
includedServiceUuids
});
};
const characteristicsDiscover = function (characteristics) {
const service = this;
const discoveredCharacteristics = [];
const read = function (data, isNotification) {
const characteristic = this;
sendEvent({
type: 'read',
peripheralUuid: peripheral.uuid,
serviceUuid: service.uuid,
characteristicUuid: characteristic.uuid,
data: data.toString('hex'),
isNotification
});
};
const write = function () {
const characteristic = this;
sendEvent({
type: 'write',
peripheralUuid: peripheral.uuid,
serviceUuid: service.uuid,
characteristicUuid: characteristic.uuid
});
};
const broadcast = function (state) {
const characteristic = this;
sendEvent({
type: 'broadcast',
peripheralUuid: peripheral.uuid,
serviceUuid: service.uuid,
characteristicUuid: characteristic.uuid,
state
});
};
const notify = function (state) {
const characteristic = this;
sendEvent({
type: 'notify',
peripheralUuid: peripheral.uuid,
serviceUuid: service.uuid,
characteristicUuid: characteristic.uuid,
state
});
};
const descriptorsDiscover = function (descriptors) {
const characteristic = this;
const discoveredDescriptors = [];
const valueRead = function (data) {
const descriptor = this;
sendEvent({
type: 'valueRead',
peripheralUuid: peripheral.uuid,
serviceUuid: service.uuid,
characteristicUuid: characteristic.uuid,
descriptorUuid: descriptor.uuid,
data: data.toString('hex')
});
};
const valueWrite = function (data) {
const descriptor = this;
sendEvent({
type: 'valueWrite',
peripheralUuid: peripheral.uuid,
serviceUuid: service.uuid,
characteristicUuid: characteristic.uuid,
descriptorUuid: descriptor.uuid
});
};
for (const k in descriptors) {
descriptors[k].on('valueRead', valueRead);
descriptors[k].on('valueWrite', valueWrite);
discoveredDescriptors.push(descriptors[k].uuid);
}
sendEvent({
type: 'descriptorsDiscover',
peripheralUuid: peripheral.uuid,
serviceUuid: service.uuid,
characteristicUuid: this.uuid,
descriptors: discoveredDescriptors
});
};
for (let j = 0; j < characteristics.length; j++) {
characteristics[j].on('read', read);
characteristics[j].on('write', write);
characteristics[j].on('broadcast', broadcast);
characteristics[j].on('notify', notify);
characteristics[j].on('descriptorsDiscover', descriptorsDiscover);
discoveredCharacteristics.push({
uuid: characteristics[j].uuid,
properties: characteristics[j].properties
});
}
sendEvent({
type: 'characteristicsDiscover',
peripheralUuid: peripheral.uuid,
serviceUuid: this.uuid,
characteristics: discoveredCharacteristics
});
};
for (const i in services) {
services[i].on('includedServicesDiscover', includedServicesDiscover);
services[i].on('characteristicsDiscover', characteristicsDiscover);
serviceUuids.push(services[i].uuid);
}
sendEvent({
type: 'servicesDiscover',
peripheralUuid: this.uuid,
serviceUuids
});
});
peripheral.on('handleRead', function (handle, data) {
sendEvent({
type: 'handleRead',
peripheralUuid: this.uuid,
handle,
data: data.toString('hex')
});
});
peripheral.on('handleWrite', function (handle) {
sendEvent({
type: 'handleWrite',
peripheralUuid: this.uuid,
handle
});
});
peripheral.on('handleNotify', function (handle, data) {
sendEvent({
type: 'handleNotify',
peripheralUuid: this.uuid,
handle,
data: data.toString('hex')
});
});
sendEvent({
type: 'discover',
peripheralUuid: peripheral.uuid,
address: peripheral.address,
addressType: peripheral.addressType,
connectable: peripheral.connectable,
advertisement: {
localName: peripheral.advertisement.localName,
txPowerLevel: peripheral.advertisement.txPowerLevel,
serviceUuids: peripheral.advertisement.serviceUuids,
manufacturerData: (peripheral.advertisement.manufacturerData ? peripheral.advertisement.manufacturerData.toString('hex') : null),
serviceData: (peripheral.advertisement.serviceData ? peripheral.advertisement.serviceData.toString('hex') : null)
},
rssi: peripheral.rssi
});
});