diff --git a/CODEOWNERS b/CODEOWNERS
index 69a36f516e7a3..f2e185b3a7975 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -128,6 +128,7 @@
/bundles/org.openhab.binding.minecraft/ @ibaton
/bundles/org.openhab.binding.modbus/ @ssalonen
/bundles/org.openhab.binding.modbus.sunspec/ @mrbig
+/bundles/org.openhab.binding.modbus.stiebeleltron/ @pail23
/bundles/org.openhab.binding.mqtt/ @davidgraeff
/bundles/org.openhab.binding.mqtt.generic/ @davidgraeff
/bundles/org.openhab.binding.mqtt.homeassistant/ @davidgraeff
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index a51e78c240322..5778ce8181597 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -629,6 +629,11 @@
org.openhab.binding.modbus
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.modbus.stiebeleltron
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.mqtt
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/.classpath b/bundles/org.openhab.binding.modbus.stiebeleltron/.classpath
new file mode 100644
index 0000000000000..39abf1c5e9102
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/.classpath
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/.project b/bundles/org.openhab.binding.modbus.stiebeleltron/.project
new file mode 100644
index 0000000000000..b285157a2c7f4
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/.project
@@ -0,0 +1,23 @@
+
+
+ org.openhab.binding.modbus.stiebeleltron
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/NOTICE b/bundles/org.openhab.binding.modbus.stiebeleltron/NOTICE
new file mode 100644
index 0000000000000..38d625e349232
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-addons
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/README.md b/bundles/org.openhab.binding.modbus.stiebeleltron/README.md
new file mode 100644
index 0000000000000..c58a896c18640
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/README.md
@@ -0,0 +1,206 @@
+# Stiebel Eltron ISG Binding
+
+This extension adds support for the Stiebel Eltron modbus protocol.
+
+An Internet Service Gateway (ISG) with an installed modbus extension is required in order to run this binding.
+In case the modbus extension is not yet installed on the ISG, the ISG Updater Tool for the update can be found here: https://www.stiebel-eltron.de/de/home/produkte-loesungen/erneuerbare_energien/regelung_energiemanagement/internet_servicegateway/isg_web/downloads.html
+
+
+
+
+## Supported Things
+
+This bundle adds the following thing types to the Modbus binding.
+Note, that the things will show up under the Modbus binding.
+
+| Thing | ThingTypeID | Description |
+| ------------------ | ----------- | -------------------------------------------------- |
+| Stiebel Eltron ISG | heatpump | A stiebel eltron heat pump connected through a ISG |
+
+## Discovery
+
+This extension does not support autodiscovery. The things need to be added manually.
+
+A typical bridge configuration would look like this:
+
+```
+Bridge modbus:tcp:bridge [ host="10.0.0.2", port=502, id=1 ]
+```
+
+
+## Thing Configuration
+
+You need first to set up a TCP Modbus bridge according to the Modbus documentation.
+Things in this extension will use the selected bridge to connect to the device.
+
+The following parameters are valid for all thing types:
+
+| Parameter | Type | Required | Default if omitted | Description |
+| --------- | ------- | -------- | ------------------ | -------------------------------------------------------------------------- |
+| refresh | integer | no | 5 | Poll interval in seconds. Increase this if you encounter connection errors |
+| maxTries | integer | no | 3 | Number of retries when before giving up reading from this thing. |
+
+## Channels
+
+Channels are grouped into channel groups.
+
+### System State Group
+
+This group contains general operational information about the heat pump.
+
+| Channel ID | Item Type | Read only | Description |
+| ---------------- | --------- | --------- | ------------------------------------------------------------- |
+| is-heating | Contact | true | OPEN in case the heat pump is currently in heating mode |
+| is-heating-water | Contact | true | OPEN in case the heat pump is currently in heating water mode |
+| is-cooling | Contact | true | OPEN in case the heat pump is currently in cooling mode |
+| is-pumping | Contact | true | OPEN in case the heat pump is currently in pumping mode |
+| is-summer | Contact | true | OPEN in case the heat pump is currently in summer mode |
+
+### System Parameters Group
+
+This group contains system paramters of the heat pump.
+
+| Channel ID | Item Type | Read only | Description |
+| --------------------------- | ------------------ | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
+| operation-mode | Number | false | The current operation mode of the heat pump (1=ready mode, 2=program mode, 3=comfort mode, 4=eco mode, 5=heating water mode, 0=emergency mode) |
+| comfort-temperature-heating | Number:Temperature | false | The current heating comfort temperature |
+| eco-temperature-heating | Number:Temperature | false | The current heating eco temperature |
+| comfort-temperature-water | Number:Temperature | false | The current water comfort temperature |
+| eco-temperature-water | Number:Temperature | false | The current water eco temperature |
+
+### System Information Group
+
+This group contains general operational information about the device.
+
+| Channel ID | Item Type | Read only | Description |
+| -------------------------- | -------------------- | --------- | ----------------------------------------------------- |
+| fek-temperature | Number:Temperature | true | The current temperature measured by the FEK |
+| fek-temperature-setpoint | Number:Temperature | true | The current set point of the FEK temperature |
+| fek-humidity | Number:Dimensionless | true | The current humidity measured by the FEK |
+| fek-dewpoint | Number:Temperature | true | The current dew point temperature measured by the FEK |
+| outdoor-temperature | Number:Temperature | true | The current outdoor temperature |
+| hk1-temperature | Number:Temperature | true | The current temperature of the HK1 |
+| hk1-temperature-setpoint | Number:Temperature | true | The current temperature set point of the HK1 |
+| supply-temperature | Number:Temperature | true | The current supply temperature |
+| return-temperature | Number:Temperature | true | The current return measured |
+| source-temperature | Number:Temperature | true | The current sourcetemperature |
+| water-temperature | Number:Temperature | true | The current water temperature |
+| water-temperature-setpoint | Number:Temperature | true | The current water temperature set point |
+
+### Energy Information Group
+
+This group contains about the energy consumption and delivery of the heat pump.
+
+| Channel ID | Item Type | Read only | Description |
+| ----------------------- | ------------- | --------- | ------------------------------------------------ |
+| production-heat-today | Number:Energy | true | The heat quantity delivered today |
+| production-heat-total | Number:Energy | true | The heat quantity delivered in total |
+| production-water-today | Number:Energy | true | The water heat quantity delivered today |
+| production-water-total | Number:Energy | true | The water heat quantity delivered in total |
+| consumption-heat-today | Number:Energy | true | The power consumption for heating today |
+| consumption-heat-total | Number:Energy | true | The power consumption for heating in total |
+| consumption-water-today | Number:Energy | true | The power consumption for water heating today |
+| consumption-water-total | Number:Energy | true | The power consumption for water heating in total |
+
+
+
+## Full Example
+
+### Thing Configuration
+
+```
+Bridge modbus:tcp:bridge [ host="hostname|ip", port=502, id=1]
+Thing modbus:heatpump:stiebelEltron "StiebelEltron" (modbus:tcp:modbusbridge) [ ]
+```
+
+
+### Item Configuration
+
+```
+Number:Temperature stiebel_eltron_temperature_ffk "Temperature FFK [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#fek-temperature" }
+Number:Temperature stiebel_eltron_setpoint_ffk "Set point FFK [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#fek-temperature-setpoint" }
+Number:Dimensionless stiebel_eltron_humidity_ffk "Humidity FFK [%.1f %%]" { channel="modbus:heatpump:stiebelEltron:systemInformation#fek-humidity" }
+Number:Temperature stiebel_eltron_dewpoint_ffk "Dew point FFK [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#fek-dewpoint" }
+
+Number:Temperature stiebel_eltron_outdoor_temp "Outdoor temperature [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#outdoor-temperature" }
+Number:Temperature stiebel_eltron_temp_hk1 "Temperature HK1 [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#hk1-temperature" }
+Number:Temperature stiebel_eltron_setpoint_hk1 "Set point HK1 [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#hk1-temperature-setpoint" }
+Number:Temperature stiebel_eltron_temp_water "Water temperature [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#water-temperature" }
+Number:Temperature stiebel_eltron_setpoint_water "Water setpoint [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#water-temperature-setpoint" }
+Number:Temperature stiebel_eltron_source_temp "Source temperature [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#source-temperature" }
+Number:Temperature stiebel_eltron_vorlauf_temp "Supply tempertature [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#supply-temperature" }
+Number:Temperature stiebel_eltron_ruecklauf_temp "Return temperature [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemInformation#return-temperature" }
+
+Number stiebel_eltron_heating_comfort_temp "Heating Comfort Temperature [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemParameter#comfort-temperature-heating" }
+Number stiebel_eltron_heating_eco_temp "Heating Eco Temperature [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemParameter#eco-temperature-heating" }
+Number stiebel_eltron_water_comfort_temp "Water Comfort Temperature [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemParameter#comfort-temperature-water" }
+Number stiebel_eltron_water_eco_temp "Water Eco Temperature [%.1f °C]" { channel="modbus:heatpump:stiebelEltron:systemParameter#eco-temperature-water" }
+Number stiebel_eltron_operation_mode "Operation Mode" { channel="modbus:heatpump:stiebelEltron:systemParameter#operation-mode" }
+
+Contact stiebel_eltron_mode_pump "Pump [%d]" { channel="modbus:heatpump:stiebelEltron:systemState#is-pumping" }
+Contact stiebel_eltron_mode_heating "Heating [%d]" { channel="modbus:heatpump:stiebelEltron:systemState#is-heating" }
+Contact stiebel_eltron_mode_water "Heating Water [%d]" { channel="modbus:heatpump:stiebelEltron:systemState#is-heating-water" }
+Contact stiebel_eltron_mode_cooling "Cooling [%d]" { channel="modbus:heatpump:stiebelEltron:systemState#is-cooling" }
+Contact stiebel_eltron_mode_summer "Summer Mode [%d]" { channel="modbus:heatpump:stiebelEltron:systemState#is-summer" }
+
+
+Number:Energy stiebel_eltron_production_heat_today "Heat quantity today [%.0f kWh]" { channel="modbus:heatpump:stiebelEltron:energyInformation#production_heat_today" }
+Number:Energy stiebel_eltron_production_heat_total "Heat quantity total [%.3f MWh]" {channel="modbus:heatpump:stiebelEltron:energyInformation#production_heat_total"}
+Number:Energy stiebel_eltron_production_water_today "Water heat quantity today [%.0f kWh]" { channel="modbus:heatpump:stiebelEltron:energyInformation#production_water_today" }
+Number:Energy stiebel_eltron_production_water_total "Water heat quantity total [%.3f MWh]" {channel="modbus:heatpump:stiebelEltron:energyInformation#production_water_total"}
+Number:Energy stiebel_eltron_consumption_heat_total "Heating power consumption total [%.3f MWh]" {channel="modbus:heatpump:stiebelEltron:energyInformation#consumption_heat_total"}
+Number:Energy stiebel_eltron_consumption_heat_today "Heating power consumption today [%.0f kWh]" { channel="modbus:heatpump:stiebelEltron:energyInformation#consumption_heat_today" }
+Number:Energy stiebel_eltron_consumption_water_today "Water heating power consumption today [%.0f kWh]" { channel="modbus:heatpump:stiebelEltron:energyInformation#consumption_water_today" }
+Number:Energy stiebel_eltron_consumption_water_total "Water heating power consumption total [%.3f MWh]" {channel="modbus:heatpump:stiebelEltron:energyInformation#consumption_water_total"}
+
+
+```
+
+### Sitemap Configuration
+
+```
+ Text label="Heat pumpt" icon="temperature" {
+ Frame label="Optation Mode" {
+ Default item=stiebel_eltron_mode_pump
+ Default item=stiebel_eltron_mode_heating
+ Default item=stiebel_eltron_mode_water
+ Default item=stiebel_eltron_mode_cooling
+ Default item=stiebel_eltron_mode_summer
+ }
+ Frame label= "State" {
+ Default item=stiebel_eltron_operation_mode icon="settings"
+ Default item=stiebel_eltron_outdoor_temp icon="temperature"
+ Default item=stiebel_eltron_temp_hk1 icon="temperature"
+ Default item=stiebel_eltron_setpoint_hk1 icon="temperature"
+ Default item=stiebel_eltron_vorlauf_temp icon="temperature"
+ Default item=stiebel_eltron_ruecklauf_temp icon="temperature"
+ Default item=stiebel_eltron_temp_water icon="temperature"
+ Default item=stiebel_eltron_setpoint_water icon="temperature"
+ Default item=stiebel_eltron_temperature_ffk icon="temperature"
+ Default item=stiebel_eltron_setpoint_ffk icon="temperature"
+ Default item=stiebel_eltron_humidity_ffk icon="humidity"
+ Default item=stiebel_eltron_dewpoint_ffk icon="temperature"
+ Default item=stiebel_eltron_source_temp icon="temperature"
+ }
+ Frame label="Paramters" {
+ Setpoint item=stiebel_eltron_heating_comfort_temp icon="temperature" step=1 minValue=5 maxValue=30
+ Setpoint item=stiebel_eltron_heating_eco_temp icon="temperature" step=1 minValue=5 maxValue=30
+ Setpoint item=stiebel_eltron_water_comfort_temp icon="temperature" step=1 minValue=10 maxValue=60
+ Setpoint item=stiebel_eltron_water_eco_temp icon="temperature" step=1 minValue=10 maxValue=60
+ }
+ Frame label="Energy consumption" {
+ Default item=stiebel_eltron_consumption_heat_today icon="energy"
+ Default item=stiebel_eltron_consumption_heat_total icon="energy"
+ Default item=stiebel_eltron_consumption_water_today icon="energy"
+ Default item=stiebel_eltron_consumption_water_total icon="energy"
+ }
+ Frame label="Heat quantity" {
+ Default item=stiebel_eltron_production_heat_today icon="radiator"
+ Default item=stiebel_eltron_production_heat_total icon="radiator"
+ Default item=stiebel_eltron_production_water_today icon="water"
+ Default item=stiebel_eltron_production_water_total icon="water"
+ }
+
+ }
+
+```
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/pom.xml b/bundles/org.openhab.binding.modbus.stiebeleltron/pom.xml
new file mode 100644
index 0000000000000..4a1805500df35
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/pom.xml
@@ -0,0 +1,32 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 2.5.9-SNAPSHOT
+
+
+ org.openhab.binding.modbus.stiebeleltron
+
+ openHAB Add-ons :: Bundles :: StiebelEltron Bundle
+
+
+
+ org.openhab.addons.bundles
+ org.openhab.binding.modbus
+ ${project.version}
+ provided
+
+
+ org.openhab.addons.bundles
+ org.openhab.io.transport.modbus
+ ${project.version}
+ provided
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/feature/feature.xml b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..5e1f17a38ce85
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/feature/feature.xml
@@ -0,0 +1,11 @@
+
+
+ file:${basedirRoot}/bundles/org.openhab.io.transport.modbus/target/feature/feature.xml
+
+
+ openhab-runtime-base
+ openhab-transport-modbus
+ mvn:org.openhab.addons.bundles/org.openhab.binding.modbus/${project.version}
+ mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.stiebeleltron/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/StiebelEltronBindingConstants.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/StiebelEltronBindingConstants.java
new file mode 100644
index 0000000000000..3a40ecb48bdfc
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/StiebelEltronBindingConstants.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.openhab.binding.modbus.ModbusBindingConstants;
+
+/**
+ * The {@link Modbus.StiebelEltronBindingConstants} class defines common
+ * constants, which are used across the whole binding.
+ *
+ * @author Paul Frank - Initial contribution
+ */
+@NonNullByDefault
+public class StiebelEltronBindingConstants {
+
+ private static final String BINDING_ID = ModbusBindingConstants.BINDING_ID;
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_HEATPUMP = new ThingTypeUID(BINDING_ID, "heatpump");
+
+ // Channel group ids
+ public static final String GROUP_SYSTEM_STATE = "systemState";
+ public static final String GROUP_SYSTEM_PARAMETER = "systemParameter";
+ public static final String GROUP_SYSTEM_INFO = "systemInformation";
+ public static final String GROUP_ENERGY_INFO = "energyInformation";
+
+ // List of all Channel ids in device information group
+ public static final String CHANNEL_FEK_TEMPERATURE = "fek-temperature";
+ public static final String CHANNEL_FEK_TEMPERATURE_SETPOINT = "fek-temperature-setpoint";
+ public static final String CHANNEL_FEK_HUMIDITY = "fek-humidity";
+ public static final String CHANNEL_FEK_DEWPOINT = "fek-dewpoint";
+ public static final String CHANNEL_OUTDOOR_TEMPERATURE = "outdoor-temperature";
+ public static final String CHANNEL_HK1_TEMPERATURE = "hk1-temperature";
+ public static final String CHANNEL_HK1_TEMPERATURE_SETPOINT = "hk1-temperature-setpoint";
+ public static final String CHANNEL_SUPPLY_TEMPERATURE = "supply-temperature";
+ public static final String CHANNEL_RETURN_TEMPERATURE = "return-temperature";
+ public static final String CHANNEL_SOURCE_TEMPERATURE = "source-temperature";
+ public static final String CHANNEL_WATER_TEMPERATURE = "water-temperature";
+ public static final String CHANNEL_WATER_TEMPERATURE_SETPOINT = "water-temperature-setpoint";
+
+ public static final String CHANNEL_PRODUCTION_HEAT_TODAY = "production-heat-today";
+ public static final String CHANNEL_PRODUCTION_HEAT_TOTAL = "production-heat-total";
+ public static final String CHANNEL_PRODUCTION_WATER_TODAY = "production-water-today";
+ public static final String CHANNEL_PRODUCTION_WATER_TOTAL = "production-water-total";
+ public static final String CHANNEL_CONSUMPTION_HEAT_TODAY = "consumption-heat-today";
+ public static final String CHANNEL_CONSUMPTION_HEAT_TOTAL = "consumption-heat-total";
+ public static final String CHANNEL_CONSUMPTION_WATER_TODAY = "consumption-water-today";
+ public static final String CHANNEL_CONSUMPTION_WATER_TOTAL = "consumption-water-total";
+
+ public static final String CHANNEL_IS_HEATING = "is-heating";
+ public static final String CHANNEL_IS_HEATING_WATER = "is-heating-water";
+ public static final String CHANNEL_IS_COOLING = "is-cooling";
+ public static final String CHANNEL_IS_PUMPING = "is-pumping";
+ public static final String CHANNEL_IS_SUMMER = "is-summer";
+
+ public static final String CHANNEL_OPERATION_MODE = "operation-mode";
+ public static final String CHANNEL_COMFORT_TEMPERATURE_HEATING = "comfort-temperature-heating";
+ public static final String CHANNEL_ECO_TEMPERATURE_HEATING = "eco-temperature-heating";
+ public static final String CHANNEL_COMFORT_TEMPERATURE_WATER = "comfort-temperature-water";
+ public static final String CHANNEL_ECO_TEMPERATURE_WATER = "eco-temperature-water";
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/StiebelEltronConfiguration.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/StiebelEltronConfiguration.java
new file mode 100644
index 0000000000000..e86e763cbc4d8
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/StiebelEltronConfiguration.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link Modbus.StiebelEltronConfiguration} class contains fields mapping
+ * thing configuration parameters.
+ *
+ * @author Paul Frank - Initial contribution
+ */
+@NonNullByDefault
+public class StiebelEltronConfiguration {
+ /**
+ * Refresh interval in seconds
+ */
+ private long refresh;
+
+ private int maxTries = 3;// backwards compatibility and tests
+
+ /**
+ * Gets refresh period in milliseconds
+ */
+ public long getRefreshMillis() {
+ return refresh * 1000;
+ }
+
+ public int getMaxTries() {
+ return maxTries;
+ }
+
+ public void setMaxTries(int maxTries) {
+ this.maxTries = maxTries;
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/StiebelEltronHandlerFactory.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/StiebelEltronHandlerFactory.java
new file mode 100644
index 0000000000000..0b150747ceecd
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/StiebelEltronHandlerFactory.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal;
+
+import static org.openhab.binding.modbus.stiebeleltron.internal.StiebelEltronBindingConstants.THING_TYPE_HEATPUMP;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory;
+import org.openhab.binding.modbus.stiebeleltron.internal.handler.StiebelEltronHandler;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * The {@link Modbus.StiebelEltronHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Paul Frank - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.stiebeleltron", service = ThingHandlerFactory.class)
+public class StiebelEltronHandlerFactory extends BaseThingHandlerFactory {
+
+ private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_HEATPUMP);
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+ if (THING_TYPE_HEATPUMP.equals(thingTypeUID)) {
+ return new StiebelEltronHandler(thing);
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/EnergyBlock.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/EnergyBlock.java
new file mode 100644
index 0000000000000..d23fd243147bd
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/EnergyBlock.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.dto;
+
+/**
+ * Dto class for the Energy Block
+ *
+ * @author Paul Frank - Initial contribution
+ *
+ */
+public class EnergyBlock {
+
+ public int productionHeatToday;
+ public int productionHeatTotalLow;
+ public int productionHeatTotalHigh;
+ public int productionWaterToday;
+ public int productionWaterTotalLow;
+ public int productionWaterTotalHigh;
+
+ public int consumptionHeatToday;
+ public int consumptionHeatTotalLow;
+ public int consumptionHeatTotalHigh;
+ public int consumptionWaterToday;
+ public int consumptionWaterTotalLow;
+ public int consumptionWaterTotalHigh;
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/SystemInformationBlock.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/SystemInformationBlock.java
new file mode 100644
index 0000000000000..14a7dbee5b53a
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/SystemInformationBlock.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.dto;
+
+/**
+ * Dto class for the System Information Block
+ *
+ * @author Paul Frank - Initial contribution
+ *
+ */
+public class SystemInformationBlock {
+
+ public short temperatureFek;
+ public short temperatureFekSetPoint;
+ public short humidityFek;
+ public short dewpointFek;
+ public short temperatureOutdoor;
+ public short temperatureHk1;
+ public short temperatureHk1SetPoint;
+ public short temperatureSupply;
+ public short temperatureReturn;
+ public short temperatureSource;
+ public short temperatureWater;
+ public short temperatureWaterSetPoint;
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/SystemParameterBlock.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/SystemParameterBlock.java
new file mode 100644
index 0000000000000..694bb43617594
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/SystemParameterBlock.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.dto;
+
+/**
+ * Dto class for the System Parameter Block
+ *
+ * @author Paul Frank - Initial contribution
+ *
+ */
+public class SystemParameterBlock {
+
+ public int operationMode;
+ public short comfortTemperatureHeating;
+ public short ecoTemperatureHeating;
+ public short comfortTemperatureWater;
+ public short ecoTemperatureWater;
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/SystemStateBlock.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/SystemStateBlock.java
new file mode 100644
index 0000000000000..320f949eefcf4
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/dto/SystemStateBlock.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.dto;
+
+/**
+ * Dto class for the System State Block
+ *
+ * @author Paul Frank - Initial contribution
+ *
+ */
+public class SystemStateBlock {
+
+ public int state;
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/handler/StiebelEltronException.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/handler/StiebelEltronException.java
new file mode 100644
index 0000000000000..7a20e0f09cbc2
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/handler/StiebelEltronException.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Thrown when the stiebel eltron handler sees an error.
+ *
+ * @author Paul Frank - Initial contribution
+ */
+@SuppressWarnings("serial")
+@NonNullByDefault
+public class StiebelEltronException extends Exception {
+
+ public StiebelEltronException() {
+ }
+
+ public StiebelEltronException(String message) {
+ super(message);
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/handler/StiebelEltronHandler.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/handler/StiebelEltronHandler.java
new file mode 100644
index 0000000000000..963c22ebf6d73
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/handler/StiebelEltronHandler.java
@@ -0,0 +1,711 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.handler;
+
+import static org.eclipse.smarthome.core.library.unit.SIUnits.CELSIUS;
+import static org.eclipse.smarthome.core.library.unit.SmartHomeUnits.KILOWATT_HOUR;
+import static org.eclipse.smarthome.core.library.unit.SmartHomeUnits.PERCENT;
+import static org.openhab.binding.modbus.stiebeleltron.internal.StiebelEltronBindingConstants.*;
+
+import java.util.Optional;
+
+import javax.measure.Unit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.smarthome.core.library.types.DecimalType;
+import org.eclipse.smarthome.core.library.types.OpenClosedType;
+import org.eclipse.smarthome.core.library.types.QuantityType;
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.ThingStatusInfo;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.types.Command;
+import org.eclipse.smarthome.core.types.RefreshType;
+import org.eclipse.smarthome.core.types.State;
+import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
+import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler;
+import org.openhab.binding.modbus.stiebeleltron.internal.StiebelEltronConfiguration;
+import org.openhab.binding.modbus.stiebeleltron.internal.dto.EnergyBlock;
+import org.openhab.binding.modbus.stiebeleltron.internal.dto.SystemInformationBlock;
+import org.openhab.binding.modbus.stiebeleltron.internal.dto.SystemParameterBlock;
+import org.openhab.binding.modbus.stiebeleltron.internal.dto.SystemStateBlock;
+import org.openhab.binding.modbus.stiebeleltron.internal.parser.EnergyBlockParser;
+import org.openhab.binding.modbus.stiebeleltron.internal.parser.SystemInfromationBlockParser;
+import org.openhab.binding.modbus.stiebeleltron.internal.parser.SystemParameterBlockParser;
+import org.openhab.binding.modbus.stiebeleltron.internal.parser.SystemStateBlockParser;
+import org.openhab.io.transport.modbus.AsyncModbusFailure;
+import org.openhab.io.transport.modbus.ModbusCommunicationInterface;
+import org.openhab.io.transport.modbus.ModbusReadFunctionCode;
+import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint;
+import org.openhab.io.transport.modbus.ModbusRegister;
+import org.openhab.io.transport.modbus.ModbusRegisterArray;
+import org.openhab.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
+import org.openhab.io.transport.modbus.ModbusWriteRequestBlueprint;
+import org.openhab.io.transport.modbus.PollTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link Modbus.StiebelEltronHandler} is responsible for handling commands,
+ * which are sent to one of the channels and for polling the modbus.
+ *
+ * @author Paul Frank - Initial contribution
+ */
+@NonNullByDefault
+public class StiebelEltronHandler extends BaseThingHandler {
+
+ public abstract class AbstractBasePoller {
+
+ private final Logger logger = LoggerFactory.getLogger(StiebelEltronHandler.class);
+
+ private volatile @Nullable PollTask pollTask;
+
+ public synchronized void unregisterPollTask() {
+ PollTask task = pollTask;
+ if (task == null) {
+ return;
+ }
+
+ ModbusCommunicationInterface mycomms = StiebelEltronHandler.this.comms;
+ if (mycomms != null) {
+ mycomms.unregisterRegularPoll(task);
+ }
+ pollTask = null;
+ }
+
+ /**
+ * Register poll task This is where we set up our regular poller
+ */
+ public synchronized void registerPollTask(int address, int length, ModbusReadFunctionCode readFunctionCode) {
+
+ logger.debug("Setting up regular polling");
+
+ ModbusCommunicationInterface mycomms = StiebelEltronHandler.this.comms;
+ StiebelEltronConfiguration myconfig = StiebelEltronHandler.this.config;
+ if (myconfig == null || mycomms == null) {
+ throw new IllegalStateException("registerPollTask called without proper configuration");
+ }
+
+ ModbusReadRequestBlueprint request = new ModbusReadRequestBlueprint(getSlaveId(),
+ readFunctionCode, address, length, myconfig.getMaxTries());
+
+
+ long refreshMillis = myconfig.getRefreshMillis();
+
+ pollTask = mycomms.registerRegularPoll(request, refreshMillis, 1000, result -> {
+ result.getRegisters().ifPresent(this::handlePolledData);
+ if (getThing().getStatus() != ThingStatus.ONLINE) {
+ updateStatus(ThingStatus.ONLINE);
+ }
+ }, StiebelEltronHandler.this::handleReadError);
+ }
+
+ public synchronized void poll() {
+ PollTask task = pollTask;
+ ModbusCommunicationInterface mycomms = StiebelEltronHandler.this.comms;
+ if (task != null && mycomms != null) {
+ mycomms.submitOneTimePoll(task.getRequest(), task.getResultCallback(), task.getFailureCallback());
+ }
+ }
+
+ protected abstract void handlePolledData(ModbusRegisterArray registers);
+ }
+
+ /**
+ * Logger instance
+ */
+ private final Logger logger = LoggerFactory.getLogger(StiebelEltronHandler.class);
+
+ /**
+ * Configuration instance
+ */
+ protected @Nullable StiebelEltronConfiguration config = null;
+ /**
+ * Parser used to convert incoming raw messages into system blocks
+ */
+ private final SystemInfromationBlockParser systemInformationBlockParser = new SystemInfromationBlockParser();
+ /**
+ * Parser used to convert incoming raw messages into system state blocks
+ */
+ private final SystemStateBlockParser systemstateBlockParser = new SystemStateBlockParser();
+ /**
+ * Parser used to convert incoming raw messages into system parameter blocks
+ */
+ private final SystemParameterBlockParser systemParameterBlockParser = new SystemParameterBlockParser();
+ /**
+ * Parser used to convert incoming raw messages into model blocks
+ */
+ private final EnergyBlockParser energyBlockParser = new EnergyBlockParser();
+ /**
+ * This is the task used to poll the device
+ */
+ private volatile @Nullable AbstractBasePoller systemInformationPoller = null;
+ /**
+ * This is the task used to poll the device
+ */
+ private volatile @Nullable AbstractBasePoller energyPoller = null;
+ /**
+ * This is the task used to poll the device
+ */
+ private volatile @Nullable AbstractBasePoller systemStatePoller = null;
+ /**
+ * This is the task used to poll the device
+ */
+ private volatile @Nullable AbstractBasePoller systemParameterPoller = null;
+ /**
+ * Communication interface to the slave endpoint we're connecting to
+ */
+ protected volatile @Nullable ModbusCommunicationInterface comms = null;
+
+ /**
+ * This is the slave id, we store this once initialization is complete
+ */
+ private volatile int slaveId;
+
+ /**
+ * Instances of this handler should get a reference to the modbus manager
+ *
+ * @param thing the thing to handle
+ * @param modbusManager the modbus manager
+ */
+ public StiebelEltronHandler(Thing thing) {
+ super(thing);
+ }
+
+ /**
+ * @param address address of the value to be written on the modbus
+ * @param shortValue value to be written on the modbus
+ */
+ protected void writeInt16(int address, short shortValue) {
+ StiebelEltronConfiguration myconfig = StiebelEltronHandler.this.config;
+ ModbusCommunicationInterface mycomms = StiebelEltronHandler.this.comms;
+
+ if (myconfig == null || mycomms == null) {
+ throw new IllegalStateException("registerPollTask called without proper configuration");
+ }
+ // big endian byte ordering
+ byte b1 = (byte) (shortValue >> 8);
+ byte b2 = (byte) shortValue;
+
+ ModbusRegister register = new ModbusRegister(b1, b2);
+ ModbusRegisterArray data = new ModbusRegisterArray(new ModbusRegister[] { register });
+
+ ModbusWriteRegisterRequestBlueprint request = new ModbusWriteRegisterRequestBlueprint(slaveId,
+ address, data, false, myconfig.getMaxTries());
+
+ mycomms.submitOneTimeWrite(request, result -> {
+ if (hasConfigurationError()) {
+ return;
+ }
+ logger.debug("Successful write, matching request {}", request);
+ StiebelEltronHandler.this.updateStatus(ThingStatus.ONLINE);
+ }, failure -> {StiebelEltronHandler.this.handleWriteError(failure);});
+ }
+
+ /**
+ * @param command get the value of this command.
+ * @return short the value of the command multiplied by 10 (see datatype 2 in
+ * the stiebel eltron modbus documentation)
+ */
+ private short getScaledInt16Value(Command command) throws StiebelEltronException {
+ if (command instanceof QuantityType) {
+ QuantityType> c = ((QuantityType>) command).toUnit(CELSIUS);
+ if (c != null) {
+ return (short) (c.doubleValue() * 10);
+ } else {
+ throw new StiebelEltronException("Unsupported unit");
+ }
+ }
+ if (command instanceof DecimalType) {
+ DecimalType c = (DecimalType) command;
+ return (short) (c.doubleValue() * 10);
+ }
+ throw new StiebelEltronException("Unsupported command type");
+ }
+
+ /**
+ * @param command get the value of this command.
+ * @return short the value of the command as short
+ */
+ private short getInt16Value(Command command) throws StiebelEltronException {
+ if (command instanceof DecimalType) {
+ DecimalType c = (DecimalType) command;
+ return c.shortValue();
+ }
+ throw new StiebelEltronException("Unsupported command type");
+ }
+
+ /**
+ * Handle incoming commands.
+ */
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (RefreshType.REFRESH == command) {
+ String groupId = channelUID.getGroupId();
+ if (groupId != null) {
+ AbstractBasePoller poller;
+ switch (groupId) {
+ case GROUP_SYSTEM_STATE:
+ poller = systemStatePoller;
+ break;
+ case GROUP_SYSTEM_PARAMETER:
+ poller = systemParameterPoller;
+ break;
+ case GROUP_SYSTEM_INFO:
+ poller = systemInformationPoller;
+ break;
+ case GROUP_ENERGY_INFO:
+ poller = energyPoller;
+ break;
+ default:
+ poller = null;
+ break;
+ }
+ if (poller != null) {
+ poller.poll();
+ }
+ }
+ } else {
+ try {
+ if (GROUP_SYSTEM_PARAMETER.equals(channelUID.getGroupId())) {
+ switch (channelUID.getIdWithoutGroup()) {
+ case CHANNEL_OPERATION_MODE:
+ writeInt16(1500, getInt16Value(command));
+ break;
+ case CHANNEL_COMFORT_TEMPERATURE_HEATING:
+ writeInt16(1501, getScaledInt16Value(command));
+ break;
+ case CHANNEL_ECO_TEMPERATURE_HEATING:
+ writeInt16(1502, getScaledInt16Value(command));
+ break;
+ case CHANNEL_COMFORT_TEMPERATURE_WATER:
+ writeInt16(1509, getScaledInt16Value(command));
+ break;
+ case CHANNEL_ECO_TEMPERATURE_WATER:
+ writeInt16(1510, getScaledInt16Value(command));
+ break;
+ }
+ }
+ } catch (StiebelEltronException error) {
+ if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
+ return;
+ }
+ String cls = error.getClass().getName();
+ String msg = error.getMessage();
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ String.format("Error with: %s: %s", cls, msg));
+ }
+ }
+ }
+
+ /**
+ * Initialization: Load the config object of the block Connect to the slave
+ * bridge Start the periodic polling
+ */
+ @Override
+ public void initialize() {
+ config = getConfigAs(StiebelEltronConfiguration.class);
+ logger.debug("Initializing thing with properties: {}", thing.getProperties());
+
+ startUp();
+ }
+
+ /*
+ * This method starts the operation of this handler Connect to the slave bridge
+ * Start the periodic polling1
+ */
+ private void startUp() {
+
+ if (comms != null) {
+ return;
+ }
+
+ ModbusEndpointThingHandler slaveEndpointThingHandler = getEndpointThingHandler();
+ if (slaveEndpointThingHandler == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Bridge is offline");
+ return;
+ }
+
+ try {
+ slaveId = slaveEndpointThingHandler.getSlaveId();
+
+ comms = slaveEndpointThingHandler.getCommunicationInterface();
+ } catch (EndpointNotInitializedException e) {
+ // this will be handled below as endpoint remains null
+ }
+
+ if (comms == null) {
+ @SuppressWarnings("null")
+ String label = Optional.ofNullable(getBridge()).map(b -> b.getLabel()).orElse("");
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
+ String.format("Bridge '%s' not completely initialized", label));
+ return;
+ }
+
+ if (config == null) {
+ logger.debug("Invalid comms/config/manager ref for stiebel eltron handler");
+ return;
+ }
+
+ if (systemInformationPoller == null) {
+ AbstractBasePoller poller = new AbstractBasePoller() {
+ @Override
+ protected void handlePolledData(ModbusRegisterArray registers) {
+ handlePolledSystemInformationData(registers);
+ }
+ };
+ poller.registerPollTask(500, 36, ModbusReadFunctionCode.READ_INPUT_REGISTERS);
+ systemInformationPoller = poller;
+ }
+ if (energyPoller == null) {
+ AbstractBasePoller poller = new AbstractBasePoller() {
+ @Override
+ protected void handlePolledData(ModbusRegisterArray registers) {
+ handlePolledEnergyData(registers);
+ }
+ };
+ poller.registerPollTask(3500, 16, ModbusReadFunctionCode.READ_INPUT_REGISTERS);
+ energyPoller = poller;
+ }
+ if (systemStatePoller == null) {
+ AbstractBasePoller poller = new AbstractBasePoller() {
+ @Override
+ protected void handlePolledData(ModbusRegisterArray registers) {
+ handlePolledSystemStateData(registers);
+ }
+ };
+ poller.registerPollTask(2500, 2, ModbusReadFunctionCode.READ_INPUT_REGISTERS);
+ systemStatePoller = poller;
+ }
+ if (systemParameterPoller == null) {
+ AbstractBasePoller poller = new AbstractBasePoller() {
+ @Override
+ protected void handlePolledData(ModbusRegisterArray registers) {
+ handlePolledSystemParameterData(registers);
+ }
+ };
+ poller.registerPollTask(1500, 11, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
+ systemParameterPoller = poller;
+ }
+ updateStatus(ThingStatus.UNKNOWN);
+ }
+
+ /**
+ * Dispose the binding correctly
+ */
+ @Override
+ public void dispose() {
+ tearDown();
+ }
+
+ /**
+ * Unregister the poll tasks and release the endpoint reference
+ */
+ private void tearDown() {
+ AbstractBasePoller poller = systemInformationPoller;
+ if (poller != null) {
+ logger.debug("Unregistering systemInformationPoller from ModbusManager");
+ poller.unregisterPollTask();
+
+ systemInformationPoller = null;
+ }
+
+ poller = energyPoller;
+ if (poller != null) {
+ logger.debug("Unregistering energyPoller from ModbusManager");
+ poller.unregisterPollTask();
+
+ energyPoller = null;
+ }
+
+ poller = systemStatePoller;
+ if (poller != null) {
+ logger.debug("Unregistering systemStatePoller from ModbusManager");
+ poller.unregisterPollTask();
+
+ systemStatePoller = null;
+ }
+
+ poller = systemParameterPoller;
+ if (poller != null) {
+ logger.debug("Unregistering systemParameterPoller from ModbusManager");
+ poller.unregisterPollTask();
+
+ systemParameterPoller = null;
+ }
+
+ comms = null;
+ }
+
+ /**
+ * Returns the current slave id from the bridge
+ */
+ public int getSlaveId() {
+ return slaveId;
+ }
+
+ /**
+ * Get the endpoint handler from the bridge this handler is connected to Checks
+ * that we're connected to the right type of bridge
+ *
+ * @return the endpoint handler or null if the bridge does not exist
+ */
+ private @Nullable ModbusEndpointThingHandler getEndpointThingHandler() {
+ Bridge bridge = getBridge();
+ if (bridge == null) {
+ logger.debug("Bridge is null");
+ return null;
+ }
+ if (bridge.getStatus() != ThingStatus.ONLINE) {
+ logger.debug("Bridge is not online");
+ return null;
+ }
+
+ ThingHandler handler = bridge.getHandler();
+ if (handler == null) {
+ logger.debug("Bridge handler is null");
+ return null;
+ }
+
+ if (handler instanceof ModbusEndpointThingHandler) {
+ ModbusEndpointThingHandler slaveEndpoint = (ModbusEndpointThingHandler) handler;
+ return slaveEndpoint;
+ } else {
+ throw new IllegalStateException("Unexpected bridge handler: " + handler.toString());
+ }
+ }
+
+ /**
+ * Returns value divided by the 10
+ *
+ * @param value the value to alter
+ * @return the scaled value as a DecimalType
+ */
+ protected State getScaled(Number value, Unit> unit) {
+ return QuantityType.valueOf(value.doubleValue() / 10, unit);
+ }
+
+ /**
+ * Returns high value * 1000 + low value
+ *
+ * @param high the high value
+ * @param low the low valze
+ * @return the scaled value as a DecimalType
+ */
+ protected State getEnergyQuantity(int high, int low) {
+ double value = high * 1000 + low;
+ return QuantityType.valueOf(value, KILOWATT_HOUR);
+ }
+
+ /**
+ * This method is called each time new data has been polled from the modbus
+ * slave The register array is first parsed, then each of the channels are
+ * updated to the new values
+ *
+ * @param registers byte array read from the modbus slave
+ */
+ protected void handlePolledSystemInformationData(ModbusRegisterArray registers) {
+ logger.trace("System Information block received, size: {}", registers.size());
+
+ SystemInformationBlock block = systemInformationBlockParser.parse(registers);
+
+ // System information group
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_FEK_TEMPERATURE), getScaled(block.temperatureFek, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_FEK_TEMPERATURE_SETPOINT),
+ getScaled(block.temperatureFekSetPoint, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_FEK_HUMIDITY), getScaled(block.humidityFek, PERCENT));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_FEK_DEWPOINT), getScaled(block.dewpointFek, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_OUTDOOR_TEMPERATURE),
+ getScaled(block.temperatureOutdoor, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_HK1_TEMPERATURE), getScaled(block.temperatureHk1, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_HK1_TEMPERATURE_SETPOINT),
+ getScaled(block.temperatureHk1SetPoint, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_SUPPLY_TEMPERATURE),
+ getScaled(block.temperatureSupply, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_RETURN_TEMPERATURE),
+ getScaled(block.temperatureReturn, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_SOURCE_TEMPERATURE),
+ getScaled(block.temperatureSource, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_WATER_TEMPERATURE),
+ getScaled(block.temperatureWater, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_WATER_TEMPERATURE_SETPOINT),
+ getScaled(block.temperatureWaterSetPoint, CELSIUS));
+
+ resetCommunicationError();
+ }
+
+ /**
+ * This method is called each time new data has been polled from the modbus
+ * slave The register array is first parsed, then each of the channels are
+ * updated to the new values
+ *
+ * @param registers byte array read from the modbus slave
+ */
+ protected void handlePolledEnergyData(ModbusRegisterArray registers) {
+ logger.trace("Energy block received, size: {}", registers.size());
+
+ EnergyBlock block = energyBlockParser.parse(registers);
+
+ // Energy information group
+ updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_PRODUCTION_HEAT_TODAY),
+ new QuantityType<>(block.productionHeatToday, KILOWATT_HOUR));
+ updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_PRODUCTION_HEAT_TOTAL),
+ getEnergyQuantity(block.productionHeatTotalHigh, block.productionHeatTotalLow));
+ updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_PRODUCTION_WATER_TODAY),
+ new QuantityType<>(block.productionWaterToday, KILOWATT_HOUR));
+ updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_PRODUCTION_WATER_TOTAL),
+ getEnergyQuantity(block.productionWaterTotalHigh, block.productionWaterTotalLow));
+ updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_CONSUMPTION_HEAT_TODAY),
+ new QuantityType<>(block.consumptionHeatToday, KILOWATT_HOUR));
+ updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_CONSUMPTION_HEAT_TOTAL),
+ getEnergyQuantity(block.consumptionHeatTotalHigh, block.consumptionHeatTotalLow));
+ updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_CONSUMPTION_WATER_TODAY),
+ new QuantityType<>(block.consumptionWaterToday, KILOWATT_HOUR));
+ updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_CONSUMPTION_WATER_TOTAL),
+ getEnergyQuantity(block.consumptionWaterTotalHigh, block.consumptionWaterTotalLow));
+
+ resetCommunicationError();
+ }
+
+ /**
+ * This method is called each time new data has been polled from the modbus
+ * slave The register array is first parsed, then each of the channels are
+ * updated to the new values
+ *
+ * @param registers byte array read from the modbus slave
+ */
+ protected void handlePolledSystemStateData(ModbusRegisterArray registers) {
+ logger.trace("System state block received, size: {}", registers.size());
+
+ SystemStateBlock block = systemstateBlockParser.parse(registers);
+ boolean isHeating = (block.state & 16) != 0;
+ updateState(channelUID(GROUP_SYSTEM_STATE, CHANNEL_IS_HEATING),
+ isHeating ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
+ updateState(channelUID(GROUP_SYSTEM_STATE, CHANNEL_IS_HEATING_WATER),
+ (block.state & 32) != 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
+ updateState(channelUID(GROUP_SYSTEM_STATE, CHANNEL_IS_COOLING),
+ (block.state & 256) != 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
+ updateState(channelUID(GROUP_SYSTEM_STATE, CHANNEL_IS_SUMMER),
+ (block.state & 128) != 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
+ updateState(channelUID(GROUP_SYSTEM_STATE, CHANNEL_IS_PUMPING),
+ (block.state & 1) != 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
+
+ resetCommunicationError();
+ }
+
+ /**
+ * This method is called each time new data has been polled from the modbus
+ * slave The register array is first parsed, then each of the channels are
+ * updated to the new values
+ *
+ * @param registers byte array read from the modbus slave
+ */
+ protected void handlePolledSystemParameterData(ModbusRegisterArray registers) {
+ logger.trace("System state block received, size: {}", registers.size());
+
+ SystemParameterBlock block = systemParameterBlockParser.parse(registers);
+ updateState(channelUID(GROUP_SYSTEM_PARAMETER, CHANNEL_OPERATION_MODE), new DecimalType(block.operationMode));
+
+ updateState(channelUID(GROUP_SYSTEM_PARAMETER, CHANNEL_COMFORT_TEMPERATURE_HEATING),
+ getScaled(block.comfortTemperatureHeating, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_PARAMETER, CHANNEL_ECO_TEMPERATURE_HEATING),
+ getScaled(block.ecoTemperatureHeating, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_PARAMETER, CHANNEL_COMFORT_TEMPERATURE_WATER),
+ getScaled(block.comfortTemperatureWater, CELSIUS));
+ updateState(channelUID(GROUP_SYSTEM_PARAMETER, CHANNEL_ECO_TEMPERATURE_WATER),
+ getScaled(block.ecoTemperatureWater, CELSIUS));
+
+ resetCommunicationError();
+ }
+
+ /**
+ * @param bridgeStatusInfo
+ */
+ @Override
+ public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
+ super.bridgeStatusChanged(bridgeStatusInfo);
+
+ if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
+ startUp();
+ } else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
+ tearDown();
+ }
+ }
+
+ /**
+ * Handle errors received during communication
+ */
+ protected void handleReadError(AsyncModbusFailure failure) {
+ // Ignore all incoming data and errors if configuration is not correct
+ if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
+ return;
+ }
+ String msg = failure.getCause().getMessage();
+ String cls = failure.getCause().getClass().getName();
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ String.format("Error with read: %s: %s", cls, msg));
+ }
+
+ /**
+ * Handle errors received during communication
+ */
+ protected void handleWriteError(AsyncModbusFailure failure) {
+ // Ignore all incoming data and errors if configuration is not correct
+ if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
+ return;
+ }
+ String msg = failure.getCause().getMessage();
+ String cls = failure.getCause().getClass().getName();
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ String.format("Error with write: %s: %s", cls, msg));
+ }
+
+
+ /**
+ * Returns true, if we're in a CONFIGURATION_ERROR state
+ *
+ * @return
+ */
+ protected boolean hasConfigurationError() {
+ ThingStatusInfo statusInfo = getThing().getStatusInfo();
+ return statusInfo.getStatus() == ThingStatus.OFFLINE
+ && statusInfo.getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR;
+ }
+
+ /**
+ * Reset communication status to ONLINE if we're in an OFFLINE state
+ */
+ protected void resetCommunicationError() {
+ ThingStatusInfo statusInfo = thing.getStatusInfo();
+ if (ThingStatus.OFFLINE.equals(statusInfo.getStatus())
+ && ThingStatusDetail.COMMUNICATION_ERROR.equals(statusInfo.getStatusDetail())) {
+ updateStatus(ThingStatus.ONLINE);
+ }
+ }
+
+ /**
+ * Returns the channel UID for the specified group and channel id
+ *
+ * @param string the channel group
+ * @param string the channel id in that group
+ * @return the globally unique channel uid
+ */
+ ChannelUID channelUID(String group, String id) {
+ return new ChannelUID(getThing().getUID(), group, id);
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/AbstractBaseParser.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/AbstractBaseParser.java
new file mode 100644
index 0000000000000..1b1319a681e60
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/AbstractBaseParser.java
@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.parser;
+
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.library.types.DecimalType;
+import org.openhab.io.transport.modbus.ModbusBitUtilities;
+import org.openhab.io.transport.modbus.ModbusConstants.ValueType;
+import org.openhab.io.transport.modbus.ModbusRegisterArray;
+
+/**
+ * Base class for parsers with some helper methods
+ *
+ * @author Nagy Attila Gabor - Initial contribution
+ * @author Paul Frank - Added more methods
+ */
+@NonNullByDefault
+public class AbstractBaseParser {
+
+ /**
+ * Extract an optional double value
+ *
+ * @param raw the register array to extract from
+ * @param index the address of the field
+ * @return the parsed value or empty if the field is not implemented
+ */
+ protected Optional extractOptionalDouble(ModbusRegisterArray raw, int index) {
+ return ModbusBitUtilities.extractStateFromRegisters(raw, index, ValueType.INT16)
+ .map(value -> ((double) value.intValue()) / 10.0).filter(value -> value != (short) 0x8000);
+ }
+
+ /**
+ * Extract a mandatory double value
+ *
+ * @param raw the register array to extract from
+ * @param index the address of the field
+ * @param def the default value
+ * @return the parsed value or the default if the field is not implemented
+ */
+ protected Double extractDouble(ModbusRegisterArray raw, int index, double def) {
+ return extractOptionalDouble(raw, index).orElse(def);
+ }
+
+ /**
+ * Extract an optional int16 value
+ *
+ * @param raw the register array to extract from
+ * @param index the address of the field
+ * @return the parsed value or empty if the field is not implemented
+ */
+ protected Optional extractOptionalInt16(ModbusRegisterArray raw, int index) {
+ return ModbusBitUtilities.extractStateFromRegisters(raw, index, ValueType.INT16).map(DecimalType::shortValue)
+ .filter(value -> value != (short) 0x8000);
+ }
+
+ /**
+ * Extract a mandatory int16 value
+ *
+ * @param raw the register array to extract from
+ * @param index the address of the field
+ * @param def the default value
+ * @return the parsed value or the default if the field is not implemented
+ */
+ protected Short extractInt16(ModbusRegisterArray raw, int index, short def) {
+ return extractOptionalInt16(raw, index).orElse(def);
+ }
+
+ /**
+ * Extract an optional uint16 value
+ *
+ * @param raw the register array to extract from
+ * @param index the address of the field
+ * @return the parsed value or empty if the field is not implemented
+ */
+ protected Optional extractOptionalUInt16(ModbusRegisterArray raw, int index) {
+ return ModbusBitUtilities.extractStateFromRegisters(raw, index, ValueType.UINT16).map(DecimalType::intValue)
+ .filter(value -> value != 0xffff);
+ }
+
+ /**
+ * Extract a mandatory uint16 value
+ *
+ * @param raw the register array to extract from
+ * @param index the address of the field
+ * @param def the default value
+ * @return the parsed value or the default if the field is not implemented
+ */
+ protected Integer extractUInt16(ModbusRegisterArray raw, int index, int def) {
+ return extractOptionalUInt16(raw, index).orElse(def);
+ }
+
+ /**
+ * Extract an optional acc32 value
+ *
+ * @param raw the register array to extract from
+ * @param index the address of the field
+ * @return the parsed value or empty if the field is not implemented
+ */
+ protected Optional extractOptionalUInt32(ModbusRegisterArray raw, int index) {
+ return ModbusBitUtilities.extractStateFromRegisters(raw, index, ValueType.UINT32).map(DecimalType::longValue)
+ .filter(value -> value != 0);
+ }
+
+ /**
+ * Extract a mandatory acc32 value
+ *
+ * @param raw the register array to extract from
+ * @param index the address of the field
+ * @param def the default value
+ * @return the parsed value or default if the field is not implemented
+ */
+ protected Long extractUnit32(ModbusRegisterArray raw, int index, long def) {
+ return extractOptionalUInt32(raw, index).orElse(def);
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/EnergyBlockParser.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/EnergyBlockParser.java
new file mode 100644
index 0000000000000..f31028cb79da4
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/EnergyBlockParser.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.parser;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.modbus.stiebeleltron.internal.dto.EnergyBlock;
+import org.openhab.io.transport.modbus.ModbusRegisterArray;
+
+/**
+ * Parses inverter modbus data into an Energy Block
+ *
+ * @author Paul Frank - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class EnergyBlockParser extends AbstractBaseParser {
+
+ public EnergyBlock parse(ModbusRegisterArray raw) {
+ EnergyBlock block = new EnergyBlock();
+
+ block.productionHeatToday = extractUInt16(raw, 0, (short) 0);
+ block.productionHeatTotalLow = extractUInt16(raw, 1, (short) 0);
+ block.productionHeatTotalHigh = extractUInt16(raw, 2, (short) 0);
+ block.productionWaterToday = extractUInt16(raw, 3, (short) 0);
+ block.productionWaterTotalLow = extractUInt16(raw, 4, (short) 0);
+ block.productionWaterTotalHigh = extractUInt16(raw, 5, (short) 0);
+
+ block.consumptionHeatToday = extractUInt16(raw, 10, (short) 0);
+ block.consumptionHeatTotalLow = extractUInt16(raw, 11, (short) 0);
+ block.consumptionHeatTotalHigh = extractUInt16(raw, 12, (short) 0);
+ block.consumptionWaterToday = extractUInt16(raw, 13, (short) 0);
+ block.consumptionWaterTotalLow = extractUInt16(raw, 14, (short) 0);
+ block.consumptionWaterTotalHigh = extractUInt16(raw, 15, (short) 0);
+ return block;
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/SystemInfromationBlockParser.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/SystemInfromationBlockParser.java
new file mode 100644
index 0000000000000..71b81b70fa0da
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/SystemInfromationBlockParser.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.parser;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.modbus.stiebeleltron.internal.dto.SystemInformationBlock;
+import org.openhab.io.transport.modbus.ModbusRegisterArray;
+
+/**
+ * Parses inverter modbus data into an SystemB Information lock
+ *
+ * @author Paul Frank - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class SystemInfromationBlockParser extends AbstractBaseParser {
+
+ public SystemInformationBlock parse(ModbusRegisterArray raw) {
+ SystemInformationBlock block = new SystemInformationBlock();
+
+ block.temperatureFek = extractInt16(raw, 2, (short) 0);
+ block.temperatureFekSetPoint = extractInt16(raw, 3, (short) 0);
+ block.humidityFek = extractInt16(raw, 4, (short) 0);
+ block.dewpointFek = extractInt16(raw, 5, (short) 0);
+ block.temperatureOutdoor = extractInt16(raw, 6, (short) 0);
+ block.temperatureHk1 = extractInt16(raw, 7, (short) 0);
+ block.temperatureHk1SetPoint = extractInt16(raw, 9, (short) 0);
+ block.temperatureSupply = extractInt16(raw, 12, (short) 0);
+ block.temperatureReturn = extractInt16(raw, 15, (short) 0);
+ block.temperatureWater = extractInt16(raw, 21, (short) 0);
+ block.temperatureWaterSetPoint = extractInt16(raw, 22, (short) 0);
+ block.temperatureSource = extractInt16(raw, 35, (short) 0);
+ return block;
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/SystemParameterBlockParser.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/SystemParameterBlockParser.java
new file mode 100644
index 0000000000000..f1bd72f7c4321
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/SystemParameterBlockParser.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.parser;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.modbus.stiebeleltron.internal.dto.SystemParameterBlock;
+import org.openhab.io.transport.modbus.ModbusRegisterArray;
+
+/**
+ * Parses inverter modbus data into an System Parameter Block
+ *
+ * @author Paul Frank - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class SystemParameterBlockParser extends AbstractBaseParser {
+
+ public SystemParameterBlock parse(ModbusRegisterArray raw) {
+ SystemParameterBlock block = new SystemParameterBlock();
+
+ block.operationMode = extractUInt16(raw, 0, 0);
+ block.comfortTemperatureHeating = extractInt16(raw, 1, (short) 0);
+ block.ecoTemperatureHeating = extractInt16(raw, 2, (short) 0);
+ block.comfortTemperatureWater = extractInt16(raw, 9, (short) 0);
+ block.ecoTemperatureWater = extractInt16(raw, 10, (short) 0);
+ return block;
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/SystemStateBlockParser.java b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/SystemStateBlockParser.java
new file mode 100644
index 0000000000000..a49d8388a5aa5
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/java/org/openhab/binding/modbus/stiebeleltron/internal/parser/SystemStateBlockParser.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.modbus.stiebeleltron.internal.parser;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.modbus.stiebeleltron.internal.dto.SystemStateBlock;
+import org.openhab.io.transport.modbus.ModbusRegisterArray;
+
+/**
+ * Parses inverter modbus data into an System State Block
+ *
+ * @author Paul Frank - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class SystemStateBlockParser extends AbstractBaseParser {
+
+ public SystemStateBlock parse(ModbusRegisterArray raw) {
+ SystemStateBlock block = new SystemStateBlock();
+
+ block.state = extractUInt16(raw, 0, 0);
+ return block;
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/config/config-descriptions.xml b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/config/config-descriptions.xml
new file mode 100644
index 0000000000000..d88938a7c4f46
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/config/config-descriptions.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+ Poll interval in seconds. Use zero to disable automatic polling.
+ 5
+ s
+
+
+
+
+ 3
+ Number of tries when reading data, if some of the reading fail. For single try, enter 1.
+ true
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/thing/heatpump-channel-groups.xml b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/thing/heatpump-channel-groups.xml
new file mode 100644
index 0000000000000..f35627a7cfb00
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/thing/heatpump-channel-groups.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/thing/heatpump-channel-types.xml b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/thing/heatpump-channel-types.xml
new file mode 100644
index 0000000000000..b409f22da06c8
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/thing/heatpump-channel-types.xml
@@ -0,0 +1,171 @@
+
+
+
+
+ Number:Temperature
+
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Dimensionless
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Temperature
+
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Temperature
+
+
+
+
+
+
+ Number:Energy
+
+
+
+
+ Number:Energy
+
+
+
+
+ Number:Energy
+
+
+
+
+ Number:Energy
+
+
+
+
+
+ Number:Energy
+
+
+
+
+ Number:Energy
+
+
+
+
+ Number:Energy
+
+
+
+
+ Number:Energy
+
+
+
+
+ Contact
+
+
+
+
+ Contact
+
+
+
+
+ Contact
+
+
+
+
+ Contact
+
+
+
+
+ Contact
+
+
+
+
+ Number
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Temperature
+
+
+
+
+ Number:Temperature
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/thing/heatpump-types.xml b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/thing/heatpump-types.xml
new file mode 100644
index 0000000000000..deca01b55d0f8
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.stiebeleltron/src/main/resources/ESH-INF/thing/heatpump-types.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+ Stiebel Eltron ISG connected through modbus
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index d302827819432..b81fc302fa8f2 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -162,6 +162,7 @@
org.openhab.binding.minecraft
org.openhab.binding.modbus
org.openhab.binding.modbus.sunspec
+ org.openhab.binding.modbus.stiebeleltron
org.openhab.binding.mpd
org.openhab.binding.mqtt
org.openhab.binding.mqtt.generic