More type-safe typings for bleno module.

Define types for callbacks and properties; define Bleno as a class extending EventEmitter,
so we don't have to explicitly define EventEmitter methods.
This commit is contained in:
Yevgen Muntyan 2018-06-05 10:40:47 -07:00
parent 0501552498
commit 55fc8a3510
3 changed files with 371 additions and 83 deletions

View File

@ -1,4 +1,7 @@
import * as Bleno from 'bleno';
// can also import these types as if they were exported from the module
// tslint:disable-next-line:no-duplicate-imports
import { Characteristic, Descriptor, PrimaryService } from 'bleno';
export class EchoCharacteristic extends Bleno.Characteristic {
_value: any;
@ -46,7 +49,7 @@ Bleno.on('stateChange', (state: string) => {
});
const characteristic = new EchoCharacteristic();
Bleno.on('advertisingStart', (error: string) => {
Bleno.on('advertisingStart', (error?: Error | null) => {
if (!error) {
Bleno.setServices(
[new Bleno.PrimaryService({uuid: 'ec00', characteristics: [ characteristic ] })],
@ -54,3 +57,266 @@ Bleno.on('advertisingStart', (error: string) => {
);
}
});
const SERVICE_UUID = '87B1E448-1C07-4957-B7D3-017DF4EE3863';
const CONTROL_CHAR_UUID = '87B1E448-1C07-4957-B7D3-017DF4EE3864';
const STATUS_CHAR_UUID = '87B1E448-1C07-4957-B7D3-017DF4EE3866';
class StatusCharacteristic extends Characteristic {
constructor() {
super({
uuid: 'STATUS_CHAR_UUID',
properties: ['read'],
descriptors: [
new Descriptor({
uuid: '2901',
value: Buffer.from('Status'),
}),
],
});
}
onReadRequest(
offset: number,
callback: (result: number, data?: Buffer) => void
) {
try {
const data = Buffer.from('status ok');
callback(Characteristic.RESULT_SUCCESS, data.slice(offset));
} catch (error) {
callback(Characteristic.RESULT_UNLIKELY_ERROR);
}
}
}
class ControlCharacteristic extends Characteristic {
private readonly callback: (data: Buffer) => void;
constructor(onWriteRequestCb: (data: Buffer) => void) {
super({
uuid: CONTROL_CHAR_UUID,
properties: ['write'],
descriptors: [
new Descriptor({
uuid: '2901',
value: Buffer.from('test characteristic'),
}),
],
});
this.callback = onWriteRequestCb;
}
onWriteRequest(
data: Buffer,
offset: number,
withoutResponse: any,
callback: (result: number) => void
): void {
if (offset > 0) {
this.callback(data.slice(offset));
} else {
this.callback(data);
}
callback(this.RESULT_SUCCESS);
}
}
export class BluetoothController {
private isAdvertising: boolean;
constructor() {
this.isAdvertising = false;
this.setup();
}
toString(): string {
return '<Control>';
}
dispose(callback: () => void) {
if (this.isAdvertising) {
Bleno.stopAdvertising(callback);
} else {
callback();
}
}
private setup() {
Bleno.on('stateChange', (state: string) => {
console.log('bluetooth', `stateChange: ${state}, address = ${Bleno.address}`);
if (state === 'poweredOn') {
Bleno.startAdvertising('device', [SERVICE_UUID]);
} else {
Bleno.stopAdvertising();
}
});
Bleno.on('accept', (clientAddress: string) => {
console.log('bluetooth', `accept, client: ${clientAddress}`);
Bleno.updateRssi();
});
Bleno.on('disconnect', (clientAddress: string) => {
console.log('bluetooth', `disconnect, client: ${clientAddress}`);
});
Bleno.on('rssiUpdate', (rssi: number) => {
console.log('bluetooth', `rssiUpdate: ${rssi}`);
});
Bleno.on('mtuChange', (mtu: number) => {
console.log('bluetooth', `mtuChange: ${mtu}`);
});
Bleno.on('advertisingStart', (error?: Error | null) => {
console.log('bluetooth', `advertisingStart: ${error ? error : 'success'}`);
if (!error) {
this.isAdvertising = true;
Bleno.setServices([
new PrimaryService({
uuid: SERVICE_UUID,
characteristics: [
new ControlCharacteristic((data) => {
this.onWriteRequest(data);
}),
new StatusCharacteristic(),
],
}),
]);
}
});
Bleno.on('advertisingStop', () => {
this.isAdvertising = false;
console.log('bluetooth', 'advertisingStop');
});
Bleno.on('servicesSet', (error?: Error | null) => {
console.log('bluetooth', `servicesSet: ${error ? error : 'success'}`);
});
}
private onWriteRequest(data: Buffer) {
console.log(data.toString('hex'));
}
}
///////////////////////////////////////////////////////////////////////////////
// code from bleno's README
//
// tslint:disable-next-line:no-duplicate-imports
import * as bleno from 'bleno';
function test1() {
const name = 'name';
const serviceUuids = ['fffffffffffffffffffffffffffffff0'];
bleno.startAdvertising(name);
bleno.startAdvertising(name, serviceUuids);
bleno.startAdvertising(name, serviceUuids, (error) => {});
}
function test2() {
const uuid = 'e2c56db5dffb48d2b060d0f5a71096e0';
const major = 0; // 0x0000 - 0xffff
const minor = 0; // 0x0000 - 0xffff
const measuredPower = -59; // -128 - 127
bleno.startAdvertisingIBeacon(uuid, major, minor, measuredPower);
bleno.startAdvertisingIBeacon(uuid, major, minor, measuredPower, (error) => {});
}
function test3() {
const scanData = new Buffer(''); // maximum 31 bytes
const advertisementData = new Buffer(''); // maximum 31 bytes
bleno.startAdvertisingWithEIRData(advertisementData);
bleno.startAdvertisingWithEIRData(advertisementData, (error) => {});
bleno.startAdvertisingWithEIRData(advertisementData, scanData, (error) => {});
}
function test4() {
bleno.stopAdvertising();
bleno.stopAdvertising(() => {});
}
function test5() {
const service = new PrimaryService({uuid: '0000'});
const services = [
service
];
bleno.setServices(services);
bleno.setServices(services, (error) => {});
}
function test6() {
bleno.disconnect(); // Linux only
}
function test7() {
bleno.updateRssi(); // not available in OS X 10.9
bleno.updateRssi((error, rssi) => {}); // not available in OS X 10.9
}
function test8() {
const PrimaryService = bleno.PrimaryService;
const primaryService = new PrimaryService({
uuid: 'fffffffffffffffffffffffffffffff0', // or 'fff0' for 16-bit
characteristics: [
// see Characteristic for data type
]
});
}
function test9() {
const Characteristic = bleno.Characteristic;
const characteristic = new Characteristic({
uuid: 'fffffffffffffffffffffffffffffff1', // or 'fff1' for 16-bit
properties: ['read'], // can be a combination of 'read', 'write', 'writeWithoutResponse', 'notify', 'indicate'
secure: ['notify'], // enable security for properties, can be a combination of 'read', 'write', 'writeWithoutResponse', 'notify', 'indicate'
value: null, // optional static value, must be of type Buffer - for read only characteristics
descriptors: [
// see Descriptor for data type
new Descriptor({uuid: '0000'})
],
onReadRequest: null, // optional read request handler, function(offset, callback) { ... }
onWriteRequest: null, // optional write request handler, function(data, offset, withoutResponse, callback) { ...}
onSubscribe: null, // optional notify/indicate subscribe handler, function(maxValueSize, updateValueCallback) { ...}
onUnsubscribe: null, // optional notify/indicate unsubscribe handler, function() { ...}
onNotify: null, // optional notify sent handler, function() { ...}
onIndicate: null // optional indicate confirmation received handler, function() { ...}
});
}
function test10() {
Characteristic.RESULT_SUCCESS;
Characteristic.RESULT_INVALID_OFFSET;
Characteristic.RESULT_INVALID_ATTRIBUTE_LENGTH;
Characteristic.RESULT_UNLIKELY_ERROR;
}
function test11() {
const Descriptor = bleno.Descriptor;
const descriptor = new Descriptor({
uuid: '2901',
value: 'value' // static value, must be of type Buffer or string if set
});
}
function test12() {
bleno.on('stateChange', (state: string) => {});
bleno.on('advertisingStart', (error?: Error | null) => {});
bleno.on('advertisingStartError', (error: Error) => {});
bleno.on('advertisingStop', () => {});
bleno.on('servicesSet', (error?: Error | null) => {});
bleno.on('servicesSetError', (error: Error) => {});
bleno.on('accept', (clientAddress) => {}); // not available on OS X 10.9
bleno.on('disconnect', (clientAddress) => {}); // Linux only
bleno.on('rssiUpdate', (rssi) => {}); // not available on OS X 10.9
}

183
types/bleno/index.d.ts vendored
View File

@ -3,126 +3,147 @@
// Definitions by: Manuel Francisco Naranjo <naranjo.manuel@gmail.com>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
export class Characteristic {
uuid: any;
properties: any;
secure: any;
value: any;
descriptors: any;
/// <reference types="node" />
constructor(options: any);
type State = 'poweredOn' | 'poweredOff' | 'unauthorized' | 'unsupported' | 'unknown' | 'resetting';
type Property = 'read' | 'write' | 'indicate' | 'notify' | 'writeWithoutResponse';
interface CharacteristicOptions {
uuid: string;
properties?: ReadonlyArray<Property> | null;
secure?: ReadonlyArray<Property> | null;
value?: Buffer | null;
descriptors?: ReadonlyArray<Descriptor> | null;
onIndicate?: (() => void) | null;
onNotify?: (() => void) | null;
onReadRequest?: ((
offset: number,
callback: (result: number, data?: Buffer) => void
) => void) | null;
onSubscribe?: ((maxValueSize: number, updateValueCallback: any) => void) | null;
onUnsubscribe?: (() => void) | null;
onWriteRequest?: ((
data: Buffer,
offset: number,
withoutResponse: boolean,
callback: (result: number) => void
) => void) | null;
}
declare class Characteristic {
uuid: string;
properties: ReadonlyArray<Property>;
secure: ReadonlyArray<Property>;
value: Buffer | null;
descriptors: ReadonlyArray<Descriptor>;
constructor(options: CharacteristicOptions);
onIndicate(): void;
onNotify(): void;
onReadRequest(offset: any, callback: any): void;
onReadRequest(offset: number, callback: (result: number, data?: Buffer) => void): void;
onSubscribe(maxValueSize: any, updateValueCallback: any): void;
onSubscribe(maxValueSize: number, updateValueCallback: any): void;
onUnsubscribe(): void;
onWriteRequest(data: any, offset: any, withoutResponse: any, callback: any): void;
onWriteRequest(data: Buffer, offset: number, withoutResponse: boolean, callback: (result: number) => void): void;
toString(): any;
toString(): string;
static RESULT_ATTR_NOT_LONG: number;
readonly RESULT_ATTR_NOT_LONG: number;
static RESULT_INVALID_ATTRIBUTE_LENGTH: number;
readonly RESULT_INVALID_ATTRIBUTE_LENGTH: number;
static RESULT_INVALID_OFFSET: number;
readonly RESULT_INVALID_OFFSET: number;
static RESULT_SUCCESS: number;
readonly RESULT_SUCCESS: number;
static RESULT_UNLIKELY_ERROR: number;
readonly RESULT_UNLIKELY_ERROR: number;
static readonly RESULT_ATTR_NOT_LONG: number;
static readonly RESULT_INVALID_ATTRIBUTE_LENGTH: number;
static readonly RESULT_INVALID_OFFSET: number;
static readonly RESULT_SUCCESS: number;
static readonly RESULT_UNLIKELY_ERROR: number;
}
export class Descriptor {
uuid: any;
value: any;
constructor(options: any);
toString(): any;
interface DescriptorOptions {
uuid: string;
value?: Buffer | string | null;
}
export class PrimaryService {
uuid: any;
characteristics: any;
declare class Descriptor {
uuid: string;
value: Buffer;
constructor(options: any);
constructor(options: DescriptorOptions);
toString(): any;
toString(): string;
}
export const address: string;
interface PrimaryServiceOptions {
uuid: string;
characteristics?: ReadonlyArray<Characteristic> | null;
}
export const domain: any;
declare class PrimaryService {
uuid: string;
characteristics: ReadonlyArray<Characteristic>;
export const mtu: number;
constructor(options: PrimaryServiceOptions);
export const platform: string;
toString(): string;
}
export const rssi: number;
interface Bleno extends NodeJS.EventEmitter {
readonly Characteristic: typeof Characteristic;
readonly Descriptor: typeof Descriptor;
readonly PrimaryService: typeof PrimaryService;
export const state: string;
readonly address: string;
export function addListener(type: any, listener: any): any;
readonly mtu: number;
export function disconnect(): void;
readonly platform: string;
export function emit(type: any, ...args: any[]): any;
readonly rssi: number;
export function eventNames(): any;
readonly state: State;
export function getMaxListeners(): any;
disconnect(): void;
export function listenerCount(type: any): any;
setServices(services: ReadonlyArray<PrimaryService>, callback?: (arg: Error | undefined | null) => void): void;
export function listeners(type: any): any;
startAdvertising(name: string, serviceUuids?: ReadonlyArray<string>, callback?: (arg: Error | undefined | null) => void): void;
export function on(type: any, listener: any): any;
startAdvertisingIBeacon(uuid: string, major: number, minor: number, measuredPower: number, callback?: (arg: Error | undefined | null) => void): void;
export function onAccept(clientAddress: any): void;
startAdvertisingWithEIRData(advertisementData: Buffer, callback?: (arg: Error | undefined | null) => void): void;
startAdvertisingWithEIRData(advertisementData: Buffer, scanData: Buffer, callback?: (arg: Error | undefined | null) => void): void;
export function onAddressChange(address: any): void;
stopAdvertising(callback?: () => void): void;
export function onAdvertisingStart(error: any): void;
updateRssi(callback?: (err: null, rssi: number) => void): void;
export function onAdvertisingStop(): void;
on(event: 'stateChange', cb: (state: State) => void): this;
on(event: 'accept', cb: (address: string) => void): this;
on(event: 'mtuChange', cb: (mtu: number) => void): this;
on(event: 'disconnect', cb: (clientAddress: string) => void): this;
on(event: 'advertisingStart', cb: (err?: Error | null) => void): this;
on(event: 'advertisingStartError', cb: (err: Error) => void): this;
on(event: 'advertisingStop', cb: () => void): this;
on(event: 'servicesSet', cb: (err?: Error | null) => void): this;
on(event: 'servicesSetError', cb: (err: Error) => void): this;
on(event: 'rssiUpdate', cb: (rssi: number) => void): this;
}
export function onDisconnect(clientAddress: any): void;
export function onMtuChange(mtu: any): void;
export function onPlatform(platform: any): void;
export function onRssiUpdate(rssi: any): void;
export function onServicesSet(error: any): void;
export function onStateChange(state: any): void;
export function once(type: any, listener: any): any;
export function prependListener(type: any, listener: any): any;
export function prependOnceListener(type: any, listener: any): any;
export function removeAllListeners(type: any, ...args: any[]): any;
export function removeListener(type: any, listener: any): any;
export function setMaxListeners(n: any): any;
export function setServices(services: any, callback: any): void;
export function startAdvertising(name: any, serviceUuids: any, callback: any): void;
export function startAdvertisingIBeacon(uuid: any, major: any, minor: any, measuredPower: any, callback: any): void;
export function startAdvertisingWithEIRData(advertisementData: any, scanData: any, callback: any): void;
export function stopAdvertising(callback: any): void;
export function updateRssi(callback: any): void;
declare const bleno: Bleno;
export = bleno;

View File

@ -1,6 +1,7 @@
{
"extends": "dtslint/dt.json",
"rules": {
"dt-header": false
"dt-header": false,
"unified-signatures": false
}
}