diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py
index 60104545dea..91d1d9fa1c5 100644
--- a/homeassistant/components/knx/__init__.py
+++ b/homeassistant/components/knx/__init__.py
@@ -12,7 +12,7 @@ from xknx import XKNX
 from xknx.core import XknxConnectionState
 from xknx.core.telegram_queue import TelegramQueue
 from xknx.dpt import DPTArray, DPTBase, DPTBinary
-from xknx.exceptions import ConversionError, XKNXException
+from xknx.exceptions import ConversionError, CouldNotParseTelegram, XKNXException
 from xknx.io import ConnectionConfig, ConnectionType, SecureConfig
 from xknx.telegram import AddressFilter, Telegram
 from xknx.telegram.address import (
@@ -513,31 +513,29 @@ class KNXModule:
             )
         ):
             data = telegram.payload.value.value
-
-            if isinstance(data, tuple):
-                if transcoder := (
-                    self._group_address_transcoder.get(telegram.destination_address)
-                    or next(
+            if transcoder := (
+                self._group_address_transcoder.get(telegram.destination_address)
+                or next(
+                    (
+                        _transcoder
+                        for _filter, _transcoder in self._address_filter_transcoder.items()
+                        if _filter.match(telegram.destination_address)
+                    ),
+                    None,
+                )
+            ):
+                try:
+                    value = transcoder.from_knx(telegram.payload.value)
+                except (ConversionError, CouldNotParseTelegram) as err:
+                    _LOGGER.warning(
                         (
-                            _transcoder
-                            for _filter, _transcoder in self._address_filter_transcoder.items()
-                            if _filter.match(telegram.destination_address)
+                            "Error in `knx_event` at decoding type '%s' from"
+                            " telegram %s\n%s"
                         ),
-                        None,
+                        transcoder.__name__,
+                        telegram,
+                        err,
                     )
-                ):
-                    try:
-                        value = transcoder.from_knx(data)
-                    except ConversionError as err:
-                        _LOGGER.warning(
-                            (
-                                "Error in `knx_event` at decoding type '%s' from"
-                                " telegram %s\n%s"
-                            ),
-                            transcoder.__name__,
-                            telegram,
-                            err,
-                        )
 
         self.hass.bus.async_fire(
             "knx_event",
@@ -656,7 +654,7 @@ class KNXModule:
             transcoder = DPTBase.parse_transcoder(attr_type)
             if transcoder is None:
                 raise ValueError(f"Invalid type for knx.send service: {attr_type}")
-            payload = DPTArray(transcoder.to_knx(attr_payload))
+            payload = transcoder.to_knx(attr_payload)
         elif isinstance(attr_payload, int):
             payload = DPTBinary(attr_payload)
         else:
diff --git a/homeassistant/components/knx/config_flow.py b/homeassistant/components/knx/config_flow.py
index 85e23cbe547..81610d62dcf 100644
--- a/homeassistant/components/knx/config_flow.py
+++ b/homeassistant/components/knx/config_flow.py
@@ -9,10 +9,15 @@ from typing import Any, Final
 
 import voluptuous as vol
 from xknx import XKNX
-from xknx.exceptions.exception import CommunicationError, InvalidSecureConfiguration
+from xknx.exceptions.exception import (
+    CommunicationError,
+    InvalidSecureConfiguration,
+    XKNXException,
+)
 from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT
 from xknx.io.gateway_scanner import GatewayDescriptor, GatewayScanner
 from xknx.io.self_description import request_description
+from xknx.io.util import validate_ip as xknx_validate_ip
 from xknx.secure.keyring import Keyring, XMLInterface, sync_load_keyring
 
 from homeassistant.components.file_upload import process_uploaded_file
@@ -258,21 +263,25 @@ class KNXCommonFlow(ABC, FlowHandler):
 
         if user_input is not None:
             try:
-                _host = ip_v4_validator(user_input[CONF_HOST], multicast=False)
-            except vol.Invalid:
+                _host = user_input[CONF_HOST]
+                _host_ip = await xknx_validate_ip(_host)
+                ip_v4_validator(_host_ip, multicast=False)
+            except (vol.Invalid, XKNXException):
                 errors[CONF_HOST] = "invalid_ip_address"
 
-            if _local_ip := user_input.get(CONF_KNX_LOCAL_IP):
+            _local_ip = None
+            if _local := user_input.get(CONF_KNX_LOCAL_IP):
                 try:
-                    _local_ip = ip_v4_validator(_local_ip, multicast=False)
-                except vol.Invalid:
+                    _local_ip = await xknx_validate_ip(_local)
+                    ip_v4_validator(_local_ip, multicast=False)
+                except (vol.Invalid, XKNXException):
                     errors[CONF_KNX_LOCAL_IP] = "invalid_ip_address"
 
             selected_tunnelling_type = user_input[CONF_KNX_TUNNELING_TYPE]
             if not errors:
                 try:
                     self._selected_tunnel = await request_description(
-                        gateway_ip=_host,
+                        gateway_ip=_host_ip,
                         gateway_port=user_input[CONF_PORT],
                         local_ip=_local_ip,
                         route_back=user_input[CONF_KNX_ROUTE_BACK],
@@ -296,7 +305,7 @@ class KNXCommonFlow(ABC, FlowHandler):
                     host=_host,
                     port=user_input[CONF_PORT],
                     route_back=user_input[CONF_KNX_ROUTE_BACK],
-                    local_ip=_local_ip,
+                    local_ip=_local,
                     device_authentication=None,
                     user_id=None,
                     user_password=None,
@@ -636,10 +645,11 @@ class KNXCommonFlow(ABC, FlowHandler):
                 ip_v4_validator(_multicast_group, multicast=True)
             except vol.Invalid:
                 errors[CONF_KNX_MCAST_GRP] = "invalid_ip_address"
-            if _local_ip := user_input.get(CONF_KNX_LOCAL_IP):
+            if _local := user_input.get(CONF_KNX_LOCAL_IP):
                 try:
+                    _local_ip = await xknx_validate_ip(_local)
                     ip_v4_validator(_local_ip, multicast=False)
-                except vol.Invalid:
+                except (vol.Invalid, XKNXException):
                     errors[CONF_KNX_LOCAL_IP] = "invalid_ip_address"
 
             if not errors:
@@ -653,7 +663,7 @@ class KNXCommonFlow(ABC, FlowHandler):
                     individual_address=_individual_address,
                     multicast_group=_multicast_group,
                     multicast_port=_multicast_port,
-                    local_ip=_local_ip,
+                    local_ip=_local,
                     device_authentication=None,
                     user_id=None,
                     user_password=None,
diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json
index 0ad4404290a..d3aeced46c9 100644
--- a/homeassistant/components/knx/manifest.json
+++ b/homeassistant/components/knx/manifest.json
@@ -9,5 +9,5 @@
   "iot_class": "local_push",
   "loggers": ["xknx"],
   "quality_scale": "platinum",
-  "requirements": ["xknx==2.7.0"]
+  "requirements": ["xknx==2.9.0"]
 }
diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py
index a505714c0d0..0f627b724cb 100644
--- a/homeassistant/components/knx/schema.py
+++ b/homeassistant/components/knx/schema.py
@@ -10,7 +10,7 @@ from typing import Any, ClassVar, Final
 import voluptuous as vol
 from xknx.devices.climate import SetpointShiftMode
 from xknx.dpt import DPTBase, DPTNumeric, DPTString
-from xknx.exceptions import ConversionError, CouldNotParseAddress
+from xknx.exceptions import ConversionError, CouldNotParseAddress, CouldNotParseTelegram
 from xknx.telegram.address import IndividualAddress, parse_device_group_address
 
 from homeassistant.components.binary_sensor import (
@@ -185,13 +185,13 @@ def button_payload_sub_validator(entity_config: OrderedDict) -> OrderedDict:
             raise vol.Invalid(f"'type: {_type}' is not a valid sensor type.")
         entity_config[CONF_PAYLOAD_LENGTH] = transcoder.payload_length
         try:
-            entity_config[CONF_PAYLOAD] = int.from_bytes(
-                transcoder.to_knx(_payload), byteorder="big"
-            )
-        except ConversionError as ex:
+            _dpt_payload = transcoder.to_knx(_payload)
+            _raw_payload = transcoder.validate_payload(_dpt_payload)
+        except (ConversionError, CouldNotParseTelegram) as ex:
             raise vol.Invalid(
                 f"'payload: {_payload}' not valid for 'type: {_type}'"
             ) from ex
+        entity_config[CONF_PAYLOAD] = int.from_bytes(_raw_payload, byteorder="big")
         return entity_config
 
     _payload = entity_config[CONF_PAYLOAD]
diff --git a/homeassistant/components/knx/strings.json b/homeassistant/components/knx/strings.json
index a781f9d73cc..800945ab6bd 100644
--- a/homeassistant/components/knx/strings.json
+++ b/homeassistant/components/knx/strings.json
@@ -23,13 +23,13 @@
           "port": "[%key:common::config_flow::data::port%]",
           "host": "[%key:common::config_flow::data::host%]",
           "route_back": "Route back / NAT mode",
-          "local_ip": "Local IP of Home Assistant"
+          "local_ip": "Local IP interface"
         },
         "data_description": {
           "port": "Port of the KNX/IP tunneling device.",
-          "host": "IP address of the KNX/IP tunneling device.",
+          "host": "IP address or hostname of the KNX/IP tunneling device.",
           "route_back": "Enable if your KNXnet/IP tunneling server is behind NAT. Only applies for UDP connections.",
-          "local_ip": "Leave blank to use auto-discovery."
+          "local_ip": "Local IP or interface name used for the connection from Home Assistant. Leave blank to use auto-discovery."
         }
       },
       "secure_key_source": {
@@ -93,11 +93,11 @@
           "routing_secure": "Use KNX IP Secure",
           "multicast_group": "Multicast group",
           "multicast_port": "Multicast port",
-          "local_ip": "Local IP of Home Assistant"
+          "local_ip": "[%key:component::knx::config::step::manual_tunnel::data::local_ip%]"
         },
         "data_description": {
           "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`",
-          "local_ip": "Leave blank to use auto-discovery."
+          "local_ip": "[%key:component::knx::config::step::manual_tunnel::data_description::local_ip%]"
         }
       }
     },
