Repository: XKNX/xknx
Pull request #516 (2020-11-28 23:01:03)

source_address in Telegram()
<!--
  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.
-->
- add Telegram.source_address
- use positional arguments in tests for Telegram() (i was half way through when I realized I should just move Telegram.source_address - so I finished, but I think its nicer to read anyway)

first point of #497

## 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.
-->

- [ ] 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] I have performed a self-review of my own code
- [ ] The documentation has been adjusted accordingly
- [x] The changes generate no new warnings
- [ ] Tests have been added that prove the fix is effective or that the feature works
- [x] The changes are documented in the changelog
- [x] The Homeassistant plugin has been adjusted in case of new config options


coveralls: 
[![Coverage Status](https://coveralls.io/builds/35302334/badge)](https://coveralls.io/builds/35302334)

Coverage increased (+0.001%) to 94.5% when pulling **c50baa52993efc1fed16bb62cf6e75744ed5c01e on farmio:Telegram.source_address** into **4a3718d0d71e793e5ff24c2668b287489dabbc9b on XKNX:master**.


Patch:
diff --git a/changelog.md b/changelog.md
index da63d2e1..e61724dd 100644
--- a/changelog.md
+++ b/changelog.md
@@ -2,9 +2,13 @@
 
 ## Unreleased changes
 
+### HA integration
+
+- knx_event: renamed `address` to `destination` and added `source`, `telegramtype`, `direction` attributes.
+
 ### Internals
 
-- Telegram: `group_address` renamed to `destination_address`, to prepare support for other APCI services.
+- Telegram: `group_address` renamed to `destination_address`, to prepare support for other APCI services and add `source_address`
 - Farewell Travis CI; Welcome Github Actions!
 
 ## 0.15.6 Bugfix for StateUpater 2020-11-26
diff --git a/docs/configuration.md b/docs/configuration.md
index 839a15e4..5755f961 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -1,7 +1,8 @@
 ---
 layout: default
 title: Configuration
-nav_order: 12
+nav_order: 5
+has_children: true
 ---
 
 # Configuration
diff --git a/docs/splitting_up.md b/docs/splitting_up.md
new file mode 100644
index 00000000..b4d9068a
--- /dev/null
+++ b/docs/splitting_up.md
@@ -0,0 +1,25 @@
+---
+layout: default
+title: Splitting up your configuration
+nav_order: 1
+parent: Configuration
+---
+
+# Splitting up your configuration
+
+## Basic usage
+
+XKNX is controlled via a configuration file. Per default the configuration file is named `xknx.yaml`. You can change this by providing the `config` option like so: `XKNX(config='xknx.yaml')`.
+
+Throughout the time this configuration has grown a lot meaning that it can be really hard to maintain for bigger installations.
+
+There are several ways to improve the readability of your configuration:
+
+* `lights: !include lights.yaml` will load all lights from a dedicated `lights.yaml`.
+* `host: !env_var XKNX_HOST 192.168.0.200` will load the host variable from an environment variable `XKNX_HOST` and if `XKNX_HOST` does not exist falls back to `192.168.0.200`.
+
+## Advanced usage
+
+There is one advanced method to load a whole directory of YAML files at once. Your files must have the `.yaml` file extension.
+
+* `!include_dir_list` will return the complete content of a directory as a list with each file content being and entry in the list. The list entries are unordered.
\ No newline at end of file
diff --git a/examples/example_tunnel.py b/examples/example_tunnel.py
index 090df9db..e6461125 100644
--- a/examples/example_tunnel.py
+++ b/examples/example_tunnel.py
@@ -37,9 +37,13 @@ async def main():
     await tunnel.connect_udp()
     await tunnel.connect()
 
-    await tunnel.send_telegram(Telegram(GroupAddress("1/0/15"), payload=DPTBinary(1)))
+    await tunnel.send_telegram(
+        Telegram(destination_address=GroupAddress("1/0/15"), payload=DPTBinary(1))
+    )
     await asyncio.sleep(2)
-    await tunnel.send_telegram(Telegram(GroupAddress("1/0/15"), payload=DPTBinary(0)))
+    await tunnel.send_telegram(
+        Telegram(destination_address=GroupAddress("1/0/15"), payload=DPTBinary(0))
+    )
     await asyncio.sleep(2)
 
     await tunnel.connectionstate()
diff --git a/home-assistant-plugin/custom_components/xknx/__init__.py b/home-assistant-plugin/custom_components/xknx/__init__.py
index 5a788491..4b82e1d3 100644
--- a/home-assistant-plugin/custom_components/xknx/__init__.py
+++ b/home-assistant-plugin/custom_components/xknx/__init__.py
@@ -325,8 +325,11 @@ class KNXModule:
         self.hass.bus.async_fire(
             "knx_event",
             {
-                "address": str(telegram.destination_address),
                 "data": telegram.payload.value,
+                "destination": str(telegram.destination_address),
+                "direction": telegram.direction.value,
+                "source": str(telegram.source_address),
+                "telegramtype": telegram.telegramtype.value,
             },
         )
 
diff --git a/xknx.yaml b/xknx.yaml
index ed119385..18dd1142 100644
--- a/xknx.yaml
+++ b/xknx.yaml
@@ -1,5 +1,3 @@
-version: 1
-
 general:
     own_address: "15.15.249"
     rate_limit: 18
diff --git a/xknx/config/config.py b/xknx/config/config.py
index 5a9fbff7..60e724c4 100644
--- a/xknx/config/config.py
+++ b/xknx/config/config.py
@@ -7,9 +7,8 @@ Module for reading config files (xknx.yaml).
 from enum import Enum
 import logging
 
-import yaml
-
 from .config_v1 import ConfigV1
+from .yaml_loader import load_yaml
 
 logger = logging.getLogger("xknx.log")
 
@@ -31,12 +30,8 @@ class Config:
     def read(self, file="xknx.yaml"):
         """Read config."""
         logger.debug("Reading %s", file)
-        try:
-            with open(file) as filehandle:
-                doc = yaml.safe_load(filehandle)
-                self.parse(doc)
-        except FileNotFoundError as ex:
-            logger.error("Error while reading %s: %s", file, ex)
+        doc = load_yaml(file)
+        self.parse(doc)
 
     @staticmethod
     def parse_version(doc):
diff --git a/xknx/config/objects.py b/xknx/config/objects.py
new file mode 100644
index 00000000..cae95774
--- /dev/null
+++ b/xknx/config/objects.py
@@ -0,0 +1,9 @@
+"""Custom yaml object types."""
+
+
+class NodeListClass(list):
+    """Wrapper class to be able to add attributes on a list."""
+
+
+class NodeStrClass(str):
+    """Wrapper class to be able to add attributes on a string."""
diff --git a/xknx/config/yaml_loader.py b/xknx/config/yaml_loader.py
new file mode 100644
index 00000000..27581566
--- /dev/null
+++ b/xknx/config/yaml_loader.py
@@ -0,0 +1,116 @@
+"""Custom YAML loader."""
+from collections import OrderedDict
+import fnmatch
+import logging
+import os
+from typing import Dict, Iterator, List, TypeVar, Union
+
+from xknx.config.objects import NodeListClass, NodeStrClass
+from xknx.exceptions import XKNXException
+import yaml
+
+logger = logging.getLogger("xknx.log")
+
+JSON_TYPE = Union[List, Dict, str]  # pylint: disable=invalid-name
+DICT_T = TypeVar("DICT_T", bound=Dict)  # pylint: disable=invalid-name
+
+
+class SafeLineLoader(yaml.SafeLoader):
+    """Loader class that keeps track of line numbers."""
+
+    def compose_node(self, parent: yaml.nodes.Node, index: int) -> yaml.nodes.Node:
+        """Annotate a node with the first line it was seen."""
+        last_line: int = self.line
+        node: yaml.nodes.Node = super().compose_node(parent, index)
+        node.__line__ = last_line + 1  # type: ignore
+        return node
+
+
+def _add_reference(obj, loader: SafeLineLoader, node: yaml.nodes.Node):  # type: ignore
+    """Add file reference information to an object."""
+    if isinstance(obj, list):
+        obj = NodeListClass(obj)
+    if isinstance(obj, str):
+        obj = NodeStrClass(obj)
+    setattr(obj, "__config_file__", loader.name)
+    setattr(obj, "__line__", node.start_mark.line)
+    return obj
+
+
+def load_yaml(fname: str) -> JSON_TYPE:
+    """Load a YAML file."""
+    try:
+        with open(fname, encoding="utf-8") as conf_file:
+            # If configuration file is empty YAML returns None
+            # We convert that to an empty dict
+            return yaml.load(conf_file, Loader=SafeLineLoader) or OrderedDict()
+    except yaml.YAMLError as exc:
+        logger.error(str(exc))
+        raise XKNXException(exc) from exc
+    except UnicodeDecodeError as exc:
+        logger.error("Unable to read file %s: %s", fname, exc)
+        raise XKNXException(exc) from exc
+
+
+def _include_yaml(loader: SafeLineLoader, node: yaml.nodes.Node) -> JSON_TYPE:
+    """Load another YAML file and embeds it using the !include tag.
+
+    Example:
+        device_tracker: !include device_tracker.yaml
+
+    """
+    fname = os.path.join(os.path.dirname(loader.name), node.value)
+    try:
+        return _add_reference(load_yaml(fname), loader, node)
+    except FileNotFoundError as exc:
+        raise XKNXException(f"{node.start_mark}: Unable to read file {fname}.") from exc
+
+
+def _is_file_valid(name: str) -> bool:
+    """Decide if a file is valid."""
+    return not name.startswith(".")
+
+
+def _find_files(directory: str, pattern: str) -> Iterator[str]:
+    """Recursively load files in a directory."""
+    for root, dirs, files in os.walk(directory, topdown=True):
+        dirs[:] = [d for d in dirs if _is_file_valid(d)]
+        for basename in sorted(files):
+            if _is_file_valid(basename) and fnmatch.fnmatch(basename, pattern):
+                filename = os.path.join(root, basename)
+                yield filename
+
+
+def _construct_seq(loader: SafeLineLoader, node: yaml.nodes.Node) -> JSON_TYPE:
+    """Add line number and file name to Load YAML sequence."""
+    (obj,) = loader.construct_yaml_seq(node)
+    return _add_reference(obj, loader, node)
+
+
+def _include_dir_list_yaml(
+    loader: SafeLineLoader, node: yaml.nodes.Node
+) -> List[JSON_TYPE]:
+    """Load multiple files from directory as a list."""
+    loc = os.path.join(os.path.dirname(loader.name), node.value)
+    return [load_yaml(f) for f in _find_files(loc, "*.yaml")]
+
+
+def _env_var_yaml(loader: SafeLineLoader, node: yaml.nodes.Node) -> str:
+    """Load environment variables and embed it into the configuration YAML."""
+    args = node.value.split()
+
+    # Check for a default value
+    if len(args) > 1:
+        return os.getenv(args[0], " ".join(args[1:]))
+    if args[0] in os.environ:
+        return os.environ[args[0]]
+    logger.error("Environment variable %s not defined", node.value)
+    raise XKNXException(node.value)
+
+
+yaml.SafeLoader.add_constructor("!include", _include_yaml)
+yaml.SafeLoader.add_constructor(
+    yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, _construct_seq
+)
+yaml.SafeLoader.add_constructor("!env_var", _env_var_yaml)
+yaml.SafeLoader.add_constructor("!include_dir_list", _include_dir_list_yaml)
diff --git a/xknx/core/value_reader.py b/xknx/core/value_reader.py
index 3ef0f778..6c3ea6f6 100644
--- a/xknx/core/value_reader.py
+++ b/xknx/core/value_reader.py
@@ -48,7 +48,9 @@ class ValueReader:
 
     async def send_group_read(self):
         """Send group read."""
-        telegram = Telegram(self.group_address, TelegramType.GROUP_READ)
+        telegram = Telegram(
+            destination_address=self.group_address, telegramtype=TelegramType.GROUP_READ
+        )
         await self.xknx.telegrams.put(telegram)
 
     async def telegram_received(self, telegram):
diff --git a/xknx/knxip/cemi_frame.py b/xknx/knxip/cemi_frame.py
index 44b7dedb..5f4ac8be 100644
--- a/xknx/knxip/cemi_frame.py
+++ b/xknx/knxip/cemi_frame.py
@@ -75,8 +75,9 @@ class CEMIFrame:
 
         return Telegram(
             destination_address=self.dst_addr,
-            payload=self.payload,
             telegramtype=resolve_telegram_type(self.cmd),
+            payload=self.payload,
+            source_address=self.src_addr,
         )
 
     @telegram.setter
diff --git a/xknx/telegram/telegram.py b/xknx/telegram/telegram.py
index 492db070..b5fe023c 100644
--- a/xknx/telegram/telegram.py
+++ b/xknx/telegram/telegram.py
@@ -23,16 +23,16 @@ from .address import GroupAddress, PhysicalAddress
 class TelegramDirection(Enum):
     """Enum class for the communication direction of a telegram (from KNX bus or to KNX bus)."""
 
-    INCOMING = 1
-    OUTGOING = 2
+    INCOMING = "Incoming"
+    OUTGOING = "Outgoing"
 
 
 class TelegramType(Enum):
     """Enum class for type of telegram."""
 
-    GROUP_READ = 1
-    GROUP_WRITE = 2
-    GROUP_RESPONSE = 3
+    GROUP_READ = "GroupValueRead"
+    GROUP_WRITE = "GroupValueWrite"
+    GROUP_RESPONSE = "GroupValueResponse"
 
 
 class Telegram:
@@ -46,22 +46,25 @@ class Telegram:
         telegramtype: TelegramType = TelegramType.GROUP_WRITE,
         direction: TelegramDirection = TelegramDirection.OUTGOING,
         payload: Any = None,
+        source_address: PhysicalAddress = PhysicalAddress(None),
     ) -> None:
         """Initialize Telegram class."""
-        self.direction = direction
-        self.telegramtype = telegramtype
         self.destination_address = destination_address
+        self.telegramtype = telegramtype
+        self.direction = direction
         self.payload = payload
+        self.source_address = source_address
 
     def __str__(self) -> str:
         """Return object as readable string."""
         return (
-            '<Telegram destination_address="{}", payload="{}" '
-            'telegramtype="{}" direction="{}" />'.format(
+            '<Telegram direction="{}" telegramtype="{}" source_address="{}" '
+            'destination_address="{}" payload="{}" />'.format(
+                self.direction.value,
+                self.telegramtype.value,
+                self.source_address.__repr__(),
                 self.destination_address.__repr__(),
                 self.payload,
-                self.telegramtype,
-                self.direction,
             )
         )
 


Test patch:
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 00000000..4c7db209
--- /dev/null
+++ b/test/__init__.py
@@ -0,0 +1,1 @@
+"""Tests for XKNX."""
diff --git a/test/config_tests/config_v1_test.py b/test/config_tests/config_v1_test.py
index 25b1b563..5479f439 100644
--- a/test/config_tests/config_v1_test.py
+++ b/test/config_tests/config_v1_test.py
@@ -565,12 +565,6 @@ class TestConfig(unittest.TestCase):
         )
         self.assertFalse(TestConfig.xknx.devices.__contains__("Remote_wind_alarm"))
 
-    def test_config_file_not_found(self):
-        """Test error message when reading a non exisiting config file."""
-        with patch("logging.Logger.error") as mock_err:
-            XKNX(config="xknx_does_not_exist.yaml")
-            self.assertEqual(mock_err.call_count, 1)
-
     def test_config_file_error(self):
         """Test error message when reading an errornous config file."""
         with patch("logging.Logger.error") as mock_err, patch(
diff --git a/test/config_tests/yaml_loader_test.py b/test/config_tests/yaml_loader_test.py
new file mode 100644
index 00000000..e1475218
--- /dev/null
+++ b/test/config_tests/yaml_loader_test.py
@@ -0,0 +1,105 @@
+import io
+import os
+from unittest.mock import patch
+
+import pytest
+from xknx.config import yaml_loader
+from xknx.exceptions import XKNXException
+import yaml
+
+from test.util import patch_yaml_files
+
+
+def test_simple_list():
+    """Test simple list."""
+    conf = "config:\n  - simple\n  - list"
+    with io.StringIO(conf) as file:
+        doc = yaml_loader.yaml.safe_load(file)
+    assert doc["config"] == ["simple", "list"]
+
+
+def test_simple_dict():
+    """Test simple dict."""
+    conf = "key: value"
+    with io.StringIO(conf) as file:
+        doc = yaml_loader.yaml.safe_load(file)
+    assert doc["key"] == "value"
+
+
+@patch("xknx.config.yaml_loader.open", create=True)
+def test_load_yaml_encoding_error(mock_open):
+    """Test raising a UnicodeDecodeError."""
+    mock_open.side_effect = UnicodeDecodeError("", b"", 1, 0, "")
+    with pytest.raises(XKNXException):
+        yaml_loader.load_yaml("test")
+
+
+@patch("xknx.config.yaml_loader.open", create=True)
+def test_load_yaml_loading_error(mock_open):
+    """Test raising a YAMLError."""
+    mock_open.side_effect = yaml.error.YAMLError
+    with pytest.raises(XKNXException):
+        yaml_loader.load_yaml("test")
+
+
+@patch("xknx.config.yaml_loader.os.walk")
+def test_include_dir_list(mock_walk):
+    """Test include dir list yaml."""
+    mock_walk.return_value = [["/test", [], ["two.yaml", "one.yaml"]]]
+
+    with patch_yaml_files({"/test/one.yaml": "one", "/test/two.yaml": "two"}):
+        conf = "key: !include_dir_list /test"
+        with io.StringIO(conf) as file:
+            doc = yaml_loader.yaml.safe_load(file)
+            assert doc["key"] == sorted(["one", "two"])
+
+
+def test_environment_variable():
+    """Test config file with environment variable."""
+    os.environ["XKNX_HOST"] = "192.168.30.32"
+    conf = "host: !env_var XKNX_HOST"
+    with io.StringIO(conf) as file:
+        doc = yaml_loader.yaml.safe_load(file)
+    assert doc["host"] == "192.168.30.32"
+    del os.environ["XKNX_HOST"]
+
+
+def test_environment_variable_default():
+    """Test config file with default value for environment variable."""
+    conf = "host: !env_var XKNX_HOST 127.0.0.1"
+    with io.StringIO(conf) as file:
+        doc = yaml_loader.yaml.safe_load(file)
+    assert doc["host"] == "127.0.0.1"
+
+
+def test_invalid_environment_variable():
+    """Test config file with no environment variable sat."""
+    conf = "host: !env_var XKNX_HOST"
+    with pytest.raises(XKNXException):
+        with io.StringIO(conf) as file:
+            yaml_loader.yaml.safe_load(file)
+
+
+def test_include_yaml():
+    """Test include yaml."""
+    with patch_yaml_files({"test.yaml": "value"}):
+        conf = "key: !include test.yaml"
+        with io.StringIO(conf) as file:
+            doc = yaml_loader.yaml.safe_load(file)
+            assert doc["key"] == "value"
+
+    with patch_yaml_files({"test.yaml": None}):
+        conf = "key: !include test.yaml"
+        with io.StringIO(conf) as file:
+            doc = yaml_loader.yaml.safe_load(file)
+            assert doc["key"] == {}
+
+
+def test_include_yaml_error():
+    """Test include yaml error."""
+    with patch_yaml_files({"test.yaml": "value"}):
+        conf = "key: !include test2.yaml"
+        with io.StringIO(conf) as file:
+            with pytest.raises(XKNXException):
+                doc = yaml_loader.yaml.safe_load(file)
+                assert doc["key"] == "value"
diff --git a/test/devices_tests/binary_sensor_test.py b/test/devices_tests/binary_sensor_test.py
index 4eb71932..33649836 100644
--- a/test/devices_tests/binary_sensor_test.py
+++ b/test/devices_tests/binary_sensor_test.py
@@ -196,7 +196,9 @@ class TestBinarySensor(unittest.TestCase):
         """Test process wrong telegram (wrong payload type)."""
         xknx = XKNX()
         binary_sensor = BinarySensor(xknx, "Warning", group_address_state="1/2/3")
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x1, 0x2, 0x3)))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x1, 0x2, 0x3))
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(binary_sensor.process(telegram))
 
