public static final long WEB_REQUEST_INTERVAL = TimeUnit.SECONDS.toMillis(5);
public static final int WEB_REQUEST_QUEUE_MAX_SIZE = 20;
+ // Status Keys
+ public static final String STATUS_INVALID_SOLAR_ID = "@text/status.invalid.solarId";
+ public static final String STATUS_INVALID_TOKEN = "@text/status.invalid.token";
+ public static final String STATUS_UNKNOWN_ERROR = "@text/status.unknown.error";
+ public static final String STATUS_INVALID_TOKEN_LENGTH = "@text/status.invalid.token.length";
+ public static final String STATUS_INVALID_API_KEY_LENGTH = "@text/status.invalid.api.key.length";
+ public static final String STATUS_REQUEST_LIMIT_EXCEEDED = "@text/status.request.limit.exceeded [\""
+ + WEB_REQUEST_PUBLIC_API_DAY_LIMIT + "\"]";
+ public static final String STATUS_NO_METER_CONFIGURED = "@text/status.no.meter.configured";
+ public static final String STATUS_WAITING_FOR_LOGIN = "@text/status.waiting.for.login";
+
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_GENERIC);
}
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
-import org.openhab.binding.solaredge.internal.handler.GenericSolarEdgeHandler;
+import org.openhab.binding.solaredge.internal.handler.SolarEdgeGenericHandler;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_GENERIC)) {
- return new GenericSolarEdgeHandler(thing, httpClient);
+ return new SolarEdgeGenericHandler(thing, httpClient);
} else {
logger.warn("Unsupported Thing-Type: {}", thingTypeUID.getAsString());
}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.solaredge.internal.callback;
-
-import static org.openhab.binding.solaredge.internal.SolarEdgeBindingConstants.*;
-
-import java.net.CookieStore;
-import java.net.HttpCookie;
-import java.net.SocketTimeoutException;
-import java.net.URI;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-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.Response;
-import org.eclipse.jetty.client.util.BufferingResponseListener;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.HttpStatus.Code;
-import org.openhab.binding.solaredge.internal.command.SolarEdgeCommand;
-import org.openhab.binding.solaredge.internal.config.SolarEdgeConfiguration;
-import org.openhab.binding.solaredge.internal.connector.CommunicationStatus;
-import org.openhab.binding.solaredge.internal.connector.StatusUpdateListener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonSyntaxException;
-
-/**
- * base class for all commands. common logic should be implemented here
- *
- * @author Alexander Friese - initial contribution
- */
-@NonNullByDefault
-public abstract class AbstractCommandCallback extends BufferingResponseListener implements SolarEdgeCommand {
-
- /**
- * logger
- */
- protected final Logger logger = LoggerFactory.getLogger(AbstractCommandCallback.class);
-
- /**
- * the configuration
- */
- protected final SolarEdgeConfiguration config;
-
- /**
- * JSON deserializer
- */
- private final Gson gson;
-
- /**
- * status code of fulfilled request
- */
- private final CommunicationStatus communicationStatus;
-
- /**
- * listener to provide updates to the WebInterface class
- */
- private @Nullable StatusUpdateListener listener;
-
- /**
- * the constructor
- *
- * @param config
- */
- public AbstractCommandCallback(SolarEdgeConfiguration config) {
- this.communicationStatus = new CommunicationStatus();
- this.config = config;
- this.gson = new Gson();
- }
-
- /**
- * the constructor
- *
- * @param config
- */
- public AbstractCommandCallback(SolarEdgeConfiguration config, StatusUpdateListener listener) {
- this(config);
- this.listener = listener;
- }
-
- /**
- * Log request success
- */
- @Override
- public final void onSuccess(@Nullable Response response) {
- super.onSuccess(response);
- if (response != null) {
- communicationStatus.setHttpCode(HttpStatus.getCode(response.getStatus()));
- logger.debug("HTTP response {}", response.getStatus());
- }
- }
-
- /**
- * Log request failure
- */
- @Override
- public final void onFailure(@Nullable Response response, @Nullable Throwable failure) {
- super.onFailure(response, failure);
- if (failure != null) {
- logger.debug("Request failed: {}", failure.toString());
- communicationStatus.setError((Exception) failure);
-
- if (failure instanceof SocketTimeoutException || failure instanceof TimeoutException) {
- communicationStatus.setHttpCode(Code.REQUEST_TIMEOUT);
- } else if (failure instanceof UnknownHostException) {
- communicationStatus.setHttpCode(Code.BAD_GATEWAY);
- } else {
- communicationStatus.setHttpCode(Code.INTERNAL_SERVER_ERROR);
- }
- } else {
- logger.debug("Request failed");
- }
- }
-
- @Override
- public void onContent(@Nullable Response response, @Nullable ByteBuffer content) {
- super.onContent(response, content);
- logger.debug("received content, length: {}", getContentAsString().length());
- }
-
- @Override
- public void performAction(HttpClient asyncclient) {
- Request request = asyncclient.newRequest(getURL()).timeout(config.getAsyncTimeout(), TimeUnit.SECONDS);
-
- // add authentication data for every request. Handling this here makes it obsolete to implement for each and
- // every command
- if (config.isUsePrivateApi()) {
- // token cookie is only used by private API therefore this can be skipped when using public API
- CookieStore cookieStore = asyncclient.getCookieStore();
- HttpCookie c = new HttpCookie(PRIVATE_API_TOKEN_COOKIE_NAME, config.getTokenOrApiKey());
- c.setDomain(PRIVATE_API_TOKEN_COOKIE_DOMAIN);
- c.setPath(PRIVATE_API_TOKEN_COOKIE_PATH);
- cookieStore.add(URI.create(getURL()), c);
- } else {
- // this is only relevant when using public API
- request.param(PUBLIC_DATA_API_KEY_FIELD, config.getTokenOrApiKey());
-
- }
-
- prepareRequest(request).send(this);
- }
-
- /**
- * @return returns Http Status Code
- */
- public CommunicationStatus getCommunicationStatus() {
- return communicationStatus;
- }
-
- @Override
- public void updateListenerStatus() {
- if (listener != null) {
- listener.update(communicationStatus);
- }
- }
-
- /**
- * concrete implementation has to prepare the requests with additional parameters, etc
- *
- * @param requestToPrepare the request to prepare
- * @return prepared Request object
- */
- protected abstract Request prepareRequest(Request requestToPrepare);
-
- /**
- * concrete implementation has to provide the URL
- *
- * @return Url
- */
- protected abstract String getURL();
-
- @Override
- public final void setListener(StatusUpdateListener listener) {
- this.listener = listener;
- }
-
- /**
- * just a wrapper as fromJson could return null. This will avoid warnings as eclipse otherwise assumes unnecessary
- * null checks which are not unnecessary
- *
- * @param <T>
- * @param json
- * @param classOfT
- * @return
- * @throws JsonSyntaxException
- */
- protected <T> @Nullable T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
- return gson.fromJson(json, classOfT);
- }
-}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.solaredge.internal.command;
+
+import static org.openhab.binding.solaredge.internal.SolarEdgeBindingConstants.*;
+
+import java.net.CookieStore;
+import java.net.HttpCookie;
+import java.net.SocketTimeoutException;
+import java.net.URI;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+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.Response;
+import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpStatus.Code;
+import org.openhab.binding.solaredge.internal.config.SolarEdgeConfiguration;
+import org.openhab.binding.solaredge.internal.connector.CommunicationStatus;
+import org.openhab.binding.solaredge.internal.connector.StatusUpdateListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * base class for all commands. common logic should be implemented here
+ *
+ * @author Alexander Friese - initial contribution
+ */
+@NonNullByDefault
+public abstract class AbstractCommand extends BufferingResponseListener implements SolarEdgeCommand {
+
+ /**
+ * logger
+ */
+ protected final Logger logger = LoggerFactory.getLogger(AbstractCommand.class);
+
+ /**
+ * the configuration
+ */
+ protected final SolarEdgeConfiguration config;
+
+ /**
+ * JSON deserializer
+ */
+ private final Gson gson;
+
+ /**
+ * status code of fulfilled request
+ */
+ private final CommunicationStatus communicationStatus;
+
+ /**
+ * listener to provide updates to the WebInterface class
+ */
+ private final StatusUpdateListener listener;
+
+ /**
+ * the constructor
+ *
+ * @param config
+ * @param listener
+ *
+ */
+ public AbstractCommand(SolarEdgeConfiguration config, StatusUpdateListener listener) {
+ this.communicationStatus = new CommunicationStatus();
+ this.config = config;
+ this.listener = listener;
+ this.gson = new Gson();
+ }
+
+ /**
+ * Log request success
+ */
+ @Override
+ public final void onSuccess(@Nullable Response response) {
+ super.onSuccess(response);
+ if (response != null) {
+ communicationStatus.setHttpCode(HttpStatus.getCode(response.getStatus()));
+ logger.debug("HTTP response {}", response.getStatus());
+ }
+ }
+
+ /**
+ * Log request failure
+ */
+ @Override
+ public final void onFailure(@Nullable Response response, @Nullable Throwable failure) {
+ super.onFailure(response, failure);
+ if (failure != null) {
+ logger.debug("Request failed: {}", failure.toString());
+ communicationStatus.setError((Exception) failure);
+
+ if (failure instanceof SocketTimeoutException || failure instanceof TimeoutException) {
+ communicationStatus.setHttpCode(Code.REQUEST_TIMEOUT);
+ } else if (failure instanceof UnknownHostException) {
+ communicationStatus.setHttpCode(Code.BAD_GATEWAY);
+ } else {
+ communicationStatus.setHttpCode(Code.INTERNAL_SERVER_ERROR);
+ }
+ } else {
+ logger.debug("Request failed");
+ }
+ }
+
+ @Override
+ public void onContent(@Nullable Response response, @Nullable ByteBuffer content) {
+ super.onContent(response, content);
+ logger.debug("received content, length: {}", getContentAsString().length());
+ }
+
+ @Override
+ public void performAction(HttpClient asyncclient) {
+ Request request = asyncclient.newRequest(getURL()).timeout(config.getAsyncTimeout(), TimeUnit.SECONDS);
+
+ // add authentication data for every request. Handling this here makes it obsolete to implement for each and
+ // every command
+ if (config.isUsePrivateApi()) {
+ // token cookie is only used by private API therefore this can be skipped when using public API
+ CookieStore cookieStore = asyncclient.getCookieStore();
+ HttpCookie c = new HttpCookie(PRIVATE_API_TOKEN_COOKIE_NAME, config.getTokenOrApiKey());
+ c.setDomain(PRIVATE_API_TOKEN_COOKIE_DOMAIN);
+ c.setPath(PRIVATE_API_TOKEN_COOKIE_PATH);
+ cookieStore.add(URI.create(getURL()), c);
+ } else {
+ // this is only relevant when using public API
+ request.param(PUBLIC_DATA_API_KEY_FIELD, config.getTokenOrApiKey());
+ }
+
+ prepareRequest(request).send(this);
+ }
+
+ /**
+ * @return returns Http Status Code
+ */
+ public CommunicationStatus getCommunicationStatus() {
+ return communicationStatus;
+ }
+
+ /**
+ * updates status of the registered listener.
+ */
+ protected final void updateListenerStatus() {
+ try {
+ listener.update(communicationStatus);
+ } catch (Exception ex) {
+ // this should not happen
+ logger.warn("Exception caught: {}", ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * concrete implementation has to prepare the requests with additional parameters, etc
+ *
+ * @param requestToPrepare the request to prepare
+ * @return prepared Request object
+ */
+ protected abstract Request prepareRequest(Request requestToPrepare);
+
+ /**
+ * concrete implementation has to provide the URL
+ *
+ * @return Url
+ */
+ protected abstract String getURL();
+
+ /**
+ * just a wrapper as fromJson could return null. This will avoid warnings as eclipse otherwise assumes unnecessary
+ * null checks which are not unnecessary
+ *
+ * @param <T>
+ * @param json
+ * @param classOfT
+ * @return
+ * @throws JsonSyntaxException
+ */
+ protected <T> @Nullable T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
+ return gson.fromJson(json, classOfT);
+ }
+}
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
-import org.openhab.binding.solaredge.internal.callback.AbstractCommandCallback;
+import org.openhab.binding.solaredge.internal.connector.StatusUpdateListener;
import org.openhab.binding.solaredge.internal.handler.SolarEdgeHandler;
import org.openhab.binding.solaredge.internal.model.AggregateDataResponsePrivateApi;
import org.openhab.binding.solaredge.internal.model.AggregateDataResponseTransformerPrivateApi;
* @author Alexander Friese - initial contribution
*/
@NonNullByDefault
-public class AggregateDataUpdatePrivateApi extends AbstractCommandCallback implements SolarEdgeCommand {
+public class AggregateDataUpdatePrivateApi extends AbstractCommand implements SolarEdgeCommand {
/**
* the solaredge handler
* @param handler
* @param period
*/
- public AggregateDataUpdatePrivateApi(SolarEdgeHandler handler, AggregatePeriod period) {
- super(handler.getConfiguration());
+ public AggregateDataUpdatePrivateApi(SolarEdgeHandler handler, AggregatePeriod period,
+ StatusUpdateListener listener) {
+ super(handler.getConfiguration(), listener);
this.handler = handler;
this.transformer = new AggregateDataResponseTransformerPrivateApi(handler);
this.period = period;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
-import org.openhab.binding.solaredge.internal.callback.AbstractCommandCallback;
+import org.openhab.binding.solaredge.internal.connector.StatusUpdateListener;
import org.openhab.binding.solaredge.internal.handler.SolarEdgeHandler;
import org.openhab.binding.solaredge.internal.model.AggregateDataResponsePublicApi;
import org.openhab.binding.solaredge.internal.model.AggregateDataResponseTransformerPublicApi;
* @author Alexander Friese - initial contribution
*/
@NonNullByDefault
-public class AggregateDataUpdatePublicApi extends AbstractCommandCallback implements SolarEdgeCommand {
+public class AggregateDataUpdatePublicApi extends AbstractCommand implements SolarEdgeCommand {
/**
* the solaredge handler
* @param handler
* @param period
*/
- public AggregateDataUpdatePublicApi(SolarEdgeHandler handler, AggregatePeriod period) {
- super(handler.getConfiguration());
+ public AggregateDataUpdatePublicApi(SolarEdgeHandler handler, AggregatePeriod period,
+ StatusUpdateListener listener) {
+ super(handler.getConfiguration(), listener);
this.dateFormat = new SimpleDateFormat("yyyy-MM-dd");
this.handler = handler;
this.transformer = new AggregateDataResponseTransformerPublicApi(handler);
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
-import org.openhab.binding.solaredge.internal.callback.AbstractCommandCallback;
+import org.openhab.binding.solaredge.internal.connector.StatusUpdateListener;
import org.openhab.binding.solaredge.internal.handler.SolarEdgeHandler;
import org.openhab.binding.solaredge.internal.model.LiveDataResponseMeterless;
import org.openhab.binding.solaredge.internal.model.LiveDataResponseTransformer;
* @author Alexander Friese - initial contribution
*/
@NonNullByDefault
-public class LiveDataUpdateMeterless extends AbstractCommandCallback implements SolarEdgeCommand {
+public class LiveDataUpdateMeterless extends AbstractCommand implements SolarEdgeCommand {
private final SolarEdgeHandler handler;
private final LiveDataResponseTransformer transformer;
private int retries = 0;
- public LiveDataUpdateMeterless(SolarEdgeHandler handler) {
- super(handler.getConfiguration());
+ public LiveDataUpdateMeterless(SolarEdgeHandler handler, StatusUpdateListener listener) {
+ super(handler.getConfiguration(), listener);
this.handler = handler;
this.transformer = new LiveDataResponseTransformer(handler);
}
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
-import org.openhab.binding.solaredge.internal.callback.AbstractCommandCallback;
+import org.openhab.binding.solaredge.internal.connector.StatusUpdateListener;
import org.openhab.binding.solaredge.internal.handler.SolarEdgeHandler;
import org.openhab.binding.solaredge.internal.model.LiveDataResponse;
import org.openhab.binding.solaredge.internal.model.LiveDataResponseTransformer;
* @author Alexander Friese - initial contribution
*/
@NonNullByDefault
-public class LiveDataUpdatePrivateApi extends AbstractCommandCallback implements SolarEdgeCommand {
+public class LiveDataUpdatePrivateApi extends AbstractCommand implements SolarEdgeCommand {
private final SolarEdgeHandler handler;
private final LiveDataResponseTransformer transformer;
private int retries = 0;
- public LiveDataUpdatePrivateApi(SolarEdgeHandler handler) {
- super(handler.getConfiguration());
+ public LiveDataUpdatePrivateApi(SolarEdgeHandler handler, StatusUpdateListener listener) {
+ super(handler.getConfiguration(), listener);
this.handler = handler;
this.transformer = new LiveDataResponseTransformer(handler);
}
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
-import org.openhab.binding.solaredge.internal.callback.AbstractCommandCallback;
+import org.openhab.binding.solaredge.internal.connector.StatusUpdateListener;
import org.openhab.binding.solaredge.internal.handler.SolarEdgeHandler;
import org.openhab.binding.solaredge.internal.model.LiveDataResponse;
import org.openhab.binding.solaredge.internal.model.LiveDataResponseTransformer;
* @author Alexander Friese - initial contribution
*/
@NonNullByDefault
-public class LiveDataUpdatePublicApi extends AbstractCommandCallback implements SolarEdgeCommand {
+public class LiveDataUpdatePublicApi extends AbstractCommand implements SolarEdgeCommand {
private final SolarEdgeHandler handler;
private final LiveDataResponseTransformer transformer;
private int retries = 0;
- public LiveDataUpdatePublicApi(SolarEdgeHandler handler) {
- super(handler.getConfiguration());
+ public LiveDataUpdatePublicApi(SolarEdgeHandler handler, StatusUpdateListener listener) {
+ super(handler.getConfiguration(), listener);
this.handler = handler;
this.transformer = new LiveDataResponseTransformer(handler);
}
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpMethod;
-import org.openhab.binding.solaredge.internal.callback.AbstractCommandCallback;
import org.openhab.binding.solaredge.internal.connector.StatusUpdateListener;
import org.openhab.binding.solaredge.internal.handler.SolarEdgeHandler;
* @author Alexander Friese - initial contribution
*/
@NonNullByDefault
-public class PrivateApiTokenCheck extends AbstractCommandCallback implements SolarEdgeCommand {
+public class PrivateApiTokenCheck extends AbstractCommand implements SolarEdgeCommand {
public PrivateApiTokenCheck(SolarEdgeHandler handler, StatusUpdateListener listener) {
super(handler.getConfiguration(), listener);
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpMethod;
-import org.openhab.binding.solaredge.internal.callback.AbstractCommandCallback;
import org.openhab.binding.solaredge.internal.connector.StatusUpdateListener;
import org.openhab.binding.solaredge.internal.handler.SolarEdgeHandler;
* @author Alexander Friese - initial contribution
*/
@NonNullByDefault
-public class PublicApiKeyCheck extends AbstractCommandCallback implements SolarEdgeCommand {
+public class PublicApiKeyCheck extends AbstractCommand implements SolarEdgeCommand {
public PublicApiKeyCheck(SolarEdgeHandler handler, StatusUpdateListener listener) {
super(handler.getConfiguration(), listener);
import org.eclipse.jetty.client.api.Response.ContentListener;
import org.eclipse.jetty.client.api.Response.FailureListener;
import org.eclipse.jetty.client.api.Response.SuccessListener;
-import org.openhab.binding.solaredge.internal.connector.StatusUpdateListener;
/**
* public interface for all commands
* @param asyncclient
*/
void performAction(HttpClient asyncclient);
-
- /**
- * updates the listener's status
- *
- */
- void updateListenerStatus();
-
- /**
- * register a listener
- *
- * @param listener
- */
- void setListener(StatusUpdateListener listener);
}
package org.openhab.binding.solaredge.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
/**
* Bean holding configuration data according to bridge.xml
@NonNullByDefault
public class SolarEdgeConfiguration {
- private @Nullable String tokenOrApiKey;
- private @Nullable String solarId;
+ private String tokenOrApiKey = "";
+ private String solarId = "";
private boolean meterInstalled = false;
private boolean usePrivateApi = false;
private Integer liveDataPollingInterval = 10;
private Integer aggregateDataPollingInterval = 60;
- public @Nullable String getTokenOrApiKey() {
+ public String getTokenOrApiKey() {
return tokenOrApiKey;
}
this.tokenOrApiKey = tokenOrApiKey;
}
- public @Nullable String getSolarId() {
+ public String getSolarId() {
return solarId;
}
this.commandQueue = new BlockingArrayQueue<>(WEB_REQUEST_QUEUE_MAX_SIZE);
}
+ private void processAuthenticationResult(CommunicationStatus status) {
+ String errorMessageCodeFound;
+ String errorMessgaeCodeForbidden = STATUS_INVALID_SOLAR_ID;
+ if (config.isUsePrivateApi()) {
+ errorMessageCodeFound = STATUS_INVALID_TOKEN;
+ } else {
+ errorMessageCodeFound = STATUS_UNKNOWN_ERROR;
+ }
+
+ switch (status.getHttpCode()) {
+ case OK:
+ handler.setStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
+ setAuthenticated(true);
+ break;
+ case FOUND:
+ handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ errorMessageCodeFound);
+ setAuthenticated(false);
+ break;
+ case FORBIDDEN:
+ handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ errorMessgaeCodeForbidden);
+ setAuthenticated(false);
+ break;
+ case SERVICE_UNAVAILABLE:
+ handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, status.getMessage());
+ setAuthenticated(false);
+ break;
+ default:
+ handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ status.getMessage());
+ setAuthenticated(false);
+ }
+ }
+
+ /**
+ * authenticates with the Solaredge WEB interface
+ */
+ private synchronized void authenticate() {
+ setAuthenticated(false);
+
+ if (preCheck()) {
+ SolarEdgeCommand tokenCheckCommand;
+
+ if (config.isUsePrivateApi()) {
+ tokenCheckCommand = new PrivateApiTokenCheck(handler, this::processAuthenticationResult);
+ } else {
+ tokenCheckCommand = new PublicApiKeyCheck(handler, this::processAuthenticationResult);
+ }
+ tokenCheckCommand.performAction(httpClient);
+ }
+ }
+
+ /**
+ * performs some pre cheks on configuration before attempting to login
+ *
+ * @return true on success, false otherwise
+ */
+ private boolean preCheck() {
+ String preCheckStatusMessage = "";
+ String localTokenOrApiKey = config.getTokenOrApiKey();
+
+ if (config.isUsePrivateApi() && localTokenOrApiKey.length() < TOKEN_THRESHOLD) {
+ preCheckStatusMessage = STATUS_INVALID_TOKEN_LENGTH;
+ } else if (!config.isUsePrivateApi() && localTokenOrApiKey.length() > API_KEY_THRESHOLD) {
+ preCheckStatusMessage = STATUS_INVALID_API_KEY_LENGTH;
+ } else if (!config.isUsePrivateApi() && calcRequestsPerDay() > WEB_REQUEST_PUBLIC_API_DAY_LIMIT) {
+ preCheckStatusMessage = STATUS_REQUEST_LIMIT_EXCEEDED;
+ } else if (config.isUsePrivateApi() && !config.isMeterInstalled()) {
+ preCheckStatusMessage = STATUS_NO_METER_CONFIGURED;
+ } else {
+ return true;
+ }
+
+ handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, preCheckStatusMessage);
+ return false;
+ }
+
+ /**
+ * calculates requests per day. just an internal helper
+ *
+ * @return
+ */
+ private long calcRequestsPerDay() {
+ return MINUTES_PER_DAY / config.getLiveDataPollingInterval()
+ + 4 * MINUTES_PER_DAY / config.getAggregateDataPollingInterval();
+ }
+
/**
* puts a command into the queue
*
authenticate();
}
- else if (isAuthenticated() && !commandQueue.isEmpty()) {
- StatusUpdateListener statusUpdater = new StatusUpdateListener() {
- @Override
- public void update(CommunicationStatus status) {
- switch (status.getHttpCode()) {
- case SERVICE_UNAVAILABLE:
- handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
- status.getMessage());
- setAuthenticated(false);
- break;
- case OK:
- // no action needed as the thing is already online.
- break;
- default:
- handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
- status.getMessage());
- setAuthenticated(false);
-
- }
- }
- };
-
- SolarEdgeCommand command = commandQueue.poll();
- command.setListener(statusUpdater);
+ if (isAuthenticated() && !commandQueue.isEmpty()) {
+ try {
+ executeCommand();
+ } catch (Exception ex) {
+ logger.warn("command execution ended with exception:", ex);
+ }
+ }
+ }
+
+ /**
+ * executes the next command in the queue. requires authenticated session.
+ *
+ * @throws ValidationException
+ */
+ private void executeCommand() {
+ SolarEdgeCommand command = commandQueue.poll();
+ if (command != null) {
command.performAction(httpClient);
}
}
requestExecutor.enqueue(command);
}
- /**
- * authenticates with the Solaredge WEB interface
- */
- private synchronized void authenticate() {
- setAuthenticated(false);
-
- if (preCheck()) {
- SolarEdgeCommand tokenCheckCommand;
-
- StatusUpdateListener tokenCheckListener = new StatusUpdateListener() {
-
- @Override
- public void update(CommunicationStatus status) {
- String errorMessageCodeFound;
- String errorMessgaeCodeForbidden;
- if (config.isUsePrivateApi()) {
- errorMessageCodeFound = "login error with private API: invalid token";
- errorMessgaeCodeForbidden = "login error with private API: invalid solarId";
- } else {
- errorMessageCodeFound = "login error with public API: unknown error";
- errorMessgaeCodeForbidden = "login error with public API: invalid api key or solarId is not valid for this api key";
- }
-
- switch (status.getHttpCode()) {
- case OK:
- handler.setStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, "logged in");
- setAuthenticated(true);
- break;
- case FOUND:
- handler.setStatusInfo(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_ERROR,
- errorMessageCodeFound);
- setAuthenticated(false);
- break;
- case FORBIDDEN:
- handler.setStatusInfo(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_ERROR,
- errorMessgaeCodeForbidden);
- setAuthenticated(false);
- break;
- case SERVICE_UNAVAILABLE:
- handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
- status.getMessage());
- setAuthenticated(false);
- break;
- default:
- handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
- status.getMessage());
- setAuthenticated(false);
- }
- }
- };
-
- if (config.isUsePrivateApi()) {
- tokenCheckCommand = new PrivateApiTokenCheck(handler, tokenCheckListener);
- } else {
- tokenCheckCommand = new PublicApiKeyCheck(handler, tokenCheckListener);
- }
- tokenCheckCommand.performAction(httpClient);
- }
- }
-
- /**
- * performs some pre cheks on configuration before attempting to login
- *
- * @return true on success, false otherwise
- */
- private boolean preCheck() {
- String preCheckStatusMessage = "";
- String localTokenOrApiKey = config.getTokenOrApiKey();
- String localSolarId = config.getSolarId();
-
- if (localTokenOrApiKey == null || localTokenOrApiKey.isEmpty()) {
- preCheckStatusMessage = "please configure token/api_key first";
- } else if (localSolarId == null || localSolarId.isEmpty()) {
- preCheckStatusMessage = "please configure solarId first";
- } else if (config.isUsePrivateApi() && localTokenOrApiKey.length() < TOKEN_THRESHOLD) {
- preCheckStatusMessage = "you will have to use a 'token' and not an 'api key' when using private API";
- } else if (!config.isUsePrivateApi() && localTokenOrApiKey.length() > API_KEY_THRESHOLD) {
- preCheckStatusMessage = "you will have to use an 'api key' and not a 'token' when using public API";
- } else if (!config.isUsePrivateApi() && calcRequestsPerDay() > WEB_REQUEST_PUBLIC_API_DAY_LIMIT) {
- preCheckStatusMessage = "daily request limit (" + WEB_REQUEST_PUBLIC_API_DAY_LIMIT + ") exceeded: "
- + calcRequestsPerDay();
- } else if (config.isUsePrivateApi() && !config.isMeterInstalled()) {
- preCheckStatusMessage = "a meter must be present in order to use the private API";
- } else {
- return true;
- }
-
- this.handler.setStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, preCheckStatusMessage);
- return false;
- }
-
- /**
- * calculates requests per day. just an internal helper
- *
- * @return
- */
- private long calcRequestsPerDay() {
- return MINUTES_PER_DAY / this.config.getLiveDataPollingInterval()
- + 4 * MINUTES_PER_DAY / this.config.getAggregateDataPollingInterval();
- }
-
/**
* will be called by the ThingHandler to abort periodic jobs.
*/
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.solaredge.internal.handler;
-
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.client.HttpClient;
-import org.openhab.core.thing.Channel;
-import org.openhab.core.thing.ChannelGroupUID;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingUID;
-
-/**
- * generic thing handler for solaredge
- *
- * @author Alexander Friese - initial contribution
- */
-@NonNullByDefault
-public class GenericSolarEdgeHandler extends SolarEdgeBaseHandler {
-
- public GenericSolarEdgeHandler(Thing thing, HttpClient httpClient) {
- super(thing, httpClient);
- }
-
- @Override
- public List<Channel> getChannels() {
- return getThing().getChannels();
- }
-
- @Override
- public @Nullable Channel getChannel(String groupId, String channelId) {
- ThingUID thingUID = this.getThing().getUID();
- ChannelGroupUID channelGroupUID = new ChannelGroupUID(thingUID, groupId);
- Channel channel = getThing().getChannel(new ChannelUID(channelGroupUID, channelId));
- return channel;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.solaredge.internal.handler;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.solaredge.internal.command.AggregateDataUpdatePrivateApi;
-import org.openhab.binding.solaredge.internal.command.AggregateDataUpdatePublicApi;
-import org.openhab.binding.solaredge.internal.command.SolarEdgeCommand;
-import org.openhab.binding.solaredge.internal.model.AggregatePeriod;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Polling worker class. This is responsible for periodic polling of sensor data.
- *
- * @author Alexander Friese - initial contribution
- */
-@NonNullByDefault
-public class SolarEdgeAggregateDataPolling implements Runnable {
- /**
- * Logger
- */
- private final Logger logger = LoggerFactory.getLogger(getClass());
-
- /**
- * Handler for delegation to callbacks.
- */
- private final SolarEdgeHandler handler;
-
- /**
- * Constructor.
- *
- * @param handler handler which handles results of polling
- */
- public SolarEdgeAggregateDataPolling(SolarEdgeHandler handler) {
- this.handler = handler;
- }
-
- /**
- * Poll the SolarEdge Webservice one time per call.
- */
- @Override
- public void run() {
- // if no meter is present all data will be fetched by the 'LiveDataUpdateMeterless'
- if (handler.getConfiguration().isMeterInstalled()) {
- logger.debug("polling SolarEdge aggregate data {}", handler.getConfiguration());
-
- List<SolarEdgeCommand> commands = new ArrayList<>();
-
- if (handler.getConfiguration().isUsePrivateApi()) {
- commands.add(new AggregateDataUpdatePrivateApi(handler, AggregatePeriod.DAY));
- commands.add(new AggregateDataUpdatePrivateApi(handler, AggregatePeriod.WEEK));
- commands.add(new AggregateDataUpdatePrivateApi(handler, AggregatePeriod.MONTH));
- commands.add(new AggregateDataUpdatePrivateApi(handler, AggregatePeriod.YEAR));
- } else {
- commands.add(new AggregateDataUpdatePublicApi(handler, AggregatePeriod.DAY));
- commands.add(new AggregateDataUpdatePublicApi(handler, AggregatePeriod.WEEK));
- commands.add(new AggregateDataUpdatePublicApi(handler, AggregatePeriod.MONTH));
- commands.add(new AggregateDataUpdatePublicApi(handler, AggregatePeriod.YEAR));
- }
-
- for (SolarEdgeCommand command : commands) {
- handler.getWebInterface().enqueueCommand(command);
- }
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.solaredge.internal.handler;
-
-import java.util.Map;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.client.HttpClient;
-import org.openhab.binding.solaredge.internal.AtomicReferenceTrait;
-import org.openhab.binding.solaredge.internal.config.SolarEdgeConfiguration;
-import org.openhab.binding.solaredge.internal.connector.WebInterface;
-import org.openhab.core.thing.Channel;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.binding.BaseThingHandler;
-import org.openhab.core.types.Command;
-import org.openhab.core.types.State;
-import org.openhab.core.types.UnDefType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link SolarEdgeBaseHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Alexander Friese - initial contribution
- */
-@NonNullByDefault
-public abstract class SolarEdgeBaseHandler extends BaseThingHandler implements SolarEdgeHandler, AtomicReferenceTrait {
- private final Logger logger = LoggerFactory.getLogger(SolarEdgeBaseHandler.class);
-
- private final long LIVE_POLLING_INITIAL_DELAY = 1;
- private final long AGGREGATE_POLLING_INITIAL_DELAY = 2;
-
- /**
- * Interface object for querying the Solaredge web interface
- */
- private WebInterface webInterface;
-
- /**
- * Schedule for polling live data
- */
- private final AtomicReference<@Nullable Future<?>> liveDataPollingJobReference;
-
- /**
- * Schedule for polling aggregate data
- */
- private final AtomicReference<@Nullable Future<?>> aggregateDataPollingJobReference;
-
- public SolarEdgeBaseHandler(Thing thing, HttpClient httpClient) {
- super(thing);
- this.webInterface = new WebInterface(scheduler, this, httpClient);
- this.liveDataPollingJobReference = new AtomicReference<>(null);
- this.aggregateDataPollingJobReference = new AtomicReference<>(null);
- }
-
- @Override
- public void handleCommand(ChannelUID channelUID, Command command) {
- logger.debug("command for {}: {}", channelUID, command);
- // write access is not supported.
- }
-
- @Override
- public void initialize() {
- logger.debug("About to initialize SolarEdge");
- SolarEdgeConfiguration config = getConfiguration();
- logger.debug("Solaredge initialized with configuration: {}", config);
-
- startPolling();
- webInterface.start();
- updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "waiting for web api login");
- }
-
- /**
- * Start the polling.
- */
- private void startPolling() {
- updateJobReference(liveDataPollingJobReference,
- scheduler.scheduleWithFixedDelay(new SolarEdgeLiveDataPolling(this), LIVE_POLLING_INITIAL_DELAY,
- getConfiguration().getLiveDataPollingInterval(), TimeUnit.MINUTES));
-
- updateJobReference(aggregateDataPollingJobReference,
- scheduler.scheduleWithFixedDelay(new SolarEdgeAggregateDataPolling(this),
- AGGREGATE_POLLING_INITIAL_DELAY, getConfiguration().getAggregateDataPollingInterval(),
- TimeUnit.MINUTES));
- }
-
- /**
- * Disposes the bridge.
- */
- @Override
- public void dispose() {
- logger.debug("Handler disposed.");
-
- cancelJobReference(liveDataPollingJobReference);
- cancelJobReference(aggregateDataPollingJobReference);
-
- webInterface.dispose();
- }
-
- @Override
- public WebInterface getWebInterface() {
- return webInterface;
- }
-
- /**
- * will update all channels provided in the map
- */
- @Override
- public void updateChannelStatus(Map<Channel, State> values) {
- logger.debug("Handling channel update.");
-
- for (Channel channel : values.keySet()) {
- if (getChannels().contains(channel)) {
- State value = values.get(channel);
- if (value != null) {
- logger.debug("Channel is to be updated: {}: {}", channel.getUID().getAsString(), value);
- updateState(channel.getUID(), value);
- } else {
- logger.debug("Value is null or not provided by solaredge (channel: {})",
- channel.getUID().getAsString());
- updateState(channel.getUID(), UnDefType.UNDEF);
- }
- } else {
- logger.debug("Could not identify channel: {} for model {}", channel.getUID().getAsString(),
- getThing().getThingTypeUID().getAsString());
- }
- }
- }
-
- @Override
- public void setStatusInfo(ThingStatus status, ThingStatusDetail statusDetail, String description) {
- super.updateStatus(status, statusDetail, description);
- }
-
- @Override
- public SolarEdgeConfiguration getConfiguration() {
- return this.getConfigAs(SolarEdgeConfiguration.class);
- }
-}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 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.solaredge.internal.handler;
+
+import static org.openhab.binding.solaredge.internal.SolarEdgeBindingConstants.STATUS_WAITING_FOR_LOGIN;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.openhab.binding.solaredge.internal.AtomicReferenceTrait;
+import org.openhab.binding.solaredge.internal.command.AggregateDataUpdatePrivateApi;
+import org.openhab.binding.solaredge.internal.command.AggregateDataUpdatePublicApi;
+import org.openhab.binding.solaredge.internal.command.LiveDataUpdateMeterless;
+import org.openhab.binding.solaredge.internal.command.LiveDataUpdatePrivateApi;
+import org.openhab.binding.solaredge.internal.command.LiveDataUpdatePublicApi;
+import org.openhab.binding.solaredge.internal.command.SolarEdgeCommand;
+import org.openhab.binding.solaredge.internal.config.SolarEdgeConfiguration;
+import org.openhab.binding.solaredge.internal.connector.CommunicationStatus;
+import org.openhab.binding.solaredge.internal.connector.WebInterface;
+import org.openhab.binding.solaredge.internal.model.AggregatePeriod;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelGroupUID;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link SolarEdgeGenericHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Alexander Friese - initial contribution
+ */
+@NonNullByDefault
+public class SolarEdgeGenericHandler extends BaseThingHandler implements SolarEdgeHandler, AtomicReferenceTrait {
+ private final Logger logger = LoggerFactory.getLogger(SolarEdgeGenericHandler.class);
+
+ private static final long LIVE_POLLING_INITIAL_DELAY = 1;
+ private static final long AGGREGATE_POLLING_INITIAL_DELAY = 2;
+
+ /**
+ * Interface object for querying the Solaredge web interface
+ */
+ private WebInterface webInterface;
+
+ /**
+ * Schedule for polling live data
+ */
+ private final AtomicReference<@Nullable Future<?>> liveDataPollingJobReference;
+
+ /**
+ * Schedule for polling aggregate data
+ */
+ private final AtomicReference<@Nullable Future<?>> aggregateDataPollingJobReference;
+
+ public SolarEdgeGenericHandler(Thing thing, HttpClient httpClient) {
+ super(thing);
+ this.webInterface = new WebInterface(scheduler, this, httpClient);
+ this.liveDataPollingJobReference = new AtomicReference<>(null);
+ this.aggregateDataPollingJobReference = new AtomicReference<>(null);
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ logger.debug("command for {}: {}", channelUID, command);
+ // write access is not supported.
+ }
+
+ @Override
+ public void initialize() {
+ logger.debug("About to initialize SolarEdge");
+ SolarEdgeConfiguration config = getConfiguration();
+ logger.debug("SolarEdge initialized with configuration: {}", config);
+
+ updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, STATUS_WAITING_FOR_LOGIN);
+ webInterface.start();
+ startPolling();
+ }
+
+ /**
+ * Start the polling.
+ */
+ private void startPolling() {
+ updateJobReference(liveDataPollingJobReference, scheduler.scheduleWithFixedDelay(this::liveDataPollingRun,
+ LIVE_POLLING_INITIAL_DELAY, getConfiguration().getLiveDataPollingInterval(), TimeUnit.MINUTES));
+
+ updateJobReference(aggregateDataPollingJobReference,
+ scheduler.scheduleWithFixedDelay(this::aggregateDataPollingRun, AGGREGATE_POLLING_INITIAL_DELAY,
+ getConfiguration().getAggregateDataPollingInterval(), TimeUnit.MINUTES));
+ }
+
+ /**
+ * Poll the SolarEdge Webservice one time per call to retrieve live data.
+ */
+ void liveDataPollingRun() {
+ logger.debug("polling SolarEdge live data {}", getConfiguration());
+ SolarEdgeCommand ldu;
+
+ if (getConfiguration().isUsePrivateApi()) {
+ ldu = new LiveDataUpdatePrivateApi(this, this::updateOnlineStatus);
+ } else {
+ if (getConfiguration().isMeterInstalled()) {
+ ldu = new LiveDataUpdatePublicApi(this, this::updateOnlineStatus);
+ } else {
+ ldu = new LiveDataUpdateMeterless(this, this::updateOnlineStatus);
+ }
+ }
+ getWebInterface().enqueueCommand(ldu);
+ }
+
+ /**
+ * Poll the SolarEdge Webservice one time per call to retrieve aggregate data.
+ */
+ void aggregateDataPollingRun() {
+ // if no meter is present all data will be fetched by the 'LiveDataUpdateMeterless'
+ if (getConfiguration().isMeterInstalled()) {
+ logger.debug("polling SolarEdge aggregate data {}", getConfiguration());
+ List<SolarEdgeCommand> commands = new ArrayList<>();
+
+ if (getConfiguration().isUsePrivateApi()) {
+ commands.add(new AggregateDataUpdatePrivateApi(this, AggregatePeriod.DAY, this::updateOnlineStatus));
+ commands.add(new AggregateDataUpdatePrivateApi(this, AggregatePeriod.WEEK, this::updateOnlineStatus));
+ commands.add(new AggregateDataUpdatePrivateApi(this, AggregatePeriod.MONTH, this::updateOnlineStatus));
+ commands.add(new AggregateDataUpdatePrivateApi(this, AggregatePeriod.YEAR, this::updateOnlineStatus));
+ } else {
+ commands.add(new AggregateDataUpdatePublicApi(this, AggregatePeriod.DAY, this::updateOnlineStatus));
+ commands.add(new AggregateDataUpdatePublicApi(this, AggregatePeriod.WEEK, this::updateOnlineStatus));
+ commands.add(new AggregateDataUpdatePublicApi(this, AggregatePeriod.MONTH, this::updateOnlineStatus));
+ commands.add(new AggregateDataUpdatePublicApi(this, AggregatePeriod.YEAR, this::updateOnlineStatus));
+ }
+
+ for (SolarEdgeCommand command : commands) {
+ getWebInterface().enqueueCommand(command);
+ }
+ }
+ }
+
+ private void updateOnlineStatus(CommunicationStatus status) {
+ switch (status.getHttpCode()) {
+ case SERVICE_UNAVAILABLE:
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, status.getMessage());
+ break;
+ case OK:
+ updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
+ break;
+ default:
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, status.getMessage());
+ }
+ }
+
+ /**
+ * Disposes the bridge.
+ */
+ @Override
+ public void dispose() {
+ logger.debug("Handler disposed.");
+
+ cancelJobReference(liveDataPollingJobReference);
+ cancelJobReference(aggregateDataPollingJobReference);
+
+ webInterface.dispose();
+ }
+
+ @Override
+ public WebInterface getWebInterface() {
+ return webInterface;
+ }
+
+ /**
+ * will update all channels provided in the map
+ */
+ @Override
+ public void updateChannelStatus(Map<Channel, State> values) {
+ logger.debug("Handling channel update.");
+
+ for (Channel channel : values.keySet()) {
+ if (getChannels().contains(channel)) {
+ State value = values.get(channel);
+ if (value != null) {
+ logger.debug("Channel is to be updated: {}: {}", channel.getUID().getAsString(), value);
+ updateState(channel.getUID(), value);
+ } else {
+ logger.debug("Value is null or not provided by solaredge (channel: {})",
+ channel.getUID().getAsString());
+ updateState(channel.getUID(), UnDefType.UNDEF);
+ }
+ } else {
+ logger.debug("Could not identify channel: {} for model {}", channel.getUID().getAsString(),
+ getThing().getThingTypeUID().getAsString());
+ }
+ }
+ }
+
+ @Override
+ public void setStatusInfo(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
+ super.updateStatus(status, statusDetail, description);
+ }
+
+ @Override
+ public SolarEdgeConfiguration getConfiguration() {
+ return this.getConfigAs(SolarEdgeConfiguration.class);
+ }
+
+ @Override
+ public List<Channel> getChannels() {
+ return getThing().getChannels();
+ }
+
+ @Override
+ public @Nullable Channel getChannel(String groupId, String channelId) {
+ ThingUID thingUID = this.getThing().getUID();
+ ChannelGroupUID channelGroupUID = new ChannelGroupUID(thingUID, groupId);
+ Channel channel = getThing().getChannel(new ChannelUID(channelGroupUID, channelId));
+ return channel;
+ }
+}
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solaredge.internal.config.SolarEdgeConfiguration;
import org.openhab.binding.solaredge.internal.connector.WebInterface;
import org.openhab.core.thing.Channel;
* @param statusDetail Bridge status detail
* @param description Bridge status description
*/
- void setStatusInfo(ThingStatus status, ThingStatusDetail statusDetail, String description);
+ void setStatusInfo(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description);
/**
* Provides the web interface object.
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 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.solaredge.internal.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.solaredge.internal.command.LiveDataUpdateMeterless;
-import org.openhab.binding.solaredge.internal.command.LiveDataUpdatePrivateApi;
-import org.openhab.binding.solaredge.internal.command.LiveDataUpdatePublicApi;
-import org.openhab.binding.solaredge.internal.command.SolarEdgeCommand;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Polling worker class. This is responsible for periodic polling of sensor data.
- *
- * @author Alexander Friese - initial contribution
- */
-@NonNullByDefault
-public class SolarEdgeLiveDataPolling implements Runnable {
- /**
- * Logger
- */
- private final Logger logger = LoggerFactory.getLogger(getClass());
-
- /**
- * Handler for delegation to callbacks.
- */
- private final SolarEdgeHandler handler;
-
- /**
- * Constructor.
- *
- * @param handler handler which handles results of polling
- */
- public SolarEdgeLiveDataPolling(SolarEdgeHandler handler) {
- this.handler = handler;
- }
-
- /**
- * Poll the SolarEdge Webservice one time per call.
- */
- @Override
- public void run() {
- logger.debug("polling SolarEdge live data {}", handler.getConfiguration());
-
- SolarEdgeCommand ldu;
-
- if (handler.getConfiguration().isUsePrivateApi()) {
- ldu = new LiveDataUpdatePrivateApi(handler);
- } else {
- if (handler.getConfiguration().isMeterInstalled()) {
- ldu = new LiveDataUpdatePublicApi(handler);
- } else {
- ldu = new LiveDataUpdateMeterless(handler);
- }
- }
-
- handler.getWebInterface().enqueueCommand(ldu);
- }
-}
/**
* logger
*/
- private static final Logger logger = LoggerFactory.getLogger(AbstractDataResponseTransformer.class);
+ private final Logger logger = LoggerFactory.getLogger(AbstractDataResponseTransformer.class);
/**
* determines the unit, also handles wrong spelling of kWh (which is spelled with capital K by API)
MeterTelemetry... values) {
double sum = 0.0;
for (MeterTelemetry value : values) {
- if (value.value != null) {
- sum += value.value;
+ Double innerValue = value.value;
+ if (innerValue != null) {
+ sum += innerValue;
}
}
putEnergyType(targetMap, channel, sum, unit);
if (energyDetails != null) {
AggregatePeriod timeUnit = energyDetails.timeUnit;
String unit = energyDetails.unit;
- if (timeUnit != null && unit != null && energyDetails.meters != null) {
- for (MeterTelemetries meter : energyDetails.meters) {
+ List<MeterTelemetries> meters = energyDetails.meters;
+ if (timeUnit != null && unit != null && meters != null) {
+ for (MeterTelemetries meter : meters) {
String type = meter.type;
if (type != null) {
if (type.equals(METER_TYPE_PRODUCTION)) {
*/
package org.openhab.binding.solaredge.internal.model;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* defines the level of data aggregation
*
* @author Alexander Friese - initial contribution
*/
+@NonNullByDefault
public enum AggregatePeriod {
DAY,
WEEK,
import static org.openhab.binding.solaredge.internal.SolarEdgeBindingConstants.*;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
ZERO_POWER, siteCurrentPowerFlow.unit);
// determine power flow from connection list
- if (siteCurrentPowerFlow.connections != null) {
- for (Connection con : siteCurrentPowerFlow.connections) {
+ List<Connection> connections = siteCurrentPowerFlow.connections;
+ if (connections != null) {
+ for (Connection con : connections) {
+ String conFrom = con.from;
+ String conTo = con.to;
if (grid != null) {
- if (con.from != null && con.from.equalsIgnoreCase(LiveDataResponse.GRID)) {
+ if (conFrom != null && conFrom.equalsIgnoreCase(LiveDataResponse.GRID)) {
putPowerType(result, channelProvider.getChannel(CHANNEL_GROUP_LIVE, CHANNEL_ID_IMPORT),
grid.currentPower, siteCurrentPowerFlow.unit);
- } else if (con.to != null && con.to.equalsIgnoreCase(LiveDataResponse.GRID)) {
+ } else if (conTo != null && conTo.equalsIgnoreCase(LiveDataResponse.GRID)) {
putPowerType(result, channelProvider.getChannel(CHANNEL_GROUP_LIVE, CHANNEL_ID_EXPORT),
grid.currentPower, siteCurrentPowerFlow.unit);
}
}
if (storage != null) {
- Double currentPower = storage.currentPower != null ? storage.currentPower : 0;
- if (con.from != null && con.from.equalsIgnoreCase(LiveDataResponse.STORAGE)) {
+ Double currentPower = storage.currentPower;
+ currentPower = currentPower != null ? currentPower : 0;
+ if (conFrom != null && conFrom.equalsIgnoreCase(LiveDataResponse.STORAGE)) {
putPowerType(result,
channelProvider.getChannel(CHANNEL_GROUP_LIVE, CHANNEL_ID_BATTERY_DISCHARGE),
currentPower, siteCurrentPowerFlow.unit);
putPowerType(result,
channelProvider.getChannel(CHANNEL_GROUP_LIVE, CHANNEL_ID_BATTERY_CHARGE_DISCHARGE),
-1 * currentPower, siteCurrentPowerFlow.unit);
- } else if (con.to != null && con.to.equalsIgnoreCase(LiveDataResponse.STORAGE)) {
+ } else if (conTo != null && conTo.equalsIgnoreCase(LiveDataResponse.STORAGE)) {
putPowerType(result,
channelProvider.getChannel(CHANNEL_GROUP_LIVE, CHANNEL_ID_BATTERY_CHARGE),
currentPower, siteCurrentPowerFlow.unit);
channel-type.solaredge.type-percent.label = Percent
channel-type.solaredge.type-power.label = Power
channel-type.solaredge.type-status.label = Status Text
+
+# status translations
+
+status.invalid.solarId = solarId is either invalid or belongs to another account.
+status.invalid.token = The token seems to be invalid.
+status.unknown.error = Unknown error
+status.invalid.token.length = You will have to use a 'token' and not an 'api key' when using private API.
+status.invalid.api.key.length = You will have to use an 'api key' and not a 'token' when using public API.
+status.request.limit.exceeded = Daily request limit exceeded: {0}.
+status.no.meter.configured = A meter must be present in order to use the private API.
+status.waiting.for.login = Waiting for web api login.