Skip to content

MQTT audio: fix mic_mute desync + gate volume/speaker on fixed-volume devices - #192

Open
LeonArmston wants to merge 4 commits into
starbrightlab:mainfrom
LeonArmston:gate-volume-on-fixed-devices
Open

MQTT audio: fix mic_mute desync + gate volume/speaker on fixed-volume devices#192
LeonArmston wants to merge 4 commits into
starbrightlab:mainfrom
LeonArmston:gate-volume-on-fixed-devices

Conversation

@LeonArmston

@LeonArmston LeonArmston commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Follow-up fixes to the MQTT audio controls (#179), from testing on a Portal TV. Related to #183 (does not close it — see below).

  1. mic_mute muted only every other press. The handler set the mute then re-read isMicrophoneMute to report state, but that getter lags a set by one call — so the published state was a press behind, HA desynced, and sent a redundant no-op command every other press. Now it echoes the commanded value (publishMicMute takes the intended state; still defaults to the live getter for the initial/periodic publish, where the value has settled).

  2. Media volume, speaker mute, and volume up/down did nothing on the Portal TV. They act on STREAM_MUSIC, but the Portal TV sends audio to the TV over HDMI at a fixed volume, so setStreamVolume/adjustStreamVolume are ignored. Its real volume path is an IR blaster, which only fires from a system volume key event that an app can't inject (Immortal has accessibility services but no INJECT_EVENTS; the IR key delegation happens in WindowManager, not via AudioManager). So there's no app-reachable volume path on the Portal TV. Tablet Portals own their speakers, so the stream API works there and is unchanged.

    Publish those four entities (and their state) only when the stream volume isn't fixed, and clear any previously-published discovery configs on fixed-volume devices so they don't orphan in HA. Runtime-gated on isVolumeFixed(), so nothing is device-model-specific: tablet Portals keep all four, the Portal TV hides them.

  3. Removed the dead pre-M branch in publishSpeakerMute()isStreamMute() exists since API 23 and minSdk is 24, so the fallback was unreachable. (The minor cleanup from MQTT audio controls: keep state fresh on local changes and verify mic-mute against the hardware switch #183.)

mic_mute works on every Portal and is unaffected by the gate.

Relation to #183 (why "related", not "closes"):

  • Point 2 (mic-mute semantics): confirmed on-device that software setMicrophoneMute does control the mic — muted a live Zoom Rooms call — so the entity should stay, not be dropped. The hardware privacy-switch reflection (the rest of point 2) is untested here and still open.
  • Point 1 (stale state on local changes): not addressed for tablet Portals (no VOLUME_CHANGED_ACTION receiver yet); moot on the Portal TV since the volume entities are gated off.
  • Point 3: dead-code branch removed here. The FLAG_SHOW_UI vs 0 consistency is a deliberate call left for you.

Note: I couldn't read isVolumeFixed() over ADB to fully confirm it returns true on the Portal TV (HDMI output sits pinned at max, which is consistent). Worth a one-line log to verify; if it ever returns false there, the gate should fall back to "active output is HDMI".

Media volume, speaker mute, and volume up/down act on STREAM_MUSIC, which
only works on Portals that own their speakers (Portal, Portal+, Go, Mini).
On the Portal TV audio goes out over HDMI at a fixed volume, so those calls
are silently ignored; its real volume path is an IR blaster reachable only
via system key events, which a normal app can't inject. So the four entities
do nothing on a Portal TV.

Publish them (and their state) only when the stream volume isn't fixed, and
clear any previously-published discovery configs on fixed-volume devices so
they don't orphan in Home Assistant. Mic mute is unaffected and stays on
every Portal.
The mic_mute handler set the mute then re-read isMicrophoneMute to report
state. That getter lags a set by one call, so the published state was always
one press behind; with a non-optimistic switch HA desynced and sent a
redundant (no-op) command every other press, so it only muted on every
second press.

Publish the commanded value instead. publishMicMute now takes the intended
state (defaulting to the live getter for the initial/periodic publish, where
the value has settled).
isStreamMute() exists since API 23 and minSdk is 24, so the
getStreamVolume() == 0 fallback was unreachable. Addresses the
minor cleanup noted in starbrightlab#183.
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.

1 participant