diff --git a/test/devices_tests/climate_test.py b/test/devices_tests/climate_test.py
index d9fb3e30..7d9587b5 100644
--- a/test/devices_tests/climate_test.py
+++ b/test/devices_tests/climate_test.py
@@ -232,21 +232,24 @@ class TestClimate(unittest.TestCase):
         climate.register_device_updated_cb(async_after_update_callback)
 
         telegram = Telegram(
-            GroupAddress("1/2/1"), payload=DPTArray(DPTTemperature.to_knx(23))
+            destination_address=GroupAddress("1/2/1"),
+            payload=DPTArray(DPTTemperature.to_knx(23)),
         )
         self.loop.run_until_complete(climate.process(telegram))
         after_update_callback.assert_called_with(climate)
         after_update_callback.reset_mock()
 
         telegram = Telegram(
-            GroupAddress("1/2/2"), payload=DPTArray(DPTTemperature.to_knx(23))
+            destination_address=GroupAddress("1/2/2"),
+            payload=DPTArray(DPTTemperature.to_knx(23)),
         )
         self.loop.run_until_complete(climate.process(telegram))
         after_update_callback.assert_called_with(climate)
         after_update_callback.reset_mock()
 
         telegram = Telegram(
-            GroupAddress("1/2/3"), payload=DPTArray(DPTValue1Count.to_knx(-4))
+            destination_address=GroupAddress("1/2/3"),
+            payload=DPTArray(DPTValue1Count.to_knx(-4)),
         )
         self.loop.run_until_complete(climate.process(telegram))
         after_update_callback.assert_called_with(climate)
