package de.ph87.data.topic.parser; import com.fasterxml.jackson.annotation.JsonProperty; import de.ph87.data.series.data.delta.DeltaService; import de.ph87.data.series.data.varying.VaryingService; import de.ph87.data.topic.TopicDto; import de.ph87.data.topic.TopicParserAbstract; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import tools.jackson.databind.ObjectMapper; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; @Slf4j @Service public class TasmotaSmartMeter extends TopicParserAbstract { public TasmotaSmartMeter(@NonNull final ObjectMapper mapper, @NonNull final DeltaService deltaService, @NonNull final VaryingService varyingService) { super(Dto.class, mapper, deltaService, varyingService); } @Override protected void handle2(final @NonNull TopicDto topic, @NonNull final Dto dto) { delta(topic.series0, dto.meter.number, dto.date, dto.meter.purchaseKWh); delta(topic.series1, dto.meter.number, dto.date, dto.meter.deliverKWh); varying(topic.series2, dto.date, Math.max(0, dto.meter.powerW)); varying(topic.series3, dto.date, Math.max(0, -dto.meter.powerW)); } public static class Dto { @NonNull public final ZonedDateTime date; @NonNull public final Meter meter; public Dto( @JsonProperty(value = "Time", required = true) @NonNull final String localDateTimeString, @JsonProperty(value = "meter", required = true) @NonNull final Meter meter ) { this.date = ZonedDateTime.of(LocalDateTime.parse(localDateTimeString), ZoneId.systemDefault()); this.meter = meter; } public static class Meter { @NonNull public final String number; public final double purchaseKWh; public final double deliverKWh; public final double powerW; public Meter( @JsonProperty(value = "number", required = true) @NonNull final String number, @JsonProperty(value = "energy_purchased_kwh", required = true) final double purchaseWh, @JsonProperty(value = "energy_delivered_kwh", required = true) final double deliverWh, @JsonProperty(value = "power_w", required = true) final double powerW ) { this.number = parseMeterNumber(number); this.purchaseKWh = purchaseWh; this.deliverKWh = deliverWh; this.powerW = powerW; } @NonNull private static String parseMeterNumber(@NonNull final String input) { if (input.isEmpty()) { throw new NumberFormatException("Cannot parse Meter number: No Hex-chars read."); } if (input.length() % 2 != 0) { throw new NumberFormatException("Cannot parse Meter number: Hex-char count must be multiple of 2."); } final int length = Integer.parseInt(input.substring(0, 2), 16); if (input.length() != length * 2) { throw new NumberFormatException("Cannot parse Meter number: Invalid length"); } final int type = Integer.parseInt(input.substring(2, 4), 16); final String name = "" + (char) Integer.parseInt(input.substring(4, 6), 16) + (char) Integer.parseInt(input.substring(6, 8), 16) + (char) Integer.parseInt(input.substring(8, 10), 16); final int number = Integer.parseInt(input.substring(10), 16); return "%d%s%s".formatted(type, name, number); } } } }