diff --git a/CODEOWNERS b/CODEOWNERS
index e129d743c8bf3..9e60ad6b66254 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -147,6 +147,7 @@
/bundles/org.openhab.binding.ntp/ @marcelrv
/bundles/org.openhab.binding.nuki/ @mkatter
/bundles/org.openhab.binding.oceanic/ @kgoderis
+/bundles/org.openhab.binding.ojelectronics/ @EvilPingu
/bundles/org.openhab.binding.omnikinverter/ @hansbogert
/bundles/org.openhab.binding.onebusaway/ @sdwilsh
/bundles/org.openhab.binding.onewiregpio/ @aogorek
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index c210a5b03f7a5..37c5b8dbaea60 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -724,6 +724,11 @@
org.openhab.binding.oceanic
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.ojelectronics
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.omnikinverter
diff --git a/bundles/org.openhab.binding.ojelectronics/.classpath b/bundles/org.openhab.binding.ojelectronics/.classpath
new file mode 100644
index 0000000000000..615608997a6c5
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/.classpath
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.ojelectronics/.project b/bundles/org.openhab.binding.ojelectronics/.project
new file mode 100644
index 0000000000000..c0196c4e93bbf
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/.project
@@ -0,0 +1,23 @@
+
+
+ org.openhab.binding.ojelectronics
+
+
+
+
+
+ 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.ojelectronics/NOTICE b/bundles/org.openhab.binding.ojelectronics/NOTICE
new file mode 100644
index 0000000000000..38d625e349232
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/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.ojelectronics/README.md b/bundles/org.openhab.binding.ojelectronics/README.md
new file mode 100644
index 0000000000000..43e1234e13293
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/README.md
@@ -0,0 +1,86 @@
+# OJElectronics Binding
+
+With this binding it is possible to connect [OWD5/MWD5 Thermostat](https://www.ojelectronics.com/business-areas/wifi-thermostat-owd5-prod400) of OJ Electronics.
+
+At this moment all information is read only.
+
+## Supported Things
+
+There are two things:
+
+| Thing | Type | Description |
+|----------------------|--------|-------------------------------------|
+| ojcloud | Bridge | OJ Electronics Cloud Connector |
+| owd5 | Thing | OJ Electronics OWD5/MWD5 Thermostat |
+
+## Discovery
+
+Not supported at the moment.
+
+## Thing Configuration
+
+### OJ Electronics Bridge configuration (ojcloud)
+
+| Parameter | Description |
+|-----------------------|--------------------------------------------------------------------------|
+| userName | user name from the OJElectronics App (required) |
+| password | password from the OJElectronics App (required) |
+| apiKey | API key. You get the key from your local distributor. |
+| apiUrl | URL of the API endpoint. Optional, the default value should always work. |
+| refreshDelayInSeconds | Refresh interval in seconds. Optional, the default value is 30 seconds. |
+| customerId | Customer ID. Optional, the default value should always work. |
+| softwareVersion | Software version. Optional, the default value should always work. |
+
+### OJ Electronics OWD5/MWD5 Thermostat configuration (owd5)
+
+| Parameter | Description |
+|-----------------------|--------------------------------------------------------------------------|
+| serialNumber | serial number from the OJElectronics App or the thermostat (required) |
+
+## Channels
+
+| Channel | Type | Description |
+|--------------------|--------------------|------------------------------------------------------------------------------------|
+| floorTemperature | Number:Temperature | Floor temperature |
+| groupName | Text | Group name |
+| groupId | Number | Group Id |
+| online | Contact | Online |
+| heating | Contact | Heating |
+| roomTemperature | Number:Temperature | Room temperature |
+| thermostatName | Text | Thermostat name |
+| regulationMode | Text | Regulation mode |
+| serialNumber | Text | Serial number |
+| comfortSetpoint | Number:Temperature | Target comfort temperature |
+| comfortEndTime | Date time | Date and time when the thermostat switchs back from comfort mode to automatic mode |
+| boostEndTime | Date time | Date and time when the thermostat switchs back from boost mode to automatic mode |
+| manualModeSetpoint | Number:Temperature | Target temperature of the manual mode |
+| vacationEnabled | Switch | Vacation is enabled |
+
+## Example
+
+This example shows how to configure the OJElecttronics binding.
+
+### demo.things
+
+```
+Binding ojelectronics:ojcloud:myCloud "My Cloud" @ "My Home" [ userName="MyUserName" password="MyPassword" apiKey="The Key" ] {
+ Thing owd5 myThermostat [ serialNumber="123" ]
+}
+```
+
+### demo.items
+
+```
+Number Bath_Floor_Temperature "Bathroom: Floor Temperature" {channel="ojelectronics:owd5:myThermostat:floorTemperature"}
+String Bath_Mode "Bathroom: Mode" {channel="ojelectronics:owd5:myThermostat:regulationMode"}
+```
+
+### demo.sitemap
+
+```
+sitemap myHome label="my Home"{
+ Text item=Bath_Floor_Temperature
+ Text item=Bath_Mode
+}
+```
+
diff --git a/bundles/org.openhab.binding.ojelectronics/pom.xml b/bundles/org.openhab.binding.ojelectronics/pom.xml
new file mode 100644
index 0000000000000..add24ff8da2ae
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/pom.xml
@@ -0,0 +1,16 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 2.5.7-SNAPSHOT
+
+
+ org.openhab.binding.ojelectronics
+
+ openHAB Add-ons :: Bundles :: OJElectronics Binding
+
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/feature/feature.xml b/bundles/org.openhab.binding.ojelectronics/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..1bda37db28d3c
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/feature/feature.xml
@@ -0,0 +1,9 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ mvn:org.openhab.addons.bundles/org.openhab.binding.ojelectronics/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/BindingConstants.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/BindingConstants.java
new file mode 100644
index 0000000000000..2a6a59c7506dc
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/BindingConstants.java
@@ -0,0 +1,47 @@
+/**
+ * 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.ojelectronics.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+
+/**
+ * The {@link OJElectronicsBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class BindingConstants {
+
+ private static final String BINDING_ID = "ojelectronics";
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_OJCLOUD = new ThingTypeUID(BINDING_ID, "ojcloud");
+ public static final ThingTypeUID THING_TYPE_OWD5 = new ThingTypeUID(BINDING_ID, "owd5");
+
+ // List of all Channel ids
+ public static final String CHANNEL_OWD5_FLOORTEMPERATURE = "floorTemperature";
+ public static final String CHANNEL_OWD5_GROUPNAME = "groupName";
+ public static final String CHANNEL_OWD5_GROUPID = "groupId";
+ public static final String CHANNEL_OWD5_ONLINE = "online";
+ public static final String CHANNEL_OWD5_HEATING = "heating";
+ public static final String CHANNEL_OWD5_ROOMTEMPERATURE = "roomTemperature";
+ public static final String CHANNEL_OWD5_THERMOSTATNAME = "thermostatName";
+ public static final String CHANNEL_OWD5_REGULATIONMODE = "regulationMode";
+ public static final String CHANNEL_OWD5_COMFORTSETPOINT = "comfortSetpoint";
+ public static final String CHANNEL_OWD5_COMFORTENDTIME = "comfortEndTime";
+ public static final String CHANNEL_OWD5_BOOSTENDTIME = "boostEndTime";
+ public static final String CHANNEL_OWD5_MANUALSETPOINT = "manualSetpoint";
+ public static final String CHANNEL_OWD5_VACATIONENABLED = "vacationEnabled";
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/OJCloudHandler.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/OJCloudHandler.java
new file mode 100644
index 0000000000000..0a1f4ae60456c
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/OJCloudHandler.java
@@ -0,0 +1,159 @@
+/**
+ * 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.ojelectronics.internal;
+
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler;
+import org.eclipse.smarthome.core.thing.binding.BridgeHandler;
+import org.eclipse.smarthome.core.types.Command;
+import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConfiguration;
+import org.openhab.binding.ojelectronics.internal.models.groups.GroupContentResponseModel;
+import org.openhab.binding.ojelectronics.internal.services.RefreshGroupContentService;
+import org.openhab.binding.ojelectronics.internal.services.RefreshService;
+import org.openhab.binding.ojelectronics.internal.services.SignInService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Handles all traffic with OJ Electronics cloud
+ *
+ * @author Christian Kittel - Initial Contribution
+ */
+@NonNullByDefault
+public class OJCloudHandler extends BaseBridgeHandler implements BridgeHandler {
+
+ private final Logger logger = LoggerFactory.getLogger(OJCloudHandler.class);
+ private final HttpClient httpClient;
+
+ private @Nullable RefreshService refreshService;
+ private @Nullable SignInService signInService;
+ private OJElectronicsBridgeConfiguration configuration;
+ private @Nullable ScheduledFuture> signTask;
+
+ public OJCloudHandler(Bridge bridge, HttpClient httpClient) {
+ super(bridge);
+ this.httpClient = httpClient;
+ this.configuration = new OJElectronicsBridgeConfiguration();
+ }
+
+ /**
+ * Initializes the binding.
+ */
+ @Override
+ public void initialize() {
+ configuration = getConfigAs(OJElectronicsBridgeConfiguration.class);
+ ensureSignIn();
+ }
+
+ /**
+ * Disposes the binding.
+ */
+ @Override
+ public void dispose() {
+ final RefreshService refreshService = this.refreshService;
+ if (refreshService != null) {
+ refreshService.stop();
+ }
+ final ScheduledFuture> signTask = this.signTask;
+ if (signTask != null) {
+ signTask.cancel(true);
+ }
+ this.refreshService = null;
+ signInService = null;
+ super.dispose();
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ }
+
+ private void ensureSignIn() {
+ if (signInService == null) {
+ signInService = new SignInService(configuration, httpClient);
+ }
+ final SignInService signInService = this.signInService;
+ if (signInService != null) {
+ signInService.signIn(this::handleSignInDone, this::handleConnectionLost,
+ this::handleUnauthorizedWhileSignIn);
+ }
+ }
+
+ private void handleRefreshDone(@Nullable GroupContentResponseModel groupContentResponse,
+ @Nullable String errorMessage) {
+ logger.trace("OJElectronicsCloudHandler.handleRefreshDone({})", groupContentResponse);
+
+ if (groupContentResponse != null && groupContentResponse.errorCode == 0) {
+ new RefreshGroupContentService(groupContentResponse.groupContents, getThing().getThings()).handle();
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ (errorMessage == null) ? "Wrong or no result model; Refreshing stoppped" : errorMessage);
+ final RefreshService refreshService = this.refreshService;
+ if (refreshService != null) {
+ refreshService.stop();
+ }
+ }
+ }
+
+ private void handleSignInDone(String sessionId) {
+ logger.trace("OJElectronicsCloudHandler.handleSignInDone({})", sessionId);
+ if (refreshService == null) {
+ refreshService = new RefreshService(configuration, httpClient, scheduler);
+ }
+ final RefreshService refreshService = this.refreshService;
+ if (refreshService != null) {
+ refreshService.start(sessionId, this::handleRefreshDone, this::handleConnectionLost,
+ this::handleUnauthorized);
+
+ updateStatus(ThingStatus.ONLINE);
+ }
+ }
+
+ private void handleUnauthorized() {
+ final RefreshService refreshService = this.refreshService;
+ if (refreshService != null) {
+ refreshService.stop();
+ }
+ restartRefreshServiceAsync(1);
+ }
+
+ private void handleUnauthorizedWhileSignIn() {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "Could not sign in. Check user name and password.");
+ final RefreshService refreshService = this.refreshService;
+ if (refreshService != null) {
+ refreshService.stop();
+ }
+ }
+
+ private void handleConnectionLost() {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
+ final RefreshService refreshService = this.refreshService;
+ if (refreshService != null) {
+ refreshService.stop();
+ }
+ restartRefreshServiceAsync(configuration.refreshDelayInSeconds);
+ }
+
+ private void restartRefreshServiceAsync(long delayInSeconds) {
+ signTask = scheduler.schedule(this::ensureSignIn, delayInSeconds, TimeUnit.SECONDS);
+ }
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/OJCloudHandlerFactory.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/OJCloudHandlerFactory.java
new file mode 100644
index 0000000000000..92f0300afcb93
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/OJCloudHandlerFactory.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.ojelectronics.internal;
+
+import static org.openhab.binding.ojelectronics.internal.BindingConstants.THING_TYPE_OJCLOUD;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.smarthome.core.thing.Bridge;
+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.eclipse.smarthome.io.net.http.HttpClientFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * Factory to create {@link OJCloudHandler}
+ *
+ * @author Christian Kittel - Initial Contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.ojelectronics", service = ThingHandlerFactory.class)
+public class OJCloudHandlerFactory extends BaseThingHandlerFactory {
+
+ private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_OJCLOUD);
+
+ private final HttpClient httpClient;
+
+ /**
+ * Creates a new factory
+ *
+ * @param httpClientFactory Factory for HttpClient
+ */
+ @Activate
+ public OJCloudHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
+ this.httpClient = httpClientFactory.getCommonHttpClient();
+ }
+
+ /**
+ * Supported things for this factory
+ */
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ if (SUPPORTED_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
+ OJCloudHandler handler = new OJCloudHandler((Bridge) thing, httpClient);
+ return handler;
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandler.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandler.java
new file mode 100644
index 0000000000000..56553d7b6605f
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandler.java
@@ -0,0 +1,193 @@
+/**
+ * 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.ojelectronics.internal;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import javax.measure.quantity.Temperature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.smarthome.core.library.types.DateTimeType;
+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.library.types.StringType;
+import org.eclipse.smarthome.core.library.unit.SIUnits;
+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.binding.BaseThingHandler;
+import org.eclipse.smarthome.core.types.Command;
+import org.eclipse.smarthome.core.types.RefreshType;
+import org.openhab.binding.ojelectronics.internal.config.OJElectronicsThermostatConfiguration;
+import org.openhab.binding.ojelectronics.internal.models.groups.Thermostat;
+
+/**
+ * The {@link ThermostatHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class ThermostatHandler extends BaseThingHandler {
+
+ private final String serialNumber;
+ private @Nullable Thermostat currentThermostat;
+ private static final Map REGULATION_MODES = createRegulationMap();
+ private final Map> channelrefreshActions = createChannelRefreshActionMap();
+
+ /**
+ * Creates a new instance of {@link ThermostatHandler}
+ *
+ * @param thing Thing
+ */
+ public ThermostatHandler(Thing thing) {
+ super(thing);
+ serialNumber = getConfigAs(OJElectronicsThermostatConfiguration.class).serialNumber;
+ }
+
+ /**
+ * Gets the thing's serial number.
+ *
+ * @return serial number
+ */
+ public String getSerialNumber() {
+ return serialNumber;
+ }
+
+ /**
+ * Handles commands to this thing.
+ */
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (command instanceof RefreshType) {
+ final Thermostat thermostat = currentThermostat;
+ if (thermostat != null && channelrefreshActions.containsKey(channelUID.getId())) {
+ channelrefreshActions.get(channelUID.getId()).accept(thermostat);
+ }
+ }
+ }
+
+ /**
+ * Initializes the thing handler.
+ */
+ @Override
+ public void initialize() {
+ updateStatus(ThingStatus.ONLINE);
+ }
+
+ /**
+ * Sets the values after refreshing the thermostats values
+ *
+ * @param thermostat thermostat values
+ */
+ public void handleThermostatRefresh(Thermostat thermostat) {
+ currentThermostat = thermostat;
+ channelrefreshActions.forEach((channelUID, action) -> action.accept(thermostat));
+ }
+
+ private void updateManualSetpoint(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT,
+ new QuantityType(thermostat.manualModeSetpoint / (double) 100, SIUnits.CELSIUS));
+ }
+
+ private void updateBoostEndTime(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME,
+ new DateTimeType(ZonedDateTime.ofInstant(thermostat.boostEndTime.toInstant(), ZoneId.systemDefault())));
+ }
+
+ private void updateComfortEndTime(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, new DateTimeType(
+ ZonedDateTime.ofInstant(thermostat.comfortEndTime.toInstant(), ZoneId.systemDefault())));
+ }
+
+ private void updateComfortSetpoint(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_COMFORTSETPOINT,
+ new QuantityType(thermostat.comfortSetpoint / (double) 100, SIUnits.CELSIUS));
+ }
+
+ private void updateRegulationMode(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_REGULATIONMODE,
+ StringType.valueOf(getRegulationMode(thermostat.regulationMode)));
+ }
+
+ private void updateThermostatName(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_THERMOSTATNAME, StringType.valueOf(thermostat.thermostatName));
+ }
+
+ private void updateFloorTemperature(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_FLOORTEMPERATURE,
+ new QuantityType(thermostat.floorTemperature / (double) 100, SIUnits.CELSIUS));
+ }
+
+ private void updateRoomTemperature(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_ROOMTEMPERATURE,
+ new QuantityType(thermostat.roomTemperature / (double) 100, SIUnits.CELSIUS));
+ }
+
+ private void updateHeating(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_HEATING,
+ thermostat.heating ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
+ }
+
+ private void updateOnline(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_ONLINE,
+ thermostat.online ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
+ }
+
+ private void updateGroupId(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_GROUPID, new DecimalType(thermostat.groupId));
+ }
+
+ private void updateGroupName(Thermostat thermostat) {
+ updateState(BindingConstants.CHANNEL_OWD5_GROUPNAME, StringType.valueOf(thermostat.groupName));
+ }
+
+ private String getRegulationMode(int regulationMode) {
+ return REGULATION_MODES.get(regulationMode);
+ }
+
+ private static Map createRegulationMap() {
+ HashMap map = new HashMap<>();
+ map.put(1, "auto");
+ map.put(2, "comfort");
+ map.put(3, "manual");
+ map.put(4, "vacation");
+ map.put(6, "frostProtection");
+ map.put(8, "boost");
+ map.put(9, "eco");
+ return map;
+ };
+
+ private Map> createChannelRefreshActionMap() {
+ HashMap> map = new HashMap<>();
+ map.put(BindingConstants.CHANNEL_OWD5_GROUPNAME, this::updateGroupName);
+ map.put(BindingConstants.CHANNEL_OWD5_GROUPID, this::updateGroupId);
+ map.put(BindingConstants.CHANNEL_OWD5_ONLINE, this::updateOnline);
+ map.put(BindingConstants.CHANNEL_OWD5_HEATING, this::updateHeating);
+ map.put(BindingConstants.CHANNEL_OWD5_ROOMTEMPERATURE, this::updateRoomTemperature);
+ map.put(BindingConstants.CHANNEL_OWD5_FLOORTEMPERATURE, this::updateFloorTemperature);
+ map.put(BindingConstants.CHANNEL_OWD5_THERMOSTATNAME, this::updateThermostatName);
+ map.put(BindingConstants.CHANNEL_OWD5_REGULATIONMODE, this::updateRegulationMode);
+ map.put(BindingConstants.CHANNEL_OWD5_COMFORTSETPOINT, this::updateComfortSetpoint);
+ map.put(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, this::updateComfortEndTime);
+ map.put(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, this::updateBoostEndTime);
+ map.put(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT, this::updateManualSetpoint);
+ return map;
+ }
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandlerFactory.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandlerFactory.java
new file mode 100644
index 0000000000000..e2b9000927db7
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/ThermostatHandlerFactory.java
@@ -0,0 +1,58 @@
+/**
+ * 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.ojelectronics.internal;
+
+import static org.openhab.binding.ojelectronics.internal.BindingConstants.THING_TYPE_OWD5;
+
+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.osgi.service.component.annotations.Component;
+
+/**
+ * The {@link ThermostatHandlerFactory} is responsible for creating {@link OJElectronicsThermostatHandler}.
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.ojelectronics", service = ThingHandlerFactory.class)
+public class ThermostatHandlerFactory extends BaseThingHandlerFactory {
+
+ private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_OWD5);
+
+ /**
+ * Supported things of this factory.
+ */
+ @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_OWD5.equals(thingTypeUID)) {
+ return new ThermostatHandler(thing);
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/config/OJElectronicsBridgeConfiguration.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/config/OJElectronicsBridgeConfiguration.java
new file mode 100644
index 0000000000000..dce7231a357c3
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/config/OJElectronicsBridgeConfiguration.java
@@ -0,0 +1,59 @@
+/**
+ * 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.ojelectronics.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The configuration for {@link org.openhab.binding.ojelectronics.internal.OJElectronicsCloudHandler}
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class OJElectronicsBridgeConfiguration {
+
+ /**
+ * Password
+ */
+ public String password = "";
+
+ /**
+ * Customer-ID
+ */
+ public int customerId = 1;
+
+ /**
+ * User Name
+ */
+ public String userName = "";
+
+ /**
+ * Url for API
+ */
+ public String apiUrl = "https://OWD5-OJ001-App.ojelectronics.com/api";
+
+ /**
+ * API-Key
+ */
+ public String apiKey = "";
+
+ /**
+ * Software Version
+ */
+ public int softwareVersion = 1060;
+
+ /**
+ * Refresh-Delay
+ */
+ public long refreshDelayInSeconds = 30;
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/config/OJElectronicsThermostatConfiguration.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/config/OJElectronicsThermostatConfiguration.java
new file mode 100644
index 0000000000000..a11600f6efdb9
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/config/OJElectronicsThermostatConfiguration.java
@@ -0,0 +1,29 @@
+/**
+ * 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.ojelectronics.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The configuration for {@link org.openhab.binding.ojelectronics.internal.OJElectronicsThermostatHandler}
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class OJElectronicsThermostatConfiguration {
+
+ /**
+ * serial number of thermostat
+ */
+ public String serialNumber = "";
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Day.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Day.java
new file mode 100644
index 0000000000000..daf784a670c93
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Day.java
@@ -0,0 +1,31 @@
+/**
+ * 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.ojelectronics.internal.models.groups;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Model for a day
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class Day {
+
+ public int weekDayGrpNo;
+
+ public List events = new ArrayList<>();
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Event.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Event.java
new file mode 100644
index 0000000000000..b3ede9a86eba0
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Event.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.ojelectronics.internal.models.groups;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Model for events
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class Event {
+
+ public int scheduleType;
+
+ public String clock = "";
+
+ public int temperature;
+
+ public boolean active;
+
+ public boolean eventIsOnNextDay;
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContent.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContent.java
new file mode 100644
index 0000000000000..78fdb1c6500ae
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContent.java
@@ -0,0 +1,60 @@
+/**
+ * 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.ojelectronics.internal.models.groups;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Model for content of a group
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class GroupContent {
+
+ public int action;
+
+ public int groupId;
+
+ public String groupName = "";
+
+ public List thermostats = new ArrayList();
+
+ public int regulationMode;
+
+ public @Nullable Schedule schedule;
+
+ public int comfortSetpoint;
+
+ public String comfortEndTime = "";
+
+ public int manualModeSetpoint;
+
+ public boolean vacationEnabled;
+
+ public String vacationBeginDay = "";
+
+ public String vacationEndDay = "";
+
+ public int vacationTemperature;
+
+ public boolean lastPrimaryModeIsAuto;
+
+ public String boostEndTime = "";
+
+ public int frostProtectionTemperature;
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContentResponseModel.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContentResponseModel.java
new file mode 100644
index 0000000000000..bd9aa9429f912
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/GroupContentResponseModel.java
@@ -0,0 +1,31 @@
+/**
+ * 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.ojelectronics.internal.models.groups;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Model for the response of a content group
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class GroupContentResponseModel {
+
+ public List groupContents = new ArrayList();
+
+ public int errorCode;
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Schedule.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Schedule.java
new file mode 100644
index 0000000000000..3172b1bb8c7a2
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Schedule.java
@@ -0,0 +1,31 @@
+/**
+ * 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.ojelectronics.internal.models.groups;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Model for a schedule
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class Schedule {
+
+ public List days = new ArrayList();
+
+ public boolean modifiedDueToVerification;
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Thermostat.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Thermostat.java
new file mode 100644
index 0000000000000..e08132cad9874
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/groups/Thermostat.java
@@ -0,0 +1,98 @@
+/**
+ * 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.ojelectronics.internal.models.groups;
+
+import java.util.Date;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Model for a thermostat
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class Thermostat {
+
+ public int id;
+
+ public int action;
+
+ public String serialNumber = "";
+
+ public String groupName = "";
+
+ public int groupId;
+
+ public int customerId;
+
+ @SerializedName("SWversion")
+ public String softwareVersion = "";
+
+ public boolean online;
+
+ public boolean heating;
+
+ public int roomTemperature;
+
+ public int floorTemperature;
+
+ public int regulationMode;
+
+ public @Nullable Schedule schedule;
+
+ public int comfortSetpoint;
+
+ public Date comfortEndTime = new Date();
+
+ public int manualModeSetpoint;
+
+ public boolean vacationEnabled;
+
+ public Date vacationBeginDay = new Date();
+
+ public Date vacationEndDay = new Date();
+
+ public int vacationTemperature;
+
+ public boolean lastPrimaryModeIsAuto;
+
+ public Date boostEndTime = new Date();
+
+ public int frostProtectionTemperature;
+
+ public int errorCode;
+
+ public String thermostatName = "";
+
+ public boolean openWindow;
+
+ public boolean adaptiveMode;
+
+ public boolean daylightSaving;
+
+ public int sensorAppl;
+
+ public int minSetpoint;
+
+ public int maxSetpoint;
+
+ public int timeZone;
+
+ public boolean daylightSavingActive;
+
+ public int floorType;
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInQueryModel.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInQueryModel.java
new file mode 100644
index 0000000000000..1c606ae4eb535
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInQueryModel.java
@@ -0,0 +1,92 @@
+/**
+ * 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.ojelectronics.internal.models.userprofile;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Model for signing sin
+ *
+ * @author Christian Kittel - Initial contribution
+ */
+@NonNullByDefault
+public class PostSignInQueryModel {
+
+ @SerializedName("APIKEY")
+ public String apiKey = "";
+
+ public String userName = "";
+
+ public String password = "";
+
+ public int customerId;
+
+ public int clientSWVersion;
+
+ /**
+ * Add API-Key
+ *
+ * @param apiKey API-Key
+ * @return Model
+ */
+ public PostSignInQueryModel withApiKey(String apiKey) {
+ this.apiKey = apiKey;
+ return this;
+ }
+
+ /**
+ * Add User-Name
+ *
+ * @param userName User-Name for API access
+ * @return Model
+ */
+ public PostSignInQueryModel withUserName(String userName) {
+ this.userName = userName;
+ return this;
+ }
+
+ /**
+ * Add Password
+ *
+ * @param password Password for API access
+ * @return Model
+ */
+ public PostSignInQueryModel withPassword(String password) {
+ this.password = password;
+ return this;
+ }
+
+ /**
+ * Add customer ID
+ *
+ * @param customerId Customer Id
+ * @return Model
+ */
+ public PostSignInQueryModel withCustomerId(int customerId) {
+ this.customerId = customerId;
+ return this;
+ }
+
+ /**
+ * Add Software Version
+ *
+ * @param clientSWVersion Software Version
+ * @return Model
+ */
+ public PostSignInQueryModel withClientSWVersion(int clientSWVersion) {
+ this.clientSWVersion = clientSWVersion;
+ return this;
+ }
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInResponseModel.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInResponseModel.java
new file mode 100644
index 0000000000000..334a5791c0af4
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/models/userprofile/PostSignInResponseModel.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.ojelectronics.internal.models.userprofile;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Response-Model after signing in
+ *
+ * @author Christian Kittel - Initial Contribution
+ */
+@NonNullByDefault
+public class PostSignInResponseModel {
+
+ public String sessionId = "";
+
+ public String userName = "";
+
+ public int errorCode;
+
+ public PostSignInResponseModel withSessionId(String sessionId) {
+ this.sessionId = sessionId;
+ return this;
+ }
+
+ public PostSignInResponseModel withUserName(String userName) {
+ this.userName = userName;
+ return this;
+ }
+
+ public PostSignInResponseModel withErrorCode(int errorCode) {
+ this.errorCode = errorCode;
+ return this;
+ }
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshGroupContentService.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshGroupContentService.java
new file mode 100644
index 0000000000000..2cd0ade35e536
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshGroupContentService.java
@@ -0,0 +1,58 @@
+/**
+ * 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.ojelectronics.internal.services;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.openhab.binding.ojelectronics.internal.ThermostatHandler;
+import org.openhab.binding.ojelectronics.internal.models.groups.GroupContent;
+import org.openhab.binding.ojelectronics.internal.models.groups.Thermostat;
+
+/**
+ * Refreshes values of {@link ThermostatHandler}
+ *
+ * @author Christian Kittel - Initial Contribution
+ */
+@NonNullByDefault
+public class RefreshGroupContentService {
+
+ private final List groupContentList;
+ private List things;
+
+ /**
+ * Creates a new instance of {@link RefreshGroupContentService}
+ *
+ * @param groupContents {@link GroupContent}
+ * @param things Things
+ */
+ public RefreshGroupContentService(List groupContents, List things) {
+ this.groupContentList = groupContents;
+ this.things = things;
+ }
+
+ /**
+ * Handles the changes to all things.
+ */
+ public void handle() {
+ groupContentList.stream().flatMap(entry -> entry.thermostats.stream()).forEach(this::handleThermostat);
+ }
+
+ private void handleThermostat(Thermostat thermostat) {
+ things.stream().filter(thing -> thing.getHandler() instanceof ThermostatHandler)
+ .map(thing -> (ThermostatHandler) thing.getHandler())
+ .filter(thingHandler -> thingHandler.getSerialNumber().equals(thermostat.serialNumber))
+ .forEach(thingHandler -> thingHandler.handleThermostatRefresh(thermostat));
+ }
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshService.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshService.java
new file mode 100644
index 0000000000000..fa489152a75b5
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/RefreshService.java
@@ -0,0 +1,166 @@
+/**
+ * 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.ojelectronics.internal.services;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConfiguration;
+import org.openhab.binding.ojelectronics.internal.models.groups.GroupContentResponseModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * Handles the refreshing of the devices of a session
+ *
+ * @author Christian Kittel - Initial Contribution
+ */
+@NonNullByDefault
+public final class RefreshService implements AutoCloseable {
+
+ private final OJElectronicsBridgeConfiguration config;
+ private final Logger logger = LoggerFactory.getLogger(RefreshService.class);
+ private final HttpClient httpClient;
+ private final Gson gson = createGson();
+
+ private final ScheduledExecutorService schedulerService;
+
+ private @Nullable Runnable connectionLost;
+ private @Nullable BiConsumer<@Nullable GroupContentResponseModel, @Nullable String> refreshDone;
+ private @Nullable ScheduledFuture> scheduler;
+ private @Nullable Runnable unauthorized;
+ private @Nullable String sessionId;
+ private static boolean destroyed = false;
+
+ /**
+ * Creates a new instance of {@link RefreshService}
+ *
+ * @param config Configuration of the bridge
+ * @param httpClient HTTP client
+ */
+ public RefreshService(OJElectronicsBridgeConfiguration config, HttpClient httpClient,
+ ScheduledExecutorService schedulerService) {
+ this.config = config;
+ this.httpClient = httpClient;
+ this.schedulerService = schedulerService;
+ }
+
+ /**
+ * Starts refreshing all thing values
+ *
+ * @param sessionId Session-Id
+ * @param refreshDone This method is called if refreshing is done.
+ * @param connectionLosed This method is called if no connection could established.
+ * @param unauthorized This method is called if the result is unauthorized.
+ */
+ public void start(String sessionId, BiConsumer<@Nullable GroupContentResponseModel, @Nullable String> refreshDone,
+ Runnable connectionLost, Runnable unauthorized) {
+ logger.trace("RefreshService.startService({})", sessionId);
+ this.connectionLost = connectionLost;
+ this.refreshDone = refreshDone;
+ this.unauthorized = unauthorized;
+ this.sessionId = sessionId;
+ long refreshTime = config.refreshDelayInSeconds;
+ scheduler = schedulerService.scheduleWithFixedDelay(this::refresh, refreshTime, refreshTime, TimeUnit.SECONDS);
+ refresh();
+ destroyed = false;
+ }
+
+ /**
+ * Stops refreshing.
+ */
+ public void stop() {
+ destroyed = true;
+ final ScheduledFuture> scheduler = this.scheduler;
+ if (scheduler != null) {
+ scheduler.cancel(false);
+ }
+ this.scheduler = null;
+ }
+
+ private Gson createGson() {
+ return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting()
+ .setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create();
+ }
+
+ private void refresh() {
+ final String sessionId = this.sessionId;
+ if (sessionId == null) {
+ handleConnectionLost();
+ }
+ final Runnable unauthorized = this.unauthorized;
+ createRequest().send(new BufferingResponseListener() {
+ @Override
+ public void onComplete(@Nullable Result result) {
+ if (!destroyed) {
+ if (result == null || result.isFailed()) {
+ handleConnectionLost();
+ } else if (result.getResponse().getStatus() == HttpStatus.FORBIDDEN_403) {
+ if (unauthorized != null) {
+ unauthorized.run();
+ }
+ } else {
+ handleRefreshDone(getContentAsString());
+ }
+ }
+ }
+ });
+ }
+
+ private Request createRequest() {
+ Request request = httpClient.newRequest(config.apiUrl + "/Group/GroupContents").param("sessionid", sessionId)
+ .param("apiKey", config.apiKey).method(HttpMethod.GET);
+ return request;
+ }
+
+ private void handleRefreshDone(String responseBody) {
+ BiConsumer<@Nullable GroupContentResponseModel, @Nullable String> refreshDone = this.refreshDone;
+ if (refreshDone != null) {
+ logger.trace("refresh {}", responseBody);
+ try {
+ GroupContentResponseModel content = gson.fromJson(responseBody, GroupContentResponseModel.class);
+ refreshDone.accept(content, null);
+ } catch (JsonSyntaxException exception) {
+ logger.debug("Error mapping Result to model", exception);
+ refreshDone.accept(null, exception.getMessage());
+ }
+ }
+ }
+
+ private void handleConnectionLost() {
+ final Runnable connectionLost = this.connectionLost;
+ if (connectionLost != null) {
+ connectionLost.run();
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ stop();
+ }
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/SignInService.java b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/SignInService.java
new file mode 100644
index 0000000000000..8897fc8d416bf
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/java/org/openhab/binding/ojelectronics/internal/services/SignInService.java
@@ -0,0 +1,99 @@
+/**
+ * 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.ojelectronics.internal.services;
+
+import java.util.function.Consumer;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.openhab.binding.ojelectronics.internal.config.OJElectronicsBridgeConfiguration;
+import org.openhab.binding.ojelectronics.internal.models.userprofile.PostSignInQueryModel;
+import org.openhab.binding.ojelectronics.internal.models.userprofile.PostSignInResponseModel;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * Handles the sign in process.
+ *
+ * @author Christian Kittel - Initial Contribution
+ */
+@NonNullByDefault
+public class SignInService {
+
+ private final Gson gson = createGson();
+
+ private final HttpClient httpClient;
+ private final OJElectronicsBridgeConfiguration config;
+
+ /**
+ * Creates a new instance of {@link SignInService}
+ *
+ * @param config configuration {@link OJElectronicsBridgeConfiguration}
+ * @param httpClient HTTP client
+ */
+ public SignInService(OJElectronicsBridgeConfiguration config, HttpClient httpClient) {
+ this.config = config;
+ this.httpClient = httpClient;
+ }
+
+ /**
+ * Signing in
+ *
+ * @param signInDone This method is called if sign in process was successful.
+ * @param connectionLosed This method is called if no connection could established.
+ * @param unauthorized This method is called if the result is unauthorized.
+ */
+ public void signIn(Consumer signInDone, Runnable connectionLosed, Runnable unauthorized) {
+ Request request = httpClient.POST(config.apiUrl + "/UserProfile/SignIn")
+ .header(HttpHeader.CONTENT_TYPE, "application/json")
+ .content(new StringContentProvider(gson.toJson(getPostSignInQueryModel())));
+
+ request.send(new BufferingResponseListener() {
+ @Override
+ public void onComplete(@Nullable Result result) {
+ if (result == null || result.isFailed()) {
+ connectionLosed.run();
+ return;
+ }
+ if (result.getResponse().getStatus() != 200) {
+ unauthorized.run();
+ return;
+ }
+ PostSignInResponseModel signInModel = gson.fromJson(getContentAsString(),
+ PostSignInResponseModel.class);
+ if (signInModel.errorCode != 0 || signInModel.sessionId.equals("")) {
+ unauthorized.run();
+ return;
+ }
+ signInDone.accept(signInModel.sessionId);
+ }
+ });
+ }
+
+ private Gson createGson() {
+ return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create();
+ }
+
+ private PostSignInQueryModel getPostSignInQueryModel() {
+ return new PostSignInQueryModel().withApiKey(config.apiKey).withClientSWVersion(config.softwareVersion)
+ .withCustomerId(config.customerId).withUserName(config.userName).withPassword(config.password);
+ }
+}
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.ojelectronics/src/main/resources/ESH-INF/binding/binding.xml
new file mode 100644
index 0000000000000..4bcdb5112e9ae
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/resources/ESH-INF/binding/binding.xml
@@ -0,0 +1,8 @@
+
+
+ OJElectronics Binding
+ This is the binding for OJElectronics.
+ Christian Kittel
+
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/resources/ESH-INF/i18n/ojelectronics_de_DE.properties b/bundles/org.openhab.binding.ojelectronics/src/main/resources/ESH-INF/i18n/ojelectronics_de_DE.properties
new file mode 100644
index 0000000000000..f39dae1806f73
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/resources/ESH-INF/i18n/ojelectronics_de_DE.properties
@@ -0,0 +1,38 @@
+# binding
+binding.ojelectronics.name = Binding für OJElectronics
+binding.ojelectronics.description = Binding für OJElectronics.
+
+# thing types
+thing-type.ojelectronics.owd5.label = OWD5/MWD5 Thermostate
+thing-type.ojelectronics.owd5.description = OWD5/MWD5 Thermostate
+thing-type.ojelectronics.ojcloud.label = OJ Electronics Cloud
+thing-type.ojelectronics.ojcloud.description = Zugriff auf alle OJ Electronic Geräte.
+
+# thing type config description
+thing-type.config.ojelectronics.ojcloud.userName.label = Nutzername
+thing-type.config.ojelectronics.ojcloud.userName.description = Nutzername für den Zugriff auf die Cloud.
+thing-type.config.ojelectronics.ojcloud.password.label = Passwort
+thing-type.config.ojelectronics.ojcloud.password.description = Passwort für den Zugriff auf die Cloud.
+thing-type.config.ojelectronics.ojcloud.apiKey.label = API-Key
+thing-type.config.ojelectronics.ojcloud.apiKey.description = API-Key von deinem Händler
+
+# channel types
+channel-type.ojelectronics.floorTemperature.label = Bodentemperatur
+channel-type.ojelectronics.roomTemperature.label = Zimmertemperatur
+channel-type.ojelectronics.groupName.label = Gruppenname
+channel-type.ojelectronics.online.label = Ist Online
+channel-type.ojelectronics.heating.label = Heizt
+channel-type.ojelectronics.thermostatName.label = Name des Thermostats
+channel-type.ojelectronics.regulationMode.label = Regelungsmodus
+channel-type.ojelectronics.regulationMode.state.option.auto = Automatisch
+channel-type.ojelectronics.regulationMode.state.option.comfort = Komfort
+channel-type.ojelectronics.regulationMode.state.option.manual = Manuell
+channel-type.ojelectronics.regulationMode.state.option.vacation = Urlaub
+channel-type.ojelectronics.regulationMode.state.option.frostProtection = Frostschutz
+channel-type.ojelectronics.regulationMode.state.option.boost = Boost
+channel-type.ojelectronics.regulationMode.state.option.eco = Spar
+channel-type.ojelectronics.comfortSetpoint.label = Komfort-Sollwert
+channel-type.ojelectronics.comfortEndTime.label = Komfort-Endzeit
+channel-type.ojelectronics.boostEndTime.label = Boost Endzeit
+channel-type.ojelectronics.manualSetpoint.label = manuelle Endzeit
+channel-type.ojelectronics.vacationEnabled.label = Urlausbmodus aktiviert
diff --git a/bundles/org.openhab.binding.ojelectronics/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.ojelectronics/src/main/resources/ESH-INF/thing/thing-types.xml
new file mode 100644
index 0000000000000..f253b3177b8f1
--- /dev/null
+++ b/bundles/org.openhab.binding.ojelectronics/src/main/resources/ESH-INF/thing/thing-types.xml
@@ -0,0 +1,161 @@
+
+
+
+
+
+ Access to all OJ Electronic devices.
+
+
+
+ User Name for access cloud service.
+
+
+
+ Password for access cloud service.
+ password
+
+
+
+ API-Key from your local distributor
+
+
+
+ URL to cloud API-service.
+ url
+ true
+ https://OWD5-OJ001-App.ojelectronics.com/api
+
+
+
+ Refresh delay in seconds.
+ true
+ 30
+
+
+
+ Customer ID
+ true
+ 1
+
+
+
+ Software Version
+ true
+ 1060
+
+
+
+
+
+
+
+
+ OWD5/MWD5 Thermostat
+ RadiatorControl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OJ Electronics
+
+
+
+
+ Serial number of the thermostat. You can find the serial number in the app or on the thermostat itself.
+
+
+
+
+ Number:Temperature
+
+ Temperature
+
+
+
+ String
+
+
+
+
+ Number
+
+
+
+
+ Contact
+
+
+
+
+ Contact
+
+
+
+
+ Number:Temperature
+
+ Temperature
+
+
+
+ String
+
+
+
+
+ String
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Number:Temperature
+
+ Temperature
+
+
+
+ DateTime
+
+
+
+
+ DateTime
+
+
+
+
+ Number:Temperature
+
+ Temperature
+
+
+
+ Switch
+
+
+
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 96090ffaacecf..6586283452a17 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -183,6 +183,7 @@
org.openhab.binding.ntp
org.openhab.binding.nuki
org.openhab.binding.oceanic
+ org.openhab.binding.ojelectronics
org.openhab.binding.omnikinverter
org.openhab.binding.onebusaway
org.openhab.binding.onewiregpio