@@ -272,7 +275,9 @@ class TestClimate(unittest.TestCase):
 
         # Note: the climate object processes the telegram, but the cb
         # is called with the climate_mode object.
-        telegram = Telegram(GroupAddress("1/2/4"), payload=DPTArray(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/4"), payload=DPTArray(1)
+        )
         self.loop.run_until_complete(climate.process(telegram))
         after_update_callback.assert_called_with(climate_mode)
         after_update_callback.reset_mock()
@@ -297,7 +302,7 @@ class TestClimate(unittest.TestCase):
             self.assertEqual(
                 telegram,
                 Telegram(
-                    GroupAddress("1/2/4"),
+                    destination_address=GroupAddress("1/2/4"),
                     payload=DPTArray(DPTHVACMode.to_knx(operation_mode)),
                 ),
             )
@@ -319,7 +324,7 @@ class TestClimate(unittest.TestCase):
             self.assertEqual(
                 telegram,
                 Telegram(
-                    GroupAddress("1/2/4"),
+                    destination_address=GroupAddress("1/2/4"),
                     payload=DPTArray(DPTHVACContrMode.to_knx(controller_mode)),
                 ),
             )
@@ -361,7 +366,7 @@ class TestClimate(unittest.TestCase):
             self.assertEqual(
                 telegram,
                 Telegram(
-                    GroupAddress("1/2/4"),
+                    destination_address=GroupAddress("1/2/4"),
                     payload=DPTArray(DPTControllerStatus.to_knx(operation_mode)),
                 ),
             )
@@ -388,10 +393,16 @@ class TestClimate(unittest.TestCase):
             telegrams.append(xknx.telegrams.get_nowait())
 
         test_telegrams = [
-            Telegram(GroupAddress("1/2/4"), payload=DPTArray(1)),
-            Telegram(GroupAddress("1/2/5"), payload=DPTBinary(False)),
-            Telegram(GroupAddress("1/2/6"), payload=DPTBinary(False)),
-            Telegram(GroupAddress("1/2/7"), payload=DPTBinary(True)),
+            Telegram(destination_address=GroupAddress("1/2/4"), payload=DPTArray(1)),
+            Telegram(
+                destination_address=GroupAddress("1/2/5"), payload=DPTBinary(False)
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/6"), payload=DPTBinary(False)
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/7"), payload=DPTBinary(True)
+            ),
         ]
 
         self.assertSetEqual(set(telegrams), set(test_telegrams))
@@ -412,7 +423,10 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/14"), payload=DPTBinary(True))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/14"), payload=DPTBinary(True)
+            ),
         )
         self.loop.run_until_complete(
             climate_mode.set_controller_mode(HVACControllerMode.COOL)
@@ -420,7 +434,10 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/14"), payload=DPTBinary(False))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/14"), payload=DPTBinary(False)
+            ),
         )
 
     #
@@ -516,7 +533,7 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(
             _telegram,
             # DEFAULT_TEMPERATURE_STEP is 0.1 -> payload = setpoint_shift * 10
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray(30)),
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(30)),
         )
         self.loop.run_until_complete(xknx.devices.process(_telegram))
 
@@ -526,7 +543,8 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(
             _telegram,
             Telegram(
-                GroupAddress("1/2/2"), payload=DPTArray(DPT2ByteFloat().to_knx(23.00))
+                destination_address=GroupAddress("1/2/2"),
+                payload=DPTArray(DPT2ByteFloat().to_knx(23.00)),
             ),
         )
         self.loop.run_until_complete(xknx.devices.process(_telegram))
@@ -538,14 +556,15 @@ class TestClimate(unittest.TestCase):
         _telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray(40)),
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(40)),
         )
         self.loop.run_until_complete(xknx.devices.process(_telegram))
         _telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             _telegram,
             Telegram(
-                GroupAddress("1/2/2"), payload=DPTArray(DPT2ByteFloat().to_knx(24.00))
+                destination_address=GroupAddress("1/2/2"),
+                payload=DPTArray(DPT2ByteFloat().to_knx(24.00)),
             ),
         )
         self.loop.run_until_complete(xknx.devices.process(_telegram))
@@ -557,14 +576,15 @@ class TestClimate(unittest.TestCase):
         _telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray(35)),
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(35)),
         )
         self.loop.run_until_complete(xknx.devices.process(_telegram))
         _telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             _telegram,
             Telegram(
-                GroupAddress("1/2/2"), payload=DPTArray(DPT2ByteFloat().to_knx(23.50))
+                destination_address=GroupAddress("1/2/2"),
+                payload=DPTArray(DPT2ByteFloat().to_knx(23.50)),
             ),
         )
         self.loop.run_until_complete(xknx.devices.process(_telegram))
@@ -599,7 +619,7 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(
             _telegram,
             # DEFAULT_TEMPERATURE_STEP is 0.1 -> payload = setpoint_shift * 10
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray(10)),
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(10)),
         )
         self.loop.run_until_complete(xknx.devices.process(_telegram))
 
@@ -609,7 +629,8 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(
             _telegram,
             Telegram(
-                GroupAddress("1/2/2"), payload=DPTArray(DPT2ByteFloat().to_knx(23.00))
+                destination_address=GroupAddress("1/2/2"),
+                payload=DPTArray(DPT2ByteFloat().to_knx(23.00)),
             ),
         )
         self.loop.run_until_complete(xknx.devices.process(_telegram))
@@ -621,14 +642,15 @@ class TestClimate(unittest.TestCase):
         _telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray(0xF1)),
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(0xF1)),
         )  # -15
         self.loop.run_until_complete(xknx.devices.process(_telegram))
         _telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             _telegram,
             Telegram(
-                GroupAddress("1/2/2"), payload=DPTArray(DPT2ByteFloat().to_knx(20.50))
+                destination_address=GroupAddress("1/2/2"),
+                payload=DPTArray(DPT2ByteFloat().to_knx(20.50)),
             ),
         )
         self.loop.run_until_complete(xknx.devices.process(_telegram))
@@ -640,14 +662,15 @@ class TestClimate(unittest.TestCase):
         _telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray(0xE2)),
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(0xE2)),
         )  # -30
         self.loop.run_until_complete(xknx.devices.process(_telegram))
         _telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             _telegram,
             Telegram(
-                GroupAddress("1/2/2"), payload=DPTArray(DPT2ByteFloat().to_knx(19.00))
+                destination_address=GroupAddress("1/2/2"),
+                payload=DPTArray(DPT2ByteFloat().to_knx(19.00)),
             ),
         )
         self.loop.run_until_complete(xknx.devices.process(_telegram))
@@ -686,7 +709,7 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(
             _telegram,
             # temperature_step is 0.5 -> payload = setpoint_shift * 2
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray(6)),
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(6)),
         )
 
         self.loop.run_until_complete(climate.target_temperature.set(23.00))
@@ -696,7 +719,8 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(
             _telegram,
             Telegram(
-                GroupAddress("1/2/2"), payload=DPTArray(DPT2ByteFloat().to_knx(23.00))
+                destination_address=GroupAddress("1/2/2"),
+                payload=DPTArray(DPT2ByteFloat().to_knx(23.00)),
             ),
         )
         self.assertEqual(climate.base_temperature, 20.00)
@@ -706,14 +730,15 @@ class TestClimate(unittest.TestCase):
         self.loop.run_until_complete(xknx.devices.process(_telegram))
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray(8)),
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(8)),
         )
         _telegram = xknx.telegrams.get_nowait()
         self.loop.run_until_complete(xknx.devices.process(_telegram))
         self.assertEqual(
             _telegram,
             Telegram(
-                GroupAddress("1/2/2"), payload=DPTArray(DPT2ByteFloat().to_knx(24.00))
+                destination_address=GroupAddress("1/2/2"),
+                payload=DPTArray(DPT2ByteFloat().to_knx(24.00)),
             ),
         )
         self.assertEqual(climate.target_temperature.value, 24.00)
@@ -745,7 +770,8 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(
             _telegram,
             Telegram(
-                GroupAddress("1/2/2"), payload=DPTArray(DPT2ByteFloat().to_knx(21.00))
+                destination_address=GroupAddress("1/2/2"),
+                payload=DPTArray(DPT2ByteFloat().to_knx(21.00)),
             ),
         )
         self.assertFalse(climate.initialized_for_setpoint_shift_calculations)
@@ -759,7 +785,7 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(
             _telegram,
             # DEFAULT_TEMPERATURE_STEP is 0.1 -> payload = setpoint_shift * 10
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray(10)),
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(10)),
         )
         self.assertTrue(climate.initialized_for_setpoint_shift_calculations)
         self.assertEqual(climate.base_temperature, 20.00)
@@ -773,7 +799,7 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(
             _telegram,
             # DEFAULT_TEMPERATURE_STEP is 0.1 -> payload = setpoint_shift * 10
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray(20)),
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(20)),
         )
         _telegram = xknx.telegrams.get_nowait()
         self.loop.run_until_complete(xknx.devices.process(_telegram))
@@ -799,7 +825,7 @@ class TestClimate(unittest.TestCase):
         self.loop.run_until_complete(
             climate.target_temperature.process(
                 Telegram(
-                    GroupAddress("1/2/2"),
+                    destination_address=GroupAddress("1/2/2"),
                     payload=DPTArray(DPT2ByteFloat().to_knx(20.00)),
                 )
             )
@@ -813,7 +839,10 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(climate.base_temperature, 20.00)
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x00, 0x00))),
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                payload=DPTArray((0x00, 0x00)),
+            ),
         )  # 0
         # - 0.6 °C = 19.4
         self.loop.run_until_complete(climate.set_target_temperature(19.40))
@@ -822,13 +851,16 @@ class TestClimate(unittest.TestCase):
         self.loop.run_until_complete(xknx.devices.process(_telegram))
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x87, 0xC4))),
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                payload=DPTArray((0x87, 0xC4)),
+            ),
         )  # -0.6
         # simulate incoming new target temperature for next calculation
         self.loop.run_until_complete(
             climate.target_temperature.process(
                 Telegram(
-                    GroupAddress("1/2/2"),
+                    destination_address=GroupAddress("1/2/2"),
                     payload=DPTArray(DPT2ByteFloat().to_knx(19.40)),
                 )
             )
@@ -840,7 +872,10 @@ class TestClimate(unittest.TestCase):
         self.loop.run_until_complete(xknx.devices.process(_telegram))
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x01, 0x5E))),
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                payload=DPTArray((0x01, 0x5E)),
+            ),
         )  # +3.5
 
     #
