Rewrite connect/reconnect code
This commit is contained in:
parent
2342b0d82a
commit
b6ba971d50
8
go.mod
8
go.mod
@ -3,8 +3,8 @@ module go.arsenm.dev/infinitime
|
|||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fxamacker/cbor/v2 v2.3.0
|
github.com/fxamacker/cbor/v2 v2.4.0
|
||||||
github.com/godbus/dbus/v5 v5.0.3
|
github.com/godbus/dbus/v5 v5.0.6
|
||||||
github.com/muka/go-bluetooth v0.0.0-20211122080231-b99792bbe62a
|
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
|
||||||
)
|
)
|
||||||
|
10
go.sum
10
go.sum
@ -5,15 +5,19 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
|||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
github.com/fxamacker/cbor/v2 v2.3.0 h1:aM45YGMctNakddNNAezPxDUpv38j44Abh+hifNuqXik=
|
github.com/fxamacker/cbor/v2 v2.3.0 h1:aM45YGMctNakddNNAezPxDUpv38j44Abh+hifNuqXik=
|
||||||
github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||||
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
|
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/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
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/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
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=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/muka/go-bluetooth v0.0.0-20211122080231-b99792bbe62a h1:KxRXeSWoBM5FCPAnSUYxt1qwEzmoH/K7upb4fiSDwdc=
|
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a h1:fnzS9RRQW8B5AgNCxkN0vJ/AoX+Xfqk3sAYon3iVrzA=
|
||||||
github.com/muka/go-bluetooth v0.0.0-20211122080231-b99792bbe62a/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
|
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk=
|
github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@ -45,6 +49,8 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
|
||||||
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||||
|
372
infinitime.go
372
infinitime.go
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fxamacker/cbor/v2"
|
"github.com/fxamacker/cbor/v2"
|
||||||
bt "github.com/muka/go-bluetooth/api"
|
bt "github.com/muka/go-bluetooth/api"
|
||||||
|
"github.com/muka/go-bluetooth/bluez"
|
||||||
"github.com/muka/go-bluetooth/bluez/profile/adapter"
|
"github.com/muka/go-bluetooth/bluez/profile/adapter"
|
||||||
"github.com/muka/go-bluetooth/bluez/profile/device"
|
"github.com/muka/go-bluetooth/bluez/profile/device"
|
||||||
"github.com/muka/go-bluetooth/bluez/profile/gatt"
|
"github.com/muka/go-bluetooth/bluez/profile/gatt"
|
||||||
@ -33,7 +34,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
opts *Options
|
|
||||||
device *device.Device1
|
device *device.Device1
|
||||||
newAlertChar *gatt.GattCharacteristic1
|
newAlertChar *gatt.GattCharacteristic1
|
||||||
notifEventChar *gatt.GattCharacteristic1
|
notifEventChar *gatt.GattCharacteristic1
|
||||||
@ -48,7 +48,6 @@ type Device struct {
|
|||||||
weatherDataChar *gatt.GattCharacteristic1
|
weatherDataChar *gatt.GattCharacteristic1
|
||||||
notifEventCh chan uint8
|
notifEventCh chan uint8
|
||||||
notifEventDone bool
|
notifEventDone bool
|
||||||
onReconnect func()
|
|
||||||
Music MusicCtrl
|
Music MusicCtrl
|
||||||
DFU DFU
|
DFU DFU
|
||||||
}
|
}
|
||||||
@ -67,6 +66,7 @@ type Options struct {
|
|||||||
WhitelistEnabled bool
|
WhitelistEnabled bool
|
||||||
Whitelist []string
|
Whitelist []string
|
||||||
OnReqPasskey func() (uint32, error)
|
OnReqPasskey func() (uint32, error)
|
||||||
|
OnReconnect func()
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultOptions = &Options{
|
var DefaultOptions = &Options{
|
||||||
@ -79,207 +79,215 @@ var DefaultOptions = &Options{
|
|||||||
// it will attempt to discover and pair one.
|
// it will attempt to discover and pair one.
|
||||||
//
|
//
|
||||||
// It will also attempt to reconnect to the device
|
// It will also attempt to reconnect to the device
|
||||||
// if it disconnects.
|
// if it disconnects and that is enabled in the options.
|
||||||
func Connect(opts *Options) (*Device, error) {
|
func Connect(opts *Options) (*Device, error) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = DefaultOptions
|
opts = DefaultOptions
|
||||||
}
|
}
|
||||||
// Attempt to connect to paired device by name
|
|
||||||
dev, err := connectByName(opts)
|
// Set passkey request callback
|
||||||
// If such device does not exist
|
|
||||||
if errors.Is(err, ErrNoDevices) {
|
|
||||||
// Attempt to pair device
|
|
||||||
dev, err = pair(opts)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dev.opts = opts
|
|
||||||
dev.onReconnect = func() {}
|
|
||||||
setOnPasskeyReq(opts.OnReqPasskey)
|
setOnPasskeyReq(opts.OnReqPasskey)
|
||||||
|
|
||||||
// Watch device properties
|
// Connect to bluetooth device
|
||||||
devEvtCh, err := dev.device.WatchProperties()
|
btDev, err := connect(opts, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If AttemptReconnect enabled
|
|
||||||
if dev.opts.AttemptReconnect {
|
|
||||||
go func() {
|
|
||||||
disconnEvtNum := 0
|
|
||||||
// For every event
|
|
||||||
for evt := range devEvtCh {
|
|
||||||
// If device disconnected
|
|
||||||
if evt.Name == "Connected" && evt.Value == false {
|
|
||||||
// Increment disconnect event number
|
|
||||||
disconnEvtNum++
|
|
||||||
// If more than one disconnect event
|
|
||||||
if disconnEvtNum > 1 {
|
|
||||||
// Decrement disconnect event number
|
|
||||||
disconnEvtNum--
|
|
||||||
// Skip loop
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Set connected to false
|
|
||||||
dev.device.Properties.Connected = false
|
|
||||||
// While not connected
|
|
||||||
for !dev.device.Properties.Connected {
|
|
||||||
reConnDev := dev
|
|
||||||
|
|
||||||
paired, err := reConnDev.device.GetPaired()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !paired {
|
|
||||||
err = reConnDev.pairTimeout()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Attempt to connect via bluetooth address
|
|
||||||
reConnDev, err = connectByName(opts)
|
|
||||||
if err != nil {
|
|
||||||
// Decrement disconnect event number
|
|
||||||
disconnEvtNum--
|
|
||||||
// Skip rest of loop
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store onReconn callback
|
|
||||||
onReconn := dev.onReconnect
|
|
||||||
// Set device to new device
|
|
||||||
*dev = *reConnDev
|
|
||||||
// Run on reconnect callback
|
|
||||||
onReconn()
|
|
||||||
// Assign callback to new device
|
|
||||||
dev.onReconnect = onReconn
|
|
||||||
}
|
|
||||||
// Decrement disconnect event number
|
|
||||||
disconnEvtNum--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
return dev, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnReconnect sets the callback that runs on reconnect
|
|
||||||
func (i *Device) OnReconnect(f func()) {
|
|
||||||
i.onReconnect = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect connects to a paired InfiniTime device
|
|
||||||
func connectByName(opts *Options) (*Device, error) {
|
|
||||||
setOnPasskeyReq(opts.OnReqPasskey)
|
|
||||||
// Create new device
|
// Create new device
|
||||||
out := &Device{}
|
out := &Device{device: btDev}
|
||||||
// Get devices from default adapter
|
|
||||||
|
// Resolve characteristics
|
||||||
|
err = out.resolveChars()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect connects to the InfiniTime bluez device
|
||||||
|
func connect(opts *Options, first bool) (dev *device.Device1, err error) {
|
||||||
|
// Get devices
|
||||||
devs, err := defaultAdapter.GetDevices()
|
devs, err := defaultAdapter.GetDevices()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// For every device
|
// For every device
|
||||||
for _, dev := range devs {
|
for _, listDev := range devs {
|
||||||
// If device name is InfiniTime
|
// If device name does not match, skip
|
||||||
if dev.Properties.Name == BTName {
|
if listDev.Properties.Name != BTName {
|
||||||
if opts.WhitelistEnabled && !contains(opts.Whitelist, dev.Properties.Address) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Set outout device to discovered device
|
|
||||||
out.device = dev
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if out.device == nil {
|
|
||||||
return nil, ErrNoDevices
|
|
||||||
}
|
|
||||||
// Connect to device
|
|
||||||
err = out.device.Connect()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
out.device.Properties.Connected = true
|
|
||||||
|
|
||||||
// Resolve characteristics
|
|
||||||
err = out.resolveChars()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func contains(ss []string, s string) bool {
|
|
||||||
for _, str := range ss {
|
|
||||||
if strings.EqualFold(str, s) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pair attempts to discover and pair an InfiniTime device
|
|
||||||
func pair(opts *Options) (*Device, error) {
|
|
||||||
setOnPasskeyReq(opts.OnReqPasskey)
|
|
||||||
// Create new device
|
|
||||||
out := &Device{}
|
|
||||||
// Start bluetooth discovery
|
|
||||||
// Ignore the cancel function as it blocks forever
|
|
||||||
discovery, _, err := bt.Discover(defaultAdapter, &adapter.DiscoveryFilter{Transport: "le"})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// For every discovery event
|
|
||||||
for event := range discovery {
|
|
||||||
// If device removed, skip event
|
|
||||||
if event.Type == adapter.DeviceRemoved {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Create new device with discovered path
|
// If whitelist enabled and doesn't contain
|
||||||
dev, err := device.NewDevice1(event.Path)
|
// device, skip
|
||||||
|
if opts.WhitelistEnabled &&
|
||||||
|
!contains(opts.Whitelist, listDev.Properties.Address) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set device
|
||||||
|
dev = listDev
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// If device not set
|
||||||
|
if dev == nil {
|
||||||
|
// Discover devices on adapter
|
||||||
|
discoverCh, cancel, err := bt.Discover(defaultAdapter, &adapter.DiscoveryFilter{Transport: "le"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// If device name is InfiniTime
|
|
||||||
if dev.Properties.Name == BTName {
|
// For every discovery event
|
||||||
if opts.WhitelistEnabled && !contains(opts.Whitelist, dev.Properties.Address) {
|
for event := range discoverCh {
|
||||||
|
// If event type is not device added, skip
|
||||||
|
if event.Type != adapter.DeviceAdded {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Set output device
|
|
||||||
out.device = dev
|
// Create new device from event path
|
||||||
|
discovered, err := device.NewDevice1(event.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If device name does not match, skip
|
||||||
|
if discovered.Properties.Name != BTName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// If whitelist enabled and doesn't contain
|
||||||
|
// device, skip
|
||||||
|
if opts.WhitelistEnabled &&
|
||||||
|
!contains(opts.Whitelist, discovered.Properties.Address) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set device
|
||||||
|
dev = discovered
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
// Stop discovery
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If device is still not set, return error
|
||||||
|
if dev == nil {
|
||||||
|
return nil, ErrNoDevices
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create variable to track if reconnect
|
||||||
|
// was required
|
||||||
|
reconnRequired := false
|
||||||
|
// If device is not connected
|
||||||
|
if !dev.Properties.Connected {
|
||||||
|
// Connect to device
|
||||||
|
err = dev.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Set reconnect required to true
|
||||||
|
reconnRequired = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If device is not paired
|
||||||
|
if !dev.Properties.Paired {
|
||||||
|
// Pair device
|
||||||
|
err = dev.Pair()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if out.device == nil {
|
// If this is the first connection and reconnect
|
||||||
return nil, ErrNotFound
|
// is enabled, start reconnect goroutine
|
||||||
|
if first && opts.AttemptReconnect {
|
||||||
|
go reconnect(opts, dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to device
|
// If this is not the first connection, a reonnect
|
||||||
err = out.device.Connect()
|
// was required, and the OnReconnect callback exists,
|
||||||
|
// run it
|
||||||
|
if !first && reconnRequired && opts.OnReconnect != nil {
|
||||||
|
opts.OnReconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reconnect reconnects to a device if it disconnects
|
||||||
|
func reconnect(opts *Options, dev *device.Device1) {
|
||||||
|
// Watch device properties
|
||||||
|
propCh := watchProps(dev)
|
||||||
|
|
||||||
|
// Create variables to store time of last disconnect
|
||||||
|
// and amount of diconnects
|
||||||
|
lastDisconnect := time.Unix(0, 0)
|
||||||
|
amtDisconnects := 0
|
||||||
|
|
||||||
|
for event := range propCh {
|
||||||
|
// If event name is not Connected and value is not false, skip
|
||||||
|
if event.Name != "Connected" && event.Value != false {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store seconds since last disconnect
|
||||||
|
secsSince := time.Since(lastDisconnect).Seconds()
|
||||||
|
// If over 3 seconds have passed, reset disconnect count
|
||||||
|
if secsSince > 3 {
|
||||||
|
amtDisconnects = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// If less than 3 seconds have past and more than 6
|
||||||
|
// disconnects have occurred, remove the device and reset
|
||||||
|
if secsSince <= 3 && amtDisconnects >= 6 {
|
||||||
|
defaultAdapter.RemoveDevice(dev.Path())
|
||||||
|
lastDisconnect = time.Unix(0, 0)
|
||||||
|
amtDisconnects = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set disconnect variables
|
||||||
|
lastDisconnect = time.Now()
|
||||||
|
amtDisconnects++
|
||||||
|
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
// If three tries failed, remove device
|
||||||
|
if i == 3 {
|
||||||
|
defaultAdapter.RemoveDevice(dev.Path())
|
||||||
|
}
|
||||||
|
// Connect to device
|
||||||
|
newDev, err := connect(opts, false)
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Replace device with new device
|
||||||
|
*dev = *newDev
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bufferChannel writes all events on propCh to a new, buffered channel
|
||||||
|
func bufferChannel(propCh chan *bluez.PropertyChanged) <-chan *bluez.PropertyChanged {
|
||||||
|
out := make(chan *bluez.PropertyChanged, 10)
|
||||||
|
go func() {
|
||||||
|
for event := range propCh {
|
||||||
|
out <- event
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// watchProps returns a buffered channel for the device properties
|
||||||
|
func watchProps(dev *device.Device1) <-chan *bluez.PropertyChanged {
|
||||||
|
uPropCh, err := dev.WatchProperties()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
panic(err)
|
||||||
}
|
}
|
||||||
|
return bufferChannel(uPropCh)
|
||||||
// Pair device
|
|
||||||
err = out.pairTimeout()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set connected to true
|
|
||||||
out.device.Properties.Connected = true
|
|
||||||
|
|
||||||
// Resolve characteristics
|
|
||||||
err = out.resolveChars()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setOnPasskeyReq sets the callback for a passkey request.
|
// setOnPasskeyReq sets the callback for a passkey request.
|
||||||
@ -293,22 +301,14 @@ func setOnPasskeyReq(onReqPasskey func() (uint32, error)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pairTimeout tries to pair with the device.
|
// contains checks if s is contained within ss
|
||||||
// It will time out after 20 seconds.
|
func contains(ss []string, s string) bool {
|
||||||
func (i *Device) pairTimeout() error {
|
for _, str := range ss {
|
||||||
errCh := make(chan error)
|
if strings.EqualFold(str, s) {
|
||||||
go func() {
|
return true
|
||||||
errCh <- i.device.Pair()
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case err := <-errCh:
|
|
||||||
return err
|
|
||||||
case <-time.After(20 * time.Second):
|
|
||||||
if err := i.device.CancelPairing(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return ErrPairTimeout
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveChars attempts to set all required
|
// resolveChars attempts to set all required
|
||||||
|
Reference in New Issue
Block a user