-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdiscovery.go
More file actions
158 lines (122 loc) · 3.28 KB
/
discovery.go
File metadata and controls
158 lines (122 loc) · 3.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package goralb
import (
"context"
"errors"
"fmt"
"github.com/muka/go-bluetooth/api"
"github.com/muka/go-bluetooth/bluez/profile/adapter"
"github.com/muka/go-bluetooth/bluez/profile/device"
)
const PGCompanyID = 0xDC
type (
BrushScanner interface {
FindBrush(ctx context.Context) (Brush, error)
FindBrushes(ctx context.Context, count int) ([]Brush, error)
Close() error
}
brushScanner struct {
adapter *adapter.Adapter1
}
)
func NewScanner() (*brushScanner, error) {
btAdapter, err := adapter.GetDefaultAdapter()
if err != nil {
return nil, err
}
return &brushScanner{
adapter: btAdapter,
}, nil
}
// Searches for a single brush
func (bm brushScanner) FindBrush(ctx context.Context) (Brush, error) {
brushes, err := bm.FindBrushes(ctx, 1)
if err != nil {
return nil, err
}
if len(brushes) == 0 {
return nil, errors.New("could not find brush")
}
return brushes[0], nil
}
// Searches for specified amount of brushes
func (bm brushScanner) FindBrushes(ctx context.Context, count int) ([]Brush, error) {
err := bm.flushBrushDiscoveries()
if err != nil {
return nil, fmt.Errorf("could not flush brush discoveries: %w", err)
}
adverts, err := bm.discoverBrushes(ctx, count)
if err != nil {
return nil, fmt.Errorf("could not discover brushes: %w", err)
}
return adverts, nil
}
// Same as Adapter1#FlushDevices() except specifically for PG company ID
func (bm brushScanner) flushBrushDiscoveries() error {
devices, err := bm.adapter.GetDevices()
if err != nil {
return err
}
for _, dev := range devices {
// Do not try flush connected devices
if dev.Properties.Connected {
continue
}
// There should only be one companyId per device, but the data is exposed as a map
for companyId := range dev.Properties.ManufacturerData {
if companyId == PGCompanyID {
// Remove device, ignore when unsuccessful
err = bm.adapter.RemoveDevice(dev.Path())
if err != nil {
return fmt.Errorf("could not remove %s from brush cache: %w", dev.Properties.Address, err)
}
// We only care about this companyID
break
}
}
}
return nil
}
func (bm brushScanner) discoverBrushes(ctx context.Context, count int) ([]Brush, error) {
// Only discover LE devices and do not give duplicates
filter := &adapter.DiscoveryFilter{
Transport: adapter.DiscoveryFilterTransportLE,
DuplicateData: false,
}
// Discover new devices
discoveries, cancel, err := api.Discover(bm.adapter, filter)
if err != nil {
return nil, err
}
// FIXME: this sometimes hangs?
defer cancel()
brushes := make([]Brush, 0, count)
for {
select {
// When a new device has been discovered
case discovery := <-discoveries:
// Ignore devices which have been removed
if discovery.Type == adapter.DeviceRemoved {
continue
}
// Create device handle
dev, err := device.NewDevice1(discovery.Path)
if err != nil || dev == nil {
continue
}
// Check if device has the right companyID
if _, exists := dev.Properties.ManufacturerData[PGCompanyID]; !exists {
continue
}
// Create brush from found device, append to list
brushes = append(brushes, NewBrush(dev))
if len(brushes) == count {
return brushes, nil
}
case <-ctx.Done():
return brushes, nil
}
}
}
func (bm brushScanner) Close() error {
return api.Exit()
}