Repository: XKNX/xknx (python)

Issue:
Separate discovery multicast group from routing group
Discovery shall always happen on `224.0.23.12:3671` - even if routing communication is set to use a different multicast group.

Tested with Weinzierl 752 and Enertex Secure Router both set to use `224.0.23.13`. `SearchRequest`s to ...13 are ignored. Requests to the default group are answered. The configured multicast group is listed in `DIBDeviceInformation`.

<img width="903" alt="Bildschirm­foto 2022-10-28 um 11 25 42" src="https://user-images.githubusercontent.com/12422879/198553811-63f99a1e-4c1d-4104-9327-08566e6c0fd9.png">


Pull request:
Routing multicast group in ConnectionConfig
<!--
  You are awesome! Thanks for contributing to our project!
  Please, DO NOT DELETE ANY TEXT from this template!.
-->
## Description
<!--
Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context.
List any dependencies that are required for this change.
-->
Allow setting Routing multicast group in ConnectionConfig

Fixes #1061 

## Type of change
<!--
Please tick the applicable options.
NOTE: Ticking multiple options most likely indicates
that your change is to big and it is suggested to split it into several smaller PRs.
-->

- [x] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update

## Checklist:

- [x] The documentation has been adjusted accordingly
- [ ] Tests have been added that prove the fix is effective or that the feature works
- [x] The changes are documented in the changelog (docs/changelog.md)

Patch:
diff --git a/docs/changelog.md b/docs/changelog.md
index d99e2dd1..64064b73 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -8,6 +8,10 @@ nav_order: 2
 
 # Unreleased changes
 
+### Connection
+
+- GatewayScanFilter now also matches secure enabled gateways by default. The `secure` argument as been replaced by `secure_tunnelling` and `secure_routing` arguments. When multiple methods are `True` a gateway is matched if one of them is supported. Non-secure methods don't match if secure is required for that gateway.
+
 ### Features
 
 - Add support for python 3.11
@@ -23,6 +27,7 @@ nav_order: 2
 # Bugfixes
 
 - No mutable default arguments. Fixes unexpected behaviour like GatewayScanner not finding all interfaces.
+- Separate discovery multicast group from routing group. Add `multicast_group` and `multicast_port` `ConnectionConfig` parameters.
 
 # 1.2.0 Secure Routing 2022-10-10
 
