diff --git a/.circleci/config.yml b/.circleci/config.yml index bf67440e..058d8c85 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,4 @@ -version: 2 +version: 2.1 jobs: build: @@ -22,3 +22,24 @@ jobs: - run: name: "Run Windows smoke tests" command: make smoketest-windows + build-macos: + macos: + xcode: "10.1.0" + steps: + - checkout + - run: + name: "Install dependencies" + command: | + curl https://dl.google.com/go/go1.14.darwin-amd64.tar.gz -o go1.14.darwin-amd64.tar.gz + sudo tar -C /usr/local -xzf go1.14.darwin-amd64.tar.gz + ln -s /usr/local/go/bin/go /usr/local/bin/go + - run: go version + - run: + name: "Run macOS smoke tests" + command: make smoketest-macos + +workflows: + test-all: + jobs: + - build + - build-macos diff --git a/Makefile b/Makefile index 9cde3886..0315e015 100644 --- a/Makefile +++ b/Makefile @@ -35,3 +35,7 @@ smoketest-linux: smoketest-windows: # Test on Windows. GOOS=windows CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc go build -o /tmp/go-build-discard ./examples/scanner + +smoketest-macos: + # Test on macos. + GOOS=darwin CGO_ENABLED=1 go build -o /tmp/go-build-discard ./examples/scanner diff --git a/README.md b/README.md index 47b4b0ba..643d9dca 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,21 @@ # Go Bluetooth -[![CircleCI](https://circleci.com/gh/tinygo-org/bluetooth/tree/master.svg?style=svg)](https://circleci.com/gh/tinygo-org/bluetooth/tree/master) +[![CircleCI](https://circleci.com/gh/tinygo-org/bluetooth/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/bluetooth/tree/dev) [![GoDoc](https://godoc.org/github.com/tinygo-org/bluetooth?status.svg)](https://godoc.org/github.com/tinygo-org/bluetooth) This package attempts to build a cross-platform Bluetooth Low Energy module for Go. It currently supports the following systems: -| | Windows | Linux | Nordic chips | -| -------------------------------- | ------------------ | ------------------ | ------------------ | -| API used | WinRT | BlueZ (over D-Bus) | SoftDevice | -| Scanning | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| Connect to peripheral | :x: | :heavy_check_mark: | :heavy_check_mark: | -| Write peripheral characteristics | :x: | :heavy_check_mark: | :heavy_check_mark: | -| Receive notifications | :x: | :heavy_check_mark: | :heavy_check_mark: | -| Advertisement | :x: | :heavy_check_mark: | :heavy_check_mark: | -| Local services | :x: | :heavy_check_mark: | :heavy_check_mark: | -| Local characteristics | :x: | :heavy_check_mark: | :heavy_check_mark: | -| Send notifications | :x: | :heavy_check_mark: | :heavy_check_mark: | +| | Windows | Linux | Nordic chips | macOS | +| -------------------------------- | ------------------ | ------------------ | ------------------ | ------------------ | +| API used | WinRT | BlueZ (over D-Bus) | SoftDevice | CoreBluetooth | +| Scanning | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Connect to peripheral | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Write peripheral characteristics | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Receive notifications | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Advertisement | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: | +| Local services | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: | +| Local characteristics | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: | +| Send notifications | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: | ## Baremetal support @@ -52,7 +52,7 @@ Flashing will then need to be done a bit differently, using the CMSIS-DAP interf ## API stability -**The API is not stable!** Because many features are not yet implemented and some platforms (e.g. MacOS) are not yet supported, it's hard to say what a good API will be. Therefore, if you want stability you should pick a particular git commit and use that. Go modules can be useful for this purpose. +**The API is not stable!** Because many features are not yet implemented and some platforms (e.g. Windows and macOS) are not yet fully supported, it's hard to say what a good API will be. Therefore, if you want stability you should pick a particular git commit and use that. Go modules can be useful for this purpose. Some things that will probably change: diff --git a/adapter_darwin.go b/adapter_darwin.go new file mode 100644 index 00000000..a19ce03d --- /dev/null +++ b/adapter_darwin.go @@ -0,0 +1,124 @@ +package bluetooth + +import ( + "errors" + "time" + + "github.com/JuulLabs-OSS/cbgo" +) + +// Adapter is a connection to BLE devices. +type Adapter struct { + cmd *centralManagerDelegate + pmd *peripheralManagerDelegate + + 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.cmd = ¢ralManagerDelegate{a: a} + a.cm.SetDelegate(a.cmd) + select { + case <-a.poweredChan: + case <-time.NewTimer(10 * time.Second).C: + return errors.New("timeout enabling CentralManager") + } + a.poweredChan = nil + + // wait until powered? + a.pmd = &peripheralManagerDelegate{a: a} + a.pm.SetDelegate(a.pmd) + + return nil +} + +// CentralManager delegate functions + +type centralManagerDelegate struct { + cbgo.CentralManagerDelegateBase + + a *Adapter +} + +// CentralManagerDidUpdateState when central manager state updated. +func (cmd *centralManagerDelegate) CentralManagerDidUpdateState(cmgr cbgo.CentralManager) { + // powered on? + if cmgr.State() == cbgo.ManagerStatePoweredOn { + close(cmd.a.poweredChan) + } + + // TODO: handle other state changes. +} + +// DidDiscoverPeripheral when peripheral is discovered. +func (cmd *centralManagerDelegate) DidDiscoverPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral, + advFields cbgo.AdvFields, rssi int) { + if cmd.a.peripheralFoundHandler != nil { + sr := makeScanResult(prph, advFields, rssi) + cmd.a.peripheralFoundHandler(cmd.a, sr) + } +} + +// DidConnectPeripheral when peripheral is connected. +func (cmd *centralManagerDelegate) DidConnectPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral) { + // Unblock now that we're connected. + cmd.a.connectChan <- prph +} + +// 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) + } + + // Peripheral UUID is randomized on macOS, which means to + // different centrals it will appear to have a different UUID. + return ScanResult{ + RSSI: int16(rssi), + Address: Address{ + UUID: uuid, + }, + AdvertisementPayload: &advertisementFields{ + AdvertisementFields{ + LocalName: advFields.LocalName, + ServiceUUIDs: serviceUUIDs, + }, + }, + } +} + +// PeripheralManager delegate functions + +type peripheralManagerDelegate struct { + cbgo.PeripheralManagerDelegateBase + + a *Adapter +} diff --git a/adapter_nrf528xx.go b/adapter_nrf528xx.go index 9fd3a30d..c7a8602f 100644 --- a/adapter_nrf528xx.go +++ b/adapter_nrf528xx.go @@ -102,8 +102,8 @@ func handleEvent() { // callback. scanReportBuffer.len = byte(advReport.data.len) globalScanResult.RSSI = int16(advReport.rssi) - globalScanResult.Address.MAC = advReport.peer_addr.addr - globalScanResult.Address.IsRandom = advReport.peer_addr.bitfield_addr_type() != 0 + globalScanResult.Address.Set(advReport.peer_addr.addr) + globalScanResult.Address.SetRandom(advReport.peer_addr.bitfield_addr_type() != 0) globalScanResult.AdvertisementPayload = &scanReportBuffer // Signal to the main thread that there was a scan report. // Scanning will be resumed (from the main thread) once the scan diff --git a/gap.go b/gap.go index bd15c318..8f1adc15 100644 --- a/gap.go +++ b/gap.go @@ -11,6 +11,30 @@ var ( errAdvertisementPacketTooBig = errors.New("bluetooth: advertisement packet overflows") ) +// MACAddress contains a Bluetooth address which is a MAC address. +type MACAddress struct { + // MAC address of the Bluetooth device. + MAC + + isRandom bool +} + +// IsRandom if the address is randomly created. +func (mac MACAddress) IsRandom() bool { + return mac.isRandom +} + +// SetRandom if is a random address. +func (mac MACAddress) SetRandom(val bool) { + mac.isRandom = val +} + +// Set the address +func (mac MACAddress) Set(val interface{}) { + m := val.(MAC) + mac.MAC = m +} + // AdvertisementOptions configures an advertisement instance. More options may // be added over time. type AdvertisementOptions struct { @@ -41,11 +65,14 @@ func NewDuration(interval time.Duration) Duration { // Connection is a numeric identifier that indicates a connection handle. type Connection uint16 -// Address contains a Bluetooth address, which is a MAC address plus some extra +// Addresser contains a Bluetooth address, which is a MAC address plus some extra // information. -type Address struct { - // The MAC address of a Bluetooth device. - MAC +type Addresser interface { + // String of the address + String() string + + // Set the address + Set(val interface{}) // Is this address a random address? // Bluetooth addresses are roughly split in two kinds: public @@ -54,14 +81,16 @@ type Address struct { // random. Sometimes, it contains a hash. // For more information: // https://www.novelbits.io/bluetooth-address-privacy-ble/ - IsRandom bool + // Set the address + SetRandom(bool) + IsRandom() bool } // ScanResult contains information from when an advertisement packet was // received. It is passed as a parameter to the callback of the Scan method. type ScanResult struct { // Bluetooth address of the scanned device. - Address Address + Address Addresser // RSSI the last time a packet from this device has been received. RSSI int16 diff --git a/gap_darwin.go b/gap_darwin.go new file mode 100644 index 00000000..46413c91 --- /dev/null +++ b/gap_darwin.go @@ -0,0 +1,154 @@ +package bluetooth + +import ( + "errors" + "fmt" + "time" + + "github.com/JuulLabs-OSS/cbgo" +) + +// Address contains a Bluetooth address which on macOS is a UUID. +type Address struct { + // UUID since this is macOS. + UUID +} + +// IsRandom ignored on macOS. +func (ad Address) IsRandom() bool { + return false +} + +// SetRandom ignored on macOS. +func (ad Address) SetRandom(val bool) { +} + +// Set the address +func (ad Address) Set(val interface{}) { + ad.UUID = val.(UUID) +} + +// Scan starts a BLE scan. It is stopped by a call to StopScan. A common pattern +// is to cancel the scan when a particular device has been found. +func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) (err error) { + if callback == nil { + return errors.New("must provide callback to Scan function") + } + + if a.scanChan != nil { + return errors.New("already calling Scan function") + } + + a.peripheralFoundHandler = callback + + // Channel that will be closed when the scan is stopped. + // Detecting whether the scan is stopped can be done by doing a non-blocking + // read from it. If it succeeds, the scan is stopped. + a.scanChan = make(chan error) + + a.cm.Scan(nil, &cbgo.CentralManagerScanOpts{ + AllowDuplicates: false, + }) + + // Check whether the scan is stopped. This is necessary to avoid a race + // condition between the signal channel and the cancelScan channel when + // the callback calls StopScan() (no new callbacks may be called after + // StopScan is called). + select { + case <-a.scanChan: + close(a.scanChan) + a.scanChan = nil + return nil + } +} + +// StopScan stops any in-progress scan. It can be called from within a Scan +// callback to stop the current scan. If no scan is in progress, an error will +// be returned. +func (a *Adapter) StopScan() error { + if a.scanChan == nil { + return errors.New("not calling Scan function") + } + + a.scanChan <- nil + a.cm.StopScan() + + return nil +} + +// Device is a connection to a remote peripheral. +type Device struct { + delegate *peripheralDelegate + + cm cbgo.CentralManager + prph cbgo.Peripheral + + servicesChan chan error + charsChan chan error + + services map[UUID]*DeviceService + characteristics map[UUID]*DeviceCharacteristic +} + +// Connect starts a connection attempt to the given peripheral device address. +func (a *Adapter) Connect(address Addresser, params ConnectionParams) (*Device, error) { + adr := address.(Address) + uuid, err := cbgo.ParseUUID(adr.UUID.String()) + if err != nil { + return nil, err + } + prphs := a.cm.RetrievePeripheralsWithIdentifiers([]cbgo.UUID{uuid}) + if len(prphs) == 0 { + return nil, fmt.Errorf("Connect failed: no peer with address: %s", adr.UUID.String()) + } + a.cm.Connect(prphs[0], nil) + + // wait on channel for connect + select { + case p := <-a.connectChan: + d := &Device{ + cm: a.cm, + prph: p, + servicesChan: make(chan error), + charsChan: make(chan error), + } + + d.delegate = &peripheralDelegate{d: d} + p.SetDelegate(d.delegate) + + return d, nil + case <-time.NewTimer(10 * time.Second).C: + return nil, errors.New("timeout on Connect") + } +} + +// Peripheral delegate functions + +type peripheralDelegate struct { + cbgo.PeripheralDelegateBase + + d *Device +} + +// DidDiscoverServices is called when the services for a Peripheral +// have been discovered. +func (pd *peripheralDelegate) DidDiscoverServices(prph cbgo.Peripheral, err error) { + pd.d.servicesChan <- nil +} + +// DidDiscoverCharacteristics is called when the characteristics for a Service +// for a Peripheral have been discovered. +func (pd *peripheralDelegate) DidDiscoverCharacteristics(prph cbgo.Peripheral, svc cbgo.Service, err error) { + pd.d.charsChan <- nil +} + +// DidUpdateValueForCharacteristic is called when the characteristic for a Service +// for a Peripheral receives a notification with a new value. +func (pd *peripheralDelegate) DidUpdateValueForCharacteristic(prph cbgo.Peripheral, chr cbgo.Characteristic, err error) { + uuid, _ := ParseUUID(chr.UUID().String()) + if char, ok := pd.d.characteristics[uuid]; ok { + if char != nil && char.callback != nil { + go char.callback(chr.Value()) + } + } +} diff --git a/gap_linux.go b/gap_linux.go index e3b73116..f1359f2d 100644 --- a/gap_linux.go +++ b/gap_linux.go @@ -11,6 +11,11 @@ import ( "github.com/muka/go-bluetooth/bluez/profile/device" ) +// Address contains a Bluetooth MAC address. +type Address struct { + MACAddress +} + // Advertisement encapsulates a single advertisement instance. type Advertisement struct { adapter *Adapter @@ -214,12 +219,12 @@ func makeScanResult(props *device.Device1Properties) ScanResult { serviceUUIDs = append(serviceUUIDs, parsedUUID) } + a := &Address{MACAddress{MAC: addr}} + a.SetRandom(props.AddressType == "random") + return ScanResult{ - RSSI: props.RSSI, - Address: Address{ - MAC: addr, - IsRandom: props.AddressType == "random", - }, + RSSI: props.RSSI, + Address: a, AdvertisementPayload: &advertisementFields{ AdvertisementFields{ LocalName: props.Name, @@ -237,8 +242,9 @@ type Device struct { // Connect starts a connection attempt to the given peripheral device address. // // On Linux and Windows, the IsRandom part of the address is ignored. -func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, error) { - devicePath := dbus.ObjectPath(string(a.adapter.Path()) + "/dev_" + strings.Replace(address.MAC.String(), ":", "_", -1)) +func (a *Adapter) Connect(address Addresser, params ConnectionParams) (*Device, error) { + adr := address.(Address) + devicePath := dbus.ObjectPath(string(a.adapter.Path()) + "/dev_" + strings.Replace(adr.MAC.String(), ":", "_", -1)) dev, err := device.NewDevice1(devicePath) if err != nil { return nil, err diff --git a/gap_nrf51.go b/gap_nrf51.go index d6b1bbf3..86da013a 100644 --- a/gap_nrf51.go +++ b/gap_nrf51.go @@ -16,6 +16,11 @@ import ( "time" ) +// Address contains a Bluetooth MAC address. +type Address struct { + MACAddress +} + // Advertisement encapsulates a single advertisement instance. type Advertisement struct { interval Duration diff --git a/gap_nrf528xx.go b/gap_nrf528xx.go index 8a9c373d..b2a89338 100644 --- a/gap_nrf528xx.go +++ b/gap_nrf528xx.go @@ -27,6 +27,11 @@ var ( globalScanResult ScanResult ) +// Address contains a Bluetooth MAC address. +type Address struct { + MACAddress +} + // Advertisement encapsulates a single advertisement instance. type Advertisement struct { handle uint8 @@ -169,12 +174,13 @@ var connectionAttempt struct { // connection attempt at once and that the address parameter must have the // IsRandom bit set correctly. This bit is set correctly for scan results, so // you can reuse that address directly. -func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, error) { +func (a *Adapter) Connect(address Addresser, params ConnectionParams) (*Device, error) { + adr := address.(Address) // Construct an address object as used in the SoftDevice. var addr C.ble_gap_addr_t - addr.addr = address.MAC - if address.IsRandom { - switch address.MAC[5] >> 6 { + addr.addr = adr.MAC + if address.IsRandom() { + switch adr.MAC[5] >> 6 { case 0b11: addr.set_bitfield_addr_type(C.BLE_GAP_ADDR_TYPE_RANDOM_STATIC) case 0b01: diff --git a/gap_windows.go b/gap_windows.go index 4322bd11..dda3fbff 100644 --- a/gap_windows.go +++ b/gap_windows.go @@ -4,6 +4,11 @@ import ( "github.com/tinygo-org/bluetooth/winbt" ) +// Address contains a Bluetooth MAC address. +type Address struct { + MACAddress +} + // Scan starts a BLE scan. It is stopped by a call to StopScan. A common pattern // is to cancel the scan when a particular device has been found. func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) (err error) { @@ -24,8 +29,9 @@ func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) (err error) { var result ScanResult result.RSSI = args.RawSignalStrengthInDBm() addr := args.BluetoothAddress() - for i := range result.Address.MAC { - result.Address.MAC[i] = byte(addr) + adr := result.Address.(Address) + for i := range adr.MAC { + adr.MAC[i] = byte(addr) addr >>= 8 } // Note: the IsRandom bit is never set. diff --git a/gattc_darwin.go b/gattc_darwin.go new file mode 100644 index 00000000..6beba58c --- /dev/null +++ b/gattc_darwin.go @@ -0,0 +1,127 @@ +package bluetooth + +import ( + "errors" + "time" + + "github.com/JuulLabs-OSS/cbgo" +) + +// DiscoverServices starts a service discovery procedure. Pass a list of service +// UUIDs you are interested in to this function. Either a slice of all services +// is returned (of the same length as the requested UUIDs and in the same +// order), or if some services could not be discovered an error is returned. +func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) { + cbuuids := []cbgo.UUID{} + for _, u := range uuids { + uuid, _ := cbgo.ParseUUID(u.String()) + cbuuids = append(cbuuids, uuid) + } + + d.prph.DiscoverServices(cbuuids) + + // clear cache of services + d.services = make(map[UUID]*DeviceService) + + // wait on channel for service discovery + select { + case <-d.servicesChan: + svcs := []DeviceService{} + for _, dsvc := range d.prph.Services() { + uuid, _ := ParseUUID(dsvc.UUID().String()) + svc := DeviceService{ + UUID: uuid, + device: d, + service: dsvc, + } + svcs = append(svcs, svc) + d.services[svc.UUID] = &svc + } + return svcs, nil + case <-time.NewTimer(10 * time.Second).C: + return nil, errors.New("timeout on DiscoverServices") + } +} + +// DeviceService is a BLE service on a connected peripheral device. +type DeviceService struct { + UUID + + device *Device + + service cbgo.Service +} + +// DiscoverCharacteristics discovers characteristics in this service. Pass a +// list of characteristic UUIDs you are interested in to this function. Either a +// list of all requested services is returned, or if some services could not be +// discovered an error is returned. If there is no error, the characteristics +// slice has the same length as the UUID slice with characteristics in the same +// order in the slice as in the requested UUID list. +func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteristic, error) { + cbuuids := []cbgo.UUID{} + for _, u := range uuids { + uuid, _ := cbgo.ParseUUID(u.String()) + cbuuids = append(cbuuids, uuid) + } + + s.device.prph.DiscoverCharacteristics(cbuuids, s.service) + + // clear cache of characteristics + s.device.characteristics = make(map[UUID]*DeviceCharacteristic) + + // wait on channel for characteristic discovery + select { + case <-s.device.charsChan: + chars := []DeviceCharacteristic{} + for _, dchar := range s.service.Characteristics() { + uuid, _ := ParseUUID(dchar.UUID().String()) + char := DeviceCharacteristic{ + UUID: uuid, + service: s, + characteristic: dchar, + } + chars = append(chars, char) + s.device.characteristics[char.UUID] = &char + } + return chars, nil + case <-time.NewTimer(10 * time.Second).C: + return nil, errors.New("timeout on DiscoverCharacteristics") + } +} + +// DeviceCharacteristic is a BLE characteristic on a connected peripheral +// device. +type DeviceCharacteristic struct { + UUID + + service *DeviceService + + characteristic cbgo.Characteristic + callback func(buf []byte) +} + +// WriteWithoutResponse replaces the characteristic value with a new value. The +// call will return before all data has been written. A limited number of such +// writes can be in flight at any given time. This call is also known as a +// "write command" (as opposed to a write request). +func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error) { + c.service.device.prph.WriteCharacteristic(p, c.characteristic, false) + + return len(p), nil +} + +// EnableNotifications enables notifications in the Client Characteristic +// Configuration Descriptor (CCCD). This means that most peripherals will send a +// notification with a new value every time the value of the characteristic +// changes. +func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error { + if callback == nil { + return errors.New("must provide a callback for EnableNotifications") + } + + c.callback = callback + c.service.device.prph.SetNotify(true, c.characteristic) + + return nil +} diff --git a/go.mod b/go.mod index cbd04ec6..4fdf6bae 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,11 @@ module github.com/tinygo-org/bluetooth go 1.14 require ( + github.com/JuulLabs-OSS/cbgo v0.0.2 github.com/go-ole/go-ole v1.2.4 github.com/godbus/dbus/v5 v5.0.3 github.com/muka/go-bluetooth v0.0.0-20200619025933-f6113f7141c5 + github.com/sirupsen/logrus v1.6.0 // indirect golang.org/x/crypto v0.0.0-20200602180216-279210d13fed + golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 // indirect ) diff --git a/go.sum b/go.sum index 018679d7..26739ae8 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/JuulLabs-OSS/cbgo v0.0.2 h1:gCDyT0+EPuI8GOFyvAksFcVD2vF4CXBAVwT6uVnD9oo= +github.com/JuulLabs-OSS/cbgo v0.0.2/go.mod h1:L4YtGP+gnyD84w7+jN66ncspFRfOYB5aj9QSXaFHmBA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -8,7 +10,10 @@ github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -19,6 +24,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -32,6 +39,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=