diff --git a/client/src/main/java/me/retrodaredevil/solarthing/config/options/PVOutputUploadProgramOptions.java b/client/src/main/java/me/retrodaredevil/solarthing/config/options/PVOutputUploadProgramOptions.java index b12b8102..602a363d 100644 --- a/client/src/main/java/me/retrodaredevil/solarthing/config/options/PVOutputUploadProgramOptions.java +++ b/client/src/main/java/me/retrodaredevil/solarthing/config/options/PVOutputUploadProgramOptions.java @@ -2,9 +2,16 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeName; +import me.retrodaredevil.solarthing.SolarThingConstants; import me.retrodaredevil.solarthing.annotations.JsonExplicit; import me.retrodaredevil.solarthing.packets.identification.IdentifierFragmentMatcher; import me.retrodaredevil.solarthing.packets.identification.IdentifierRepFragment; +import me.retrodaredevil.solarthing.program.pvoutput.provider.PacketVoltageProvider; +import me.retrodaredevil.solarthing.program.pvoutput.provider.PacketTemperatureCelsiusProvider; +import me.retrodaredevil.solarthing.program.pvoutput.provider.TemperatureCelsiusProvider; +import me.retrodaredevil.solarthing.program.pvoutput.provider.VoltageProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.List; @@ -14,6 +21,8 @@ @JsonTypeName("pvoutput-upload") @JsonExplicit public class PVOutputUploadProgramOptions extends DatabaseTimeZoneOptionBase implements AnalyticsOption, DatabaseOption, ProgramOptions { + private static final Logger LOGGER = LoggerFactory.getLogger(PVOutputUploadProgramOptions.class); + @JsonProperty(value = "system_id", required = true) private int systemId; @JsonProperty(value = "api_key", required = true) @@ -27,8 +36,13 @@ public class PVOutputUploadProgramOptions extends DatabaseTimeZoneOptionBase imp @JsonProperty("voltage_identifier") private IdentifierRepFragment voltageIdentifierFragmentObject = null; + @JsonProperty("voltage_from") + private VoltageProvider voltageProvider = null; + @JsonProperty("temperature_identifier") private IdentifierRepFragment temperatureIdentifierFragmentObject = null; + @JsonProperty("temperature_from") + private TemperatureCelsiusProvider temperatureCelsiusProvider = null; @JsonProperty("include_import") private boolean includeImport = false; @@ -62,6 +76,7 @@ public Map> getRequiredIdentifierMap() { } return r; } + @Deprecated public IdentifierFragmentMatcher getVoltageIdentifierFragmentMatcher() { IdentifierRepFragment object = voltageIdentifierFragmentObject; if (object == null) { @@ -69,6 +84,7 @@ public IdentifierFragmentMatcher getVoltageIdentifierFragmentMatcher() { } return object; } + @Deprecated public IdentifierFragmentMatcher getTemperatureIdentifierFragmentMatcher() { IdentifierRepFragment object = temperatureIdentifierFragmentObject; if (object == null) { @@ -77,6 +93,25 @@ public IdentifierFragmentMatcher getTemperatureIdentifierFragmentMatcher() { return object; } + public VoltageProvider getVoltageProvider() { + IdentifierRepFragment fragmentObject = voltageIdentifierFragmentObject; + if (fragmentObject != null) { + LOGGER.warn(SolarThingConstants.SUMMARY_MARKER, "(Deprecated) Do not use \"voltage_identifier\" anymore! Switch to \"voltage_from\"!"); + return new PacketVoltageProvider(fragmentObject, null); // null voltagePacketType defaults to PV + } + VoltageProvider voltageProvider = this.voltageProvider; + return voltageProvider == null ? VoltageProvider.NONE : voltageProvider; + } + public TemperatureCelsiusProvider getTemperatureCelsiusProvider() { + IdentifierRepFragment fragmentObject = temperatureIdentifierFragmentObject; + if (fragmentObject != null) { + LOGGER.warn(SolarThingConstants.SUMMARY_MARKER, "(Deprecated) Do not use \"temperature_identifier\" anymore! Switch to \"temperature_from\"!"); + return new PacketTemperatureCelsiusProvider(fragmentObject, null); // null temperaturePacketType defaults to PACKET + } + TemperatureCelsiusProvider temperatureCelsiusProvider = this.temperatureCelsiusProvider; + return temperatureCelsiusProvider == null ? TemperatureCelsiusProvider.NONE : temperatureCelsiusProvider; + } + public boolean isIncludeImport() { return includeImport; } diff --git a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/PVOutputUploadMain.java b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/PVOutputUploadMain.java index 05c2fec3..21422f7d 100644 --- a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/PVOutputUploadMain.java +++ b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/PVOutputUploadMain.java @@ -19,13 +19,15 @@ import me.retrodaredevil.solarthing.program.ConfigUtil; import me.retrodaredevil.solarthing.program.DatabaseConfig; import me.retrodaredevil.solarthing.program.PacketUtil; -import me.retrodaredevil.solarthing.program.pvoutput.provider.PVVoltageProvider; import me.retrodaredevil.solarthing.program.pvoutput.provider.TemperatureCelsiusProvider; import me.retrodaredevil.solarthing.program.pvoutput.provider.VoltageProvider; -import me.retrodaredevil.solarthing.program.pvoutput.provider.TemperaturePacketTemperatureCelsiusProvider; import me.retrodaredevil.solarthing.pvoutput.CsvUtil; import me.retrodaredevil.solarthing.pvoutput.SimpleDate; -import me.retrodaredevil.solarthing.pvoutput.data.*; +import me.retrodaredevil.solarthing.pvoutput.data.AddBatchOutputParameters; +import me.retrodaredevil.solarthing.pvoutput.data.AddOutputParameters; +import me.retrodaredevil.solarthing.pvoutput.data.AddOutputParametersBuilder; +import me.retrodaredevil.solarthing.pvoutput.data.AddStatusParameters; +import me.retrodaredevil.solarthing.pvoutput.data.ImmutableAddBatchOutputParameters; import me.retrodaredevil.solarthing.pvoutput.service.PVOutputOkHttpUtil; import me.retrodaredevil.solarthing.pvoutput.service.PVOutputRetrofitUtil; import me.retrodaredevil.solarthing.pvoutput.service.PVOutputService; @@ -46,7 +48,10 @@ import java.text.SimpleDateFormat; import java.time.ZoneId; import java.time.format.TextStyle; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; public class PVOutputUploadMain { private PVOutputUploadMain(){ throw new UnsupportedOperationException(); } @@ -78,9 +83,8 @@ public static int startPVOutputUpload(PVOutputUploadProgramOptions options, Comm .build(); Retrofit retrofit = PVOutputRetrofitUtil.defaultBuilder().client(client).build(); PVOutputService service = retrofit.create(PVOutputService.class); - // TODO allow a VoltageProvider and a TemperatureCelsiusProvider to be explicitly specified in program options - VoltageProvider voltageProvider = new PVVoltageProvider(options.getVoltageIdentifierFragmentMatcher()); - TemperatureCelsiusProvider temperatureCelsiusProvider = new TemperaturePacketTemperatureCelsiusProvider(options.getTemperatureIdentifierFragmentMatcher(), null); + VoltageProvider voltageProvider = options.getVoltageProvider(); + TemperatureCelsiusProvider temperatureCelsiusProvider = options.getTemperatureCelsiusProvider(); PVOutputHandler handler = new PVOutputHandler(zoneId, options.getRequiredIdentifierMap(), voltageProvider, temperatureCelsiusProvider); String fromDateString = commandOptions.getPVOutputFromDate(); diff --git a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/PVVoltageProvider.java b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/PVVoltageProvider.java deleted file mode 100644 index 5dcf6255..00000000 --- a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/PVVoltageProvider.java +++ /dev/null @@ -1,36 +0,0 @@ -package me.retrodaredevil.solarthing.program.pvoutput.provider; - -import me.retrodaredevil.solarthing.annotations.Nullable; -import me.retrodaredevil.solarthing.packets.Packet; -import me.retrodaredevil.solarthing.packets.collection.FragmentedPacketGroup; -import me.retrodaredevil.solarthing.packets.identification.IdentifierFragment; -import me.retrodaredevil.solarthing.packets.identification.IdentifierFragmentMatcher; -import me.retrodaredevil.solarthing.solar.common.PVCurrentAndVoltage; - -import static java.util.Objects.requireNonNull; - - -public class PVVoltageProvider implements VoltageProvider { - private final IdentifierFragmentMatcher voltageIdentifierFragmentMatcher; - - public PVVoltageProvider(IdentifierFragmentMatcher voltageIdentifierFragmentMatcher) { - this.voltageIdentifierFragmentMatcher = voltageIdentifierFragmentMatcher; - } - - @Override - public @Nullable Result getResult(FragmentedPacketGroup fragmentedPacketGroup) { - for (Packet packet : fragmentedPacketGroup.getPackets()) { - long dateMillis = requireNonNull(fragmentedPacketGroup.getDateMillis(packet), "Implementation of FragmentedPacketGroup did not provide individual dateMillis! type: " + fragmentedPacketGroup.getClass().getName()); - if (packet instanceof PVCurrentAndVoltage) { - int fragmentId = fragmentedPacketGroup.getFragmentId(packet); - PVCurrentAndVoltage pvCurrentAndVoltage = (PVCurrentAndVoltage) packet; - IdentifierFragment identifierFragment = IdentifierFragment.create(fragmentId, pvCurrentAndVoltage.getIdentifier()); - if (voltageIdentifierFragmentMatcher.matches(identifierFragment)) { - float voltage = pvCurrentAndVoltage.getPVVoltage().floatValue(); - return new Result(voltage, identifierFragment, dateMillis, false); - } - } - } - return null; - } -} diff --git a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/TemperaturePacketTemperatureCelsiusProvider.java b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/PacketTemperatureCelsiusProvider.java similarity index 83% rename from client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/TemperaturePacketTemperatureCelsiusProvider.java rename to client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/PacketTemperatureCelsiusProvider.java index 5758fcc8..0096f62b 100644 --- a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/TemperaturePacketTemperatureCelsiusProvider.java +++ b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/PacketTemperatureCelsiusProvider.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import me.retrodaredevil.solarthing.annotations.Nullable; import me.retrodaredevil.solarthing.misc.source.W1Source; import me.retrodaredevil.solarthing.misc.weather.TemperaturePacket; @@ -10,24 +12,26 @@ import me.retrodaredevil.solarthing.packets.identification.Identifiable; import me.retrodaredevil.solarthing.packets.identification.IdentifierFragment; import me.retrodaredevil.solarthing.packets.identification.IdentifierFragmentMatcher; +import me.retrodaredevil.solarthing.packets.identification.IdentifierRepFragment; import me.retrodaredevil.solarthing.solar.common.BatteryTemperature; import me.retrodaredevil.solarthing.solar.common.ControllerTemperature; import static java.util.Objects.requireNonNull; /** - * Identifies a {@link TemperaturePacket} from a given {@link IdentifierFragmentMatcher}. + * Selects a packet a given {@link IdentifierFragmentMatcher} and retrieves temperature depending on {@link #temperaturePacketType}. *

* Most of the time the source of the temperature packet is */ -public class TemperaturePacketTemperatureCelsiusProvider implements TemperatureCelsiusProvider { +@JsonTypeName("packet") +public class PacketTemperatureCelsiusProvider implements TemperatureCelsiusProvider { private final IdentifierFragmentMatcher temperatureIdentifierFragmentMatcher; private final TemperaturePacketType temperaturePacketType; @JsonCreator - public TemperaturePacketTemperatureCelsiusProvider( - @JsonProperty(value = "identifier", required = true) IdentifierFragmentMatcher temperatureIdentifierFragmentMatcher, + public PacketTemperatureCelsiusProvider( + @JsonProperty(value = "identifier", required = true) @JsonDeserialize(as = IdentifierRepFragment.class) IdentifierFragmentMatcher temperatureIdentifierFragmentMatcher, @JsonProperty("from") TemperaturePacketType temperaturePacketType) { this.temperatureIdentifierFragmentMatcher = requireNonNull(temperatureIdentifierFragmentMatcher); this.temperaturePacketType = temperaturePacketType == null ? TemperaturePacketType.PACKET : temperaturePacketType; @@ -64,7 +68,7 @@ public TemperaturePacketTemperatureCelsiusProvider( BatteryTemperature controllerTemperature = (BatteryTemperature) packet; return new Result(controllerTemperature.getBatteryTemperatureCelsius().floatValue(), identifierFragment, dateMillis, false); } - } + } else throw new AssertionError(); } return null; } diff --git a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/PacketVoltageProvider.java b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/PacketVoltageProvider.java new file mode 100644 index 00000000..c1317480 --- /dev/null +++ b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/PacketVoltageProvider.java @@ -0,0 +1,67 @@ +package me.retrodaredevil.solarthing.program.pvoutput.provider; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import me.retrodaredevil.solarthing.annotations.Nullable; +import me.retrodaredevil.solarthing.packets.Packet; +import me.retrodaredevil.solarthing.packets.collection.FragmentedPacketGroup; +import me.retrodaredevil.solarthing.packets.identification.Identifiable; +import me.retrodaredevil.solarthing.packets.identification.IdentifierFragment; +import me.retrodaredevil.solarthing.packets.identification.IdentifierFragmentMatcher; +import me.retrodaredevil.solarthing.packets.identification.IdentifierRepFragment; +import me.retrodaredevil.solarthing.solar.common.BatteryVoltage; +import me.retrodaredevil.solarthing.solar.common.PVCurrentAndVoltage; + +import static java.util.Objects.requireNonNull; + + +@JsonTypeName("packet") +public class PacketVoltageProvider implements VoltageProvider { + private final IdentifierFragmentMatcher voltageIdentifierFragmentMatcher; + private final VoltagePacketType voltagePacketType; + + @JsonCreator + public PacketVoltageProvider( + @JsonProperty(value = "identifier", required = true) @JsonDeserialize(as = IdentifierRepFragment.class) IdentifierFragmentMatcher voltageIdentifierFragmentMatcher, + @JsonProperty("from") VoltagePacketType voltagePacketType) { + this.voltageIdentifierFragmentMatcher = requireNonNull(voltageIdentifierFragmentMatcher); + this.voltagePacketType = voltagePacketType == null ? VoltagePacketType.PV : voltagePacketType; + } + + @Override + public @Nullable Result getResult(FragmentedPacketGroup fragmentedPacketGroup) { + for (Packet packet : fragmentedPacketGroup.getPackets()) { + if (!(packet instanceof Identifiable)) { + continue; + } + int fragmentId = fragmentedPacketGroup.getFragmentId(packet); + IdentifierFragment identifierFragment = IdentifierFragment.create(fragmentId, ((Identifiable) packet).getIdentifier()); + if (!voltageIdentifierFragmentMatcher.matches(identifierFragment)) { + continue; + } + long dateMillis = requireNonNull(fragmentedPacketGroup.getDateMillis(packet), "Implementation of FragmentedPacketGroup did not provide individual dateMillis! type: " + fragmentedPacketGroup.getClass().getName()); + if (voltagePacketType == VoltagePacketType.PV) { + if (packet instanceof PVCurrentAndVoltage) { + PVCurrentAndVoltage pvCurrentAndVoltage = (PVCurrentAndVoltage) packet; + float voltage = pvCurrentAndVoltage.getPVVoltage().floatValue(); + return new Result(voltage, identifierFragment, dateMillis, false); + } + } else if (voltagePacketType == VoltagePacketType.BATTERY) { + if (packet instanceof BatteryVoltage) { + BatteryVoltage batteryVoltagePacket = (BatteryVoltage) packet; + float batteryVoltage = batteryVoltagePacket.getBatteryVoltage(); + return new Result(batteryVoltage, identifierFragment, dateMillis, false); + } + } else throw new AssertionError(); + } + + return null; + } + public enum VoltagePacketType { + PV, + BATTERY + } + +} diff --git a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/TemperatureCelsiusProvider.java b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/TemperatureCelsiusProvider.java index 27e954c7..1c4b4d05 100644 --- a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/TemperatureCelsiusProvider.java +++ b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/TemperatureCelsiusProvider.java @@ -4,8 +4,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonSubTypes({ - @JsonSubTypes.Type(TemperaturePacketTemperatureCelsiusProvider.class), + @JsonSubTypes.Type(PacketTemperatureCelsiusProvider.class), }) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") public interface TemperatureCelsiusProvider extends DataProvider { + TemperatureCelsiusProvider NONE = packet -> null; } diff --git a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/VoltageProvider.java b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/VoltageProvider.java index 1aac0bfd..fab97f12 100644 --- a/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/VoltageProvider.java +++ b/client/src/main/java/me/retrodaredevil/solarthing/program/pvoutput/provider/VoltageProvider.java @@ -4,10 +4,11 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonSubTypes({ - @JsonSubTypes.Type(PVVoltageProvider.class), + @JsonSubTypes.Type(PacketVoltageProvider.class), @JsonSubTypes.Type(AverageBatteryVoltageProvider.class), }) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") public interface VoltageProvider extends DataProvider { + VoltageProvider NONE = packet -> null; } diff --git a/other/docs/todo.md b/other/docs/todo.md index b9991260..847ea57b 100644 --- a/other/docs/todo.md +++ b/other/docs/todo.md @@ -5,6 +5,7 @@ * Packet for disk usage * Send packet when mate serial port hasn't output data for 30 seconds * Create Dockerfile and example docker-compose file + * We should use https://hub.docker.com/r/azul/zulu-openjdk * Short term record packets for high/low battery voltage, FX inverter current, pv wattage, charging current, etc * This would be very useful so that if packets are replaced there is still information on how high or low the battery voltage got or how high the load was