This commit is contained in:
Patrick Haßel 2021-11-03 22:48:39 +01:00
parent a42cb9f915
commit 2c50639b6a
29 changed files with 356 additions and 283 deletions

View File

@ -4,12 +4,21 @@ import lombok.RequiredArgsConstructor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.PostConstruct;
@SpringBootApplication
@RequiredArgsConstructor
public class BackendApplication {
private final DemoDataService demoDataService;
public static void main(String[] args) {
SpringApplication.run(BackendApplication.class);
}
@PostConstruct
public void postConstruct() {
demoDataService.insertDemoData();
}
}

View File

@ -4,11 +4,11 @@ import com.luckycatlabs.sunrisesunset.Zenith;
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.KnxGroupDto;
import de.ph87.homeautomation.knx.group.KnxGroup;
import de.ph87.homeautomation.knx.group.KnxGroupRepository;
import de.ph87.homeautomation.knx.group.KnxGroupWriteService;
import de.ph87.homeautomation.property.Property;
import de.ph87.homeautomation.property.PropertyDto;
import de.ph87.homeautomation.property.PropertyType;
import de.ph87.homeautomation.schedule.Schedule;
import de.ph87.homeautomation.schedule.ScheduleRepository;
import de.ph87.homeautomation.schedule.entry.ScheduleEntry;
@ -16,16 +16,15 @@ import de.ph87.homeautomation.schedule.entry.ScheduleEntryType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tuwien.auto.calimero.GroupAddress;
import javax.annotation.PostConstruct;
import java.time.ZonedDateTime;
import static de.ph87.homeautomation.shared.Helpers.mapIfNotNull;
@SuppressWarnings({"unchecked", "UnusedReturnValue", "SameParameterValue", "UnusedAssignment", "RedundantSuppression"})
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class DemoDataService {
@ -43,41 +42,40 @@ public class DemoDataService {
private final DeviceRepository deviceRepository;
@PostConstruct
public void postConstruct() {
final KnxGroupDto eg_flur_licht_schalten = createKnxGroupIfNotExists("EG Flur Licht Schalten", 0, 5, 14, "1.001", PropertyType.ON_OFF, false, false);
public void insertDemoData() {
final KnxGroup eg_flur_licht_schalten = createKnxGroupIfNotExists("EG Flur Licht Schalten", 0, 5, 14, "1.001", false, false);
final KnxGroupDto ambiente_eg_schalten = createKnxGroupIfNotExists("Ambiente EG Schalten", 0, 3, 80, "1.001", PropertyType.ON_OFF, false, false);
final KnxGroupDto ambiente_eg_status = createKnxGroupIfNotExists("Ambiente EG Status", 0, 3, 81, "1.001", PropertyType.ON_OFF, true, false);
final KnxGroup ambiente_eg_status = createKnxGroupIfNotExists("Ambiente EG Status", 0, 3, 81, "1.001", true, false);
final KnxGroup ambiente_eg_schalten = createKnxGroupIfNotExists("Ambiente EG Schalten", 0, 3, 80, "1.001", false, false);
final KnxGroupDto ambiente_og_schalten = createKnxGroupIfNotExists("Ambiente OG Schalten", 0, 6, 3, "1.001", PropertyType.ON_OFF, false, false);
final KnxGroupDto ambiente_og_status = createKnxGroupIfNotExists("Ambiente OG Status", 0, 6, 2, "1.001", PropertyType.ON_OFF, true, false);
final KnxGroup ambiente_og_status = createKnxGroupIfNotExists("Ambiente OG Status", 0, 6, 2, "1.001", true, false);
final KnxGroup ambiente_og_schalten = createKnxGroupIfNotExists("Ambiente OG Schalten", 0, 6, 3, "1.001", false, false);
final KnxGroupDto wohnzimmer_rollladen_position_anfahren = createKnxGroupIfNotExists("Wohnzimmer Rollladen Position Anfahren", 0, 4, 24, "5.001", PropertyType.PERCENT, false, false);
final KnxGroup wohnzimmer_rollladen_position_anfahren = createKnxGroupIfNotExists("Wohnzimmer Rollladen Position Anfahren", 0, 4, 24, "5.001", false, false);
final KnxGroupDto schlafzimmer_rollladen_position_anfahren = createKnxGroupIfNotExists("Schlafzimmer Rollladen Position Anfahren", 0, 3, 3, "5.001", PropertyType.PERCENT, false, false);
final KnxGroup schlafzimmer_rollladen_position_anfahren = createKnxGroupIfNotExists("Schlafzimmer Rollladen Position Anfahren", 0, 3, 3, "5.001", false, false);
final KnxGroupDto flur_og_rollladen_position_anfahren = createKnxGroupIfNotExists("Flur Rollladen Position Anfahren", 0, 5, 13, "5.001", PropertyType.PERCENT, false, false);
final KnxGroup flur_og_rollladen_position_anfahren = createKnxGroupIfNotExists("Flur Rollladen Position Anfahren", 0, 5, 13, "5.001", false, false);
final KnxGroupDto bad_licht_schalten = createKnxGroupIfNotExists("Bad Licht Schalten", 0, 3, 73, "1.001", PropertyType.ON_OFF, false, false);
final KnxGroupDto bad_licht_status = createKnxGroupIfNotExists("Bad Licht Status", 0, 5, 19, "1.001", PropertyType.ON_OFF, true, false);
final KnxGroup bad_licht_status = createKnxGroupIfNotExists("Bad Licht Status", 0, 5, 19, "1.001", true, false);
final KnxGroup bad_licht_schalten = createKnxGroupIfNotExists("Bad Licht Schalten", 0, 3, 73, "1.001", false, false);
final KnxGroupDto bad_licht_mitte_schalten = createKnxGroupIfNotExists("Bad Licht Mitte Schalten", 0, 3, 29, "1.001", PropertyType.ON_OFF, false, false);
final KnxGroupDto bad_licht_mitte_status = createKnxGroupIfNotExists("Bad Licht Mitte Status", 0, 3, 30, "1.001", PropertyType.ON_OFF, true, false);
final KnxGroup bad_licht_mitte_status = createKnxGroupIfNotExists("Bad Licht Mitte Status", 0, 3, 30, "1.001", true, false);
final KnxGroup bad_licht_mitte_schalten = createKnxGroupIfNotExists("Bad Licht Mitte Schalten", 0, 3, 29, "1.001", false, false);
final KnxGroupDto helligkeit = createKnxGroupIfNotExists("Helligkeit", 0, 5, 6, "9.004", PropertyType.LUX, false, true);
final KnxGroup helligkeit = createKnxGroupIfNotExists("Helligkeit", 0, 5, 6, "9.004", false, true);
if (deviceRepository.count() == 0) {
createDeviceSwitch("Ambiente EG", ambiente_eg_status.getProperty(), ambiente_eg_schalten.getProperty());
createDeviceSwitch("Ambiente OG", ambiente_og_status.getProperty(), ambiente_og_schalten.getProperty());
createDeviceSwitch("Bad Licht", bad_licht_status.getProperty(), bad_licht_schalten.getProperty());
createDeviceShutter("Wohnzimmer Rollladen", null, wohnzimmer_rollladen_position_anfahren.getProperty());
createDeviceShutter("Schlafzimmer Rollladen", null, schlafzimmer_rollladen_position_anfahren.getProperty());
createDeviceShutter("Flur Rollladen", null, flur_og_rollladen_position_anfahren.getProperty());
createDeviceSwitch("Ambiente EG", ambiente_eg_status, ambiente_eg_schalten);
createDeviceSwitch("Ambiente OG", ambiente_og_status, ambiente_og_schalten);
createDeviceSwitch("Bad Licht", bad_licht_status, bad_licht_schalten);
createDeviceShutter("Wohnzimmer Rollladen", null, wohnzimmer_rollladen_position_anfahren);
createDeviceShutter("Schlafzimmer Rollladen", null, schlafzimmer_rollladen_position_anfahren);
createDeviceShutter("Flur Rollladen", null, flur_og_rollladen_position_anfahren);
}
if (scheduleRepository.count() == 0) {
final Schedule scheduleEgFlurLicht = createSchedule(true, "EG Flur Licht", eg_flur_licht_schalten.getProperty());
final Schedule scheduleEgFlurLicht = createSchedule(true, "EG Flur Licht", eg_flur_licht_schalten);
createTime(scheduleEgFlurLicht, true, 1, 0, 0, MIN30, true);
createTime(scheduleEgFlurLicht, true, 2, 0, 0, MIN30, false);
createTime(scheduleEgFlurLicht, true, 7, 30, 0, MIN30, true);
@ -88,36 +86,36 @@ public class DemoDataService {
createTime(scheduleEgFlurLicht, true, 20, 0, 0, MIN30, false);
scheduleRepository.save(scheduleEgFlurLicht);
final Schedule scheduleEgAmbiente = createSchedule(false, "Ambiente EG", ambiente_eg_schalten.getProperty());
final Schedule scheduleEgAmbiente = createSchedule(false, "Ambiente EG", ambiente_eg_schalten);
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_schalten.getProperty());
final Schedule scheduleOgAmbiente = createSchedule(false, "Ambiente OG", ambiente_og_schalten);
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_position_anfahren.getProperty());
final Schedule scheduleWohnzimmerRollladen = createSchedule(true, "Rollläden Wohnzimmer", wohnzimmer_rollladen_position_anfahren);
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_position_anfahren.getProperty());
final Schedule scheduleSchlafzimmerRollladen = createSchedule(true, "Rollläden Schlafzimmer", schlafzimmer_rollladen_position_anfahren);
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_position_anfahren.getProperty());
final Schedule scheduleFlurRollladen = createSchedule(true, "Rollladen Flur", flur_og_rollladen_position_anfahren);
createSunrise(scheduleFlurRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 0);
createSunset(scheduleFlurRollladen, true, BETWEEN_OFFICIAL_AND_CIVIL, 0, 100);
scheduleRepository.save(scheduleFlurRollladen);
final Schedule scheduleBadLichtMitte = createSchedule(false, "Bad Licht Mitte", bad_licht_mitte_schalten.getProperty());
final Schedule scheduleBadLichtMitte = createSchedule(false, "Bad Licht Mitte", bad_licht_mitte_schalten);
createTime(scheduleBadLichtMitte, true, 10, 30, 0, MIN30, true);
createTime(scheduleBadLichtMitte, true, 11, 30, 0, MIN30, false);
createTime(scheduleBadLichtMitte, true, 15, 30, 0, MIN30, true);
@ -130,12 +128,12 @@ public class DemoDataService {
}
}
private DeviceDto createDeviceSwitch(final String title, final PropertyDto getPercent, final PropertyDto setPercent) {
return deviceWriteService.createDeviceSwitch(title, mapIfNotNull(getPercent, PropertyDto::getName), mapIfNotNull(setPercent, PropertyDto::getName));
private DeviceDto createDeviceSwitch(final String title, final Property getPercent, final Property setPercent) {
return deviceWriteService.createDeviceSwitch(title, getPercent, setPercent);
}
private DeviceDto createDeviceShutter(final String title, final PropertyDto getPercent, final PropertyDto setPercent) {
return deviceWriteService.createDeviceShutter(title, mapIfNotNull(getPercent, PropertyDto::getName), mapIfNotNull(setPercent, PropertyDto::getName));
private DeviceDto createDeviceShutter(final String title, final Property getPercent, final Property setPercent) {
return deviceWriteService.createDeviceShutter(title, getPercent, setPercent);
}
private Schedule createSchedule(final boolean enabled, final String title, final PropertyDto propertyDto) {
@ -147,9 +145,9 @@ public class DemoDataService {
return schedule;
}
private KnxGroupDto createKnxGroupIfNotExists(final String name, final int main, final int mid, final int sub, final String dpt, final PropertyType type, final boolean readable, final boolean multiGroup) {
private KnxGroup createKnxGroupIfNotExists(final String name, final int main, final int mid, final int sub, final String dpt, final boolean readable, final boolean multiGroup) {
final GroupAddress address = new GroupAddress(main, mid, sub);
return knxGroupRepository.findByAddressRaw(address.getRawAddress()).map(KnxGroupDto::new).orElseGet(() -> knxGroupWriteService.create(name, address, dpt, type, readable, multiGroup));
return knxGroupRepository.findByAddressRaw(address.getRawAddress()).orElseGet(() -> knxGroupWriteService.create(name, address, dpt, readable, multiGroup));
}
private ScheduleEntry createRelative(final Schedule schedule, final boolean enabled, final int inSeconds, final int fuzzySeconds, final Object value) {

View File

@ -1,7 +1,7 @@
package de.ph87.homeautomation.device;
import de.ph87.homeautomation.device.devices.*;
import de.ph87.homeautomation.property.PropertyService;
import de.ph87.homeautomation.property.Property;
import de.ph87.homeautomation.web.NotFoundException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -21,8 +21,6 @@ public class DeviceReadService {
private final DeviceRepository deviceRepository;
private final PropertyService propertyService;
public List<DeviceDto> findAll() {
return deviceRepository.findAll().stream().map(this::toDto).collect(Collectors.toList());
}
@ -30,12 +28,12 @@ public class DeviceReadService {
public DeviceDto toDto(final Device device) {
if (device instanceof DeviceSwitch) {
final DeviceSwitch deviceSwitch = (DeviceSwitch) device;
final Boolean state = mapIfNotNull(deviceSwitch.getGetState(), propertyService::readBoolean);
return new DeviceSwitchDto(deviceSwitch, deviceSwitch.getGetState(), deviceSwitch.getSetState(), state);
final Boolean state = mapIfNotNull(deviceSwitch.getGetState(), Property::getBoolean);
return new DeviceSwitchDto(deviceSwitch, mapIfNotNull(deviceSwitch.getGetState(), Property::getName), mapIfNotNull(deviceSwitch.getSetState(), Property::getName), state);
} else if (device instanceof DeviceShutter) {
final DeviceShutter deviceShutter = (DeviceShutter) device;
final Double percent = mapIfNotNull(deviceShutter.getGetPercent(), propertyService::readNumber);
return new DeviceShutterDto(deviceShutter, deviceShutter.getGetPercent(), deviceShutter.getSetPercent(), percent);
final Double percent = mapIfNotNull(deviceShutter.getGetPercent(), Property::getPercent);
return new DeviceShutterDto(deviceShutter, mapIfNotNull(deviceShutter.getGetPercent(), Property::getName), mapIfNotNull(deviceShutter.getSetPercent(), Property::getName), percent);
}
throw new RuntimeException("Not implemented: toDto(" + device + ")");
}

View File

@ -4,8 +4,8 @@ import de.ph87.homeautomation.device.devices.Device;
import de.ph87.homeautomation.device.devices.DeviceDto;
import de.ph87.homeautomation.device.devices.DeviceShutter;
import de.ph87.homeautomation.device.devices.DeviceSwitch;
import de.ph87.homeautomation.property.Property;
import de.ph87.homeautomation.property.PropertyService;
import de.ph87.homeautomation.property.PropertySetException;
import de.ph87.homeautomation.schedule.ScheduleWriteService;
import de.ph87.homeautomation.web.BadRequestException;
import lombok.RequiredArgsConstructor;
@ -27,7 +27,7 @@ public class DeviceWriteService {
private final DeviceReadService deviceReadService;
public DeviceDto createDeviceSwitch(final String title, final String getStatePropertyName, final String setStatePropertyName) {
public DeviceDto createDeviceSwitch(final String title, final Property getStatePropertyName, final Property setStatePropertyName) {
final DeviceSwitch deviceSwitch = new DeviceSwitch();
deviceSwitch.setTitle(title);
deviceSwitch.setGetState(getStatePropertyName);
@ -36,7 +36,7 @@ public class DeviceWriteService {
return deviceReadService.toDto(deviceSwitch);
}
public DeviceDto createDeviceShutter(final String title, final String getPercentPropertyName, final String setPercentPropertyName) {
public DeviceDto createDeviceShutter(final String title, final Property getPercentPropertyName, final Property setPercentPropertyName) {
final DeviceShutter deviceShutter = new DeviceShutter();
deviceShutter.setTitle(title);
deviceShutter.setGetPercent(getPercentPropertyName);
@ -45,7 +45,7 @@ public class DeviceWriteService {
return deviceReadService.toDto(deviceShutter);
}
public void set(final DeviceSetDto dto) throws PropertySetException {
public void set(final DeviceSetDto dto) {
final Device device = deviceReadService.getById(dto.getId());
if (device instanceof DeviceSwitch) {
setSwitch((DeviceSwitch) device, dto.getProperty(), dto.getValue());
@ -54,14 +54,14 @@ public class DeviceWriteService {
}
}
private void setSwitch(final DeviceSwitch device, final String property, final double value) throws PropertySetException {
private void setSwitch(final DeviceSwitch device, final String property, final double value) {
switch (property) {
case "switch":
propertyService.set(device.getSetState(), value);
}
}
private void setShutter(final DeviceShutter device, final String property, final double value) throws PropertySetException {
private void setShutter(final DeviceShutter device, final String property, final double value) {
switch (property) {
case "percent":
propertyService.set(device.getSetPercent(), value);

View File

@ -1,10 +1,12 @@
package de.ph87.homeautomation.device.devices;
import de.ph87.homeautomation.property.Property;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
@Getter
@Setter
@ -12,8 +14,10 @@ import javax.persistence.Entity;
@Entity
public class DeviceShutter extends Device {
private String getPercent;
@ManyToOne
private Property getPercent;
private String setPercent;
@ManyToOne
private Property setPercent;
}

View File

@ -1,10 +1,12 @@
package de.ph87.homeautomation.device.devices;
import de.ph87.homeautomation.property.Property;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
@Getter
@Setter
@ -12,8 +14,10 @@ import javax.persistence.Entity;
@Entity
public class DeviceSwitch extends Device {
private String setState;
@ManyToOne
private Property setState;
private String getState;
@ManyToOne
private Property getState;
}

View File

@ -1,6 +1,5 @@
package de.ph87.homeautomation.knx.group;
import de.ph87.homeautomation.property.PropertyType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

View File

@ -1,7 +1,5 @@
package de.ph87.homeautomation.knx.group;
import de.ph87.homeautomation.property.PropertyChangedEvent;
import de.ph87.homeautomation.property.PropertyType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent;
@ -67,7 +65,6 @@ public class KnxGroupWriteService {
translate(DPTXlator64BitSigned.class, translator, knxGroup::setNumberValue, DPTXlator64BitSigned::getNumericValue);
translate(DPTXlatorSceneNumber.class, translator, knxGroup::setNumberValue, DPTXlatorSceneNumber::getNumericValue);
// TODO implement all DPTXlator...
publish(knxGroup);
} catch (NoTranslatorException e) {
log.error(e.getMessage());
}
@ -75,10 +72,6 @@ public class KnxGroupWriteService {
});
}
private void publish(final KnxGroup knxGroup) {
applicationEventPublisher.publishEvent(new PropertyChangedEvent(knxGroupMapperService.toPropertyDto(knxGroup), true));
}
private void setBooleanValue(final KnxGroup knxGroup, final Boolean value) {
knxGroup.setBooleanValue(value);
if (value == null) {
@ -94,15 +87,14 @@ public class KnxGroupWriteService {
}
}
public KnxGroupDto create(final String name, final GroupAddress address, final String dpt, final PropertyType type, final boolean readable, final boolean multiGroup) {
public KnxGroup 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.setTitle(name);
trans.setPropertyType(type);
trans.getRead().setAble(readable);
return new KnxGroupDto(knxGroupRepository.save(trans));
return knxGroupRepository.save(trans);
}
public boolean setSendValue(final GroupAddress groupAddress, final double value) throws KnxGroupFormatException {

View File

@ -1,23 +1,9 @@
package de.ph87.homeautomation.property;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
public interface IPropertyOwner {
Pattern getPropertyNamePattern();
String getPropertyOwnerName();
void setProperty(final String propertyName, final double value) throws PropertySetException;
Boolean readBoolean(final String propertyName);
Double readNumber(final String propertyName);
List<PropertyDto> findAllProperties();
List<PropertyDto> findAllPropertiesLike(final String like);
Optional<? extends PropertyDto> findPropertyDtoByPropertyName(final String propertyName);
Property write(final String name, final double value);
}

View File

@ -0,0 +1,52 @@
package de.ph87.homeautomation.property;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.time.ZonedDateTime;
import java.util.Objects;
@Getter
@Setter
@ToString
@Entity
public final class Property {
@Id
@GeneratedValue
@Setter(AccessLevel.NONE)
private Long id;
@Column(nullable = false, unique = true)
private String name;
@Column(unique = true)
private String title;
private ZonedDateTime timestamp;
private Double value;
public Boolean getBoolean() {
final boolean isTrue = Objects.equals(value, 1.0);
final boolean isFalse = Objects.equals(value, 0.0);
if (value == null || (!isTrue && !isFalse)) {
return null;
}
return isTrue;
}
public Double getPercent() {
if (value == null || value < 0.0 || 100.0 < value) {
return null;
}
return value;
}
}

View File

@ -1,17 +0,0 @@
package de.ph87.homeautomation.property;
import lombok.Getter;
@Getter
public class PropertyChangedEvent {
private final PropertyDto propertyDto;
private final boolean existing;
public PropertyChangedEvent(final PropertyDto propertyDto, final boolean existing) {
this.propertyDto = propertyDto;
this.existing = existing;
}
}

View File

@ -1,54 +0,0 @@
package de.ph87.homeautomation.property;
import de.ph87.homeautomation.knx.group.KnxGroup;
import de.ph87.homeautomation.web.WebSocketService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class PropertyChangedService {
private DatagramSocket broadcastDatagramSocket;
private InetAddress broadcastAddress;
private final WebSocketService webSocketService;
@PostConstruct
public void postConstruct() throws SocketException, UnknownHostException {
broadcastDatagramSocket = new DatagramSocket();
broadcastDatagramSocket.setBroadcast(true);
broadcastAddress = InetAddress.getByName("255.255.255.255");
}
@TransactionalEventListener
public void propertyChangedListener(final PropertyChangedEvent event) {
webSocketService.send(event.getPropertyDto(), event.isExisting());
}
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 {
final int broadcastPort = 1987;
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());
}
}
}
}

View File

@ -1,39 +0,0 @@
package de.ph87.homeautomation.property;
import de.ph87.homeautomation.shared.ISearchController;
import de.ph87.homeautomation.shared.KeyValuePair;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("property")
@RequiredArgsConstructor
public class PropertyController implements ISearchController {
private final PropertyService propertyService;
@GetMapping("findAll")
public List<PropertyDto> findAll() {
return propertyService.findAll();
}
@PostMapping("getById")
public KeyValuePair getById(@RequestBody final String id) {
final PropertyDto propertyDto = propertyService.getById(id);
return new KeyValuePair(propertyDto.name, propertyDto.title);
}
@PostMapping("searchLike")
public List<KeyValuePair> searchLike(@RequestBody final String term) {
return propertyService.findAllLike("%" + term + "%").stream().map(propertyDto -> new KeyValuePair(propertyDto.name, propertyDto.title)).collect(Collectors.toList());
}
@PostMapping("set")
public void set(@RequestBody final PropertySetDto dto) throws PropertySetException {
propertyService.set(dto.getName(), dto.getValue());
}
}

View File

@ -1,32 +1,28 @@
package de.ph87.homeautomation.property;
import lombok.Getter;
import lombok.Data;
import java.io.Serializable;
import java.time.ZonedDateTime;
@Getter
public final class PropertyDto implements Serializable {
@Data
public final class PropertyDto {
public final String name;
private final long id;
public final String title;
private final String name;
public final PropertyType propertyType;
private final String title;
public final Boolean booleanValue;
private final ZonedDateTime timestamp;
public final Number numberValue;
private final Double value;
public final ZonedDateTime timestamp;
public PropertyDto(final String name, final String title, final PropertyType propertyType, final Boolean booleanValue, final Number numberValue, final ZonedDateTime timestamp) {
this.name = name;
this.title = title;
this.propertyType = propertyType;
this.booleanValue = booleanValue;
this.numberValue = numberValue;
this.timestamp = timestamp;
public PropertyDto(final Property property) {
this.id = property.getId();
this.name = property.getName();
this.title = property.getTitle();
this.timestamp = property.getTimestamp();
this.value = property.getValue();
}
}

View File

@ -0,0 +1,18 @@
package de.ph87.homeautomation.property;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class PropertyMapper {
public PropertyDto toDto(final Property property) {
return new PropertyDto(property);
}
}

View File

@ -0,0 +1,44 @@
package de.ph87.homeautomation.property;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class PropertyOwnerValidatorService {
private final Set<IPropertyOwner> owners;
@PostConstruct
public void postConstruct() {
final Map<IPropertyOwner, List<IPropertyOwner>> overlapping = owners.stream().collect(Collectors.toMap(owner -> owner, this::getOverlappingList));
if (!overlapping.values().stream().allMatch(List::isEmpty)) {
throw new RuntimeException(
String.format(
"Following IPropertyOwners have overlapping names:\n\t%s",
overlapping.entrySet().stream()
.map(entry -> {
final String prefixClassName = entry.getKey().getClass().getCanonicalName();
final String prefixPropertyName = entry.getKey().getPropertyOwnerName();
final String overlappingList = entry.getValue().stream().map(owner -> owner.getClass().getCanonicalName() + "(" + owner.getPropertyOwnerName() + ")").collect(Collectors.joining("\n\t\t"));
return String.format("%s(%s):\n\t\t%s", prefixClassName, prefixPropertyName, overlappingList);
})
.collect(Collectors.joining("\n\t"))
)
);
}
}
private List<IPropertyOwner> getOverlappingList(final IPropertyOwner a) {
return owners.stream().filter(b -> b.getPropertyOwnerName().startsWith(a.getPropertyOwnerName())).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,11 @@
package de.ph87.homeautomation.property;
import org.springframework.data.repository.CrudRepository;
import java.util.Optional;
public interface PropertyRepository extends CrudRepository<Property, Long> {
Optional<Property> findByName(String name);
}

View File

@ -1,63 +1,36 @@
package de.ph87.homeautomation.property;
import de.ph87.homeautomation.web.NotFoundException;
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 java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class PropertyService {
private final Set<IPropertyOwner> propertyOwners;
private final Set<IPropertyOwner> owners;
public void set(final String propertyName, final double value) throws PropertySetException {
log.debug("Setting property \"{}\" => {}", propertyName, value);
getOwnerOrThrow(propertyName).setProperty(propertyName, value);
private final PropertyRepository propertyRepository;
private final ApplicationEventPublisher applicationEventPublisher;
private final PropertyMapper propertyMapper;
public Optional<PropertyDto> find(final String name) {
return propertyRepository.findByName(name).map(propertyMapper::toDto);
}
public Boolean readBoolean(final String propertyName) {
return getOwnerOrThrow(propertyName).readBoolean(propertyName);
}
public Double readNumber(final String propertyName) {
return getOwnerOrThrow(propertyName).readNumber(propertyName);
}
private IPropertyOwner getOwnerOrThrow(final String propertyName) {
return findOwner(propertyName)
.orElseThrow(() -> new RuntimeException("No IPropertyOwner found for propertyName: " + propertyName));
}
public List<PropertyDto> findAll() {
return mergeDtoLists(propertyOwners.stream().map(IPropertyOwner::findAllProperties));
}
public List<PropertyDto> findAllLike(final String like) {
return mergeDtoLists(propertyOwners.stream().map(iProperyOwner -> iProperyOwner.findAllPropertiesLike(like)));
}
private List<PropertyDto> mergeDtoLists(final Stream<? extends List<? extends PropertyDto>> stream) {
final List<PropertyDto> result = new ArrayList<>();
stream.forEach(result::addAll);
return result;
}
public PropertyDto getById(final String propertyName) {
return findOwner(propertyName).flatMap(iPropertyOwner -> iPropertyOwner.findPropertyDtoByPropertyName(propertyName)).orElseThrow(() -> new NotFoundException("Property.name=%s", propertyName));
}
private Optional<IPropertyOwner> findOwner(final String propertyName) {
return propertyOwners.stream()
.filter(iPropertyOwner -> iPropertyOwner.getPropertyNamePattern().matcher(propertyName).matches())
.findFirst();
public Optional<PropertyDto> set(final String name, final double value) {
final Optional<PropertyDto> propertyOptional = owners.stream().map(owner -> owner.write(name, value)).findFirst().map(propertyMapper::toDto);
propertyOptional.ifPresent(applicationEventPublisher::publishEvent);
return propertyOptional;
}
}

View File

@ -1,12 +0,0 @@
package de.ph87.homeautomation.property;
import lombok.Data;
@Data
public class PropertySetDto {
private String name;
private double value;
}

View File

@ -1,9 +0,0 @@
package de.ph87.homeautomation.property;
public class PropertySetException extends Exception {
public PropertySetException(final String propertyName, final double value, final Exception e) {
super(String.format("Failed to set property %s to value %f: %s", propertyName, value, e.getMessage()));
}
}

View File

@ -1,5 +0,0 @@
package de.ph87.homeautomation.property;
public enum PropertyType {
ON_OFF, PERCENT, SHUTTER, LUX
}

View File

@ -0,0 +1,9 @@
package de.ph87.homeautomation.property2;
public interface IPropertyOwner {
String getPropertyOwnerName();
Property write(final String name, final double value);
}

View File

@ -0,0 +1,35 @@
package de.ph87.homeautomation.property2;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.time.ZonedDateTime;
@Getter
@Setter
@ToString
@Entity
public final class Property {
@Id
@GeneratedValue
@Setter(AccessLevel.NONE)
private Long id;
@Column(nullable = false, unique = true)
private String name;
@Column(unique = true)
private String title;
private ZonedDateTime timestamp;
private Double value;
}

View File

@ -0,0 +1,44 @@
package de.ph87.homeautomation.property2;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class PropertyOwnerValidatorService {
private final Set<IPropertyOwner> owners;
@PostConstruct
public void postConstruct() {
final Map<IPropertyOwner, List<IPropertyOwner>> overlapping = owners.stream().collect(Collectors.toMap(owner -> owner, this::getOverlappingList));
if (!overlapping.values().stream().allMatch(List::isEmpty)) {
throw new RuntimeException(
String.format(
"Following IPropertyOwners have overlapping names:\n\t%s",
overlapping.entrySet().stream()
.map(entry -> {
final String prefixClassName = entry.getKey().getClass().getCanonicalName();
final String prefixPropertyName = entry.getKey().getPropertyOwnerName();
final String overlappingList = entry.getValue().stream().map(owner -> owner.getClass().getCanonicalName() + "(" + owner.getPropertyOwnerName() + ")").collect(Collectors.joining("\n\t\t"));
return String.format("%s(%s):\n\t\t%s", prefixClassName, prefixPropertyName, overlappingList);
})
.collect(Collectors.joining("\n\t"))
)
);
}
}
private List<IPropertyOwner> getOverlappingList(final IPropertyOwner a) {
return owners.stream().filter(b -> b.getPropertyOwnerName().startsWith(a.getPropertyOwnerName())).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,11 @@
package de.ph87.homeautomation.property2;
import org.springframework.data.repository.CrudRepository;
import java.util.Optional;
public interface PropertyRepository extends CrudRepository<Property, Long> {
Optional<Property> findByName(String name);
}

View File

@ -0,0 +1,29 @@
package de.ph87.homeautomation.property2;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
import java.util.Set;
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class PropertyService {
private final Set<IPropertyOwner> owners;
private final PropertyRepository propertyRepository;
public Optional<Property> write(final String name, final double value) {
return owners.stream().map(owner -> owner.write(name, value)).findFirst();
}
public Optional<Property> read(final String name) {
return propertyRepository.findByName(name);
}
}

View File

@ -1,6 +1,5 @@
package de.ph87.homeautomation.schedule;
import de.ph87.homeautomation.property.PropertyType;
import de.ph87.homeautomation.schedule.entry.ScheduleEntry;
import lombok.AccessLevel;
import lombok.Getter;

View File

@ -1,6 +1,5 @@
package de.ph87.homeautomation.schedule;
import de.ph87.homeautomation.property.PropertyType;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

View File

@ -1,6 +1,5 @@
package de.ph87.homeautomation.schedule;
import de.ph87.homeautomation.property.PropertyType;
import de.ph87.homeautomation.schedule.entry.ScheduleEntryDto;
import lombok.Getter;