mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[solaredge] Fix code style warnings, fix NPE warnings, improve i18n (#14666)
* code style fixes * fixed potential NPE * fix warning * simplified code * refactoring, fixed some NPE warnings * i18n * refactoring Signed-off-by: Alexander Friese <af944580@googlemail.com>
This commit is contained in:
parent
8dd0028315
commit
b6da1ec590
@ -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<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_GENERIC);
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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<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;
|
||||
}
|
||||
}
|
@ -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<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<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);
|
||||
}
|
||||
}
|
@ -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<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;
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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<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)) {
|
||||
|
@ -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,
|
||||
|
@ -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<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);
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user