-
Notifications
You must be signed in to change notification settings - Fork 467
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2627 from entrylabs/issue/3424
[4.31.0] 하드웨어 웹연결에 bluetooth 기능 추가, 마이크로비트 ble연결 지원
- Loading branch information
Showing
40 changed files
with
5,252 additions
and
400 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/* | ||
* micro:bit Web Bluetooth | ||
* Copyright (c) 2019 Rob Moran | ||
* | ||
* The MIT License (MIT) | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
|
||
import { EventEmitter } from 'events'; | ||
|
||
/** | ||
* @hidden | ||
*/ | ||
export interface TypedDispatcher<T> { | ||
addEventListener<K extends keyof T>( | ||
type: K, | ||
listener: (event: CustomEvent<T[K]>) => void | ||
): void; | ||
removeEventListener<K extends keyof T>( | ||
type: K, | ||
callback: (event: CustomEvent<T[K]>) => void | ||
): void; | ||
dispatchEvent(event: CustomEvent<T>): boolean; | ||
dispatchEvent<K extends keyof T>(type: K, detail: T[K]): boolean; | ||
addListener<K extends keyof T>(event: K, listener: (data: T[K]) => void): this; | ||
on<K extends keyof T>(event: K, listener: (data: T[K]) => void): this; | ||
once<K extends keyof T>(event: K, listener: (data: T[K]) => void): this; | ||
prependListener<K extends keyof T>(event: K, listener: (data: T[K]) => void): this; | ||
prependOnceListener<K extends keyof T>(event: K, listener: (data: T[K]) => void): this; | ||
removeListener<K extends keyof T>(event: K, listener: (data: T[K]) => void): this; | ||
removeAllListeners<K extends keyof T>(event?: K): this; | ||
// tslint:disable-next-line:ban-types | ||
listeners<K extends keyof T>(event: K): Function[]; | ||
emit<K extends keyof T>(event: K, data: T[K]): boolean; | ||
// tslint:disable-next-line:array-type | ||
eventNames<K extends keyof T>(): Array<K>; | ||
listenerCount<K extends keyof T>(type: K): number; | ||
setMaxListeners(n: number): this; | ||
getMaxListeners(): number; | ||
} | ||
|
||
/** | ||
* @hidden | ||
*/ | ||
export class EventDispatcher extends EventEmitter implements EventTarget { | ||
private isEventListenerObject = ( | ||
listener: EventListenerOrEventListenerObject | ||
): listener is EventListenerObject => | ||
(listener as EventListenerObject).handleEvent !== undefined; | ||
|
||
public addEventListener(type: string, listener: EventListenerOrEventListenerObject | null) { | ||
if (listener) { | ||
const handler = this.isEventListenerObject(listener) ? listener.handleEvent : listener; | ||
super.addListener(type, handler); | ||
} | ||
} | ||
|
||
public removeEventListener(type: string, callback: EventListenerOrEventListenerObject | null) { | ||
if (callback) { | ||
const handler = this.isEventListenerObject(callback) ? callback.handleEvent : callback; | ||
super.removeListener(type, handler); | ||
} | ||
} | ||
|
||
public dispatchEvent(event: Event): boolean; | ||
public dispatchEvent<T>(type: string, detail: T): boolean; | ||
public dispatchEvent<T>(eventOrType: Event | string, detail?: T): boolean { | ||
let event: Event; | ||
if (typeof eventOrType === 'string') { | ||
event = new CustomEvent(eventOrType, { | ||
detail, | ||
}); | ||
} else { | ||
event = eventOrType; | ||
} | ||
|
||
return super.emit(event.type, event); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// 사용하는 서비스 정리 | ||
|
||
import { AccelerometerService } from './services/accelerometer'; | ||
import { ButtonService } from './services/button'; | ||
import { DeviceInformationService } from './services/device-information'; | ||
import { DfuControlService } from './services/dfu-control'; | ||
import { EventService } from './services/event'; | ||
import { IoPinService } from './services/io-pin'; | ||
import { LedService } from './services/led'; | ||
import { MagnetometerService } from './services/magnetometer'; | ||
import { TemperatureService } from './services/temperature'; | ||
import { UartService } from './services/uart'; | ||
|
||
export const getServiceClassesByModuleId = (moduleId: string) => { | ||
switch (moduleId) { | ||
case '220302': | ||
return [ | ||
DeviceInformationService, | ||
ButtonService, | ||
LedService, | ||
TemperatureService, | ||
AccelerometerService, | ||
MagnetometerService, | ||
UartService, | ||
EventService, | ||
DfuControlService, | ||
IoPinService, | ||
]; | ||
break; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* micro:bit Web Bluetooth | ||
* Copyright (c) 2019 Rob Moran | ||
* | ||
* The MIT License (MIT) | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
|
||
/** | ||
* @hidden | ||
*/ | ||
interface QueuedPromise { | ||
fn: () => Promise<any>; | ||
resolve: (value?: any | PromiseLike<any> | undefined) => void; | ||
reject: (reason?: any) => void; | ||
} | ||
|
||
/** | ||
* @hidden | ||
*/ | ||
export class PromiseQueue { | ||
private queue: QueuedPromise[] = []; | ||
private running = 0; | ||
|
||
constructor(private concurrent = 1) {} | ||
|
||
private async pump(): Promise<void> { | ||
if (this.running >= this.concurrent) { | ||
return; | ||
} | ||
|
||
const promise = this.queue.shift(); | ||
|
||
if (!promise) { | ||
return; | ||
} | ||
|
||
this.running++; | ||
|
||
try { | ||
const result = await promise.fn(); | ||
promise.resolve(result); | ||
} catch (error) { | ||
promise.reject(error); | ||
} | ||
|
||
this.running--; | ||
return this.pump(); | ||
} | ||
|
||
public add<T>(fn: () => Promise<T>): Promise<T> { | ||
return new Promise((resolve, reject) => { | ||
this.queue.push({ | ||
fn, | ||
resolve, | ||
reject, | ||
}); | ||
|
||
return this.pump(); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
* micro:bit Web Bluetooth | ||
* Copyright (c) 2019 Rob Moran | ||
* | ||
* The MIT License (MIT) | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
|
||
import { EventEmitter } from 'events'; | ||
import { PromiseQueue } from './promise-queue'; | ||
|
||
/** | ||
* @hidden | ||
*/ | ||
export interface ServiceEventHandler { | ||
characteristic: BluetoothCharacteristicUUID; | ||
handler: (event: Event) => void; | ||
} | ||
|
||
/** | ||
* @hidden | ||
*/ | ||
export class ServiceHelper { | ||
private static queue = new PromiseQueue(); | ||
|
||
private characteristics?: BluetoothRemoteGATTCharacteristic[]; | ||
|
||
constructor(private service: BluetoothRemoteGATTService, private emitter?: EventEmitter) {} | ||
|
||
private async getCharacteristic( | ||
uuid: BluetoothCharacteristicUUID | ||
): Promise<BluetoothRemoteGATTCharacteristic | undefined> { | ||
if (!this.characteristics) { | ||
this.characteristics = await this.service.getCharacteristics(); | ||
} | ||
|
||
return this.characteristics.find((characteristic) => characteristic.uuid === uuid); | ||
} | ||
|
||
public async getCharacteristicValue(uuid: BluetoothCharacteristicUUID): Promise<DataView> { | ||
const characteristic = await this.getCharacteristic(uuid); | ||
|
||
if (!characteristic) { | ||
throw new Error('Unable to locate characteristic'); | ||
} | ||
|
||
return await ServiceHelper.queue.add(async () => characteristic.readValue()); | ||
} | ||
|
||
public async setCharacteristicValue( | ||
uuid: BluetoothCharacteristicUUID, | ||
value: BufferSource | ||
): Promise<void> { | ||
const characteristic = await this.getCharacteristic(uuid); | ||
|
||
if (!characteristic) { | ||
throw new Error('Unable to locate characteristic'); | ||
} | ||
|
||
await ServiceHelper.queue.add(async () => characteristic.writeValue(value)); | ||
} | ||
|
||
public async handleListener( | ||
event: string, | ||
uuid: BluetoothCharacteristicUUID, | ||
handler: (event: Event) => void | ||
) { | ||
const characteristic = await this.getCharacteristic(uuid); | ||
|
||
if (!characteristic) { | ||
return; | ||
} | ||
|
||
await ServiceHelper.queue.add(async () => characteristic.startNotifications()); | ||
|
||
this.emitter!.on('newListener', (emitterEvent: string) => { | ||
if (emitterEvent !== event || this.emitter!.listenerCount(event) > 0) { | ||
return; | ||
} | ||
|
||
return ServiceHelper.queue.add(async () => | ||
characteristic.addEventListener('characteristicvaluechanged', handler) | ||
); | ||
}); | ||
|
||
this.emitter!.on('removeListener', (emitterEvent: string) => { | ||
if (emitterEvent !== event || this.emitter!.listenerCount(event) > 0) { | ||
return; | ||
} | ||
|
||
return ServiceHelper.queue.add(async () => | ||
characteristic.removeEventListener('characteristicvaluechanged', handler) | ||
); | ||
}); | ||
} | ||
} |
Oops, something went wrong.