diff --git a/homeassistant/components/demo/__init__.py b/homeassistant/components/demo/__init__.py index 2602d674005..3f0bb09cdbd 100644 --- a/homeassistant/components/demo/__init__.py +++ b/homeassistant/components/demo/__init__.py @@ -10,6 +10,7 @@ from homeassistant.components.recorder.statistics import ( async_add_external_statistics, get_last_statistics, ) +from homeassistant.components.repairs import IssueSeverity, async_create_issue from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_ENTITY_ID, @@ -177,6 +178,39 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass.bus.async_listen(EVENT_HOMEASSISTANT_START, demo_start_listener) + # Create issues + async_create_issue( + hass, + DOMAIN, + "transmogrifier_deprecated", + breaks_in_ha_version="2023.1.1", + is_fixable=False, + learn_more_url="https://en.wiktionary.org/wiki/transmogrifier", + severity=IssueSeverity.WARNING, + translation_key="transmogrifier_deprecated", + ) + + async_create_issue( + hass, + DOMAIN, + "out_of_blinker_fluid", + breaks_in_ha_version="2023.1.1", + is_fixable=True, + learn_more_url="https://www.youtube.com/watch?v=b9rntRxLlbU", + severity=IssueSeverity.CRITICAL, + translation_key="out_of_blinker_fluid", + ) + + async_create_issue( + hass, + DOMAIN, + "unfixable_problem", + is_fixable=False, + learn_more_url="https://www.youtube.com/watch?v=dQw4w9WgXcQ", + severity=IssueSeverity.WARNING, + translation_key="unfixable_problem", + ) + return True diff --git a/homeassistant/components/demo/manifest.json b/homeassistant/components/demo/manifest.json index df6fa494079..2965a66e23e 100644 --- a/homeassistant/components/demo/manifest.json +++ b/homeassistant/components/demo/manifest.json @@ -3,7 +3,7 @@ "name": "Demo", "documentation": "https://www.home-assistant.io/integrations/demo", "after_dependencies": ["recorder"], - "dependencies": ["conversation", "group", "zone"], + "dependencies": ["conversation", "group", "repairs", "zone"], "codeowners": ["@home-assistant/core"], "quality_scale": "internal", "iot_class": "calculated" diff --git a/homeassistant/components/demo/repairs.py b/homeassistant/components/demo/repairs.py new file mode 100644 index 00000000000..e5d31c18971 --- /dev/null +++ b/homeassistant/components/demo/repairs.py @@ -0,0 +1,33 @@ +"""Repairs platform for the demo integration.""" + +from __future__ import annotations + +import voluptuous as vol + +from homeassistant import data_entry_flow +from homeassistant.components.repairs import RepairsFlow + + +class DemoFixFlow(RepairsFlow): + """Handler for an issue fixing flow.""" + + async def async_step_init( + self, user_input: dict[str, str] | None = None + ) -> data_entry_flow.FlowResult: + """Handle the first step of a fix flow.""" + + return await (self.async_step_confirm()) + + async def async_step_confirm( + self, user_input: dict[str, str] | None = None + ) -> data_entry_flow.FlowResult: + """Handle the confirm step of a fix flow.""" + if user_input is not None: + return self.async_create_entry(title="Fixed issue", data={}) + + return self.async_show_form(step_id="confirm", data_schema=vol.Schema({})) + + +async def async_create_fix_flow(hass, issue_id): + """Create flow.""" + return DemoFixFlow() diff --git a/homeassistant/components/demo/strings.json b/homeassistant/components/demo/strings.json index c861ca9e5e9..a3a5b11f336 100644 --- a/homeassistant/components/demo/strings.json +++ b/homeassistant/components/demo/strings.json @@ -1,5 +1,26 @@ { "title": "Demo", + "issues": { + "out_of_blinker_fluid": { + "title": "The blinker fluid is empty and needs to be refilled", + "fix_flow": { + "step": { + "confirm": { + "title": "Blinker fluid needs to be refilled", + "description": "Press OK when blinker fluid has been refilled" + } + } + } + }, + "transmogrifier_deprecated": { + "title": "The transmogrifier component is deprecated", + "description": "The transmogrifier component is now deprecated due to the lack of local control available in the new API" + }, + "unfixable_problem": { + "title": "This is not a fixable problem", + "description": "This issue is never going to give up." + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/demo/translations/en.json b/homeassistant/components/demo/translations/en.json index 2e70c88962a..326e741f764 100644 --- a/homeassistant/components/demo/translations/en.json +++ b/homeassistant/components/demo/translations/en.json @@ -1,6 +1,30 @@ { + "issues": { + "out_of_blinker_fluid": { + "fix_flow": { + "step": { + "confirm": { + "description": "Press OK when blinker fluid has been refilled", + "title": "Blinker fluid needs to be refilled" + } + } + }, + "title": "The blinker fluid is empty and needs to be refilled" + }, + "transmogrifier_deprecated": { + "description": "The transmogrifier component is now deprecated due to the lack of local control available in the new API", + "title": "The transmogrifier component is deprecated" + }, + "unfixable_problem": { + "description": "This issue is never going to give up.", + "title": "This is not a fixable problem" + } + }, "options": { "step": { + "init": { + "data": {} + }, "options_1": { "data": { "bool": "Optional boolean", diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py index a1f520808f6..9c4f75f1b2d 100644 --- a/script/hassfest/translations.py +++ b/script/hassfest/translations.py @@ -21,7 +21,7 @@ REMOVED = 2 RE_REFERENCE = r"\[\%key:(.+)\%\]" -# Only allow translatino of integration names if they contain non-brand names +# Only allow translation of integration names if they contain non-brand names ALLOW_NAME_TRANSLATION = { "cert_expiry", "cpuspeed", @@ -185,7 +185,7 @@ def gen_data_entry_schema( return vol.All(*validators) -def gen_strings_schema(config: Config, integration: Integration): +def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: """Generate a strings schema.""" return vol.Schema( { @@ -227,6 +227,25 @@ def gen_strings_schema(config: Config, integration: Integration): vol.Optional("application_credentials"): { vol.Optional("description"): cv.string_with_no_html, }, + vol.Optional("issues"): { + str: vol.All( + cv.has_at_least_one_key("description", "fix_flow"), + vol.Schema( + { + vol.Required("title"): cv.string_with_no_html, + vol.Exclusive( + "description", "fixable" + ): cv.string_with_no_html, + vol.Exclusive("fix_flow", "fixable"): gen_data_entry_schema( + config=config, + integration=integration, + flow_title=UNDEFINED, + require_step_title=False, + ), + }, + ), + ) + }, } ) diff --git a/tests/components/demo/test_init.py b/tests/components/demo/test_init.py index fa0aff8223b..85ff2a16405 100644 --- a/tests/components/demo/test_init.py +++ b/tests/components/demo/test_init.py @@ -1,6 +1,7 @@ """The tests for the Demo component.""" +from http import HTTPStatus import json -from unittest.mock import patch +from unittest.mock import ANY, patch import pytest @@ -69,3 +70,133 @@ async def test_demo_statistics(hass, recorder_mock): "statistic_id": "demo:energy_consumption", "unit_of_measurement": "kWh", } in statistic_ids + + +async def test_issues_created(hass, hass_client, hass_ws_client): + """Test issues are created and can be fixed.""" + assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + await hass.async_block_till_done() + await hass.async_start() + + ws_client = await hass_ws_client(hass) + client = await hass_client() + + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + assert msg["result"] == { + "issues": [ + { + "breaks_in_ha_version": "2023.1.1", + "created": ANY, + "dismissed_version": None, + "domain": "demo", + "ignored": False, + "is_fixable": False, + "issue_id": "transmogrifier_deprecated", + "learn_more_url": "https://en.wiktionary.org/wiki/transmogrifier", + "severity": "warning", + "translation_key": "transmogrifier_deprecated", + "translation_placeholders": None, + }, + { + "breaks_in_ha_version": "2023.1.1", + "created": ANY, + "dismissed_version": None, + "domain": "demo", + "ignored": False, + "is_fixable": True, + "issue_id": "out_of_blinker_fluid", + "learn_more_url": "https://www.youtube.com/watch?v=b9rntRxLlbU", + "severity": "critical", + "translation_key": "out_of_blinker_fluid", + "translation_placeholders": None, + }, + { + "breaks_in_ha_version": None, + "created": ANY, + "dismissed_version": None, + "domain": "demo", + "ignored": False, + "is_fixable": False, + "issue_id": "unfixable_problem", + "learn_more_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", + "severity": "warning", + "translation_key": "unfixable_problem", + "translation_placeholders": None, + }, + ] + } + + url = "/api/repairs/issues/fix" + resp = await client.post( + url, json={"handler": "demo", "issue_id": "out_of_blinker_fluid"} + ) + + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data == { + "data_schema": [], + "description_placeholders": None, + "errors": None, + "flow_id": ANY, + "handler": "demo", + "last_step": None, + "step_id": "confirm", + "type": "form", + } + + url = f"/api/repairs/issues/fix/{flow_id}" + resp = await client.post(url) + + assert resp.status == HTTPStatus.OK + data = await resp.json() + + flow_id = data["flow_id"] + assert data == { + "description": None, + "description_placeholders": None, + "flow_id": flow_id, + "handler": "demo", + "title": "Fixed issue", + "type": "create_entry", + "version": 1, + } + + await ws_client.send_json({"id": 4, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + assert msg["result"] == { + "issues": [ + { + "breaks_in_ha_version": "2023.1.1", + "created": ANY, + "dismissed_version": None, + "domain": "demo", + "ignored": False, + "is_fixable": False, + "issue_id": "transmogrifier_deprecated", + "learn_more_url": "https://en.wiktionary.org/wiki/transmogrifier", + "severity": "warning", + "translation_key": "transmogrifier_deprecated", + "translation_placeholders": None, + }, + { + "breaks_in_ha_version": None, + "created": ANY, + "dismissed_version": None, + "domain": "demo", + "ignored": False, + "is_fixable": False, + "issue_id": "unfixable_problem", + "learn_more_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", + "severity": "warning", + "translation_key": "unfixable_problem", + "translation_placeholders": None, + }, + ] + }