feat: SPE amp ON/OFF buttons and Low/Mid/High power-level control (keystroke codes best-guess from APG, pending hw verification)

This commit is contained in:
2026-07-21 01:25:55 +02:00
parent be1ae76eb3
commit 7d7d175ede
5 changed files with 92 additions and 2 deletions
+37 -1
View File
@@ -30,9 +30,17 @@ import (
)
const (
cmdOperate byte = 0x0D // toggles STANDBY ↔ OPERATE
cmdOperate byte = 0x0D // toggles STANDBY ↔ OPERATE (confirmed on hw)
cmdStatus byte = 0x90 // request the status string
// Best-guess keystroke codes reconstructed from the APG command table after
// correcting the PDF's note-wrap misalignment (anchored to the confirmed
// OPERATE=0x0D): OFF=0x0A, POWER=0x0B. The POWER key doubles as power-on (when
// the amp is off) and the output-level cycle L→M→H (when it's on) — same as the
// single physical POWER button. To be verified on hardware.
cmdOff byte = 0x0A // OFF key — switch the amplifier off
cmdPower byte = 0x0B // POWER key — turn on / cycle output power level
syncHost = 0x55
syncAmp = 0xAA
@@ -138,6 +146,34 @@ func (c *Client) Operate(on bool) error {
// ToggleOperate flips STANDBY/OPERATE unconditionally.
func (c *Client) ToggleOperate() error { return c.sendCmd(cmdOperate) }
// PowerOn presses the POWER key (turns the amp on when it's off).
func (c *Client) PowerOn() error { return c.sendCmd(cmdPower) }
// PowerOff presses the OFF key (switches the amp off).
func (c *Client) PowerOff() error { return c.sendCmd(cmdOff) }
// SetPowerLevel cycles the output power level (L/M/H) to the requested one by
// tapping the POWER key until the reported level matches, giving the amp time to
// report each change. `level` is "L", "M" or "H" (case-insensitive).
func (c *Client) SetPowerLevel(level string) error {
want := strings.ToUpper(strings.TrimSpace(level))
if want == "" {
return nil
}
// At most 3 taps to walk the 3-way cycle back around to the target.
for i := 0; i < 3; i++ {
cur := strings.ToUpper(strings.TrimSpace(c.GetStatus().PowerLevel))
if cur == want {
return nil
}
if err := c.sendCmd(cmdPower); err != nil {
return err
}
time.Sleep(1 * time.Second) // let the amp apply it and the poll refresh status
}
return nil
}
func (c *Client) pollLoop() {
t := time.NewTicker(pollEvery)
defer t.Stop()