diff --git a/CODEOWNERS b/CODEOWNERS
index 633bb100fabbd..66aac3bf1fb14 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -129,6 +129,7 @@
/bundles/org.openhab.binding.minecraft/ @ibaton
/bundles/org.openhab.binding.modbus/ @ssalonen
/bundles/org.openhab.binding.modbus.e3dc/ @weymann
+/bundles/org.openhab.binding.modbus.studer/ @giovannimirulla
/bundles/org.openhab.binding.modbus.sunspec/ @mrbig
/bundles/org.openhab.binding.modbus.stiebeleltron/ @pail23
/bundles/org.openhab.binding.monopriceaudio/ @mlobstein
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index 9854dc430c463..cbf7f42233686 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -1046,6 +1046,11 @@
org.openhab.binding.squeezebox
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.modbus.studer
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.modbus.sunspec
diff --git a/bundles/org.openhab.binding.modbus.studer/.classpath b/bundles/org.openhab.binding.modbus.studer/.classpath
new file mode 100644
index 0000000000000..f79ae85591f5a
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/.classpath
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.studer/.project b/bundles/org.openhab.binding.modbus.studer/.project
new file mode 100644
index 0000000000000..90e9e10eff905
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/.project
@@ -0,0 +1,23 @@
+
+
+ org.openhab.binding.modbus.studer
+
+
+
+
+
+ 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.studer/NOTICE b/bundles/org.openhab.binding.modbus.studer/NOTICE
new file mode 100644
index 0000000000000..38d625e349232
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/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.studer/README.md b/bundles/org.openhab.binding.modbus.studer/README.md
new file mode 100644
index 0000000000000..4520f35f4d066
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/README.md
@@ -0,0 +1,142 @@
+# Studer
+
+This extension adds support for the Studer protocol.
+
+Studer Innotec, founded in 1987 by Roland Studer, is an ISO certified company that develops and manufactures inverters, inverter/chargers and MPPT solar charge controllers to communicate over the Modbus protocol entirely in Switzerland
+
+For a list of certified products see this page: https://www.studer-innotec.com/
+
+## Supported Things
+
+This bundle adds the following thing type to the Modbus binding.
+Note, that the things will show up under the Modbus binding.
+
+| Thing Type IDs | Description | Picture |
+|----------------|----------------------------------------------------------------------------------------------------------------------|-------------------------------------|
+| bsp | For BSP that offer a highly precise measuring for Xtender, VarioTrack and VarioString systems |  |
+| xtender | For the Xtender models for system capacities from 0.5kVA to 72kVA that allow for the optimal use of available energy |  |
+| variotrack | For the VarioTrack models of MPPT solar charge controllers for systems with solar PV capacity from 1 - 75kWp |  |
+| variostring | For the VarioString models of MPPT solar charge controllers for systems with solar PV capacity from 4 |  |
+
+
+## Thing Configuration
+
+You need first to set up a Serial Modbus bridge according to the Modbus documentation.
+Things in this extension will use the selected bridge to connect to the device.
+
+For defining a thing textually, you have to find out the start address of the model block and the length of it.
+While the length is usually fixed, the address is not.
+Please refer to your device's vendor documentation how model blocks are laid for your equipment.
+
+The following parameters are valid for all thing types:
+
+| Parameter | Type | Required | Default if omitted | Description |
+|-----------|---------|----------|-------------------------|----------------------------------------------------------------------------|
+| address | integer | yes | `first slave of device` | Address of slave |
+| refresh | integer | yes | 5 | Poll interval in seconds. Increase this if you encounter connection errors |
+
+## Channels
+
+The following Channels, and their associated channel types are shown below divided by device.
+
+#### BSP
+
+All channels read for a BSP device
+
+| Channel | Type | Description |
+| ------------------ | ------------------------ | --------------------- |
+| power | Number:Power | Power |
+| batteryVoltage | Number:ElectricPotential | Battery voltage |
+| batteryCurrent | Number:ElectricCurrent | Battery current |
+| stateOfCharge | Number:Dimensionless | State of Charge |
+| batteryTemperature | Number:Temperature | Battery temperature |
+
+#### Xtender
+
+All channels read for a Xtender device
+
+| Channel | Type | Description |
+| ----------------- | ------------------------ | ----------------------- |
+| inputVoltage | Number:ElectricPotential | Input voltage |
+| inputCurrent | Number:ElectricCurrent | Input current |
+| inputActivePower | Number:Power | Input active power |
+| inputFrequency | Number:Frequency | Input frequency |
+| outputVoltage | Number:ElectricPotential | Output voltage |
+| outputCurrent | Number:ElectricCurrent | Output current |
+| outputActivePower | Number:Power | Output active power |
+| outputFrequency | Number:Frequency | Output frequency |
+| operatingState | String | Operating state |
+| stateInverter | String | State of the inverter |
+
+#### VarioTrack
+
+All channels read for a VarioTrack device
+
+| Channel | Type | Description |
+| -------------------- | ------------------------ | ----------------------------------------- |
+| modelVarioTrack | String | Model of VarioTrack |
+| voltagePVGenerator | Number:ElectricPotential | Voltage of the PV generator |
+| powerPVGenerator | Number:Power | Power of the PV generator |
+| productionCurrentDay | Number:Energy | Production in (kWh) for the current day |
+| batteryVoltage | Number:ElectricPotential | Battery voltage |
+| batteryCurrent | Number:ElectricCurrent | Battery current |
+| operatingMode | String | Operating mode |
+| stateVarioTrack | String | State of the VarioTrack |
+
+#### VarioString
+
+All channels read for a VarioString device
+
+| Channel | Type | Description |
+| ----------------------- | ------------------------ | --------------------------------------------- |
+| PVVoltage | Number:ElectricPotential | PV voltage |
+| PVCurrent | Number:ElectricCurrent | PV current |
+| PVPower | Number:Power | PV power |
+| ProductionPVCurrentDay | Number:Energy | Production PV in (kWh) for the current day |
+| PV1Voltage | Number:ElectricPotential | PV1 voltage |
+| PV1Current | Number:ElectricCurrent | PV1 current |
+| PV1Power | Number:Power | PV1 power |
+| ProductionPV1CurrentDay | Number:Energy | Production PV1 in (kWh) for the current day |
+| PV2Voltage | Number:ElectricPotential | PV2 voltage |
+| PV2Current | Number:ElectricCurrent | PV2 current |
+| PV2Power | Number:Power | PV2 power |
+| ProductionPV2CurrentDay | Number:Energy | Production PV2 in (kWh) for the current day |
+| batteryVoltage | Number:ElectricPotential | Battery voltage |
+| batteryCurrent | Number:ElectricCurrent | Battery current |
+| PVMode | String | PV operating mode |
+| PV1Mode | String | PV1 operating mode |
+| PV2Mode | String | PV2 operating mode |
+| stateVarioString | String | State of the VarioString |
+
+## Example
+
+### Thing Configuration
+
+```
+Bridge modbus:serial:bridge [port="/dev/ttyUSB0",baud=9600,dataBits=8,parity="even",stopBits="1.0",encoding="rtu"]
+Thing modbus:xtender:bridge:xtenderdevice "Xtender" (modbus:serial:modbusbridge) [ slaveAddress=10, refresh=5 ]
+```
+
+Note: Make sure that refresh and slave address are numerical, without quotes.
+
+### Item Configuration
+
+```
+Number XtenderStuderThing_InputVoltage "Input Voltage [%.2f %unit%]"
+{channel="modbus:xtender:bridge:xtenderdevice:inputVoltage"}
+
+Number XtenderStuderThing_InputCurrent "Input Current [%.2f %unit%]" {channel="modbus:xtender:bridge:xtenderdevice:inputCurrent"}
+
+String XtenderStuderThing_StateInverter "State: [%s]" {channel="modbus:xtender:bridge:xtenderdevice:stateInverter"}
+```
+
+### Sitemap Configuration
+
+```
+Text item=XtenderStuderThing_InputVoltage
+Text item=XtenderStuderThing_InputCurrent
+Text item=XtenderStuderThing_StateInverter
+
+Chart item=XtenderStuderThing_InputVoltage period=D refresh=600000
+Chart item=XtenderStuderThing_InputCurrent period=D refresh=30000
+```
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.modbus.studer/doc/bsp.png b/bundles/org.openhab.binding.modbus.studer/doc/bsp.png
new file mode 100644
index 0000000000000..fdb291aa2daaa
Binary files /dev/null and b/bundles/org.openhab.binding.modbus.studer/doc/bsp.png differ
diff --git a/bundles/org.openhab.binding.modbus.studer/doc/variostring.png b/bundles/org.openhab.binding.modbus.studer/doc/variostring.png
new file mode 100644
index 0000000000000..2c0927af2949b
Binary files /dev/null and b/bundles/org.openhab.binding.modbus.studer/doc/variostring.png differ
diff --git a/bundles/org.openhab.binding.modbus.studer/doc/variotrack.png b/bundles/org.openhab.binding.modbus.studer/doc/variotrack.png
new file mode 100644
index 0000000000000..b79f8a87e9ef4
Binary files /dev/null and b/bundles/org.openhab.binding.modbus.studer/doc/variotrack.png differ
diff --git a/bundles/org.openhab.binding.modbus.studer/doc/xtender.png b/bundles/org.openhab.binding.modbus.studer/doc/xtender.png
new file mode 100644
index 0000000000000..3234918d3914b
Binary files /dev/null and b/bundles/org.openhab.binding.modbus.studer/doc/xtender.png differ
diff --git a/bundles/org.openhab.binding.modbus.studer/pom.xml b/bundles/org.openhab.binding.modbus.studer/pom.xml
new file mode 100644
index 0000000000000..0f46566058e4c
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 2.5.9-SNAPSHOT
+
+
+ org.openhab.binding.modbus.studer
+
+ openHAB Add-ons :: Bundles :: Studer Binding
+
+
+ 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.studer/src/main/feature/feature.xml b/bundles/org.openhab.binding.modbus.studer/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..c46afeefc0570
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/feature/feature.xml
@@ -0,0 +1,12 @@
+
+
+ file:${basedirRoot}/bundles/org.openhab.io.transport.modbus/target/feature/feature.xml
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ 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.studer/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderBindingConstants.java b/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderBindingConstants.java
new file mode 100644
index 0000000000000..5edc3e367a274
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderBindingConstants.java
@@ -0,0 +1,229 @@
+/**
+ * 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.studer.internal;
+
+import static org.eclipse.smarthome.core.library.unit.MetricPrefix.KILO;
+import static org.eclipse.smarthome.core.library.unit.SIUnits.CELSIUS;
+import static org.eclipse.smarthome.core.library.unit.SmartHomeUnits.*;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.measure.Unit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.openhab.binding.modbus.ModbusBindingConstants;
+
+/**
+ * The {@link StuderBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Giovanni Mirulla - Initial contribution
+ */
+@NonNullByDefault
+public class StuderBindingConstants {
+
+ private static final String BINDING_ID = ModbusBindingConstants.BINDING_ID;
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_BSP = new ThingTypeUID(BINDING_ID, "bsp");
+ public static final ThingTypeUID THING_TYPE_XTENDER = new ThingTypeUID(BINDING_ID, "xtender");
+ public static final ThingTypeUID THING_TYPE_VARIOTRACK = new ThingTypeUID(BINDING_ID, "variotrack");
+ public static final ThingTypeUID THING_TYPE_VARIOSTRING = new ThingTypeUID(BINDING_ID, "variostring");
+
+ public static final Set SUPPORTED_THING_TYPES_UIDS = new HashSet<>();
+ static {
+ SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_BSP);
+ SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_XTENDER);
+ SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_VARIOTRACK);
+ SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_VARIOSTRING);
+ }
+
+ // List of all Channel ids
+ public static final String CHANNEL_POWER = "power";
+ public static final String CHANNEL_BATTERY_VOLTAGE = "batteryVoltage";
+ public static final String CHANNEL_BATTERY_CURRENT = "batteryCurrent";
+ public static final String CHANNEL_STATE_OF_CHARGE = "stateOfCharge";
+ public static final String CHANNEL_BATTERY_TEMPERATURE = "batteryTemperature";
+
+ public static final String CHANNEL_INPUT_VOLTAGE = "inputVoltage";
+ public static final String CHANNEL_INPUT_CURRENT = "inputCurrent";
+ public static final String CHANNEL_INPUT_ACTIVE_POWER = "inputActivePower";
+ public static final String CHANNEL_INPUT_FREQUENCY = "inputFrequency";
+ public static final String CHANNEL_OUTPUT_VOLTAGE = "outputVoltage";
+ public static final String CHANNEL_OUTPUT_CURRENT = "outputCurrent";
+ public static final String CHANNEL_OUTPUT_ACTIVE_POWER = "outputActivePower";
+ public static final String CHANNEL_OUTPUT_FREQUENCY = "outputFrequency";
+ public static final String CHANNEL_OPERATING_STATE = "operatingState";
+ public static final String CHANNEL_STATE_INVERTER = "stateInverter";
+
+ public static final String CHANNEL_MODEL_VARIOTRACK = "modelVarioTrack";
+ public static final String CHANNEL_VOLTAGE_PV_GENERATOR = "voltagePVGenerator";
+ public static final String CHANNEL_POWER_PV_GENERATOR = "powerPVGenerator";
+ public static final String CHANNEL_PRODUCTION_CURRENT_DAY = "productionCurrentDay";
+ public static final String CHANNEL_OPERATING_MODE = "operatingMode";
+ public static final String CHANNEL_STATE_VARIOTRACK = "stateVarioTrack";
+
+ public static final String CHANNEL_PV_VOLTAGE = "PVVoltage";
+ public static final String CHANNEL_PV_CURRENT = "PVCurrent";
+ public static final String CHANNEL_PV_POWER = "PVPower";
+ public static final String CHANNEL_PRODUCTION_PV_CURRENT_DAY = "ProductionPVCurrentDay";
+ public static final String CHANNEL_PV_OPERATING_MODE = "PVMode";
+ public static final String CHANNEL_PV1_VOLTAGE = "PV1Voltage";
+ public static final String CHANNEL_PV1_CURRENT = "PV1Current";
+ public static final String CHANNEL_PV1_POWER = "PV1Power";
+ public static final String CHANNEL_PRODUCTION_PV1_CURRENT_DAY = "ProductionPV1CurrentDay";
+ public static final String CHANNEL_PV1_OPERATING_MODE = "PV1Mode";
+ public static final String CHANNEL_PV2_VOLTAGE = "PV2Voltage";
+ public static final String CHANNEL_PV2_CURRENT = "PV2Current";
+ public static final String CHANNEL_PV2_POWER = "PV2Power";
+ public static final String CHANNEL_PRODUCTION_PV2_CURRENT_DAY = "ProductionPV2CurrentDay";
+ public static final String CHANNEL_PV2_OPERATING_MODE = "PV2Mode";
+ public static final String CHANNEL_STATE_VARIOSTRING = "stateVarioString";
+
+ /**
+ * Map of the supported BSP channel with their registers
+ */
+ public static final Map CHANNELS_BSP = new HashMap<>();
+ static {
+ CHANNELS_BSP.put(6, CHANNEL_POWER);
+ CHANNELS_BSP.put(0, CHANNEL_BATTERY_VOLTAGE);
+ CHANNELS_BSP.put(2, CHANNEL_BATTERY_CURRENT);
+ CHANNELS_BSP.put(4, CHANNEL_STATE_OF_CHARGE);
+ CHANNELS_BSP.put(58, CHANNEL_BATTERY_TEMPERATURE);
+ }
+
+ /**
+ * Map of the supported BSP channel with their unit
+ */
+ public static final Map> UNIT_CHANNELS_BSP = new HashMap<>();
+ static {
+ UNIT_CHANNELS_BSP.put(6, WATT);
+ UNIT_CHANNELS_BSP.put(0, VOLT);
+ UNIT_CHANNELS_BSP.put(2, AMPERE);
+ UNIT_CHANNELS_BSP.put(4, PERCENT);
+ UNIT_CHANNELS_BSP.put(58, CELSIUS);
+ }
+
+ /**
+ * Map of the supported Xtender channel with their registers
+ */
+ public static final Map CHANNELS_XTENDER = new HashMap<>();
+ static {
+ CHANNELS_XTENDER.put(22, CHANNEL_INPUT_VOLTAGE);
+ CHANNELS_XTENDER.put(24, CHANNEL_INPUT_CURRENT);
+ CHANNELS_XTENDER.put(274, CHANNEL_INPUT_ACTIVE_POWER);
+ CHANNELS_XTENDER.put(168, CHANNEL_INPUT_FREQUENCY);
+ CHANNELS_XTENDER.put(42, CHANNEL_OUTPUT_VOLTAGE);
+ CHANNELS_XTENDER.put(44, CHANNEL_OUTPUT_CURRENT);
+ CHANNELS_XTENDER.put(272, CHANNEL_OUTPUT_ACTIVE_POWER);
+ CHANNELS_XTENDER.put(170, CHANNEL_OUTPUT_FREQUENCY);
+ CHANNELS_XTENDER.put(56, CHANNEL_OPERATING_STATE);
+ CHANNELS_XTENDER.put(98, CHANNEL_STATE_INVERTER);
+ }
+
+ /**
+ * Map of the supported Xtender channel with their unit
+ */
+ public static final Map> UNIT_CHANNELS_XTENDER = new HashMap<>();
+ static {
+ UNIT_CHANNELS_XTENDER.put(22, VOLT);
+ UNIT_CHANNELS_XTENDER.put(24, AMPERE);
+ UNIT_CHANNELS_XTENDER.put(274, KILO(WATT));
+ UNIT_CHANNELS_XTENDER.put(168, HERTZ);
+ UNIT_CHANNELS_XTENDER.put(42, VOLT);
+ UNIT_CHANNELS_XTENDER.put(44, AMPERE);
+ UNIT_CHANNELS_XTENDER.put(272, KILO(WATT));
+ UNIT_CHANNELS_XTENDER.put(170, HERTZ);
+ }
+
+ /**
+ * Map of the supported VarioTrack channel with their registers
+ */
+ public static final Map CHANNELS_VARIOTRACK = new HashMap<>();
+ static {
+ CHANNELS_VARIOTRACK.put(30, CHANNEL_MODEL_VARIOTRACK);
+ CHANNELS_VARIOTRACK.put(4, CHANNEL_VOLTAGE_PV_GENERATOR);
+ CHANNELS_VARIOTRACK.put(8, CHANNEL_POWER_PV_GENERATOR);
+ CHANNELS_VARIOTRACK.put(14, CHANNEL_PRODUCTION_CURRENT_DAY);
+ CHANNELS_VARIOTRACK.put(0, CHANNEL_BATTERY_VOLTAGE);
+ CHANNELS_VARIOTRACK.put(2, CHANNEL_BATTERY_CURRENT);
+ CHANNELS_VARIOTRACK.put(32, CHANNEL_OPERATING_MODE);
+ CHANNELS_VARIOTRACK.put(138, CHANNEL_STATE_VARIOTRACK);
+ }
+
+ /**
+ * Map of the supported VarioTrack channel with their unit
+ */
+ public static final Map> UNIT_CHANNELS_VARIOTRACK = new HashMap<>();
+ static {
+ UNIT_CHANNELS_VARIOTRACK.put(4, VOLT);
+ UNIT_CHANNELS_VARIOTRACK.put(8, KILO(WATT));
+ UNIT_CHANNELS_VARIOTRACK.put(14, KILOWATT_HOUR);
+ UNIT_CHANNELS_VARIOTRACK.put(0, VOLT);
+ UNIT_CHANNELS_VARIOTRACK.put(2, AMPERE);
+ }
+
+ /**
+ * Map of the supported VarioString channel with their registers
+ */
+ public static final Map CHANNELS_VARIOSTRING = new HashMap<>();
+ static {
+ CHANNELS_VARIOSTRING.put(0, CHANNEL_BATTERY_VOLTAGE);
+ CHANNELS_VARIOSTRING.put(2, CHANNEL_BATTERY_CURRENT);
+ CHANNELS_VARIOSTRING.put(8, CHANNEL_PV_VOLTAGE);
+ CHANNELS_VARIOSTRING.put(14, CHANNEL_PV_CURRENT);
+ CHANNELS_VARIOSTRING.put(20, CHANNEL_PV_POWER);
+ CHANNELS_VARIOSTRING.put(34, CHANNEL_PRODUCTION_PV_CURRENT_DAY);
+ CHANNELS_VARIOSTRING.put(26, CHANNEL_PV_OPERATING_MODE);
+ CHANNELS_VARIOSTRING.put(10, CHANNEL_PV1_VOLTAGE);
+ CHANNELS_VARIOSTRING.put(16, CHANNEL_PV1_CURRENT);
+ CHANNELS_VARIOSTRING.put(22, CHANNEL_PV1_POWER);
+ CHANNELS_VARIOSTRING.put(36, CHANNEL_PRODUCTION_PV1_CURRENT_DAY);
+ CHANNELS_VARIOSTRING.put(28, CHANNEL_PV1_OPERATING_MODE);
+ CHANNELS_VARIOSTRING.put(12, CHANNEL_PV2_VOLTAGE);
+ CHANNELS_VARIOSTRING.put(18, CHANNEL_PV2_CURRENT);
+ CHANNELS_VARIOSTRING.put(24, CHANNEL_PV2_POWER);
+ CHANNELS_VARIOSTRING.put(38, CHANNEL_PRODUCTION_PV2_CURRENT_DAY);
+ CHANNELS_VARIOSTRING.put(30, CHANNEL_PV2_OPERATING_MODE);
+ CHANNELS_VARIOSTRING.put(216, CHANNEL_STATE_VARIOSTRING);
+ }
+
+ /**
+ * Map of the supported VarioString channel with their unit
+ */
+ public static final Map> UNIT_CHANNELS_VARIOSTRING = new HashMap<>();
+ static {
+ UNIT_CHANNELS_VARIOSTRING.put(0, VOLT);
+ UNIT_CHANNELS_VARIOSTRING.put(2, AMPERE);
+ UNIT_CHANNELS_VARIOSTRING.put(8, VOLT);
+ UNIT_CHANNELS_VARIOSTRING.put(14, AMPERE);
+ UNIT_CHANNELS_VARIOSTRING.put(20, KILO(WATT));
+ UNIT_CHANNELS_VARIOSTRING.put(34, KILOWATT_HOUR);
+ UNIT_CHANNELS_VARIOSTRING.put(10, VOLT);
+ UNIT_CHANNELS_VARIOSTRING.put(16, AMPERE);
+ UNIT_CHANNELS_VARIOSTRING.put(22, KILO(WATT));
+ UNIT_CHANNELS_VARIOSTRING.put(36, KILOWATT_HOUR);
+ UNIT_CHANNELS_VARIOSTRING.put(12, VOLT);
+ UNIT_CHANNELS_VARIOSTRING.put(18, AMPERE);
+ UNIT_CHANNELS_VARIOSTRING.put(24, KILO(WATT));
+ UNIT_CHANNELS_VARIOSTRING.put(38, KILOWATT_HOUR);
+ }
+
+ // List of all parameters
+ public static final String SLAVE_ADDRESS = "slaveAddress";
+ public static final String REFRESH = "refresh";
+}
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderConfiguration.java b/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderConfiguration.java
new file mode 100644
index 0000000000000..1436a3b81b0e9
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderConfiguration.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.studer.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link StuderConfiguration} class contains fields mapping thing configuration parameters.
+ *
+ * @author Giovanni Mirulla - Initial contribution
+ */
+@NonNullByDefault
+public class StuderConfiguration {
+ /**
+ * Address of slave device
+ */
+ public int slaveAddress = 0;
+ /**
+ * Refresh interval in seconds
+ */
+ public int refresh = 5;
+ /**
+ * Max tries for one register
+ */
+ public int maxTries = 3;
+}
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderHandler.java b/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderHandler.java
new file mode 100644
index 0000000000000..c8f6e6ef36d09
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderHandler.java
@@ -0,0 +1,442 @@
+/**
+ * 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.studer.internal;
+
+import static org.openhab.binding.modbus.studer.internal.StuderBindingConstants.*;
+
+import java.util.ArrayList;
+import java.util.Optional;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.smarthome.core.library.types.OnOffType;
+import org.eclipse.smarthome.core.library.types.QuantityType;
+import org.eclipse.smarthome.core.library.types.StringType;
+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.ThingTypeUID;
+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.UnDefType;
+import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler;
+import org.openhab.binding.modbus.studer.internal.StuderParser.ModeXtender;
+import org.openhab.binding.modbus.studer.internal.StuderParser.VSMode;
+import org.openhab.binding.modbus.studer.internal.StuderParser.VTMode;
+import org.openhab.binding.modbus.studer.internal.StuderParser.VTType;
+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.ModbusRegisterArray;
+import org.openhab.io.transport.modbus.PollTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link StuderHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Giovanni Mirulla - Initial contribution
+ */
+@NonNullByDefault
+public class StuderHandler extends BaseThingHandler {
+
+ private final Logger logger = LoggerFactory.getLogger(StuderHandler.class);
+
+ private @Nullable StuderConfiguration config;
+
+ /**
+ * Array of tasks used to poll the device
+ */
+ private ArrayList pollTasks = new ArrayList();
+
+ /**
+ * Communication interface to the slave endpoint we're connecting to
+ */
+ protected volatile @Nullable ModbusCommunicationInterface comms = null;
+
+ /**
+ * Importing parser methods and enums
+ */
+ final StuderParser parser = new StuderParser();
+ /**
+ * Support variable for type of thing
+ */
+ protected ThingTypeUID type;
+
+ /**
+ * Array of registers of Studer slave to read, we store this once initialization is complete
+ */
+ private Integer[] registers = new Integer[0];
+
+ /**
+ * Instances of this handler
+ *
+ * @param thing the thing to handle
+ * @param type the type of thing to handle
+ * @param slaveAddress the address of thing
+ * @param refreshSec the address of thing
+ */
+ public StuderHandler(Thing thing) {
+ super(thing);
+ this.type = thing.getThingTypeUID();
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+
+ // Currently we do not support any commands
+ }
+
+ /**
+ * Initialization:
+ * Load the config object
+ * Connect to the slave bridge
+ * Get registers to poll
+ * Start the periodic polling
+ */
+ @Override
+ public void initialize() {
+ config = getConfigAs(StuderConfiguration.class);
+ logger.debug("Initializing thing with configuration: {}", thing.getConfiguration());
+
+ startUp();
+ }
+
+ /*
+ * This method starts the operation of this handler
+ * Connect to the slave bridge
+ * Get registers to poll
+ * Start the periodic polling
+ */
+ private void startUp() {
+
+ connectEndpoint();
+
+ if (comms == null || config == null) {
+ logger.debug("Invalid endpoint/config/manager ref for studer handler");
+ return;
+ }
+
+ if (!pollTasks.isEmpty()) {
+ return;
+ }
+
+ if (type.equals(THING_TYPE_BSP)) {
+ Set keys = CHANNELS_BSP.keySet();
+ registers = keys.toArray(new Integer[keys.size()]);
+ } else if (type.equals(THING_TYPE_XTENDER)) {
+ Set keys = CHANNELS_XTENDER.keySet();
+ registers = keys.toArray(new Integer[keys.size()]);
+ } else if (type.equals(THING_TYPE_VARIOTRACK)) {
+ Set keys = CHANNELS_VARIOTRACK.keySet();
+ registers = keys.toArray(new Integer[keys.size()]);
+ } else if (type.equals(THING_TYPE_VARIOSTRING)) {
+ Set keys = CHANNELS_VARIOSTRING.keySet();
+ registers = keys.toArray(new Integer[keys.size()]);
+ }
+
+ for (int r : registers) {
+ registerPollTask(r);
+
+ }
+ }
+
+ /**
+ * Dispose the binding correctly
+ */
+ @Override
+ public void dispose() {
+ tearDown();
+ }
+
+ /**
+ * Unregister the poll tasks and release the endpoint reference
+ */
+ private void tearDown() {
+ unregisterPollTasks();
+ unregisterEndpoint();
+ }
+
+ /**
+ * 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 {
+ logger.debug("Unexpected bridge handler: {}", handler);
+ return null;
+ }
+ }
+
+ /**
+ * Get a reference to the modbus endpoint
+ */
+ private void connectEndpoint() {
+ if (comms != null) {
+ return;
+ }
+
+ ModbusEndpointThingHandler slaveEndpointThingHandler = getEndpointThingHandler();
+ if (slaveEndpointThingHandler == null) {
+ @SuppressWarnings("null")
+ String label = Optional.ofNullable(getBridge()).map(b -> b.getLabel()).orElse("");
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
+ String.format("Bridge '%s' is offline", label));
+ logger.debug("No bridge handler available -- aborting init for {}", label);
+ return;
+ }
+ comms = slaveEndpointThingHandler.getCommunicationInterface();
+ 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));
+ logger.debug("Bridge not initialized fully (no endpoint) -- aborting init for {}", this);
+ return;
+ }
+ }
+
+ /**
+ * Remove the endpoint if exists
+ */
+ private void unregisterEndpoint() {
+ // Comms will be close()'d by endpoint thing handler
+ comms = null;
+ }
+
+ private synchronized void unregisterPollTasks() {
+ if (pollTasks.isEmpty()) {
+ return;
+ }
+ logger.debug("Unregistering polling from ModbusManager");
+ ModbusCommunicationInterface mycomms = comms;
+ if (mycomms != null) {
+ for (PollTask t : pollTasks) {
+ mycomms.unregisterRegularPoll(t);
+ }
+ pollTasks.clear();
+ }
+ }
+
+ /**
+ * Register poll task
+ * This is where we set up our regular poller
+ */
+ private synchronized void registerPollTask(int registerNumber) {
+ if (pollTasks.size() >= registers.length) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
+ throw new IllegalStateException("New pollTask invalid");
+ }
+ ModbusCommunicationInterface mycomms = comms;
+ StuderConfiguration studerConfig = config;
+ if (studerConfig == null || mycomms == null) {
+ throw new IllegalStateException("registerPollTask called without proper configuration");
+ }
+
+ logger.debug("Setting up regular polling");
+
+ ModbusReadRequestBlueprint request = new ModbusReadRequestBlueprint(studerConfig.slaveAddress,
+ ModbusReadFunctionCode.READ_INPUT_REGISTERS, registerNumber, 2, studerConfig.maxTries);
+ long refreshMillis = studerConfig.refresh * 1000;
+ PollTask pollTask = mycomms.registerRegularPoll(request, refreshMillis, 1000, result -> {
+ if (result.getRegisters().isPresent()) {
+ ModbusRegisterArray reg = result.getRegisters().get();
+ handlePolledData(registerNumber, reg);
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
+ return;
+ }
+ if (getThing().getStatus() != ThingStatus.ONLINE) {
+ updateStatus(ThingStatus.ONLINE);
+ }
+ }, this::handleError);
+ pollTasks.add(pollTask);
+ }
+
+ /**
+ * 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 n register readed
+ * @param registers byte array read from the modbus slave
+ */
+ protected void handlePolledData(int registerNumber, ModbusRegisterArray registers) {
+ String hexString = registers.toHexString().toString();
+ Float quantity = parser.hexToFloat(hexString);
+ if (quantity != null) {
+ if (type.equals(THING_TYPE_BSP)) {
+ updateState(CHANNELS_BSP.get(registerNumber),
+ new QuantityType<>(quantity, UNIT_CHANNELS_BSP.get(registerNumber)));
+ } else if (type.equals(THING_TYPE_XTENDER)) {
+ handlePolledDataXtender(registerNumber, quantity);
+ } else if (type.equals(THING_TYPE_VARIOTRACK)) {
+ handlePolledDataVarioTrack(registerNumber, quantity);
+ } else if (type.equals(THING_TYPE_VARIOSTRING)) {
+ handlePolledDataVarioString(registerNumber, quantity);
+ }
+ }
+ resetCommunicationError();
+ }
+
+ /**
+ * This method is called each time new data has been polled from the VarioString slave
+ * The register array is first parsed, then each of the channels are updated
+ * to the new values
+ */
+ protected void handlePolledDataVarioString(int registerNumber, Float quantity) {
+ switch (CHANNELS_VARIOSTRING.get(registerNumber)) {
+ case CHANNEL_PV_OPERATING_MODE:
+ case CHANNEL_PV1_OPERATING_MODE:
+ case CHANNEL_PV2_OPERATING_MODE:
+ VSMode vsmode = StuderParser.getVSModeByCode(quantity.intValue());
+ if (vsmode == VSMode.UNKNOWN) {
+ updateState(CHANNELS_VARIOSTRING.get(registerNumber), UnDefType.UNDEF);
+ } else {
+ updateState(CHANNELS_VARIOSTRING.get(registerNumber), new StringType(vsmode.name()));
+ }
+ break;
+ case CHANNEL_STATE_VARIOSTRING:
+ OnOffType vsstate = StuderParser.getStateByCode(quantity.intValue());
+ updateState(CHANNELS_VARIOSTRING.get(registerNumber), vsstate);
+ break;
+ default:
+ updateState(CHANNELS_VARIOSTRING.get(registerNumber),
+ new QuantityType<>(quantity, UNIT_CHANNELS_VARIOSTRING.get(registerNumber)));
+ }
+ }
+
+ /**
+ * This method is called each time new data has been polled from the VarioTrack slave
+ * The register array is first parsed, then each of the channels are updated
+ * to the new values
+ */
+ protected void handlePolledDataVarioTrack(int registerNumber, Float quantity) {
+ switch (CHANNELS_VARIOTRACK.get(registerNumber)) {
+ case CHANNEL_MODEL_VARIOTRACK:
+ VTType type = StuderParser.getVTTypeByCode(quantity.intValue());
+ if (type == VTType.UNKNOWN) {
+ updateState(CHANNELS_VARIOTRACK.get(registerNumber), UnDefType.UNDEF);
+ } else {
+ updateState(CHANNELS_VARIOTRACK.get(registerNumber), new StringType(type.name()));
+ }
+ break;
+
+ case CHANNEL_OPERATING_MODE:
+ VTMode vtmode = StuderParser.getVTModeByCode(quantity.intValue());
+ if (vtmode == VTMode.UNKNOWN) {
+ updateState(CHANNELS_VARIOTRACK.get(registerNumber), UnDefType.UNDEF);
+ } else {
+ updateState(CHANNELS_VARIOTRACK.get(registerNumber), new StringType(vtmode.name()));
+ }
+ break;
+
+ case CHANNEL_STATE_VARIOTRACK:
+ OnOffType vtstate = StuderParser.getStateByCode(quantity.intValue());
+ updateState(CHANNELS_VARIOTRACK.get(registerNumber), vtstate);
+ break;
+ default:
+ updateState(CHANNELS_VARIOTRACK.get(registerNumber),
+ new QuantityType<>(quantity, UNIT_CHANNELS_VARIOTRACK.get(registerNumber)));
+ }
+ }
+
+ /**
+ * This method is called each time new data has been polled from the Xtender slave
+ * The register array is first parsed, then each of the channels are updated
+ * to the new values
+ */
+ protected void handlePolledDataXtender(int registerNumber, Float quantity) {
+ switch (CHANNELS_XTENDER.get(registerNumber)) {
+ case CHANNEL_OPERATING_STATE:
+ ModeXtender mode = StuderParser.getModeXtenderByCode(quantity.intValue());
+ if (mode == ModeXtender.UNKNOWN) {
+ updateState(CHANNELS_XTENDER.get(registerNumber), UnDefType.UNDEF);
+ } else {
+ updateState(CHANNELS_XTENDER.get(registerNumber), new StringType(mode.name()));
+ }
+ break;
+ case CHANNEL_STATE_INVERTER:
+ OnOffType xtstate = StuderParser.getStateByCode(quantity.intValue());
+ updateState(CHANNELS_XTENDER.get(registerNumber), xtstate);
+ break;
+ default:
+ updateState(CHANNELS_XTENDER.get(registerNumber),
+ new QuantityType<>(quantity, UNIT_CHANNELS_XTENDER.get(registerNumber)));
+ }
+ }
+
+ /**
+ * Handle errors received during communication
+ */
+ protected void handleError(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));
+ }
+
+ /**
+ * 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);
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderHandlerFactory.java b/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderHandlerFactory.java
new file mode 100644
index 0000000000000..f15eb9eea264d
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderHandlerFactory.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.studer.internal;
+
+import static org.openhab.binding.modbus.studer.internal.StuderBindingConstants.SUPPORTED_THING_TYPES_UIDS;
+
+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.osgi.service.component.annotations.Component;
+
+/**
+ * The {@link StuderHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Giovanni Mirulla - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.studer", service = ThingHandlerFactory.class)
+public class StuderHandlerFactory extends BaseThingHandlerFactory {
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ return new StuderHandler(thing);
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderParser.java b/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderParser.java
new file mode 100644
index 0000000000000..830792a4efed0
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/java/org/openhab/binding/modbus/studer/internal/StuderParser.java
@@ -0,0 +1,218 @@
+/**
+ * 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.studer.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.smarthome.core.library.types.OnOffType;
+
+/**
+ * The {@link StuderParser} class with helper method
+ * and possible values for mode and state
+ *
+ * @author Giovanni Mirulla - Initial contribution
+ */
+@NonNullByDefault
+public class StuderParser {
+ public enum ModeXtender {
+ INVALID(0),
+ INVERTER(1),
+ CHARGER(2),
+ BOOST(3),
+ INJECTION(4),
+ UNKNOWN(-1);
+
+ private final int code;
+
+ ModeXtender(int code) {
+ this.code = code;
+ }
+
+ public int code() {
+ return this.code;
+ }
+ }
+
+ public static ModeXtender getModeXtenderByCode(int code) {
+ switch (code) {
+ case 0:
+ return ModeXtender.INVALID;
+ case 1:
+ return ModeXtender.INVERTER;
+ case 2:
+ return ModeXtender.CHARGER;
+ case 3:
+ return ModeXtender.BOOST;
+ case 4:
+ return ModeXtender.INJECTION;
+ default:
+ return ModeXtender.UNKNOWN;
+ }
+ }
+
+ public static OnOffType getStateByCode(int code) {
+ switch (code) {
+ case 0:
+ return OnOffType.OFF;
+ case 1:
+ return OnOffType.ON;
+ default:
+ return OnOffType.OFF;
+ }
+ }
+
+ public enum VTType {
+ VT80(0),
+ VT65(1),
+ UNKNOWN(-1);
+
+ private final int code;
+
+ VTType(int code) {
+ this.code = code;
+ }
+
+ public int code() {
+ return this.code;
+ }
+ }
+
+ public static VTType getVTTypeByCode(int code) {
+ switch (code) {
+ case 0:
+ return VTType.VT80;
+ case 1:
+ return VTType.VT65;
+ default:
+ return VTType.UNKNOWN;
+ }
+ }
+
+ public enum VTMode {
+ NIGHT(0),
+ STARTUP(1),
+ CHARGER(3),
+ SECURITY(5),
+ OFF(6),
+ CHARGE(8),
+ CHARGEV(9),
+ CHARGEI(10),
+ CHARGET(11),
+ CHIBSP(12),
+ UNKNOWN(-1);
+
+ private final int code;
+
+ VTMode(int code) {
+ this.code = code;
+ }
+
+ public int code() {
+ return this.code;
+ }
+ }
+
+ public static VTMode getVTModeByCode(int code) {
+ switch (code) {
+ case 0:
+ return VTMode.NIGHT;
+ case 1:
+ return VTMode.STARTUP;
+ case 3:
+ return VTMode.CHARGER;
+ case 5:
+ return VTMode.SECURITY;
+ case 6:
+ return VTMode.OFF;
+ case 8:
+ return VTMode.CHARGE;
+ case 9:
+ return VTMode.CHARGEV;
+ case 10:
+ return VTMode.CHARGEI;
+ case 11:
+ return VTMode.CHARGET;
+ case 12:
+ return VTMode.CHIBSP;
+ default:
+ return VTMode.UNKNOWN;
+ }
+ }
+
+ public enum VSMode {
+ NIGHT(0),
+ SECURITY(1),
+ OFF(2),
+ CHARGE(3),
+ CHARGEV(4),
+ CHARGEI(5),
+ CHARGEP(6),
+ CHARGEIPV(7),
+ CHARGET(8),
+ CHIBSP(10),
+ UNKNOWN(-1);
+
+ private final int code;
+
+ VSMode(int code) {
+ this.code = code;
+ }
+
+ public int code() {
+ return this.code;
+ }
+ }
+
+ public static VSMode getVSModeByCode(int code) {
+ switch (code) {
+ case 0:
+ return VSMode.NIGHT;
+ case 1:
+ return VSMode.SECURITY;
+ case 2:
+ return VSMode.OFF;
+ case 3:
+ return VSMode.CHARGE;
+ case 4:
+ return VSMode.CHARGEV;
+ case 5:
+ return VSMode.CHARGEI;
+ case 6:
+ return VSMode.CHARGEP;
+ case 7:
+ return VSMode.CHARGEIPV;
+ case 8:
+ return VSMode.CHARGET;
+ case 10:
+ return VSMode.CHIBSP;
+ default:
+ return VSMode.UNKNOWN;
+ }
+ }
+
+ /**
+ * Convert an hex string to float
+ *
+ * @param hex string to convert from
+ * @return the converted float
+ */
+ public @Nullable Float hexToFloat(String hex) {
+ String t = hex.replaceAll(" ", "");
+ float f = Float.intBitsToFloat((int) Long.parseLong(t, 16));
+ if (Float.isNaN(f)) {
+ return null;
+ } else {
+ return f;
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/BSP-channel-types.xml b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/BSP-channel-types.xml
new file mode 100644
index 0000000000000..39dbdaa3c5493
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/BSP-channel-types.xml
@@ -0,0 +1,24 @@
+
+
+
+
+ Number:Power
+
+
+
+
+
+ Number:Dimensionless
+
+
+
+
+
+ Number:Temperature
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/Common-channel-types.xml b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/Common-channel-types.xml
new file mode 100644
index 0000000000000..84ea4f78e70da
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/Common-channel-types.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ Number:ElectricPotential
+
+
+
+
+
+ Number:ElectricCurrent
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/VarioTrack-channel-types.xml b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/VarioTrack-channel-types.xml
new file mode 100644
index 0000000000000..313e11902b8de
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/VarioTrack-channel-types.xml
@@ -0,0 +1,61 @@
+
+
+
+
+ String
+
+
+
+
+
+
+
+
+
+
+ Number:ElectricPotential
+
+
+
+
+
+ Number:Power
+
+
+
+
+
+ Number:Energy
+
+
+
+
+
+ String
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Switch
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/VariotString-channel-types.xml b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/VariotString-channel-types.xml
new file mode 100644
index 0000000000000..d76ef0431bd45
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/VariotString-channel-types.xml
@@ -0,0 +1,144 @@
+
+
+
+
+ Number:ElectricPotential
+
+
+
+
+
+ Number:ElectricCurrent
+
+
+
+
+
+ Number:Power
+
+
+
+
+
+ Number:Energy
+
+
+
+
+
+ String
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Number:ElectricPotential
+
+
+
+
+
+ Number:ElectricCurrent
+
+
+
+
+
+ Number:Power
+
+
+
+
+
+ Number:Energy
+
+
+
+
+
+ String
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Number:ElectricPotential
+
+
+
+
+
+ Number:ElectricCurrent
+
+
+
+
+
+ Number:Power
+
+
+
+
+
+ Number:Energy
+
+
+
+
+
+ String
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Switch
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/Xtender-channel-types.xml b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/Xtender-channel-types.xml
new file mode 100644
index 0000000000000..00cd830c25760
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/Xtender-channel-types.xml
@@ -0,0 +1,79 @@
+
+
+
+
+ Number:ElectricPotential
+
+
+
+
+
+ Number:ElectricCurrent
+
+
+
+
+
+ Number:Power
+
+
+
+
+
+ Number:Frequency
+
+
+
+
+
+ Number:ElectricPotential
+
+
+
+
+
+ Number:ElectricCurrent
+
+
+
+
+
+ Number:Power
+
+
+
+
+
+ Number:Frequency
+
+
+
+
+
+ String
+
+
+
+
+
+
+
+
+
+
+
+
+
+ String
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/thing-types.xml
new file mode 100644
index 0000000000000..8c0d9ea38330e
--- /dev/null
+++ b/bundles/org.openhab.binding.modbus.studer/src/main/resources/ESH-INF/thing/thing-types.xml
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+ Thing for Studer BSP Device
+
+
+
+
+
+
+
+
+
+
+ Slave address of BSP device
+ 60
+
+
+
+ Poll interval
+ 5
+ true
+
+
+
+
+
+
+
+
+
+ Thing for Studer Xtender Device
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Slave address of Xtender device
+ 10
+
+
+
+ Poll interval
+ 5
+ true
+
+
+
+
+
+
+
+
+
+ Thing for Studer VarioTrack Device
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Slave address of VarioTrack device
+ 20
+
+
+
+ Poll interval
+ 5
+ true
+
+
+
+
+
+
+
+
+
+ Thing for Studer VarioString Device
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Slave address of VarioString device
+ 40
+
+
+
+ Poll interval
+ 5
+ true
+
+
+
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 52f3a06b66a64..78d2713a4ba41 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -166,6 +166,7 @@
org.openhab.binding.minecraft
org.openhab.binding.modbus
org.openhab.binding.modbus.e3dc
+ org.openhab.binding.modbus.studer
org.openhab.binding.modbus.sunspec
org.openhab.binding.modbus.stiebeleltron
org.openhab.binding.monopriceaudio