Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions homeassistant/components/squeezebox/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
from .const import (
CONF_BROWSE_LIMIT,
CONF_HTTPS,
CONF_LMS_TIMEOUT,
CONF_VOLUME_STEP,
DEFAULT_BROWSE_LIMIT,
DEFAULT_LMS_TIMEOUT,
DEFAULT_PORT,
DEFAULT_VOLUME_STEP,
DOMAIN,
Expand Down Expand Up @@ -267,6 +269,12 @@ async def async_step_dhcp(
),
vol.Coerce(int),
),
vol.Required(CONF_LMS_TIMEOUT): vol.All(
NumberSelector(
NumberSelectorConfig(min=1, max=60, mode=NumberSelectorMode.SLIDER)
),
vol.Coerce(float),
),
}
)

Expand All @@ -293,6 +301,9 @@ async def async_step_init(
CONF_VOLUME_STEP: self.config_entry.options.get(
CONF_VOLUME_STEP, DEFAULT_VOLUME_STEP
),
CONF_LMS_TIMEOUT: self.config_entry.options.get(
CONF_LMS_TIMEOUT, DEFAULT_LMS_TIMEOUT
),
},
),
)
2 changes: 2 additions & 0 deletions homeassistant/components/squeezebox/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@
PLAYER_UPDATE_INTERVAL = 5
CONF_BROWSE_LIMIT = "browse_limit"
CONF_VOLUME_STEP = "volume_step"
CONF_LMS_TIMEOUT = "lms_timeout"
DEFAULT_BROWSE_LIMIT = 1000
DEFAULT_VOLUME_STEP = 5
DEFAULT_LMS_TIMEOUT = 5
ATTR_ANNOUNCE_VOLUME = "announce_volume"
ATTR_ANNOUNCE_TIMEOUT = "announce_timeout"
UNPLAYABLE_TYPES = ("text", "actions")
Expand Down
44 changes: 41 additions & 3 deletions homeassistant/components/squeezebox/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@
ATTR_ANNOUNCE_TIMEOUT,
ATTR_ANNOUNCE_VOLUME,
CONF_BROWSE_LIMIT,
CONF_LMS_TIMEOUT,
CONF_VOLUME_STEP,
DEFAULT_BROWSE_LIMIT,
DEFAULT_LMS_TIMEOUT,
DEFAULT_VOLUME_STEP,
DOMAIN,
SERVER_MANUFACTURER,
Expand Down Expand Up @@ -237,6 +239,14 @@ def __init__(self, coordinator: SqueezeBoxPlayerUpdateCoordinator) -> None:
self._browse_data = BrowseData()
self._synthetic_media_browser_thumbnail_items: LRU[str, str] = LRU(5000)

def _get_options_lms_timeout(self) -> float:
"""Get the LMS timeout from the config entry options."""
return float(
self.coordinator.config_entry.options.get(
CONF_LMS_TIMEOUT, DEFAULT_LMS_TIMEOUT
)
)

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
Expand Down Expand Up @@ -405,7 +415,10 @@ def query_result(self) -> dict | bool:
async def async_turn_off(self) -> None:
"""Turn off media player."""
await safe_library_call(
self._player.async_set_power, False, translation_key="turn_off_failed"
self._player.async_set_power,
False,
self._get_options_lms_timeout(),
translation_key="turn_off_failed",
)
await self.coordinator.async_refresh()

