diff --git a/homeassistant/components/ecowitt/__init__.py b/homeassistant/components/ecowitt/__init__.py index 71d42643cfb..ebd861c1377 100644 --- a/homeassistant/components/ecowitt/__init__.py +++ b/homeassistant/components/ecowitt/__init__.py @@ -2,32 +2,38 @@ from __future__ import annotations from aioecowitt import EcoWittListener +from aiohttp import web +from homeassistant.components import webhook from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_PORT, EVENT_HOMEASSISTANT_STOP, Platform -from homeassistant.core import Event, HomeAssistant +from homeassistant.const import CONF_WEBHOOK_ID, EVENT_HOMEASSISTANT_STOP, Platform +from homeassistant.core import Event, HomeAssistant, callback -from .const import CONF_PATH, DOMAIN +from .const import DOMAIN PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the Ecowitt component from UI.""" - hass.data.setdefault(DOMAIN, {}) - - ecowitt = hass.data[DOMAIN][entry.entry_id] = EcoWittListener( - port=entry.data[CONF_PORT], path=entry.data[CONF_PATH] - ) + ecowitt = hass.data.setdefault(DOMAIN, {})[entry.entry_id] = EcoWittListener() await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - await ecowitt.start() + async def handle_webhook( + hass: HomeAssistant, webhook_id: str, request: web.Request + ) -> web.Response: + """Handle webhook callback.""" + return await ecowitt.handler(request) - # Close on shutdown - async def _stop_ecowitt(_: Event): + webhook.async_register( + hass, DOMAIN, entry.title, entry.data[CONF_WEBHOOK_ID], handle_webhook + ) + + @callback + def _stop_ecowitt(_: Event): """Stop the Ecowitt listener.""" - await ecowitt.stop() + webhook.async_unregister(hass, entry.data[CONF_WEBHOOK_ID]) entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _stop_ecowitt) @@ -38,9 +44,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" - ecowitt = hass.data[DOMAIN][entry.entry_id] - await ecowitt.stop() - if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): hass.data[DOMAIN].pop(entry.entry_id) diff --git a/homeassistant/components/ecowitt/config_flow.py b/homeassistant/components/ecowitt/config_flow.py index c3406652665..2b6744790bf 100644 --- a/homeassistant/components/ecowitt/config_flow.py +++ b/homeassistant/components/ecowitt/config_flow.py @@ -1,77 +1,50 @@ """Config flow for ecowitt.""" from __future__ import annotations -import logging import secrets from typing import Any -from aioecowitt import EcoWittListener -import voluptuous as vol +from yarl import URL from homeassistant import config_entries -from homeassistant.const import CONF_PORT -from homeassistant.core import HomeAssistant +from homeassistant.components import webhook +from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.data_entry_flow import FlowResult from homeassistant.exceptions import HomeAssistantError -import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.network import get_url -from .const import CONF_PATH, DEFAULT_PORT, DOMAIN - -_LOGGER = logging.getLogger(__name__) - - -STEP_USER_DATA_SCHEMA = vol.Schema( - { - vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, - vol.Optional(CONF_PATH, default=f"/{secrets.token_urlsafe(16)}"): cv.string, - } -) - - -async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, str]: - """Validate user input.""" - # Check if the port is in use - try: - listener = EcoWittListener(port=data[CONF_PORT]) - await listener.start() - await listener.stop() - except OSError: - raise InvalidPort from None - - return {"title": f"Ecowitt on port {data[CONF_PORT]}"} +from .const import DOMAIN class EcowittConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for the Ecowitt.""" VERSION = 1 + _webhook_id: str async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle the initial step.""" if user_input is None: + self._webhook_id = secrets.token_hex(16) return self.async_show_form( - step_id="user", data_schema=STEP_USER_DATA_SCHEMA + step_id="user", ) - errors = {} + base_url = URL(get_url(self.hass)) + assert base_url.host - # Check if the port is in use by another config entry - self._async_abort_entries_match({CONF_PORT: user_input[CONF_PORT]}) - - try: - info = await validate_input(self.hass, user_input) - except InvalidPort: - errors["base"] = "invalid_port" - except Exception: # pylint: disable=broad-except - _LOGGER.exception("Unexpected exception") - errors["base"] = "unknown" - else: - return self.async_create_entry(title=info["title"], data=user_input) - - return self.async_show_form( - step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors + return self.async_create_entry( + title="Ecowitt", + data={ + CONF_WEBHOOK_ID: self._webhook_id, + }, + description_placeholders={ + "path": webhook.async_generate_path(self._webhook_id), + "server": base_url.host, + "port": str(base_url.port), + }, ) diff --git a/homeassistant/components/ecowitt/const.py b/homeassistant/components/ecowitt/const.py index a7011f24e0c..3c8dcca8723 100644 --- a/homeassistant/components/ecowitt/const.py +++ b/homeassistant/components/ecowitt/const.py @@ -1,7 +1,3 @@ """Constants used by ecowitt component.""" DOMAIN = "ecowitt" - -DEFAULT_PORT = 49199 - -CONF_PATH = "path" diff --git a/homeassistant/components/ecowitt/manifest.json b/homeassistant/components/ecowitt/manifest.json index cafd140828e..348df17b0cd 100644 --- a/homeassistant/components/ecowitt/manifest.json +++ b/homeassistant/components/ecowitt/manifest.json @@ -1,6 +1,6 @@ { "domain": "ecowitt", - "name": "Ecowitt Weather Station", + "name": "Ecowitt", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/ecowitt", "requirements": ["aioecowitt==2022.08.3"], diff --git a/homeassistant/components/ecowitt/strings.json b/homeassistant/components/ecowitt/strings.json index a4e12e69d57..cca51c1129e 100644 --- a/homeassistant/components/ecowitt/strings.json +++ b/homeassistant/components/ecowitt/strings.json @@ -1,17 +1,12 @@ { "config": { - "error": { - "invalid_port": "Port is already used.", - "unknown": "[%key:common::config_flow::error::unknown%]" - }, "step": { "user": { - "description": "The following steps must be performed to set up this integration.\n\nUse the Ecowitt App (on your phone) or access the Ecowitt WebUI in a browser at the station IP address.\nPick your station -> Menu Others -> DIY Upload Servers.\nHit next and select 'Customized'\n\nPick the protocol Ecowitt, and put in the ip/hostname of your hass server.\nPath have to match, you can copy with secure token /.\nSave configuration. The Ecowitt should then start attempting to send data to your server.", - "data": { - "port": "Listening port", - "path": "Path with Security token" - } + "description": "Are you sure you want to set up Ecowitt?" } + }, + "create_entry": { + "default": "To finish setting up the integration, use the Ecowitt App (on your phone) or access the Ecowitt WebUI in a browser at the station IP address.\n\nPick your station -> Menu Others -> DIY Upload Servers. Hit next and select 'Customized'\n\n- Server IP: `{server}`\n- Path: `{path}`\n- Port: `{port}`\n\nClick on 'Save'." } } } diff --git a/homeassistant/components/ecowitt/translations/en.json b/homeassistant/components/ecowitt/translations/en.json index 041fa9f697b..b8ce69c10b8 100644 --- a/homeassistant/components/ecowitt/translations/en.json +++ b/homeassistant/components/ecowitt/translations/en.json @@ -1,17 +1,12 @@ { "config": { - "error": { - "invalid_port": "Port is already used.", - "unknown": "Unknown error." - }, - "step": { - "user": { - "description": "The following steps must be performed to set up this integration.\n\nUse the Ecowitt App (on your phone) or your Ecowitt WebUI over the station IP address.\nPick your station -> Menu Others -> DIY Upload Servers.\nHit next and select 'Customized'\n\nPick the protocol Ecowitt, and put in the ip/hostname of your hass server.\nPath have to match, you can copy with secure token /.\nSave configuration. The Ecowitt should then start attempting to send data to your server.", - "data": { - "port": "Listening port", - "path": "Path with Security token" - } - } - } + "create_entry": { + "default": "To finish setting up the integration, use the Ecowitt App (on your phone) or access the Ecowitt WebUI in a browser at the station IP address.\n\nPick your station -> Menu Others -> DIY Upload Servers. Hit next and select 'Customized'\n\n- Server IP: `{server}`\n- Path: `{path}`\n- Port: `{port}`\n\nClick on 'Save'." + }, + "step": { + "user": { + "description": "Are you sure you want to set up Ecowitt?" + } + } } -} +} \ No newline at end of file diff --git a/tests/components/ecowitt/test_config_flow.py b/tests/components/ecowitt/test_config_flow.py index 6ddd475121b..c09fb951b11 100644 --- a/tests/components/ecowitt/test_config_flow.py +++ b/tests/components/ecowitt/test_config_flow.py @@ -5,12 +5,13 @@ from homeassistant import config_entries from homeassistant.components.ecowitt.const import DOMAIN from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType - -from tests.common import MockConfigEntry +from homeassistant.setup import async_setup_component async def test_create_entry(hass: HomeAssistant) -> None: """Test we can create a config entry.""" + await async_setup_component(hass, "http", {}) + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -18,93 +19,18 @@ async def test_create_entry(hass: HomeAssistant) -> None: assert result["errors"] is None with patch( - "homeassistant.components.ecowitt.config_flow.EcoWittListener.start" - ), patch( - "homeassistant.components.ecowitt.config_flow.EcoWittListener.stop" - ), patch( "homeassistant.components.ecowitt.async_setup_entry", return_value=True, ) as mock_setup_entry: result2 = await hass.config_entries.flow.async_configure( result["flow_id"], - { - "port": 49911, - "path": "/ecowitt-station", - }, + {}, ) await hass.async_block_till_done() assert result2["type"] == FlowResultType.CREATE_ENTRY - assert result2["title"] == "Ecowitt on port 49911" + assert result2["title"] == "Ecowitt" assert result2["data"] == { - "port": 49911, - "path": "/ecowitt-station", + "webhook_id": result2["description_placeholders"]["path"].split("/")[-1], } assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_form_invalid_port(hass: HomeAssistant) -> None: - """Test we handle invalid port.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - with patch( - "homeassistant.components.ecowitt.config_flow.EcoWittListener.start", - side_effect=OSError, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "port": 49911, - "path": "/ecowitt-station", - }, - ) - - assert result2["type"] == FlowResultType.FORM - assert result2["errors"] == {"base": "invalid_port"} - - -async def test_already_configured_port(hass: HomeAssistant) -> None: - """Test already configured port.""" - MockConfigEntry(domain=DOMAIN, data={"port": 49911}).add_to_hass(hass) - - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - with patch( - "homeassistant.components.ecowitt.config_flow.EcoWittListener.start", - side_effect=OSError, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "port": 49911, - "path": "/ecowitt-station", - }, - ) - - assert result2["type"] == FlowResultType.ABORT - - -async def test_unknown_error(hass: HomeAssistant) -> None: - """Test we handle unknown error.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - with patch( - "homeassistant.components.ecowitt.config_flow.EcoWittListener.start", - side_effect=Exception(), - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "port": 49911, - "path": "/ecowitt-station", - }, - ) - - assert result2["type"] == FlowResultType.FORM - assert result2["errors"] == {"base": "unknown"}