Add call notification support
This commit is contained in:
parent
a1e08ed862
commit
c101249d3e
@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
bt "github.com/muka/go-bluetooth/api"
|
bt "github.com/muka/go-bluetooth/api"
|
||||||
@ -17,6 +16,7 @@ const BTName = "InfiniTime"
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
NewAlertChar = "00002a46-0000-1000-8000-00805f9b34fb"
|
NewAlertChar = "00002a46-0000-1000-8000-00805f9b34fb"
|
||||||
|
NotifEventChar = "00020001-78fc-48fe-8e23-433b3a1942d0"
|
||||||
FirmwareVerChar = "00002a26-0000-1000-8000-00805f9b34fb"
|
FirmwareVerChar = "00002a26-0000-1000-8000-00805f9b34fb"
|
||||||
CurrentTimeChar = "00002a2b-0000-1000-8000-00805f9b34fb"
|
CurrentTimeChar = "00002a2b-0000-1000-8000-00805f9b34fb"
|
||||||
BatteryLvlChar = "00002a19-0000-1000-8000-00805f9b34fb"
|
BatteryLvlChar = "00002a19-0000-1000-8000-00805f9b34fb"
|
||||||
@ -27,6 +27,7 @@ type Device struct {
|
|||||||
opts *Options
|
opts *Options
|
||||||
device *device.Device1
|
device *device.Device1
|
||||||
newAlertChar *gatt.GattCharacteristic1
|
newAlertChar *gatt.GattCharacteristic1
|
||||||
|
notifEventChar *gatt.GattCharacteristic1
|
||||||
fwVersionChar *gatt.GattCharacteristic1
|
fwVersionChar *gatt.GattCharacteristic1
|
||||||
currentTimeChar *gatt.GattCharacteristic1
|
currentTimeChar *gatt.GattCharacteristic1
|
||||||
battLevelChar *gatt.GattCharacteristic1
|
battLevelChar *gatt.GattCharacteristic1
|
||||||
@ -41,10 +42,13 @@ var ErrNotFound = errors.New("could not find any advertising InfiniTime devices"
|
|||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
AttemptReconnect bool
|
AttemptReconnect bool
|
||||||
|
WhitelistEnabled bool
|
||||||
|
Whitelist []string
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultOptions = &Options{
|
var DefaultOptions = &Options{
|
||||||
AttemptReconnect: true,
|
AttemptReconnect: true,
|
||||||
|
WhitelistEnabled: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect will attempt to connect to a
|
// Connect will attempt to connect to a
|
||||||
@ -58,7 +62,7 @@ func Connect(opts *Options) (*Device, error) {
|
|||||||
opts = DefaultOptions
|
opts = DefaultOptions
|
||||||
}
|
}
|
||||||
// Attempt to connect to paired device by name
|
// Attempt to connect to paired device by name
|
||||||
dev, err := connectByName()
|
dev, err := connectByName(opts)
|
||||||
// If such device does not exist
|
// If such device does not exist
|
||||||
if errors.Is(err, ErrNoDevices) {
|
if errors.Is(err, ErrNoDevices) {
|
||||||
// Attempt to pair device
|
// Attempt to pair device
|
||||||
@ -128,7 +132,7 @@ func (i *Device) OnReconnect(f func()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connect connects to a paired InfiniTime device
|
// Connect connects to a paired InfiniTime device
|
||||||
func connectByName() (*Device, error) {
|
func connectByName(opts *Options) (*Device, error) {
|
||||||
// Create new device
|
// Create new device
|
||||||
out := &Device{}
|
out := &Device{}
|
||||||
// Get devices from default adapter
|
// Get devices from default adapter
|
||||||
@ -140,6 +144,9 @@ func connectByName() (*Device, error) {
|
|||||||
for _, dev := range devs {
|
for _, dev := range devs {
|
||||||
// If device name is InfiniTime
|
// If device name is InfiniTime
|
||||||
if dev.Properties.Name == BTName {
|
if dev.Properties.Name == BTName {
|
||||||
|
if opts.WhitelistEnabled && !contains(opts.Whitelist, dev.Properties.Address) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// Set outout device to discovered device
|
// Set outout device to discovered device
|
||||||
out.device = dev
|
out.device = dev
|
||||||
break
|
break
|
||||||
@ -161,6 +168,15 @@ func connectByName() (*Device, error) {
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func contains(ss []string, s string) bool {
|
||||||
|
for _, str := range ss {
|
||||||
|
if str == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Pair attempts to discover and pair an InfiniTime device
|
// Pair attempts to discover and pair an InfiniTime device
|
||||||
func pair() (*Device, error) {
|
func pair() (*Device, error) {
|
||||||
// Create new device
|
// Create new device
|
||||||
@ -259,6 +275,8 @@ func (i *Device) resolveChars() error {
|
|||||||
switch char.Properties.UUID {
|
switch char.Properties.UUID {
|
||||||
case NewAlertChar:
|
case NewAlertChar:
|
||||||
i.newAlertChar = char
|
i.newAlertChar = char
|
||||||
|
case NotifEventChar:
|
||||||
|
i.notifEventChar = char
|
||||||
case FirmwareVerChar:
|
case FirmwareVerChar:
|
||||||
i.fwVersionChar = char
|
i.fwVersionChar = char
|
||||||
case CurrentTimeChar:
|
case CurrentTimeChar:
|
||||||
@ -414,7 +432,55 @@ func (i *Device) Notify(title, body string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return i.newAlertChar.WriteValue(
|
return i.newAlertChar.WriteValue(
|
||||||
[]byte(fmt.Sprintf("00\x00%s\x00%s", title, body)),
|
append([]byte{0x00, 0x01, 0x00}, []byte(title+"\x00"+body)...),
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These constants represent the possible call statuses selected by the user
|
||||||
|
const (
|
||||||
|
CallStatusDeclined uint8 = iota
|
||||||
|
CallStatusAccepted
|
||||||
|
CallStatusMuted
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (i *Device) NotifyCall(from string) (<-chan uint8, error) {
|
||||||
|
if !i.device.Properties.Connected {
|
||||||
|
return make(<-chan uint8), nil
|
||||||
|
}
|
||||||
|
// Write call notification to new alert characteristic
|
||||||
|
err := i.newAlertChar.WriteValue(
|
||||||
|
append([]byte{0x03, 0x01, 0x00}, []byte(from)...),
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Start notifications on notification event characteristic
|
||||||
|
err = i.notifEventChar.StartNotify()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Watch properties of notification event characteristic
|
||||||
|
ch, err := i.notifEventChar.WatchProperties()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Create new output channel for status
|
||||||
|
out := 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user