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 @@ - - - {{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 @@ + + +
+ {{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