Shutter
This commit is contained in:
parent
fdf64ebd17
commit
b14c8d63d2
30
src/main/angular/src/app/api/Shutter/Shutter.ts
Normal file
30
src/main/angular/src/app/api/Shutter/Shutter.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import {Property} from "../Property/Property";
|
||||||
|
import {orNull, validateString} from "../common/validators";
|
||||||
|
|
||||||
|
export class Shutter {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly uuid: string,
|
||||||
|
readonly name: string,
|
||||||
|
readonly slug: string,
|
||||||
|
readonly positionPropertyId: string,
|
||||||
|
readonly positionProperty: Property | null,
|
||||||
|
) {
|
||||||
|
// -
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json: any): Shutter {
|
||||||
|
return new Shutter(
|
||||||
|
validateString(json.uuid),
|
||||||
|
validateString(json.name),
|
||||||
|
validateString(json.slug),
|
||||||
|
validateString(json.positionPropertyId),
|
||||||
|
orNull(json.positionProperty, Property.fromJson),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static trackBy(index: number, shutter: Shutter) {
|
||||||
|
return shutter.uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
5
src/main/angular/src/app/api/Shutter/ShutterFilter.ts
Normal file
5
src/main/angular/src/app/api/Shutter/ShutterFilter.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export class ShutterFilter {
|
||||||
|
|
||||||
|
search: string = "";
|
||||||
|
|
||||||
|
}
|
||||||
32
src/main/angular/src/app/api/Shutter/shutter.service.ts
Normal file
32
src/main/angular/src/app/api/Shutter/shutter.service.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {CrudService} from '../common/CrudService';
|
||||||
|
import {Shutter} from './Shutter';
|
||||||
|
import {ApiService} from '../common/api.service';
|
||||||
|
import {Next} from '../common/types';
|
||||||
|
|
||||||
|
import {ShutterFilter} from './ShutterFilter';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ShutterService extends CrudService<Shutter> {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
api: ApiService,
|
||||||
|
) {
|
||||||
|
super(api, ['Shutter'], Shutter.fromJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
getByUuid(uuid: string, next: Next<Shutter>): void {
|
||||||
|
this.getSingle(['getByUuid', uuid], next);
|
||||||
|
}
|
||||||
|
|
||||||
|
list(filter: ShutterFilter | null, next: Next<Shutter[]>): void {
|
||||||
|
this.postList(['list'], filter, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPosition(shutter: Shutter, position: number, next?: Next<void>): void {
|
||||||
|
this.getNone(['setPosition', shutter.uuid, position], next);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
<div class="flexBox">
|
<div class="flexBox">
|
||||||
<div class="flexBoxFixed menu">
|
<div class="flexBoxFixed menu">
|
||||||
<div class="item itemLeft" routerLink="DeviceList" routerLinkActive="active">Geräte</div>
|
<div class="item itemLeft" routerLink="DeviceList" routerLinkActive="active">Geräte</div>
|
||||||
|
<div class="item itemLeft" routerLink="ShutterList" routerLinkActive="active">Rollläden</div>
|
||||||
<div class="item itemRight" routerLink="GroupList" routerLinkActive="active">KNX</div>
|
<div class="item itemRight" routerLink="GroupList" routerLinkActive="active">KNX</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flexBoxRest">
|
<div class="flexBoxRest">
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import {Routes} from '@angular/router';
|
import {Routes} from '@angular/router';
|
||||||
import {KnxGroupListPageComponent} from './pages/knx-group-list-page/knx-group-list-page.component';
|
import {KnxGroupListPageComponent} from './pages/knx-group-list-page/knx-group-list-page.component';
|
||||||
import {DeviceListPageComponent} from './pages/device-list-page/device-list-page.component';
|
import {DeviceListPageComponent} from './pages/device-list-page/device-list-page.component';
|
||||||
|
import {ShutterListPageComponent} from './pages/shutter-list-page/shutter-list-page.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{path: 'DeviceList', component: DeviceListPageComponent},
|
{path: 'DeviceList', component: DeviceListPageComponent},
|
||||||
|
{path: 'ShutterList', component: ShutterListPageComponent},
|
||||||
{path: 'GroupList', component: KnxGroupListPageComponent},
|
{path: 'GroupList', component: KnxGroupListPageComponent},
|
||||||
{path: '**', redirectTo: 'GroupList'},
|
{path: '**', redirectTo: 'GroupList'},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
<div class="flexBox">
|
||||||
|
<div class="flexBoxFixed">
|
||||||
|
<input [(ngModel)]="filter.search" (ngModelChange)="fetchDelayed()" placeholder="Suchen...">
|
||||||
|
</div>
|
||||||
|
<div class="flexBoxRest">
|
||||||
|
<app-shutter-list [shutterList]="shutterList"></app-shutter-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
@import "../../../config";
|
||||||
|
|
||||||
|
input {
|
||||||
|
border-bottom: @border solid lightgray;
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
|
import {ShutterListComponent} from '../../shared/shutter-list/shutter-list.component';
|
||||||
|
import {Shutter} from '../../api/Shutter/Shutter';
|
||||||
|
import {ShutterService} from '../../api/Shutter/shutter.service';
|
||||||
|
import {FormsModule} from '@angular/forms';
|
||||||
|
import {ShutterFilter} from '../../api/Shutter/ShutterFilter';
|
||||||
|
import {Subscription} from 'rxjs';
|
||||||
|
import {KnxGroupListComponent} from '../../shared/knx-group-list/knx-group-list.component';
|
||||||
|
import {ApiService} from '../../api/common/api.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-shutter-list-page',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
ShutterListComponent,
|
||||||
|
FormsModule,
|
||||||
|
KnxGroupListComponent
|
||||||
|
],
|
||||||
|
templateUrl: './shutter-list-page.component.html',
|
||||||
|
styleUrl: './shutter-list-page.component.less'
|
||||||
|
})
|
||||||
|
export class ShutterListPageComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
private readonly subs: Subscription[] = [];
|
||||||
|
|
||||||
|
protected shutterList: Shutter[] = [];
|
||||||
|
|
||||||
|
protected filter: ShutterFilter = new ShutterFilter();
|
||||||
|
|
||||||
|
private fetchTimeout: any;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected readonly shutterService: ShutterService,
|
||||||
|
protected readonly apiService: ApiService,
|
||||||
|
) {
|
||||||
|
// -
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.fetch();
|
||||||
|
this.subs.push(this.shutterService.subscribe(shutter => this.updateShutter(shutter)));
|
||||||
|
this.apiService.connected(() => this.fetch());
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs.forEach(sub => sub.unsubscribe());
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchDelayed() {
|
||||||
|
if (this.fetchTimeout) {
|
||||||
|
clearTimeout(this.fetchTimeout);
|
||||||
|
this.fetchTimeout = undefined;
|
||||||
|
}
|
||||||
|
this.fetchTimeout = setTimeout(() => this.fetch(), 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetch() {
|
||||||
|
this.shutterService.list(this.filter, list => this.shutterList = list)
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateShutter(shutter: Shutter) {
|
||||||
|
const index = this.shutterList.findIndex(d => d.uuid === shutter.uuid);
|
||||||
|
if (index >= 0) {
|
||||||
|
this.shutterList.splice(index, 1, shutter);
|
||||||
|
} else {
|
||||||
|
this.fetch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<div class="window" (click)="activate.emit(position)">
|
||||||
|
<div class="shutter" [style.height]="position + '%'">
|
||||||
|
<!-- -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
@import "../../../../config";
|
||||||
|
|
||||||
|
.window {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: lightskyblue;
|
||||||
|
border: @border solid black;
|
||||||
|
|
||||||
|
.shutter {
|
||||||
|
background-color: saddlebrown;
|
||||||
|
border-bottom: @border solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-shutter-icon',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './shutter-icon.component.html',
|
||||||
|
styleUrl: './shutter-icon.component.less'
|
||||||
|
})
|
||||||
|
export class ShutterIconComponent {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
position?: number
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
activate: EventEmitter<number> = new EventEmitter();
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
<div class="shutterList tileContainer">
|
||||||
|
|
||||||
|
<div class="tile" *ngFor="let shutter of shutterList; trackBy: Shutter.trackBy">
|
||||||
|
|
||||||
|
<div class="shutter tileInner">
|
||||||
|
|
||||||
|
<div class="name">
|
||||||
|
{{ shutter.name }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="icon">
|
||||||
|
<app-shutter-icon [position]="shutter.positionProperty?.state?.value"></app-shutter-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="timestamp details">
|
||||||
|
{{ shutter.positionProperty?.lastValueChange | relative:now }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<div class="action">
|
||||||
|
<app-shutter-icon [position]="0" (activate)="shutterService.setPosition(shutter, $event)"></app-shutter-icon>
|
||||||
|
</div>
|
||||||
|
<div class="action">
|
||||||
|
<app-shutter-icon [position]="50" (activate)="shutterService.setPosition(shutter, $event)"></app-shutter-icon>
|
||||||
|
</div>
|
||||||
|
<div class="action">
|
||||||
|
<app-shutter-icon [position]="80" (activate)="shutterService.setPosition(shutter, $event)"></app-shutter-icon>
|
||||||
|
</div>
|
||||||
|
<div class="action">
|
||||||
|
<app-shutter-icon [position]="90" (activate)="shutterService.setPosition(shutter, $event)"></app-shutter-icon>
|
||||||
|
</div>
|
||||||
|
<div class="action">
|
||||||
|
<app-shutter-icon [position]="100" (activate)="shutterService.setPosition(shutter, $event)"></app-shutter-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
@import "../../../config";
|
||||||
|
|
||||||
|
.shutterList {
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.shutter {
|
||||||
|
|
||||||
|
.name {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
clear: left;
|
||||||
|
float: left;
|
||||||
|
width: 4em;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timestamp {
|
||||||
|
float: right;
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
clear: right;
|
||||||
|
float: right;
|
||||||
|
|
||||||
|
div {
|
||||||
|
float: left;
|
||||||
|
margin-left: @space;
|
||||||
|
width: 3em;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
||||||
|
import {NgClass, NgForOf} from '@angular/common';
|
||||||
|
import {Shutter} from '../../api/Shutter/Shutter';
|
||||||
|
import {ShutterService} from '../../api/Shutter/shutter.service';
|
||||||
|
import {RelativePipe} from '../../api/common/relative.pipe';
|
||||||
|
import {Subscription, timer} from 'rxjs';
|
||||||
|
import {ShutterIconComponent} from './shutter-icon/shutter-icon.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-shutter-list',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
NgForOf,
|
||||||
|
NgClass,
|
||||||
|
RelativePipe,
|
||||||
|
ShutterIconComponent
|
||||||
|
],
|
||||||
|
templateUrl: './shutter-list.component.html',
|
||||||
|
styleUrl: './shutter-list.component.less'
|
||||||
|
})
|
||||||
|
export class ShutterListComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
protected readonly Shutter = Shutter;
|
||||||
|
|
||||||
|
private readonly subs: Subscription[] = [];
|
||||||
|
|
||||||
|
protected now: Date = new Date();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
shutterList: Shutter[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected readonly shutterService: ShutterService,
|
||||||
|
) {
|
||||||
|
// -
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.now = new Date();
|
||||||
|
this.subs.push(timer(1000, 1000).subscribe(() => this.now = new Date()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs.forEach(sub => sub.unsubscribe());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package de.ph87.home.demo;
|
|||||||
import de.ph87.home.device.DeviceService;
|
import de.ph87.home.device.DeviceService;
|
||||||
import de.ph87.home.knx.property.KnxPropertyService;
|
import de.ph87.home.knx.property.KnxPropertyService;
|
||||||
import de.ph87.home.knx.property.KnxPropertyType;
|
import de.ph87.home.knx.property.KnxPropertyType;
|
||||||
|
import de.ph87.home.shutter.ShutterService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||||
@ -21,21 +22,42 @@ public class DemoService {
|
|||||||
|
|
||||||
private final DeviceService deviceService;
|
private final DeviceService deviceService;
|
||||||
|
|
||||||
|
private final ShutterService shutterService;
|
||||||
|
|
||||||
@EventListener(ApplicationStartedEvent.class)
|
@EventListener(ApplicationStartedEvent.class)
|
||||||
public void startup() {
|
public void startup() {
|
||||||
knxPropertyService.create("eg_ambiente", KnxPropertyType.BOOLEAN, adr(849), adr(848));
|
knxPropertyService.create("eg_ambiente", KnxPropertyType.BOOLEAN, adr(849), adr(848));
|
||||||
knxPropertyService.create("fernseher", KnxPropertyType.BOOLEAN, adr(20), adr(4));
|
|
||||||
knxPropertyService.create("verstaerker", KnxPropertyType.BOOLEAN, adr(825), adr(824));
|
|
||||||
knxPropertyService.create("fensterdeko", KnxPropertyType.BOOLEAN, adr(1823), adr(1822));
|
|
||||||
knxPropertyService.create("haengelampe", KnxPropertyType.BOOLEAN, adr(1794), adr(1799));
|
|
||||||
knxPropertyService.create("receiver", KnxPropertyType.BOOLEAN, adr(2561), adr(2560));
|
|
||||||
|
|
||||||
deviceService.create("EG Ambiente", "eg_ambiente", "eg_ambiente");
|
deviceService.create("EG Ambiente", "eg_ambiente", "eg_ambiente");
|
||||||
|
|
||||||
|
knxPropertyService.create("fernseher", KnxPropertyType.BOOLEAN, adr(20), adr(4));
|
||||||
deviceService.create("Wohnzimmer Fernseher", "fernseher", "fernseher");
|
deviceService.create("Wohnzimmer Fernseher", "fernseher", "fernseher");
|
||||||
|
|
||||||
|
knxPropertyService.create("verstaerker", KnxPropertyType.BOOLEAN, adr(825), adr(824));
|
||||||
deviceService.create("Wohnzimmer Verstärker", "verstaerker", "verstaerker");
|
deviceService.create("Wohnzimmer Verstärker", "verstaerker", "verstaerker");
|
||||||
|
|
||||||
|
knxPropertyService.create("fensterdeko", KnxPropertyType.BOOLEAN, adr(1823), adr(1822));
|
||||||
deviceService.create("Wohnzimmer Fenster", "fensterdeko", "fensterdeko");
|
deviceService.create("Wohnzimmer Fenster", "fensterdeko", "fensterdeko");
|
||||||
|
|
||||||
|
knxPropertyService.create("haengelampe", KnxPropertyType.BOOLEAN, adr(1794), adr(1799));
|
||||||
deviceService.create("Wohnzimmer Hängelampe", "haengelampe", "haengelampe");
|
deviceService.create("Wohnzimmer Hängelampe", "haengelampe", "haengelampe");
|
||||||
|
|
||||||
|
knxPropertyService.create("receiver", KnxPropertyType.BOOLEAN, adr(2561), adr(2560));
|
||||||
deviceService.create("Receiver", "receiver", "receiver");
|
deviceService.create("Receiver", "receiver", "receiver");
|
||||||
|
|
||||||
|
knxPropertyService.create("wohnzimmer_links", KnxPropertyType.DOUBLE, adr(1048), adr(1048));
|
||||||
|
shutterService.create("Wohnzimmer Links", "wohnzimmer_links", "wohnzimmer_links");
|
||||||
|
|
||||||
|
knxPropertyService.create("wohnzimmer_rechts", KnxPropertyType.DOUBLE, adr(1811), adr(1811));
|
||||||
|
shutterService.create("Wohnzimmer Rechts", "wohnzimmer_rechts", "wohnzimmer_rechts");
|
||||||
|
|
||||||
|
knxPropertyService.create("kueche_seite", KnxPropertyType.DOUBLE, adr(2316), adr(2316));
|
||||||
|
shutterService.create("Küche Seite", "kueche_seite", "kueche_seite");
|
||||||
|
|
||||||
|
knxPropertyService.create("kueche_theke", KnxPropertyType.DOUBLE, adr(2320), adr(2320));
|
||||||
|
shutterService.create("Küche Theke", "kueche_theke", "kueche_theke");
|
||||||
|
|
||||||
|
knxPropertyService.create("kueche_tuer", KnxPropertyType.DOUBLE, adr(2324), adr(2324));
|
||||||
|
shutterService.create("Küche Tür", "kueche_tuer", "kueche_tuer");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GroupAddress adr(final int rawGroupAddress) {
|
private static GroupAddress adr(final int rawGroupAddress) {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import java.util.List;
|
|||||||
@ToString
|
@ToString
|
||||||
public class DeviceDto implements IWebSocketMessage {
|
public class DeviceDto implements IWebSocketMessage {
|
||||||
|
|
||||||
|
@ToString.Exclude
|
||||||
private final List<Object> websocketTopic = List.of("Device");
|
private final List<Object> websocketTopic = List.of("Device");
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import java.util.List;
|
|||||||
@ToString
|
@ToString
|
||||||
public class GroupDto implements IWebSocketMessage {
|
public class GroupDto implements IWebSocketMessage {
|
||||||
|
|
||||||
|
@ToString.Exclude
|
||||||
private final List<Object> websocketTopic = List.of("Knx", "Group");
|
private final List<Object> websocketTopic = List.of("Knx", "Group");
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|||||||
@ -71,11 +71,6 @@ public class KnxPropertyService {
|
|||||||
findAllByAddress(event.getDestination()).forEach(knxProperty -> onProcessEvent(knxProperty, event));
|
findAllByAddress(event.getDestination()).forEach(knxProperty -> onProcessEvent(knxProperty, event));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private List<KnxProperty> findAllByAddress(@NonNull final GroupAddress address) {
|
|
||||||
return knxPropertyRepository.findDistinctByReadOrWrite(address, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onProcessEvent(@NonNull final KnxProperty knxProperty, @NonNull final ProcessEvent event) {
|
private void onProcessEvent(@NonNull final KnxProperty knxProperty, @NonNull final ProcessEvent event) {
|
||||||
log.debug("onProcessEvent: knxProperty={}, event={}", knxProperty, event);
|
log.debug("onProcessEvent: knxProperty={}, event={}", knxProperty, event);
|
||||||
groupRepository.findByAddress(event.getDestination()).ifPresent(group -> onProcessEvent(knxProperty, event, group));
|
groupRepository.findByAddress(event.getDestination()).ifPresent(group -> onProcessEvent(knxProperty, event, group));
|
||||||
@ -138,4 +133,9 @@ public class KnxPropertyService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private List<KnxProperty> findAllByAddress(@NonNull final GroupAddress address) {
|
||||||
|
return knxPropertyRepository.findDistinctByReadOrWrite(address, address);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/main/java/de/ph87/home/shutter/Shutter.java
Normal file
39
src/main/java/de/ph87/home/shutter/Shutter.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package de.ph87.home.shutter;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Shutter {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@NonNull
|
||||||
|
private String uuid = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Column(nullable = false, unique = true)
|
||||||
|
private String slug;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@NonNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String positionPropertyId;
|
||||||
|
|
||||||
|
public Shutter(@NonNull final String name, @NonNull final String slug, @NonNull final String positionPropertyId) {
|
||||||
|
this.name = name;
|
||||||
|
this.slug = slug;
|
||||||
|
this.positionPropertyId = positionPropertyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
59
src/main/java/de/ph87/home/shutter/ShutterController.java
Normal file
59
src/main/java/de/ph87/home/shutter/ShutterController.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package de.ph87.home.shutter;
|
||||||
|
|
||||||
|
import de.ph87.home.property.PropertyNotFound;
|
||||||
|
import de.ph87.home.property.PropertyNotWritable;
|
||||||
|
import de.ph87.home.property.PropertyTypeMismatch;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import tuwien.auto.calimero.KNXFormatException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("Shutter")
|
||||||
|
public class ShutterController {
|
||||||
|
|
||||||
|
private final ShutterService shutterService;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@GetMapping("getByUuid/{id}")
|
||||||
|
@ExceptionHandler(KNXFormatException.class)
|
||||||
|
private ShutterDto getByUuid(@PathVariable final String id, @NonNull final HttpServletRequest request) {
|
||||||
|
log.debug("getByUuid: path={}", request.getServletPath());
|
||||||
|
return shutterService.getByUuidDto(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@RequestMapping(value = "list", method = {RequestMethod.GET, RequestMethod.POST})
|
||||||
|
private List<ShutterDto> list(@RequestBody(required = false) @Nullable final ShutterFilter filter, @NonNull final HttpServletRequest request) throws PropertyTypeMismatch {
|
||||||
|
log.debug("list: path={} filter={}", request.getServletPath(), filter);
|
||||||
|
return shutterService.list(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@GetMapping("get/{uuidOrSlug}")
|
||||||
|
private ShutterDto get(@PathVariable @NonNull final String uuidOrSlug, @NonNull final HttpServletRequest request) {
|
||||||
|
log.debug("get: path={}", request.getServletPath());
|
||||||
|
return shutterService.getByUuidOrSlugDto(uuidOrSlug);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@GetMapping("getPosition/{uuidOrSlug}")
|
||||||
|
private Double getPosition(@PathVariable @NonNull final String uuidOrSlug, @NonNull final HttpServletRequest request) throws PropertyTypeMismatch {
|
||||||
|
log.debug("getPosition: path={}", request.getServletPath());
|
||||||
|
return shutterService.getByUuidOrSlugDto(uuidOrSlug).getPositionValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("setPosition/{uuidOrSlug}/{position}")
|
||||||
|
private void setPosition(@PathVariable @NonNull final String uuidOrSlug, @PathVariable final double position, @NonNull final HttpServletRequest request) throws PropertyNotFound, PropertyNotWritable, PropertyTypeMismatch {
|
||||||
|
log.debug("setPosition: path={}", request.getServletPath());
|
||||||
|
shutterService.setPosition(uuidOrSlug, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
62
src/main/java/de/ph87/home/shutter/ShutterDto.java
Normal file
62
src/main/java/de/ph87/home/shutter/ShutterDto.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package de.ph87.home.shutter;
|
||||||
|
|
||||||
|
import de.ph87.home.property.PropertyDto;
|
||||||
|
import de.ph87.home.property.PropertyTypeMismatch;
|
||||||
|
import de.ph87.home.web.IWebSocketMessage;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
public class ShutterDto implements IWebSocketMessage {
|
||||||
|
|
||||||
|
@ToString.Exclude
|
||||||
|
private final List<Object> websocketTopic = List.of("Shutter");
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final String uuid;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final String slug;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final String positionPropertyId;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@ToString.Exclude
|
||||||
|
private final PropertyDto<Double> positionProperty;
|
||||||
|
|
||||||
|
public ShutterDto(@NonNull final Shutter shutter, @Nullable final PropertyDto<Double> positionProperty) {
|
||||||
|
this.uuid = shutter.getUuid();
|
||||||
|
this.name = shutter.getName();
|
||||||
|
this.slug = shutter.getSlug();
|
||||||
|
this.positionPropertyId = shutter.getPositionPropertyId();
|
||||||
|
this.positionProperty = positionProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@ToString.Include
|
||||||
|
public String position() {
|
||||||
|
try {
|
||||||
|
return "" + getPositionValue();
|
||||||
|
} catch (PropertyTypeMismatch e) {
|
||||||
|
return "[PropertyTypeMismatch]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Double getPositionValue() throws PropertyTypeMismatch {
|
||||||
|
if (positionProperty == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return positionProperty.getStateValueAs(Double.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
41
src/main/java/de/ph87/home/shutter/ShutterFilter.java
Normal file
41
src/main/java/de/ph87/home/shutter/ShutterFilter.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package de.ph87.home.shutter;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import de.ph87.home.common.crud.AbstractSearchFilter;
|
||||||
|
import de.ph87.home.property.PropertyTypeMismatch;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
public class ShutterFilter extends AbstractSearchFilter {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@JsonProperty
|
||||||
|
private Boolean positionNull;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@JsonProperty
|
||||||
|
private Double positionMin;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@JsonProperty
|
||||||
|
private Double positionMax;
|
||||||
|
|
||||||
|
public boolean filter(@NonNull final ShutterDto dto) throws PropertyTypeMismatch {
|
||||||
|
if (positionNull != null && positionNull != (dto.getPositionProperty() == null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final Double value = dto.getPositionValue();
|
||||||
|
if (positionMin != null && value != null && positionMin <= value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (positionMax != null && value != null && positionMax >= value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return search(dto.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
src/main/java/de/ph87/home/shutter/ShutterRepository.java
Normal file
15
src/main/java/de/ph87/home/shutter/ShutterRepository.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package de.ph87.home.shutter;
|
||||||
|
|
||||||
|
import lombok.NonNull;
|
||||||
|
import org.springframework.data.repository.ListCrudRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface ShutterRepository extends ListCrudRepository<Shutter, String> {
|
||||||
|
|
||||||
|
Optional<Shutter> findByUuidOrSlug(@NonNull String uuid, @NonNull String slug);
|
||||||
|
|
||||||
|
List<Shutter> findAllByPositionPropertyId(@NonNull String propertyId);
|
||||||
|
|
||||||
|
}
|
||||||
95
src/main/java/de/ph87/home/shutter/ShutterService.java
Normal file
95
src/main/java/de/ph87/home/shutter/ShutterService.java
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package de.ph87.home.shutter;
|
||||||
|
|
||||||
|
import de.ph87.home.common.crud.CrudAction;
|
||||||
|
import de.ph87.home.common.crud.EntityNotFound;
|
||||||
|
import de.ph87.home.property.*;
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ShutterService {
|
||||||
|
|
||||||
|
private final PropertyService propertyService;
|
||||||
|
|
||||||
|
private final ShutterRepository shutterRepository;
|
||||||
|
|
||||||
|
private final ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ShutterDto create(@NonNull final String name, @NonNull final String slug, @NonNull final String positionProperty) {
|
||||||
|
return publish(shutterRepository.save(new Shutter(name, slug, positionProperty)), CrudAction.CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPosition(@NonNull final String uuidOrSlug, final double position) throws PropertyNotFound, PropertyNotWritable, PropertyTypeMismatch {
|
||||||
|
log.debug("setPosition: uuidOrSlug={}, position={}", uuidOrSlug, position);
|
||||||
|
final Shutter shutter = getByUuidOrSlug(uuidOrSlug);
|
||||||
|
propertyService.write(shutter.getPositionPropertyId(), position, Double.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ShutterDto getByUuidOrSlugDto(final @NonNull String uuidOrSlug) {
|
||||||
|
return toDto(getByUuidOrSlug(uuidOrSlug));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Shutter getByUuidOrSlug(@NonNull final String uuidOrSlug) {
|
||||||
|
return shutterRepository.findByUuidOrSlug(uuidOrSlug, uuidOrSlug).orElseThrow(() -> new EntityNotFound("uuidOrSlug", uuidOrSlug));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ShutterDto toDto(@NonNull final Shutter shutter) {
|
||||||
|
final PropertyDto<Double> position = propertyService.dtoByIdAndTypeOrNull(shutter.getPositionPropertyId(), Double.class);
|
||||||
|
return new ShutterDto(shutter, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Shutter getByUuid(@NonNull final String uuid) {
|
||||||
|
return shutterRepository.findById(uuid).orElseThrow(() -> new EntityNotFound("uuid", uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public List<ShutterDto> list(@Nullable final ShutterFilter filter) throws PropertyTypeMismatch {
|
||||||
|
final List<ShutterDto> all = shutterRepository.findAll().stream().map(this::toDto).toList();
|
||||||
|
if (filter == null) {
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
final List<ShutterDto> results = new ArrayList<>();
|
||||||
|
for (final ShutterDto dto : all) {
|
||||||
|
if (filter.filter(dto)) {
|
||||||
|
results.add(dto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener(PropertyDto.class)
|
||||||
|
public void onPropertyChange(@NonNull final PropertyDto<?> dto) {
|
||||||
|
shutterRepository.findAllByPositionPropertyId(dto.getId()).forEach(shutter -> publish(shutter, CrudAction.UPDATED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private ShutterDto publish(@NonNull final Shutter shutter, @NonNull final CrudAction action) {
|
||||||
|
final ShutterDto dto = toDto(shutter);
|
||||||
|
log.info("Shutter {}: {}", action, dto);
|
||||||
|
applicationEventPublisher.publishEvent(dto);
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ShutterDto getByUuidDto(@NonNull final String uuid) {
|
||||||
|
return toDto(getByUuid(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user