From d8664a800a930f5a79ef339204c329849866e256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Thu, 28 Oct 2021 08:50:53 +0200 Subject: [PATCH] MultiGroups + UDP Broadcast --- deploy.sh | 2 +- .../ph87/homeautomation/DemoDataService.java | 116 +++++++++--------- .../device/DeviceWriteService.java | 4 +- .../homeautomation/knx/KnxThreadService.java | 4 +- .../homeautomation/knx/group/KnxGroup.java | 11 ++ .../homeautomation/knx/group/KnxGroupDto.java | 3 + .../knx/group/KnxGroupSetService.java | 2 +- .../knx/group/KnxGroupWriteService.java | 50 +++++++- .../schedule/PropertyEntry.java | 13 +- .../schedule/entry/ScheduleEntry.java | 8 +- 10 files changed, 136 insertions(+), 77 deletions(-) diff --git a/deploy.sh b/deploy.sh index bae732a..96c72d7 100755 --- a/deploy.sh +++ b/deploy.sh @@ -3,5 +3,5 @@ cd "$(dirname "$0")" || exit 1 mvn clean package spring-boot:repackage && \ -scp target/Homeautomation.jar media@10.0.0.50:/home/media/Homeautomation/Homeautomation.jar && \ +scp target/Homeautomation.jar media@10.0.0.50:/home/media/Homeautomation/Homeautomation.jar.update && \ curl -m 2 -s http://10.0.0.50:8080/server/shutdown && echo "Server restarting..." || echo "Failed to restart server!" \ No newline at end of file diff --git a/src/main/java/de/ph87/homeautomation/DemoDataService.java b/src/main/java/de/ph87/homeautomation/DemoDataService.java index 766d0ee..4bf847c 100644 --- a/src/main/java/de/ph87/homeautomation/DemoDataService.java +++ b/src/main/java/de/ph87/homeautomation/DemoDataService.java @@ -35,112 +35,111 @@ public class DemoDataService { @PostConstruct public void postConstruct() { - final KnxGroupDto eg_flur_licht_schalten = createKnxGroupIfNotExists("EG Flur Licht Schalten", 1294, "1.001", false); - final KnxGroupDto eg_ambiente_schalten = createKnxGroupIfNotExists("EG Ambiente Schalten", 848, "1.001", false); - final KnxGroupDto og_ambiente_schalten = createKnxGroupIfNotExists("OG Ambiente Schalten", 1539, "1.001", false); - final KnxGroupDto wohnzimmer_rollladen_position_anfahren = createKnxGroupIfNotExists("Wohnzimmer Rollladen Position Anfahren", new GroupAddress(0, 4, 24), "5.001", false); - final KnxGroupDto schlafzimmer_rollladen_position_anfahren = createKnxGroupIfNotExists("Schlafzimmer Rollladen Position Anfahren", new GroupAddress(0, 3, 3), "5.001", false); - final KnxGroupDto flur_rollladen_position_anfahren = createKnxGroupIfNotExists("Flur Rollladen Position Anfahren", new GroupAddress(0, 5, 13), "5.001", false); - final KnxGroupDto badewanne_schalten = createKnxGroupIfNotExists("Badewanne Schalten", 781, "1.001", false); - final KnxGroupDto bad_licht_mitteschalten = createKnxGroupIfNotExists("Bad Licht Mitte Schalten", 797, "1.001", false); - final KnxGroupDto helligkeit = createKnxGroupIfNotExists("Helligkeit", 1286, "9.004", false); + final KnxGroupDto eg_flur_licht_schalten = createKnxGroupIfNotExists("EG Flur Licht Schalten", 1294, "1.001", false, false); + final KnxGroupDto eg_ambiente_schalten = createKnxGroupIfNotExists("EG Ambiente Schalten", 848, "1.001", false, false); + final KnxGroupDto og_ambiente_schalten = createKnxGroupIfNotExists("OG Ambiente Schalten", 1539, "1.001", false, false); + final KnxGroupDto wohnzimmer_rollladen_position_anfahren = createKnxGroupIfNotExists("Wohnzimmer Rollladen Position Anfahren", new GroupAddress(0, 4, 24), "5.001", false, false); + final KnxGroupDto schlafzimmer_rollladen_position_anfahren = createKnxGroupIfNotExists("Schlafzimmer Rollladen Position Anfahren", new GroupAddress(0, 3, 3), "5.001", false, false); + final KnxGroupDto flur_rollladen_position_anfahren = createKnxGroupIfNotExists("Flur Rollladen Position Anfahren", new GroupAddress(0, 5, 13), "5.001", false, false); + final KnxGroupDto badewanne_schalten = createKnxGroupIfNotExists("Badewanne Schalten", 781, "1.001", false, false); + final KnxGroupDto bad_licht_mitteschalten = createKnxGroupIfNotExists("Bad Licht Mitte Schalten", 797, "1.001", false, false); + final KnxGroupDto helligkeit = createKnxGroupIfNotExists("Helligkeit", 1286, "9.004", false, true); final Schedule scheduleEgFlurLicht = new Schedule(); scheduleEgFlurLicht.setName("EG Flur Licht"); - createTime(scheduleEgFlurLicht, 11, 30, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten.getAddress(), "true")); - createTime(scheduleEgFlurLicht, 12, 30, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten.getAddress(), "false")); - createTime(scheduleEgFlurLicht, 16, 30, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten.getAddress(), "true")); - createTime(scheduleEgFlurLicht, 17, 30, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten.getAddress(), "false")); - createTime(scheduleEgFlurLicht, 22, 0, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten.getAddress(), "true")); - createTime(scheduleEgFlurLicht, 23, 0, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten.getAddress(), "false")); - createTime(scheduleEgFlurLicht, 1, 0, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten.getAddress(), "true")); - createTime(scheduleEgFlurLicht, 2, 0, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten.getAddress(), "false")); + createTime(scheduleEgFlurLicht, 11, 30, 0, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten, true)); + createTime(scheduleEgFlurLicht, 12, 30, 0, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten, false)); + createTime(scheduleEgFlurLicht, 16, 30, 0, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten, true)); + createTime(scheduleEgFlurLicht, 17, 30, 0, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten, false)); + createTime(scheduleEgFlurLicht, 22, 0, 0, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten, true)); + createTime(scheduleEgFlurLicht, 23, 0, 0, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten, false)); + createTime(scheduleEgFlurLicht, 1, 0, 0, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten, true)); + createTime(scheduleEgFlurLicht, 2, 0, 0, MIN30_SEC, new PropertyEntry(eg_flur_licht_schalten, false)); scheduleRepository.save(scheduleEgFlurLicht); final Schedule scheduleEgAmbiente = new Schedule(); scheduleEgAmbiente.setName("EG Ambiente"); - createTime(scheduleEgAmbiente, 7, 15, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_ambiente_schalten.getAddress(), "true")); - createTime(scheduleEgAmbiente, 9, 30, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_ambiente_schalten.getAddress(), "false")); - createSunset(scheduleEgAmbiente, Zenith.OFFICIAL, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_ambiente_schalten.getAddress(), "true")); - createSunset(scheduleEgAmbiente, Zenith.ASTRONOMICAL, MIN30_SEC, MIN30_SEC, new PropertyEntry(eg_ambiente_schalten.getAddress(), "false")); + createTime(scheduleEgAmbiente, 7, 15, 0, MIN30_SEC, new PropertyEntry(eg_ambiente_schalten, true)); + createTime(scheduleEgAmbiente, 9, 30, 0, MIN30_SEC, new PropertyEntry(eg_ambiente_schalten, false)); + createSunset(scheduleEgAmbiente, Zenith.OFFICIAL, MIN30_SEC, new PropertyEntry(eg_ambiente_schalten, true)); + createSunset(scheduleEgAmbiente, Zenith.ASTRONOMICAL, MIN30_SEC, new PropertyEntry(eg_ambiente_schalten, false)); scheduleRepository.save(scheduleEgAmbiente); final Schedule scheduleOgAmbiente = new Schedule(); scheduleOgAmbiente.setName("OG Ambiente"); - createTime(scheduleOgAmbiente, 7, 15, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(og_ambiente_schalten.getAddress(), "true")); - createTime(scheduleOgAmbiente, 9, 30, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(og_ambiente_schalten.getAddress(), "false")); - createSunset(scheduleOgAmbiente, Zenith.OFFICIAL, MIN30_SEC, MIN30_SEC, new PropertyEntry(og_ambiente_schalten.getAddress(), "true")); - createSunset(scheduleOgAmbiente, Zenith.ASTRONOMICAL, MIN30_SEC, MIN30_SEC, new PropertyEntry(og_ambiente_schalten.getAddress(), "false")); + createTime(scheduleOgAmbiente, 7, 15, 0, MIN30_SEC, new PropertyEntry(og_ambiente_schalten, true)); + createTime(scheduleOgAmbiente, 9, 30, 0, MIN30_SEC, new PropertyEntry(og_ambiente_schalten, false)); + createSunset(scheduleOgAmbiente, Zenith.OFFICIAL, MIN30_SEC, new PropertyEntry(og_ambiente_schalten, true)); + createSunset(scheduleOgAmbiente, Zenith.ASTRONOMICAL, MIN30_SEC, new PropertyEntry(og_ambiente_schalten, false)); scheduleRepository.save(scheduleOgAmbiente); final Schedule scheduleWohnzimmerRollladen = new Schedule(); scheduleWohnzimmerRollladen.setName("Rollläden Wohnzimmer"); - createSunrise(scheduleWohnzimmerRollladen, Zenith.OFFICIAL, 0, 0, new PropertyEntry(wohnzimmer_rollladen_position_anfahren.getAddress(), "0")); - createSunset(scheduleWohnzimmerRollladen, Zenith.OFFICIAL, 0, 0, new PropertyEntry(wohnzimmer_rollladen_position_anfahren.getAddress(), "100")); + createSunrise(scheduleWohnzimmerRollladen, Zenith.CIVIL, 0, new PropertyEntry(wohnzimmer_rollladen_position_anfahren, 0)); + createSunset(scheduleWohnzimmerRollladen, Zenith.CIVIL, 0, new PropertyEntry(wohnzimmer_rollladen_position_anfahren, 100)); scheduleRepository.save(scheduleWohnzimmerRollladen); final Schedule scheduleSchlafzimmerRollladen = new Schedule(); scheduleSchlafzimmerRollladen.setName("Rollläden Schlafzimmer"); - createTime(scheduleSchlafzimmerRollladen, 7, 0, 0, 0, 0, new PropertyEntry(schlafzimmer_rollladen_position_anfahren.getAddress(), "0")); - createTime(scheduleSchlafzimmerRollladen, 20, 0, 0, 0, 0, new PropertyEntry(schlafzimmer_rollladen_position_anfahren.getAddress(), "100")); + createTime(scheduleSchlafzimmerRollladen, 7, 0, 0, 0, new PropertyEntry(schlafzimmer_rollladen_position_anfahren, 0)); + createSunset(scheduleSchlafzimmerRollladen, Zenith.CIVIL, 0, new PropertyEntry(schlafzimmer_rollladen_position_anfahren, 100)); scheduleRepository.save(scheduleSchlafzimmerRollladen); final Schedule scheduleFlurRollladen = new Schedule(); scheduleFlurRollladen.setName("Rollläden Flur"); - createSunrise(scheduleFlurRollladen, Zenith.NAUTICAL, 0, 0, new PropertyEntry(flur_rollladen_position_anfahren.getAddress(), "0")); - createSunset(scheduleFlurRollladen, Zenith.NAUTICAL, 0, 0, new PropertyEntry(flur_rollladen_position_anfahren.getAddress(), "100")); + createSunrise(scheduleFlurRollladen, Zenith.CIVIL, 0, new PropertyEntry(flur_rollladen_position_anfahren, 0)); + createSunset(scheduleFlurRollladen, Zenith.CIVIL, 0, new PropertyEntry(flur_rollladen_position_anfahren, 100)); scheduleRepository.save(scheduleFlurRollladen); final Schedule scheduleBadewanneBlinken = new Schedule(); scheduleBadewanneBlinken.setName("Badewanne"); final int interval = 2; - final int fuzzy = 0; int seconds = interval; - createRelative(scheduleBadewanneBlinken, seconds += interval, fuzzy, fuzzy, new PropertyEntry(badewanne_schalten.getAddress(), "true")); - createRelative(scheduleBadewanneBlinken, seconds += interval, fuzzy, fuzzy, new PropertyEntry(badewanne_schalten.getAddress(), "false")); - createRelative(scheduleBadewanneBlinken, seconds += interval, fuzzy, fuzzy, new PropertyEntry(badewanne_schalten.getAddress(), "true")); - createRelative(scheduleBadewanneBlinken, seconds += interval, fuzzy, fuzzy, new PropertyEntry(badewanne_schalten.getAddress(), "false")); + createRelative(scheduleBadewanneBlinken, seconds += interval, 0, new PropertyEntry(badewanne_schalten, true)); + createRelative(scheduleBadewanneBlinken, seconds += interval, 0, new PropertyEntry(badewanne_schalten, false)); + createRelative(scheduleBadewanneBlinken, seconds += interval, 0, new PropertyEntry(badewanne_schalten, true)); + createRelative(scheduleBadewanneBlinken, seconds += interval, 0, new PropertyEntry(badewanne_schalten, false)); scheduleRepository.save(scheduleBadewanneBlinken); final Schedule scheduleBadLichtMitte = new Schedule(); scheduleBadLichtMitte.setName("Bad Licht Mitte"); - createTime(scheduleBadLichtMitte, 10, 30, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten.getAddress(), "true")); - createTime(scheduleBadLichtMitte, 11, 30, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten.getAddress(), "false")); - createTime(scheduleBadLichtMitte, 15, 30, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten.getAddress(), "true")); - createTime(scheduleBadLichtMitte, 16, 30, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten.getAddress(), "false")); - createTime(scheduleBadLichtMitte, 21, 0, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten.getAddress(), "true")); - createTime(scheduleBadLichtMitte, 22, 0, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten.getAddress(), "false")); - createTime(scheduleBadLichtMitte, 0, 0, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten.getAddress(), "true")); - createTime(scheduleBadLichtMitte, 1, 0, 0, MIN30_SEC, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten.getAddress(), "false")); + createTime(scheduleBadLichtMitte, 10, 30, 0, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten, true)); + createTime(scheduleBadLichtMitte, 11, 30, 0, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten, false)); + createTime(scheduleBadLichtMitte, 15, 30, 0, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten, true)); + createTime(scheduleBadLichtMitte, 16, 30, 0, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten, false)); + createTime(scheduleBadLichtMitte, 21, 0, 0, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten, true)); + createTime(scheduleBadLichtMitte, 22, 0, 0, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten, false)); + createTime(scheduleBadLichtMitte, 0, 0, 0, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten, true)); + createTime(scheduleBadLichtMitte, 1, 0, 0, MIN30_SEC, new PropertyEntry(bad_licht_mitteschalten, false)); scheduleRepository.save(scheduleBadLichtMitte); } - private KnxGroupDto createKnxGroupIfNotExists(final String name, final int address, final String dpt, final boolean readable) { - return createKnxGroupIfNotExists(name, new GroupAddress(address), dpt, readable); + private KnxGroupDto createKnxGroupIfNotExists(final String name, final int address, final String dpt, final boolean readable, final boolean multiGroup) { + return createKnxGroupIfNotExists(name, new GroupAddress(address), dpt, readable, multiGroup); } - private KnxGroupDto createKnxGroupIfNotExists(final String name, final GroupAddress address, final String dpt, final boolean readable) { - return knxGroupRepository.findByAddressRaw(address.getRawAddress()).map(KnxGroupDto::new).orElseGet(() -> knxGroupWriteService.create(name, address, dpt, readable)); + private KnxGroupDto createKnxGroupIfNotExists(final String name, final GroupAddress address, final String dpt, final boolean readable, final boolean multiGroup) { + return knxGroupRepository.findByAddressRaw(address.getRawAddress()).map(KnxGroupDto::new).orElseGet(() -> knxGroupWriteService.create(name, address, dpt, readable, multiGroup)); } - private ScheduleEntry createRelative(final Schedule schedule, final int inSeconds, final int fuzzyMinusSeconds, final int fuzzyPlusSeconds, final Map.Entry... entries) { + private ScheduleEntry createRelative(final Schedule schedule, final int inSeconds, final int plusMinusSeconds, final Map.Entry... entries) { final ZonedDateTime now = ZonedDateTime.now().plusSeconds(inSeconds).withNano(0); - return create(schedule, ScheduleEntryType.TIME, null, now.getHour(), now.getMinute(), now.getSecond(), fuzzyMinusSeconds, fuzzyPlusSeconds, entries); + return create(schedule, ScheduleEntryType.TIME, null, now.getHour(), now.getMinute(), now.getSecond(), plusMinusSeconds, entries); } - private ScheduleEntry createTime(final Schedule schedule, final int hour, final int minute, final int second, final int fuzzyMinusSeconds, final int fuzzyPlusSeconds, final Map.Entry... entries) { - return create(schedule, ScheduleEntryType.TIME, null, hour, minute, second, fuzzyMinusSeconds, fuzzyPlusSeconds, entries); + private ScheduleEntry createTime(final Schedule schedule, final int hour, final int minute, final int second, final int plusMinusSeconds, final Map.Entry... entries) { + return create(schedule, ScheduleEntryType.TIME, null, hour, minute, second, plusMinusSeconds, entries); } - private ScheduleEntry createSunrise(final Schedule schedule, final Zenith zenith, final int fuzzyMinusSeconds, final int fuzzyPlusSeconds, final Map.Entry... entries) { - return create(schedule, ScheduleEntryType.SUNRISE, zenith, 0, 0, 0, fuzzyMinusSeconds, fuzzyPlusSeconds, entries); + private ScheduleEntry createSunrise(final Schedule schedule, final Zenith zenith, final int plusMinusSeconds, final Map.Entry... entries) { + return create(schedule, ScheduleEntryType.SUNRISE, zenith, 0, 0, 0, plusMinusSeconds, entries); } - private ScheduleEntry createSunset(final Schedule schedule, final Zenith zenith, final int fuzzyMinusSeconds, final int fuzzyPlusSeconds, final Map.Entry... entries) { - return create(schedule, ScheduleEntryType.SUNSET, zenith, 0, 0, 0, fuzzyMinusSeconds, fuzzyPlusSeconds, entries); + private ScheduleEntry createSunset(final Schedule schedule, final Zenith zenith, final int plusMinusSeconds, final Map.Entry... entries) { + return create(schedule, ScheduleEntryType.SUNSET, zenith, 0, 0, 0, plusMinusSeconds, entries); } - private ScheduleEntry create(final Schedule schedule, final ScheduleEntryType type, final Zenith zenith, final int hour, final int minute, final int second, final int fuzzyMinusSeconds, final int fuzzyPlusSeconds, final Map.Entry... entries) { + private ScheduleEntry create(final Schedule schedule, final ScheduleEntryType type, final Zenith zenith, final int hour, final int minute, final int second, final int plusMinusSeconds, final Map.Entry... entries) { final ScheduleEntry entry = new ScheduleEntry(); entry.setType(type); if (zenith != null) { @@ -149,8 +148,7 @@ public class DemoDataService { entry.setHour(hour); entry.setMinute(minute); entry.setSecond(second); - entry.setFuzzyMinusSeconds(fuzzyMinusSeconds); - entry.setFuzzyPlusSeconds(fuzzyPlusSeconds); + entry.setPlusMinusSeconds(plusMinusSeconds); Arrays.stream(entries).forEach(p -> entry.getProperties().put(p.getKey(), p.getValue())); schedule.getEntries().add(entry); return entry; diff --git a/src/main/java/de/ph87/homeautomation/device/DeviceWriteService.java b/src/main/java/de/ph87/homeautomation/device/DeviceWriteService.java index 752bd6c..446ea0f 100644 --- a/src/main/java/de/ph87/homeautomation/device/DeviceWriteService.java +++ b/src/main/java/de/ph87/homeautomation/device/DeviceWriteService.java @@ -21,12 +21,12 @@ public class DeviceWriteService { public void postConstruct() { final DeviceSwitch deviceSwitch = new DeviceSwitch(); deviceSwitch.setName("TEST"); - deviceSwitch.setStatePropertyName("knx.group.0/3/6"); + deviceSwitch.setStatePropertyName("knx.group.0.3.6"); deviceRepository.save(deviceSwitch); final DeviceNumber deviceNumber = new DeviceNumber(); deviceNumber.setName("Helligkeit"); - deviceNumber.setValuePropertyName("knx.group.0/5/6"); + deviceNumber.setValuePropertyName("knx.group.0.5.6"); deviceRepository.save(deviceNumber); } diff --git a/src/main/java/de/ph87/homeautomation/knx/KnxThreadService.java b/src/main/java/de/ph87/homeautomation/knx/KnxThreadService.java index 75bf2b4..99a0ef5 100644 --- a/src/main/java/de/ph87/homeautomation/knx/KnxThreadService.java +++ b/src/main/java/de/ph87/homeautomation/knx/KnxThreadService.java @@ -129,14 +129,14 @@ public class KnxThreadService extends AbstractThreadService implements NetworkLi @Override public void groupReadResponse(final ProcessEvent processEvent) { synchronized (databaseAccessLock) { - knxGroupWriteService.updateIfExists(processEvent.getDestination().getRawAddress(), processEvent.getASDU()); + knxGroupWriteService.updateIfExists(processEvent.getDestination().getRawAddress(), processEvent.getASDU(), processEvent.getSourceAddr()); } } @Override public void groupWrite(final ProcessEvent processEvent) { synchronized (databaseAccessLock) { - knxGroupWriteService.updateIfExists(processEvent.getDestination().getRawAddress(), processEvent.getASDU()); + knxGroupWriteService.updateIfExists(processEvent.getDestination().getRawAddress(), processEvent.getASDU(), processEvent.getSourceAddr()); } } diff --git a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroup.java b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroup.java index a4bd67a..7534b11 100644 --- a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroup.java +++ b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroup.java @@ -32,6 +32,10 @@ public class KnxGroup { private byte[] value; + private int lastDeviceAddressRaw; + + private String lastDeviceAddressString; + private Boolean booleanValue; private Double numberValue; @@ -42,6 +46,8 @@ public class KnxGroup { private int readInterval; + private boolean multiGroup; + @Embedded private KnxGroupLinkInfo read = new KnxGroupLinkInfo(); @@ -61,4 +67,9 @@ public class KnxGroup { return new GroupAddress(addressRaw); } + public String getPropertyName() { + final GroupAddress address = getAddress(); + return "knx.group." + address.getMainGroup() + "." + address.getMiddleGroup() + "." + address.getSubGroup8(); + } + } 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 411dfed..9f70786 100644 --- a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupDto.java +++ b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupDto.java @@ -14,6 +14,8 @@ public class KnxGroupDto { public final String addressStr; + public final String propertyName; + public final String dpt; public final String name; @@ -28,6 +30,7 @@ public class KnxGroupDto { id = knxGroup.getId(); addressRaw = knxGroup.getAddressRaw(); addressStr = knxGroup.getAddressStr(); + propertyName = knxGroup.getPropertyName(); dpt = knxGroup.getDpt(); name = knxGroup.getName(); booleanValue = knxGroup.getBooleanValue(); diff --git a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupSetService.java b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupSetService.java index ab92996..4018aee 100644 --- a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupSetService.java +++ b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupSetService.java @@ -25,7 +25,7 @@ import static de.ph87.homeautomation.shared.Helpers.quoteOrNull; public class KnxGroupSetService implements IPropertyOwner { @Getter - private final Pattern propertyNamePattern = Pattern.compile("^knx\\.group\\.(\\d+)/(\\d+)/(\\d+)$"); + private final Pattern propertyNamePattern = Pattern.compile("^knx\\.group\\.(\\d+)\\.(\\d+)\\.(\\d+)$"); private final KnxThreadService knxThreadService; 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 96826d0..ba70770 100644 --- a/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupWriteService.java +++ b/src/main/java/de/ph87/homeautomation/knx/group/KnxGroupWriteService.java @@ -6,10 +6,15 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import tuwien.auto.calimero.GroupAddress; +import tuwien.auto.calimero.IndividualAddress; import tuwien.auto.calimero.KNXException; import tuwien.auto.calimero.KNXFormatException; import tuwien.auto.calimero.dptxlator.*; +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.net.*; +import java.nio.charset.StandardCharsets; import java.time.ZonedDateTime; import java.util.Objects; import java.util.Optional; @@ -24,20 +29,35 @@ public class KnxGroupWriteService { private final KnxGroupRepository knxGroupRepository; - public void updateIfExists(final int rawAddress, final byte[] data) { + private DatagramSocket broadcastDatagramSocket; + + private InetAddress broadcastAddress; + + private final int broadcastPort = 1987; + + @PostConstruct + public void postConstruct() throws SocketException, UnknownHostException { + broadcastDatagramSocket = new DatagramSocket(); + broadcastDatagramSocket.setBroadcast(true); + broadcastAddress = InetAddress.getByName("255.255.255.255"); + } + + public void updateIfExists(final int rawAddress, final byte[] data, final IndividualAddress knxDeviceAddress) { final Optional knxGroupOptional = knxGroupRepository.findByAddressRaw(rawAddress); if (knxGroupOptional.isEmpty()) { log.debug("No KnxGroup with address={}", new GroupAddress(rawAddress)); } knxGroupOptional.ifPresent(knxGroup -> { knxGroup.setValue(data); + knxGroup.setLastDeviceAddressRaw(knxDeviceAddress.getRawAddress()); + knxGroup.setLastDeviceAddressString(knxDeviceAddress.toString()); knxGroup.setValueTimestamp(ZonedDateTime.now()); knxGroup.setBooleanValue(null); knxGroup.setNumberValue(null); try { final DPTXlator translator = findTranslator(knxGroup); translator.setData(data); - translate(DPTXlatorBoolean.class, translator, knxGroup::setBooleanValue, DPTXlatorBoolean::getValueBoolean); + translate(DPTXlatorBoolean.class, translator, value -> setBooleanValue(knxGroup, value), DPTXlatorBoolean::getValueBoolean); translate(DPTXlator8BitUnsigned.class, translator, knxGroup::setNumberValue, DPTXlator8BitUnsigned::getNumericValue); translate(DPTXlator2ByteFloat.class, translator, knxGroup::setNumberValue, DPTXlator2ByteFloat::getNumericValue); translate(DPTXlator2ByteUnsigned.class, translator, knxGroup::setNumberValue, DPTXlator2ByteUnsigned::getNumericValue); @@ -48,6 +68,7 @@ public class KnxGroupWriteService { translate(DPTXlator64BitSigned.class, translator, knxGroup::setNumberValue, DPTXlator64BitSigned::getNumericValue); translate(DPTXlatorSceneNumber.class, translator, knxGroup::setNumberValue, DPTXlatorSceneNumber::getNumericValue); // TODO implement all DPTXlator... + broadcast(knxGroup); } catch (NoTranslatorException e) { log.error(e.getMessage()); } @@ -55,6 +76,28 @@ public class KnxGroupWriteService { }); } + private void setBooleanValue(final KnxGroup knxGroup, final Boolean value) { + knxGroup.setBooleanValue(value); + if (value == null) { + knxGroup.setNumberValue(null); + } else { + knxGroup.setNumberValue(value ? 1.0 : 0.0); + } + } + + private void broadcast(final KnxGroup knxGroup) { + if (knxGroup.getNumberValue() != null && !knxGroup.getNumberValue().isNaN()) { + final String message = knxGroup.getPropertyName() + (knxGroup.isMultiGroup() ? ".from." + knxGroup.getLastDeviceAddressString() : "") + " " + knxGroup.getNumberValue(); + try { + log.info("UDP Broadcast {}:{}: {}", broadcastAddress, broadcastPort, message); + final byte[] bytes = message.getBytes(StandardCharsets.UTF_8); + broadcastDatagramSocket.send(new DatagramPacket(bytes, bytes.length, broadcastAddress, broadcastPort)); + } catch (IOException e) { + log.error("Failed to broadcast property change of {}: {}", knxGroup, e.getMessage()); + } + } + } + private void translate(final Class dptXlatorClass, final DPTXlator translator, final Consumer setter, final Function getter) { if (dptXlatorClass.isInstance(translator)) { setter.accept(getter.apply(dptXlatorClass.cast(translator))); @@ -65,10 +108,11 @@ public class KnxGroupWriteService { knxGroupRepository.findAllByRead_AbleTrue().forEach(knxGroup -> knxGroup.getRead().setNextTimestamp(ZonedDateTime.now())); } - public KnxGroupDto create(final String name, final GroupAddress address, final String dpt, final boolean readable) { + public KnxGroupDto create(final String name, final GroupAddress address, final String dpt, final boolean readable, final boolean multiGroup) { final KnxGroup trans = new KnxGroup(); trans.setAddress(address); trans.setDpt(dpt); + trans.setMultiGroup(multiGroup); trans.setName(name); trans.getRead().setAble(readable); return new KnxGroupDto(knxGroupRepository.save(trans)); diff --git a/src/main/java/de/ph87/homeautomation/schedule/PropertyEntry.java b/src/main/java/de/ph87/homeautomation/schedule/PropertyEntry.java index c45557d..8196427 100644 --- a/src/main/java/de/ph87/homeautomation/schedule/PropertyEntry.java +++ b/src/main/java/de/ph87/homeautomation/schedule/PropertyEntry.java @@ -1,7 +1,7 @@ package de.ph87.homeautomation.schedule; +import de.ph87.homeautomation.knx.group.KnxGroupDto; import lombok.Data; -import tuwien.auto.calimero.GroupAddress; import java.util.Map; @@ -12,9 +12,14 @@ public class PropertyEntry implements Map.Entry { private String value; - public PropertyEntry(final GroupAddress groupAddress, final String value) { - this.key = "knx.group." + groupAddress; - this.value = value; + public PropertyEntry(final KnxGroupDto knxGroupDto, final boolean value) { + this.key = knxGroupDto.getPropertyName(); + this.value = "" + value; + } + + public PropertyEntry(final KnxGroupDto knxGroupDto, final int value) { + this.key = knxGroupDto.getPropertyName(); + this.value = "" + value; } public String setValue(final String value) { diff --git a/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntry.java b/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntry.java index 2aa1aca..622eaa3 100644 --- a/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntry.java +++ b/src/main/java/de/ph87/homeautomation/schedule/entry/ScheduleEntry.java @@ -50,9 +50,7 @@ public class ScheduleEntry { private int second; - private int fuzzyMinusSeconds = 0; - - private int fuzzyPlusSeconds = 0; + private int plusMinusSeconds = 0; private ZonedDateTime nextClearTimestamp; @@ -67,8 +65,8 @@ public class ScheduleEntry { public void setNextClearTimestamp(final ZonedDateTime next) { nextClearTimestamp = next; if (nextClearTimestamp != null && (lastClearTimestamp == null || nextClearTimestamp.compareTo(lastClearTimestamp) != 0)) { - final int secondsRange = fuzzyPlusSeconds + fuzzyMinusSeconds; - final int fuzzySeconds = secondsRange > 0 ? RANDOM.nextInt(secondsRange) - fuzzyMinusSeconds : 0; + final int secondsRange = 2 * plusMinusSeconds; + final int fuzzySeconds = secondsRange > 0 ? RANDOM.nextInt(secondsRange) - plusMinusSeconds : 0; nextFuzzyTimestamp = nextClearTimestamp.plusSeconds(fuzzySeconds); } else { nextFuzzyTimestamp = null;