@@ -892,7 +927,11 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     def test_sync_operation_mode(self):
@@ -908,7 +947,11 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/4"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/4"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     def test_sync_controller_status(self):
@@ -924,7 +967,11 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/24"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/24"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     def test_sync_controller_mode(self):
@@ -940,7 +987,11 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/14"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/14"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     def test_sync_operation_mode_state(self):
@@ -965,9 +1016,18 @@ class TestClimate(unittest.TestCase):
         self.assertSetEqual(
             set(telegrams),
             {
-                Telegram(GroupAddress("1/2/5"), TelegramType.GROUP_READ),
-                Telegram(GroupAddress("1/2/6"), TelegramType.GROUP_READ),
-                Telegram(GroupAddress("1/2/14"), TelegramType.GROUP_READ),
+                Telegram(
+                    destination_address=GroupAddress("1/2/5"),
+                    telegramtype=TelegramType.GROUP_READ,
+                ),
+                Telegram(
+                    destination_address=GroupAddress("1/2/6"),
+                    telegramtype=TelegramType.GROUP_READ,
+                ),
+                Telegram(
+                    destination_address=GroupAddress("1/2/14"),
+                    telegramtype=TelegramType.GROUP_READ,
+                ),
             },
         )
 
@@ -984,7 +1044,11 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/15"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/15"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     #
@@ -995,7 +1059,7 @@ class TestClimate(unittest.TestCase):
         xknx = XKNX()
         climate = Climate(xknx, "TestClimate", group_address_temperature="1/2/3")
 
-        telegram = Telegram(GroupAddress("1/2/3"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/3"))
         telegram.payload = DPTArray(DPTTemperature().to_knx(21.34))
         self.loop.run_until_complete(climate.process(telegram))
         self.assertEqual(climate.temperature.value, 21.34)
@@ -1010,14 +1074,14 @@ class TestClimate(unittest.TestCase):
             group_address_controller_status="1/2/3",
         )
         for operation_mode in DPT_20102_MODES:
-            telegram = Telegram(GroupAddress("1/2/5"))
+            telegram = Telegram(destination_address=GroupAddress("1/2/5"))
             telegram.payload = DPTArray(DPTHVACMode.to_knx(operation_mode))
             self.loop.run_until_complete(climate_mode.process(telegram))
             self.assertEqual(climate_mode.operation_mode, operation_mode)
         for operation_mode in DPT_20102_MODES:
             if operation_mode == HVACOperationMode.AUTO:
                 continue
-            telegram = Telegram(GroupAddress("1/2/3"))
+            telegram = Telegram(destination_address=GroupAddress("1/2/3"))
             telegram.payload = DPTArray(DPTControllerStatus.to_knx(operation_mode))
             self.loop.run_until_complete(climate_mode.process(telegram))
             self.assertEqual(climate_mode.operation_mode, operation_mode)
@@ -1029,7 +1093,7 @@ class TestClimate(unittest.TestCase):
             xknx, "TestClimate", group_address_controller_mode="1/2/5"
         )
         for _, controller_mode in DPTHVACContrMode.SUPPORTED_MODES.items():
-            telegram = Telegram(GroupAddress("1/2/5"))
+            telegram = Telegram(destination_address=GroupAddress("1/2/5"))
             telegram.payload = DPTArray(DPTHVACContrMode.to_knx(controller_mode))
             self.loop.run_until_complete(climate_mode.process(telegram))
             self.assertEqual(climate_mode.controller_mode, controller_mode)
@@ -1043,7 +1107,9 @@ class TestClimate(unittest.TestCase):
             group_address_operation_mode="1/2/5",
             group_address_controller_status="1/2/3",
         )
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(climate_mode.process(telegram))
 
@@ -1056,7 +1122,9 @@ class TestClimate(unittest.TestCase):
             group_address_operation_mode="1/2/5",
             group_address_controller_status="1/2/3",
         )
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTArray((23, 24)))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTArray((23, 24))
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(climate_mode.process(telegram))
 
@@ -1069,7 +1137,9 @@ class TestClimate(unittest.TestCase):
             group_address_operation_mode="1/2/5",
             group_address_controller_status="1/2/3",
         )
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTBinary(1)
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(climate_mode.process(telegram))
 
@@ -1082,7 +1152,9 @@ class TestClimate(unittest.TestCase):
             group_address_operation_mode="1/2/5",
             group_address_controller_status="1/2/3",
         )
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTArray((23, 24)))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTArray((23, 24))
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(climate_mode.process(telegram))
 
@@ -1100,7 +1172,7 @@ class TestClimate(unittest.TestCase):
 
         climate.register_device_updated_cb(async_after_update_callback)
 
-        telegram = Telegram(GroupAddress("1/2/3"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/3"))
         telegram.payload = DPTArray(DPTTemperature().to_knx(21.34))
         self.loop.run_until_complete(climate.process(telegram))
         after_update_callback.assert_called_with(climate)
@@ -1115,12 +1187,12 @@ class TestClimate(unittest.TestCase):
             group_address_heat_cool_state="1/2/15",
         )
 
-        telegram = Telegram(GroupAddress("1/2/14"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/14"))
         telegram.payload = DPTBinary(False)
         self.loop.run_until_complete(climate_mode.process(telegram))
         self.assertEqual(climate_mode.controller_mode, HVACControllerMode.COOL)
 
-        telegram = Telegram(GroupAddress("1/2/14"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/14"))
         telegram.payload = DPTBinary(True)
         self.loop.run_until_complete(climate_mode.process(telegram))
         self.assertEqual(climate_mode.controller_mode, HVACControllerMode.HEAT)
@@ -1277,7 +1349,7 @@ class TestClimate(unittest.TestCase):
         """Test process / reading telegrams from telegram queue. Test if DPT20.105 controller mode is set correctly."""
         xknx = XKNX()
         climate = Climate(xknx, "TestClimate", group_address_on_off="1/2/2")
-        telegram = Telegram(GroupAddress("1/2/2"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/2"))
         telegram.payload = DPTBinary(1)
         self.loop.run_until_complete(climate.process(telegram))
         self.assertEqual(climate.is_on, True)
@@ -1285,7 +1357,7 @@ class TestClimate(unittest.TestCase):
         climate_inv = Climate(
             xknx, "TestClimate", group_address_on_off="1/2/2", on_off_invert=True
         )
-        telegram = Telegram(GroupAddress("1/2/2"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/2"))
         telegram.payload = DPTBinary(1)
         self.loop.run_until_complete(climate_inv.process(telegram))
         self.assertEqual(climate_inv.is_on, False)
@@ -1300,7 +1372,9 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(climate.is_on, True)
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/2"), payload=DPTBinary(True)),
+            Telegram(
+                destination_address=GroupAddress("1/2/2"), payload=DPTBinary(True)
+            ),
         )
         self.loop.run_until_complete(climate.turn_off())
         _telegram = xknx.telegrams.get_nowait()
@@ -1308,7 +1382,9 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(climate.is_on, False)
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/2"), payload=DPTBinary(False)),
+            Telegram(
+                destination_address=GroupAddress("1/2/2"), payload=DPTBinary(False)
+            ),
         )
 
         climate_inv = Climate(
@@ -1320,7 +1396,9 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(climate_inv.is_on, True)
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/2"), payload=DPTBinary(False)),
+            Telegram(
+                destination_address=GroupAddress("1/2/2"), payload=DPTBinary(False)
+            ),
         )
         self.loop.run_until_complete(climate_inv.turn_off())
         _telegram = xknx.telegrams.get_nowait()
@@ -1328,5 +1406,7 @@ class TestClimate(unittest.TestCase):
         self.assertEqual(climate_inv.is_on, False)
         self.assertEqual(
             _telegram,
-            Telegram(GroupAddress("1/2/2"), payload=DPTBinary(True)),
+            Telegram(
+                destination_address=GroupAddress("1/2/2"), payload=DPTBinary(True)
+            ),
         )
diff --git a/test/devices_tests/cover_test.py b/test/devices_tests/cover_test.py
index ddef1cd7..c7035337 100644
--- a/test/devices_tests/cover_test.py
+++ b/test/devices_tests/cover_test.py
@@ -127,7 +127,11 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     def test_sync_state(self):
@@ -145,7 +149,11 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/4"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/4"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     def test_sync_angle(self):
@@ -163,11 +171,19 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 2)
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
         telegram2 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram2, Telegram(GroupAddress("1/2/4"), TelegramType.GROUP_READ)
+            telegram2,
+            Telegram(
+                destination_address=GroupAddress("1/2/4"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     def test_sync_angle_state(self):
@@ -185,7 +201,11 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/4"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/4"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     #
@@ -207,7 +227,8 @@ class TestCover(unittest.TestCase):
         telegram = xknx.telegrams.get_nowait()
         # DPT 1.008 - 0:up 1:down
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/1"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(0)),
         )
 
     #
@@ -228,7 +249,8 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/1"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(1)),
         )
 
     #
@@ -250,7 +272,8 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/1"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(0)),
         )
 
     #
@@ -272,7 +295,8 @@ class TestCover(unittest.TestCase):
         telegram = xknx.telegrams.get_nowait()
         # DPT 1.008 - 0:up 1:down
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/2"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/2"), payload=DPTBinary(0)),
         )
 
     #
@@ -295,7 +319,8 @@ class TestCover(unittest.TestCase):
         telegram = xknx.telegrams.get_nowait()
         # DPT 1.008 - 0:up 1:down
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/2"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/2"), payload=DPTBinary(1)),
         )
 
     #
@@ -317,7 +342,8 @@ class TestCover(unittest.TestCase):
         telegram = xknx.telegrams.get_nowait()
         # DPT 1.008 - 0:up 1:down
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/2"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/2"), payload=DPTBinary(1)),
         )
 
     #
@@ -338,7 +364,8 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/2"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/2"), payload=DPTBinary(1)),
         )
 
         cover_manual_stop = Cover(
@@ -354,7 +381,8 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/0"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/0"), payload=DPTBinary(1)),
         )
 
     #
@@ -375,7 +403,8 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray(0x80))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(0x80)),
         )
 
     def test_position_without_position_address_up(self):
@@ -394,7 +423,8 @@ class TestCover(unittest.TestCase):
         telegram = xknx.telegrams.get_nowait()
         # DPT 1.008 - 0:up 1:down
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/1"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(0)),
         )
         self.assertEqual(cover.travelcalculator.travel_to_position, 50)
         self.assertTrue(cover.is_opening())
@@ -418,7 +448,8 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/1"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(1)),
         )
         self.assertEqual(cover.travelcalculator.travel_to_position, 80)
         self.assertTrue(cover.is_closing())
@@ -448,7 +479,8 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/1"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(0)),
         )
 
     def test_position_without_position_address_uninitialized_down(self):
