BrowseMediaSource: domain is now required
The BrowseMediaSource class in the media_source integration has been tightened up. The domain parameter is now a required str instead of str | None, and the special "list every media source" root node has moved to its own class, RootBrowseMediaSource.
Previously, domain was optional only to represent one edge case: the top-level node returned when browsing media-source:// with no specific source selected. That made the type hint misleading for the 99% case — every actual media source has a domain — and added a None branch that consumers had to think about. Splitting the root into its own class removes that branch.
What changed
BrowseMediaSource.__init__now requiresdomain: str.- A new
RootBrowseMediaSourceclass represents the root browse node listing all available media sources. It hardcodesdomain=Noneandidentifier=Noneand usesmedia-source://as its content ID. media_source.async_browse_media()andMediaSourceItem.async_browse()now returnBrowseMediaSource | RootBrowseMediaSource.
Impact on custom integrations
Most integrations don't need any changes. If you implement a media_source.py platform, you were already passing your own domain to BrowseMediaSource — that keeps working.
You only need to act if:
-
You pass
domain=NonetoBrowseMediaSource. This is no longer allowed. Set your integration domain instead. -
You call
media_source.async_browse_media()and annotate the result. Update the type hint toBrowseMediaSource | RootBrowseMediaSource, or narrow withisinstance()before using domain-specific attributes:from homeassistant.components.media_source import (BrowseMediaSource,async_browse_media,)result = await async_browse_media(hass, media_content_id)if isinstance(result, BrowseMediaSource):# result.domain is guaranteed to be a str here...
See the updated media source platform documentation for the full reference.