diff --git a/requirements_all.txt b/requirements_all.txt
index 529ebde6c3c..fe0cb3a76d4 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -2664,7 +2664,7 @@ xbox-webapi==2.0.11
 xiaomi-ble==0.17.0
 
 # homeassistant.components.knx
-xknx==2.7.0
+xknx==2.9.0
 
 # homeassistant.components.bluesound
 # homeassistant.components.fritz
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index dfb982cb30e..5300ddf973a 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -1922,7 +1922,7 @@ xbox-webapi==2.0.11
 xiaomi-ble==0.17.0
 
 # homeassistant.components.knx
-xknx==2.7.0
+xknx==2.9.0
 
 # homeassistant.components.bluesound
 # homeassistant.components.fritz
diff --git a/tests/components/knx/test_button.py b/tests/components/knx/test_button.py
index 4fa8d02716f..eb3fee7eaf5 100644
--- a/tests/components/knx/test_button.py
+++ b/tests/components/knx/test_button.py
@@ -1,9 +1,13 @@
 """Test KNX button."""
 from datetime import timedelta
+import logging
+
+import pytest
 
 from homeassistant.components.knx.const import (
     CONF_PAYLOAD,
     CONF_PAYLOAD_LENGTH,
+    DOMAIN,
     KNX_ADDRESS,
 )
 from homeassistant.components.knx.schema import ButtonSchema