@@ -473,7 +505,8 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/1"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(1)),
         )
 
     def test_angle(self):
@@ -493,7 +526,10 @@ class TestCover(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/4/18"), payload=DPTArray(0x80))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/4/18"), payload=DPTArray(0x80)
+            ),
         )
 
     def test_angle_not_supported(self):
@@ -527,24 +563,32 @@ class TestCover(unittest.TestCase):
             group_address_position_state="1/2/4",
         )
         # initial position process - position is unknown so this is the new state - not moving
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTArray(213))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTArray(213)
+        )
         self.loop.run_until_complete(cover.process(telegram))
         self.assertEqual(cover.current_position(), 84)
         self.assertFalse(cover.is_traveling())
         # state telegram updates current position - we are not moving so this is new state - not moving
-        telegram = Telegram(GroupAddress("1/2/4"), payload=DPTArray(42))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/4"), payload=DPTArray(42)
+        )
         self.loop.run_until_complete(cover.process(telegram))
         self.assertEqual(cover.current_position(), 16)
         self.assertFalse(cover.is_traveling())
         self.assertEqual(cover.travelcalculator.travel_to_position, 16)
         # new position - movement starts
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTArray(255))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTArray(255)
+        )
         self.loop.run_until_complete(cover.process(telegram))
         self.assertEqual(cover.current_position(), 16)
         self.assertTrue(cover.is_closing())
         self.assertEqual(cover.travelcalculator.travel_to_position, 100)
         # new state while moving - movement goes on; travelcalculator updated
-        telegram = Telegram(GroupAddress("1/2/4"), payload=DPTArray(213))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/4"), payload=DPTArray(213)
+        )
         self.loop.run_until_complete(cover.process(telegram))
         self.assertEqual(cover.current_position(), 84)
         self.assertTrue(cover.is_closing())
@@ -561,7 +605,9 @@ class TestCover(unittest.TestCase):
             group_address_angle="1/2/3",
             group_address_angle_state="1/2/4",
         )
-        telegram = Telegram(GroupAddress("1/2/4"), payload=DPTArray(42))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/4"), payload=DPTArray(42)
+        )
         self.loop.run_until_complete(cover.process(telegram))
         self.assertEqual(cover.current_angle(), 16)
 
@@ -573,7 +619,9 @@ class TestCover(unittest.TestCase):
         )
         cover.travelcalculator.set_position(50)
         self.assertFalse(cover.is_traveling())
-        telegram = Telegram(GroupAddress("1/2/1"), payload=DPTBinary(0))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/1"), payload=DPTBinary(0)
+        )
         self.loop.run_until_complete(cover.process(telegram))
         self.assertTrue(cover.is_opening())
 
@@ -585,7 +633,9 @@ class TestCover(unittest.TestCase):
         )
         cover.travelcalculator.set_position(50)
         self.assertFalse(cover.is_traveling())
-        telegram = Telegram(GroupAddress("1/2/1"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/1"), payload=DPTBinary(1)
+        )
         self.loop.run_until_complete(cover.process(telegram))
         self.assertTrue(cover.is_closing())
 
@@ -601,7 +651,9 @@ class TestCover(unittest.TestCase):
         cover.travelcalculator.set_position(50)
         self.loop.run_until_complete(cover.set_down())
         self.assertTrue(cover.is_traveling())
-        telegram = Telegram(GroupAddress("1/2/2"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/2"), payload=DPTBinary(1)
+        )
         self.loop.run_until_complete(cover.process(telegram))
         self.assertFalse(cover.is_traveling())
 
@@ -617,7 +669,9 @@ class TestCover(unittest.TestCase):
         cover.travelcalculator.set_position(50)
         self.loop.run_until_complete(cover.set_down())
         self.assertTrue(cover.is_traveling())
-        telegram = Telegram(GroupAddress("1/2/2"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/2"), payload=DPTBinary(1)
+        )
         self.loop.run_until_complete(cover.process(telegram))
         self.assertFalse(cover.is_traveling())
 
@@ -653,12 +707,16 @@ class TestCover(unittest.TestCase):
             ("1/2/7", DPTArray(51), "angle state"),
         ]:
             with self.subTest(address=address, feature=feature):
-                telegram = Telegram(GroupAddress(address), payload=payload)
+                telegram = Telegram(
+                    destination_address=GroupAddress(address), payload=payload
+                )
                 self.loop.run_until_complete(cover.process(telegram))
                 after_update_callback.assert_called_with(cover)
                 after_update_callback.reset_mock()
         # Stop only when cover is travelling
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)
+        )
         self.loop.run_until_complete(cover.process(telegram))
         after_update_callback.assert_not_called()
         self.loop.run_until_complete(cover.set_down())
diff --git a/test/devices_tests/device_test.py b/test/devices_tests/device_test.py
index 27603048..c925e0b4 100644
--- a/test/devices_tests/device_test.py
+++ b/test/devices_tests/device_test.py
@@ -102,7 +102,7 @@ class TestDevice(unittest.TestCase):
             fut.set_result(None)
             mock_group_read.return_value = fut
             telegram = Telegram(
-                GroupAddress("1/2/1"),
+                destination_address=GroupAddress("1/2/1"),
                 payload=DPTArray((0x01, 0x02)),
                 telegramtype=TelegramType.GROUP_READ,
             )
@@ -114,7 +114,7 @@ class TestDevice(unittest.TestCase):
             fut.set_result(None)
             mock_group_write.return_value = fut
             telegram = Telegram(
-                GroupAddress("1/2/1"),
+                destination_address=GroupAddress("1/2/1"),
                 payload=DPTArray((0x01, 0x02)),
                 telegramtype=TelegramType.GROUP_WRITE,
             )
@@ -126,7 +126,7 @@ class TestDevice(unittest.TestCase):
             fut.set_result(None)
             mock_group_response.return_value = fut
             telegram = Telegram(
-                GroupAddress("1/2/1"),
+                destination_address=GroupAddress("1/2/1"),
                 payload=DPTArray((0x01, 0x02)),
                 telegramtype=TelegramType.GROUP_RESPONSE,
             )
diff --git a/test/devices_tests/expose_sensor_test.py b/test/devices_tests/expose_sensor_test.py
index 2ebe79c6..c7fbafe1 100644
--- a/test/devices_tests/expose_sensor_test.py
+++ b/test/devices_tests/expose_sensor_test.py
@@ -72,7 +72,9 @@ class TestExposeSensor(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"), TelegramType.GROUP_WRITE, payload=DPTBinary(0)
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_WRITE,
+                payload=DPTBinary(0),
             ),
         )
 
@@ -89,8 +91,8 @@ class TestExposeSensor(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
-                TelegramType.GROUP_WRITE,
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_WRITE,
                 payload=DPTArray((0xBF,)),
             ),
         )
@@ -107,8 +109,8 @@ class TestExposeSensor(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
-                TelegramType.GROUP_WRITE,
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_WRITE,
                 payload=DPTArray((0x0C, 0x1A)),
             ),
         )
@@ -124,7 +126,7 @@ class TestExposeSensor(unittest.TestCase):
         )
         expose_sensor.sensor_value.payload = DPTBinary(1)
 
-        telegram = Telegram(GroupAddress("1/2/3"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/3"))
         telegram.telegramtype = TelegramType.GROUP_READ
         self.loop.run_until_complete(expose_sensor.process(telegram))
         self.assertEqual(xknx.telegrams.qsize(), 1)
@@ -132,8 +134,8 @@ class TestExposeSensor(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
-                TelegramType.GROUP_RESPONSE,
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_RESPONSE,
                 payload=DPTBinary(True),
             ),
         )
@@ -146,7 +148,7 @@ class TestExposeSensor(unittest.TestCase):
         )
         expose_sensor.sensor_value.payload = DPTArray((0x40,))
 
-        telegram = Telegram(GroupAddress("1/2/3"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/3"))
         telegram.telegramtype = TelegramType.GROUP_READ
         self.loop.run_until_complete(expose_sensor.process(telegram))
         self.assertEqual(xknx.telegrams.qsize(), 1)
@@ -154,8 +156,8 @@ class TestExposeSensor(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
-                TelegramType.GROUP_RESPONSE,
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_RESPONSE,
                 payload=DPTArray((0x40,)),
             ),
         )
@@ -168,7 +170,7 @@ class TestExposeSensor(unittest.TestCase):
         )
         expose_sensor.sensor_value.payload = DPTArray((0x0C, 0x1A))
 
-        telegram = Telegram(GroupAddress("1/2/3"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/3"))
         telegram.telegramtype = TelegramType.GROUP_READ
         self.loop.run_until_complete(expose_sensor.process(telegram))
         self.assertEqual(xknx.telegrams.qsize(), 1)
@@ -176,8 +178,8 @@ class TestExposeSensor(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
-                TelegramType.GROUP_RESPONSE,
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_RESPONSE,
                 payload=DPTArray((0x0C, 0x1A)),
             ),
         )
diff --git a/test/devices_tests/fan_test.py b/test/devices_tests/fan_test.py
index 10440d74..f9715e6b 100644
--- a/test/devices_tests/fan_test.py
+++ b/test/devices_tests/fan_test.py
@@ -37,7 +37,11 @@ class TestFan(unittest.TestCase):
 
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     #
@@ -58,7 +62,11 @@ class TestFan(unittest.TestCase):
 
         telegram1 = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram1, Telegram(GroupAddress("1/2/4"), TelegramType.GROUP_READ)
+            telegram1,
+            Telegram(
+                destination_address=GroupAddress("1/2/4"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     #
@@ -74,7 +82,8 @@ class TestFan(unittest.TestCase):
         telegram = xknx.telegrams.get_nowait()
         # 140 is 55% as byte (0...255)
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray(140))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(140)),
         )
 
     #
@@ -87,7 +96,9 @@ class TestFan(unittest.TestCase):
         self.assertEqual(fan.current_speed, None)
 
         # 140 is 55% as byte (0...255)
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTArray(140))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTArray(140)
+        )
         self.loop.run_until_complete(fan.process(telegram))
         self.assertEqual(fan.current_speed, 55)
 
@@ -95,7 +106,9 @@ class TestFan(unittest.TestCase):
         """Test process wrong telegrams. (wrong payload type)."""
         xknx = XKNX()
         fan = Fan(xknx, name="TestFan", group_address_speed="1/2/3")
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(fan.process(telegram))
 
@@ -104,7 +117,9 @@ class TestFan(unittest.TestCase):
         # pylint: disable=invalid-name
         xknx = XKNX()
         fan = Fan(xknx, name="TestFan", group_address_speed="1/2/3")
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTArray((23, 24)))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTArray((23, 24))
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(fan.process(telegram))
 
