forked from Elara6331/infinitime
Add contexts and improve error handling
This commit is contained in:
parent
e3a6bd308d
commit
91a47acb50
214
infinitime.go
214
infinitime.go
@ -2,6 +2,7 @@ package infinitime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -39,6 +40,20 @@ const (
|
|||||||
WeatherDataChar = "00040001-78fc-48fe-8e23-433b3a1942d0"
|
WeatherDataChar = "00040001-78fc-48fe-8e23-433b3a1942d0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var charNames = map[string]string{
|
||||||
|
NewAlertChar: "New Alert",
|
||||||
|
NotifEventChar: "Notification Event",
|
||||||
|
StepCountChar: "Step Count",
|
||||||
|
MotionValChar: "Motion Values",
|
||||||
|
FirmwareVerChar: "Firmware Version",
|
||||||
|
CurrentTimeChar: "Current Time",
|
||||||
|
BatteryLvlChar: "Battery Level",
|
||||||
|
HeartRateChar: "Heart Rate",
|
||||||
|
FSTransferChar: "Filesystem Transfer",
|
||||||
|
FSVersionChar: "Filesystem Version",
|
||||||
|
WeatherDataChar: "Weather Data",
|
||||||
|
}
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
device *device.Device1
|
device *device.Device1
|
||||||
newAlertChar *gatt.GattCharacteristic1
|
newAlertChar *gatt.GattCharacteristic1
|
||||||
@ -62,11 +77,18 @@ var (
|
|||||||
ErrNoDevices = errors.New("no InfiniTime devices found")
|
ErrNoDevices = errors.New("no InfiniTime devices found")
|
||||||
ErrNotFound = errors.New("could not find any advertising InfiniTime devices")
|
ErrNotFound = errors.New("could not find any advertising InfiniTime devices")
|
||||||
ErrNotConnected = errors.New("not connected")
|
ErrNotConnected = errors.New("not connected")
|
||||||
ErrCharNotAvail = errors.New("required characteristic is not available")
|
|
||||||
ErrNoTimelineHeader = errors.New("events must contain the timeline header")
|
ErrNoTimelineHeader = errors.New("events must contain the timeline header")
|
||||||
ErrPairTimeout = errors.New("reached timeout while pairing")
|
ErrPairTimeout = errors.New("reached timeout while pairing")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ErrCharNotAvail struct {
|
||||||
|
uuid string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrCharNotAvail) Error() string {
|
||||||
|
return "characteristic " + e.uuid + " (" + charNames[e.uuid] + ") not available"
|
||||||
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
AttemptReconnect bool
|
AttemptReconnect bool
|
||||||
WhitelistEnabled bool
|
WhitelistEnabled bool
|
||||||
@ -90,7 +112,7 @@ var DefaultOptions = &Options{
|
|||||||
//
|
//
|
||||||
// It will also attempt to reconnect to the device
|
// It will also attempt to reconnect to the device
|
||||||
// if it disconnects and that is enabled in the options.
|
// if it disconnects and that is enabled in the options.
|
||||||
func Connect(opts *Options) (*Device, error) {
|
func Connect(ctx context.Context, opts *Options) (*Device, error) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = DefaultOptions
|
opts = DefaultOptions
|
||||||
}
|
}
|
||||||
@ -101,7 +123,7 @@ func Connect(opts *Options) (*Device, error) {
|
|||||||
setOnPasskeyReq(opts.OnReqPasskey)
|
setOnPasskeyReq(opts.OnReqPasskey)
|
||||||
|
|
||||||
// Connect to bluetooth device
|
// Connect to bluetooth device
|
||||||
btDev, err := connect(opts, true)
|
btDev, err := connect(ctx, opts, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -119,7 +141,7 @@ func Connect(opts *Options) (*Device, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// connect connects to the InfiniTime bluez device
|
// connect connects to the InfiniTime bluez device
|
||||||
func connect(opts *Options, first bool) (dev *device.Device1, err error) {
|
func connect(ctx context.Context, opts *Options, first bool) (dev *device.Device1, err error) {
|
||||||
// Get devices
|
// Get devices
|
||||||
devs, err := defaultAdapter.GetDevices()
|
devs, err := defaultAdapter.GetDevices()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -161,42 +183,49 @@ func connect(opts *Options, first bool) (dev *device.Device1, err error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// For every discovery event
|
discoverLoop:
|
||||||
for event := range discoverCh {
|
for {
|
||||||
// If event type is not device added, skip
|
select {
|
||||||
if event.Type != adapter.DeviceAdded {
|
case event := <-discoverCh:
|
||||||
continue
|
// If event type is not device added, skip
|
||||||
}
|
if event.Type != adapter.DeviceAdded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Create new device from event path
|
// Create new device from event path
|
||||||
discovered, err := device.NewDevice1(event.Path)
|
discovered, err := device.NewDevice1(event.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
||||||
|
log.Debug().
|
||||||
|
Str("mac", discovered.Properties.Address).
|
||||||
|
Msg("Discovered InfiniTime device skipped as it is not in whitelist")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set device
|
||||||
|
dev = discovered
|
||||||
|
|
||||||
// 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) {
|
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Str("mac", discovered.Properties.Address).
|
Str("mac", dev.Properties.Address).
|
||||||
Msg("Discovered InfiniTime device skipped as it is not in whitelist")
|
Msg("InfiniTime device discovered")
|
||||||
continue
|
break discoverLoop
|
||||||
|
case <-ctx.Done():
|
||||||
|
break discoverLoop
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set device
|
|
||||||
dev = discovered
|
|
||||||
|
|
||||||
log.Debug().
|
|
||||||
Str("mac", dev.Properties.Address).
|
|
||||||
Msg("InfiniTime device discovered")
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
// Stop discovery
|
|
||||||
|
// Cancel discovery
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +262,7 @@ func connect(opts *Options, first bool) (dev *device.Device1, err error) {
|
|||||||
// If this is the first connection and reconnect
|
// If this is the first connection and reconnect
|
||||||
// is enabled, start reconnect goroutine
|
// is enabled, start reconnect goroutine
|
||||||
if first && opts.AttemptReconnect {
|
if first && opts.AttemptReconnect {
|
||||||
go reconnect(opts, dev)
|
go reconnect(ctx, opts, dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is not the first connection, a reonnect
|
// If this is not the first connection, a reonnect
|
||||||
@ -248,7 +277,7 @@ func connect(opts *Options, first bool) (dev *device.Device1, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reconnect reconnects to a device if it disconnects
|
// reconnect reconnects to a device if it disconnects
|
||||||
func reconnect(opts *Options, dev *device.Device1) {
|
func reconnect(ctx context.Context, opts *Options, dev *device.Device1) {
|
||||||
// Watch device properties
|
// Watch device properties
|
||||||
propCh := watchProps(dev)
|
propCh := watchProps(dev)
|
||||||
|
|
||||||
@ -288,7 +317,7 @@ func reconnect(opts *Options, dev *device.Device1) {
|
|||||||
opts.Logger.Warn().Msg("Multiple connection attempts have failed. If this continues, try removing the InfiniTime device from bluetooth.")
|
opts.Logger.Warn().Msg("Multiple connection attempts have failed. If this continues, try removing the InfiniTime device from bluetooth.")
|
||||||
}
|
}
|
||||||
// Connect to device
|
// Connect to device
|
||||||
newDev, err := connect(opts, false)
|
newDev, err := connect(ctx, opts, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
@ -407,6 +436,7 @@ func (i *Device) resolveChars() error {
|
|||||||
if charResolved {
|
if charResolved {
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Str("uuid", char.Properties.UUID).
|
Str("uuid", char.Properties.UUID).
|
||||||
|
Str("name", charNames[char.Properties.UUID]).
|
||||||
Msg("Resolved characteristic")
|
Msg("Resolved characteristic")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -420,7 +450,7 @@ func (i *Device) Address() string {
|
|||||||
|
|
||||||
// Version returns InfiniTime's reported firmware version string
|
// Version returns InfiniTime's reported firmware version string
|
||||||
func (i *Device) Version() (string, error) {
|
func (i *Device) Version() (string, error) {
|
||||||
if err := i.checkStatus(i.fwVersionChar); err != nil {
|
if err := i.checkStatus(i.fwVersionChar, FirmwareVerChar); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
ver, err := i.fwVersionChar.ReadValue(nil)
|
ver, err := i.fwVersionChar.ReadValue(nil)
|
||||||
@ -429,7 +459,7 @@ func (i *Device) Version() (string, error) {
|
|||||||
|
|
||||||
// BatteryLevel gets the watch's battery level via the Battery Service
|
// BatteryLevel gets the watch's battery level via the Battery Service
|
||||||
func (i *Device) BatteryLevel() (uint8, error) {
|
func (i *Device) BatteryLevel() (uint8, error) {
|
||||||
if err := i.checkStatus(i.battLevelChar); err != nil {
|
if err := i.checkStatus(i.battLevelChar, BatteryLvlChar); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
battLevel, err := i.battLevelChar.ReadValue(nil)
|
battLevel, err := i.battLevelChar.ReadValue(nil)
|
||||||
@ -440,7 +470,7 @@ func (i *Device) BatteryLevel() (uint8, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Device) StepCount() (uint32, error) {
|
func (i *Device) StepCount() (uint32, error) {
|
||||||
if err := i.checkStatus(i.stepCountChar); err != nil {
|
if err := i.checkStatus(i.stepCountChar, StepCountChar); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
stepCountData, err := i.stepCountChar.ReadValue(nil)
|
stepCountData, err := i.stepCountChar.ReadValue(nil)
|
||||||
@ -458,7 +488,7 @@ type MotionValues struct {
|
|||||||
|
|
||||||
func (i *Device) Motion() (MotionValues, error) {
|
func (i *Device) Motion() (MotionValues, error) {
|
||||||
out := MotionValues{}
|
out := MotionValues{}
|
||||||
if err := i.checkStatus(i.motionValChar); err != nil {
|
if err := i.checkStatus(i.motionValChar, MotionValChar); err != nil {
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
motionVals, err := i.motionValChar.ReadValue(nil)
|
motionVals, err := i.motionValChar.ReadValue(nil)
|
||||||
@ -474,7 +504,7 @@ func (i *Device) Motion() (MotionValues, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Device) HeartRate() (uint8, error) {
|
func (i *Device) HeartRate() (uint8, error) {
|
||||||
if err := i.checkStatus(i.heartRateChar); err != nil {
|
if err := i.checkStatus(i.heartRateChar, HeartRateChar); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
heartRate, err := i.heartRateChar.ReadValue(nil)
|
heartRate, err := i.heartRateChar.ReadValue(nil)
|
||||||
@ -484,35 +514,33 @@ func (i *Device) HeartRate() (uint8, error) {
|
|||||||
return uint8(heartRate[1]), nil
|
return uint8(heartRate[1]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Device) WatchHeartRate() (<-chan uint8, func(), error) {
|
func (i *Device) WatchHeartRate(ctx context.Context) (<-chan uint8, error) {
|
||||||
if err := i.checkStatus(i.heartRateChar); err != nil {
|
if err := i.checkStatus(i.heartRateChar, HeartRateChar); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Start notifications on heart rate characteristic
|
// Start notifications on heart rate characteristic
|
||||||
err := i.heartRateChar.StartNotify()
|
err := i.heartRateChar.StartNotify()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Watch characteristics of heart rate characteristic
|
// Watch characteristics of heart rate characteristic
|
||||||
ch, err := i.heartRateChar.WatchProperties()
|
ch, err := i.heartRateChar.WatchProperties()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out := make(chan uint8, 2)
|
out := make(chan uint8, 2)
|
||||||
currentHeartRate, err := i.HeartRate()
|
currentHeartRate, err := i.HeartRate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out <- currentHeartRate
|
out <- currentHeartRate
|
||||||
cancel, done := cancelFunc()
|
|
||||||
go func() {
|
go func() {
|
||||||
// For every event
|
// For every event
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-ctx.Done():
|
||||||
log.Debug().Str("func", "WatchMotion").Msg("Received done signal")
|
log.Debug().Str("func", "WatchMotion").Msg("Received done signal")
|
||||||
close(out)
|
close(out)
|
||||||
close(done)
|
|
||||||
i.heartRateChar.StopNotify()
|
i.heartRateChar.StopNotify()
|
||||||
return
|
return
|
||||||
case event := <-ch:
|
case event := <-ch:
|
||||||
@ -527,38 +555,36 @@ func (i *Device) WatchHeartRate() (<-chan uint8, func(), error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return out, cancel, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Device) WatchBatteryLevel() (<-chan uint8, func(), error) {
|
func (i *Device) WatchBatteryLevel(ctx context.Context) (<-chan uint8, error) {
|
||||||
if err := i.checkStatus(i.battLevelChar); err != nil {
|
if err := i.checkStatus(i.battLevelChar, BatteryLvlChar); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Start notifications on heart rate characteristic
|
// Start notifications on heart rate characteristic
|
||||||
err := i.battLevelChar.StartNotify()
|
err := i.battLevelChar.StartNotify()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Watch characteristics of heart rate characteristic
|
// Watch characteristics of heart rate characteristic
|
||||||
ch, err := i.battLevelChar.WatchProperties()
|
ch, err := i.battLevelChar.WatchProperties()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out := make(chan uint8, 2)
|
out := make(chan uint8, 2)
|
||||||
currentBattLevel, err := i.BatteryLevel()
|
currentBattLevel, err := i.BatteryLevel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out <- currentBattLevel
|
out <- currentBattLevel
|
||||||
cancel, done := cancelFunc()
|
|
||||||
go func() {
|
go func() {
|
||||||
// For every event
|
// For every event
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-ctx.Done():
|
||||||
log.Debug().Str("func", "WatchMotion").Msg("Received done signal")
|
log.Debug().Str("func", "WatchMotion").Msg("Received done signal")
|
||||||
close(out)
|
close(out)
|
||||||
close(done)
|
|
||||||
i.battLevelChar.StopNotify()
|
i.battLevelChar.StopNotify()
|
||||||
return
|
return
|
||||||
case event := <-ch:
|
case event := <-ch:
|
||||||
@ -573,38 +599,36 @@ func (i *Device) WatchBatteryLevel() (<-chan uint8, func(), error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return out, cancel, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Device) WatchStepCount() (<-chan uint32, func(), error) {
|
func (i *Device) WatchStepCount(ctx context.Context) (<-chan uint32, error) {
|
||||||
if err := i.checkStatus(i.stepCountChar); err != nil {
|
if err := i.checkStatus(i.stepCountChar, StepCountChar); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Start notifications on step count characteristic
|
// Start notifications on step count characteristic
|
||||||
err := i.stepCountChar.StartNotify()
|
err := i.stepCountChar.StartNotify()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Watch properties of step count characteristic
|
// Watch properties of step count characteristic
|
||||||
ch, err := i.stepCountChar.WatchProperties()
|
ch, err := i.stepCountChar.WatchProperties()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out := make(chan uint32, 2)
|
out := make(chan uint32, 2)
|
||||||
currentStepCount, err := i.StepCount()
|
currentStepCount, err := i.StepCount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out <- currentStepCount
|
out <- currentStepCount
|
||||||
cancel, done := cancelFunc()
|
|
||||||
go func() {
|
go func() {
|
||||||
// For every event
|
// For every event
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-ctx.Done():
|
||||||
log.Debug().Str("func", "WatchMotion").Msg("Received done signal")
|
log.Debug().Str("func", "WatchMotion").Msg("Received done signal")
|
||||||
close(out)
|
close(out)
|
||||||
close(done)
|
|
||||||
i.stepCountChar.StopNotify()
|
i.stepCountChar.StopNotify()
|
||||||
return
|
return
|
||||||
case event := <-ch:
|
case event := <-ch:
|
||||||
@ -619,38 +643,36 @@ func (i *Device) WatchStepCount() (<-chan uint32, func(), error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return out, cancel, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Device) WatchMotion() (<-chan MotionValues, func(), error) {
|
func (i *Device) WatchMotion(ctx context.Context) (<-chan MotionValues, error) {
|
||||||
if err := i.checkStatus(i.motionValChar); err != nil {
|
if err := i.checkStatus(i.motionValChar, MotionValChar); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Start notifications on motion characteristic
|
// Start notifications on motion characteristic
|
||||||
err := i.motionValChar.StartNotify()
|
err := i.motionValChar.StartNotify()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Watch properties of motion characteristic
|
// Watch properties of motion characteristic
|
||||||
ch, err := i.motionValChar.WatchProperties()
|
ch, err := i.motionValChar.WatchProperties()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out := make(chan MotionValues, 2)
|
out := make(chan MotionValues, 2)
|
||||||
motionVals, err := i.Motion()
|
motionVals, err := i.Motion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out <- motionVals
|
out <- motionVals
|
||||||
cancel, done := cancelFunc()
|
|
||||||
go func() {
|
go func() {
|
||||||
// For every event
|
// For every event
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-ctx.Done():
|
||||||
log.Debug().Str("func", "WatchMotion").Msg("Received done signal")
|
log.Debug().Str("func", "WatchMotion").Msg("Received done signal")
|
||||||
close(out)
|
close(out)
|
||||||
close(done)
|
|
||||||
i.motionValChar.StopNotify()
|
i.motionValChar.StopNotify()
|
||||||
return
|
return
|
||||||
case event := <-ch:
|
case event := <-ch:
|
||||||
@ -668,19 +690,12 @@ func (i *Device) WatchMotion() (<-chan MotionValues, func(), error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return out, cancel, nil
|
return out, nil
|
||||||
}
|
|
||||||
|
|
||||||
func cancelFunc() (func(), chan struct{}) {
|
|
||||||
done := make(chan struct{}, 1)
|
|
||||||
return func() {
|
|
||||||
done <- struct{}{}
|
|
||||||
}, done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTime sets the watch's time using the Current Time Service
|
// SetTime sets the watch's time using the Current Time Service
|
||||||
func (i *Device) SetTime(t time.Time) error {
|
func (i *Device) SetTime(t time.Time) error {
|
||||||
if err := i.checkStatus(i.currentTimeChar); err != nil {
|
if err := i.checkStatus(i.currentTimeChar, CurrentTimeChar); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
@ -699,7 +714,7 @@ func (i *Device) SetTime(t time.Time) error {
|
|||||||
// Notify sends a notification to InfiniTime via
|
// Notify sends a notification to InfiniTime via
|
||||||
// the Alert Notification Service (ANS)
|
// the Alert Notification Service (ANS)
|
||||||
func (i *Device) Notify(title, body string) error {
|
func (i *Device) Notify(title, body string) error {
|
||||||
if err := i.checkStatus(i.newAlertChar); err != nil {
|
if err := i.checkStatus(i.newAlertChar, NewAlertChar); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return i.newAlertChar.WriteValue(
|
return i.newAlertChar.WriteValue(
|
||||||
@ -718,7 +733,7 @@ const (
|
|||||||
// NotifyCall sends a call notification to the PineTime and returns a channel.
|
// NotifyCall sends a call notification to the PineTime and returns a channel.
|
||||||
// This channel will contain the user's response to the call notification.
|
// This channel will contain the user's response to the call notification.
|
||||||
func (i *Device) NotifyCall(from string) (<-chan uint8, error) {
|
func (i *Device) NotifyCall(from string) (<-chan uint8, error) {
|
||||||
if err := i.checkStatus(i.newAlertChar); err != nil {
|
if err := i.checkStatus(i.newAlertChar, NewAlertChar); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Write call notification to new alert characteristic
|
// Write call notification to new alert characteristic
|
||||||
@ -770,7 +785,7 @@ func (i *Device) initNotifEvent() error {
|
|||||||
|
|
||||||
// FS creates and returns a new filesystem from the device
|
// FS creates and returns a new filesystem from the device
|
||||||
func (i *Device) FS() (*blefs.FS, error) {
|
func (i *Device) FS() (*blefs.FS, error) {
|
||||||
if err := i.checkStatus(i.fsTransferChar); err != nil {
|
if err := i.checkStatus(i.fsTransferChar, FSTransferChar); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return blefs.New(i.fsTransferChar)
|
return blefs.New(i.fsTransferChar)
|
||||||
@ -780,7 +795,7 @@ func (i *Device) FS() (*blefs.FS, error) {
|
|||||||
// the weather package to the timeline. Input must be
|
// the weather package to the timeline. Input must be
|
||||||
// a struct containing TimelineHeader.
|
// a struct containing TimelineHeader.
|
||||||
func (i *Device) AddWeatherEvent(event interface{}) error {
|
func (i *Device) AddWeatherEvent(event interface{}) error {
|
||||||
if err := i.checkStatus(i.weatherDataChar); err != nil {
|
if err := i.checkStatus(i.weatherDataChar, WeatherDataChar); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Get type of input
|
// Get type of input
|
||||||
@ -803,7 +818,7 @@ func (i *Device) AddWeatherEvent(event interface{}) error {
|
|||||||
return i.weatherDataChar.WriteValue(data, nil)
|
return i.weatherDataChar.WriteValue(data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Device) checkStatus(char *gatt.GattCharacteristic1) error {
|
func (i *Device) checkStatus(char *gatt.GattCharacteristic1, uuid string) error {
|
||||||
log.Debug().Msg("Checking characteristic status")
|
log.Debug().Msg("Checking characteristic status")
|
||||||
connected, err := i.device.GetConnected()
|
connected, err := i.device.GetConnected()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -814,8 +829,11 @@ func (i *Device) checkStatus(char *gatt.GattCharacteristic1) error {
|
|||||||
}
|
}
|
||||||
if char == nil {
|
if char == nil {
|
||||||
log.Debug().Msg("Characteristic not available (nil)")
|
log.Debug().Msg("Characteristic not available (nil)")
|
||||||
return ErrCharNotAvail
|
return ErrCharNotAvail{uuid}
|
||||||
}
|
}
|
||||||
log.Debug().Str("uuid", char.Properties.UUID).Msg("Characteristic available")
|
log.Debug().
|
||||||
|
Str("uuid", char.Properties.UUID).
|
||||||
|
Str("name", charNames[char.Properties.UUID]).
|
||||||
|
Msg("Characteristic available")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user