Expand All @@ -415,6 +428,7 @@ async def async_set_volume_level(self, volume: float) -> None:
await safe_library_call(
self._player.async_set_volume,
volume_percent,
self._get_options_lms_timeout(),
translation_key="set_volume_failed",
translation_placeholders={"volume": volume_percent},
)
Expand All @@ -425,6 +439,7 @@ async def async_mute_volume(self, mute: bool) -> None:
await safe_library_call(
self._player.async_set_muting,
mute,
self._get_options_lms_timeout(),
translation_key="set_mute_failed",
)
await self.coordinator.async_refresh()
Expand All @@ -433,6 +448,7 @@ async def async_media_stop(self) -> None:
"""Send stop command to media player."""
await safe_library_call(
self._player.async_stop,
self._get_options_lms_timeout(),
translation_key="stop_failed",
)
await self.coordinator.async_refresh()
Expand All @@ -441,6 +457,7 @@ async def async_media_play_pause(self) -> None:
"""Send pause/play toggle command to media player."""
await safe_library_call(
self._player.async_toggle_pause,
self._get_options_lms_timeout(),
translation_key="play_pause_failed",
)
await self.coordinator.async_refresh()
Expand All @@ -449,6 +466,7 @@ async def async_media_play(self) -> None:
"""Send play command to media player."""
await safe_library_call(
self._player.async_play,
self._get_options_lms_timeout(),
translation_key="play_failed",
)
await self.coordinator.async_refresh()
Expand All @@ -457,6 +475,7 @@ async def async_media_pause(self) -> None:
"""Send pause command to media player."""
await safe_library_call(
self._player.async_pause,
self._get_options_lms_timeout(),
translation_key="pause_failed",
)
await self.coordinator.async_refresh()
Expand All @@ -466,6 +485,7 @@ async def async_media_next_track(self) -> None:
await safe_library_call(
self._player.async_index,
"+1",
self._get_options_lms_timeout(),
translation_key="next_track_failed",
)
await self.coordinator.async_refresh()
Expand All @@ -475,6 +495,7 @@ async def async_media_previous_track(self) -> None:
await safe_library_call(
self._player.async_index,
"-1",
self._get_options_lms_timeout(),
translation_key="previous_track_failed",
)
await self.coordinator.async_refresh()
Expand All @@ -484,6 +505,7 @@ async def async_media_seek(self, position: float) -> None:
await safe_library_call(
self._player.async_time,
position,
self._get_options_lms_timeout(),
translation_key="seek_failed",
translation_placeholders={"position": position},
)
Expand All @@ -494,6 +516,7 @@ async def async_turn_on(self) -> None:
await safe_library_call(
self._player.async_set_power,
True,
self._get_options_lms_timeout(),
translation_key="turn_on_failed",
)
await self.coordinator.async_refresh()
Expand Down Expand Up @@ -571,6 +594,7 @@ async def async_play_media(
self._player.async_load_url,
media_id,
cmd,
self._get_options_lms_timeout(),
translation_key="load_url_failed",
Comment on lines 593 to 598
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

The new lms_timeout option is applied to async_load_url(...) here, but other LMS calls in the same code path (e.g., async_load_playlist(...) and the custom async_query calls used by the services) still use the library default timeout. Consider applying the same configured timeout consistently to all LMS requests to match the option’s description.

Copilot uses AI. Check for mistakes.
translation_placeholders={"media_id": media_id, "cmd": cmd},
)
Expand Down Expand Up @@ -608,7 +632,12 @@ async def async_play_media(
)

if index is not None:
await self._player.async_index(index)
await safe_library_call(
self._player.async_index,
index,
self._get_options_lms_timeout(),
translation_key="next_track_failed",
)

await self.coordinator.async_refresh()

Expand Down Expand Up @@ -692,6 +721,7 @@ async def async_set_repeat(self, repeat: RepeatMode) -> None:
await safe_library_call(
self._player.async_set_repeat,
repeat_mode,
self._get_options_lms_timeout(),
translation_key="set_repeat_failed",
)
await self.coordinator.async_refresh()
Expand All @@ -702,6 +732,7 @@ async def async_set_shuffle(self, shuffle: bool) -> None:
await safe_library_call(
self._player.async_set_shuffle,
shuffle_mode,
self._get_options_lms_timeout(),
translation_key="set_shuffle_failed",
)
await self.coordinator.async_refresh()
Expand All @@ -710,6 +741,7 @@ async def async_clear_playlist(self) -> None:
"""Send the media player the command to clear the playlist."""
await safe_library_call(
self._player.async_clear_playlist,
self._get_options_lms_timeout(),
translation_key="clear_playlist_failed",
)
await self.coordinator.async_refresh()
Expand Down Expand Up @@ -772,7 +804,12 @@ async def async_join_players(self, group_members: list[str]) -> None:
},
)
if other_player_id := other_player.unique_id:
await self._player.async_sync(other_player_id)
await safe_library_call(
self._player.async_sync,
other_player_id,
self._get_options_lms_timeout(),
translation_key="unjoin_failed",
)
else:
raise ServiceValidationError(
translation_domain=DOMAIN,
Expand All @@ -786,6 +823,7 @@ async def async_unjoin_player(self) -> None:
"""Unsync this Squeezebox player."""
await safe_library_call(
self._player.async_unsync,
self._get_options_lms_timeout(),
translation_key="unjoin_failed",
)
await self.coordinator.async_refresh()
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/squeezebox/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@
"join_cannot_join_unknown_player": {
"message": "Could not join unknown player {other_player_entity_id}."
},
"join_failed": {
"message": "Failed to unsync the player."
},
"load_playlist_failed": {
"message": "Failed to load playlist with command {cmd}."
},
Expand Down Expand Up @@ -245,10 +248,12 @@
"init": {
"data": {
"browse_limit": "Browse limit",
"lms_timeout": "LMS timeout",
"volume_step": "Volume step"
},
"data_description": {
"browse_limit": "Maximum number of items when browsing or in a playlist.",
"lms_timeout": "Timeout in seconds for requests to the LMS. If you experience timeouts due to a slow LMS or network, increase this value.",
"volume_step": "Amount to adjust the volume when turning volume up or down."
},
"title": "LMS Configuration"
Expand Down
Loading