diff --git a/test/devices_tests/light_test.py b/test/devices_tests/light_test.py
index 3666e01e..0d6e8b10 100644
--- a/test/devices_tests/light_test.py
+++ b/test/devices_tests/light_test.py
@@ -155,12 +155,30 @@ class TestLight(unittest.TestCase):
             telegrams.append(xknx.telegrams.get_nowait())
 
         test_telegrams = [
-            Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ),
-            Telegram(GroupAddress("1/2/5"), TelegramType.GROUP_READ),
-            Telegram(GroupAddress("1/2/6"), TelegramType.GROUP_READ),
-            Telegram(GroupAddress("1/2/7"), TelegramType.GROUP_READ),
-            Telegram(GroupAddress("1/2/8"), TelegramType.GROUP_READ),
-            Telegram(GroupAddress("1/2/9"), TelegramType.GROUP_READ),
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/5"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/6"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/7"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/8"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/9"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         ]
 
         self.assertEqual(len(set(telegrams)), 6)
@@ -197,12 +215,30 @@ class TestLight(unittest.TestCase):
             telegrams.append(xknx.telegrams.get_nowait())
 
         test_telegrams = [
-            Telegram(GroupAddress("1/2/4"), TelegramType.GROUP_READ),
-            Telegram(GroupAddress("1/2/6"), TelegramType.GROUP_READ),
-            Telegram(GroupAddress("1/2/8"), TelegramType.GROUP_READ),
-            Telegram(GroupAddress("1/2/10"), TelegramType.GROUP_READ),
-            Telegram(GroupAddress("1/2/12"), TelegramType.GROUP_READ),
-            Telegram(GroupAddress("1/2/14"), TelegramType.GROUP_READ),
+            Telegram(
+                destination_address=GroupAddress("1/2/4"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/6"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/8"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/10"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/12"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
+            Telegram(
+                destination_address=GroupAddress("1/2/14"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         ]
 
         self.assertEqual(len(set(telegrams)), 6)
@@ -224,7 +260,8 @@ class TestLight(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)),
         )
 
     #
@@ -243,7 +280,8 @@ class TestLight(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)),
         )
 
     #
@@ -262,7 +300,8 @@ class TestLight(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/5"), payload=DPTArray(23))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/5"), payload=DPTArray(23)),
         )
 
     def test_set_brightness_not_dimmable(self):
@@ -293,7 +332,11 @@ class TestLight(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/5"), payload=DPTArray((23, 24, 25)))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/5"),
+                payload=DPTArray((23, 24, 25)),
+            ),
         )
         self.loop.run_until_complete(xknx.devices.process(telegram))
         self.assertEqual(light.current_color, ((23, 24, 25), None))
@@ -328,7 +371,10 @@ class TestLight(unittest.TestCase):
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             telegram,
-            Telegram(GroupAddress("1/2/5"), payload=DPTArray((23, 24, 25, 26, 0, 15))),
+            Telegram(
+                destination_address=GroupAddress("1/2/5"),
+                payload=DPTArray((23, 24, 25, 26, 0, 15)),
+            ),
         )
         self.loop.run_until_complete(xknx.devices.process(telegram))
         self.assertEqual(light.current_color, ([23, 24, 25], 26))
@@ -367,7 +413,8 @@ class TestLight(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/5"), payload=DPTArray(23))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/5"), payload=DPTArray(23)),
         )
 
     def test_set_tw_unsupported(self):
@@ -400,7 +447,7 @@ class TestLight(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/5"),
+                destination_address=GroupAddress("1/2/5"),
                 payload=DPTArray(
                     (
                         0x0F,
@@ -436,11 +483,15 @@ class TestLight(unittest.TestCase):
         )
         self.assertEqual(light.state, None)
 
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)
+        )
         self.loop.run_until_complete(light.process(telegram))
         self.assertEqual(light.state, True)
 
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTBinary(0))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)
+        )
         self.loop.run_until_complete(light.process(telegram))
         self.assertEqual(light.state, False)
 
@@ -463,7 +514,9 @@ class TestLight(unittest.TestCase):
 
         light.register_device_updated_cb(async_after_update_callback)
 
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)
+        )
         self.loop.run_until_complete(light.process(telegram))
 
         after_update_callback.assert_called_with(light)
@@ -479,7 +532,9 @@ class TestLight(unittest.TestCase):
         )
         self.assertEqual(light.current_brightness, None)
 
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTArray(23))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTArray(23)
+        )
         self.loop.run_until_complete(light.process(telegram))
         self.assertEqual(light.current_brightness, 23)
 
@@ -492,7 +547,9 @@ class TestLight(unittest.TestCase):
             group_address_switch="1/2/3",
             group_address_brightness="1/2/5",
         )
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTBinary(1)
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(light.process(telegram))
 
@@ -506,7 +563,9 @@ class TestLight(unittest.TestCase):
             group_address_switch="1/2/3",
             group_address_brightness="1/2/5",
         )
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTArray((23, 24)))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTArray((23, 24))
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(light.process(telegram))
 
@@ -520,7 +579,9 @@ class TestLight(unittest.TestCase):
             group_address_color="1/2/5",
         )
         self.assertEqual(light.current_color, (None, None))
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTArray((23, 24, 25)))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTArray((23, 24, 25))
+        )
         self.loop.run_until_complete(light.process(telegram))
         self.assertEqual(light.current_color, ((23, 24, 25), None))
 
@@ -536,7 +597,8 @@ class TestLight(unittest.TestCase):
         )
         self.assertEqual(light.current_color, (None, None))
         telegram = Telegram(
-            GroupAddress("1/2/5"), payload=DPTArray((23, 24, 25, 26, 0, 15))
+            destination_address=GroupAddress("1/2/5"),
+            payload=DPTArray((23, 24, 25, 26, 0, 15)),
         )
         self.loop.run_until_complete(light.process(telegram))
         self.assertEqual(light.current_color, ([23, 24, 25], 26))
@@ -552,7 +614,9 @@ class TestLight(unittest.TestCase):
         )
         self.assertEqual(light.current_tunable_white, None)
 
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTArray(23))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTArray(23)
+        )
         self.loop.run_until_complete(light.process(telegram))
         self.assertEqual(light.current_tunable_white, 23)
 
@@ -565,7 +629,9 @@ class TestLight(unittest.TestCase):
             group_address_switch="1/2/3",
             group_address_tunable_white="1/2/5",
         )
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTBinary(1)
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(light.process(telegram))
 
@@ -579,7 +645,9 @@ class TestLight(unittest.TestCase):
             group_address_switch="1/2/3",
             group_address_tunable_white="1/2/5",
         )
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTArray((23, 24)))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTArray((23, 24))
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(light.process(telegram))
 
@@ -595,7 +663,7 @@ class TestLight(unittest.TestCase):
         self.assertEqual(light.current_color_temperature, None)
 
         telegram = Telegram(
-            GroupAddress("1/2/5"),
+            destination_address=GroupAddress("1/2/5"),
             payload=DPTArray(
                 (
                     0x0F,
@@ -615,7 +683,9 @@ class TestLight(unittest.TestCase):
             group_address_switch="1/2/3",
             group_address_color_temperature="1/2/5",
         )
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTBinary(1)
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(light.process(telegram))
 
@@ -629,7 +699,9 @@ class TestLight(unittest.TestCase):
             group_address_switch="1/2/3",
             group_address_color_temperature="1/2/5",
         )
-        telegram = Telegram(GroupAddress("1/2/5"), payload=DPTArray(23))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/5"), payload=DPTArray(23)
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(light.process(telegram))
 
diff --git a/test/devices_tests/notification_test.py b/test/devices_tests/notification_test.py
index 174f12b2..31e5a22c 100644
--- a/test/devices_tests/notification_test.py
+++ b/test/devices_tests/notification_test.py
@@ -35,7 +35,11 @@ class TestNotification(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/4"), TelegramType.GROUP_READ)
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/4"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     #
@@ -46,13 +50,15 @@ class TestNotification(unittest.TestCase):
         xknx = XKNX()
         notification = Notification(xknx, "Warning", group_address="1/2/3")
         telegram_set = Telegram(
-            GroupAddress("1/2/3"), payload=DPTArray(DPTString().to_knx("Ein Prosit!"))
+            destination_address=GroupAddress("1/2/3"),
+            payload=DPTArray(DPTString().to_knx("Ein Prosit!")),
         )
         self.loop.run_until_complete(notification.process(telegram_set))
         self.assertEqual(notification.message, "Ein Prosit!")
 
         telegram_unset = Telegram(
-            GroupAddress("1/2/3"), payload=DPTArray(DPTString().to_knx(""))
+            destination_address=GroupAddress("1/2/3"),
+            payload=DPTArray(DPTString().to_knx("")),
         )
         self.loop.run_until_complete(notification.process(telegram_unset))
         self.assertEqual(notification.message, "")
@@ -70,7 +76,8 @@ class TestNotification(unittest.TestCase):
 
         notification.register_device_updated_cb(async_after_update_callback)
         telegram_set = Telegram(
-            GroupAddress("1/2/3"), payload=DPTArray(DPTString().to_knx("Ein Prosit!"))
+            destination_address=GroupAddress("1/2/3"),
+            payload=DPTArray(DPTString().to_knx("Ein Prosit!")),
         )
         self.loop.run_until_complete(notification.process(telegram_set))
         after_update_callback.assert_called_with(notification)
@@ -80,7 +87,9 @@ class TestNotification(unittest.TestCase):
         # pylint: disable=invalid-name
         xknx = XKNX()
         notification = Notification(xknx, "Warning", group_address="1/2/3")
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTArray((23, 24)))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTArray((23, 24))
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(notification.process(telegram))
 
@@ -88,7 +97,9 @@ class TestNotification(unittest.TestCase):
         """Test process wrong telegram (wrong payload type)."""
         xknx = XKNX()
         notification = Notification(xknx, "Warning", group_address="1/2/3")
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)
+        )
         with self.assertRaises(CouldNotParseTelegram):
             self.loop.run_until_complete(notification.process(telegram))
 
@@ -105,7 +116,7 @@ class TestNotification(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
+                destination_address=GroupAddress("1/2/3"),
                 payload=DPTArray(DPTString().to_knx("Ein Prosit!")),
             ),
         )
@@ -117,7 +128,7 @@ class TestNotification(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
+                destination_address=GroupAddress("1/2/3"),
                 payload=DPTArray(DPTString().to_knx("This is too lo")),
             ),
         )
diff --git a/test/devices_tests/scene_test.py b/test/devices_tests/scene_test.py
index 2daf43ce..671b9400 100644
--- a/test/devices_tests/scene_test.py
+++ b/test/devices_tests/scene_test.py
@@ -45,7 +45,8 @@ class TestScene(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/1"), payload=DPTArray(0x16))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTArray(0x16)),
         )
 
     def test_do(self):
