Files
Bose-SoundTouch/pkg/models/systemsettings.go
T

317 lines
9.4 KiB
Go

package models
import (
"encoding/xml"
"fmt"
"strconv"
"strings"
)
// SystemTimeout is the power-saving setting returned by /systemtimeout.
type SystemTimeout struct {
XMLName xml.Name `xml:"systemtimeout"`
PowerSavingEnabled bool `xml:"powersaving_enabled"`
}
// Validate checks whether the update model is present.
func (s *SystemTimeout) Validate() error {
if s == nil {
return fmt.Errorf("system timeout is nil")
}
return nil
}
// UnmarshalXML rejects responses that omit the required power-saving value.
func (s *SystemTimeout) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
if start.Name.Local != "systemtimeout" {
return fmt.Errorf("expected systemtimeout element, got %s", start.Name.Local)
}
var wire struct {
PowerSavingEnabled *bool `xml:"powersaving_enabled"`
}
if err := d.DecodeElement(&wire, &start); err != nil {
return err
}
if wire.PowerSavingEnabled == nil {
return fmt.Errorf("systemtimeout is missing powersaving_enabled")
}
s.XMLName = start.Name
s.PowerSavingEnabled = *wire.PowerSavingEnabled
return nil
}
// RebroadcastLatencyModeValue is a firmware-supported rebroadcast timing mode.
type RebroadcastLatencyModeValue string
const (
// RebroadcastLatencySyncToRoom prioritizes the selected room for video sync.
RebroadcastLatencySyncToRoom RebroadcastLatencyModeValue = "SYNC_TO_ROOM"
// RebroadcastLatencySyncToZone prioritizes synchronization across the zone.
RebroadcastLatencySyncToZone RebroadcastLatencyModeValue = "SYNC_TO_ZONE"
)
// Validate rejects values that the SoundTouch firmware does not understand.
func (m RebroadcastLatencyModeValue) Validate() error {
switch m {
case RebroadcastLatencySyncToRoom, RebroadcastLatencySyncToZone:
return nil
default:
return fmt.Errorf("unknown rebroadcast latency mode %q", m)
}
}
// RebroadcastLatencyMode is the setting returned by /rebroadcastlatencymode.
// Controllable is response metadata and is not included in update requests.
type RebroadcastLatencyMode struct {
XMLName xml.Name `xml:"rebroadcastlatencymode"`
Mode RebroadcastLatencyModeValue `xml:"mode,attr"`
Controllable bool `xml:"controllable,attr"`
}
// Validate checks whether the reported mode is supported.
func (r *RebroadcastLatencyMode) Validate() error {
if r == nil {
return fmt.Errorf("rebroadcast latency mode is nil")
}
return r.Mode.Validate()
}
// UnmarshalXML rejects responses that omit either required attribute.
func (r *RebroadcastLatencyMode) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
if start.Name.Local != "rebroadcastlatencymode" {
return fmt.Errorf("expected rebroadcastlatencymode element, got %s", start.Name.Local)
}
var wire struct {
Mode *RebroadcastLatencyModeValue `xml:"mode,attr"`
Controllable *bool `xml:"controllable,attr"`
}
if err := d.DecodeElement(&wire, &start); err != nil {
return err
}
if wire.Mode == nil {
return fmt.Errorf("rebroadcastlatencymode is missing mode")
}
if wire.Controllable == nil {
return fmt.Errorf("rebroadcastlatencymode is missing controllable")
}
if err := wire.Mode.Validate(); err != nil {
return err
}
r.XMLName = start.Name
r.Mode = *wire.Mode
r.Controllable = *wire.Controllable
return nil
}
// RebroadcastLatencyModeRequest is the update body accepted by the firmware.
type RebroadcastLatencyModeRequest struct {
XMLName xml.Name `xml:"rebroadcastlatencymode"`
Mode RebroadcastLatencyModeValue `xml:"mode,attr"`
}
// Validate checks whether the requested latency mode is known.
func (r *RebroadcastLatencyModeRequest) Validate() error {
if r == nil {
return fmt.Errorf("rebroadcast latency mode request is nil")
}
return r.Mode.Validate()
}
// LanguageCode is a SoundTouch system-language identifier.
type LanguageCode int
// Supported system language codes match the set exposed by Stockholm.
const (
LanguageDanish LanguageCode = 1
LanguageGerman LanguageCode = 2
LanguageEnglish LanguageCode = 3
LanguageSpanish LanguageCode = 4
LanguageFrench LanguageCode = 5
LanguageItalian LanguageCode = 6
LanguageDutch LanguageCode = 7
LanguageSwedish LanguageCode = 8
LanguageJapanese LanguageCode = 9
LanguageSimplifiedChinese LanguageCode = 10
LanguageTraditionalChinese LanguageCode = 11
LanguageKorean LanguageCode = 12
LanguageThai LanguageCode = 13
LanguageCzech LanguageCode = 15
LanguageFinnish LanguageCode = 16
LanguageGreek LanguageCode = 17
LanguageNorwegian LanguageCode = 18
LanguagePolish LanguageCode = 19
LanguagePortuguese LanguageCode = 20
LanguageRomanian LanguageCode = 21
LanguageRussian LanguageCode = 22
LanguageSlovenian LanguageCode = 23
LanguageTurkish LanguageCode = 24
LanguageHungarian LanguageCode = 25
)
var knownSystemLanguageNames = map[LanguageCode]string{
LanguageDanish: "Dansk",
LanguageGerman: "Deutsch",
LanguageEnglish: "English",
LanguageSpanish: "Español",
LanguageFrench: "Français",
LanguageItalian: "Italiano",
LanguageDutch: "Nederlands",
LanguageSwedish: "Svenska",
LanguageJapanese: "日本語",
LanguageSimplifiedChinese: "简体中文",
LanguageTraditionalChinese: "繁體中文",
LanguageKorean: "한국어",
LanguageThai: "ไทย",
LanguageCzech: "Čeština",
LanguageFinnish: "Suomi",
LanguageGreek: "Ελληνικά",
LanguageNorwegian: "Norsk",
LanguagePolish: "Polski",
LanguagePortuguese: "Português",
LanguageRomanian: "Română",
LanguageRussian: "Русский",
LanguageSlovenian: "Slovenščina",
LanguageTurkish: "Türkçe",
LanguageHungarian: "Magyar",
}
// SystemLanguageNames returns the language labels and codes used by Stockholm.
// Each call returns a copy so callers cannot mutate shared validation state.
func SystemLanguageNames() map[LanguageCode]string {
names := make(map[LanguageCode]string, len(knownSystemLanguageNames))
for code, name := range knownSystemLanguageNames {
names[code] = name
}
return names
}
// Validate rejects language codes that Stockholm does not offer for writes.
func (l LanguageCode) Validate() error {
if _, ok := knownSystemLanguageNames[l]; !ok {
return fmt.Errorf("unknown system language code %d", l)
}
return nil
}
// SystemLanguage is the integer value read from or written to /language.
// Unknown values are retained when reading so newer firmware remains usable.
type SystemLanguage struct {
XMLName xml.Name `xml:"sysLanguage"`
Code LanguageCode `xml:",chardata"`
}
// UnmarshalXML retains unknown integer codes for forward compatibility while
// rejecting a response that omits the language value entirely.
func (l *SystemLanguage) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
if start.Name.Local != "sysLanguage" {
return fmt.Errorf("expected sysLanguage element, got %s", start.Name.Local)
}
var raw string
if err := d.DecodeElement(&raw, &start); err != nil {
return err
}
raw = strings.TrimSpace(raw)
if raw == "" {
return fmt.Errorf("sysLanguage is missing its language code")
}
code, err := strconv.Atoi(raw)
if err != nil {
return fmt.Errorf("invalid sysLanguage code %q: %w", raw, err)
}
l.XMLName = start.Name
l.Code = LanguageCode(code)
return nil
}
// Validate checks whether this language can be sent to the speaker.
func (l *SystemLanguage) Validate() error {
if l == nil {
return fmt.Errorf("system language is nil")
}
return l.Code.Validate()
}
// BluetoothInfo is the speaker Bluetooth adapter information.
type BluetoothInfo struct {
XMLName xml.Name `xml:"BluetoothInfo"`
BluetoothMACAddress string `xml:"BluetoothMACAddress,attr"`
}
// Validate requires the adapter address returned by the firmware.
func (b *BluetoothInfo) Validate() error {
if b == nil {
return fmt.Errorf("bluetooth info is nil")
}
if b.BluetoothMACAddress == "" {
return fmt.Errorf("bluetooth info is missing BluetoothMACAddress")
}
return nil
}
// UnmarshalXML rejects responses without the adapter address.
func (b *BluetoothInfo) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
if start.Name.Local != "BluetoothInfo" {
return fmt.Errorf("expected BluetoothInfo element, got %s", start.Name.Local)
}
var wire struct {
BluetoothMACAddress string `xml:"BluetoothMACAddress,attr"`
}
if err := d.DecodeElement(&wire, &start); err != nil {
return err
}
b.XMLName = start.Name
b.BluetoothMACAddress = wire.BluetoothMACAddress
return b.Validate()
}
// SourceRenameRequest is the exact update body accepted by /nameSource.
type SourceRenameRequest struct {
XMLName xml.Name `xml:"ContentItem"`
Source string `xml:"source,attr"`
SourceAccount string `xml:"sourceAccount,attr,omitempty"`
ItemName string `xml:"itemName"`
}
// Validate requires the source identity and replacement display name.
func (r *SourceRenameRequest) Validate() error {
if r == nil {
return fmt.Errorf("source rename request is nil")
}
if r.Source == "" {
return fmt.Errorf("source is required")
}
if r.ItemName == "" {
return fmt.Errorf("item name is required")
}
return nil
}