forked from llun/soundtouch-golang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathupdate.go
172 lines (147 loc) · 3.99 KB
/
update.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
package soundtouch
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"reflect"
)
// Update carries all update messages communicated to/from soundtouch system
type Update struct {
DeviceID string
Value interface{}
}
// NewUpdate creates a new Update object based on the provided XML body.
// It parses the XML and extracts the necessary information to create the Update object.
// The XML body should be in the following format:
// <updates deviceID="deviceID">
//
// <updateType>
// <value>...</value>
// </updateType>
//
// </updates>
// The function returns the created Update object and an error if any occurred during parsing.
func NewUpdate(body []byte) (*Update, error) {
// Create a new XML decoder to parse the XML body
decoder := xml.NewDecoder(bytes.NewBuffer(body))
root, err := decoder.Token() // Get the root element
if err != nil {
return nil, err
}
if root == nil {
return nil, errors.New("invalid XML format")
}
rootElement, ok := root.(xml.StartElement)
if !ok {
return nil, errors.New("invalid XML format")
}
if rootElement.Name.Local != "updates" {
return nil, errors.New("unsupported event")
}
var deviceID string
for i := 0; i < len(rootElement.Attr); i++ {
if rootElement.Attr[i].Name.Local == "deviceID" {
deviceID = rootElement.Attr[i].Value
}
}
// Get the update type
updateType, err := decoder.Token()
if err != nil {
return nil, err
}
value, err := decoder.Token()
if err != nil {
return nil, err
}
updateTypeElement := updateType.(xml.StartElement)
switch updateTypeElement.Name.Local {
case "connectionStateUpdated":
valueElement := updateTypeElement
var connState ConnectionStateUpdated
err = decoder.DecodeElement(&connState, &valueElement)
if err != nil {
return nil, err
}
return &Update{deviceID, connState}, nil
case "volumeUpdated":
valueElement := value.(xml.StartElement)
var volume Volume
err = decoder.DecodeElement(&volume, &valueElement)
if err != nil {
return nil, err
}
return &Update{deviceID, volume}, nil
case "nowPlayingUpdated":
valueElement := value.(xml.StartElement)
var nowPlaying NowPlaying
err = decoder.DecodeElement(&nowPlaying, &valueElement)
if err != nil {
return nil, err
}
return &Update{deviceID, nowPlaying}, nil
case "nowSelectionUpdated":
valueElement := value.(xml.StartElement)
var preset Preset
err = decoder.DecodeElement(&preset, &valueElement)
if err != nil {
return nil, err
}
return &Update{deviceID, preset}, nil
default:
return nil, fmt.Errorf("unhandeld Update Message. %v", string(body))
}
}
// String returns the specific part of an update message
func (u Update) String() string {
return fmt.Sprintf("%v", u.Value)
}
// Is returns true in case Update message is of type msgTypeName
// is one of
// - ConnectionStateUpdated
// - Volume
// - NowPlaying
// - Preset
func (u Update) Is(msgTypeName string) bool {
return reflect.TypeOf(u.Value).Name() == msgTypeName
}
// Artist returns the artist if present, empty else
func (u Update) Artist() string {
switch reflect.TypeOf(u.Value).Name() {
case "NowPlaying":
return u.Value.(NowPlaying).Artist
}
return ""
}
// Album returns the Album if present, else empty
func (u Update) Album() string {
switch reflect.TypeOf(u.Value).Name() {
case "NowPlaying":
return u.Value.(NowPlaying).Album
}
return ""
}
// HasContentItem returns true if the Update message has contentItem, false else
func (u Update) HasContentItem() bool {
switch reflect.TypeOf(u.Value).Name() {
case "NowPlaying":
return true
}
return false
}
// ContentItem returns the ContentItem if present, or an empty one if not present
func (u Update) ContentItem() ContentItem {
if u.HasContentItem() {
return u.Value.(NowPlaying).Content
}
return ContentItem{}
}
// GetSpeaker returns the Speaker instance the Update has been send from
func GetSpeaker(updateMsg Update) *Speaker {
for _, aKnownDevice := range GetKnownDevices() {
if aKnownDevice.DeviceID() == updateMsg.DeviceID {
return aKnownDevice
}
}
return nil
}