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
1 changed files with 66 additions and 35 deletions

View File

@ -42,13 +42,19 @@ type Device struct {
heartRateChar *gatt.GattCharacteristic1
fsVersionChar *gatt.GattCharacteristic1
fsTransferChar *gatt.GattCharacteristic1
notifEventCh chan uint8
notifEventDone bool
onReconnect func()
Music MusicCtrl
DFU DFU
}
var ErrNoDevices = errors.New("no InfiniTime devices found")
var ErrNotFound = errors.New("could not find any advertising InfiniTime devices")
var (
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 {
AttemptReconnect bool
@ -332,8 +338,8 @@ func (i *Device) Address() string {
// Version returns InfiniTime's reported firmware version string
func (i *Device) Version() (string, error) {
if !i.device.Properties.Connected {
return "", nil
if err := i.checkStatus(i.fwVersionChar); err != nil {
return "", err
}
ver, err := i.fwVersionChar.ReadValue(nil)
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
func (i *Device) BatteryLevel() (uint8, error) {
if !i.device.Properties.Connected {
return 0, nil
if err := i.checkStatus(i.battLevelChar); err != nil {
return 0, err
}
battLevel, err := i.battLevelChar.ReadValue(nil)
if err != nil {
@ -352,8 +358,8 @@ func (i *Device) BatteryLevel() (uint8, error) {
}
func (i *Device) StepCount() (uint32, error) {
if !i.device.Properties.Connected {
return 0, nil
if err := i.checkStatus(i.stepCountChar); err != nil {
return 0, err
}
stepCountData, err := i.stepCountChar.ReadValue(nil)
if err != nil {
@ -370,8 +376,8 @@ type MotionValues struct {
func (i *Device) Motion() (MotionValues, error) {
out := MotionValues{}
if !i.device.Properties.Connected {
return out, nil
if err := i.checkStatus(i.motionValChar); err != nil {
return out, err
}
motionVals, err := i.motionValChar.ReadValue(nil)
if err != nil {
@ -386,8 +392,8 @@ func (i *Device) Motion() (MotionValues, error) {
}
func (i *Device) HeartRate() (uint8, error) {
if !i.device.Properties.Connected {
return 0, nil
if err := i.checkStatus(i.heartRateChar); err != nil {
return 0, err
}
heartRate, err := i.heartRateChar.ReadValue(nil)
if err != nil {
@ -397,8 +403,8 @@ func (i *Device) HeartRate() (uint8, error) {
}
func (i *Device) WatchHeartRate() (<-chan uint8, func(), error) {
if !i.device.Properties.Connected {
return make(<-chan uint8), nil, nil
if err := i.checkStatus(i.heartRateChar); err != nil {
return nil, nil, err
}
// Start notifications on heart rate characteristic
err := i.heartRateChar.StartNotify()
@ -439,8 +445,8 @@ func (i *Device) WatchHeartRate() (<-chan uint8, func(), error) {
}
func (i *Device) WatchBatteryLevel() (<-chan uint8, func(), error) {
if !i.device.Properties.Connected {
return make(<-chan uint8), nil, nil
if err := i.checkStatus(i.battLevelChar); err != nil {
return nil, nil, err
}
// Start notifications on heart rate characteristic
err := i.battLevelChar.StartNotify()
@ -481,8 +487,8 @@ func (i *Device) WatchBatteryLevel() (<-chan uint8, func(), error) {
}
func (i *Device) WatchStepCount() (<-chan uint32, func(), error) {
if !i.device.Properties.Connected {
return make(<-chan uint32), nil, nil
if err := i.checkStatus(i.stepCountChar); err != nil {
return nil, nil, err
}
// Start notifications on step count characteristic
err := i.stepCountChar.StartNotify()
@ -523,8 +529,8 @@ func (i *Device) WatchStepCount() (<-chan uint32, func(), error) {
}
func (i *Device) WatchMotion() (<-chan MotionValues, func(), error) {
if !i.device.Properties.Connected {
return make(<-chan MotionValues), nil, nil
if err := i.checkStatus(i.motionValChar); err != nil {
return nil, nil, err
}
// Start notifications on motion characteristic
err := i.motionValChar.StartNotify()
@ -576,8 +582,8 @@ func cancelFunc() (func(), chan struct{}) {
// SetTime sets the watch's time using the Current Time Service
func (i *Device) SetTime(t time.Time) error {
if !i.device.Properties.Connected {
return nil
if err := i.checkStatus(i.currentTimeChar); err != nil {
return err
}
buf := &bytes.Buffer{}
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
// the Alert Notification Service (ANS)
func (i *Device) Notify(title, body string) error {
if !i.device.Properties.Connected {
return nil
if err := i.checkStatus(i.newAlertChar); err != nil {
return err
}
return i.newAlertChar.WriteValue(
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.
// This channel will contain the user's response to the call notification. It
// can only contain one value.
// This channel will contain the user's response to the call notification.
func (i *Device) NotifyCall(from string) (<-chan uint8, error) {
if !i.device.Properties.Connected {
return make(<-chan uint8), nil
if err := i.checkStatus(i.newAlertChar); err != nil {
return nil, err
}
// Write call notification to new alert characteristic
err := i.newAlertChar.WriteValue(
@ -626,33 +631,59 @@ func (i *Device) NotifyCall(from string) (<-chan uint8, error) {
if err != nil {
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
err = i.notifEventChar.StartNotify()
err := i.notifEventChar.StartNotify()
if err != nil {
return nil, err
return err
}
// Watch properties of notification event characteristic
ch, err := i.notifEventChar.WatchProperties()
if err != nil {
return nil, err
return err
}
// Create new output channel for status
out := make(chan uint8, 1)
i.notifEventCh = make(chan uint8, 1)
go func() {
// For every event
for event := range ch {
// If value changed
if event.Name == "Value" {
// Send status to channel
out <- uint8(event.Value.([]byte)[0])
return
i.notifEventCh <- uint8(event.Value.([]byte)[0])
}
}
}()
return out, nil
return nil
}
// FS creates and returns a new filesystem from the device
func (i *Device) FS() (*blefs.FS, error) {
if err := i.checkStatus(i.currentTimeChar); err != nil {
return nil, err
}
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
}