Skip to content

Devialet browse and play media #134168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: dev
Choose a base branch
from

Conversation

fwestenberg
Copy link
Contributor

@fwestenberg fwestenberg commented Dec 28, 2024

Breaking change

Proposed change

Add play media and browse media support for Devialet.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR fixes or closes issue:
  • This PR is related to issue:
  • Link to documentation pull request:

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

@fwestenberg fwestenberg reopened this Dec 28, 2024
@fwestenberg fwestenberg marked this pull request as ready for review December 28, 2024 21:43
@fwestenberg fwestenberg marked this pull request as draft December 28, 2024 21:49
@fwestenberg fwestenberg marked this pull request as ready for review December 28, 2024 21:49
@fwestenberg fwestenberg marked this pull request as draft December 28, 2024 23:16
@fwestenberg fwestenberg marked this pull request as ready for review December 29, 2024 14:22
@fwestenberg fwestenberg marked this pull request as draft December 29, 2024 23:15
@fwestenberg fwestenberg marked this pull request as ready for review January 6, 2025 09:10
Comment on lines 40 to 45
if await self.client.async_search_allowed():
self.entry.async_create_background_task(
hass=self.hass,
target=self.client.async_discover_upnp_device(),
name=f"{DOMAIN}_UPnP_Discovery",
)
Copy link
Member

Choose a reason for hiding this comment

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

Why do we do this?

Copy link
Contributor Author

@fwestenberg fwestenberg Jan 17, 2025

Choose a reason for hiding this comment

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

At every restart of the speaker, the UPnP port will change. This method finds the UPnP port and initiates the playback capability.

@home-assistant
Copy link

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

@home-assistant home-assistant bot marked this pull request as draft January 17, 2025 16:20
@home-assistant home-assistant bot marked this pull request as draft January 21, 2025 15:24
@fwestenberg fwestenberg marked this pull request as ready for review January 29, 2025 22:00
@home-assistant home-assistant bot requested a review from joostlek January 29, 2025 22:00
},
"exceptions": {
"upnp_error": {
"message": "Unable to play media. UPnP setup not ready for Devialet device."
Copy link
Member

Choose a reason for hiding this comment

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

What can the user do about this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The idea is, when you select a source (radio browser for example) and playback is not possible, you get noticed instead of waiting and nothing happens...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But usually when UPnP is not available, browsing media is also not supported. This should (almost) never happen.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, but what does UPnP setup not ready mean for the user? What do they need to do to fix it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is no known solution, beside maybe restart the speaker.

@@ -13,18 +17,19 @@ async def test_load_unload_config_entry(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test the Devialet configuration entry loading and unloading."""
entry = await setup_integration(hass, aioclient_mock)
with patch.object(DevialetApi, "upnp_available", return_value=True):
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
with patch.object(DevialetApi, "upnp_available", return_value=True):
with patch("homeassistant.components.devialet.DevialetApi.upnp_available", return_value=True):

The integration would be a lot nicer if we could patch the library instead of the HTTP calls

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why do you prefer the patch above the patch.object here?

assert entry.state is ConfigEntryState.LOADED

freezer.tick(10)
async_fire_time_changed(hass)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
async_fire_time_changed(hass)
async_fire_time_changed(hass)
await hass.async_block_till_done()

Copy link
Member

Choose a reason for hiding this comment

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

Why do we need to skip time here in the first place? The integration already has state at this point right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If this part is removed, the update has not taken place yet and the attributes are not set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So without the tick: KeyError: 'volume_level'

Copy link
Contributor

@emontnemery emontnemery Jun 25, 2025

Choose a reason for hiding this comment

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

I think what Joost means is to explain why the integration is not in a stable state after calling await setup_integration(hass, aioclient_mock), considering that function does:

        await hass.config_entries.async_setup(entry.entry_id)
        await hass.async_block_till_done()

And the config entry setup does an initial refresh of the coordinator:

await coordinator.async_config_entry_first_refresh()

I think the problem is that the media player entity only sets its state in DevialetMediaPlayerEntity._handle_coordinator_update, which is not called when the entity is added

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
if not self.coordinator.client.is_available:
self.async_write_ha_state()
return

Comment on lines 182 to 189
with patch.object(DevialetApi, "equalizer", new_callable=PropertyMock) as mock:
mock.return_value = None

with patch.object(
DevialetApi, "night_mode", new_callable=PropertyMock
) as mock:
mock.return_value = True

Copy link
Member

Choose a reason for hiding this comment

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

Seeing this I would really recommend doing proper mocks, because this isn't ideal. Like, either use library mocks or use http mocks. Don't use both as this becomes hard to maintain

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can improve testing in another PR. It requires a lot of time to me and this is already an existing part.

Copy link
Member

Choose a reason for hiding this comment

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

Yes but the tests themselves can be a lot cleaner than having multiple nested with patches, you can consider patching the full object or merging the patch statements with patch(), patch():

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So I merged the patch statements at all locations. It's not perfect, but already a lot cleaner I think. Thanks for the suggestion.

Comment on lines +323 to +326
patch(
"homeassistant.components.media_source.async_browse_media",
return_value=True,
) as mock_browse_media,
Copy link
Member

Choose a reason for hiding this comment

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

Why do we patch this internal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What would be another solution to test the async browse media function?

Copy link
Member

Choose a reason for hiding this comment

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

I don't know, I did work on the Spotify one, but I am more wondering why we do this so I can see how we would solve this otherwise, hence its just a question rather than a "this should be changed" :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried to patch the first function after the Devialet integration's call. So that's why I've patched this one. Not sure if it's the best way to test it but it seems to work.

@home-assistant home-assistant bot marked this pull request as draft January 31, 2025 11:26
Copy link

There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days.
If you are the author of this PR, please leave a comment if you want to keep it open. Also, please rebase your PR onto the latest dev branch to ensure that it's up to date with the latest changes.
Thank you for your contribution!

@github-actions github-actions bot added the stale label May 20, 2025
@github-actions github-actions bot removed the stale label May 20, 2025
@emontnemery
Copy link
Contributor

@fwestenberg if you think the PR is ready for another round of review, or if you expect some feedback, please click the "Ready for review" button.

@fwestenberg fwestenberg marked this pull request as ready for review June 25, 2025 12:49
@home-assistant home-assistant bot requested a review from joostlek June 25, 2025 12:49
@fwestenberg
Copy link
Contributor Author

Great idea. I think only the tests are left.

@emontnemery
Copy link
Contributor

@fwestenberg Did you see this comment #134168 (comment) ?

@fwestenberg
Copy link
Contributor Author

@fwestenberg Did you see this comment #134168 (comment) ?

I've seen it, but currently I don't have the time to improve/fix this. Beside: the Devialet speaker seems to not play all different stream types from the Radio Browser for example, so it is not very reliable. A very nice solution is to install Music Assistant within Home Assistant and stream using Airplay.

@emontnemery
Copy link
Contributor

I've seen it, but currently I don't have the time to improve/fix this

OK, should this PR be moved back to draft state then until you have time to address that change?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants