From 606ee34731a5c9f652ecb42d5d9eb9b6f6402efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Fri, 18 Apr 2025 21:38:39 +0200 Subject: [PATCH] mqtt, series, meter, varying, Heizung, OpenDTU, SmartMeter, PatrixJson --- .gitignore | 38 ++++++++ application.properties | 10 +++ pom.xml | 32 +++++++ src/main/java/de/ph87/home/Backend.java | 15 ++++ src/main/java/de/ph87/home/meter/Meter.java | 58 +++++++++++++ .../de/ph87/home/meter/MeterRepository.java | 14 +++ .../java/de/ph87/home/meter/MeterService.java | 29 +++++++ .../de/ph87/home/meter/value/MeterPoint.java | 19 ++++ .../de/ph87/home/meter/value/MeterValue.java | 82 ++++++++++++++++++ .../home/meter/value/MeterValueInbound.java | 37 ++++++++ .../home/meter/value/MeterValueReceiver.java | 75 ++++++++++++++++ .../meter/value/MeterValueRepository.java | 17 ++++ .../home/meter/value/MeterValueService.java | 47 ++++++++++ .../ph87/home/meter/value/day/MeterDay.java | 21 +++++ .../meter/value/day/MeterDayRepository.java | 7 ++ .../ph87/home/meter/value/five/MeterFive.java | 21 +++++ .../meter/value/five/MeterFiveRepository.java | 7 ++ .../ph87/home/meter/value/hour/MeterHour.java | 21 +++++ .../meter/value/hour/MeterHourRepository.java | 7 ++ .../home/meter/value/month/MeterMonth.java | 21 +++++ .../value/month/MeterMonthRepository.java | 7 ++ .../ph87/home/meter/value/week/MeterWeek.java | 21 +++++ .../meter/value/week/MeterWeekRepository.java | 7 ++ .../ph87/home/meter/value/year/MeterYear.java | 21 +++++ .../meter/value/year/MeterYearRepository.java | 7 ++ .../de/ph87/home/point/PointController.java | 21 +++++ .../java/de/ph87/home/point/PointRequest.java | 45 ++++++++++ .../java/de/ph87/home/point/PointResult.java | 28 ++++++ .../java/de/ph87/home/point/PointService.java | 35 ++++++++ .../de/ph87/home/receive/HeizungHandler.java | 62 +++++++++++++ .../de/ph87/home/receive/OpenDTUHandler.java | 83 ++++++++++++++++++ .../ph87/home/receive/PatrixJsonHandler.java | 73 ++++++++++++++++ .../ph87/home/receive/SmartMeterHandler.java | 86 +++++++++++++++++++ src/main/java/de/ph87/home/series/Series.java | 49 +++++++++++ .../java/de/ph87/home/series/SeriesDto.java | 32 +++++++ .../de/ph87/home/series/SeriesRepository.java | 16 ++++ .../de/ph87/home/series/SeriesService.java | 29 +++++++ src/main/java/de/ph87/home/series/Type.java | 6 ++ src/main/java/de/ph87/home/unit/Aligned.java | 32 +++++++ src/main/java/de/ph87/home/unit/Interval.java | 29 +++++++ .../java/de/ph87/home/unit/PatrixUnit.java | 45 ++++++++++ .../de/ph87/home/unit/QuantityConverter.java | 28 ++++++ .../java/de/ph87/home/unit/UnitHelper.java | 41 +++++++++ .../de/ph87/home/varying/VaryingPoint.java | 25 ++++++ .../de/ph87/home/varying/VaryingValue.java | 80 +++++++++++++++++ .../home/varying/VaryingValueInbound.java | 33 +++++++ .../home/varying/VaryingValueReceiver.java | 76 ++++++++++++++++ .../home/varying/VaryingValueRepository.java | 17 ++++ .../home/varying/VaryingValueService.java | 45 ++++++++++ .../de/ph87/home/varying/day/VaryingDay.java | 21 +++++ .../varying/day/VaryingDayRepository.java | 7 ++ .../ph87/home/varying/five/VaryingFive.java | 21 +++++ .../varying/five/VaryingFiveRepository.java | 7 ++ .../ph87/home/varying/hour/VaryingHour.java | 21 +++++ .../varying/hour/VaryingHourRepository.java | 7 ++ .../ph87/home/varying/month/VaryingMonth.java | 21 +++++ .../varying/month/VaryingMonthRepository.java | 7 ++ .../ph87/home/varying/week/VaryingWeek.java | 21 +++++ .../varying/week/VaryingWeekRepository.java | 7 ++ .../ph87/home/varying/year/VaryingYear.java | 21 +++++ .../varying/year/VaryingYearRepository.java | 7 ++ src/main/resources/application.properties | 10 +++ 62 files changed, 1835 insertions(+) create mode 100644 .gitignore create mode 100644 application.properties create mode 100644 pom.xml create mode 100644 src/main/java/de/ph87/home/Backend.java create mode 100644 src/main/java/de/ph87/home/meter/Meter.java create mode 100644 src/main/java/de/ph87/home/meter/MeterRepository.java create mode 100644 src/main/java/de/ph87/home/meter/MeterService.java create mode 100644 src/main/java/de/ph87/home/meter/value/MeterPoint.java create mode 100644 src/main/java/de/ph87/home/meter/value/MeterValue.java create mode 100644 src/main/java/de/ph87/home/meter/value/MeterValueInbound.java create mode 100644 src/main/java/de/ph87/home/meter/value/MeterValueReceiver.java create mode 100644 src/main/java/de/ph87/home/meter/value/MeterValueRepository.java create mode 100644 src/main/java/de/ph87/home/meter/value/MeterValueService.java create mode 100644 src/main/java/de/ph87/home/meter/value/day/MeterDay.java create mode 100644 src/main/java/de/ph87/home/meter/value/day/MeterDayRepository.java create mode 100644 src/main/java/de/ph87/home/meter/value/five/MeterFive.java create mode 100644 src/main/java/de/ph87/home/meter/value/five/MeterFiveRepository.java create mode 100644 src/main/java/de/ph87/home/meter/value/hour/MeterHour.java create mode 100644 src/main/java/de/ph87/home/meter/value/hour/MeterHourRepository.java create mode 100644 src/main/java/de/ph87/home/meter/value/month/MeterMonth.java create mode 100644 src/main/java/de/ph87/home/meter/value/month/MeterMonthRepository.java create mode 100644 src/main/java/de/ph87/home/meter/value/week/MeterWeek.java create mode 100644 src/main/java/de/ph87/home/meter/value/week/MeterWeekRepository.java create mode 100644 src/main/java/de/ph87/home/meter/value/year/MeterYear.java create mode 100644 src/main/java/de/ph87/home/meter/value/year/MeterYearRepository.java create mode 100644 src/main/java/de/ph87/home/point/PointController.java create mode 100644 src/main/java/de/ph87/home/point/PointRequest.java create mode 100644 src/main/java/de/ph87/home/point/PointResult.java create mode 100644 src/main/java/de/ph87/home/point/PointService.java create mode 100644 src/main/java/de/ph87/home/receive/HeizungHandler.java create mode 100644 src/main/java/de/ph87/home/receive/OpenDTUHandler.java create mode 100644 src/main/java/de/ph87/home/receive/PatrixJsonHandler.java create mode 100644 src/main/java/de/ph87/home/receive/SmartMeterHandler.java create mode 100644 src/main/java/de/ph87/home/series/Series.java create mode 100644 src/main/java/de/ph87/home/series/SeriesDto.java create mode 100644 src/main/java/de/ph87/home/series/SeriesRepository.java create mode 100644 src/main/java/de/ph87/home/series/SeriesService.java create mode 100644 src/main/java/de/ph87/home/series/Type.java create mode 100644 src/main/java/de/ph87/home/unit/Aligned.java create mode 100644 src/main/java/de/ph87/home/unit/Interval.java create mode 100644 src/main/java/de/ph87/home/unit/PatrixUnit.java create mode 100644 src/main/java/de/ph87/home/unit/QuantityConverter.java create mode 100644 src/main/java/de/ph87/home/unit/UnitHelper.java create mode 100644 src/main/java/de/ph87/home/varying/VaryingPoint.java create mode 100644 src/main/java/de/ph87/home/varying/VaryingValue.java create mode 100644 src/main/java/de/ph87/home/varying/VaryingValueInbound.java create mode 100644 src/main/java/de/ph87/home/varying/VaryingValueReceiver.java create mode 100644 src/main/java/de/ph87/home/varying/VaryingValueRepository.java create mode 100644 src/main/java/de/ph87/home/varying/VaryingValueService.java create mode 100644 src/main/java/de/ph87/home/varying/day/VaryingDay.java create mode 100644 src/main/java/de/ph87/home/varying/day/VaryingDayRepository.java create mode 100644 src/main/java/de/ph87/home/varying/five/VaryingFive.java create mode 100644 src/main/java/de/ph87/home/varying/five/VaryingFiveRepository.java create mode 100644 src/main/java/de/ph87/home/varying/hour/VaryingHour.java create mode 100644 src/main/java/de/ph87/home/varying/hour/VaryingHourRepository.java create mode 100644 src/main/java/de/ph87/home/varying/month/VaryingMonth.java create mode 100644 src/main/java/de/ph87/home/varying/month/VaryingMonthRepository.java create mode 100644 src/main/java/de/ph87/home/varying/week/VaryingWeek.java create mode 100644 src/main/java/de/ph87/home/varying/week/VaryingWeekRepository.java create mode 100644 src/main/java/de/ph87/home/varying/year/VaryingYear.java create mode 100644 src/main/java/de/ph87/home/varying/year/VaryingYearRepository.java create mode 100644 src/main/resources/application.properties diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/application.properties b/application.properties new file mode 100644 index 0000000..2e14b3c --- /dev/null +++ b/application.properties @@ -0,0 +1,10 @@ +logging.level.de.ph87.home=DEBUG +#- +spring.datasource.url=jdbc:h2:./database;AUTO_SERVER=TRUE +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +#- +#spring.jpa.hibernate.ddl-auto=create +#- +de.ph87.patrix.mqtt.host=10.0.0.50 \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8ba1bfa --- /dev/null +++ b/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + Home + 1.0-SNAPSHOT + + + de.ph87.patrix + PatrixParent + 1.0-SNAPSHOT + + + + + de.ph87.patrix + PatrixCrud + + + de.ph87.patrix + PatrixMqtt + + + tech.units + indriya + 2.2.2 + + + + \ No newline at end of file diff --git a/src/main/java/de/ph87/home/Backend.java b/src/main/java/de/ph87/home/Backend.java new file mode 100644 index 0000000..fbd8dad --- /dev/null +++ b/src/main/java/de/ph87/home/Backend.java @@ -0,0 +1,15 @@ +package de.ph87.home; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages = {"de.ph87.home", "de.ph87.patrix"}) +public class Backend { + + public static void main(String[] args) { + SpringApplication.run(Backend.class, args); + } + +} \ No newline at end of file diff --git a/src/main/java/de/ph87/home/meter/Meter.java b/src/main/java/de/ph87/home/meter/Meter.java new file mode 100644 index 0000000..27f5e39 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/Meter.java @@ -0,0 +1,58 @@ +package de.ph87.home.meter; + +import de.ph87.home.series.Series; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +import java.time.ZonedDateTime; + +import static de.ph87.home.unit.UnitHelper.convert; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class Meter { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Version + @ToString.Exclude + private long version; + + @NonNull + @ToString.Exclude + @ManyToOne(optional = false) + private Series series; + + @NonNull + @ToString.Include + public String series() { + return series.getName(); + } + + @NonNull + @Column(nullable = false) + private ZonedDateTime since; + + @NonNull + @Column(nullable = false) + private String number; + + @Column(nullable = false, name = "`value`") + private double value; + + public Meter(@NonNull final Series series, @NonNull final ZonedDateTime since, @NonNull final String number, @NonNull final ComparableQuantity value) { + this.series = series; + this.since = since; + this.number = number; + this.value = convert(value, series.getUnit()).getValue().doubleValue(); + } + +} diff --git a/src/main/java/de/ph87/home/meter/MeterRepository.java b/src/main/java/de/ph87/home/meter/MeterRepository.java new file mode 100644 index 0000000..d0a4505 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/MeterRepository.java @@ -0,0 +1,14 @@ +package de.ph87.home.meter; + +import de.ph87.home.series.Series; +import lombok.NonNull; +import org.springframework.data.repository.ListCrudRepository; + +import java.util.Optional; + +public interface MeterRepository extends ListCrudRepository { + + @NonNull + Optional findFirstBySeriesOrderBySinceDesc(@NonNull final Series series); + +} diff --git a/src/main/java/de/ph87/home/meter/MeterService.java b/src/main/java/de/ph87/home/meter/MeterService.java new file mode 100644 index 0000000..368dfc6 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/MeterService.java @@ -0,0 +1,29 @@ +package de.ph87.home.meter; + +import de.ph87.home.meter.value.MeterValueInbound; +import de.ph87.home.series.Series; +import de.ph87.home.series.SeriesService; +import de.ph87.home.series.Type; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class MeterService { + + private final MeterRepository meterRepository; + + private final SeriesService seriesService; + + @NonNull + public Meter getOrCreate(@NonNull final MeterValueInbound event) { + final Series series = seriesService.getOrCreate(event.getName(), Type.METER, event.value.getUnit()); + return meterRepository.findFirstBySeriesOrderBySinceDesc(series) + .filter(meter -> meter.getNumber().equals(event.getNumber())) + .orElseGet(() -> meterRepository.save(new Meter(series, event.getDate(), event.getNumber(), event.getValue()))); + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/MeterPoint.java b/src/main/java/de/ph87/home/meter/value/MeterPoint.java new file mode 100644 index 0000000..5393af8 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/MeterPoint.java @@ -0,0 +1,19 @@ +package de.ph87.home.meter.value; + +import de.ph87.home.point.PointResult; +import lombok.Getter; +import lombok.NonNull; + +import java.time.ZonedDateTime; + +@Getter +public class MeterPoint extends PointResult.Point { + + public final double delta; + + public MeterPoint(@NonNull final ZonedDateTime date, final double delta) { + super(date); + this.delta = delta; + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/MeterValue.java b/src/main/java/de/ph87/home/meter/value/MeterValue.java new file mode 100644 index 0000000..93088a9 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/MeterValue.java @@ -0,0 +1,82 @@ +package de.ph87.home.meter.value; + +import de.ph87.home.meter.Meter; +import de.ph87.home.unit.Aligned; +import de.ph87.home.unit.Interval; +import de.ph87.home.unit.UnitHelper; +import jakarta.persistence.*; +import lombok.*; +import tech.units.indriya.ComparableQuantity; + +import java.time.ZonedDateTime; + +@Getter +@ToString +@MappedSuperclass +@NoArgsConstructor +public abstract class MeterValue { + + @EmbeddedId + private Id id; + + @Column(nullable = false, updatable = false) + private double min; + + @Column(nullable = false) + private double max; + + protected MeterValue(@NonNull final Id id, @NonNull final ComparableQuantity value) { + this.id = id; + this.min = UnitHelper.convert(value, id.meter.getSeries().getUnit()).getValue().doubleValue(); + this.max = this.min; + } + + public void update(@NonNull final ComparableQuantity value) { + final double converted = UnitHelper.convert(value, id.meter.getSeries().getUnit()).getValue().doubleValue(); + if (converted < min) { + throw new RuntimeException("Meter cannot run backwards: MeterValue=%s, inbound=%s".formatted(this, value)); + } + min = Math.min(converted, min); + max = Math.max(converted, max); + } + + @Getter + @Embeddable + @EqualsAndHashCode + @NoArgsConstructor + public static class Id { + + @NonNull + @ToString.Exclude + @ManyToOne(optional = false) + private Meter meter; + + @NonNull + @ToString.Include + public String series() { + return meter.series(); + } + + @NonNull + @ToString.Include + public String meter() { + return meter.getNumber(); + } + + @NonNull + @Column(nullable = false, updatable = false) + private ZonedDateTime date; + + public Id(@NonNull final Interval interval, @NonNull final ZonedDateTime date, @NonNull final Meter meter) { + this.date = new Aligned(date, interval).date; + this.meter = meter; + } + + @Override + public String toString() { + return "%s()"; + } + + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/MeterValueInbound.java b/src/main/java/de/ph87/home/meter/value/MeterValueInbound.java new file mode 100644 index 0000000..cff783a --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/MeterValueInbound.java @@ -0,0 +1,37 @@ +package de.ph87.home.meter.value; + +import lombok.Data; +import lombok.NonNull; +import tech.units.indriya.ComparableQuantity; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +@Data +public class MeterValueInbound { + + @NonNull + public final String name; + + @NonNull + public final String number; + + @NonNull + public final ZonedDateTime date; + + @NonNull + public final ComparableQuantity value; + + public MeterValueInbound(@NonNull final String name, @NonNull final String number, final long date, @NonNull final ComparableQuantity value) { + this(name, number, ZonedDateTime.ofInstant(Instant.ofEpochSecond(date), ZoneId.systemDefault()), value); + } + + public MeterValueInbound(@NonNull final String name, @NonNull final String number, @NonNull final ZonedDateTime date, @NonNull final ComparableQuantity value) { + this.name = name; + this.number = number; + this.date = date; + this.value = value; + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/MeterValueReceiver.java b/src/main/java/de/ph87/home/meter/value/MeterValueReceiver.java new file mode 100644 index 0000000..7537819 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/MeterValueReceiver.java @@ -0,0 +1,75 @@ +package de.ph87.home.meter.value; + +import de.ph87.home.meter.Meter; +import de.ph87.home.meter.MeterService; +import de.ph87.home.meter.value.day.MeterDay; +import de.ph87.home.meter.value.day.MeterDayRepository; +import de.ph87.home.meter.value.five.MeterFive; +import de.ph87.home.meter.value.five.MeterFiveRepository; +import de.ph87.home.meter.value.hour.MeterHour; +import de.ph87.home.meter.value.hour.MeterHourRepository; +import de.ph87.home.meter.value.month.MeterMonth; +import de.ph87.home.meter.value.month.MeterMonthRepository; +import de.ph87.home.meter.value.week.MeterWeek; +import de.ph87.home.meter.value.week.MeterWeekRepository; +import de.ph87.home.meter.value.year.MeterYear; +import de.ph87.home.meter.value.year.MeterYearRepository; +import de.ph87.home.unit.Interval; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import tech.units.indriya.ComparableQuantity; + +import java.util.function.BiFunction; + +@Slf4j +@Service +@RequiredArgsConstructor +public class MeterValueReceiver { + + private final MeterFiveRepository meterFiveRepository; + + private final MeterHourRepository meterHourRepository; + + private final MeterDayRepository meterDayRepository; + + private final MeterWeekRepository meterWeekRepository; + + private final MeterMonthRepository meterMonthRepository; + + private final MeterYearRepository meterYearRepository; + + private final MeterService meterService; + + @Transactional + @EventListener + public void onMeterInbound(@NonNull final MeterValueInbound event) { + final Meter meter = meterService.getOrCreate(event); + updateOrCreate(event, meter, Interval.FIVE, MeterFive::new, meterFiveRepository); + updateOrCreate(event, meter, Interval.HOUR, MeterHour::new, meterHourRepository); + updateOrCreate(event, meter, Interval.DAY, MeterDay::new, meterDayRepository); + updateOrCreate(event, meter, Interval.WEEK, MeterWeek::new, meterWeekRepository); + updateOrCreate(event, meter, Interval.MONTH, MeterMonth::new, meterMonthRepository); + updateOrCreate(event, meter, Interval.YEAR, MeterYear::new, meterYearRepository); + } + + private void updateOrCreate( + @NonNull final MeterValueInbound event, + @NonNull final Meter meter, + @NonNull final Interval interval, + @NonNull final BiFunction, T> create, + @NonNull final MeterValueRepository repository + ) { + final MeterYear.Id id = new MeterValue.Id(interval, event.getDate(), meter); + repository + .findById(id) + .ifPresentOrElse( + existing -> existing.update(event.getValue()), + () -> repository.save(create.apply(id, event.getValue())) + ); + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/MeterValueRepository.java b/src/main/java/de/ph87/home/meter/value/MeterValueRepository.java new file mode 100644 index 0000000..1aebcd2 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/MeterValueRepository.java @@ -0,0 +1,17 @@ +package de.ph87.home.meter.value; + +import lombok.NonNull; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.ListCrudRepository; +import org.springframework.data.repository.NoRepositoryBean; + +import java.time.ZonedDateTime; +import java.util.List; + +@NoRepositoryBean +public interface MeterValueRepository extends ListCrudRepository { + + @Query("select new de.ph87.home.meter.value.MeterPoint(v.id.date, sum(v.max - v.min)) from #{#entityName} v where v.id.meter.series.id = :id and v.id.date >= :first and v.id.date <= :last group by v.id.date") + List points(@NonNull long id, @NonNull ZonedDateTime first, @NonNull ZonedDateTime last); + +} diff --git a/src/main/java/de/ph87/home/meter/value/MeterValueService.java b/src/main/java/de/ph87/home/meter/value/MeterValueService.java new file mode 100644 index 0000000..9e92cae --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/MeterValueService.java @@ -0,0 +1,47 @@ +package de.ph87.home.meter.value; + +import de.ph87.home.meter.value.day.MeterDayRepository; +import de.ph87.home.meter.value.five.MeterFiveRepository; +import de.ph87.home.meter.value.hour.MeterHourRepository; +import de.ph87.home.meter.value.month.MeterMonthRepository; +import de.ph87.home.meter.value.week.MeterWeekRepository; +import de.ph87.home.meter.value.year.MeterYearRepository; +import de.ph87.home.point.PointRequest; +import de.ph87.home.series.SeriesDto; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class MeterValueService { + + private final MeterFiveRepository meterFiveRepository; + + private final MeterHourRepository meterHourRepository; + + private final MeterDayRepository meterDayRepository; + + private final MeterWeekRepository meterWeekRepository; + + private final MeterMonthRepository meterMonthRepository; + + private final MeterYearRepository meterYearRepository; + + @NonNull + public List points(@NonNull final SeriesDto series, @NonNull final PointRequest request) { + return switch (request.getInner()) { + case FIVE -> meterFiveRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + case HOUR -> meterHourRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + case DAY -> meterDayRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + case WEEK -> meterWeekRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + case MONTH -> meterMonthRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + case YEAR -> meterYearRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + }; + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/day/MeterDay.java b/src/main/java/de/ph87/home/meter/value/day/MeterDay.java new file mode 100644 index 0000000..193081b --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/day/MeterDay.java @@ -0,0 +1,21 @@ +package de.ph87.home.meter.value.day; + +import de.ph87.home.meter.value.MeterValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class MeterDay extends MeterValue { + + public MeterDay(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/day/MeterDayRepository.java b/src/main/java/de/ph87/home/meter/value/day/MeterDayRepository.java new file mode 100644 index 0000000..ce17a9a --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/day/MeterDayRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.meter.value.day; + +import de.ph87.home.meter.value.MeterValueRepository; + +public interface MeterDayRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/home/meter/value/five/MeterFive.java b/src/main/java/de/ph87/home/meter/value/five/MeterFive.java new file mode 100644 index 0000000..7c3edf6 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/five/MeterFive.java @@ -0,0 +1,21 @@ +package de.ph87.home.meter.value.five; + +import de.ph87.home.meter.value.MeterValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class MeterFive extends MeterValue { + + public MeterFive(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/five/MeterFiveRepository.java b/src/main/java/de/ph87/home/meter/value/five/MeterFiveRepository.java new file mode 100644 index 0000000..d7faeb8 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/five/MeterFiveRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.meter.value.five; + +import de.ph87.home.meter.value.MeterValueRepository; + +public interface MeterFiveRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/home/meter/value/hour/MeterHour.java b/src/main/java/de/ph87/home/meter/value/hour/MeterHour.java new file mode 100644 index 0000000..55485e6 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/hour/MeterHour.java @@ -0,0 +1,21 @@ +package de.ph87.home.meter.value.hour; + +import de.ph87.home.meter.value.MeterValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class MeterHour extends MeterValue { + + public MeterHour(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/hour/MeterHourRepository.java b/src/main/java/de/ph87/home/meter/value/hour/MeterHourRepository.java new file mode 100644 index 0000000..2e447bb --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/hour/MeterHourRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.meter.value.hour; + +import de.ph87.home.meter.value.MeterValueRepository; + +public interface MeterHourRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/home/meter/value/month/MeterMonth.java b/src/main/java/de/ph87/home/meter/value/month/MeterMonth.java new file mode 100644 index 0000000..2e4b34a --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/month/MeterMonth.java @@ -0,0 +1,21 @@ +package de.ph87.home.meter.value.month; + +import de.ph87.home.meter.value.MeterValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class MeterMonth extends MeterValue { + + public MeterMonth(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/month/MeterMonthRepository.java b/src/main/java/de/ph87/home/meter/value/month/MeterMonthRepository.java new file mode 100644 index 0000000..430d62d --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/month/MeterMonthRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.meter.value.month; + +import de.ph87.home.meter.value.MeterValueRepository; + +public interface MeterMonthRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/home/meter/value/week/MeterWeek.java b/src/main/java/de/ph87/home/meter/value/week/MeterWeek.java new file mode 100644 index 0000000..74280f4 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/week/MeterWeek.java @@ -0,0 +1,21 @@ +package de.ph87.home.meter.value.week; + +import de.ph87.home.meter.value.MeterValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class MeterWeek extends MeterValue { + + public MeterWeek(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/week/MeterWeekRepository.java b/src/main/java/de/ph87/home/meter/value/week/MeterWeekRepository.java new file mode 100644 index 0000000..d9e966e --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/week/MeterWeekRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.meter.value.week; + +import de.ph87.home.meter.value.MeterValueRepository; + +public interface MeterWeekRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/home/meter/value/year/MeterYear.java b/src/main/java/de/ph87/home/meter/value/year/MeterYear.java new file mode 100644 index 0000000..0ed5547 --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/year/MeterYear.java @@ -0,0 +1,21 @@ +package de.ph87.home.meter.value.year; + +import de.ph87.home.meter.value.MeterValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class MeterYear extends MeterValue { + + public MeterYear(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/meter/value/year/MeterYearRepository.java b/src/main/java/de/ph87/home/meter/value/year/MeterYearRepository.java new file mode 100644 index 0000000..191447e --- /dev/null +++ b/src/main/java/de/ph87/home/meter/value/year/MeterYearRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.meter.value.year; + +import de.ph87.home.meter.value.MeterValueRepository; + +public interface MeterYearRepository extends MeterValueRepository { + +} diff --git a/src/main/java/de/ph87/home/point/PointController.java b/src/main/java/de/ph87/home/point/PointController.java new file mode 100644 index 0000000..b35a019 --- /dev/null +++ b/src/main/java/de/ph87/home/point/PointController.java @@ -0,0 +1,21 @@ +package de.ph87.home.point; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("points") +public class PointController { + + private final PointService pointService; + + @PostMapping + public PointResult request(@RequestBody final PointRequest request) { + return pointService.request(request); + } + +} diff --git a/src/main/java/de/ph87/home/point/PointRequest.java b/src/main/java/de/ph87/home/point/PointRequest.java new file mode 100644 index 0000000..0d01e67 --- /dev/null +++ b/src/main/java/de/ph87/home/point/PointRequest.java @@ -0,0 +1,45 @@ +package de.ph87.home.point; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import de.ph87.home.unit.Aligned; +import de.ph87.home.unit.Interval; +import lombok.Data; +import lombok.NonNull; + +import java.time.ZonedDateTime; + +@Data +public class PointRequest { + + @NonNull + public final String seriesName; + + public final long offset; + + public final long count; + + @NonNull + public final Interval outer; + + @NonNull + public final Interval inner; + + @NonNull + @JsonIgnore + public final Aligned beginIncluding; + + @NonNull + @JsonIgnore + public final Aligned endIncluding; + + public PointRequest(@NonNull final String seriesName, final long offset, final long count, @NonNull final Interval outer, @NonNull final Interval inner) { + this.seriesName = seriesName; + this.offset = offset; + this.count = count; + this.outer = outer; + this.inner = inner; + this.endIncluding = new Aligned(ZonedDateTime.now(), outer).minus(offset); + this.beginIncluding = endIncluding.minus(this.count - 1); + } + +} diff --git a/src/main/java/de/ph87/home/point/PointResult.java b/src/main/java/de/ph87/home/point/PointResult.java new file mode 100644 index 0000000..dd2bfcd --- /dev/null +++ b/src/main/java/de/ph87/home/point/PointResult.java @@ -0,0 +1,28 @@ +package de.ph87.home.point; + +import de.ph87.home.meter.value.MeterValue; +import de.ph87.home.series.SeriesDto; +import de.ph87.home.varying.VaryingValue; +import lombok.Data; +import lombok.Getter; +import lombok.NonNull; + +import java.time.ZonedDateTime; +import java.util.List; + +@Data +public class PointResult { + + public final SeriesDto series; + + public final List points; + + @Data + public static abstract class Point { + + @NonNull + public final ZonedDateTime date; + + } + +} diff --git a/src/main/java/de/ph87/home/point/PointService.java b/src/main/java/de/ph87/home/point/PointService.java new file mode 100644 index 0000000..ce1b36a --- /dev/null +++ b/src/main/java/de/ph87/home/point/PointService.java @@ -0,0 +1,35 @@ +package de.ph87.home.point; + +import de.ph87.home.meter.value.MeterValueService; +import de.ph87.home.series.SeriesDto; +import de.ph87.home.series.SeriesRepository; +import de.ph87.home.varying.VaryingValueService; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class PointService { + + private final SeriesRepository seriesRepository; + + private final MeterValueService meterValueService; + + private final VaryingValueService varyingValueService; + + @NonNull + public PointResult request(@NonNull final PointRequest request) { + final SeriesDto series = seriesRepository.findDtoByName(request.getSeriesName()).orElseThrow(); + final List points = switch (series.getType()) { + case METER -> meterValueService.points(series, request); + case VARYING -> varyingValueService.points(series, request); + }; + return new PointResult(series, points); + } + +} diff --git a/src/main/java/de/ph87/home/receive/HeizungHandler.java b/src/main/java/de/ph87/home/receive/HeizungHandler.java new file mode 100644 index 0000000..628d2e8 --- /dev/null +++ b/src/main/java/de/ph87/home/receive/HeizungHandler.java @@ -0,0 +1,62 @@ +package de.ph87.home.receive; + +import com.fasterxml.jackson.databind.ObjectMapper; +import de.ph87.home.varying.VaryingValueInbound; +import de.ph87.patrix.mqtt.MqttEvent; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import tech.units.indriya.ComparableQuantity; +import tech.units.indriya.quantity.Quantities; +import tech.units.indriya.unit.Units; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Slf4j +@Service +@RequiredArgsConstructor +public class HeizungHandler { + + private static final Pattern REGEX = Pattern.compile("^aggregation/(?heizung/.+)/temperatur$"); + + private final ObjectMapper objectMapper; + + private final ApplicationEventPublisher applicationEventPublisher; + + @EventListener + public void handle(@NonNull final MqttEvent event) throws Exception { + final Matcher matcher = REGEX.matcher(event.topic); + if (!matcher.find()) { + return; + } + final String property = matcher.group("property"); + final Inbound inbound = objectMapper.readValue(event.payload, Inbound.class); + applicationEventPublisher.publishEvent(new VaryingValueInbound(property, inbound.date, inbound.value)); + } + + @Getter + @ToString + private static class Inbound { + + @NonNull + public final ZonedDateTime date; + + public final ComparableQuantity value; + + public Inbound(final long timestamp, final double sum, final int count) { + this.date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault()); + this.value = Quantities.getQuantity(sum / count, Units.CELSIUS); + } + + } + +} diff --git a/src/main/java/de/ph87/home/receive/OpenDTUHandler.java b/src/main/java/de/ph87/home/receive/OpenDTUHandler.java new file mode 100644 index 0000000..9469de5 --- /dev/null +++ b/src/main/java/de/ph87/home/receive/OpenDTUHandler.java @@ -0,0 +1,83 @@ +package de.ph87.home.receive; + +import com.fasterxml.jackson.databind.ObjectMapper; +import de.ph87.home.meter.value.MeterValueInbound; +import de.ph87.home.varying.VaryingValueInbound; +import de.ph87.patrix.mqtt.MqttEvent; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import tech.units.indriya.ComparableQuantity; +import tech.units.indriya.quantity.Quantities; +import tech.units.indriya.unit.Units; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import static de.ph87.home.unit.UnitHelper.ENERGY_KILOWATT_HOUR; + +@Slf4j +@Service +@RequiredArgsConstructor +public class OpenDTUHandler { + + private final ObjectMapper objectMapper; + + private final ApplicationEventPublisher applicationEventPublisher; + + @EventListener + public void handle(@NonNull final MqttEvent event) throws Exception { + if (!"openDTU/pv/patrix/json2".equals(event.topic)) { + return; + } + final Inbound inbound = objectMapper.readValue(event.payload, Inbound.class); + applicationEventPublisher.publishEvent(new VaryingValueInbound("electricity/producing", inbound.date, inbound.producing)); + applicationEventPublisher.publishEvent(new MeterValueInbound("electricity/produced", inbound.inverter, inbound.date, inbound.produced)); + applicationEventPublisher.publishEvent(new VaryingValueInbound("electricity/producing/string0", inbound.date, inbound.producing0)); + applicationEventPublisher.publishEvent(new MeterValueInbound("electricity/produced/string0", inbound.inverter, inbound.date, inbound.produced0)); + applicationEventPublisher.publishEvent(new VaryingValueInbound("electricity/producing/string1", inbound.date, inbound.producing1)); + applicationEventPublisher.publishEvent(new MeterValueInbound("electricity/produced/string1", inbound.inverter, inbound.date, inbound.produced1)); + } + + @Getter + @ToString + public static class Inbound { + + @NonNull + public final String inverter; + + @NonNull + public final ZonedDateTime date; + + public final ComparableQuantity produced; + + public final ComparableQuantity producing; + + public final ComparableQuantity produced0; + + public final ComparableQuantity producing0; + + public final ComparableQuantity produced1; + + public final ComparableQuantity producing1; + + public Inbound(final long timestamp, @NonNull final String inverter, final double totalKWh, final double totalW, final double string0KWh, final double string0W, final double string1KWh, final double string1W) { + this.inverter = inverter; + this.date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault()); + this.produced = Quantities.getQuantity(totalKWh, ENERGY_KILOWATT_HOUR); + this.producing = Quantities.getQuantity(totalW, Units.WATT); + this.produced0 = Quantities.getQuantity(string0KWh, ENERGY_KILOWATT_HOUR); + this.producing0 = Quantities.getQuantity(string0W, Units.WATT); + this.produced1 = Quantities.getQuantity(string1KWh, ENERGY_KILOWATT_HOUR); + this.producing1 = Quantities.getQuantity(string1W, Units.WATT); + } + + } + +} diff --git a/src/main/java/de/ph87/home/receive/PatrixJsonHandler.java b/src/main/java/de/ph87/home/receive/PatrixJsonHandler.java new file mode 100644 index 0000000..d9c2b97 --- /dev/null +++ b/src/main/java/de/ph87/home/receive/PatrixJsonHandler.java @@ -0,0 +1,73 @@ +package de.ph87.home.receive; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.ph87.home.meter.value.MeterValueInbound; +import de.ph87.home.unit.PatrixUnit; +import de.ph87.home.varying.VaryingValueInbound; +import de.ph87.patrix.mqtt.MqttEvent; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import tech.units.indriya.quantity.Quantities; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +@Slf4j +@Service +@RequiredArgsConstructor +public class PatrixJsonHandler { + + private final ObjectMapper objectMapper; + + private final ApplicationEventPublisher applicationEventPublisher; + + @EventListener + public void handle(@NonNull final MqttEvent event) throws Exception { + if (!event.topic.endsWith("/PatrixJson")) { + return; + } + final Inbound inbound = objectMapper.readValue(event.payload, Inbound.class); + applicationEventPublisher.publishEvent(inbound); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") + @JsonSubTypes({ + @JsonSubTypes.Type(name = "VARYING", value = Varying.class), + @JsonSubTypes.Type(name = "METER", value = Meter.class), + }) + private interface Inbound { + + enum Type { + VARYING, + METER, + } + + } + + @ToString(callSuper = true) + public static class Varying extends VaryingValueInbound implements Inbound { + + protected Varying(@NonNull final String name, final long date, final double value, @NonNull final PatrixUnit unit) { + super(name, ZonedDateTime.ofInstant(Instant.ofEpochSecond(date), ZoneId.systemDefault()), Quantities.getQuantity(value, unit.unit)); + } + + } + + @ToString(callSuper = true) + public static class Meter extends MeterValueInbound implements Inbound { + + protected Meter(@NonNull final String name, @NonNull final String number, final long date, final double value, @NonNull final PatrixUnit unit) { + super(name, number, ZonedDateTime.ofInstant(Instant.ofEpochSecond(date), ZoneId.systemDefault()), Quantities.getQuantity(value, unit.unit)); + } + + } + +} diff --git a/src/main/java/de/ph87/home/receive/SmartMeterHandler.java b/src/main/java/de/ph87/home/receive/SmartMeterHandler.java new file mode 100644 index 0000000..233fb44 --- /dev/null +++ b/src/main/java/de/ph87/home/receive/SmartMeterHandler.java @@ -0,0 +1,86 @@ +package de.ph87.home.receive; + +import com.fasterxml.jackson.databind.ObjectMapper; +import de.ph87.home.meter.value.MeterValueInbound; +import de.ph87.home.varying.VaryingValueInbound; +import de.ph87.patrix.mqtt.MqttEvent; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import tech.units.indriya.ComparableQuantity; +import tech.units.indriya.quantity.Quantities; + +import javax.measure.quantity.Energy; +import javax.measure.quantity.Power; +import java.math.BigDecimal; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import static de.ph87.home.unit.UnitHelper.*; +import static tech.units.indriya.unit.Units.WATT; + +@Slf4j +@Service +@RequiredArgsConstructor +public class SmartMeterHandler { + + private final ObjectMapper objectMapper; + + private final ApplicationEventPublisher applicationEventPublisher; + + @EventListener + public void handle(@NonNull final MqttEvent event) throws Exception { + if (!"electricity/grid/json".equals(event.topic)) { + return; + } + final Inbound inbound = objectMapper.readValue(event.payload, Inbound.class); + applicationEventPublisher.publishEvent(new MeterValueInbound("electricity/purchased", inbound.number, inbound.date, inbound.purchased)); + applicationEventPublisher.publishEvent(new MeterValueInbound("electricity/delivered", inbound.number, inbound.date, inbound.delivered)); + applicationEventPublisher.publishEvent(new VaryingValueInbound("electricity/purchasing", inbound.date, inbound.purchasing)); + applicationEventPublisher.publishEvent(new VaryingValueInbound("electricity/delivering", inbound.date, inbound.delivering)); + } + + @Getter + @ToString + public static class Inbound { + + @NonNull + public String number = "1ZPA0020300305"; // TODO implement in smart-meter firmware + + @NonNull + public final ZonedDateTime date; + + public final ComparableQuantity purchased; + + @NonNull + public final ComparableQuantity delivered; + + @NonNull + public final ComparableQuantity purchasing; + + @NonNull + public final ComparableQuantity delivering; + + public Inbound( + final long timestamp, + @NonNull final BigDecimal purchaseWh, + @NonNull final BigDecimal deliveryWh, + @NonNull final BigDecimal powerW + ) { + this.date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault()); + this.purchased = Quantities.getQuantity(purchaseWh, ENERGY_WATT_HOUR); + this.delivered = Quantities.getQuantity(deliveryWh, ENERGY_WATT_HOUR); + final ComparableQuantity power = Quantities.getQuantity(powerW, WATT); + this.purchasing = zeroIfNegative(power); + this.delivering = zeroIfNegative(negate(power)); + } + + } + +} diff --git a/src/main/java/de/ph87/home/series/Series.java b/src/main/java/de/ph87/home/series/Series.java new file mode 100644 index 0000000..7f8aa26 --- /dev/null +++ b/src/main/java/de/ph87/home/series/Series.java @@ -0,0 +1,49 @@ +package de.ph87.home.series; + +import de.ph87.home.unit.PatrixUnit; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class Series { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Version + @ToString.Exclude + private long version; + + @NonNull + @Column(nullable = false) + private String name; + + @NonNull + @Column(nullable = false) + private String title; + + @NonNull + @Column(nullable = false, updatable = false) + @Enumerated(EnumType.STRING) + private Type type; + + @NonNull + @Column(nullable = false, updatable = false) + @Enumerated(EnumType.STRING) + private PatrixUnit unit; + + public Series(@NonNull final String name, @NonNull final Type type, @NonNull final PatrixUnit unit) { + this.name = name; + this.title = name; + this.type = type; + this.unit = unit; + } + +} diff --git a/src/main/java/de/ph87/home/series/SeriesDto.java b/src/main/java/de/ph87/home/series/SeriesDto.java new file mode 100644 index 0000000..6fa6548 --- /dev/null +++ b/src/main/java/de/ph87/home/series/SeriesDto.java @@ -0,0 +1,32 @@ +package de.ph87.home.series; + +import de.ph87.home.unit.PatrixUnit; +import lombok.Data; +import lombok.NonNull; + +@Data +public class SeriesDto { + + public final long id; + + @NonNull + public final String name; + + @NonNull + public final String title; + + @NonNull + private final Type type; + + @NonNull + public final PatrixUnit unit; + + public SeriesDto(@NonNull final Series series) { + this.id = series.getId(); + this.name = series.getName(); + this.title = series.getTitle(); + this.type = series.getType(); + this.unit = series.getUnit(); + } + +} diff --git a/src/main/java/de/ph87/home/series/SeriesRepository.java b/src/main/java/de/ph87/home/series/SeriesRepository.java new file mode 100644 index 0000000..a2053bb --- /dev/null +++ b/src/main/java/de/ph87/home/series/SeriesRepository.java @@ -0,0 +1,16 @@ +package de.ph87.home.series; + +import lombok.NonNull; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.ListCrudRepository; + +import java.util.Optional; + +public interface SeriesRepository extends ListCrudRepository { + + Optional findByName(@NonNull String name); + + @Query("select new de.ph87.home.series.SeriesDto(s) from Series s where s.name = :name") + Optional findDtoByName(@NonNull String name); + +} diff --git a/src/main/java/de/ph87/home/series/SeriesService.java b/src/main/java/de/ph87/home/series/SeriesService.java new file mode 100644 index 0000000..9ac0678 --- /dev/null +++ b/src/main/java/de/ph87/home/series/SeriesService.java @@ -0,0 +1,29 @@ +package de.ph87.home.series; + +import de.ph87.home.unit.PatrixUnit; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.measure.Unit; + +@Slf4j +@Service +@RequiredArgsConstructor +public class SeriesService { + + private final SeriesRepository seriesRepository; + + @NonNull + @Transactional + public Series getOrCreate(@NonNull final String name, final @NonNull Type type, @NonNull final Unit unit) { + return seriesRepository.findByName(name).orElseGet(() -> { + final Series fresh = new Series(name, type, PatrixUnit.from(unit)); + log.info("Series CREATED: {}", fresh); + return seriesRepository.save(fresh); + }); + } + +} diff --git a/src/main/java/de/ph87/home/series/Type.java b/src/main/java/de/ph87/home/series/Type.java new file mode 100644 index 0000000..84c97e4 --- /dev/null +++ b/src/main/java/de/ph87/home/series/Type.java @@ -0,0 +1,6 @@ +package de.ph87.home.series; + +public enum Type { + VARYING, + METER, +} diff --git a/src/main/java/de/ph87/home/unit/Aligned.java b/src/main/java/de/ph87/home/unit/Aligned.java new file mode 100644 index 0000000..d9ea628 --- /dev/null +++ b/src/main/java/de/ph87/home/unit/Aligned.java @@ -0,0 +1,32 @@ +package de.ph87.home.unit; + +import lombok.Data; +import lombok.NonNull; + +import java.time.ZonedDateTime; + +@Data +public class Aligned { + + @NonNull + public final ZonedDateTime date; + + @NonNull + public final Interval interval; + + public Aligned(@NonNull final ZonedDateTime date, @NonNull final Interval interval) { + this.date = interval.align.apply(date); + this.interval = interval; + } + + public Aligned(@NonNull final Aligned aligned, final long plusAmount) { + this.date = aligned.interval.plus.apply(aligned.date, plusAmount); + this.interval = aligned.interval; + } + + @NonNull + public Aligned minus(final long amount) { + return new Aligned(this, -amount); + } + +} diff --git a/src/main/java/de/ph87/home/unit/Interval.java b/src/main/java/de/ph87/home/unit/Interval.java new file mode 100644 index 0000000..881f71a --- /dev/null +++ b/src/main/java/de/ph87/home/unit/Interval.java @@ -0,0 +1,29 @@ +package de.ph87.home.unit; + +import lombok.NonNull; + +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.function.BiFunction; +import java.util.function.Function; + +public enum Interval { + FIVE(date -> date.truncatedTo(ChronoUnit.MINUTES).minusMinutes(date.getMinute() % 5), (date, amount) -> date.plusMinutes(5 * amount)), + HOUR(date -> date.truncatedTo(ChronoUnit.HOURS), ZonedDateTime::plusHours), + DAY(date -> date.truncatedTo(ChronoUnit.DAYS), ZonedDateTime::plusDays), + WEEK(date -> date.truncatedTo(ChronoUnit.DAYS).minusDays(date.getDayOfWeek().getValue() - 1), (date, amount) -> date.plusDays(7 * amount)), + MONTH(date -> date.truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1), ZonedDateTime::plusMonths), + YEAR(date -> date.truncatedTo(ChronoUnit.DAYS).withDayOfYear(1), ZonedDateTime::plusYears), + ; + + @NonNull + public final Function align; + + public final BiFunction plus; + + Interval(@NonNull final Function align, final BiFunction plus) { + this.align = align; + this.plus = plus; + } + +} diff --git a/src/main/java/de/ph87/home/unit/PatrixUnit.java b/src/main/java/de/ph87/home/unit/PatrixUnit.java new file mode 100644 index 0000000..179da53 --- /dev/null +++ b/src/main/java/de/ph87/home/unit/PatrixUnit.java @@ -0,0 +1,45 @@ +package de.ph87.home.unit; + +import lombok.NonNull; +import tech.units.indriya.AbstractUnit; +import tech.units.indriya.unit.Units; + +import javax.measure.Quantity; +import javax.measure.Unit; +import java.util.Arrays; + +import static de.ph87.home.unit.UnitHelper.ENERGY_KILOWATT_HOUR; + +public enum PatrixUnit { + BOOLEAN(AbstractUnit.ONE), + VOLUME_L(Units.LITRE), + VOLUME_MM3(Units.LITRE.divide(1000000)), + LENGTH_CM(Units.METRE.divide(100)), + + POWER_W(Units.WATT), + ENERGY_KWH(ENERGY_KILOWATT_HOUR), + + // TODO + IAQ(AbstractUnit.ONE), + IAQ_CO2_EQUIVALENT(AbstractUnit.ONE), + IAQ_VOC_EQUIVALENT(AbstractUnit.ONE), + PRESSURE_HPA(AbstractUnit.ONE), + TEMPERATURE_C(Units.CELSIUS), + HUMIDITY_RELATIVE_PERCENT(AbstractUnit.ONE), + HUMIDITY_ABSOLUTE_GM3(AbstractUnit.ONE), + SUN_DC(AbstractUnit.ONE), + ; + + @NonNull + public final Unit> unit; + + > PatrixUnit(@NonNull final Unit unit) { + this.unit = unit; + } + + @NonNull + public static PatrixUnit from(@NonNull final Unit unit) { + return Arrays.stream(values()).filter(u -> u.unit.isCompatible(unit)).findFirst().orElseThrow(); + } + +} diff --git a/src/main/java/de/ph87/home/unit/QuantityConverter.java b/src/main/java/de/ph87/home/unit/QuantityConverter.java new file mode 100644 index 0000000..390e2e4 --- /dev/null +++ b/src/main/java/de/ph87/home/unit/QuantityConverter.java @@ -0,0 +1,28 @@ +package de.ph87.home.unit; + +import jakarta.annotation.Nullable; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import tech.units.indriya.format.SimpleQuantityFormat; + +import javax.measure.Quantity; +import javax.measure.format.QuantityFormat; + +@Converter(autoApply = true) +public class QuantityConverter implements AttributeConverter, String> { + + public static final QuantityFormat FORMAT = SimpleQuantityFormat.getInstance(); + + @Nullable + @Override + public String convertToDatabaseColumn(@Nullable Quantity quantity) { + return quantity == null ? null : FORMAT.format(quantity); + } + + @Nullable + @Override + public Quantity convertToEntityAttribute(@Nullable String string) { + return string == null ? null : FORMAT.parse(string); + } + +} \ No newline at end of file diff --git a/src/main/java/de/ph87/home/unit/UnitHelper.java b/src/main/java/de/ph87/home/unit/UnitHelper.java new file mode 100644 index 0000000..731d39a --- /dev/null +++ b/src/main/java/de/ph87/home/unit/UnitHelper.java @@ -0,0 +1,41 @@ +package de.ph87.home.unit; + +import lombok.NonNull; +import tech.units.indriya.ComparableQuantity; +import tech.units.indriya.quantity.Quantities; + +import javax.measure.MetricPrefix; +import javax.measure.Quantity; +import javax.measure.Unit; +import javax.measure.quantity.Energy; + +import static tech.units.indriya.unit.Units.HOUR; +import static tech.units.indriya.unit.Units.WATT; + +public class UnitHelper { + + public static final Unit ENERGY_WATT_HOUR = WATT.multiply(HOUR).asType(Energy.class); + + public static final Unit ENERGY_KILOWATT_HOUR = MetricPrefix.KILO(ENERGY_WATT_HOUR); + + @NonNull + public static > ComparableQuantity zeroIfNegative(final ComparableQuantity value) { + final ComparableQuantity zero = Quantities.getQuantity(0, value.getUnit()); + return value.isGreaterThanOrEqualTo(zero) ? value : zero; + } + + @NonNull + public static > ComparableQuantity negate(final ComparableQuantity value) { + return value.multiply(-1); + } + + @NonNull + public static > ComparableQuantity convert(@NonNull final ComparableQuantity value, @NonNull final PatrixUnit unit) { + if (!value.getUnit().isCompatible(unit.unit)) { + throw new RuntimeException("Incompatible units: value=%s, targetUnit=%s".formatted(value, unit)); + } + //noinspection unchecked + return ((ComparableQuantity) value).to((Unit) unit.unit); + } + +} diff --git a/src/main/java/de/ph87/home/varying/VaryingPoint.java b/src/main/java/de/ph87/home/varying/VaryingPoint.java new file mode 100644 index 0000000..b83ad95 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/VaryingPoint.java @@ -0,0 +1,25 @@ +package de.ph87.home.varying; + +import de.ph87.home.point.PointResult; +import lombok.Getter; +import lombok.NonNull; + +import java.time.ZonedDateTime; + +@Getter +public class VaryingPoint extends PointResult.Point { + + public final double min; + + public final double max; + + public final double avg; + + public VaryingPoint(@NonNull final ZonedDateTime date, final double min, final double max, final double avg) { + super(date); + this.min = min; + this.max = max; + this.avg = avg; + } + +} diff --git a/src/main/java/de/ph87/home/varying/VaryingValue.java b/src/main/java/de/ph87/home/varying/VaryingValue.java new file mode 100644 index 0000000..87de073 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/VaryingValue.java @@ -0,0 +1,80 @@ +package de.ph87.home.varying; + +import de.ph87.home.series.Series; +import de.ph87.home.unit.Aligned; +import de.ph87.home.unit.Interval; +import jakarta.persistence.*; +import lombok.*; +import tech.units.indriya.ComparableQuantity; + +import java.time.ZonedDateTime; + +import static de.ph87.home.unit.UnitHelper.convert; + +@Getter +@ToString +@MappedSuperclass +@NoArgsConstructor +public abstract class VaryingValue { + + @EmbeddedId + private Id id; + + @Column(nullable = false) + private double min; + + @Column(nullable = false) + private double max; + + @Column(nullable = false) + private double avg; + + @Column(nullable = false) + private long count; + + protected VaryingValue(@NonNull final Id id, @NonNull final ComparableQuantity value) { + this.id = id; + final double converted = convert(value, id.series.getUnit()).getValue().doubleValue(); + this.min = converted; + this.max = converted; + this.avg = converted; + } + + public void update(@NonNull final ComparableQuantity value) { + final double converted = convert(value, id.series.getUnit()).getValue().doubleValue(); + min = Math.min(min, converted); + max = Math.max(max, converted); + avg = (avg * count + converted) / (count + 1); + count++; + } + + @Getter + @ToString + @Embeddable + @EqualsAndHashCode + @NoArgsConstructor + public static class Id { + + @NonNull + @ToString.Exclude + @ManyToOne(optional = false) + private Series series; + + @NonNull + @ToString.Include + public String series() { + return series.getName(); + } + + @NonNull + @Column(nullable = false, updatable = false) + private ZonedDateTime date; + + public Id(@NonNull final Interval interval, @NonNull final ZonedDateTime date, @NonNull final Series series) { + this.date = new Aligned(date, interval).date; + this.series = series; + } + + } + +} diff --git a/src/main/java/de/ph87/home/varying/VaryingValueInbound.java b/src/main/java/de/ph87/home/varying/VaryingValueInbound.java new file mode 100644 index 0000000..aee342b --- /dev/null +++ b/src/main/java/de/ph87/home/varying/VaryingValueInbound.java @@ -0,0 +1,33 @@ +package de.ph87.home.varying; + +import lombok.Data; +import lombok.NonNull; +import tech.units.indriya.ComparableQuantity; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +@Data +public class VaryingValueInbound { + + @NonNull + public final String name; + + @NonNull + public final ZonedDateTime date; + + @NonNull + public final ComparableQuantity value; + + public VaryingValueInbound(@NonNull final String name, final long date, @NonNull final ComparableQuantity value) { + this(name, ZonedDateTime.ofInstant(Instant.ofEpochSecond(date), ZoneId.systemDefault()), value); + } + + public VaryingValueInbound(@NonNull final String name, @NonNull final ZonedDateTime date, @NonNull final ComparableQuantity value) { + this.name = name; + this.date = date; + this.value = value; + } + +} diff --git a/src/main/java/de/ph87/home/varying/VaryingValueReceiver.java b/src/main/java/de/ph87/home/varying/VaryingValueReceiver.java new file mode 100644 index 0000000..12b4bd9 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/VaryingValueReceiver.java @@ -0,0 +1,76 @@ +package de.ph87.home.varying; + +import de.ph87.home.series.Series; +import de.ph87.home.series.SeriesService; +import de.ph87.home.series.Type; +import de.ph87.home.unit.Interval; +import de.ph87.home.varying.day.VaryingDay; +import de.ph87.home.varying.day.VaryingDayRepository; +import de.ph87.home.varying.five.VaryingFive; +import de.ph87.home.varying.five.VaryingFiveRepository; +import de.ph87.home.varying.hour.VaryingHour; +import de.ph87.home.varying.hour.VaryingHourRepository; +import de.ph87.home.varying.month.VaryingMonth; +import de.ph87.home.varying.month.VaryingMonthRepository; +import de.ph87.home.varying.week.VaryingWeek; +import de.ph87.home.varying.week.VaryingWeekRepository; +import de.ph87.home.varying.year.VaryingYear; +import de.ph87.home.varying.year.VaryingYearRepository; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import tech.units.indriya.ComparableQuantity; + +import java.util.function.BiFunction; + +@Slf4j +@Service +@RequiredArgsConstructor +public class VaryingValueReceiver { + + private final VaryingFiveRepository varyingFiveRepository; + + private final VaryingHourRepository varyingHourRepository; + + private final VaryingDayRepository varyingDayRepository; + + private final VaryingWeekRepository varyingWeekRepository; + + private final VaryingMonthRepository varyingMonthRepository; + + private final VaryingYearRepository varyingYearRepository; + + private final SeriesService seriesService; + + @Transactional + @EventListener + public void onVaryingInbound(@NonNull final VaryingValueInbound event) { + final Series series = seriesService.getOrCreate(event.getName(), Type.VARYING, event.value.getUnit()); + updateOrCreate(event, series, Interval.FIVE, VaryingFive::new, varyingFiveRepository); + updateOrCreate(event, series, Interval.HOUR, VaryingHour::new, varyingHourRepository); + updateOrCreate(event, series, Interval.DAY, VaryingDay::new, varyingDayRepository); + updateOrCreate(event, series, Interval.WEEK, VaryingWeek::new, varyingWeekRepository); + updateOrCreate(event, series, Interval.MONTH, VaryingMonth::new, varyingMonthRepository); + updateOrCreate(event, series, Interval.YEAR, VaryingYear::new, varyingYearRepository); + } + + private void updateOrCreate( + @NonNull final VaryingValueInbound event, + @NonNull final Series series, + @NonNull final Interval interval, + @NonNull final BiFunction, T> create, + @NonNull final VaryingValueRepository repository + ) { + final VaryingYear.Id id = new VaryingValue.Id(interval, event.getDate(), series); + repository + .findById(id) + .ifPresentOrElse( + existing -> existing.update(event.getValue()), + () -> repository.save(create.apply(id, event.getValue())) + ); + } + +} diff --git a/src/main/java/de/ph87/home/varying/VaryingValueRepository.java b/src/main/java/de/ph87/home/varying/VaryingValueRepository.java new file mode 100644 index 0000000..eb9dec9 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/VaryingValueRepository.java @@ -0,0 +1,17 @@ +package de.ph87.home.varying; + +import lombok.NonNull; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.ListCrudRepository; +import org.springframework.data.repository.NoRepositoryBean; + +import java.time.ZonedDateTime; +import java.util.List; + +@NoRepositoryBean +public interface VaryingValueRepository extends ListCrudRepository { + + @Query("select new de.ph87.home.varying.VaryingPoint(v.id.date, v.min, v.max, v.avg) from #{#entityName} v where v.id.series.id = :id and v.id.date >= :begin and v.id.date < :end") + List points(@NonNull final long seriesId, @NonNull final ZonedDateTime begin, @NonNull final ZonedDateTime end); + +} diff --git a/src/main/java/de/ph87/home/varying/VaryingValueService.java b/src/main/java/de/ph87/home/varying/VaryingValueService.java new file mode 100644 index 0000000..a9517cc --- /dev/null +++ b/src/main/java/de/ph87/home/varying/VaryingValueService.java @@ -0,0 +1,45 @@ +package de.ph87.home.varying; + +import de.ph87.home.point.PointRequest; +import de.ph87.home.series.SeriesDto; +import de.ph87.home.varying.day.VaryingDayRepository; +import de.ph87.home.varying.five.VaryingFiveRepository; +import de.ph87.home.varying.hour.VaryingHourRepository; +import de.ph87.home.varying.month.VaryingMonthRepository; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class VaryingValueService { + + private final VaryingFiveRepository varyingFiveRepository; + + private final VaryingHourRepository varyingHourRepository; + + private final VaryingDayRepository varyingDayRepository; + + private final de.ph87.home.varying.week.VaryingWeekRepository varyingWeekRepository; + + private final VaryingMonthRepository varyingMonthRepository; + + private final de.ph87.home.varying.year.VaryingYearRepository varyingYearRepository; + + @NonNull + public List points(@NonNull final SeriesDto series, @NonNull final PointRequest request) { + return switch (request.getInner()) { + case FIVE -> varyingFiveRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + case HOUR -> varyingHourRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + case DAY -> varyingDayRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + case WEEK -> varyingWeekRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + case MONTH -> varyingMonthRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + case YEAR -> varyingYearRepository.points(series.id, request.beginIncluding.date, request.endIncluding.date); + }; + } + +} diff --git a/src/main/java/de/ph87/home/varying/day/VaryingDay.java b/src/main/java/de/ph87/home/varying/day/VaryingDay.java new file mode 100644 index 0000000..6d1da22 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/day/VaryingDay.java @@ -0,0 +1,21 @@ +package de.ph87.home.varying.day; + +import de.ph87.home.varying.VaryingValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class VaryingDay extends VaryingValue { + + public VaryingDay(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/varying/day/VaryingDayRepository.java b/src/main/java/de/ph87/home/varying/day/VaryingDayRepository.java new file mode 100644 index 0000000..c0b2fd8 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/day/VaryingDayRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.varying.day; + +import de.ph87.home.varying.VaryingValueRepository; + +public interface VaryingDayRepository extends VaryingValueRepository { + +} diff --git a/src/main/java/de/ph87/home/varying/five/VaryingFive.java b/src/main/java/de/ph87/home/varying/five/VaryingFive.java new file mode 100644 index 0000000..8b2d714 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/five/VaryingFive.java @@ -0,0 +1,21 @@ +package de.ph87.home.varying.five; + +import de.ph87.home.varying.VaryingValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class VaryingFive extends VaryingValue { + + public VaryingFive(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/varying/five/VaryingFiveRepository.java b/src/main/java/de/ph87/home/varying/five/VaryingFiveRepository.java new file mode 100644 index 0000000..724bec2 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/five/VaryingFiveRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.varying.five; + +import de.ph87.home.varying.VaryingValueRepository; + +public interface VaryingFiveRepository extends VaryingValueRepository { + +} diff --git a/src/main/java/de/ph87/home/varying/hour/VaryingHour.java b/src/main/java/de/ph87/home/varying/hour/VaryingHour.java new file mode 100644 index 0000000..2f61435 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/hour/VaryingHour.java @@ -0,0 +1,21 @@ +package de.ph87.home.varying.hour; + +import de.ph87.home.varying.VaryingValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class VaryingHour extends VaryingValue { + + public VaryingHour(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/varying/hour/VaryingHourRepository.java b/src/main/java/de/ph87/home/varying/hour/VaryingHourRepository.java new file mode 100644 index 0000000..aca9a73 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/hour/VaryingHourRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.varying.hour; + +import de.ph87.home.varying.VaryingValueRepository; + +public interface VaryingHourRepository extends VaryingValueRepository { + +} diff --git a/src/main/java/de/ph87/home/varying/month/VaryingMonth.java b/src/main/java/de/ph87/home/varying/month/VaryingMonth.java new file mode 100644 index 0000000..9fa14d1 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/month/VaryingMonth.java @@ -0,0 +1,21 @@ +package de.ph87.home.varying.month; + +import de.ph87.home.varying.VaryingValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class VaryingMonth extends VaryingValue { + + public VaryingMonth(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/varying/month/VaryingMonthRepository.java b/src/main/java/de/ph87/home/varying/month/VaryingMonthRepository.java new file mode 100644 index 0000000..a579187 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/month/VaryingMonthRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.varying.month; + +import de.ph87.home.varying.VaryingValueRepository; + +public interface VaryingMonthRepository extends VaryingValueRepository { + +} diff --git a/src/main/java/de/ph87/home/varying/week/VaryingWeek.java b/src/main/java/de/ph87/home/varying/week/VaryingWeek.java new file mode 100644 index 0000000..71db82e --- /dev/null +++ b/src/main/java/de/ph87/home/varying/week/VaryingWeek.java @@ -0,0 +1,21 @@ +package de.ph87.home.varying.week; + +import de.ph87.home.varying.VaryingValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class VaryingWeek extends VaryingValue { + + public VaryingWeek(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/varying/week/VaryingWeekRepository.java b/src/main/java/de/ph87/home/varying/week/VaryingWeekRepository.java new file mode 100644 index 0000000..2c10ab8 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/week/VaryingWeekRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.varying.week; + +import de.ph87.home.varying.VaryingValueRepository; + +public interface VaryingWeekRepository extends VaryingValueRepository { + +} diff --git a/src/main/java/de/ph87/home/varying/year/VaryingYear.java b/src/main/java/de/ph87/home/varying/year/VaryingYear.java new file mode 100644 index 0000000..a898546 --- /dev/null +++ b/src/main/java/de/ph87/home/varying/year/VaryingYear.java @@ -0,0 +1,21 @@ +package de.ph87.home.varying.year; + +import de.ph87.home.varying.VaryingValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import tech.units.indriya.ComparableQuantity; + +@Entity +@Getter +@NoArgsConstructor +@ToString(callSuper = true) +public class VaryingYear extends VaryingValue { + + public VaryingYear(@NonNull final Id id, @NonNull final ComparableQuantity value) { + super(id, value); + } + +} diff --git a/src/main/java/de/ph87/home/varying/year/VaryingYearRepository.java b/src/main/java/de/ph87/home/varying/year/VaryingYearRepository.java new file mode 100644 index 0000000..898627f --- /dev/null +++ b/src/main/java/de/ph87/home/varying/year/VaryingYearRepository.java @@ -0,0 +1,7 @@ +package de.ph87.home.varying.year; + +import de.ph87.home.varying.VaryingValueRepository; + +public interface VaryingYearRepository extends VaryingValueRepository { + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..f9fffb6 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,10 @@ +logging.level.root=WARN +logging.level.de.ph87=INFO +#- +spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl +spring.jpa.hibernate.ddl-auto=update +spring.jpa.open-in-view=false +#- +spring.jackson.serialization.indent_output=true +#- +spring.main.banner-mode=off