mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 00:56:16 +00:00
fix(client): log unhandled WebSocket event names instead of empty list
An <updates> frame whose only child is an element the WebSocketEvent
struct doesn't model (e.g. nowSelectionUpdated, sent by SoundTouch 10
firmware around a play action) produced no known event types, so
handleEvent logged "Received unknown event types: []" repeatedly. The
empty list carried no information and flooded soundtouch-web's logs and
the CLI events subscribe output we point people at for debugging.
Capture unmodeled <updates> children by name via an xml:",any" catch-all
on WebSocketEvent and log the actual element names ("[nowSelectionUpdated]"),
skipping frames that carry no child events entirely. A regression test
confirms a modeled event is not also captured as unknown.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
040469a074
commit
44d16e54da
@@ -563,7 +563,11 @@ func (ws *WebSocketClient) handleEvent(event *models.WebSocketEvent) {
|
||||
if !hasKnownEvent && handlers.OnUnknownEvent != nil {
|
||||
handlers.OnUnknownEvent(event)
|
||||
} else if !hasKnownEvent {
|
||||
ws.logger.Printf("Received unknown event types: %v", eventTypes)
|
||||
// Log the actual unmodeled element names (e.g. nowSelectionUpdated)
|
||||
// rather than an empty list; skip frames that carry no child events.
|
||||
if names := event.UnknownEventNames(); len(names) > 0 {
|
||||
ws.logger.Printf("Received unhandled event types: %v", names)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-1
@@ -102,7 +102,28 @@ type WebSocketEvent struct {
|
||||
ErrorUpdated *ErrorUpdatedEvent `xml:"errorUpdated,omitempty"`
|
||||
RecentsUpdated *RecentsUpdatedEvent `xml:"recentsUpdated,omitempty"`
|
||||
LanguageUpdated *LanguageUpdatedEvent `xml:"languageUpdated,omitempty"`
|
||||
Timestamp time.Time `json:"timestamp"` // Added by client for tracking
|
||||
// UnknownElements captures <updates> children we don't model yet (e.g.
|
||||
// nowSelectionUpdated), so callers can log them by name instead of an
|
||||
// empty list when no known event matched.
|
||||
UnknownElements []UnknownElement `xml:",any"`
|
||||
Timestamp time.Time `json:"timestamp"` // Added by client for tracking
|
||||
}
|
||||
|
||||
// UnknownElement records the tag name of an <updates> child element that the
|
||||
// WebSocketEvent struct does not (yet) model.
|
||||
type UnknownElement struct {
|
||||
XMLName xml.Name
|
||||
}
|
||||
|
||||
// UnknownEventNames returns the tag names of any unmodeled <updates> children,
|
||||
// for diagnostic logging.
|
||||
func (e *WebSocketEvent) UnknownEventNames() []string {
|
||||
names := make([]string, 0, len(e.UnknownElements))
|
||||
for _, u := range e.UnknownElements {
|
||||
names = append(names, u.XMLName.Local)
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
// GetEvents returns all events present in this WebSocket event
|
||||
|
||||
@@ -540,3 +540,41 @@ func TestCreateMockWebSocketEvent(t *testing.T) {
|
||||
t.Errorf("Event types don't match expected values")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseWebSocketEvent_UnknownElements verifies that an <updates> envelope
|
||||
// whose only child is an unmodeled element (e.g. nowSelectionUpdated, observed
|
||||
// on real SoundTouch 10 firmware) parses with no known event types but with
|
||||
// the element captured by name, so callers can log something useful instead
|
||||
// of an empty list.
|
||||
func TestParseWebSocketEvent_UnknownElements(t *testing.T) {
|
||||
raw := []byte(`<updates deviceID="A81B6A536A98"><nowSelectionUpdated><preset id="0"><ContentItem source="TUNEIN" type="stationurl" location="/v1/playback/station/s308770" sourceAccount="" isPresetable="true"><itemName>Willy</itemName></ContentItem></preset></nowSelectionUpdated></updates>`)
|
||||
|
||||
event, err := ParseWebSocketEvent(raw)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseWebSocketEvent: %v", err)
|
||||
}
|
||||
|
||||
if got := event.GetEventTypes(); len(got) != 0 {
|
||||
t.Errorf("expected no known event types, got %v", got)
|
||||
}
|
||||
|
||||
names := event.UnknownEventNames()
|
||||
if len(names) != 1 || names[0] != "nowSelectionUpdated" {
|
||||
t.Errorf("expected [nowSelectionUpdated], got %v", names)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseWebSocketEvent_KnownEventNoUnknowns confirms a modeled event is not
|
||||
// also captured as an unknown element.
|
||||
func TestParseWebSocketEvent_KnownEventNoUnknowns(t *testing.T) {
|
||||
raw := []byte(`<updates deviceID="A81B6A536A98"><nowPlayingUpdated><nowPlaying deviceID="A81B6A536A98" source="TUNEIN"></nowPlaying></nowPlayingUpdated></updates>`)
|
||||
|
||||
event, err := ParseWebSocketEvent(raw)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseWebSocketEvent: %v", err)
|
||||
}
|
||||
|
||||
if names := event.UnknownEventNames(); len(names) != 0 {
|
||||
t.Errorf("expected no unknown elements for a modeled event, got %v", names)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user