159 lines
6.9 KiB
Java
159 lines
6.9 KiB
Java
package de.ph87.homeautomation.knx.group;
|
|
|
|
import de.ph87.homeautomation.property.PropertyType;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
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.Optional;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Function;
|
|
|
|
@Slf4j
|
|
@Service
|
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
|
@RequiredArgsConstructor
|
|
public class KnxGroupWriteService {
|
|
|
|
private final KnxGroupRepository knxGroupRepository;
|
|
|
|
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, 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);
|
|
translate(DPTXlator4ByteFloat.class, translator, knxGroup::setNumberValue, DPTXlator4ByteFloat::getNumericValue);
|
|
translate(DPTXlator4ByteSigned.class, translator, knxGroup::setNumberValue, DPTXlator4ByteSigned::getNumericValue);
|
|
translate(DPTXlator4ByteUnsigned.class, translator, knxGroup::setNumberValue, DPTXlator4ByteUnsigned::getNumericValue);
|
|
translate(DPTXlator8BitSigned.class, translator, knxGroup::setNumberValue, DPTXlator8BitSigned::getNumericValue);
|
|
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());
|
|
}
|
|
log.debug("KnxGroup updated: {}", knxGroup);
|
|
});
|
|
}
|
|
|
|
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.debug("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)));
|
|
}
|
|
}
|
|
|
|
public void markAllForRead() {
|
|
knxGroupRepository.findAllByRead_AbleTrue().forEach(knxGroup -> knxGroup.getRead().setNextTimestamp(ZonedDateTime.now()));
|
|
}
|
|
|
|
public KnxGroupDto create(final String name, final GroupAddress address, final String dpt, final PropertyType type, final boolean readable, final boolean multiGroup) {
|
|
final KnxGroup trans = new KnxGroup();
|
|
trans.setAddress(address);
|
|
trans.setDpt(dpt);
|
|
trans.setMultiGroup(multiGroup);
|
|
trans.setTitle(name);
|
|
trans.setPropertyType(type);
|
|
trans.getRead().setAble(readable);
|
|
return new KnxGroupDto(knxGroupRepository.save(trans));
|
|
}
|
|
|
|
public boolean setSendValue(final GroupAddress groupAddress, final double value) throws KnxGroupFormatException {
|
|
final Optional<KnxGroup> knxGroupOptional = knxGroupRepository.findByAddressRaw(groupAddress.getRawAddress());
|
|
if (knxGroupOptional.isEmpty()) {
|
|
return false;
|
|
}
|
|
final KnxGroup knxGroup = knxGroupOptional.get();
|
|
|
|
try {
|
|
final DPTXlator translator = findTranslator(knxGroup);
|
|
if (translator instanceof DPTXlatorBoolean) {
|
|
((DPTXlatorBoolean) translator).setValue(value == 1.0);
|
|
} else if (translator instanceof DPTXlator8BitUnsigned) {
|
|
((DPTXlator8BitUnsigned) translator).setValue((int) value);
|
|
} else { // TODO implement all DPTXlator...
|
|
translator.setValue("" + value);
|
|
}
|
|
knxGroup.setSendValue(translator.getData());
|
|
knxGroup.getSend().setNextTimestamp(ZonedDateTime.now());
|
|
return true;
|
|
} catch (NoTranslatorException | KNXFormatException e) {
|
|
throw new KnxGroupFormatException(knxGroup, value, e.getMessage());
|
|
}
|
|
}
|
|
|
|
private DPTXlator findTranslator(final KnxGroup knxGroup) throws NoTranslatorException {
|
|
if (knxGroup.getDpt() == null) {
|
|
throw new NoTranslatorException("Missing DPT");
|
|
}
|
|
final int mainNumber = Integer.parseInt(knxGroup.getDpt().split("\\.")[0]);
|
|
try {
|
|
return TranslatorTypes.createTranslator(mainNumber, knxGroup.getDpt());
|
|
} catch (KNXException e) {
|
|
throw new NoTranslatorException(e);
|
|
}
|
|
}
|
|
|
|
}
|