You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2021/02/05 16:52:41 UTC

[plc4x] branch develop updated: - Refactoring of the KNX LData messages - Finished a first working KNX Group-Address & ComObject discovery

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/develop by this push:
     new db126a9  - Refactoring of the KNX LData messages - Finished a first working KNX Group-Address & ComObject discovery
db126a9 is described below

commit db126a9a665c8d4ed56cfce18f4a29117f7253c2
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Fri Feb 5 17:52:32 2021 +0100

    - Refactoring of the KNX LData messages
    - Finished a first working KNX Group-Address & ComObject discovery
---
 .../knxnetip/ParserSerializerTestsuite.xml         |   56 +-
 .../testing/protocols/modbus/DriverTestsuite.xml   |    4 +-
 .../protocols/modbus/ParserSerializerTestsuite.xml |   15 +-
 .../protocols/s7/ParserSerializerTestsuite.xml     |   22 +-
 plc4go/cmd/main/drivers/knxnetip_test.go           |   27 +-
 .../discovery/hello_world_plc4go_knx_discovery.go  |   99 +
 plc4go/examples/read/hello_world_plc4go_read.go    |   14 +-
 plc4go/examples/write/hello_world_plc4go_write.go  |   14 +-
 plc4go/internal/plc4go/knxnetip/KnxNetIpBrowser.go |  584 ++--
 .../internal/plc4go/knxnetip/KnxNetIpConnection.go | 2946 ++++++++++----------
 .../internal/plc4go/knxnetip/KnxNetIpDiscoverer.go |    2 +-
 plc4go/internal/plc4go/knxnetip/KnxNetIpField.go   |  196 +-
 .../plc4go/knxnetip/KnxNetIpFieldHandler.go        |  100 +-
 plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go  |   36 +-
 .../internal/plc4go/knxnetip/KnxNetIpSubscriber.go |   79 +-
 plc4go/internal/plc4go/knxnetip/Utils.go           |   30 -
 .../plc4go/knxnetip/readwrite/ParserHelper.go      |    6 +-
 .../plc4go/knxnetip/readwrite/model/Apdu.go        |   36 +-
 .../readwrite/model/ApduControlContainer.go        |    7 +-
 .../knxnetip/readwrite/model/ApduDataContainer.go  |    7 +-
 .../plc4go/knxnetip/readwrite/model/KnxProperty.go |   22 +-
 .../plc4go/knxnetip/readwrite/model/LBusmonInd.go  |    2 +-
 .../{LDataFrameDataExt.go => LDataExtended.go}     |   70 +-
 .../plc4go/knxnetip/readwrite/model/LDataFrame.go  |  106 +-
 .../knxnetip/readwrite/model/LDataFrameACK.go      |  144 +
 .../knxnetip/readwrite/model/LDataFrameData.go     |  301 --
 .../{LDataFramePollingData.go => LPollData.go}     |   54 +-
 .../knxnetip/protocol/KnxNetIpProtocolLogic.java   |   23 +-
 .../resources/protocols/knxnetip/knxnetip.mspec    |   61 +-
 .../knxnetip/ParserSerializerTestsuite.xml         |   14 +-
 30 files changed, 2566 insertions(+), 2511 deletions(-)

diff --git a/plc4go/assets/testing/protocols/knxnetip/ParserSerializerTestsuite.xml b/plc4go/assets/testing/protocols/knxnetip/ParserSerializerTestsuite.xml
index 6ad045f..9e954a3 100644
--- a/plc4go/assets/testing/protocols/knxnetip/ParserSerializerTestsuite.xml
+++ b/plc4go/assets/testing/protocols/knxnetip/ParserSerializerTestsuite.xml
@@ -123,7 +123,7 @@
         <hpaiIDiscoveryEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDiscoveryEndpoint">
           <hostProtocolCode>IPV4_UDP</hostProtocolCode>
           <ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>wKgqyA==</addr>
+            <addr>C0A82AC8</addr>
           </ipAddress>
           <ipPort>54964</ipPort>
         </hpaiIDiscoveryEndpoint>
@@ -140,7 +140,7 @@
         <hpaiControlEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIControlEndpoint">
           <hostProtocolCode>IPV4_UDP</hostProtocolCode>
           <ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>wKgqCw==</addr>
+            <addr>C0A82A0B</addr>
           </ipAddress>
           <ipPort>3671</ipPort>
         </hpaiControlEndpoint>
@@ -159,14 +159,14 @@
             <projectNumber>0</projectNumber>
             <installationNumber>0</installationNumber>
           </projectInstallationIdentifier>
-          <knxNetIpDeviceSerialNumber>AAgtQJhS</knxNetIpDeviceSerialNumber>
+          <knxNetIpDeviceSerialNumber>00082D409852</knxNetIpDeviceSerialNumber>
           <knxNetIpDeviceMulticastAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>4AAXDA==</addr>
+            <addr>E000170C</addr>
           </knxNetIpDeviceMulticastAddress>
           <knxNetIpDeviceMacAddress className="org.apache.plc4x.java.knxnetip.readwrite.MACAddress">
-            <addr>AAqzJ1U2</addr>
+            <addr>000AB3275536</addr>
           </knxNetIpDeviceMacAddress>
-          <deviceFriendlyName>R2lyYSBLTlgvSVAtU2Nobml0dHN0ZWxsZQAAAAAA</deviceFriendlyName>
+          <deviceFriendlyName>47697261204B4E582F49502D5363686E6974747374656C6C650000000000</deviceFriendlyName>
         </dibDeviceInfo>
         <dibSuppSvcFamilies className="org.apache.plc4x.java.knxnetip.readwrite.DIBSuppSvcFamilies">
           <descriptionType>2</descriptionType>
@@ -195,7 +195,7 @@
         <hpaiControlEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIControlEndpoint">
           <hostProtocolCode>IPV4_UDP</hostProtocolCode>
           <ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>AAAAAA==</addr>
+            <addr>00000000</addr>
           </ipAddress>
           <ipPort>0</ipPort>
         </hpaiControlEndpoint>
@@ -224,14 +224,14 @@
             <projectNumber>0</projectNumber>
             <installationNumber>0</installationNumber>
           </projectInstallationIdentifier>
-          <knxNetIpDeviceSerialNumber>AAgtQJhS</knxNetIpDeviceSerialNumber>
+          <knxNetIpDeviceSerialNumber>00082D409852</knxNetIpDeviceSerialNumber>
           <knxNetIpDeviceMulticastAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>4AAXDA==</addr>
+            <addr>E000170C</addr>
           </knxNetIpDeviceMulticastAddress>
           <knxNetIpDeviceMacAddress className="org.apache.plc4x.java.knxnetip.readwrite.MACAddress">
-            <addr>AAqzJ1U2</addr>
+            <addr>000AB3275536</addr>
           </knxNetIpDeviceMacAddress>
-          <deviceFriendlyName>R2lyYSBLTlgvSVAtU2Nobml0dHN0ZWxsZQAAAAAA</deviceFriendlyName>
+          <deviceFriendlyName>47697261204B4E582F49502D5363686E6974747374656C6C650000000000</deviceFriendlyName>
         </dibDeviceInfo>
         <dibSuppSvcFamilies className="org.apache.plc4x.java.knxnetip.readwrite.DIBSuppSvcFamilies">
           <descriptionType>2</descriptionType>
@@ -261,14 +261,14 @@
         <hpaiDiscoveryEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDiscoveryEndpoint">
           <hostProtocolCode>IPV4_UDP</hostProtocolCode>
           <ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>wKgqyA==</addr>
+            <addr>C0A82AC8</addr>
           </ipAddress>
           <ipPort>54964</ipPort>
         </hpaiDiscoveryEndpoint>
         <hpaiDataEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDataEndpoint">
           <hostProtocolCode>IPV4_UDP</hostProtocolCode>
           <ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>wKgqyA==</addr>
+            <addr>C0A82AC8</addr>
           </ipAddress>
           <ipPort>54964</ipPort>
         </hpaiDataEndpoint>
@@ -290,7 +290,7 @@
         <hpaiDataEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDataEndpoint">
           <hostProtocolCode>IPV4_UDP</hostProtocolCode>
           <ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>wKgqCw==</addr>
+            <addr>C0A82A0B</addr>
           </ipAddress>
           <ipPort>3671</ipPort>
         </hpaiDataEndpoint>
@@ -315,7 +315,7 @@
         <hpaiControlEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIControlEndpoint">
           <hostProtocolCode>IPV4_UDP</hostProtocolCode>
           <ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>wKgqyA==</addr>
+            <addr>C0A82AC8</addr>
           </ipAddress>
           <ipPort>54964</ipPort>
         </hpaiControlEndpoint>
@@ -381,7 +381,7 @@
         <hpaiControlEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIControlEndpoint">
           <hostProtocolCode>IPV4_UDP</hostProtocolCode>
           <ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>wKgqCw==</addr>
+            <addr>C0A82A0B</addr>
           </ipAddress>
           <ipPort>3671</ipPort>
         </hpaiControlEndpoint>
@@ -414,9 +414,9 @@
         <cemi className="org.apache.plc4x.java.knxnetip.readwrite.LDataInd">
           <additionalInformationLength>0</additionalInformationLength>
           <additionalInformation/>
-          <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataFrameDataExt">
-            <repeated>true</repeated>
-            <notAckFrame>true</notAckFrame>
+          <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataExtended">
+            <frameType>true</frameType>
+            <notRepeated>true</notRepeated>
             <priority>LOW</priority>
             <acknowledgeRequested>false</acknowledgeRequested>
             <errorFlag>false</errorFlag>
@@ -428,9 +428,8 @@
               <middleGroup>2</middleGroup>
               <subGroup>10</subGroup>
             </sourceAddress>
-            <destinationAddress>Egw=</destinationAddress>
+            <destinationAddress>120C</destinationAddress>
             <apdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduDataContainer">
-              <dataLength>1</dataLength>
               <numbered>false</numbered>
               <counter>0</counter>
               <dataApdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduDataGroupValueWrite">
@@ -475,9 +474,9 @@
               </relativeTimestamp>
             </additionalInformation>
           </additionalInformation>
-          <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataFrameDataExt">
-            <repeated>true</repeated>
-            <notAckFrame>true</notAckFrame>
+          <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataExtended">
+            <frameType>true</frameType>
+            <notRepeated>true</notRepeated>
             <priority>LOW</priority>
             <acknowledgeRequested>false</acknowledgeRequested>
             <errorFlag>false</errorFlag>
@@ -489,9 +488,8 @@
               <middleGroup>10</middleGroup>
               <subGroup>30</subGroup>
             </sourceAddress>
-            <destinationAddress>DOE=</destinationAddress>
+            <destinationAddress>0CE1</destinationAddress>
             <apdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduControlContainer">
-              <dataLength>0</dataLength>
               <numbered>false</numbered>
               <counter>0</counter>
               <controlApdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduControlDisconnect"/>
@@ -527,14 +525,14 @@
         <hpaiDiscoveryEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDiscoveryEndpoint">
           <hostProtocolCode>IPV4_UDP</hostProtocolCode>
           <ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>wKgqRg==</addr>
+            <addr>C0A82A46</addr>
           </ipAddress>
           <ipPort>50185</ipPort>
         </hpaiDiscoveryEndpoint>
         <hpaiDataEndpoint className="org.apache.plc4x.java.knxnetip.readwrite.HPAIDataEndpoint">
           <hostProtocolCode>IPV4_UDP</hostProtocolCode>
           <ipAddress className="org.apache.plc4x.java.knxnetip.readwrite.IPAddress">
-            <addr>wKgqRg==</addr>
+            <addr>C0A82A46</addr>
           </ipAddress>
           <ipPort>50186</ipPort>
         </hpaiDataEndpoint>
@@ -543,4 +541,4 @@
     </xml>
   </testcase>
 
-</test:testsuite>
\ No newline at end of file
+</test:testsuite>
diff --git a/plc4go/assets/testing/protocols/modbus/DriverTestsuite.xml b/plc4go/assets/testing/protocols/modbus/DriverTestsuite.xml
index ed35a3f..bfe6617 100644
--- a/plc4go/assets/testing/protocols/modbus/DriverTestsuite.xml
+++ b/plc4go/assets/testing/protocols/modbus/DriverTestsuite.xml
@@ -275,7 +275,7 @@
           <pdu className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUWriteMultipleHoldingRegistersRequest">
             <startingAddress>0</startingAddress>
             <quantity>2</quantity>
-            <value>QEkP2w==</value>
+            <value>40490FDB</value>
           </pdu>
         </ModbusTcpADU>
       </outgoing-plc-message>
@@ -339,7 +339,7 @@
           <pdu className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUWriteMultipleHoldingRegistersRequest">
             <startingAddress>0</startingAddress>
             <quantity>4</quantity>
-            <value>QEkP20BJD9s=</value>
+            <value>40490FDB40490FDB</value>
           </pdu>
         </ModbusTcpADU>
       </outgoing-plc-message>
diff --git a/plc4go/assets/testing/protocols/modbus/ParserSerializerTestsuite.xml b/plc4go/assets/testing/protocols/modbus/ParserSerializerTestsuite.xml
index 3dd0c83..e33dd17 100644
--- a/plc4go/assets/testing/protocols/modbus/ParserSerializerTestsuite.xml
+++ b/plc4go/assets/testing/protocols/modbus/ParserSerializerTestsuite.xml
@@ -54,10 +54,7 @@
         <transactionIdentifier>31998</transactionIdentifier>
         <unitIdentifier>255</unitIdentifier>
         <pdu className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUReadInputRegistersResponse">
-          <value>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAABAAAAAAAAAAAAAAAAAAAAdsAAAHWAABKOAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRhaW0AbgAAAAAAAAAAAAAAAAAAMDEAMAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</value>
+          <value>00000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000004000000000000000000000000000001DB000001D600004A380000000000000000000000000000000000000000000000000000000000006461696D006E0000000000000000000000000000303100300000000000000000000000000000000000000000000000000000000000000000000000000000</value>
         </pdu>
       </ModbusTcpADU>
     </xml>
@@ -109,11 +106,11 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</value>
           <items>
             <items className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUReadFileRecordResponseItem">
               <referenceType>6</referenceType>
-              <data>AAAAAA==</data>
+              <data>00000000</data>
             </items>
             <items className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUReadFileRecordResponseItem">
               <referenceType>6</referenceType>
-              <data>AAAAAAAAAAAAAAAAAAAAAA==</data>
+              <data>00000000000000000000000000000000</data>
             </items>
           </items>
         </pdu>
@@ -139,7 +136,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</value>
               <referenceType>6</referenceType>
               <fileNumber>2</fileNumber>
               <recordNumber>0</recordNumber>
-              <recordData>AAg=</recordData>
+              <recordData>0008</recordData>
             </items>
           </items>
         </pdu>
@@ -164,13 +161,13 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</value>
               <referenceType>6</referenceType>
               <fileNumber>1</fileNumber>
               <recordNumber>9999</recordNumber>
-              <recordData>AAA=</recordData>
+              <recordData>0000</recordData>
             </items>
             <items className="org.apache.plc4x.java.modbus.readwrite.ModbusPDUWriteFileRecordRequestItem">
               <referenceType>6</referenceType>
               <fileNumber>2</fileNumber>
               <recordNumber>0</recordNumber>
-              <recordData>AAA=</recordData>
+              <recordData>0000</recordData>
             </items>
           </items>
         </pdu>
diff --git a/plc4go/assets/testing/protocols/s7/ParserSerializerTestsuite.xml b/plc4go/assets/testing/protocols/s7/ParserSerializerTestsuite.xml
index 1c3b735..01e1381 100644
--- a/plc4go/assets/testing/protocols/s7/ParserSerializerTestsuite.xml
+++ b/plc4go/assets/testing/protocols/s7/ParserSerializerTestsuite.xml
@@ -209,21 +209,21 @@
                   <items>
                     <items className="org.apache.plc4x.java.s7.readwrite.SzlDataTreeItem">
                       <itemIndex>1</itemIndex>
-                      <mlfb>NkVTNyAyMTItMUJEMzAtMFhCMCA=</mlfb>
+                      <mlfb>36455337203231322D31424433302D3058423020</mlfb>
                       <moduleTypeId>8224</moduleTypeId>
                       <ausbg>1</ausbg>
                       <ausbe>8224</ausbe>
                     </items>
                     <items className="org.apache.plc4x.java.s7.readwrite.SzlDataTreeItem">
                       <itemIndex>6</itemIndex>
-                      <mlfb>NkVTNyAyMTItMUJEMzAtMFhCMCA=</mlfb>
+                      <mlfb>36455337203231322D31424433302D3058423020</mlfb>
                       <moduleTypeId>8224</moduleTypeId>
                       <ausbg>1</ausbg>
                       <ausbe>8224</ausbe>
                     </items>
                     <items className="org.apache.plc4x.java.s7.readwrite.SzlDataTreeItem">
                       <itemIndex>7</itemIndex>
-                      <mlfb>NkVTNyAyMTItMUJEMzAtMFhCMCA=</mlfb>
+                      <mlfb>36455337203231322D31424433302D3058423020</mlfb>
                       <moduleTypeId>8224</moduleTypeId>
                       <ausbg>22018</ausbg>
                       <ausbe>2</ausbe>
@@ -322,22 +322,22 @@
                 <items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
                   <returnCode>OK</returnCode>
                   <transportSize>BIT</transportSize>
-                  <data>AQ==</data>
+                  <data>01</data>
                 </items>
                 <items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
                   <returnCode>OK</returnCode>
                   <transportSize>BIT</transportSize>
-                  <data>AQ==</data>
+                  <data>01</data>
                 </items>
                 <items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
                   <returnCode>OK</returnCode>
                   <transportSize>BIT</transportSize>
-                  <data>AQ==</data>
+                  <data>01</data>
                 </items>
                 <items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
                   <returnCode>OK</returnCode>
                   <transportSize>BIT</transportSize>
-                  <data>AQ==</data>
+                  <data>01</data>
                 </items>
               </items>
             </payload>
@@ -432,22 +432,22 @@
                 <items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
                   <returnCode>OK</returnCode>
                   <transportSize>BIT</transportSize>
-                  <data>AQ==</data>
+                  <data>01</data>
                 </items>
                 <items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
                   <returnCode>OK</returnCode>
                   <transportSize>BIT</transportSize>
-                  <data>AQ==</data>
+                  <data>01</data>
                 </items>
                 <items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
                   <returnCode>OK</returnCode>
                   <transportSize>BIT</transportSize>
-                  <data>AQ==</data>
+                  <data>01</data>
                 </items>
                 <items className="org.apache.plc4x.java.s7.readwrite.S7VarPayloadDataItem">
                   <returnCode>OK</returnCode>
                   <transportSize>BIT</transportSize>
-                  <data>AQ==</data>
+                  <data>01</data>
                 </items>
               </items>
             </payload>
diff --git a/plc4go/cmd/main/drivers/knxnetip_test.go b/plc4go/cmd/main/drivers/knxnetip_test.go
index 0b0c205..21a2a97 100644
--- a/plc4go/cmd/main/drivers/knxnetip_test.go
+++ b/plc4go/cmd/main/drivers/knxnetip_test.go
@@ -384,6 +384,10 @@ func TestKnxNetIpPlc4goMemoryRead(t *testing.T) {
 	connection := connectionResult.Connection
 	defer connection.Close()
 
+	/////////////////////////////////////////////////////////////////////////////////////////////////////
+	// Group Address Table reading
+	/////////////////////////////////////////////////////////////////////////////////////////////////////
+
 	// First of all, request the starting address of the group address table
 	readRequestBuilder := connection.ReadRequestBuilder()
 	readRequestBuilder.AddItem("groupAddressTableAddress", "1.1.10/1/7")
@@ -426,6 +430,10 @@ func TestKnxNetIpPlc4goMemoryRead(t *testing.T) {
 		knxGroupAddresses = append(knxGroupAddresses, groupAddress)
 	}
 
+	/////////////////////////////////////////////////////////////////////////////////////////////////////
+	// Group Address Association Table reading
+	/////////////////////////////////////////////////////////////////////////////////////////////////////
+
 	// Now we read the group address association table address
 	readRequestBuilder = connection.ReadRequestBuilder()
 	readRequestBuilder.AddItem("groupAddressAssociationTableAddress", "1.1.10/2/7")
@@ -472,7 +480,24 @@ func TestKnxNetIpPlc4goMemoryRead(t *testing.T) {
 		}
 	}
 
