@@ -7,8 +7,11 @@ import (
7
7
"log"
8
8
"time"
9
9
10
- "github.com/currantlabs/ble "
10
+ "github.com/pkg/errors "
11
11
"github.com/raff/goble/xpc"
12
+ "golang.org/x/net/context"
13
+
14
+ "github.com/currantlabs/ble"
12
15
)
13
16
14
17
const (
@@ -41,7 +44,9 @@ const (
41
44
42
45
// Device is either a Peripheral or Central device.
43
46
type Device struct {
44
- xpc xpc.XPC
47
+ pm xpc.XPC // peripheralManager
48
+ cm xpc.XPC // centralManager
49
+
45
50
role int // 1: peripheralManager (server), 0: centralManager (client)
46
51
47
52
rspc chan msg
@@ -70,9 +75,10 @@ func NewDevice(opts ...Option) (*Device, error) {
70
75
return nil , err
71
76
}
72
77
73
- d .xpc = xpc .XpcConnect ("com.apple.blued" , d )
78
+ d .pm = xpc .XpcConnect ("com.apple.blued" , d )
79
+ d .cm = xpc .XpcConnect ("com.apple.blued" , d )
74
80
75
- return d , nil
81
+ return d , errors . Wrap ( d . Init (), "can't init" )
76
82
}
77
83
78
84
// Option sets the options specified.
@@ -86,86 +92,114 @@ func (d *Device) Option(opts ...Option) error {
86
92
87
93
// Init ...
88
94
func (d * Device ) Init () error {
89
- rsp := d .sendReq (1 , xpc.Dict {
95
+ rsp := d .sendReq (d .cm , 1 , xpc.Dict {
96
+ "kCBMsgArgName" : fmt .Sprintf ("gopher-%v" , time .Now ().Unix ()),
97
+ "kCBMsgArgOptions" : xpc.Dict {
98
+ "kCBInitOptionShowPowerAlert" : 1 ,
99
+ },
100
+ "kCBMsgArgType" : 0 ,
101
+ })
102
+ s := State (rsp .state ())
103
+ if s != StatePoweredOn {
104
+ return fmt .Errorf ("state: %s" , s )
105
+ }
106
+
107
+ rsp = d .sendReq (d .pm , 1 , xpc.Dict {
90
108
"kCBMsgArgName" : fmt .Sprintf ("gopher-%v" , time .Now ().Unix ()),
91
109
"kCBMsgArgOptions" : xpc.Dict {
92
110
"kCBInitOptionShowPowerAlert" : 1 ,
93
111
},
94
- "kCBMsgArgType" : d . role ,
112
+ "kCBMsgArgType" : 1 ,
95
113
})
96
- if s := State (rsp .state ()); s != StatePoweredOn {
114
+ s = State (rsp .state ())
115
+ if s != StatePoweredOn {
97
116
return fmt .Errorf ("state: %s" , s )
98
117
}
99
118
return nil
100
119
}
101
120
102
121
// AdvertiseMfgData ...
103
- func (d * Device ) AdvertiseMfgData (b []byte ) error {
104
- return d .sendReq (8 , xpc.Dict {
122
+ func (d * Device ) AdvertiseMfgData (ctx context. Context , b []byte ) error {
123
+ if err := d .sendReq (d . pm , 8 , xpc.Dict {
105
124
"kCBAdvDataAppleMfgData" : b ,
106
- }).err ()
125
+ }).err (); err != nil {
126
+ return errors .Wrap (err , "can't advertise" )
127
+ }
128
+ <- ctx .Done ()
129
+ return ctx .Err ()
107
130
}
108
131
109
132
// AdvertiseNameAndServices advertises name and specifid service UUIDs.
110
- func (d * Device ) AdvertiseNameAndServices (name string , ss ... ble.UUID ) error {
111
- return d .sendReq (8 , xpc.Dict {
133
+ func (d * Device ) AdvertiseNameAndServices (ctx context. Context , name string , ss ... ble.UUID ) error {
134
+ if err := d .sendReq (d . pm , 8 , xpc.Dict {
112
135
"kCBAdvDataLocalName" : name ,
113
136
"kCBAdvDataServiceUUIDs" : uuidSlice (ss )},
114
- ).err ()
137
+ ).err (); err != nil {
138
+ return err
139
+ }
140
+ <- ctx .Done ()
141
+ d .stopAdvertising ()
142
+ return ctx .Err ()
115
143
}
116
144
117
145
// AdvertiseIBeaconData advertises iBeacon packet with specified manufacturer data.
118
- func (d * Device ) AdvertiseIBeaconData (md []byte ) error {
146
+ func (d * Device ) AdvertiseIBeaconData (ctx context. Context , md []byte ) error {
119
147
var utsname xpc.Utsname
120
148
xpc .Uname (& utsname )
121
149
122
150
if utsname .Release >= "14." {
123
151
l := len (md )
124
152
b := []byte {byte (l + 5 ), 0xFF , 0x4C , 0x00 , 0x02 , byte (l )}
125
- return d .AdvertiseMfgData (append (b , md ... ))
153
+ return d .AdvertiseMfgData (ctx , append (b , md ... ))
126
154
}
127
- return d .sendReq (8 , xpc.Dict {"kCBAdvDataAppleBeaconKey" : md }).err ()
155
+ if err := d .sendReq (d .pm , 8 , xpc.Dict {"kCBAdvDataAppleBeaconKey" : md }).err (); err != nil {
156
+ return err
157
+ }
158
+ <- ctx .Done ()
159
+ return d .stopAdvertising ()
128
160
}
129
161
130
162
// AdvertiseIBeacon advertises iBeacon packet.
131
- func (d * Device ) AdvertiseIBeacon (u ble.UUID , major , minor uint16 , pwr int8 ) error {
163
+ func (d * Device ) AdvertiseIBeacon (ctx context. Context , u ble.UUID , major , minor uint16 , pwr int8 ) error {
132
164
b := make ([]byte , 21 )
133
165
copy (b , ble .Reverse (u )) // Big endian
134
166
binary .BigEndian .PutUint16 (b [16 :], major ) // Big endian
135
167
binary .BigEndian .PutUint16 (b [18 :], minor ) // Big endian
136
168
b [20 ] = uint8 (pwr ) // Measured Tx Power
137
- return d .AdvertiseIBeaconData (b )
138
- }
139
-
140
- // StopAdvertising stops advertising.
141
- func (d * Device ) StopAdvertising () error {
142
- return d .sendReq (9 , nil ).err ()
169
+ return d .AdvertiseIBeaconData (ctx , b )
143
170
}
144
171
145
- // SetAdvHandler ...
146
- func (d * Device ) SetAdvHandler (ah ble.AdvHandler ) error {
147
- d .advHandler = ah
148
- return nil
172
+ // stopAdvertising stops advertising.
173
+ func (d * Device ) stopAdvertising () error {
174
+ return errors .Wrap (d .sendReq (d .pm , 9 , nil ).err (), "can't stop advertising" )
149
175
}
150
176
151
177
// Scan ...
152
- func (d * Device ) Scan (allowDup bool ) error {
153
- return d .sendCmd (29 , xpc.Dict {
178
+ func (d * Device ) Scan (ctx context.Context , allowDup bool , h ble.AdvHandler ) error {
179
+ d .advHandler = h
180
+ if err := d .sendCmd (d .cm , 29 , xpc.Dict {
154
181
// "kCBMsgArgUUIDs": uuidSlice(ss),
155
182
"kCBMsgArgOptions" : xpc.Dict {
156
183
"kCBScanOptionAllowDuplicates" : map [bool ]int {true : 1 , false : 0 }[allowDup ],
157
184
},
158
- })
185
+ }); err != nil {
186
+ return err
187
+ }
188
+ <- ctx .Done ()
189
+ if err := d .stopScanning (); err != nil {
190
+ return errors .Wrap (ctx .Err (), err .Error ())
191
+ }
192
+ return ctx .Err ()
159
193
}
160
194
161
- // StopScanning stops scanning
162
- func (d * Device ) StopScanning () error {
163
- return d .sendCmd (30 , nil )
195
+ // stopAdvertising stops advertising.
196
+ func (d * Device ) stopScanning () error {
197
+ return errors . Wrap ( d .sendCmd (d . cm , 30 , nil ), "can't stop scanning" )
164
198
}
165
199
166
200
// RemoveAllServices removes all services of device's
167
201
func (d * Device ) RemoveAllServices () error {
168
- return d .sendCmd (12 , nil )
202
+ return d .sendCmd (d . pm , 12 , nil )
169
203
}
170
204
171
205
// AddService adds a service to device's database.
@@ -266,7 +300,7 @@ func (d *Device) AddService(s *ble.Service) error {
266
300
}
267
301
xs ["kCBMsgArgCharacteristics" ] = xcs
268
302
269
- return d .sendReq (10 , xs ).err ()
303
+ return d .sendReq (d . pm , 10 , xs ).err ()
270
304
}
271
305
272
306
// SetServices ...
@@ -283,14 +317,24 @@ func (d *Device) SetServices(ss []*ble.Service) error {
283
317
}
284
318
285
319
// Dial ...
286
- func (d * Device ) Dial (a ble.Addr ) (ble.Client , error ) {
287
- d .sendCmd (31 , xpc.Dict {
320
+ func (d * Device ) Dial (ctx context. Context , a ble.Addr ) (ble.Client , error ) {
321
+ d .sendCmd (d . cm , 31 , xpc.Dict {
288
322
"kCBMsgArgDeviceUUID" : xpc .MakeUUID (a .String ()),
289
323
"kCBMsgArgOptions" : xpc.Dict {
290
324
"kCBConnectOptionNotifyOnDisconnection" : 1 ,
291
325
},
292
326
})
293
- return NewClient (<- d .chConn )
327
+ select {
328
+ case <- ctx .Done ():
329
+ return nil , ctx .Err ()
330
+ case c := <- d .chConn :
331
+ return NewClient (c )
332
+ }
333
+ }
334
+
335
+ // Stop ...
336
+ func (d * Device ) Stop () error {
337
+ return nil
294
338
}
295
339
296
340
// HandleXpcEvent process Device events and asynchronous errors.
@@ -316,7 +360,7 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) {
316
360
break
317
361
}
318
362
a := & adv {args : m .args (), ad : args .advertisementData ()}
319
- go d .advHandler . Handle (a )
363
+ go d .advHandler (a )
320
364
321
365
case evtConfirmation :
322
366
// log.Printf("confirmed: %d", args.attributeID())
@@ -349,7 +393,7 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) {
349
393
v = buf .Bytes ()
350
394
}
351
395
352
- d .sendCmd (13 , xpc.Dict {
396
+ d .sendCmd (d . pm , 13 , xpc.Dict {
353
397
"kCBMsgArgAttributeID" : aid ,
354
398
"kCBMsgArgData" : v ,
355
399
"kCBMsgArgTransactionID" : args .transactionID (),
@@ -366,7 +410,7 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) {
366
410
if xw .ignoreResponse () == 1 {
367
411
continue
368
412
}
369
- d .sendCmd (13 , xpc.Dict {
413
+ d .sendCmd (d . pm , 13 , xpc.Dict {
370
414
"kCBMsgArgAttributeID" : aid ,
371
415
"kCBMsgArgData" : nil ,
372
416
"kCBMsgArgTransactionID" : args .transactionID (),
@@ -422,16 +466,6 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) {
422
466
}
423
467
}
424
468
425
- // Addr ...
426
- func (d * Device ) Addr () ble.Addr {
427
- return nil
428
- }
429
-
430
- // Stop ...
431
- func (d * Device ) Stop () error {
432
- return nil
433
- }
434
-
435
469
func (d * Device ) conn (m msg ) * conn {
436
470
// Convert xpc.UUID to ble.UUID.
437
471
a := ble .MustParse (m .deviceUUID ().String ())
@@ -444,13 +478,13 @@ func (d *Device) conn(m msg) *conn {
444
478
}
445
479
446
480
// sendReq sends a message and waits for its reply.
447
- func (d * Device ) sendReq (id int , args xpc.Dict ) msg {
448
- d .sendCmd (id , args )
481
+ func (d * Device ) sendReq (x xpc. XPC , id int , args xpc.Dict ) msg {
482
+ d .sendCmd (x , id , args )
449
483
return <- d .rspc
450
484
}
451
485
452
- func (d * Device ) sendCmd (id int , args xpc.Dict ) error {
486
+ func (d * Device ) sendCmd (x xpc. XPC , id int , args xpc.Dict ) error {
453
487
logger .Info ("send" , "id" , id , "args" , fmt .Sprintf ("%v" , args ))
454
- d . xpc .Send (xpc.Dict {"kCBMsgId" : id , "kCBMsgArgs" : args }, false )
488
+ x .Send (xpc.Dict {"kCBMsgId" : id , "kCBMsgArgs" : args }, false )
455
489
return nil
456
490
}
0 commit comments