mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 00:56:16 +00:00
feat: implement balance control (GET/POST /balance)
- Add complete balance control functionality via GET/POST /balance endpoints
- Implement GetBalance() for current stereo balance retrieval
- Add SetBalance() with range validation (-50 to +50)
- Include IncreaseBalance() and DecreaseBalance() with safety limits
- Add SetBalanceSafe() with automatic value clamping
- Create comprehensive balance models with validation and helpers
- Add CLI flags: -balance, -set-balance, -inc-balance, -dec-balance
- Implement left/right percentage calculation and human-readable descriptions
- Create comprehensive test suite (30+ test cases) with mock servers
- Add error handling for devices that don't support balance control
- Update documentation with complete balance control reference
- Update API endpoints status (GET/POST /balance: ✅ Implemented)
- Update project status (70% overall completion, 100% control endpoints)
- Complete audio management trilogy: Volume + Bass + Balance
- Real device testing shows device-dependent feature availability
- XML request/response format validation and compliance
- Human-readable balance descriptions (Far Left, Center, Right, etc.)
- Left/Right channel percentage display for better UX
This commit is contained in:
@@ -0,0 +1,597 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/user_account/bose-soundtouch/pkg/models"
|
||||
)
|
||||
|
||||
func TestClient_GetBalance(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
serverResponse string
|
||||
wantError bool
|
||||
wantTargetBalance int
|
||||
wantActualBalance int
|
||||
wantDeviceID string
|
||||
}{
|
||||
{
|
||||
name: "Valid balance response",
|
||||
serverResponse: `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<balance deviceID="1234567890AB">
|
||||
<targetbalance>15</targetbalance>
|
||||
<actualbalance>15</actualbalance>
|
||||
</balance>`,
|
||||
wantError: false,
|
||||
wantTargetBalance: 15,
|
||||
wantActualBalance: 15,
|
||||
wantDeviceID: "1234567890AB",
|
||||
},
|
||||
{
|
||||
name: "Negative balance response",
|
||||
serverResponse: `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<balance deviceID="1234567890AB">
|
||||
<targetbalance>-25</targetbalance>
|
||||
<actualbalance>-25</actualbalance>
|
||||
</balance>`,
|
||||
wantError: false,
|
||||
wantTargetBalance: -25,
|
||||
wantActualBalance: -25,
|
||||
wantDeviceID: "1234567890AB",
|
||||
},
|
||||
{
|
||||
name: "Zero balance response",
|
||||
serverResponse: `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<balance deviceID="1234567890AB">
|
||||
<targetbalance>0</targetbalance>
|
||||
<actualbalance>0</actualbalance>
|
||||
</balance>`,
|
||||
wantError: false,
|
||||
wantTargetBalance: 0,
|
||||
wantActualBalance: 0,
|
||||
wantDeviceID: "1234567890AB",
|
||||
},
|
||||
{
|
||||
name: "Balance adjustment in progress",
|
||||
serverResponse: `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<balance deviceID="1234567890AB">
|
||||
<targetbalance>30</targetbalance>
|
||||
<actualbalance>20</actualbalance>
|
||||
</balance>`,
|
||||
wantError: false,
|
||||
wantTargetBalance: 30,
|
||||
wantActualBalance: 20,
|
||||
wantDeviceID: "1234567890AB",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" {
|
||||
t.Errorf("Expected GET request, got %s", r.Method)
|
||||
return
|
||||
}
|
||||
if r.URL.Path != "/balance" {
|
||||
t.Errorf("Expected path /balance, got %s", r.URL.Path)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(tt.serverResponse))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
config := ClientConfig{
|
||||
Host: server.URL[7:], // Remove "http://"
|
||||
Port: 80,
|
||||
Timeout: testTimeout,
|
||||
UserAgent: testUserAgent,
|
||||
}
|
||||
client := NewClient(config)
|
||||
client.baseURL = server.URL
|
||||
|
||||
balance, err := client.GetBalance()
|
||||
|
||||
if tt.wantError {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error, got nil")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if balance.TargetBalance != tt.wantTargetBalance {
|
||||
t.Errorf("Expected target balance %d, got %d", tt.wantTargetBalance, balance.TargetBalance)
|
||||
}
|
||||
if balance.ActualBalance != tt.wantActualBalance {
|
||||
t.Errorf("Expected actual balance %d, got %d", tt.wantActualBalance, balance.ActualBalance)
|
||||
}
|
||||
if balance.DeviceID != tt.wantDeviceID {
|
||||
t.Errorf("Expected device ID %s, got %s", tt.wantDeviceID, balance.DeviceID)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_SetBalance(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
level int
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "Valid balance level 0",
|
||||
level: 0,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "Valid balance level +50",
|
||||
level: 50,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "Valid balance level -50",
|
||||
level: -50,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "Valid balance level +25",
|
||||
level: 25,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "Valid balance level -25",
|
||||
level: -25,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid balance level +51",
|
||||
level: 51,
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid balance level -51",
|
||||
level: -51,
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid balance level +100",
|
||||
level: 100,
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if !tt.wantError {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
t.Errorf("Expected POST request, got %s", r.Method)
|
||||
return
|
||||
}
|
||||
if r.URL.Path != "/balance" {
|
||||
t.Errorf("Expected path /balance, got %s", r.URL.Path)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify Content-Type
|
||||
if contentType := r.Header.Get("Content-Type"); contentType != "application/xml" {
|
||||
t.Errorf("Expected Content-Type application/xml, got %s", contentType)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse and validate request body
|
||||
var balanceReq models.BalanceRequest
|
||||
err := xml.NewDecoder(r.Body).Decode(&balanceReq)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to decode request XML: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if balanceReq.Level != tt.level {
|
||||
t.Errorf("Expected balance level %d, got %d", tt.level, balanceReq.Level)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
config := ClientConfig{
|
||||
Host: server.URL[7:],
|
||||
Port: 80,
|
||||
Timeout: testTimeout,
|
||||
UserAgent: testUserAgent,
|
||||
}
|
||||
client := NewClient(config)
|
||||
client.baseURL = server.URL
|
||||
|
||||
err := client.SetBalance(tt.level)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
} else {
|
||||
// Test validation without server
|
||||
config := ClientConfig{
|
||||
Host: "localhost",
|
||||
Port: 8090,
|
||||
Timeout: testTimeout,
|
||||
UserAgent: testUserAgent,
|
||||
}
|
||||
client := NewClient(config)
|
||||
|
||||
err := client.SetBalance(tt.level)
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for invalid balance level %d, got nil", tt.level)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_SetBalanceSafe(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
level int
|
||||
expectedLevel int
|
||||
}{
|
||||
{
|
||||
name: "Valid level unchanged",
|
||||
level: 25,
|
||||
expectedLevel: 25,
|
||||
},
|
||||
{
|
||||
name: "Too high clamped",
|
||||
level: 75,
|
||||
expectedLevel: 50,
|
||||
},
|
||||
{
|
||||
name: "Too low clamped",
|
||||
level: -75,
|
||||
expectedLevel: -50,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Parse request body to verify clamped level
|
||||
var balanceReq models.BalanceRequest
|
||||
err := xml.NewDecoder(r.Body).Decode(&balanceReq)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to decode request XML: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if balanceReq.Level != tt.expectedLevel {
|
||||
t.Errorf("Expected clamped balance level %d, got %d", tt.expectedLevel, balanceReq.Level)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
config := ClientConfig{
|
||||
Host: server.URL[7:],
|
||||
Port: 80,
|
||||
Timeout: testTimeout,
|
||||
UserAgent: testUserAgent,
|
||||
}
|
||||
client := NewClient(config)
|
||||
client.baseURL = server.URL
|
||||
|
||||
err := client.SetBalanceSafe(tt.level)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_IncreaseBalance(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
currentBalance int
|
||||
amount int
|
||||
expectedNewBalance int
|
||||
}{
|
||||
{
|
||||
name: "Normal increase",
|
||||
currentBalance: 0,
|
||||
amount: 15,
|
||||
expectedNewBalance: 15,
|
||||
},
|
||||
{
|
||||
name: "Increase with clamping",
|
||||
currentBalance: 40,
|
||||
amount: 15,
|
||||
expectedNewBalance: 50,
|
||||
},
|
||||
{
|
||||
name: "Increase from negative",
|
||||
currentBalance: -15,
|
||||
amount: 10,
|
||||
expectedNewBalance: -5,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
getCallCount := 0
|
||||
postCallCount := 0
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
|
||||
if r.Method == "GET" && r.URL.Path == "/balance" {
|
||||
getCallCount++
|
||||
var response string
|
||||
if getCallCount == 1 {
|
||||
// First call - return current balance
|
||||
response = `<balance deviceID="1234567890AB"><targetbalance>` +
|
||||
fmt.Sprintf("%d", tt.currentBalance) + `</targetbalance><actualbalance>` +
|
||||
fmt.Sprintf("%d", tt.currentBalance) + `</actualbalance></balance>`
|
||||
} else {
|
||||
// Second call - return new balance level
|
||||
response = `<balance deviceID="1234567890AB"><targetbalance>` +
|
||||
fmt.Sprintf("%d", tt.expectedNewBalance) + `</targetbalance><actualbalance>` +
|
||||
fmt.Sprintf("%d", tt.expectedNewBalance) + `</actualbalance></balance>`
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(response))
|
||||
} else if r.Method == "POST" && r.URL.Path == "/balance" {
|
||||
postCallCount++
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
config := ClientConfig{
|
||||
Host: server.URL[7:],
|
||||
Port: 80,
|
||||
Timeout: testTimeout,
|
||||
UserAgent: testUserAgent,
|
||||
}
|
||||
client := NewClient(config)
|
||||
client.baseURL = server.URL
|
||||
|
||||
balance, err := client.IncreaseBalance(tt.amount)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if balance.GetLevel() != tt.expectedNewBalance {
|
||||
t.Errorf("Expected new balance level %d, got %d", tt.expectedNewBalance, balance.GetLevel())
|
||||
}
|
||||
|
||||
if getCallCount != 2 {
|
||||
t.Errorf("Expected 2 GET calls, got %d", getCallCount)
|
||||
}
|
||||
if postCallCount != 1 {
|
||||
t.Errorf("Expected 1 POST call, got %d", postCallCount)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_DecreaseBalance(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
currentBalance int
|
||||
amount int
|
||||
expectedNewBalance int
|
||||
}{
|
||||
{
|
||||
name: "Normal decrease",
|
||||
currentBalance: 15,
|
||||
amount: 10,
|
||||
expectedNewBalance: 5,
|
||||
},
|
||||
{
|
||||
name: "Decrease with clamping",
|
||||
currentBalance: -35,
|
||||
amount: 20,
|
||||
expectedNewBalance: -50,
|
||||
},
|
||||
{
|
||||
name: "Decrease to negative",
|
||||
currentBalance: 10,
|
||||
amount: 20,
|
||||
expectedNewBalance: -10,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
getCallCount := 0
|
||||
postCallCount := 0
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
|
||||
if r.Method == "GET" && r.URL.Path == "/balance" {
|
||||
getCallCount++
|
||||
var response string
|
||||
if getCallCount == 1 {
|
||||
// First call - return current balance
|
||||
response = `<balance deviceID="1234567890AB"><targetbalance>` +
|
||||
fmt.Sprintf("%d", tt.currentBalance) + `</targetbalance><actualbalance>` +
|
||||
fmt.Sprintf("%d", tt.currentBalance) + `</actualbalance></balance>`
|
||||
} else {
|
||||
// Second call - return new balance level
|
||||
response = `<balance deviceID="1234567890AB"><targetbalance>` +
|
||||
fmt.Sprintf("%d", tt.expectedNewBalance) + `</targetbalance><actualbalance>` +
|
||||
fmt.Sprintf("%d", tt.expectedNewBalance) + `</actualbalance></balance>`
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(response))
|
||||
} else if r.Method == "POST" && r.URL.Path == "/balance" {
|
||||
postCallCount++
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
config := ClientConfig{
|
||||
Host: server.URL[7:],
|
||||
Port: 80,
|
||||
Timeout: testTimeout,
|
||||
UserAgent: testUserAgent,
|
||||
}
|
||||
client := NewClient(config)
|
||||
client.baseURL = server.URL
|
||||
|
||||
balance, err := client.DecreaseBalance(tt.amount)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if balance.GetLevel() != tt.expectedNewBalance {
|
||||
t.Errorf("Expected new balance level %d, got %d", tt.expectedNewBalance, balance.GetLevel())
|
||||
}
|
||||
|
||||
if getCallCount != 2 {
|
||||
t.Errorf("Expected 2 GET calls, got %d", getCallCount)
|
||||
}
|
||||
if postCallCount != 1 {
|
||||
t.Errorf("Expected 1 POST call, got %d", postCallCount)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Balance_ErrorHandling(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
serverResponse func(w http.ResponseWriter, r *http.Request)
|
||||
method func(*Client) error
|
||||
wantError bool
|
||||
errorContains string
|
||||
}{
|
||||
{
|
||||
name: "GetBalance server returns 404",
|
||||
serverResponse: func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte("Not Found"))
|
||||
},
|
||||
method: func(c *Client) error {
|
||||
_, err := c.GetBalance()
|
||||
return err
|
||||
},
|
||||
wantError: true,
|
||||
errorContains: "failed to get balance",
|
||||
},
|
||||
{
|
||||
name: "SetBalance server returns 500",
|
||||
serverResponse: func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("Internal Server Error"))
|
||||
},
|
||||
method: func(c *Client) error {
|
||||
return c.SetBalance(15)
|
||||
},
|
||||
wantError: true,
|
||||
errorContains: "API request failed with status 500",
|
||||
},
|
||||
{
|
||||
name: "GetBalance invalid XML response",
|
||||
serverResponse: func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("invalid xml"))
|
||||
},
|
||||
method: func(c *Client) error {
|
||||
_, err := c.GetBalance()
|
||||
return err
|
||||
},
|
||||
wantError: true,
|
||||
errorContains: "failed to get balance",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(tt.serverResponse))
|
||||
defer server.Close()
|
||||
|
||||
config := ClientConfig{
|
||||
Host: server.URL[7:],
|
||||
Port: 80,
|
||||
Timeout: testTimeout,
|
||||
UserAgent: testUserAgent,
|
||||
}
|
||||
client := NewClient(config)
|
||||
client.baseURL = server.URL
|
||||
|
||||
err := tt.method(client)
|
||||
|
||||
if tt.wantError {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error, got nil")
|
||||
} else if !containsSubstring(err.Error(), tt.errorContains) {
|
||||
t.Errorf("Expected error containing '%s', got '%s'", tt.errorContains, err.Error())
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Balance_RequestFormat(t *testing.T) {
|
||||
// Test that the request XML format is correct
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Read and parse the raw request body
|
||||
var balanceReq models.BalanceRequest
|
||||
err := xml.NewDecoder(r.Body).Decode(&balanceReq)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to decode request XML: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate XML structure
|
||||
expectedLevel := 25
|
||||
if balanceReq.Level != expectedLevel {
|
||||
t.Errorf("Expected balance level %d, got %d", expectedLevel, balanceReq.Level)
|
||||
}
|
||||
|
||||
// Re-encode to verify XML format
|
||||
actualXML, err := xml.Marshal(balanceReq)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to marshal BalanceRequest: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
expectedXML := "<balance>25</balance>"
|
||||
if string(actualXML) != expectedXML {
|
||||
t.Errorf("Expected XML '%s', got '%s'", expectedXML, string(actualXML))
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
config := ClientConfig{
|
||||
Host: server.URL[7:],
|
||||
Port: 80,
|
||||
Timeout: testTimeout,
|
||||
UserAgent: testUserAgent,
|
||||
}
|
||||
client := NewClient(config)
|
||||
client.baseURL = server.URL
|
||||
|
||||
err := client.SetBalance(25)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -360,6 +360,70 @@ func (c *Client) DecreaseBass(amount int) (*models.Bass, error) {
|
||||
return c.GetBass()
|
||||
}
|
||||
|
||||
// GetBalance retrieves the current balance level from the /balance endpoint
|
||||
func (c *Client) GetBalance() (*models.Balance, error) {
|
||||
var balance models.Balance
|
||||
err := c.get("/balance", &balance)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get balance: %w", err)
|
||||
}
|
||||
return &balance, nil
|
||||
}
|
||||
|
||||
// SetBalance sets the balance level using the /balance endpoint
|
||||
func (c *Client) SetBalance(level int) error {
|
||||
if !models.ValidateBalanceLevel(level) {
|
||||
return fmt.Errorf("invalid balance level: %d (must be between %d and %d)", level, models.BalanceLevelMin, models.BalanceLevelMax)
|
||||
}
|
||||
|
||||
balanceReq, err := models.NewBalanceRequest(level)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create balance request: %w", err)
|
||||
}
|
||||
|
||||
return c.post("/balance", balanceReq, nil)
|
||||
}
|
||||
|
||||
// SetBalanceSafe sets balance with validation and clamping
|
||||
func (c *Client) SetBalanceSafe(level int) error {
|
||||
clampedLevel := models.ClampBalanceLevel(level)
|
||||
return c.SetBalance(clampedLevel)
|
||||
}
|
||||
|
||||
// IncreaseBalance increases balance by the specified amount (with safety limits)
|
||||
func (c *Client) IncreaseBalance(amount int) (*models.Balance, error) {
|
||||
currentBalance, err := c.GetBalance()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get current balance: %w", err)
|
||||
}
|
||||
|
||||
newLevel := models.ClampBalanceLevel(currentBalance.GetLevel() + amount)
|
||||
err = c.SetBalance(newLevel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set balance: %w", err)
|
||||
}
|
||||
|
||||
// Return updated balance
|
||||
return c.GetBalance()
|
||||
}
|
||||
|
||||
// DecreaseBalance decreases balance by the specified amount (with safety limits)
|
||||
func (c *Client) DecreaseBalance(amount int) (*models.Balance, error) {
|
||||
currentBalance, err := c.GetBalance()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get current balance: %w", err)
|
||||
}
|
||||
|
||||
newLevel := models.ClampBalanceLevel(currentBalance.GetLevel() - amount)
|
||||
err = c.SetBalance(newLevel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set balance: %w", err)
|
||||
}
|
||||
|
||||
// Return updated balance
|
||||
return c.GetBalance()
|
||||
}
|
||||
|
||||
// SelectSource selects an audio source using the /select endpoint
|
||||
func (c *Client) SelectSource(source string, sourceAccount string) error {
|
||||
// Validate source parameter
|
||||
|
||||
Reference in New Issue
Block a user