diff --git a/bundles/org.openhab.binding.myenergi/pom.xml b/bundles/org.openhab.binding.myenergi/pom.xml
index 96e950a7c393d..04964788621cc 100644
--- a/bundles/org.openhab.binding.myenergi/pom.xml
+++ b/bundles/org.openhab.binding.myenergi/pom.xml
@@ -7,7 +7,7 @@
org.openhab.addons.bundlesorg.openhab.addons.reactor.bundles
- 3.1.0-SNAPSHOT
+ 3.2.0-SNAPSHOTorg.openhab.binding.myenergi
diff --git a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiApiClient.java b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiApiClient.java
index 32c0022978dd1..009aa8419ea0b 100644
--- a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiApiClient.java
+++ b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiApiClient.java
@@ -93,12 +93,12 @@ public void setHttpClientFactory(@Nullable HttpClientFactory httpClientFactory)
/**
* Sets the credentials (username/password) to be used for API calls.
*
- * @param username the username to be used.
- * @param password the password to be used.
+ * @param hubSerialNumber the serial number of the myenergi hub
+ * @param password the password for this hub in the myenergi mobile app.
* @throws MyEnergiApiException
*/
- public void initialize(final String username, final String password) throws ApiException {
- this.username = username;
+ public void initialize(final String hubSerialNumber, final String password) throws ApiException {
+ this.username = hubSerialNumber;
this.password = password;
HttpClientFactory factory = httpClientFactory;
if (factory == null) {
@@ -113,17 +113,17 @@ public void initialize(final String username, final String password) throws ApiE
AuthenticationStore auth = client.getAuthenticationStore();
auth.clearAuthentications();
auth.clearAuthenticationResults();
- if (host.equals("")) {
- host = "s" + username.charAt(username.length() - 1) + ".myenergi.net";
+ if ("".equals(host)) {
+ host = new MyEnergiGetHostFromDirector().getHostName(client, hubSerialNumber);
}
try {
URL baseURL = new URL("https", host, "/");
logger.debug("API base URL: {}", baseURL.toString());
client.getAuthenticationStore().addAuthentication(
- new DigestAuthentication(baseURL.toURI(), Authentication.ANY_REALM, username, password));
+ new DigestAuthentication(baseURL.toURI(), Authentication.ANY_REALM, hubSerialNumber, password));
this.baseURL = baseURL;
- logger.debug("Digest authentication added: {}", username);
+ logger.debug("Digest authentication added: {}", hubSerialNumber);
if (!client.isStarted()) {
client.start();
}
diff --git a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiBridgeConfiguration.java b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiBridgeConfiguration.java
index 044576d79795a..5bdbadc8372a9 100644
--- a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiBridgeConfiguration.java
+++ b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiBridgeConfiguration.java
@@ -22,7 +22,7 @@
@NonNullByDefault
public class MyEnergiBridgeConfiguration {
- public String username = "";
+ public String hubSerialNumber = "";
public String password = "";
public int refreshInterval = 24; // 24 hours
diff --git a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiGetHostFromDirector.java b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiGetHostFromDirector.java
new file mode 100644
index 0000000000000..3383c3af1b2ec
--- /dev/null
+++ b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/MyEnergiGetHostFromDirector.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2010-2021 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.myenergi.internal;
+
+import java.net.URL;
+
+import javax.validation.constraints.NotNull;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Authentication;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.DigestAuthentication;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpMethod;
+import org.openhab.binding.myenergi.internal.exception.ApiException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link MyEnergiGetHostFromDirector} is a helper class to get the hostname on
+ * myenergi.net for the the myenergi API.
+ * It finds the server for a given hub serial number
+ *
+ * @author Volkmar Nissen - Initial contribution
+ */
+@NonNullByDefault
+public class MyEnergiGetHostFromDirector {
+ private static final int SLEEP_BEFORE_REINIT_MS = 3000;
+ public static final String MY_ENERGI_RESPONSE_FIELD = "X_MYENERGI-asn";
+
+ private final Logger logger = LoggerFactory.getLogger(MyEnergiGetHostFromDirector.class);
+
+ /**
+ * Finds the server for a given hub serial number
+ *
+ * @param httpClientFactory the client to be used.
+ * @throws ApiException
+ */
+
+ public String getHostName(@NotNull HttpClient httpClient, @NotNull String hubSerialNumber) throws ApiException {
+ String directorHostname = "director.myenergi.net";
+ try {
+ URL directorURL = new URL("https", directorHostname, "/");
+ // No password is needed at director.myenergie.net
+ httpClient.getAuthenticationStore().addAuthentication(
+ new DigestAuthentication(directorURL.toURI(), Authentication.ANY_REALM, hubSerialNumber, ""));
+ int innerLoop = 0;
+ while ((innerLoop < 2)) {
+ innerLoop++;
+ if (!httpClient.isStarted()) {
+ httpClient.start();
+ }
+ Request request = httpClient.newRequest(directorURL.toString()).method(HttpMethod.GET);
+ logger.trace("sending get hostname request: {}", innerLoop);
+ ContentResponse response = request.send();
+ String hostname = response.getHeaders().get(MY_ENERGI_RESPONSE_FIELD);
+ if (null != hostname) {
+ return hostname;
+ }
+
+ if (logger.isTraceEnabled()) {
+ for (HttpField field : response.getHeaders()) {
+ logger.trace("HTTP header: {}", field.toString());
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new ApiException("Exception caught during API execution", e);
+ }
+ // This code will never be executed, because the ApiException will be thrown earlier
+ return "";
+ }
+}
diff --git a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/handler/MyEnergiBridgeHandler.java b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/handler/MyEnergiBridgeHandler.java
index bf08b71a6d283..258192da9aa7a 100644
--- a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/handler/MyEnergiBridgeHandler.java
+++ b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/handler/MyEnergiBridgeHandler.java
@@ -68,9 +68,9 @@ public void initialize() {
logger.debug("Initializing MyEnergiBridgeHandler");
MyEnergiBridgeConfiguration config = getConfigAs(MyEnergiBridgeConfiguration.class);
- if (config.username.isEmpty() || config.password.isEmpty()) {
+ if (config.hubSerialNumber.isEmpty() || config.password.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "@text/offline.conf-error-missing-username-or-password");
+ "@text/offline.conf-error-missing-hubSerialNumber-or-password");
return;
}
if (config.refreshInterval < 1) {
@@ -81,8 +81,8 @@ public void initialize() {
updateStatus(ThingStatus.UNKNOWN);
try {
- logger.debug("Login to MyEnergi API with username: {}", config.username);
- apiClient.initialize(config.username, config.password);
+ logger.debug("Login to MyEnergi API with hubSerialNumber: {}", config.hubSerialNumber);
+ apiClient.initialize(config.hubSerialNumber, config.password);
apiClient.updateTopologyCache();
logger.debug("Cache update successful, setting bridge status to ONLINE");
updateStatus(ThingStatus.ONLINE);
diff --git a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/util/ZappiBoostMode.java b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/util/ZappiBoostMode.java
index d7c7047a61a32..e75e490c20b36 100644
--- a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/util/ZappiBoostMode.java
+++ b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/util/ZappiBoostMode.java
@@ -12,12 +12,15 @@
*/
package org.openhab.binding.myenergi.internal.util;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* The {@link ZappiBoostMode} enumeration is used to model the various Zappi boost charging modes.
*
* @author Rene Scherer - Initial contribution
*
*/
+@NonNullByDefault
public enum ZappiBoostMode {
// stops the current boost cycle
STOP(2),
diff --git a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/util/ZappiChargingMode.java b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/util/ZappiChargingMode.java
index 026fb02bbcf7a..180e1ff263562 100644
--- a/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/util/ZappiChargingMode.java
+++ b/bundles/org.openhab.binding.myenergi/src/main/java/org/openhab/binding/myenergi/internal/util/ZappiChargingMode.java
@@ -12,12 +12,15 @@
*/
package org.openhab.binding.myenergi.internal.util;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* The {@link ZappiChargingMode} enumeration is used to model the various Zappi charging modes.
*
* @author Rene Scherer - Initial contribution
*
*/
+@NonNullByDefault
public enum ZappiChargingMode {
BOOST(0),
FAST(1),
diff --git a/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/binding/binding.xml
index 2f05181988ed3..1c27d8096c395 100644
--- a/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/binding/binding.xml
+++ b/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/binding/binding.xml
@@ -4,6 +4,10 @@
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
myenergi Binding
- This is the binding for myenergi.
+ The myenergi binding connects to the myenergi hub and interacts with the following myenergi products:
+ zappi wallbox,
+ harvi remote device for CT sensors,
+ eddi control device for huge power consumers like heat pumps
+ .
diff --git a/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/i18n/myenergi.properties b/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/i18n/myenergi.properties
index bf429264bce89..3b3388b0b3336 100644
--- a/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/i18n/myenergi.properties
+++ b/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/i18n/myenergi.properties
@@ -1,5 +1,5 @@
# thing status description
offline.conf-error-invalid-refresh-intervals = Invalid refresh intervals
-offline.conf-error-missing-username-or-password = Missing username or password
+offline.conf-error-missing-hubSerialNumber-or-password = Missing hubSerialNumber or password
offline.conf-error-authentication = Authentication Error (Invalid API key or account number)
offline.comm-error-general = General communication error with API
diff --git a/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/thing/things.xml b/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/thing/things.xml
index 172188e8ac3d9..54e709c04ed96 100644
--- a/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/thing/things.xml
+++ b/bundles/org.openhab.binding.myenergi/src/main/resources/OH-INF/thing/things.xml
@@ -11,9 +11,9 @@
The MyEnergi Cloud API Bridge
-
-
- The username to access the MyEnergi API (Hub Serial Number)
+
+
+ The Hub Serial Number to access the MyEnergi API ()
@@ -39,12 +39,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
@@ -86,16 +86,10 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
@@ -123,17 +117,29 @@
-
+ String
-
- The name of the thing
-
+
+ CT Sensors have the following usages
+
+
+
+
+
+
+
+
+
+
+
+
Number:Power
- The measured grid power
+ Grid power property shows the import and export power. It is used to determine if surplus power is
+ available.Energy
@@ -141,7 +147,7 @@
Number:Power
- The generated power
+ Power from the generator (if available)Energy
@@ -157,23 +163,37 @@
Number:Power
- The total consumed power (grid + generated)
+ Power consumed by the house (if available)Energy
-
+ Number:Power
- The measured power at the clamp
+ The measured power at the CT L1Energy
+
+ Number:Power
+
+ The measured power at the CT L2
+ Energy
+
+
+
+ Number:Power
+
+ The measured power at the CT L3
+ Energy
+
+ Number
- The phase this clamp is attached to
+ The phase this CT is attached toEnergy
@@ -188,7 +208,7 @@
Number:ElectricPotential
- The measured voltage
+ Supply voltage to the unitEnergy
@@ -196,7 +216,7 @@
Number:Frequency
- The measured frequency
+ Grid frequencyEnergy
@@ -204,14 +224,14 @@
Number:Energy
- The charge energy
+ Energy supplied to the EV during current charge sessionNumber
- The current locking mode of the device as bitmap
+ The current locking mode of the device as bitmap.Bit 0: Locked Now, Bit 1: Lock when plugged in, Bit 2: Lock when unplugged, Bit 3: Charge when locked, Bit 4: Charge Session Allowed (Even if locked)
diff --git a/bundles/org.openhab.binding.myenergi/src/test/java/org/openhab/binding/myenergi/internal/MyEnergiApiClientTest.java b/bundles/org.openhab.binding.myenergi/src/test/java/org/openhab/binding/myenergi/internal/MyEnergiApiClientTest.java
index c6acc461299f2..089db16d59bd2 100644
--- a/bundles/org.openhab.binding.myenergi/src/test/java/org/openhab/binding/myenergi/internal/MyEnergiApiClientTest.java
+++ b/bundles/org.openhab.binding.myenergi/src/test/java/org/openhab/binding/myenergi/internal/MyEnergiApiClientTest.java
@@ -25,6 +25,7 @@
import org.eclipse.jetty.client.HttpContentResponse;
import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.api.AuthenticationStore;
+import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.BeforeAll;
@@ -76,6 +77,7 @@ public HttpClient getCommonHttpClient() {
private static final int ZAPPI_SERIAL_NUMBER = 21287642;
private static MyEnergiApiClient api = new MyEnergiApiClient();
+ private static HttpFields responseFields = mock(HttpFields.class);
private static AuthenticationStore authenticationStore = mock(AuthenticationStore.class);
private static HttpClientFactory httpClientFactory = mock(HttpClientFactoryMock.class);
@@ -90,12 +92,15 @@ static void setUp() throws Exception {
when(httpClient.getAuthenticationStore()).thenReturn(authenticationStore);
when(httpClient.newRequest(anyString())).thenReturn(request);
when(httpClient.isStarted()).thenReturn(true);
+ // doNothing().when(httpClient).stop();
+
when(request.method(HttpMethod.GET)).thenReturn(request);
when(request.send()).thenReturn(response);
when(response.getStatus()).thenReturn(RESPONSE_200_STATUS);
when(response.getReason()).thenReturn(RESPONSE_200_REASON);
-
+ when(responseFields.get(MyEnergiGetHostFromDirector.MY_ENERGI_RESPONSE_FIELD)).thenReturn("SomeHost");
+ when(response.getHeaders()).thenReturn(responseFields);
api.setHttpClientFactory(httpClientFactory);
api.initialize(TEST_USERNAME, TEST_PASSWORD_VALID);
}
diff --git a/bundles/org.openhab.binding.myenergi/src/test/java/org/openhab/binding/myenergi/internal/MyEnergiGetHostFromDirectorTest.java b/bundles/org.openhab.binding.myenergi/src/test/java/org/openhab/binding/myenergi/internal/MyEnergiGetHostFromDirectorTest.java
new file mode 100644
index 0000000000000..598aa151cd29c
--- /dev/null
+++ b/bundles/org.openhab.binding.myenergi/src/test/java/org/openhab/binding/myenergi/internal/MyEnergiGetHostFromDirectorTest.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2010-2021 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.myenergi.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.openhab.core.io.net.http.HttpClientFactory;
+
+/**
+ * The {@link MyEnergiGetHostFromDirectorTest} is a test class for {@link MyEnergiGetHostFromDirector}.
+ *
+ * @author Volkmar Nissen - Initial contribution
+ */
+@NonNullByDefault
+class MyEnergiGetHostFromDirectorTest {
+
+ class HttpClientFactoryForTest implements HttpClientFactory {
+
+ @Override
+ public HttpClient createHttpClient(String consumerName) {
+ SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
+ sslContextFactory.setTrustAll(true); // you might want to think about this first
+ return new HttpClient(sslContextFactory);
+ }
+
+ @Override
+ public HttpClient getCommonHttpClient() {
+ // TODO Auto-generated method stub
+ return new HttpClient();
+ }
+ }
+
+ /**
+ * There is no password check at director.myenergi.net. So, the password can be left empty.
+ * Only the hub serial number must be an existing one. May be this changes in the future.
+ * This test uses Internet URL to director.myenergi.net. The access is not mocked
+ *
+ * @throws Exception
+ */
+ @Test
+ void testGetHostName() throws Exception {
+ HttpClient client = new HttpClientFactoryForTest()
+ .createHttpClient(MyEnergiGetHostFromDirectorTest.class.getSimpleName());
+ try {
+
+ client.start();
+ String hostName = new MyEnergiGetHostFromDirector().getHostName(client, "12215753");
+ Assertions.assertTrue(hostName.contains("myenergi"));
+ } catch (Exception e) {
+ Assertions.fail("Exception caught" + e.getMessage());
+ } finally {
+ client.stop();
+ }
+ }
+}