Repository: openhab/openhab-addons
Pull request #18631: [freeathome] Add Wireless Blind Actuator support, Improve connection handling
Author: login: ML19821
Labels: name: enhancement
description: An enhancement or new feature for an existing add-on
Created: 2025-05-01T21:35:56Z | Merged: 2025-05-11T13:02:44Z

- Enhancement: Added support for Wireless Blind Actuator devices
- Enhancement: Added keep-alive message option, improved error handling, and enhanced reconnect behavior in the FreeAtHome binding
- Improved (re)connection handling: A SysApp restart is now correctly managed, and a disconnected SysApp is properly detected.
- Added sendPing() as an alternative to sendString().

fixes:
https://github.com/openhab/openhab-addons/issues/18440
https://github.com/openhab/openhab-addons/issues/18441

Community:
https://community.openhab.org/t/abb-busch-jager-free-home-official-rest-api/141698/165

Signed-off-by: Martin Littkovsky <2018turtle@proton.me>



Diff:
diff --git a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/configuration/FreeAtHomeBridgeHandlerConfiguration.java b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/configuration/FreeAtHomeBridgeHandlerConfiguration.java
index 7f29e5cee3d30..ce2fcce16467c 100644
--- a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/configuration/FreeAtHomeBridgeHandlerConfiguration.java
+++ b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/configuration/FreeAtHomeBridgeHandlerConfiguration.java
@@ -28,4 +28,5 @@ public class FreeAtHomeBridgeHandlerConfiguration {
     public String ipAddress = "";
     public String username = "";
     public String password = "";
+    public boolean sendKeepAliveMessage = false;
 }
diff --git a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/datamodel/FreeAtHomeDeviceChannel.java b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/datamodel/FreeAtHomeDeviceChannel.java
index 73b9df161eb2e..c75938d1b876f 100644
--- a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/datamodel/FreeAtHomeDeviceChannel.java
+++ b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/datamodel/FreeAtHomeDeviceChannel.java
@@ -84,7 +84,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -97,12 +97,12 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 17, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -116,7 +116,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 256, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 1, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -129,23 +129,23 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 6, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 7, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1027, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT_AS_OUTPUT, 256, channelId,
                         channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -161,40 +161,40 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 304, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 333, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 331, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 54, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 51, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 320, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 68, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 58, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 56, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 66, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 // Additional channel for RTC device
                 if (Integer.parseInt(channelFunctionID, 16) == FID_ROOM_TEMPERATURE_CONTROLLER_MASTER_WITHOUT_FAN) {
                     newDatapointGroup = new FreeAtHomeDatapointGroup();
                     newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 48, channelId, channelObject);
-                    AddDatapointGroup(newDatapointGroup);
+                    addDatapointGroup(newDatapointGroup);
                 }
 
                 break;
@@ -208,12 +208,12 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 // 53 AL_WINDOW_DOOR Window/Door Open = 1 / closed = 0
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 53, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 // 41 AL_WINDOW_DOOR_POSITION Window/Door position Delivers position for Window/Door(Open/Tilted/Closed)
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 41, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -225,7 +225,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 4, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -238,7 +238,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 61698, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 61697, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -251,7 +251,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 256, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 2, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -264,7 +264,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 2, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -277,7 +277,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 256, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 2, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -291,19 +291,46 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 272, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 17, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 256, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 1, channelId, channelObject);
 
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
+
+                break;
+            }
+            case FID_DIMMING_SENSOR_PUSHBUTTON_TYPE0:
+            case FID_DIMMING_SENSOR_PUSHBUTTON_TYPE1:
+            case FID_DIMMING_SENSOR_PUSHBUTTON_TYPE2:
+            case FID_DIMMING_SENSOR_PUSHBUTTON_TYPE3:
+            case FID_DIMMING_SENSOR_PUSHBUTTON_TYPE4:
+            case FID_DIMMING_SENSOR_PUSHBUTTON_TYPE5:
+            case FID_DIMMING_SENSOR_PUSHBUTTON_TYPE6:
+            case FID_DIMMING_SENSOR_PUSHBUTTON_TYPE7: {
+                this.channelId = channelId;
+
+                logger.debug("Dimming actuator channel - Channel FID: 0x{}", channelFunctionID);
+
+                FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
+                newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 17, channelId, channelObject);
+                newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 272, channelId, channelObject);
+
+                addDatapointGroup(newDatapointGroup);
+
+                newDatapointGroup = new FreeAtHomeDatapointGroup();
+                newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1, channelId, channelObject);
+                newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 256, channelId, channelObject);
+
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
             case FID_AWNING_ACTUATOR:
             case FID_ATTIC_WINDOW_ACTUATOR:
             case FID_BLIND_ACTUATOR:
