Repository: XKNX/xknx
Pull request #867 (2022-02-13 22:42:31)

Return `bytes` from `to_knx()` in knxip package
<!--
  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.
-->
Return `bytes` from `to_knx()` in knxip package instead of `list[int]`.

This will ease the implementation of KNX IP-secure as `bytes` are commonly used by cryptography methods.

It has the side-effect of raising earlier when a used int was out of range (or not an int) which should yield more reasonable error messages.

Fixes # (issue)

## 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)
- [ ] 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
- [x] Tests have been added that prove the fix is effective or that the feature works
- [x] The changes are documented in the changelog
- [ ] The Homeassistant plugin has been adjusted in case of new config options


codecov[bot]: # [Codecov](https://codecov.io/gh/XKNX/xknx/pull/867?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX) Report
> Merging [#867](https://codecov.io/gh/XKNX/xknx/pull/867?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX) (550e642) into [main](https://codecov.io/gh/XKNX/xknx/commit/25bb77fcdda00e614f6848f60821761ffc8cfbb3?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX) (25bb77f) will **decrease** coverage by `0.06%`.
> The diff coverage is `93.20%`.

[![Impacted file tree graph](https://codecov.io/gh/XKNX/xknx/pull/867/graphs/tree.svg?width=650&height=150&src=pr&token=irWbIygS84&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX)](https://codecov.io/gh/XKNX/xknx/pull/867?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX)

```diff
@@            Coverage Diff             @@
##             main     #867      +/-   ##
==========================================
- Coverage   95.72%   95.65%   -0.07%     
==========================================
  Files         114      114              
  Lines        7014     6929      -85     
==========================================
- Hits         6714     6628      -86     
- Misses        300      301       +1     
```


| [Impacted Files](https://codecov.io/gh/XKNX/xknx/pull/867?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX) | Coverage Δ | |
|---|---|---|
| [xknx/io/transport/tcp\_transport.py](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9pby90cmFuc3BvcnQvdGNwX3RyYW5zcG9ydC5weQ==) | `46.42% <0.00%> (ø)` | |
| [xknx/io/transport/udp\_transport.py](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9pby90cmFuc3BvcnQvdWRwX3RyYW5zcG9ydC5weQ==) | `50.00% <0.00%> (ø)` | |
| [xknx/knxip/hpai.py](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9rbnhpcC9ocGFpLnB5) | `94.44% <77.77%> (-5.56%)` | :arrow_down: |
| [xknx/knxip/disconnect\_request.py](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9rbnhpcC9kaXNjb25uZWN0X3JlcXVlc3QucHk=) | `95.65% <83.33%> (-1.50%)` | :arrow_down: |
| [xknx/knxip/body.py](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9rbnhpcC9ib2R5LnB5) | `100.00% <100.00%> (ø)` | |
| [xknx/knxip/cemi\_frame.py](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9rbnhpcC9jZW1pX2ZyYW1lLnB5) | `100.00% <100.00%> (ø)` | |
| [xknx/knxip/connect\_request.py](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9rbnhpcC9jb25uZWN0X3JlcXVlc3QucHk=) | `97.22% <100.00%> (-0.56%)` | :arrow_down: |
| [xknx/knxip/connect\_response.py](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9rbnhpcC9jb25uZWN0X3Jlc3BvbnNlLnB5) | `97.67% <100.00%> (-0.44%)` | :arrow_down: |
| [xknx/knxip/connectionstate\_request.py](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9rbnhpcC9jb25uZWN0aW9uc3RhdGVfcmVxdWVzdC5weQ==) | `96.29% <100.00%> (ø)` | |
| [xknx/knxip/connectionstate\_response.py](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX#diff-eGtueC9rbnhpcC9jb25uZWN0aW9uc3RhdGVfcmVzcG9uc2UucHk=) | `96.00% <100.00%> (ø)` | |
| ... and [12 more](https://codecov.io/gh/XKNX/xknx/pull/867/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=XKNX) | |


Patch:
diff --git a/changelog.md b/changelog.md
index 2e991c1a..8d46a38c 100644
--- a/changelog.md
+++ b/changelog.md
@@ -5,6 +5,7 @@
 ### Internals
 
 - Drop support for Python 3.8 to follow Home Assistant changes
+- Return `bytes` from to_knx() in knxip package instead of `list[int]`
 
 ## 0.19.2 TCP Heartbeat 2022-02-06
 
diff --git a/xknx/io/transport/tcp_transport.py b/xknx/io/transport/tcp_transport.py
index 2822ec2b..a9deb70d 100644
--- a/xknx/io/transport/tcp_transport.py
+++ b/xknx/io/transport/tcp_transport.py
@@ -71,7 +71,7 @@ class TCPTransport(KNXIPTransport):
         """Parse and process KNXIP frame. Callback for having received data over TCP."""
         if self._buffer:
             raw = self._buffer + raw
-            self._buffer = bytes()
+            self._buffer = b""
         if not raw:
             return
         try:
@@ -131,4 +131,4 @@ class TCPTransport(KNXIPTransport):
         if self.transport is None:
             raise CommunicationError("Transport not connected")
 
-        self.transport.write(bytes(knxipframe.to_knx()))
+        self.transport.write(knxipframe.to_knx())
diff --git a/xknx/io/transport/udp_transport.py b/xknx/io/transport/udp_transport.py
index 23ed4258..493e5d90 100644
--- a/xknx/io/transport/udp_transport.py
+++ b/xknx/io/transport/udp_transport.py
@@ -186,6 +186,6 @@ class UDPTransport(KNXIPTransport):
                     "Multicast send to specific address is invalid. %s",
                     knxipframe,
                 )
-            self.transport.sendto(bytes(knxipframe.to_knx()), self.remote_addr)
+            self.transport.sendto(knxipframe.to_knx(), self.remote_addr)
         else:
-            self.transport.sendto(bytes(knxipframe.to_knx()), addr=_addr)
+            self.transport.sendto(knxipframe.to_knx(), addr=_addr)
diff --git a/xknx/knxip/body.py b/xknx/knxip/body.py
index f44b51f9..ec3ce111 100644
--- a/xknx/knxip/body.py
+++ b/xknx/knxip/body.py
@@ -32,7 +32,7 @@ class KNXIPBody(ABC):
         """Parse/deserialize from KNX/IP raw data."""
 
     @abstractmethod
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
 
     def __eq__(self, other: object) -> bool:
diff --git a/xknx/knxip/cemi_frame.py b/xknx/knxip/cemi_frame.py
index 82b3e877..8ad7996f 100644
--- a/xknx/knxip/cemi_frame.py
+++ b/xknx/knxip/cemi_frame.py
@@ -177,7 +177,7 @@ class CEMIFrame:
 
         return 10 + addil + self.mpdu_len
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
         if not isinstance(self.payload, APCI):
             raise TypeError()
@@ -186,18 +186,23 @@ class CEMIFrame:
         if not isinstance(self.dst_addr, (GroupAddress, IndividualAddress)):
             raise ConversionError("dst_addr not set")
 
-        data = []
-
-        data.append(self.code.value)
-        data.append(0x00)
-        data.append((self.flags >> 8) & 255)
-        data.append(self.flags & 255)
-        data.extend(self.src_addr.to_knx())
-        data.extend(self.dst_addr.to_knx())
-        data.append(self.payload.calculated_length())
-        data.extend(self.payload.to_knx())
-
-        return data
+        return (
+            bytes(
+                (
+                    self.code.value,
+                    0x00,  # Additional information length
+                )
+            )
+            + self.flags.to_bytes(2, "big")
+            + bytes(
+                (
+                    *self.src_addr.to_knx(),
+                    *self.dst_addr.to_knx(),
+                    self.payload.calculated_length(),
+                )
+            )
+            + self.payload.to_knx()
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/connect_request.py b/xknx/knxip/connect_request.py
index 3b330704..39db6f15 100644
--- a/xknx/knxip/connect_request.py
+++ b/xknx/knxip/connect_request.py
@@ -60,23 +60,23 @@ class ConnectRequest(KNXIPBody):
         pos += cri_from_knx(raw[pos:])
         return pos
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
 
-        def cri_to_knx() -> list[int]:
+        def cri_to_knx() -> bytes:
             """Serialize CRI (Connect Request Information)."""
-            cri = []
-            cri.append(ConnectRequest.CRI_LENGTH)
-            cri.append(self.request_type.value)
-            cri.append(self.flags)
-            cri.append(0x00)  # Reserved
-            return cri
-
-        data = []
-        data.extend(self.control_endpoint.to_knx())
-        data.extend(self.data_endpoint.to_knx())
-        data.extend(cri_to_knx())
-        return data
+            return bytes(
+                (
+                    ConnectRequest.CRI_LENGTH,
+                    self.request_type.value,
+                    self.flags,
+                    0x00,  # Reserved
+                )
+            )
+
+        return (
+            self.control_endpoint.to_knx() + self.data_endpoint.to_knx() + cri_to_knx()
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/connect_response.py b/xknx/knxip/connect_response.py
index 865923ef..a54ddc7b 100644
--- a/xknx/knxip/connect_response.py
+++ b/xknx/knxip/connect_response.py
@@ -74,27 +74,22 @@ class ConnectResponse(KNXIPBodyResponse):
             pos = len(raw)
         return pos
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
 
-        def crd_to_knx() -> list[int]:
+        def crd_to_knx() -> bytes:
             """Serialize CRD (Connect Response Data Block)."""
             assert self.identifier is not None
 
-            crd = []
-            crd.append(ConnectResponse.CRD_LENGTH)
-            crd.append(self.request_type.value)
-            crd.append((self.identifier >> 8) & 255)
-            crd.append(self.identifier & 255)
-            return crd
+            return bytes(
+                (ConnectResponse.CRD_LENGTH, self.request_type.value)
+            ) + self.identifier.to_bytes(2, "big")
 
-        data = []
-        data.append(self.communication_channel)
-        data.append(self.status_code.value)
-        data.extend(self.data_endpoint.to_knx())
-        data.extend(crd_to_knx())
-
-        return data
+        return (
+            bytes((self.communication_channel, self.status_code.value))
+            + self.data_endpoint.to_knx()
+            + crd_to_knx()
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/connectionstate_request.py b/xknx/knxip/connectionstate_request.py
index 1846604c..d92fac8b 100644
--- a/xknx/knxip/connectionstate_request.py
+++ b/xknx/knxip/connectionstate_request.py
@@ -52,13 +52,12 @@ class ConnectionStateRequest(KNXIPBody):
         pos += self.control_endpoint.from_knx(raw[pos:])
         return pos
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
-        return [
-            self.communication_channel_id,
-            0x00,  # Reserved
-            *self.control_endpoint.to_knx(),
-        ]
+        return (
+            bytes((self.communication_channel_id, 0x00))  # 2nd byte is reserved
+            + self.control_endpoint.to_knx()
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/connectionstate_response.py b/xknx/knxip/connectionstate_response.py
index 44445d2a..bfe57aa1 100644
--- a/xknx/knxip/connectionstate_response.py
+++ b/xknx/knxip/connectionstate_response.py
@@ -47,9 +47,9 @@ class ConnectionStateResponse(KNXIPBodyResponse):
         self.status_code = ErrorCode(raw[1])
         return ConnectionStateResponse.LENGTH
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
-        return [self.communication_channel_id, self.status_code.value]
+        return bytes((self.communication_channel_id, self.status_code.value))
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/description_request.py b/xknx/knxip/description_request.py
index b9fa9e9f..c61d6a28 100644
--- a/xknx/knxip/description_request.py
+++ b/xknx/knxip/description_request.py
@@ -34,7 +34,7 @@ class DescriptionRequest(KNXIPBody):
         """Parse/deserialize from KNX/IP raw data."""
         return self.control_endpoint.from_knx(raw)
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
         return self.control_endpoint.to_knx()
 
diff --git a/xknx/knxip/description_response.py b/xknx/knxip/description_response.py
index 75763e5d..ff6f2cb8 100644
--- a/xknx/knxip/description_response.py
+++ b/xknx/knxip/description_response.py
@@ -50,12 +50,9 @@ class DescriptionResponse(KNXIPBody):
                 return dib.name
         return "UNKNOWN"
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
-        data = []
-        for dib in self.dibs:
-            data.extend(dib.to_knx())
-        return data
+        return b"".join(dib.to_knx() for dib in self.dibs)
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/dib.py b/xknx/knxip/dib.py
index d726894c..bb82cacc 100644
--- a/xknx/knxip/dib.py
+++ b/xknx/knxip/dib.py
@@ -13,13 +13,15 @@ A KNX/IP Search Response may contain several DIBs of different types:
 from __future__ import annotations
 
 from abc import ABC, abstractmethod
-from typing import Iterator
+import socket
 
 from xknx.exceptions import CouldNotParseKNXIP
 from xknx.telegram import IndividualAddress
 
 from .knxip_enum import DIBServiceFamily, DIBTypeCode, KNXMedium
 
+DIB_HEADER_LENGTH = 2  # structure lenght and description type code
+
 
 class DIB(ABC):
     """
@@ -36,13 +38,15 @@ class DIB(ABC):
     @abstractmethod
     def calculated_length(self) -> int:
         """Get length of KNX/IP object."""
+        # The structure shall always have an even number of octets which may have to be
+        # achieved by padding with 00h in the last octet of the DIB structure.
 
     @abstractmethod
     def from_knx(self, raw: bytes) -> int:
         """Parse/deserialize from KNX/IP raw data."""
 
     @abstractmethod
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
 
     @staticmethod
@@ -72,11 +76,12 @@ class DIBGeneric(DIB):
         # DTC Description Type Code
         self.dtc: DIBTypeCode | int = 0
         # IBD Information Block Data
-        self.data: list[int] | bytes = []
+        self.data = bytes()
 
     def calculated_length(self) -> int:
         """Get length of KNX/IP object."""
-        return len(self.data)
+        data_length = len(self.data)
+        return DIB_HEADER_LENGTH + data_length + data_length % 2
 
     def from_knx(self, raw: bytes) -> int:
         """Parse/deserialize from KNX/IP raw data."""
@@ -90,22 +95,22 @@ class DIBGeneric(DIB):
             self.dtc = DIBTypeCode(raw[1])
         except ValueError:
             self.dtc = raw[1]
-        self.data = raw[:dib_length]
+        self.data = raw[2:dib_length]
 
         return dib_length
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
         if not isinstance(self.dtc, DIBTypeCode):
             try:
                 self.dtc = DIBTypeCode(self.dtc)
             except ValueError:
                 raise CouldNotParseKNXIP("DTC invalid")
-        data = []
-        data.append(len(self.data))
-        data.append(self.dtc.value)
-        data.extend(self.data[2:])
-        return data
+        return (
+            bytes((self.calculated_length(), self.dtc.value))
+            + self.data
+            + bytes(len(self.data) % 2)  # padding
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
@@ -123,6 +128,10 @@ class DIBSuppSVCFamilies(DIB):
             self.name = name
             self.version = version
 
+        def to_knx(self) -> bytes:
+            """Serialize to KNX/IP raw data."""
+            return bytes((self.name.value, self.version))
+
         def __str__(self) -> str:
             """Return object as readable string."""
             return f'<Family name="{self.name}" version="{self.version}" />'
@@ -146,7 +155,7 @@ class DIBSuppSVCFamilies(DIB):
 
     def calculated_length(self) -> int:
         """Get length of KNX/IP object."""
-        return len(self.families) * 2 + 2
+        return len(self.families) * 2 + DIB_HEADER_LENGTH
 
     def from_knx(self, raw: bytes) -> int:
         """Parse/deserialize from KNX/IP raw data."""
@@ -158,21 +167,23 @@ class DIBSuppSVCFamilies(DIB):
         if DIBTypeCode(raw[1]) != DIBTypeCode.SUPP_SVC_FAMILIES:
             raise CouldNotParseKNXIP("DIB is no device info")
 
-        for i in range(0, int((length - 2) / 2)):
-            name = DIBServiceFamily(raw[i * 2 + 2])
-            version = raw[i * 2 + 3]
+        for pos in range(2, length, 2):
+            name = DIBServiceFamily(raw[pos])
+            version = raw[pos + 1]
             self.families.append(DIBSuppSVCFamilies.Family(name, version))
         return length
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
-        data = []
-        data.append(len(self.families) * 2 + 2)
-        data.append(DIBTypeCode.SUPP_SVC_FAMILIES.value)
-        for family in self.families:
-            data.append(family.name.value)
-            data.append(family.version)
-        return data
+        return (
+            bytes(
+                (
+                    self.calculated_length(),
+                    DIBTypeCode.SUPP_SVC_FAMILIES.value,
+                )
+            )
+            + b"".join(family.to_knx() for family in self.families)
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
@@ -220,50 +231,48 @@ class DIBDeviceInformation(DIB):
         installation_project_identifier = raw[6] * 256 + raw[7]
         self.project_number = installation_project_identifier >> 4
         self.installation_number = installation_project_identifier & 15
-        self.serial_number = ":".join(f"{i:02x}" for i in raw[8:14])
-        self.multicast_address = ".".join(f"{i:d}" for i in raw[14:18])
-        self.mac_address = ":".join(f"{i:02x}" for i in raw[18:24])
-        self.name = "".join(map(chr, raw[24:54])).rstrip("\0")
+        self.serial_number = raw[8:14].hex(":")
+        self.multicast_address = socket.inet_ntoa(raw[14:18])
+        self.mac_address = raw[18:24].hex(":")
+        self.name = raw[24:54].decode(encoding="latin_1", errors="replace").rstrip("\0")
         return DIBDeviceInformation.LENGTH
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
 
-        def hex_notation_to_knx(serial_number: str) -> Iterator[int]:
+        def hex_notation_to_knx(colon_hex: str) -> bytes:
             """Serialize hex notation."""
-            for part in serial_number.split(":"):
-                yield int(part, 16)
+            return bytes.fromhex(colon_hex.replace(":", ""))
 
-        def ip_to_knx(ip_addr: str) -> Iterator[int]:
+        def ip_to_knx(ip_addr: str) -> bytes:
             """Serialize ip."""
-            for part in ip_addr.split("."):
-                yield int(part)
-
-        def str_to_knx(string: str, length: int) -> Iterator[int]:
-            """Serialize string."""
-            if len(string) > length - 1:
-                string = string[: length - 1]
-            for char in string:
-                yield ord(char)
-            for _ in range(0, 30 - len(string)):
-                yield 0x00
+            return socket.inet_aton(ip_addr)
+
+        def name_str_to_knx(string: str) -> bytes:
+            """Serialize name string."""
+            # pad with null bytes to length 30; ISO 8859-1 (latin_1) according to KNX specification
+            return bytes(string[:30], "latin_1").ljust(30, b"\0")
 
         installation_project_identifier = (
-            self.project_number * 16
-        ) + self.installation_number
-        data = []
-        data.append(DIBDeviceInformation.LENGTH)
-        data.append(DIBTypeCode.DEVICE_INFO.value)
-        data.append(self.knx_medium.value)
-        data.append(int(self.programming_mode))
-        data.extend(self.individual_address.to_knx())
-        data.append((installation_project_identifier >> 8) & 255)
-        data.append(installation_project_identifier & 255)
-        data.extend(hex_notation_to_knx(self.serial_number))
-        data.extend(ip_to_knx(self.multicast_address))
-        data.extend(hex_notation_to_knx(self.mac_address))
-        data.extend(str_to_knx(self.name, 30))
-        return data
+            (self.project_number * 16) + self.installation_number
+        ).to_bytes(2, "big")
+
+        return (
+            bytes(
+                (
+                    DIBDeviceInformation.LENGTH,
+                    DIBTypeCode.DEVICE_INFO.value,
+                    self.knx_medium.value,
+                    self.programming_mode,
+                )
+            )
+            + bytes(self.individual_address.to_knx())
+            + installation_project_identifier
+            + hex_notation_to_knx(self.serial_number)
+            + ip_to_knx(self.multicast_address)
+            + hex_notation_to_knx(self.mac_address)
+            + name_str_to_knx(self.name)
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/disconnect_request.py b/xknx/knxip/disconnect_request.py
index 14ba17d6..c3566cc0 100644
--- a/xknx/knxip/disconnect_request.py
+++ b/xknx/knxip/disconnect_request.py
@@ -40,33 +40,18 @@ class DisconnectRequest(KNXIPBody):
 
     def from_knx(self, raw: bytes) -> int:
         """Parse/deserialize from KNX/IP raw data."""
+        if len(raw) < 2:
+            raise CouldNotParseKNXIP("Disconnect info has wrong length")
+        self.communication_channel_id = raw[0]
+        # raw[1] is reserved
+        return self.control_endpoint.from_knx(raw[2:]) + 2
 
-        def info_from_knx(info: bytes) -> int:
-            """Parse info bytes."""
-            if len(info) < 2:
-                raise CouldNotParseKNXIP("Disconnect info has wrong length")
-            self.communication_channel_id = info[0]
-            # info[1] is reserved
-            return 2
-
-        pos = info_from_knx(raw)
-        pos += self.control_endpoint.from_knx(raw[pos:])
-        return pos
-
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
-
-        def info_to_knx() -> list[int]:
-            """Serialize information bytes."""
-            info = []
-            info.append(self.communication_channel_id)
-            info.append(0x00)  # Reserved
-            return info
-
-        data = []
-        data.extend(info_to_knx())
-        data.extend(self.control_endpoint.to_knx())
-        return data
+        return (
+            bytes((self.communication_channel_id, 0x00))  # 2nd byte is reserved
+            + self.control_endpoint.to_knx()
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/disconnect_response.py b/xknx/knxip/disconnect_response.py
index f883ac5a..4b02b1c0 100644
--- a/xknx/knxip/disconnect_response.py
+++ b/xknx/knxip/disconnect_response.py
@@ -48,12 +48,14 @@ class DisconnectResponse(KNXIPBodyResponse):
         self.status_code = ErrorCode(raw[1])
         return DisconnectResponse.LENGTH
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
-        return [
-            self.communication_channel_id,
-            self.status_code.value,
-        ]
+        return bytes(
+            (
+                self.communication_channel_id,
+                self.status_code.value,
+            )
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/header.py b/xknx/knxip/header.py
index eeefbf42..b657c9c0 100644
--- a/xknx/knxip/header.py
+++ b/xknx/knxip/header.py
@@ -47,16 +47,13 @@ class KNXIPHeader:
             raise TypeError()
         self.total_length = KNXIPHeader.HEADERLENGTH + body.calculated_length()
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
-        return [
-            KNXIPHeader.HEADERLENGTH,
-            KNXIPHeader.PROTOCOLVERSION,
-            (self.service_type_ident.value >> 8) & 255,
-            self.service_type_ident.value & 255,
-            (self.total_length >> 8) & 255,
-            self.total_length & 255,
-        ]
+        return (
+            bytes((KNXIPHeader.HEADERLENGTH, KNXIPHeader.PROTOCOLVERSION))
+            + self.service_type_ident.value.to_bytes(2, "big")
+            + self.total_length.to_bytes(2, "big")
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/hpai.py b/xknx/knxip/hpai.py
index 1a2be6d6..7ae5a0a3 100644
--- a/xknx/knxip/hpai.py
+++ b/xknx/knxip/hpai.py
@@ -5,7 +5,7 @@ A HPAI contains an IP address and a port.
 """
 from __future__ import annotations
 
-from typing import Iterator
+import socket
 
 from xknx.exceptions import ConversionError, CouldNotParseKNXIP
 
@@ -43,27 +43,24 @@ class HPAI:
             self.protocol = HostProtocol(raw[1])
         except ValueError as err:
             raise CouldNotParseKNXIP("unsupported host protocol code") from err
-        self.ip_addr = f"{raw[2]}.{raw[3]}.{raw[4]}.{raw[5]}"
+        self.ip_addr = socket.inet_ntoa(raw[2:6])
         self.port = raw[6] * 256 + raw[7]
         return HPAI.LENGTH
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
-
-        def ip_addr_to_bytes(ip_addr: str) -> Iterator[int]:
-            """Serialize ip address to byte array."""
-            if not isinstance(ip_addr, str):
-                raise ConversionError("ip_addr is not a string")
-            for i in ip_addr.split("."):
-                yield int(i)
-
-        return [
-            HPAI.LENGTH,
-            self.protocol.value,
-            *ip_addr_to_bytes(self.ip_addr),
-            (self.port >> 8) & 255,
-            self.port & 255,
-        ]
+        try:
+            return (
+                bytes((HPAI.LENGTH, self.protocol.value))
+                + socket.inet_aton(self.ip_addr)
+                + self.port.to_bytes(2, "big")
+            )
+        except (OSError, TypeError) as err:
+            # OSError for invalid address strings; TypeError for non-strings
+            raise ConversionError(f"Invalid IPv4 address: {self.ip_addr}") from err
+        except (OverflowError, AttributeError) as err:
+            # OverflowError for int < 0 or int > 65535; AttributeError for non-integers
+            raise ConversionError(f"Port is not valid: {self.port}") from err
 
     def __repr__(self) -> str:
         """Representation of object."""
diff --git a/xknx/knxip/knxip.py b/xknx/knxip/knxip.py
index a1715532..5767b64c 100644
--- a/xknx/knxip/knxip.py
+++ b/xknx/knxip/knxip.py
@@ -45,32 +45,35 @@ class KNXIPFrame:
         self.header.service_type_ident = service_type_ident
 
         body: KNXIPBody
-        if service_type_ident == KNXIPServiceType.ROUTING_INDICATION:
-            body = RoutingIndication(self.xknx)
-        elif service_type_ident == KNXIPServiceType.CONNECT_REQUEST:
-            body = ConnectRequest(self.xknx)
-        elif service_type_ident == KNXIPServiceType.CONNECT_RESPONSE:
-            body = ConnectResponse(self.xknx)
-        elif service_type_ident == KNXIPServiceType.TUNNELLING_REQUEST:
-            body = TunnellingRequest(self.xknx)
-        elif service_type_ident == KNXIPServiceType.TUNNELLING_ACK:
-            body = TunnellingAck(self.xknx)
+        # Core
+        if service_type_ident == KNXIPServiceType.SEARCH_REQUEST:
+            body = SearchRequest(self.xknx)
+        elif service_type_ident == KNXIPServiceType.SEARCH_RESPONSE:
+            body = SearchResponse(self.xknx)
         elif service_type_ident == KNXIPServiceType.DESCRIPTION_REQUEST:
             body = DescriptionRequest(self.xknx)
         elif service_type_ident == KNXIPServiceType.DESCRIPTION_RESPONSE:
             body = DescriptionResponse(self.xknx)
-        elif service_type_ident == KNXIPServiceType.SEARCH_REQUEST:
-            body = SearchRequest(self.xknx)
-        elif service_type_ident == KNXIPServiceType.SEARCH_RESPONSE:
-            body = SearchResponse(self.xknx)
-        elif service_type_ident == KNXIPServiceType.DISCONNECT_REQUEST:
-            body = DisconnectRequest(self.xknx)
-        elif service_type_ident == KNXIPServiceType.DISCONNECT_RESPONSE:
-            body = DisconnectResponse(self.xknx)
+        elif service_type_ident == KNXIPServiceType.CONNECT_REQUEST:
+            body = ConnectRequest(self.xknx)
+        elif service_type_ident == KNXIPServiceType.CONNECT_RESPONSE:
+            body = ConnectResponse(self.xknx)
         elif service_type_ident == KNXIPServiceType.CONNECTIONSTATE_REQUEST:
             body = ConnectionStateRequest(self.xknx)
         elif service_type_ident == KNXIPServiceType.CONNECTIONSTATE_RESPONSE:
             body = ConnectionStateResponse(self.xknx)
+        elif service_type_ident == KNXIPServiceType.DISCONNECT_REQUEST:
+            body = DisconnectRequest(self.xknx)
+        elif service_type_ident == KNXIPServiceType.DISCONNECT_RESPONSE:
+            body = DisconnectResponse(self.xknx)
+        # Tunneling
+        elif service_type_ident == KNXIPServiceType.TUNNELLING_REQUEST:
+            body = TunnellingRequest(self.xknx)
+        elif service_type_ident == KNXIPServiceType.TUNNELLING_ACK:
+            body = TunnellingAck(self.xknx)
+        # Routing
+        elif service_type_ident == KNXIPServiceType.ROUTING_INDICATION:
+            body = RoutingIndication(self.xknx)
         else:
             raise CouldNotParseKNXIP(
                 f"KNXIPServiceType not implemented: {service_type_ident.name}"
@@ -98,14 +101,11 @@ class KNXIPFrame:
         )
         return self.header.total_length
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
         if self.body is None:
             raise CouldNotParseKNXIP("No body defined in KNXIPFrame.")
-        data = []
-        data.extend(self.header.to_knx())
-        data.extend(self.body.to_knx())
-        return data
+        return self.header.to_knx() + self.body.to_knx()
 
     def __repr__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/knxip_enum.py b/xknx/knxip/knxip_enum.py
index 9e15df11..6b5913ce 100644
--- a/xknx/knxip/knxip_enum.py
+++ b/xknx/knxip/knxip_enum.py
@@ -43,11 +43,11 @@ class KNXIPServiceType(Enum):
     # 0x08 Object Server services
     # 0x09 Security services
     SECURE_WRAPPER = 0x0950
-    SECURE_SESSION_REQUEST = 0x0951
-    SECURE_SESSION_RESPONSE = 0x0952
-    SECURE_SESSION_AUTHENTICATE = 0x0953
-    SECURE_SESSION_STATUS = 0x0954
-    SECURE_TIMER_NOTIFY = 0x0955
+    SESSION_REQUEST = 0x0951
+    SESSION_RESPONSE = 0x0952
+    SESSION_AUTHENTICATE = 0x0953
+    SESSION_STATUS = 0x0954
+    TIMER_NOTIFY = 0x0955
 
 
 class CEMIMessageCode(Enum):
diff --git a/xknx/knxip/routing_indication.py b/xknx/knxip/routing_indication.py
index 5eb68848..45405941 100644
--- a/xknx/knxip/routing_indication.py
+++ b/xknx/knxip/routing_indication.py
@@ -49,7 +49,7 @@ class RoutingIndication(KNXIPBody):
             self.cemi = None
             return len(raw)
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
         if self.cemi is None:
             raise CouldNotParseKNXIP("No CEMIFrame defined.")
diff --git a/xknx/knxip/search_request.py b/xknx/knxip/search_request.py
index 87249349..6e64c1ab 100644
--- a/xknx/knxip/search_request.py
+++ b/xknx/knxip/search_request.py
@@ -37,7 +37,7 @@ class SearchRequest(KNXIPBody):
         """Parse/deserialize from KNX/IP raw data."""
         return self.discovery_endpoint.from_knx(raw)
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
         return self.discovery_endpoint.to_knx()
 
diff --git a/xknx/knxip/search_response.py b/xknx/knxip/search_response.py
index 7e5a438b..f35059ba 100644
--- a/xknx/knxip/search_response.py
+++ b/xknx/knxip/search_response.py
@@ -51,13 +51,11 @@ class SearchResponse(KNXIPBody):
                 return dib.name
         return "UNKNOWN"
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
-        data = []
-        data.extend(self.control_endpoint.to_knx())
-        for dib in self.dibs:
-            data.extend(dib.to_knx())
-        return data
+        return self.control_endpoint.to_knx() + b"".join(
+            dib.to_knx() for dib in self.dibs
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/tunnelling_ack.py b/xknx/knxip/tunnelling_ack.py
index 83e41eef..80da22a8 100644
--- a/xknx/knxip/tunnelling_ack.py
+++ b/xknx/knxip/tunnelling_ack.py
@@ -48,14 +48,16 @@ class TunnellingAck(KNXIPBodyResponse):
         self.status_code = ErrorCode(raw[3])
         return TunnellingAck.BODY_LENGTH
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
-        return [
-            TunnellingAck.BODY_LENGTH,
-            self.communication_channel_id,
-            self.sequence_counter,
-            self.status_code.value,
-        ]
+        return bytes(
+            (
+                TunnellingAck.BODY_LENGTH,
+                self.communication_channel_id,
+                self.sequence_counter,
+                self.status_code.value,
+            )
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""
diff --git a/xknx/knxip/tunnelling_request.py b/xknx/knxip/tunnelling_request.py
index 78bb4b87..fc956954 100644
--- a/xknx/knxip/tunnelling_request.py
+++ b/xknx/knxip/tunnelling_request.py
@@ -73,24 +73,21 @@ class TunnellingRequest(KNXIPBody):
             return len(raw)
         return pos
 
-    def to_knx(self) -> list[int]:
+    def to_knx(self) -> bytes:
         """Serialize to KNX/IP raw data."""
         if self.cemi is None:
             raise CouldNotParseKNXIP("No CEMIFrame defined.")
-
-        def header_to_knx() -> list[int]:
-            """Serialize header."""
-            cri = []
-            cri.append(TunnellingRequest.HEADER_LENGTH)
-            cri.append(self.communication_channel_id)
-            cri.append(self.sequence_counter)
-            cri.append(0x00)  # Reserved
-            return cri
-
-        data = []
-        data.extend(header_to_knx())
-        data.extend(self.cemi.to_knx())
-        return data
+        return (
+            bytes(
+                (
+                    TunnellingRequest.HEADER_LENGTH,
+                    self.communication_channel_id,
+                    self.sequence_counter,
+                    0x00,  # Reserved
+                )
+            )
+            + self.cemi.to_knx()
+        )
 
     def __str__(self) -> str:
         """Return object as readable string."""


Test patch:
diff --git a/requirements/testing.txt b/requirements/testing.txt
index 610a9577..bf2d534c 100644
--- a/requirements/testing.txt
+++ b/requirements/testing.txt
@@ -8,6 +8,6 @@ pylint==2.12.2
 pytest==7.0.1
 pytest-asyncio==0.18.1
 pytest-cov==3.0.0
-setuptools==60.9.0
+setuptools==60.9.3
 tox==3.24.5
 mypy==0.931
diff --git a/test/core_tests/payload_reader_test.py b/test/core_tests/payload_reader_test.py
index faf22d14..bb927b31 100644
--- a/test/core_tests/payload_reader_test.py
+++ b/test/core_tests/payload_reader_test.py
@@ -14,9 +14,7 @@ class TestPayloadReader:
     """Test class for payload reader."""
 
     def create_telegram_queue_mock(self, xknx: XKNX, response_telegram: Telegram):
-        """
-        Create a TelegramQueue mock that returns a specific response telegram.
-        """
+        """Create a TelegramQueue mock that returns a specific response telegram."""
         xknx.telegram_queue = MagicMock()
 
         def _register_telegram_received_cb(func):
diff --git a/test/devices_tests/datetime_test.py b/test/devices_tests/datetime_test.py
index f837fe9f..03652d98 100644
--- a/test/devices_tests/datetime_test.py
+++ b/test/devices_tests/datetime_test.py
@@ -16,7 +16,7 @@ class TestDateTime:
 
     # pylint: disable=attribute-defined-outside-init
     def teardown_method(self):
-        """Cancel broadcast_task"""
+        """Cancel broadcast_task."""
         self.datetime.__del__()
 
     #
diff --git a/test/devices_tests/device_test.py b/test/devices_tests/device_test.py
index d853fb8a..8c27355c 100644
--- a/test/devices_tests/device_test.py
+++ b/test/devices_tests/device_test.py
@@ -62,7 +62,7 @@ class TestDevice:
 
     @patch("logging.Logger.exception")
     async def test_bad_callback(self, logging_exception_mock):
-        """Test handling callback raising an exception"""
+        """Test handling callback raising an exception."""
         xknx = XKNX()
         device = Device(xknx, "TestDevice")
         good_callback_1 = AsyncMock()
diff --git a/test/devices_tests/weather_test.py b/test/devices_tests/weather_test.py
index 8fa25bc4..81e3567b 100644
--- a/test/devices_tests/weather_test.py
+++ b/test/devices_tests/weather_test.py
@@ -241,7 +241,7 @@ class TestWeather:
         )
 
     async def test_sunny_summer(self):
-        """Test returns sunny condition if illuminance is in defined interval"""
+        """Test returns sunny condition if illuminance is in defined interval."""
         xknx = XKNX()
         weather: Weather = Weather(
             name="weather",
diff --git a/test/io_tests/self_description_test.py b/test/io_tests/self_description_test.py
index 00fc51ff..2cb31e29 100644
--- a/test/io_tests/self_description_test.py
+++ b/test/io_tests/self_description_test.py
@@ -28,7 +28,11 @@ class TestSelfDescription:
             DescriptionRequest(xknx, control_endpoint=HPAI(*local_addr))
         )
         description_response_raw = bytes.fromhex(
-            "061002040048360102001000000000082d40834de000170c000ab3274a324b4e582f49502d526f7574657200000000000000000000000000000000000c0202020302040205020701",
+            "06 10 02 04 00 48 36 01 02 00 10 00 00 00 00"
+            "08 2d 40 83 4d e0 00 17 0c 00 0a b3 27 4a 32"
+            "4b 4e 58 2f 49 50 2d 52 6f 75 74 65 72 00 00"
+            "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
+            "0c 02 02 02 03 02 04 02 05 02 07 01",
         )
         description_query = DescriptionQuery(
             xknx, transport=transport_mocked, local_hpai=HPAI(*local_addr)
diff --git a/test/knxip_tests/cemi_frame_test.py b/test/knxip_tests/cemi_frame_test.py
index 8a935015..1e0f3cf7 100644
--- a/test/knxip_tests/cemi_frame_test.py
+++ b/test/knxip_tests/cemi_frame_test.py
@@ -1,4 +1,4 @@
-"""Tests for the CEMIFrame object"""
+"""Tests for the CEMIFrame object."""
 from unittest.mock import MagicMock
 
 from pytest import fixture, raises
@@ -32,13 +32,13 @@ def get_data(code, adil, flags, src, dst, mpdu_len, tpci_apci, payload):
 
 @fixture(name="frame")
 def fixture_frame():
-    """Fixture to get a simple mocked frame"""
+    """Fixture to get a simple mocked frame."""
     xknx = MagicMock()
     return CEMIFrame(xknx)
 
 
 def test_valid_command(frame):
-    """Test for valid frame parsing"""
+    """Test for valid frame parsing."""
     packet_len = frame.from_knx(get_data(0x29, 0, 0, 0, 0, 1, 0, []))
     assert frame.code == CEMIMessageCode.L_DATA_IND
     assert frame.flags == 0
@@ -50,19 +50,19 @@ def test_valid_command(frame):
 
 
 def test_invalid_tpci_apci(frame):
-    """Test for invalid APCIService"""
+    """Test for invalid APCIService."""
     with raises(UnsupportedCEMIMessage, match=r".*APCI not supported: .*"):
         frame.from_knx_data_link_layer(get_data(0x29, 0, 0, 0, 0, 1, 0xFFC0, []))
 
 
 def test_invalid_apdu_len(frame):
-    """Test for invalid apdu len"""
+    """Test for invalid apdu len."""
     with raises(CouldNotParseKNXIP, match=r".*APDU LEN should be .*"):
         frame.from_knx(get_data(0x29, 0, 0, 0, 0, 2, 0, []))
 
 
 def test_invalid_src_addr(frame):
-    """Test for invalid src addr"""
+    """Test for invalid src addr."""
     frame.code = CEMIMessageCode.L_DATA_IND
     frame.flags = 0
     frame.mpdu_len = 1
@@ -75,7 +75,7 @@ def test_invalid_src_addr(frame):
 
 
 def test_invalid_dst_addr(frame):
-    """Test for invalid dst addr"""
+    """Test for invalid dst addr."""
     frame.code = CEMIMessageCode.L_DATA_IND
     frame.flags = 0
     frame.mpdu_len = 1
@@ -88,7 +88,7 @@ def test_invalid_dst_addr(frame):
 
 
 def test_invalid_payload(frame):
-    """Test for having wrong payload set"""
+    """Test for having wrong payload set."""
     frame.code = CEMIMessageCode.L_DATA_IND
     frame.flags = 0
     frame.mpdu_len = 1
@@ -104,13 +104,13 @@ def test_invalid_payload(frame):
 
 
 def test_from_knx_with_not_handleable_cemi(frame):
-    """Test for having unhandlebale cemi set"""
+    """Test for having unhandlebale cemi set."""
     with raises(UnsupportedCEMIMessage, match=r".*CEMIMessageCode not implemented:.*"):
         frame.from_knx(get_data(0x30, 0, 0, 0, 0, 2, 0, []))
 
 
 def test_from_knx_with_not_implemented_cemi(frame):
-    """Test for having not implemented CEMI set"""
+    """Test for having not implemented CEMI set."""
     with raises(UnsupportedCEMIMessage, match=r".*Could not handle CEMIMessageCode:.*"):
         frame.from_knx(
             get_data(CEMIMessageCode.L_BUSMON_IND.value, 0, 0, 0, 0, 2, 0, [])
@@ -118,27 +118,27 @@ def test_from_knx_with_not_implemented_cemi(frame):
 
 
 def test_invalid_invalid_len(frame):
-    """Test for invalid cemi len"""
+    """Test for invalid cemi len."""
     with raises(UnsupportedCEMIMessage, match=r".*CEMI too small.*"):
         frame.from_knx_data_link_layer(get_data(0x29, 0, 0, 0, 0, 2, 0, [])[:5])
 
 
 def test_from_knx_group_address(frame):
-    """Test conversion for a cemi with a group address as destination"""
+    """Test conversion for a cemi with a group address as destination."""
     frame.from_knx(get_data(0x29, 0, 0x80, 0, 0, 1, 0, []))
 
     assert frame.dst_addr == GroupAddress(0)
 
 
 def test_from_knx_individual_address(frame):
-    """Test conversion for a cemi with a individual address as destination"""
+    """Test conversion for a cemi with a individual address as destination."""
     frame.from_knx(get_data(0x29, 0, 0x00, 0, 0, 1, 0, []))
 
     assert frame.dst_addr == IndividualAddress(0)
 
 
 def test_telegram_group_address(frame):
-    """Test telegram conversion flags with a group address"""
+    """Test telegram conversion flags with a group address."""
     frame.telegram = Telegram(destination_address=GroupAddress(0))
 
     assert (
@@ -147,7 +147,7 @@ def test_telegram_group_address(frame):
 
 
 def test_telegram_individual_address(frame):
-    """Test telegram conversion flags with a individual address"""
+    """Test telegram conversion flags with a individual address."""
     frame.telegram = Telegram(destination_address=IndividualAddress(0))
 
     assert (
@@ -156,6 +156,6 @@ def test_telegram_individual_address(frame):
 
 
 def test_telegram_unsupported_address(frame):
-    """Test telegram conversion flags with an unsupported address"""
+    """Test telegram conversion flags with an unsupported address."""
     with raises(TypeError):
         frame.telegram = Telegram(destination_address=object())
diff --git a/test/knxip_tests/connect_request_test.py b/test/knxip_tests/connect_request_test.py
index 4ad11256..b97e9635 100644
--- a/test/knxip_tests/connect_request_test.py
+++ b/test/knxip_tests/connect_request_test.py
@@ -10,33 +10,9 @@ class TestKNXIPConnectRequest:
 
     def test_connect_request(self):
         """Test parsing and streaming connection request KNX/IP packet."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x05,
-            0x00,
-            0x1A,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x2A,
-            0x01,
-            0x84,
-            0x95,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x2A,
-            0x01,
-            0xCC,
-            0xA9,
-            0x04,
-            0x04,
-            0x02,
-            0x00,
+        raw = bytes.fromhex(
+            "06 10 02 05 00 1A 08 01 C0 A8 2A 01 84 95 08 01"
+            "C0 A8 2A 01 CC A9 04 04 02 00"
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
@@ -57,37 +33,13 @@ class TestKNXIPConnectRequest:
         )
         knxipframe2 = KNXIPFrame.init_from_body(connect_request)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_from_knx_wrong_length_of_cri(self):
         """Test parsing and streaming wrong ConnectRequest."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x05,
-            0x00,
-            0x1A,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x2A,
-            0x01,
-            0x84,
-            0x95,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x2A,
-            0x01,
-            0xCC,
-            0xA9,
-            0x02,
-            0x04,
-            0x02,
-            0x00,
+        raw = bytes.fromhex(
+            "06 10 02 05 00 1A 08 01 C0 A8 2A 01 84 95 08 01"
+            "C0 A8 2A 01 CC A9 02 04 02 00"
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
@@ -96,32 +48,9 @@ class TestKNXIPConnectRequest:
 
     def test_from_knx_wrong_cri(self):
         """Test parsing and streaming wrong ConnectRequest."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x05,
-            0x00,
-            0x1A,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x2A,
-            0x01,
-            0x84,
-            0x95,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x2A,
-            0x01,
-            0xCC,
-            0xA9,
-            0x04,
-            0x04,
-            0x02,
+        raw = bytes.fromhex(
+            "06 10 02 05 00 1A 08 01 C0 A8 2A 01 84 95 08 01"
+            "C0 A8 2A 01 CC A9 04 04 02"
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
diff --git a/test/knxip_tests/connect_response_test.py b/test/knxip_tests/connect_response_test.py
index 3bd85054..34b994e1 100644
--- a/test/knxip_tests/connect_response_test.py
+++ b/test/knxip_tests/connect_response_test.py
@@ -10,27 +10,8 @@ class TestKNXIPConnectResponse:
 
     def test_connect_response(self):
         """Test parsing and streaming connection response KNX/IP packet."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x06,
-            0x00,
-            0x14,
-            0x01,
-            0x00,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x2A,
-            0x0A,
-            0x0E,
-            0x57,
-            0x04,
-            0x04,
-            0x11,
-            0xFF,
+        raw = bytes.fromhex(
+            "06 10 02 06 00 14 01 00 08 01 C0 A8 2A 0A 0E 57 04 04 11 FF"
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
@@ -52,31 +33,12 @@ class TestKNXIPConnectResponse:
         )
         knxipframe2 = KNXIPFrame.init_from_body(connect_response)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_from_knx_wrong_crd(self):
         """Test parsing and streaming wrong ConnectRequest (wrong CRD length byte)."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x06,
-            0x00,
-            0x14,
-            0x01,
-            0x00,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x2A,
-            0x0A,
-            0x0E,
-            0x57,
-            0x03,
-            0x04,
-            0x11,
-            0xFF,
+        raw = bytes.fromhex(
+            "06 10 02 06 00 14 01 00 08 01 C0 A8 2A 0A 0E 57 03 04 11 FF"
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
@@ -85,26 +47,7 @@ class TestKNXIPConnectResponse:
 
     def test_from_knx_wrong_crd2(self):
         """Test parsing and streaming wrong ConnectRequest (wrong CRD length)."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x06,
-            0x00,
-            0x14,
-            0x01,
-            0x00,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x2A,
-            0x0A,
-            0x0E,
-            0x57,
-            0x04,
-            0x04,
-        )
+        raw = bytes.fromhex("06 10 02 06 00 14 01 00 08 01 C0 A8 2A 0A 0E 57 04 04")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         with pytest.raises(CouldNotParseKNXIP):
@@ -113,29 +56,11 @@ class TestKNXIPConnectResponse:
     def test_connect_response_connection_error_gira(self):
         """
         Test parsing and streaming connection response KNX/IP packet with error e_no_more_connections.
+
         HPAI and CRD normal. This was received from Gira devices (2020).
         """
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x06,
-            0x00,
-            0x14,
-            0xC0,
-            0x24,
-            0x08,
-            0x01,
-            0x0A,
-            0x01,
-            0x00,
-            0x29,
-            0x0E,
-            0x57,
-            0x04,
-            0x04,
-            0x00,
-            0x00,
+        raw = bytes.fromhex(
+            "06 10 02 06 00 14 C0 24 08 01 0A 01 00 29 0E 57 04 04 00 00"
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
@@ -154,34 +79,16 @@ class TestKNXIPConnectResponse:
         )
         knxipframe2 = KNXIPFrame.init_from_body(connect_response)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_connect_response_connection_error_lox(self):
         """
         Test parsing and streaming connection response KNX/IP packet with error e_no_more_connections.
+
         HPAI given, CRD all zero. This was received from Loxone device (2020).
         """
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x06,
-            0x00,
-            0x14,
-            0x00,
-            0x24,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x01,
-            0x01,
-            0x0E,
-            0x57,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
+        raw = bytes.fromhex(
+            "06 10 02 06 00 14 00 24 08 01 C0 A8 01 01 0E 57 00 00 00 00"
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
@@ -195,28 +102,10 @@ class TestKNXIPConnectResponse:
     def test_connect_response_connection_error_mdt(self):
         """
         Test parsing and streaming connection response KNX/IP packet with error e_no_more_connections.
+
         HPAI and CRD all zero. This was received from MDT device (2020).
         """
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x06,
-            0x00,
-            0x08,
-            0x00,
-            0x24,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-        )
+        raw = bytes.fromhex("06 10 02 06 00 08 00 24 00 00 00 00 00 00 00 00 00 00")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
diff --git a/test/knxip_tests/connectionstate_request_test.py b/test/knxip_tests/connectionstate_request_test.py
index 6ef91760..b847e376 100644
--- a/test/knxip_tests/connectionstate_request_test.py
+++ b/test/knxip_tests/connectionstate_request_test.py
@@ -10,24 +10,7 @@ class TestKNXIPConnectionStateRequest:
 
     def test_connection_state_request(self):
         """Test parsing and streaming connection state request KNX/IP packet."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x07,
-            0x00,
-            0x10,
-            0x15,
-            0x00,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0xC8,
-            0x0C,
-            0xC3,
-            0xB4,
-        )
+        raw = bytes.fromhex("06 10 02 07 00 10 15 00 08 01 C0 A8 C8 0C C3 B4")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -46,11 +29,11 @@ class TestKNXIPConnectionStateRequest:
         )
         knxipframe2 = KNXIPFrame.init_from_body(connectionstate_request)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_from_knx_wrong_info(self):
         """Test parsing and streaming wrong ConnectionStateRequest."""
-        raw = (0x06, 0x10, 0x02, 0x07, 0x00, 0x010)
+        raw = bytes((0x06, 0x10, 0x02, 0x07, 0x00, 0x010))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         with pytest.raises(CouldNotParseKNXIP):
diff --git a/test/knxip_tests/connectionstate_response_test.py b/test/knxip_tests/connectionstate_response_test.py
index bd37c04c..10b68a6b 100644
--- a/test/knxip_tests/connectionstate_response_test.py
+++ b/test/knxip_tests/connectionstate_response_test.py
@@ -10,7 +10,7 @@ class TestKNXIPConnectionStateResponse:
 
     def test_disconnect_response(self):
         """Test parsing and streaming connection state response KNX/IP packet."""
-        raw = (0x06, 0x10, 0x02, 0x08, 0x00, 0x08, 0x15, 0x21)
+        raw = bytes((0x06, 0x10, 0x02, 0x08, 0x00, 0x08, 0x15, 0x21))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -25,11 +25,11 @@ class TestKNXIPConnectionStateResponse:
         )
         knxipframe2 = KNXIPFrame.init_from_body(connectionstate_response)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_from_knx_wrong_header(self):
         """Test parsing and streaming wrong ConnectionStateResponse (wrong header length)."""
-        raw = (0x06, 0x10, 0x02, 0x08, 0x00, 0x08, 0x15)
+        raw = bytes((0x06, 0x10, 0x02, 0x08, 0x00, 0x08, 0x15))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         with pytest.raises(CouldNotParseKNXIP):
diff --git a/test/knxip_tests/description_request_test.py b/test/knxip_tests/description_request_test.py
index bfdd806f..72e029e4 100644
--- a/test/knxip_tests/description_request_test.py
+++ b/test/knxip_tests/description_request_test.py
@@ -8,22 +8,7 @@ class TestKNXIPDescriptionRequest:
 
     def test_description_request(self):
         """Test parsing and streaming DescriptionRequest KNX/IP packet."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x03,
-            0x00,
-            0x0E,
-            0x08,
-            0x01,
-            0x7F,
-            0x00,
-            0x00,
-            0x02,
-            0x0E,
-            0x57,
-        )
+        raw = bytes.fromhex("06 10 02 03 00 0E 08 01 7F 00 00 02 0E 57")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -37,4 +22,4 @@ class TestKNXIPDescriptionRequest:
             )
         )
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
diff --git a/test/knxip_tests/description_response_test.py b/test/knxip_tests/description_response_test.py
index 8ef1636b..6cbd177a 100644
--- a/test/knxip_tests/description_response_test.py
+++ b/test/knxip_tests/description_response_test.py
@@ -15,12 +15,16 @@ class TestKNXIPDescriptionResponse:
     def test_description_response(self):
         """Test parsing and streaming DescriptionResponse KNX/IP packet."""
         raw = bytes.fromhex(
-            "061002040048360102001000000000082d40834de000170c000ab3274a324b4e582f49502d526f7574657200000000000000000000000000000000000c0202020302040205020701",
+            "06 10 02 04 00 48 36 01 02 00 10 00 00 00 00 08"
+            "2d 40 83 4d e0 00 17 0c 00 0a b3 27 4a 32 4b 4e"
+            "58 2f 49 50 2d 52 6f 75 74 65 72 00 00 00 00 00"
+            "00 00 00 00 00 00 00 00 00 00 00 00 0c 02 02 02"
+            "03 02 04 02 05 02 07 01",
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         assert knxipframe.from_knx(raw) == 72
-        assert knxipframe.to_knx() == list(raw)
+        assert knxipframe.to_knx() == raw
 
         assert isinstance(knxipframe.body, DescriptionResponse)
         assert len(knxipframe.body.dibs) == 2
@@ -46,7 +50,7 @@ class TestKNXIPDescriptionResponse:
         description_response.dibs.append(knxipframe.body.dibs[1])
         knxipframe2 = KNXIPFrame.init_from_body(description_response)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_unknown_device_name(self):
         """Test device_name if no DIBDeviceInformation is present."""
diff --git a/test/knxip_tests/dib_test.py b/test/knxip_tests/dib_test.py
index bd77b4fb..b881e39c 100644
--- a/test/knxip_tests/dib_test.py
+++ b/test/knxip_tests/dib_test.py
@@ -18,11 +18,13 @@ class TestKNXIPDIB:
 
     def test_dib_base(self):
         """Test parsing and streaming KNX/IP DIB packet."""
-        raw = (0x0C, 0x02, 0x02, 0x01, 0x03, 0x02, 0x04, 0x01, 0x05, 0x01, 0x07, 0x01)
+        raw = bytes(
+            (0x0C, 0x02, 0x02, 0x01, 0x03, 0x02, 0x04, 0x01, 0x05, 0x01, 0x07, 0x01)
+        )
         dib = DIBGeneric()
         assert dib.from_knx(raw) == 12
         assert dib.dtc == DIBTypeCode.SUPP_SVC_FAMILIES
-        assert dib.to_knx() == list(raw)
+        assert dib.to_knx() == raw
         assert dib.calculated_length() == 12
 
     def test_dib_wrong_input(self):
@@ -33,61 +35,11 @@ class TestKNXIPDIB:
 
     def test_device_info(self):
         """Test parsing of device info."""
-        raw = (
-            0x36,
-            0x01,
-            0x02,
-            0x00,
-            0x11,
-            0x00,
-            0x23,
-            0x42,
-            0x13,
-            0x37,
-            0x13,
-            0x37,
-            0x13,
-            0x37,
-            0xE0,
-            0x00,
-            0x17,
-            0x0C,
-            0x00,
-            0x01,
-            0x02,
-            0x03,
-            0x04,
-            0x05,
-            0x47,
-            0x69,
-            0x72,
-            0x61,
-            0x20,
-            0x4B,
-            0x4E,
-            0x58,
-            0x2F,
-            0x49,
-            0x50,
-            0x2D,
-            0x52,
-            0x6F,
-            0x75,
-            0x74,
-            0x65,
-            0x72,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
+        raw = bytes.fromhex(
+            "36 01 02 00 11 00 23 42 13 37 13 37 13 37 E0 00"
+            "17 0C 00 01 02 03 04 05 47 69 72 61 20 4B 4E 58"
+            "2F 49 50 2D 52 6F 75 74 65 72 00 00 00 00 00 00"
+            "00 00 00 00 00 00"
         )
 
         dib = DIB.determine_dib(raw)
@@ -102,11 +54,13 @@ class TestKNXIPDIB:
         assert dib.serial_number == "13:37:13:37:13:37"
         assert dib.project_number == 564
         assert dib.installation_number == 2
-        assert dib.to_knx() == list(raw)
+        assert dib.to_knx() == raw
 
     def test_dib_sup_svc_families_router(self):
         """Test parsing of svc families."""
-        raw = (0x0C, 0x02, 0x02, 0x01, 0x03, 0x02, 0x04, 0x01, 0x05, 0x01, 0x07, 0x01)
+        raw = bytes(
+            (0x0C, 0x02, 0x02, 0x01, 0x03, 0x02, 0x04, 0x01, 0x05, 0x01, 0x07, 0x01)
+        )
 
         dib = DIB.determine_dib(raw)
         assert isinstance(dib, DIBSuppSVCFamilies)
@@ -122,7 +76,7 @@ class TestKNXIPDIB:
             ),
         ]
 
-        assert dib.to_knx() == list(raw)
+        assert dib.to_knx() == raw
 
         assert dib.supports(DIBServiceFamily.CORE)
         assert dib.supports(DIBServiceFamily.DEVICE_MANAGEMENT)
@@ -133,7 +87,7 @@ class TestKNXIPDIB:
 
     def test_dib_sup_svc_families_interface(self):
         """Test parsing of svc families."""
-        raw = (0x0A, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04, 0x02, 0x07, 0x01)
+        raw = bytes((0x0A, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04, 0x02, 0x07, 0x01))
 
         dib = DIB.determine_dib(raw)
         assert isinstance(dib, DIBSuppSVCFamilies)
@@ -148,7 +102,7 @@ class TestKNXIPDIB:
             ),
         ]
 
-        assert dib.to_knx() == list(raw)
+        assert dib.to_knx() == raw
 
         assert dib.supports(DIBServiceFamily.TUNNELING)
         assert dib.supports(DIBServiceFamily.TUNNELING, version=2)
diff --git a/test/knxip_tests/disconnect_request_test.py b/test/knxip_tests/disconnect_request_test.py
index 4577292c..21494690 100644
--- a/test/knxip_tests/disconnect_request_test.py
+++ b/test/knxip_tests/disconnect_request_test.py
@@ -10,24 +10,7 @@ class TestKNXIPDisconnectRequest:
 
     def test_disconnect_request(self):
         """Test parsing and streaming DisconnectRequest KNX/IP packet."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x09,
-            0x00,
-            0x10,
-            0x15,
-            0x00,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0xC8,
-            0x0C,
-            0xC3,
-            0xB4,
-        )
+        raw = bytes.fromhex("06 10 02 09 00 10 15 00 08 01 C0 A8 C8 0C C3 B4")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -46,11 +29,11 @@ class TestKNXIPDisconnectRequest:
         )
         knxipframe2 = KNXIPFrame.init_from_body(disconnect_request)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_from_knx_wrong_length(self):
         """Test parsing and streaming wrong DisconnectRequest."""
-        raw = (0x06, 0x10, 0x02, 0x09, 0x00, 0x10)
+        raw = bytes((0x06, 0x10, 0x02, 0x09, 0x00, 0x10))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         with pytest.raises(CouldNotParseKNXIP):
diff --git a/test/knxip_tests/disconnect_response_test.py b/test/knxip_tests/disconnect_response_test.py
index a323fae9..5a0cabb3 100644
--- a/test/knxip_tests/disconnect_response_test.py
+++ b/test/knxip_tests/disconnect_response_test.py
@@ -10,7 +10,7 @@ class TestKNXIPDisconnectResponse:
 
     def test_disconnect_response(self):
         """Test parsing and streaming DisconnectResponse KNX/IP packet."""
-        raw = (0x06, 0x10, 0x02, 0x0A, 0x00, 0x08, 0x15, 0x25)
+        raw = bytes((0x06, 0x10, 0x02, 0x0A, 0x00, 0x08, 0x15, 0x25))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -27,11 +27,11 @@ class TestKNXIPDisconnectResponse:
         )
         knxipframe2 = KNXIPFrame.init_from_body(disconnect_response)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_from_knx_wrong_length(self):
         """Test parsing and streaming wrong DisconnectResponse."""
-        raw = (0x06, 0x10, 0x02, 0x0A, 0x00, 0x08, 0x15)
+        raw = bytes((0x06, 0x10, 0x02, 0x0A, 0x00, 0x08, 0x15))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         with pytest.raises(CouldNotParseKNXIP):
diff --git a/test/knxip_tests/header_test.py b/test/knxip_tests/header_test.py
index 7a053429..278db27a 100644
--- a/test/knxip_tests/header_test.py
+++ b/test/knxip_tests/header_test.py
@@ -10,7 +10,7 @@ class TestKNXIPHeader:
 
     def test_from_knx(self):
         """Test parsing and streaming wrong Header (wrong length byte)."""
-        raw = (0x06, 0x10, 0x04, 0x21, 0x00, 0x0A)
+        raw = bytes((0x06, 0x10, 0x04, 0x21, 0x00, 0x0A))
         header = KNXIPHeader()
         assert header.from_knx(raw) == 6
         assert header.HEADERLENGTH == 6
@@ -18,7 +18,7 @@ class TestKNXIPHeader:
         assert header.service_type_ident == KNXIPServiceType.TUNNELLING_ACK
         assert header.b4_reserve == 0
         assert header.total_length == 10
-        assert header.to_knx() == list(raw)
+        assert header.to_knx() == raw
 
     def test_set_length(self):
         """Test setting length."""
@@ -36,21 +36,21 @@ class TestKNXIPHeader:
 
     def test_from_knx_wrong_header(self):
         """Test parsing and streaming wrong Header (wrong length)."""
-        raw = (0x06, 0x10, 0x04, 0x21, 0x00)
+        raw = bytes((0x06, 0x10, 0x04, 0x21, 0x00))
         header = KNXIPHeader()
         with pytest.raises(CouldNotParseKNXIP):
             header.from_knx(raw)
 
     def test_from_knx_wrong_header2(self):
         """Test parsing and streaming wrong Header (wrong length byte)."""
-        raw = (0x05, 0x10, 0x04, 0x21, 0x00, 0x0A)
+        raw = bytes((0x05, 0x10, 0x04, 0x21, 0x00, 0x0A))
         header = KNXIPHeader()
         with pytest.raises(CouldNotParseKNXIP):
             header.from_knx(raw)
 
     def test_from_knx_wrong_header3(self):
         """Test parsing and streaming wrong Header (wrong protocol version)."""
-        raw = (0x06, 0x11, 0x04, 0x21, 0x00, 0x0A)
+        raw = bytes((0x06, 0x11, 0x04, 0x21, 0x00, 0x0A))
         header = KNXIPHeader()
         with pytest.raises(CouldNotParseKNXIP):
             header.from_knx(raw)
@@ -58,7 +58,7 @@ class TestKNXIPHeader:
     def test_from_knx_wrong_header4(self):
         """Test parsing and streaming wrong Header (unsupported service type)."""
         # 0x0000 as service type
-        raw = (0x06, 0x10, 0x00, 0x00, 0x00, 0x0A)
+        raw = bytes((0x06, 0x10, 0x00, 0x00, 0x00, 0x0A))
         header = KNXIPHeader()
         with pytest.raises(CouldNotParseKNXIP):
             header.from_knx(raw)
diff --git a/test/knxip_tests/hpai_test.py b/test/knxip_tests/hpai_test.py
index 5c5f9106..92dcdd59 100644
--- a/test/knxip_tests/hpai_test.py
+++ b/test/knxip_tests/hpai_test.py
@@ -9,7 +9,7 @@ class TestKNXIPHPAI:
 
     def test_hpai_udp(self):
         """Test parsing and streaming HPAI KNX/IP fragment."""
-        raw = (0x08, 0x01, 0xC0, 0xA8, 0x2A, 0x01, 0x84, 0x95)
+        raw = bytes((0x08, 0x01, 0xC0, 0xA8, 0x2A, 0x01, 0x84, 0x95))
 
         hpai = HPAI()
         assert hpai.from_knx(raw) == 8
@@ -19,12 +19,12 @@ class TestKNXIPHPAI:
         assert not hpai.route_back
 
         hpai2 = HPAI(ip_addr="192.168.42.1", port=33941)
-        assert hpai2.to_knx() == list(raw)
+        assert hpai2.to_knx() == raw
         assert not hpai2.route_back
 
     def test_hpai_tcp(self):
         """Test parsing and streaming HPAI KNX/IP fragment."""
-        raw = (0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
+        raw = bytes((0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00))
 
         hpai = HPAI()
         assert hpai.from_knx(raw) == 8
@@ -34,23 +34,23 @@ class TestKNXIPHPAI:
         assert hpai.route_back
 
         hpai2 = HPAI(ip_addr="0.0.0.0", port=0, protocol=HostProtocol.IPV4_TCP)
-        assert hpai2.to_knx() == list(raw)
+        assert hpai2.to_knx() == raw
 
     def test_from_knx_wrong_input1(self):
         """Test parsing of wrong HPAI KNX/IP packet (wrong length)."""
-        raw = (0x08, 0x01, 0xC0, 0xA8, 0x2A)
+        raw = bytes((0x08, 0x01, 0xC0, 0xA8, 0x2A))
         with pytest.raises(CouldNotParseKNXIP):
             HPAI().from_knx(raw)
 
     def test_from_knx_wrong_input2(self):
         """Test parsing of wrong HPAI KNX/IP packet (wrong length byte)."""
-        raw = (0x09, 0x01, 0xC0, 0xA8, 0x2A, 0x01, 0x84, 0x95)
+        raw = bytes((0x09, 0x01, 0xC0, 0xA8, 0x2A, 0x01, 0x84, 0x95))
         with pytest.raises(CouldNotParseKNXIP):
             HPAI().from_knx(raw)
 
     def test_from_knx_wrong_input3(self):
         """Test parsing of wrong HPAI KNX/IP packet (wrong HPAI type)."""
-        raw = (0x08, 0x03, 0xC0, 0xA8, 0x2A, 0x01, 0x84, 0x95)
+        raw = bytes((0x08, 0x03, 0xC0, 0xA8, 0x2A, 0x01, 0x84, 0x95))
         with pytest.raises(CouldNotParseKNXIP):
             HPAI().from_knx(raw)
 
diff --git a/test/knxip_tests/knxip_test.py b/test/knxip_tests/knxip_test.py
index f7c5c104..76ede7fd 100644
--- a/test/knxip_tests/knxip_test.py
+++ b/test/knxip_tests/knxip_test.py
@@ -25,30 +25,17 @@ class TestKNXIPFrame:
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         raw = bytes.fromhex(
-            "061004200015040251002900bce010fa092d010080061004200015040252002900bce0101f082d010080"
+            "06 10 04 20 00 15 04 02 51 00 29 00 bc e0 10 fa"
+            "09 2d 01 00 80"
+            "06 10 04 20 00 15 04 02 52 00 29 00 bc e0 10 1f"
+            "08 2d 01 00 80"
         )  # both frames have lenght 21
         assert knxipframe.from_knx(raw) == 21
         assert knxipframe.from_knx(raw[21:]) == 21
 
     def test_parsing_too_short_knxip(self):
         """Test parsing and streaming connection state request KNX/IP packet."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x07,
-            0x00,
-            0x10,
-            0x15,
-            0x00,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0xC8,
-            0x0C,
-            0xC3,
-        )
+        raw = bytes.fromhex("06 10 02 07 00 10 15 00 08 01 C0 A8 C8 0C C3")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         with pytest.raises(IncompleteKNXIPFrame):
diff --git a/test/knxip_tests/routing_indication_test.py b/test/knxip_tests/routing_indication_test.py
index b5b46fcf..faa4ef6f 100644
--- a/test/knxip_tests/routing_indication_test.py
+++ b/test/knxip_tests/routing_indication_test.py
@@ -13,7 +13,7 @@ class TestKNXIPRountingIndication:
 
     def test_from_knx(self):
         """Test parsing and streaming CEMIFrame KNX/IP packet (payload=0xf0)."""
-        raw = bytes.fromhex("0610053000122900BCD012020151020040F0")
+        raw = bytes.fromhex("06 10 05 30 00 12 29 00 bc d0 12 02 01 51 02 00 40 f0")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -29,15 +29,15 @@ class TestKNXIPRountingIndication:
 
     def test_from_knx_to_knx(self):
         """Test parsing and streaming CEMIFrame KNX/IP."""
-        raw = bytes.fromhex("0610053000122900BCD012020151020040F0")
+        raw = bytes.fromhex("06 10 05 30 00 12 29 00 bc d0 12 02 01 51 02 00 40 f0")
         xknx = XKNX()
 
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
 
-        assert knxipframe.header.to_knx() == list(raw[0:6])
-        assert knxipframe.body.to_knx() == list(raw[6:])
-        assert knxipframe.to_knx() == list(raw)
+        assert knxipframe.header.to_knx() == raw[0:6]
+        assert knxipframe.body.to_knx() == raw[6:]
+        assert knxipframe.to_knx() == raw
 
     def test_telegram_set(self):
         """Test parsing and streaming CEMIFrame KNX/IP packet with DPTArray/DPTTime as payload."""
@@ -55,15 +55,17 @@ class TestKNXIPRountingIndication:
         routing_indication = RoutingIndication(xknx, cemi=cemi)
         knxipframe = KNXIPFrame.init_from_body(routing_indication)
 
-        raw = bytes.fromhex("0610053000142900BCD012020151040080 0d 17 2a")
+        raw = bytes.fromhex(
+            "06 10 05 30 00 14 29 00 bc d0 12 02 01 51 04 00 80 0d 17 2a"
+        )
 
-        assert knxipframe.header.to_knx() == list(raw[0:6])
-        assert knxipframe.body.to_knx() == list(raw[6:])
-        assert knxipframe.to_knx() == list(raw)
+        assert knxipframe.header.to_knx() == raw[0:6]
+        assert knxipframe.body.to_knx() == raw[6:]
+        assert knxipframe.to_knx() == raw
 
     def test_telegram_get(self):
         """Test parsing and streaming CEMIFrame KNX/IP packet, group read."""
-        raw = bytes.fromhex("0610053000122900BCD012020151020040F0")
+        raw = bytes.fromhex("06 10 05 30 00 12 29 00 bc d0 12 02 01 51 02 00 40 f0")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -86,7 +88,7 @@ class TestKNXIPRountingIndication:
     def test_end_to_end_group_write_binary_on(self):
         """Test parsing and streaming CEMIFrame KNX/IP packet, switch on light in my kitchen."""
         # Switch on Kitchen-L1
-        raw = bytes.fromhex("0610053000112900BCD0FFF90149010081")
+        raw = bytes.fromhex("06 10 05 30 00 11 29 00 bc d0 ff f9 01 49 01 00 81")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -103,14 +105,14 @@ class TestKNXIPRountingIndication:
         routing_indication = RoutingIndication(xknx, cemi=cemi)
         knxipframe2 = KNXIPFrame.init_from_body(routing_indication)
 
-        assert knxipframe2.header.to_knx() == list(raw[0:6])
-        assert knxipframe2.body.to_knx() == list(raw[6:])
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.header.to_knx() == raw[0:6]
+        assert knxipframe2.body.to_knx() == raw[6:]
+        assert knxipframe2.to_knx() == raw
 
     def test_end_to_end_group_write_binary_off(self):
         """Test parsing and streaming CEMIFrame KNX/IP packet, switch off light in my kitchen."""
         # Switch off Kitchen-L1
-        raw = bytes.fromhex("0610053000112900BCD0FFF90149010080")
+        raw = bytes.fromhex("06 10 05 30 00 11 29 00 bc d0 ff f9 01 49 01 00 80")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -127,14 +129,14 @@ class TestKNXIPRountingIndication:
         routing_indication = RoutingIndication(xknx, cemi=cemi)
         knxipframe2 = KNXIPFrame.init_from_body(routing_indication)
 
-        assert knxipframe2.header.to_knx() == list(raw[0:6])
-        assert knxipframe2.body.to_knx() == list(raw[6:])
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.header.to_knx() == raw[0:6]
+        assert knxipframe2.body.to_knx() == raw[6:]
+        assert knxipframe2.to_knx() == raw
 
     def test_end_to_end_group_write_1byte(self):
         """Test parsing and streaming CEMIFrame KNX/IP packet, dimm light in my kitchen."""
         # Dimm Kitchen L1 to 0x65
-        raw = bytes.fromhex("0610053000122900BCD0FFF9014B02008065")
+        raw = bytes.fromhex("06 10 05 30 00 12 29 00 bc d0 ff f9 01 4b 02 00 80 65")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -151,14 +153,14 @@ class TestKNXIPRountingIndication:
         routing_indication = RoutingIndication(xknx, cemi=cemi)
         knxipframe2 = KNXIPFrame.init_from_body(routing_indication)
 
-        assert knxipframe2.header.to_knx() == list(raw[0:6])
-        assert knxipframe2.body.to_knx() == list(raw[6:])
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.header.to_knx() == raw[0:6]
+        assert knxipframe2.body.to_knx() == raw[6:]
+        assert knxipframe2.to_knx() == raw
 
     def test_end_to_end_group_write_2bytes(self):
         """Test parsing and streaming CEMIFrame KNX/IP packet, setting value of thermostat."""
         # Incoming Temperature from thermostat
-        raw = bytes.fromhex("0610053000132900BCD01402080103008007C1")
+        raw = bytes.fromhex("06 10 05 30 00 13 29 00 bc d0 14 02 08 01 03 00 80 07 c1")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -175,14 +177,14 @@ class TestKNXIPRountingIndication:
         routing_indication = RoutingIndication(xknx, cemi=cemi)
         knxipframe2 = KNXIPFrame.init_from_body(routing_indication)
 
-        assert knxipframe2.header.to_knx() == list(raw[0:6])
-        assert knxipframe2.body.to_knx() == list(raw[6:])
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.header.to_knx() == raw[0:6]
+        assert knxipframe2.body.to_knx() == raw[6:]
+        assert knxipframe2.to_knx() == raw
 
     def test_end_to_end_group_read(self):
         """Test parsing and streaming CEMIFrame KNX/IP packet, group read."""
         # State request
-        raw = bytes.fromhex("0610053000112900BCD0FFF901B8010000")
+        raw = bytes.fromhex("06 10 05 30 00 11 29 00 bc d0 ff f9 01 b8 01 00 00")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -199,14 +201,14 @@ class TestKNXIPRountingIndication:
         routing_indication = RoutingIndication(xknx, cemi=cemi)
         knxipframe2 = KNXIPFrame.init_from_body(routing_indication)
 
-        assert knxipframe2.header.to_knx() == list(raw[0:6])
-        assert knxipframe2.body.to_knx() == list(raw[6:])
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.header.to_knx() == raw[0:6]
+        assert knxipframe2.body.to_knx() == raw[6:]
+        assert knxipframe2.to_knx() == raw
 
     def test_end_to_end_group_response(self):
         """Test parsing and streaming CEMIFrame KNX/IP packet, group response."""
         # Incoming state
-        raw = bytes.fromhex("0610053000112900BCD013010188010041")
+        raw = bytes.fromhex("06 10 05 30 00 11 29 00 bc d0 13 01 01 88 01 00 41")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -223,9 +225,9 @@ class TestKNXIPRountingIndication:
         routing_indication = RoutingIndication(xknx, cemi=cemi)
         knxipframe2 = KNXIPFrame.init_from_body(routing_indication)
 
-        assert knxipframe2.header.to_knx() == list(raw[0:6])
-        assert knxipframe2.body.to_knx() == list(raw[6:])
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.header.to_knx() == raw[0:6]
+        assert knxipframe2.body.to_knx() == raw[6:]
+        assert knxipframe2.to_knx() == raw
 
     def test_maximum_apci(self):
         """Test parsing and streaming CEMIFrame KNX/IP packet, testing maximum APCI."""
@@ -242,9 +244,9 @@ class TestKNXIPRountingIndication:
         routing_indication = RoutingIndication(xknx, cemi=cemi)
         knxipframe = KNXIPFrame.init_from_body(routing_indication)
 
-        raw = bytes.fromhex("0610053000112900BCD0130101510100BF")
+        raw = bytes.fromhex("06 10 05 30 00 11 29 00 bc d0 13 01 01 51 01 00 bf")
 
-        assert knxipframe.to_knx() == list(raw)
+        assert knxipframe.to_knx() == raw
 
         knxipframe2 = KNXIPFrame(xknx)
         knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION)
diff --git a/test/knxip_tests/search_request_test.py b/test/knxip_tests/search_request_test.py
index f87c3e7c..169dd5ac 100644
--- a/test/knxip_tests/search_request_test.py
+++ b/test/knxip_tests/search_request_test.py
@@ -8,22 +8,7 @@ class TestKNXIPSearchRequest:
 
     def test_search_request(self):
         """Test parsing and streaming SearchRequest KNX/IP packet."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x01,
-            0x00,
-            0x0E,
-            0x08,
-            0x01,
-            0xE0,
-            0x00,
-            0x17,
-            0x0C,
-            0x0E,
-            0x57,
-        )
+        raw = bytes.fromhex("06 10 02 01 00 0E 08 01 E0 00 17 0C 0E 57")
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -39,4 +24,4 @@ class TestKNXIPSearchRequest:
             )
         )
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
diff --git a/test/knxip_tests/search_response_test.py b/test/knxip_tests/search_response_test.py
index a23a288a..06f65846 100644
--- a/test/knxip_tests/search_response_test.py
+++ b/test/knxip_tests/search_response_test.py
@@ -15,92 +15,17 @@ class TestKNXIPSearchResponse:
 
     def test_search_response(self):
         """Test parsing and streaming SearchResponse KNX/IP packet."""
-        raw = (
-            0x06,
-            0x10,
-            0x02,
-            0x02,
-            0x00,
-            0x50,
-            0x08,
-            0x01,
-            0xC0,
-            0xA8,
-            0x2A,
-            0x0A,
-            0x0E,
-            0x57,
-            0x36,
-            0x01,
-            0x02,
-            0x00,
-            0x11,
-            0x00,
-            0x00,
-            0x00,
-            0x11,
-            0x22,
-            0x33,
-            0x44,
-            0x55,
-            0x66,
-            0xE0,
-            0x00,
-            0x17,
-            0x0C,
-            0x01,
-            0x02,
-            0x03,
-            0x04,
-            0x05,
-            0x06,
-            0x47,
-            0x69,
-            0x72,
-            0x61,
-            0x20,
-            0x4B,
-            0x4E,
-            0x58,
-            0x2F,
-            0x49,
-            0x50,
-            0x2D,
-            0x52,
-            0x6F,
-            0x75,
-            0x74,
-            0x65,
-            0x72,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x00,
-            0x0C,
-            0x02,
-            0x02,
-            0x01,
-            0x03,
-            0x02,
-            0x04,
-            0x01,
-            0x05,
-            0x01,
-            0x07,
-            0x01,
+        raw = bytes.fromhex(
+            "06 10 02 02 00 50 08 01 C0 A8 2A 0A 0E 57 36 01"
+            "02 00 11 00 00 00 11 22 33 44 55 66 E0 00 17 0C"
+            "01 02 03 04 05 06 47 69 72 61 20 4B 4E 58 2F 49"
+            "50 2D 52 6F 75 74 65 72 00 00 00 00 00 00 00 00"
+            "00 00 00 00 0C 02 02 01 03 02 04 01 05 01 07 01"
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         assert knxipframe.from_knx(raw) == 80
-        assert knxipframe.to_knx() == list(raw)
+        assert knxipframe.to_knx() == raw
 
         assert isinstance(knxipframe.body, SearchResponse)
         assert knxipframe.body.control_endpoint == HPAI("192.168.42.10", 3671)
@@ -122,7 +47,7 @@ class TestKNXIPSearchResponse:
         search_response.dibs.append(knxipframe.body.dibs[1])
         knxipframe2 = KNXIPFrame.init_from_body(search_response)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_unknown_device_name(self):
         """Test device_name if no DIBDeviceInformation is present."""
diff --git a/test/knxip_tests/tunnelling_ack_test.py b/test/knxip_tests/tunnelling_ack_test.py
index 40507415..4ca40318 100644
--- a/test/knxip_tests/tunnelling_ack_test.py
+++ b/test/knxip_tests/tunnelling_ack_test.py
@@ -10,7 +10,7 @@ class TestKNXIPTunnelingAck:
 
     def test_connect_request(self):
         """Test parsing and streaming tunneling ACK KNX/IP packet."""
-        raw = (0x06, 0x10, 0x04, 0x21, 0x00, 0x0A, 0x04, 0x2A, 0x17, 0x00)
+        raw = bytes((0x06, 0x10, 0x04, 0x21, 0x00, 0x0A, 0x04, 0x2A, 0x17, 0x00))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.from_knx(raw)
@@ -25,11 +25,11 @@ class TestKNXIPTunnelingAck:
         )
         knxipframe2 = KNXIPFrame.init_from_body(tunnelling_ack)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_from_knx_wrong_ack_information(self):
         """Test parsing and streaming wrong TunnellingAck (wrong length byte)."""
-        raw = (0x06, 0x10, 0x04, 0x21, 0x00, 0x0A, 0x03, 0x2A, 0x17, 0x00)
+        raw = bytes((0x06, 0x10, 0x04, 0x21, 0x00, 0x0A, 0x03, 0x2A, 0x17, 0x00))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         with pytest.raises(CouldNotParseKNXIP):
@@ -37,7 +37,7 @@ class TestKNXIPTunnelingAck:
 
     def test_from_knx_wrong_ack_information2(self):
         """Test parsing and streaming wrong TunnellingAck (wrong length)."""
-        raw = (0x06, 0x10, 0x04, 0x21, 0x00, 0x0A, 0x04, 0x2A, 0x17)
+        raw = bytes((0x06, 0x10, 0x04, 0x21, 0x00, 0x0A, 0x04, 0x2A, 0x17))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         with pytest.raises(CouldNotParseKNXIP):
diff --git a/test/knxip_tests/tunnelling_request_test.py b/test/knxip_tests/tunnelling_request_test.py
index 5a75f23c..8eb3f93f 100644
--- a/test/knxip_tests/tunnelling_request_test.py
+++ b/test/knxip_tests/tunnelling_request_test.py
@@ -13,28 +13,8 @@ class TestKNXIPTunnelingRequest:
 
     def test_connect_request(self):
         """Test parsing and streaming connection tunneling request KNX/IP packet."""
-        raw = (
-            0x06,
-            0x10,
-            0x04,
-            0x20,
-            0x00,
-            0x15,
-            0x04,
-            0x01,
-            0x17,
-            0x00,
-            0x11,
-            0x00,
-            0xBC,
-            0xE0,
-            0x00,
-            0x00,
-            0x48,
-            0x08,
-            0x01,
-            0x00,
-            0x81,
+        raw = bytes.fromhex(
+            "06 10 04 20 00 15 04 01 17 00 11 00 BC E0 00 00 48 08 01 00 81"
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
@@ -60,11 +40,11 @@ class TestKNXIPTunnelingRequest:
         )
         knxipframe2 = KNXIPFrame.init_from_body(tunnelling_request)
 
-        assert knxipframe2.to_knx() == list(raw)
+        assert knxipframe2.to_knx() == raw
 
     def test_from_knx_wrong_header(self):
         """Test parsing and streaming wrong TunnellingRequest (wrong header length byte)."""
-        raw = (0x06, 0x10, 0x04, 0x20, 0x00, 0x15, 0x03)
+        raw = bytes((0x06, 0x10, 0x04, 0x20, 0x00, 0x15, 0x03))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         with pytest.raises(CouldNotParseKNXIP):
@@ -72,7 +52,7 @@ class TestKNXIPTunnelingRequest:
 
     def test_from_knx_wrong_header2(self):
         """Test parsing and streaming wrong TunnellingRequest (wrong header length)."""
-        raw = (0x06, 0x10, 0x04, 0x20, 0x00, 0x15, 0x04)
+        raw = bytes((0x06, 0x10, 0x04, 0x20, 0x00, 0x15, 0x04))
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         with pytest.raises(CouldNotParseKNXIP):
diff --git a/test/xknx_test.py b/test/xknx_test.py
index 55fe8435..676b5feb 100644
--- a/test/xknx_test.py
+++ b/test/xknx_test.py
@@ -15,45 +15,27 @@ class TestXknxModule:
     def test_log_to_file(self):
         """Test logging enable."""
         XKNX(log_directory="/tmp/")
-
         assert os.path.isfile("/tmp/xknx.log")
-
         os.remove("/tmp/xknx.log")
 
     def test_log_to_file_when_dir_does_not_exist(self):
         """Test logging enable with non existent directory."""
         XKNX(log_directory="/xknx/is/fun")
-
         assert not os.path.isfile("/xknx/is/fun/xknx.log")
 
     def test_register_telegram_cb(self):
         """Test register telegram callback."""
-
-        async def telegram_received(telegram):
-            """Telegram received."""
-
-        xknx = XKNX(telegram_received_cb=telegram_received)
-
+        xknx = XKNX(telegram_received_cb=AsyncMock())
         assert len(xknx.telegram_queue.telegram_received_cbs) == 1
 
     def test_register_device_cb(self):
         """Test register telegram callback."""
-
-        async def device_update(device):
-            """Device updated."""
-
-        xknx = XKNX(device_updated_cb=device_update)
-
+        xknx = XKNX(device_updated_cb=AsyncMock())
         assert len(xknx.devices.device_updated_cbs) == 1
 
     def test_register_connection_state_change_cb(self):
         """Test register con state callback."""
-
-        async def con_state_change(connection_state):
-            """Connection State Changed."""
-
-        xknx = XKNX(connection_state_changed_cb=con_state_change)
-
+        xknx = XKNX(connection_state_changed_cb=AsyncMock())
         assert len(xknx.connection_manager._connection_state_changed_cbs) == 1
 
     @patch("xknx.io.KNXIPInterface._start", new_callable=AsyncMock)
@@ -68,13 +50,9 @@ class TestXknxModule:
     @patch("xknx.io.KNXIPInterface._start", new_callable=AsyncMock)
     async def test_xknx_start_as_context_manager(self, ipinterface_mock):
         """Test xknx start."""
-
-        async def run_in_contextmanager():
-            async with XKNX(state_updater=True) as xknx:
-                assert xknx.started.is_set()
-                ipinterface_mock.assert_called_once()
-
-        await run_in_contextmanager()
+        async with XKNX(state_updater=True) as xknx:
+            assert xknx.started.is_set()
+            ipinterface_mock.assert_called_once()
 
     @patch("xknx.io.KNXIPInterface._start", new_callable=AsyncMock)
     async def test_xknx_start_and_stop_with_dedicated_connection_config(