diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/SolarEdgeBindingConstants.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/SolarEdgeBindingConstants.java index 7b5fa5a1e..dae67f3b5 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/SolarEdgeBindingConstants.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/SolarEdgeBindingConstants.java @@ -100,5 +100,16 @@ public class SolarEdgeBindingConstants { 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 SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_GENERIC); } diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/SolarEdgeHandlerFactory.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/SolarEdgeHandlerFactory.java index 6c7f81b23..3301159c1 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/SolarEdgeHandlerFactory.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/SolarEdgeHandlerFactory.java @@ -17,7 +17,7 @@ import static org.openhab.binding.solaredge.internal.SolarEdgeBindingConstants.* 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; @@ -62,7 +62,7 @@ public class SolarEdgeHandlerFactory extends BaseThingHandlerFactory { 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()); } diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/callback/AbstractCommandCallback.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AbstractCommand.java similarity index 88% rename from bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/callback/AbstractCommandCallback.java rename to bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AbstractCommand.java index 8f28187ca..f5d169557 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/callback/AbstractCommandCallback.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AbstractCommand.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.solaredge.internal.callback; +package org.openhab.binding.solaredge.internal.command; import static org.openhab.binding.solaredge.internal.SolarEdgeBindingConstants.*; @@ -31,7 +31,6 @@ 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; @@ -47,12 +46,12 @@ import com.google.gson.JsonSyntaxException; * @author Alexander Friese - initial contribution */ @NonNullByDefault -public abstract class AbstractCommandCallback extends BufferingResponseListener implements SolarEdgeCommand { +public abstract class AbstractCommand extends BufferingResponseListener implements SolarEdgeCommand { /** * logger */ - protected final Logger logger = LoggerFactory.getLogger(AbstractCommandCallback.class); + protected final Logger logger = LoggerFactory.getLogger(AbstractCommand.class); /** * the configuration @@ -72,27 +71,20 @@ public abstract class AbstractCommandCallback extends BufferingResponseListener /** * listener to provide updates to the WebInterface class */ - private @Nullable StatusUpdateListener listener; + private final StatusUpdateListener listener; /** * the constructor * * @param config + * @param listener + * */ - public AbstractCommandCallback(SolarEdgeConfiguration config) { + public AbstractCommand(SolarEdgeConfiguration config, StatusUpdateListener listener) { 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; + this.gson = new Gson(); } /** @@ -151,7 +143,6 @@ public abstract class AbstractCommandCallback extends BufferingResponseListener } else { // this is only relevant when using public API request.param(PUBLIC_DATA_API_KEY_FIELD, config.getTokenOrApiKey()); - } prepareRequest(request).send(this); @@ -164,10 +155,15 @@ public abstract class AbstractCommandCallback extends BufferingResponseListener return communicationStatus; } - @Override - public void updateListenerStatus() { - if (listener != null) { + /** + * 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); } } @@ -186,11 +182,6 @@ public abstract class AbstractCommandCallback extends BufferingResponseListener */ 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 diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AggregateDataUpdatePrivateApi.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AggregateDataUpdatePrivateApi.java index 06606bdc8..6d33505a1 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AggregateDataUpdatePrivateApi.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AggregateDataUpdatePrivateApi.java @@ -22,7 +22,7 @@ import org.eclipse.jetty.client.api.Request; 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; @@ -34,7 +34,7 @@ import org.openhab.binding.solaredge.internal.model.AggregatePeriod; * @author Alexander Friese - initial contribution */ @NonNullByDefault -public class AggregateDataUpdatePrivateApi extends AbstractCommandCallback implements SolarEdgeCommand { +public class AggregateDataUpdatePrivateApi extends AbstractCommand implements SolarEdgeCommand { /** * the solaredge handler @@ -59,8 +59,9 @@ public class AggregateDataUpdatePrivateApi extends AbstractCommandCallback imple * @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; diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AggregateDataUpdatePublicApi.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AggregateDataUpdatePublicApi.java index 28d3c0195..4be0a17f2 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AggregateDataUpdatePublicApi.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/AggregateDataUpdatePublicApi.java @@ -24,7 +24,7 @@ import org.eclipse.jetty.client.api.Request; 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; @@ -36,7 +36,7 @@ import org.openhab.binding.solaredge.internal.model.AggregatePeriod; * @author Alexander Friese - initial contribution */ @NonNullByDefault -public class AggregateDataUpdatePublicApi extends AbstractCommandCallback implements SolarEdgeCommand { +public class AggregateDataUpdatePublicApi extends AbstractCommand implements SolarEdgeCommand { /** * the solaredge handler @@ -61,8 +61,9 @@ public class AggregateDataUpdatePublicApi extends AbstractCommandCallback implem * @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); diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdateMeterless.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdateMeterless.java index ca4f10bbe..16dca88ac 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdateMeterless.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdateMeterless.java @@ -22,7 +22,7 @@ import org.eclipse.jetty.client.api.Request; 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; @@ -33,14 +33,14 @@ 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); } diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdatePrivateApi.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdatePrivateApi.java index 58d00c94c..1806d5aeb 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdatePrivateApi.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdatePrivateApi.java @@ -22,7 +22,7 @@ import org.eclipse.jetty.client.api.Request; 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; @@ -33,14 +33,14 @@ 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); } diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdatePublicApi.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdatePublicApi.java index 016c668d1..4000f704d 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdatePublicApi.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/LiveDataUpdatePublicApi.java @@ -22,7 +22,7 @@ import org.eclipse.jetty.client.api.Request; 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; @@ -33,14 +33,14 @@ 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); } diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/PrivateApiTokenCheck.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/PrivateApiTokenCheck.java index 4f9005e7f..c2fe4deae 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/PrivateApiTokenCheck.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/PrivateApiTokenCheck.java @@ -19,7 +19,6 @@ import org.eclipse.jdt.annotation.Nullable; 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; @@ -29,7 +28,7 @@ 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); diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/PublicApiKeyCheck.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/PublicApiKeyCheck.java index ed956a60f..ee1de7459 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/PublicApiKeyCheck.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/PublicApiKeyCheck.java @@ -19,7 +19,6 @@ import org.eclipse.jdt.annotation.Nullable; 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; @@ -29,7 +28,7 @@ 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); diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/SolarEdgeCommand.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/SolarEdgeCommand.java index cbd9879b0..9d607f761 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/SolarEdgeCommand.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/command/SolarEdgeCommand.java @@ -18,7 +18,6 @@ import org.eclipse.jetty.client.api.Response.CompleteListener; 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 @@ -36,17 +35,4 @@ public interface SolarEdgeCommand extends SuccessListener, FailureListener, Cont * @param asyncclient */ void performAction(HttpClient asyncclient); - - /** - * updates the listener's status - * - */ - void updateListenerStatus(); - - /** - * register a listener - * - * @param listener - */ - void setListener(StatusUpdateListener listener); } diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/config/SolarEdgeConfiguration.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/config/SolarEdgeConfiguration.java index 9534cbbc5..6fdb08b8b 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/config/SolarEdgeConfiguration.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/config/SolarEdgeConfiguration.java @@ -13,7 +13,6 @@ 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 @@ -23,8 +22,8 @@ import org.eclipse.jdt.annotation.Nullable; @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; @@ -34,7 +33,7 @@ public class SolarEdgeConfiguration { private Integer liveDataPollingInterval = 10; private Integer aggregateDataPollingInterval = 60; - public @Nullable String getTokenOrApiKey() { + public String getTokenOrApiKey() { return tokenOrApiKey; } @@ -42,7 +41,7 @@ public class SolarEdgeConfiguration { this.tokenOrApiKey = tokenOrApiKey; } - public @Nullable String getSolarId() { + public String getSolarId() { return solarId; } diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/connector/WebInterface.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/connector/WebInterface.java index fe3d2ffee..b9fff4b80 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/connector/WebInterface.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/connector/WebInterface.java @@ -104,6 +104,94 @@ public class WebInterface implements AtomicReferenceTrait { 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 * @@ -131,30 +219,23 @@ public class WebInterface implements AtomicReferenceTrait { 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); + if (isAuthenticated() && !commandQueue.isEmpty()) { + try { + executeCommand(); + } catch (Exception ex) { + logger.warn("command execution ended with exception:", ex); + } + } + } - } - } - }; - - SolarEdgeCommand command = commandQueue.poll(); - command.setListener(statusUpdater); + /** + * 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); } } @@ -190,107 +271,6 @@ public class WebInterface implements AtomicReferenceTrait { 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. */ diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/GenericSolarEdgeHandler.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/GenericSolarEdgeHandler.java deleted file mode 100644 index 4b93b0bf6..000000000 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/GenericSolarEdgeHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * 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 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; - } -} diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeAggregateDataPolling.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeAggregateDataPolling.java deleted file mode 100644 index 601c48133..000000000 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeAggregateDataPolling.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * 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 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); - } - } - } -} diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeBaseHandler.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeBaseHandler.java deleted file mode 100644 index 681fceadb..000000000 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeBaseHandler.java +++ /dev/null @@ -1,156 +0,0 @@ -/** - * 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 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); - } -} diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeGenericHandler.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeGenericHandler.java new file mode 100644 index 000000000..0af1180dc --- /dev/null +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeGenericHandler.java @@ -0,0 +1,240 @@ +/** + * 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 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 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 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; + } +} diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeHandler.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeHandler.java index bdd44133a..0834b7246 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeHandler.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeHandler.java @@ -15,6 +15,7 @@ package org.openhab.binding.solaredge.internal.handler; 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; @@ -38,7 +39,7 @@ public interface SolarEdgeHandler extends ThingHandler, ChannelProvider { * @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. diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeLiveDataPolling.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeLiveDataPolling.java deleted file mode 100644 index f4d5180f2..000000000 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/handler/SolarEdgeLiveDataPolling.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * 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); - } -} diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AbstractDataResponseTransformer.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AbstractDataResponseTransformer.java index 4931c1377..2763357ec 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AbstractDataResponseTransformer.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AbstractDataResponseTransformer.java @@ -54,7 +54,7 @@ abstract class AbstractDataResponseTransformer { /** * 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) @@ -178,8 +178,9 @@ abstract class AbstractDataResponseTransformer { 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); diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AggregateDataResponseTransformerPublicApi.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AggregateDataResponseTransformerPublicApi.java index 3a2d4fe49..a9533ad9e 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AggregateDataResponseTransformerPublicApi.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AggregateDataResponseTransformerPublicApi.java @@ -56,8 +56,9 @@ public class AggregateDataResponseTransformerPublicApi extends AbstractDataRespo 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 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)) { diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AggregatePeriod.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AggregatePeriod.java index 120c32542..2767e3cfd 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AggregatePeriod.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/AggregatePeriod.java @@ -12,11 +12,14 @@ */ 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, diff --git a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/LiveDataResponseTransformer.java b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/LiveDataResponseTransformer.java index f66f9050c..3e3ef5c2e 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/LiveDataResponseTransformer.java +++ b/bundles/org.openhab.binding.solaredge/src/main/java/org/openhab/binding/solaredge/internal/model/LiveDataResponseTransformer.java @@ -15,6 +15,7 @@ package org.openhab.binding.solaredge.internal.model; 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; @@ -142,28 +143,32 @@ public class LiveDataResponseTransformer extends AbstractDataResponseTransformer ZERO_POWER, siteCurrentPowerFlow.unit); // determine power flow from connection list - if (siteCurrentPowerFlow.connections != null) { - for (Connection con : siteCurrentPowerFlow.connections) { + List 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); diff --git a/bundles/org.openhab.binding.solaredge/src/main/resources/OH-INF/i18n/solaredge.properties b/bundles/org.openhab.binding.solaredge/src/main/resources/OH-INF/i18n/solaredge.properties index 3f4598dd1..d553da644 100644 --- a/bundles/org.openhab.binding.solaredge/src/main/resources/OH-INF/i18n/solaredge.properties +++ b/bundles/org.openhab.binding.solaredge/src/main/resources/OH-INF/i18n/solaredge.properties @@ -129,3 +129,14 @@ channel-type.solaredge.type-energy.label = Energy 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.