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 extends ChannelDto> 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 extends ChannelDto> findAllDto() {
return channelOwners.stream().map(IChannelOwner::findAllDto).reduce(new ArrayList<>(), Helpers::merge);
}
- public List findAllDtoLike(final String term) {
+ public List extends ChannelDto> 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 extends ChannelDto> 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 extends ChannelDto> findAllDto();
- List findAllDtoLike(final String like);
+ List extends ChannelDto> 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 extends IChannelOwner> 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 extends Serializable> 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);
}