+            case FID_BLIND_ACTUATOR_WIRELESS:
             case FID_SHUTTER_ACTUATOR: {
                 this.channelId = channelId;
 
@@ -312,17 +339,17 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 32, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 288, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 33, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 288, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 289, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 35, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 /*
                  * 290 AL_CURRENT_ABSOLUTE_POSITION_SLATS_PERCENTAGE Current Absolute Position Slats Percentage Indicate
@@ -333,7 +360,14 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 290, channelId, channelObject);
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 36, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
+
+                break;
+            }
+            case FID_BLIND_SENSOR: {
+                this.channelId = channelId;
+
+                logger.warn("Blind sensor channel - Channel FID: 0x{}, not implemented yet", channelFunctionID);
 
                 break;
             }
@@ -344,11 +378,11 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1026, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1027, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -359,15 +393,15 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 39, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1029, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1030, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -378,17 +412,17 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 if (newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 38, channelId, channelObject)) {
-                    AddDatapointGroup(newDatapointGroup);
+                    addDatapointGroup(newDatapointGroup);
                 }
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 if (newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1024, channelId, channelObject)) {
-                    AddDatapointGroup(newDatapointGroup);
+                    addDatapointGroup(newDatapointGroup);
                 }
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 if (newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 304, channelId, channelObject)) {
-                    AddDatapointGroup(newDatapointGroup);
+                    addDatapointGroup(newDatapointGroup);
                 }
 
                 break;
@@ -400,15 +434,15 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 37, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1025, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1028, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -419,7 +453,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1564, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -430,7 +464,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1563, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -441,7 +475,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 337, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -452,7 +486,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1562, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -463,7 +497,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1565, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -474,7 +508,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1566, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -485,7 +519,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1567, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -496,7 +530,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1569, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -507,7 +541,7 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1568, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
@@ -516,19 +550,18 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
                 logger.debug("Wind Alarm channel - Channel FID: 0x{}", channelFunctionID);
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 37, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
             }
-            case FID_RAIN_ALARM_SENSOR: { // 0x000E Wind Alarm
+            case FID_RAIN_ALARM_SENSOR: { // 0x000E Rain Alarm
                 this.channelId = channelId;
                 logger.debug("Rain Alarm channel - Channel FID: 0x{}", channelFunctionID);
                 FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup();
                 newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 39, channelId, channelObject);
-                AddDatapointGroup(newDatapointGroup);
+                addDatapointGroup(newDatapointGroup);
 
                 break;
-
             }
             case FID_SCENE_SENSOR: {
                 this.channelId = channelId;
@@ -537,7 +570,6 @@ public boolean createChannelFromJson(String deviceLabel, String channelId, JsonO
 
                 break;
             }
-
             default: {
                 logger.debug("Unknown channel found - Channel FID: 0x{}", channelFunctionID);
 
@@ -584,7 +616,7 @@ public void applyChangesForVirtualDevice() {
         }
     }
 
-    private void AddDatapointGroup(FreeAtHomeDatapointGroup DatapointGroup) {
+    private void addDatapointGroup(FreeAtHomeDatapointGroup DatapointGroup) {
         if (DatapointGroup.isValid()) {
             logger.debug("Datapoint group is added");
             datapointGroups.add(DatapointGroup);
diff --git a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
index 914c1d8c22ed2..2467c8e18bd6b 100644
--- a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
+++ b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
@@ -16,6 +16,7 @@
 import java.io.StringReader;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Base64;
 import java.util.Collection;
@@ -83,33 +84,37 @@ public class FreeAtHomeBridgeHandler extends BaseBridgeHandler implements WebSoc
 
     private final Logger logger = LoggerFactory.getLogger(FreeAtHomeBridgeHandler.class);
 
-    private Map<String, FreeAtHomeDeviceHandler> mapEventListeners = new ConcurrentHashMap<>();
+    private final Map<String, FreeAtHomeDeviceHandler> mapEventListeners = new ConcurrentHashMap<>();
 
     // Clients for the network communication
-    private HttpClient httpClient;
+    private final HttpClient httpClient;
     private @Nullable WebSocketClient websocketClient = null;
-    private FreeAtHomeWebsocketMonitorThread socketMonitor = new FreeAtHomeWebsocketMonitorThread();
+    private final FreeAtHomeWebsocketMonitorThread socketMonitor = new FreeAtHomeWebsocketMonitorThread();
     private @Nullable QueuedThreadPool jettyThreadPool = null;
     private volatile @Nullable Session websocketSession = null;
 
-    private String sysApUID = "00000000-0000-0000-0000-000000000000";
+    private final String sysApUID = "00000000-0000-0000-0000-000000000000";
     private String ipAddress = "";
     private String username = "";
     private String password = "";
+    private boolean sendKeepAliveMessage = true;
 
     private String baseUrl = "";
 
     private String authField = "";
 
-    private Lock lock = new ReentrantLock();
-    private AtomicBoolean httpConnectionOK = new AtomicBoolean(false);
-    private Condition websocketSessionEstablished = lock.newCondition();
+    private String sysapVersion = "";
+
+    private final Lock lock = new ReentrantLock();
+    private final AtomicBoolean httpConnectionOK = new AtomicBoolean(false);
+    private final Condition websocketSessionEstablished = lock.newCondition();
+    private volatile long lastReceivedTime = 0;
 
     int numberOfComponents = 0;
 
-    private static final int BRIDGE_WEBSOCKET_RECONNECT_DELAY = 60;
-    private static final int BRIDGE_WEBSOCKET_TIMEOUT = 90;
-    private static final int BRIDGE_WEBSOCKET_KEEPALIVE = 50;
+    private static final int BRIDGE_WEBSOCKET_RECONNECT_DELAY = 5; // Seconds
+    private static final int BRIDGE_WEBSOCKET_TIMEOUT = 90; // Seconds
+    private static final int BRIDGE_WEBSOCKET_KEEPALIVE = 10; // Seconds
     private static final String BRIDGE_URL_GETDEVICELIST = "/rest/devicelist";
 
     public FreeAtHomeBridgeHandler(Bridge thing, HttpClient client) {
@@ -131,10 +136,85 @@ public Collection<Class<? extends ThingHandlerService>> getServices() {
         return List.of(FreeAtHomeDiscoveryService.class);
     }
 
+    /**
+     * Method to fetch SysApp Version
+     */
+    public boolean fetchSysapVersion() {
+        String url = baseUrl + "/rest/configuration";
+        try {
+            HttpClient client = httpClient;
+            Request req = client.newRequest(url);
+
+            if (req == null) {
+                logger.warn("Invalid request object in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            ContentResponse response = req.send();
+
+            if (response.getStatus() != 200) {
+                logger.warn("HTTP request failed in fetchSysapVersion with status [{}] and reason [{}]",
+                        response.getStatus(), response.getReason());
+                return false;
+            }
+
+            String configString = new String(response.getContent());
+
+            JsonReader reader = new JsonReader(new StringReader(configString));
+            reader.setLenient(true); // Deprecated: use reader.setStrictness(Strictness.LENIENT) in future. Kept for
+                                     // backward compatibility with older library versions.
+            JsonElement jsonTree = JsonParser.parseReader(reader);
+
+            if (!jsonTree.isJsonObject()) {
+                logger.warn("Invalid jsonObject in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonObject jsonObject = jsonTree.getAsJsonObject();
+            JsonObject sysapObject = jsonObject.getAsJsonObject(sysApUID);
+
+            if (sysapObject == null) {
+                logger.warn("SysAP object not found in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonObject sysapDetails = sysapObject.getAsJsonObject("sysap");
+            if (sysapDetails == null) {
+                logger.warn("SysAP details not found in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonElement versionElement = sysapDetails.get("version");
+            if (versionElement == null || !versionElement.isJsonPrimitive()) {
+                logger.warn("Version not found or invalid in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            sysapVersion = versionElement.getAsString();
+            logger.debug("SysAP version fetched: {}", sysapVersion);
+            return true;
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            logger.warn("Http communication interrupted in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        } catch (ExecutionException | TimeoutException e) {
+            logger.warn("Http communication exception in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        } catch (JsonParseException e) {
+            logger.warn("Invalid JSON in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        } catch (Exception e) {
+            logger.warn("Unexpected error in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        }
+    }
+
     /**
      * Method to get the device list
      */
     public List<String> getDeviceDeviceList() throws FreeAtHomeHttpCommunicationException {
+        fetchSysapVersion();
+
         List<String> listOfComponentId = new ArrayList<String>();
         boolean ret = false;
 
@@ -232,7 +312,8 @@ public FreeAtHomeDeviceDescription getFreeatHomeDeviceDescription(String id)
             String deviceString = new String(response.getContent());
 
             JsonReader reader = new JsonReader(new StringReader(deviceString));
-            reader.setLenient(true);
+            reader.setLenient(true); // Deprecated: use reader.setStrictness(Strictness.LENIENT) in future. Kept for
+                                     // backward compatibility with older library versions.
             JsonElement jsonTree = JsonParser.parseReader(reader);
 
             if (!jsonTree.isJsonObject()) {
@@ -312,7 +393,8 @@ public String getDatapoint(String deviceId, String channel, String datapoint)
             String deviceString = new String(response.getContent());
 
             JsonReader reader = new JsonReader(new StringReader(deviceString));
-            reader.setLenient(true);
+            reader.setLenient(true); // Deprecated: use reader.setStrictness(Strictness.LENIENT) in future. Kept for
+                                     // backward compatibility with older library versions.
             JsonElement jsonTree = JsonParser.parseReader(reader);
 
             if (!jsonTree.isJsonObject()) {
@@ -403,7 +485,8 @@ public boolean setDatapoint(String deviceId, String channel, String datapoint, S
      */
     public void setDatapointOnWebsocketFeedback(String receivedText) {
         JsonReader reader = new JsonReader(new StringReader(receivedText));
-        reader.setLenient(true);
+        reader.setLenient(true); // Deprecated: use reader.setStrictness(Strictness.LENIENT) in future. Kept for
+                                 // backward compatibility with older library versions.
         JsonElement jsonTree = JsonParser.parseReader(reader);
 
         // check the output
@@ -439,7 +522,8 @@ public void setDatapointOnWebsocketFeedback(String receivedText) {
 
     public void markDeviceRemovedOnWebsocketFeedback(String receivedText) {
         JsonReader reader = new JsonReader(new StringReader(receivedText));
-        reader.setLenient(true);
+        reader.setLenient(true); // Deprecated: use reader.setStrictness(Strictness.LENIENT) in future. Kept for
+                                 // backward compatibility with older library versions.
         JsonElement jsonTree = JsonParser.parseReader(reader);
 
         // check the output
@@ -470,80 +554,114 @@ public void unregisterDeviceStateListener(String deviceID) {
     }
 
     /**
-     * Method to open Http connection
+     * Establishes an HTTP connection to the free@home system.
+     * This method sets up authentication and tests the connection by making a request.
+     *
+     * @return true if the HTTP connection is successfully established, false otherwise.
      */
     public boolean openHttpConnection() {
-        boolean ret = false;
+        logger.debug("Attempting to open HTTP connection to free@home system");
 
         try {
-            // Add authentication credentials.
+            // Set up authentication for the HTTP client
             AuthenticationStore auth = httpClient.getAuthenticationStore();
+            URI baseUri = new URI(baseUrl);
+            auth.addAuthenticationResult(new BasicAuthentication.BasicResult(baseUri, username, password));
 
-            URI uri1 = new URI(baseUrl);
-            auth.addAuthenticationResult(new BasicAuthentication.BasicResult(uri1, username, password));
+            // Construct the URL for the device list (used as a test endpoint)
+            String testUrl = baseUrl + BRIDGE_URL_GETDEVICELIST;
+            logger.debug("Test URL for HTTP connection: {}", testUrl);
 
-            String url = baseUrl + BRIDGE_URL_GETDEVICELIST;
-
-            Request req = httpClient.newRequest(url);
-            ContentResponse res = req.send();
+            // Create and send a test request
+            Request request = httpClient.newRequest(testUrl);
+            if (request == null) {
+                throw new IllegalStateException("Failed to create HTTP request");
+            }
 
-            // check status
-            if (res.getStatus() == HttpStatus.OK_200) {
-                // response OK
-                httpConnectionOK.set(true);
+            // Send the request and get the response
+            ContentResponse response = request.send();
 
-                ret = true;
+            // Check the response status
+            int statusCode = response.getStatus();
 
-                logger.debug("HTTP connection to SysAP is OK");
+            if (statusCode == HttpStatus.OK_200) {
+                logger.debug("HTTP connection to free@home system established successfullystatus code: {}", statusCode);
+                httpConnectionOK.set(true);
+                return true;
             } else {
-                // response NOK, set error
+                logger.warn("HTTP connection failed. Status code: {}, Reason: {}", statusCode, response.getReason());
                 httpConnectionOK.set(false);
-
-                ret = false;
+                return false;
             }
-        } catch (URISyntaxException | InterruptedException | ExecutionException | TimeoutException ex) {
-            logger.debug("Initial HTTP connection to SysAP is not successful");
-
-            ret = false;
+        } catch (URISyntaxException e) {
+            logger.warn("Invalid URI syntax for base URL: {}", baseUrl, e);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            logger.warn("HTTP connection attempt was interrupted", e);
+        } catch (TimeoutException e) {
+            logger.warn("HTTP connection attempt timed out", e);
+        } catch (ExecutionException e) {
+            logger.warn("Error executing HTTP request", e);
+        } catch (Exception e) {
+            logger.warn("Unexpected error while opening HTTP connection", e);
         }
 
-        return ret;
+        httpConnectionOK.set(false);
+        return false;
     }
 
     /**
-     * Method to connect the websocket session
+     * Method to connect the WebSocket session.
+     * Attempts to establish a WebSocket connection to the SysAP and handles authentication.
+     * 
+     * @return true if the connection attempt is initiated successfully, false otherwise
      */
     public boolean connectWebsocketSession() {
         boolean ret = false;
 
+        // Create the WebSocket URI using the configured IP address
         URI uri = URI.create("ws://" + ipAddress + "/fhapi/v1/api/ws");
 
+        // Combine username and password for authentication
         String authString = username + ":" + password;
 
-        // create base64 encoder
+        // Create a Base64 encoder
         Base64.Encoder bas64Encoder = Base64.getEncoder();
 
-        // Encoding string using encoder object
+        // Encode the authentication string to Base64
         String authStringEnc = bas64Encoder.encodeToString(authString.getBytes());
 
+        // Set the Authorization header value
         authField = "Basic " + authStringEnc;
 
         WebSocketClient localWebsocketClient = websocketClient;
 
         try {
-            // Start socket client
+            // Start the WebSocket client if it exists
             if (localWebsocketClient != null) {
                 localWebsocketClient.setMaxTextMessageBufferSize(8 * 1024);
-                localWebsocketClient.setMaxIdleTimeout(BRIDGE_WEBSOCKET_TIMEOUT * 60 * 1000);
-                localWebsocketClient.setConnectTimeout(BRIDGE_WEBSOCKET_TIMEOUT * 60 * 1000);
+
+                // Set the maximum idle timeout for the WebSocket connection.
+                // If no activity occurs within this time, the connection will be closed automatically.
+                localWebsocketClient.setMaxIdleTimeout(BRIDGE_WEBSOCKET_TIMEOUT * 1000); // milliseconds
+
+                // Set the connection timeout for the WebSocket client.
+                // This defines how long the client should wait before considering the connection attempt as failed.
+                // Like the idle timeout, the value is converted from seconds to milliseconds.
+                localWebsocketClient.setConnectTimeout(BRIDGE_WEBSOCKET_TIMEOUT * 1000); // milliseconds
                 localWebsocketClient.start();
                 ClientUpgradeRequest request = new ClientUpgradeRequest();
                 request.setHeader("Authorization", authField);
-                request.setTimeout(BRIDGE_WEBSOCKET_TIMEOUT, TimeUnit.MINUTES);
-                localWebsocketClient.connect(this, uri, request);
 
-                logger.debug("Websocket connection to SysAP is OK, timeout: {}", BRIDGE_WEBSOCKET_TIMEOUT);
+                // Set the timeout for the WebSocket upgrade process (i.e., the time allowed for the handshake to
+                // complete).
+                // Unlike `setConnectTimeout()`, which applies to the lower-level network connection, this timeout
+                // applies specifically to the WebSocket upgrade request.
+                // The timeout is specified in SECONDS, using `TimeUnit.SECONDS`.
+                request.setTimeout(BRIDGE_WEBSOCKET_TIMEOUT, TimeUnit.SECONDS);
+                localWebsocketClient.connect(this, uri, request);
 
+                logger.debug("WebSocket connection attempt initiated, timeout: {} seconds", BRIDGE_WEBSOCKET_TIMEOUT);
                 ret = true;
             } else {
                 ret = false;
@@ -599,45 +717,67 @@ public void closeWebSocketConnection() {
     }
 
     /**
-     * Method to open the websocket connection
+     * Opens a WebSocket connection to the free@home system.
+     * This method initializes the thread pool and WebSocket client if they don't exist.
+     *
+     * @return true if the WebSocket connection is successfully opened or already exists, false otherwise.
      */
     public boolean openWebSocketConnection() {
         boolean ret = false;
 
-        QueuedThreadPool localThreadPool = jettyThreadPool;
-
-        if (localThreadPool == null) {
-            jettyThreadPool = new QueuedThreadPool();
+        try {
+            logger.debug("Current Jetty version: {}", org.eclipse.jetty.util.Jetty.VERSION);
 
-            localThreadPool = jettyThreadPool;
+            QueuedThreadPool localThreadPool = jettyThreadPool;
 
-            if (localThreadPool != null) {
-                localThreadPool.setName(FreeAtHomeBridgeHandler.class.getSimpleName());
-                localThreadPool.setDaemon(true);
-                localThreadPool.setStopTimeout(0);
+            if (localThreadPool == null) {
+                // Create a new thread pool if it doesn't exist
+                jettyThreadPool = new QueuedThreadPool();
+                localThreadPool = jettyThreadPool;
 
-                ret = true;
+                if (localThreadPool != null) {
+                    localThreadPool.setName(FreeAtHomeBridgeHandler.class.getSimpleName());
+                    localThreadPool.setDaemon(true);
+                    localThreadPool.setStopTimeout(0);
+                } else {
+                    throw new IllegalStateException("Failed to create QueuedThreadPool");
+                }
             }
-        }
-
-        WebSocketClient localWebSocketClient = websocketClient;
-
-        if (localWebSocketClient == null) {
-            websocketClient = new WebSocketClient(httpClient);
-
-            localWebSocketClient = websocketClient;
 
-            if (localWebSocketClient != null) {
-                localWebSocketClient.setExecutor(jettyThreadPool);
-
-                socketMonitor.start();
-
-                ret = true;
+            WebSocketClient localWebSocketClient = websocketClient;
+
+            if (localWebSocketClient == null) {
+                // Create a new WebSocket client if it doesn't exist
+                logger.debug("Creating new WebSocketClient with Jetty version {}",
+                        org.eclipse.jetty.util.Jetty.VERSION);
+                localWebSocketClient = new WebSocketClient(httpClient);
+                websocketClient = localWebSocketClient;
+
+                if (localWebSocketClient != null) {
+                    // Set the executor immediately after creation, before any start
+                    localWebSocketClient.setExecutor(localThreadPool);
+                    // Do not start the client here; let connectWebsocketSession() handle it, see
+                    // localWebsocketClient.start() there
+                    socketMonitor.start();
+                    ret = true;
+                } else {
+                    throw new IllegalStateException("WebSocketClient initialization failed");
+                }
             } else {
-                ret = false;
+                if (localWebSocketClient.isStarted()) {
+                    logger.debug("WebSocketClient is already started, skipping setExecutor()");
+                    ret = true; // Client exists and is running, no need to reconfigure
+                } else {
+                    // Set executor only if the client is not yet started
+                    logger.debug("WebSocketClient exists but not started, setting executor");
+                    localWebSocketClient.setExecutor(localThreadPool);
+                    socketMonitor.start();
+                    ret = true;
+                }
             }
-        } else {
-            ret = true;
+        } catch (Exception e) {
+            logger.warn("Error in openWebSocketConnection: {}", e.getMessage());
+            ret = false;
         }
 
         return ret;
@@ -653,6 +793,8 @@ public void initialize() {
         // load configuration
         FreeAtHomeBridgeHandlerConfiguration locConfig = getConfigAs(FreeAtHomeBridgeHandlerConfiguration.class);
 
+        sendKeepAliveMessage = locConfig.sendKeepAliveMessage;
+
         ipAddress = locConfig.ipAddress;
         if (ipAddress.isBlank()) {
             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
@@ -707,63 +849,133 @@ public void initialize() {
     /**
      * Method to dispose
      */
+    @SuppressWarnings("null")
     @Override
     public void dispose() {
         // let run out the thread
+        logger.debug("Dispose called, interrupting socket monitor thread");
         socketMonitor.interrupt();
 
+        logger.debug("Closing WebSocket connection");
         closeWebSocketConnection();
+
+        if (jettyThreadPool != null) {
+            try {
+                logger.debug("Stopping Jetty thread pool");
+                jettyThreadPool.stop();
+            } catch (Exception e) {
+                logger.warn("Error stopping Jetty thread pool: {}", e.getMessage());
+            } finally {
+                jettyThreadPool = null;
+            }
+        }
     }
 
     /**
-     * Thread that maintains connection via Websocket.
+     * Inner class implementing the WebSocket monitor thread.
+     * This thread continuously monitors the WebSocket connection and attempts to reconnect if it fails.
      */
     private class FreeAtHomeWebsocketMonitorThread extends Thread {
 
-        // initial delay to initiate connection
-        private AtomicInteger reconnectDelay = new AtomicInteger();
+        // AtomicInteger to manage the delay (in seconds) before attempting to reconnect the WebSocket.
+        // This ensures thread-safe updates to the reconnect delay value across multiple threads.
+        private final AtomicInteger reconnectDelay = new AtomicInteger();
 
+        /**
+         * Default constructor for the FreeAtHomeWebsocketMonitorThread.
+         * Initializes a new instance of the monitor thread without any specific configuration.
+         * The reconnectDelay is implicitly initialized to 0 by AtomicInteger's default constructor.
+         */
         public FreeAtHomeWebsocketMonitorThread() {
+            // No additional initialization required at this point.
+            // The reconnectDelay is already set to 0 by default via AtomicInteger.
         }
 
+        /**
+         * Main execution method of the monitor thread.
+         * Runs a loop that checks the HTTP connection status and manages WebSocket reconnection attempts.
+         */
         @Override
         public void run() {
-            // set initial connect delay to 0
+            // Initialize reconnect delay to 0
             reconnectDelay.set(0);
+            int reconnectCounter = 0; // Counter for reconnection attempts
 
-            try {
-                while (!isInterrupted()) {
+            while (!isInterrupted()) {
+                try {
                     if (httpConnectionOK.get()) {
+                        reconnectCounter++;
+                        logger.debug("httpConnectionOK: {}", httpConnectionOK.get());
+                        logger.debug("Attempting WebSocket connection, attempt #{}", reconnectCounter);
+
+                        // Attempt to establish the WebSocket connection
                         if (connectSession()) {
+                            logger.debug("WebSocket connection established, starting monitoring");
+                            int aliveCounter = 0; // Counter for successful alive checks
+
+                            // Inner loop to monitor the active WebSocket connection
                             while (isSocketConnectionAlive()) {
+                                logger.debug(
+                                        "isSocketConnectionAlive is true, aliveCounter {}, (re)connectCounter {}, sleeping for {}s, SysApp Version {}, jetty {}, lastReceived {}ms ago",
+                                        aliveCounter++, reconnectCounter, BRIDGE_WEBSOCKET_KEEPALIVE, sysapVersion,
+                                        org.eclipse.jetty.util.Jetty.VERSION,
+                                        lastReceivedTime == 0 ? "nothing recieved yet"
+                                                : System.currentTimeMillis() - lastReceivedTime);
+
+                                // Sleep for the keep-alive interval to periodically check the connection
                                 TimeUnit.SECONDS.sleep(BRIDGE_WEBSOCKET_KEEPALIVE);
 
-                                logger.debug("Sending keep-alive message {}", System.currentTimeMillis());
-                                sendWebsocketKeepAliveMessage("keep-alive");
+                                // Send keep-alive message or ping based on configuration
+                                if (sendKeepAliveMessage) {
+                                    logger.debug("Sending keep-alive message, System.currentTimeMillis {}ms",
+                                            System.currentTimeMillis());
+                                    sendWebsocketKeepAliveMessage("keep-alive");
+                                } else {
+                                    logger.debug("Sending ping message, System.currentTimeMillis {}ms",
+                                            System.currentTimeMillis());
+                                    sendWebsocketPing();
+                                }
                             }
+                            logger.debug("Socket connection closed - isSocketConnectionAlive == false");
+                        } else {
+                            // Log if the connection attempt failed
+                            logger.debug("WebSocket connection attempt failed");
                         }
-                        logger.debug("Socket connection closed");
-                        reconnectDelay.set(BRIDGE_WEBSOCKET_RECONNECT_DELAY);
+
+                        // Delay before the next reconnect attempt
+                        logger.debug("Delaying (re)connect request by {} seconds", BRIDGE_WEBSOCKET_RECONNECT_DELAY);
+                        TimeUnit.SECONDS.sleep(BRIDGE_WEBSOCKET_RECONNECT_DELAY);
                     } else {
+                        logger.debug("httpConnectionOK NOT True, this should not happen");
+                        logger.debug("Retrying in {} seconds", BRIDGE_WEBSOCKET_RECONNECT_DELAY);
+                        TimeUnit.SECONDS.sleep(BRIDGE_WEBSOCKET_RECONNECT_DELAY);
+                    }
+                } catch (InterruptedException e) {
+                    // Handle thread interruption (e.g., during shutdown)
+                    Thread.currentThread().interrupt();
+                    logger.debug("WebSocket monitor thread interrupted as expected during shutdown");
+                } catch (IOException e) {
+                    // Handle IO errors (e.g., from sendWebsocketKeepAliveMessage or sendWebsocketPing)
+                    logger.warn("Error in WebSocket communication: {}", e.getMessage());
+                    try {
+                        // Delay before retrying after an IO error
+                        logger.debug("Retrying after IO error in {} seconds", BRIDGE_WEBSOCKET_RECONNECT_DELAY);
                         TimeUnit.SECONDS.sleep(BRIDGE_WEBSOCKET_RECONNECT_DELAY);
+                    } catch (InterruptedException ie) {
+                        Thread.currentThread().interrupt();
+                        logger.debug("Interrupted during IO error recovery {}", ie.getMessage());
                     }
                 }
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-
-                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
-                        "@text/comm-error.general-websocket-issue");
-            } catch (IOException e) {
-                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
-                        "@text/comm-error.websocket-keep-alive-error");
             }
+            // Log when the thread stops
+            logger.debug("WebSocket monitor thread stopped");
         }
 
         private boolean connectSession() throws InterruptedException {
             int delay = reconnectDelay.get();
 
             if (delay > 0) {
-                logger.debug("Delaying (re)connect request by {} seconds.", reconnectDelay);
+                logger.debug("Delaying (re)connect request by {} seconds.", delay);
                 TimeUnit.SECONDS.sleep(delay);
             }
 
@@ -778,15 +990,25 @@ private boolean connectSession() throws InterruptedException {
                 return false;
             }
 
-            if (websocketSession == null) {
-                lock.lock();
-                try {
-                    websocketSessionEstablished.await();
-                } finally {
-                    lock.unlock();
+            // Wait for connection to be established or fail with a timeout
+            lock.lock();
+            try {
+                if (websocketSession == null) {
+                    boolean established = websocketSessionEstablished.await(BRIDGE_WEBSOCKET_TIMEOUT, TimeUnit.SECONDS);
+                    if (!established || websocketSession == null) {
+                        logger.trace("WebSocket connection timed out or failed during establishment");
+                        updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                                "@text/comm-error.general-websocket-issue");
+                        reconnectDelay.set(BRIDGE_WEBSOCKET_RECONNECT_DELAY);
+                        return false;
+                    }
                 }
+            } finally {
+                lock.unlock();
             }
 
+            logger.debug("WebSocket session successfully established");
+            reconnectDelay.set(0); // Reset delay on successful connection
             return true;
         }
     }
@@ -802,15 +1024,41 @@ public void sendWebsocketKeepAliveMessage(String message) throws IOException {
         }
     }
 
+    /**
+     * Send ping message to SysAp
+     */
+    public void sendWebsocketPing() throws IOException {
+        Session localSession = websocketSession;
+
+        if (localSession != null) {
+            localSession.getRemote().sendPing(ByteBuffer.wrap("ping".getBytes()));
+        }
+    }
+
     /**
      * Get socket alive state
-     * 
+     *
      * @throws InterruptedException
      */
-    public boolean isSocketConnectionAlive() throws InterruptedException {
+    public boolean isSocketConnectionAlive() {
         Session localSession = websocketSession;
 
-        return (localSession != null) ? localSession.isOpen() : false;
+        if (localSession == null) {
+            logger.debug("Socket connection is null");
+            return false;
+        } else if (!localSession.isOpen()) {
+            logger.debug("Socket connection is closed");
+            return false;
+        } else if (lastReceivedTime != 0) {
+            long timeSinceLastReceived = System.currentTimeMillis() - lastReceivedTime;
+            if (timeSinceLastReceived > BRIDGE_WEBSOCKET_TIMEOUT * 1000) {
+                logger.warn("No data received for {} ms, assuming connection is dead", timeSinceLastReceived);
+                localSession.close(StatusCode.ABNORMAL, "No data received");
+                return false;
+            }
+            return true;
+        }
+        return true;
     }
 
     /**
@@ -819,7 +1067,8 @@ public boolean isSocketConnectionAlive() throws InterruptedException {
     @Override
     public void onWebSocketClose(int statusCode, @Nullable String reason) {
         websocketSession = null;
-        logger.debug("Socket Closed: [ {} ] {}", statusCode, reason);
+        lastReceivedTime = 0;
+        logger.warn("Socket Closed: [ {} ] {}", statusCode, reason);
     }
 
     /**
@@ -831,12 +1080,29 @@ public void onWebSocketConnect(@Nullable Session session) {
 
         if (localSession != null) {
             websocketSession = localSession;
+            localSession.setIdleTimeout(2 * BRIDGE_WEBSOCKET_KEEPALIVE * 1000); // Unit is milliseconds,
+                                                                                // sendWebsocketPing() and
+                                                                                // sendWebsocketKeepAliveMessage() are
+                                                                                // called every
+                                                                                // BRIDGE_WEBSOCKET_KEEPALIVE, so the
+                                                                                // timeout should be at least 2 times
+                                                                                // that
+            logger.debug("WebSocket connection to SysAP established successfully, timeout: {} ms",
+                    localSession.getIdleTimeout());
+
+            // Fetch the SysAP version after a successful connection establishment
+            if (fetchSysapVersion()) {
+                logger.debug("SysAP version fetched successfully after reconnect: {}", sysapVersion);
+                updateStatus(ThingStatus.ONLINE); // Set the status to ONLINE
+            } else {
+                logger.warn("Failed to fetch SysAP version after successful reconnect");
+                updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                        "@text/comm-error.fetch-version-error");
+            }
 
-            localSession.setIdleTimeout(-1);
-
-            logger.debug("Socket Connected - Timeout {} - sesson: {}", localSession.getIdleTimeout(), session);
+            lastReceivedTime = System.currentTimeMillis();
         } else {
-            logger.debug("Socket Connected - Timeout (invalid) - sesson: (invalid)");
+            logger.debug("Socket Connected - Timeout (invalid) - session: (invalid)");
         }
 
         lock.lock();
@@ -852,13 +1118,31 @@ public void onWebSocketConnect(@Nullable Session session) {
      */
     @Override
     public void onWebSocketError(@Nullable Throwable cause) {
-        websocketSession = null;
+        Session localSession = websocketSession;
 
+        // Log the error with details if available
         if (cause != null) {
-            logger.debug("Socket Error: {}", cause.getLocalizedMessage());
+            logger.warn("WebSocket error occurred: {}", cause.getLocalizedMessage());
         } else {
-            logger.debug("Socket Error: unknown");
+            logger.warn("WebSocket error occurred: unknown cause");
+        }
+
+        // Check and close the session if it is still open
+        if (localSession != null && localSession.isOpen()) {
+            try {
+                localSession.close(StatusCode.ABNORMAL, "Closed due to error");
+                logger.debug("WebSocket session closed due to error");
+            } catch (Exception e) {
+                logger.warn("Failed to close WebSocket session: {}", e.getMessage());
+            }
         }
+
+        // Set the session to null to indicate it is no longer valid
+        websocketSession = null;
+
+        // Update the thing status to OFFLINE with error details
+        updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                "WebSocket connection failed: " + (cause != null ? cause.getMessage() : "unknown error"));
     }
 
     /**
@@ -867,7 +1151,7 @@ public void onWebSocketError(@Nullable Throwable cause) {
     @Override
     @NonNullByDefault({})
     public void onWebSocketBinary(byte[] payload, int offset, int len) {
-        logger.debug("Binary message received via websocket");
+        logger.warn("Binary message received via websocket - It shall not happen with the free@home SysAp");
     }
 
     /**
@@ -876,6 +1160,7 @@ public void onWebSocketBinary(byte[] payload, int offset, int len) {
     @Override
     public void onWebSocketText(@Nullable String message) {
         if (message != null) {
+            lastReceivedTime = System.currentTimeMillis();
             if (message.toLowerCase(Locale.US).contains("bye")) {
                 Session localSession = websocketSession;
 
diff --git a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeDeviceHandler.java b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeDeviceHandler.java
index 2af2f14e9fdf4..a2f754e25c068 100644
--- a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeDeviceHandler.java
+++ b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeDeviceHandler.java
@@ -75,13 +75,13 @@ public class FreeAtHomeDeviceHandler extends BaseThingHandler implements FreeAtH
 
     private final Logger logger = LoggerFactory.getLogger(FreeAtHomeDeviceHandler.class);
     private FreeAtHomeDeviceDescription device = new FreeAtHomeDeviceDescription();
-    private FreeAtHomeChannelTypeProvider channelTypeProvider;
-    private TranslationProvider i18nProvider;
-    private Locale locale;
+    private final FreeAtHomeChannelTypeProvider channelTypeProvider;
+    private final TranslationProvider i18nProvider;
+    private final Locale locale;
     private Bundle bundle;
 
-    private Map<ChannelUID, FreeAtHomeDatapointGroup> mapChannelUID = new HashMap<ChannelUID, FreeAtHomeDatapointGroup>();
-    private Map<String, ChannelUID> mapEventToChannelUID = new HashMap<String, ChannelUID>();
+    private final Map<ChannelUID, FreeAtHomeDatapointGroup> mapChannelUID = new HashMap<ChannelUID, FreeAtHomeDatapointGroup>();
+    private final Map<String, ChannelUID> mapEventToChannelUID = new HashMap<String, ChannelUID>();
 
     public FreeAtHomeDeviceHandler(Thing thing, FreeAtHomeChannelTypeProvider channelTypeProvider,
             TranslationProvider i18nProvider, LocaleProvider localeProvider) {
@@ -305,10 +305,12 @@ public void handleCommand(ChannelUID channelUID, Command command) {
         }
     }
 
+    @Override
     public void onDeviceRemoved() {
         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.GONE);
     }
 
+    @Override
     public void onDeviceStateChanged(String event, String valueString) {
         // Get the channle UID belonging to this event
         ChannelUID channelUID = mapEventToChannelUID.get(event);
@@ -499,8 +501,8 @@ public void updateChannels() throws FreeAtHomeGeneralException {
 
                     // in case of output channel, register it for updates
                     if (outputDatapoint != null) {
-                        String eventDatapointID = new String(device.getDeviceId() + "/" + channel.getChannelId() + "/"
-                                + outputDatapoint.getDatapointId());
+                        String eventDatapointID = device.getDeviceId() + "/" + channel.getChannelId() + "/"
+                                + outputDatapoint.getDatapointId();
 
                         mapEventToChannelUID.put(eventDatapointID, channelUID);
                     }
@@ -572,8 +574,8 @@ private void reloadChannelTypes() throws FreeAtHomeGeneralException {
 
                 // in case of output channel, register it for updates
                 if (outputDatapoint != null) {
-                    String eventDatapointID = new String(device.getDeviceId() + "/" + channel.getChannelId() + "/"
-                            + outputDatapoint.getDatapointId());
+                    String eventDatapointID = device.getDeviceId() + "/" + channel.getChannelId() + "/"
+                            + outputDatapoint.getDatapointId();
 
                     mapEventToChannelUID.put(eventDatapointID, channelUID);
                 }
diff --git a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/util/FidTranslationUtils.java b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/util/FidTranslationUtils.java
index f9ce65d3888df..cbe1491707fe3 100644
--- a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/util/FidTranslationUtils.java
+++ b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/util/FidTranslationUtils.java
@@ -29,8 +29,11 @@ public class FidTranslationUtils {
     public static final int FID_UNKNOWN = 0xFFFFAAFF; // Unknown
 
     // free@home constants
+    // Function IDs https://developer.eu.mybuildings.abb.com/fah_cloud/reference/functionids
+    // Pairing IDs https://developer.eu.mybuildings.abb.com/fah_cloud/references/pairingids
     public static final int FID_SWITCH_SENSOR = 0x0000; // Control element
     public static final int FID_DIMMING_SENSOR = 0x0001; // Dimming sensor
+    public static final int FID_SHUTTER_SENSOR = 0x0002; // Shutter Sensor
     public static final int FID_BLIND_SENSOR = 0x0003; // Blind sensor
     public static final int FID_STAIRCASE_LIGHT_SENSOR = 0x0004; // Stairwell light sensor
     public static final int FID_FORCE_ON_OFF_SENSOR = 0x0005; // Force On/Off sensor
@@ -51,6 +54,8 @@ public class FidTranslationUtils {
     public static final int FID_UNDERFLOOR_HEATING = 0x0015; // Underfloor heating
     public static final int FID_FAN_COIL = 0x0016; // Fan Coil
     public static final int FID_TWO_LEVEL_CONTROLLER = 0x0017; // Two-level controller
+    public static final int FID_PUSH_BUTTON_SENSOR = 0x0018; // Push button
+    public static final int FID_RING_INDICATION_SENSOR = 0x0019; // Ring indicator
     public static final int FID_DES_DOOR_OPENER_ACTUATOR = 0x001A; // Door opener
     public static final int FID_PROXY = 0x001B; // Proxy
     public static final int FID_DES_LEVEL_CALL_ACTUATOR = 0x001D; // Door Map.entry System Call Level Actuator
@@ -60,15 +65,19 @@ public class FidTranslationUtils {
     public static final int FID_DES_LIGHT_SWITCH_ACTUATOR = 0x0021; // Corridor light
     public static final int FID_ROOM_TEMPERATURE_CONTROLLER_MASTER_WITHOUT_FAN = 0x0023; // Room temperature controller
     public static final int FID_COOLING_ACTUATOR = 0x0024; // Cooling mode
+    public static final int FID_DAY_NIGHT_SENSOR = 0x0025; // Day/night sensor
     public static final int FID_HEATING_ACTUATOR = 0x0027; // Heating mode
     public static final int FID_FORCE_UP_DOWN_SENSOR = 0x0028; // Force-position blind
     public static final int FID_HEATING_COOLING_ACTUATOR = 0x0029; // Auto. heating/cooling mode
     public static final int FID_HEATING_COOLING_SENSOR = 0x002A; // Switchover heating/cooling
     public static final int FID_DES_DEVICE_SETTINGS = 0x002B; // Device settings
+    public static final int FID_SACE_BLIND_ACTUATOR = 0x002C; // Space blind
+    public static final int FID_RGB_SENSOR = 0x002D; // RGB sensor
     public static final int FID_RGB_W_ACTUATOR = 0x002E; // Dim actuator
     public static final int FID_RGB_ACTUATOR = 0x002F; // Dim actuator
     public static final int FID_PANEL_SWITCH_SENSOR = 0x0030; // Control element
     public static final int FID_PANEL_DIMMING_SENSOR = 0x0031; // Dimming sensor
+    public static final int FID_PANEL_SHUTTER_SENSOR = 0x0032; // Shutter
     public static final int FID_PANEL_BLIND_SENSOR = 0x0033; // Blind sensor
     public static final int FID_PANEL_STAIRCASE_LIGHT_SENSOR = 0x0034; // Stairwell light sensor
     public static final int FID_PANEL_FORCE_ON_OFF_SENSOR = 0x0035; // Force On/Off sensor
@@ -83,6 +92,7 @@ public class FidTranslationUtils {
     public static final int FID_ADDITIONAL_HEATING_ACTUATOR = 0x003D; // Add. stage for heating mode
     public static final int FID_RADIATOR_ACTUATOR_MASTER = 0x003E; // Radiator thermostate
     public static final int FID_RADIATOR_ACTUATOR_SLAVE = 0x003F; // Room temperature controller extension unit
+    public static final int FID_COLORTEMPERATURE_ACTUATOR = 0x0040; // Color temperature
     public static final int FID_BRIGHTNESS_SENSOR = 0x0041; // Brightness sensor
     public static final int FID_RAIN_SENSOR = 0x0042; // Rain sensor
     public static final int FID_TEMPERATURE_SENSOR = 0x0043; // Temperature sensor
@@ -93,13 +103,15 @@ public class FidTranslationUtils {
     public static final int FID_FCA_2_PIPE_HEATING_COOLING = 0x0049; // Auto. heating/cooling mode
     public static final int FID_FCA_4_PIPE_HEATING_AND_COOLING = 0x004A; // Two valves for heating and cooling
     public static final int FID_WINDOW_DOOR_ACTUATOR = 0x004B; // Window/Door
-    public static final int FID_INVERTER_INFO = 0x004E; // ABC
-    public static final int FID_METER_INFO = 0x004F; // ABD
-    public static final int FID_BATTERY_INFO = 0x0050; // ACD
+    public static final int FID_INVERTER_INFO = 0x004E; // Inverter information
+    public static final int FID_METER_INFO = 0x004F; // Meter information
+    public static final int FID_BATTERY_INFO = 0x0050; // Battery information
     public static final int FID_PANEL_TIMER_PROGRAM_SWITCH_SENSOR = 0x0051; // Timer program switch sensor
+    public static final int FID_SAFETY_SENSOR = 0x0053; // Safety sensor
     public static final int FID_DOMUSTECH_ZONE = 0x0055; // Zone
     public static final int FID_CENTRAL_HEATING_ACTUATOR = 0x0056; // Central heating actuator
     public static final int FID_CENTRAL_COOLING_ACTUATOR = 0x0057; // Central cooling actuator
+    public static final int FID_LINK_ACTUATOR = 0x0058; // Link actuator
     public static final int FID_HOUSE_KEEPING = 0x0059; // Housekeeping
     public static final int FID_MEDIA_PLAYER = 0x005A; // Media Player
     public static final int FID_PANEL_ROOM_TEMPERATURE_CONTROLLER_SLAVE_FOR_BATTERY_DEVICE = 0x005B; // Panel Room
@@ -107,6 +119,10 @@ public class FidTranslationUtils {
                                                                                                      // Controller Slave
                                                                                                      // For Battery
                                                                                                      // Device
+    public static final int FID_WELCOME_IP_MUTE_SENSOR = 0x005C; // Welcome-IP mute sensor
+    public static final int FID_WELCOME_IP_MUTE_ACTUATOR = 0x005D; // Welcome-IP mute
+    public static final int FID_WELCOME_IP_DOOR_OPEN_SENSOR = 0x005E; // Welcome-IP doors open sensor
+    public static final int FID_WELCOME_IP_SWITCH_SENSOR = 0x005F; // Welcome-IP switch
     public static final int FID_PANEL_MEDIA_PLAYER_SENSOR = 0x0060; // Media Player Sensor
     public static final int FID_BLIND_ACTUATOR = 0x0061; // Roller blind actuator
     public static final int FID_ATTIC_WINDOW_ACTUATOR = 0x0062; // Attic window actuator
@@ -125,6 +141,7 @@ public class FidTranslationUtils {
     public static final int FID_COFFEE_MACHINE = 0x006F; // Coffee machine
     public static final int FID_FRIDGE_FREEZER = 0x0070; // Fridge/Freezer
     public static final int FID_TIMER_PROGRAM_OR_ALERT_SWITCH_SENSOR = 0x0071; // Timer program switch sensor
+    public static final int FID_WELCOME_IP_BELL_INDICATOR_SENSOR = 0x0072; // Welcome-IP bell indicator
     public static final int FID_CEILING_FAN_ACTUATOR = 0x0073; // Ceiling fan actuator
     public static final int FID_CEILING_FAN_SENSOR = 0x0074; // Ceiling fan sensor
     public static final int FID_SPLIT_UNIT_GATEWAY = 0x0075; // Room temperature controller with fan speed level
@@ -148,6 +165,10 @@ public class FidTranslationUtils {
     public static final int FID_ADDITIONAL_COOLING_ACTUATOR = 0x0087; // Add. stage for cooling mode
     public static final int FID_TWO_LEVEL_HEATING_ACTUATOR = 0x0088; // Two Level Heating Actuator
     public static final int FID_TWO_LEVEL_COOLING_ACTUATOR = 0x0089; // Two Level Cooling Actuator
+    public static final int FID_DOOR_LOCK_SENSOR = 0x008A; // Door lock sensor
+    public static final int FID_DOOR_LOCK_ACTUATOR = 0x008B; // Door lock actuator
+    public static final int FID_AC_ROUTING = 0x008C; // AC routing
+    public static final int FID_EXTERNAL_SIREN = 0x008D; // External siren
     public static final int FID_GLOBAL_ZONE = 0x008E; // Zone
     public static final int FID_VOLUME_UP_SENSOR = 0x008F; // Volume up
     public static final int FID_VOLUME_DOWN_SENSOR = 0x0090; // Volume down
@@ -168,6 +189,7 @@ public class FidTranslationUtils {
     public static final int FID_DOMUSCURTAINDETECTOR = 0x009F; // External IR Sensor Curtain
     public static final int FID_DOMUSSMOKEDETECTOR = 0x00A0; // Smoke Detector
     public static final int FID_DOMUSFLOODDETECTOR = 0x00A1; // Flood Detection
+    public static final int FID_HOB = 0x00A2; // HOB
     public static final int FID_PANEL_SUG_SENSOR = 0x00A3; // Sensor for air-conditioning unit
     public static final int FID_TWO_LEVEL_HEATING_COOLING_ACTUATOR = 0x00A4; // Two-point controller for heating or
                                                                              // cooling
@@ -175,27 +197,47 @@ public class FidTranslationUtils {
     public static final int FID_WALLBOX = 0x00A6; // Wallbox
     public static final int FID_PANEL_WALLBOX = 0x00A7; // Wallbox
     public static final int FID_DOOR_LOCK_CONTROL = 0x00A8; // Door lock control
+    public static final int FID_DOOR_LOCK_SETTINGS = 0x00A9; // Door lock settings
     public static final int FID_VRV_GATEWAY = 0x00AA; // Room temperature controller with fan speed level
-
+    public static final int FID_CO_2 = 0x00BA; // Co2
+    public static final int FID_VOC = 0x00BB; // Voc
+    public static final int FID_AIRQUALITY_SENSOR = 0x00BD; // Airquality sensor
+    public static final int FID_SWITCH_SENSOR_PUSHBUTTON_TYPE0 = 0x1008; // Switch sensor push button
+    public static final int FID_SWITCH_SENSOR_PUSHBUTTON_TYPE1 = 0x1009; // Switch sensor push button
+    public static final int FID_SWITCH_SENSOR_PUSHBUTTON_TYPE2 = 0x100A; // Switch sensor push button
+    public static final int FID_SWITCH_SENSOR_PUSHBUTTON_TYPE3 = 0x100B; // Switch sensor push button
+    public static final int FID_SWITCH_SENSOR_PUSHBUTTON_TYPE4 = 0x100C; // Switch sensor push button
+    public static final int FID_SWITCH_SENSOR_PUSHBUTTON_TYPE5 = 0x100D; // Switch sensor push button
+    public static final int FID_SWITCH_SENSOR_PUSHBUTTON_TYPE6 = 0x100E; // Switch sensor push button
+    public static final int FID_SWITCH_SENSOR_PUSHBUTTON_TYPE7 = 0x100F; // Switch sensor push button
+    public static final int FID_DIMMING_SENSOR_PUSHBUTTON_TYPE0 = 0x1018; // Dimming sensor push button
+    public static final int FID_DIMMING_SENSOR_PUSHBUTTON_TYPE1 = 0x1019; // Dimming sensor push button
+    public static final int FID_DIMMING_SENSOR_PUSHBUTTON_TYPE2 = 0x101A; // Dimming sensor push button
+    public static final int FID_DIMMING_SENSOR_PUSHBUTTON_TYPE3 = 0x101B; // Dimming sensor push button
+    public static final int FID_DIMMING_SENSOR_PUSHBUTTON_TYPE4 = 0x101C; // Dimming sensor push button
+    public static final int FID_DIMMING_SENSOR_PUSHBUTTON_TYPE5 = 0x101D; // Dimming sensor push button
+    public static final int FID_DIMMING_SENSOR_PUSHBUTTON_TYPE6 = 0x101E; // Dimming sensor push button
+    public static final int FID_DIMMING_SENSOR_PUSHBUTTON_TYPE7 = 0x101F; // Dimming sensor push button
+    public static final int FID_MOVEMENT_DETECTOR_FLEX = 0x1090; // Movement detector flex
+    public static final int FID_MOVEMENT_DETECTOR_TYPE0 = 0x1090; // Movement Detector
+    public static final int FID_DIMMING_ACTUATOR_FLEX = 0x1810; // Switch actuator flex
+    public static final int FID_DIMMING_ACTUATOR_TYPE0 = 0x1810; // Dim actuator
+    public static final int FID_BLIND_ACTUATOR_WIRELESS = 0x1821; // Wireless blind actuator
     public static final int FID_SCENE_TRIGGER = 0x4800; // Scene trigger
-    public static final int FID_RULE_SWITCH = 0x4A00; // Scene trigger
-
-    // FID added based on tests
-    public static final int FID_AIRQUALITYSENSOR_PRESSURE = 0x0E017;
-    public static final int FID_AIRQUALITYSENSOR_CO2 = 0x0E018;
-    public static final int FID_AIRQUALITYSENSOR_CO = 0x0E019;
-    public static final int FID_AIRQUALITYSENSOR_NO2 = 0x0E01A;
-    public static final int FID_AIRQUALITYSENSOR_O3 = 0x0E01B;
-    public static final int FID_AIRQUALITYSENSOR_PM10 = 0x0E01C;
-    public static final int FID_AIRQUALITYSENSOR_PM25 = 0x0E01D;
-    public static final int FID_AIRQUALITYSENSOR_VOC = 0x0E01E;
-    public static final int FID_AIRQUALITYSENSOR_HUMIDITY = 0x0B03F;
-
-    public static final int FID_MOVEMENT_DETECTOR_FLEX = 0x1090;
-    public static final int FID_DIMMING_ACTUATOR_FLEX = 0x1810;
+    public static final int FID_RULE_SWITCH = 0x4A00; // Rule Switch
+    public static final int FID_AIRQUALITYSENSOR_HUMIDITY = 0xB03F; // Air quality sensor humidity
+    public static final int FID_AIRQUALITYSENSOR_PRESSURE = 0xE017; // Air quality sensor Pressure
+    public static final int FID_AIRQUALITYSENSOR_CO2 = 0xE018; // Air quality sensor CO2
+    public static final int FID_AIRQUALITYSENSOR_CO = 0xE019; // Air quality sensor CO
+    public static final int FID_AIRQUALITYSENSOR_NO2 = 0xE01A; // Air quality sensor NO2
+    public static final int FID_AIRQUALITYSENSOR_O3 = 0xE01B; // Air quality sensor O3
+    public static final int FID_AIRQUALITYSENSOR_PM10 = 0xE01C; // Air quality sensor PM10
+    public static final int FID_AIRQUALITYSENSOR_PM25 = 0xE01D; // Air quality sensor PM25
+    public static final int FID_AIRQUALITYSENSOR_VOC = 0xE01E; // Air quality sensor VOC
 
     private static final Map<String, String> MAP_FUNCTION_ID = Map.ofEntries(Map.entry("0x0000", "fid-control-element"), // FID_SWITCH_SENSOR
             Map.entry("0x0001", "fid-dimming-sensor"), // FID_DIMMING_SENSOR
+            Map.entry("0x0002", "fid-shutter-sensor"), // FID_SHUTTER_SENSOR
             Map.entry("0x0003", "fid-blind-sensor"), // FID_BLIND_SENSOR
             Map.entry("0x0004", "fid-stairwell-light-sensor"), // FID_STAIRCASE_LIGHT_SENSOR
             Map.entry("0x0005", "fid-force-on/off-sensor"), // FID_FORCE_ON_OFF_SENSOR
@@ -214,6 +256,8 @@ public class FidTranslationUtils {
             Map.entry("0x0015", "fid-underfloor-heating"), // FID_UNDERFLOOR_HEATING
             Map.entry("0x0016", "fid-fan-coil"), // FID_FAN_COIL
             Map.entry("0x0017", "fid-two-level-controller"), // FID_TWO_LEVEL_CONTROLLER
+            Map.entry("0x0018", "fid-push-button"), // FID_PUSH_BUTTON_SENSOR
+            Map.entry("0x0019", "fid-ring-indicator"), // FID_RING_INDICATION_SENSOR
             Map.entry("0x001A", "fid-door-opener"), // FID_DES_DOOR_OPENER_ACTUATOR
             Map.entry("0x001B", "fid-proxy"), // FID_PROXY
             Map.entry("0x001D", "fid-door-map.entry-system-call-level-actuator"), // FID_DES_LEVEL_CALL_ACTUATOR
@@ -223,15 +267,19 @@ public class FidTranslationUtils {
             Map.entry("0x0021", "fid-corridor-light"), // FID_DES_LIGHT_SWITCH_ACTUATOR
             Map.entry("0x0023", "fid-room-temperature-controller"), // FID_ROOM_TEMPERATURE_CONTROLLER_MASTER_WITHOUT_FAN
             Map.entry("0x0024", "fid-cooling-mode"), // FID_COOLING_ACTUATOR
+            Map.entry("0x0025", "fid-day/night-sensor"), // FID_DAY_NIGHT_SENSOR
             Map.entry("0x0027", "fid-heating-mode"), // FID_HEATING_ACTUATOR
             Map.entry("0x0028", "fid-force-position-blind"), // FID_FORCE_UP_DOWN_SENSOR
             Map.entry("0x0029", "fid-auto.-heating/cooling-mode"), // FID_HEATING_COOLING_ACTUATOR
             Map.entry("0x002A", "fid-switchover-heating/cooling"), // FID_HEATING_COOLING_SENSOR
             Map.entry("0x002B", "fid-device-settings"), // FID_DES_DEVICE_SETTINGS
+            Map.entry("0x002C", "fid-space-blind"), // FID_SACE_BLIND_ACTUATOR
+            Map.entry("0x002D", "fid-rgb-sensor"), // FID_RGB_SENSOR
             Map.entry("0x002E", "fid-dim-actuator"), // FID_RGB_W_ACTUATOR
             Map.entry("0x002F", "fid-dim-actuator"), // FID_RGB_ACTUATOR
             Map.entry("0x0030", "fid-control-element"), // FID_PANEL_SWITCH_SENSOR
             Map.entry("0x0031", "fid-dimming-sensor"), // FID_PANEL_DIMMING_SENSOR
+            Map.entry("0x0032", "fid-shutter"), // FID_PANEL_SHUTTER_SENSOR
             Map.entry("0x0033", "fid-blind-sensor"), // FID_PANEL_BLIND_SENSOR
             Map.entry("0x0034", "fid-stairwell-light-sensor"), // FID_PANEL_STAIRCASE_LIGHT_SENSOR
             Map.entry("0x0035", "fid-force-on/off-sensor"), // FID_PANEL_FORCE_ON_OFF_SENSOR
@@ -245,6 +293,7 @@ public class FidTranslationUtils {
             Map.entry("0x003D", "fid-add.-stage-for-heating-mode"), // FID_ADDITIONAL_HEATING_ACTUATOR
             Map.entry("0x003E", "fid-radiator-thermostate"), // FID_RADIATOR_ACTUATOR_MASTER
             Map.entry("0x003F", "fid-room-temperature-controller-extension-unit"), // FID_RADIATOR_ACTUATOR_SLAVE
+            Map.entry("0x0040", "fid-color-temperature"), // FID_COLORTEMPERATURE_ACTUATOR
             Map.entry("0x0041", "fid-brightness-sensor"), // FID_BRIGHTNESS_SENSOR
             Map.entry("0x0042", "fid-rain-sensor"), // FID_RAIN_SENSOR
             Map.entry("0x0043", "fid-temperature-sensor"), // FID_TEMPERATURE_SENSOR
@@ -255,16 +304,22 @@ public class FidTranslationUtils {
             Map.entry("0x0049", "fid-auto.-heating/cooling-mode"), // FID_FCA_2_PIPE_HEATING_COOLING
             Map.entry("0x004A", "fid-two-valves-for-heating-and-cooling"), // FID_FCA_4_PIPE_HEATING_AND_COOLING
             Map.entry("0x004B", "fid-window/door"), // FID_WINDOW_DOOR_ACTUATOR
-            Map.entry("0x004E", "fid-abc"), // FID_INVERTER_INFO
-            Map.entry("0x004F", "fid-abd"), // FID_METER_INFO
-            Map.entry("0x0050", "fid-acd"), // FID_BATTERY_INFO
+            Map.entry("0x004E", "fid-inverter-information"), // FID_INVERTER_INFO
+            Map.entry("0x004F", "fid-meter-information"), // FID_METER_INFO
+            Map.entry("0x0050", "fid-battery-information"), // FID_BATTERY_INFO
             Map.entry("0x0051", "fid-timer-program-switch-sensor"), // FID_PANEL_TIMER_PROGRAM_SWITCH_SENSOR
+            Map.entry("0x0053", "fid-safety-sensor"), // FID_SAFETY_SENSOR
             Map.entry("0x0055", "fid-zone"), // FID_DOMUSTECH_ZONE
             Map.entry("0x0056", "fid-central-heating-actuator"), // FID_CENTRAL_HEATING_ACTUATOR
             Map.entry("0x0057", "fid-central-cooling-actuator"), // FID_CENTRAL_COOLING_ACTUATOR
+            Map.entry("0x0058", "fid-link-actuator"), // FID_LINK_ACTUATOR
             Map.entry("0x0059", "fid-housekeeping"), // FID_HOUSE_KEEPING
             Map.entry("0x005A", "fid-media-player"), // FID_MEDIA_PLAYER
             Map.entry("0x005B", "fid-panel-room-temperature-controller-slave-for-battery-device"), // FID_PANEL_ROOM_TEMPERATURE_CONTROLLER_SLAVE_FOR_BATTERY_DEVICE
+            Map.entry("0x005C", "fid-welcome-ip-mute-sensor"), // FID_WELCOME_IP_MUTE_SENSOR
+            Map.entry("0x005D", "fid-welcome-ip-mute"), // FID_WELCOME_IP_MUTE_ACTUATOR
+            Map.entry("0x005E", "fid-welcome-ip-doors-open-sensor"), // FID_WELCOME_IP_DOOR_OPEN_SENSOR
+            Map.entry("0x005F", "fid-welcome-ip-switch"), // FID_WELCOME_IP_SWITCH_SENSOR
             Map.entry("0x0060", "fid-media-player-sensor"), // FID_PANEL_MEDIA_PLAYER_SENSOR
             Map.entry("0x0061", "fid-roller-blind-actuator"), // FID_BLIND_ACTUATOR
             Map.entry("0x0062", "fid-attic-window-actuator"), // FID_ATTIC_WINDOW_ACTUATOR
@@ -283,6 +338,7 @@ public class FidTranslationUtils {
             Map.entry("0x006F", "fid-coffee-machine"), // FID_COFFEE_MACHINE
             Map.entry("0x0070", "fid-fridge/freezer"), // FID_FRIDGE_FREEZER
             Map.entry("0x0071", "fid-timer-program-switch-sensor"), // FID_TIMER_PROGRAM_OR_ALERT_SWITCH_SENSOR
+            Map.entry("0x0072", "fid-welcome-ip-bell-indicator"), // FID_WELCOME_IP_BELL_INDICATOR_SENSOR
             Map.entry("0x0073", "fid-ceiling-fan-actuator"), // FID_CEILING_FAN_ACTUATOR
             Map.entry("0x0074", "fid-ceiling-fan-sensor"), // FID_CEILING_FAN_SENSOR
             Map.entry("0x0075", "fid-room-temperature-controller-with-fan-speed-level"), // FID_SPLIT_UNIT_GATEWAY
@@ -306,6 +362,10 @@ public class FidTranslationUtils {
             Map.entry("0x0087", "fid-add.-stage-for-cooling-mode"), // FID_ADDITIONAL_COOLING_ACTUATOR
             Map.entry("0x0088", "fid-two-level-heating-actuator"), // FID_TWO_LEVEL_HEATING_ACTUATOR
             Map.entry("0x0089", "fid-two-level-cooling-actuator"), // FID_TWO_LEVEL_COOLING_ACTUATOR
+            Map.entry("0x008A", "fid-door-lock-sensor"), // FID_DOOR_LOCK_SENSOR
+            Map.entry("0x008B", "fid-door-lock-actuator"), // FID_DOOR_LOCK_ACTUATOR
+            Map.entry("0x008C", "fid-ac-routing"), // FID_AC_ROUTING
+            Map.entry("0x008D", "fid-external-siren"), // FID_EXTERNAL_SIREN
             Map.entry("0x008E", "fid-zone"), // FID_GLOBAL_ZONE
             Map.entry("0x008F", "fid-volume-up"), // FID_VOLUME_UP_SENSOR
             Map.entry("0x0090", "fid-volume-down"), // FID_VOLUME_DOWN_SENSOR
@@ -326,15 +386,42 @@ public class FidTranslationUtils {
             Map.entry("0x009F", "fid-external-ir-sensor-curtain"), // FID_DOMUSCURTAINDETECTOR
             Map.entry("0x00A0", "fid-smoke-detector"), // FID_DOMUSSMOKEDETECTOR
             Map.entry("0x00A1", "fid-flood-detection"), // FID_DOMUSFLOODDETECTOR
+            Map.entry("0x00A2", "fid-hob"), // FID_HOB
             Map.entry("0x00A3", "fid-sensor-for-air-conditioning-unit"), // FID_PANEL_SUG_SENSOR
             Map.entry("0x00A4", "fid-two-point-controller-for-heating-or-cooling"), // FID_TWO_LEVEL_HEATING_COOLING_ACTUATOR
             Map.entry("0x00A5", "fid-slave-thermostat"), // FID_PANEL_THERMOSTAT_CONTROLLER_SLAVE
             Map.entry("0x00A6", "fid-wallbox"), // FID_WALLBOX
             Map.entry("0x00A7", "fid-wallbox"), // FID_PANEL_WALLBOX
             Map.entry("0x00A8", "fid-door-lock-control"), // FID_DOOR_LOCK_CONTROL
+            Map.entry("0x00A9", "fid-door-lock-settings"), // FID_DOOR_LOCK_SETTINGS
             Map.entry("0x00AA", "fid-room-temperature-controller-with-fan-speed-level"), // FID_VRV_GATEWAY
+            Map.entry("0x00BA", "fid-co2"), // FID_CO_2
+            Map.entry("0x00BB", "fid-voc"), // FID_VOC
+            Map.entry("0x00BD", "fid-airquality-sensor"), // FID_AIRQUALITY_SENSOR
+            Map.entry("0x1008", "fid-switch-sensor-push-button"), // FID_SWITCH_SENSOR_PUSHBUTTON_TYPE0
+            Map.entry("0x1009", "fid-switch-sensor-push-button"), // FID_SWITCH_SENSOR_PUSHBUTTON_TYPE1
+            Map.entry("0x100A", "fid-switch-sensor-push-button"), // FID_SWITCH_SENSOR_PUSHBUTTON_TYPE2
+            Map.entry("0x100B", "fid-switch-sensor-push-button"), // FID_SWITCH_SENSOR_PUSHBUTTON_TYPE3
+            Map.entry("0x100C", "fid-switch-sensor-push-button"), // FID_SWITCH_SENSOR_PUSHBUTTON_TYPE4
+            Map.entry("0x100D", "fid-switch-sensor-push-button"), // FID_SWITCH_SENSOR_PUSHBUTTON_TYPE5
+            Map.entry("0x100E", "fid-switch-sensor-push-button"), // FID_SWITCH_SENSOR_PUSHBUTTON_TYPE6
+            Map.entry("0x100F", "fid-switch-sensor-push-button"), // FID_SWITCH_SENSOR_PUSHBUTTON_TYPE7
+            Map.entry("0x1018", "fid-dimming-sensor-push-button"), // FID_DIMMING_SENSOR_PUSHBUTTON_TYPE0
+            Map.entry("0x1019", "fid-dimming-sensor-push-button"), // FID_DIMMING_SENSOR_PUSHBUTTON_TYPE1
+            Map.entry("0x101A", "fid-dimming-sensor-push-button"), // FID_DIMMING_SENSOR_PUSHBUTTON_TYPE2
+            Map.entry("0x101B", "fid-dimming-sensor-push-button"), // FID_DIMMING_SENSOR_PUSHBUTTON_TYPE3
+            Map.entry("0x101C", "fid-dimming-sensor-push-button"), // FID_DIMMING_SENSOR_PUSHBUTTON_TYPE4
+            Map.entry("0x101D", "fid-dimming-sensor-push-button"), // FID_DIMMING_SENSOR_PUSHBUTTON_TYPE5
+            Map.entry("0x101E", "fid-dimming-sensor-push-button"), // FID_DIMMING_SENSOR_PUSHBUTTON_TYPE6
+            Map.entry("0x101F", "fid-dimming-sensor-push-button"), // FID_DIMMING_SENSOR_PUSHBUTTON_TYPE7
+            Map.entry("0x1090", "fid-movement-detector-flex"), // FID_MOVEMENT_DETECTOR_FLEX
+            Map.entry("0x1090", "fid-movement-detector"), // FID_MOVEMENT_DETECTOR_TYPE0
+            Map.entry("0x1810", "fid-switch-actuator-flex"), // FID_DIMMING_ACTUATOR_FLEX
+            Map.entry("0x1810", "fid-dim-actuator"), // FID_DIMMING_ACTUATOR_TYPE0
+            Map.entry("0x1821", "fid-wireless-blind-actuator"), // FID_BLIND_ACTUATOR_WIRELESS
             Map.entry("0x4800", "fid-scene-trigger"), // FID_SCENE_TRIGGER
             Map.entry("0x4A00", "fid-rule-switch"), // FID_RULE_SWITCH
+            Map.entry("0xB03F", "fid-air-quality-sensor-humidity"), // FID_AIRQUALITYSENSOR_HUMIDITY
             Map.entry("0xE017", "fid-air-quality-sensor-pressure"), // FID_AIRQUALITYSENSOR_PRESSURE
             Map.entry("0xE018", "fid-air-quality-sensor-co2"), // FID_AIRQUALITYSENSOR_CO2
             Map.entry("0xE019", "fid-air-quality-sensor-co"), // FID_AIRQUALITYSENSOR_CO
@@ -342,10 +429,8 @@ public class FidTranslationUtils {
             Map.entry("0xE01B", "fid-air-quality-sensor-o3"), // FID_AIRQUALITYSENSOR_O3
             Map.entry("0xE01C", "fid-air-quality-sensor-pm10"), // FID_AIRQUALITYSENSOR_PM10
             Map.entry("0xE01D", "fid-air-quality-sensor-pm25"), // FID_AIRQUALITYSENSOR_PM25
-            Map.entry("0xE01E", "fid-air-quality-sensor-voc"), // FID_AIRQUALITYSENSOR_VOC
-            Map.entry("0xB03F", "fid-air-quality-sensor-humidity"), // FID_AIRQUALITYSENSOR_HUMIDITY
-            Map.entry("0x1090", "fid-movement-detector-flex"), // FID_MOVEMENT_DETECTOR_FLEX
-            Map.entry("0x1810", "fid-dim-actuator-flex") // FID_SWITCH_ACTUATOR_FLEX
+            Map.entry("0xE01E", "fid-air-quality-sensor-voc") // FID_AIRQUALITYSENSOR_VOC
+
     );
 
     @Nullable
diff --git a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/util/FreeAtHomeHttpCommunicationException.java b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/util/FreeAtHomeHttpCommunicationException.java
index 499feb653a0a2..afbd8198f4a26 100644
--- a/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/util/FreeAtHomeHttpCommunicationException.java
+++ b/bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/util/FreeAtHomeHttpCommunicationException.java
@@ -28,7 +28,7 @@
 public class FreeAtHomeHttpCommunicationException extends Exception {
     private static final long serialVersionUID = -817364286035448863L;
     private String errorMessage = "Unknown_Exception";
-    private int errorCode;
+    private final int errorCode;
 
     public FreeAtHomeHttpCommunicationException(int errorCode, String message) {
         super(message);
@@ -37,6 +37,7 @@ public FreeAtHomeHttpCommunicationException(int errorCode, String message) {
         this.errorCode = errorCode;
     }
 
+    @Override
     public @Nullable String getMessage() {
         return this.errorMessage;
     }
diff --git a/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/addon/addon.xml
index 3733278678abc..95de143568f09 100644
--- a/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/addon/addon.xml
+++ b/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/addon/addon.xml
@@ -5,6 +5,6 @@
 
 	<type>binding</type>
 	<name>FreeAtHome Binding</name>
-	<description>This is the binding for free@home system.</description>
+	<description>This is the binding for Busch Jaeger (alternativ Vendor ABB) free@home system.</description>
 	<connection>local</connection>
 </addon:addon>
diff --git a/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/i18n/freeathomesystem.properties b/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/i18n/freeathome.properties
similarity index 97%
rename from bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/i18n/freeathomesystem.properties
rename to bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/i18n/freeathome.properties
index fb2ae8d7bc722..e6a547ad437d2 100644
--- a/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/i18n/freeathomesystem.properties
+++ b/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/i18n/freeathome.properties
@@ -1,7 +1,7 @@
 # add-on
 
 addon.freeathome.name = FreeAtHome Binding
-addon.freeathome.description = This is the binding for free@home system.
+addon.freeathome.description = This is the binding for Busch Jaeger (alternativ Vendor ABB) free@home system.
 
 # thing types
 
@@ -14,12 +14,16 @@ thing-type.freeathome.gateway.description = This gateway represents the free@hom
 
 thing-type.config.freeathome.device.deviceId.label = Device Id
 thing-type.config.freeathome.device.deviceId.description = This is the unique id of the free@home device (Please do not modify after the Thing is generated)
+thing-type.config.freeathome.gateway.group.advanced_com_settings.label = Advanced Communication Settings
+thing-type.config.freeathome.gateway.group.advanced_com_settings.description = Detailed settings for SysAp communication behavior
 thing-type.config.freeathome.gateway.group.identification.label = SysAP Setting
 thing-type.config.freeathome.gateway.group.identification.description = SysAP network address and user settings
 thing-type.config.freeathome.gateway.ipAddress.label = Sysap IP Address
 thing-type.config.freeathome.gateway.ipAddress.description = IP Address of the Busch-Jaeger Gateway
 thing-type.config.freeathome.gateway.password.label = Password
 thing-type.config.freeathome.gateway.password.description = Password for gateway
+thing-type.config.freeathome.gateway.sendKeepAliveMessage.label = Send keep-alive message to SysAp
+thing-type.config.freeathome.gateway.sendKeepAliveMessage.description = Older SysAp might require a keep-alive message over the socket instead of a ping message.
 thing-type.config.freeathome.gateway.username.label = User Name
 thing-type.config.freeathome.gateway.username.description = The login name
 
@@ -29,6 +33,7 @@ comm-error.not-able-open-httpconnection = Cannot open http connection, wrong pas
 comm-error.http-wrongpass-or-ip = Cannot open http connection, wrong password or IP address
 comm-error.not-able-open-websocketconnection = Cannot open websocket connection
 comm-error.general-websocket-issue = General issue in websocket connection
+comm-error.fetch-version-error = Connected, but failed to fetch SysAP version
 comm-error.websocket-keep-alive-error = Websocket keep alive error
 comm-error.wrong-credentials = Wrong credentials for SysAP
 comm-error.error-in-sysap-com = Error in SysAp communication
diff --git a/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/thing/bridge-type.xml b/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/thing/bridge-type.xml
index 9c05898742450..3974f4b4e29fc 100644
--- a/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/thing/bridge-type.xml
+++ b/bundles/org.openhab.binding.freeathome/src/main/resources/OH-INF/thing/bridge-type.xml
@@ -16,6 +16,12 @@
 				<advanced>false</advanced>
 			</parameter-group>
 
+			<parameter-group name="advanced_com_settings">
+				<label>Advanced Communication Settings</label>
+				<description>Detailed settings for SysAp communication behavior</description>
+				<advanced>true</advanced>
+			</parameter-group>
+
 			<parameter name="ipAddress" type="text" required="true" groupName="identification">
 				<context>network-address</context>
 				<label>Sysap IP Address</label>
@@ -31,6 +37,14 @@
 				<description>Password for gateway</description>
 			</parameter>
 
+			<parameter name="sendKeepAliveMessage" type="boolean" required="false" groupName="identification">
+				<label>Send keep-alive message to SysAp</label>
+				<context>password</context>
+				<description>Older SysAp might require a keep-alive message over the socket instead of a ping message.</description>
+				<default>false</default>
+				<advanced>true</advanced>
+			</parameter>
+
 		</config-description>
 	</bridge-type>
 </thing:thing-descriptions>


Reviews:
user: login: lsiepel
body: Thanks for this improvement. Besides the generic comment about log levels, also check the properties file as i expect some more changes when the i18n plugin is run.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T12:58:03Z
user: login: ML19821
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T19:31:18Z
user: login: ML19821
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T19:39:36Z
user: login: ML19821
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-10T20:14:26Z
user: login: lsiepel
body: Thanks, LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-11T13:02:20Z

Review comments:
diff_hunk: @@ -131,10 +136,86 @@ public Collection<Class<? extends ThingHandlerService>> getServices() {
         return List.of(FreeAtHomeDiscoveryService.class);
     }
 
+    /**
+     * Method to fetch SysApp Version
+     */
+    public boolean fetchSysapVersion() {
+        String url = baseUrl + "/rest/configuration";
+        try {
+            HttpClient client = httpClient;
+            Request req = client.newRequest(url);
+
+            if (req == null) {
+                logger.error("Invalid request object in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            ContentResponse response = req.send();
+
+            if (response.getStatus() != 200) {
+                logger.error("HTTP request failed in fetchSysapVersion with status [{}] and reason [{}]",
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: lsiepel
body: ```suggestion
                logger.warn("HTTP request failed in fetchSysapVersion with status [{}] and reason [{}]",
```
created_at: 2025-05-02T12:46:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -131,10 +136,86 @@ public Collection<Class<? extends ThingHandlerService>> getServices() {
         return List.of(FreeAtHomeDiscoveryService.class);
     }
 
+    /**
+     * Method to fetch SysApp Version
+     */
+    public boolean fetchSysapVersion() {
+        String url = baseUrl + "/rest/configuration";
+        try {
+            HttpClient client = httpClient;
+            Request req = client.newRequest(url);
+
+            if (req == null) {
+                logger.error("Invalid request object in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            ContentResponse response = req.send();
+
+            if (response.getStatus() != 200) {
+                logger.error("HTTP request failed in fetchSysapVersion with status [{}] and reason [{}]",
+                        response.getStatus(), response.getReason());
+                return false;
+            }
+
+            String configString = new String(response.getContent());
+
+            JsonReader reader = new JsonReader(new StringReader(configString));
+            reader.setLenient(true);
+            JsonElement jsonTree = JsonParser.parseReader(reader);
+
+            if (!jsonTree.isJsonObject()) {
+                logger.error("Invalid jsonObject in fetchSysapVersion with the URL [ {} ]", url);
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: lsiepel
body: Please also adjust other log levels. error should only be used for a system wide issue or serieus bug. Not if the binding can recover or the scope is limited to the binding.

```suggestion
                logger.warn("Invalid jsonObject in fetchSysapVersion with the URL [ {} ]", url);
```
created_at: 2025-05-02T12:47:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -131,10 +136,86 @@ public Collection<Class<? extends ThingHandlerService>> getServices() {
         return List.of(FreeAtHomeDiscoveryService.class);
     }
 
+    /**
+     * Method to fetch SysApp Version
+     */
+    public boolean fetchSysapVersion() {
+        String url = baseUrl + "/rest/configuration";
+        try {
+            HttpClient client = httpClient;
+            Request req = client.newRequest(url);
+
+            if (req == null) {
+                logger.error("Invalid request object in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            ContentResponse response = req.send();
+
+            if (response.getStatus() != 200) {
+                logger.error("HTTP request failed in fetchSysapVersion with status [{}] and reason [{}]",
+                        response.getStatus(), response.getReason());
+                return false;
+            }
+
+            String configString = new String(response.getContent());
+
+            JsonReader reader = new JsonReader(new StringReader(configString));
+            reader.setLenient(true);
+            JsonElement jsonTree = JsonParser.parseReader(reader);
+
+            if (!jsonTree.isJsonObject()) {
+                logger.error("Invalid jsonObject in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonObject jsonObject = jsonTree.getAsJsonObject();
+            JsonObject sysapObject = jsonObject.getAsJsonObject(sysApUID);
+
+            if (sysapObject == null) {
+                logger.error("SysAP object not found in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonObject sysapDetails = sysapObject.getAsJsonObject("sysap");
+            if (sysapDetails == null) {
+                logger.error("SysAP details not found in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonElement versionElement = sysapDetails.get("version");
+            if (versionElement == null || !versionElement.isJsonPrimitive()) {
+                logger.error("Version not found or invalid in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            sysapVersion = versionElement.getAsString();
+            logger.debug("SysAP version fetched: {}", sysapVersion);
+            return true;
+
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            logger.error("Http communication interrupted in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        } catch (ExecutionException | TimeoutException e) {
+            logger.error("Http communication interrupted in fetchSysapVersion: {}", e.getMessage());
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: lsiepel
body: ```suggestion
            logger.error("Http communication exception in fetchSysapVersion: {}", e.getMessage());
```
created_at: 2025-05-02T12:48:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -131,10 +136,86 @@ public Collection<Class<? extends ThingHandlerService>> getServices() {
         return List.of(FreeAtHomeDiscoveryService.class);
     }
 
+    /**
+     * Method to fetch SysApp Version
+     */
+    public boolean fetchSysapVersion() {
+        String url = baseUrl + "/rest/configuration";
+        try {
+            HttpClient client = httpClient;
+            Request req = client.newRequest(url);
+
+            if (req == null) {
+                logger.error("Invalid request object in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            ContentResponse response = req.send();
+
+            if (response.getStatus() != 200) {
+                logger.error("HTTP request failed in fetchSysapVersion with status [{}] and reason [{}]",
+                        response.getStatus(), response.getReason());
+                return false;
+            }
+
+            String configString = new String(response.getContent());
+
+            JsonReader reader = new JsonReader(new StringReader(configString));
+            reader.setLenient(true);
+            JsonElement jsonTree = JsonParser.parseReader(reader);
+
+            if (!jsonTree.isJsonObject()) {
+                logger.error("Invalid jsonObject in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonObject jsonObject = jsonTree.getAsJsonObject();
+            JsonObject sysapObject = jsonObject.getAsJsonObject(sysApUID);
+
+            if (sysapObject == null) {
+                logger.error("SysAP object not found in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonObject sysapDetails = sysapObject.getAsJsonObject("sysap");
+            if (sysapDetails == null) {
+                logger.error("SysAP details not found in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonElement versionElement = sysapDetails.get("version");
+            if (versionElement == null || !versionElement.isJsonPrimitive()) {
+                logger.error("Version not found or invalid in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            sysapVersion = versionElement.getAsString();
+            logger.debug("SysAP version fetched: {}", sysapVersion);
+            return true;
+
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            logger.error("Http communication interrupted in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        } catch (ExecutionException | TimeoutException e) {
+            logger.error("Http communication interrupted in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        } catch (JsonParseException e) {
+            logger.error("Invalid JSON in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        } catch (Exception e) {
+            logger.error("Unexpected error in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        }
+    }
+
     /**
      * Method to get the device list
      */
     public List<String> getDeviceDeviceList() throws FreeAtHomeHttpCommunicationException {
+
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: lsiepel
body: ```suggestion
```
created_at: 2025-05-02T12:48:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -232,7 +313,7 @@ public FreeAtHomeDeviceDescription getFreeatHomeDeviceDescription(String id)
             String deviceString = new String(response.getContent());
 
             JsonReader reader = new JsonReader(new StringReader(deviceString));
-            reader.setLenient(true);
+            reader.setLenient(true); // reader.setStrictness(Strictness.LENIENT);
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: lsiepel
body: What is the purpose of the comment? Also see other occurrences.
created_at: 2025-05-02T12:49:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -470,81 +551,118 @@ public void unregisterDeviceStateListener(String deviceID) {
     }
 
     /**
-     * Method to open Http connection
+     * Establishes an HTTP connection to the free@home system.
+     * This method sets up authentication and tests the connection by making a request.
+     *
+     * @return true if the HTTP connection is successfully established, false otherwise.
      */
     public boolean openHttpConnection() {
-        boolean ret = false;
+        logger.debug("Attempting to open HTTP connection to free@home system");
 
         try {
-            // Add authentication credentials.
+            // Set up authentication for the HTTP client
             AuthenticationStore auth = httpClient.getAuthenticationStore();
+            URI baseUri = new URI(baseUrl);
+            auth.addAuthenticationResult(new BasicAuthentication.BasicResult(baseUri, username, password));
 
-            URI uri1 = new URI(baseUrl);
-            auth.addAuthenticationResult(new BasicAuthentication.BasicResult(uri1, username, password));
-
-            String url = baseUrl + BRIDGE_URL_GETDEVICELIST;
+            // Construct the URL for the device list (used as a test endpoint)
+            String testUrl = baseUrl + BRIDGE_URL_GETDEVICELIST;
+            logger.debug("Test URL for HTTP connection: {}", testUrl);
 
-            Request req = httpClient.newRequest(url);
-            ContentResponse res = req.send();
+            // Create and send a test request
+            Request request = httpClient.newRequest(testUrl);
+            if (request == null) {
+                throw new IllegalStateException("Failed to create HTTP request");
+            }
 
-            // check status
-            if (res.getStatus() == HttpStatus.OK_200) {
-                // response OK
-                httpConnectionOK.set(true);
+            // Send the request and get the response
+            ContentResponse response = request.send();
 
-                ret = true;
+            // Check the response status
+            int statusCode = response.getStatus();
+            logger.debug("HTTP response status code: {}", statusCode);
 
-                logger.debug("HTTP connection to SysAP is OK");
+            if (statusCode == HttpStatus.OK_200) {
+                logger.info("HTTP connection to free@home system established successfully");
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: lsiepel
body: ```suggestion
                logger.debug("HTTP connection to free@home system established successfully");
```
created_at: 2025-05-02T12:50:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -470,81 +551,118 @@ public void unregisterDeviceStateListener(String deviceID) {
     }
 
     /**
-     * Method to open Http connection
+     * Establishes an HTTP connection to the free@home system.
+     * This method sets up authentication and tests the connection by making a request.
+     *
+     * @return true if the HTTP connection is successfully established, false otherwise.
      */
     public boolean openHttpConnection() {
-        boolean ret = false;
+        logger.debug("Attempting to open HTTP connection to free@home system");
 
         try {
-            // Add authentication credentials.
+            // Set up authentication for the HTTP client
             AuthenticationStore auth = httpClient.getAuthenticationStore();
+            URI baseUri = new URI(baseUrl);
+            auth.addAuthenticationResult(new BasicAuthentication.BasicResult(baseUri, username, password));
 
-            URI uri1 = new URI(baseUrl);
-            auth.addAuthenticationResult(new BasicAuthentication.BasicResult(uri1, username, password));
-
-            String url = baseUrl + BRIDGE_URL_GETDEVICELIST;
+            // Construct the URL for the device list (used as a test endpoint)
+            String testUrl = baseUrl + BRIDGE_URL_GETDEVICELIST;
+            logger.debug("Test URL for HTTP connection: {}", testUrl);
 
-            Request req = httpClient.newRequest(url);
-            ContentResponse res = req.send();
+            // Create and send a test request
+            Request request = httpClient.newRequest(testUrl);
+            if (request == null) {
+                throw new IllegalStateException("Failed to create HTTP request");
+            }
 
-            // check status
-            if (res.getStatus() == HttpStatus.OK_200) {
-                // response OK
-                httpConnectionOK.set(true);
+            // Send the request and get the response
+            ContentResponse response = request.send();
 
-                ret = true;
+            // Check the response status
+            int statusCode = response.getStatus();
+            logger.debug("HTTP response status code: {}", statusCode);
 
-                logger.debug("HTTP connection to SysAP is OK");
+            if (statusCode == HttpStatus.OK_200) {
+                logger.info("HTTP connection to free@home system established successfully");
+                httpConnectionOK.set(true);
+                return true;
             } else {
-                // response NOK, set error
+                logger.warn("HTTP connection failed. Status code: {}, Reason: {}", statusCode, response.getReason());
                 httpConnectionOK.set(false);
-
-                ret = false;
+                return false;
             }
-        } catch (URISyntaxException | InterruptedException | ExecutionException | TimeoutException ex) {
-            logger.debug("Initial HTTP connection to SysAP is not successful");
 
-            ret = false;
+        } catch (URISyntaxException e) {
+            logger.error("Invalid URI syntax for base URL: {}", baseUrl, e);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            logger.error("HTTP connection attempt was interrupted", e);
+        } catch (TimeoutException e) {
+            logger.error("HTTP connection attempt timed out", e);
+        } catch (ExecutionException e) {
+            logger.error("Error executing HTTP request", e);
+        } catch (Exception e) {
+            logger.error("Unexpected error while opening HTTP connection", e);
         }
 
-        return ret;
+        httpConnectionOK.set(false);
+        return false;
     }
 
     /**
-     * Method to connect the websocket session
+     * Method to connect the WebSocket session.
+     * Attempts to establish a WebSocket connection to the SysAP and handles authentication.
+     * 
+     * @return true if the connection attempt is initiated successfully, false otherwise
      */
     public boolean connectWebsocketSession() {
         boolean ret = false;
 
+        // Create the WebSocket URI using the configured IP address
         URI uri = URI.create("ws://" + ipAddress + "/fhapi/v1/api/ws");
 
+        // Combine username and password for authentication
         String authString = username + ":" + password;
 
-        // create base64 encoder
+        // Create a Base64 encoder
         Base64.Encoder bas64Encoder = Base64.getEncoder();
 
-        // Encoding string using encoder object
+        // Encode the authentication string to Base64
         String authStringEnc = bas64Encoder.encodeToString(authString.getBytes());
 
+        // Set the Authorization header value
         authField = "Basic " + authStringEnc;
 
         WebSocketClient localWebsocketClient = websocketClient;
 
         try {
-            // Start socket client
+            // Start the WebSocket client if it exists
             if (localWebsocketClient != null) {
                 localWebsocketClient.setMaxTextMessageBufferSize(8 * 1024);
-                localWebsocketClient.setMaxIdleTimeout(BRIDGE_WEBSOCKET_TIMEOUT * 60 * 1000);
-                localWebsocketClient.setConnectTimeout(BRIDGE_WEBSOCKET_TIMEOUT * 60 * 1000);
+
+                // Set the maximum idle timeout for the WebSocket connection.
+                // If no activity occurs within this time, the connection will be closed automatically.
+                localWebsocketClient.setMaxIdleTimeout(BRIDGE_WEBSOCKET_TIMEOUT * 1000); // milliseconds
+
+                // Set the connection timeout for the WebSocket client.
+                // This defines how long the client should wait before considering the connection attempt as failed.
+                // Like the idle timeout, the value is converted from seconds to milliseconds.
+                localWebsocketClient.setConnectTimeout(BRIDGE_WEBSOCKET_TIMEOUT * 1000); // milliseconds
                 localWebsocketClient.start();
                 ClientUpgradeRequest request = new ClientUpgradeRequest();
                 request.setHeader("Authorization", authField);
-                request.setTimeout(BRIDGE_WEBSOCKET_TIMEOUT, TimeUnit.MINUTES);
-                localWebsocketClient.connect(this, uri, request);
 
-                logger.debug("Websocket connection to SysAP is OK, timeout: {}", BRIDGE_WEBSOCKET_TIMEOUT);
+                // Set the timeout for the WebSocket upgrade process (i.e., the time allowed for the handshake to
+                // complete).
+                // Unlike `setConnectTimeout()`, which applies to the lower-level network connection, this timeout
+                // applies specifically to the WebSocket upgrade request.
+                // The timeout is specified in SECONDS, using `TimeUnit.SECONDS`.
+                request.setTimeout(BRIDGE_WEBSOCKET_TIMEOUT, TimeUnit.SECONDS);
+                localWebsocketClient.connect(this, uri, request);
 
+                logger.debug("WebSocket connection attempt initiated, timeout: {} seconds", BRIDGE_WEBSOCKET_TIMEOUT);
                 ret = true;
+
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: lsiepel
body: ```suggestion
```
created_at: 2025-05-02T12:50:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -778,15 +990,25 @@ private boolean connectSession() throws InterruptedException {
                 return false;
             }
 
-            if (websocketSession == null) {
-                lock.lock();
-                try {
-                    websocketSessionEstablished.await();
-                } finally {
-                    lock.unlock();
+            // Wait for connection to be established or fail with a timeout
+            lock.lock();
+            try {
+                if (websocketSession == null) {
+                    boolean established = websocketSessionEstablished.await(BRIDGE_WEBSOCKET_TIMEOUT, TimeUnit.SECONDS);
+                    if (!established || websocketSession == null) {
+                        logger.debug("WebSocket connection timed out or failed during establishment");
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: lsiepel
body: No need to log while the thingstatus is also set. (and logged)
created_at: 2025-05-02T12:52:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -232,7 +313,7 @@ public FreeAtHomeDeviceDescription getFreeatHomeDeviceDescription(String id)
             String deviceString = new String(response.getContent());
 
             JsonReader reader = new JsonReader(new StringReader(deviceString));
-            reader.setLenient(true);
+            reader.setLenient(true); // reader.setStrictness(Strictness.LENIENT);
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: ML19821
body: see just pushed commit, "Add comment explaining deprecated use of setLenient(true) for backward compatibility"

created_at: 2025-05-07T19:31:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -232,7 +313,7 @@ public FreeAtHomeDeviceDescription getFreeatHomeDeviceDescription(String id)
             String deviceString = new String(response.getContent());
 
             JsonReader reader = new JsonReader(new StringReader(deviceString));
-            reader.setLenient(true);
+            reader.setLenient(true); // reader.setStrictness(Strictness.LENIENT);
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: ML19821
body: thanks for reviewing the code. For this topic, please refer to the two latest commits. I’ll address your other comments next weekend.
created_at: 2025-05-07T19:39:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -131,10 +136,86 @@ public Collection<Class<? extends ThingHandlerService>> getServices() {
         return List.of(FreeAtHomeDiscoveryService.class);
     }
 
+    /**
+     * Method to fetch SysApp Version
+     */
+    public boolean fetchSysapVersion() {
+        String url = baseUrl + "/rest/configuration";
+        try {
+            HttpClient client = httpClient;
+            Request req = client.newRequest(url);
+
+            if (req == null) {
+                logger.error("Invalid request object in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            ContentResponse response = req.send();
+
+            if (response.getStatus() != 200) {
+                logger.error("HTTP request failed in fetchSysapVersion with status [{}] and reason [{}]",
+                        response.getStatus(), response.getReason());
+                return false;
+            }
+
+            String configString = new String(response.getContent());
+
+            JsonReader reader = new JsonReader(new StringReader(configString));
+            reader.setLenient(true);
+            JsonElement jsonTree = JsonParser.parseReader(reader);
+
+            if (!jsonTree.isJsonObject()) {
+                logger.error("Invalid jsonObject in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonObject jsonObject = jsonTree.getAsJsonObject();
+            JsonObject sysapObject = jsonObject.getAsJsonObject(sysApUID);
+
+            if (sysapObject == null) {
+                logger.error("SysAP object not found in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonObject sysapDetails = sysapObject.getAsJsonObject("sysap");
+            if (sysapDetails == null) {
+                logger.error("SysAP details not found in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            JsonElement versionElement = sysapDetails.get("version");
+            if (versionElement == null || !versionElement.isJsonPrimitive()) {
+                logger.error("Version not found or invalid in fetchSysapVersion with the URL [ {} ]", url);
+                return false;
+            }
+
+            sysapVersion = versionElement.getAsString();
+            logger.debug("SysAP version fetched: {}", sysapVersion);
+            return true;
+
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            logger.error("Http communication interrupted in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        } catch (ExecutionException | TimeoutException e) {
+            logger.error("Http communication interrupted in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        } catch (JsonParseException e) {
+            logger.error("Invalid JSON in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        } catch (Exception e) {
+            logger.error("Unexpected error in fetchSysapVersion: {}", e.getMessage());
+            return false;
+        }
+    }
+
     /**
      * Method to get the device list
      */
     public List<String> getDeviceDeviceList() throws FreeAtHomeHttpCommunicationException {
+
path: bundles/org.openhab.binding.freeathome/src/main/java/org/openhab/binding/freeathome/internal/handler/FreeAtHomeBridgeHandler.java
user: login: ML19821
body: Resolved in the latest commit.
created_at: 2025-05-10T20:14:26Z
author_association: CONTRIBUTOR

PR comments:
user: login: openhab-bot
created_at: 2025-05-01T21:36:32Z
body: This pull request has been mentioned on **openHAB Community**. There might be relevant details there:

https://community.openhab.org/t/abb-busch-jager-free-home-official-rest-api/141698/167

author_association: COLLABORATOR
user: login: ML19821
created_at: 2025-05-10T20:17:29Z
body: > Thanks for this improvement. Besides the generic comment about log levels, also check the properties file as i expect some more changes when the i18n plugin is run.

@lsiepel Thanks! I'm still pretty new to the whole i18n stuff, so I'm not really sure what I’m supposed to do with the properties file or what happens when the i18n plugin runs. Could you give me a bit more guidance?
author_association: CONTRIBUTOR
user: login: lsiepel
created_at: 2025-05-10T22:36:29Z
body: > > Thanks for this improvement. Besides the generic comment about log levels, also check the properties file as i expect some more changes when the i18n plugin is run.
> 
> @lsiepel Thanks! I'm still pretty new to the whole i18n stuff, so I'm not really sure what I’m supposed to do with the properties file or what happens when the i18n plugin runs. Could you give me a bit more guidance?

Just run `mvn i18n:generate-default-translations` form your binding folder.
For reference: https://www.openhab.org/docs/developer/utils/i18n.html#generating-i18n-properties-file
author_association: CONTRIBUTOR
user: login: ML19821
created_at: 2025-05-11T09:07:52Z
body: Mind having a quick look to see if the property file renaming went through correctly?
Still figuring Git out, so not totally sure if the old file (freeathomesystem.properties) got removed properly.
author_association: CONTRIBUTOR