-
Notifications
You must be signed in to change notification settings - Fork 70
/
bluetooth.js
175 lines (147 loc) · 3.93 KB
/
bluetooth.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
* @module Bluetooth
*
* A high-level, cross-platform API for Bluetooth Pub-Sub
*
* Example usage:
* ```js
* import { Bluetooth } from 'socket:bluetooth'
* ```
*/
import { EventEmitter } from './events.js'
import diagnostics from './diagnostics.js'
import ipc from './ipc.js'
import * as exports from './bluetooth.js'
export default exports
const dc = diagnostics.channels.group('bluetooth', [
'data',
'event',
'start',
'handle',
'publish',
'subscribe'
])
/**
* Create an instance of a Bluetooth service.
*/
export class Bluetooth extends EventEmitter {
static isInitalized = false
/**
* constructor is an example property that is set to `true`
* Creates a new service with key-value pairs
* @param {string} serviceId - Given a default value to determine the type
*/
constructor (serviceId = '') {
super()
if (!serviceId || serviceId.length !== 36) {
throw new Error('expected serviceId of length 36')
}
this.serviceId = serviceId
window.addEventListener('data', e => {
if (!e.detail.params) return
const { err, data } = e.detail.params
if (err) return this.emit('error', err)
if (data?.serviceId === this.serviceId) {
this.emit(data.characteristicId, data, e.detail.data)
dc.channel('data').publish({
bluetooth: this,
serviceId,
characteristicId: data.characteristicId,
data: e.detail.data
})
}
})
window.addEventListener('bluetooth', e => {
if (typeof e.detail !== 'object') return
const { err, data } = e.detail
if (err) {
return this.emit('error', err)
}
this.emit(data.event, data)
dc.channel('event').publish({
...data,
bluetooth: this,
event: data.event
})
})
dc.channel('handle').publish({ bluetooth: this })
}
/**
* Start the Bluetooth service.
* @return {Promise<ipc.Result>}
*
*/
async start () {
const result = await ipc.send('bluetooth.start', { serviceId: this.serviceId })
if (result.err) {
throw result.err
}
dc.channel('start').publish({
bluetooth: this,
serviceId: this.serviceId
})
}
/**
* Start scanning for published values that correspond to a well-known UUID.
* Once subscribed to a UUID, events that correspond to that UUID will be
* emitted. To receive these events you can add an event listener, for example...
*
* ```js
* const ble = new Bluetooth(id)
* ble.subscribe(uuid)
* ble.on(uuid, (data, details) => {
* // ...do something interesting
* })
* ```
*
* @param {string} [id = ''] - A well-known UUID
* @return {Promise<ipc.Result>}
*/
async subscribe (id = '') {
const result = await ipc.send('bluetooth.subscribe', {
characteristicId: id,
serviceId: this.serviceId
})
if (result.err) {
throw result.err
}
dc.channel('subscribe').publish({
bluetooth: this,
serviceId: this.serviceId,
characteristicId: id
})
}
/**
* Start advertising a new value for a well-known UUID
* @param {string} [id=''] - A well-known UUID
* @param {string} [value='']
* @return {Promise<void>}
*/
async publish (id = '', value = '') {
if (!id || id.length !== 36) {
throw new Error('expected id of length 36')
}
const params = {
characteristicId: id,
serviceId: this.serviceId
}
if (!(value instanceof ArrayBuffer) && typeof value === 'object') {
value = JSON.stringify(value)
}
if (typeof value === 'string') {
const enc = new TextEncoder().encode(value)
value = enc
params.length = enc.length
}
const result = await ipc.write('bluetooth.publish', params, value)
if (result.err) {
throw result.err
}
dc.channel('publish').publish({
bluetooth: this,
serviceId: this.serviceId,
characteristicId: id,
data: value
})
}
}