Analysis of all integrations providing media player entities, focused on volume control behavior.
The default async_volume_up/async_volume_down in MediaPlayerEntity (__init__.py:1036-1070):
- If the entity defines a sync
volume_up/volume_downmethod, calls it via executor - Otherwise, if
volume_levelis known andVOLUME_SETis supported, callsset_volume_level(volume_level +/- volume_step)wherevolume_stepdefaults to0.1(10%)
This means: if an integration supports VOLUME_SET and has accurate volume_level, the base class can handle stepping automatically. Overriding is only truly needed when:
- The device has a native step command that should be preferred
- The volume level might not be up-to-date (push devices with stale state)
- A different step granularity is needed (though
_attr_volume_stepcan handle this)
These are the key candidates for analysis. Could they drop their overrides and rely on the base class?
These integrations call a device-specific step API rather than computing current + delta. The native command is preferred because it works even if volume_level is stale, and respects the device's own step granularity.
| Integration | Override calls | Update model | Could remove override? |
|---|---|---|---|
| smartthings | Command.VOLUME_UP/DOWN via SmartThings API |
Push | No - native command preferred |
| philips_js | sendKey("VolumeUp"/"VolumeDown") |
Coordinator | No - native key press |
| soundtouch | device.volume_up()/volume_down() |
Polling | Maybe - polling keeps volume_level fresh |
| control4 | setIncrementVolume()/setDecrementVolume() |
Coordinator | No - native command preferred |
| denon_rs232 | receiver.volume_up()/volume_down() |
Push | No - native serial command |
| vizio | device.vol_up(num=step)/vol_down(num=step) + optimistic update |
Polling | No - configurable device step + optimistic |
| apple_tv | atv.audio.volume_up()/volume_down() |
Push | No - native command, volume_level may be stale |
| volumio | volumio.volume_up()/volume_down() |
Polling | Maybe - polling keeps volume_level fresh |
| music_assistant | player_command_volume_up()/volume_down() |
Push | No - native command preferred |
| kodi | kodi.volume_up()/volume_down() |
Conditional (websocket/poll) | No - native command preferred |
| kef | speaker.increase_volume()/decrease_volume() |
Polling | Maybe - polling keeps volume_level fresh |
| samsungtv | KEY_VOLUP/KEY_VOLDOWN IR keys |
Coordinator + UPnP | No - IR key command, not absolute volume |
| russound_rio | zone.volume_up()/volume_down() |
Push | No - native command preferred |
| panasonic_viera | Keys.VOLUME_UP/DOWN key commands |
Polling | No - IR key command |
| braviatv | client.volume_up()/volume_down() |
Coordinator | No - native command preferred |
| hegel | COMMANDS["volume_up"/"volume_down"] |
Push | No - native command, push model |
| onkyo | Volume(zone, UP/DOWN) |
Push | No - native command, push model |
| openhome | device.increase_volume()/decrease_volume() |
Polling | Maybe - polling keeps volume_level fresh |
| lg_netcast | LG_COMMAND.VOLUME_UP/DOWN remote commands |
Polling | No - IR-style remote command |
| denonavr | receiver.async_volume_up()/volume_down() |
Hybrid (telnet push + poll fallback) | No - native command preferred |
| cambridge_audio | client.volume_up()/volume_down() |
Push | No - native command, push model |
| enigma2 | SetVolumeOption.UP/DOWN enum commands |
Coordinator | No - native step enum command |
| nad (RS232) | main_volume("+")/("-") |
Polling | No - native RS232 step command |
| pioneer | telnet "VU"/"VD" commands |
Polling | No - native telnet command |
| arcam_fmj | state.inc_volume()/dec_volume() |
Push | No - native command, push model |
| yamaha_musiccast | musiccast.volume_up(zone)/volume_down(zone) |
Push + coordinator | No - native command, push model |
| roon | change_volume_raw(relative) or change_volume_percent(3) |
Push | No - native relative volume API |
| androidtv | ADB key events returning new volume level | Polling | No - ADB command returns updated level |
| webostv | client.volume_up()/volume_down() |
Hybrid (poll + push) | No - native command, VOLUME_SET is conditional |
These integrations read the current volume, adjust by a fixed delta, and call set_volume on the device (or HA's set_volume_level). The base class does essentially the same thing. They could be replaced by setting _attr_volume_step to the appropriate value.
| Integration | Override logic | Step size | Equivalent _attr_volume_step |
Update model | Could remove override? |
|---|---|---|---|---|---|
| frontier_silicon | get_volume(), then set_volume(vol +/- 1) |
1 raw unit | Depends on device max volume | Polling | Yes - set volume_step, use base class |
| group | set_volume_level(level +/- 0.1) per child |
0.1 (10%) | 0.1 (already default!) | Push | Yes - base class does exactly this |
| monoprice | set_volume(raw +/- 1), raw range 0-38 |
1/38 ~= 0.026 | ~0.026 | Polling | Yes - set volume_step to 1/38 |
| bluesound | set_volume_level(level +/- 0.01) |
0.01 (1%) | 0.01 | Coordinator | Yes - set _attr_volume_step = 0.01 |
| clementine | set_volume(raw +/- 4), raw range 0-100 |
4/100 = 0.04 | 0.04 | Polling | Yes - set _attr_volume_step = 0.04 |
| mpd | setvol(current +/- 5), raw range 0-100 |
5/100 = 0.05 | 0.05 | Polling | Yes - set _attr_volume_step = 0.05 |
| aquostv | volume(raw +/- 2), raw range 0-60 |
2/60 ~= 0.033 | ~0.033 | Polling | Yes - set volume_step to 2/60 |
| songpal | set_volume(vol +/- 1) on device |
1 raw unit | Depends on device range | Push | Probably - but push model means volume_level could be stale |
| ws66i | set_volume(raw +/- 1), custom range |
1 raw unit | Depends on MAX_VOL | Coordinator | Yes - set appropriate volume_step |
| nad (TCP) | set_volume(raw +/- 2*step), configurable |
Configurable | User-configured | Polling | Yes - translate step to volume_step |
| demo | _attr_volume_level +/- 0.1 directly |
0.1 (10%) | 0.1 (already default!) | Push (simulated) | Yes - base class does exactly this |
Note on the "compute + set" group: These could all be simplified by:
- Setting
_attr_volume_stepto the appropriate fraction - Removing the
volume_up/volume_downoverrides - Letting the base class call
set_volume_level(volume_level +/- volume_step)
The main risk is for push-based integrations (songpal) where volume_level might be briefly stale after a rapid sequence of volume steps. For polling-based integrations, this is less of a concern since the level was recently polled.
These MUST override volume_up/down because the base class fallback requires VOLUME_SET.
| Integration | Override calls | Update model |
|---|---|---|
| epson | projector.send_command(VOL_UP/VOL_DOWN) |
Polling |
| xbox | SmartGlass volume(VolumeDirection.Up/Down) |
Coordinator |
| xiaomi_tv | tv.volume_up()/volume_down() |
Assumed state |
| harman_kardon_avr | avr.volume_up()/volume_down() |
Polling |
| hdmi_cec | CEC KEY_VOLUME_UP/DOWN keypress |
Push |
| androidtv_remote | "VOLUME_UP"/"VOLUME_DOWN" key commands |
Push |
| mediaroom | stb.send_cmd("VolUp"/"VolDown") |
Push |
| roku | roku.remote("volume_up"/"volume_down") |
Coordinator |
| lookin | IR commands "volup"/"voldown" |
Push |
These have unreachable override code (since the service layer won't dispatch volume_up/down without VOLUME_STEP or VOLUME_SET supporting it, unless VOLUME_SET is declared too).
| Integration | Features | Override calls | Note |
|---|---|---|---|
| cmus | SET only | set_volume(raw +/- 5) |
Override reachable via VOLUME_SET base class path |
| sisyphus | SET only | set_speed(speed +/- 0.1) |
Override reachable via VOLUME_SET base class path |
| devialet | SET only | client.async_volume_up()/volume_down() |
Override reachable via VOLUME_SET base class path |
| denon | SET only | telnet "MVUP"/"MVDOWN" |
Override reachable via VOLUME_SET base class path |
| sonos | SET only | soco.volume +/- 2 |
Override reachable via VOLUME_SET base class path |
| dunehd | NEITHER | player.volume_up()/volume_down() |
Truly unreachable - no volume features declared |
Note: For the ones with VOLUME_SET, the overrides ARE reachable because the service registration (lines 305-314 in __init__.py) allows volume_up/down when either VOLUME_SET or VOLUME_STEP is present. So these overrides do get called, they just don't advertise a step button in the UI.
These rely entirely on the base class default behavior.
| Integration | Update model | Custom volume_step | Notes |
|---|---|---|---|
| heos | Coordinator + events | No (default 0.1) | Also has custom group volume up/down methods |
| squeezebox | Coordinator | Yes - configurable via options | volume_step property divides config value by 100 |
| unifiprotect | Push | No (default 0.1) | |
| esphome | Push | No (default 0.1) | Features are dynamic from device capabilities |
| Integration | Update model | Custom volume_step | Notes |
|---|---|---|---|
| cast | Push | No | VOLUME_SET is conditional (not fixed volume) |
| jellyfin | Coordinator | No | VOLUME_SET is conditional |
| spotify | Coordinator | No | Premium users, non-restricted devices |
| tesla_fleet | Coordinator | Yes (dynamic) | 1.0 / _volume_max / audio_volume_increment |
| teslemetry | Coordinator/Stream | Yes (1/31) |
Two entity variants: polling and streaming |
| tessie | Coordinator | Yes (1/31) |
Read-only entity, no volume control |
| snapcast | Push | No | |
| itunes | Polling | No | |
| yamaha | Polling | No | Inverted dB-based volume scale |
| slimproto | Push | No | |
| vlc | Polling | No | |
| vlc_telnet | Polling | No | Max volume is 500 |
| bang_olufsen | Hybrid (WS + poll) | No | |
| linkplay | Polling (5s) | No | |
| plex | Push | No | Volume stored locally (can't read from device) |
| dlna_dmr | Hybrid (events + poll) | No | VOLUME_SET is conditional |
| anthemav | Push | Yes (0.01) |
1% steps |
| lg_soundbar | Push | No | |
| russound_rnet | Polling | No |
No, for most of them. ~30 integrations use native device step commands. These are preferable because:
- They work even if
volume_levelis stale (important for push-based devices) - They respect the device's own step granularity and behavior
- They avoid a read-modify-write cycle
Yes, for ~10 integrations that manually compute current + delta and call set_volume:
- group and demo - override is identical to base class behavior (step = 0.1)
- bluesound, clementine, mpd, monoprice, aquostv, frontier_silicon, ws66i, nad (TCP) - could set
_attr_volume_stepto their custom step size and remove the override
These override volume_up/down but don't declare VOLUME_STEP, meaning the UI won't show step buttons:
- cmus, sisyphus, devialet, denon, sonos - all have working overrides that are callable but not properly advertised