Home4/src/main/java/de/ph87/home/knx/property/KnxPropertyService.java

142 lines
6.2 KiB
Java

package de.ph87.home.knx.property;
import de.ph87.home.knx.group.Group;
import de.ph87.home.knx.group.GroupLoaded;
import de.ph87.home.knx.group.GroupRepository;
import de.ph87.home.knx.group.GroupService;
import de.ph87.home.knx.link.KnxLinkService;
import de.ph87.home.property.*;
import jakarta.annotation.Nullable;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tuwien.auto.calimero.GroupAddress;
import tuwien.auto.calimero.KNXException;
import tuwien.auto.calimero.dptxlator.DPTXlator;
import tuwien.auto.calimero.dptxlator.DPTXlatorBoolean;
import tuwien.auto.calimero.process.ProcessEvent;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
@Transactional
@EnableScheduling
@RequiredArgsConstructor
public class KnxPropertyService {
private final KnxPropertyRepository knxPropertyRepository;
private final PropertyService propertyService;
private final GroupRepository groupRepository;
private final KnxLinkService knxLinkService;
private final GroupService groupService;
@Scheduled(initialDelay = 1, fixedDelay = 1, timeUnit = TimeUnit.HOURS)
public void readAll() {
knxPropertyRepository.findAll().forEach(this::read);
}
@EventListener(GroupLoaded.class)
public void onGroupLoad(@NonNull final GroupLoaded groupLoaded) {
findAllByReadAddress(groupLoaded.getGroup().getAddress()).forEach(this::read);
}
public void create(@NonNull final String id, @NonNull final KnxPropertyType type, @Nullable final GroupAddress read, @Nullable final GroupAddress write) {
final KnxProperty knxProperty = knxPropertyRepository.save(new KnxProperty(id, type, read, write));
updateOrCreateProperty(knxProperty);
}
private void updateOrCreateProperty(@NonNull final KnxProperty knxProperty) {
try {
propertyService.createOrUpdate(this, knxProperty.getId(), knxProperty.getType().clazz, (p, value) -> write(knxProperty, value));
read(knxProperty);
} catch (PropertyNotOwned e) {
log.error("Failed to register KnxProperty: knxProperty={}, error={}", knxProperty, e.toString());
}
}
@EventListener(ProcessEvent.class)
public void onProcessEvent(@NonNull final ProcessEvent event) {
findAllByReadAddress(event.getDestination()).forEach(knxProperty -> onProcessEvent(knxProperty, event));
}
private void onProcessEvent(@NonNull final KnxProperty knxProperty, @NonNull final ProcessEvent event) {
log.debug("onProcessEvent: knxProperty={}, event={}", knxProperty, event);
groupRepository.findByAddress(event.getDestination()).ifPresent(group -> onProcessEvent(knxProperty, event, group));
}
private void onProcessEvent(@NonNull final KnxProperty knxProperty, @NonNull final ProcessEvent event, @NonNull final Group group) {
log.debug("onProcessEvent: knxProperty={}, group={}, event={}", knxProperty, group, event);
try {
final DPTXlator translator = group.getDpt().createTranslator();
translator.setData(event.getASDU());
log.debug("translator: {}", translator);
final Property<?> property = switch (knxProperty.getType()) {
case BOOLEAN -> {
if (!(translator instanceof final DPTXlatorBoolean booleanTranslator)) {
throw new RuntimeException("DPTXlator type should be DPTXlatorBoolean for property.type = BOOLEAN but is: " + translator.getClass().getSimpleName());
}
yield propertyService.update(this, knxProperty.getId(), Boolean.class, booleanTranslator.getValueBoolean(), booleanTranslator.getValue());
}
case DOUBLE -> propertyService.update(this, knxProperty.getId(), Double.class, translator.getNumericValue(), translator.getValue());
};
if (property.getState() == null) {
throw new RuntimeException();
}
group.setState(property.getState());
groupService.publish(group);
} catch (KNXException | PropertyNotFound | PropertyTypeMismatch | PropertyNotOwned e) {
log.error("Failed to handle ProcessEvent: knxProperty={}, error={}", knxProperty, e.toString());
}
}
private void read(@NonNull final KnxProperty knxProperty) {
final Optional<KnxProperty> property = knxPropertyRepository.findById(knxProperty.getId());
final Optional<GroupAddress> address = property.map(KnxProperty::getRead);
final Optional<Group> group = address.map(groupRepository::findByAddress).filter(Optional::isPresent).map(Optional::get);
group.ifPresent(knxLinkService::queueRead);
}
private void write(@NonNull final KnxProperty knxProperty, @NonNull final Object value) {
knxPropertyRepository.findById(knxProperty.getId()).map(KnxProperty::getWrite).map(groupRepository::findByAddress).filter(Optional::isPresent).map(Optional::get).ifPresent(group -> write(knxProperty, group, value));
}
private void write(@NonNull final KnxProperty knxProperty, @NonNull final Group group, @NonNull final Object value) {
try {
if (!knxProperty.getType().clazz.isInstance(value)) {
throw new RuntimeException("Cannot write invalid value type: type=%s, value=%s, knxProperty=%s".formatted(value.getClass(), value, knxProperty));
}
final DPTXlator translator = group.getDpt().createTranslator();
switch (knxProperty.getType()) {
case BOOLEAN -> {
if (!(translator instanceof final DPTXlatorBoolean booleanTranslator)) {
throw new RuntimeException("DPTXlator type should be DPTXlatorBoolean for property.type = BOOLEAN but is: " + translator.getClass().getSimpleName());
}
booleanTranslator.setValue((boolean) value);
}
case DOUBLE -> translator.setValue((double) value);
}
knxLinkService.queueWrite(group, translator);
} catch (KNXException e) {
log.error("Failed to write KnxProperty: knxProperty={}, error={}", knxProperty, e.toString());
}
}
@NonNull
private List<KnxProperty> findAllByReadAddress(@NonNull final GroupAddress address) {
return knxPropertyRepository.findAllByRead(address);
}
}