-	fmt.Printf("%d", numberOfGroupAddressAssociationTableEntries)
+	/////////////////////////////////////////////////////////////////////////////////////////////////////
+	// Com Object Table reading
+	/////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// Now we read the group address association table address
+	readRequestBuilder = connection.ReadRequestBuilder()
+	readRequestBuilder.AddItem("comObjectTableAddress", "1.1.10/3/7")
+	readRequest, err = readRequestBuilder.Build()
+	if err != nil {
+		t.Errorf("error creating read request: %s", err.Error())
+		t.Fail()
+		return
+	}
+	rrr = readRequest.Execute()
+	readResult = <-rrr
+	comObjectTableAddress := readResult.Response.GetValue("comObjectTableAddress").GetUint16()
+
+	fmt.Printf("%d", comObjectTableAddress)
 }
 
 func PlcValueUint8ListToByteArray(value values.PlcValue) []byte {
diff --git a/plc4go/examples/discovery/hello_world_plc4go_knx_discovery.go b/plc4go/examples/discovery/hello_world_plc4go_knx_discovery.go
new file mode 100644
index 0000000..c9f743f
--- /dev/null
+++ b/plc4go/examples/discovery/hello_world_plc4go_knx_discovery.go
@@ -0,0 +1,99 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+package main
+
+import (
+    "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip"
+    "github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports/udp"
+    "github.com/apache/plc4x/plc4go/pkg/plc4go"
+    "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+    log "github.com/sirupsen/logrus"
+    "os"
+    "time"
+)
+
+func main() {
+    //log.SetFormatter(&log.JSONFormatter{})
+    log.SetOutput(os.Stdout)
+    log.SetLevel(log.DebugLevel)
+
+    driverManager := plc4go.NewPlcDriverManager()
+    driverManager.RegisterDriver(knxnetip.NewKnxNetIpDriver())
+    driverManager.RegisterTransport(udp.NewUdpTransport())
+
+    // Try to auto-find KNX gateways via broadcast-message discovery
+    driverManager.Discover(func(event model.PlcDiscoveryEvent) {
+        connStr := event.ProtocolCode + /*":" + event.TransportCode +*/ "://" + event.TransportUrl.Host
+        crc := driverManager.GetConnection(connStr)
+
+        // Wait for the driver to connect (or not)
+        connectionResult := <-crc
+        if connectionResult.Err != nil {
+            log.Errorf("error connecting to PLC: %s", connectionResult.Err.Error())
+            return
+        }
+        connection := connectionResult.Connection
+        defer connection.Close()
+
+        // Try to find all KNX devices on the current network
+        browseRequestBuilder := connection.BrowseRequestBuilder()
+        //browseRequestBuilder.AddItem("allDevices", "[1-15].[1-15].[0-255]")
+        browseRequestBuilder.AddItem("allDevices", "[1-3].[1-6].[0-60]")
+        browseRequest, err := browseRequestBuilder.Build()
+        if err != nil {
+            log.Errorf("error creating browse request: %s", err.Error())
+            return
+        }
+        brr := browseRequest.ExecuteWithInterceptor(func(result model.PlcBrowseEvent) bool {
+            knxAddress := result.Result.Address
+            log.Info("Inspecting detected Device at KNX Address: " + knxAddress)
+
+            // Try to get all the com-objects and the group addresses they are attached to.
+            browseRequestBuilder = connection.BrowseRequestBuilder()
+            browseRequestBuilder.AddItem("comObjects", knxAddress+"#com-obj")
+            browseRequest, err := browseRequestBuilder.Build()
+            if err != nil {
+                log.Errorf("error creating read request: %s", err.Error())
+                return false
+            }
+            brr := browseRequest.Execute()
+            browseResult := <-brr
+            if browseResult.Err != nil {
+                log.Errorf("error executing the browse request for com-objects: %s", browseResult.Err.Error())
+                return false
+            }
+            for _, result := range browseResult.Response.GetQueryResults("comObjects") {
+                log.Infof(" - %s", result.Address)
+            }
+
+            return true
+        })
+        if brr == nil {
+            log.Errorf("error executing browse request")
+            return
+        }
+        select {
+        case browseRequestResult := <- brr:
+            log.Info(browseRequestResult)
+        }
+        return
+    })
+
+    time.Sleep(time.Second * 1000000)
+}
diff --git a/plc4go/examples/read/hello_world_plc4go_read.go b/plc4go/examples/read/hello_world_plc4go_read.go
index ef45fdc..81954b7 100644
--- a/plc4go/examples/read/hello_world_plc4go_read.go
+++ b/plc4go/examples/read/hello_world_plc4go_read.go
@@ -16,7 +16,7 @@
 // specific language governing permissions and limitations
 // under the License.
 //
-package read
+package main
 
 import (
 	"encoding/json"
@@ -24,7 +24,7 @@ import (
 	"github.com/apache/plc4x/plc4go/pkg/plc4go"
 )
 
-func main() int {
+func main() {
 	// Get a connection to a remote PLC
 	crc := plc4go.NewPlcDriverManager().GetConnection("modbus:tcp://192.168.23.30")
 
@@ -32,7 +32,7 @@ func main() int {
 	connectionResult := <-crc
 	if connectionResult.Err != nil {
 		fmt.Printf("error connecting to PLC: %s", connectionResult.Err.Error())
-		return 1
+		return
 	}
 	connection := connectionResult.Connection
 
@@ -45,7 +45,7 @@ func main() int {
 	readRequest, err := rrb.Build()
 	if err != nil {
         fmt.Printf("error preparing read-request: %s", connectionResult.Err.Error())
-		return 2
+		return
 	}
 
 	// Execute a read-request
@@ -55,16 +55,14 @@ func main() int {
 	rrr := <-rrc
 	if rrr.Err != nil {
         fmt.Printf("error executing read-request: %s", rrr.Err.Error())
-		return 3
+		return
 	}
 
 	// Do something with the response
 	readResponseJson, err := json.Marshal(rrr.Response)
 	if err != nil {
         fmt.Printf("error serializing read-response: %s", err.Error())
-		return 4
+		return
 	}
 	fmt.Printf("Result: %s\n", string(readResponseJson))
-
-	return 0
 }
diff --git a/plc4go/examples/write/hello_world_plc4go_write.go b/plc4go/examples/write/hello_world_plc4go_write.go
index ba4b84c..c585d57 100644
--- a/plc4go/examples/write/hello_world_plc4go_write.go
+++ b/plc4go/examples/write/hello_world_plc4go_write.go
@@ -16,7 +16,7 @@
 // specific language governing permissions and limitations
 // under the License.
 //
-package write
+package main
 
 import (
 	"encoding/json"
@@ -24,7 +24,7 @@ import (
 	"github.com/apache/plc4x/plc4go/pkg/plc4go"
 )
 
-func main() int {
+func main() {
 	// Get a connection to a remote PLC
 	crc := plc4go.NewPlcDriverManager().GetConnection("s7://192.168.23.30")
 
@@ -32,7 +32,7 @@ func main() int {
 	connectionResult := <-crc
 	if connectionResult.Err != nil {
         fmt.Printf("error connecting to PLC: %s", connectionResult.Err.Error())
-		return 1
+		return
 	}
 	connection := connectionResult.Connection
 
@@ -46,7 +46,7 @@ func main() int {
 	writeRequest, err := wrb.Build()
 	if err != nil {
         fmt.Printf("error preparing read-request: %s", connectionResult.Err.Error())
-		return 2
+		return
 	}
 
 	// Execute a read-request
@@ -56,16 +56,14 @@ func main() int {
 	wrr := <-wrc
 	if wrr.Err != nil {
         fmt.Printf("error executing write-request: %s", wrr.Err.Error())
-		return 3
+		return
 	}
 
 	// Do something with the response
 	writeResponseJson, err := json.Marshal(wrr.Response)
 	if err != nil {
         fmt.Printf("error serializing write-response: %s", err.Error())
-		return 4
+		return
 	}
 	fmt.Printf("Result: %s\n", string(writeResponseJson))
-
-	return 0
 }
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpBrowser.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpBrowser.go
index 56269e6..ca51851 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpBrowser.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpBrowser.go
@@ -19,210 +19,416 @@
 package knxnetip
 
 import (
-	"fmt"
-	driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
-	"github.com/apache/plc4x/plc4go/internal/plc4go/spi"
-	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
-	apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
-	"strconv"
-	"strings"
-	"time"
+    "errors"
+    "fmt"
+    driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
+    "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+    "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+    apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+    "strconv"
+    "strings"
+    "time"
 )
 
 type KnxNetIpBrowser struct {
-	connection      *KnxNetIpConnection
-	messageCodec    spi.MessageCodec
-	sequenceCounter uint8
-	spi.PlcBrowser
+    connection      *KnxNetIpConnection
+    messageCodec    spi.MessageCodec
+    sequenceCounter uint8
+    spi.PlcBrowser
 }
 
 func NewKnxNetIpBrowser(connection *KnxNetIpConnection, messageCodec spi.MessageCodec) *KnxNetIpBrowser {
-	return &KnxNetIpBrowser{
-		connection:      connection,
-		messageCodec:    messageCodec,
-		sequenceCounter: 0,
-	}
+    return &KnxNetIpBrowser{
+        connection:      connection,
+        messageCodec:    messageCodec,
+        sequenceCounter: 0,
+    }
 }
 
 func (b KnxNetIpBrowser) Browse(browseRequest apiModel.PlcBrowseRequest) <-chan apiModel.PlcBrowseRequestResult {
-	return b.BrowseWithInterceptor(browseRequest, func(result apiModel.PlcBrowseEvent) bool {
-		return true
-	})
+    return b.BrowseWithInterceptor(browseRequest, func(result apiModel.PlcBrowseEvent) bool {
+        return true
+    })
 }
 
 func (b KnxNetIpBrowser) BrowseWithInterceptor(browseRequest apiModel.PlcBrowseRequest, interceptor func(result apiModel.PlcBrowseEvent) bool) <-chan apiModel.PlcBrowseRequestResult {
-	result := make(chan apiModel.PlcBrowseRequestResult)
-	sendResult := func(browseResponse apiModel.PlcBrowseResponse, err error) {
-		result <- apiModel.PlcBrowseRequestResult{
-			Request:  browseRequest,
-			Response: browseResponse,
-			Err:      err,
-		}
-	}
-
-	go func() {
-		results := map[string][]apiModel.PlcBrowseQueryResult{}
-		for _, queryName := range browseRequest.GetQueryNames() {
-			queryString := browseRequest.GetQueryString(queryName)
-			field, err := b.connection.fieldHandler.ParseQuery(queryString)
-			if err != nil {
-				sendResult(nil, err)
-			}
-
-			// Create a list of address strings, which doesn't contain any ranges, lists or wildcards
-			options, err := b.calculateAddresses(field)
-			if err != nil {
-				sendResult(nil, err)
-			}
-
-			var queryResults []apiModel.PlcBrowseQueryResult
-			// Parse each of these expanded addresses and handle them accordingly.
-			for _, option := range options {
-				field, err = b.connection.fieldHandler.ParseQuery(option)
-				if err != nil {
-					sendResult(nil, err)
-				}
-
-				// The following browse actions could be required:
-				switch field.(type) {
-				// - A Device Address
-				//   - A Device has to be detected (This is done in every case)
-				//      TODO: Send a Connect to the physical knx address
-				//   - If an object-id is provided, check if this object id exists
-				//   - If a property-id is provided, check if this property exists and try to get more information about it
-				case KnxNetIpDevicePropertyAddressPlcField:
-					targetAddress := FieldToKnxAddress(field.(KnxNetIpDevicePropertyAddressPlcField))
-
-					// Send a connection request to the device
-					deviceConnections := b.connection.ConnectToDevice(*targetAddress)
-					select {
-					case deviceConnection := <-deviceConnections:
-						if deviceConnection != nil {
-							queryResult := apiModel.PlcBrowseQueryResult{
-								Address: fmt.Sprintf("%d.%d.%d",
-									targetAddress.MainGroup,
-									targetAddress.MiddleGroup,
-									targetAddress.SubGroup),
-								PossibleDataTypes: nil,
-							}
-
-							// Pass it to the callback
-							add := interceptor(apiModel.PlcBrowseEvent{
-								Request:   browseRequest,
-								QueryName: queryName,
-								Result:    &queryResult,
-								Err:       nil,
-							})
-
-							// If the interceptor opted for adding it to the result, do so
-							if add {
-								queryResults = append(queryResults, queryResult)
-							}
-
-							deviceDisconnections := b.connection.DisconnectFromDevice(*targetAddress)
-							select {
-							case _ = <-deviceDisconnections:
-							case <-time.After(b.connection.defaultTtl * 10):
-								fmt.Printf("Timedout")
-							}
-						}
-					case <-time.After(b.connection.defaultTtl):
-						// In this case the remote was just not responding.
-					}
-					// Just to slow things down a bit (This way we can't exceed the max number of requests per minute)
-					//time.Sleep(time.Millisecond * 20)
-				// - A Group Address
-				//   - Check the cache of known group addresses. If there is data available from that group-id, it's returned
-				case KnxNetIpGroupAddress3LevelPlcField:
-				// - A Group Address
-				//   - Check the cache of known group addresses. If there is data available from that group-id, it's returned
-				case KnxNetIpGroupAddress2LevelPlcField:
-				// - A Group Address
-				//   - Check the cache of known group addresses. If there is data available from that group-id, it's returned
-				case KnxNetIpGroupAddress1LevelPlcField:
-				}
-			}
-			results[queryName] = queryResults
-		}
-		sendResult(model.NewDefaultPlcBrowseResponse(browseRequest, results), nil)
-	}()
-	return result
+    result := make(chan apiModel.PlcBrowseRequestResult)
+    sendResult := func(browseResponse apiModel.PlcBrowseResponse, err error) {
+        result <- apiModel.PlcBrowseRequestResult{
+            Request:  browseRequest,
+            Response: browseResponse,
+            Err:      err,
+        }
+    }
+
+    go func() {
+        results := map[string][]apiModel.PlcBrowseQueryResult{}
+        for _, queryName := range browseRequest.GetQueryNames() {
+            queryString := browseRequest.GetQueryString(queryName)
+            field, err := b.connection.fieldHandler.ParseQuery(queryString)
+            if err != nil {
+                sendResult(nil, err)
+                return
+            }
+
+            switch field.(type) {
+            case KnxNetIpDeviceQueryField:
+                queryResults, err := b.executeDeviceQuery(field.(KnxNetIpDeviceQueryField), browseRequest, queryName, interceptor)
+                if err != nil {
+                    // TODO: Return some sort of return code like with the read and write APIs
+                    results[queryName] = nil
+                } else {
+                    results[queryName] = queryResults
+                }
+            case KnxNetIpCommunicationObjectQueryField:
+                queryResults, err := b.executeCommunicationObjectQuery(field.(KnxNetIpCommunicationObjectQueryField))
+                if err != nil {
+                    // TODO: Return some sort of return code like with the read and write APIs
+                    results[queryName] = nil
+                } else {
+                    results[queryName] = queryResults
+                }
+            default:
+                // TODO: Return some sort of return code like with the read and write APIs
+                results[queryName] = nil
+            }
+        }
+        sendResult(model.NewDefaultPlcBrowseResponse(browseRequest, results), nil)
+    }()
+    return result
+}
+
+func (b KnxNetIpBrowser) executeDeviceQuery(field KnxNetIpDeviceQueryField, browseRequest apiModel.PlcBrowseRequest, queryName string, interceptor func(result apiModel.PlcBrowseEvent) bool) ([]apiModel.PlcBrowseQueryResult, error) {
+    // Create a list of address strings, which doesn't contain any ranges, lists or wildcards
+    knxAddresses, err := b.calculateAddresses(field)
+    if err != nil {
+        return nil, err
+    }
+    if len(knxAddresses) == 0 {
+        return nil, errors.New("query resulted in not a single valid address")
+    }
+
+    var queryResults []apiModel.PlcBrowseQueryResult
+    // Parse each of these expanded addresses and handle them accordingly.
+    for _, knxAddress := range knxAddresses {
+        // Send a connection request to the device
+        deviceConnections := b.connection.ConnectToDevice(knxAddress)
+        select {
+        case deviceConnection := <-deviceConnections:
+            if deviceConnection != nil {
+                queryResult := apiModel.PlcBrowseQueryResult{
+                    Address: fmt.Sprintf("%d.%d.%d",
+                        knxAddress.MainGroup,
+                        knxAddress.MiddleGroup,
+                        knxAddress.SubGroup),
+                    PossibleDataTypes: nil,
+                }
+
+                // Pass it to the callback
+                add := true
+                if interceptor != nil {
+                    add = interceptor(apiModel.PlcBrowseEvent{
+                        Request:   browseRequest,
+                        QueryName: queryName,
+                        Result:    &queryResult,
+                        Err:       nil,
+                    })
+                }
+
+                // If the interceptor opted for adding it to the result, do so
+                if add {
+                    queryResults = append(queryResults, queryResult)
+                }
+
+                deviceDisconnections := b.connection.DisconnectFromDevice(knxAddress)
+                select {
+                case _ = <-deviceDisconnections:
+                case <-time.After(b.connection.defaultTtl * 10):
+                    fmt.Printf("Timedout")
+                }
+            }
+        case <-time.After(b.connection.defaultTtl):
+            // In this case the remote was just not responding.
+        }
+        // Just to slow things down a bit (This way we can't exceed the max number of requests per minute)
+        //time.Sleep(time.Millisecond * 20)
+    }
+    return queryResults, nil
+}
+
+func (b KnxNetIpBrowser) executeCommunicationObjectQuery(field KnxNetIpCommunicationObjectQueryField) ([]apiModel.PlcBrowseQueryResult, error) {
+    var results []apiModel.PlcBrowseQueryResult
+
+    knxAddress := field.toKnxAddress()
+    knxAddressString := KnxAddressToString(knxAddress)
+
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+    // Group Address Table reading
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    // First of all, request the starting address of the group address table
+    readRequestBuilder := b.connection.ReadRequestBuilder()
+    readRequestBuilder.AddItem("groupAddressTableAddress", knxAddressString+"#1/7")
+    readRequest, err := readRequestBuilder.Build()
+    if err != nil {
+        return nil, errors.New("error creating read request: " + err.Error())
+    }
+    rrr := readRequest.Execute()
+    readResult := <-rrr
+    if readResult.Err != nil {
+        return nil, errors.New("error reading the group address table starting address: " + readResult.Err.Error())
+    }
+    if readResult.Response.GetResponseCode("groupAddressTableAddress") != apiModel.PlcResponseCode_OK {
+        return nil, errors.New("error reading group address table starting address: " +
+            readResult.Response.GetResponseCode("groupAddressTableAddress").GetName())
+    }
+    groupAddressTableStartAddress := readResult.Response.GetValue("groupAddressTableAddress").GetUint32()
+
+    // Then read one byte at the given location.
+    // This will return the number of entries in the group address table (each 2 bytes)
+    readRequestBuilder = b.connection.ReadRequestBuilder()
+    // Depending on the type of device, query an USINT (1 byte) or UINT (2 bytes)
+    // TODO: Do this correctly depending on the device connection device-descriptor
+    if b.connection.DeviceConnections[*knxAddress].deviceDescriptor == uint16(0x07B0) /* SystemB */ {
+        readRequestBuilder.AddItem("numberOfAddressTableEntries",
+            fmt.Sprintf("%s#%X:UINT", knxAddressString, groupAddressTableStartAddress))
+    } else {
+        readRequestBuilder.AddItem("numberOfAddressTableEntries",
+            fmt.Sprintf("%s#%X:USINT", knxAddressString, groupAddressTableStartAddress))
+    }
+    readRequest, err = readRequestBuilder.Build()
+    if err != nil {
+        return nil, errors.New("error creating read request: " + err.Error())
+    }
+    rrr = readRequest.Execute()
+    readResult = <-rrr
+    if readResult.Err != nil {
+        return nil, errors.New("error reading the number of group address table entries: " + readResult.Err.Error())
+    }
+    if readResult.Response.GetResponseCode("numberOfAddressTableEntries") != apiModel.PlcResponseCode_OK {
+        return nil, errors.New("error reading the number of group address table entries: " +
+            readResult.Response.GetResponseCode("numberOfAddressTableEntries").GetName())
+    }
+    numGroupAddresses := readResult.Response.GetValue("numberOfAddressTableEntries").GetUint16()
+
+    // Read the data in the group address table
+    readRequestBuilder = b.connection.ReadRequestBuilder()
+    // TODO: This request needs to be automatically split up into multiple requests.
+    // Reasons for splitting up:
+    // - Max APDU Size exceeded
+    // - Max 63 bytes readable in one request, due to max of count field
+
+    if b.connection.DeviceConnections[*knxAddress].deviceDescriptor == uint16(0x07B0) /* SystemB */ {
+        readRequestBuilder.AddItem("groupAddressTable",
+            fmt.Sprintf("%s#%X:UINT[%d]", knxAddressString, groupAddressTableStartAddress+2, numGroupAddresses))
+    } else {
+        readRequestBuilder.AddItem("groupAddressTable",
+            fmt.Sprintf("%s#%X:UINT[%d]", knxAddressString, groupAddressTableStartAddress+3, numGroupAddresses-1))
+    }
+    readRequest, err = readRequestBuilder.Build()
+    if err != nil {
+        return nil, errors.New("error creating read request: " + err.Error())
+    }
+    rrr = readRequest.Execute()
+    readResult = <-rrr
+    if readResult.Err != nil {
+        return nil, errors.New("error reading the group address table content: " + readResult.Err.Error())
+    }
+    if (readResult.Response == nil) ||
+        (readResult.Response.GetResponseCode("groupAddressTable") != apiModel.PlcResponseCode_OK) {
+        return nil, errors.New("error reading the group address table content: " +
+            readResult.Response.GetResponseCode("groupAddressTable").GetName())
+    }
+    var knxGroupAddresses []*driverModel.KnxGroupAddress
+    for _, groupAddress := range readResult.Response.GetValue("groupAddressTable").GetList() {
+        groupAddress := Uint16ToKnxGroupAddress(groupAddress.GetUint16(), 3)
+        knxGroupAddresses = append(knxGroupAddresses, groupAddress)
+    }
+
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+    // Group Address Association Table reading
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    // Now we read the group address association table address
+    readRequestBuilder = b.connection.ReadRequestBuilder()
+    readRequestBuilder.AddItem("groupAddressAssociationTableAddress",
+        fmt.Sprintf("%s#2/7", knxAddressString))
+    readRequest, err = readRequestBuilder.Build()
+    if err != nil {
+        return nil, errors.New("error creating read request: " + err.Error())
+    }
+    rrr = readRequest.Execute()
+    readResult = <-rrr
+    if readResult.Err != nil {
+        return nil, errors.New("error reading the group address association table content: " + readResult.Err.Error())
+    }
+    if (readResult.Response != nil) &&
+        (readResult.Response.GetResponseCode("groupAddressAssociationTableAddress") != apiModel.PlcResponseCode_OK) {
+        return nil, errors.New("error reading the group address association table content: " +
+            readResult.Response.GetResponseCode("groupAddressAssociationTableAddress").GetName())
+    }
+    groupAddressAssociationTableAddress := readResult.Response.GetValue("groupAddressAssociationTableAddress").GetUint16()
+
+    // Then read one uint16 at the given location.
+    // This will return the number of entries in the group address table (each 2 bytes)
+    readRequestBuilder = b.connection.ReadRequestBuilder()
+    if b.connection.DeviceConnections[*knxAddress].deviceDescriptor == uint16(0x07B0) /* SystemB */ {
+        readRequestBuilder.AddItem("numberOfGroupAddressAssociationTableEntries",
+            fmt.Sprintf("%s#%X:UINT", knxAddressString, groupAddressAssociationTableAddress))
+    } else {
+        readRequestBuilder.AddItem("numberOfGroupAddressAssociationTableEntries",
+            fmt.Sprintf("%s#%X:USINT", knxAddressString, groupAddressAssociationTableAddress))
+    }
+    readRequest, err = readRequestBuilder.Build()
+    if err != nil {
+        return nil, errors.New("error creating read request: " + err.Error())
+    }
+    rrr = readRequest.Execute()
+    readResult = <-rrr
+    if readResult.Err != nil {
+        return nil, errors.New("error reading the number of group address association table entries: " + readResult.Err.Error())
+    }
+    if (readResult.Response != nil) &&
+        (readResult.Response.GetResponseCode("numberOfGroupAddressAssociationTableEntries") != apiModel.PlcResponseCode_OK) {
+        return nil, errors.New("error reading the number of group address association table entries: " +
+            readResult.Response.GetResponseCode("numberOfGroupAddressAssociationTableEntries").GetName())
+    }
+    numberOfGroupAddressAssociationTableEntries := readResult.Response.GetValue("numberOfGroupAddressAssociationTableEntries").GetUint16()
+
+    // Read the data in the group address table
+    readRequestBuilder = b.connection.ReadRequestBuilder()
+    // TODO: This request needs to be automatically split up into multiple requests.
+    // Reasons for splitting up:
+    // - Max APDU Size exceeded
+    // - Max 63 bytes readable in one request, due to max of count field
+    if b.connection.DeviceConnections[*knxAddress].deviceDescriptor == uint16(0x07B0) /* SystemB */ {
+        readRequestBuilder.AddItem("groupAddressAssociationTable",
+            fmt.Sprintf("%s#%X:UDINT[%d]", knxAddressString, groupAddressAssociationTableAddress+2, numberOfGroupAddressAssociationTableEntries))
+    } else {
+        readRequestBuilder.AddItem("groupAddressAssociationTable",
+            fmt.Sprintf("%s#%X:UINT[%d]", knxAddressString, groupAddressAssociationTableAddress+1, numberOfGroupAddressAssociationTableEntries))
+    }
+    readRequest, err = readRequestBuilder.Build()
+    if err != nil {
+        return nil, errors.New("error creating read request: " + err.Error())
+    }
+    rrr = readRequest.Execute()
+    readResult = <-rrr
+    if readResult.Err != nil {
+        return nil, errors.New("error reading the group address association table content: " + readResult.Err.Error())
+    }
+    if (readResult.Response != nil) &&
+        (readResult.Response.GetResponseCode("groupAddressAssociationTable") != apiModel.PlcResponseCode_OK) {
+        return nil, errors.New("error reading the group address association table content: " +
+            readResult.Response.GetResponseCode("groupAddressAssociationTable").GetName())
+    }
+    // Output the group addresses
+    for _, groupAddressAssociation := range readResult.Response.GetValue("groupAddressAssociationTable").GetList() {
+        var addressIndex uint16
+        var comObjectNumber uint16
+        if b.connection.DeviceConnections[*knxAddress].deviceDescriptor == uint16(0x07B0) /* SystemB */ {
+            addressIndex = uint16((groupAddressAssociation.GetUint32()>>16)&0xFFFF) - 1
+            comObjectNumber = uint16(groupAddressAssociation.GetUint32() & 0xFFFF)
+        } else {
+            addressIndex = ((groupAddressAssociation.GetUint16() >> 8) & 0xFF) - 1
+            comObjectNumber = groupAddressAssociation.GetUint16() & 0xFF
+        }
+        if addressIndex < uint16(len(knxGroupAddresses)) {
+            groupAddress := knxGroupAddresses[addressIndex]
+            result := apiModel.PlcBrowseQueryResult{
+                Address: fmt.Sprintf(
+                    "%s#%s %d", knxAddressString, GroupAddressToString(groupAddress), comObjectNumber),
+                PossibleDataTypes: nil,
+            }
+            results = append(results, result)
+        }
+    }
+
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+    // Com Object Table reading (Not supported on all devices)
+    // (This part is optional and experimental ...)
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    // Now we read the group address association table address
+    /*readRequestBuilder = b.connection.ReadRequestBuilder()
+    readRequestBuilder.AddItem("comObjectTableAddress", fmt.Sprintf("%s#3/7", knxAddressString))
+    readRequest, err = readRequestBuilder.Build()
+    if err != nil {
+        return nil, errors.New("error creating read request: " + err.Error())
+    }
+    rrr = readRequest.Execute()
+    readResult = <-rrr
+    if readResult.Response.GetResponseCode("comObjectTableAddress") == apiModel.PlcResponseCode_OK {
+        comObjectTableAddress := readResult.Response.GetValue("comObjectTableAddress").GetUint16()
+        log.Info("Com Object Table Address: %d", comObjectTableAddress)
+    }*/
+
+    return results, nil
 }
 
-func (b KnxNetIpBrowser) calculateAddresses(field apiModel.PlcField) ([]string, error) {
-	var addresses []string
-	switch field.(type) {
-	case KnxNetIpDevicePropertyAddressPlcField:
-		propertyAddressField := field.(KnxNetIpDevicePropertyAddressPlcField)
-		mainGroupOptions, err := b.explodeSegment(propertyAddressField.MainGroup, 1, 15)
-		if err != nil {
-			return nil, err
-		}
-		middleGroupOptions, err := b.explodeSegment(propertyAddressField.MiddleGroup, 1, 15)
-		if err != nil {
-			return nil, err
-		}
-		subGroupOptions, err := b.explodeSegment(propertyAddressField.SubGroup, 0, 255)
-		if err != nil {
-			return nil, err
-		}
-		for _, mainOption := range mainGroupOptions {
-			for _, middleOption := range middleGroupOptions {
-				for _, subOption := range subGroupOptions {
-					// Don't try connecting to ourselves.
-					if b.connection.ClientKnxAddress != nil {
-						currentAddress := driverModel.KnxAddress{
-							MainGroup:   mainOption,
-							MiddleGroup: middleOption,
-							SubGroup:    subOption,
-						}
-						if currentAddress != *b.connection.ClientKnxAddress {
-							address := fmt.Sprintf("%d.%d.%d", mainOption, middleOption, subOption)
-							addresses = append(addresses, address)
-						}
-					}
-				}
-			}
-		}
-	case KnxNetIpGroupAddress3LevelPlcField:
-	case KnxNetIpGroupAddress2LevelPlcField:
-	case KnxNetIpGroupAddress1LevelPlcField:
-
-	}
-	return addresses, nil
+func (b KnxNetIpBrowser) calculateAddresses(field KnxNetIpDeviceQueryField) ([]driverModel.KnxAddress, error) {
+    var explodedAddresses []driverModel.KnxAddress
+    mainGroupOptions, err := b.explodeSegment(field.MainGroup, 1, 15)
+    if err != nil {
+        return nil, err
+    }
+    middleGroupOptions, err := b.explodeSegment(field.MiddleGroup, 1, 15)
+    if err != nil {
+        return nil, err
+    }
+    subGroupOptions, err := b.explodeSegment(field.SubGroup, 0, 255)
+    if err != nil {
+        return nil, err
+    }
+    for _, mainOption := range mainGroupOptions {
+        for _, middleOption := range middleGroupOptions {
+            for _, subOption := range subGroupOptions {
+                // Don't try connecting to ourselves.
+                if b.connection.ClientKnxAddress != nil {
+                    currentAddress := driverModel.KnxAddress{
+                        MainGroup:   mainOption,
+                        MiddleGroup: middleOption,
+                        SubGroup:    subOption,
+                    }
+                    explodedAddresses = append(explodedAddresses, currentAddress)
+                }
+            }
+        }
+    }
+    return explodedAddresses, nil
 }
 
 func (b KnxNetIpBrowser) explodeSegment(segment string, min uint8, max uint8) ([]uint8, error) {
-	var options []uint8
-	if strings.Contains(segment, "*") {
-		for i := min; i <= max; i++ {
-			options = append(options, i)
-		}
-	} else if strings.HasPrefix(segment, "[") && strings.HasSuffix(segment, "]") {
-		segment = strings.TrimPrefix(segment, "[")
-		segment = strings.TrimSuffix(segment, "]")
-		for _, segment := range strings.Split(segment, ",") {
-			if strings.Contains(segment, "-") {
-				split := strings.Split(segment, "-")
-				localMin, err := strconv.Atoi(split[0])
-				if err != nil {
-					return nil, err
-				}
-				localMax, err := strconv.Atoi(split[1])
-				if err != nil {
-					return nil, err
-				}
-				for i := localMin; i <= localMax; i++ {
-					options = append(options, uint8(i))
-				}
-			} else {
-				option, err := strconv.Atoi(segment)
-				if err != nil {
-					return nil, err
-				}
-				options = append(options, uint8(option))
-			}
-		}
-	}
-	return options, nil
+    var options []uint8
+    if strings.Contains(segment, "*") {
+        for i := min; i <= max; i++ {
+            options = append(options, i)
+        }
+    } else if strings.HasPrefix(segment, "[") && strings.HasSuffix(segment, "]") {
+        segment = strings.TrimPrefix(segment, "[")
+        segment = strings.TrimSuffix(segment, "]")
+        for _, segment := range strings.Split(segment, ",") {
+            if strings.Contains(segment, "-") {
+                split := strings.Split(segment, "-")
+                localMin, err := strconv.Atoi(split[0])
+                if err != nil {
+                    return nil, err
+                }
+                localMax, err := strconv.Atoi(split[1])
+                if err != nil {
+                    return nil, err
+                }
+                for i := localMin; i <= localMax; i++ {
+                    options = append(options, uint8(i))
+                }
+            } else {
+                option, err := strconv.Atoi(segment)
+                if err != nil {
+                    return nil, err
+                }
+                options = append(options, uint8(option))
+            }
+        }
+    }
+    return options, nil
 }
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go
index 8fe8612..f96d2e3 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpConnection.go
@@ -19,1679 +19,1581 @@
 package knxnetip
 
 import (
-	"bytes"
-	"errors"
-	"fmt"
-	driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
-	"github.com/apache/plc4x/plc4go/internal/plc4go/spi"
-	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/interceptors"
-	internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
-	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports"
-	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports/udp"
-	"github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
-	values2 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
-	"github.com/apache/plc4x/plc4go/pkg/plc4go"
-	apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
-	"github.com/apache/plc4x/plc4go/pkg/plc4go/values"
-	log "github.com/sirupsen/logrus"
-	"math"
-	"net"
-	"strconv"
-	"strings"
-	"sync"
-	"sync/atomic"
-	"time"
+    "bytes"
+    "errors"
+    "fmt"
+    driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
+    "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+    "github.com/apache/plc4x/plc4go/internal/plc4go/spi/interceptors"
+    internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+    "github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports"
+    "github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports/udp"
+    "github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+    values2 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
+    "github.com/apache/plc4x/plc4go/pkg/plc4go"
+    apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+    "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+    log "github.com/sirupsen/logrus"
+    "math"
+    "net"
+    "strconv"
+    "strings"
+    "sync"
+    "sync/atomic"
+    "time"
 )
 
 type ConnectionMetadata struct {
-	KnxMedium         driverModel.KnxMedium
-	GatewayName       string
-	GatewayKnxAddress string
-	ClientKnxAddress  string
-
-	ProjectNumber          uint8
-	InstallationNumber     uint8
-	DeviceSerialNumber     []int8
-	DeviceMulticastAddress []int8
-	DeviceMacAddress       []int8
-	SupportedServices      []string
-
-	apiModel.PlcConnectionMetadata
+    KnxMedium         driverModel.KnxMedium
+    GatewayName       string
+    GatewayKnxAddress string
+    ClientKnxAddress  string
+
+    ProjectNumber          uint8
+    InstallationNumber     uint8
+    DeviceSerialNumber     []int8
+    DeviceMulticastAddress []int8
+    DeviceMacAddress       []int8
+    SupportedServices      []string
+
+    apiModel.PlcConnectionMetadata
 }
 
 func (m ConnectionMetadata) GetConnectionAttributes() map[string]string {
-	return map[string]string{
-		"KnxMedium":         m.KnxMedium.String(),
-		"GatewayName":       m.GatewayName,
-		"GatewayKnxAddress": m.GatewayKnxAddress,
-		"ClientKnxAddress":  m.ClientKnxAddress,
-
-		"ProjectNumber":          strconv.Itoa(int(m.ProjectNumber)),
-		"InstallationNumber":     strconv.Itoa(int(m.InstallationNumber)),
-		"DeviceSerialNumber":     utils.Int8ArrayToString(m.DeviceSerialNumber, " "),
-		"DeviceMulticastAddress": utils.Int8ArrayToString(m.DeviceSerialNumber, "."),
-		"DeviceMacAddress":       utils.Int8ArrayToString(m.DeviceSerialNumber, ":"),
-		"SupportedServices":      strings.Join(m.SupportedServices, ", "),
-	}
+    return map[string]string{
+        "KnxMedium":         m.KnxMedium.String(),
+        "GatewayName":       m.GatewayName,
+        "GatewayKnxAddress": m.GatewayKnxAddress,
+        "ClientKnxAddress":  m.ClientKnxAddress,
+
+        "ProjectNumber":          strconv.Itoa(int(m.ProjectNumber)),
+        "InstallationNumber":     strconv.Itoa(int(m.InstallationNumber)),
+        "DeviceSerialNumber":     utils.Int8ArrayToString(m.DeviceSerialNumber, " "),
+        "DeviceMulticastAddress": utils.Int8ArrayToString(m.DeviceSerialNumber, "."),
+        "DeviceMacAddress":       utils.Int8ArrayToString(m.DeviceSerialNumber, ":"),
+        "SupportedServices":      strings.Join(m.SupportedServices, ", "),
+    }
 }
 
 func (m ConnectionMetadata) CanRead() bool {
-	return true
+    return true
 }
 
 func (m ConnectionMetadata) CanWrite() bool {
-	return true
+    return true
 }
 
 func (m ConnectionMetadata) CanSubscribe() bool {
-	return true
+    return true
 }
 
 func (m ConnectionMetadata) CanBrowse() bool {
-	return true
+    return true
 }
 
 type KnxDeviceConnection struct {
-	counter          uint8
-	deviceDescriptor uint16
-	maxApdu          uint16
+    counter          uint8
+    deviceDescriptor uint16
+    maxApdu          uint16
 }
 
 type KnxMemoryReadFragment struct {
-	numElements     uint8
-	startingAddress uint16
+    numElements     uint8
+    startingAddress uint16
 }
 
 type KnxNetIpConnection struct {
-	messageCodec             spi.MessageCodec
-	options                  map[string][]string
-	fieldHandler             spi.PlcFieldHandler
-	valueHandler             spi.PlcValueHandler
-	connectionStateTimer     *time.Ticker
-	quitConnectionStateTimer chan struct{}
-	subscribers              []*KnxNetIpSubscriber
-	leve3AddressCache        map[uint16]*driverModel.KnxGroupAddress3Level
-	leve2AddressCache        map[uint16]*driverModel.KnxGroupAddress2Level
-	leve1AddressCache        map[uint16]*driverModel.KnxGroupAddressFreeLevel
-
-	valueCache      map[uint16][]int8
-	valueCacheMutex sync.RWMutex
-	metadata        *ConnectionMetadata
-	defaultTtl      time.Duration
-
-	GatewayKnxAddress             *driverModel.KnxAddress
-	ClientKnxAddress              *driverModel.KnxAddress
-	CommunicationChannelId        uint8
-	SequenceCounter               int32
-	TunnelingRequestExpectationId int32
-	DeviceConnections             map[driverModel.KnxAddress]*KnxDeviceConnection
-
-	requestInterceptor internalModel.RequestInterceptor
-	plc4go.PlcConnection
+    messageCodec             spi.MessageCodec
+    options                  map[string][]string
+    fieldHandler             spi.PlcFieldHandler
+    valueHandler             spi.PlcValueHandler
+    connectionStateTimer     *time.Ticker
+    quitConnectionStateTimer chan struct{}
+    subscribers              []*KnxNetIpSubscriber
+    leve3AddressCache        map[uint16]*driverModel.KnxGroupAddress3Level
+    leve2AddressCache        map[uint16]*driverModel.KnxGroupAddress2Level
+    leve1AddressCache        map[uint16]*driverModel.KnxGroupAddressFreeLevel
+
+    valueCache      map[uint16][]int8
+    valueCacheMutex sync.RWMutex
+    metadata        *ConnectionMetadata
+    defaultTtl      time.Duration
+
+    GatewayKnxAddress             *driverModel.KnxAddress
+    ClientKnxAddress              *driverModel.KnxAddress
+    CommunicationChannelId        uint8
+    SequenceCounter               int32
+    TunnelingRequestExpectationId int32
+    DeviceConnections             map[driverModel.KnxAddress]*KnxDeviceConnection
+
+    requestInterceptor internalModel.RequestInterceptor
+    plc4go.PlcConnection
 }
 
 type KnxReadResult struct {
-	returnCode apiModel.PlcResponseCode
-	value      *values.PlcValue
+    returnCode apiModel.PlcResponseCode
+    value      *values.PlcValue
 }
 
 type InternalResult struct {
-	responseMessage interface{}
-	err             error
+    responseMessage interface{}
+    err             error
 }
 
 func NewKnxNetIpConnection(transportInstance transports.TransportInstance, options map[string][]string, fieldHandler spi.PlcFieldHandler) *KnxNetIpConnection {
-	connection := &KnxNetIpConnection{
-		options:            options,
-		fieldHandler:       fieldHandler,
-		valueHandler:       NewValueHandler(),
-		requestInterceptor: interceptors.NewSingleItemRequestInterceptor(),
-		subscribers:        []*KnxNetIpSubscriber{},
-		leve3AddressCache:  map[uint16]*driverModel.KnxGroupAddress3Level{},
-		leve2AddressCache:  map[uint16]*driverModel.KnxGroupAddress2Level{},
-		leve1AddressCache:  map[uint16]*driverModel.KnxGroupAddressFreeLevel{},
-		valueCache:         map[uint16][]int8{},
-		valueCacheMutex:    sync.RWMutex{},
-		metadata:           &ConnectionMetadata{},
-		defaultTtl:         time.Millisecond * 10000,
-		DeviceConnections:  map[driverModel.KnxAddress]*KnxDeviceConnection{},
-	}
-	connection.messageCodec = NewKnxNetIpMessageCodec(transportInstance, connection.interceptIncomingMessage)
-	return connection
+    connection := &KnxNetIpConnection{
+        options:            options,
+        fieldHandler:       fieldHandler,
+        valueHandler:       NewValueHandler(),
+        requestInterceptor: interceptors.NewSingleItemRequestInterceptor(),
+        subscribers:        []*KnxNetIpSubscriber{},
+        leve3AddressCache:  map[uint16]*driverModel.KnxGroupAddress3Level{},
+        leve2AddressCache:  map[uint16]*driverModel.KnxGroupAddress2Level{},
+        leve1AddressCache:  map[uint16]*driverModel.KnxGroupAddressFreeLevel{},
+        valueCache:         map[uint16][]int8{},
+        valueCacheMutex:    sync.RWMutex{},
+        metadata:           &ConnectionMetadata{},
+        defaultTtl:         time.Second * 60,
+        DeviceConnections:  map[driverModel.KnxAddress]*KnxDeviceConnection{},
+    }
+    connection.messageCodec = NewKnxNetIpMessageCodec(transportInstance, connection.interceptIncomingMessage)
+    return connection
 }
 
 func (m *KnxNetIpConnection) Connect() <-chan plc4go.PlcConnectionConnectResult {
-	result := make(chan plc4go.PlcConnectionConnectResult)
-	sendResult := func(connection plc4go.PlcConnection, err error) {
-		result <- plc4go.NewPlcConnectionConnectResult(connection, err)
-	}
-
-	go func() {
-		err := m.messageCodec.Connect()
-		if err != nil {
-			sendResult(nil, errors.New("error opening connection"))
-			return
-		}
-
-		searchResponseChannel := m.sendGatewaySearchRequest()
-		select {
-		case searchResponse := <-searchResponseChannel:
-			if searchResponse.err != nil {
-				sendResult(nil, errors.New("error discovering device capabilities"))
-				return
-			}
-
-			searchResponseMessage := driverModel.CastSearchResponse(searchResponse.responseMessage)
-			// Save some important information
-			m.metadata.KnxMedium = searchResponseMessage.DibDeviceInfo.KnxMedium
-			m.metadata.GatewayName = string(bytes.Trim(utils.Int8ArrayToByteArray(
-				searchResponseMessage.DibDeviceInfo.DeviceFriendlyName), "\x00"))
-			m.GatewayKnxAddress = searchResponseMessage.DibDeviceInfo.KnxAddress
-			m.metadata.GatewayKnxAddress = KnxAddressToString(m.GatewayKnxAddress)
-			m.metadata.ProjectNumber = searchResponseMessage.DibDeviceInfo.ProjectInstallationIdentifier.ProjectNumber
-			m.metadata.InstallationNumber = searchResponseMessage.DibDeviceInfo.ProjectInstallationIdentifier.InstallationNumber
-			m.metadata.DeviceSerialNumber = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceSerialNumber
-			m.metadata.DeviceMulticastAddress = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceMulticastAddress.Addr
-			m.metadata.DeviceMacAddress = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceMacAddress.Addr
-			m.metadata.SupportedServices = []string{}
-			supportsTunneling := false
-			for _, serviceId := range searchResponseMessage.DibSuppSvcFamilies.ServiceIds {
-				m.metadata.SupportedServices = append(m.metadata.SupportedServices, serviceId.Child.GetTypeName())
-				// If this is an instance of the "tunneling", service, this connection supports tunneling
-				_, ok := serviceId.Child.(*driverModel.KnxNetIpTunneling)
-				if ok {
-					supportsTunneling = true
-					break
-				}
-			}
-
-			// If the current device supports tunneling, create a tunneling connection.
-			// Via this connection we then get access to the entire KNX network this Gateway is connected to.
-			if supportsTunneling {
-				// As soon as we got a successful search-response back, send a connection request.
-				connectionResponseChannel := m.sendGatewayConnectionRequest()
-				select {
-				case connectionResponse := <-connectionResponseChannel:
-					if connectionResponse.err != nil {
-						sendResult(nil, errors.New("error connecting to device"))
-						return
-					}
-
-					connectionResponseMessage := driverModel.CastConnectionResponse(connectionResponse.responseMessage)
-					// Save the communication channel id
-					m.CommunicationChannelId = connectionResponseMessage.CommunicationChannelId
-
-					// Reset the sequence counter
-					m.SequenceCounter = -1
-
-					// If the connection was successful, the gateway will now forward any packets
-					// on the KNX bus that are broadcast packets to us, so we have to setup things
-					// to handle these incoming messages.
-					switch connectionResponseMessage.Status {
-					case driverModel.Status_NO_ERROR:
-						// Save the KNX Address the Gateway assigned to us for this connection.
-						tunnelConnectionDataBlock :=
-							driverModel.CastConnectionResponseDataBlockTunnelConnection(
-								connectionResponseMessage.ConnectionResponseDataBlock)
-						m.ClientKnxAddress = tunnelConnectionDataBlock.KnxAddress
-
-						// Start a timer that sends connection-state requests every 60 seconds
-						m.connectionStateTimer = time.NewTicker(60 * time.Second)
-						m.quitConnectionStateTimer = make(chan struct{})
-						go func() {
-							for {
-								select {
-								case <-m.connectionStateTimer.C:
-									// We're using the connection-state-request as ping operation ...
-									ping := m.Ping()
-									select {
-									case pingResult := <-ping:
-										if pingResult.Err != nil {
-											// TODO: Do some error handling here ...
-											m.connectionStateTimer.Stop()
-										}
-									case <-time.After(m.defaultTtl * 2):
-										// Close the connection
-										m.Close()
-									}
-
-								// If externally a request to stop the timer was issued, stop the timer.
-								case <-m.quitConnectionStateTimer:
-									// TODO: Do some error handling here ...
-									m.connectionStateTimer.Stop()
-									return
-								}
-							}
-						}()
-
-						// Create a go routine to handle incoming tunneling-requests which haven't been
-						// handled by any other handler. This is where usually the GroupValueWrite messages
-						// are being handled.
-						go func() {
-							defaultIncomingMessageChannel := m.messageCodec.GetDefaultIncomingMessageChannel()
-							for {
-								incomingMessage := <-defaultIncomingMessageChannel
-								tunnelingRequest := driverModel.CastTunnelingRequest(incomingMessage)
-								if tunnelingRequest == nil {
-									tunnelingResponse := driverModel.CastTunnelingResponse(incomingMessage)
-									if tunnelingResponse != nil {
-										log.Warnf("Got an unhandled TunnelingResponse message %v\n", tunnelingResponse)
-									} else {
-										log.Warnf("Not a TunnelingRequest or TunnelingResponse message %v\n", incomingMessage)
-									}
-								} else {
-									if tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
-										log.Warnf("Not for this connection %v\n", tunnelingRequest)
-										continue
-									}
-
-									lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
-									if lDataInd != nil {
-										// Get APDU, source and target address
-										var apdu *driverModel.Apdu
-										var sourceAddress *driverModel.KnxAddress
-										lDataFrameData := driverModel.CastLDataFrameData(lDataInd.DataFrame)
-										if lDataFrameData != nil {
-											apdu = lDataFrameData.Apdu
-											sourceAddress = lDataFrameData.SourceAddress
-										} else {
-											lDataFrameDataExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
-											if lDataFrameDataExt != nil {
-												apdu = lDataFrameDataExt.Apdu
-												sourceAddress = lDataFrameDataExt.SourceAddress
-											}
-										}
-
-										// If this is not an APDU, there is no need to further handle it.
-										if apdu == nil {
-											continue
-										}
-
-										// If this is an incoming disconnect request, remove the device
-										// from the device connections, otherwise handle it as normal
-										// incoming message.
-										apduControlContainer := driverModel.CastApduControlContainer(apdu)
-										if apduControlContainer != nil {
-											disconnectApdu := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
-											if disconnectApdu != nil {
-												if m.DeviceConnections[*sourceAddress] != nil /* && m.ClientKnxAddress == Int8ArrayToKnxAddress(targetAddress)*/ {
-													// Remove the connection
-													delete(m.DeviceConnections, *sourceAddress)
-												}
-											}
-										} else {
-											m.handleIncomingTunnelingRequest(tunnelingRequest)
-										}
-									}
-								}
-							}
-						}()
-
-						// Fire the "connected" event
-						sendResult(m, nil)
-					case driverModel.Status_NO_MORE_CONNECTIONS:
-						sendResult(nil, errors.New("no more connections"))
-					}
-				case <-time.After(m.defaultTtl * 2):
-					log.Errorf("Timeout receiving connection response from gateway.")
-				}
-			}
-		case <-time.After(m.defaultTtl * 3):
-			log.Errorf("Timeout receiving search request from gateway.")
-		}
-	}()
-	return result
+    result := make(chan plc4go.PlcConnectionConnectResult)
+    sendResult := func(connection plc4go.PlcConnection, err error) {
+        result <- plc4go.NewPlcConnectionConnectResult(connection, err)
+    }
+
+    go func() {
+        err := m.messageCodec.Connect()
+        if err != nil {
+            sendResult(nil, errors.New("error opening connection"))
+            return
+        }
+
+        searchResponseChannel := m.sendGatewaySearchRequest()
+        select {
+        case searchResponse := <-searchResponseChannel:
+            if searchResponse.err != nil {
+                sendResult(nil, errors.New("error discovering device capabilities"))
+                return
+            }
+
+            searchResponseMessage := driverModel.CastSearchResponse(searchResponse.responseMessage)
+            // Save some important information
+            m.metadata.KnxMedium = searchResponseMessage.DibDeviceInfo.KnxMedium
+            m.metadata.GatewayName = string(bytes.Trim(utils.Int8ArrayToByteArray(
+                searchResponseMessage.DibDeviceInfo.DeviceFriendlyName), "\x00"))
+            m.GatewayKnxAddress = searchResponseMessage.DibDeviceInfo.KnxAddress
+            m.metadata.GatewayKnxAddress = KnxAddressToString(m.GatewayKnxAddress)
+            m.metadata.ProjectNumber = searchResponseMessage.DibDeviceInfo.ProjectInstallationIdentifier.ProjectNumber
+            m.metadata.InstallationNumber = searchResponseMessage.DibDeviceInfo.ProjectInstallationIdentifier.InstallationNumber
+            m.metadata.DeviceSerialNumber = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceSerialNumber
+            m.metadata.DeviceMulticastAddress = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceMulticastAddress.Addr
+            m.metadata.DeviceMacAddress = searchResponseMessage.DibDeviceInfo.KnxNetIpDeviceMacAddress.Addr
+            m.metadata.SupportedServices = []string{}
+            supportsTunneling := false
+            for _, serviceId := range searchResponseMessage.DibSuppSvcFamilies.ServiceIds {
+                m.metadata.SupportedServices = append(m.metadata.SupportedServices, serviceId.Child.GetTypeName())
+                // If this is an instance of the "tunneling", service, this connection supports tunneling
+                _, ok := serviceId.Child.(*driverModel.KnxNetIpTunneling)
+                if ok {
+                    supportsTunneling = true
+                    break
+                }
+            }
+
+            // If the current device supports tunneling, create a tunneling connection.
+            // Via this connection we then get access to the entire KNX network this Gateway is connected to.
+            if supportsTunneling {
+                // As soon as we got a successful search-response back, send a connection request.
+                connectionResponseChannel := m.sendGatewayConnectionRequest()
+                select {
+                case connectionResponse := <-connectionResponseChannel:
+                    if connectionResponse.err != nil {
+                        sendResult(nil, errors.New("error connecting to device"))
+                        return
+                    }
+
+                    connectionResponseMessage := driverModel.CastConnectionResponse(connectionResponse.responseMessage)
+                    // Save the communication channel id
+                    m.CommunicationChannelId = connectionResponseMessage.CommunicationChannelId
+
+                    // Reset the sequence counter
+                    m.SequenceCounter = -1
+
+                    // If the connection was successful, the gateway will now forward any packets
+                    // on the KNX bus that are broadcast packets to us, so we have to setup things
+                    // to handle these incoming messages.
+                    switch connectionResponseMessage.Status {
+                    case driverModel.Status_NO_ERROR:
+                        // Save the KNX Address the Gateway assigned to us for this connection.
+                        tunnelConnectionDataBlock :=
+                            driverModel.CastConnectionResponseDataBlockTunnelConnection(
+                                connectionResponseMessage.ConnectionResponseDataBlock)
+                        m.ClientKnxAddress = tunnelConnectionDataBlock.KnxAddress
+
+                        // Start a timer that sends connection-state requests every 60 seconds
+                        m.connectionStateTimer = time.NewTicker(60 * time.Second)
+                        m.quitConnectionStateTimer = make(chan struct{})
+                        go func() {
+                            for {
+                                select {
+                                case <-m.connectionStateTimer.C:
+                                    // We're using the connection-state-request as ping operation ...
+                                    ping := m.Ping()
+                                    select {
+                                    case pingResult := <-ping:
+                                        if pingResult.Err != nil {
+                                            // TODO: Do some error handling here ...
+                                            m.connectionStateTimer.Stop()
+                                        }
+                                    case <-time.After(m.defaultTtl * 2):
+                                        // Close the connection
+                                        m.Close()
+                                    }
+
+                                // If externally a request to stop the timer was issued, stop the timer.
+                                case <-m.quitConnectionStateTimer:
+                                    // TODO: Do some error handling here ...
+                                    m.connectionStateTimer.Stop()
+                                    return
+                                }
+                            }
+                        }()
+
+                        // Create a go routine to handle incoming tunneling-requests which haven't been
+                        // handled by any other handler. This is where usually the GroupValueWrite messages
+                        // are being handled.
+                        go func() {
+                            defaultIncomingMessageChannel := m.messageCodec.GetDefaultIncomingMessageChannel()
+                            for {
+                                incomingMessage := <-defaultIncomingMessageChannel
+                                tunnelingRequest := driverModel.CastTunnelingRequest(incomingMessage)
+                                if tunnelingRequest == nil {
+                                    tunnelingResponse := driverModel.CastTunnelingResponse(incomingMessage)
+                                    if tunnelingResponse != nil {
+                                        log.Warnf("Got an unhandled TunnelingResponse message %v\n", tunnelingResponse)
+                                    } else {
+                                        log.Warnf("Not a TunnelingRequest or TunnelingResponse message %v\n", incomingMessage)
+                                    }
+                                } else {
+                                    if tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+                                        log.Warnf("Not for this connection %v\n", tunnelingRequest)
+                                        continue
+                                    }
+
+                                    lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+                                    if lDataInd != nil {
+                                        // Get APDU, source and target address
+                                        lDataFrameData := driverModel.CastLDataExtended(lDataInd.DataFrame)
+                                        sourceAddress := lDataFrameData.SourceAddress
+
+                                        // If this is not an APDU, there is no need to further handle it.
+                                        if lDataFrameData.Apdu == nil {
+                                            continue
+                                        }
+
+                                        // If this is an incoming disconnect request, remove the device
+                                        // from the device connections, otherwise handle it as normal
+                                        // incoming message.
+                                        apduControlContainer := driverModel.CastApduControlContainer(lDataFrameData.Apdu)
+                                        if apduControlContainer != nil {
+                                            disconnectApdu := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
+                                            if disconnectApdu != nil {
+                                                if m.DeviceConnections[*sourceAddress] != nil /* && m.ClientKnxAddress == Int8ArrayToKnxAddress(targetAddress)*/ {
+                                                    // Remove the connection
+                                                    delete(m.DeviceConnections, *sourceAddress)
+                                                }
+                                            }
+                                        } else {
+                                            m.handleIncomingTunnelingRequest(tunnelingRequest)
+                                        }
+                                    }
+                                }
+                            }
+                        }()
+
+                        // Fire the "connected" event
+                        sendResult(m, nil)
+                    case driverModel.Status_NO_MORE_CONNECTIONS:
+                        sendResult(nil, errors.New("no more connections"))
+                    }
+                case <-time.After(m.defaultTtl * 2):
+                    log.Debug("Timeout receiving connection response from gateway.")
+                }
+            }
+        case <-time.After(m.defaultTtl * 3):
+            log.Debug("Timeout receiving search request from gateway.")
+        }
+    }()
+    return result
 }
 
 func (m *KnxNetIpConnection) Close() <-chan plc4go.PlcConnectionCloseResult {
-	result := make(chan plc4go.PlcConnectionCloseResult)
-
-	go func() {
-		// Stop the connection-state checker.
-		m.connectionStateTimer.Stop()
-
-		// Disconnect from all knx devices we are still connected to.
-		for targetAddress := range m.DeviceConnections {
-			disconnects := m.DisconnectFromDevice(targetAddress)
-			select {
-			case _ = <-disconnects:
-			case <-time.After(m.defaultTtl * 2):
-				log.Errorf("Timeout disconnecting from device %s.", KnxAddressToString(&targetAddress))
-			}
-		}
-
-		// Send a disconnect request from the gateway.
-		disconnectionResponseChannel := m.sendGatewayDisconnectionRequest()
-		select {
-		case disconnectResponse := <-disconnectionResponseChannel:
-			if disconnectResponse.err != nil {
-				result <- plc4go.NewPlcConnectionCloseResult(m, errors.New(
-					fmt.Sprintf("got an error when disconnecting: %s", disconnectResponse.err.Error())))
-			} else {
-				result <- plc4go.NewPlcConnectionCloseResult(m, nil)
-			}
-
-		case <-time.After(m.defaultTtl * 2):
-			log.Errorf("Timeout disconnecting from gateway.")
-			result <- plc4go.NewPlcConnectionCloseResult(m, nil)
-		}
-	}()
-
-	return result
+    result := make(chan plc4go.PlcConnectionCloseResult)
+
+    go func() {
+        // Stop the connection-state checker.
+        m.connectionStateTimer.Stop()
+
+        // Disconnect from all knx devices we are still connected to.
+        for targetAddress := range m.DeviceConnections {
+            disconnects := m.DisconnectFromDevice(targetAddress)
+            select {
+            case _ = <-disconnects:
+            case <-time.After(m.defaultTtl * 2):
+                log.Debugf("Timeout disconnecting from device %s.", KnxAddressToString(&targetAddress))
+            }
+        }
+
+        // Send a disconnect request from the gateway.
+        disconnectionResponseChannel := m.sendGatewayDisconnectionRequest()
+        select {
+        case disconnectResponse := <-disconnectionResponseChannel:
+            if disconnectResponse.err != nil {
+                result <- plc4go.NewPlcConnectionCloseResult(m, errors.New(
+                    fmt.Sprintf("got an error when disconnecting: %s", disconnectResponse.err.Error())))
+            } else {
+                result <- plc4go.NewPlcConnectionCloseResult(m, nil)
+            }
+
+        case <-time.After(m.defaultTtl * 2):
+            log.Debug("Timeout disconnecting from gateway.")
+            result <- plc4go.NewPlcConnectionCloseResult(m, nil)
+        }
+    }()
+
+    return result
 }
 
 func (m *KnxNetIpConnection) IsConnected() bool {
-	if m.messageCodec != nil {
-		pingChannel := m.Ping()
-		select {
-		case pingResponse := <-pingChannel:
-			return pingResponse.Err == nil
-		case <-time.After(m.defaultTtl * 2):
-			log.Errorf("Timeout checking if the connection is alive.")
-			return false
-		}
-	}
-	return false
+    if m.messageCodec != nil {
+        pingChannel := m.Ping()
+        select {
+        case pingResponse := <-pingChannel:
+            return pingResponse.Err == nil
+        case <-time.After(m.defaultTtl * 2):
+            log.Debug("Timeout checking if the connection is alive.")
+            return false
+        }
+    }
+    return false
 }
 
 func (m *KnxNetIpConnection) Ping() <-chan plc4go.PlcConnectionPingResult {
-	result := make(chan plc4go.PlcConnectionPingResult)
-
-	go func() {
-		// Send the connection state request
-		connectionStateResponseChannel := m.sendConnectionStateRequest()
-		select {
-		case connectionStateResponse := <-connectionStateResponseChannel:
-			if connectionStateResponse.err != nil {
-				result <- plc4go.NewPlcConnectionPingResult(errors.New(
-					fmt.Sprintf("got an error: %s", connectionStateResponse.err.Error())))
-			} else {
-				result <- plc4go.NewPlcConnectionPingResult(nil)
-			}
-		case <-time.After(m.defaultTtl * 2):
-			log.Errorf("Timeout executing connection-state-request.")
-		}
-		return
-	}()
-
-	return result
+    result := make(chan plc4go.PlcConnectionPingResult)
+
+    go func() {
+        // Send the connection state request
+        connectionStateResponseChannel := m.sendConnectionStateRequest()
+        select {
+        case connectionStateResponse := <-connectionStateResponseChannel:
+            if connectionStateResponse.err != nil {
+                result <- plc4go.NewPlcConnectionPingResult(errors.New(
+                    fmt.Sprintf("got an error: %s", connectionStateResponse.err.Error())))
+            } else {
+                result <- plc4go.NewPlcConnectionPingResult(nil)
+            }
+        case <-time.After(m.defaultTtl * 2):
+            log.Debug("Timeout executing connection-state-request.")
+        }
+        return
+    }()
+
+    return result
 }
 
 func (m *KnxNetIpConnection) ConnectToDevice(targetAddress driverModel.KnxAddress) <-chan *KnxDeviceConnection {
-	result := make(chan *KnxDeviceConnection)
-
-	// If we're already connected, use that connection instead.
-	if connection, ok := m.DeviceConnections[targetAddress]; ok {
-		result <- connection
-		return result
-	}
-
-	// First send a connection request
-	go func() {
-		controlConnectResponseChannel := m.sendDeviceConnectionRequest(targetAddress)
-		select {
-		case conResult := <-controlConnectResponseChannel:
-			if conResult.err != nil {
-				result <- nil
-				return
-			}
-
-			// If the connection request was successful, try to read the device-descriptor
-			deviceDescriptorResponses := m.sendDeviceDeviceDescriptorReadRequest(targetAddress)
-			go func() {
-				select {
-				case _ = <-deviceDescriptorResponses:
-					// Last, not least, read the max APDU size
-					propertyValueResponses := m.sendDevicePropertyReadRequest(targetAddress, 0, 56)
-					go func() {
-						select {
-						case propertyValueResponse := <-propertyValueResponses:
-							// If we were able to read the max APDU size, then use the minimum of
-							// the connection APDU size and the device APDU size, otherwise use the
-							// default APDU Size of 15
-							// Defined in: 03_05_01 Resources v01.09.03 AS Page 40
-							deviceApduSize := uint16(15)
-							if propertyValueResponse.returnCode == apiModel.PlcResponseCode_OK {
-								deviceApduSize = (*propertyValueResponse.value).GetUint16()
-							}
-							connection := m.DeviceConnections[targetAddress]
-							connection.maxApdu = uint16(math.Min(float64(deviceApduSize), 240))
-							result <- connection
-						case <-time.After(m.defaultTtl * 2):
-							result <- nil
-							log.Errorf("Timeout receiving property read response from device: %s", KnxAddressToString(&targetAddress))
-						}
-					}()
-				}
-			}()
-		case <-time.After(m.defaultTtl * 3):
-			result <- nil
-			log.Errorf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
-		}
-	}()
-
-	return result
+    result := make(chan *KnxDeviceConnection)
+
+    // If we're already connected, use that connection instead.
+    if connection, ok := m.DeviceConnections[targetAddress]; ok {
+        result <- connection
+        return result
+    }
+
+    // First send a connection request
+    go func() {
+        controlConnectResponseChannel := m.sendDeviceConnectionRequest(targetAddress)
+        select {
+        case conResult := <-controlConnectResponseChannel:
+            if conResult.err != nil {
+                result <- nil
+                return
+            }
+
+            // If the connection request was successful, try to read the device-descriptor
+            deviceDescriptorResponses := m.sendDeviceDeviceDescriptorReadRequest(targetAddress)
+            go func() {
+                select {
+                case _ = <-deviceDescriptorResponses:
+                    // Last, not least, read the max APDU size
+                    propertyValueResponses := m.sendDevicePropertyReadRequest(targetAddress, 0, 56)
+                    go func() {
+                        select {
+                        case propertyValueResponse := <-propertyValueResponses:
+                            // If we were able to read the max APDU size, then use the minimum of
+                            // the connection APDU size and the device APDU size, otherwise use the
+                            // default APDU Size of 15
+                            // Defined in: 03_05_01 Resources v01.09.03 AS Page 40
+                            deviceApduSize := uint16(15)
+                            if propertyValueResponse.returnCode == apiModel.PlcResponseCode_OK {
+                                deviceApduSize = (*propertyValueResponse.value).GetUint16()
+                            }
+                            connection := m.DeviceConnections[targetAddress]
+                            connection.maxApdu = uint16(math.Min(float64(deviceApduSize), 240))
+                            result <- connection
+                        case <-time.After(m.defaultTtl * 2):
+                            result <- nil
+                            log.Debugf("Timeout receiving property read response from device: %s", KnxAddressToString(&targetAddress))
+                        }
+                    }()
+                }
+            }()
+        case <-time.After(m.defaultTtl * 3):
+            result <- nil
+            log.Debugf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
+        }
+    }()
+
+    return result
 }
 
 func (m *KnxNetIpConnection) DisconnectFromDevice(targetAddress driverModel.KnxAddress) <-chan *KnxDeviceConnection {
-	result := make(chan *KnxDeviceConnection)
-
-	if connection, ok := m.DeviceConnections[targetAddress]; ok {
-		disconnects := m.sendDeviceDisconnectionRequest(targetAddress)
-		go func() {
-			select {
-			case _ = <-disconnects:
-				// Remove the connection from the list.
-				delete(m.DeviceConnections, targetAddress)
-				result <- connection
-			case <-time.After(m.defaultTtl * 2):
-				log.Errorf("Timeout disconnecting from device: %s", KnxAddressToString(&targetAddress))
-			}
-		}()
-	} else {
-		result <- nil
-	}
-
-	return result
+    result := make(chan *KnxDeviceConnection)
+
+    if connection, ok := m.DeviceConnections[targetAddress]; ok {
+        disconnects := m.sendDeviceDisconnectionRequest(targetAddress)
+        go func() {
+            select {
+            case _ = <-disconnects:
+                // Remove the connection from the list.
+                delete(m.DeviceConnections, targetAddress)
+                result <- connection
+            case <-time.After(m.defaultTtl * 2):
+                log.Debugf("Timeout disconnecting from device: %s", KnxAddressToString(&targetAddress))
+            }
+        }()
+    } else {
+        result <- nil
+    }
+
+    return result
 }
 
 func (m *KnxNetIpConnection) ReadDeviceProperty(targetAddress driverModel.KnxAddress, objectId uint8, propertyId uint8) <-chan KnxReadResult {
-	result := make(chan KnxReadResult)
-
-	go func() {
-		// Check if there is already a connection available,
-		// if not, create a new one.
-		connection, ok := m.DeviceConnections[targetAddress]
-		if !ok {
-			connections := m.ConnectToDevice(targetAddress)
-			select {
-			case connection = <-connections:
-				// If we didn't get a connect, abort
-				if connection == nil {
-					result <- KnxReadResult{
-						returnCode: apiModel.PlcResponseCode_INVALID_ADDRESS,
-						value:      nil,
-					}
-				}
-			case <-time.After(m.defaultTtl * 2):
-				log.Errorf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
-			}
-		}
-
-		// If we successfully got a connection, read the property
-		if connection != nil {
-			propertyValueResponses := m.sendDevicePropertyReadRequest(targetAddress, objectId, propertyId)
-			select {
-			case propertyValueResponse := <-propertyValueResponses:
-				result <- propertyValueResponse
-			case <-time.After(m.defaultTtl * 2):
-				log.Errorf("Timeout reading device property from device: %s: ObjectId %d, PropertyId %d",
-					KnxAddressToString(&targetAddress), objectId, propertyId)
-			}
-		}
-	}()
-
-	return result
+    result := make(chan KnxReadResult)
+
+    go func() {
+        // Check if there is already a connection available,
+        // if not, create a new one.
+        connection, ok := m.DeviceConnections[targetAddress]
+        if !ok {
+            connections := m.ConnectToDevice(targetAddress)
+            select {
+            case connection = <-connections:
+                // If we didn't get a connect, abort
+                if connection == nil {
+                    result <- KnxReadResult{
+                        returnCode: apiModel.PlcResponseCode_INVALID_ADDRESS,
+                        value:      nil,
+                    }
+                }
+            case <-time.After(m.defaultTtl * 2):
+                log.Debugf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
+            }
+        }
+
+        // If we successfully got a connection, read the property
+        if connection != nil {
+            propertyValueResponses := m.sendDevicePropertyReadRequest(targetAddress, objectId, propertyId)
+            select {
+            case propertyValueResponse := <-propertyValueResponses:
+                result <- propertyValueResponse
+            case <-time.After(m.defaultTtl * 2):
+                log.Debugf("Timeout reading device property from device: %s: ObjectId %d, PropertyId %d",
+                    KnxAddressToString(&targetAddress), objectId, propertyId)
+            }
+        }
+    }()
+
+    return result
 }
 
 func (m *KnxNetIpConnection) ReadDeviceMemory(targetAddress driverModel.KnxAddress, address uint16, numElements uint8, datapointType *driverModel.KnxDatapointType) <-chan KnxReadResult {
-	result := make(chan KnxReadResult)
-
-	if datapointType == nil {
-		// This is a 0-255 valued 1-byte value.
-		dpt := driverModel.KnxDatapointType_DPT_DecimalFactor
-		datapointType = &dpt
-	}
-
-	go func() {
-		// Check if there is already a connection available,
-		// if not, create a new one.
-		connection, ok := m.DeviceConnections[targetAddress]
-		if !ok {
-			connections := m.ConnectToDevice(targetAddress)
-			select {
-			case connection = <-connections:
-				// If we didn't get a connect, abort
-				if connection == nil {
-					result <- KnxReadResult{
-						returnCode: apiModel.PlcResponseCode_INVALID_ADDRESS,
-						value:      nil,
-					}
-				}
-			case <-time.After(m.defaultTtl * 2):
-				log.Errorf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
-			}
-		}
-
-		// If we successfully got a connection, read the property
-		if connection != nil {
-			// Depending on the gateway Max APDU and the device Max APDU, split this up into multiple requests.
-			// An APDU starts with the last 6 bits of the first data byte containing the count
-			// followed by the 16-bit address, so these are already used.
-			maxNumBytes := uint8(math.Min(float64(connection.maxApdu-3), float64(63)))
-			elementSize := datapointType.DatapointMainType().SizeInBits() / 8
-			maxNumElementsPerRequest := uint8(math.Floor(float64(maxNumBytes / elementSize)))
-			maxNumBytesPerRequest := maxNumElementsPerRequest * elementSize
-			remainingRequestElements := numElements
-			curStartingAddress := address
-			var fragments []KnxMemoryReadFragment
-			for remainingRequestElements > maxNumElementsPerRequest {
-				fragment := KnxMemoryReadFragment{
-					numElements:     maxNumElementsPerRequest,
-					startingAddress: curStartingAddress,
-				}
-				fragments = append(fragments, fragment)
-				remainingRequestElements = remainingRequestElements - maxNumElementsPerRequest
-				curStartingAddress = curStartingAddress + uint16(maxNumBytesPerRequest)
-			}
-			fragment := KnxMemoryReadFragment{
-				numElements:     remainingRequestElements,
-				startingAddress: curStartingAddress,
-			}
-			fragments = append(fragments, fragment)
-
-			var results []KnxReadResult
-			for _, fragment := range fragments {
-				propertyValueResponses := m.sendDeviceMemoryReadRequest(targetAddress, fragment.startingAddress, fragment.numElements, *datapointType)
-				select {
-				case propertyValueResponse := <-propertyValueResponses:
-					results = append(results, propertyValueResponse)
-				case <-time.After(m.defaultTtl * 2):
-					log.Errorf("Timeout reading device memory from device: %s: address %d, number of elements %d of type %s",
-						KnxAddressToString(&targetAddress), address, numElements, datapointType.Name())
-				}
-			}
-			if len(results) > 1 {
-				returnCode := apiModel.PlcResponseCode_OK
-				// Merge the results
-				var plcValues []values.PlcValue
-				for _, curResult := range results {
-					if curResult.returnCode > returnCode {
-						returnCode = curResult.returnCode
-					}
-					plcValues = append(plcValues, (*(curResult.value)).GetList()...)
-				}
-				var plcList values.PlcValue
-				plcList = values2.NewPlcList(plcValues)
-				result <- KnxReadResult{
-					returnCode: returnCode,
-					value:      &plcList,
-				}
-			} else if len(results) == 1 {
-				result <- results[0]
-			}
-		}
-	}()
-
-	return result
+    result := make(chan KnxReadResult)
+
+    if datapointType == nil {
+        // This is a 0-255 valued 1-byte value.
+        dpt := driverModel.KnxDatapointType_DPT_DecimalFactor
+        datapointType = &dpt
+    }
+
+    go func() {
+        // Check if there is already a connection available,
+        // if not, create a new one.
+        connection, ok := m.DeviceConnections[targetAddress]
+        if !ok {
+            connections := m.ConnectToDevice(targetAddress)
+            select {
+            case connection = <-connections:
+                // If we didn't get a connect, abort
+                if connection == nil {
+                    result <- KnxReadResult{
+                        returnCode: apiModel.PlcResponseCode_INVALID_ADDRESS,
+                        value:      nil,
+                    }
+                }
+            case <-time.After(m.defaultTtl * 2):
+                log.Debugf("Timeout connecting to device: %s", KnxAddressToString(&targetAddress))
+            }
+        }
+
+        // If we successfully got a connection, read the property
+        if connection != nil {
+            // Depending on the gateway Max APDU and the device Max APDU, split this up into multiple requests.
+            // An APDU starts with the last 6 bits of the first data byte containing the count
+            // followed by the 16-bit address, so these are already used.
+            maxNumBytes := uint8(math.Min(float64(connection.maxApdu-3), float64(63)))
+            elementSize := datapointType.DatapointMainType().SizeInBits() / 8
+            maxNumElementsPerRequest := uint8(math.Floor(float64(maxNumBytes / elementSize)))
+            maxNumBytesPerRequest := maxNumElementsPerRequest * elementSize
+            remainingRequestElements := numElements
+            curStartingAddress := address
+            var fragments []KnxMemoryReadFragment
+            for remainingRequestElements > maxNumElementsPerRequest {
+                fragment := KnxMemoryReadFragment{
+                    numElements:     maxNumElementsPerRequest,
+                    startingAddress: curStartingAddress,
+                }
+                fragments = append(fragments, fragment)
+                remainingRequestElements = remainingRequestElements - maxNumElementsPerRequest
+                curStartingAddress = curStartingAddress + uint16(maxNumBytesPerRequest)
+            }
+            fragment := KnxMemoryReadFragment{
+                numElements:     remainingRequestElements,
+                startingAddress: curStartingAddress,
+            }
+            fragments = append(fragments, fragment)
+
+            var results []KnxReadResult
+            for _, fragment := range fragments {
+                propertyValueResponses := m.sendDeviceMemoryReadRequest(targetAddress, fragment.startingAddress, fragment.numElements, *datapointType)
+                select {
+                case propertyValueResponse := <-propertyValueResponses:
+                    results = append(results, propertyValueResponse)
+                case <-time.After(m.defaultTtl * 4):
+                    log.Debugf("Timeout reading device memory from device: %s: address %d, number of elements %d of type %s",
+                        KnxAddressToString(&targetAddress), address, numElements, datapointType.Name())
+                }
+            }
+            if len(results) > 1 {
+                returnCode := apiModel.PlcResponseCode_OK
+                // Merge the results
+                var plcValues []values.PlcValue
+                for _, curResult := range results {
+                    if curResult.returnCode > returnCode {
+                        returnCode = curResult.returnCode
+                    }
+                    plcValues = append(plcValues, (*(curResult.value)).GetList()...)
+                }
+                var plcList values.PlcValue
+                plcList = values2.NewPlcList(plcValues)
+                result <- KnxReadResult{
+                    returnCode: returnCode,
+                    value:      &plcList,
+                }
+            } else if len(results) == 1 {
+                result <- results[0]
+            }
+        }
+    }()
+
+    return result
 }
 
 func (m *KnxNetIpConnection) GetMetadata() apiModel.PlcConnectionMetadata {
-	return m.metadata
+    return m.metadata
 }
 
 func (m *KnxNetIpConnection) ReadRequestBuilder() apiModel.PlcReadRequestBuilder {
-	return internalModel.NewDefaultPlcReadRequestBuilder(
-		m.fieldHandler, NewKnxNetIpReader(m))
+    return internalModel.NewDefaultPlcReadRequestBuilder(
+        m.fieldHandler, NewKnxNetIpReader(m))
 }
 
 func (m *KnxNetIpConnection) WriteRequestBuilder() apiModel.PlcWriteRequestBuilder {
-	return internalModel.NewDefaultPlcWriteRequestBuilder(
-		m.fieldHandler, m.valueHandler, NewKnxNetIpWriter(m.messageCodec))
+    return internalModel.NewDefaultPlcWriteRequestBuilder(
+        m.fieldHandler, m.valueHandler, NewKnxNetIpWriter(m.messageCodec))
 }
 
 func (m *KnxNetIpConnection) SubscriptionRequestBuilder() apiModel.PlcSubscriptionRequestBuilder {
-	return internalModel.NewDefaultPlcSubscriptionRequestBuilder(
-		m.fieldHandler, m.valueHandler, NewKnxNetIpSubscriber(m))
+    return internalModel.NewDefaultPlcSubscriptionRequestBuilder(
+        m.fieldHandler, m.valueHandler, NewKnxNetIpSubscriber(m))
 }
 
 func (m *KnxNetIpConnection) BrowseRequestBuilder() apiModel.PlcBrowseRequestBuilder {
-	return internalModel.NewDefaultPlcBrowseRequestBuilder(NewKnxNetIpBrowser(m, m.messageCodec))
+    return internalModel.NewDefaultPlcBrowseRequestBuilder(NewKnxNetIpBrowser(m, m.messageCodec))
 }
 
 func (m *KnxNetIpConnection) UnsubscriptionRequestBuilder() apiModel.PlcUnsubscriptionRequestBuilder {
-	return nil /*internalModel.NewDefaultPlcUnsubscriptionRequestBuilder(
-	  m.fieldHandler, m.valueHandler, NewKnxNetIpSubscriber(m.messageCodec))*/
+    return nil /*internalModel.NewDefaultPlcUnsubscriptionRequestBuilder(
+      m.fieldHandler, m.valueHandler, NewKnxNetIpSubscriber(m.messageCodec))*/
 }
 
 func (m *KnxNetIpConnection) GetTransportInstance() transports.TransportInstance {
-	if mc, ok := m.messageCodec.(spi.TransportInstanceExposer); ok {
-		return mc.GetTransportInstance()
-	}
-	return nil
+    if mc, ok := m.messageCodec.(spi.TransportInstanceExposer); ok {
+        return mc.GetTransportInstance()
+    }
+    return nil
 }
 
 func (m *KnxNetIpConnection) GetPlcFieldHandler() spi.PlcFieldHandler {
-	return m.fieldHandler
+    return m.fieldHandler
 }
 
 func (m *KnxNetIpConnection) GetPlcValueHandler() spi.PlcValueHandler {
-	return m.valueHandler
+    return m.valueHandler
 }
 
 func (m *KnxNetIpConnection) send(request *driverModel.KnxNetIpMessage) error {
-	// If this is a tunneling request, we need to update the communicationChannelId and assign a sequenceCounter
-	tunnelingRequest := driverModel.CastTunnelingRequest(request)
-	if tunnelingRequest != nil {
-		tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId = m.CommunicationChannelId
-		tunnelingRequest.TunnelingRequestDataBlock.SequenceCounter = m.getNewSequenceCounter()
-	}
-	return m.messageCodec.Send(request)
+    // If this is a tunneling request, we need to update the communicationChannelId and assign a sequenceCounter
+    tunnelingRequest := driverModel.CastTunnelingRequest(request)
+    if tunnelingRequest != nil {
+        tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId = m.CommunicationChannelId
+        tunnelingRequest.TunnelingRequestDataBlock.SequenceCounter = m.getNewSequenceCounter()
+    }
+    return m.messageCodec.Send(request)
 }
 
 func (m *KnxNetIpConnection) sendGatewaySearchRequest() chan InternalResult {
-	result := make(chan InternalResult)
-	localAddress, err := m.getLocalAddress()
-	if err != nil {
-		//		close(result)
-		return result
-	}
-	localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP))
-	discoveryEndpoint := driverModel.NewHPAIDiscoveryEndpoint(
-		driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port))
-	searchRequest := driverModel.NewSearchRequest(discoveryEndpoint)
-	err = m.messageCodec.SendRequest(searchRequest,
-		func(message interface{}) bool {
-			searchResponse := driverModel.CastSearchResponse(message)
-			return searchResponse != nil
-		},
-		func(message interface{}) error {
-			searchResponse := driverModel.CastSearchResponse(message)
-			result <- InternalResult{
-				responseMessage: *searchResponse,
-			}
-			//			close(result)
-			return nil
-		},
-		func(err error) error {
-			result <- InternalResult{
-				err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
-			}
-			return nil
-		},
-		m.defaultTtl)
-	if err != nil {
-		result <- InternalResult{
-			err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
-		}
-	}
-	return result
+    result := make(chan InternalResult)
+    localAddress, err := m.getLocalAddress()
+    if err != nil {
+        //		close(result)
+        return result
+    }
+    localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP))
+    discoveryEndpoint := driverModel.NewHPAIDiscoveryEndpoint(
+        driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port))
+    searchRequest := driverModel.NewSearchRequest(discoveryEndpoint)
+    err = m.messageCodec.SendRequest(searchRequest,
+        func(message interface{}) bool {
+            searchResponse := driverModel.CastSearchResponse(message)
+            return searchResponse != nil
+        },
+        func(message interface{}) error {
+            searchResponse := driverModel.CastSearchResponse(message)
+            result <- InternalResult{
+                responseMessage: *searchResponse,
+            }
+            //			close(result)
+            return nil
+        },
+        func(err error) error {
+            result <- InternalResult{
+                err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+            }
+            return nil
+        },
+        m.defaultTtl)
+    if err != nil {
+        result <- InternalResult{
+            err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+        }
+    }
+    return result
 }
 
 func (m *KnxNetIpConnection) sendGatewayConnectionRequest() chan InternalResult {
-	result := make(chan InternalResult)
-	localAddress, err := m.getLocalAddress()
-	if err != nil {
-		//		close(result)
-		result <- InternalResult{
-			err: err,
-		}
-		return result
-	}
-	localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
-	connectionRequest := driverModel.NewConnectionRequest(
-		driverModel.NewHPAIDiscoveryEndpoint(driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port)),
-		driverModel.NewHPAIDataEndpoint(driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port)),
-		driverModel.NewConnectionRequestInformationTunnelConnection(driverModel.KnxLayer_TUNNEL_LINK_LAYER),
-	)
-	err = m.messageCodec.SendRequest(connectionRequest,
-		func(message interface{}) bool {
-			connectionResponse := driverModel.CastConnectionResponse(message)
-			return connectionResponse != nil
-		},
-		func(message interface{}) error {
-			connectionResponse := driverModel.CastConnectionResponse(message)
-			result <- InternalResult{
-				responseMessage: *connectionResponse,
-			}
-			//			close(result)
-			return nil
-		},
-		func(err error) error {
-			result <- InternalResult{
-				err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
-			}
-			return nil
-		},
-		m.defaultTtl)
-	if err != nil {
-		result <- InternalResult{
-			err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
-		}
-	}
-	return result
+    result := make(chan InternalResult)
+    localAddress, err := m.getLocalAddress()
+    if err != nil {
+        //		close(result)
+        result <- InternalResult{
+            err: err,
+        }
+        return result
+    }
+    localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
+    connectionRequest := driverModel.NewConnectionRequest(
+        driverModel.NewHPAIDiscoveryEndpoint(driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port)),
+        driverModel.NewHPAIDataEndpoint(driverModel.HostProtocolCode_IPV4_UDP, localAddr, uint16(localAddress.Port)),
+        driverModel.NewConnectionRequestInformationTunnelConnection(driverModel.KnxLayer_TUNNEL_LINK_LAYER),
+    )
+    err = m.messageCodec.SendRequest(connectionRequest,
+        func(message interface{}) bool {
+            connectionResponse := driverModel.CastConnectionResponse(message)
+            return connectionResponse != nil
+        },
+        func(message interface{}) error {
+            connectionResponse := driverModel.CastConnectionResponse(message)
+            result <- InternalResult{
+                responseMessage: *connectionResponse,
+            }
+            //			close(result)
+            return nil
+        },
+        func(err error) error {
+            result <- InternalResult{
+                err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+            }
+            return nil
+        },
+        m.defaultTtl)
+    if err != nil {
+        result <- InternalResult{
+            err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+        }
+    }
+    return result
 }
 
 func (m *KnxNetIpConnection) sendGatewayDisconnectionRequest() chan InternalResult {
-	result := make(chan InternalResult)
-	localAddress, err := m.getLocalAddress()
-	if err != nil {
-		//		close(result)
-		return result
-	}
-	localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
-	disconnectRequest := driverModel.NewDisconnectRequest(
-		m.CommunicationChannelId,
-		driverModel.NewHPAIControlEndpoint(
-			driverModel.HostProtocolCode_IPV4_UDP,
-			localAddr,
-			uint16(localAddress.Port)))
-	err = m.messageCodec.SendRequest(disconnectRequest,
-		func(message interface{}) bool {
-			disconnectResponse := driverModel.CastDisconnectResponse(message)
-			return disconnectResponse != nil
-		},
-		func(message interface{}) error {
-			disconnectResponse := driverModel.CastDisconnectResponse(message)
-			result <- InternalResult{
-				responseMessage: *disconnectResponse,
-			}
-			//			close(result)
-			return nil
-		},
-		func(err error) error {
-			result <- InternalResult{
-				err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
-			}
-			return nil
-		},
-		m.defaultTtl)
-	if err != nil {
-		result <- InternalResult{
-			err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
-		}
-	}
-	return result
+    result := make(chan InternalResult)
+    localAddress, err := m.getLocalAddress()
+    if err != nil {
+        //		close(result)
+        return result
+    }
+    localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
+    disconnectRequest := driverModel.NewDisconnectRequest(
+        m.CommunicationChannelId,
+        driverModel.NewHPAIControlEndpoint(
+            driverModel.HostProtocolCode_IPV4_UDP,
+            localAddr,
+            uint16(localAddress.Port)))
+    err = m.messageCodec.SendRequest(disconnectRequest,
+        func(message interface{}) bool {
+            disconnectResponse := driverModel.CastDisconnectResponse(message)
+            return disconnectResponse != nil
+        },
+        func(message interface{}) error {
+            disconnectResponse := driverModel.CastDisconnectResponse(message)
+            result <- InternalResult{
+                responseMessage: *disconnectResponse,
+            }
+            //			close(result)
+            return nil
+        },
+        func(err error) error {
+            result <- InternalResult{
+                err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+            }
+            return nil
+        },
+        m.defaultTtl)
+    if err != nil {
+        result <- InternalResult{
+            err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+        }
+    }
+    return result
 }
 
 func (m *KnxNetIpConnection) sendConnectionStateRequest() chan InternalResult {
-	result := make(chan InternalResult)
-	localAddress, err := m.getLocalAddress()
-	if err != nil {
-		//		close(result)
-		return result
-	}
-	localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
-	connectionStateRequest := driverModel.NewConnectionStateRequest(
-		m.CommunicationChannelId,
-		driverModel.NewHPAIControlEndpoint(
-			driverModel.HostProtocolCode_IPV4_UDP,
-			localAddr, uint16(localAddress.Port)))
-	err = m.messageCodec.SendRequest(connectionStateRequest,
-		func(message interface{}) bool {
-			connectionStateResponse := driverModel.CastConnectionStateResponse(message)
-			return connectionStateResponse != nil
-		},
-		func(message interface{}) error {
-			connectionStateResponse := driverModel.CastConnectionStateResponse(message)
-			result <- InternalResult{
-				responseMessage: *connectionStateResponse,
-			}
-			//			close(result)
-			return nil
-		},
-		func(err error) error {
-			result <- InternalResult{
-				err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
-			}
-			return nil
-		},
-		m.defaultTtl)
-	if err != nil {
-		result <- InternalResult{
-			err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
-		}
-	}
-	return result
+    result := make(chan InternalResult)
+    localAddress, err := m.getLocalAddress()
+    if err != nil {
+        //		close(result)
+        return result
+    }
+    localAddr := driverModel.NewIPAddress(utils.ByteArrayToInt8Array(localAddress.IP)[len(localAddress.IP)-4:])
+    connectionStateRequest := driverModel.NewConnectionStateRequest(
+        m.CommunicationChannelId,
+        driverModel.NewHPAIControlEndpoint(
+            driverModel.HostProtocolCode_IPV4_UDP,
+            localAddr, uint16(localAddress.Port)))
+    err = m.messageCodec.SendRequest(connectionStateRequest,
+        func(message interface{}) bool {
+            connectionStateResponse := driverModel.CastConnectionStateResponse(message)
+            return connectionStateResponse != nil
+        },
+        func(message interface{}) error {
+            connectionStateResponse := driverModel.CastConnectionStateResponse(message)
+            result <- InternalResult{
+                responseMessage: *connectionStateResponse,
+            }
+            //			close(result)
+            return nil
+        },
+        func(err error) error {
+            result <- InternalResult{
+                err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+            }
+            return nil
+        },
+        m.defaultTtl)
+    if err != nil {
+        result <- InternalResult{
+            err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+        }
+    }
+    return result
 }
 
 func (m *KnxNetIpConnection) sendDeviceConnectionRequest(targetAddress driverModel.KnxAddress) chan InternalResult {
-	result := make(chan InternalResult)
-
-	go func() {
-		// Send a connection request to the individual KNX device
-		deviceConnectionRequest := driverModel.NewTunnelingRequest(
-			driverModel.NewTunnelingRequestDataBlock(0, 0),
-			driverModel.NewLDataReq(0, nil,
-				driverModel.NewLDataFrameDataExt(false, 6, uint8(0),
-					driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
-					driverModel.NewApduControlContainer(driverModel.NewApduControlConnect(), 0, false, 0),
-					true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
-		err := m.sendRequest(
-			deviceConnectionRequest,
-			// The Gateway is now supposed to send an Ack to this request.
-			func(message interface{}) bool {
-				tunnelingRequest := driverModel.CastTunnelingRequest(message)
-				if tunnelingRequest == nil ||
-					tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
-					return false
-				}
-				lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
-				if lDataCon == nil {
-					return false
-				}
-				lDataFrameExt := driverModel.CastLDataFrameDataExt(lDataCon.DataFrame)
-				if lDataFrameExt == nil {
-					return false
-				}
-				// Check if the address matches
-				if *Int8ArrayToKnxAddress(lDataFrameExt.DestinationAddress) != targetAddress {
-					return false
-				}
-				apduControlContainer := driverModel.CastApduControlContainer(lDataFrameExt.Apdu)
-				if apduControlContainer == nil {
-					return false
-				}
-				apduControlConnect := driverModel.CastApduControlConnect(apduControlContainer.ControlApdu)
-				return apduControlConnect != nil
-			},
-			func(message interface{}) error {
-				tunnelingRequest := driverModel.CastTunnelingRequest(message)
-				lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
-				// If the error flag is set, there was an error connecting
-				if lDataCon.DataFrame.ErrorFlag {
-					result <- InternalResult{
-						err: errors.New("error flag set"),
-					}
-					return nil
-				}
-				lDataFrameExt := driverModel.CastLDataFrameDataExt(lDataCon.DataFrame)
-				apduControlContainer := driverModel.CastApduControlContainer(lDataFrameExt.Apdu)
-				apduControlConnect := driverModel.CastApduControlConnect(apduControlContainer.ControlApdu)
-
-				// Create a new connection object and save it in the map
-				deviceConnection := &KnxDeviceConnection{
-					counter: 0,
-					// I was told this value on the knx-forum.
-					// Seems the max payload is 3 bytes less ...
-					maxApdu: 0, // This is the default max APDU Size
-				}
-				m.DeviceConnections[targetAddress] = deviceConnection
-
-				result <- InternalResult{
-					responseMessage: apduControlConnect,
-				}
-				return nil
-			},
-			func(err error) error {
-				result <- InternalResult{
-					err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
-				}
-				return nil
-			},
-			m.defaultTtl)
-		if err != nil {
-			result <- InternalResult{
-				err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
-			}
-		}
-	}()
-
-	return result
+    result := make(chan InternalResult)
+
+    go func() {
+        // Send a connection request to the individual KNX device
+        deviceConnectionRequest := driverModel.NewTunnelingRequest(
+            driverModel.NewTunnelingRequestDataBlock(0, 0),
+            driverModel.NewLDataReq(0, nil,
+                driverModel.NewLDataExtended(false, 6, uint8(0),
+                    driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
+                    driverModel.NewApduControlContainer(driverModel.NewApduControlConnect(), false, 0),
+                    true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
+        err := m.sendRequest(
+            deviceConnectionRequest,
+            // The Gateway is now supposed to send an Ack to this request.
+            func(message interface{}) bool {
+                tunnelingRequest := driverModel.CastTunnelingRequest(message)
+                if tunnelingRequest == nil ||
+                    tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+                    return false
+                }
+                lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
+                if lDataCon == nil {
+                    return false
+                }
+                lDataFrameExt := driverModel.CastLDataExtended(lDataCon.DataFrame)
+                if lDataFrameExt == nil {
+                    return false
+                }
+                // Check if the address matches
+                if *Int8ArrayToKnxAddress(lDataFrameExt.DestinationAddress) != targetAddress {
+                    return false
+                }
+                apduControlContainer := driverModel.CastApduControlContainer(lDataFrameExt.Apdu)
+                if apduControlContainer == nil {
+                    return false
+                }
+                apduControlConnect := driverModel.CastApduControlConnect(apduControlContainer.ControlApdu)
+                return apduControlConnect != nil
+            },
+            func(message interface{}) error {
+                tunnelingRequest := driverModel.CastTunnelingRequest(message)
+                lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
+                // If the error flag is set, there was an error connecting
+                if lDataCon.DataFrame.ErrorFlag {
+                    result <- InternalResult{
+                        err: errors.New("error flag set"),
+                    }
+                    return nil
+                }
+                lDataFrameExt := driverModel.CastLDataExtended(lDataCon.DataFrame)
+                apduControlContainer := driverModel.CastApduControlContainer(lDataFrameExt.Apdu)
+                apduControlConnect := driverModel.CastApduControlConnect(apduControlContainer.ControlApdu)
+
+                // Create a new connection object and save it in the map
+                deviceConnection := &KnxDeviceConnection{
+                    counter: 0,
+                    // I was told this value on the knx-forum.
+                    // Seems the max payload is 3 bytes less ...
+                    maxApdu: 0, // This is the default max APDU Size
+                }
+                m.DeviceConnections[targetAddress] = deviceConnection
+
+                result <- InternalResult{
+                    responseMessage: apduControlConnect,
+                }
+                return nil
+            },
+            func(err error) error {
+                result <- InternalResult{
+                    err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+                }
+                return nil
+            },
+            m.defaultTtl)
+        if err != nil {
+            result <- InternalResult{
+                err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+            }
+        }
+    }()
+
+    return result
 }
 
 func (m *KnxNetIpConnection) sendDeviceDisconnectionRequest(targetAddress driverModel.KnxAddress) chan InternalResult {
-	result := make(chan InternalResult)
-
-	go func() {
-		// Send a connection request to the individual KNX device
-		deviceDisconnectionRequest := driverModel.NewTunnelingRequest(
-			driverModel.NewTunnelingRequestDataBlock(0, 0),
-			driverModel.NewLDataReq(0, nil,
-				driverModel.NewLDataFrameDataExt(false, 6, uint8(0),
-					driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
-					driverModel.NewApduControlContainer(driverModel.NewApduControlDisconnect(), 0, false, 0),
-					true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
-		err := m.sendRequest(
-			deviceDisconnectionRequest,
-			// The Gateway is now supposed to send an Ack to this request.
-			func(message interface{}) bool {
-				tunnelingRequest := driverModel.CastTunnelingRequest(message)
-				if tunnelingRequest == nil ||
-					tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
-					return false
-				}
-				lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
-				if lDataCon == nil {
-					return false
-				}
-				var apdu *driverModel.Apdu
-				var curTargetAddress *driverModel.KnxAddress
-				dataFrameExt := driverModel.CastLDataFrameDataExt(lDataCon.DataFrame)
-				if dataFrameExt != nil {
-					apdu = dataFrameExt.Apdu
-					curTargetAddress = Int8ArrayToKnxAddress(dataFrameExt.DestinationAddress)
-				} else {
-					dataFrame := driverModel.CastLDataFrameData(lDataCon.DataFrame)
-					if dataFrame != nil {
-						apdu = dataFrame.Apdu
-						curTargetAddress = Int8ArrayToKnxAddress(dataFrame.DestinationAddress)
-					} else {
-						return false
-					}
-				}
-				// Check if the address matches
-				if *curTargetAddress != targetAddress {
-					return false
-				}
-				apduControlContainer := driverModel.CastApduControlContainer(apdu)
-				if apduControlContainer == nil {
-					return false
-				}
-				apduControlDisconnect := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
-				return apduControlDisconnect != nil
-			},
-			func(message interface{}) error {
-				tunnelingRequest := driverModel.CastTunnelingRequest(message)
-				lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
-				// If the error flag is set, there was an error connecting
-				if lDataCon.DataFrame.ErrorFlag {
-					//					close(result)
-				}
-				var apdu *driverModel.Apdu
-				dataFrameExt := driverModel.CastLDataFrameDataExt(lDataCon.DataFrame)
-				if dataFrameExt != nil {
-					apdu = dataFrameExt.Apdu
-				} else {
-					dataFrame := driverModel.CastLDataFrameData(lDataCon.DataFrame)
-					if dataFrame != nil {
-						apdu = dataFrame.Apdu
-					}
-				}
-				apduControlContainer := driverModel.CastApduControlContainer(apdu)
-				apduControlDisconnect := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
-				result <- InternalResult{
-					responseMessage: *apduControlDisconnect,
-				}
-				return nil
-			},
-			func(err error) error {
-				result <- InternalResult{
-					err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
-				}
-				return nil
-			},
-			m.defaultTtl)
-		if err != nil {
-			result <- InternalResult{
-				err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
-			}
-		}
-	}()
-
-	return result
+    result := make(chan InternalResult)
+
+    go func() {
+        // Send a connection request to the individual KNX device
+        deviceDisconnectionRequest := driverModel.NewTunnelingRequest(
+            driverModel.NewTunnelingRequestDataBlock(0, 0),
+            driverModel.NewLDataReq(0, nil,
+                driverModel.NewLDataExtended(false, 6, uint8(0),
+                    driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
+                    driverModel.NewApduControlContainer(driverModel.NewApduControlDisconnect(), false, 0),
+                    true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
+        err := m.sendRequest(
+            deviceDisconnectionRequest,
+            // The Gateway is now supposed to send an Ack to this request.
+            func(message interface{}) bool {
+                tunnelingRequest := driverModel.CastTunnelingRequest(message)
+                if tunnelingRequest == nil ||
+                    tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+                    return false
+                }
+                lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
+                if lDataCon == nil {
+                    return false
+                }
+                dataFrameExt := driverModel.CastLDataExtended(lDataCon.DataFrame)
+                if dataFrameExt == nil {
+                    return false
+                }
+                curTargetAddress := Int8ArrayToKnxAddress(dataFrameExt.DestinationAddress)
+                // Check if the address matches
+                if *curTargetAddress != targetAddress {
+                    return false
+                }
+                apduControlContainer := driverModel.CastApduControlContainer(dataFrameExt.Apdu)
+                if apduControlContainer == nil {
+                    return false
+                }
+                apduControlDisconnect := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
+                return apduControlDisconnect != nil
+            },
+            func(message interface{}) error {
+                tunnelingRequest := driverModel.CastTunnelingRequest(message)
+                lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
+                // If the error flag is set, there was an error connecting
+                if lDataCon.DataFrame.ErrorFlag {
+                    //					close(result)
+                }
+                dataFrameExt := driverModel.CastLDataExtended(lDataCon.DataFrame)
+                apduControlContainer := driverModel.CastApduControlContainer(dataFrameExt.Apdu)
+                apduControlDisconnect := driverModel.CastApduControlDisconnect(apduControlContainer.ControlApdu)
+                result <- InternalResult{
+                    responseMessage: *apduControlDisconnect,
+                }
+                return nil
+            },
+            func(err error) error {
+                result <- InternalResult{
+                    err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+                }
+                return nil
+            },
+            m.defaultTtl)
+        if err != nil {
+            result <- InternalResult{
+                err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+            }
+        }
+    }()
+
+    return result
 }
 
 func (m *KnxNetIpConnection) sendDeviceDeviceDescriptorReadRequest(targetAddress driverModel.KnxAddress) chan InternalResult {
-	result := make(chan InternalResult)
-
-	connection, ok := m.DeviceConnections[targetAddress]
-	if !ok {
-		result <- InternalResult{
-			err: errors.New("not connected"),
-		}
-		return result
-	}
-
-	// Next, read the device descriptor so we know how we have to communicate with the device.
-	counter := connection.counter
-	connection.counter++
-	deviceDescriptorReadRequest := driverModel.NewTunnelingRequest(
-		driverModel.NewTunnelingRequestDataBlock(
-			// This is actually set in the KnxNetIpConnection.SendMessage method
-			0,
-			// This is actually set in the KnxNetIpConnection.SendMessage method
-			0),
-		driverModel.NewLDataReq(0, nil,
-			driverModel.NewLDataFrameDataExt(false, 6, uint8(0),
-				driverModel.NewKnxAddress(0, 0, 0),
-				KnxAddressToInt8Array(targetAddress),
-				driverModel.NewApduDataContainer(
-					driverModel.NewApduDataDeviceDescriptorRead(0), 1, true, counter),
-				true, true, driverModel.CEMIPriority_LOW, false, false)))
-	err := m.sendRequest(
-		deviceDescriptorReadRequest,
-		func(message interface{}) bool {
-			tunnelingRequest := driverModel.CastTunnelingRequest(message)
-			if tunnelingRequest == nil ||
-				tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
-				return false
-			}
-			lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
-			if lDataInd == nil {
-				return false
-			}
-			var apdu *driverModel.Apdu
-			var sourceAddress *driverModel.KnxAddress
-			dataFrameExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
-			if dataFrameExt != nil {
-				apdu = dataFrameExt.Apdu
-				sourceAddress = dataFrameExt.SourceAddress
-			} else {
-				dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
-				if dataFrame != nil {
-					apdu = dataFrame.Apdu
-					sourceAddress = dataFrame.SourceAddress
-				} else {
-					return false
-				}
-			}
-			// Check if the address matches
-			if *sourceAddress != targetAddress {
-				return false
-			}
-			// Check if the counter matches
-			if apdu.Counter != counter {
-				return false
-			}
-			dataContainer := driverModel.CastApduDataContainer(apdu)
-			if dataContainer == nil {
-				return false
-			}
-			deviceDescriptorResponse := driverModel.CastApduDataDeviceDescriptorResponse(dataContainer.DataApdu)
-			if deviceDescriptorResponse == nil {
-				return false
-			}
-			return true
-		},
-		func(message interface{}) error {
-			tunnelingRequest := driverModel.CastTunnelingRequest(message)
-			lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
-			dataFrame := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
-			dataContainer := driverModel.CastApduDataContainer(dataFrame.Apdu)
-			deviceDescriptorResponse := driverModel.CastApduDataDeviceDescriptorResponse(dataContainer.DataApdu)
-
-			// Send an ACK and wait for it to be delivered
-			go func() {
-				ackResults := m.sendDeviceAck(targetAddress, dataFrame.Apdu.Counter)
-				select {
-				case ackResult := <-ackResults:
-					if ackResult.err != nil {
-						fmt.Printf("Error sending Ack: %s", ackResult.err)
-					}
-				case <-time.After(m.defaultTtl * 2):
-					log.Errorf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
-				}
-
-				// Save the device-descriptor value
-				deviceDescriptor := uint16(deviceDescriptorResponse.Data[0])<<8 | uint16(deviceDescriptorResponse.Data[1])
-				connection.deviceDescriptor = deviceDescriptor
-
-				result <- InternalResult{
-					responseMessage: *deviceDescriptorResponse,
-				}
-			}()
-
-			return nil
-		},
-		func(err error) error {
-			result <- InternalResult{
-				err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
-			}
-			return nil
-		},
-		m.defaultTtl)
-	if err != nil {
-		result <- InternalResult{
-			err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
-		}
-	}
-	return result
+    result := make(chan InternalResult)
+
+    connection, ok := m.DeviceConnections[targetAddress]
+    if !ok {
+        result <- InternalResult{
+            err: errors.New("not connected"),
+        }
+        return result
+    }
+
+    // Next, read the device descriptor so we know how we have to communicate with the device.
+    counter := connection.counter
+    connection.counter++
+    deviceDescriptorReadRequest := driverModel.NewTunnelingRequest(
+        driverModel.NewTunnelingRequestDataBlock(
+            // This is actually set in the KnxNetIpConnection.SendMessage method
+            0,
+            // This is actually set in the KnxNetIpConnection.SendMessage method
+            0),
+        driverModel.NewLDataReq(0, nil,
+            driverModel.NewLDataExtended(false, 6, uint8(0),
+                driverModel.NewKnxAddress(0, 0, 0),
+                KnxAddressToInt8Array(targetAddress),
+                driverModel.NewApduDataContainer(
+                    driverModel.NewApduDataDeviceDescriptorRead(0), true, counter),
+                true, true, driverModel.CEMIPriority_LOW, false, false)))
+    err := m.sendRequest(
+        deviceDescriptorReadRequest,
+        func(message interface{}) bool {
+            tunnelingRequest := driverModel.CastTunnelingRequest(message)
+            if tunnelingRequest == nil ||
+                tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+                return false
+            }
+            lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+            if lDataInd == nil {
+                return false
+            }
+            dataFrameExt := driverModel.CastLDataExtended(lDataInd.DataFrame)
+            if dataFrameExt == nil {
+                return false
+            }
+            // Check if the address matches
+            if *dataFrameExt.SourceAddress != targetAddress {
+                return false
+            }
+            // Check if the counter matches
+            if dataFrameExt.Apdu.Counter != counter {
+                return false
+            }
+            dataContainer := driverModel.CastApduDataContainer(dataFrameExt.Apdu)
+            if dataContainer == nil {
+                return false
+            }
+            deviceDescriptorResponse := driverModel.CastApduDataDeviceDescriptorResponse(dataContainer.DataApdu)
+            if deviceDescriptorResponse == nil {
+                return false
+            }
+            return true
+        },
+        func(message interface{}) error {
+            tunnelingRequest := driverModel.CastTunnelingRequest(message)
+            lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+            dataFrame := driverModel.CastLDataExtended(lDataInd.DataFrame)
+            dataContainer := driverModel.CastApduDataContainer(dataFrame.Apdu)
+            deviceDescriptorResponse := driverModel.CastApduDataDeviceDescriptorResponse(dataContainer.DataApdu)
+
+            // Send an ACK and wait for it to be delivered
+            go func() {
+                ackResults := m.sendDeviceAck(targetAddress, dataFrame.Apdu.Counter)
+                select {
+                case ackResult := <-ackResults:
+                    if ackResult.err != nil {
+                        fmt.Printf("Error sending Ack: %s", ackResult.err)
+                    }
+                case <-time.After(m.defaultTtl * 2):
+                    log.Debugf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
+                }
+
+                // Save the device-descriptor value
+                deviceDescriptor := uint16(deviceDescriptorResponse.Data[0])<<8 | (uint16(deviceDescriptorResponse.Data[1]) & 0xFF)
+                connection.deviceDescriptor = deviceDescriptor
+
+                result <- InternalResult{
+                    responseMessage: *deviceDescriptorResponse,
+                }
+            }()
+
+            return nil
+        },
+        func(err error) error {
+            result <- InternalResult{
+                err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+            }
+            return nil
+        },
+        m.defaultTtl)
+    if err != nil {
+        result <- InternalResult{
+            err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+        }
+    }
+    return result
 }
 
 func (m *KnxNetIpConnection) sendDevicePropertyReadRequest(targetAddress driverModel.KnxAddress, objectId uint8, propertyId uint8) chan KnxReadResult {
-	result := make(chan KnxReadResult)
-
-	connection, ok := m.DeviceConnections[targetAddress]
-	if !ok {
-		//		close(result)
-		result <- KnxReadResult{
-			returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
-		}
-		return result
-	}
-
-	// Next, read the device descriptor so we know how we have to communicate with the device.
-	counter := connection.counter
-	connection.counter++
-
-	// Send the property read request and wait for a confirmation that this property is readable.
-	propertyReadRequest := driverModel.NewTunnelingRequest(
-		driverModel.NewTunnelingRequestDataBlock(0, 0),
-		driverModel.NewLDataReq(0, nil,
-			driverModel.NewLDataFrameDataExt(false, 6, 0,
-				driverModel.NewKnxAddress(0, 0, 0),
-				KnxAddressToInt8Array(targetAddress),
-				driverModel.NewApduDataContainer(
-					driverModel.NewApduDataOther(
-						driverModel.NewApduDataExtPropertyValueRead(objectId, propertyId, 1, 1)),
-					5, true, counter),
-				true, true, driverModel.CEMIPriority_LOW, false, false)))
-	err := m.sendRequest(
-		propertyReadRequest,
-		func(message interface{}) bool {
-			tunnelingRequest := driverModel.CastTunnelingRequest(message)
-			if tunnelingRequest == nil ||
-				tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
-				return false
-			}
-			lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
-			if lDataInd == nil {
-				return false
-			}
-			var apdu *driverModel.Apdu
-			var sourceAddress *driverModel.KnxAddress
-			dataFrameExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
-			if dataFrameExt != nil {
-				apdu = dataFrameExt.Apdu
-				sourceAddress = dataFrameExt.SourceAddress
-			} else {
-				dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
-				if dataFrame != nil {
-					apdu = dataFrame.Apdu
-					sourceAddress = dataFrame.SourceAddress
-				} else {
-					return false
-				}
-			}
-			// Check if the address matches
-			if *sourceAddress != targetAddress {
-				return false
-			}
-			// Check if the counter matches
-			if apdu.Counter != counter {
-				return false
-			}
-			dataContainer := driverModel.CastApduDataContainer(apdu)
-			if dataContainer == nil {
-				return false
-			}
-			dataApduOther := driverModel.CastApduDataOther(dataContainer.DataApdu)
-			if dataApduOther == nil {
-				return false
-			}
-			propertyValueResponse := driverModel.CastApduDataExtPropertyValueResponse(dataApduOther.ExtendedApdu)
-			if propertyValueResponse == nil {
-				return false
-			}
-			return propertyValueResponse.ObjectIndex == objectId && propertyValueResponse.PropertyId == propertyId
-		},
-		func(message interface{}) error {
-			tunnelingRequest := driverModel.CastTunnelingRequest(message)
-			lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
-			var apdu *driverModel.Apdu
-			dataFrameExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
-			if dataFrameExt != nil {
-				apdu = dataFrameExt.Apdu
-			} else {
-				dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
-				if dataFrame != nil {
-					apdu = dataFrame.Apdu
-				}
-			}
-			dataContainer := driverModel.CastApduDataContainer(apdu)
-			dataApduOther := driverModel.CastApduDataOther(dataContainer.DataApdu)
-			propertyValueResponse := driverModel.CastApduDataExtPropertyValueResponse(dataApduOther.ExtendedApdu)
-
-			// Send an ACK and wait for it to be delivered
-			go func() {
-				ackResults := m.sendDeviceAck(targetAddress, apdu.Counter)
-				select {
-				case ackResult := <-ackResults:
-					if ackResult.err != nil {
-						log.Errorf("Error sending Ack: %s", ackResult.err)
-					}
-				case <-time.After(m.defaultTtl * 2):
-					log.Errorf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
-				}
-
-				// If the count is 0, then this property doesn't exist or the user has no permission to read it.
-				if propertyValueResponse.Count == 0 {
-					result <- KnxReadResult{
-						returnCode: apiModel.PlcResponseCode_NOT_FOUND,
-					}
-				} else {
-					// Find out the type of the property
-					var objectType *driverModel.KnxInterfaceObjectType
-					for curObjectType := driverModel.KnxInterfaceObjectType_OT_UNKNOWN; curObjectType <= driverModel.KnxInterfaceObjectType_OT_SUNBLIND_SENSOR_BASIC; curObjectType++ {
-						if curObjectType.Code() == strconv.Itoa(int(objectId)) {
-							objectType = &curObjectType
-							break
-						}
-					}
-					property := driverModel.KnxInterfaceObjectProperty_PID_UNKNOWN
-					if objectType != nil {
-						for curProperty := driverModel.KnxInterfaceObjectProperty_PID_UNKNOWN; curProperty <= driverModel.KnxInterfaceObjectProperty_PID_SUNBLIND_SENSOR_BASIC_ENABLE_TOGGLE_MODE; curProperty++ {
-							if curProperty.PropertyId() == propertyId {
-								if curProperty.ObjectType() == driverModel.KnxInterfaceObjectType_OT_GENERAL || curProperty.ObjectType() == *objectType {
-									property = curProperty
-									break
-								}
-							}
-						}
-					}
-
-					dataLength := uint8(len(propertyValueResponse.Data))
-					data := propertyValueResponse.Data
-					rb := utils.NewReadBuffer(data)
-					plcValue, err := driverModel.KnxPropertyParse(rb, property.PropertyDataType(), dataLength)
-
-					// Return the result
-					if err != nil {
-						result <- KnxReadResult{
-							returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
-							value:      nil,
-						}
-					} else {
-						result <- KnxReadResult{
-							returnCode: apiModel.PlcResponseCode_OK,
-							value:      &plcValue,
-						}
-					}
-				}
-
-			}()
-			return nil
-		},
-		func(err error) error {
-			result <- KnxReadResult{
-				returnCode: apiModel.PlcResponseCode_RESPONSE_PENDING,
-			}
-			return nil
-		},
-		m.defaultTtl)
-
-	if err != nil {
-		//		close(result)
-	}
-	return result
+    result := make(chan KnxReadResult)
+
+    connection, ok := m.DeviceConnections[targetAddress]
+    if !ok {
+        //		close(result)
+        result <- KnxReadResult{
+            returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
+        }
+        return result
+    }
+
+    // Next, read the device descriptor so we know how we have to communicate with the device.
+    counter := connection.counter
+    connection.counter++
+
+    // Send the property read request and wait for a confirmation that this property is readable.
+    propertyReadRequest := driverModel.NewTunnelingRequest(
+        driverModel.NewTunnelingRequestDataBlock(0, 0),
+        driverModel.NewLDataReq(0, nil,
+            driverModel.NewLDataExtended(false, 6, 0,
+                driverModel.NewKnxAddress(0, 0, 0),
+                KnxAddressToInt8Array(targetAddress),
+                driverModel.NewApduDataContainer(
+                    driverModel.NewApduDataOther(
+                        driverModel.NewApduDataExtPropertyValueRead(objectId, propertyId, 1, 1)),
+                    true, counter),
+                true, true, driverModel.CEMIPriority_LOW, false, false)))
+    err := m.sendRequest(
+        propertyReadRequest,
+        func(message interface{}) bool {
+            tunnelingRequest := driverModel.CastTunnelingRequest(message)
+            if tunnelingRequest == nil ||
+                tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+                return false
+            }
+            lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+            if lDataInd == nil {
+                return false
+            }
+            dataFrameExt := driverModel.CastLDataExtended(lDataInd.DataFrame)
+            if dataFrameExt == nil {
+                return false
+            }
+            // Check if the address matches
+            if *dataFrameExt.SourceAddress != targetAddress {
+                return false
+            }
+            // Check if the counter matches
+            if dataFrameExt.Apdu.Counter != counter {
+                return false
+            }
+            dataContainer := driverModel.CastApduDataContainer(dataFrameExt.Apdu)
+            if dataContainer == nil {
+                return false
+            }
+            dataApduOther := driverModel.CastApduDataOther(dataContainer.DataApdu)
+            if dataApduOther == nil {
+                return false
+            }
+            propertyValueResponse := driverModel.CastApduDataExtPropertyValueResponse(dataApduOther.ExtendedApdu)
+            if propertyValueResponse == nil {
+                return false
+            }
+            return propertyValueResponse.ObjectIndex == objectId && propertyValueResponse.PropertyId == propertyId
+        },
+        func(message interface{}) error {
+            tunnelingRequest := driverModel.CastTunnelingRequest(message)
+            lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+            dataFrameExt := driverModel.CastLDataExtended(lDataInd.DataFrame)
+            dataContainer := driverModel.CastApduDataContainer(dataFrameExt.Apdu)
+            dataApduOther := driverModel.CastApduDataOther(dataContainer.DataApdu)
+            propertyValueResponse := driverModel.CastApduDataExtPropertyValueResponse(dataApduOther.ExtendedApdu)
+
+            // Send an ACK and wait for it to be delivered
+            go func() {
+                ackResults := m.sendDeviceAck(targetAddress, dataFrameExt.Apdu.Counter)
+                select {
+                case ackResult := <-ackResults:
+                    if ackResult.err != nil {
+                        log.Errorf("Error sending Ack: %s", ackResult.err)
+                    }
+                case <-time.After(m.defaultTtl * 2):
+                    log.Debugf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
+                }
+
+                // If the count is 0, then this property doesn't exist or the user has no permission to read it.
+                if propertyValueResponse.Count == 0 {
+                    result <- KnxReadResult{
+                        returnCode: apiModel.PlcResponseCode_NOT_FOUND,
+                    }
+                } else {
+                    // Find out the type of the property
+                    var objectType *driverModel.KnxInterfaceObjectType
+                    for curObjectType := driverModel.KnxInterfaceObjectType_OT_UNKNOWN; curObjectType <= driverModel.KnxInterfaceObjectType_OT_SUNBLIND_SENSOR_BASIC; curObjectType++ {
+                        if curObjectType.Code() == strconv.Itoa(int(objectId)) {
+                            objectType = &curObjectType
+                            break
+                        }
+                    }
+                    property := driverModel.KnxInterfaceObjectProperty_PID_UNKNOWN
+                    if objectType != nil {
+                        for curProperty := driverModel.KnxInterfaceObjectProperty_PID_UNKNOWN; curProperty <= driverModel.KnxInterfaceObjectProperty_PID_SUNBLIND_SENSOR_BASIC_ENABLE_TOGGLE_MODE; curProperty++ {
+                            if curProperty.PropertyId() == propertyId {
+                                if curProperty.ObjectType() == driverModel.KnxInterfaceObjectType_OT_GENERAL || curProperty.ObjectType() == *objectType {
+                                    property = curProperty
+                                    break
+                                }
+                            }
+                        }
+                    }
+
+                    dataLength := uint8(len(propertyValueResponse.Data))
+                    data := propertyValueResponse.Data
+                    rb := utils.NewReadBuffer(data)
+                    plcValue, err := driverModel.KnxPropertyParse(rb, property.PropertyDataType(), dataLength)
+
+                    // Return the result
+                    if err != nil {
+                        result <- KnxReadResult{
+                            returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
+                            value:      nil,
+                        }
+                    } else {
+                        result <- KnxReadResult{
+                            returnCode: apiModel.PlcResponseCode_OK,
+                            value:      &plcValue,
+                        }
+                    }
+                }
+
+            }()
+            return nil
+        },
+        func(err error) error {
+            result <- KnxReadResult{
+                returnCode: apiModel.PlcResponseCode_RESPONSE_PENDING,
+            }
+            return nil
+        },
+        m.defaultTtl)
+
+    if err != nil {
+        //		close(result)
+    }
+    return result
 }
 
 func (m *KnxNetIpConnection) sendDeviceMemoryReadRequest(targetAddress driverModel.KnxAddress, address uint16, numElements uint8, datapointType driverModel.KnxDatapointType) chan KnxReadResult {
-	result := make(chan KnxReadResult)
-
-	connection, ok := m.DeviceConnections[targetAddress]
-	if !ok {
-		//		close(result)
-		result <- KnxReadResult{
-			returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
-		}
-		return result
-	}
-
-	// Next, read the device descriptor so we know how we have to communicate with the device.
-	counter := connection.counter
-	connection.counter++
-	numBytes := numElements * uint8(math.Max(float64(1), float64(datapointType.DatapointMainType().SizeInBits()/8)))
-
-	// Send the property read request and wait for a confirmation that this property is readable.
-	propertyReadRequest := driverModel.NewTunnelingRequest(
-		driverModel.NewTunnelingRequestDataBlock(0, 0),
-		driverModel.NewLDataReq(0, nil,
-			driverModel.NewLDataFrameDataExt(false, 6, 0,
-				driverModel.NewKnxAddress(0, 0, 0),
-				KnxAddressToInt8Array(targetAddress),
-				driverModel.NewApduDataContainer(
-					driverModel.NewApduDataMemoryRead(numBytes, address),
-					3, true, counter),
-				true, true, driverModel.CEMIPriority_LOW, false, false)))
-	err := m.sendRequest(
-		propertyReadRequest,
-		func(message interface{}) bool {
-			tunnelingRequest := driverModel.CastTunnelingRequest(message)
-			if tunnelingRequest == nil ||
-				tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
-				return false
-			}
-			lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
-			if lDataInd == nil {
-				return false
-			}
-			var apdu *driverModel.Apdu
-			var sourceAddress *driverModel.KnxAddress
-			dataFrameExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
-			if dataFrameExt != nil {
-				apdu = dataFrameExt.Apdu
-				sourceAddress = dataFrameExt.SourceAddress
-			} else {
-				dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
-				if dataFrame != nil {
-					apdu = dataFrame.Apdu
-					sourceAddress = dataFrame.SourceAddress
-				} else {
-					return false
-				}
-			}
-			dataContainer := driverModel.CastApduDataContainer(apdu)
-			if dataContainer == nil {
-				return false
-			}
-			dataApduMemoryResponse := driverModel.CastApduDataMemoryResponse(dataContainer.DataApdu)
-			if dataApduMemoryResponse == nil {
-				return false
-			}
-
-			// Check if the address matches
-			if *sourceAddress != targetAddress {
-				return false
-			}
-			// Check if the counter matches
-			if apdu.Counter != counter {
-				return false
-			}
-			return dataApduMemoryResponse.Address == address && uint8(len(dataApduMemoryResponse.Data)) == numBytes
-		},
-		func(message interface{}) error {
-			tunnelingRequest := driverModel.CastTunnelingRequest(message)
-			lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
-			var apdu *driverModel.Apdu
-			dataFrameExt := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
-			if dataFrameExt != nil {
-				apdu = dataFrameExt.Apdu
-			} else {
-				dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
-				if dataFrame != nil {
-					apdu = dataFrame.Apdu
-				}
-			}
-			dataContainer := driverModel.CastApduDataContainer(apdu)
-			dataApduMemoryResponse := driverModel.CastApduDataMemoryResponse(dataContainer.DataApdu)
-
-			// Send an ACK and wait for it to be delivered
-			go func() {
-				ackResults := m.sendDeviceAck(targetAddress, apdu.Counter)
-				select {
-				case ackResult := <-ackResults:
-					if ackResult.err != nil {
-						log.Errorf("Error sending Ack: %s", ackResult.err)
-					}
-				case <-time.After(m.defaultTtl * 2):
-					log.Errorf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
-				}
-
-				// Parse the data according to the property type information
-				rb := utils.NewReadBuffer(dataApduMemoryResponse.Data)
-				var plcValues []values.PlcValue
-				for i := uint8(0); i < numElements; i++ {
-					plcValue, err := driverModel.KnxDatapointParse(rb, datapointType)
-					// Return the result
-					if err != nil {
-						result <- KnxReadResult{
-							returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
-							value:      nil,
-						}
-						return
-					} else {
-						plcValues = append(plcValues, plcValue)
-					}
-				}
-
-				// If this is a single value, just return that directly.
-				// If it's not, wrap it in a PlcList structure.
-				if len(plcValues) == 1 {
-					result <- KnxReadResult{
-						returnCode: apiModel.PlcResponseCode_OK,
-						value:      &plcValues[0],
-					}
-				} else {
-					var plcList values.PlcValue
-					plcList = values2.NewPlcList(plcValues)
-					result <- KnxReadResult{
-						returnCode: apiModel.PlcResponseCode_OK,
-						// TODO: Check if this is correct ...
-						value: &plcList,
-					}
-				}
-			}()
-			return nil
-		},
-		func(err error) error {
-			result <- KnxReadResult{
-				returnCode: apiModel.PlcResponseCode_RESPONSE_PENDING,
-			}
-			return nil
-		},
-		m.defaultTtl)
-
-	if err != nil {
-		//		close(result)
-	}
-	return result
+    result := make(chan KnxReadResult)
+
+    connection, ok := m.DeviceConnections[targetAddress]
+    if !ok {
+        //		close(result)
+        result <- KnxReadResult{
+            returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
+        }
+        return result
+    }
+
+    // Next, read the device descriptor so we know how we have to communicate with the device.
+    counter := connection.counter
+    connection.counter++
+    numBytes := numElements * uint8(math.Max(float64(1), float64(datapointType.DatapointMainType().SizeInBits()/8)))
+
+    // Send the property read request and wait for a confirmation that this property is readable.
+    propertyReadRequest := driverModel.NewTunnelingRequest(
+        driverModel.NewTunnelingRequestDataBlock(0, 0),
+        driverModel.NewLDataReq(0, nil,
+            driverModel.NewLDataExtended(false, 6, 0,
+                driverModel.NewKnxAddress(0, 0, 0),
+                KnxAddressToInt8Array(targetAddress),
+                driverModel.NewApduDataContainer(
+                    driverModel.NewApduDataMemoryRead(numBytes, address),
+                    true, counter),
+                true, true, driverModel.CEMIPriority_LOW, false, false)))
+    err := m.sendRequest(
+        propertyReadRequest,
+        func(message interface{}) bool {
+            tunnelingRequest := driverModel.CastTunnelingRequest(message)
+            if tunnelingRequest == nil ||
+                tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+                return false
+            }
+            lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+            if lDataInd == nil {
+                return false
+            }
+            dataFrameExt := driverModel.CastLDataExtended(lDataInd.DataFrame)
+            if dataFrameExt == nil {
+                return false
+            }
+            dataContainer := driverModel.CastApduDataContainer(dataFrameExt.Apdu)
+            if dataContainer == nil {
+                return false
+            }
+            dataApduMemoryResponse := driverModel.CastApduDataMemoryResponse(dataContainer.DataApdu)
+            if dataApduMemoryResponse == nil {
+                return false
+            }
+
+            // Check if the address matches
+            if *dataFrameExt.SourceAddress != targetAddress {
+                return false
+            }
+            // Check if the counter matches
+            if dataFrameExt.Apdu.Counter != counter {
+                return false
+            }
+            return dataApduMemoryResponse.Address == address && uint8(len(dataApduMemoryResponse.Data)) == numBytes
+        },
+        func(message interface{}) error {
+            tunnelingRequest := driverModel.CastTunnelingRequest(message)
+            lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi)
+            dataFrameExt := driverModel.CastLDataExtended(lDataInd.DataFrame)
+            dataContainer := driverModel.CastApduDataContainer(dataFrameExt.Apdu)
+            dataApduMemoryResponse := driverModel.CastApduDataMemoryResponse(dataContainer.DataApdu)
+
+            // Send an ACK and wait for it to be delivered
+            go func() {
+                ackResults := m.sendDeviceAck(targetAddress, dataFrameExt.Apdu.Counter)
+                select {
+                case ackResult := <-ackResults:
+                    if ackResult.err != nil {
+                        log.Errorf("Error sending Ack: %s", ackResult.err)
+                    }
+                case <-time.After(m.defaultTtl * 2):
+                    log.Debugf("Timeout sending device ack to %s.", KnxAddressToString(&targetAddress))
+                }
+
+                // Parse the data according to the property type information
+                rb := utils.NewReadBuffer(dataApduMemoryResponse.Data)
+                var plcValues []values.PlcValue
+                for i := uint8(0); i < numElements; i++ {
+                    plcValue, err := driverModel.KnxDatapointParse(rb, datapointType)
+                    // Return the result
+                    if err != nil {
+                        result <- KnxReadResult{
+                            returnCode: apiModel.PlcResponseCode_INTERNAL_ERROR,
+                            value:      nil,
+                        }
+                        return
+                    } else {
+                        plcValues = append(plcValues, plcValue)
+                    }
+                }
+
+                // If this is a single value, just return that directly.
+                // If it's not, wrap it in a PlcList structure.
+                if len(plcValues) == 1 {
+                    result <- KnxReadResult{
+                        returnCode: apiModel.PlcResponseCode_OK,
+                        value:      &plcValues[0],
+                    }
+                } else {
+                    var plcList values.PlcValue
+                    plcList = values2.NewPlcList(plcValues)
+                    result <- KnxReadResult{
+                        returnCode: apiModel.PlcResponseCode_OK,
+                        // TODO: Check if this is correct ...
+                        value: &plcList,
+                    }
+                }
+            }()
+            return nil
+        },
+        func(err error) error {
+            result <- KnxReadResult{
+                returnCode: apiModel.PlcResponseCode_RESPONSE_PENDING,
+            }
+            return nil
+        },
+        m.defaultTtl)
+
+    if err != nil {
+        //		close(result)
+    }
+    return result
 }
 
 func (m *KnxNetIpConnection) sendDeviceAck(targetAddress driverModel.KnxAddress, counter uint8) chan InternalResult {
-	result := make(chan InternalResult)
-
-	ack := driverModel.NewTunnelingRequest(
-		driverModel.NewTunnelingRequestDataBlock(0, 0),
-		driverModel.NewLDataReq(0, nil,
-			driverModel.NewLDataFrameDataExt(false, 6, uint8(0),
-				driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
-				driverModel.NewApduControlContainer(driverModel.NewApduControlAck(), 0, true, counter),
-				true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
-	err := m.sendRequest(
-		ack,
-		func(message interface{}) bool {
-			tunnelingRequest := driverModel.CastTunnelingRequest(message)
-			if tunnelingRequest == nil ||
-				tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
-				return false
-			}
-			lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
-			if lDataCon == nil {
-				return false
-			}
-			var apdu *driverModel.Apdu
-			var curSourceAddress *driverModel.KnxAddress
-			var curTargetAddress *driverModel.KnxAddress
-			dataFrameExt := driverModel.CastLDataFrameDataExt(lDataCon.DataFrame)
-			if dataFrameExt != nil {
-				apdu = dataFrameExt.Apdu
-				curSourceAddress = dataFrameExt.SourceAddress
-				curTargetAddress = Int8ArrayToKnxAddress(dataFrameExt.DestinationAddress)
-			} else {
-				dataFrame := driverModel.CastLDataFrameData(lDataCon.DataFrame)
-				if dataFrame != nil {
-					apdu = dataFrame.Apdu
-					curSourceAddress = dataFrame.SourceAddress
-					curTargetAddress = Int8ArrayToKnxAddress(dataFrame.DestinationAddress)
-				} else {
-					return false
-				}
-			}
-			// Check if the addresses match
-			if *curSourceAddress != *m.ClientKnxAddress {
-				return false
-			}
-			if *curTargetAddress != targetAddress {
-				return false
-			}
-			// Check if the counter matches
-			if apdu.Counter != counter {
-				return false
-			}
-			controlContainer := driverModel.CastApduControlContainer(apdu)
-			if controlContainer == nil {
-				return false
-			}
-			dataApduAck := driverModel.CastApduControlAck(controlContainer.ControlApdu)
-			if dataApduAck == nil {
-				return false
-			}
-			return true
-		},
-		func(message interface{}) error {
-			result <- InternalResult{}
-			return nil
-		},
-		func(err error) error {
-			result <- InternalResult{
-				err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
-			}
-			return nil
-		},
-		m.defaultTtl)
-
-	if err != nil {
-		result <- InternalResult{
-			err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
-		}
-	}
-
-	return result
+    result := make(chan InternalResult)
+
+    ack := driverModel.NewTunnelingRequest(
+        driverModel.NewTunnelingRequestDataBlock(0, 0),
+        driverModel.NewLDataReq(0, nil,
+            driverModel.NewLDataExtended(false, 6, uint8(0),
+                driverModel.NewKnxAddress(0, 0, 0), KnxAddressToInt8Array(targetAddress),
+                driverModel.NewApduControlContainer(driverModel.NewApduControlAck(), true, counter),
+                true, true, driverModel.CEMIPriority_SYSTEM, false, false)))
+    err := m.sendRequest(
+        ack,
+        func(message interface{}) bool {
+            tunnelingRequest := driverModel.CastTunnelingRequest(message)
+            if tunnelingRequest == nil ||
+                tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId != m.CommunicationChannelId {
+                return false
+            }
+            lDataCon := driverModel.CastLDataCon(tunnelingRequest.Cemi)
+            if lDataCon == nil {
+                return false
+            }
+            dataFrameExt := driverModel.CastLDataExtended(lDataCon.DataFrame)
+            if dataFrameExt == nil {
+                return false
+            }
+            // Check if the addresses match
+            if *dataFrameExt.SourceAddress != *m.ClientKnxAddress {
+                return false
+            }
+            curTargetAddress := Int8ArrayToKnxAddress(dataFrameExt.DestinationAddress)
+            if *curTargetAddress != targetAddress {
+                return false
+            }
+            // Check if the counter matches
+            if dataFrameExt.Apdu.Counter != counter {
+                return false
+            }
+            controlContainer := driverModel.CastApduControlContainer(dataFrameExt.Apdu)
+            if controlContainer == nil {
+                return false
+            }
+            dataApduAck := driverModel.CastApduControlAck(controlContainer.ControlApdu)
+            if dataApduAck == nil {
+                return false
+            }
+            return true
+        },
+        func(message interface{}) error {
+            result <- InternalResult{}
+            return nil
+        },
+        func(err error) error {
+            result <- InternalResult{
+                err: errors.New(fmt.Sprintf("got error processing request: %s", err)),
+            }
+            return nil
+        },
+        m.defaultTtl)
+
+    if err != nil {
+        result <- InternalResult{
+            err: errors.New(fmt.Sprintf("got error sending request: %s", err)),
+        }
+    }
+
+    return result
 }
 
 func (m *KnxNetIpConnection) sendRequest(request *driverModel.KnxNetIpMessage, acceptsMessage spi.AcceptsMessage, handleMessage spi.HandleMessage, handleError spi.HandleError, ttl time.Duration) error {
-	// If this is a tunneling request, we need to update the communicationChannelId and assign a sequenceCounter
-	tunnelingRequest := driverModel.CastTunnelingRequest(request)
-	if tunnelingRequest != nil {
-		tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId = m.CommunicationChannelId
-		tunnelingRequest.TunnelingRequestDataBlock.SequenceCounter = m.getNewSequenceCounter()
-	}
-	return m.messageCodec.SendRequest(request, acceptsMessage, handleMessage, handleError, ttl)
+    // If this is a tunneling request, we need to update the communicationChannelId and assign a sequenceCounter
+    tunnelingRequest := driverModel.CastTunnelingRequest(request)
+    if tunnelingRequest != nil {
+        tunnelingRequest.TunnelingRequestDataBlock.CommunicationChannelId = m.CommunicationChannelId
+        tunnelingRequest.TunnelingRequestDataBlock.SequenceCounter = m.getNewSequenceCounter()
+    }
+    return m.messageCodec.SendRequest(request, acceptsMessage, handleMessage, handleError, ttl)
 }
 
 func (m *KnxNetIpConnection) getLocalAddress() (*net.UDPAddr, error) {
-	transportInstanceExposer, ok := m.messageCodec.(spi.TransportInstanceExposer)
-	if !ok {
-		return nil, errors.New("used transport, is not a TransportInstanceExposer")
-	}
-
-	// Prepare a SearchReq
-	udpTransportInstance, ok := transportInstanceExposer.GetTransportInstance().(*udp.UdpTransportInstance)
-	if !ok {
-		return nil, errors.New("used transport, is not a UdpTransportInstance")
-	}
-
-	return udpTransportInstance.LocalAddress, nil
+    transportInstanceExposer, ok := m.messageCodec.(spi.TransportInstanceExposer)
+    if !ok {
+        return nil, errors.New("used transport, is not a TransportInstanceExposer")
+    }
+
+    // Prepare a SearchReq
+    udpTransportInstance, ok := transportInstanceExposer.GetTransportInstance().(*udp.UdpTransportInstance)
+    if !ok {
+        return nil, errors.New("used transport, is not a UdpTransportInstance")
+    }
+
+    return udpTransportInstance.LocalAddress, nil
 }
 
 func (m *KnxNetIpConnection) interceptIncomingMessage(interface{}) {
-	if m.connectionStateTimer != nil {
-		// Reset the timer for sending the ConnectionStateRequest
-		m.connectionStateTimer.Reset(60 * time.Second)
-	}
+    if m.connectionStateTimer != nil {
+        // Reset the timer for sending the ConnectionStateRequest
+        m.connectionStateTimer.Reset(60 * time.Second)
+    }
 }
 
 func (m *KnxNetIpConnection) getNewSequenceCounter() uint8 {
-	sequenceCounter := atomic.AddInt32(&m.SequenceCounter, 1)
-	if sequenceCounter >= math.MaxUint8 {
-		atomic.StoreInt32(&m.SequenceCounter, -1)
-		sequenceCounter = -1
-	}
-	return uint8(sequenceCounter)
+    sequenceCounter := atomic.AddInt32(&m.SequenceCounter, 1)
+    if sequenceCounter >= math.MaxUint8 {
+        atomic.StoreInt32(&m.SequenceCounter, -1)
+        sequenceCounter = -1
+    }
+    return uint8(sequenceCounter)
 }
 
 func (m *KnxNetIpConnection) castIpToKnxAddress(ip net.IP) *driverModel.IPAddress {
-	return driverModel.NewIPAddress(utils.ByteArrayToInt8Array(ip)[len(ip)-4:])
+    return driverModel.NewIPAddress(utils.ByteArrayToInt8Array(ip)[len(ip)-4:])
 }
 
 func (m *KnxNetIpConnection) handleIncomingTunnelingRequest(tunnelingRequest *driverModel.TunnelingRequest) {
-	go func() {
-		lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi.Child)
-		if lDataInd != nil {
-			var destinationAddress []int8
-			var apdu *driverModel.Apdu
-			switch lDataInd.DataFrame.Child.(type) {
-			case *driverModel.LDataFrameData:
-				dataFrame := driverModel.CastLDataFrameData(lDataInd.DataFrame)
-				destinationAddress = dataFrame.DestinationAddress
-				apdu = dataFrame.Apdu
-			case *driverModel.LDataFrameDataExt:
-				dataFrame := driverModel.CastLDataFrameDataExt(lDataInd.DataFrame)
-				destinationAddress = dataFrame.DestinationAddress
-				apdu = dataFrame.Apdu
-			}
-			container := driverModel.CastApduDataContainer(apdu)
-			if container == nil {
-				return
-			}
-			groupValueWrite := driverModel.CastApduDataGroupValueWrite(container.DataApdu)
-			if groupValueWrite == nil {
-				return
-			}
-			if destinationAddress != nil {
-				addressData := uint16(destinationAddress[0])<<8 | (uint16(destinationAddress[1]) & 0xFF)
-				m.valueCacheMutex.RLock()
-				val, ok := m.valueCache[addressData]
-				m.valueCacheMutex.RUnlock()
-				changed := false
-
-				var payload []int8
-				payload = append(payload, groupValueWrite.DataFirstByte)
-				payload = append(payload, groupValueWrite.Data...)
-				if !ok || !m.sliceEqual(val, payload) {
-					m.valueCacheMutex.Lock()
-					m.valueCache[addressData] = payload
-					m.valueCacheMutex.Unlock()
-					// If this is a new value, we have to also provide the 3 different types of addresses.
-					if !ok {
-						arb := utils.NewReadBuffer(utils.Int8ArrayToUint8Array(destinationAddress))
-						if address, err2 := driverModel.KnxGroupAddressParse(arb, 3); err2 == nil {
-							m.leve3AddressCache[addressData] = driverModel.CastKnxGroupAddress3Level(address)
-						} else {
-							fmt.Printf("Error parsing Group Address %s", err2.Error())
-						}
-						arb.Reset()
-						if address, err2 := driverModel.KnxGroupAddressParse(arb, 2); err2 == nil {
-							m.leve2AddressCache[addressData] = driverModel.CastKnxGroupAddress2Level(address)
-						}
-						arb.Reset()
-						if address, err2 := driverModel.KnxGroupAddressParse(arb, 1); err2 == nil {
-							m.leve1AddressCache[addressData] = driverModel.CastKnxGroupAddressFreeLevel(address)
-						}
-					}
-					changed = true
-				}
-				if m.subscribers != nil {
-					for _, subscriber := range m.subscribers {
-						subscriber.handleValueChange(lDataInd.DataFrame, changed)
-					}
-				}
-			}
-		}
-	}()
+    go func() {
+        lDataInd := driverModel.CastLDataInd(tunnelingRequest.Cemi.Child)
+        if lDataInd != nil {
+            var destinationAddress []int8
+            var apdu *driverModel.Apdu
+            switch lDataInd.DataFrame.Child.(type) {
+            case *driverModel.LDataExtended:
+                dataFrame := driverModel.CastLDataExtended(lDataInd.DataFrame)
+                destinationAddress = dataFrame.DestinationAddress
+                apdu = dataFrame.Apdu
+            }
+            container := driverModel.CastApduDataContainer(apdu)
+            if container == nil {
+                return
+            }
+            groupValueWrite := driverModel.CastApduDataGroupValueWrite(container.DataApdu)
+            if groupValueWrite == nil {
+                return
+            }
+            if destinationAddress != nil {
+                addressData := uint16(destinationAddress[0])<<8 | (uint16(destinationAddress[1]) & 0xFF)
+                m.valueCacheMutex.RLock()
+                val, ok := m.valueCache[addressData]
+                m.valueCacheMutex.RUnlock()
+                changed := false
+
+                var payload []int8
+                payload = append(payload, groupValueWrite.DataFirstByte)
+                payload = append(payload, groupValueWrite.Data...)
+                if !ok || !m.sliceEqual(val, payload) {
+                    m.valueCacheMutex.Lock()
+                    m.valueCache[addressData] = payload
+                    m.valueCacheMutex.Unlock()
+                    // If this is a new value, we have to also provide the 3 different types of addresses.
+                    if !ok {
+                        arb := utils.NewReadBuffer(utils.Int8ArrayToUint8Array(destinationAddress))
+                        if address, err2 := driverModel.KnxGroupAddressParse(arb, 3); err2 == nil {
+                            m.leve3AddressCache[addressData] = driverModel.CastKnxGroupAddress3Level(address)
+                        } else {
+                            fmt.Printf("Error parsing Group Address %s", err2.Error())
+                        }
+                        arb.Reset()
+                        if address, err2 := driverModel.KnxGroupAddressParse(arb, 2); err2 == nil {
+                            m.leve2AddressCache[addressData] = driverModel.CastKnxGroupAddress2Level(address)
+                        }
+                        arb.Reset()
+                        if address, err2 := driverModel.KnxGroupAddressParse(arb, 1); err2 == nil {
+                            m.leve1AddressCache[addressData] = driverModel.CastKnxGroupAddressFreeLevel(address)
+                        }
+                    }
+                    changed = true
+                }
+                if m.subscribers != nil {
+                    for _, subscriber := range m.subscribers {
+                        subscriber.handleValueChange(lDataInd.DataFrame, changed)
+                    }
+                }
+            }
+        }
+    }()
 }
 
 func (m *KnxNetIpConnection) getGroupAddressNumLevels() uint8 {
-	if val, ok := m.options["group-address-num-levels"]; ok {
-		groupAddressNumLevels, err := strconv.Atoi(val[0])
-		if err == nil {
-			return uint8(groupAddressNumLevels)
-		}
-	}
-	return 3
+    if val, ok := m.options["group-address-num-levels"]; ok {
+        groupAddressNumLevels, err := strconv.Atoi(val[0])
+        if err == nil {
+            return uint8(groupAddressNumLevels)
+        }
+    }
+    return 3
 }
 
 func (m *KnxNetIpConnection) addSubscriber(subscriber *KnxNetIpSubscriber) {
-	for _, sub := range m.subscribers {
-		if sub == subscriber {
-			return
-		}
-	}
-	m.subscribers = append(m.subscribers, subscriber)
+    for _, sub := range m.subscribers {
+        if sub == subscriber {
+            return
+        }
+    }
+    m.subscribers = append(m.subscribers, subscriber)
 }
 
 func (m *KnxNetIpConnection) removeSubscriber(subscriber *KnxNetIpSubscriber) {
-	for i, sub := range m.subscribers {
-		if sub == subscriber {
-			m.subscribers = append(m.subscribers[:i], m.subscribers[i+1:]...)
-		}
-	}
+    for i, sub := range m.subscribers {
+        if sub == subscriber {
+            m.subscribers = append(m.subscribers[:i], m.subscribers[i+1:]...)
+        }
+    }
 }
 
 func (m *KnxNetIpConnection) sliceEqual(a, b []int8) bool {
-	if len(a) != len(b) {
-		return false
-	}
-	for i, v := range a {
-		if v != b[i] {
-			return false
-		}
-	}
-	return true
+    if len(a) != len(b) {
+        return false
+    }
+    for i, v := range a {
+        if v != b[i] {
+            return false
+        }
+    }
+    return true
 }
 
 func KnxAddressToString(knxAddress *driverModel.KnxAddress) string {
-	return strconv.Itoa(int(knxAddress.MainGroup)) + "." + strconv.Itoa(int(knxAddress.MiddleGroup)) + "." + strconv.Itoa(int(knxAddress.SubGroup))
+    return strconv.Itoa(int(knxAddress.MainGroup)) + "." + strconv.Itoa(int(knxAddress.MiddleGroup)) + "." + strconv.Itoa(int(knxAddress.SubGroup))
 }
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpDiscoverer.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpDiscoverer.go
index 1963c73..d1461ee 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpDiscoverer.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpDiscoverer.go
@@ -142,7 +142,7 @@ func (d *KnxNetIpDiscoverer) Discover(callback func(event model.PlcDiscoveryEven
 					return nil
 				},
 				func(err error) error {
-					log.Error(fmt.Sprintf("got timeout waiting for search-response"))
+					log.Debugf(fmt.Sprintf("got timeout waiting for search-response"))
 					return nil
 				},
 				time.Second*1)
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpField.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpField.go
index db8de06..b684879 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpField.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpField.go
@@ -27,30 +27,35 @@ import (
 )
 
 type KnxNetIpField interface {
-    IsPatternField() bool
+    apiModel.PlcField
+}
+
+type KnxNetIpGroupAddressField interface {
     GetFieldType() *driverModel.KnxDatapointType
+    IsPatternField() bool
     matches(knxGroupAddress *driverModel.KnxGroupAddress) bool
     toGroupAddress() *driverModel.KnxGroupAddress
-    apiModel.PlcField
+    KnxNetIpField
 }
 
-type KnxNetIpGroupAddress3LevelPlcField struct {
-    FieldType *driverModel.KnxDatapointType
-    // 5 Bits: Values 0-31
-    MainGroup string
-    // 3 Bits: values 0-7
-    MiddleGroup string
-    // 8 Bits
-    SubGroup string
+type KnxNetIpDeviceField interface {
+    toKnxAddress() *driverModel.KnxAddress
     KnxNetIpField
 }
 
-func NewKnxNetIpGroupAddress3LevelPlcField(fieldType *driverModel.KnxDatapointType, mainGroup string, middleGroup string, subGroup string) KnxNetIpGroupAddress3LevelPlcField {
+type KnxNetIpGroupAddress3LevelPlcField struct {
+    MainGroup   string // 5 Bits: Values 0-31
+    MiddleGroup string // 3 Bits: values 0-7
+    SubGroup    string // 8 Bits
+    FieldType   *driverModel.KnxDatapointType
+}
+
+func NewKnxNetIpGroupAddress3LevelPlcField(mainGroup string, middleGroup string, subGroup string, fieldType *driverModel.KnxDatapointType) KnxNetIpGroupAddress3LevelPlcField {
     return KnxNetIpGroupAddress3LevelPlcField{
-        FieldType:   fieldType,
         MainGroup:   mainGroup,
         MiddleGroup: middleGroup,
         SubGroup:    subGroup,
+        FieldType:   fieldType,
     }
 }
 
@@ -115,19 +120,17 @@ func (k KnxNetIpGroupAddress3LevelPlcField) toGroupAddress() *driverModel.KnxGro
 }
 
 type KnxNetIpGroupAddress2LevelPlcField struct {
+    MainGroup string // 5 Bits: Values 0-31
+    SubGroup  string // 11 Bits
     FieldType *driverModel.KnxDatapointType
-    // 5 Bits: Values 0-31
-    MainGroup string
-    // 11 Bits
-    SubGroup string
     KnxNetIpField
 }
 
-func NewKnxNetIpGroupAddress2LevelPlcField(fieldType *driverModel.KnxDatapointType, mainGroup string, subGroup string) KnxNetIpGroupAddress2LevelPlcField {
+func NewKnxNetIpGroupAddress2LevelPlcField(mainGroup string, subGroup string, fieldType *driverModel.KnxDatapointType) KnxNetIpGroupAddress2LevelPlcField {
     return KnxNetIpGroupAddress2LevelPlcField{
-        FieldType: fieldType,
         MainGroup: mainGroup,
         SubGroup:  subGroup,
+        FieldType: fieldType,
     }
 }
 
@@ -183,16 +186,15 @@ func (k KnxNetIpGroupAddress2LevelPlcField) toGroupAddress() *driverModel.KnxGro
 }
 
 type KnxNetIpGroupAddress1LevelPlcField struct {
+    MainGroup string // 16 Bits
     FieldType *driverModel.KnxDatapointType
-    // 16 Bits
-    MainGroup string
     KnxNetIpField
 }
 
-func NewKnxNetIpGroupAddress1LevelPlcField(fieldType *driverModel.KnxDatapointType, mainGroup string) KnxNetIpGroupAddress1LevelPlcField {
+func NewKnxNetIpGroupAddress1LevelPlcField(mainGroup string, fieldType *driverModel.KnxDatapointType) KnxNetIpGroupAddress1LevelPlcField {
     return KnxNetIpGroupAddress1LevelPlcField{
-        FieldType: fieldType,
         MainGroup: mainGroup,
+        FieldType: fieldType,
     }
 }
 
@@ -238,111 +240,95 @@ func (k KnxNetIpGroupAddress1LevelPlcField) toGroupAddress() *driverModel.KnxGro
     return ga
 }
 
-type KnxNetIpDevicePropertyAddressPlcField struct {
-    // 5 Bits: Values 0-31
-    MainGroup string
-    // 3 Bits: values 0-7
-    MiddleGroup string
-    // 8 Bits
-    SubGroup   string
-    ObjectId   string
-    PropertyId string
-    KnxNetIpField
+type KnxNetIpDeviceQueryField struct {
+    MainGroup   string // 5 Bits: Values 0-31
+    MiddleGroup string // 3 Bits: values 0-7
+    SubGroup    string // 8 Bits
+    KnxNetIpDeviceField
 }
 
-func NewKnxNetIpDevicePropertyAddressPlcField(mainGroup string, middleGroup string, subGroup string, objectId string, propertyId string) KnxNetIpDevicePropertyAddressPlcField {
-    return KnxNetIpDevicePropertyAddressPlcField{
+func NewKnxNetIpDeviceQueryField(mainGroup string, middleGroup string, subGroup string) KnxNetIpDeviceQueryField {
+    return KnxNetIpDeviceQueryField{
         MainGroup:   mainGroup,
         MiddleGroup: middleGroup,
         SubGroup:    subGroup,
-        ObjectId:    objectId,
-        PropertyId:  propertyId,
     }
 }
 
-func (k KnxNetIpDevicePropertyAddressPlcField) GetTypeName() string {
-	return ""
+func (k KnxNetIpDeviceQueryField) GetTypeName() string {
+    return ""
 }
 
-func (k KnxNetIpDevicePropertyAddressPlcField) GetFieldType() *driverModel.KnxDatapointType {
+func (k KnxNetIpDeviceQueryField) GetQuantity() uint16 {
+    return 1
+}
+
+func (k KnxNetIpDeviceQueryField) toKnxAddress() *driverModel.KnxAddress {
     return nil
 }
 
-func (k KnxNetIpDevicePropertyAddressPlcField) GetQuantity() uint16 {
-	return 1
+type KnxNetIpDevicePropertyAddressPlcField struct {
+    MainGroup   uint8 // 5 Bits: Values 0-31
+    MiddleGroup uint8 // 3 Bits: values 0-7
+    SubGroup    uint8 // 8 Bits
+    ObjectId    uint8
+    PropertyId  uint8
+    KnxNetIpDeviceField
 }
 
-func (k KnxNetIpDevicePropertyAddressPlcField) IsPatternField() bool {
-    _, err := strconv.Atoi(k.MainGroup)
-    if err == nil {
-        _, err = strconv.Atoi(k.MiddleGroup)
-        if err == nil {
-            _, err = strconv.Atoi(k.SubGroup)
-            if err == nil {
-                return false
-            }
-        }
+func NewKnxNetIpDevicePropertyAddressPlcField(mainGroup uint8, middleGroup uint8, subGroup uint8, objectId uint8, propertyId uint8) KnxNetIpDevicePropertyAddressPlcField {
+    return KnxNetIpDevicePropertyAddressPlcField{
+        MainGroup:   mainGroup,
+        MiddleGroup: middleGroup,
+        SubGroup:    subGroup,
+        ObjectId:    objectId,
+        PropertyId:  propertyId,
     }
-    return true
 }
 
-func (k KnxNetIpDevicePropertyAddressPlcField) matches(knxGroupAddress *driverModel.KnxGroupAddress) bool {
-	level3KnxGroupAddress := driverModel.CastKnxGroupAddress3Level(knxGroupAddress)
-	if level3KnxGroupAddress == nil {
-		return false
-	}
-	return matches(k.MainGroup, strconv.Itoa(int(level3KnxGroupAddress.MainGroup))) &&
-		matches(k.MiddleGroup, strconv.Itoa(int(level3KnxGroupAddress.MiddleGroup))) &&
-		matches(k.SubGroup, strconv.Itoa(int(level3KnxGroupAddress.SubGroup)))
+func (k KnxNetIpDevicePropertyAddressPlcField) GetTypeName() string {
+    return ""
+}
+
+func (k KnxNetIpDevicePropertyAddressPlcField) GetQuantity() uint16 {
+    return 1
 }
 
 func (k KnxNetIpDevicePropertyAddressPlcField) toKnxAddress() *driverModel.KnxAddress {
-    mainGroup, err := strconv.Atoi(k.MainGroup)
-    if err != nil {
-        return nil
-    }
-    middleGroup, err := strconv.Atoi(k.MiddleGroup)
-    if err != nil {
-        return nil
-    }
-    subGroup, err := strconv.Atoi(k.SubGroup)
-    if err != nil {
-        return nil
-    }
     ga := &driverModel.KnxAddress{
-        MainGroup:   uint8(mainGroup),
-        MiddleGroup: uint8(middleGroup),
-        SubGroup:    uint8(subGroup),
+        MainGroup:   k.MainGroup,
+        MiddleGroup: k.MiddleGroup,
+        SubGroup:    k.SubGroup,
     }
     return ga
 }
 
 type KnxNetIpDeviceMemoryAddressPlcField struct {
-    FieldType *driverModel.KnxDatapointType
-    // 5 Bits: Values 0-31
-    MainGroup uint8
-    // 3 Bits: values 0-7
-    MiddleGroup uint8
-    // 8 Bits
-    SubGroup         uint8
+    MainGroup        uint8 // 5 Bits: Values 0-31
+    MiddleGroup      uint8 // 3 Bits: values 0-7
+    SubGroup         uint8 // 8 Bits
     Address          uint16
     NumberOfElements uint8
-    KnxNetIpField
+    FieldType        *driverModel.KnxDatapointType
+    KnxNetIpDeviceField
 }
 
-func NewKnxNetIpDeviceMemoryAddressPlcField(fieldType *driverModel.KnxDatapointType, mainGroup uint8, middleGroup uint8, subGroup uint8, address uint16, numberOfElements uint8) KnxNetIpDeviceMemoryAddressPlcField {
+func NewKnxNetIpDeviceMemoryAddressPlcField(mainGroup uint8, middleGroup uint8, subGroup uint8, address uint16, numberOfElements uint8, fieldType *driverModel.KnxDatapointType) KnxNetIpDeviceMemoryAddressPlcField {
     return KnxNetIpDeviceMemoryAddressPlcField{
-        FieldType:        fieldType,
         MainGroup:        mainGroup,
         MiddleGroup:      middleGroup,
         SubGroup:         subGroup,
         Address:          address,
         NumberOfElements: numberOfElements,
+        FieldType:        fieldType,
     }
 }
 
 func (k KnxNetIpDeviceMemoryAddressPlcField) GetTypeName() string {
-    return k.FieldType.Name()
+    if k.FieldType != nil {
+        return k.FieldType.Name()
+    }
+    return ""
 }
 
 func (k KnxNetIpDeviceMemoryAddressPlcField) GetFieldType() *driverModel.KnxDatapointType {
@@ -353,21 +339,45 @@ func (k KnxNetIpDeviceMemoryAddressPlcField) GetQuantity() uint16 {
     return uint16(k.NumberOfElements)
 }
 
-func (k KnxNetIpDeviceMemoryAddressPlcField) IsPatternField() bool {
-    return false
+func (k KnxNetIpDeviceMemoryAddressPlcField) toKnxAddress() *driverModel.KnxAddress {
+    individualAddress := &driverModel.KnxAddress{
+        MainGroup:   k.MainGroup,
+        MiddleGroup: k.MiddleGroup,
+        SubGroup:    k.SubGroup,
+    }
+    return individualAddress
 }
 
-func (k KnxNetIpDeviceMemoryAddressPlcField) matches(knxGroupAddress *driverModel.KnxGroupAddress) bool {
-    return false
+type KnxNetIpCommunicationObjectQueryField struct {
+    MainGroup   uint8 // 5 Bits: Values 0-31
+    MiddleGroup uint8 // 3 Bits: values 0-7
+    SubGroup    uint8 // 8 Bits
+    KnxNetIpDeviceField
 }
 
-func (k KnxNetIpDeviceMemoryAddressPlcField) toKnxAddress() *driverModel.KnxAddress {
-    ga := &driverModel.KnxAddress{
+func NewKnxNetIpCommunicationObjectQueryField(mainGroup uint8, middleGroup uint8, subGroup uint8) KnxNetIpCommunicationObjectQueryField {
+    return KnxNetIpCommunicationObjectQueryField{
+        MainGroup:   mainGroup,
+        MiddleGroup: middleGroup,
+        SubGroup:    subGroup,
+    }
+}
+
+func (k KnxNetIpCommunicationObjectQueryField) GetTypeName() string {
+    return ""
+}
+
+func (k KnxNetIpCommunicationObjectQueryField) GetQuantity() uint16 {
+    return 1
+}
+
+func (k KnxNetIpCommunicationObjectQueryField) toKnxAddress() *driverModel.KnxAddress {
+    individualAddress := &driverModel.KnxAddress{
         MainGroup:   k.MainGroup,
         MiddleGroup: k.MiddleGroup,
         SubGroup:    k.SubGroup,
     }
-    return ga
+    return individualAddress
 }
 
 func CastToKnxNetIpFieldFromPlcField(plcField apiModel.PlcField) (KnxNetIpField, error) {
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpFieldHandler.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpFieldHandler.go
index e6a0c49..9aea58f 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpFieldHandler.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpFieldHandler.go
@@ -44,35 +44,62 @@ func (m KnxNetIpFieldType) GetName() string {
 }
 
 type FieldHandler struct {
-    knxNetIpGroupAddress3Level    *regexp.Regexp
-    knxNetIpGroupAddress2Level    *regexp.Regexp
-    knxNetIpGroupAddress1Level    *regexp.Regexp
-    knxNetIpDevicePropertyAddress *regexp.Regexp
-    knxNetIpDeviceMemoryAddress   *regexp.Regexp
-    knxNetIpDeviceQuery           *regexp.Regexp
+    knxNetIpGroupAddress3Level             *regexp.Regexp
+    knxNetIpGroupAddress2Level             *regexp.Regexp
+    knxNetIpGroupAddress1Level             *regexp.Regexp
+    knxNetIpDeviceQuery                    *regexp.Regexp
+    knxNetIpDevicePropertyAddress          *regexp.Regexp
+    knxNetIpDeviceMemoryAddress            *regexp.Regexp
+    knxNetIpDeviceCommunicationObjectQuery *regexp.Regexp
     spi.PlcFieldHandler
 }
 
 func NewFieldHandler() FieldHandler {
     return FieldHandler{
-        knxNetIpGroupAddress3Level:    regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\/(?P<middleGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\/(?P<subGroup>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\-\d{1,3}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
-        knxNetIpGroupAddress2Level:    regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))/(?P<subGroup>(\d{1,4}|\*|\[(\d{1,4}|\d{1,4}\-\d{1,4})(,(\d{1,4}|\d{1,4}\-\d{1,4}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
-        knxNetIpGroupAddress1Level:    regexp.MustCompile(`^(?P<mainGroup>(\d{1,5}|\*|\[(\d{1,5}|\d{1,5}\-\d{1,5})(,(\d{1,5}|\d{1,5}\-\d{1,5}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
-        knxNetIpDevicePropertyAddress: regexp.MustCompile(`^(?P<mainGroup>\d{1,2})\.(?P<middleGroup>\d)\.(?P<subGroup>\d{1,3})\/(?P<objectId>\d{1,3})\/(?P<propertyId>\d{1,3})$`),
-        knxNetIpDeviceMemoryAddress:   regexp.MustCompile(`^(?P<mainGroup>\d{1,2})\.(?P<middleGroup>\d)\.(?P<subGroup>\d{1,3})#(?P<address>\d{1,5})(:(?P<datatype>[a-zA-Z_]+)(\[(?P<numElements>\d+)])?)?$`),
-        knxNetIpDeviceQuery:           regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\.(?P<middleGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\.(?P<subGroup>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\-\d{1,3}))*]))(\/(?P<objectId>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\-\d{1,3}))*]))(\/(?P<propertyId>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\ [...]
+        knxNetIpGroupAddress3Level:             regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\/(?P<middleGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\/(?P<subGroup>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\-\d{1,3}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
+        knxNetIpGroupAddress2Level:             regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))/(?P<subGroup>(\d{1,4}|\*|\[(\d{1,4}|\d{1,4}\-\d{1,4})(,(\d{1,4}|\d{1,4}\-\d{1,4}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
+        knxNetIpGroupAddress1Level:             regexp.MustCompile(`^(?P<mainGroup>(\d{1,5}|\*|\[(\d{1,5}|\d{1,5}\-\d{1,5})(,(\d{1,5}|\d{1,5}\-\d{1,5}))*]))(:(?P<datatype>[a-zA-Z_]+))?$`),
+
+        knxNetIpDeviceQuery:                    regexp.MustCompile(`^(?P<mainGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\.(?P<middleGroup>(\d{1,2}|\*|\[(\d{1,2}|\d{1,2}\-\d{1,2})(,(\d{1,2}|\d{1,2}\-\d{1,2}))*]))\.(?P<subGroup>(\d{1,3}|\*|\[(\d{1,3}|\d{1,3}\-\d{1,3})(,(\d{1,3}|\d{1,3}\-\d{1,3}))*]))$`),
+        knxNetIpDevicePropertyAddress:          regexp.MustCompile(`^(?P<mainGroup>\d{1,2})\.(?P<middleGroup>\d)\.(?P<subGroup>\d{1,3})#(?P<objectId>\d{1,3})\/(?P<propertyId>\d{1,3})$`),
+        knxNetIpDeviceMemoryAddress:            regexp.MustCompile(`^(?P<mainGroup>\d{1,2})\.(?P<middleGroup>\d)\.(?P<subGroup>\d{1,3})#(?P<address>[0-9a-fA-F]{1,8})(:(?P<datatype>[a-zA-Z_]+)(\[(?P<numElements>\d+)])?)?$`),
+        knxNetIpDeviceCommunicationObjectQuery: regexp.MustCompile(`^(?P<mainGroup>\d{1,2})\.(?P<middleGroup>\d)\.(?P<subGroup>\d{1,3})#com-obj$`),
     }
 }
 
 func (m FieldHandler) ParseQuery(query string) (apiModel.PlcField, error) {
-    if match := utils.GetSubgroupMatches(m.knxNetIpDeviceQuery, query); match != nil {
-        return NewKnxNetIpDevicePropertyAddressPlcField(
-            match["mainGroup"], match["middleGroup"], match["subGroup"],
-            match["objectId"], match["propertyId"]), nil
+    if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress1Level, query); match != nil {
+        fieldTypeName, ok := match["datatype"]
+        var fieldType driverModel.KnxDatapointType
+        if ok {
+            fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
+        }
+        return NewKnxNetIpGroupAddress1LevelPlcField(match["mainGroup"], &fieldType), nil
+    } else if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress2Level, query); match != nil {
+        fieldTypeName, ok := match["datatype"]
+        var fieldType driverModel.KnxDatapointType
+        if ok {
+            fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
+        }
+        return NewKnxNetIpGroupAddress2LevelPlcField(match["mainGroup"], match["subGroup"], &fieldType), nil
+    } else if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress3Level, query); match != nil {
+        fieldTypeName, ok := match["datatype"]
+        var fieldType driverModel.KnxDatapointType
+        if ok {
+            fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
+        }
+        return NewKnxNetIpGroupAddress3LevelPlcField(match["mainGroup"], match["middleGroup"], match["subGroup"], &fieldType), nil
+    } else if match := utils.GetSubgroupMatches(m.knxNetIpDeviceQuery, query); match != nil {
+        return NewKnxNetIpDeviceQueryField(
+            match["mainGroup"], match["middleGroup"], match["subGroup"]), nil
     } else if match := utils.GetSubgroupMatches(m.knxNetIpDevicePropertyAddress, query); match != nil {
-       return NewKnxNetIpDevicePropertyAddressPlcField(
-           match["mainGroup"], match["middleGroup"], match["subGroup"],
-           match["objectId"], match["propertyId"]), nil
+        mainGroup, _ := strconv.Atoi(match["mainGroup"])
+        middleGroup, _ := strconv.Atoi(match["middleGroup"])
+        subGroup, _ := strconv.Atoi(match["subGroup"])
+        objectId, _ := strconv.Atoi(match["objectId"])
+        propertyId, _ := strconv.Atoi(match["propertyId"])
+        return NewKnxNetIpDevicePropertyAddressPlcField(
+            uint8(mainGroup), uint8(middleGroup), uint8(subGroup), uint8(objectId), uint8(propertyId)), nil
     } else if match := utils.GetSubgroupMatches(m.knxNetIpDeviceMemoryAddress, query); match != nil {
         fieldTypeName, ok := match["datatype"]
         // This is a 0-255 valued 1-byte value.
@@ -86,37 +113,24 @@ func (m FieldHandler) ParseQuery(query string) (apiModel.PlcField, error) {
         addressData, _ := hex.DecodeString(match["address"])
         var address uint16
         if len(addressData) == 2 {
-            address = uint16(addressData[0]) << 8 | uint16(addressData[1])
-        } else {
+            address = uint16(addressData[0])<<8 | uint16(addressData[1])
+        } else if len(addressData) == 1 {
             address = uint16(addressData[0])
+        } else {
+            return nil, errors.New("invalid address: " + match["address"])
         }
         numberOfElements := 1
         numElements, ok := match["numElements"]
         if ok && len(numElements) > 0 {
             numberOfElements, _ = strconv.Atoi(numElements)
         }
-        return NewKnxNetIpDeviceMemoryAddressPlcField(&fieldType, uint8(mainGroup), uint8(middleGroup), uint8(subGroup), address, uint8(numberOfElements)), nil
-    } else if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress3Level, query); match != nil {
-        fieldTypeName, ok := match["datatype"]
-        var fieldType driverModel.KnxDatapointType
-        if ok {
-            fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
-        }
-        return NewKnxNetIpGroupAddress3LevelPlcField(&fieldType, match["mainGroup"], match["middleGroup"], match["subGroup"]), nil
-    } else if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress2Level, query); match != nil {
-        fieldTypeName, ok := match["datatype"]
-        var fieldType driverModel.KnxDatapointType
-        if ok {
-            fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
-        }
-        return NewKnxNetIpGroupAddress2LevelPlcField(&fieldType, match["mainGroup"], match["subGroup"]), nil
-    } else if match := utils.GetSubgroupMatches(m.knxNetIpGroupAddress1Level, query); match != nil {
-        fieldTypeName, ok := match["datatype"]
-        var fieldType driverModel.KnxDatapointType
-        if ok {
-            fieldType = driverModel.KnxDatapointTypeByName(fieldTypeName)
-        }
-        return NewKnxNetIpGroupAddress1LevelPlcField(&fieldType, match["mainGroup"]), nil
+        return NewKnxNetIpDeviceMemoryAddressPlcField(uint8(mainGroup), uint8(middleGroup), uint8(subGroup), address, uint8(numberOfElements), &fieldType), nil
+    } else if match := utils.GetSubgroupMatches(m.knxNetIpDeviceCommunicationObjectQuery, query); match != nil {
+        mainGroup, _ := strconv.Atoi(match["mainGroup"])
+        middleGroup, _ := strconv.Atoi(match["middleGroup"])
+        subGroup, _ := strconv.Atoi(match["subGroup"])
+        return NewKnxNetIpCommunicationObjectQueryField(
+            uint8(mainGroup), uint8(middleGroup), uint8(subGroup)), nil
     }
     return nil, errors.New("Invalid address format for address '" + query + "'")
 }
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
index 3d215b1..221d60a 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
@@ -26,7 +26,6 @@ import (
     internalValues "github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
     apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
     apiValues "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
-    "strconv"
     "time"
 )
 
@@ -49,8 +48,8 @@ func (m KnxNetIpReader) Read(readRequest apiModel.PlcReadRequest) <-chan apiMode
 
         // Sort the fields in direct properties and memory addresses, which will have to be actively
         // read from the devices and group-addresses which will be locally processed from the local cache.
-        deviceAddresses := map[driverModel.KnxAddress]map[string]KnxNetIpField{}
-        groupAddresses := map[string]KnxNetIpField{}
+        deviceAddresses := map[driverModel.KnxAddress]map[string]KnxNetIpDeviceField{}
+        groupAddresses := map[string]KnxNetIpGroupAddressField{}
         for _, fieldName := range readRequest.GetFieldNames() {
             // Get the knx field
             field, err := CastToKnxNetIpFieldFromPlcField(readRequest.GetField(fieldName))
@@ -63,26 +62,34 @@ func (m KnxNetIpReader) Read(readRequest apiModel.PlcReadRequest) <-chan apiMode
             switch field.(type) {
             case KnxNetIpDevicePropertyAddressPlcField:
                 propertyField := field.(KnxNetIpDevicePropertyAddressPlcField)
-                knxAddress := FieldToKnxAddress(propertyField)
+                knxAddress := propertyField.toKnxAddress()
                 if knxAddress == nil {
                     continue
                 }
                 if _, ok := deviceAddresses[*knxAddress]; !ok {
-                    deviceAddresses[*knxAddress] = map[string]KnxNetIpField{}
+                    deviceAddresses[*knxAddress] = map[string]KnxNetIpDeviceField{}
                 }
                 deviceAddresses[*knxAddress][fieldName] = propertyField
             case KnxNetIpDeviceMemoryAddressPlcField:
                 memoryField := field.(KnxNetIpDeviceMemoryAddressPlcField)
-                knxAddress := FieldToKnxAddress(memoryField)
+                knxAddress := memoryField.toKnxAddress()
                 if knxAddress == nil {
                     continue
                 }
                 if _, ok := deviceAddresses[*knxAddress]; !ok {
-                    deviceAddresses[*knxAddress] = map[string]KnxNetIpField{}
+                    deviceAddresses[*knxAddress] = map[string]KnxNetIpDeviceField{}
                 }
                 deviceAddresses[*knxAddress][fieldName] = memoryField
+            case KnxNetIpCommunicationObjectQueryField:
+                responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
+                plcValues[fieldName] = nil
+                continue
+            case KnxNetIpGroupAddressField:
+                groupAddressField := field.(KnxNetIpGroupAddressField)
+                groupAddresses[fieldName] = groupAddressField
             default:
-                groupAddresses[fieldName] = field
+                responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
+                plcValues[fieldName] = nil
             }
         }
 
@@ -91,20 +98,11 @@ func (m KnxNetIpReader) Read(readRequest apiModel.PlcReadRequest) <-chan apiMode
         for deviceAddress, fields := range deviceAddresses {
             // Collect all the properties on this device
             for fieldName, field := range fields {
-                // TODO: We'll add this as time progresses, for now we only support fully qualified addresses
-                if field.IsPatternField() {
-                    responseCodes[fieldName] = apiModel.PlcResponseCode_UNSUPPORTED
-                    plcValues[fieldName] = nil
-                    continue
-                }
-
                 switch field.(type) {
                 case KnxNetIpDevicePropertyAddressPlcField:
                     propertyField := field.(KnxNetIpDevicePropertyAddressPlcField)
-                    objectId, _ := strconv.Atoi(propertyField.ObjectId)
-                    propertyId, _ := strconv.Atoi(propertyField.PropertyId)
 
-                    results := m.connection.ReadDeviceProperty(deviceAddress, uint8(objectId), uint8(propertyId))
+                    results := m.connection.ReadDeviceProperty(deviceAddress, propertyField.ObjectId, propertyField.PropertyId)
                     select {
                     case result := <-results:
                         responseCodes[fieldName] = result.returnCode
@@ -154,7 +152,7 @@ func (m KnxNetIpReader) Read(readRequest apiModel.PlcReadRequest) <-chan apiMode
     return resultChan
 }
 
-func (m KnxNetIpReader) readGroupAddress(field KnxNetIpField) (apiModel.PlcResponseCode, apiValues.PlcValue) {
+func (m KnxNetIpReader) readGroupAddress(field KnxNetIpGroupAddressField) (apiModel.PlcResponseCode, apiValues.PlcValue) {
     // Pattern fields can match more than one value, therefore we have to handle things differently
     if field.IsPatternField() {
         // Depending on the type of field, get the uint16 ids of all values that match the current field
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go
index 7838a2e..967e632 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpSubscriber.go
@@ -81,12 +81,8 @@ func (m *KnxNetIpSubscriber) handleValueChange(lDataFrame *driverModel.LDataFram
 	var destinationAddress []int8
     var apdu *driverModel.Apdu
 	switch lDataFrame.Child.(type) {
-	case *driverModel.LDataFrameData:
-		dataFrame := driverModel.CastLDataFrameData(lDataFrame)
-		destinationAddress = dataFrame.DestinationAddress
-        apdu = dataFrame.Apdu
-	case *driverModel.LDataFrameDataExt:
-		dataFrame := driverModel.CastLDataFrameDataExt(lDataFrame)
+	case *driverModel.LDataExtended:
+		dataFrame := driverModel.CastLDataExtended(lDataFrame)
 		destinationAddress = dataFrame.DestinationAddress
         apdu = dataFrame.Apdu
 	}
@@ -124,39 +120,46 @@ func (m *KnxNetIpSubscriber) handleValueChange(lDataFrame *driverModel.LDataFram
 				if err != nil {
 					continue
 				}
-				subscriptionType := subscriptionRequest.GetType(fieldName)
-				// If it matches, take the datatype of each matching field and try to decode the payload
-				if field.matches(groupAddress) {
-                    // If this is a CHANGE_OF_STATE field, filter out the events where the value actually hasn't changed.
-					if subscriptionType == internalModel.SUBSCRIPTION_CHANGE_OF_STATE && changed {
-                        var payload []int8
-                        payload = append(payload, groupValueWrite.DataFirstByte)
-                        payload = append(payload, groupValueWrite.Data...)
-                        rb := utils.NewReadBuffer(utils.Int8ArrayToByteArray(payload))
-                        if field.GetFieldType() == nil {
-                            responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATATYPE
-                            plcValues[fieldName] = nil
-                            continue
-                        }
-                        // If the size of the field is greater than 6, we have to skip the first byte
-                        if field.GetFieldType().LengthInBits() > 6 {
-                            _, _ = rb.ReadUint8(8)
+                switch field.(type) {
+                case KnxNetIpGroupAddressField:
+                    subscriptionType := subscriptionRequest.GetType(fieldName)
+                    groupAddressField := field.(KnxNetIpGroupAddressField)
+                    // If it matches, take the datatype of each matching field and try to decode the payload
+                    if groupAddressField.matches(groupAddress) {
+                        // If this is a CHANGE_OF_STATE field, filter out the events where the value actually hasn't changed.
+                        if subscriptionType == internalModel.SUBSCRIPTION_CHANGE_OF_STATE && changed {
+                            var payload []int8
+                            payload = append(payload, groupValueWrite.DataFirstByte)
+                            payload = append(payload, groupValueWrite.Data...)
+                            rb := utils.NewReadBuffer(utils.Int8ArrayToByteArray(payload))
+                            if groupAddressField.GetFieldType() == nil {
+                                responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATATYPE
+                                plcValues[fieldName] = nil
+                                continue
+                            }
+                            // If the size of the field is greater than 6, we have to skip the first byte
+                            if groupAddressField.GetFieldType().LengthInBits() > 6 {
+                                _, _ = rb.ReadUint8(8)
+                            }
+                            plcValue, err2 := driverModel.KnxDatapointParse(rb, *groupAddressField.GetFieldType())
+                            fields[fieldName] = field
+                            types[fieldName] = subscriptionRequest.GetType(fieldName)
+                            intervals[fieldName] = subscriptionRequest.GetInterval(fieldName)
+                            addresses[fieldName] = destinationAddress
+                            if err2 == nil {
+                                responseCodes[fieldName] = apiModel.PlcResponseCode_OK
+                                plcValues[fieldName] = plcValue
+                            } else {
+                                // TODO: Do a little more here ...
+                                responseCodes[fieldName] = apiModel.PlcResponseCode_INTERNAL_ERROR
+                                plcValues[fieldName] = nil
+                            }
                         }
-                        plcValue, err2 := driverModel.KnxDatapointParse(rb, *field.GetFieldType())
-                        fields[fieldName] = field
-                        types[fieldName] = subscriptionRequest.GetType(fieldName)
-                        intervals[fieldName] = subscriptionRequest.GetInterval(fieldName)
-                        addresses[fieldName] = destinationAddress
-                        if err2 == nil {
-                            responseCodes[fieldName] = apiModel.PlcResponseCode_OK
-                            plcValues[fieldName] = plcValue
-                        } else {
-                            // TODO: Do a little more here ...
-                            responseCodes[fieldName] = apiModel.PlcResponseCode_INTERNAL_ERROR
-                            plcValues[fieldName] = nil
-                        }
-					}
-				}
+                    }
+                default:
+                    responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
+                    plcValues[fieldName] = nil
+                }
 			}
 
 			// Assemble a PlcSubscription event
diff --git a/plc4go/internal/plc4go/knxnetip/Utils.go b/plc4go/internal/plc4go/knxnetip/Utils.go
index 26eec2e..110c9c7 100644
--- a/plc4go/internal/plc4go/knxnetip/Utils.go
+++ b/plc4go/internal/plc4go/knxnetip/Utils.go
@@ -41,36 +41,6 @@ func GroupAddressToString(groupAddress *driverModel.KnxGroupAddress) string {
 	return ""
 }
 
-func FieldToKnxAddress(field KnxNetIpField) *driverModel.KnxAddress {
-	if field.IsPatternField() {
-		return nil
-	}
-	var mainAddress int
-	var middleAddress int
-	var subAddress int
-	switch field.(type) {
-	case KnxNetIpDevicePropertyAddressPlcField:
-		plcField := field.(KnxNetIpDevicePropertyAddressPlcField)
-		mainAddress, _ = strconv.Atoi(plcField.MainGroup)
-		middleAddress, _ = strconv.Atoi(plcField.MiddleGroup)
-		subAddress, _ = strconv.Atoi(plcField.SubGroup)
-    case KnxNetIpDeviceMemoryAddressPlcField:
-        plcField := field.(KnxNetIpDeviceMemoryAddressPlcField)
-        mainAddress = int(plcField.MainGroup)
-        middleAddress = int(plcField.MiddleGroup)
-        subAddress = int(plcField.SubGroup)
-	case KnxNetIpGroupAddress3LevelPlcField:
-		plcField := field.(KnxNetIpGroupAddress3LevelPlcField)
-		mainAddress, _ = strconv.Atoi(plcField.MainGroup)
-		middleAddress, _ = strconv.Atoi(plcField.MiddleGroup)
-		subAddress, _ = strconv.Atoi(plcField.SubGroup)
-	default:
-		return nil
-	}
-
-	return driverModel.NewKnxAddress(uint8(mainAddress), uint8(middleAddress), uint8(subAddress))
-}
-
 func Int8ArrayToKnxAddress(data []int8) *driverModel.KnxAddress {
 	readBuffer := utils.NewReadBuffer(utils.Int8ArrayToUint8Array(data))
 	knxAddress, err := driverModel.KnxAddressParse(readBuffer)
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go b/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go
index d1ddcd9..2498980 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go
@@ -43,7 +43,11 @@ func (m KnxnetipParserHelper) Parse(typeName string, arguments []string, io *uti
     case "ConnectionRequestInformation":
         return model.ConnectionRequestInformationParse(io)
     case "Apdu":
-        return model.ApduParse(io)
+        dataLength, err := utils.StrToUint8(arguments[0])
+        if err != nil {
+            return nil, err
+        }
+        return model.ApduParse(io, dataLength)
     case "HPAIDiscoveryEndpoint":
         return model.HPAIDiscoveryEndpointParse(io)
     case "ProjectInstallationIdentifier":
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/Apdu.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/Apdu.go
index 9bee363..ae349a2 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/Apdu.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/Apdu.go
@@ -29,7 +29,6 @@ import (
 
 // The data-structure of this message
 type Apdu struct {
-    DataLength uint8
     Numbered bool
     Counter uint8
     Child IApduChild
@@ -53,13 +52,13 @@ type IApduParent interface {
 
 type IApduChild interface {
     Serialize(io utils.WriteBuffer) error
-    InitializeParent(parent *Apdu, dataLength uint8, numbered bool, counter uint8)
+    InitializeParent(parent *Apdu, numbered bool, counter uint8)
     GetTypeName() string
     IApdu
 }
 
-func NewApdu(dataLength uint8, numbered bool, counter uint8) *Apdu {
-    return &Apdu{DataLength: dataLength, Numbered: numbered, Counter: counter}
+func NewApdu(numbered bool, counter uint8) *Apdu {
+    return &Apdu{Numbered: numbered, Counter: counter}
 }
 
 func CastApdu(structType interface{}) *Apdu {
@@ -82,9 +81,6 @@ func (m *Apdu) GetTypeName() string {
 func (m *Apdu) LengthInBits() uint16 {
     lengthInBits := uint16(0)
 
-    // Simple field (dataLength)
-    lengthInBits += 8
-
     // Discriminator Field (control)
     lengthInBits += 1
 
@@ -104,13 +100,7 @@ func (m *Apdu) LengthInBytes() uint16 {
     return m.LengthInBits() / 8
 }
 
-func ApduParse(io *utils.ReadBuffer) (*Apdu, error) {
-
-    // Simple Field (dataLength)
-    dataLength, _dataLengthErr := io.ReadUint8(8)
-    if _dataLengthErr != nil {
-        return nil, errors.New("Error parsing 'dataLength' field " + _dataLengthErr.Error())
-    }
+func ApduParse(io *utils.ReadBuffer, dataLength uint8) (*Apdu, error) {
 
     // Discriminator Field (control) (Used as input to a switch field)
     control, _controlErr := io.ReadUint8(1)
@@ -144,7 +134,7 @@ func ApduParse(io *utils.ReadBuffer) (*Apdu, error) {
     }
 
     // Finish initializing
-    _parent.Child.InitializeParent(_parent, dataLength, numbered, counter)
+    _parent.Child.InitializeParent(_parent, numbered, counter)
     return _parent, nil
 }
 
@@ -154,13 +144,6 @@ func (m *Apdu) Serialize(io utils.WriteBuffer) error {
 
 func (m *Apdu) SerializeParent(io utils.WriteBuffer, child IApdu, serializeChildFunction func() error) error {
 
-    // Simple Field (dataLength)
-    dataLength := uint8(m.DataLength)
-    _dataLengthErr := io.WriteUint8(8, (dataLength))
-    if _dataLengthErr != nil {
-        return errors.New("Error serializing 'dataLength' field " + _dataLengthErr.Error())
-    }
-
     // Discriminator Field (control) (Used as input to a switch field)
     control := uint8(child.Control())
     _controlErr := io.WriteUint8(1, (control))
@@ -206,12 +189,6 @@ func (m *Apdu) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
         case xml.StartElement:
             tok := token.(xml.StartElement)
             switch tok.Name.Local {
-            case "dataLength":
-                var data uint8
-                if err := d.DecodeElement(&data, &tok); err != nil {
-                    return err
-                }
-                m.DataLength = data
             case "numbered":
                 var data bool
                 if err := d.DecodeElement(&data, &tok); err != nil {
@@ -264,9 +241,6 @@ func (m *Apdu) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
         }}); err != nil {
         return err
     }
-    if err := e.EncodeElement(m.DataLength, xml.StartElement{Name: xml.Name{Local: "dataLength"}}); err != nil {
-        return err
-    }
     if err := e.EncodeElement(m.Numbered, xml.StartElement{Name: xml.Name{Local: "numbered"}}); err != nil {
         return err
     }
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduControlContainer.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduControlContainer.go
index 008fd70..f2bcde9 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduControlContainer.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduControlContainer.go
@@ -48,16 +48,15 @@ func (m *ApduControlContainer) Control() uint8 {
 }
 
 
-func (m *ApduControlContainer) InitializeParent(parent *Apdu, dataLength uint8, numbered bool, counter uint8) {
-    m.Parent.DataLength = dataLength
+func (m *ApduControlContainer) InitializeParent(parent *Apdu, numbered bool, counter uint8) {
     m.Parent.Numbered = numbered
     m.Parent.Counter = counter
 }
 
-func NewApduControlContainer(controlApdu *ApduControl, dataLength uint8, numbered bool, counter uint8) *Apdu {
+func NewApduControlContainer(controlApdu *ApduControl, numbered bool, counter uint8) *Apdu {
     child := &ApduControlContainer{
         ControlApdu: controlApdu,
-        Parent: NewApdu(dataLength, numbered, counter),
+        Parent: NewApdu(numbered, counter),
     }
     child.Parent.Child = child
     return child.Parent
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduDataContainer.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduDataContainer.go
index a866208..567b042 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduDataContainer.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/ApduDataContainer.go
@@ -48,16 +48,15 @@ func (m *ApduDataContainer) Control() uint8 {
 }
 
 
-func (m *ApduDataContainer) InitializeParent(parent *Apdu, dataLength uint8, numbered bool, counter uint8) {
-    m.Parent.DataLength = dataLength
+func (m *ApduDataContainer) InitializeParent(parent *Apdu, numbered bool, counter uint8) {
     m.Parent.Numbered = numbered
     m.Parent.Counter = counter
 }
 
-func NewApduDataContainer(dataApdu *ApduData, dataLength uint8, numbered bool, counter uint8) *Apdu {
+func NewApduDataContainer(dataApdu *ApduData, numbered bool, counter uint8) *Apdu {
     child := &ApduDataContainer{
         DataApdu: dataApdu,
-        Parent: NewApdu(dataLength, numbered, counter),
+        Parent: NewApdu(numbered, counter),
     }
     child.Parent.Child = child
     return child.Parent
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/KnxProperty.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/KnxProperty.go
index 177a78f..9525f35 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/KnxProperty.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/KnxProperty.go
@@ -25,7 +25,7 @@ import (
             api "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
 )
 
-func KnxPropertyParse(io *utils.ReadBuffer, propertyType KnxPropertyDataType, dataLength uint8) (api.PlcValue, error) {
+func KnxPropertyParse(io *utils.ReadBuffer, propertyType KnxPropertyDataType, dataLengthInBytes uint8) (api.PlcValue, error) {
     switch {
         case propertyType == KnxPropertyDataType_PDT_CONTROL: // BOOL
 
@@ -64,6 +64,14 @@ func KnxPropertyParse(io *utils.ReadBuffer, propertyType KnxPropertyDataType, da
                 return nil, errors.New("Error parsing 'value' field " + _valueErr.Error())
             }
             return values.NewPlcINT(value), nil
+        case propertyType == KnxPropertyDataType_PDT_UNSIGNED_INT && dataLengthInBytes == 4: // UDINT
+
+            // Simple Field (value)
+            value, _valueErr := io.ReadUint32(32)
+            if _valueErr != nil {
+                return nil, errors.New("Error parsing 'value' field " + _valueErr.Error())
+            }
+            return values.NewPlcUDINT(value), nil
         case propertyType == KnxPropertyDataType_PDT_UNSIGNED_INT: // UINT
 
             // Simple Field (value)
@@ -799,7 +807,7 @@ func KnxPropertyParse(io *utils.ReadBuffer, propertyType KnxPropertyDataType, da
 
             // Array Field (value)
             var value []api.PlcValue
-            for i := 0; i < int(dataLength); i++ {
+            for i := 0; i < int(dataLengthInBytes); i++ {
                 _item, _itemErr := io.ReadUint8(8)
                 if _itemErr != nil {
                     return nil, errors.New("Error parsing 'value' field " + _itemErr.Error())
@@ -811,7 +819,7 @@ func KnxPropertyParse(io *utils.ReadBuffer, propertyType KnxPropertyDataType, da
     return nil, errors.New("unsupported type")
 }
 
-func KnxPropertySerialize(io *utils.WriteBuffer, value api.PlcValue, propertyType KnxPropertyDataType, dataLength uint8) error {
+func KnxPropertySerialize(io *utils.WriteBuffer, value api.PlcValue, propertyType KnxPropertyDataType, dataLengthInBytes uint8) error {
     switch {
         case propertyType == KnxPropertyDataType_PDT_CONTROL: // BOOL
 
@@ -842,6 +850,12 @@ func KnxPropertySerialize(io *utils.WriteBuffer, value api.PlcValue, propertyTyp
             if _err := io.WriteInt16(16, value.GetInt16()); _err != nil {
                 return errors.New("Error serializing 'value' field " + _err.Error())
             }
+        case propertyType == KnxPropertyDataType_PDT_UNSIGNED_INT && dataLengthInBytes == 4: // UDINT
+
+            // Simple Field (value)
+            if _err := io.WriteUint32(32, value.GetUint32()); _err != nil {
+                return errors.New("Error serializing 'value' field " + _err.Error())
+            }
         case propertyType == KnxPropertyDataType_PDT_UNSIGNED_INT: // UINT
 
             // Simple Field (value)
@@ -1395,7 +1409,7 @@ func KnxPropertySerialize(io *utils.WriteBuffer, value api.PlcValue, propertyTyp
         default: // List
 
             // Array Field (value)
-            for i := uint32(0); i < uint32(dataLength); i++ {
+            for i := uint32(0); i < uint32(dataLengthInBytes); i++ {
                 _itemErr := io.WriteUint8(8, value.GetIndex(i).GetUint8())
                 if _itemErr != nil {
                     return errors.New("Error serializing 'value' field " + _itemErr.Error())
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LBusmonInd.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LBusmonInd.go
index 050e47d..5877709 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/LBusmonInd.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/LBusmonInd.go
@@ -146,7 +146,7 @@ func LBusmonIndParse(io *utils.ReadBuffer) (*CEMI, error) {
 
     // Optional Field (crc) (Can be skipped, if a given expression evaluates to false)
     var crc *uint8 = nil
-    if dataFrame.NotAckFrame {
+    if CastLDataFrame(dataFrame).NotAckFrame() {
         _val, _err := io.ReadUint8(8)
         if _err != nil {
             return nil, errors.New("Error parsing 'crc' field " + _err.Error())
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameDataExt.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataExtended.go
similarity index 79%
rename from plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameDataExt.go
rename to plc4go/internal/plc4go/knxnetip/readwrite/model/LDataExtended.go
index c875cf1..76dae80 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameDataExt.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataExtended.go
@@ -27,7 +27,7 @@ import (
 )
 
 // The data-structure of this message
-type LDataFrameDataExt struct {
+type LDataExtended struct {
     GroupAddress bool
     HopCount uint8
     ExtendedFrameFormat uint8
@@ -35,11 +35,11 @@ type LDataFrameDataExt struct {
     DestinationAddress []int8
     Apdu *Apdu
     Parent *LDataFrame
-    ILDataFrameDataExt
+    ILDataExtended
 }
 
 // The corresponding interface
-type ILDataFrameDataExt interface {
+type ILDataExtended interface {
     LengthInBytes() uint16
     LengthInBits() uint16
     Serialize(io utils.WriteBuffer) error
@@ -49,61 +49,61 @@ type ILDataFrameDataExt interface {
 ///////////////////////////////////////////////////////////
 // Accessors for discriminator values.
 ///////////////////////////////////////////////////////////
-func (m *LDataFrameDataExt) ExtendedFrame() bool {
+func (m *LDataExtended) NotAckFrame() bool {
     return true
 }
 
-func (m *LDataFrameDataExt) Polling() bool {
+func (m *LDataExtended) Polling() bool {
     return false
 }
 
 
-func (m *LDataFrameDataExt) InitializeParent(parent *LDataFrame, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
-    m.Parent.Repeated = repeated
-    m.Parent.NotAckFrame = notAckFrame
+func (m *LDataExtended) InitializeParent(parent *LDataFrame, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
+    m.Parent.FrameType = frameType
+    m.Parent.NotRepeated = notRepeated
     m.Parent.Priority = priority
     m.Parent.AcknowledgeRequested = acknowledgeRequested
     m.Parent.ErrorFlag = errorFlag
 }
 
-func NewLDataFrameDataExt(groupAddress bool, hopCount uint8, extendedFrameFormat uint8, sourceAddress *KnxAddress, destinationAddress []int8, apdu *Apdu, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
-    child := &LDataFrameDataExt{
+func NewLDataExtended(groupAddress bool, hopCount uint8, extendedFrameFormat uint8, sourceAddress *KnxAddress, destinationAddress []int8, apdu *Apdu, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
+    child := &LDataExtended{
         GroupAddress: groupAddress,
         HopCount: hopCount,
         ExtendedFrameFormat: extendedFrameFormat,
         SourceAddress: sourceAddress,
         DestinationAddress: destinationAddress,
         Apdu: apdu,
-        Parent: NewLDataFrame(repeated, notAckFrame, priority, acknowledgeRequested, errorFlag),
+        Parent: NewLDataFrame(frameType, notRepeated, priority, acknowledgeRequested, errorFlag),
     }
     child.Parent.Child = child
     return child.Parent
 }
 
-func CastLDataFrameDataExt(structType interface{}) *LDataFrameDataExt {
-    castFunc := func(typ interface{}) *LDataFrameDataExt {
-        if casted, ok := typ.(LDataFrameDataExt); ok {
+func CastLDataExtended(structType interface{}) *LDataExtended {
+    castFunc := func(typ interface{}) *LDataExtended {
+        if casted, ok := typ.(LDataExtended); ok {
             return &casted
         }
-        if casted, ok := typ.(*LDataFrameDataExt); ok {
+        if casted, ok := typ.(*LDataExtended); ok {
             return casted
         }
         if casted, ok := typ.(LDataFrame); ok {
-            return CastLDataFrameDataExt(casted.Child)
+            return CastLDataExtended(casted.Child)
         }
         if casted, ok := typ.(*LDataFrame); ok {
-            return CastLDataFrameDataExt(casted.Child)
+            return CastLDataExtended(casted.Child)
         }
         return nil
     }
     return castFunc(structType)
 }
 
-func (m *LDataFrameDataExt) GetTypeName() string {
-    return "LDataFrameDataExt"
+func (m *LDataExtended) GetTypeName() string {
+    return "LDataExtended"
 }
 
-func (m *LDataFrameDataExt) LengthInBits() uint16 {
+func (m *LDataExtended) LengthInBits() uint16 {
     lengthInBits := uint16(0)
 
     // Simple field (groupAddress)
@@ -123,17 +123,20 @@ func (m *LDataFrameDataExt) LengthInBits() uint16 {
         lengthInBits += 8 * uint16(len(m.DestinationAddress))
     }
 
+    // Implicit Field (dataLength)
+    lengthInBits += 8
+
     // Simple field (apdu)
     lengthInBits += m.Apdu.LengthInBits()
 
     return lengthInBits
 }
 
-func (m *LDataFrameDataExt) LengthInBytes() uint16 {
+func (m *LDataExtended) LengthInBytes() uint16 {
     return m.LengthInBits() / 8
 }
 
-func LDataFrameDataExtParse(io *utils.ReadBuffer) (*LDataFrame, error) {
+func LDataExtendedParse(io *utils.ReadBuffer) (*LDataFrame, error) {
 
     // Simple Field (groupAddress)
     groupAddress, _groupAddressErr := io.ReadBit()
@@ -170,14 +173,20 @@ func LDataFrameDataExtParse(io *utils.ReadBuffer) (*LDataFrame, error) {
         destinationAddress[curItem] = _item
     }
 
+    // Implicit Field (dataLength) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+    dataLength, _dataLengthErr := io.ReadUint8(8)
+    if _dataLengthErr != nil {
+        return nil, errors.New("Error parsing 'dataLength' field " + _dataLengthErr.Error())
+    }
+
     // Simple Field (apdu)
-    apdu, _apduErr := ApduParse(io)
+    apdu, _apduErr := ApduParse(io, dataLength)
     if _apduErr != nil {
         return nil, errors.New("Error parsing 'apdu' field " + _apduErr.Error())
     }
 
     // Create a partially initialized instance
-    _child := &LDataFrameDataExt{
+    _child := &LDataExtended{
         GroupAddress: groupAddress,
         HopCount: hopCount,
         ExtendedFrameFormat: extendedFrameFormat,
@@ -190,7 +199,7 @@ func LDataFrameDataExtParse(io *utils.ReadBuffer) (*LDataFrame, error) {
     return _child.Parent, nil
 }
 
-func (m *LDataFrameDataExt) Serialize(io utils.WriteBuffer) error {
+func (m *LDataExtended) Serialize(io utils.WriteBuffer) error {
     ser := func() error {
 
     // Simple Field (groupAddress)
@@ -230,6 +239,13 @@ func (m *LDataFrameDataExt) Serialize(io utils.WriteBuffer) error {
         }
     }
 
+    // Implicit Field (dataLength) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+    dataLength := uint8(uint8(m.Apdu.LengthInBytes()) - uint8(uint8(1)))
+    _dataLengthErr := io.WriteUint8(8, (dataLength))
+    if _dataLengthErr != nil {
+        return errors.New("Error serializing 'dataLength' field " + _dataLengthErr.Error())
+    }
+
     // Simple Field (apdu)
     _apduErr := m.Apdu.Serialize(io)
     if _apduErr != nil {
@@ -241,7 +257,7 @@ func (m *LDataFrameDataExt) Serialize(io utils.WriteBuffer) error {
     return m.Parent.SerializeParent(io, m, ser)
 }
 
-func (m *LDataFrameDataExt) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+func (m *LDataExtended) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
     var token xml.Token
     var err error
     token = start
@@ -303,7 +319,7 @@ func (m *LDataFrameDataExt) UnmarshalXML(d *xml.Decoder, start xml.StartElement)
     }
 }
 
-func (m *LDataFrameDataExt) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (m *LDataExtended) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
     if err := e.EncodeElement(m.GroupAddress, xml.StartElement{Name: xml.Name{Local: "groupAddress"}}); err != nil {
         return err
     }
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrame.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrame.go
index 8bf3bff..eececbf 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrame.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrame.go
@@ -29,8 +29,8 @@ import (
 
 // The data-structure of this message
 type LDataFrame struct {
-    Repeated bool
-    NotAckFrame bool
+    FrameType bool
+    NotRepeated bool
     Priority CEMIPriority
     AcknowledgeRequested bool
     ErrorFlag bool
@@ -41,7 +41,7 @@ type LDataFrame struct {
 
 // The corresponding interface
 type ILDataFrame interface {
-    ExtendedFrame() bool
+    NotAckFrame() bool
     Polling() bool
     LengthInBytes() uint16
     LengthInBits() uint16
@@ -56,13 +56,13 @@ type ILDataFrameParent interface {
 
 type ILDataFrameChild interface {
     Serialize(io utils.WriteBuffer) error
-    InitializeParent(parent *LDataFrame, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool)
+    InitializeParent(parent *LDataFrame, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool)
     GetTypeName() string
     ILDataFrame
 }
 
-func NewLDataFrame(repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
-    return &LDataFrame{Repeated: repeated, NotAckFrame: notAckFrame, Priority: priority, AcknowledgeRequested: acknowledgeRequested, ErrorFlag: errorFlag}
+func NewLDataFrame(frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
+    return &LDataFrame{FrameType: frameType, NotRepeated: notRepeated, Priority: priority, AcknowledgeRequested: acknowledgeRequested, ErrorFlag: errorFlag}
 }
 
 func CastLDataFrame(structType interface{}) *LDataFrame {
@@ -85,16 +85,16 @@ func (m *LDataFrame) GetTypeName() string {
 func (m *LDataFrame) LengthInBits() uint16 {
     lengthInBits := uint16(0)
 
-    // Discriminator Field (extendedFrame)
+    // Simple field (frameType)
     lengthInBits += 1
 
     // Discriminator Field (polling)
     lengthInBits += 1
 
-    // Simple field (repeated)
+    // Simple field (notRepeated)
     lengthInBits += 1
 
-    // Simple field (notAckFrame)
+    // Discriminator Field (notAckFrame)
     lengthInBits += 1
 
     // Enum Field (priority)
@@ -118,10 +118,10 @@ func (m *LDataFrame) LengthInBytes() uint16 {
 
 func LDataFrameParse(io *utils.ReadBuffer) (*LDataFrame, error) {
 
-    // Discriminator Field (extendedFrame) (Used as input to a switch field)
-    extendedFrame, _extendedFrameErr := io.ReadBit()
-    if _extendedFrameErr != nil {
-        return nil, errors.New("Error parsing 'extendedFrame' field " + _extendedFrameErr.Error())
+    // Simple Field (frameType)
+    frameType, _frameTypeErr := io.ReadBit()
+    if _frameTypeErr != nil {
+        return nil, errors.New("Error parsing 'frameType' field " + _frameTypeErr.Error())
     }
 
     // Discriminator Field (polling) (Used as input to a switch field)
@@ -130,13 +130,13 @@ func LDataFrameParse(io *utils.ReadBuffer) (*LDataFrame, error) {
         return nil, errors.New("Error parsing 'polling' field " + _pollingErr.Error())
     }
 
-    // Simple Field (repeated)
-    repeated, _repeatedErr := io.ReadBit()
-    if _repeatedErr != nil {
-        return nil, errors.New("Error parsing 'repeated' field " + _repeatedErr.Error())
+    // Simple Field (notRepeated)
+    notRepeated, _notRepeatedErr := io.ReadBit()
+    if _notRepeatedErr != nil {
+        return nil, errors.New("Error parsing 'notRepeated' field " + _notRepeatedErr.Error())
     }
 
-    // Simple Field (notAckFrame)
+    // Discriminator Field (notAckFrame) (Used as input to a switch field)
     notAckFrame, _notAckFrameErr := io.ReadBit()
     if _notAckFrameErr != nil {
         return nil, errors.New("Error parsing 'notAckFrame' field " + _notAckFrameErr.Error())
@@ -164,21 +164,19 @@ func LDataFrameParse(io *utils.ReadBuffer) (*LDataFrame, error) {
     var _parent *LDataFrame
     var typeSwitchError error
     switch {
-    case extendedFrame == false && polling == false:
-        _parent, typeSwitchError = LDataFrameDataParse(io)
-    case extendedFrame == true && polling == false:
-        _parent, typeSwitchError = LDataFrameDataExtParse(io)
-    case extendedFrame == true && polling == true:
-        _parent, typeSwitchError = LDataFramePollingDataParse(io)
-    case extendedFrame == false && polling == true:
-        _parent, typeSwitchError = LDataFramePollingDataParse(io)
+    case notAckFrame == true && polling == false:
+        _parent, typeSwitchError = LDataExtendedParse(io)
+    case notAckFrame == true && polling == true:
+        _parent, typeSwitchError = LPollDataParse(io)
+    case notAckFrame == false:
+        _parent, typeSwitchError = LDataFrameACKParse(io)
     }
     if typeSwitchError != nil {
         return nil, errors.New("Error parsing sub-type for type-switch. " + typeSwitchError.Error())
     }
 
     // Finish initializing
-    _parent.Child.InitializeParent(_parent, repeated, notAckFrame, priority, acknowledgeRequested, errorFlag)
+    _parent.Child.InitializeParent(_parent, frameType, notRepeated, priority, acknowledgeRequested, errorFlag)
     return _parent, nil
 }
 
@@ -188,11 +186,11 @@ func (m *LDataFrame) Serialize(io utils.WriteBuffer) error {
 
 func (m *LDataFrame) SerializeParent(io utils.WriteBuffer, child ILDataFrame, serializeChildFunction func() error) error {
 
-    // Discriminator Field (extendedFrame) (Used as input to a switch field)
-    extendedFrame := bool(child.ExtendedFrame())
-    _extendedFrameErr := io.WriteBit((extendedFrame))
-    if _extendedFrameErr != nil {
-        return errors.New("Error serializing 'extendedFrame' field " + _extendedFrameErr.Error())
+    // Simple Field (frameType)
+    frameType := bool(m.FrameType)
+    _frameTypeErr := io.WriteBit((frameType))
+    if _frameTypeErr != nil {
+        return errors.New("Error serializing 'frameType' field " + _frameTypeErr.Error())
     }
 
     // Discriminator Field (polling) (Used as input to a switch field)
@@ -202,15 +200,15 @@ func (m *LDataFrame) SerializeParent(io utils.WriteBuffer, child ILDataFrame, se
         return errors.New("Error serializing 'polling' field " + _pollingErr.Error())
     }
 
-    // Simple Field (repeated)
-    repeated := bool(m.Repeated)
-    _repeatedErr := io.WriteBit((repeated))
-    if _repeatedErr != nil {
-        return errors.New("Error serializing 'repeated' field " + _repeatedErr.Error())
+    // Simple Field (notRepeated)
+    notRepeated := bool(m.NotRepeated)
+    _notRepeatedErr := io.WriteBit((notRepeated))
+    if _notRepeatedErr != nil {
+        return errors.New("Error serializing 'notRepeated' field " + _notRepeatedErr.Error())
     }
 
-    // Simple Field (notAckFrame)
-    notAckFrame := bool(m.NotAckFrame)
+    // Discriminator Field (notAckFrame) (Used as input to a switch field)
+    notAckFrame := bool(child.NotAckFrame())
     _notAckFrameErr := io.WriteBit((notAckFrame))
     if _notAckFrameErr != nil {
         return errors.New("Error serializing 'notAckFrame' field " + _notAckFrameErr.Error())
@@ -261,18 +259,18 @@ func (m *LDataFrame) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
         case xml.StartElement:
             tok := token.(xml.StartElement)
             switch tok.Name.Local {
-            case "repeated":
+            case "frameType":
                 var data bool
                 if err := d.DecodeElement(&data, &tok); err != nil {
                     return err
                 }
-                m.Repeated = data
-            case "notAckFrame":
+                m.FrameType = data
+            case "notRepeated":
                 var data bool
                 if err := d.DecodeElement(&data, &tok); err != nil {
                     return err
                 }
-                m.NotAckFrame = data
+                m.NotRepeated = data
             case "priority":
                 var data CEMIPriority
                 if err := d.DecodeElement(&data, &tok); err != nil {
@@ -293,10 +291,10 @@ func (m *LDataFrame) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
                 m.ErrorFlag = data
             default:
                 switch start.Attr[0].Value {
-                    case "org.apache.plc4x.java.knxnetip.readwrite.LDataFrameData":
-                        var dt *LDataFrameData
+                    case "org.apache.plc4x.java.knxnetip.readwrite.LDataExtended":
+                        var dt *LDataExtended
                         if m.Child != nil {
-                            dt = m.Child.(*LDataFrameData)
+                            dt = m.Child.(*LDataExtended)
                         }
                         if err := d.DecodeElement(&dt, &tok); err != nil {
                             return err
@@ -305,10 +303,10 @@ func (m *LDataFrame) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
                             dt.Parent = m
                             m.Child = dt
                         }
-                    case "org.apache.plc4x.java.knxnetip.readwrite.LDataFrameDataExt":
-                        var dt *LDataFrameDataExt
+                    case "org.apache.plc4x.java.knxnetip.readwrite.LPollData":
+                        var dt *LPollData
                         if m.Child != nil {
-                            dt = m.Child.(*LDataFrameDataExt)
+                            dt = m.Child.(*LPollData)
                         }
                         if err := d.DecodeElement(&dt, &tok); err != nil {
                             return err
@@ -317,10 +315,10 @@ func (m *LDataFrame) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
                             dt.Parent = m
                             m.Child = dt
                         }
-                    case "org.apache.plc4x.java.knxnetip.readwrite.LDataFramePollingData":
-                        var dt *LDataFramePollingData
+                    case "org.apache.plc4x.java.knxnetip.readwrite.LDataFrameACK":
+                        var dt *LDataFrameACK
                         if m.Child != nil {
-                            dt = m.Child.(*LDataFramePollingData)
+                            dt = m.Child.(*LDataFrameACK)
                         }
                         if err := d.DecodeElement(&dt, &tok); err != nil {
                             return err
@@ -343,10 +341,10 @@ func (m *LDataFrame) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
         }}); err != nil {
         return err
     }
-    if err := e.EncodeElement(m.Repeated, xml.StartElement{Name: xml.Name{Local: "repeated"}}); err != nil {
+    if err := e.EncodeElement(m.FrameType, xml.StartElement{Name: xml.Name{Local: "frameType"}}); err != nil {
         return err
     }
-    if err := e.EncodeElement(m.NotAckFrame, xml.StartElement{Name: xml.Name{Local: "notAckFrame"}}); err != nil {
+    if err := e.EncodeElement(m.NotRepeated, xml.StartElement{Name: xml.Name{Local: "notRepeated"}}); err != nil {
         return err
     }
     if err := e.EncodeElement(m.Priority, xml.StartElement{Name: xml.Name{Local: "priority"}}); err != nil {
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameACK.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameACK.go
new file mode 100644
index 0000000..2800681
--- /dev/null
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameACK.go
@@ -0,0 +1,144 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+package model
+
+import (
+    "encoding/xml"
+    "github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+    "io"
+)
+
+// The data-structure of this message
+type LDataFrameACK struct {
+    Parent *LDataFrame
+    ILDataFrameACK
+}
+
+// The corresponding interface
+type ILDataFrameACK interface {
+    LengthInBytes() uint16
+    LengthInBits() uint16
+    Serialize(io utils.WriteBuffer) error
+    xml.Marshaler
+}
+
+///////////////////////////////////////////////////////////
+// Accessors for discriminator values.
+///////////////////////////////////////////////////////////
+func (m *LDataFrameACK) NotAckFrame() bool {
+    return false
+}
+
+func (m *LDataFrameACK) Polling() bool {
+    return false
+}
+
+
+func (m *LDataFrameACK) InitializeParent(parent *LDataFrame, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
+    m.Parent.FrameType = frameType
+    m.Parent.NotRepeated = notRepeated
+    m.Parent.Priority = priority
+    m.Parent.AcknowledgeRequested = acknowledgeRequested
+    m.Parent.ErrorFlag = errorFlag
+}
+
+func NewLDataFrameACK(frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
+    child := &LDataFrameACK{
+        Parent: NewLDataFrame(frameType, notRepeated, priority, acknowledgeRequested, errorFlag),
+    }
+    child.Parent.Child = child
+    return child.Parent
+}
+
+func CastLDataFrameACK(structType interface{}) *LDataFrameACK {
+    castFunc := func(typ interface{}) *LDataFrameACK {
+        if casted, ok := typ.(LDataFrameACK); ok {
+            return &casted
+        }
+        if casted, ok := typ.(*LDataFrameACK); ok {
+            return casted
+        }
+        if casted, ok := typ.(LDataFrame); ok {
+            return CastLDataFrameACK(casted.Child)
+        }
+        if casted, ok := typ.(*LDataFrame); ok {
+            return CastLDataFrameACK(casted.Child)
+        }
+        return nil
+    }
+    return castFunc(structType)
+}
+
+func (m *LDataFrameACK) GetTypeName() string {
+    return "LDataFrameACK"
+}
+
+func (m *LDataFrameACK) LengthInBits() uint16 {
+    lengthInBits := uint16(0)
+
+    return lengthInBits
+}
+
+func (m *LDataFrameACK) LengthInBytes() uint16 {
+    return m.LengthInBits() / 8
+}
+
+func LDataFrameACKParse(io *utils.ReadBuffer) (*LDataFrame, error) {
+
+    // Create a partially initialized instance
+    _child := &LDataFrameACK{
+        Parent: &LDataFrame{},
+    }
+    _child.Parent.Child = _child
+    return _child.Parent, nil
+}
+
+func (m *LDataFrameACK) Serialize(io utils.WriteBuffer) error {
+    ser := func() error {
+
+        return nil
+    }
+    return m.Parent.SerializeParent(io, m, ser)
+}
+
+func (m *LDataFrameACK) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+    var token xml.Token
+    var err error
+    token = start
+    for {
+        switch token.(type) {
+        case xml.StartElement:
+            tok := token.(xml.StartElement)
+            switch tok.Name.Local {
+            }
+        }
+        token, err = d.Token()
+        if err != nil {
+            if err == io.EOF {
+                return nil
+            }
+            return err
+        }
+    }
+}
+
+func (m *LDataFrameACK) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+    return nil
+}
+
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameData.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameData.go
deleted file mode 100644
index 6821d11..0000000
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFrameData.go
+++ /dev/null
@@ -1,301 +0,0 @@
-//
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-//
-package model
-
-import (
-    "encoding/base64"
-    "encoding/xml"
-    "errors"
-    "github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
-    "io"
-)
-
-// The data-structure of this message
-type LDataFrameData struct {
-    SourceAddress *KnxAddress
-    DestinationAddress []int8
-    GroupAddress bool
-    HopCount uint8
-    Apdu *Apdu
-    Parent *LDataFrame
-    ILDataFrameData
-}
-
-// The corresponding interface
-type ILDataFrameData interface {
-    LengthInBytes() uint16
-    LengthInBits() uint16
-    Serialize(io utils.WriteBuffer) error
-    xml.Marshaler
-}
-
-///////////////////////////////////////////////////////////
-// Accessors for discriminator values.
-///////////////////////////////////////////////////////////
-func (m *LDataFrameData) ExtendedFrame() bool {
-    return false
-}
-
-func (m *LDataFrameData) Polling() bool {
-    return false
-}
-
-
-func (m *LDataFrameData) InitializeParent(parent *LDataFrame, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
-    m.Parent.Repeated = repeated
-    m.Parent.NotAckFrame = notAckFrame
-    m.Parent.Priority = priority
-    m.Parent.AcknowledgeRequested = acknowledgeRequested
-    m.Parent.ErrorFlag = errorFlag
-}
-
-func NewLDataFrameData(sourceAddress *KnxAddress, destinationAddress []int8, groupAddress bool, hopCount uint8, apdu *Apdu, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
-    child := &LDataFrameData{
-        SourceAddress: sourceAddress,
-        DestinationAddress: destinationAddress,
-        GroupAddress: groupAddress,
-        HopCount: hopCount,
-        Apdu: apdu,
-        Parent: NewLDataFrame(repeated, notAckFrame, priority, acknowledgeRequested, errorFlag),
-    }
-    child.Parent.Child = child
-    return child.Parent
-}
-
-func CastLDataFrameData(structType interface{}) *LDataFrameData {
-    castFunc := func(typ interface{}) *LDataFrameData {
-        if casted, ok := typ.(LDataFrameData); ok {
-            return &casted
-        }
-        if casted, ok := typ.(*LDataFrameData); ok {
-            return casted
-        }
-        if casted, ok := typ.(LDataFrame); ok {
-            return CastLDataFrameData(casted.Child)
-        }
-        if casted, ok := typ.(*LDataFrame); ok {
-            return CastLDataFrameData(casted.Child)
-        }
-        return nil
-    }
-    return castFunc(structType)
-}
-
-func (m *LDataFrameData) GetTypeName() string {
-    return "LDataFrameData"
-}
-
-func (m *LDataFrameData) LengthInBits() uint16 {
-    lengthInBits := uint16(0)
-
-    // Simple field (sourceAddress)
-    lengthInBits += m.SourceAddress.LengthInBits()
-
-    // Array field
-    if len(m.DestinationAddress) > 0 {
-        lengthInBits += 8 * uint16(len(m.DestinationAddress))
-    }
-
-    // Simple field (groupAddress)
-    lengthInBits += 1
-
-    // Simple field (hopCount)
-    lengthInBits += 3
-
-    // Simple field (apdu)
-    lengthInBits += m.Apdu.LengthInBits()
-
-    return lengthInBits
-}
-
-func (m *LDataFrameData) LengthInBytes() uint16 {
-    return m.LengthInBits() / 8
-}
-
-func LDataFrameDataParse(io *utils.ReadBuffer) (*LDataFrame, error) {
-
-    // Simple Field (sourceAddress)
-    sourceAddress, _sourceAddressErr := KnxAddressParse(io)
-    if _sourceAddressErr != nil {
-        return nil, errors.New("Error parsing 'sourceAddress' field " + _sourceAddressErr.Error())
-    }
-
-    // Array field (destinationAddress)
-    // Count array
-    destinationAddress := make([]int8, uint16(2))
-    for curItem := uint16(0); curItem < uint16(uint16(2)); curItem++ {
-        _item, _err := io.ReadInt8(8)
-        if _err != nil {
-            return nil, errors.New("Error parsing 'destinationAddress' field " + _err.Error())
-        }
-        destinationAddress[curItem] = _item
-    }
-
-    // Simple Field (groupAddress)
-    groupAddress, _groupAddressErr := io.ReadBit()
-    if _groupAddressErr != nil {
-        return nil, errors.New("Error parsing 'groupAddress' field " + _groupAddressErr.Error())
-    }
-
-    // Simple Field (hopCount)
-    hopCount, _hopCountErr := io.ReadUint8(3)
-    if _hopCountErr != nil {
-        return nil, errors.New("Error parsing 'hopCount' field " + _hopCountErr.Error())
-    }
-
-    // Simple Field (apdu)
-    apdu, _apduErr := ApduParse(io)
-    if _apduErr != nil {
-        return nil, errors.New("Error parsing 'apdu' field " + _apduErr.Error())
-    }
-
-    // Create a partially initialized instance
-    _child := &LDataFrameData{
-        SourceAddress: sourceAddress,
-        DestinationAddress: destinationAddress,
-        GroupAddress: groupAddress,
-        HopCount: hopCount,
-        Apdu: apdu,
-        Parent: &LDataFrame{},
-    }
-    _child.Parent.Child = _child
-    return _child.Parent, nil
-}
-
-func (m *LDataFrameData) Serialize(io utils.WriteBuffer) error {
-    ser := func() error {
-
-    // Simple Field (sourceAddress)
-    _sourceAddressErr := m.SourceAddress.Serialize(io)
-    if _sourceAddressErr != nil {
-        return errors.New("Error serializing 'sourceAddress' field " + _sourceAddressErr.Error())
-    }
-
-    // Array Field (destinationAddress)
-    if m.DestinationAddress != nil {
-        for _, _element := range m.DestinationAddress {
-            _elementErr := io.WriteInt8(8, _element)
-            if _elementErr != nil {
-                return errors.New("Error serializing 'destinationAddress' field " + _elementErr.Error())
-            }
-        }
-    }
-
-    // Simple Field (groupAddress)
-    groupAddress := bool(m.GroupAddress)
-    _groupAddressErr := io.WriteBit((groupAddress))
-    if _groupAddressErr != nil {
-        return errors.New("Error serializing 'groupAddress' field " + _groupAddressErr.Error())
-    }
-
-    // Simple Field (hopCount)
-    hopCount := uint8(m.HopCount)
-    _hopCountErr := io.WriteUint8(3, (hopCount))
-    if _hopCountErr != nil {
-        return errors.New("Error serializing 'hopCount' field " + _hopCountErr.Error())
-    }
-
-    // Simple Field (apdu)
-    _apduErr := m.Apdu.Serialize(io)
-    if _apduErr != nil {
-        return errors.New("Error serializing 'apdu' field " + _apduErr.Error())
-    }
-
-        return nil
-    }
-    return m.Parent.SerializeParent(io, m, ser)
-}
-
-func (m *LDataFrameData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
-    var token xml.Token
-    var err error
-    token = start
-    for {
-        switch token.(type) {
-        case xml.StartElement:
-            tok := token.(xml.StartElement)
-            switch tok.Name.Local {
-            case "sourceAddress":
-                var data *KnxAddress
-                if err := d.DecodeElement(data, &tok); err != nil {
-                    return err
-                }
-                m.SourceAddress = data
-            case "destinationAddress":
-                var _encoded string
-                if err := d.DecodeElement(&_encoded, &tok); err != nil {
-                    return err
-                }
-                _decoded := make([]byte, base64.StdEncoding.DecodedLen(len(_encoded)))
-                _len, err := base64.StdEncoding.Decode(_decoded, []byte(_encoded))
-                if err != nil {
-                    return err
-                }
-                m.DestinationAddress = utils.ByteArrayToInt8Array(_decoded[0:_len])
-            case "groupAddress":
-                var data bool
-                if err := d.DecodeElement(&data, &tok); err != nil {
-                    return err
-                }
-                m.GroupAddress = data
-            case "hopCount":
-                var data uint8
-                if err := d.DecodeElement(&data, &tok); err != nil {
-                    return err
-                }
-                m.HopCount = data
-            case "apdu":
-                var dt *Apdu
-                if err := d.DecodeElement(&dt, &tok); err != nil {
-                    return err
-                }
-                m.Apdu = dt
-            }
-        }
-        token, err = d.Token()
-        if err != nil {
-            if err == io.EOF {
-                return nil
-            }
-            return err
-        }
-    }
-}
-
-func (m *LDataFrameData) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
-    if err := e.EncodeElement(m.SourceAddress, xml.StartElement{Name: xml.Name{Local: "sourceAddress"}}); err != nil {
-        return err
-    }
-    _encodedDestinationAddress := make([]byte, base64.StdEncoding.EncodedLen(len(m.DestinationAddress)))
-    base64.StdEncoding.Encode(_encodedDestinationAddress, utils.Int8ArrayToByteArray(m.DestinationAddress))
-    if err := e.EncodeElement(_encodedDestinationAddress, xml.StartElement{Name: xml.Name{Local: "destinationAddress"}}); err != nil {
-        return err
-    }
-    if err := e.EncodeElement(m.GroupAddress, xml.StartElement{Name: xml.Name{Local: "groupAddress"}}); err != nil {
-        return err
-    }
-    if err := e.EncodeElement(m.HopCount, xml.StartElement{Name: xml.Name{Local: "hopCount"}}); err != nil {
-        return err
-    }
-    if err := e.EncodeElement(m.Apdu, xml.StartElement{Name: xml.Name{Local: "apdu"}}); err != nil {
-        return err
-    }
-    return nil
-}
-
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFramePollingData.go b/plc4go/internal/plc4go/knxnetip/readwrite/model/LPollData.go
similarity index 80%
rename from plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFramePollingData.go
rename to plc4go/internal/plc4go/knxnetip/readwrite/model/LPollData.go
index a00c2c5..a886230 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/model/LDataFramePollingData.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/model/LPollData.go
@@ -28,16 +28,16 @@ import (
 )
 
 // The data-structure of this message
-type LDataFramePollingData struct {
+type LPollData struct {
     SourceAddress *KnxAddress
     TargetAddress []int8
     NumberExpectedPollData uint8
     Parent *LDataFrame
-    ILDataFramePollingData
+    ILPollData
 }
 
 // The corresponding interface
-type ILDataFramePollingData interface {
+type ILPollData interface {
     LengthInBytes() uint16
     LengthInBits() uint16
     Serialize(io utils.WriteBuffer) error
@@ -47,58 +47,58 @@ type ILDataFramePollingData interface {
 ///////////////////////////////////////////////////////////
 // Accessors for discriminator values.
 ///////////////////////////////////////////////////////////
-func (m *LDataFramePollingData) ExtendedFrame() bool {
-    return false
+func (m *LPollData) NotAckFrame() bool {
+    return true
 }
 
-func (m *LDataFramePollingData) Polling() bool {
+func (m *LPollData) Polling() bool {
     return true
 }
 
 
-func (m *LDataFramePollingData) InitializeParent(parent *LDataFrame, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
-    m.Parent.Repeated = repeated
-    m.Parent.NotAckFrame = notAckFrame
+func (m *LPollData) InitializeParent(parent *LDataFrame, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) {
+    m.Parent.FrameType = frameType
+    m.Parent.NotRepeated = notRepeated
     m.Parent.Priority = priority
     m.Parent.AcknowledgeRequested = acknowledgeRequested
     m.Parent.ErrorFlag = errorFlag
 }
 
-func NewLDataFramePollingData(sourceAddress *KnxAddress, targetAddress []int8, numberExpectedPollData uint8, repeated bool, notAckFrame bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
-    child := &LDataFramePollingData{
+func NewLPollData(sourceAddress *KnxAddress, targetAddress []int8, numberExpectedPollData uint8, frameType bool, notRepeated bool, priority CEMIPriority, acknowledgeRequested bool, errorFlag bool) *LDataFrame {
+    child := &LPollData{
         SourceAddress: sourceAddress,
         TargetAddress: targetAddress,
         NumberExpectedPollData: numberExpectedPollData,
-        Parent: NewLDataFrame(repeated, notAckFrame, priority, acknowledgeRequested, errorFlag),
+        Parent: NewLDataFrame(frameType, notRepeated, priority, acknowledgeRequested, errorFlag),
     }
     child.Parent.Child = child
     return child.Parent
 }
 
-func CastLDataFramePollingData(structType interface{}) *LDataFramePollingData {
-    castFunc := func(typ interface{}) *LDataFramePollingData {
-        if casted, ok := typ.(LDataFramePollingData); ok {
+func CastLPollData(structType interface{}) *LPollData {
+    castFunc := func(typ interface{}) *LPollData {
+        if casted, ok := typ.(LPollData); ok {
             return &casted
         }
-        if casted, ok := typ.(*LDataFramePollingData); ok {
+        if casted, ok := typ.(*LPollData); ok {
             return casted
         }
         if casted, ok := typ.(LDataFrame); ok {
-            return CastLDataFramePollingData(casted.Child)
+            return CastLPollData(casted.Child)
         }
         if casted, ok := typ.(*LDataFrame); ok {
-            return CastLDataFramePollingData(casted.Child)
+            return CastLPollData(casted.Child)
         }
         return nil
     }
     return castFunc(structType)
 }
 
-func (m *LDataFramePollingData) GetTypeName() string {
-    return "LDataFramePollingData"
+func (m *LPollData) GetTypeName() string {
+    return "LPollData"
 }
 
-func (m *LDataFramePollingData) LengthInBits() uint16 {
+func (m *LPollData) LengthInBits() uint16 {
     lengthInBits := uint16(0)
 
     // Simple field (sourceAddress)
@@ -118,11 +118,11 @@ func (m *LDataFramePollingData) LengthInBits() uint16 {
     return lengthInBits
 }
 
-func (m *LDataFramePollingData) LengthInBytes() uint16 {
+func (m *LPollData) LengthInBytes() uint16 {
     return m.LengthInBits() / 8
 }
 
-func LDataFramePollingDataParse(io *utils.ReadBuffer) (*LDataFrame, error) {
+func LPollDataParse(io *utils.ReadBuffer) (*LDataFrame, error) {
 
     // Simple Field (sourceAddress)
     sourceAddress, _sourceAddressErr := KnxAddressParse(io)
@@ -162,7 +162,7 @@ func LDataFramePollingDataParse(io *utils.ReadBuffer) (*LDataFrame, error) {
     }
 
     // Create a partially initialized instance
-    _child := &LDataFramePollingData{
+    _child := &LPollData{
         SourceAddress: sourceAddress,
         TargetAddress: targetAddress,
         NumberExpectedPollData: numberExpectedPollData,
@@ -172,7 +172,7 @@ func LDataFramePollingDataParse(io *utils.ReadBuffer) (*LDataFrame, error) {
     return _child.Parent, nil
 }
 
-func (m *LDataFramePollingData) Serialize(io utils.WriteBuffer) error {
+func (m *LPollData) Serialize(io utils.WriteBuffer) error {
     ser := func() error {
 
     // Simple Field (sourceAddress)
@@ -211,7 +211,7 @@ func (m *LDataFramePollingData) Serialize(io utils.WriteBuffer) error {
     return m.Parent.SerializeParent(io, m, ser)
 }
 
-func (m *LDataFramePollingData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+func (m *LPollData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
     var token xml.Token
     var err error
     token = start
@@ -255,7 +255,7 @@ func (m *LDataFramePollingData) UnmarshalXML(d *xml.Decoder, start xml.StartElem
     }
 }
 
-func (m *LDataFramePollingData) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (m *LPollData) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
     if err := e.EncodeElement(m.SourceAddress, xml.StartElement{Name: xml.Name{Local: "sourceAddress"}}); err != nil {
         return err
     }
diff --git a/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java b/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
index 1bf649b..0bcbb7f 100644
--- a/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
+++ b/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
@@ -344,10 +344,9 @@ public class KnxNetIpProtocolLogic extends Plc4xProtocolBase<KnxNetIpMessage> im
                 new TunnelingRequestDataBlock(communicationChannelId,
                     (short) sequenceCounter.getAndIncrement()),
                 new LDataReq((short) 0, new CEMIAdditionalInformation[0],
-                    new LDataFrameData(false, false, CEMIPriority.LOW, false, false,
-                        knxNetIpDriverContext.getClientKnxAddress(), destinationAddress, true,
-                        (byte) 6, new ApduDataContainer((short) ((data != null) ? data.length + 1 : 1), false, (byte) 0,
-                        new ApduDataGroupValueWrite(dataFirstByte, data)))
+                    new LDataExtended(false, false, CEMIPriority.LOW, false, false,
+                        true, (byte) 6, (byte) 0, knxNetIpDriverContext.getClientKnxAddress(), destinationAddress,
+                        new ApduDataContainer(true, (byte) 0, new ApduDataGroupValueWrite(dataFirstByte, data)))
                 ));
 
             // Start a new request-transaction (Is ended in the response-handler)
@@ -400,20 +399,8 @@ public class KnxNetIpProtocolLogic extends Plc4xProtocolBase<KnxNetIpMessage> im
                 if (tunnelingRequest.getCemi() instanceof LDataInd) {
                     LDataInd dataInd = (LDataInd) tunnelingRequest.getCemi();
                     final LDataFrame lDataFrame = dataInd.getDataFrame();
-                    if (lDataFrame instanceof LDataFrameData) {
-                        LDataFrameData lDataFrameData = (LDataFrameData) lDataFrame;
-                        Apdu apdu = lDataFrameData.getApdu();
-                        if(apdu instanceof ApduDataContainer) {
-                            ApduDataContainer apduDataContainer = (ApduDataContainer) apdu;
-                            ApduData dataApdu = apduDataContainer.getDataApdu();
-                            if(dataApdu instanceof ApduDataGroupValueWrite) {
-                                ApduDataGroupValueWrite groupWrite = (ApduDataGroupValueWrite) dataApdu;
-                                processCemiData(lDataFrameData.getSourceAddress(), lDataFrameData.getDestinationAddress(),
-                                    groupWrite.getDataFirstByte(),groupWrite.getData());
-                            }
-                        }
-                    } else if (lDataFrame instanceof LDataFrameDataExt) {
-                        LDataFrameDataExt lDataFrameDataExt = (LDataFrameDataExt) lDataFrame;
+                    if (lDataFrame instanceof LDataExtended) {
+                        LDataExtended lDataFrameDataExt = (LDataExtended) lDataFrame;
                         Apdu apdu = lDataFrameDataExt.getApdu();
                         if(apdu instanceof ApduDataContainer) {
                             ApduDataContainer apduDataContainer = (ApduDataContainer) apdu;
diff --git a/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec b/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec
index a189c46..534f310 100644
--- a/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec
+++ b/protocols/knxnetip/src/main/resources/protocols/knxnetip/knxnetip.mspec
@@ -347,50 +347,51 @@
 // The CEMI part is described in the document "03_06_03 EMI_IMI v01.03.03 AS" Page 73
 // "03_02_02 Communication Medium TP1 v01.02.02 AS" Page 27
 [discriminatedType 'LDataFrame'
-    [discriminator bit          'extendedFrame']
+    [simple        bit          'frameType']
     [discriminator bit          'polling']
-    [simple        bit          'repeated']
-    [simple        bit          'notAckFrame']
+    [simple        bit          'notRepeated']
+    [discriminator bit          'notAckFrame']
     [enum          CEMIPriority 'priority']
     [simple        bit          'acknowledgeRequested']
     [simple        bit          'errorFlag']
-    [typeSwitch 'extendedFrame','polling'
-       // Page 28ff
-        ['false','false' LDataFrameData
-            [simple   KnxAddress   'sourceAddress']
-            [array    int 8        'destinationAddress' count '2']
-            [simple   bit          'groupAddress']
-            [simple   uint 3       'hopCount']
-            [simple   Apdu         'apdu']
-        ]
+    // "03_02_02 Communication Medium TP1 v01.02.02 AS" Page 27
+    [typeSwitch 'notAckFrame','polling'
         // Page 29ff
-        ['true','false' LDataFrameDataExt
+        // TODO: For some reason it doesn't seem to matter what the frame format is set to, it always seems to be an extended frame
+        // ['true','false','false' LDataExtended
+        ['true','false' LDataExtended
             [simple   bit          'groupAddress']
             [simple   uint 3       'hopCount']
             [simple   uint 4       'extendedFrameFormat']
             [simple   KnxAddress   'sourceAddress']
             [array    int 8        'destinationAddress' count '2']
-            [simple   Apdu         'apdu']
-        ]
+            [implicit uint 8       'dataLength' 'apdu.lengthInBytes - 1']
+            [simple   Apdu         'apdu' ['dataLength']]
+        ]
+        // Page 28ff
+        //['true','false','true' LDataStandard
+        //    [simple   KnxAddress   'sourceAddress']
+        //    [array    int 8        'destinationAddress' count '2']
+        //    [simple   bit          'groupAddress']
+        //    [simple   uint 3       'hopCount']
+        //    [simple   uint 4       'dataLength']
+        //    [simple   Apdu         'apdu' ['dataLength']]
+        //]
         // Page 31ff
-        ['true','true' LDataFramePollingData
+        //['true','true','true' LPollData
+        ['true','true' LPollData
             [simple   KnxAddress   'sourceAddress']
             [array    int 8        'targetAddress' count '2']
             [reserved uint 4       '0x00']
             [simple   uint 6       'numberExpectedPollData']
         ]
-        // Page 31ff
-        ['false','true' LDataFramePollingData
-            [simple   KnxAddress   'sourceAddress']
-            [array    int 8        'targetAddress' count '2']
-            [reserved uint 4       '0x00']
-            [simple   uint 6       'numberExpectedPollData']
+        ['false' LDataFrameACK
+            // TODO: Implement this
         ]
     ]
 ]
 
-[discriminatedType 'Apdu'
-    [simple   uint 8      'dataLength']
+[discriminatedType 'Apdu' [uint 8 'dataLength']
     // 10_01 Logical Tag Extended v01.02.01 AS.pdf Page 74ff
     [discriminator uint 1 'control']
     [simple        bit    'numbered']
@@ -771,8 +772,8 @@
 // - 03_05_01 Resources v01.09.03 AS.pdf
 // - 03_07_03 Standardized Identifier Tables v01.03.01 AS.pdf
 // - 03_07_02 Datapoint Types v01.08.02 AS.pdf
-[dataIo 'KnxProperty' [KnxPropertyDataType 'propertyType', uint 8 'dataLength']
-    [typeSwitch 'propertyType'
+[dataIo 'KnxProperty' [KnxPropertyDataType 'propertyType', uint 8 'dataLengthInBytes']
+    [typeSwitch 'propertyType','dataLengthInBytes'
         ['KnxPropertyDataType.PDT_CONTROL' BOOL
             [reserved uint 7        '0x00']
             [simple   bit           'value']
@@ -786,6 +787,10 @@
         ['KnxPropertyDataType.PDT_INT' INT
             [simple   int 16        'value']
         ]
+        // On some systems this property is bigger
+        ['KnxPropertyDataType.PDT_UNSIGNED_INT','4' UDINT
+            [simple   uint 32       'value']
+        ]
         ['KnxPropertyDataType.PDT_UNSIGNED_INT' UINT
             [simple   uint 16       'value']
         ]
@@ -966,8 +971,8 @@
         //['KnxPropertyDataType.PDT_ESCAPE'
         //]
         // 'KnxPropertyDataType.PDT_VARIABLE_LENGTH' == Catch all
-        [ List [uint 8 'dataLength']
-            [array uint 8 'value' count 'dataLength']
+        [ List [uint 8 'dataLengthInBytes']
+            [array uint 8 'value' count 'dataLengthInBytes']
         ]
     ]
 ]
\ No newline at end of file
diff --git a/protocols/knxnetip/src/test/resources/protocols/knxnetip/ParserSerializerTestsuite.xml b/protocols/knxnetip/src/test/resources/protocols/knxnetip/ParserSerializerTestsuite.xml
index d20bbe2..9e954a3 100644
--- a/protocols/knxnetip/src/test/resources/protocols/knxnetip/ParserSerializerTestsuite.xml
+++ b/protocols/knxnetip/src/test/resources/protocols/knxnetip/ParserSerializerTestsuite.xml
@@ -414,9 +414,9 @@
         <cemi className="org.apache.plc4x.java.knxnetip.readwrite.LDataInd">
           <additionalInformationLength>0</additionalInformationLength>
           <additionalInformation/>
-          <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataFrameDataExt">
-            <repeated>true</repeated>
-            <notAckFrame>true</notAckFrame>
+          <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataExtended">
+            <frameType>true</frameType>
+            <notRepeated>true</notRepeated>
             <priority>LOW</priority>
             <acknowledgeRequested>false</acknowledgeRequested>
             <errorFlag>false</errorFlag>
@@ -430,7 +430,6 @@
             </sourceAddress>
             <destinationAddress>120C</destinationAddress>
             <apdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduDataContainer">
-              <dataLength>1</dataLength>
               <numbered>false</numbered>
               <counter>0</counter>
               <dataApdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduDataGroupValueWrite">
@@ -475,9 +474,9 @@
               </relativeTimestamp>
             </additionalInformation>
           </additionalInformation>
-          <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataFrameDataExt">
-            <repeated>true</repeated>
-            <notAckFrame>true</notAckFrame>
+          <dataFrame className="org.apache.plc4x.java.knxnetip.readwrite.LDataExtended">
+            <frameType>true</frameType>
+            <notRepeated>true</notRepeated>
             <priority>LOW</priority>
             <acknowledgeRequested>false</acknowledgeRequested>
             <errorFlag>false</errorFlag>
@@ -491,7 +490,6 @@
             </sourceAddress>
             <destinationAddress>0CE1</destinationAddress>
             <apdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduControlContainer">
-              <dataLength>0</dataLength>
               <numbered>false</numbered>
               <counter>0</counter>
               <controlApdu className="org.apache.plc4x.java.knxnetip.readwrite.ApduControlDisconnect"/>