diff --git a/docs/xknx.md b/docs/xknx.md
index bfb7d21a..f8f75dff 100644
--- a/docs/xknx.md
+++ b/docs/xknx.md
@@ -46,8 +46,8 @@ The constructor of the XKNX object takes several parameters:
 - `telegram_received_cb` is a callback which is called after every received KNX telegram. See [callbacks](#callbacks) documentation for details.
 - `device_updated_cb` is an async callback after a [XKNX device](#devices) was updated. See [callbacks](#callbacks) documentation for details.
 - `rate_limit` in telegrams per second - can be used to limit the outgoing traffic to the KNX/IP interface. The default value is 20 packets per second.
-- `multicast_group` is the multicast IP address - can be used to override the default multicast address (`224.0.23.12`)
-- `multicast_port` is the multicast port - can be used to override the default multicast port (`3671`)
+- `multicast_group` is the multicast group used for discovery - can be used to override the default multicast address (`224.0.23.12`)
+- `multicast_port` is the multicast port used for discovery - can be used to override the default multicast port (`3671`)
 - `log_directory` is the path to the log directory - when set to a valid directory we log to a dedicated file in this directory called `xknx.log`. The log files are rotated each night and will exist for 7 days. After that the oldest one will be deleted.
 - `state_updater` is used to set the default state-updating mechanism used by devices. `False` to  disable state-updating by default, `True` to use default 60 minutes expire-interval, a number between 2 to 1440 to configure expire-time or a string "expire 50", "every 90" for strict periodically update or "init" for update when a connection is established. Default: `False`.
 - if `daemon_mode` is set, start will only stop if Control-X is pressed. This function is useful for using XKNX as a daemon, e.g. for using the callback functions or using the internal action logic.
diff --git a/xknx/io/connection.py b/xknx/io/connection.py
index 1ac14ec4..4cac67b6 100644
--- a/xknx/io/connection.py
+++ b/xknx/io/connection.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 
 from enum import Enum, auto
 
-from .const import DEFAULT_MCAST_PORT
+from .const import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT
 from .gateway_scanner import GatewayScanFilter
 
 
@@ -34,6 +34,8 @@ class ConnectionConfig:
     * route_back: For TUNNELING connection.
         The KNXnet/IP Server shall use the IP address and port in the received IP package
         as the target IP address or port number for the response to the KNXnet/IP Client.
+    * multicast_group: Multicast group for KNXnet/IP routing.
+    * multicast_port: Multicast port for KNXnet/IP routing.
     * auto_reconnect: Auto reconnect to KNX/IP tunneling device if connection cannot be established.
     * auto_reconnect_wait: Wait n seconds before trying to reconnect to KNX/IP tunneling device.
     * scan_filter: For AUTOMATIC connection, limit scan with the given filter
@@ -50,6 +52,8 @@ class ConnectionConfig:
         gateway_ip: str | None = None,
         gateway_port: int = DEFAULT_MCAST_PORT,
         route_back: bool = False,
+        multicast_group: str = DEFAULT_MCAST_GRP,
+        multicast_port: int = DEFAULT_MCAST_PORT,
         auto_reconnect: bool = True,
         auto_reconnect_wait: int = 3,
         scan_filter: GatewayScanFilter | None = None,
@@ -63,6 +67,8 @@ class ConnectionConfig:
         self.gateway_ip = gateway_ip
         self.gateway_port = gateway_port
         self.route_back = route_back
+        self.multicast_group = multicast_group
+        self.multicast_port = multicast_port
         self.auto_reconnect = auto_reconnect
         self.auto_reconnect_wait = auto_reconnect_wait
         self.scan_filter = scan_filter or GatewayScanFilter()
diff --git a/xknx/io/gateway_scanner.py b/xknx/io/gateway_scanner.py
index 5b403cbb..e5ab9d6f 100644
--- a/xknx/io/gateway_scanner.py
+++ b/xknx/io/gateway_scanner.py
@@ -130,51 +130,66 @@ class GatewayDescriptor:
 
 
 class GatewayScanFilter:
-    """Filter to limit gateway scan attempts.
+    """Filter to limit gateway scan results.
 
-    If `tunnelling` and `routing` are set it is treated as AND.
-    KNX/IP devices that don't support `tunnelling` or `routing` aren't matched.
+    If `name` doesn't match the gateway name, the gateway will be ignored.
+
+    Connection methods are treated as OR if `True` is set for multiple methods.
+    Non-secure methods don't match if secure is required.
     """
 
     def __init__(
         self,
         name: str | None = None,
-        tunnelling: bool | None = None,
-        tunnelling_tcp: bool | None = None,
-        routing: bool | None = None,
-        secure: bool | None = False,
+        tunnelling: bool | None = True,
+        tunnelling_tcp: bool | None = True,
+        routing: bool | None = True,
+        secure_tunnelling: bool | None = True,
+        secure_routing: bool | None = True,
     ):
         """Initialize GatewayScanFilter class."""
         self.name = name
         self.tunnelling = tunnelling
         self.tunnelling_tcp = tunnelling_tcp
         self.routing = routing
-        self.secure = secure
+        self.secure_tunnelling = secure_tunnelling
+        self.secure_routing = secure_routing
 
     def match(self, gateway: GatewayDescriptor) -> bool:
         """Check whether the device is a gateway and given GatewayDescriptor matches the filter."""
         if self.name is not None and self.name != gateway.name:
             return False
         if (
-            self.tunnelling is not None
-            and self.tunnelling != gateway.supports_tunnelling
+            self.tunnelling
+            and gateway.supports_tunnelling
+            and not gateway.tunnelling_requires_secure
         ):
-            return False
+            return True
         if (
-            self.tunnelling_tcp is not None
-            and self.tunnelling_tcp != gateway.supports_tunnelling_tcp
+            self.tunnelling_tcp
+            and gateway.supports_tunnelling_tcp
+            and not gateway.tunnelling_requires_secure
         ):
-            return False
-        if self.routing is not None and self.routing != gateway.supports_routing:
-            return False
-        if self.secure is not None:
-            if self.secure is not bool(gateway.tunnelling_requires_secure):
-                return False
-        return (
-            gateway.supports_tunnelling
-            or gateway.supports_tunnelling_tcp
-            or gateway.supports_routing
-        )
+            return True
+        if (
+            self.routing
+            and gateway.supports_routing
+            and not gateway.routing_requires_secure
+        ):
+            return True
+        if (
+            self.secure_tunnelling
+            and gateway.supports_tunnelling_tcp
+            and gateway.tunnelling_requires_secure
+        ):
+            return True
+        if (
+            self.secure_routing
+            and gateway.supports_routing
+            and gateway.routing_requires_secure
+        ):
+            return True
+        return False
 
 
 class GatewayScanner:
diff --git a/xknx/io/knxip_interface.py b/xknx/io/knxip_interface.py
index f58eccb5..510e84f3 100644
--- a/xknx/io/knxip_interface.py
+++ b/xknx/io/knxip_interface.py
@@ -25,6 +25,7 @@ from xknx.secure import load_key_ring
 from xknx.telegram import IndividualAddress, Telegram
 
 from .connection import ConnectionConfig, ConnectionType
+from .const import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT
 from .gateway_scanner import GatewayDescriptor, GatewayScanner
 from .routing import Routing, SecureRouting
 from .tunnel import SecureTunnel, TCPTunnel, UDPTunnel, _Tunnel
@@ -72,7 +73,11 @@ class KNXIPInterface:
     async def _start(self) -> None:
         """Start interface. Connecting KNX/IP device with the selected method."""
         if self.connection_config.connection_type == ConnectionType.ROUTING:
-            await self._start_routing(local_ip=self.connection_config.local_ip)
+            await self._start_routing(
+                local_ip=self.connection_config.local_ip,
+                multicast_group=self.connection_config.multicast_group,
+                multicast_port=self.connection_config.multicast_port,
+            )
         elif (
             self.connection_config.connection_type == ConnectionType.ROUTING_SECURE
             and self.connection_config.secure_config is not None
@@ -80,6 +85,7 @@ class KNXIPInterface:
             secure_config = self.connection_config.secure_config
             backbone_key = secure_config.backbone_key
             latency_ms = secure_config.latency_ms
+            multicast_group = self.connection_config.multicast_group
             if (
                 secure_config.knxkeys_file_path is not None
                 and secure_config.knxkeys_password is not None
@@ -94,7 +100,7 @@ class KNXIPInterface:
                 backbone_key = backbone_key or keyring.backbone.decrypted_key
                 latency_ms = latency_ms or keyring.backbone.latency
                 if keyring.backbone.multicast_address:
-                    self.xknx.multicast_group = keyring.backbone.multicast_address
+                    multicast_group = keyring.backbone.multicast_address
             if not backbone_key:
                 raise InvalidSecureConfiguration(
                     "No backbone key found in secure configuration"
@@ -103,6 +109,8 @@ class KNXIPInterface:
                 backbone_key,
                 latency_ms=latency_ms,
                 local_ip=self.connection_config.local_ip,
+                multicast_group=multicast_group,
+                multicast_port=self.connection_config.multicast_port,
             )
         elif (
             self.connection_config.connection_type == ConnectionType.TUNNELING
@@ -175,20 +183,26 @@ class KNXIPInterface:
         async for gateway in GatewayScanner(
             self.xknx,
             local_ip=self.connection_config.local_ip,
-            scan_filter=self.connection_config.scan_filter,  # secure disabled by default
+            scan_filter=self.connection_config.scan_filter,
         ).async_scan():
             try:
-                if gateway.supports_tunnelling_tcp:
+                if (
+                    gateway.supports_tunnelling_tcp
+                    and not gateway.tunnelling_requires_secure
+                ):
                     await self._start_tunnelling_tcp(
                         gateway_ip=gateway.ip_addr,
                         gateway_port=gateway.port,
                     )
-                elif gateway.supports_tunnelling:
+                elif (
+                    gateway.supports_tunnelling
+                    and not gateway.tunnelling_requires_secure
+                ):
                     await self._start_tunnelling_udp(
                         gateway_ip=gateway.ip_addr,
                         gateway_port=gateway.port,
                     )
-                elif gateway.supports_routing:
+                elif gateway.supports_routing and not gateway.routing_requires_secure:
                     await self._start_routing(
                         local_ip=self.connection_config.local_ip,
                     )
@@ -294,6 +308,8 @@ class KNXIPInterface:
     async def _start_routing(
         self,
         local_ip: str | None = None,
+        multicast_group: str = DEFAULT_MCAST_GRP,
+        multicast_port: int = DEFAULT_MCAST_PORT,
     ) -> None:
         """Start KNX/IP Routing."""
         local_ip = local_ip or await util.get_default_local_ip()
@@ -302,12 +318,19 @@ class KNXIPInterface:
         util.validate_ip(local_ip, address_name="Local IP address")
 
         logger.debug(
-            "Starting Routing from %s as %s via %s",
+            "Starting Routing from %s as %s via %s:%s",
             local_ip,
             self.xknx.own_address,
-            self.xknx.multicast_group,
+            multicast_group,
+            multicast_port,
+        )
+        self._interface = Routing(
+            self.xknx,
+            self.telegram_received,
+            local_ip=local_ip,
+            multicast_group=multicast_group,
+            multicast_port=multicast_port,
         )
-        self._interface = Routing(self.xknx, self.telegram_received, local_ip)
         await self._interface.connect()
 
     async def _start_secure_routing(
@@ -315,6 +338,8 @@ class KNXIPInterface:
         backbone_key: bytes,
         latency_ms: int | None = None,
         local_ip: str | None = None,
+        multicast_group: str = DEFAULT_MCAST_GRP,
+        multicast_port: int = DEFAULT_MCAST_PORT,
     ) -> None:
         """Start KNX/IP Routing."""
         local_ip = local_ip or await util.get_default_local_ip()
@@ -323,13 +348,20 @@ class KNXIPInterface:
         util.validate_ip(local_ip, address_name="Local IP address")
 
         logger.debug(
-            "Starting Secure Routing from %s as %s via %s",
+            "Starting Secure Routing from %s as %s via %s:%s",
             local_ip,
             self.xknx.own_address,
-            self.xknx.multicast_group,
+            multicast_group,
+            multicast_port,
         )
         self._interface = SecureRouting(
-            self.xknx, self.telegram_received, local_ip, backbone_key, latency_ms
+            self.xknx,
+            self.telegram_received,
+            local_ip=local_ip,
+            backbone_key=backbone_key,
+            latency_ms=latency_ms,
+            multicast_group=multicast_group,
+            multicast_port=multicast_port,
         )
         await self._interface.connect()
 
diff --git a/xknx/io/routing.py b/xknx/io/routing.py
index 6ae3a41f..16b4082b 100644
--- a/xknx/io/routing.py
+++ b/xknx/io/routing.py
@@ -26,6 +26,7 @@ from xknx.knxip import (
 )
 from xknx.telegram import Telegram, TelegramDirection
 
+from .const import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT
 from .interface import Interface, TelegramCallbackType
 from .ip_secure import SecureGroup
 from .transport import KNXIPTransport, UDPTransport
@@ -139,11 +140,15 @@ class Routing(Interface):
         xknx: XKNX,
         telegram_received_callback: TelegramCallbackType,
         local_ip: str,
+        multicast_group: str = DEFAULT_MCAST_GRP,
+        multicast_port: int = DEFAULT_MCAST_PORT,
     ):
         """Initialize Routing class."""
         self.xknx = xknx
         self.telegram_received_callback = telegram_received_callback
         self.local_ip = local_ip
+        self.multicast_group = multicast_group
+        self.multicast_port = multicast_port
 
         self._init_transport()
         self.transport.register_callback(
@@ -160,7 +165,7 @@ class Routing(Interface):
         """Initialize transport."""
         self.transport = UDPTransport(
             local_addr=(self.local_ip, 0),
-            remote_addr=(self.xknx.multicast_group, self.xknx.multicast_port),
+            remote_addr=(self.multicast_group, self.multicast_port),
             multicast=True,
         )
 
@@ -282,17 +287,25 @@ class SecureRouting(Routing):
         local_ip: str,
         backbone_key: bytes,
         latency_ms: int | None = None,
+        multicast_group: str = DEFAULT_MCAST_GRP,
+        multicast_port: int = DEFAULT_MCAST_PORT,
     ):
         """Initialize SecureRouting class."""
         self.backbone_key = backbone_key
         self.latency_ms = latency_ms or DEFAULT_LATENCY_TOLERANCE_MS
-        super().__init__(xknx, telegram_received_callback, local_ip)
+        super().__init__(
+            xknx,
+            telegram_received_callback,
+            local_ip=local_ip,
+            multicast_group=multicast_group,
+            multicast_port=multicast_port,
+        )
 
     def _init_transport(self) -> None:
         """Initialize transport."""
         self.transport = SecureGroup(
             local_addr=(self.local_ip, 0),
-            remote_addr=(self.xknx.multicast_group, self.xknx.multicast_port),
+            remote_addr=(self.multicast_group, self.multicast_port),
             backbone_key=self.backbone_key,
             latency_ms=self.latency_ms,
         )


Test patch:
diff --git a/requirements/testing.txt b/requirements/testing.txt
index 57e84bcd..d3893504 100644
--- a/requirements/testing.txt
+++ b/requirements/testing.txt
@@ -6,8 +6,8 @@ flake8-isort==5.0.0
 pydocstyle==6.1.1
 pylint==2.15.5
 pytest==7.2.0
-pytest-asyncio==0.20.1
+pytest-asyncio==0.20.2
 pytest-cov==4.0.0
-setuptools==65.5.0
-tox==3.27.0
-mypy==0.982
+setuptools==65.5.1
+tox==3.27.1
+mypy==0.991
diff --git a/test/io_tests/gateway_scanner_test.py b/test/io_tests/gateway_scanner_test.py
index 97cfc19e..40a939c3 100644
--- a/test/io_tests/gateway_scanner_test.py
+++ b/test/io_tests/gateway_scanner_test.py
@@ -214,72 +214,99 @@ class TestGatewayScanner:
         supports_secure=True,
     )
     gateway_desc_secure_tunnel.tunnelling_requires_secure = True
+    gateway_desc_secure_router = GatewayDescriptor(
+        name="KNX-Secure-Router",
+        ip_addr="10.1.1.12",
+        port=3761,
+        local_interface="en1",
+        local_ip="10.1.1.100",
+        supports_tunnelling=False,
+        supports_routing=True,
+    )
+    gateway_desc_secure_router.routing_requires_secure = True
 
     def test_gateway_scan_filter_match(self):
         """Test match function of gateway filter."""
         filter_default = GatewayScanFilter()
-        filter_tunnel = GatewayScanFilter(tunnelling=True)
-        filter_tcp_tunnel = GatewayScanFilter(tunnelling_tcp=True, secure=None)
-        filter_secure_tunnel = GatewayScanFilter(tunnelling_tcp=True, secure=True)
-        filter_router = GatewayScanFilter(routing=True)
+        filter_tunnel = GatewayScanFilter(routing=False, secure_routing=False)
+        filter_tcp_tunnel = GatewayScanFilter(
+            tunnelling=False,
+            tunnelling_tcp=True,
+            secure_tunnelling=None,
+            routing=False,
+            secure_routing=False,
+        )
+        filter_secure_tunnel = GatewayScanFilter(
+            tunnelling=False,
+            tunnelling_tcp=False,
+            secure_tunnelling=True,
+            routing=False,
+            secure_routing=False,
+        )
+        filter_router = GatewayScanFilter(
+            tunnelling=False,
+            tunnelling_tcp=False,
+            secure_tunnelling=False,
+            routing=True,
+            secure_routing=False,
+        )
         filter_name = GatewayScanFilter(name="KNX-Router")
-        filter_no_tunnel = GatewayScanFilter(tunnelling=False)
-        filter_no_router = GatewayScanFilter(routing=False)
-        filter_tunnel_and_router = GatewayScanFilter(tunnelling=True, routing=True)
+        filter_secure_router = GatewayScanFilter(
+            tunnelling=False,
+            tunnelling_tcp=False,
+            secure_tunnelling=False,
+            routing=False,
+            secure_routing=True,
+        )
 
         assert filter_default.match(self.gateway_desc_interface)
         assert filter_default.match(self.gateway_desc_router)
         assert filter_default.match(self.gateway_desc_both)
         assert not filter_default.match(self.gateway_desc_neither)
-        assert not filter_default.match(self.gateway_desc_secure_tunnel)
+        assert filter_default.match(self.gateway_desc_secure_tunnel)
+        assert filter_default.match(self.gateway_desc_secure_router)
 
         assert filter_tunnel.match(self.gateway_desc_interface)
         assert not filter_tunnel.match(self.gateway_desc_router)
         assert filter_tunnel.match(self.gateway_desc_both)
         assert not filter_tunnel.match(self.gateway_desc_neither)
-        assert not filter_tunnel.match(self.gateway_desc_secure_tunnel)
+        assert filter_tunnel.match(self.gateway_desc_secure_tunnel)
+        assert not filter_tunnel.match(self.gateway_desc_secure_router)
 
         assert not filter_tcp_tunnel.match(self.gateway_desc_interface)
         assert not filter_tcp_tunnel.match(self.gateway_desc_router)
         assert filter_tcp_tunnel.match(self.gateway_desc_both)
         assert not filter_tcp_tunnel.match(self.gateway_desc_neither)
-        assert filter_tcp_tunnel.match(self.gateway_desc_secure_tunnel)
+        assert not filter_tcp_tunnel.match(self.gateway_desc_secure_tunnel)
+        assert not filter_tcp_tunnel.match(self.gateway_desc_secure_router)
 
         assert not filter_secure_tunnel.match(self.gateway_desc_interface)
         assert not filter_secure_tunnel.match(self.gateway_desc_router)
         assert not filter_secure_tunnel.match(self.gateway_desc_both)
         assert not filter_secure_tunnel.match(self.gateway_desc_neither)
         assert filter_secure_tunnel.match(self.gateway_desc_secure_tunnel)
+        assert not filter_secure_tunnel.match(self.gateway_desc_secure_router)
 
         assert not filter_router.match(self.gateway_desc_interface)
         assert filter_router.match(self.gateway_desc_router)
         assert filter_router.match(self.gateway_desc_both)
         assert not filter_router.match(self.gateway_desc_neither)
         assert not filter_router.match(self.gateway_desc_secure_tunnel)
+        assert not filter_router.match(self.gateway_desc_secure_router)
 
         assert not filter_name.match(self.gateway_desc_interface)
         assert filter_name.match(self.gateway_desc_router)
         assert not filter_name.match(self.gateway_desc_both)
         assert not filter_name.match(self.gateway_desc_neither)
         assert not filter_name.match(self.gateway_desc_secure_tunnel)
-
-        assert not filter_no_tunnel.match(self.gateway_desc_interface)
-        assert filter_no_tunnel.match(self.gateway_desc_router)
-        assert not filter_no_tunnel.match(self.gateway_desc_both)
-        assert not filter_no_tunnel.match(self.gateway_desc_neither)
-        assert not filter_no_tunnel.match(self.gateway_desc_secure_tunnel)
-
-        assert filter_no_router.match(self.gateway_desc_interface)
-        assert not filter_no_router.match(self.gateway_desc_router)
-        assert not filter_no_router.match(self.gateway_desc_both)
-        assert not filter_no_router.match(self.gateway_desc_neither)
-        assert not filter_no_router.match(self.gateway_desc_secure_tunnel)
-
-        assert not filter_tunnel_and_router.match(self.gateway_desc_interface)
-        assert not filter_tunnel_and_router.match(self.gateway_desc_router)
-        assert filter_tunnel_and_router.match(self.gateway_desc_both)
-        assert not filter_tunnel_and_router.match(self.gateway_desc_neither)
-        assert not filter_tunnel_and_router.match(self.gateway_desc_secure_tunnel)
+        assert not filter_name.match(self.gateway_desc_secure_router)
+
+        assert not filter_secure_router.match(self.gateway_desc_interface)
+        assert not filter_secure_router.match(self.gateway_desc_router)
+        assert not filter_secure_router.match(self.gateway_desc_both)
+        assert not filter_secure_router.match(self.gateway_desc_neither)
+        assert not filter_secure_router.match(self.gateway_desc_secure_tunnel)
+        assert filter_secure_router.match(self.gateway_desc_secure_router)
 
     def test_search_response_reception(self):
         """Test function of gateway scanner."""
diff --git a/test/io_tests/knxip_interface_test.py b/test/io_tests/knxip_interface_test.py
index 6c03fe65..8088c189 100644
--- a/test/io_tests/knxip_interface_test.py
+++ b/test/io_tests/knxip_interface_test.py
@@ -144,7 +144,7 @@ class TestKNXIPInterface:
             interface = knx_interface_factory(self.xknx, connection_config)
             await interface.start()
             start_routing.assert_called_once_with(
-                local_ip=local_ip,
+                local_ip=local_ip, multicast_group="224.0.23.12", multicast_port=3671
             )
         with patch("xknx.io.routing.Routing.connect") as connect_routing:
             interface = knx_interface_factory(self.xknx, connection_config)
@@ -388,11 +388,15 @@ class TestKNXIPInterface:
         )
         with patch(
             "xknx.io.KNXIPInterface._start_secure_routing"
-        ) as start_secure_tunnel:
+        ) as start_secure_routing:
             interface = knx_interface_factory(self.xknx, connection_config)
             await interface.start()
-            start_secure_tunnel.assert_called_once_with(
-                backbone_key, latency_ms=1000, local_ip=None
+            start_secure_routing.assert_called_once_with(
+                backbone_key,
+                latency_ms=1000,
+                local_ip=None,
+                multicast_group="224.0.23.12",
+                multicast_port=3671,
             )
         with patch("xknx.io.routing.SecureRouting.connect") as connect_secure:
             interface = knx_interface_factory(self.xknx, connection_config)
@@ -402,7 +406,7 @@ class TestKNXIPInterface:
             assert interface._interface.latency_ms == 1000
             assert isinstance(interface._interface.transport, SecureGroup)
             assert interface._interface.transport.remote_addr == (
-                self.xknx.multicast_group,
+                "224.0.23.12",
                 3671,
             )
             assert (  # pylint: disable=comparison-with-callable
@@ -421,11 +425,15 @@ class TestKNXIPInterface:
         )
         with patch(
             "xknx.io.KNXIPInterface._start_secure_routing"
-        ) as start_secure_tunnel:
+        ) as start_secure_routing:
             interface = knx_interface_factory(self.xknx, connection_config)
             await interface.start()
-            start_secure_tunnel.assert_called_once_with(
-                backbone_key, latency_ms=2000, local_ip=None
+            start_secure_routing.assert_called_once_with(
+                backbone_key,
+                latency_ms=2000,
+                local_ip=None,
+                multicast_group="224.0.23.12",
+                multicast_port=3671,
             )
         with patch("xknx.io.routing.SecureRouting.connect") as connect_secure:
             interface = knx_interface_factory(self.xknx, connection_config)
@@ -435,7 +443,7 @@ class TestKNXIPInterface:
             assert interface._interface.latency_ms == 2000
             assert isinstance(interface._interface.transport, SecureGroup)
             assert interface._interface.transport.remote_addr == (
-                self.xknx.multicast_group,
+                "224.0.23.12",
                 3671,
             )
             assert (  # pylint: disable=comparison-with-callable