websocket property updates
This commit is contained in:
parent
927e8df13a
commit
bfa6b7b12b
5
src/main/angular/package-lock.json
generated
5
src/main/angular/package-lock.json
generated
@ -1821,6 +1821,11 @@
|
||||
"jsonc-parser": "3.0.0"
|
||||
}
|
||||
},
|
||||
"@stomp/stompjs": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-6.1.2.tgz",
|
||||
"integrity": "sha512-FHDTrIFM5Ospi4L3Xhj6v2+NzCVAeNDcBe95YjUWhWiRMrBF6uN3I7AUOlRgT6jU/2WQvvYK8ZaIxFfxFp+uHQ=="
|
||||
},
|
||||
"@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
|
||||
@ -23,7 +23,8 @@
|
||||
"@fortawesome/free-regular-svg-icons": "^5.15.4",
|
||||
"rxjs": "~6.6.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
"zone.js": "~0.11.4",
|
||||
"@stomp/stompjs": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~12.2.0",
|
||||
|
||||
29
src/main/angular/src/app/api/Update.ts
Normal file
29
src/main/angular/src/app/api/Update.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import {validateBooleanNotNull, validateStringNotEmptyNotNull} from "./validators";
|
||||
|
||||
export class Update<T> {
|
||||
|
||||
constructor(
|
||||
readonly type: string,
|
||||
readonly existing: boolean,
|
||||
readonly payload: T,
|
||||
) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
static fromJson(json: any) {
|
||||
return new Update<any>(
|
||||
validateStringNotEmptyNotNull(json["type"]),
|
||||
validateBooleanNotNull(json["existing"]),
|
||||
json["payload"],
|
||||
)
|
||||
}
|
||||
|
||||
static convert<T>(update: Update<any>, fromJson: (json: any) => T) {
|
||||
return new Update<T>(
|
||||
update.type,
|
||||
update.existing,
|
||||
fromJson(update.payload),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,10 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {map} from "rxjs/operators";
|
||||
import {filter, map} from "rxjs/operators";
|
||||
import {environment} from "../../environments/environment";
|
||||
import {Subject} from "rxjs";
|
||||
import {CompatClient, Stomp} from "@stomp/stompjs";
|
||||
import {Update} from "./Update";
|
||||
|
||||
export function NO_OP() {
|
||||
}
|
||||
@ -22,10 +25,24 @@ function errorInterceptor(errorHandler: (error: any) => void): ((error: any) =>
|
||||
})
|
||||
export class ApiService {
|
||||
|
||||
private webSocketClient: CompatClient;
|
||||
|
||||
private updateSubject = new Subject<Update<object>>();
|
||||
|
||||
constructor(
|
||||
private readonly http: HttpClient,
|
||||
private http: HttpClient,
|
||||
) {
|
||||
// nothing
|
||||
this.webSocketClient = Stomp.over(function () {
|
||||
return new WebSocket(environment.apiBasePath.replace('http', 'ws') + "websocket");
|
||||
});
|
||||
this.webSocketClient.debug = () => null;
|
||||
this.webSocketClient.connect({}, () => {
|
||||
this.webSocketClient.subscribe("/updates", stompMessage => this.updateSubject.next(Update.fromJson(JSON.parse(stompMessage.body))));
|
||||
});
|
||||
}
|
||||
|
||||
subscribe<T>(type: string, fromJson: (json: object) => T, next: (item: Update<T>) => void) {
|
||||
this.updateSubject.pipe(filter(update => update.type === type), map(update => Update.convert(update, fromJson))).subscribe(next);
|
||||
}
|
||||
|
||||
getItem<T>(path: string, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {validateBooleanAllowNull, validateNumberAllowNull, validateNumberNotNull, validateStringNotEmptyNotNull, validateStringNullToEmpty} from "../validators";
|
||||
import {Property} from "../property/property.service";
|
||||
|
||||
export abstract class Device {
|
||||
|
||||
@ -43,6 +44,8 @@ export abstract class Device {
|
||||
return a.title.localeCompare(b.title);
|
||||
}
|
||||
|
||||
abstract updateProperty(property: Property): void;
|
||||
|
||||
}
|
||||
|
||||
export class DeviceSwitch extends Device {
|
||||
@ -58,6 +61,12 @@ export class DeviceSwitch extends Device {
|
||||
super(id, title, type);
|
||||
}
|
||||
|
||||
updateProperty(property: Property): void {
|
||||
if (this.getState === property.name) {
|
||||
this.state = property.booleanValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class DeviceShutter extends Device {
|
||||
@ -73,4 +82,10 @@ export class DeviceShutter extends Device {
|
||||
super(id, title, type);
|
||||
}
|
||||
|
||||
updateProperty(property: Property): void {
|
||||
if (this.getPercent === property.name) {
|
||||
this.percent = property.numberValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import {ApiService, NO_OP} from "../api.service";
|
||||
import {validateBooleanAllowNull, validateDateAllowNull, validateNumberAllowNull, validateStringNotEmptyNotNull} from "../validators";
|
||||
import {ISearchService} from "../ISearchService";
|
||||
import {KeyValuePair} from "../KeyValuePair";
|
||||
import {Update} from "../Update";
|
||||
|
||||
export class Property {
|
||||
|
||||
@ -56,6 +57,10 @@ export class PropertyService implements ISearchService {
|
||||
// nothing
|
||||
}
|
||||
|
||||
subscribe(next: (device: Update<Property>) => void): void {
|
||||
this.api.subscribe("PropertyDto", Property.fromJson, next);
|
||||
}
|
||||
|
||||
get(id: string, next: (results: KeyValuePair) => void, error: (error: any) => void): void {
|
||||
this.api.postReturnItem("property/getById", id, KeyValuePair.fromJson, next, error);
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ export class DeviceListComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.propertyService.subscribe(update => this.devices.forEach(d => d.updateProperty(update.payload)));
|
||||
this.deviceService.findAll(devices => this.devices = devices);
|
||||
}
|
||||
|
||||
@ -52,7 +53,22 @@ export class DeviceListComponent implements OnInit {
|
||||
}
|
||||
|
||||
create(): void {
|
||||
this.deviceService.create(this.createType, device => this.devices.push(device));
|
||||
this.deviceService.create(this.createType, device => this.updateDevice(device, true));
|
||||
}
|
||||
|
||||
private updateDevice(device: Device, existing: boolean): void {
|
||||
const index: number = this.devices.findIndex(d => d.id === device.id);
|
||||
if (index >= 0) {
|
||||
if (existing) {
|
||||
this.devices[index] = device;
|
||||
} else {
|
||||
this.devices.splice(index, 1)
|
||||
}
|
||||
} else {
|
||||
if (existing) {
|
||||
this.devices.push(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getSwitchClassList(device: Device): object {
|
||||
|
||||
@ -43,20 +43,36 @@ public class DemoDataService {
|
||||
@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);
|
||||
final KnxGroupDto eg_ambiente_schalten = createKnxGroupIfNotExists("EG Ambiente Schalten", 0, 3, 80, "1.001", PropertyType.ON_OFF, false, false);
|
||||
final KnxGroupDto og_ambiente_schalten = createKnxGroupIfNotExists("OG Ambiente Schalten", 0, 6, 3, "1.001", PropertyType.ON_OFF, 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 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 KnxGroupDto wohnzimmer_rollladen_position_anfahren = createKnxGroupIfNotExists("Wohnzimmer Rollladen Position Anfahren", 0, 4, 24, "5.001", PropertyType.PERCENT, false, false);
|
||||
|
||||
final KnxGroupDto schlafzimmer_rollladen_position_anfahren = createKnxGroupIfNotExists("Schlafzimmer Rollladen Position Anfahren", 0, 3, 3, "5.001", PropertyType.PERCENT, false, false);
|
||||
final KnxGroupDto flur_rollladen_position_anfahren = createKnxGroupIfNotExists("Flur Rollladen Position Anfahren", 0, 5, 13, "5.001", PropertyType.PERCENT, false, false);
|
||||
|
||||
final KnxGroupDto flur_og_rollladen_position_anfahren = createKnxGroupIfNotExists("Flur Rollladen Position Anfahren", 0, 5, 13, "5.001", PropertyType.PERCENT, 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 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 KnxGroupDto helligkeit = createKnxGroupIfNotExists("Helligkeit", 0, 5, 6, "9.004", PropertyType.LUX, false, true);
|
||||
|
||||
createDeviceSwitch("Bad Licht Mitte", bad_licht_mitte_status, bad_licht_mitte_schalten);
|
||||
createDeviceShutter("Flur OG Rollladen", null, flur_rollladen_position_anfahren);
|
||||
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());
|
||||
|
||||
if (scheduleRepository.count() == 0) {
|
||||
final Schedule scheduleEgFlurLicht = createSchedule("EG Flur Licht", eg_flur_licht_schalten);
|
||||
final Schedule scheduleEgFlurLicht = createSchedule("EG Flur Licht", eg_flur_licht_schalten.getProperty());
|
||||
createTime(scheduleEgFlurLicht, 11, 30, 0, MIN30, true);
|
||||
createTime(scheduleEgFlurLicht, 12, 30, 0, MIN30, false);
|
||||
createTime(scheduleEgFlurLicht, 16, 30, 0, MIN30, true);
|
||||
@ -67,36 +83,36 @@ public class DemoDataService {
|
||||
createTime(scheduleEgFlurLicht, 2, 0, 0, MIN30, false);
|
||||
scheduleRepository.save(scheduleEgFlurLicht);
|
||||
|
||||
final Schedule scheduleEgAmbiente = createSchedule("EG Ambiente", eg_ambiente_schalten);
|
||||
final Schedule scheduleEgAmbiente = createSchedule("Ambiente EG", ambiente_eg_schalten.getProperty());
|
||||
createTime(scheduleEgAmbiente, 7, 15, 0, MIN30, true);
|
||||
createTime(scheduleEgAmbiente, 9, 30, 0, MIN30, false);
|
||||
createSunset(scheduleEgAmbiente, Zenith.OFFICIAL, MIN30, true);
|
||||
createSunset(scheduleEgAmbiente, Zenith.ASTRONOMICAL, MIN30, false);
|
||||
scheduleRepository.save(scheduleEgAmbiente);
|
||||
|
||||
final Schedule scheduleOgAmbiente = createSchedule("OG Ambiente", og_ambiente_schalten);
|
||||
final Schedule scheduleOgAmbiente = createSchedule("Ambiente OG", ambiente_og_schalten.getProperty());
|
||||
createTime(scheduleOgAmbiente, 7, 15, 0, MIN30, true);
|
||||
createTime(scheduleOgAmbiente, 9, 30, 0, MIN30, false);
|
||||
createSunset(scheduleOgAmbiente, Zenith.OFFICIAL, MIN30, true);
|
||||
createSunset(scheduleOgAmbiente, Zenith.ASTRONOMICAL, MIN30, false);
|
||||
scheduleRepository.save(scheduleOgAmbiente);
|
||||
|
||||
final Schedule scheduleWohnzimmerRollladen = createSchedule("Rollläden Wohnzimmer", wohnzimmer_rollladen_position_anfahren);
|
||||
final Schedule scheduleWohnzimmerRollladen = createSchedule("Rollläden Wohnzimmer", wohnzimmer_rollladen_position_anfahren.getProperty());
|
||||
createSunrise(scheduleWohnzimmerRollladen, BETWEEN_OFFICIAL_AND_CIVIL, 0, 0);
|
||||
createSunset(scheduleWohnzimmerRollladen, BETWEEN_OFFICIAL_AND_CIVIL, 0, 100);
|
||||
scheduleRepository.save(scheduleWohnzimmerRollladen);
|
||||
|
||||
final Schedule scheduleSchlafzimmerRollladen = createSchedule("Rollläden Schlafzimmer", schlafzimmer_rollladen_position_anfahren);
|
||||
final Schedule scheduleSchlafzimmerRollladen = createSchedule("Rollläden Schlafzimmer", schlafzimmer_rollladen_position_anfahren.getProperty());
|
||||
createTime(scheduleSchlafzimmerRollladen, 7, 0, 0, 0, 0);
|
||||
createSunset(scheduleSchlafzimmerRollladen, BETWEEN_OFFICIAL_AND_CIVIL, 0, 100);
|
||||
scheduleRepository.save(scheduleSchlafzimmerRollladen);
|
||||
|
||||
final Schedule scheduleFlurRollladen = createSchedule("Rollladen Flur", flur_rollladen_position_anfahren);
|
||||
final Schedule scheduleFlurRollladen = createSchedule("Rollladen Flur", flur_og_rollladen_position_anfahren.getProperty());
|
||||
createSunrise(scheduleFlurRollladen, BETWEEN_OFFICIAL_AND_CIVIL, 0, 0);
|
||||
createSunset(scheduleFlurRollladen, BETWEEN_OFFICIAL_AND_CIVIL, 0, 100);
|
||||
scheduleRepository.save(scheduleFlurRollladen);
|
||||
|
||||
final Schedule scheduleBadLichtMitte = createSchedule("Bad Licht Mitte", bad_licht_mitte_schalten);
|
||||
final Schedule scheduleBadLichtMitte = createSchedule("Bad Licht Mitte", bad_licht_mitte_schalten.getProperty());
|
||||
createTime(scheduleBadLichtMitte, 10, 30, 0, MIN30, true);
|
||||
createTime(scheduleBadLichtMitte, 11, 30, 0, MIN30, false);
|
||||
createTime(scheduleBadLichtMitte, 15, 30, 0, MIN30, true);
|
||||
|
||||
@ -2,7 +2,7 @@ package de.ph87.homeautomation.device;
|
||||
|
||||
import de.ph87.homeautomation.device.devices.*;
|
||||
import de.ph87.homeautomation.property.PropertyService;
|
||||
import de.ph87.office.web.NotFoundException;
|
||||
import de.ph87.homeautomation.web.NotFoundException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -7,7 +7,7 @@ import de.ph87.homeautomation.device.devices.DeviceSwitch;
|
||||
import de.ph87.homeautomation.property.PropertyService;
|
||||
import de.ph87.homeautomation.property.PropertySetException;
|
||||
import de.ph87.homeautomation.schedule.ScheduleWriteService;
|
||||
import de.ph87.office.web.BadRequestException;
|
||||
import de.ph87.homeautomation.web.BadRequestException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -4,10 +4,12 @@ import de.ph87.homeautomation.property.PropertyDto;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class KnxGroupDto extends PropertyDto {
|
||||
public class KnxGroupDto {
|
||||
|
||||
public final long id;
|
||||
|
||||
public final PropertyDto property;
|
||||
|
||||
public final int addressRaw;
|
||||
|
||||
public final String addressStr;
|
||||
@ -15,11 +17,11 @@ public class KnxGroupDto extends PropertyDto {
|
||||
public final String dpt;
|
||||
|
||||
public KnxGroupDto(final KnxGroup knxGroup) {
|
||||
super(knxGroup.getPropertyName(), knxGroup.getTitle(), knxGroup.getPropertyType(), knxGroup.getBooleanValue(), knxGroup.getNumberValue(), knxGroup.getValueTimestamp());
|
||||
id = knxGroup.getId();
|
||||
addressRaw = knxGroup.getAddressRaw();
|
||||
addressStr = knxGroup.getAddressStr();
|
||||
dpt = knxGroup.getDpt();
|
||||
this.property = new PropertyDto(knxGroup.getPropertyName(), knxGroup.getTitle(), knxGroup.getPropertyType(), knxGroup.getBooleanValue(), knxGroup.getNumberValue(), knxGroup.getValueTimestamp());
|
||||
this.id = knxGroup.getId();
|
||||
this.addressRaw = knxGroup.getAddressRaw();
|
||||
this.addressStr = knxGroup.getAddressStr();
|
||||
this.dpt = knxGroup.getDpt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package de.ph87.homeautomation.knx.group;
|
||||
|
||||
import de.ph87.homeautomation.property.PropertyDto;
|
||||
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 KnxGroupMapperService {
|
||||
|
||||
public PropertyDto toPropertyDto(final KnxGroup knxGroup) {
|
||||
return new PropertyDto(knxGroup.getPropertyName(), knxGroup.getTitle(), knxGroup.getPropertyType(), knxGroup.getBooleanValue(), knxGroup.getNumberValue(), knxGroup.getValueTimestamp());
|
||||
}
|
||||
|
||||
}
|
||||
@ -6,9 +6,7 @@ import de.ph87.homeautomation.property.PropertySetException;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tuwien.auto.calimero.GroupAddress;
|
||||
@ -25,7 +23,7 @@ import static de.ph87.homeautomation.shared.Helpers.quoteOrNull;
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class KnxGroupSetService implements IPropertyOwner {
|
||||
public class KnxGroupOwnerService implements IPropertyOwner {
|
||||
|
||||
@Getter
|
||||
private final Pattern propertyNamePattern = Pattern.compile("^knx\\.group\\.(\\d+)\\.(\\d+)\\.(\\d+)$");
|
||||
@ -36,11 +34,7 @@ public class KnxGroupSetService implements IPropertyOwner {
|
||||
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@EventListener(ApplicationStartedEvent.class)
|
||||
public void requestAll() {
|
||||
knxGroupWriteService.markAllForRead();
|
||||
eventPublisher.publishEvent(new KnxThreadWakeUpEvent());
|
||||
}
|
||||
private final KnxGroupMapperService knxGroupMapperService;
|
||||
|
||||
@Override
|
||||
public void setProperty(final String propertyName, final double value) throws PropertySetException {
|
||||
@ -66,19 +60,23 @@ public class KnxGroupSetService implements IPropertyOwner {
|
||||
return knxGroupRepository.findByAddressRaw(parseGroupAddress(propertyName).getRawAddress()).map(KnxGroup::getNumberValue).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<KnxGroupDto> findPropertyByName(final String propertyName) {
|
||||
return knxGroupRepository.findByPropertyName(propertyName).map(KnxGroupDto::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PropertyDto> findAllProperties() {
|
||||
return knxGroupRepository.findAll().stream().map(KnxGroupDto::new).collect(Collectors.toList());
|
||||
return knxGroupRepository.findAll().stream().map(knxGroupMapperService::toPropertyDto).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PropertyDto> findAllPropertiesLike(final String like) {
|
||||
return knxGroupRepository.findAllByPropertyNameLikeIgnoreCaseOrTitleLikeIgnoreCase(like, like).stream().map(KnxGroupDto::new).collect(Collectors.toList());
|
||||
return knxGroupRepository.findAllByPropertyNameLikeIgnoreCaseOrTitleLikeIgnoreCase(like, like).stream().map(knxGroupMapperService::toPropertyDto).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<PropertyDto> findPropertyDtoByPropertyName(final String propertyName) {
|
||||
return findKnxGroupByPropertyName(propertyName).map(knxGroupMapperService::toPropertyDto);
|
||||
}
|
||||
|
||||
private Optional<KnxGroup> findKnxGroupByPropertyName(final String propertyName) {
|
||||
return knxGroupRepository.findByPropertyName(propertyName);
|
||||
}
|
||||
|
||||
private GroupAddress parseGroupAddress(final String propertyName) {
|
||||
@ -0,0 +1,14 @@
|
||||
package de.ph87.homeautomation.knx.group;
|
||||
|
||||
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 KnxGroupReadService {
|
||||
|
||||
}
|
||||
@ -1,8 +1,12 @@
|
||||
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;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -12,10 +16,6 @@ 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;
|
||||
@ -29,17 +29,16 @@ public class KnxGroupWriteService {
|
||||
|
||||
private final KnxGroupRepository knxGroupRepository;
|
||||
|
||||
private DatagramSocket broadcastDatagramSocket;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private InetAddress broadcastAddress;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
private final int broadcastPort = 1987;
|
||||
private final KnxGroupMapperService knxGroupMapperService;
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() throws SocketException, UnknownHostException {
|
||||
broadcastDatagramSocket = new DatagramSocket();
|
||||
broadcastDatagramSocket.setBroadcast(true);
|
||||
broadcastAddress = InetAddress.getByName("255.255.255.255");
|
||||
@EventListener(ApplicationStartedEvent.class)
|
||||
public void markAllForRead() {
|
||||
knxGroupRepository.findAllByRead_AbleTrue().forEach(knxGroup -> knxGroup.getRead().setNextTimestamp(ZonedDateTime.now()));
|
||||
eventPublisher.publishEvent(new KnxThreadWakeUpEvent());
|
||||
}
|
||||
|
||||
public void updateIfExists(final int rawAddress, final byte[] data, final IndividualAddress knxDeviceAddress) {
|
||||
@ -68,7 +67,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);
|
||||
publish(knxGroup);
|
||||
} catch (NoTranslatorException e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
@ -76,6 +75,10 @@ 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) {
|
||||
@ -85,29 +88,12 @@ public class KnxGroupWriteService {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -10,14 +10,14 @@ public interface IPropertyOwner {
|
||||
|
||||
void setProperty(final String propertyName, final double value) throws PropertySetException;
|
||||
|
||||
Boolean readBoolean(String propertyName);
|
||||
Boolean readBoolean(final String propertyName);
|
||||
|
||||
Double readNumber(String propertyName);
|
||||
Double readNumber(final String propertyName);
|
||||
|
||||
List<PropertyDto> findAllProperties();
|
||||
|
||||
List<PropertyDto> findAllPropertiesLike(final String like);
|
||||
|
||||
Optional<? extends PropertyDto> findPropertyByName(final String propertyName);
|
||||
Optional<? extends PropertyDto> findPropertyDtoByPropertyName(final String propertyName);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,10 +2,11 @@ package de.ph87.homeautomation.property;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
@Getter
|
||||
public class PropertyDto {
|
||||
public final class PropertyDto implements Serializable {
|
||||
|
||||
public final String name;
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package de.ph87.homeautomation.property;
|
||||
|
||||
import de.ph87.office.web.NotFoundException;
|
||||
import de.ph87.homeautomation.web.NotFoundException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -51,7 +51,7 @@ public class PropertyService {
|
||||
}
|
||||
|
||||
public PropertyDto getById(final String propertyName) {
|
||||
return findOwner(propertyName).flatMap(iPropertyOwner -> iPropertyOwner.findPropertyByName(propertyName)).orElseThrow(() -> new NotFoundException("Property.name=%s", propertyName));
|
||||
return findOwner(propertyName).flatMap(iPropertyOwner -> iPropertyOwner.findPropertyDtoByPropertyName(propertyName)).orElseThrow(() -> new NotFoundException("Property.name=%s", propertyName));
|
||||
}
|
||||
|
||||
private Optional<IPropertyOwner> findOwner(final String propertyName) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package de.ph87.homeautomation.schedule;
|
||||
|
||||
import de.ph87.homeautomation.schedule.entry.ScheduleEntry;
|
||||
import de.ph87.office.web.NotFoundException;
|
||||
import de.ph87.homeautomation.web.NotFoundException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package de.ph87.homeautomation.schedule.entry;
|
||||
|
||||
import de.ph87.office.web.NotFoundException;
|
||||
import de.ph87.homeautomation.web.NotFoundException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package de.ph87.office.web;
|
||||
package de.ph87.homeautomation.web;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package de.ph87.office.web;
|
||||
package de.ph87.homeautomation.web;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package de.ph87.office.web;
|
||||
package de.ph87.homeautomation.web;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package de.ph87.office.web;
|
||||
package de.ph87.homeautomation.web;
|
||||
|
||||
import de.ph87.homeautomation.web.WebSocketConfig;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user