@@ -56,7 +57,8 @@ class TestScene(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/1"), payload=DPTArray(0x16))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTArray(0x16)),
         )
 
     def test_wrong_do(self):
diff --git a/test/devices_tests/sensor_expose_loop_test.py b/test/devices_tests/sensor_expose_loop_test.py
index 0716b081..db73175b 100644
--- a/test/devices_tests/sensor_expose_loop_test.py
+++ b/test/devices_tests/sensor_expose_loop_test.py
@@ -1459,8 +1459,8 @@ class SensorExposeLoopTest(unittest.TestCase):
                 )
 
                 incoming_telegram = Telegram(
-                    GroupAddress("1/1/1"),
-                    TelegramType.GROUP_WRITE,
+                    destination_address=GroupAddress("1/1/1"),
+                    telegramtype=TelegramType.GROUP_WRITE,
                     direction=TelegramDirection.INCOMING,
                     payload=test_payload,
                 )
@@ -1481,8 +1481,8 @@ class SensorExposeLoopTest(unittest.TestCase):
                 self.assertEqual(
                     outgoing_telegram,
                     Telegram(
-                        GroupAddress("2/2/2"),
-                        TelegramType.GROUP_WRITE,
+                        destination_address=GroupAddress("2/2/2"),
+                        telegramtype=TelegramType.GROUP_WRITE,
                         direction=TelegramDirection.OUTGOING,
                         payload=test_payload,
                     ),
@@ -1509,8 +1509,8 @@ class SensorExposeLoopTest(unittest.TestCase):
                 )
 
                 incoming_telegram = Telegram(
-                    GroupAddress("1/1/1"),
-                    TelegramType.GROUP_WRITE,
+                    destination_address=GroupAddress("1/1/1"),
+                    telegramtype=TelegramType.GROUP_WRITE,
                     direction=TelegramDirection.INCOMING,
                     payload=test_payload,
                 )
@@ -1525,8 +1525,8 @@ class SensorExposeLoopTest(unittest.TestCase):
                 self.assertEqual(
                     outgoing_telegram,
                     Telegram(
-                        GroupAddress("2/2/2"),
-                        TelegramType.GROUP_WRITE,
+                        destination_address=GroupAddress("2/2/2"),
+                        telegramtype=TelegramType.GROUP_WRITE,
                         direction=TelegramDirection.OUTGOING,
                         payload=test_payload,
                     ),
diff --git a/test/devices_tests/sensor_test.py b/test/devices_tests/sensor_test.py
index 5394a1d8..7157ce38 100644
--- a/test/devices_tests/sensor_test.py
+++ b/test/devices_tests/sensor_test.py
@@ -2640,7 +2640,11 @@ class TestSensor(unittest.TestCase):
 
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ)
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     #
@@ -2665,7 +2669,7 @@ class TestSensor(unittest.TestCase):
             xknx, "TestSensor", value_type="temperature", group_address_state="1/2/3"
         )
 
-        telegram = Telegram(GroupAddress("1/2/3"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/3"))
         telegram.payload = DPTArray((0x06, 0xA0))
         self.loop.run_until_complete(sensor.process(telegram))
         self.assertEqual(sensor.sensor_value.payload, DPTArray((0x06, 0xA0)))
@@ -2687,7 +2691,7 @@ class TestSensor(unittest.TestCase):
 
         sensor.register_device_updated_cb(async_after_update_callback)
 
-        telegram = Telegram(GroupAddress("1/2/3"))
+        telegram = Telegram(destination_address=GroupAddress("1/2/3"))
         telegram.payload = DPTArray((0x01, 0x02))
         self.loop.run_until_complete(sensor.process(telegram))
         after_update_callback.assert_called_with(sensor)
diff --git a/test/devices_tests/switch_test.py b/test/devices_tests/switch_test.py
index 08525bb7..2ed54db4 100644
--- a/test/devices_tests/switch_test.py
+++ b/test/devices_tests/switch_test.py
@@ -42,7 +42,11 @@ class TestSwitch(unittest.TestCase):
 
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ)
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     def test_sync_state_address(self):
@@ -57,7 +61,11 @@ class TestSwitch(unittest.TestCase):
 
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/4"), TelegramType.GROUP_READ)
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/4"),
+                telegramtype=TelegramType.GROUP_READ,
+            ),
         )
 
     #
@@ -247,7 +255,8 @@ class TestSwitch(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)),
         )
 
     #
@@ -261,7 +270,8 @@ class TestSwitch(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)),
         )
 
     #
@@ -276,14 +286,16 @@ class TestSwitch(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)),
         )
 
         self.loop.run_until_complete(switch.set_off())
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)),
         )
 
     #
diff --git a/test/io_tests/tunnelling_test.py b/test/io_tests/tunnelling_test.py
index 6f29ad1c..265e5ef9 100644
--- a/test/io_tests/tunnelling_test.py
+++ b/test/io_tests/tunnelling_test.py
@@ -28,7 +28,9 @@ class TestTunnelling(unittest.TestCase):
         xknx = XKNX()
         communication_channel_id = 23
         udp_client = UDPClient(xknx, ("192.168.1.1", 0), ("192.168.1.2", 1234))
-        telegram = Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x1, 0x2, 0x3)))
+        telegram = Telegram(
+            destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x1, 0x2, 0x3))
+        )
         sequence_counter = 42
         src_address = PhysicalAddress("2.2.2")
         tunnelling = Tunnelling(
diff --git a/test/knxip_tests/routing_indication_test.py b/test/knxip_tests/routing_indication_test.py
index c5f43e5f..6ee944a7 100644
--- a/test/knxip_tests/routing_indication_test.py
+++ b/test/knxip_tests/routing_indication_test.py
@@ -105,7 +105,14 @@ class Test_KNXIP(unittest.TestCase):
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
         telegram = knxipframe.body.cemi.telegram
-        self.assertEqual(telegram, Telegram(GroupAddress("329"), payload=DPTBinary(1)))
+        self.assertEqual(
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("329"),
+                payload=DPTBinary(1),
+                source_address=PhysicalAddress("15.15.249"),
+            ),
+        )
 
         knxipframe2 = KNXIPFrame(xknx)
         knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION)
@@ -126,7 +133,14 @@ class Test_KNXIP(unittest.TestCase):
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
         telegram = knxipframe.body.cemi.telegram
-        self.assertEqual(telegram, Telegram(GroupAddress("329"), payload=DPTBinary(0)))
+        self.assertEqual(
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("329"),
+                payload=DPTBinary(0),
+                source_address=PhysicalAddress("15.15.249"),
+            ),
+        )
 
         knxipframe2 = KNXIPFrame(xknx)
         knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION)
@@ -148,7 +162,12 @@ class Test_KNXIP(unittest.TestCase):
         knxipframe.from_knx(raw)
         telegram = knxipframe.body.cemi.telegram
         self.assertEqual(
-            telegram, Telegram(GroupAddress("331"), payload=DPTArray(0x65))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("331"),
+                payload=DPTArray(0x65),
+                source_address=PhysicalAddress("15.15.249"),
+            ),
         )
 
         knxipframe2 = KNXIPFrame(xknx)
@@ -173,7 +192,9 @@ class Test_KNXIP(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("2049"), payload=DPTArray(DPTTemperature().to_knx(19.85))
+                destination_address=GroupAddress("2049"),
+                payload=DPTArray(DPTTemperature().to_knx(19.85)),
+                source_address=PhysicalAddress("1.4.2"),
             ),
         )
 
@@ -197,7 +218,12 @@ class Test_KNXIP(unittest.TestCase):
         knxipframe.from_knx(raw)
         telegram = knxipframe.body.cemi.telegram
         self.assertEqual(
-            telegram, Telegram(GroupAddress("440"), TelegramType.GROUP_READ)
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("440"),
+                telegramtype=TelegramType.GROUP_READ,
+                source_address=PhysicalAddress("15.15.249"),
+            ),
         )
 
         knxipframe2 = KNXIPFrame(xknx)
@@ -222,7 +248,10 @@ class Test_KNXIP(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("392"), TelegramType.GROUP_RESPONSE, payload=DPTBinary(1)
+                destination_address=GroupAddress("392"),
+                telegramtype=TelegramType.GROUP_RESPONSE,
+                payload=DPTBinary(1),
+                source_address=PhysicalAddress("1.3.1"),
             ),
         )
 
@@ -242,6 +271,7 @@ class Test_KNXIP(unittest.TestCase):
         telegram = Telegram(
             destination_address=GroupAddress(337),
             payload=DPTBinary(DPTBinary.APCI_MAX_VALUE),
+            source_address=PhysicalAddress("1.3.1"),
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
diff --git a/test/knxip_tests/tunnelling_request_test.py b/test/knxip_tests/tunnelling_request_test.py
index d334b1cd..d13eca7a 100644
--- a/test/knxip_tests/tunnelling_request_test.py
+++ b/test/knxip_tests/tunnelling_request_test.py
@@ -59,13 +59,13 @@ class Test_KNXIP_TunnelingReq(unittest.TestCase):
 
         self.assertEqual(
             knxipframe.body.cemi.telegram,
-            Telegram(GroupAddress("9/0/8"), payload=DPTBinary(1)),
+            Telegram(destination_address=GroupAddress("9/0/8"), payload=DPTBinary(1)),
         )
 
         knxipframe2 = KNXIPFrame(xknx)
         knxipframe2.init(KNXIPServiceType.TUNNELLING_REQUEST)
         knxipframe2.body.cemi.telegram = Telegram(
-            GroupAddress("9/0/8"), payload=DPTBinary(1)
+            destination_address=GroupAddress("9/0/8"), payload=DPTBinary(1)
         )
         knxipframe2.body.sequence_counter = 23
         knxipframe2.normalize()
diff --git a/test/remote_value_tests/remote_value_1count_test.py b/test/remote_value_tests/remote_value_1count_test.py
index 29a734ba..bcf5b411 100644
--- a/test/remote_value_tests/remote_value_1count_test.py
+++ b/test/remote_value_tests/remote_value_1count_test.py
@@ -41,7 +41,10 @@ class TestRemoteValue1Count(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x64,)))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x64,))
+            ),
         )
         self.loop.run_until_complete(
             remote_value.set(
@@ -51,7 +54,10 @@ class TestRemoteValue1Count(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x65,)))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x65,))
+            ),
         )
 
     def test_process(self):
diff --git a/test/remote_value_tests/remote_value_climate_mode_test.py b/test/remote_value_tests/remote_value_climate_mode_test.py
index c30c4a80..2f745e36 100644
--- a/test/remote_value_tests/remote_value_climate_mode_test.py
+++ b/test/remote_value_tests/remote_value_climate_mode_test.py
@@ -176,7 +176,10 @@ class TestRemoteValueDptValue1Ucount(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x03,)))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x03,))
+            ),
         )
         self.loop.run_until_complete(
             remote_value.set(HVACOperationMode.FROST_PROTECTION)
@@ -184,7 +187,10 @@ class TestRemoteValueDptValue1Ucount(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x04,)))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x04,))
+            ),
         )
 
     def test_set_binary(self):
