Skip to content

agent: configurable volume command - #22

Merged
yawaflua merged 1 commit into
yawaflua:stagefrom
ryysel:pr/volume-cmd
Oct 31, 2026
Merged

yawaflua merged 1 commit into
yawaflua:stagefrom
ryysel:pr/volume-cmd

Conversation

@ryysel

@ryysel ryysel commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

Adds an opt-in volume backend that runs an external program, so a device can use whichever tool owns its mixer without the agent knowing about it. This is the rework of #15 (closed) in the direction you suggested: the core stays free of any single program instead of shipping a device-specific one.

Contract

<cmd> get           -> "<level> <muted|unmuted>", e.g. "40 unmuted"
<cmd> set <0..100>  -> set the level in percent
<cmd> mute on|off   -> mute or unmute; setting the current state is a no-op

The program runs directly, not through a shell, with the 10 s timeout from syscmd. The state word is optional and defaults to unmuted; a trailing % on the level is accepted. Levels outside 0..100 are clamped.

Wiring

  • -volume_cmd / MARUSYA_VOLUME_CMD;
  • takes precedence over mixer_control, which stays the default, so nothing changes for existing speakers;
  • the package imports only the standard library and internal packages.

Example adapter

configs/volume-capsule-ipc.example.sh adapts capsule-ipc on a VK Capsula Mini: it is the client to the tweaker IPC that the panel and AirPlay use, so panel, ALSA and the phone slider stay in sync. It is a config example, not part of the package.

With #21

When /run/capsule-controls.sock exists, the AirPlay plugin leaves the volume to ap2-bridge; the speaker still reports and accepts volume through the configured command, so both paths agree.

Tests

  • unit tests with a fake runner: parsing (state, no state, percent, case), clamping, mute on/off, error propagation, missing command;
  • go vet, go test ./internal/controller/..., marusya-agent builds;
  • on a real Capsula Mini: get 60 unmuted -> set 85 -> set 60, mute on twice (second is a no-op), mute off, volume restored.

Targets stage, as requested.

@yawaflua

Copy link
Copy Markdown
Owner

Если я не ошибаюсь, уже имеется такой способ взаимодействия через dbus, там же можно читать ивенты из приложения (активация, изменение громкости и тд).

@yawaflua
yawaflua self-requested a review October 31, 2026 21:50
@ryysel

ryysel commented Oct 31, 2026

Copy link
Copy Markdown
Contributor Author

Проверил на колонке, чтобы не гадать.

В системной шине: org.gnome.ShairportSync, org.bluez, org.bluealsa, Avahi и net.connman.iwd. Твикера (capsule-tweaker --daemon, он же capsule-controls) в шине нет вообще — в его бинарнике ноль строк про dbus, управление идёт через unix-сокет /run/capsule-controls.sock (JSON-строки состояния). То есть до панели и микшера D-Bus на этой колонке не достаёт.

У shairport D-Bus действительно богатый: RemoteControl.SetAirplayVolume, VolumeUp/Down, ToggleMute, AdvancedRemoteControl.SetVolume, PlayerState и метаданные. Но громкость там не хранится — это уровень потока AirPlay, а не микшер, и прочитать текущую громкость из shairport нельзя. Проверил сейчас на холодной колонке (сессии нет, active 0): SetAirplayVolume -9.0 вернул method return, rc=0, а громкость осталась 60. Хук run_this_when_volume_is_set = ap2-volume-sync этот вызов не дёрнул — он срабатывает на изменение громкости отправителем (pvol). При активной сессии D-Bus всё равно менял бы только поток и упирался в тот же capsule-ipc через хук.

У агента своя шина ru.marusya.Agent: State, MicMuted, Version + Ring, Display, Sensors. Громкости там нет. Плагины получают событие volume (#21), но это уведомление хоста, а не запись в микшер.

Поэтому на этой колонке источник громкости — твикер, и backend должен звать то, что умеет в него писать. -volume_cmd это и даёт: по умолчанию amixer, у нас capsule-ipc, у других pactl или свой скрипт.

Но D-Bus-часть могу закрыть отдельно, и, кажется, это полезно: добавить в ru.marusya.Agent свойство Volume i, метод SetVolume(i) и PropertiesChanged. godbus уже в зависимостях (internal/dbusapi), новых библиотек не появится. Тогда скрипты, плагины и HA смогут читать и менять громкость по D-Bus, как ты говоришь, а микшер останется за backend'ом. Сделать отдельным PR?

@yawaflua yawaflua left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это может быть удобно, но только если DBus не установлен или был выключен юзером вручную. Как по мне, DBus все-таки лучше, но резервный путь можно оставить

@yawaflua
yawaflua merged commit 8ae05b7 into yawaflua:stage Oct 31, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants