Skip to content

Commit

Permalink
Merge pull request #16 from volkmarnissen/myenergi_binding1
Browse files Browse the repository at this point in the history
[12251] [myenergi] Improved texts things.xml
  • Loading branch information
renescherer committed Feb 16, 2022
2 parents b9f4fbe + 8019916 commit 72d547e
Show file tree
Hide file tree
Showing 12 changed files with 238 additions and 50 deletions.
2 changes: 1 addition & 1 deletion bundles/org.openhab.binding.myenergi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
</parent>

<artifactId>org.openhab.binding.myenergi</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
@NonNullByDefault
public class MyEnergiBridgeConfiguration {

public String username = "";
public String hubSerialNumber = "";
public String password = "";

public int refreshInterval = 24; // 24 hours
Expand Down
Original file line number Diff line number Diff line change
@@ -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 "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">

<name>myenergi Binding</name>
<description>This is the binding for myenergi.</description>
<description>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
.</description>

</binding:binding>
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
<description>The MyEnergi Cloud API Bridge</description>

<config-description>
<parameter name="username" type="text" required="true">
<label>Username (Hub Serial Number)</label>
<description>The username to access the MyEnergi API (Hub Serial Number)</description>
<parameter name="hubSerialNumber" type="text" required="true">
<label>Hub Serial Number.</label>
<description>The Hub Serial Number to access the MyEnergi API ()</description>
</parameter>
<parameter name="password" type="text" required="true">
<label>Password</label>
Expand All @@ -39,12 +39,12 @@

<channels>
<channel id="lastUpdatedTime" typeId="lastUpdatedTimeType"/>
<channel id="clampName1" typeId="nameType"/>
<channel id="clampName2" typeId="nameType"/>
<channel id="clampName3" typeId="nameType"/>
<channel id="clampPower1" typeId="clampPowerType"/>
<channel id="clampPower2" typeId="clampPowerType"/>
<channel id="clampPower3" typeId="clampPowerType"/>
<channel id="clampName1" typeId="CTType"/>
<channel id="clampName2" typeId="CTType"/>
<channel id="clampName3" typeId="CTType"/>
<channel id="clampPower1" typeId="clampPowerTypeL1"/>
<channel id="clampPower2" typeId="clampPowerTypeL2"/>
<channel id="clampPower3" typeId="clampPowerTypeL3"/>
<channel id="clampPhase1" typeId="phaseType"/>
<channel id="clampPhase2" typeId="phaseType"/>
<channel id="clampPhase3" typeId="phaseType"/>
Expand Down Expand Up @@ -86,16 +86,10 @@
<channel id="smartBoostCharge" typeId="chargeType"/>
<channel id="timedBoostTime" typeId="boostEndTimeType"/>
<channel id="timedBoostCharge" typeId="chargeType"/>
<channel id="clampName1" typeId="nameType"/>
<channel id="clampName2" typeId="nameType"/>
<channel id="clampName3" typeId="nameType"/>
<channel id="clampName4" typeId="nameType"/>
<channel id="clampName5" typeId="nameType"/>
<channel id="clampName6" typeId="nameType"/>
<channel id="clampPower1" typeId="clampPowerType"/>
<channel id="clampPower2" typeId="clampPowerType"/>
<channel id="clampPower3" typeId="clampPowerType"/>
<channel id="clampPower4" typeId="clampPowerType"/>
<channel id="clampName4" typeId="CTTypeL1"/>
<channel id="clampName5" typeId="CTTypeL2"/>
<channel id="clampName6" typeId="CTTypeL3"/>
<channel id="clampPower4" typeId="clampPowerType"/>
<channel id="clampPower5" typeId="clampPowerType"/>
<channel id="clampPower6" typeId="clampPowerType"/>
</channels>
Expand Down Expand Up @@ -123,25 +117,37 @@
<state readOnly="true" pattern="%1$tF %1$tR"/>
</channel-type>

<channel-type id="nameType">
<channel-type id="CTType">
<item-type>String</item-type>
<label>Name</label>
<description>The name of the thing</description>
<state readOnly="true" pattern="%s"/>
<label>CT Type</label>
<description>CT Sensors have the following usages</description>
<state readOnly="true" pattern="%s">
<options>
<option value="None">None</option>
<option value="Grid">Grid</option>
<option value="Generation Only">Generation Only</option>
<option value="Storage Only">Storage Only</option>
<option value="Gen &amp; Battery">Gen &amp; Battery</option>
<option value="Monitor">Monitor</option>
<option value="AC Battery">AC Battery</option>
</options>
</state>

</channel-type>

<channel-type id="gridPowerType">
<item-type>Number:Power</item-type>
<label>Grid Power</label>
<description>The measured grid power</description>
<description>Grid power property shows the import and export power. It is used to determine if surplus power is
available.</description>
<category>Energy</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>

<channel-type id="generatedPowerType">
<item-type>Number:Power</item-type>
<label>Generated Power</label>
<description>The generated power</description>
<description>Power from the generator (if available)</description>
<category>Energy</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
Expand All @@ -157,23 +163,37 @@
<channel-type id="consumedPowerType">
<item-type>Number:Power</item-type>
<label>Consumed Power</label>
<description>The total consumed power (grid + generated)</description>
<description>Power consumed by the house (if available)</description>
<category>Energy</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>

<channel-type id="clampPowerType">
<channel-type id="clampPowerTypeL1">
<item-type>Number:Power</item-type>
<label>Clamp Power</label>
<description>The measured power at the clamp</description>
<description>The measured power at the CT L1</description>
<category>Energy</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="clampPowerTypeL2">
<item-type>Number:Power</item-type>
<label>Clamp Power</label>
<description>The measured power at the CT L2</description>
<category>Energy</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="clampPowerTypeL3">
<item-type>Number:Power</item-type>
<label>Clamp Power</label>
<description>The measured power at the CT L3</description>
<category>Energy</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>

<channel-type id="phaseType">
<item-type>Number</item-type>
<label>Phase</label>
<description>The phase this clamp is attached to</description>
<description>The phase this CT is attached to</description>
<category>Energy</category>
<state readOnly="true" pattern="%d"/>
</channel-type>
Expand All @@ -188,30 +208,30 @@
<channel-type id="voltageType">
<item-type>Number:ElectricPotential</item-type>
<label>Voltage</label>
<description>The measured voltage</description>
<description>Supply voltage to the unit</description>
<category>Energy</category>
<state readOnly="true" pattern="%d %unit%"/>
</channel-type>

<channel-type id="frequencyType">
<item-type>Number:Frequency</item-type>
<label>Frequency</label>
<description>The measured frequency</description>
<description>Grid frequency</description>
<category>Energy</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>

<channel-type id="chargeType">
<item-type>Number:Energy</item-type>
<label>Charge</label>
<description>The charge energy</description>
<description>Energy supplied to the EV during current charge session</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>

<channel-type id="lockingModeType">
<item-type>Number</item-type>
<label>Locking Mode</label>
<description>The current locking mode of the device as bitmap</description>
<description>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)</description>
<state readOnly="true" pattern="%d"/>
</channel-type>

Expand Down
Loading

0 comments on commit 72d547e

Please sign in to comment.