diff --git a/src/main/angular/src/app/api/astro/Astro.ts b/src/main/angular/src/app/api/astro/Astro.ts
new file mode 100644
index 0000000..a9123a8
--- /dev/null
+++ b/src/main/angular/src/app/api/astro/Astro.ts
@@ -0,0 +1,25 @@
+import {validateDateAllowNull, validateNumberNotNull, validateStringNullToEmpty} from "../validators";
+
+export class Astro {
+
+ constructor(
+ readonly zenith: number,
+ readonly sunrise: Date,
+ readonly sunset: Date,
+ readonly sunriseName: string,
+ readonly sunsetName: string,
+ ) {
+ // nothing
+ }
+
+ static fromJson(json: any): Astro {
+ return new Astro(
+ validateNumberNotNull(json['zenith']),
+ validateDateAllowNull(json['sunrise']),
+ validateDateAllowNull(json['sunset']),
+ validateStringNullToEmpty(json['sunriseName']),
+ validateStringNullToEmpty(json['sunsetName']),
+ );
+ }
+
+}
diff --git a/src/main/angular/src/app/api/schedule/Schedule.ts b/src/main/angular/src/app/api/schedule/Schedule.ts
index f4e94d9..b8f9326 100644
--- a/src/main/angular/src/app/api/schedule/Schedule.ts
+++ b/src/main/angular/src/app/api/schedule/Schedule.ts
@@ -1,17 +1,22 @@
import {validateBooleanNotNull, validateListOrEmpty, validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
import {ScheduleEntry} from "./entry/ScheduleEntry";
+import {Astro} from "../astro/Astro";
export class Schedule {
readonly next?: ScheduleEntry;
+ readonly last?: ScheduleEntry;
+
constructor(
readonly id: number,
readonly enabled: boolean,
readonly title: string,
readonly entries: ScheduleEntry[],
+ readonly astros: Astro[],
) {
this.next = entries.filter(e => e.nextFuzzyTimestamp).sort((a, b) => a.nextFuzzyTimestamp.date.getTime() - b.nextFuzzyTimestamp.date.getTime())[0];
+ this.last = entries.filter(e => e.lastFuzzyTimestamp).sort((a, b) => b.nextFuzzyTimestamp.date.getTime() - a.nextFuzzyTimestamp.date.getTime())[0];
}
static fromJson(json: any): Schedule {
@@ -20,6 +25,7 @@ export class Schedule {
validateBooleanNotNull(json['enabled']),
validateStringNotEmptyNotNull(json['title']),
validateListOrEmpty(json['entries'], ScheduleEntry.fromJson, ScheduleEntry.comparePosition),
+ validateListOrEmpty(json['astros'], Astro.fromJson),
);
}
diff --git a/src/main/angular/src/app/api/schedule/entry/ScheduleEntry.ts b/src/main/angular/src/app/api/schedule/entry/ScheduleEntry.ts
index 5cf70a4..6cc3154 100644
--- a/src/main/angular/src/app/api/schedule/entry/ScheduleEntry.ts
+++ b/src/main/angular/src/app/api/schedule/entry/ScheduleEntry.ts
@@ -29,6 +29,7 @@ export class ScheduleEntry {
readonly lastClearTimestamp: Timestamp | null,
readonly nextClearTimestamp: Timestamp | null,
readonly nextFuzzyTimestamp: Timestamp | null,
+ readonly lastFuzzyTimestamp: Timestamp | null,
readonly property: Property | null,
readonly value: number,
readonly bulk: Bulk | null,
@@ -57,6 +58,7 @@ export class ScheduleEntry {
Timestamp.fromDateOrNull(validateDateAllowNull(json['lastClearTimestamp'])),
Timestamp.fromDateOrNull(validateDateAllowNull(json['nextClearTimestamp'])),
Timestamp.fromDateOrNull(validateDateAllowNull(json['nextFuzzyTimestamp'])),
+ Timestamp.fromDateOrNull(validateDateAllowNull(json['lastFuzzyTimestamp'])),
Property.fromJsonAllowNull(json['property']),
validateNumberNotNull(json['value']),
Bulk.fromJsonOrNull(json['bulk']),
diff --git a/src/main/angular/src/app/app.module.ts b/src/main/angular/src/app/app.module.ts
index 76cab52..d5379d7 100644
--- a/src/main/angular/src/app/app.module.ts
+++ b/src/main/angular/src/app/app.module.ts
@@ -17,6 +17,8 @@ import {PropertyListComponent} from './pages/property/list/property-list.compone
import {ChannelListComponent} from './pages/channel/list/channel-list.component';
import {BulkListComponent} from './pages/bulk/list/bulk-list.component';
import {BulkEditorComponent} from './pages/bulk/editor/bulk-editor.component';
+import {LeftPadDirective} from './pipes/left-pad.directive';
+import {EntryValueComponent} from './shared/entry-value/entry-value.component';
@NgModule({
declarations: [
@@ -33,6 +35,8 @@ import {BulkEditorComponent} from './pages/bulk/editor/bulk-editor.component';
DeviceListComponent,
BulkListComponent,
BulkEditorComponent,
+ LeftPadDirective,
+ EntryValueComponent,
],
imports: [
BrowserModule,
diff --git a/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.html b/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.html
index 37bb452..595e729 100644
--- a/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.html
+++ b/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.html
@@ -55,23 +55,11 @@
|
+
|
|
@@ -85,7 +73,7 @@
: |
|
@@ -132,44 +120,10 @@
|
-
- 0 ? 0 : 1)">
- {{entry.value ? "An" : "Aus"}}
- |
-
-
- |
-
-
- |
-
-
- |
-
-
- |
-
-
- |
-
-
- |
-
+
+
+
+ |
diff --git a/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.ts b/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.ts
index 551ef3c..6d7cfee 100644
--- a/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.ts
+++ b/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.ts
@@ -6,8 +6,6 @@ import {ScheduleEntryService} from "../../../api/schedule/entry/schedule-entry.s
import {faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCircle, faTimesCircle} from '@fortawesome/free-regular-svg-icons';
import {ActivatedRoute, Router} from "@angular/router";
import {PropertyService} from "../../../api/property/property.service";
-import {Scene} from "../../../api/scene/Scene";
-import {SceneService} from "../../../api/scene/scene.service";
import {BulkService} from "../../../api/bulk/BulkService";
import {Update} from "../../../api/Update";
import {NO_OP} from "../../../api/api.service";
@@ -35,8 +33,6 @@ export class ScheduleEditorComponent implements OnInit {
schedule!: Schedule;
- scenes: Scene[] = [];
-
constructor(
readonly router: Router,
readonly activatedRoute: ActivatedRoute,
@@ -44,14 +40,12 @@ export class ScheduleEditorComponent implements OnInit {
readonly scheduleEntryService: ScheduleEntryService,
readonly propertyService: PropertyService,
readonly bulkService: BulkService,
- readonly sceneService: SceneService,
) {
// nothing
}
ngOnInit(): void {
this.scheduleService.subscribe(update => this.update(update));
- this.sceneService.findAll(scenes => this.scenes = scenes);
this.activatedRoute.params.subscribe(params => this.scheduleService.getById(params['id'], schedule => this.schedule = schedule));
}
diff --git a/src/main/angular/src/app/pages/schedule/list/schedule-list.component.html b/src/main/angular/src/app/pages/schedule/list/schedule-list.component.html
index 7f10ec9..2cc3109 100644
--- a/src/main/angular/src/app/pages/schedule/list/schedule-list.component.html
+++ b/src/main/angular/src/app/pages/schedule/list/schedule-list.component.html
@@ -2,9 +2,10 @@
|
| |
Bezeichnung |
- Zeitpunkt |
+ Nächste |
Eigenschaft |
Massenausführung |
+ Letzte |
|
@@ -24,12 +25,18 @@
| {{schedule.next?.property?.title}} |
= |
- {{schedule.next?.value}} |
+
+
+ |
{{schedule.next?.bulk?.name}}
|
+ {{schedule.last?.lastFuzzyTimestamp.dayName}} |
+ : |
+ {{schedule.last?.lastFuzzyTimestamp.timeString}} |
+
|
diff --git a/src/main/angular/src/app/pipes/left-pad.directive.ts b/src/main/angular/src/app/pipes/left-pad.directive.ts
new file mode 100644
index 0000000..75006a0
--- /dev/null
+++ b/src/main/angular/src/app/pipes/left-pad.directive.ts
@@ -0,0 +1,20 @@
+import {Pipe, PipeTransform} from '@angular/core';
+
+@Pipe({
+ name: 'leftPad',
+})
+export class LeftPadDirective implements PipeTransform {
+
+ constructor() {
+ }
+
+ transform(value: any, count: number): any {
+ let result = "" + value;
+ const rest: number = count - result.length;
+ if (rest > 0) {
+ result = " ".repeat(rest) + result;
+ }
+ return result;
+ }
+
+}
diff --git a/src/main/angular/src/app/shared/entry-value/entry-value.component.html b/src/main/angular/src/app/shared/entry-value/entry-value.component.html
new file mode 100644
index 0000000..777c84a
--- /dev/null
+++ b/src/main/angular/src/app/shared/entry-value/entry-value.component.html
@@ -0,0 +1,46 @@
+
+
+ 0 ? 0 : 1)">
+ {{entry.value ? "An" : "Aus"}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/angular/src/app/shared/entry-value/entry-value.component.less b/src/main/angular/src/app/shared/entry-value/entry-value.component.less
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/angular/src/app/shared/entry-value/entry-value.component.ts b/src/main/angular/src/app/shared/entry-value/entry-value.component.ts
new file mode 100644
index 0000000..d288603
--- /dev/null
+++ b/src/main/angular/src/app/shared/entry-value/entry-value.component.ts
@@ -0,0 +1,51 @@
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {ScheduleEntry} from "../../api/schedule/entry/ScheduleEntry";
+import {Scene} from "../../api/scene/Scene";
+import {SceneService} from "../../api/scene/scene.service";
+
+export class OnSet {
+
+ constructor(
+ readonly key: string,
+ readonly value: any,
+ ) {
+ // nothing
+ }
+
+}
+
+@Component({
+ selector: 'app-entry-value',
+ templateUrl: './entry-value.component.html',
+ styleUrls: ['./entry-value.component.less']
+})
+export class EntryValueComponent implements OnInit {
+
+ @Input()
+ entry!: ScheduleEntry;
+
+ @Input()
+ allowChange: boolean = false;
+
+ @Output()
+ onSet: EventEmitter = new EventEmitter();
+
+ scenes: Scene[] = [];
+
+ constructor(
+ readonly sceneService: SceneService,) {
+ // nothing
+ }
+
+ ngOnInit(): void {
+ this.sceneService.findAll(scenes => this.scenes = scenes);
+ }
+
+ set(key: string, value: any): void {
+ if (!this.allowChange) {
+ return;
+ }
+ this.onSet.emit(new OnSet(key, value));
+ }
+
+}
diff --git a/src/main/angular/src/environments/environment.ts b/src/main/angular/src/environments/environment.ts
index 66de4ae..eb18ddd 100644
--- a/src/main/angular/src/environments/environment.ts
+++ b/src/main/angular/src/environments/environment.ts
@@ -4,10 +4,12 @@
import {getBaseUrl} from "./UrlHelper";
+const PROD: boolean = false;
+
export const environment = {
production: false,
- restBase: getBaseUrl('http', 8080),
- websocketBase: getBaseUrl('ws', 8080),
+ restBase: PROD ? 'http://10.0.0.50:8082' : getBaseUrl('http', 8080),
+ websocketBase: PROD ? 'ws://10.0.0.50:8082' : getBaseUrl('ws', 8080),
};
/*
diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculator.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculator.java
index 7ed8742..3a429f1 100644
--- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculator.java
+++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculator.java
@@ -1,9 +1,6 @@
package de.ph87.homeautomation.schedule;
-import com.luckycatlabs.sunrisesunset.Zenith;
-import com.luckycatlabs.sunrisesunset.calculator.SolarEventCalculator;
-import com.luckycatlabs.sunrisesunset.dto.Location;
-import de.ph87.homeautomation.Config;
+import de.ph87.homeautomation.schedule.astro.AstroCalculator;
import de.ph87.homeautomation.schedule.entry.ScheduleEntry;
import de.ph87.homeautomation.schedule.entry.ScheduleEntryType;
import lombok.RequiredArgsConstructor;
@@ -14,11 +11,8 @@ import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import java.time.Instant;
import java.time.ZonedDateTime;
-import java.util.Calendar;
import java.util.Comparator;
-import java.util.GregorianCalendar;
import java.util.Optional;
@Slf4j
@@ -27,14 +21,14 @@ import java.util.Optional;
@RequiredArgsConstructor
public class ScheduleCalculator {
- private final Config config;
-
private final ScheduleReader scheduleReader;
private final ApplicationEventPublisher applicationEventPublisher;
private final ScheduleMapper scheduleMapper;
+ private final AstroCalculator astroCalculator;
+
@EventListener(ApplicationStartedEvent.class)
public void calculateAllNext() {
final ZonedDateTime now = ZonedDateTime.now();
@@ -82,7 +76,8 @@ public class ScheduleCalculator {
return midnight.withHour(entry.getHour()).withMinute(entry.getMinute()).withSecond(entry.getSecond());
case SUNRISE:
case SUNSET:
- return astroNext(entry, midnight);
+ final boolean sunrise = entry.getType() == ScheduleEntryType.SUNRISE;
+ return astroCalculator.forDay(midnight, sunrise, entry.getZenith());
default:
log.error("AstroEvent not implemented: {}", entry.getType());
break;
@@ -90,49 +85,20 @@ public class ScheduleCalculator {
return null;
}
- private ZonedDateTime astroNext(final ScheduleEntry entry, ZonedDateTime midnight) {
- final Location location = new Location(config.getLatitude(), config.getLongitude());
- final SolarEventCalculator calculator = new SolarEventCalculator(location, config.getTimezone());
- final Calendar calendar = GregorianCalendar.from(midnight);
- final Calendar nextCalendar = astroNext(calculator, entry.getType(), new Zenith(entry.getZenith()), calendar);
- if (nextCalendar == null) {
- return null;
- }
- return ZonedDateTime.ofInstant(Instant.ofEpochMilli(nextCalendar.getTimeInMillis()), midnight.getZone());
- }
-
- private Calendar astroNext(final SolarEventCalculator calculator, final ScheduleEntryType type, final Zenith solarZenith, final Calendar calendar) {
- switch (type) {
- case SUNRISE:
- return calculator.computeSunriseCalendar(solarZenith, calendar);
- case SUNSET:
- return calculator.computeSunsetCalendar(solarZenith, calendar);
- }
- return null;
- }
-
private boolean isAnyWeekdayEnabled(final ScheduleEntry entry) {
return entry.isMonday() || entry.isTuesday() || entry.isWednesday() || entry.isThursday() || entry.isFriday() || entry.isSaturday() || entry.isSunday();
}
private boolean isWeekdayEnabled(final ScheduleEntry entry, final ZonedDateTime value) {
- switch (value.getDayOfWeek()) {
- case MONDAY:
- return entry.isMonday();
- case TUESDAY:
- return entry.isTuesday();
- case WEDNESDAY:
- return entry.isWednesday();
- case THURSDAY:
- return entry.isThursday();
- case FRIDAY:
- return entry.isFriday();
- case SATURDAY:
- return entry.isSaturday();
- case SUNDAY:
- return entry.isSunday();
- }
- return false;
+ return switch (value.getDayOfWeek()) {
+ case MONDAY -> entry.isMonday();
+ case TUESDAY -> entry.isTuesday();
+ case WEDNESDAY -> entry.isWednesday();
+ case THURSDAY -> entry.isThursday();
+ case FRIDAY -> entry.isFriday();
+ case SATURDAY -> entry.isSaturday();
+ case SUNDAY -> entry.isSunday();
+ };
}
}
diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleController.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleController.java
index f766f3b..a6aad39 100644
--- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleController.java
+++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleController.java
@@ -1,6 +1,5 @@
package de.ph87.homeautomation.schedule;
-import de.ph87.homeautomation.property.PropertyReader;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@@ -15,8 +14,6 @@ public class ScheduleController {
private final ScheduleWriter scheduleWriter;
- private final PropertyReader propertyReader;
-
@GetMapping("findAll")
public List findAll() {
return scheduleReader.findAllDtos();
diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleDto.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleDto.java
index 1454e38..c06b506 100644
--- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleDto.java
+++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleDto.java
@@ -1,27 +1,32 @@
package de.ph87.homeautomation.schedule;
+import de.ph87.homeautomation.schedule.astro.AstroDto;
import de.ph87.homeautomation.schedule.entry.ScheduleEntryDto;
import lombok.Getter;
import java.io.Serializable;
+import java.util.List;
import java.util.Set;
@Getter
public class ScheduleDto implements Serializable {
- public final long id;
+ private final long id;
- public final boolean enabled;
+ private final boolean enabled;
- public final String title;
+ private final String title;
- public final Set entries;
+ private final Set entries;
- public ScheduleDto(final Schedule schedule, final Set entries) {
+ private final List astros;
+
+ public ScheduleDto(final Schedule schedule, final Set entries, final List astros) {
this.id = schedule.getId();
this.enabled = schedule.isEnabled();
this.title = schedule.getTitle();
this.entries = entries;
+ this.astros = astros;
}
}
diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleExecutor.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleExecutor.java
index c235b0a..6fa442e 100644
--- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleExecutor.java
+++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleExecutor.java
@@ -48,6 +48,7 @@ public class ScheduleExecutor {
bulkExecutor.execute(entry.getBulk());
}
entry.setLastClearTimestamp(entry.getNextClearTimestamp());
+ entry.setLastFuzzyTimestamp(entry.getNextFuzzyTimestamp());
scheduleWriter.notifyChanged(schedule);
}
diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleMapper.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleMapper.java
index f8e47a2..3c8ee95 100644
--- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleMapper.java
+++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleMapper.java
@@ -1,5 +1,7 @@
package de.ph87.homeautomation.schedule;
+import de.ph87.homeautomation.schedule.astro.AstroDto;
+import de.ph87.homeautomation.schedule.astro.AstroService;
import de.ph87.homeautomation.schedule.entry.ScheduleEntryDto;
import de.ph87.homeautomation.schedule.entry.ScheduleEntryMapper;
import de.ph87.homeautomation.web.WebSocketService;
@@ -8,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -21,15 +24,17 @@ public class ScheduleMapper {
private final WebSocketService webSocketService;
+ private final AstroService astroService;
+
public ScheduleDto toDto(final Schedule schedule) {
final Set entries = schedule.getEntries().stream().map(scheduleEntryMapper::toDto).collect(Collectors.toSet());
- return new ScheduleDto(schedule, entries);
+ final List astros = astroService.findAllNext();
+ return new ScheduleDto(schedule, entries, astros);
}
- public ScheduleDto publish(final Schedule schedule, final boolean existing) {
+ public void publish(final Schedule schedule, final boolean existing) {
final ScheduleDto dto = toDto(schedule);
webSocketService.send(ScheduleDto.class, dto, existing);
- return dto;
}
}
diff --git a/src/main/java/de/ph87/homeautomation/schedule/astro/Astro.java b/src/main/java/de/ph87/homeautomation/schedule/astro/Astro.java
new file mode 100644
index 0000000..393b969
--- /dev/null
+++ b/src/main/java/de/ph87/homeautomation/schedule/astro/Astro.java
@@ -0,0 +1,45 @@
+package de.ph87.homeautomation.schedule.astro;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+
+import javax.persistence.*;
+
+@Entity
+@Getter
+@ToString
+@NoArgsConstructor
+public class Astro {
+
+ @Id
+ @GeneratedValue
+ private long id;
+
+ @Version
+ private long version;
+
+ @Setter
+ private boolean enabled = true;
+
+ @Setter
+ private String error;
+
+ @Column(unique = true)
+ private double zenith;
+
+ @Column(nullable = false)
+ private String name;
+
+ private String differentNameForSunset;
+
+ public Astro(final double zenith, @NonNull final String name, @Nullable final String differentNameForSunset) {
+ this.zenith = zenith;
+ this.name = name;
+ this.differentNameForSunset = differentNameForSunset;
+ }
+
+}
diff --git a/src/main/java/de/ph87/homeautomation/schedule/astro/AstroCalculator.java b/src/main/java/de/ph87/homeautomation/schedule/astro/AstroCalculator.java
new file mode 100644
index 0000000..5bef5cb
--- /dev/null
+++ b/src/main/java/de/ph87/homeautomation/schedule/astro/AstroCalculator.java
@@ -0,0 +1,51 @@
+package de.ph87.homeautomation.schedule.astro;
+
+import com.luckycatlabs.sunrisesunset.Zenith;
+import com.luckycatlabs.sunrisesunset.calculator.SolarEventCalculator;
+import com.luckycatlabs.sunrisesunset.dto.Location;
+import de.ph87.homeautomation.Config;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+@Service
+@RequiredArgsConstructor
+public class AstroCalculator {
+
+ private final Config config;
+
+ public ZonedDateTime next(final ZonedDateTime now, final boolean sunrise, final double zenith) {
+ ZonedDateTime day = now.truncatedTo(ChronoUnit.DAYS);
+ ZonedDateTime next;
+ do {
+ next = forDay(day, sunrise, zenith);
+ day = day.plusDays(1);
+ } while (next != null && !next.isAfter(now));
+ return next;
+ }
+
+ public ZonedDateTime forDay(final ZonedDateTime midnight, final boolean sunrise, final double zenith) {
+ final Location location = new Location(config.getLatitude(), config.getLongitude());
+ final SolarEventCalculator calculator = new SolarEventCalculator(location, config.getTimezone());
+ final Calendar calendar = GregorianCalendar.from(midnight);
+ final Calendar nextCalendar = forDay(calculator, sunrise, new Zenith(zenith), calendar);
+ if (nextCalendar == null) {
+ return null;
+ }
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(nextCalendar.getTimeInMillis()), midnight.getZone());
+ }
+
+ private Calendar forDay(final SolarEventCalculator calculator, final boolean sunrise, final Zenith solarZenith, final Calendar calendar) {
+ if (sunrise) {
+ return calculator.computeSunriseCalendar(solarZenith, calendar);
+ } else {
+ return calculator.computeSunsetCalendar(solarZenith, calendar);
+ }
+ }
+
+}
diff --git a/src/main/java/de/ph87/homeautomation/schedule/astro/AstroDto.java b/src/main/java/de/ph87/homeautomation/schedule/astro/AstroDto.java
new file mode 100644
index 0000000..34de8a3
--- /dev/null
+++ b/src/main/java/de/ph87/homeautomation/schedule/astro/AstroDto.java
@@ -0,0 +1,37 @@
+package de.ph87.homeautomation.schedule.astro;
+
+import lombok.Getter;
+import lombok.ToString;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+
+import java.time.ZonedDateTime;
+
+@Getter
+@ToString
+public class AstroDto {
+
+ @NonNull
+ private final double zenith;
+
+ @NonNull
+ private final ZonedDateTime sunrise;
+
+ @NonNull
+ private final ZonedDateTime sunset;
+
+ @NonNull
+ private final String sunriseName;
+
+ @Nullable
+ private final String sunsetName;
+
+ public AstroDto(@NonNull final Astro astro, @NonNull final ZonedDateTime sunrise, @NonNull final ZonedDateTime sunset) {
+ this.zenith = astro.getZenith();
+ this.sunrise = sunrise;
+ this.sunset = sunset;
+ this.sunriseName = astro.getName();
+ this.sunsetName = astro.getDifferentNameForSunset() == null ? astro.getName() : astro.getDifferentNameForSunset();
+ }
+
+}
diff --git a/src/main/java/de/ph87/homeautomation/schedule/astro/AstroRepository.java b/src/main/java/de/ph87/homeautomation/schedule/astro/AstroRepository.java
new file mode 100644
index 0000000..e6a6ba3
--- /dev/null
+++ b/src/main/java/de/ph87/homeautomation/schedule/astro/AstroRepository.java
@@ -0,0 +1,11 @@
+package de.ph87.homeautomation.schedule.astro;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface AstroRepository extends JpaRepository {
+
+ List findAllByEnabledTrue();
+
+}
diff --git a/src/main/java/de/ph87/homeautomation/schedule/astro/AstroService.java b/src/main/java/de/ph87/homeautomation/schedule/astro/AstroService.java
new file mode 100644
index 0000000..c78cb7b
--- /dev/null
+++ b/src/main/java/de/ph87/homeautomation/schedule/astro/AstroService.java
@@ -0,0 +1,71 @@
+package de.ph87.homeautomation.schedule.astro;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.ZonedDateTime;
+import java.util.List;
+import java.util.Objects;
+
+@RestController
+@Transactional
+@RequestMapping("Astro")
+@RequiredArgsConstructor
+public class AstroService {
+
+ private final AstroRepository astroRepository;
+
+ private final AstroCalculator astroCalculator;
+
+ @EventListener(ApplicationStartedEvent.class)
+ public void startup() {
+ if (astroRepository.count() == 0) {
+ astroRepository.save(new Astro(71.0000, "Aufgang +++++", "Untergang +++++"));
+ astroRepository.save(new Astro(75.0000, "Aufgang ++++", "Untergang ++++"));
+ astroRepository.save(new Astro(80.0000, "Aufgang +++", "Untergang +++"));
+ astroRepository.save(new Astro(85.0000, "Aufgang ++", "Untergang ++"));
+ astroRepository.save(new Astro(90.0000, "Aufgang +", "Untergang +"));
+ astroRepository.save(new Astro(90.8333, "Aufgang", "Untergang"));
+ astroRepository.save(new Astro(92.0000, "Aufgang -", null));
+ astroRepository.save(new Astro(93.0000, "Aufgang --", null));
+ astroRepository.save(new Astro(94.0000, "Bürgerlich ++", null));
+ astroRepository.save(new Astro(95.0000, "Bürgerlich +", null));
+ astroRepository.save(new Astro(96.0000, "Bürgerlich", null));
+ astroRepository.save(new Astro(97.0000, "Bürgerlich -", null));
+ astroRepository.save(new Astro(98.0000, "Bürgerlich --", null));
+ astroRepository.save(new Astro(99.0000, "Bürgerlich ---", null));
+ astroRepository.save(new Astro(100.000, "Nautisch ++", null));
+ astroRepository.save(new Astro(101.000, "Nautisch +", null));
+ astroRepository.save(new Astro(102.000, "Nautisch", null));
+ astroRepository.save(new Astro(103.000, "Nautisch -", null));
+ astroRepository.save(new Astro(104.000, "Nautisch --", null));
+ astroRepository.save(new Astro(105.000, "Nautisch ---", null));
+ astroRepository.save(new Astro(106.000, "Astronomisch ++", null));
+ astroRepository.save(new Astro(107.000, "Astronomisch +", null));
+ astroRepository.save(new Astro(108.000, "Astronomisch", null));
+ astroRepository.save(new Astro(110.000, "Astronomisch -", null));
+ astroRepository.save(new Astro(120.000, "Astronomisch --", null));
+ }
+ }
+
+ public List findAllNext() {
+ final ZonedDateTime now = ZonedDateTime.now();
+ return astroRepository.findAllByEnabledTrue().stream().map(astro -> next(now, astro)).filter(Objects::nonNull).toList();
+ }
+
+ private AstroDto next(final ZonedDateTime now, final Astro astro) {
+ final ZonedDateTime sunrise = astroCalculator.next(now, true, astro.getZenith());
+ final ZonedDateTime sunset = astroCalculator.next(now, false, astro.getZenith());
+ if (sunrise == null || sunset == null) {
+ astro.setEnabled(false);
+ astro.setError("sunrise (%s) or sunset (%s) NULL for %s".formatted(sunrise, sunset, now));
+ return null;
+ }
+ return new AstroDto(astro, sunrise, sunset);
+ }
+
+}
diff --git a/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntry.java b/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntry.java
index 91c50ce..d691fa2 100644
--- a/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntry.java
+++ b/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntry.java
@@ -74,11 +74,13 @@ public class ScheduleEntry {
private ZonedDateTime nextClearTimestamp;
- private ZonedDateTime lastClearTimestamp;
-
@Setter(AccessLevel.NONE)
private ZonedDateTime nextFuzzyTimestamp;
+ private ZonedDateTime lastClearTimestamp;
+
+ private ZonedDateTime lastFuzzyTimestamp;
+
public ScheduleEntry(final Schedule schedule) {
this.schedule = schedule;
this.position = schedule.getEntries().size();
diff --git a/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntryDto.java b/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntryDto.java
index e6197e4..08e4961 100644
--- a/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntryDto.java
+++ b/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntryDto.java
@@ -48,6 +48,8 @@ public class ScheduleEntryDto implements Serializable {
public final ZonedDateTime nextFuzzyTimestamp;
+ public final ZonedDateTime lastFuzzyTimestamp;
+
public final PropertyDto property;
public final double value;
@@ -74,6 +76,7 @@ public class ScheduleEntryDto implements Serializable {
this.nextClearTimestamp = entry.getNextClearTimestamp();
this.lastClearTimestamp = entry.getLastClearTimestamp();
this.nextFuzzyTimestamp = entry.getNextFuzzyTimestamp();
+ this.lastFuzzyTimestamp = entry.getLastFuzzyTimestamp();
this.property = property;
this.value = entry.getValue();
this.bulk = bulk;
diff --git a/upload.sh b/upload.sh
index fb94015..df681b4 100644
--- a/upload.sh
+++ b/upload.sh
@@ -4,4 +4,5 @@ cd "$(dirname "$0")" || exit 1
scp target/Homeautomation-1.0-SNAPSHOT.jar media@10.0.0.50:/home/media/java/Homeautomation/Homeautomation.jar.update &&
git tag "$(date +'deploy---%Y-%m-%d---%H-%M-%S')" &&
- curl -m 2 -s http://10.0.0.50:8082/server/shutdown
+ curl -m 2 -s http://10.0.0.50:8082/server/shutdown &&
+ exit 0