Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fronius] Added inverter power, battery state of charge and PV solar yield #10757

Merged
merged 6 commits into from
Jul 11, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@
*/
package org.openhab.binding.fronius.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
* The {@link FroniusBaseDeviceConfiguration} is the class used to match the
* thing configuration.
*
* @author Thomas Rokohl - Initial contribution
*/
@NonNullByDefault
public class FroniusBaseDeviceConfiguration {
@Nullable
public Integer deviceId;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Syntactical sugar. Please check all.

Suggested change
@Nullable
public Integer deviceId;
public @Nullable Integer deviceId;

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*
* @author Thomas Rokohl - Initial contribution
* @author Peter Schraffl - Added device status and error status channels
* @author Thomas Kordelle - Added inverter power, battery state of charge and PV solar yield
*/
@NonNullByDefault
public class FroniusBindingConstants {
Expand All @@ -33,38 +34,53 @@ public class FroniusBindingConstants {
public static final ThingTypeUID THING_TYPE_METER = new ThingTypeUID(BINDING_ID, "meter");

// List of all Channel ids
public static final String InverterDataChannelDayEnergy = "inverterdatachanneldayenergy";
public static final String InverterDataChannelPac = "inverterdatachannelpac";
public static final String InverterDataChannelTotal = "inverterdatachanneltotal";
public static final String InverterDataChannelYear = "inverterdatachannelyear";
public static final String InverterDataChannelFac = "inverterdatachannelfac";
public static final String InverterDataChannelIac = "inverterdatachanneliac";
public static final String InverterDataChannelIdc = "inverterdatachannelidc";
public static final String InverterDataChannelUac = "inverterdatachanneluac";
public static final String InverterDataChannelUdc = "inverterdatachanneludc";
public static final String InverterDataChannelDeviceStatusErrorCode = "inverterdatadevicestatuserrorcode";
public static final String InverterDataChannelDeviceStatusStatusCode = "inverterdatadevicestatusstatuscode";
public static final String PowerFlowpGrid = "powerflowchannelpgrid";
public static final String PowerFlowpLoad = "powerflowchannelpload";
public static final String PowerFlowpAkku = "powerflowchannelpakku";
public static final String MeterModel = "model";
public static final String MeterSerial = "serial";
public static final String MeterEnable = "enable";
public static final String MeterLocation = "location";
public static final String MeterCurrentAcPhase1 = "currentacphase1";
public static final String MeterCurrentAcPhase2 = "currentacphase2";
public static final String MeterCurrentAcPhase3 = "currentacphase3";
public static final String MeterVoltageAcPhase1 = "voltageacphase1";
public static final String MeterVoltageAcPhase2 = "voltageacphase2";
public static final String MeterVoltageAcPhase3 = "voltageacphase3";
public static final String MeterPowerPhase1 = "powerrealphase1";
public static final String MeterPowerPhase2 = "powerrealphase2";
public static final String MeterPowerPhase3 = "powerrealphase3";
public static final String MeterPowerFactorPhase1 = "powerfactorphase1";
public static final String MeterPowerFactorPhase2 = "powerfactorphase2";
public static final String MeterPowerFactorPhase3 = "powerfactorphase3";
public static final String MeterEnergyRealSumConsumed = "energyrealsumconsumed";
public static final String MeterEnergyRealSumProduced = "energyrealsumproduced";
public static final String INVERTER_DATA_CHANNEL_DAY_ENERGY = "inverterdatachanneldayenergy";
public static final String INVERTER_DATA_CHANNEL_PAC = "inverterdatachannelpac";
public static final String INVERTER_DATA_CHANNEL_TOTAL = "inverterdatachanneltotal";
public static final String INVERTER_DATA_CHANNEL_YEAR = "inverterdatachannelyear";
public static final String INVERTER_DATA_CHANNEL_FAC = "inverterdatachannelfac";
public static final String INVERTER_DATA_CHANNEL_IAC = "inverterdatachanneliac";
public static final String INVERTER_DATA_CHANNEL_IDC = "inverterdatachannelidc";
public static final String INVERTER_DATA_CHANNEL_UAC = "inverterdatachanneluac";
public static final String INVERTER_DATA_CHANNEL_UDC = "inverterdatachanneludc";
public static final String INVERTER_DATA_CHANNEL_DEVICE_STATUS_ERROR_CODE = "inverterdatadevicestatuserrorcode";
public static final String INVERTER_DATA_CHANNEL_DEVICE_STATUS_STATUS_CODE = "inverterdatadevicestatusstatuscode";
public static final String POWER_FLOW_P_GRID = "powerflowchannelpgrid";
public static final String POWER_FLOW_P_LOAD = "powerflowchannelpload";
public static final String POWER_FLOW_P_AKKU = "powerflowchannelpakku";
public static final String POWER_FLOW_P_PV = "powerflowchannelppv";
public static final String METER_MODEL = "model";
public static final String METER_SERIAL = "serial";
public static final String METER_ENABLE = "enable";
public static final String METER_LOCATION = "location";
public static final String METER_CURRENT_AC_PHASE_1 = "currentacphase1";
public static final String METER_CURRENT_AC_PHASE_2 = "currentacphase2";
public static final String METER_CURRENT_AC_PHASE_3 = "currentacphase3";
public static final String METER_VOLTAGE_AC_PHASE_1 = "voltageacphase1";
public static final String METER_VOLTAGE_AC_PHASE_2 = "voltageacphase2";
public static final String METER_VOLTAGE_AC_PHASE_3 = "voltageacphase3";
public static final String METER_POWER_PHASE_1 = "powerrealphase1";
public static final String METER_POWER_PHASE_2 = "powerrealphase2";
public static final String METER_POWER_PHASE_3 = "powerrealphase3";
public static final String METER_POWER_FACTOR_PHASE_1 = "powerfactorphase1";
public static final String METER_POWER_FACTOR_PHASE_2 = "powerfactorphase2";
public static final String METER_POWER_FACTOR_PHASE_3 = "powerfactorphase3";
public static final String METER_ENERGY_REAL_SUM_CONSUMED = "energyrealsumconsumed";
public static final String METER_ENERGY_REAL_SUM_PRODUCED = "energyrealsumproduced";

/*
* part of POWERFLOW_REALTIME_DATA using Symo Gen24
* "Inverters" : {
* "1" : {
* "Battery_Mode" : "normal",
* "DT" : 1,
* "P" : 356,
* "SOC" : 95.199996948242188
* }
* },
*/
public static final String POWER_FLOW_INVERTER_1_POWER = "powerflowinverter1power";
public static final String POWER_FLOW_INVERTER_1_SOC = "powerflowinverter1soc";

// List of all Urls
public static final String INVERTER_REALTIME_DATA_URL = "http://%IP%/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DeviceId=%DEVICEID%&DataCollection=CommonInverterData";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@
*/
package org.openhab.binding.fronius.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
* The {@link FroniusBridgeConfiguration} is the class used to match the
* bridge configuration.
*
* @author Thomas Rokohl - Initial contribution
*/
@NonNullByDefault
public class FroniusBridgeConfiguration {
@Nullable
public String hostname;
@Nullable
public Integer refreshInterval;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import java.util.HashSet;
import java.util.Set;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.fronius.internal.handler.FroniusBridgeHandler;
import org.openhab.binding.fronius.internal.handler.FroniusMeterHandler;
import org.openhab.binding.fronius.internal.handler.FroniusSymoInverterHandler;
Expand All @@ -34,10 +36,11 @@
*
* @author Thomas Rokohl - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.fronius")
public class FroniusHandlerFactory extends BaseThingHandlerFactory {

private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<ThingTypeUID>() {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>() {

private static final long serialVersionUID = 1L;
{
Expand All @@ -53,6 +56,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
}

@Override
@Nullable
protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* the "inverter" node of the JSON response
*
* @author Thomas Rokohl - Initial contribution
* @author Thomas Kordelle - Added inverter power, battery state of charge and PV solar yield
*/
public class PowerFlowRealtimeInverter {

Expand All @@ -32,6 +33,10 @@ public class PowerFlowRealtimeInverter {
private double eYear;
@SerializedName("E_Total")
private double eTotal;
@SerializedName("Battery_Mode")
private String batteryMode;
@SerializedName("SOC")
private double soc;

public double getDt() {
return dt;
Expand Down Expand Up @@ -72,4 +77,20 @@ public double geteTotal() {
public void seteTotal(double eTotal) {
this.eTotal = eTotal;
}

public String getBatteryMode() {
return batteryMode;
}

public void setBatteryMode(final String batteryMode) {
this.batteryMode = batteryMode;
}

public double getSoc() {
return soc;
}

public void setSoc(double soc) {
this.soc = soc;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
*
* @author Gerrit Beine - Initial contribution
* @author Thomas Rokohl - Refactoring to merge the concepts
* @author Thomas Kordelle - Added inverter power, battery state of charge and PV solar yield
*/
public abstract class FroniusBaseThingHandler extends BaseThingHandler {

Expand Down Expand Up @@ -128,9 +129,9 @@ protected void updateChannel(String channelId) {
} else if (value instanceof ValueUnit) {
state = new DecimalType(((ValueUnit) value).getValue());
} else if (value instanceof String) {
state = new StringType((String) value);
state = StringType.valueOf((String) value);
} else if (value instanceof QuantityType) {
state = (QuantityType) value;
state = (QuantityType<?>) value;
} else {
logger.warn("Update channel {}: Unsupported value type {}", channelId, value.getClass().getSimpleName());
}
Expand Down Expand Up @@ -198,12 +199,12 @@ protected <T extends BaseFroniusResponse> T collectDataFormUrl(Class<T> type, St
if (!resultOk) {
logger.debug("Error in fronius response: {}", errorMsg);
}
} catch (JsonSyntaxException e) {
} catch (JsonSyntaxException | NumberFormatException e) {
errorMsg = "Invalid JSON data received";
logger.debug("Error running fronius request: {}", e.getMessage());
logger.error("Error running fronius request: {}", e.getMessage());
} catch (IOException | IllegalStateException e) {
errorMsg = e.getMessage();
logger.debug("Error running fronius request: {}", errorMsg);
logger.error("Error running fronius request: {}", errorMsg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bindings should only log to error if something severe happened, like the detection of a bug in your code. This could be debug as you catch an IOException. See this link for a description of the log levels: https://www.openhab.org/docs/developer/guidelines.html#f-logging

}

// Update the thing status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.fronius.internal.FroniusBridgeConfiguration;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.thing.Bridge;
Expand All @@ -36,12 +38,13 @@
* @author Thomas Rokohl - Refactoring to merge the concepts.
* Check if host is reachable.
*/
@NonNullByDefault
public class FroniusBridgeHandler extends BaseBridgeHandler {

private final Logger logger = LoggerFactory.getLogger(FroniusBridgeHandler.class);
private static final int DEFAULT_REFRESH_PERIOD = 10;
private final Set<FroniusBaseThingHandler> services = new HashSet<>();
private ScheduledFuture<?> refreshJob;
private @Nullable ScheduledFuture<?> refreshJob;

public FroniusBridgeHandler(Bridge bridge) {
super(bridge);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
package org.openhab.binding.fronius.internal.handler;

import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.openhab.binding.fronius.internal.FroniusBaseDeviceConfiguration;
import org.openhab.binding.fronius.internal.FroniusBindingConstants;
import org.openhab.binding.fronius.internal.FroniusBridgeConfiguration;
Expand All @@ -23,18 +23,16 @@
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Thing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link FroniusMeterHandler} is responsible for updating the data, which are
* sent to one of the channels.
*
* @author Jimmy Tanagra - Initial contribution
* @author Thomas Kordelle - Actually constants should be all upper case.
*/
public class FroniusMeterHandler extends FroniusBaseThingHandler {

private final Logger logger = LoggerFactory.getLogger(FroniusMeterHandler.class);
private MeterRealtimeBodyDataDTO meterRealtimeBodyData;
private FroniusBaseDeviceConfiguration config;

Expand Down Expand Up @@ -72,42 +70,47 @@ protected Object getValue(String channelId) {
return null;
}

String[] fields = StringUtils.split(channelId, "#");
String fieldName = fields[0];
final String[] fields = channelId.split("#");
if (fields.length < 1) {
return null;
}
final String fieldName = fields[0];

switch (fieldName) {
case FroniusBindingConstants.MeterEnable:
case FroniusBindingConstants.METER_ENABLE:
return meterRealtimeBodyData.getEnable();
case FroniusBindingConstants.MeterLocation:
case FroniusBindingConstants.METER_LOCATION:
return meterRealtimeBodyData.getMeterLocationCurrent();
case FroniusBindingConstants.MeterCurrentAcPhase1:
return new QuantityType(meterRealtimeBodyData.getCurrentACPhase1(), Units.AMPERE);
case FroniusBindingConstants.MeterCurrentAcPhase2:
return new QuantityType(meterRealtimeBodyData.getCurrentACPhase2(), Units.AMPERE);
case FroniusBindingConstants.MeterCurrentAcPhase3:
return new QuantityType(meterRealtimeBodyData.getCurrentACPhase3(), Units.AMPERE);
case FroniusBindingConstants.MeterVoltageAcPhase1:
return new QuantityType(meterRealtimeBodyData.getVoltageACPhase1(), Units.VOLT);
case FroniusBindingConstants.MeterVoltageAcPhase2:
return new QuantityType(meterRealtimeBodyData.getVoltageACPhase2(), Units.VOLT);
case FroniusBindingConstants.MeterVoltageAcPhase3:
return new QuantityType(meterRealtimeBodyData.getVoltageACPhase3(), Units.VOLT);
case FroniusBindingConstants.MeterPowerPhase1:
return new QuantityType(meterRealtimeBodyData.getPowerRealPPhase1(), Units.WATT);
case FroniusBindingConstants.MeterPowerPhase2:
return new QuantityType(meterRealtimeBodyData.getPowerRealPPhase2(), Units.WATT);
case FroniusBindingConstants.MeterPowerPhase3:
return new QuantityType(meterRealtimeBodyData.getPowerRealPPhase3(), Units.WATT);
case FroniusBindingConstants.MeterPowerFactorPhase1:
case FroniusBindingConstants.METER_CURRENT_AC_PHASE_1:
return new QuantityType<>(meterRealtimeBodyData.getCurrentACPhase1(), Units.AMPERE);
case FroniusBindingConstants.METER_CURRENT_AC_PHASE_2:
return new QuantityType<>(meterRealtimeBodyData.getCurrentACPhase2(), Units.AMPERE);
case FroniusBindingConstants.METER_CURRENT_AC_PHASE_3:
return new QuantityType<>(meterRealtimeBodyData.getCurrentACPhase3(), Units.AMPERE);
case FroniusBindingConstants.METER_VOLTAGE_AC_PHASE_1:
return new QuantityType<>(meterRealtimeBodyData.getVoltageACPhase1(), Units.VOLT);
case FroniusBindingConstants.METER_VOLTAGE_AC_PHASE_2:
return new QuantityType<>(meterRealtimeBodyData.getVoltageACPhase2(), Units.VOLT);
case FroniusBindingConstants.METER_VOLTAGE_AC_PHASE_3:
return new QuantityType<>(meterRealtimeBodyData.getVoltageACPhase3(), Units.VOLT);
case FroniusBindingConstants.METER_POWER_PHASE_1:
return new QuantityType<>(meterRealtimeBodyData.getPowerRealPPhase1(), Units.WATT);
case FroniusBindingConstants.METER_POWER_PHASE_2:
return new QuantityType<>(meterRealtimeBodyData.getPowerRealPPhase2(), Units.WATT);
case FroniusBindingConstants.METER_POWER_PHASE_3:
return new QuantityType<>(meterRealtimeBodyData.getPowerRealPPhase3(), Units.WATT);
case FroniusBindingConstants.METER_POWER_FACTOR_PHASE_1:
return meterRealtimeBodyData.getPowerFactorPhase1();
case FroniusBindingConstants.MeterPowerFactorPhase2:
case FroniusBindingConstants.METER_POWER_FACTOR_PHASE_2:
return meterRealtimeBodyData.getPowerFactorPhase2();
case FroniusBindingConstants.MeterPowerFactorPhase3:
case FroniusBindingConstants.METER_POWER_FACTOR_PHASE_3:
return meterRealtimeBodyData.getPowerFactorPhase3();
case FroniusBindingConstants.MeterEnergyRealSumConsumed:
return new QuantityType(meterRealtimeBodyData.getEnergyRealWACSumConsumed(), Units.WATT_HOUR);
case FroniusBindingConstants.MeterEnergyRealSumProduced:
return new QuantityType(meterRealtimeBodyData.getEnergyRealWACSumProduced(), Units.WATT_HOUR);
case FroniusBindingConstants.METER_ENERGY_REAL_SUM_CONSUMED:
return new QuantityType<>(meterRealtimeBodyData.getEnergyRealWACSumConsumed(), Units.WATT_HOUR);
case FroniusBindingConstants.METER_ENERGY_REAL_SUM_PRODUCED:
return new QuantityType<>(meterRealtimeBodyData.getEnergyRealWACSumProduced(), Units.WATT_HOUR);
default:
break;
}

return null;
Expand All @@ -118,10 +121,10 @@ private void updateProperties() {
return;
}

Map<String, String> properties = editProperties();
final Map<String, String> properties = editProperties();

properties.put(FroniusBindingConstants.MeterModel, meterRealtimeBodyData.getDetails().getModel());
properties.put(FroniusBindingConstants.MeterSerial, meterRealtimeBodyData.getDetails().getSerial());
properties.put(FroniusBindingConstants.METER_MODEL, meterRealtimeBodyData.getDetails().getModel());
properties.put(FroniusBindingConstants.METER_SERIAL, meterRealtimeBodyData.getDetails().getSerial());

updateProperties(properties);
}
Expand All @@ -146,8 +149,11 @@ private void updateData(FroniusBridgeConfiguration bridgeConfiguration, FroniusB
* @param deviceId of the device
* @return {MeterRealtimeResponse} the object representation of the json response
*/
private MeterRealtimeResponseDTO getMeterRealtimeData(String ip, int deviceId) {
String location = FroniusBindingConstants.METER_REALTIME_DATA_URL.replace("%IP%", StringUtils.trimToEmpty(ip));
private MeterRealtimeResponseDTO getMeterRealtimeData(final String ip, final Integer deviceId) {
Objects.requireNonNull(ip, "IP address must be set in the configuration.");
Objects.requireNonNull(deviceId, "Device ID must be set in the configuration.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will throw an unchecked exception, which will crash your binding. You could throw a custom exception and catch it in the calling method.


String location = FroniusBindingConstants.METER_REALTIME_DATA_URL.replace("%IP%", ip.trim());
location = location.replace("%DEVICEID%", Integer.toString(deviceId));
return collectDataFormUrl(MeterRealtimeResponseDTO.class, location);
}
Expand Down
Loading