diff --git a/homeassistant/components/blueprint/importer.py b/homeassistant/components/blueprint/importer.py index d857992a13c..4517d134e69 100644 --- a/homeassistant/components/blueprint/importer.py +++ b/homeassistant/components/blueprint/importer.py @@ -1,6 +1,8 @@ """Import logic for blueprint.""" + from __future__ import annotations +from contextlib import suppress from dataclasses import dataclass import html import re @@ -28,6 +30,10 @@ GITHUB_FILE_PATTERN = re.compile( r"^https://github.com/(?P.+)/blob/(?P.+)$" ) +WEBSITE_PATTERN = re.compile( + r"^https://(?P[a-z0-9-]+)\.home-assistant\.io/(?P.+).yaml$" +) + COMMUNITY_TOPIC_SCHEMA = vol.Schema( { "slug": str, @@ -219,18 +225,37 @@ async def fetch_blueprint_from_github_gist_url( ) +async def fetch_blueprint_from_website_url( + hass: HomeAssistant, url: str +) -> ImportedBlueprint: + """Get a blueprint from our website.""" + if (WEBSITE_PATTERN.match(url)) is None: + raise UnsupportedUrl("Not a Home Assistant website URL") + + session = aiohttp_client.async_get_clientsession(hass) + + resp = await session.get(url, raise_for_status=True) + raw_yaml = await resp.text() + data = yaml.parse_yaml(raw_yaml) + assert isinstance(data, dict) + blueprint = Blueprint(data) + + parsed_import_url = yarl.URL(url) + suggested_filename = f"homeassistant/{parsed_import_url.parts[-1][:-5]}" + return ImportedBlueprint(suggested_filename, raw_yaml, blueprint) + + async def fetch_blueprint_from_url(hass: HomeAssistant, url: str) -> ImportedBlueprint: """Get a blueprint from a url.""" for func in ( fetch_blueprint_from_community_post, fetch_blueprint_from_github_url, fetch_blueprint_from_github_gist_url, + fetch_blueprint_from_website_url, ): - try: + with suppress(UnsupportedUrl): imported_bp = await func(hass, url) imported_bp.blueprint.update_metadata(source_url=url) return imported_bp - except UnsupportedUrl: - pass - raise HomeAssistantError("Unsupported url") + raise HomeAssistantError("Unsupported URL") diff --git a/tests/components/blueprint/test_importer.py b/tests/components/blueprint/test_importer.py index cdec562b99f..d41c8417b57 100644 --- a/tests/components/blueprint/test_importer.py +++ b/tests/components/blueprint/test_importer.py @@ -162,3 +162,30 @@ async def test_fetch_blueprint_from_github_gist_url( assert imported_blueprint.blueprint.inputs == snapshot assert imported_blueprint.suggested_filename == "balloob/motion_light" assert imported_blueprint.blueprint.metadata["source_url"] == url + + +async def test_fetch_blueprint_from_website_url( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test fetching blueprint from url.""" + aioclient_mock.get( + "https://www.home-assistant.io/blueprints/awesome.yaml", + text=Path( + hass.config.path("blueprints/automation/test_event_service.yaml") + ).read_text(), + ) + + url = "https://www.home-assistant.io/blueprints/awesome.yaml" + imported_blueprint = await importer.fetch_blueprint_from_url(hass, url) + assert isinstance(imported_blueprint, importer.ImportedBlueprint) + assert imported_blueprint.blueprint.domain == "automation" + assert imported_blueprint.suggested_filename == "homeassistant/awesome" + assert imported_blueprint.blueprint.metadata["source_url"] == url + + +async def test_fetch_blueprint_from_unsupported_url(hass: HomeAssistant) -> None: + """Test fetching blueprint from an unsupported URL.""" + url = "https://example.com/unsupported.yaml" + + with pytest.raises(HomeAssistantError, match=r"^Unsupported URL$"): + await importer.fetch_blueprint_from_url(hass, url)