From 45241e57ca518e453be3707fc47ccc24df0d5eef Mon Sep 17 00:00:00 2001
From: elmurato <1382097+elmurato@users.noreply.github.com>
Date: Tue, 24 Mar 2020 00:51:13 +0100
Subject: [PATCH] Add support for Minecraft SRV records (#32372)

* Added support for Minecraft SRV records

* Switched from dnspython to aiodns, improved server ping and log messages, use address instead of host and port in config flow

* Updated component requirements
---
 .coveragerc                                   |   1 +
 .../components/minecraft_server/__init__.py   |  48 ++++--
 .../minecraft_server/config_flow.py           |  85 ++++++---
 .../components/minecraft_server/const.py      |   4 +-
 .../components/minecraft_server/helpers.py    |  32 ++++
 .../components/minecraft_server/manifest.json |   2 +-
 .../components/minecraft_server/strings.json  |   3 +-
 requirements_all.txt                          |   1 +
 requirements_test_all.txt                     |   4 +
 .../minecraft_server/test_config_flow.py      | 161 ++++++++++++------
 10 files changed, 245 insertions(+), 96 deletions(-)
 create mode 100644 homeassistant/components/minecraft_server/helpers.py

diff --git a/.coveragerc b/.coveragerc
index 09ff6115ce2..cc04fb03456 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -425,6 +425,7 @@ omit =
     homeassistant/components/minecraft_server/__init__.py
     homeassistant/components/minecraft_server/binary_sensor.py
     homeassistant/components/minecraft_server/const.py
+    homeassistant/components/minecraft_server/helpers.py
     homeassistant/components/minecraft_server/sensor.py
     homeassistant/components/minio/*
     homeassistant/components/mitemp_bt/sensor.py
diff --git a/homeassistant/components/minecraft_server/__init__.py b/homeassistant/components/minecraft_server/__init__.py
index a025c44e33c..3a8598d3fac 100644
--- a/homeassistant/components/minecraft_server/__init__.py
+++ b/homeassistant/components/minecraft_server/__init__.py
@@ -18,6 +18,7 @@ from homeassistant.helpers.entity import Entity
 from homeassistant.helpers.event import async_track_time_interval
 from homeassistant.helpers.typing import ConfigType, HomeAssistantType
 
+from . import helpers
 from .const import DOMAIN, MANUFACTURER, SCAN_INTERVAL, SIGNAL_NAME_PREFIX
 
 PLATFORMS = ["binary_sensor", "sensor"]
@@ -37,10 +38,9 @@ async def async_setup_entry(hass: HomeAssistantType, config_entry: ConfigEntry)
     # Create and store server instance.
     unique_id = config_entry.unique_id
     _LOGGER.debug(
-        "Creating server instance for '%s' (host='%s', port=%s)",
+        "Creating server instance for '%s' (%s)",
         config_entry.data[CONF_NAME],
         config_entry.data[CONF_HOST],
-        config_entry.data[CONF_PORT],
     )
     server = MinecraftServer(hass, unique_id, config_entry.data)
     domain_data[unique_id] = server
@@ -82,7 +82,6 @@ class MinecraftServer:
     """Representation of a Minecraft server."""
 
     # Private constants
-    _MAX_RETRIES_PING = 3
     _MAX_RETRIES_STATUS = 3
 
     def __init__(
@@ -98,6 +97,7 @@ class MinecraftServer:
         self.port = config_data[CONF_PORT]
         self.online = False
         self._last_status_request_failed = False
+        self.srv_record_checked = False
 
         # 3rd party library instance
         self._mc_status = MCStatus(self.host, self.port)
@@ -127,15 +127,36 @@ class MinecraftServer:
         self._stop_periodic_update()
 
     async def async_check_connection(self) -> None:
-        """Check server connection using a 'ping' request and store result."""
+        """Check server connection using a 'status' request and store connection status."""
+        # Check if host is a valid SRV record, if not already done.
+        if not self.srv_record_checked:
+            self.srv_record_checked = True
+            srv_record = await helpers.async_check_srv_record(self._hass, self.host)
+            if srv_record is not None:
+                _LOGGER.debug(
+                    "'%s' is a valid Minecraft SRV record ('%s:%s')",
+                    self.host,
+                    srv_record[CONF_HOST],
+                    srv_record[CONF_PORT],
+                )
+                # Overwrite host, port and 3rd party library instance
+                # with data extracted out of SRV record.
+                self.host = srv_record[CONF_HOST]
+                self.port = srv_record[CONF_PORT]
+                self._mc_status = MCStatus(self.host, self.port)
+
+        # Ping the server with a status request.
         try:
             await self._hass.async_add_executor_job(
-                self._mc_status.ping, self._MAX_RETRIES_PING
+                self._mc_status.status, self._MAX_RETRIES_STATUS
             )
             self.online = True
         except OSError as error:
             _LOGGER.debug(
-                "Error occurred while trying to ping the server - OSError: %s", error
+                "Error occurred while trying to check the connection to '%s:%s' - OSError: %s",
+                self.host,
+                self.port,
+                error,
             )
             self.online = False
 
@@ -148,9 +169,9 @@ class MinecraftServer:
 
         # Inform user once about connection state changes if necessary.
         if server_online_old and not server_online:
-            _LOGGER.warning("Connection to server lost")
+            _LOGGER.warning("Connection to '%s:%s' lost", self.host, self.port)
         elif not server_online_old and server_online:
-            _LOGGER.info("Connection to server (re-)established")
+            _LOGGER.info("Connection to '%s:%s' (re-)established", self.host, self.port)
 
         # Update the server properties if server is online.
         if server_online:
@@ -179,7 +200,11 @@ class MinecraftServer:
 
             # Inform user once about successful update if necessary.
             if self._last_status_request_failed:
-                _LOGGER.info("Updating the server properties succeeded again")
+                _LOGGER.info(
+                    "Updating the properties of '%s:%s' succeeded again",
+                    self.host,
+                    self.port,
+                )
             self._last_status_request_failed = False
         except OSError as error:
             # No answer to request, set all properties to unknown.
@@ -193,7 +218,10 @@ class MinecraftServer:
             # Inform user once about failed update if necessary.
             if not self._last_status_request_failed:
                 _LOGGER.warning(
-                    "Updating the server properties failed - OSError: %s", error,
+                    "Updating the properties of '%s:%s' failed - OSError: %s",
+                    self.host,
+                    self.port,
+                    error,
                 )
             self._last_status_request_failed = True
 
diff --git a/homeassistant/components/minecraft_server/config_flow.py b/homeassistant/components/minecraft_server/config_flow.py
index 8c6049a2c1b..a7cb0371f67 100644
--- a/homeassistant/components/minecraft_server/config_flow.py
+++ b/homeassistant/components/minecraft_server/config_flow.py
@@ -9,7 +9,7 @@ from homeassistant import config_entries
 from homeassistant.config_entries import ConfigFlow
 from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT
 
-from . import MinecraftServer
+from . import MinecraftServer, helpers
 from .const import (  # pylint: disable=unused-import
     DEFAULT_HOST,
     DEFAULT_NAME,
@@ -29,11 +29,24 @@ class MinecraftServerConfigFlow(ConfigFlow, domain=DOMAIN):
         errors = {}
 
         if user_input is not None:
-            # User inputs.
-            host = user_input[CONF_HOST]
-            port = user_input[CONF_PORT]
+            host = None
+            port = DEFAULT_PORT
+            # Split address at last occurrence of ':'.
+            address_left, separator, address_right = user_input[CONF_HOST].rpartition(
+                ":"
+            )
+            # If no separator is found, 'rpartition' return ('', '', original_string).
+            if separator == "":
+                host = address_right
+            else:
+                host = address_left
+                try:
+                    port = int(address_right)
+                except ValueError:
+                    pass  # 'port' is already set to default value.
 
-            unique_id = ""
+            # Remove '[' and ']' in case of an IPv6 address.
+            host = host.strip("[]")
 
             # Check if 'host' is a valid IP address and if so, get the MAC address.
             ip_address = None
@@ -42,6 +55,7 @@ class MinecraftServerConfigFlow(ConfigFlow, domain=DOMAIN):
                 ip_address = ipaddress.ip_address(host)
             except ValueError:
                 # Host is not a valid IP address.
+                # Continue with host and port.
                 pass
             else:
                 # Host is a valid IP address.
@@ -55,38 +69,56 @@ class MinecraftServerConfigFlow(ConfigFlow, domain=DOMAIN):
                     partial(getmac.get_mac_address, **params)
                 )
 
-            # Validate IP address via valid MAC address.
+            # Validate IP address (MAC address must be available).
             if ip_address is not None and mac_address is None:
                 errors["base"] = "invalid_ip"
             # Validate port configuration (limit to user and dynamic port range).
             elif (port < 1024) or (port > 65535):
                 errors["base"] = "invalid_port"
-            # Validate host and port via ping request to server.
+            # Validate host and port by checking the server connection.
             else:
-                # Build unique_id.
-                if ip_address is not None:
-                    # Since IP addresses can change and therefore are not allowed in a
-                    # unique_id, fall back to the MAC address.
-                    unique_id = f"{mac_address}-{port}"
-                else:
-                    # Use host name in unique_id (host names should not change).
-                    unique_id = f"{host}-{port}"
-
-                # Abort in case the host was already configured before.
-                await self.async_set_unique_id(unique_id)
-                self._abort_if_unique_id_configured()
-
-                # Create server instance with configuration data and try pinging the server.
-                server = MinecraftServer(self.hass, unique_id, user_input)
+                # Create server instance with configuration data and ping the server.
+                config_data = {
+                    CONF_NAME: user_input[CONF_NAME],
+                    CONF_HOST: host,
+                    CONF_PORT: port,
+                }
+                server = MinecraftServer(self.hass, "dummy_unique_id", config_data)
                 await server.async_check_connection()
                 if not server.online:
                     # Host or port invalid or server not reachable.
                     errors["base"] = "cannot_connect"
                 else:
+                    # Build unique_id and config entry title.
+                    unique_id = ""
+                    title = f"{host}:{port}"
+                    if ip_address is not None:
+                        # Since IP addresses can change and therefore are not allowed in a
+                        # unique_id, fall back to the MAC address and port (to support
+                        # servers with same MAC address but different ports).
+                        unique_id = f"{mac_address}-{port}"
+                        if ip_address.version == 6:
+                            title = f"[{host}]:{port}"
+                    else:
+                        # Check if 'host' is a valid SRV record.
+                        srv_record = await helpers.async_check_srv_record(
+                            self.hass, host
+                        )
+                        if srv_record is not None:
+                            # Use only SRV host name in unique_id (does not change).
+                            unique_id = f"{host}-srv"
+                            title = host
+                        else:
+                            # Use host name and port in unique_id (to support servers with
+                            # same host name but different ports).
+                            unique_id = f"{host}-{port}"
+
+                    # Abort in case the host was already configured before.
+                    await self.async_set_unique_id(unique_id)
+                    self._abort_if_unique_id_configured()
+
                     # Configuration data are available and no error was detected, create configuration entry.
-                    return self.async_create_entry(
-                        title=f"{host}:{port}", data=user_input
-                    )
+                    return self.async_create_entry(title=title, data=config_data)
 
         # Show configuration form (default form in case of no user_input,
         # form filled with user_input and eventually with errors otherwise).
@@ -107,9 +139,6 @@ class MinecraftServerConfigFlow(ConfigFlow, domain=DOMAIN):
                     vol.Required(
                         CONF_HOST, default=user_input.get(CONF_HOST, DEFAULT_HOST)
                     ): vol.All(str, vol.Lower),
-                    vol.Optional(
-                        CONF_PORT, default=user_input.get(CONF_PORT, DEFAULT_PORT)
-                    ): int,
                 }
             ),
             errors=errors,
diff --git a/homeassistant/components/minecraft_server/const.py b/homeassistant/components/minecraft_server/const.py
index d86faf23a81..52e6ae8fd5e 100644
--- a/homeassistant/components/minecraft_server/const.py
+++ b/homeassistant/components/minecraft_server/const.py
@@ -2,7 +2,7 @@
 
 ATTR_PLAYERS_LIST = "players_list"
 
-DEFAULT_HOST = "localhost"
+DEFAULT_HOST = "localhost:25565"
 DEFAULT_NAME = "Minecraft Server"
 DEFAULT_PORT = 25565
 
@@ -30,6 +30,8 @@ SCAN_INTERVAL = 60
 
 SIGNAL_NAME_PREFIX = f"signal_{DOMAIN}"
 
+SRV_RECORD_PREFIX = "_minecraft._tcp"
+
 UNIT_PLAYERS_MAX = "players"
 UNIT_PLAYERS_ONLINE = "players"
 UNIT_PROTOCOL_VERSION = None
diff --git a/homeassistant/components/minecraft_server/helpers.py b/homeassistant/components/minecraft_server/helpers.py
new file mode 100644
index 00000000000..7f9380cdec2
--- /dev/null
+++ b/homeassistant/components/minecraft_server/helpers.py
@@ -0,0 +1,32 @@
+"""Helper functions for the Minecraft Server integration."""
+
+from typing import Any, Dict
+
+import aiodns
+
+from homeassistant.const import CONF_HOST, CONF_PORT
+from homeassistant.helpers.typing import HomeAssistantType
+
+from .const import SRV_RECORD_PREFIX
+
+
+async def async_check_srv_record(hass: HomeAssistantType, host: str) -> Dict[str, Any]:
+    """Check if the given host is a valid Minecraft SRV record."""
+    # Check if 'host' is a valid SRV record.
+    return_value = None
+    srv_records = None
+    try:
+        srv_records = await aiodns.DNSResolver().query(
+            host=f"{SRV_RECORD_PREFIX}.{host}", qtype="SRV"
+        )
+    except (aiodns.error.DNSError):
+        # 'host' is not a SRV record.
+        pass
+    else:
+        # 'host' is a valid SRV record, extract the data.
+        return_value = {
+            CONF_HOST: srv_records[0].host,
+            CONF_PORT: srv_records[0].port,
+        }
+
+    return return_value
diff --git a/homeassistant/components/minecraft_server/manifest.json b/homeassistant/components/minecraft_server/manifest.json
index 1dda76dee77..0811c168f9f 100644
--- a/homeassistant/components/minecraft_server/manifest.json
+++ b/homeassistant/components/minecraft_server/manifest.json
@@ -3,7 +3,7 @@
   "name": "Minecraft Server",
   "config_flow": true,
   "documentation": "https://www.home-assistant.io/integrations/minecraft_server",
-  "requirements": ["getmac==0.8.1", "mcstatus==2.3.0"],
+  "requirements": ["aiodns==2.0.0", "getmac==0.8.1", "mcstatus==2.3.0"],
   "dependencies": [],
   "codeowners": ["@elmurato"],
   "quality_scale": "silver"
diff --git a/homeassistant/components/minecraft_server/strings.json b/homeassistant/components/minecraft_server/strings.json
index 7743d940be6..3a2408694ad 100644
--- a/homeassistant/components/minecraft_server/strings.json
+++ b/homeassistant/components/minecraft_server/strings.json
@@ -7,8 +7,7 @@
                 "description": "Set up your Minecraft Server instance to allow monitoring.",
                 "data": {
                     "name": "Name",
-                    "host": "Host",
-                    "port": "Port"
+                    "host": "Host"
                 }
             }
         },
diff --git a/requirements_all.txt b/requirements_all.txt
index 72bc9d27e7e..00a7a0a0073 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -152,6 +152,7 @@ aioautomatic==0.6.5
 aiobotocore==0.11.1
 
 # homeassistant.components.dnsip
+# homeassistant.components.minecraft_server
 aiodns==2.0.0
 
 # homeassistant.components.esphome
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index f8ed63bf8b3..231dfcd0cc7 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -58,6 +58,10 @@ aioautomatic==0.6.5
 # homeassistant.components.aws
 aiobotocore==0.11.1
 
+# homeassistant.components.dnsip
+# homeassistant.components.minecraft_server
+aiodns==2.0.0
+
 # homeassistant.components.esphome
 aioesphomeapi==2.6.1
 
diff --git a/tests/components/minecraft_server/test_config_flow.py b/tests/components/minecraft_server/test_config_flow.py
index 30626fbdcb0..bc49ed08109 100644
--- a/tests/components/minecraft_server/test_config_flow.py
+++ b/tests/components/minecraft_server/test_config_flow.py
@@ -1,5 +1,8 @@
 """Test the Minecraft Server config flow."""
 
+import asyncio
+
+import aiodns
 from asynctest import patch
 from mcstatus.pinger import PingResponse
 
@@ -19,6 +22,19 @@ from homeassistant.helpers.typing import HomeAssistantType
 
 from tests.common import MockConfigEntry
 
+
+class QueryMock:
+    """Mock for result of aiodns.DNSResolver.query."""
+
+    def __init__(self):
+        """Set up query result mock."""
+        self.host = "mc.dummyserver.com"
+        self.port = 23456
+        self.priority = 1
+        self.weight = 1
+        self.ttl = None
+
+
 STATUS_RESPONSE_RAW = {
     "description": {"text": "Dummy Description"},
     "version": {"name": "Dummy Version", "protocol": 123},
@@ -35,34 +51,34 @@ STATUS_RESPONSE_RAW = {
 
 USER_INPUT = {
     CONF_NAME: DEFAULT_NAME,
-    CONF_HOST: "mc.dummyserver.com",
-    CONF_PORT: DEFAULT_PORT,
+    CONF_HOST: f"mc.dummyserver.com:{DEFAULT_PORT}",
 }
 
+USER_INPUT_SRV = {CONF_NAME: DEFAULT_NAME, CONF_HOST: "dummyserver.com"}
+
 USER_INPUT_IPV4 = {
     CONF_NAME: DEFAULT_NAME,
-    CONF_HOST: "1.1.1.1",
-    CONF_PORT: DEFAULT_PORT,
+    CONF_HOST: f"1.1.1.1:{DEFAULT_PORT}",
 }
 
 USER_INPUT_IPV6 = {
     CONF_NAME: DEFAULT_NAME,
-    CONF_HOST: "::ffff:0101:0101",
-    CONF_PORT: DEFAULT_PORT,
+    CONF_HOST: f"[::ffff:0101:0101]:{DEFAULT_PORT}",
 }
 
 USER_INPUT_PORT_TOO_SMALL = {
     CONF_NAME: DEFAULT_NAME,
-    CONF_HOST: "mc.dummyserver.com",
-    CONF_PORT: 1023,
+    CONF_HOST: f"mc.dummyserver.com:1023",
 }
 
 USER_INPUT_PORT_TOO_LARGE = {
     CONF_NAME: DEFAULT_NAME,
-    CONF_HOST: "mc.dummyserver.com",
-    CONF_PORT: 65536,
+    CONF_HOST: f"mc.dummyserver.com:65536",
 }
 
+SRV_RECORDS = asyncio.Future()
+SRV_RECORDS.set_result([QueryMock()])
+
 
 async def test_show_config_form(hass: HomeAssistantType) -> None:
     """Test if initial configuration form is shown."""
@@ -87,54 +103,96 @@ async def test_invalid_ip(hass: HomeAssistantType) -> None:
 
 async def test_same_host(hass: HomeAssistantType) -> None:
     """Test abort in case of same host name."""
-    unique_id = f"{USER_INPUT[CONF_HOST]}-{USER_INPUT[CONF_PORT]}"
-    mock_config_entry = MockConfigEntry(
-        domain=DOMAIN, unique_id=unique_id, data=USER_INPUT
-    )
-    mock_config_entry.add_to_hass(hass)
+    with patch(
+        "aiodns.DNSResolver.query", side_effect=aiodns.error.DNSError,
+    ):
+        with patch(
+            "mcstatus.server.MinecraftServer.status",
+            return_value=PingResponse(STATUS_RESPONSE_RAW),
+        ):
+            unique_id = "mc.dummyserver.com-25565"
+            config_data = {
+                CONF_NAME: DEFAULT_NAME,
+                CONF_HOST: "mc.dummyserver.com",
+                CONF_PORT: DEFAULT_PORT,
+            }
+            mock_config_entry = MockConfigEntry(
+                domain=DOMAIN, unique_id=unique_id, data=config_data
+            )
+            mock_config_entry.add_to_hass(hass)
 
-    result = await hass.config_entries.flow.async_init(
-        DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT
-    )
+            result = await hass.config_entries.flow.async_init(
+                DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT
+            )
 
-    assert result["type"] == RESULT_TYPE_ABORT
-    assert result["reason"] == "already_configured"
+            assert result["type"] == RESULT_TYPE_ABORT
+            assert result["reason"] == "already_configured"
 
 
 async def test_port_too_small(hass: HomeAssistantType) -> None:
     """Test error in case of a too small port."""
-    result = await hass.config_entries.flow.async_init(
-        DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT_PORT_TOO_SMALL
-    )
+    with patch(
+        "aiodns.DNSResolver.query", side_effect=aiodns.error.DNSError,
+    ):
+        result = await hass.config_entries.flow.async_init(
+            DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT_PORT_TOO_SMALL
+        )
 
-    assert result["type"] == RESULT_TYPE_FORM
-    assert result["errors"] == {"base": "invalid_port"}
+        assert result["type"] == RESULT_TYPE_FORM
+        assert result["errors"] == {"base": "invalid_port"}
 
 
 async def test_port_too_large(hass: HomeAssistantType) -> None:
     """Test error in case of a too large port."""
-    result = await hass.config_entries.flow.async_init(
-        DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT_PORT_TOO_LARGE
-    )
+    with patch(
+        "aiodns.DNSResolver.query", side_effect=aiodns.error.DNSError,
+    ):
+        result = await hass.config_entries.flow.async_init(
+            DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT_PORT_TOO_LARGE
+        )
 
-    assert result["type"] == RESULT_TYPE_FORM
-    assert result["errors"] == {"base": "invalid_port"}
+        assert result["type"] == RESULT_TYPE_FORM
+        assert result["errors"] == {"base": "invalid_port"}
 
 
 async def test_connection_failed(hass: HomeAssistantType) -> None:
     """Test error in case of a failed connection."""
-    with patch("mcstatus.server.MinecraftServer.ping", side_effect=OSError):
-        result = await hass.config_entries.flow.async_init(
-            DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT
-        )
+    with patch(
+        "aiodns.DNSResolver.query", side_effect=aiodns.error.DNSError,
+    ):
+        with patch("mcstatus.server.MinecraftServer.status", side_effect=OSError):
+            result = await hass.config_entries.flow.async_init(
+                DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT
+            )
 
-        assert result["type"] == RESULT_TYPE_FORM
-        assert result["errors"] == {"base": "cannot_connect"}
+            assert result["type"] == RESULT_TYPE_FORM
+            assert result["errors"] == {"base": "cannot_connect"}
+
+
+async def test_connection_succeeded_with_srv_record(hass: HomeAssistantType) -> None:
+    """Test config entry in case of a successful connection with a SRV record."""
+    with patch(
+        "aiodns.DNSResolver.query", return_value=SRV_RECORDS,
+    ):
+        with patch(
+            "mcstatus.server.MinecraftServer.status",
+            return_value=PingResponse(STATUS_RESPONSE_RAW),
+        ):
+            result = await hass.config_entries.flow.async_init(
+                DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT_SRV
+            )
+
+            assert result["type"] == RESULT_TYPE_CREATE_ENTRY
+            assert result["title"] == USER_INPUT_SRV[CONF_HOST]
+            assert result["data"][CONF_NAME] == USER_INPUT_SRV[CONF_NAME]
+            assert result["data"][CONF_HOST] == USER_INPUT_SRV[CONF_HOST]
 
 
 async def test_connection_succeeded_with_host(hass: HomeAssistantType) -> None:
     """Test config entry in case of a successful connection with a host name."""
-    with patch("mcstatus.server.MinecraftServer.ping", return_value=50):
+    with patch(
+        "aiodns.DNSResolver.query", side_effect=aiodns.error.DNSError,
+    ):
         with patch(
             "mcstatus.server.MinecraftServer.status",
             return_value=PingResponse(STATUS_RESPONSE_RAW),
@@ -144,16 +202,17 @@ async def test_connection_succeeded_with_host(hass: HomeAssistantType) -> None:
             )
 
             assert result["type"] == RESULT_TYPE_CREATE_ENTRY
-            assert result["title"] == f"{USER_INPUT[CONF_HOST]}:{USER_INPUT[CONF_PORT]}"
+            assert result["title"] == USER_INPUT[CONF_HOST]
             assert result["data"][CONF_NAME] == USER_INPUT[CONF_NAME]
-            assert result["data"][CONF_HOST] == USER_INPUT[CONF_HOST]
-            assert result["data"][CONF_PORT] == USER_INPUT[CONF_PORT]
+            assert result["data"][CONF_HOST] == "mc.dummyserver.com"
 
 
 async def test_connection_succeeded_with_ip4(hass: HomeAssistantType) -> None:
     """Test config entry in case of a successful connection with an IPv4 address."""
     with patch("getmac.get_mac_address", return_value="01:23:45:67:89:ab"):
-        with patch("mcstatus.server.MinecraftServer.ping", return_value=50):
+        with patch(
+            "aiodns.DNSResolver.query", side_effect=aiodns.error.DNSError,
+        ):
             with patch(
                 "mcstatus.server.MinecraftServer.status",
                 return_value=PingResponse(STATUS_RESPONSE_RAW),
@@ -163,19 +222,17 @@ async def test_connection_succeeded_with_ip4(hass: HomeAssistantType) -> None:
                 )
 
                 assert result["type"] == RESULT_TYPE_CREATE_ENTRY
-                assert (
-                    result["title"]
-                    == f"{USER_INPUT_IPV4[CONF_HOST]}:{USER_INPUT_IPV4[CONF_PORT]}"
-                )
+                assert result["title"] == USER_INPUT_IPV4[CONF_HOST]
                 assert result["data"][CONF_NAME] == USER_INPUT_IPV4[CONF_NAME]
-                assert result["data"][CONF_HOST] == USER_INPUT_IPV4[CONF_HOST]
-                assert result["data"][CONF_PORT] == USER_INPUT_IPV4[CONF_PORT]
+                assert result["data"][CONF_HOST] == "1.1.1.1"
 
 
 async def test_connection_succeeded_with_ip6(hass: HomeAssistantType) -> None:
     """Test config entry in case of a successful connection with an IPv6 address."""
     with patch("getmac.get_mac_address", return_value="01:23:45:67:89:ab"):
-        with patch("mcstatus.server.MinecraftServer.ping", return_value=50):
+        with patch(
+            "aiodns.DNSResolver.query", side_effect=aiodns.error.DNSError,
+        ):
             with patch(
                 "mcstatus.server.MinecraftServer.status",
                 return_value=PingResponse(STATUS_RESPONSE_RAW),
@@ -185,10 +242,6 @@ async def test_connection_succeeded_with_ip6(hass: HomeAssistantType) -> None:
                 )
 
                 assert result["type"] == RESULT_TYPE_CREATE_ENTRY
-                assert (
-                    result["title"]
-                    == f"{USER_INPUT_IPV6[CONF_HOST]}:{USER_INPUT_IPV6[CONF_PORT]}"
-                )
+                assert result["title"] == USER_INPUT_IPV6[CONF_HOST]
                 assert result["data"][CONF_NAME] == USER_INPUT_IPV6[CONF_NAME]
-                assert result["data"][CONF_HOST] == USER_INPUT_IPV6[CONF_HOST]
-                assert result["data"][CONF_PORT] == USER_INPUT_IPV6[CONF_PORT]
+                assert result["data"][CONF_HOST] == "::ffff:0101:0101"
-- 
GitLab