@@ -86,3 +90,49 @@ async def test_button_type(hass: HomeAssistant, knx: KNXTestKit) -> None:
         "button", "press", {"entity_id": "button.test"}, blocking=True
     )
     await knx.assert_write("1/2/3", (0x0C, 0x33))
+
+
+@pytest.mark.parametrize(
+    ("conf_type", "conf_value", "error_msg"),
+    [
+        (
+            "2byte_float",
+            "not_valid",
+            "'payload: not_valid' not valid for 'type: 2byte_float'",
+        ),
+        (
+            "not_valid",
+            3,
+            "type 'not_valid' is not a valid DPT identifier",
+        ),
+    ],
+)
+async def test_button_invalid(
+    hass: HomeAssistant,
+    caplog: pytest.LogCaptureFixture,
+    knx: KNXTestKit,
+    conf_type: str,
+    conf_value: str,
+    error_msg: str,
+) -> None:
+    """Test KNX button with configured payload that can't be encoded."""
+    with caplog.at_level(logging.ERROR):
+        await knx.setup_integration(
+            {
+                ButtonSchema.PLATFORM: {
+                    CONF_NAME: "test",
+                    KNX_ADDRESS: "1/2/3",
+                    ButtonSchema.CONF_VALUE: conf_value,
+                    CONF_TYPE: conf_type,
+                }
+            }
+        )
+        assert len(caplog.messages) == 2
+        record = caplog.records[0]
+        assert record.levelname == "ERROR"
+        assert f"Invalid config for [knx]: {error_msg}" in record.message
+        record = caplog.records[1]
+        assert record.levelname == "ERROR"
+        assert "Setup failed for knx: Invalid config." in record.message
+    assert hass.states.get("button.test") is None
+    assert hass.data.get(DOMAIN) is None
diff --git a/tests/components/knx/test_events.py b/tests/components/knx/test_events.py
index a20c6663f08..f5c1aed7fde 100644
--- a/tests/components/knx/test_events.py
+++ b/tests/components/knx/test_events.py
@@ -1,4 +1,7 @@
 """Test KNX events."""
+import logging
+
+import pytest
 
 from homeassistant.components.knx import CONF_EVENT, CONF_TYPE, KNX_ADDRESS
 from homeassistant.core import HomeAssistant
@@ -8,7 +11,11 @@ from .conftest import KNXTestKit
 from tests.common import async_capture_events
 
 
-async def test_knx_event(hass: HomeAssistant, knx: KNXTestKit) -> None:
+async def test_knx_event(
+    hass: HomeAssistant,
+    caplog: pytest.LogCaptureFixture,
+    knx: KNXTestKit,
+) -> None:
     """Test the `knx_event` event."""
     test_group_a = "0/4/*"
     test_address_a_1 = "0/4/0"
@@ -95,3 +102,16 @@ async def test_knx_event(hass: HomeAssistant, knx: KNXTestKit) -> None:
     await knx.receive_write("2/6/6", True)
     await hass.async_block_till_done()
     assert len(events) == 0
+
+    # receive telegrams with wrong payload length
+    caplog.clear()
+    with caplog.at_level(logging.WARNING):
+        await knx.receive_write(test_address_a_1, (0x03, 0x2F, 0xFF))
+        assert len(caplog.messages) == 1
+        record = caplog.records[0]
+        assert record.levelname == "WARNING"
+        assert (
+            "Error in `knx_event` at decoding type "
+            "'DPT2ByteUnsigned' from telegram" in record.message
+        )
+    await test_event_data(test_address_a_1, (0x03, 0x2F, 0xFF), value=None)