diff --git a/src/main/angular/src/app/api/channel/Channel.ts b/src/main/angular/src/app/api/channel/Channel.ts index 3bd1a1f..78068cd 100644 --- a/src/main/angular/src/app/api/channel/Channel.ts +++ b/src/main/angular/src/app/api/channel/Channel.ts @@ -1,4 +1,4 @@ -import {validateBooleanNotNull, validateDateAllowNull, validateNumberAllowNull, validateNumberNotNull, validateStringEmptyToNull, validateStringNotEmptyNotNull} from "../validators"; +import {validateBooleanNotNull, validateDateAllowNull, validateListOrEmpty, validateNumberAllowNull, validateNumberNotNull, validateStringEmptyToNull, validateStringNotEmptyNotNull} from "../validators"; import {prefix} from "../../helpers"; export abstract class Channel { @@ -7,6 +7,8 @@ export abstract class Channel { readonly id: number, readonly title: string, readonly type: string, + readonly value: number | null, + readonly timestamp: Date | null, ) { // nothing } @@ -26,6 +28,8 @@ export abstract class Channel { validateNumberNotNull(json['id']), validateStringNotEmptyNotNull(json['title']), type, + validateNumberAllowNull(json['value']), + validateDateAllowNull(json['timestamp']), validateNumberNotNull(json['addressRaw']), validateStringNotEmptyNotNull(json['addressStr']), validateNumberNotNull(json['dptMain']), @@ -33,8 +37,16 @@ export abstract class Channel { validateStringEmptyToNull(json['description']), validateNumberNotNull(json['puid']), validateBooleanNotNull(json['ets']), + ); + case "Logic": + return new Logic( + validateNumberNotNull(json['id']), + validateStringNotEmptyNotNull(json['title']), + type, validateNumberAllowNull(json['value']), validateDateAllowNull(json['timestamp']), + validateStringNotEmptyNotNull(json['operator']), + validateListOrEmpty(json['propertyIds'], validateNumberNotNull), ); } throw new Error("No such type: " + type); @@ -55,26 +67,26 @@ export abstract class Channel { export class KnxGroup extends Channel { - public addresMain: number; - public addresMid: number; - public addresSub: number; - public dpt: string; + readonly addresMain: number; + readonly addresMid: number; + readonly addresSub: number; + readonly dpt: string; constructor( id: number, title: string, type: string, - public addressRaw: number, - public addressStr: string, - public dptMain: number, - public dptSub: number, - public description: string | null, - public puid: number, - public ets: boolean, - public value: number | null, - public timestamp: Date | null, + value: number | null, + timestamp: Date | null, + readonly addressRaw: number, + readonly addressStr: string, + readonly dptMain: number, + readonly dptSub: number, + readonly description: string | null, + readonly puid: number, + readonly ets: boolean, ) { - super(id, title, type); + super(id, title, type, value, timestamp); const addressParts = this.addressStr.split("/"); this.addresMain = parseInt(addressParts[0]); this.addresMid = parseInt(addressParts[1]); @@ -83,3 +95,20 @@ export class KnxGroup extends Channel { } } + +export class Logic extends Channel { + + constructor( + id: number, + title: string, + type: string, + value: number | null, + timestamp: Date | null, + readonly operator: string, + readonly propertyIds: number[], + ) { + super(id, title, type, value, timestamp); + // nothing + } + +} diff --git a/src/main/angular/src/app/api/device/Device.ts b/src/main/angular/src/app/api/device/Device.ts index 351adbd..0d80dd9 100644 --- a/src/main/angular/src/app/api/device/Device.ts +++ b/src/main/angular/src/app/api/device/Device.ts @@ -28,7 +28,7 @@ export abstract class Device { type, Property.fromJsonAllowNull(json['stateProperty']), Property.fromJsonAllowNull(json['sceneProperty']), - validateListOrEmpty(json['sceneNumbers'], parseInt), + validateListOrEmpty(json['sceneNumbers'], validateNumberNotNull), ); case "DeviceShutter": return new DeviceShutter( diff --git a/src/main/angular/src/app/pages/channel-list/channel-list.component.html b/src/main/angular/src/app/pages/channel-list/channel-list.component.html index 5d58b3c..8f94a4d 100644 --- a/src/main/angular/src/app/pages/channel-list/channel-list.component.html +++ b/src/main/angular/src/app/pages/channel-list/channel-list.component.html @@ -14,8 +14,11 @@ {{asKnxGroup(channel).addresMid}} /  {{asKnxGroup(channel).addresSub}} {{asKnxGroup(channel).dpt}} - {{asKnxGroup(channel).value}} - {{asKnxGroup(channel).timestamp | date:'yyyy-MM-dd HH:mm:ss'}} + + {{asLogic(channel).operator}} + + {{channel.value}} + {{channel.timestamp | date:'yyyy-MM-dd HH:mm:ss'}} diff --git a/src/main/angular/src/app/pages/channel-list/channel-list.component.ts b/src/main/angular/src/app/pages/channel-list/channel-list.component.ts index 71f0bc7..0c21e87 100644 --- a/src/main/angular/src/app/pages/channel-list/channel-list.component.ts +++ b/src/main/angular/src/app/pages/channel-list/channel-list.component.ts @@ -1,6 +1,6 @@ import {Component, OnInit} from '@angular/core'; import {ChannelService} from "../../api/channel/channel.service"; -import {Channel, KnxGroup} from "../../api/channel/Channel"; +import {Channel, KnxGroup, Logic} from "../../api/channel/Channel"; import {Update} from "../../api/Update"; @Component({ @@ -27,6 +27,10 @@ export class ChannelListComponent implements OnInit { return channel as KnxGroup; } + asLogic(channel: Channel): Logic { + return channel as Logic; + } + private updateChannel(update: Update): void { const index: number = this.channels.findIndex(c => c.id === update.payload.id); if (index >= 0) { diff --git a/src/main/java/de/ph87/homeautomation/DemoDataService.java b/src/main/java/de/ph87/homeautomation/DemoDataService.java index 1e4b071..432222c 100644 --- a/src/main/java/de/ph87/homeautomation/DemoDataService.java +++ b/src/main/java/de/ph87/homeautomation/DemoDataService.java @@ -4,12 +4,16 @@ import com.luckycatlabs.sunrisesunset.Zenith; import de.ph87.homeautomation.channel.Channel; import de.ph87.homeautomation.device.DeviceRepository; import de.ph87.homeautomation.device.DeviceWriteService; +import de.ph87.homeautomation.device.devices.DeviceDto; import de.ph87.homeautomation.knx.group.KnxGroup; import de.ph87.homeautomation.knx.group.KnxGroupReadService; +import de.ph87.homeautomation.logic.Logic; +import de.ph87.homeautomation.logic.LogicOperator; +import de.ph87.homeautomation.logic.LogicRepository; +import de.ph87.homeautomation.logic.LogicWriter; import de.ph87.homeautomation.property.Property; import de.ph87.homeautomation.property.PropertyRepository; import de.ph87.homeautomation.property.PropertyType; -import de.ph87.homeautomation.scene.SceneDto; import de.ph87.homeautomation.scene.SceneRepository; import de.ph87.homeautomation.scene.SceneWriteService; import de.ph87.homeautomation.schedule.Schedule; @@ -22,6 +26,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.HashSet; @SuppressWarnings({"unchecked", "UnusedReturnValue", "SameParameterValue", "UnusedAssignment", "RedundantSuppression"}) @Slf4j @@ -48,105 +54,114 @@ public class DemoDataService { private final SceneWriteService sceneWriteService; + private final LogicWriter logicWriter; + + private final LogicRepository logicRepository; + public void insertDemoData() { - final Property erdgeschoss = createProperty("Erdgeschoss", PropertyType.BOOLEAN, knx(0, 4, 2), null); - final Property erdgeschoss_szene = createProperty("Erdgeschoss Szene", PropertyType.SCENE, null, knx(0, 0, 1)); - final Property obergeschoss = createProperty("Obergeschoss", PropertyType.BOOLEAN, knx(0, 6, 6), null); - final Property obergeschoss_szene = createProperty("Obergeschoss Szene", PropertyType.SCENE, null, knx(0, 3, 2)); +// final Property erdgeschoss = createProperty("Erdgeschoss", PropertyType.BOOLEAN, knx(0, 4, 2), null); +// final Property erdgeschoss_szene = createProperty("Erdgeschoss Szene", PropertyType.SCENE, null, knx(0, 0, 1)); +// final Property obergeschoss = createProperty("Obergeschoss", PropertyType.BOOLEAN, knx(0, 6, 6), null); +// final Property obergeschoss_szene = createProperty("Obergeschoss Szene", PropertyType.SCENE, null, knx(0, 3, 2)); +// +// final Property fernseher = createProperty("Fernseher", PropertyType.BOOLEAN, knx(0, 0, 20), knx(0, 0, 4)); +// final Property verstaerker = createProperty("Verstärker", PropertyType.BOOLEAN, knx(0, 3, 57), knx(0, 3, 56)); +// final Property aussendekoration = createProperty("Außendekoration", PropertyType.BOOLEAN, knx(0, 4, 12), knx(0, 4, 11)); +// final Property terrasse = createProperty("Terrasse Licht", PropertyType.BOOLEAN, knx(0, 4, 1), knx(0, 4, 0)); +// +// final Property ambiente_eg = createProperty("Ambiente EG", PropertyType.BOOLEAN, knx(0, 3, 81), knx(0, 3, 80)); +// final Property ambiente_og = createProperty("Ambiente OG", PropertyType.BOOLEAN, knx(0, 6, 2), knx(0, 6, 3)); - final Property fernseher = createProperty("Fernseher", PropertyType.BOOLEAN, knx(0, 0, 20), knx(0, 0, 4)); - final Property verstaerker = createProperty("Verstärker", PropertyType.BOOLEAN, knx(0, 3, 57), knx(0, 3, 56)); - final Property aussendekoration = createProperty("Außendekoration", PropertyType.BOOLEAN, knx(0, 4, 12), knx(0, 4, 11)); - final Property terrasse = createProperty("Terrasse Licht", PropertyType.BOOLEAN, knx(0, 4, 1), knx(0, 4, 0)); +// final Property flur_eg_licht = createProperty("Flur EG Licht", PropertyType.BOOLEAN, knx(0, 4, 8), knx(0, 5, 14)); +// +// final Property wohnzimmer_rollladen = createProperty("Wohnzimmer Rollladen", PropertyType.SHUTTER, null, knx(0, 4, 24)); +// final Property schlafzimmer_rollladen = createProperty("Schlafzimmer Rollladen", PropertyType.SHUTTER, null, knx(0, 3, 3)); +// final Property flur_og_rollladen = createProperty("Flur OG Rollladen", PropertyType.SHUTTER, null, knx(0, 5, 13)); +// +// final Property helligkeit = createProperty("Helligkeit", PropertyType.LUX, knx(0, 5, 6), null); +// final Property szene_haus = createProperty("Szene Haus ", PropertyType.SCENE, null, knx(0, 0, 21)); - final Property ambiente_eg = createProperty("Ambiente EG", PropertyType.BOOLEAN, knx(0, 3, 81), knx(0, 3, 80)); - final Property ambiente_og = createProperty("Ambiente OG", PropertyType.BOOLEAN, knx(0, 6, 2), knx(0, 6, 3)); +// final SceneDto alles_aus = sceneWriteService.create(1, "Alles AUS"); +// final SceneDto nachtlicht = sceneWriteService.create(2, "Nachtlicht"); +// final SceneDto aussendekoration_aus = sceneWriteService.create(30, "Außendekoration AUS"); +// final SceneDto aussendekoration_an = sceneWriteService.create(31, "Außendekoration AN"); +// +// deviceWriteService.createDeviceStateScene(erdgeschoss, erdgeschoss_szene, alles_aus); +// deviceWriteService.createDeviceStateScene(obergeschoss, obergeschoss_szene, alles_aus); +// +// deviceWriteService.createDeviceSwitch(fernseher); +// deviceWriteService.createDeviceSwitch(verstaerker); +// deviceWriteService.createDeviceSwitch(aussendekoration); +// deviceWriteService.createDeviceSwitch(terrasse); +// +// deviceWriteService.createDeviceSwitch(ambiente_eg); +// deviceWriteService.createDeviceSwitch(ambiente_og); - final Property bad_licht = createProperty("Bad Licht", PropertyType.BOOLEAN, knx(0, 5, 19), knx(0, 3, 73)); - final Property flur_eg_licht = createProperty("Flur EG Licht", PropertyType.BOOLEAN, knx(0, 4, 8), knx(0, 5, 14)); +// deviceWriteService.createDeviceSwitch(flur_eg_licht); +// +// deviceWriteService.createDeviceShutter(wohnzimmer_rollladen); +// deviceWriteService.createDeviceShutter(schlafzimmer_rollladen); +// deviceWriteService.createDeviceShutter(flur_og_rollladen); +// +// final Schedule scheduleEgFlurLicht = createSchedule(true, "EG Flur Licht", flur_eg_licht); +// createTime(scheduleEgFlurLicht, true, 1, 0, 0, MIN30, true); +// createTime(scheduleEgFlurLicht, true, 2, 0, 0, MIN30, false); +// createTime(scheduleEgFlurLicht, true, 7, 30, 0, MIN30, true); +// createTime(scheduleEgFlurLicht, true, 8, 30, 0, MIN30, false); +// createTime(scheduleEgFlurLicht, true, 13, 30, 0, MIN30, true); +// createTime(scheduleEgFlurLicht, true, 14, 30, 0, MIN30, false); +// createTime(scheduleEgFlurLicht, true, 19, 0, 0, MIN30, true); +// createTime(scheduleEgFlurLicht, true, 20, 0, 0, MIN30, false); +// scheduleRepository.save(scheduleEgFlurLicht); +// +// final Schedule scheduleEgAmbiente = createSchedule(false, "Ambiente EG", ambiente_eg); +// createTime(scheduleEgAmbiente, true, 7, 15, 0, MIN30, true); +// createTime(scheduleEgAmbiente, true, 9, 30, 0, MIN30, false); +// createSunset(scheduleEgAmbiente, true, Zenith.OFFICIAL, MIN30, true); +// createSunset(scheduleEgAmbiente, true, Zenith.ASTRONOMICAL, MIN30, false); +// scheduleRepository.save(scheduleEgAmbiente); +// +// final Schedule scheduleOgAmbiente = createSchedule(false, "Ambiente OG", ambiente_og); +// createTime(scheduleOgAmbiente, true, 7, 15, 0, MIN30, true); +// createTime(scheduleOgAmbiente, true, 9, 30, 0, MIN30, false); +// createSunset(scheduleOgAmbiente, true, Zenith.OFFICIAL, MIN30, true); +// createSunset(scheduleOgAmbiente, true, Zenith.ASTRONOMICAL, MIN30, false); +// scheduleRepository.save(scheduleOgAmbiente); +// +// final Schedule scheduleWohnzimmerRollladen = createSchedule(true, "Rollläden Wohnzimmer", wohnzimmer_rollladen); +// createSunrise(scheduleWohnzimmerRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 0); +// createSunset(scheduleWohnzimmerRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 100); +// scheduleRepository.save(scheduleWohnzimmerRollladen); +// +// final Schedule scheduleSchlafzimmerRollladen = createSchedule(true, "Rollläden Schlafzimmer", schlafzimmer_rollladen); +// createTime(scheduleSchlafzimmerRollladen, true, 7, 0, 0, 0, 0); +// createSunset(scheduleSchlafzimmerRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 100); +// scheduleRepository.save(scheduleSchlafzimmerRollladen); +// +// final Schedule scheduleFlurRollladen = createSchedule(true, "Rollladen Flur", flur_og_rollladen); +// createSunrise(scheduleFlurRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 0); +// createSunset(scheduleFlurRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 100); +// scheduleRepository.save(scheduleFlurRollladen); +// +// final Schedule scheduleSzeneHaus = createSchedule(true, "Dekoration", szene_haus); +// createTime(scheduleSzeneHaus, true, 6, 0, 0, 0, 31); +// createTime(scheduleSzeneHaus, true, 8, 30, 0, 0, 30); +// createSunset(scheduleSzeneHaus, true, Zenith.OFFICIAL, 0, 31); +// createTime(scheduleSzeneHaus, true, 22, 0, 0, 0, 30); +// scheduleRepository.save(scheduleSzeneHaus); - final Property wohnzimmer_rollladen = createProperty("Wohnzimmer Rollladen", PropertyType.SHUTTER, null, knx(0, 4, 24)); - final Property schlafzimmer_rollladen = createProperty("Schlafzimmer Rollladen", PropertyType.SHUTTER, null, knx(0, 3, 3)); - final Property flur_og_rollladen = createProperty("Flur OG Rollladen", PropertyType.SHUTTER, null, knx(0, 5, 13)); + final Property propertyBadLicht = createProperty("Bad Licht", PropertyType.BOOLEAN, knx(0, 5, 19), knx(0, 3, 73)); + final DeviceDto deviceBadLicht = deviceWriteService.createDeviceSwitch(propertyBadLicht); + final Logic logicStatusOg = getOrCreateLogic("Status OG", LogicOperator.OR, propertyBadLicht); + final Property propertyStatusOg = createProperty(logicStatusOg.getName(), PropertyType.BOOLEAN, logicStatusOg, null); + final DeviceDto deviceStatusOg = deviceWriteService.createDeviceSwitch(propertyStatusOg); + } - final Property helligkeit = createProperty("Helligkeit", PropertyType.LUX, knx(0, 5, 6), null); - final Property szene_haus = createProperty("Szene Haus ", PropertyType.SCENE, null, knx(0, 0, 21)); - - if (sceneRepository.count() == 0) { - final SceneDto alles_aus = sceneWriteService.create(1, "Alles AUS"); - final SceneDto nachtlicht = sceneWriteService.create(2, "Nachtlicht"); - final SceneDto aussendekoration_aus = sceneWriteService.create(30, "Außendekoration AUS"); - final SceneDto aussendekoration_an = sceneWriteService.create(31, "Außendekoration AN"); - - if (deviceRepository.count() == 0) { - deviceWriteService.createDeviceStateScene(erdgeschoss, erdgeschoss_szene, alles_aus); - deviceWriteService.createDeviceStateScene(obergeschoss, obergeschoss_szene, alles_aus); - - deviceWriteService.createDeviceSwitch(fernseher); - deviceWriteService.createDeviceSwitch(verstaerker); - deviceWriteService.createDeviceSwitch(aussendekoration); - deviceWriteService.createDeviceSwitch(terrasse); - - deviceWriteService.createDeviceSwitch(ambiente_eg); - deviceWriteService.createDeviceSwitch(ambiente_og); - - deviceWriteService.createDeviceSwitch(bad_licht); - deviceWriteService.createDeviceSwitch(flur_eg_licht); - - deviceWriteService.createDeviceShutter(wohnzimmer_rollladen); - deviceWriteService.createDeviceShutter(schlafzimmer_rollladen); - deviceWriteService.createDeviceShutter(flur_og_rollladen); - } - } - - if (scheduleRepository.count() == 0) { - final Schedule scheduleEgFlurLicht = createSchedule(true, "EG Flur Licht", flur_eg_licht); - createTime(scheduleEgFlurLicht, true, 1, 0, 0, MIN30, true); - createTime(scheduleEgFlurLicht, true, 2, 0, 0, MIN30, false); - createTime(scheduleEgFlurLicht, true, 7, 30, 0, MIN30, true); - createTime(scheduleEgFlurLicht, true, 8, 30, 0, MIN30, false); - createTime(scheduleEgFlurLicht, true, 13, 30, 0, MIN30, true); - createTime(scheduleEgFlurLicht, true, 14, 30, 0, MIN30, false); - createTime(scheduleEgFlurLicht, true, 19, 0, 0, MIN30, true); - createTime(scheduleEgFlurLicht, true, 20, 0, 0, MIN30, false); - scheduleRepository.save(scheduleEgFlurLicht); - - final Schedule scheduleEgAmbiente = createSchedule(false, "Ambiente EG", ambiente_eg); - createTime(scheduleEgAmbiente, true, 7, 15, 0, MIN30, true); - createTime(scheduleEgAmbiente, true, 9, 30, 0, MIN30, false); - createSunset(scheduleEgAmbiente, true, Zenith.OFFICIAL, MIN30, true); - createSunset(scheduleEgAmbiente, true, Zenith.ASTRONOMICAL, MIN30, false); - scheduleRepository.save(scheduleEgAmbiente); - - final Schedule scheduleOgAmbiente = createSchedule(false, "Ambiente OG", ambiente_og); - createTime(scheduleOgAmbiente, true, 7, 15, 0, MIN30, true); - createTime(scheduleOgAmbiente, true, 9, 30, 0, MIN30, false); - createSunset(scheduleOgAmbiente, true, Zenith.OFFICIAL, MIN30, true); - createSunset(scheduleOgAmbiente, true, Zenith.ASTRONOMICAL, MIN30, false); - scheduleRepository.save(scheduleOgAmbiente); - - final Schedule scheduleWohnzimmerRollladen = createSchedule(true, "Rollläden Wohnzimmer", wohnzimmer_rollladen); - createSunrise(scheduleWohnzimmerRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 0); - createSunset(scheduleWohnzimmerRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 100); - scheduleRepository.save(scheduleWohnzimmerRollladen); - - final Schedule scheduleSchlafzimmerRollladen = createSchedule(true, "Rollläden Schlafzimmer", schlafzimmer_rollladen); - createTime(scheduleSchlafzimmerRollladen, true, 7, 0, 0, 0, 0); - createSunset(scheduleSchlafzimmerRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 100); - scheduleRepository.save(scheduleSchlafzimmerRollladen); - - final Schedule scheduleFlurRollladen = createSchedule(true, "Rollladen Flur", flur_og_rollladen); - createSunrise(scheduleFlurRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 0); - createSunset(scheduleFlurRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 100); - scheduleRepository.save(scheduleFlurRollladen); - - final Schedule scheduleSzeneHaus = createSchedule(true, "Dekoration", szene_haus); - createTime(scheduleSzeneHaus, true, 6, 0, 0, 0, 31); - createTime(scheduleSzeneHaus, true, 8, 30, 0, 0, 30); - createSunset(scheduleSzeneHaus, true, Zenith.OFFICIAL, 0, 31); - createTime(scheduleSzeneHaus, true, 22, 0, 0, 0, 30); - scheduleRepository.save(scheduleSzeneHaus); - } + private Logic getOrCreateLogic(final String name, final LogicOperator operator, final Property... properties) { + return logicRepository.findByName(name).orElseGet(() -> { + final Logic logic = new Logic(name, operator, new HashSet<>(Arrays.asList(properties))); + return logicRepository.save(logic); + }); } private KnxGroup knx(final int main, final int mid, final int sub) { diff --git a/src/main/java/de/ph87/homeautomation/channel/ChannelController.java b/src/main/java/de/ph87/homeautomation/channel/ChannelController.java index 69c3a3e..3aca91f 100644 --- a/src/main/java/de/ph87/homeautomation/channel/ChannelController.java +++ b/src/main/java/de/ph87/homeautomation/channel/ChannelController.java @@ -17,7 +17,7 @@ public class ChannelController implements ISearchController { private final ChannelService channelService; @GetMapping("findAll") - public List findAll() { + public List findAll() { return channelService.findAllDto(); } diff --git a/src/main/java/de/ph87/homeautomation/channel/ChannelDto.java b/src/main/java/de/ph87/homeautomation/channel/ChannelDto.java index 30dd9af..04a7100 100644 --- a/src/main/java/de/ph87/homeautomation/channel/ChannelDto.java +++ b/src/main/java/de/ph87/homeautomation/channel/ChannelDto.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.ToString; import java.io.Serializable; +import java.time.ZonedDateTime; @Getter @ToString @@ -15,10 +16,16 @@ public abstract class ChannelDto implements Serializable { private final String type; + private final Double value; + + private final ZonedDateTime timestamp; + protected ChannelDto(final Channel channel) { this.id = channel.getId(); this.title = channel.getName(); this.type = channel.getClass().getSimpleName(); + this.value = channel.getValue(); + this.timestamp = channel.getTimestamp(); } } diff --git a/src/main/java/de/ph87/homeautomation/channel/ChannelService.java b/src/main/java/de/ph87/homeautomation/channel/ChannelService.java index 7c85fca..ad6d468 100644 --- a/src/main/java/de/ph87/homeautomation/channel/ChannelService.java +++ b/src/main/java/de/ph87/homeautomation/channel/ChannelService.java @@ -48,15 +48,15 @@ public class ChannelService { return getByChannel(channel).toDto(channel); } - public List findAllDto() { + public List findAllDto() { return channelOwners.stream().map(IChannelOwner::findAllDto).reduce(new ArrayList<>(), Helpers::merge); } - public List findAllDtoLike(final String term) { + public List findAllDtoLike(final String term) { return channelOwners.stream().map(owner -> owner.findAllDtoLike(term)).reduce(new ArrayList<>(), Helpers::merge); } - public Optional findDtoById(final long id) { + public Optional findDtoById(final long id) { return channelRepository.findById(id).map(this::toDto); } diff --git a/src/main/java/de/ph87/homeautomation/channel/IChannelOwner.java b/src/main/java/de/ph87/homeautomation/channel/IChannelOwner.java index 10345ff..ca9c89e 100644 --- a/src/main/java/de/ph87/homeautomation/channel/IChannelOwner.java +++ b/src/main/java/de/ph87/homeautomation/channel/IChannelOwner.java @@ -4,14 +4,14 @@ import java.util.List; public interface IChannelOwner { - void read(final Channel channel); + void requestUpdate(final Channel channel); void write(final Channel channel, final double value); ChannelDto toDto(final Channel channel); - List findAllDto(); + List findAllDto(); - List findAllDtoLike(final String like); + List findAllDtoLike(final String like); } diff --git a/src/main/java/de/ph87/homeautomation/device/DeviceWriteService.java b/src/main/java/de/ph87/homeautomation/device/DeviceWriteService.java index 1c4e74f..fdfe63d 100644 --- a/src/main/java/de/ph87/homeautomation/device/DeviceWriteService.java +++ b/src/main/java/de/ph87/homeautomation/device/DeviceWriteService.java @@ -123,7 +123,7 @@ public class DeviceWriteService { } private DeviceDto publish(final DeviceDto dto, final boolean existing) { - webSocketService.send(dto, existing); + webSocketService.send(DeviceDto.class, dto, existing); return dto; } diff --git a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupChannelOwnerService.java b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupChannelOwnerService.java index d8a4f30..4bafc13 100644 --- a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupChannelOwnerService.java +++ b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupChannelOwnerService.java @@ -1,7 +1,6 @@ package de.ph87.homeautomation.knx.group; import de.ph87.homeautomation.channel.Channel; -import de.ph87.homeautomation.channel.ChannelDto; import de.ph87.homeautomation.channel.IChannelOwner; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -22,7 +21,7 @@ public class KnxGroupChannelOwnerService implements IChannelOwner { private final KnxGroupReadService knxGroupReadService; @Override - public void read(final Channel channel) { + public void requestUpdate(final Channel channel) { knxGroupWriteService.requestRead((KnxGroup) channel); } @@ -32,17 +31,17 @@ public class KnxGroupChannelOwnerService implements IChannelOwner { } @Override - public ChannelDto toDto(final Channel channel) { + public KnxGroupDto toDto(final Channel channel) { return new KnxGroupDto((KnxGroup) channel); } @Override - public List findAllDto() { + public List findAllDto() { return knxGroupReadService.findAll().stream().map(this::toDto).collect(Collectors.toList()); } @Override - public List findAllDtoLike(final String like) { + public List findAllDtoLike(final String like) { return knxGroupReadService.findAllLike(like).stream().map(this::toDto).collect(Collectors.toList()); } diff --git a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupDto.java b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupDto.java index 6948146..91f8247 100644 --- a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupDto.java +++ b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupDto.java @@ -4,8 +4,6 @@ import de.ph87.homeautomation.channel.ChannelDto; import lombok.Getter; import lombok.ToString; -import java.time.ZonedDateTime; - @Getter @ToString(callSuper = true) public class KnxGroupDto extends ChannelDto { @@ -26,10 +24,6 @@ public class KnxGroupDto extends ChannelDto { public final KnxTelegram lastTelegram; - public final Double value; - - public final ZonedDateTime timestamp; - public final KnxGroupLinkInfo read; public final byte[] sendValue; @@ -46,8 +40,6 @@ public class KnxGroupDto extends ChannelDto { this.puid = knxGroup.getPuid(); this.ets = knxGroup.isEts(); this.lastTelegram = knxGroup.getLastTelegram(); - this.value = knxGroup.getValue(); - this.timestamp = knxGroup.getTimestamp(); this.read = knxGroup.getRead(); this.sendValue = knxGroup.getSendValue(); this.send = knxGroup.getSend(); diff --git a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupWriteService.java b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupWriteService.java index f711f05..aa959c4 100644 --- a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupWriteService.java +++ b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupWriteService.java @@ -1,6 +1,5 @@ package de.ph87.homeautomation.knx.group; -import de.ph87.homeautomation.channel.ChannelChangedEvent; import de.ph87.homeautomation.channel.ChannelDto; import de.ph87.homeautomation.web.WebSocketService; import lombok.RequiredArgsConstructor; @@ -28,10 +27,10 @@ public class KnxGroupWriteService { private final ApplicationEventPublisher applicationEventPublisher; - private final WebSocketService webSocketService; - private final KnxGroupMapper knxGroupMapper; + private final WebSocketService webSocketService; + public void requestRead(final KnxGroup knxGroup) { knxGroup.getRead().setNextTimestamp(ZonedDateTime.now()); log.debug("Requesting read for KnxGroup: {}", knxGroup); @@ -94,7 +93,6 @@ public class KnxGroupWriteService { knxGroup.setTimestamp(ZonedDateTime.now()); log.debug("KnxGroup updated: {}", knxGroup); publish(knxGroup); - applicationEventPublisher.publishEvent(new ChannelChangedEvent(knxGroup)); } else { log.error("Failed to get value from DptXlator {} for KnxGroup {}", translator, knxGroup); } @@ -119,9 +117,11 @@ public class KnxGroupWriteService { return Optional.empty(); } + @SuppressWarnings("UnusedReturnValue") private KnxGroupDto publish(final KnxGroup knxGroup) { final KnxGroupDto dto = knxGroupMapper.toDto(knxGroup); - webSocketService.send(ChannelDto.class.getSimpleName(), dto, true); + applicationEventPublisher.publishEvent(dto); + webSocketService.send(ChannelDto.class, dto, true); return dto; } diff --git a/src/main/java/de/ph87/homeautomation/logic/Logic.java b/src/main/java/de/ph87/homeautomation/logic/Logic.java new file mode 100644 index 0000000..7b537ea --- /dev/null +++ b/src/main/java/de/ph87/homeautomation/logic/Logic.java @@ -0,0 +1,49 @@ +package de.ph87.homeautomation.logic; + +import de.ph87.homeautomation.channel.Channel; +import de.ph87.homeautomation.channel.IChannelOwner; +import de.ph87.homeautomation.property.Property; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.*; +import java.time.ZonedDateTime; +import java.util.HashSet; +import java.util.Set; + +@Getter +@Setter +@ToString +@Entity +@NoArgsConstructor +public class Logic extends Channel { + + @Column(nullable = false, unique = true) + private String name; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private LogicOperator operator; + + @ManyToMany + @ToString.Exclude + private Set properties = new HashSet<>(); + + private Double value; + + private ZonedDateTime timestamp; + + public Logic(final String name, final LogicOperator operator, final HashSet properties) { + this.name = name; + this.operator = operator; + this.properties = properties; + } + + @Override + public Class getChannelOwnerClass() { + return LogicChannelOwner.class; + } + +} diff --git a/src/main/java/de/ph87/homeautomation/logic/LogicChannelOwner.java b/src/main/java/de/ph87/homeautomation/logic/LogicChannelOwner.java new file mode 100644 index 0000000..91aa627 --- /dev/null +++ b/src/main/java/de/ph87/homeautomation/logic/LogicChannelOwner.java @@ -0,0 +1,47 @@ +package de.ph87.homeautomation.logic; + +import de.ph87.homeautomation.channel.Channel; +import de.ph87.homeautomation.channel.IChannelOwner; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class LogicChannelOwner implements IChannelOwner { + + private final LogicReader logicReader; + + private final LogicWriter logicWriter; + + @Override + public void requestUpdate(final Channel channel) { + logicWriter.update((Logic) channel); + } + + @Override + public void write(final Channel channel, final double value) { + throw new RuntimeException(); + } + + @Override + public LogicDto toDto(final Channel channel) { + return logicReader.toDto((Logic) channel); + } + + @Override + public List findAllDto() { + return logicReader.findAllDto(); + } + + @Override + public List findAllDtoLike(final String like) { + return logicReader.findAllDtoLike(like); + } + +} diff --git a/src/main/java/de/ph87/homeautomation/logic/LogicDto.java b/src/main/java/de/ph87/homeautomation/logic/LogicDto.java new file mode 100644 index 0000000..8fb25ab --- /dev/null +++ b/src/main/java/de/ph87/homeautomation/logic/LogicDto.java @@ -0,0 +1,27 @@ +package de.ph87.homeautomation.logic; + +import de.ph87.homeautomation.channel.ChannelDto; +import de.ph87.homeautomation.property.Property; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.Set; +import java.util.stream.Collectors; + +@Getter +@Setter +@ToString +public class LogicDto extends ChannelDto { + + private LogicOperator operator; + + private Set propertyIds; + + public LogicDto(final Logic logic) { + super(logic); + this.operator = logic.getOperator(); + this.propertyIds = logic.getProperties().stream().map(Property::getId).collect(Collectors.toSet()); + } + +} diff --git a/src/main/java/de/ph87/homeautomation/logic/LogicOperator.java b/src/main/java/de/ph87/homeautomation/logic/LogicOperator.java new file mode 100644 index 0000000..c738290 --- /dev/null +++ b/src/main/java/de/ph87/homeautomation/logic/LogicOperator.java @@ -0,0 +1,5 @@ +package de.ph87.homeautomation.logic; + +public enum LogicOperator { + OR, AND, XOR +} diff --git a/src/main/java/de/ph87/homeautomation/logic/LogicReader.java b/src/main/java/de/ph87/homeautomation/logic/LogicReader.java new file mode 100644 index 0000000..55f0aab --- /dev/null +++ b/src/main/java/de/ph87/homeautomation/logic/LogicReader.java @@ -0,0 +1,35 @@ +package de.ph87.homeautomation.logic; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class LogicReader { + + private final LogicRepository logicRepository; + + public List findAllDto() { + return logicRepository.findAll().stream().map(this::toDto).collect(Collectors.toList()); + } + + public LogicDto toDto(final Logic logic) { + return new LogicDto(logic); + } + + public List findAllDtoLike(final String like) { + return logicRepository.findAllByNameContainsIgnoreCase(like).stream().map(this::toDto).collect(Collectors.toList()); + } + + public List findAllByPropertyId(final long id) { + return logicRepository.findAllByPropertyName(id); + } + +} diff --git a/src/main/java/de/ph87/homeautomation/logic/LogicRepository.java b/src/main/java/de/ph87/homeautomation/logic/LogicRepository.java new file mode 100644 index 0000000..1c22c37 --- /dev/null +++ b/src/main/java/de/ph87/homeautomation/logic/LogicRepository.java @@ -0,0 +1,20 @@ +package de.ph87.homeautomation.logic; + +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; + +import java.util.List; +import java.util.Optional; + +public interface LogicRepository extends CrudRepository { + + List findAll(); + + List findAllByNameContainsIgnoreCase(String like); + + @Query("select l from Logic l join l.properties p where p.id = :id") + List findAllByPropertyName(long id); + + Optional findByName(String name); + +} diff --git a/src/main/java/de/ph87/homeautomation/logic/LogicWriter.java b/src/main/java/de/ph87/homeautomation/logic/LogicWriter.java new file mode 100644 index 0000000..36c0504 --- /dev/null +++ b/src/main/java/de/ph87/homeautomation/logic/LogicWriter.java @@ -0,0 +1,71 @@ +package de.ph87.homeautomation.logic; + +import de.ph87.homeautomation.channel.ChannelDto; +import de.ph87.homeautomation.property.Property; +import de.ph87.homeautomation.property.PropertyDto; +import de.ph87.homeautomation.web.WebSocketService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import java.time.ZonedDateTime; +import java.util.Objects; + +import static de.ph87.homeautomation.shared.Helpers.getCurrentTransactionName; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class LogicWriter { + + private final LogicReader logicReader; + + private final ApplicationEventPublisher applicationEventPublisher; + + private final WebSocketService webSocketService; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION) +// @Transactional(propagation = Propagation.REQUIRES_NEW) + public void onPropertyChanged(final PropertyDto dto) { + log.debug("Listen [{}]: {}", getCurrentTransactionName(), dto.getTitle()); + logicReader.findAllByPropertyId(dto.getId()).forEach(this::update); + } + + public void update(final Logic logic) { + final Double value = evaluate(logic); + if (!Objects.equals(logic.getValue(), value)) { + logic.setValue(value); + logic.setTimestamp(ZonedDateTime.now()); + log.info("Logic changed: {}", logic); + publish(logic, true); + } + } + + private Double evaluate(final Logic logic) { + if (logic.getProperties().stream().map(Property::getValue).anyMatch(Objects::isNull)) { + return null; + } + switch (logic.getOperator()) { + case OR: + return logic.getProperties().stream().map(Property::getValue).anyMatch(v -> v > 0) ? 1.0 : 0.0; + case AND: + return logic.getProperties().stream().map(Property::getValue).allMatch(v -> v > 0) ? 1.0 : 0.0; + case XOR: + return (double) (logic.getProperties().stream().map(Property::getValue).map(v -> v > 0).count() % 2); + } + throw new RuntimeException(); + } + + private void publish(final Logic logic, final boolean existing) { + final LogicDto dto = logicReader.toDto(logic); + log.debug("Publish [{}]: {}", getCurrentTransactionName(), dto.getTitle()); + applicationEventPublisher.publishEvent(dto); + webSocketService.send(ChannelDto.class, dto, existing); + } + +} diff --git a/src/main/java/de/ph87/homeautomation/property/PropertyChannelService.java b/src/main/java/de/ph87/homeautomation/property/PropertyChannelService.java deleted file mode 100644 index d1bf810..0000000 --- a/src/main/java/de/ph87/homeautomation/property/PropertyChannelService.java +++ /dev/null @@ -1,36 +0,0 @@ -package de.ph87.homeautomation.property; - -import de.ph87.homeautomation.channel.ChannelService; -import de.ph87.homeautomation.channel.IChannelOwner; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.event.ApplicationStartedEvent; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Optional; - -@Slf4j -@Service -@Transactional -@RequiredArgsConstructor -public class PropertyChannelService { - - private final ChannelService channelService; - - private final PropertyReadService propertyReadService; - - @EventListener(ApplicationStartedEvent.class) - public void readAllPropertyChannels() { - propertyReadService.findAllByReadChannelNotNull().forEach(property -> { - final Optional ownerOptional = channelService.findByChannel(property.getReadChannel()); - if (ownerOptional.isPresent()) { - ownerOptional.get().read(property.getReadChannel()); - } else { - log.error("No Owner for Property: {}", property); - } - }); - } - -} diff --git a/src/main/java/de/ph87/homeautomation/property/PropertyController.java b/src/main/java/de/ph87/homeautomation/property/PropertyController.java index 15a97ef..a2bf2ac 100644 --- a/src/main/java/de/ph87/homeautomation/property/PropertyController.java +++ b/src/main/java/de/ph87/homeautomation/property/PropertyController.java @@ -42,17 +42,17 @@ public class PropertyController implements ISearchController { @PostMapping("set/{id}/value") public PropertyDto setValue(@PathVariable final long id, @RequestBody final double value) { - return propertyWriteService.set(id, propertyWriteService::write, value); + return propertyWriteService.set(id, propertyWriteService::writeToChannel, value); } @PostMapping("set/{id}/readChannel") public PropertyDto setReadChannel(@PathVariable final long id, @RequestBody(required = false) final Long channelId) { - return propertyWriteService.set(id, Property::setReadChannel, channelService.getById(channelId)); + return propertyWriteService.set(id, (p, v) -> p.setReadChannel(channelService.getById(v)), channelId); } @PostMapping("set/{id}/writeChannel") public PropertyDto setWriteChannel(@PathVariable final long id, @RequestBody(required = false) final Long channelId) { - return propertyWriteService.set(id, Property::setWriteChannel, channelService.getById(channelId)); + return propertyWriteService.set(id, (p, v) -> p.setWriteChannel(channelService.getById(v)), channelId); } @Override diff --git a/src/main/java/de/ph87/homeautomation/property/PropertyEventListener.java b/src/main/java/de/ph87/homeautomation/property/PropertyEventListener.java new file mode 100644 index 0000000..4bd337c --- /dev/null +++ b/src/main/java/de/ph87/homeautomation/property/PropertyEventListener.java @@ -0,0 +1,21 @@ +package de.ph87.homeautomation.property; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class PropertyEventListener { + + private final PropertyWriteService propertyWriteService; + + @EventListener(ApplicationStartedEvent.class) + public void onApplicationStarted() { + propertyWriteService.updateAllProperties(); + } + +} diff --git a/src/main/java/de/ph87/homeautomation/property/PropertyMapper.java b/src/main/java/de/ph87/homeautomation/property/PropertyMapper.java index f11b896..748955e 100644 --- a/src/main/java/de/ph87/homeautomation/property/PropertyMapper.java +++ b/src/main/java/de/ph87/homeautomation/property/PropertyMapper.java @@ -15,7 +15,11 @@ public class PropertyMapper { private final ChannelService channelService; public PropertyDto toDto(final Property property) { - return new PropertyDto(property, channelService.toDtoAllowNull(property.getReadChannel()), channelService.toDtoAllowNull(property.getWriteChannel())); + return new PropertyDto( + property, + channelService.toDtoAllowNull(property.getReadChannel()), + channelService.toDtoAllowNull(property.getWriteChannel()) + ); } } diff --git a/src/main/java/de/ph87/homeautomation/property/PropertyWriteService.java b/src/main/java/de/ph87/homeautomation/property/PropertyWriteService.java index d82f022..3d8fbc7 100644 --- a/src/main/java/de/ph87/homeautomation/property/PropertyWriteService.java +++ b/src/main/java/de/ph87/homeautomation/property/PropertyWriteService.java @@ -1,17 +1,23 @@ package de.ph87.homeautomation.property; -import de.ph87.homeautomation.channel.ChannelChangedEvent; +import de.ph87.homeautomation.channel.ChannelDto; import de.ph87.homeautomation.channel.ChannelService; +import de.ph87.homeautomation.channel.IChannelOwner; import de.ph87.homeautomation.web.WebSocketService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; +import java.util.List; +import java.util.Optional; import java.util.function.BiConsumer; +import static de.ph87.homeautomation.shared.Helpers.getCurrentTransactionName; + @Slf4j @Service @Transactional @@ -26,24 +32,41 @@ public class PropertyWriteService { private final PropertyMapper propertyMapper; - private final WebSocketService webSocketService; - private final PropertyRepository propertyRepository; - public void write(final Property property, final double value) { - channelService.write(property, value); + private final WebSocketService webSocketService; + + private final ApplicationEventPublisher applicationEventPublisher; + + public void updateAllProperties() { + propertyReadService.findAllByReadChannelNotNull().forEach(property -> { + final Optional ownerOptional = channelService.findByChannel(property.getReadChannel()); + if (ownerOptional.isPresent()) { + ownerOptional.get().requestUpdate(property.getReadChannel()); + } else { + log.error("No Owner for Property: {}", property); + } + }); } - @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) - public void channelChangedEventListener(final ChannelChangedEvent event) { - propertyReadService.findAllByReadChannel_Id(event.getChannelId()) - .forEach(property -> { + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION) +// @Transactional(propagation = Propagation.REQUIRES_NEW) + public void onChannelChanged(final ChannelDto dto) { + log.debug("onChannelChanged [{}]: {}", getCurrentTransactionName(), dto.getTitle()); + final List properties = propertyReadService.findAllByReadChannel_Id(dto.getId()); + if (!properties.isEmpty()) { + properties.forEach(property -> { property.setValue(property.getReadChannel().getValue()); property.setTimestamp(property.getReadChannel().getTimestamp()); - log.debug("Updated Property from Channel: {}", property); + log.debug("Updated Property \"{}\" by Channel \"{}\"", property.getTitle(), property.getReadChannel().getName()); publish(property, true); } ); + } + } + + public void writeToChannel(final Property property, final double value) { + channelService.write(property, value); } public PropertyDto create() { @@ -76,7 +99,9 @@ public class PropertyWriteService { private PropertyDto publish(final Property property, final boolean existing) { final PropertyDto dto = propertyMapper.toDto(property); - webSocketService.send(dto, existing); + log.debug("Publish [{}]: {}", getCurrentTransactionName(), dto.getTitle()); + applicationEventPublisher.publishEvent(dto); + webSocketService.send(PropertyDto.class, dto, existing); return dto; } diff --git a/src/main/java/de/ph87/homeautomation/scene/SceneWriteService.java b/src/main/java/de/ph87/homeautomation/scene/SceneWriteService.java index 2204497..14ae7fd 100644 --- a/src/main/java/de/ph87/homeautomation/scene/SceneWriteService.java +++ b/src/main/java/de/ph87/homeautomation/scene/SceneWriteService.java @@ -44,7 +44,7 @@ public class SceneWriteService { private SceneDto publish(final Scene scene, final boolean existing) { final SceneDto dto = sceneMapper.toDto(scene); - webSocketService.send(dto, existing); + webSocketService.send(SceneDto.class, dto, existing); return dto; } diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculationService.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculationService.java index 6d32a4d..26603ae 100644 --- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculationService.java +++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculationService.java @@ -31,7 +31,7 @@ public class ScheduleCalculationService { private final ScheduleReadService scheduleReadService; - private final ApplicationEventPublisher eventPublisher; + private final ApplicationEventPublisher applicationEventPublisher; @EventListener(ApplicationStartedEvent.class) public void calculateAllNext() { @@ -49,7 +49,7 @@ public class ScheduleCalculationService { } else { log.info("Next schedule for \"{}\": {}", schedule.getTitle(), nextEntry.get().getNextFuzzyTimestamp()); } - eventPublisher.publishEvent(new ScheduleThreadWakeUpEvent()); + applicationEventPublisher.publishEvent(new ScheduleThreadWakeUpEvent()); } private void calculateEntry(final Schedule schedule, final ScheduleEntry entry, final ZonedDateTime now) { diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleController.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleController.java index d402750..980ff10 100644 --- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleController.java +++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleController.java @@ -6,8 +6,6 @@ import org.springframework.web.bind.annotation.*; import java.util.List; -import static de.ph87.homeautomation.shared.Helpers.mapOrNull; - @RestController @RequestMapping("schedule") @RequiredArgsConstructor @@ -51,7 +49,7 @@ public class ScheduleController { @PostMapping("set/{id}/property") public ScheduleDto setPropertyName(@PathVariable final long id, @RequestBody(required = false) final Long propertyId) { - return scheduleWriteService.set(id, Schedule::setProperty, mapOrNull(propertyId, propertyReadService::getById)); + return scheduleWriteService.set(id, (s, v) -> s.setProperty(v == null ? null : propertyReadService.getById(v)), propertyId); } } diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleExecutionService.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleExecutionService.java index eac9e01..be125ac 100644 --- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleExecutionService.java +++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleExecutionService.java @@ -41,7 +41,7 @@ public class ScheduleExecutionService { private void executeEntry(final Schedule schedule, final ScheduleEntry entry, final ZonedDateTime now) { entry.setLastClearTimestamp(entry.getNextClearTimestamp()); log.info("Executing Schedule \"{}\" Entry {}", schedule.getTitle(), entry); - propertyWriteService.write(schedule.getProperty(), entry.getValue()); + propertyWriteService.writeToChannel(schedule.getProperty(), entry.getValue()); scheduleCalculationService.calculateSchedule(schedule, now); } diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleThreadService.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleThreadService.java index 31d6871..909afdf 100644 --- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleThreadService.java +++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleThreadService.java @@ -4,7 +4,6 @@ import de.ph87.homeautomation.schedule.entry.ScheduleEntryReadService; import de.ph87.homeautomation.shared.AbstractThreadService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; @@ -46,9 +45,8 @@ public class ScheduleThreadService extends AbstractThreadService { // nothing } - @EventListener(ScheduleThreadWakeUpEvent.class) @TransactionalEventListener - public void wakeUp() { + public void wakeUp(final ScheduleThreadWakeUpEvent event) { _wakeUp(); } diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleWriteService.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleWriteService.java index ab2146e..9fdaccf 100644 --- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleWriteService.java +++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleWriteService.java @@ -57,7 +57,7 @@ public class ScheduleWriteService { private ScheduleDto publish(final Schedule schedule, final boolean existing) { final ScheduleDto dto = scheduleMapper.toDto(schedule); - webSocketService.send(dto, existing); + webSocketService.send(ScheduleDto.class, dto, existing); return dto; } diff --git a/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntryWriteService.java b/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntryWriteService.java index faaabe4..a0d9f4b 100644 --- a/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntryWriteService.java +++ b/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntryWriteService.java @@ -54,7 +54,7 @@ public class ScheduleEntryWriteService { private ScheduleEntryDto publish(final ScheduleEntry entry, final boolean existing) { final ScheduleEntryDto dto = scheduleEntryMapper.toDto(entry); - webSocketService.send(dto, existing); + webSocketService.send(ScheduleEntryDto.class, dto, existing); return dto; } diff --git a/src/main/java/de/ph87/homeautomation/shared/Helpers.java b/src/main/java/de/ph87/homeautomation/shared/Helpers.java index 25ce171..fadda02 100644 --- a/src/main/java/de/ph87/homeautomation/shared/Helpers.java +++ b/src/main/java/de/ph87/homeautomation/shared/Helpers.java @@ -1,5 +1,7 @@ package de.ph87.homeautomation.shared; +import org.springframework.transaction.support.TransactionSynchronizationManager; + import java.util.ArrayList; import java.util.List; import java.util.function.Function; @@ -62,4 +64,13 @@ public class Helpers { return c; } + public static String getCurrentTransactionName() { + final String fullName = TransactionSynchronizationManager.getCurrentTransactionName(); + if (fullName == null) { + return null; + } + final String[] parts = fullName.split("\\."); + return parts[parts.length - 2] + "." + parts[parts.length - 1]; + } + } diff --git a/src/main/java/de/ph87/homeautomation/web/WebSocketService.java b/src/main/java/de/ph87/homeautomation/web/WebSocketService.java index d20755a..cddea2a 100644 --- a/src/main/java/de/ph87/homeautomation/web/WebSocketService.java +++ b/src/main/java/de/ph87/homeautomation/web/WebSocketService.java @@ -17,12 +17,8 @@ public class WebSocketService { private final SimpMessageSendingOperations simpMessageSendingOperations; - public void send(@NonNull final Serializable payload, final boolean existing) { - send(payload.getClass().getSimpleName(), payload, existing); - } - - public void send(@NonNull final String type, @NonNull final Serializable payload, final boolean existing) { - final Message message = new Message(type, payload, existing); + public void send(@NonNull final Class asClass, @NonNull final Serializable payload, final boolean existing) { + final Message message = new Message(asClass.getSimpleName(), payload, existing); log.debug("Sending message via websocket: {}", message); simpMessageSendingOperations.convertAndSend(WebSocketConfig.DESTINATION_PREFIX, message); }