diff --git a/src/main/angular/src/app/location/detail/location-detail.ts b/src/main/angular/src/app/location/detail/location-detail.ts
index 9efeb5a..66ec39c 100644
--- a/src/main/angular/src/app/location/detail/location-detail.ts
+++ b/src/main/angular/src/app/location/detail/location-detail.ts
@@ -1,4 +1,4 @@
-import {Component, OnDestroy, OnInit} from '@angular/core';
+import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {LocationService} from '../location-service';
import {ActivatedRoute} from '@angular/router';
import {Location} from '../Location';
@@ -6,30 +6,45 @@ import {Text} from '../../shared/text/text';
import {Number} from '../../shared/number/number';
import {SeriesSelect} from '../../series/select/series-select';
import {Series} from '../../series/Series';
-import {SeriesService} from '../../series/series-service';
+import {Interval, SeriesService} from '../../series/series-service';
import {SeriesType} from '../../series/SeriesType';
import {Subscription, timer} from 'rxjs';
+import {SeriesHistory} from './history/series-history';
+
+function yesterday(now: any) {
+ const yesterday = new Date(now.getTime());
+ yesterday.setDate(yesterday.getDate() - 1);
+ return yesterday;
+}
@Component({
selector: 'app-location-detail',
imports: [
Text,
Number,
- SeriesSelect
+ SeriesSelect,
+ SeriesHistory
],
templateUrl: './location-detail.html',
styleUrl: './location-detail.less',
})
export class LocationDetail implements OnInit, OnDestroy {
+ @ViewChild("today")
+ protected today!: SeriesHistory;
+
+ protected readonly Interval = Interval;
+
+ protected location: Location | null = null;
+
private readonly subs: Subscription [] = [];
private series: Series[] = [];
- protected location: Location | null = null;
-
protected now: Date = new Date();
+ protected yesterday: Date = yesterday(this.now);
+
constructor(
readonly locationService: LocationService,
readonly seriesService: SeriesService,
@@ -44,7 +59,10 @@ export class LocationDetail implements OnInit, OnDestroy {
});
this.seriesService.findAll(list => this.series = list);
this.subs.push(this.seriesService.subscribe(this.updateSeries));
- this.subs.push(timer(1000, 1000).subscribe(() => this.now = new Date()));
+ this.subs.push(timer(1000, 1000).subscribe(() => {
+ this.now = new Date();
+ this.yesterday = yesterday(this.now);
+ }));
}
ngOnDestroy(): void {
@@ -64,6 +82,7 @@ export class LocationDetail implements OnInit, OnDestroy {
} else {
this.series.push(fresh);
}
+ this.today.updateSeries(fresh);
};
protected readonly filterEnergy = (): Series[] => {
diff --git a/src/main/angular/src/app/series/History.ts b/src/main/angular/src/app/series/History.ts
new file mode 100644
index 0000000..5bd46a0
--- /dev/null
+++ b/src/main/angular/src/app/series/History.ts
@@ -0,0 +1,22 @@
+import {getValueString, Series} from "./Series";
+import {or, validateNumber} from "../common";
+
+export class History {
+
+ readonly valueString: string;
+
+ constructor(
+ readonly series: Series,
+ readonly value: number | null,
+ ) {
+ this.valueString = getValueString(value, series);
+ }
+
+ static fromJson(json: any): History {
+ return new History(
+ Series.fromJson(json.series),
+ or(json.value, validateNumber, null),
+ );
+ }
+
+}
diff --git a/src/main/angular/src/app/series/Series.ts b/src/main/angular/src/app/series/Series.ts
index ca886e5..1020f3d 100644
--- a/src/main/angular/src/app/series/Series.ts
+++ b/src/main/angular/src/app/series/Series.ts
@@ -3,6 +3,10 @@ import {or, validateDate, validateEnum, validateNumber, validateString} from "..
import {SeriesType} from './SeriesType';
import {formatNumber} from '@angular/common';
+export function getValueString(value: number | null, series: Series): string {
+ return (value === null ? '-' : formatNumber(value, "de-DE", `0.${series.decimals}-${series.decimals}`)) + ' ' + series.unit;
+}
+
export class Series {
readonly valueString: string;
@@ -17,7 +21,7 @@ export class Series {
readonly unit: string,
readonly type: SeriesType,
) {
- this.valueString = (value === null ? '-' : formatNumber(value, "de-DE", `0.${decimals}-${decimals}`)) + ' ' + unit;
+ this.valueString = getValueString(value, this);
}
static fromJson(json: any): Series {
diff --git a/src/main/angular/src/app/series/series-service.ts b/src/main/angular/src/app/series/series-service.ts
index 289bd74..585acb7 100644
--- a/src/main/angular/src/app/series/series-service.ts
+++ b/src/main/angular/src/app/series/series-service.ts
@@ -1,18 +1,40 @@
-import {Injectable} from '@angular/core';
+import {Inject, Injectable, LOCALE_ID} from '@angular/core';
import {ApiService, CrudService, Next, WebsocketService} from '../common';
import {Series} from './Series';
+import {History} from './History';
+import {DatePipe} from '@angular/common';
+
+export enum Interval {
+ FIVE = 'FIVE',
+ HOUR = 'HOUR',
+ DAY = 'DAY',
+ WEEK = 'WEEK',
+ MONTH = 'MONTH',
+ YEAR = 'YEAR',
+}
@Injectable({
providedIn: 'root'
})
export class SeriesService extends CrudService {
- constructor(api: ApiService, ws: WebsocketService) {
+ private readonly datePipe: DatePipe;
+
+ constructor(
+ api: ApiService,
+ ws: WebsocketService,
+ @Inject(LOCALE_ID) readonly locale: string,
+ ) {
super(api, ws, ['Series'], Series.fromJson);
+ this.datePipe = new DatePipe(locale);
}
findAll(next: Next) {
this.getList(['findAll'], next);
}
+ history(series: Series, date: Date, interval: Interval, next: Next) {
+ this.api.getSingle([...this.path, series.id, 'history', Math.floor(date.getTime() / 1000), interval], History.fromJson, next);
+ }
+
}
diff --git a/src/main/angular/src/styles.less b/src/main/angular/src/styles.less
index d109b93..313a070 100644
--- a/src/main/angular/src/styles.less
+++ b/src/main/angular/src/styles.less
@@ -43,6 +43,7 @@ div {
.Section2 {
overflow: visible;
+
> .SectionHeading {
> .SectionHeadingText {
font-style: italic;
@@ -55,3 +56,39 @@ div {
padding-bottom: 0.5em;
margin-bottom: 0.5em;
}
+
+.Section3 {
+ border: 1px solid gray;
+ margin: 1em 0.5em 0.5em;
+ padding: 0.5em;
+ overflow: visible;
+
+ > .SectionHeading {
+ display: flex;
+ margin-top: -1.25em;
+
+ > .SectionHeadingText {
+ font-weight: bold;
+ background-color: white;
+ }
+ }
+
+ > .SectionBody {
+ display: flex;
+ }
+
+}
+
+.Section4 {
+ flex: 1;
+ text-align: center;
+
+ > .SectionHeadingText {
+ font-weight: bold;
+ background-color: white;
+ }
+
+ > .SectionBody {
+ }
+
+}
diff --git a/src/main/java/de/ph87/data/series/HistoryDto.java b/src/main/java/de/ph87/data/series/HistoryDto.java
new file mode 100644
index 0000000..6af0a8e
--- /dev/null
+++ b/src/main/java/de/ph87/data/series/HistoryDto.java
@@ -0,0 +1,22 @@
+package de.ph87.data.series;
+
+import de.ph87.data.series.point.AllSeriesPointResponse;
+import jakarta.annotation.Nullable;
+import lombok.Data;
+import lombok.NonNull;
+
+@Data
+public class HistoryDto {
+
+ @NonNull
+ public final SeriesDto series;
+
+ @Nullable
+ public final Double value;
+
+ public HistoryDto(final AllSeriesPointResponse.Entry entry) {
+ this.series = entry.getSeries();
+ this.value = entry.point == null ? null : entry.point.getValue();
+ }
+
+}
diff --git a/src/main/java/de/ph87/data/series/SeriesController.java b/src/main/java/de/ph87/data/series/SeriesController.java
index f4dbe3f..269324b 100644
--- a/src/main/java/de/ph87/data/series/SeriesController.java
+++ b/src/main/java/de/ph87/data/series/SeriesController.java
@@ -1,5 +1,6 @@
package de.ph87.data.series;
+import de.ph87.data.series.data.Interval;
import de.ph87.data.series.point.AllSeriesPointRequest;
import de.ph87.data.series.point.AllSeriesPointResponse;
import de.ph87.data.series.point.OneSeriesPointsRequest;
@@ -15,6 +16,9 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.List;
@CrossOrigin
@@ -64,11 +68,20 @@ public class SeriesController {
return seriesService.modify(id, series -> series.setType(type));
}
+ @NonNull
+ @GetMapping("{id}/history/{epochSeconds}/{intervalName}")
+ public HistoryDto type(@PathVariable final long id, @PathVariable @NonNull final long epochSeconds, @PathVariable @NonNull final String intervalName) {
+ final Interval interval = Interval.valueOf(intervalName);
+ return seriesPointService.history(id, ZonedDateTime.ofInstant(Instant.ofEpochSecond(epochSeconds), ZoneId.systemDefault()), interval);
+ }
+
+ @NonNull
@GetMapping("{id}")
public SeriesDto getById(@PathVariable final long id) {
return seriesRepository.getDtoById(id);
}
+ @NonNull
@GetMapping("findAll")
public List findAll() {
return seriesRepository.findAllDto();
@@ -80,6 +93,7 @@ public class SeriesController {
return seriesPointService.oneSeriesPoints(request);
}
+ @NonNull
@PostMapping("allSeriesPoint")
public AllSeriesPointResponse allSeriesPoint(@NonNull @RequestBody final AllSeriesPointRequest request) {
return seriesPointService.allSeriesPoint(request);
diff --git a/src/main/java/de/ph87/data/series/point/SeriesPointService.java b/src/main/java/de/ph87/data/series/point/SeriesPointService.java
index 3eb86c8..bc90953 100644
--- a/src/main/java/de/ph87/data/series/point/SeriesPointService.java
+++ b/src/main/java/de/ph87/data/series/point/SeriesPointService.java
@@ -1,17 +1,21 @@
package de.ph87.data.series.point;
+import de.ph87.data.series.HistoryDto;
import de.ph87.data.series.Series;
import de.ph87.data.series.SeriesDto;
import de.ph87.data.series.SeriesService;
+import de.ph87.data.series.data.Interval;
import de.ph87.data.series.data.bool.BoolService;
import de.ph87.data.series.data.delta.DeltaService;
import de.ph87.data.series.data.varying.VaryingService;
import jakarta.annotation.Nullable;
+import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
+import java.time.ZonedDateTime;
import java.util.List;
@Slf4j
@@ -66,4 +70,31 @@ public class SeriesPointService {
return points.stream().map(p -> p.times(factor)).toList();
}
+ @NonNull
+ public HistoryDto history(final long id, @NonNull final ZonedDateTime date, @NonNull final Interval interval) {
+ final Series series = seriesService.getById(id);
+ final AllSeriesPointResponse.Entry entry = map(series, new Request(date, interval));
+ return new HistoryDto(entry);
+ }
+
+ @Data
+ private static class Request implements ISeriesPointRequest {
+
+ @NonNull
+ public final Interval interval;
+
+ @NonNull
+ public final ZonedDateTime first;
+
+ @NonNull
+ public final ZonedDateTime after;
+
+ public Request(final @NonNull ZonedDateTime date, final @NonNull Interval interval) {
+ this.interval = interval;
+ this.first = interval.align.apply(date);
+ this.after = this.first.plus(interval.amount, interval.unit);
+ }
+
+ }
+
}