PropertyListComponent + Scenes
This commit is contained in:
parent
bc8ba1ef8b
commit
b3d5b3cdd2
@ -1 +1 @@
|
||||
Subproject commit 65f72ffc2cf56175b3ca2e51ec7d95228907318a
|
||||
Subproject commit b362ae703f89547339ad5762714b1f6c999429fa
|
||||
@ -2,7 +2,7 @@ import {KeyValuePair} from "./KeyValuePair";
|
||||
|
||||
export interface ISearchService {
|
||||
|
||||
get(id: string, next: (results: KeyValuePair) => void, error: (error: any) => void): void;
|
||||
get(id: number, next: (results: KeyValuePair) => void, error: (error: any) => void): void;
|
||||
|
||||
search(term: string, next: (results: KeyValuePair[]) => void, error: (error: any) => void): void;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
|
||||
import {Property} from "../property/property.service";
|
||||
import {Property} from "../property/Property";
|
||||
|
||||
export abstract class Device {
|
||||
|
||||
@ -56,7 +56,7 @@ export class DeviceSwitch extends Device {
|
||||
}
|
||||
|
||||
updateProperty(property: Property): void {
|
||||
if (this.stateProperty?.name === property.name) {
|
||||
if (this.stateProperty?.id === property.id) {
|
||||
this.stateProperty = property;
|
||||
}
|
||||
}
|
||||
@ -75,7 +75,7 @@ export class DeviceShutter extends Device {
|
||||
}
|
||||
|
||||
updateProperty(property: Property): void {
|
||||
if (this.positionProperty?.name === property.name) {
|
||||
if (this.positionProperty?.id === property.id) {
|
||||
this.positionProperty = property;
|
||||
}
|
||||
}
|
||||
|
||||
42
src/main/angular/src/app/api/property/Property.ts
Normal file
42
src/main/angular/src/app/api/property/Property.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import {validateDateAllowNull, validateNumberAllowNull, validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
|
||||
|
||||
export class Property {
|
||||
|
||||
constructor(
|
||||
public id: number,
|
||||
public name: string,
|
||||
public type: string,
|
||||
public title: string,
|
||||
public value: number | null,
|
||||
public timestamp: Date | null,
|
||||
) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
static fromJsonAllowNull(json: any): Property | null {
|
||||
if (json === undefined || json === null) {
|
||||
return null;
|
||||
}
|
||||
return this.fromJson(json);
|
||||
}
|
||||
|
||||
static fromJson(json: any): Property {
|
||||
return new Property(
|
||||
validateNumberNotNull(json['id']),
|
||||
validateStringNotEmptyNotNull(json['name']),
|
||||
validateStringNotEmptyNotNull(json['type']),
|
||||
validateStringNotEmptyNotNull(json['title']),
|
||||
validateNumberAllowNull(json['value']),
|
||||
validateDateAllowNull(json['timestamp']),
|
||||
);
|
||||
}
|
||||
|
||||
public static trackBy(index: number, item: Property): string {
|
||||
return item.name;
|
||||
}
|
||||
|
||||
public static compareName(a: Property, b: Property): number {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,48 +1,9 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {ApiService, NO_OP} from "../api.service";
|
||||
import {validateDateAllowNull, validateNumberAllowNull, validateStringNotEmptyNotNull} from "../validators";
|
||||
import {ApiService, NO_COMPARE, NO_OP} from "../api.service";
|
||||
import {ISearchService} from "../ISearchService";
|
||||
import {KeyValuePair} from "../KeyValuePair";
|
||||
import {Update} from "../Update";
|
||||
|
||||
export class Property {
|
||||
|
||||
constructor(
|
||||
public name: string,
|
||||
public type: string,
|
||||
public title: string,
|
||||
public value: number | null,
|
||||
public valueTimestamp: Date | null,
|
||||
) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
static fromJsonAllowNull(json: any): Property | null {
|
||||
if (json === undefined || json === null) {
|
||||
return null;
|
||||
}
|
||||
return this.fromJson(json);
|
||||
}
|
||||
|
||||
static fromJson(json: any): Property {
|
||||
return new Property(
|
||||
validateStringNotEmptyNotNull(json['name']),
|
||||
validateStringNotEmptyNotNull(json['type']),
|
||||
validateStringNotEmptyNotNull(json['title']),
|
||||
validateNumberAllowNull(json['value']),
|
||||
validateDateAllowNull(json['valueTimestamp']),
|
||||
);
|
||||
}
|
||||
|
||||
public static trackBy(index: number, item: Property): string {
|
||||
return item.name;
|
||||
}
|
||||
|
||||
public static compareName(a: Property, b: Property): number {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
|
||||
}
|
||||
import {Property} from "./Property";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -55,20 +16,24 @@ export class PropertyService implements ISearchService {
|
||||
// nothing
|
||||
}
|
||||
|
||||
subscribe(next: (device: Update<Property>) => void): void {
|
||||
findAll(next: (list: Property[]) => void, compare: (a: Property, b: Property) => number = NO_COMPARE, error: (error: any) => void = NO_OP): void {
|
||||
this.api.getList("property/findAll", Property.fromJson, compare, next, error);
|
||||
}
|
||||
|
||||
subscribe(next: (property: 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);
|
||||
get(id: number, next: (results: KeyValuePair) => void, error: (error: any) => void): void {
|
||||
this.api.getItem("property/getById/" + id, KeyValuePair.fromJson, next, error);
|
||||
}
|
||||
|
||||
search(term: string, next: (results: KeyValuePair[]) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||
this.api.postReturnList("property/searchLike", term, KeyValuePair.fromJson, next, error);
|
||||
}
|
||||
|
||||
set(property: Property, value: number, next: () => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||
this.api.postReturnNone("property/set", {name: property.name, value: value}, next, error)
|
||||
set(property: Property, key: string, value: any, next: (item: Property) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||
this.api.postReturnItem("property/set/" + property.id + "/" + key, value, Property.fromJson, next, error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
36
src/main/angular/src/app/api/scene/Scene.ts
Normal file
36
src/main/angular/src/app/api/scene/Scene.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import {validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
|
||||
|
||||
export class Scene {
|
||||
|
||||
constructor(
|
||||
public id: number,
|
||||
public number: number,
|
||||
public title: string,
|
||||
) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
static fromJsonAllowNull(json: any): Scene | null {
|
||||
if (json === undefined || json === null) {
|
||||
return null;
|
||||
}
|
||||
return this.fromJson(json);
|
||||
}
|
||||
|
||||
static fromJson(json: any): Scene {
|
||||
return new Scene(
|
||||
validateNumberNotNull(json['id']),
|
||||
validateNumberNotNull(json['number']),
|
||||
validateStringNotEmptyNotNull(json['title']),
|
||||
);
|
||||
}
|
||||
|
||||
public static trackBy(index: number, item: Scene): number {
|
||||
return item.id;
|
||||
}
|
||||
|
||||
public static compareNumber(a: Scene, b: Scene): number {
|
||||
return a.number - b.number;
|
||||
}
|
||||
|
||||
}
|
||||
16
src/main/angular/src/app/api/scene/scene.service.spec.ts
Normal file
16
src/main/angular/src/app/api/scene/scene.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {SceneService} from './scene.service';
|
||||
|
||||
describe('SceneService', () => {
|
||||
let service: SceneService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(SceneService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
39
src/main/angular/src/app/api/scene/scene.service.ts
Normal file
39
src/main/angular/src/app/api/scene/scene.service.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {ApiService, NO_COMPARE, NO_OP} from "../api.service";
|
||||
import {ISearchService} from "../ISearchService";
|
||||
import {KeyValuePair} from "../KeyValuePair";
|
||||
import {Update} from "../Update";
|
||||
import {Scene} from "./Scene";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SceneService implements ISearchService {
|
||||
|
||||
constructor(
|
||||
readonly api: ApiService,
|
||||
) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
findAll(next: (list: Scene[]) => void, compare: (a: Scene, b: Scene) => number = NO_COMPARE, error: (error: any) => void = NO_OP): void {
|
||||
this.api.getList("scene/findAll", Scene.fromJson, compare, next, error);
|
||||
}
|
||||
|
||||
subscribe(next: (scene: Update<Scene>) => void): void {
|
||||
this.api.subscribe("SceneDto", Scene.fromJson, next);
|
||||
}
|
||||
|
||||
get(id: number, next: (results: KeyValuePair) => void, error: (error: any) => void): void {
|
||||
this.api.getItem("scene/getById/" + id, KeyValuePair.fromJson, next, error);
|
||||
}
|
||||
|
||||
search(term: string, next: (results: KeyValuePair[]) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||
this.api.postReturnList("scene/searchLike", term, KeyValuePair.fromJson, next, error);
|
||||
}
|
||||
|
||||
set(scene: Scene, key: string, value: any, next: (item: Scene) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||
this.api.postReturnItem("scene/set/" + scene.id + "/" + key, value, Scene.fromJson, next, error);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import {validateBooleanNotNull, validateListOrEmpty, validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
|
||||
import {ScheduleEntry} from "./entry/ScheduleEntry";
|
||||
import {Property} from "../property/property.service";
|
||||
import {Property} from "../property/Property";
|
||||
|
||||
export class Schedule {
|
||||
|
||||
|
||||
@ -4,10 +4,13 @@ import {ScheduleListComponent} from "./pages/schedule-list/schedule-list.compone
|
||||
import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
||||
import {DeviceListComponent} from "./pages/device-list/device-list.component";
|
||||
import {DeviceComponent} from "./pages/device/device.component";
|
||||
import {PropertyListComponent} from "./pages/property-list/property-list.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{path: 'Device', component: DeviceComponent},
|
||||
{path: 'DeviceList', component: DeviceListComponent},
|
||||
// {path: 'Property', component: PropertyComponent},
|
||||
{path: 'PropertyList', component: PropertyListComponent},
|
||||
{path: 'Schedule', component: ScheduleComponent},
|
||||
{path: 'ScheduleList', component: ScheduleListComponent},
|
||||
{path: '**', redirectTo: '/ScheduleList'},
|
||||
|
||||
@ -4,6 +4,10 @@
|
||||
Zeitpläne
|
||||
</div>
|
||||
|
||||
<div class="item" routerLink="/PropertyList" routerLinkActive="itemActive">
|
||||
Eigenschaften
|
||||
</div>
|
||||
|
||||
<div class="item" routerLink="/DeviceList" routerLinkActive="itemActive">
|
||||
Geräte
|
||||
</div>
|
||||
|
||||
@ -13,6 +13,7 @@ import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
||||
import {SearchComponent} from './shared/search/search.component';
|
||||
import {DeviceListComponent} from './pages/device-list/device-list.component';
|
||||
import {DeviceComponent} from './pages/device/device.component';
|
||||
import {PropertyListComponent} from './pages/property-list/property-list.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -24,6 +25,7 @@ import {DeviceComponent} from './pages/device/device.component';
|
||||
SearchComponent,
|
||||
DeviceListComponent,
|
||||
DeviceComponent,
|
||||
PropertyListComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@ -31,28 +31,20 @@ export class DeviceListComponent implements OnInit {
|
||||
this.deviceService.findAll(devices => this.devices = devices);
|
||||
}
|
||||
|
||||
asDeviceSwitch(device: Device): DeviceSwitch {
|
||||
return device as DeviceSwitch;
|
||||
}
|
||||
|
||||
asDeviceShutter(device: Device): DeviceShutter {
|
||||
return device as DeviceShutter;
|
||||
}
|
||||
|
||||
setSwitchState(d: Device, value: boolean): void {
|
||||
const device: DeviceSwitch = d as DeviceSwitch;
|
||||
if (!device.stateProperty) {
|
||||
throw new Error("Property 'setState' not set for: " + device);
|
||||
throw new Error("Property 'stateProperty' not set for: " + device);
|
||||
}
|
||||
this.propertyService.set(device.stateProperty, value ? 1 : 0);
|
||||
this.propertyService.set(device.stateProperty, "value", value ? 1 : 0);
|
||||
}
|
||||
|
||||
setShutterPosition(d: Device, value: number): void {
|
||||
const device: DeviceShutter = d as DeviceShutter;
|
||||
if (!device.positionProperty) {
|
||||
throw new Error("Property 'setPosition' not set for: " + device);
|
||||
throw new Error("Property 'positionProperty' not set for: " + device);
|
||||
}
|
||||
this.propertyService.set(device.positionProperty, value);
|
||||
this.propertyService.set(device.positionProperty, "value", value);
|
||||
}
|
||||
|
||||
create(): void {
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
<tr>
|
||||
<th>Eigenschaft</th>
|
||||
<td>
|
||||
<app-search [searchService]="propertyService" [initial]="deviceSwitch.stateProperty?.name" [showKey]="true" (valueChange)="setDeviceSwitch('stateProperty', $event)"></app-search>
|
||||
<app-search [searchService]="propertyService" [initial]="deviceSwitch.stateProperty?.id" [showKey]="true" (valueChange)="setDeviceSwitch('stateProperty', $event)"></app-search>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -30,7 +30,7 @@
|
||||
<tr>
|
||||
<th>Eigenschaft</th>
|
||||
<td>
|
||||
<app-search [searchService]="propertyService" [initial]="deviceShutter.positionProperty?.name" [showKey]="true" (valueChange)="setDeviceShutter('positionProperty', $event)"></app-search>
|
||||
<app-search [searchService]="propertyService" [initial]="deviceShutter.positionProperty?.id" [showKey]="true" (valueChange)="setDeviceShutter('positionProperty', $event)"></app-search>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
<table>
|
||||
<tr>
|
||||
<th>Bezeichnung</th>
|
||||
<th>Name</th>
|
||||
<th>Typ</th>
|
||||
<th>Wert</th>
|
||||
<th>Zeitstempel</th>
|
||||
</tr>
|
||||
<tr *ngFor="let property of properties">
|
||||
<td>
|
||||
<app-edit-field [initial]="property.title" (valueChange)="setProperty(property, 'title', $event)"></app-edit-field>
|
||||
</td>
|
||||
<td>
|
||||
<app-edit-field [initial]="property.name" (valueChange)="setProperty(property, 'name', $event)"></app-edit-field>
|
||||
</td>
|
||||
<td>
|
||||
<select [(ngModel)]="property.type" (ngModelChange)="setProperty(property, 'type',property.type)">
|
||||
<option value="SWITCH">Schalter</option>
|
||||
<option value="SHUTTER">Rollladen</option>
|
||||
<option value="BRIGHTNESS_PERCENT">Helligkeit [%]</option>
|
||||
<option value="COLOR_TEMPERATURE">Farbtermperatur</option>
|
||||
<option value="LUX">Helligkeit [lux]</option>
|
||||
<option value="SCENE">Szene</option>
|
||||
</select>
|
||||
</td>
|
||||
<ng-container *ngIf="property.value !== null">
|
||||
<td *ngIf="property.type === 'SWITCH'" class="boolean" [class.true]="property.value" [class.false]="!property.value" (click)="setProperty(property, 'value', property.value > 0 ? 0 : 1)">
|
||||
{{property.value ? "An" : "Aus"}}
|
||||
</td>
|
||||
<td *ngIf="property.type === 'SHUTTER'" class="number" [class.true]="property.value === 0" [class.false]="property.value === 100" [class.tristate]="0 < property.value && property.value < 100">
|
||||
{{property.value}} %
|
||||
</td>
|
||||
<td *ngIf="property.type === 'BRIGHTNESS_PERCENT'" class="number">
|
||||
{{property.value}} %
|
||||
</td>
|
||||
<td *ngIf="property.type === 'COLOR_TEMPERATURE'" class="number">
|
||||
{{property.value}} K
|
||||
</td>
|
||||
<td *ngIf="property.type === 'LUX'" class="number">
|
||||
{{property.value | number:'0.0-0'}} lux
|
||||
</td>
|
||||
<td *ngIf="property.type === 'SCENE'">
|
||||
{{findScene(property)?.title || "Unbekannt: " + property.value}}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="property.value === null">
|
||||
<td class="empty">
|
||||
-LEER-
|
||||
</td>
|
||||
</ng-container>
|
||||
<td>{{property.timestamp | date:'yyyy-MM-dd HH:mm:ss'}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -0,0 +1,25 @@
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {PropertyListComponent} from './property-list.component';
|
||||
|
||||
describe('PropertyListComponent', () => {
|
||||
let component: PropertyListComponent;
|
||||
let fixture: ComponentFixture<PropertyListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [PropertyListComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PropertyListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,71 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Property} from "../../api/property/Property";
|
||||
import {PropertyService} from "../../api/property/property.service";
|
||||
import {Scene} from "../../api/scene/Scene";
|
||||
import {SceneService} from "../../api/scene/scene.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-property-list',
|
||||
templateUrl: './property-list.component.html',
|
||||
styleUrls: ['./property-list.component.less']
|
||||
})
|
||||
export class PropertyListComponent implements OnInit {
|
||||
|
||||
properties: Property[] = [];
|
||||
|
||||
scenes: Scene[] = [];
|
||||
|
||||
constructor(
|
||||
readonly propertyService: PropertyService,
|
||||
readonly sceneService: SceneService,
|
||||
) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.propertyService.findAll(properties => this.properties = properties, Property.compareName);
|
||||
this.propertyService.subscribe(update => this.updateProperty(update.payload, update.existing));
|
||||
|
||||
this.sceneService.findAll(scenes => this.scenes = scenes, Scene.compareNumber);
|
||||
this.sceneService.subscribe(update => this.updateScene(update.payload, update.existing));
|
||||
}
|
||||
|
||||
setProperty(property: Property, key: string, value: any): void {
|
||||
this.propertyService.set(property, key, value, property => this.updateProperty(property, true));
|
||||
}
|
||||
|
||||
setScene(scene: Scene, key: string, value: any): void {
|
||||
this.sceneService.set(scene, key, value, scene => this.updateScene(scene, true));
|
||||
}
|
||||
|
||||
private updateProperty(property: Property, existing: boolean): void {
|
||||
const index: number = this.properties.findIndex(p => p.id === property.id);
|
||||
if (index >= 0) {
|
||||
if (existing) {
|
||||
this.properties[index] = property;
|
||||
} else {
|
||||
this.properties.slice(index, 1);
|
||||
}
|
||||
} else if (existing) {
|
||||
this.properties.push(property);
|
||||
}
|
||||
}
|
||||
|
||||
private updateScene(scene: Scene, existing: boolean): void {
|
||||
const index: number = this.scenes.findIndex(p => p.id === scene.id);
|
||||
if (index >= 0) {
|
||||
if (existing) {
|
||||
this.scenes[index] = scene;
|
||||
} else {
|
||||
this.scenes.slice(index, 1);
|
||||
}
|
||||
} else if (existing) {
|
||||
this.scenes.push(scene);
|
||||
}
|
||||
}
|
||||
|
||||
findScene(property: Property): Scene | undefined {
|
||||
return this.scenes.find(s => s.id === property.value);
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,56 +9,3 @@ select {
|
||||
th {
|
||||
background-color: lightblue;
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: gray;
|
||||
background-color: #DDDDDD;
|
||||
}
|
||||
|
||||
.boolean {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.true {
|
||||
background-color: palegreen;
|
||||
}
|
||||
|
||||
.false {
|
||||
background-color: indianred;
|
||||
}
|
||||
|
||||
.number {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.full {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.first {
|
||||
border-right-width: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.middle {
|
||||
border-right-width: 0;
|
||||
border-left-width: 0;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.last {
|
||||
border-left-width: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.delete {
|
||||
color: darkred;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
* {
|
||||
background-color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<app-edit-field [initial]="schedule.title" (valueChange)="set(null, 'title', $event)"></app-edit-field>
|
||||
</td>
|
||||
<td colspan="5">
|
||||
<app-search [searchService]="propertyService" [initial]="schedule.property?.name" (valueChange)="set(null, 'propertyName', $event)"></app-search>
|
||||
<app-search [searchService]="propertyService" [initial]="schedule.property?.id" (valueChange)="set(null, 'propertyName', $event)"></app-search>
|
||||
</td>
|
||||
<td colspan="9">
|
||||
{{schedule.property?.type}}
|
||||
@ -144,7 +144,7 @@
|
||||
<option [ngValue]="100">100% Geschlossen</option>
|
||||
</select>
|
||||
</td>
|
||||
<td *ngIf="schedule.property?.type === 'BRIGHTNESS'" [class.true]="entry.value" [class.false]="!entry.value" [class.tristate]="0 < entry.value && entry.value < 100">
|
||||
<td *ngIf="schedule.property?.type === 'BRIGHTNESS_PERCENT'" [class.true]="entry.value" [class.false]="!entry.value" [class.tristate]="0 < entry.value && entry.value < 100">
|
||||
<select [(ngModel)]="entry.value" (ngModelChange)="set(entry, 'value', entry.value)">
|
||||
<option *ngFor="let _ of [].constructor(21); let value = index" [ngValue]="value * 5">{{value * 5}}%</option>
|
||||
</select>
|
||||
@ -161,7 +161,7 @@
|
||||
</td>
|
||||
<td *ngIf="schedule.property?.type === 'SCENE'">
|
||||
<select [(ngModel)]="entry.value" (ngModelChange)="set(entry, 'value', entry.value)">
|
||||
<option *ngFor="let _ of [].constructor(64); let value = index" [ngValue]="value + 1">{{value + 1}}</option>
|
||||
<option *ngFor="let scene of scenes" [ngValue]="scene.number">#{{scene.number | number:'2.0-0'}} {{scene.title}}</option>
|
||||
</select>
|
||||
</td>
|
||||
|
||||
|
||||
@ -17,60 +17,3 @@ tr.header {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: gray;
|
||||
background-color: #DDDDDD;
|
||||
}
|
||||
|
||||
.boolean {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.true {
|
||||
background-color: palegreen;
|
||||
}
|
||||
|
||||
.tristate {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.false {
|
||||
background-color: indianred;
|
||||
}
|
||||
|
||||
.number {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.full {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.first {
|
||||
border-right-width: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.middle {
|
||||
border-right-width: 0;
|
||||
border-left-width: 0;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.last {
|
||||
border-left-width: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.delete {
|
||||
color: darkred;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
* {
|
||||
background-color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@ import {faCheckCircle, faCircle, faTimesCircle} from '@fortawesome/free-regular-
|
||||
import {ActivatedRoute} from "@angular/router";
|
||||
import {DataService} from "../../data.service";
|
||||
import {PropertyService} from "../../api/property/property.service";
|
||||
import {Scene} from "../../api/scene/Scene";
|
||||
import {SceneService} from "../../api/scene/scene.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-schedule',
|
||||
@ -22,18 +24,22 @@ export class ScheduleComponent implements OnInit {
|
||||
|
||||
schedule!: Schedule;
|
||||
|
||||
scenes: Scene[] = [];
|
||||
|
||||
constructor(
|
||||
readonly activatedRoute: ActivatedRoute,
|
||||
readonly scheduleService: ScheduleService,
|
||||
readonly scheduleEntryService: ScheduleEntryService,
|
||||
readonly dataService: DataService,
|
||||
readonly propertyService: PropertyService,
|
||||
readonly sceneService: SceneService,
|
||||
readonly dataService: DataService,
|
||||
) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.dataService.schedule = undefined;
|
||||
this.sceneService.findAll(scenes => this.scenes = scenes);
|
||||
this.activatedRoute.params.subscribe(params => this.scheduleService.getById(params.id, schedule => this.setSchedule(schedule)));
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
.empty {
|
||||
color: gray;
|
||||
}
|
||||
@ -1,37 +1,22 @@
|
||||
<div
|
||||
*ngIf="!searching"
|
||||
(click)="start()"
|
||||
[class.empty]="!selected"
|
||||
>
|
||||
|
||||
<div *ngIf="!searching" (click)="start()" [class.empty]="!selected">
|
||||
<ng-container *ngIf="selected">
|
||||
{{selected.value}}
|
||||
<ng-container *ngIf="showKey">
|
||||
[{{selected.key}}]
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="!selected">
|
||||
-LEER-
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
|
||||
<input
|
||||
#input
|
||||
type="text"
|
||||
*ngIf="searching"
|
||||
[(ngModel)]="term"
|
||||
(ngModelChange)="changed()"
|
||||
(keydown)="inputKeyPress($event)"
|
||||
(blur)="blur()"
|
||||
>
|
||||
<input #input type="text" *ngIf="searching" [(ngModel)]="term" (ngModelChange)="changed()" (keydown)="inputKeyPress($event)" (blur)="blur()">
|
||||
|
||||
<div #resultList *ngIf="searching" class="resultList">
|
||||
<div *ngIf="allowEmpty" class="result" (click)="select(undefined)">
|
||||
-
|
||||
</div>
|
||||
<div *ngIf="selected" class="result selected" (click)="select(undefined)">
|
||||
<div *ngIf="selected" class="result selected" (click)="select(selected)">
|
||||
{{selected.value}}
|
||||
<ng-container *ngIf="showKey">
|
||||
[{{selected.key}}]
|
||||
|
||||
@ -1,7 +1,3 @@
|
||||
.empty {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.selected {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid black;
|
||||
|
||||
@ -24,7 +24,7 @@ export class SearchComponent<T> implements OnInit {
|
||||
searchService!: ISearchService;
|
||||
|
||||
@Input()
|
||||
initial?: string;
|
||||
initial?: number;
|
||||
|
||||
@Input()
|
||||
showKey: boolean = false;
|
||||
@ -65,7 +65,7 @@ export class SearchComponent<T> implements OnInit {
|
||||
}
|
||||
|
||||
start(): void {
|
||||
this.term = this.initial || "";
|
||||
this.term = this.selected?.value || "";
|
||||
if (this.resultList && this.input) {
|
||||
this.resultList.style.left = this.input.style.left;
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<title>Angular</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=scene-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@ -35,3 +35,61 @@ table.vertical {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: gray;
|
||||
background-color: #DDDDDD;
|
||||
}
|
||||
|
||||
.boolean {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.true {
|
||||
background-color: palegreen;
|
||||
}
|
||||
|
||||
.tristate {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.false {
|
||||
background-color: indianred;
|
||||
}
|
||||
|
||||
.number {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.full {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.first {
|
||||
border-right-width: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.middle {
|
||||
border-right-width: 0;
|
||||
border-left-width: 0;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.last {
|
||||
border-left-width: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.delete {
|
||||
color: darkred;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
* {
|
||||
background-color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,8 @@ import de.ph87.homeautomation.knx.group.KnxGroupReadService;
|
||||
import de.ph87.homeautomation.property.Property;
|
||||
import de.ph87.homeautomation.property.PropertyRepository;
|
||||
import de.ph87.homeautomation.property.PropertyType;
|
||||
import de.ph87.homeautomation.scene.SceneRepository;
|
||||
import de.ph87.homeautomation.scene.SceneWriteService;
|
||||
import de.ph87.homeautomation.schedule.Schedule;
|
||||
import de.ph87.homeautomation.schedule.ScheduleRepository;
|
||||
import de.ph87.homeautomation.schedule.entry.ScheduleEntry;
|
||||
@ -41,6 +43,10 @@ public class DemoDataService {
|
||||
|
||||
private final KnxGroupReadService knxGroupReadService;
|
||||
|
||||
private final SceneRepository sceneRepository;
|
||||
|
||||
private final SceneWriteService sceneWriteService;
|
||||
|
||||
public void insertDemoData() {
|
||||
final Property ambiente_eg = createProperty("ambiente.eg", PropertyType.SWITCH, "Ambiente EG", knx(0, 3, 81), knx(0, 3, 80));
|
||||
final Property ambiente_og = createProperty("ambiente.og", PropertyType.SWITCH, "Ambiente OG", knx(0, 6, 2), knx(0, 6, 3));
|
||||
@ -62,6 +68,13 @@ public class DemoDataService {
|
||||
deviceWriteService.createDeviceShutter("Flur Rollladen", flur_og_rollladen);
|
||||
}
|
||||
|
||||
if (sceneRepository.count() == 0) {
|
||||
sceneWriteService.create(1, "Alles AUS");
|
||||
sceneWriteService.create(2, "Nachtlicht");
|
||||
sceneWriteService.create(30, "Dekoration AUS");
|
||||
sceneWriteService.create(31, "Dekoration AN");
|
||||
}
|
||||
|
||||
if (scheduleRepository.count() == 0) {
|
||||
final Schedule scheduleEgFlurLicht = createSchedule(true, "EG Flur Licht", flur_eg_licht);
|
||||
createTime(scheduleEgFlurLicht, true, 1, 0, 0, MIN30, true);
|
||||
|
||||
@ -22,6 +22,6 @@ public abstract class Channel {
|
||||
|
||||
public abstract Double getValue();
|
||||
|
||||
public abstract ZonedDateTime getValueTimestamp();
|
||||
public abstract ZonedDateTime getTimestamp();
|
||||
|
||||
}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
package de.ph87.homeautomation.device;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DeviceSetDto {
|
||||
|
||||
private long id;
|
||||
|
||||
private String property;
|
||||
|
||||
private double value;
|
||||
|
||||
}
|
||||
@ -5,7 +5,6 @@ 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.PropertyWriteService;
|
||||
import de.ph87.homeautomation.schedule.ScheduleWriteService;
|
||||
import de.ph87.homeautomation.web.BadRequestException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -23,8 +22,6 @@ public class DeviceWriteService {
|
||||
|
||||
private final DeviceRepository deviceRepository;
|
||||
|
||||
private final PropertyWriteService propertyWriteService;
|
||||
|
||||
private final DeviceReadService deviceReadService;
|
||||
|
||||
public DeviceDto createDeviceSwitch(final String title, final Property stateProperty) {
|
||||
|
||||
@ -42,7 +42,7 @@ public class KnxGroup extends Channel {
|
||||
|
||||
private Double value;
|
||||
|
||||
private ZonedDateTime valueTimestamp;
|
||||
private ZonedDateTime timestamp;
|
||||
|
||||
@Embedded
|
||||
private KnxGroupLinkInfo read = new KnxGroupLinkInfo();
|
||||
|
||||
@ -72,7 +72,7 @@ public class KnxGroupWriteService {
|
||||
// TODO implement all DPTXlator...
|
||||
if (valueOptional.isPresent()) {
|
||||
knxGroup.setValue(valueOptional.get());
|
||||
knxGroup.setValueTimestamp(ZonedDateTime.now());
|
||||
knxGroup.setTimestamp(ZonedDateTime.now());
|
||||
log.debug("KnxGroup updated: {}", knxGroup);
|
||||
applicationEventPublisher.publishEvent(new ChannelChangedEvent(knxGroup));
|
||||
} else {
|
||||
|
||||
@ -17,15 +17,30 @@ public class PropertyController implements ISearchController {
|
||||
|
||||
private final PropertyReadService propertyReadService;
|
||||
|
||||
@PostMapping("set")
|
||||
public void set(@RequestBody final PropertySetDto dto) {
|
||||
propertyWriteService.write(dto.getName(), dto.getValue());
|
||||
@GetMapping("findAll")
|
||||
public List<PropertyDto> findAll() {
|
||||
return propertyReadService.findAllDto();
|
||||
}
|
||||
|
||||
@PostMapping("set/{id}/type")
|
||||
public PropertyDto setPropertyType(@PathVariable final long id, @RequestBody final String propertyType) {
|
||||
return propertyWriteService.set(id, Property::setType, PropertyType.valueOf(propertyType));
|
||||
}
|
||||
|
||||
@PostMapping("set/{id}/name")
|
||||
public PropertyDto setPropertyName(@PathVariable final long id, @RequestBody final String propertyName) {
|
||||
return propertyWriteService.set(id, Property::setName, propertyName);
|
||||
}
|
||||
|
||||
@PostMapping("set/{id}/value")
|
||||
public PropertyDto setValue(@PathVariable final long id, @RequestBody final double value) {
|
||||
return propertyWriteService.set(id, propertyWriteService::write, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping("getById")
|
||||
public KeyValuePair getById(@RequestBody final String name) {
|
||||
final PropertyDto propertyDto = propertyReadService.getDtoByName(name);
|
||||
@GetMapping("getById/{id}")
|
||||
public KeyValuePair getById(@PathVariable final long id) {
|
||||
final PropertyDto propertyDto = propertyReadService.getDtoById(id);
|
||||
return toKeyValuePair(propertyDto);
|
||||
}
|
||||
|
||||
@ -39,9 +54,4 @@ public class PropertyController implements ISearchController {
|
||||
return new KeyValuePair(propertyDto.getName(), propertyDto.getTitle());
|
||||
}
|
||||
|
||||
@PostMapping("set/{id}/type")
|
||||
public PropertyDto setPropertyType(@PathVariable final long id, @RequestBody final String propertyType) {
|
||||
return propertyWriteService.set(id, Property::setType, PropertyType.valueOf(propertyType));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ public final class PropertyDto implements Serializable {
|
||||
|
||||
private final Double value;
|
||||
|
||||
private final ZonedDateTime valueTimestamp;
|
||||
private final ZonedDateTime timestamp;
|
||||
|
||||
public PropertyDto(final Property property) {
|
||||
this.id = property.getId();
|
||||
@ -26,7 +26,7 @@ public final class PropertyDto implements Serializable {
|
||||
this.name = property.getName();
|
||||
this.title = property.getTitle();
|
||||
this.value = property.getValue();
|
||||
this.valueTimestamp = property.getTimestamp();
|
||||
this.timestamp = property.getTimestamp();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -30,12 +30,16 @@ public class PropertyReadService {
|
||||
return propertyRepository.findAllByNameLike(like).stream().map(propertyMapper::toDto).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public PropertyDto getDtoByName(final String name) {
|
||||
return propertyMapper.toDto(getByName(name));
|
||||
public PropertyDto getDtoById(final long id) {
|
||||
return propertyMapper.toDto(getById(id));
|
||||
}
|
||||
|
||||
public Property getById(final long id) {
|
||||
return propertyRepository.findById(id).orElseThrow(RuntimeException::new);
|
||||
}
|
||||
|
||||
public List<PropertyDto> findAllDto() {
|
||||
return propertyRepository.findAll().stream().map(propertyMapper::toDto).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ public interface PropertyRepository extends CrudRepository<Property, Long> {
|
||||
|
||||
List<Property> findAllByReadChannel_Id(long readChannelId);
|
||||
|
||||
List<Property> findAll();
|
||||
|
||||
List<Property> findAllByNameLike(final String like);
|
||||
|
||||
boolean existsByName(String name);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
package de.ph87.homeautomation.property;
|
||||
|
||||
public enum PropertyType {
|
||||
SWITCH, SHUTTER, BRIGHTNESS, COLOR_TEMPERATURE, LUX, SCENE
|
||||
SWITCH, SHUTTER, BRIGHTNESS_PERCENT, COLOR_TEMPERATURE, LUX, SCENE
|
||||
}
|
||||
|
||||
@ -43,8 +43,8 @@ public class PropertyWriteService {
|
||||
propertyReadService.findAllByReadChannel_Id(event.getChannelId())
|
||||
.forEach(property -> {
|
||||
property.setValue(property.getReadChannel().getValue());
|
||||
property.setTimestamp(property.getReadChannel().getValueTimestamp());
|
||||
log.debug("Updated Property from Channel: {}", property);
|
||||
property.setTimestamp(property.getReadChannel().getTimestamp());
|
||||
log.debug("Updated Property from Channel: {}", property);
|
||||
webSocketService.send(propertyMapper.toDto(property), true);
|
||||
}
|
||||
);
|
||||
|
||||
33
src/main/java/de/ph87/homeautomation/scene/Scene.java
Normal file
33
src/main/java/de/ph87/homeautomation/scene/Scene.java
Normal file
@ -0,0 +1,33 @@
|
||||
package de.ph87.homeautomation.scene;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public class Scene {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
@Setter(AccessLevel.NONE)
|
||||
private long id;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private int number;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private String title;
|
||||
|
||||
public Scene(final int number, final String title) {
|
||||
this.number = number;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package de.ph87.homeautomation.scene;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("scene")
|
||||
@RequiredArgsConstructor
|
||||
public class SceneController {
|
||||
|
||||
private final SceneReadService sceneReadService;
|
||||
|
||||
private final SceneWriteService sceneWriteService;
|
||||
|
||||
@GetMapping("findAll")
|
||||
public List<SceneDto> findAll() {
|
||||
return sceneReadService.findAll();
|
||||
}
|
||||
|
||||
@GetMapping("getById/{id}")
|
||||
public SceneDto getById(@PathVariable final long id) {
|
||||
return sceneReadService.getDtoById(id);
|
||||
}
|
||||
|
||||
@PostMapping("create")
|
||||
public SceneDto create(@RequestBody final int number) {
|
||||
return sceneWriteService.create(number, null);
|
||||
}
|
||||
|
||||
@GetMapping("delete/{id}")
|
||||
public void delete(@PathVariable final long id) {
|
||||
sceneWriteService.delete(id);
|
||||
}
|
||||
|
||||
@PostMapping("set/{id}/title")
|
||||
public SceneDto setName(@PathVariable final long id, @RequestBody final String title) {
|
||||
return sceneWriteService.set(id, Scene::setTitle, title);
|
||||
}
|
||||
|
||||
}
|
||||
20
src/main/java/de/ph87/homeautomation/scene/SceneDto.java
Normal file
20
src/main/java/de/ph87/homeautomation/scene/SceneDto.java
Normal file
@ -0,0 +1,20 @@
|
||||
package de.ph87.homeautomation.scene;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class SceneDto {
|
||||
|
||||
public final long id;
|
||||
|
||||
public final int number;
|
||||
|
||||
public final String title;
|
||||
|
||||
public SceneDto(final Scene scene) {
|
||||
this.id = scene.getId();
|
||||
this.number = scene.getNumber();
|
||||
this.title = scene.getTitle();
|
||||
}
|
||||
|
||||
}
|
||||
12
src/main/java/de/ph87/homeautomation/scene/SceneMapper.java
Normal file
12
src/main/java/de/ph87/homeautomation/scene/SceneMapper.java
Normal file
@ -0,0 +1,12 @@
|
||||
package de.ph87.homeautomation.scene;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SceneMapper {
|
||||
|
||||
public SceneDto toDto(final Scene scene) {
|
||||
return new SceneDto(scene);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package de.ph87.homeautomation.scene;
|
||||
|
||||
import de.ph87.homeautomation.web.NotFoundException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class SceneReadService {
|
||||
|
||||
private final SceneRepository sceneRepository;
|
||||
|
||||
private final SceneMapper sceneMapper;
|
||||
|
||||
public List<SceneDto> findAll() {
|
||||
return sceneRepository.findAll().stream().map(sceneMapper::toDto).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Scene getById(final long id) {
|
||||
return sceneRepository.findById(id).orElseThrow(() -> new NotFoundException("Scene.id=%d", id));
|
||||
}
|
||||
|
||||
public SceneDto getDtoById(final long id) {
|
||||
return sceneMapper.toDto(getById(id));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package de.ph87.homeautomation.scene;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface SceneRepository extends CrudRepository<Scene, Long> {
|
||||
|
||||
List<Scene> findAll();
|
||||
|
||||
boolean existsByTitle(String title);
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package de.ph87.homeautomation.scene;
|
||||
|
||||
import de.ph87.homeautomation.schedule.ScheduleWriteService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static de.ph87.homeautomation.shared.Helpers.orElseGet;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class SceneWriteService {
|
||||
|
||||
private final SceneRepository sceneRepository;
|
||||
|
||||
private final SceneReadService sceneReadService;
|
||||
|
||||
private final SceneMapper sceneMapper;
|
||||
|
||||
public <T> SceneDto set(final long id, final BiConsumer<Scene, T> setter, final T value) {
|
||||
final Scene scene = sceneReadService.getById(id);
|
||||
setter.accept(scene, value);
|
||||
return sceneMapper.toDto(scene);
|
||||
}
|
||||
|
||||
public void delete(final long id) {
|
||||
sceneRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public SceneDto create(final int number, final String title) {
|
||||
return sceneMapper.toDto(sceneRepository.save(new Scene(number, orElseGet(title, this::generateUnusedTitle))));
|
||||
}
|
||||
|
||||
private String generateUnusedTitle() {
|
||||
int index = 0;
|
||||
String title = null;
|
||||
while (title == null || sceneRepository.existsByTitle(title)) {
|
||||
title = ScheduleWriteService.NAME_PREFIX + ++index;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,9 +3,17 @@ package de.ph87.homeautomation.shared;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Helpers {
|
||||
|
||||
public static <T> T orElseGet(final T value, final Supplier<T> orElseGet) {
|
||||
if (value == null) {
|
||||
return orElseGet.get();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static <T, U> U mapIfNotNull(final T value, final Function<T, U> map) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
|
||||
@ -4,7 +4,7 @@ import java.util.List;
|
||||
|
||||
public interface ISearchController {
|
||||
|
||||
KeyValuePair getById(final String id);
|
||||
KeyValuePair getById(final long id);
|
||||
|
||||
List<KeyValuePair> searchLike(final String term);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user