bluetooth/adapter_darwin.go
Ron Evans 5f44bb4a96 macos: completed initial implementation
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-09-02 08:37:04 +02:00

141 lines
3.9 KiB
Go

package bluetooth
import (
"errors"
"time"
"github.com/JuulLabs-OSS/cbgo"
)
// Adapter is a connection to BLE devices.
type Adapter struct {
cbgo.CentralManagerDelegateBase
cbgo.PeripheralManagerDelegateBase
cm cbgo.CentralManager
pm cbgo.PeripheralManager
peripheralFoundHandler func(*Adapter, ScanResult)
scanChan chan error
poweredChan chan error
connectChan chan cbgo.Peripheral
}
// DefaultAdapter is the default adapter on the system.
//
// Make sure to call Enable() before using it to initialize the adapter.
var DefaultAdapter = &Adapter{
cm: cbgo.NewCentralManager(nil),
pm: cbgo.NewPeripheralManager(nil),
connectChan: make(chan cbgo.Peripheral),
}
// Enable configures the BLE stack. It must be called before any
// Bluetooth-related calls (unless otherwise indicated).
func (a *Adapter) Enable() error {
if a.poweredChan != nil {
return errors.New("already calling Enable function")
}
// wait until powered
a.poweredChan = make(chan error)
a.cm.SetDelegate(a)
select {
case <-a.poweredChan:
case <-time.NewTimer(10 * time.Second).C:
return errors.New("timeout enabling CentralManager")
}
a.poweredChan = nil
// wait until powered?
//a.pm.SetDelegate(a)
return nil
}
// CentralManager delegate functions
// CentralManagerDidUpdateState when central manager state updated.
func (a *Adapter) CentralManagerDidUpdateState(cmgr cbgo.CentralManager) {
// powered on?
if cmgr.State() == cbgo.ManagerStatePoweredOn {
close(a.poweredChan)
}
// TODO: handle other state changes.
}
// DidDiscoverPeripheral when peripheral is discovered.
func (a *Adapter) DidDiscoverPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral,
advFields cbgo.AdvFields, rssi int) {
if a.peripheralFoundHandler != nil {
sr := makeScanResult(prph, advFields, rssi)
a.peripheralFoundHandler(a, sr)
}
}
// DidConnectPeripheral when peripheral is connected.
func (a *Adapter) DidConnectPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral) {
// Unblock now that we're connected.
a.connectChan <- prph
}
// DidDisconnectPeripheral when peripheral is disconnected.
func (a *Adapter) DidDisconnectPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral, err error) {
}
// PeripheralManager delegate functions
// PeripheralManagerDidUpdateState when state updated.
func (a *Adapter) PeripheralManagerDidUpdateState(pmgr cbgo.PeripheralManager) {
}
// DidAddService when service added.
func (a *Adapter) DidAddService(pmgr cbgo.PeripheralManager, svc cbgo.Service, err error) {
}
// DidStartAdvertising when advertising starts.
func (a *Adapter) DidStartAdvertising(pmgr cbgo.PeripheralManager, err error) {
}
// DidReceiveReadRequest when read request received.
func (a *Adapter) DidReceiveReadRequest(pmgr cbgo.PeripheralManager, cbreq cbgo.ATTRequest) {
}
// DidReceiveWriteRequests when write requests received.
func (a *Adapter) DidReceiveWriteRequests(pmgr cbgo.PeripheralManager, cbreqs []cbgo.ATTRequest) {
}
// CentralDidSubscribe when central subscribed.
func (a *Adapter) CentralDidSubscribe(pmgr cbgo.PeripheralManager, cent cbgo.Central, cbchr cbgo.Characteristic) {
}
// CentralDidUnsubscribe when central unsubscribed.
func (a *Adapter) CentralDidUnsubscribe(pmgr cbgo.PeripheralManager, cent cbgo.Central, chr cbgo.Characteristic) {
}
// makeScanResult creates a ScanResult when peripheral is found.
func makeScanResult(prph cbgo.Peripheral, advFields cbgo.AdvFields, rssi int) ScanResult {
uuid, _ := ParseUUID(prph.Identifier().String())
var serviceUUIDs []UUID
for _, u := range advFields.ServiceUUIDs {
parsedUUID, _ := ParseUUID(u.String())
serviceUUIDs = append(serviceUUIDs, parsedUUID)
}
// It is never a random address on macOS.
return ScanResult{
RSSI: int16(rssi),
Address: Address{
UUID: uuid,
},
AdvertisementPayload: &advertisementFields{
AdvertisementFields{
LocalName: advFields.LocalName,
ServiceUUIDs: serviceUUIDs,
},
},
}
}