@@ -199,7 +205,10 @@ class TestRemoteValueDptValue1Ucount(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(True))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTBinary(True)
+            ),
         )
         self.loop.run_until_complete(
             remote_value.set(HVACOperationMode.FROST_PROTECTION)
@@ -207,7 +216,10 @@ class TestRemoteValueDptValue1Ucount(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(False))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTBinary(False)
+            ),
         )
 
     def test_process_operation_mode(self):
diff --git a/test/remote_value_tests/remote_value_color_rgb_test.py b/test/remote_value_tests/remote_value_color_rgb_test.py
index b75b3c19..e6c33dd0 100644
--- a/test/remote_value_tests/remote_value_color_rgb_test.py
+++ b/test/remote_value_tests/remote_value_color_rgb_test.py
@@ -64,14 +64,20 @@ class TestRemoteValueColorRGB(unittest.TestCase):
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             telegram,
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x64, 0x65, 0x66))),
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                payload=DPTArray((0x64, 0x65, 0x66)),
+            ),
         )
         self.loop.run_until_complete(remote_value.set((100, 101, 104)))
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
             telegram,
-            Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x64, 0x65, 0x68))),
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                payload=DPTArray((0x64, 0x65, 0x68)),
+            ),
         )
 
     def test_process(self):
diff --git a/test/remote_value_tests/remote_value_color_rgbw_test.py b/test/remote_value_tests/remote_value_color_rgbw_test.py
index 79f966c1..5b929b23 100644
--- a/test/remote_value_tests/remote_value_color_rgbw_test.py
+++ b/test/remote_value_tests/remote_value_color_rgbw_test.py
@@ -93,7 +93,7 @@ class TestRemoteValueColorRGBW(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
+                destination_address=GroupAddress("1/2/3"),
                 payload=DPTArray((0x64, 0x65, 0x66, 0x67, 0x00, 0x0F)),
             ),
         )
@@ -103,7 +103,7 @@ class TestRemoteValueColorRGBW(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
+                destination_address=GroupAddress("1/2/3"),
                 payload=DPTArray((0x64, 0x65, 0x68, 0x69, 0x00, 0x0F)),
             ),
         )
diff --git a/test/remote_value_tests/remote_value_dpt_2_byte_unsigned_test.py b/test/remote_value_tests/remote_value_dpt_2_byte_unsigned_test.py
index 0ac5b746..e34c8dcc 100644
--- a/test/remote_value_tests/remote_value_dpt_2_byte_unsigned_test.py
+++ b/test/remote_value_tests/remote_value_dpt_2_byte_unsigned_test.py
@@ -52,7 +52,11 @@ class TestRemoteValueDptValue2Ucount(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x0A, 0x0B)))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"),
+                payload=DPTArray((0x0A, 0x0B)),
+            ),
         )
         self.loop.run_until_complete(remote_value.set(5500))
         self.assertEqual(xknx.telegrams.qsize(), 1)
@@ -60,7 +64,7 @@ class TestRemoteValueDptValue2Ucount(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
+                destination_address=GroupAddress("1/2/3"),
                 payload=DPTArray(
                     (
                         0x15,
diff --git a/test/remote_value_tests/remote_value_dpt_value_1_ucount_test.py b/test/remote_value_tests/remote_value_dpt_value_1_ucount_test.py
index 09b82151..5f3ae1eb 100644
--- a/test/remote_value_tests/remote_value_dpt_value_1_ucount_test.py
+++ b/test/remote_value_tests/remote_value_dpt_value_1_ucount_test.py
@@ -52,13 +52,19 @@ class TestRemoteValueDptValue1Ucount(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x0A,)))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0A,))
+            ),
         )
         self.loop.run_until_complete(remote_value.set(11))
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x0B,)))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0B,))
+            ),
         )
 
     def test_process(self):
diff --git a/test/remote_value_tests/remote_value_scene_number_test.py b/test/remote_value_tests/remote_value_scene_number_test.py
index 7bd34074..a356bd7c 100644
--- a/test/remote_value_tests/remote_value_scene_number_test.py
+++ b/test/remote_value_tests/remote_value_scene_number_test.py
@@ -50,13 +50,19 @@ class TestRemoteValueSceneNumber(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x0A,)))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0A,))
+            ),
         )
         self.loop.run_until_complete(remote_value.set(12))
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTArray((0x0B,)))
+            telegram,
+            Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0B,))
+            ),
         )
 
     def test_process(self):
diff --git a/test/remote_value_tests/remote_value_step_test.py b/test/remote_value_tests/remote_value_step_test.py
index 71bf7207..5a326954 100644
--- a/test/remote_value_tests/remote_value_step_test.py
+++ b/test/remote_value_tests/remote_value_step_test.py
@@ -80,13 +80,15 @@ class TestRemoteValueStep(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)),
         )
         self.loop.run_until_complete(remote_value.increase())
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)),
         )
 
     def test_process(self):
diff --git a/test/remote_value_tests/remote_value_string_test.py b/test/remote_value_tests/remote_value_string_test.py
index 305cec59..78ac4949 100644
--- a/test/remote_value_tests/remote_value_string_test.py
+++ b/test/remote_value_tests/remote_value_string_test.py
@@ -92,7 +92,7 @@ class TestRemoteValueString(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
+                destination_address=GroupAddress("1/2/3"),
                 payload=DPTArray((97, 115, 100, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)),
             ),
         )
@@ -102,7 +102,7 @@ class TestRemoteValueString(unittest.TestCase):
         self.assertEqual(
             telegram,
             Telegram(
-                GroupAddress("1/2/3"),
+                destination_address=GroupAddress("1/2/3"),
                 payload=DPTArray((65, 83, 68, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)),
             ),
         )
diff --git a/test/remote_value_tests/remote_value_switch_test.py b/test/remote_value_tests/remote_value_switch_test.py
index ae16d07b..325dc042 100644
--- a/test/remote_value_tests/remote_value_switch_test.py
+++ b/test/remote_value_tests/remote_value_switch_test.py
@@ -64,13 +64,15 @@ class TestRemoteValueSwitch(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)),
         )
         self.loop.run_until_complete(remote_value.off())
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)),
         )
 
     def test_process(self):
diff --git a/test/remote_value_tests/remote_value_test.py b/test/remote_value_tests/remote_value_test.py
index cd87890a..44c528a0 100644
--- a/test/remote_value_tests/remote_value_test.py
+++ b/test/remote_value_tests/remote_value_test.py
@@ -94,7 +94,10 @@ class TestRemoteValue(unittest.TestCase):
             patch_valid.return_value = False
             patch_has_group_address.return_value = True
 
-            telegram = Telegram(GroupAddress("1/2/1"), payload=DPTArray((0x01, 0x02)))
+            telegram = Telegram(
+                destination_address=GroupAddress("1/2/1"),
+                payload=DPTArray((0x01, 0x02)),
+            )
             with self.assertRaises(CouldNotParseTelegram):
                 self.loop.run_until_complete(remote_value.process(telegram))
 
@@ -109,7 +112,9 @@ class TestRemoteValue(unittest.TestCase):
             patch_valid.return_value = True
 
             fut = asyncio.Future()
-            telegram = Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+            telegram = Telegram(
+                destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)
+            )
             fut.set_result(telegram)
             patch_read.return_value = fut
 
@@ -152,7 +157,9 @@ class TestRemoteValue(unittest.TestCase):
         with patch("xknx.remote_value.RemoteValue.payload_valid") as patch_valid:
             patch_valid.return_value = True
             test_payload = DPTArray((0x01, 0x02))
-            telegram = Telegram(GroupAddress("1/1/1"), payload=test_payload)
+            telegram = Telegram(
+                destination_address=GroupAddress("1/1/1"), payload=test_payload
+            )
             self.assertTrue(
                 self.loop.run_until_complete(
                     asyncio.Task(remote_value.process(telegram))
diff --git a/test/remote_value_tests/remote_value_updown_test.py b/test/remote_value_tests/remote_value_updown_test.py
index 9898b5c8..303c9fb4 100644
--- a/test/remote_value_tests/remote_value_updown_test.py
+++ b/test/remote_value_tests/remote_value_updown_test.py
@@ -80,13 +80,15 @@ class TestRemoteValueUpDown(unittest.TestCase):
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(1))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)),
         )
         self.loop.run_until_complete(remote_value.up())
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
-            telegram, Telegram(GroupAddress("1/2/3"), payload=DPTBinary(0))
+            telegram,
+            Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)),
         )
 
     def test_process(self):
diff --git a/test/str_test.py b/test/str_test.py
index 539876cc..43e9f339 100644
--- a/test/str_test.py
+++ b/test/str_test.py
@@ -473,8 +473,8 @@ class TestStringRepresentations(unittest.TestCase):
         )
         self.assertEqual(
             str(telegram),
-            '<Telegram destination_address="GroupAddress("1/2/3")", payload="<DPTBinary value="7" />" telegramtype="TelegramType.GROUP_WRITE" direction='
-            '"TelegramDirection.OUTGOING" />',
+            '<Telegram direction="Outgoing" telegramtype="GroupValueWrite" source_address="PhysicalAddress("0.0.0")" '
+            'destination_address="GroupAddress("1/2/3")" payload="<DPTBinary value="7" />" />',
         )
 
     def test_dib_generic(self):
diff --git a/test/util.py b/test/util.py
new file mode 100644
index 00000000..538a19cb
--- /dev/null
+++ b/test/util.py
@@ -0,0 +1,36 @@
+"""Utility functions for testing."""
+from io import StringIO
+import logging
+from unittest.mock import patch
+
+from xknx.config import yaml_loader
+
+logger = logging.getLogger("xknx.log")
+
+
+def patch_yaml_files(files_dict, endswith=True):
+    """Patch load_yaml with a dictionary of yaml files."""
+    # match using endswith, start search with longest string
+    matchlist = sorted(list(files_dict.keys()), key=len) if endswith else []
+
+    def mock_open_f(fname, **_):
+        """Mock open() in the yaml module, used by load_yaml."""
+        # Return the mocked file on full match
+        if fname in files_dict:
+            logger.debug("patch_yaml_files match %s", fname)
+            res = StringIO(files_dict[fname])
+            setattr(res, "name", fname)
+            return res
+
+        # Match using endswith
+        for ends in matchlist:
+            if fname.endswith(ends):
+                logger.debug("patch_yaml_files end match %s: %s", ends, fname)
+                res = StringIO(files_dict[ends])
+                setattr(res, "name", fname)
+                return res
+
+        # Not found
+        raise FileNotFoundError(f"File not found: {fname}")
+
+    return patch.object(yaml_loader, "open", mock_open_f, create=True)