[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:
alexf2015 2023-03-26 13:42:00 +02:00 committed by GitHub
parent 8dd0028315
commit b6da1ec590
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 436 additions and 563 deletions

View File

@ -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);
}

View File

@ -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());
}

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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.
*/

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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)) {

View File

@ -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,

View File

@ -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);

View File

@ -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.