Allow multiple call notification responses and improve error handling

This commit is contained in:
Elara 2021-11-25 12:39:43 -08:00
parent 9966880bc8
commit 58d5036f20

View File

@ -42,13 +42,19 @@ type Device struct {
heartRateChar *gatt.GattCharacteristic1 heartRateChar *gatt.GattCharacteristic1
fsVersionChar *gatt.GattCharacteristic1 fsVersionChar *gatt.GattCharacteristic1
fsTransferChar *gatt.GattCharacteristic1 fsTransferChar *gatt.GattCharacteristic1
notifEventCh chan uint8
notifEventDone bool
onReconnect func() onReconnect func()
Music MusicCtrl Music MusicCtrl
DFU DFU DFU DFU
} }
var ErrNoDevices = errors.New("no InfiniTime devices found") var (
var ErrNotFound = errors.New("could not find any advertising InfiniTime devices") ErrNoDevices = errors.New("no InfiniTime devices found")
ErrNotFound = errors.New("could not find any advertising InfiniTime devices")
ErrNotConnected = errors.New("not connected")
ErrCharNotAvail = errors.New("required characteristic is not available")
)
type Options struct { type Options struct {
AttemptReconnect bool AttemptReconnect bool
@ -332,8 +338,8 @@ 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 !i.device.Properties.Connected { if err := i.checkStatus(i.fwVersionChar); err != nil {
return "", nil return "", err
} }
ver, err := i.fwVersionChar.ReadValue(nil) ver, err := i.fwVersionChar.ReadValue(nil)
return string(ver), err return string(ver), err
@ -341,8 +347,8 @@ 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 !i.device.Properties.Connected { if err := i.checkStatus(i.battLevelChar); err != nil {
return 0, nil return 0, err
} }
battLevel, err := i.battLevelChar.ReadValue(nil) battLevel, err := i.battLevelChar.ReadValue(nil)
if err != nil { if err != nil {
@ -352,8 +358,8 @@ func (i *Device) BatteryLevel() (uint8, error) {
} }
func (i *Device) StepCount() (uint32, error) { func (i *Device) StepCount() (uint32, error) {
if !i.device.Properties.Connected { if err := i.checkStatus(i.stepCountChar); err != nil {
return 0, nil return 0, err
} }
stepCountData, err := i.stepCountChar.ReadValue(nil) stepCountData, err := i.stepCountChar.ReadValue(nil)
if err != nil { if err != nil {
@ -370,8 +376,8 @@ type MotionValues struct {
func (i *Device) Motion() (MotionValues, error) { func (i *Device) Motion() (MotionValues, error) {
out := MotionValues{} out := MotionValues{}
if !i.device.Properties.Connected { if err := i.checkStatus(i.motionValChar); err != nil {
return out, nil return out, err
} }
motionVals, err := i.motionValChar.ReadValue(nil) motionVals, err := i.motionValChar.ReadValue(nil)
if err != nil { if err != nil {
@ -386,8 +392,8 @@ func (i *Device) Motion() (MotionValues, error) {
} }
func (i *Device) HeartRate() (uint8, error) { func (i *Device) HeartRate() (uint8, error) {
if !i.device.Properties.Connected { if err := i.checkStatus(i.heartRateChar); err != nil {
return 0, nil return 0, err
} }
heartRate, err := i.heartRateChar.ReadValue(nil) heartRate, err := i.heartRateChar.ReadValue(nil)
if err != nil { if err != nil {
@ -397,8 +403,8 @@ func (i *Device) HeartRate() (uint8, error) {
} }
func (i *Device) WatchHeartRate() (<-chan uint8, func(), error) { func (i *Device) WatchHeartRate() (<-chan uint8, func(), error) {
if !i.device.Properties.Connected { if err := i.checkStatus(i.heartRateChar); err != nil {
return make(<-chan uint8), nil, nil return nil, nil, err
} }
// Start notifications on heart rate characteristic // Start notifications on heart rate characteristic
err := i.heartRateChar.StartNotify() err := i.heartRateChar.StartNotify()
@ -439,8 +445,8 @@ func (i *Device) WatchHeartRate() (<-chan uint8, func(), error) {
} }
func (i *Device) WatchBatteryLevel() (<-chan uint8, func(), error) { func (i *Device) WatchBatteryLevel() (<-chan uint8, func(), error) {
if !i.device.Properties.Connected { if err := i.checkStatus(i.battLevelChar); err != nil {
return make(<-chan uint8), nil, nil return nil, nil, err
} }
// Start notifications on heart rate characteristic // Start notifications on heart rate characteristic
err := i.battLevelChar.StartNotify() err := i.battLevelChar.StartNotify()
@ -481,8 +487,8 @@ func (i *Device) WatchBatteryLevel() (<-chan uint8, func(), error) {
} }
func (i *Device) WatchStepCount() (<-chan uint32, func(), error) { func (i *Device) WatchStepCount() (<-chan uint32, func(), error) {
if !i.device.Properties.Connected { if err := i.checkStatus(i.stepCountChar); err != nil {
return make(<-chan uint32), nil, nil return nil, nil, err
} }
// Start notifications on step count characteristic // Start notifications on step count characteristic
err := i.stepCountChar.StartNotify() err := i.stepCountChar.StartNotify()
@ -523,8 +529,8 @@ func (i *Device) WatchStepCount() (<-chan uint32, func(), error) {
} }
func (i *Device) WatchMotion() (<-chan MotionValues, func(), error) { func (i *Device) WatchMotion() (<-chan MotionValues, func(), error) {
if !i.device.Properties.Connected { if err := i.checkStatus(i.motionValChar); err != nil {
return make(<-chan MotionValues), nil, nil return nil, nil, err
} }
// Start notifications on motion characteristic // Start notifications on motion characteristic
err := i.motionValChar.StartNotify() err := i.motionValChar.StartNotify()
@ -576,8 +582,8 @@ func cancelFunc() (func(), chan struct{}) {
// 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 !i.device.Properties.Connected { if err := i.checkStatus(i.currentTimeChar); err != nil {
return nil return err
} }
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
binary.Write(buf, binary.LittleEndian, uint16(t.Year())) binary.Write(buf, binary.LittleEndian, uint16(t.Year()))
@ -595,8 +601,8 @@ 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 !i.device.Properties.Connected { if err := i.checkStatus(i.newAlertChar); err != nil {
return nil return err
} }
return i.newAlertChar.WriteValue( return i.newAlertChar.WriteValue(
append([]byte{0x00, 0x01, 0x00}, []byte(title+"\x00"+body)...), append([]byte{0x00, 0x01, 0x00}, []byte(title+"\x00"+body)...),
@ -612,11 +618,10 @@ 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. It // This channel will contain the user's response to the call notification.
// can only contain one value.
func (i *Device) NotifyCall(from string) (<-chan uint8, error) { func (i *Device) NotifyCall(from string) (<-chan uint8, error) {
if !i.device.Properties.Connected { if err := i.checkStatus(i.newAlertChar); err != nil {
return make(<-chan uint8), nil return nil, err
} }
// Write call notification to new alert characteristic // Write call notification to new alert characteristic
err := i.newAlertChar.WriteValue( err := i.newAlertChar.WriteValue(
@ -626,33 +631,59 @@ func (i *Device) NotifyCall(from string) (<-chan uint8, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !i.notifEventDone {
err = i.initNotifEvent()
if err != nil {
return nil, err
}
i.notifEventDone = true
}
return i.notifEventCh, nil
}
// initNotifEvent initializes the notification event channel
func (i *Device) initNotifEvent() error {
// Start notifications on notification event characteristic // Start notifications on notification event characteristic
err = i.notifEventChar.StartNotify() err := i.notifEventChar.StartNotify()
if err != nil { if err != nil {
return nil, err return err
} }
// Watch properties of notification event characteristic // Watch properties of notification event characteristic
ch, err := i.notifEventChar.WatchProperties() ch, err := i.notifEventChar.WatchProperties()
if err != nil { if err != nil {
return nil, err return err
} }
// Create new output channel for status // Create new output channel for status
out := make(chan uint8, 1) i.notifEventCh = make(chan uint8, 1)
go func() { go func() {
// For every event // For every event
for event := range ch { for event := range ch {
// If value changed // If value changed
if event.Name == "Value" { if event.Name == "Value" {
// Send status to channel // Send status to channel
out <- uint8(event.Value.([]byte)[0]) i.notifEventCh <- uint8(event.Value.([]byte)[0])
return
} }
} }
}() }()
return out, nil return nil
} }
// 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.currentTimeChar); err != nil {
return nil, err
}
return blefs.New(i.fsTransferChar) return blefs.New(i.fsTransferChar)
} }
func (i *Device) checkStatus(char *gatt.GattCharacteristic1) error {
if !i.device.Properties.Connected {
return ErrNotConnected
}
if char == nil {
return ErrCharNotAvail
}
return nil
}