diff --git a/types/bleno/bleno-tests.ts b/types/bleno/bleno-tests.ts index bd4b6971ee..7ea037587c 100644 --- a/types/bleno/bleno-tests.ts +++ b/types/bleno/bleno-tests.ts @@ -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 ''; + } + + 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 +} diff --git a/types/bleno/index.d.ts b/types/bleno/index.d.ts index a59e394a84..0dee37c9ed 100644 --- a/types/bleno/index.d.ts +++ b/types/bleno/index.d.ts @@ -3,126 +3,147 @@ // Definitions by: Manuel Francisco Naranjo // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -export class Characteristic { - uuid: any; - properties: any; - secure: any; - value: any; - descriptors: any; +/// - constructor(options: any); +type State = 'poweredOn' | 'poweredOff' | 'unauthorized' | 'unsupported' | 'unknown' | 'resetting'; + +type Property = 'read' | 'write' | 'indicate' | 'notify' | 'writeWithoutResponse'; + +interface CharacteristicOptions { + uuid: string; + properties?: ReadonlyArray | null; + secure?: ReadonlyArray | null; + value?: Buffer | null; + descriptors?: ReadonlyArray | 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; + secure: ReadonlyArray; + value: Buffer | null; + descriptors: ReadonlyArray; + + 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 | null; +} -export const domain: any; +declare class PrimaryService { + uuid: string; + characteristics: ReadonlyArray; -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, callback?: (arg: Error | undefined | null) => void): void; -export function listeners(type: any): any; + startAdvertising(name: string, serviceUuids?: ReadonlyArray, 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; diff --git a/types/bleno/tslint.json b/types/bleno/tslint.json index 26bcea6634..0f5eeaf557 100644 --- a/types/bleno/tslint.json +++ b/types/bleno/tslint.json @@ -1,6 +1,7 @@ { "extends": "dtslint/dt.json", "rules": { - "dt-header": false + "dt-header": false, + "unified-signatures": false } }