Skip to content

Vizio: unguarded audio_settings['volume'] lookup breaks media_player on newer TV firmware #179254

Description

@omarg3826

The problem

On a Vizio TV whose firmware does not list volume in its audio settings,
media_player.vizio raises KeyError: 'volume' on every coordinator update
(~every 30 s) and the entity stays permanently unavailable. The remote.vizio
entity from the same config entry works normally.

The cause is a single unguarded dictionary lookup in media_player.py. The two
lines immediately after it — for mute and sound mode — are guarded, so this
reads as an oversight rather than a deliberate assumption:

# homeassistant/components/vizio/media_player.py, _handle_coordinator_update
if data.audio_settings:
    self._attr_volume_level = (
        float(data.audio_settings[VIZIO_VOLUME].value) / self._max_volume   # <-- unguarded
    )
    if VIZIO_MUTE in data.audio_settings:                                   # <-- guarded
        ...
    if VIZIO_SOUND_MODE in data.audio_settings:                             # <-- guarded
        ...

Why volume is missing. It has not been removed — this firmware moved it out
of the audio settings collection into its own child resource:

GET /menu_native/dynamic/tv_settings/audio          -> 19 items, no "volume"
GET /menu_native/dynamic/tv_settings/audio/volume   -> Success, ITEMS[0].CNAME = "volume"

The parent collection returns:

tv_speakers, surround_sound, dialogue_enhancer, volume_leveling, bass, treble,
lip_sync, digital_audio_out, soundbar_settings, soundbar_eq_mode, sounbar_bass,
soundbar_treble, soundbar_dialogue, soundbar_subwoofer, soundbar_night_mode,
soundbar_height, soundbar_virtual_x, soundbar_speaker_test, soundbar_info

while the child resource returns a perfectly good value:

{"CNAME": "volume", "TYPE": "T_VALUE_V1", "NAME": "Volume",
 "VALUE": 8, "ENABLED": "FALSE", "HASHVAL": 73865888}

The TV also advertises "AUDIO_2.0_API": true in
/state/device/deviceinfoCAPABILITIES, which looks like the flag for this
newer audio layout.

The device is fully functional. Against the TV directly, reading volume works,
and volume control works via the ordinary key codes the remote entity already
uses (VOL_UP = codeset 5 / code 1, VOL_DOWN = 5/0). Verified by round-trip:
volume 8 → VOL_UP → 9 → VOL_DOWN → 8. Only Home Assistant's read path is
broken.

What version of Home Assistant Core has the issue?

core-2026.8.0

What was the last working version of Home Assistant Core?

Unknown — believed never to have worked on this firmware.

What type of installation are you running?

Home Assistant Container

Integration causing the issue

Vizio SmartCast

Link to integration documentation on our website

https://www.home-assistant.io/integrations/vizio/

Example YAML snippet

Not applicable — config entry created through the UI.

Anything in the logs?

ERROR (MainThread) [homeassistant.components.vizio.coordinator] Unexpected error updating listener <id> for vizio
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 202, in async_update_listeners
    update_callback()
    ~~~~~~~~~~~~~~~^^
  File "/usr/src/homeassistant/homeassistant/components/vizio/media_player.py", line 206, in _handle_coordinator_update
    float(data.audio_settings[VIZIO_VOLUME].value) / self._max_volume
          ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
KeyError: 'volume'

Repeats every ~30 seconds for as long as the TV is powered on.

Additional information

Affected device

Model V4K50S-0807 (V-Series, 50")
Firmware 86.910.19.1-2
API version 3.7.2-2621.0005
SETTINGS_ROOT tv_settings
Relevant capability AUDIO_2.0_API: true

Suggested fix — mirrors the guards already present for mute and sound mode:

if VIZIO_VOLUME in data.audio_settings:
    self._attr_volume_level = (
        float(data.audio_settings[VIZIO_VOLUME].value) / self._max_volume
    )
else:
    self._attr_volume_level = None

That alone stops the crash and lets the entity load. Actually reporting volume
on these TVs additionally needs the value read from
tv_settings/audio/volume. Note that vizaio already models a
profile-resolved Endpoint.VOLUME_LEVEL (PUT /audio/volume/level), so the
library may already have the plumbing for a per-profile read path.

This is a recurring report that has never been root-caused. The same failure
has been filed at least twice and closed by the stale bot rather than fixed:

There is also direct precedent for exactly this class of fix in this integration:
#32151 switched volume_step to .get() because it is optional, and
#34730 / #34782 added the mute guard after an identical KeyError: 'mute'.
volume appears to be the last unguarded one.

Workaround for anyone finding this issue. Disable the media_player entity
to stop the crash loop (the remote entity is unaffected), then read and control
volume directly:

sensor:
  - platform: rest
    name: "Vizio Volume"
    resource: "https://<tv-ip>:7345/menu_native/dynamic/tv_settings/audio/volume"
    headers:
      AUTH: !secret vizio_auth_token
    verify_ssl: false
    value_template: "{{ value_json.ITEMS[0].VALUE }}"
    unit_of_measurement: "%"

rest_command:
  vizio_volume_up:
    url: "https://<tv-ip>:7345/key_command/"
    method: PUT
    verify_ssl: false
    headers:
      AUTH: !secret vizio_auth_token
    content_type: "application/json"
    payload: '{"KEYLIST":[{"CODESET":5,"CODE":1,"ACTION":"KEYPRESS"}]}'

VOL_DOWN is code 0 and MUTE_TOGGLE is code 4 in the same codeset.

Metadata

Metadata

Assignees

Type

No type

Fields

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions