forked from llun/soundtouch-golang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
devices.go
193 lines (163 loc) · 4.9 KB
/
devices.go
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package soundtouch
import (
"fmt"
"net"
"time"
log "github.com/sirupsen/logrus"
)
type Speakers map[string]*Speaker
type speakerMap map[string]bool
var visibleSpeakers = make(Speakers)
var filteredSpeakers speakerMap
// NetworkConfig describes the soundtouch network
// InterfaceName as the network interface to listen, e.g. "en0"
// NoOfSystems is the number of expected systems. Searching for at least this amount of systems
// SpeakerToListenFor contains the defined list of speakers we are handling
// Plugins the list of handlers of handlers to be used.
type NetworkConfig struct {
InterfaceName string
NoOfSystems int
SpeakerToListenFor []string
StaticIPAddresses []string
Plugins []Plugin
}
// GetKnownDevices returns a map of the already seen speakers
func GetKnownDevices() Speakers {
return visibleSpeakers
}
// GetSpeakerByName retuns a speaker for a given name
func GetSpeakerByName(speakerName string) *Speaker {
for _, theSpeaker := range visibleSpeakers {
if theSpeaker.Name() == speakerName {
return theSpeaker
}
}
return nil
}
// GetSpeakerByDeviceId retuns a speaker for a given DeviceId
func GetSpeakerByDeviceId(deviceId string) *Speaker {
for _, theSpeaker := range visibleSpeakers {
if theSpeaker.DeviceID() == deviceId {
return theSpeaker
}
}
return nil
}
// GetDevices starts listening on the indicated interface for the speakers to listen for.
// passes to speakers the series of speakers that are handled for further processing.
// Closes the speaker channel after all speakers found.
func GetDevices(conf NetworkConfig) (speakers chan *Speaker) {
return getDevices(conf, true)
}
// SearchDevices searches on the indicated interface for the speakers to listen for.
// passes to speakers the series of speakers that are handled for further processing.
// Keeps the speaker channel open for additional speakers.
func SearchDevices(conf NetworkConfig) (speakers chan *Speaker) {
return getDevices(conf, false)
}
// OpenInterface
func OpenInterface(interfaceName string) *net.Interface {
log.Tracef("Opening interface %v", interfaceName)
iff, err := net.InterfaceByName(interfaceName)
if err != nil {
log.Printf("Error with interface. %s", err)
{
ifaces, err := net.Interfaces()
if err != nil {
log.Print(fmt.Errorf("local-addresses: %v", err.Error()))
return nil
}
log.Println("Available interfaces:")
for _, i := range ifaces {
addrs, err := i.Addrs()
if err != nil {
log.Print(fmt.Errorf("local-addresses: %v", err.Error()))
continue
}
for _, a := range addrs {
log.Printf(" %v %v\n", i.Name, a)
}
}
log.Fatalln("Aborting.")
}
}
return iff
}
// getDevices starts listening on the indicated interface for the speakers to listen for.
// passes to speakers the series of speakers that are handled for further processing
func getDevices(conf NetworkConfig, closeChannel bool) (speakers chan *Speaker) {
iff := OpenInterface(conf.InterfaceName)
for _, value := range conf.SpeakerToListenFor {
filteredSpeakers[value] = true
log.Debugf("Reacting only speakers %v\n", value)
}
speakers = make(chan *Speaker)
log.Debugf("Scanning for Soundtouch systems.")
go func() {
for ok := true; ok; ok = (len(visibleSpeakers) < conf.NoOfSystems) {
var speakerCh <-chan *Speaker
if len(conf.StaticIPAddresses) > 0 {
speakerCh = LookupStaticSpeakers(conf.StaticIPAddresses)
} else {
speakerCh = LookupSpeakers(iff)
}
messageCh := make(chan *Update)
for speaker := range speakerCh {
// LookUp found a speaker
speakerInfo, _ := speaker.Info()
speaker.DeviceInfo = speakerInfo
spkLogger := log.WithFields(log.Fields{
"Speaker": speaker.Name(),
"ID": speaker.DeviceID(),
})
if contains(visibleSpeakers, speaker.DeviceID()) {
spkLogger.Debugf("Already included. Ignoring.")
continue
}
// check whether we might have to ignore the speaker
if len(filteredSpeakers) > 0 && !(filteredSpeakers)[speakerInfo.Name] {
continue
}
visibleSpeakers[speaker.DeviceID()] = speaker
// register handles
for _, uh := range conf.Plugins {
speaker.AddPlugin(uh)
}
go func(s *Speaker, msgChan chan *Update) {
// defer wg.Done()
webSocketCh, _ := s.Listen()
s.WebSocketCh = webSocketCh
s.Execute(webSocketCh)
}(speaker, messageCh)
speakers <- speaker
}
if len(visibleSpeakers) < conf.NoOfSystems {
time.Sleep(10 * time.Second)
} else {
ok = false
}
}
if closeChannel {
close(speakers)
}
log.Infof("Found all Soundtouch systems. Normal Operation.")
}()
// wg.Wait()
return
}
func sliceContains(deviceID string, list []string) bool {
for _, ms := range list {
if ms == deviceID {
return true
}
}
return false
}
func contains(list Speakers, deviceID string) bool {
for _, ms := range list {
if ms.DeviceInfo.DeviceID == deviceID {
return true
}
}
return false
}