MultiGroups + UDP Broadcast

This commit is contained in:
Patrick Haßel 2021-10-28 08:50:53 +02:00
parent 06099e291f
commit d8664a800a
10 changed files with 136 additions and 77 deletions

View File

@ -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!"

View File

@ -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<String, String>... entries) {
private ScheduleEntry createRelative(final Schedule schedule, final int inSeconds, final int plusMinusSeconds, final Map.Entry<String, String>... 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<String, String>... 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<String, String>... 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<String, String>... 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<String, String>... 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<String, String>... 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<String, String>... 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<String, String>... 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<String, String>... 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;

View File

@ -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);
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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;

View File

@ -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<KnxGroup> 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 <X extends DPTXlator, T> void translate(final Class<X> dptXlatorClass, final DPTXlator translator, final Consumer<T> setter, final Function<X, T> 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));

View File

@ -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<String, String> {
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) {

View File

@ -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;