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 {
|
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;
|
search(term: string, next: (results: KeyValuePair[]) => void, error: (error: any) => void): void;
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
|
import {validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
|
||||||
import {Property} from "../property/property.service";
|
import {Property} from "../property/Property";
|
||||||
|
|
||||||
export abstract class Device {
|
export abstract class Device {
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ export class DeviceSwitch extends Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateProperty(property: Property): void {
|
updateProperty(property: Property): void {
|
||||||
if (this.stateProperty?.name === property.name) {
|
if (this.stateProperty?.id === property.id) {
|
||||||
this.stateProperty = property;
|
this.stateProperty = property;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ export class DeviceShutter extends Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateProperty(property: Property): void {
|
updateProperty(property: Property): void {
|
||||||
if (this.positionProperty?.name === property.name) {
|
if (this.positionProperty?.id === property.id) {
|
||||||
this.positionProperty = property;
|
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 {Injectable} from '@angular/core';
|
||||||
import {ApiService, NO_OP} from "../api.service";
|
import {ApiService, NO_COMPARE, NO_OP} from "../api.service";
|
||||||
import {validateDateAllowNull, validateNumberAllowNull, validateStringNotEmptyNotNull} from "../validators";
|
|
||||||
import {ISearchService} from "../ISearchService";
|
import {ISearchService} from "../ISearchService";
|
||||||
import {KeyValuePair} from "../KeyValuePair";
|
import {KeyValuePair} from "../KeyValuePair";
|
||||||
import {Update} from "../Update";
|
import {Update} from "../Update";
|
||||||
|
import {Property} from "./Property";
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -55,20 +16,24 @@ export class PropertyService implements ISearchService {
|
|||||||
// nothing
|
// 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);
|
this.api.subscribe("PropertyDto", Property.fromJson, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(id: string, next: (results: KeyValuePair) => void, error: (error: any) => void): void {
|
get(id: number, next: (results: KeyValuePair) => void, error: (error: any) => void): void {
|
||||||
this.api.postReturnItem("property/getById", id, KeyValuePair.fromJson, next, error);
|
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 {
|
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);
|
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 {
|
set(property: Property, key: string, value: any, next: (item: Property) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnNone("property/set", {name: property.name, value: value}, next, error)
|
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 {validateBooleanNotNull, validateListOrEmpty, validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
|
||||||
import {ScheduleEntry} from "./entry/ScheduleEntry";
|
import {ScheduleEntry} from "./entry/ScheduleEntry";
|
||||||
import {Property} from "../property/property.service";
|
import {Property} from "../property/Property";
|
||||||
|
|
||||||
export class Schedule {
|
export class Schedule {
|
||||||
|
|
||||||
|
|||||||
@ -4,10 +4,13 @@ import {ScheduleListComponent} from "./pages/schedule-list/schedule-list.compone
|
|||||||
import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
||||||
import {DeviceListComponent} from "./pages/device-list/device-list.component";
|
import {DeviceListComponent} from "./pages/device-list/device-list.component";
|
||||||
import {DeviceComponent} from "./pages/device/device.component";
|
import {DeviceComponent} from "./pages/device/device.component";
|
||||||
|
import {PropertyListComponent} from "./pages/property-list/property-list.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: 'Device', component: DeviceComponent},
|
{path: 'Device', component: DeviceComponent},
|
||||||
{path: 'DeviceList', component: DeviceListComponent},
|
{path: 'DeviceList', component: DeviceListComponent},
|
||||||
|
// {path: 'Property', component: PropertyComponent},
|
||||||
|
{path: 'PropertyList', component: PropertyListComponent},
|
||||||
{path: 'Schedule', component: ScheduleComponent},
|
{path: 'Schedule', component: ScheduleComponent},
|
||||||
{path: 'ScheduleList', component: ScheduleListComponent},
|
{path: 'ScheduleList', component: ScheduleListComponent},
|
||||||
{path: '**', redirectTo: '/ScheduleList'},
|
{path: '**', redirectTo: '/ScheduleList'},
|
||||||
|
|||||||
@ -4,6 +4,10 @@
|
|||||||
Zeitpläne
|
Zeitpläne
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="item" routerLink="/PropertyList" routerLinkActive="itemActive">
|
||||||
|
Eigenschaften
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="item" routerLink="/DeviceList" routerLinkActive="itemActive">
|
<div class="item" routerLink="/DeviceList" routerLinkActive="itemActive">
|
||||||
Geräte
|
Geräte
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
|||||||
import {SearchComponent} from './shared/search/search.component';
|
import {SearchComponent} from './shared/search/search.component';
|
||||||
import {DeviceListComponent} from './pages/device-list/device-list.component';
|
import {DeviceListComponent} from './pages/device-list/device-list.component';
|
||||||
import {DeviceComponent} from './pages/device/device.component';
|
import {DeviceComponent} from './pages/device/device.component';
|
||||||
|
import {PropertyListComponent} from './pages/property-list/property-list.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -24,6 +25,7 @@ import {DeviceComponent} from './pages/device/device.component';
|
|||||||
SearchComponent,
|
SearchComponent,
|
||||||
DeviceListComponent,
|
DeviceListComponent,
|
||||||
DeviceComponent,
|
DeviceComponent,
|
||||||
|
PropertyListComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|||||||
@ -31,28 +31,20 @@ export class DeviceListComponent implements OnInit {
|
|||||||
this.deviceService.findAll(devices => this.devices = devices);
|
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 {
|
setSwitchState(d: Device, value: boolean): void {
|
||||||
const device: DeviceSwitch = d as DeviceSwitch;
|
const device: DeviceSwitch = d as DeviceSwitch;
|
||||||
if (!device.stateProperty) {
|
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 {
|
setShutterPosition(d: Device, value: number): void {
|
||||||
const device: DeviceShutter = d as DeviceShutter;
|
const device: DeviceShutter = d as DeviceShutter;
|
||||||
if (!device.positionProperty) {
|
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 {
|
create(): void {
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Eigenschaft</th>
|
<th>Eigenschaft</th>
|
||||||
<td>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@ -30,7 +30,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Eigenschaft</th>
|
<th>Eigenschaft</th>
|
||||||
<td>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</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 {
|
th {
|
||||||
background-color: lightblue;
|
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>
|
<app-edit-field [initial]="schedule.title" (valueChange)="set(null, 'title', $event)"></app-edit-field>
|
||||||
</td>
|
</td>
|
||||||
<td colspan="5">
|
<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>
|
||||||
<td colspan="9">
|
<td colspan="9">
|
||||||
{{schedule.property?.type}}
|
{{schedule.property?.type}}
|
||||||
@ -144,7 +144,7 @@
|
|||||||
<option [ngValue]="100">100% Geschlossen</option>
|
<option [ngValue]="100">100% Geschlossen</option>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</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)">
|
<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>
|
<option *ngFor="let _ of [].constructor(21); let value = index" [ngValue]="value * 5">{{value * 5}}%</option>
|
||||||
</select>
|
</select>
|
||||||
@ -161,7 +161,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td *ngIf="schedule.property?.type === 'SCENE'">
|
<td *ngIf="schedule.property?.type === 'SCENE'">
|
||||||
<select [(ngModel)]="entry.value" (ngModelChange)="set(entry, 'value', entry.value)">
|
<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>
|
</select>
|
||||||
</td>
|
</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 {ActivatedRoute} from "@angular/router";
|
||||||
import {DataService} from "../../data.service";
|
import {DataService} from "../../data.service";
|
||||||
import {PropertyService} from "../../api/property/property.service";
|
import {PropertyService} from "../../api/property/property.service";
|
||||||
|
import {Scene} from "../../api/scene/Scene";
|
||||||
|
import {SceneService} from "../../api/scene/scene.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-schedule',
|
selector: 'app-schedule',
|
||||||
@ -22,18 +24,22 @@ export class ScheduleComponent implements OnInit {
|
|||||||
|
|
||||||
schedule!: Schedule;
|
schedule!: Schedule;
|
||||||
|
|
||||||
|
scenes: Scene[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly activatedRoute: ActivatedRoute,
|
readonly activatedRoute: ActivatedRoute,
|
||||||
readonly scheduleService: ScheduleService,
|
readonly scheduleService: ScheduleService,
|
||||||
readonly scheduleEntryService: ScheduleEntryService,
|
readonly scheduleEntryService: ScheduleEntryService,
|
||||||
readonly dataService: DataService,
|
|
||||||
readonly propertyService: PropertyService,
|
readonly propertyService: PropertyService,
|
||||||
|
readonly sceneService: SceneService,
|
||||||
|
readonly dataService: DataService,
|
||||||
) {
|
) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.dataService.schedule = undefined;
|
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)));
|
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
|
<div *ngIf="!searching" (click)="start()" [class.empty]="!selected">
|
||||||
*ngIf="!searching"
|
|
||||||
(click)="start()"
|
|
||||||
[class.empty]="!selected"
|
|
||||||
>
|
|
||||||
|
|
||||||
<ng-container *ngIf="selected">
|
<ng-container *ngIf="selected">
|
||||||
{{selected.value}}
|
{{selected.value}}
|
||||||
<ng-container *ngIf="showKey">
|
<ng-container *ngIf="showKey">
|
||||||
[{{selected.key}}]
|
[{{selected.key}}]
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="!selected">
|
<ng-container *ngIf="!selected">
|
||||||
-LEER-
|
-LEER-
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input
|
<input #input type="text" *ngIf="searching" [(ngModel)]="term" (ngModelChange)="changed()" (keydown)="inputKeyPress($event)" (blur)="blur()">
|
||||||
#input
|
|
||||||
type="text"
|
|
||||||
*ngIf="searching"
|
|
||||||
[(ngModel)]="term"
|
|
||||||
(ngModelChange)="changed()"
|
|
||||||
(keydown)="inputKeyPress($event)"
|
|
||||||
(blur)="blur()"
|
|
||||||
>
|
|
||||||
|
|
||||||
<div #resultList *ngIf="searching" class="resultList">
|
<div #resultList *ngIf="searching" class="resultList">
|
||||||
<div *ngIf="allowEmpty" class="result" (click)="select(undefined)">
|
<div *ngIf="allowEmpty" class="result" (click)="select(undefined)">
|
||||||
-
|
-
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="selected" class="result selected" (click)="select(undefined)">
|
<div *ngIf="selected" class="result selected" (click)="select(selected)">
|
||||||
{{selected.value}}
|
{{selected.value}}
|
||||||
<ng-container *ngIf="showKey">
|
<ng-container *ngIf="showKey">
|
||||||
[{{selected.key}}]
|
[{{selected.key}}]
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
.empty {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected {
|
.selected {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-bottom: 1px solid black;
|
border-bottom: 1px solid black;
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export class SearchComponent<T> implements OnInit {
|
|||||||
searchService!: ISearchService;
|
searchService!: ISearchService;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
initial?: string;
|
initial?: number;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
showKey: boolean = false;
|
showKey: boolean = false;
|
||||||
@ -65,7 +65,7 @@ export class SearchComponent<T> implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start(): void {
|
start(): void {
|
||||||
this.term = this.initial || "";
|
this.term = this.selected?.value || "";
|
||||||
if (this.resultList && this.input) {
|
if (this.resultList && this.input) {
|
||||||
this.resultList.style.left = this.input.style.left;
|
this.resultList.style.left = this.input.style.left;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Angular</title>
|
<title>Angular</title>
|
||||||
<base href="/">
|
<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">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@ -35,3 +35,61 @@ table.vertical {
|
|||||||
text-align: left;
|
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.Property;
|
||||||
import de.ph87.homeautomation.property.PropertyRepository;
|
import de.ph87.homeautomation.property.PropertyRepository;
|
||||||
import de.ph87.homeautomation.property.PropertyType;
|
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.Schedule;
|
||||||
import de.ph87.homeautomation.schedule.ScheduleRepository;
|
import de.ph87.homeautomation.schedule.ScheduleRepository;
|
||||||
import de.ph87.homeautomation.schedule.entry.ScheduleEntry;
|
import de.ph87.homeautomation.schedule.entry.ScheduleEntry;
|
||||||
@ -41,6 +43,10 @@ public class DemoDataService {
|
|||||||
|
|
||||||
private final KnxGroupReadService knxGroupReadService;
|
private final KnxGroupReadService knxGroupReadService;
|
||||||
|
|
||||||
|
private final SceneRepository sceneRepository;
|
||||||
|
|
||||||
|
private final SceneWriteService sceneWriteService;
|
||||||
|
|
||||||
public void insertDemoData() {
|
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_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));
|
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);
|
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) {
|
if (scheduleRepository.count() == 0) {
|
||||||
final Schedule scheduleEgFlurLicht = createSchedule(true, "EG Flur Licht", flur_eg_licht);
|
final Schedule scheduleEgFlurLicht = createSchedule(true, "EG Flur Licht", flur_eg_licht);
|
||||||
createTime(scheduleEgFlurLicht, true, 1, 0, 0, MIN30, true);
|
createTime(scheduleEgFlurLicht, true, 1, 0, 0, MIN30, true);
|
||||||
|
|||||||
@ -22,6 +22,6 @@ public abstract class Channel {
|
|||||||
|
|
||||||
public abstract Double getValue();
|
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.DeviceShutter;
|
||||||
import de.ph87.homeautomation.device.devices.DeviceSwitch;
|
import de.ph87.homeautomation.device.devices.DeviceSwitch;
|
||||||
import de.ph87.homeautomation.property.Property;
|
import de.ph87.homeautomation.property.Property;
|
||||||
import de.ph87.homeautomation.property.PropertyWriteService;
|
|
||||||
import de.ph87.homeautomation.schedule.ScheduleWriteService;
|
import de.ph87.homeautomation.schedule.ScheduleWriteService;
|
||||||
import de.ph87.homeautomation.web.BadRequestException;
|
import de.ph87.homeautomation.web.BadRequestException;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -23,8 +22,6 @@ public class DeviceWriteService {
|
|||||||
|
|
||||||
private final DeviceRepository deviceRepository;
|
private final DeviceRepository deviceRepository;
|
||||||
|
|
||||||
private final PropertyWriteService propertyWriteService;
|
|
||||||
|
|
||||||
private final DeviceReadService deviceReadService;
|
private final DeviceReadService deviceReadService;
|
||||||
|
|
||||||
public DeviceDto createDeviceSwitch(final String title, final Property stateProperty) {
|
public DeviceDto createDeviceSwitch(final String title, final Property stateProperty) {
|
||||||
|
|||||||
@ -42,7 +42,7 @@ public class KnxGroup extends Channel {
|
|||||||
|
|
||||||
private Double value;
|
private Double value;
|
||||||
|
|
||||||
private ZonedDateTime valueTimestamp;
|
private ZonedDateTime timestamp;
|
||||||
|
|
||||||
@Embedded
|
@Embedded
|
||||||
private KnxGroupLinkInfo read = new KnxGroupLinkInfo();
|
private KnxGroupLinkInfo read = new KnxGroupLinkInfo();
|
||||||
|
|||||||
@ -72,7 +72,7 @@ public class KnxGroupWriteService {
|
|||||||
// TODO implement all DPTXlator...
|
// TODO implement all DPTXlator...
|
||||||
if (valueOptional.isPresent()) {
|
if (valueOptional.isPresent()) {
|
||||||
knxGroup.setValue(valueOptional.get());
|
knxGroup.setValue(valueOptional.get());
|
||||||
knxGroup.setValueTimestamp(ZonedDateTime.now());
|
knxGroup.setTimestamp(ZonedDateTime.now());
|
||||||
log.debug("KnxGroup updated: {}", knxGroup);
|
log.debug("KnxGroup updated: {}", knxGroup);
|
||||||
applicationEventPublisher.publishEvent(new ChannelChangedEvent(knxGroup));
|
applicationEventPublisher.publishEvent(new ChannelChangedEvent(knxGroup));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -17,15 +17,30 @@ public class PropertyController implements ISearchController {
|
|||||||
|
|
||||||
private final PropertyReadService propertyReadService;
|
private final PropertyReadService propertyReadService;
|
||||||
|
|
||||||
@PostMapping("set")
|
@GetMapping("findAll")
|
||||||
public void set(@RequestBody final PropertySetDto dto) {
|
public List<PropertyDto> findAll() {
|
||||||
propertyWriteService.write(dto.getName(), dto.getValue());
|
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
|
@Override
|
||||||
@PostMapping("getById")
|
@GetMapping("getById/{id}")
|
||||||
public KeyValuePair getById(@RequestBody final String name) {
|
public KeyValuePair getById(@PathVariable final long id) {
|
||||||
final PropertyDto propertyDto = propertyReadService.getDtoByName(name);
|
final PropertyDto propertyDto = propertyReadService.getDtoById(id);
|
||||||
return toKeyValuePair(propertyDto);
|
return toKeyValuePair(propertyDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,9 +54,4 @@ public class PropertyController implements ISearchController {
|
|||||||
return new KeyValuePair(propertyDto.getName(), propertyDto.getTitle());
|
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 Double value;
|
||||||
|
|
||||||
private final ZonedDateTime valueTimestamp;
|
private final ZonedDateTime timestamp;
|
||||||
|
|
||||||
public PropertyDto(final Property property) {
|
public PropertyDto(final Property property) {
|
||||||
this.id = property.getId();
|
this.id = property.getId();
|
||||||
@ -26,7 +26,7 @@ public final class PropertyDto implements Serializable {
|
|||||||
this.name = property.getName();
|
this.name = property.getName();
|
||||||
this.title = property.getTitle();
|
this.title = property.getTitle();
|
||||||
this.value = property.getValue();
|
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());
|
return propertyRepository.findAllByNameLike(like).stream().map(propertyMapper::toDto).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PropertyDto getDtoByName(final String name) {
|
public PropertyDto getDtoById(final long id) {
|
||||||
return propertyMapper.toDto(getByName(name));
|
return propertyMapper.toDto(getById(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Property getById(final long id) {
|
public Property getById(final long id) {
|
||||||
return propertyRepository.findById(id).orElseThrow(RuntimeException::new);
|
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> findAllByReadChannel_Id(long readChannelId);
|
||||||
|
|
||||||
|
List<Property> findAll();
|
||||||
|
|
||||||
List<Property> findAllByNameLike(final String like);
|
List<Property> findAllByNameLike(final String like);
|
||||||
|
|
||||||
boolean existsByName(String name);
|
boolean existsByName(String name);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
package de.ph87.homeautomation.property;
|
package de.ph87.homeautomation.property;
|
||||||
|
|
||||||
public enum PropertyType {
|
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())
|
propertyReadService.findAllByReadChannel_Id(event.getChannelId())
|
||||||
.forEach(property -> {
|
.forEach(property -> {
|
||||||
property.setValue(property.getReadChannel().getValue());
|
property.setValue(property.getReadChannel().getValue());
|
||||||
property.setTimestamp(property.getReadChannel().getValueTimestamp());
|
property.setTimestamp(property.getReadChannel().getTimestamp());
|
||||||
log.debug("Updated Property from Channel: {}", property);
|
log.debug("Updated Property from Channel: {}", property);
|
||||||
webSocketService.send(propertyMapper.toDto(property), true);
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class Helpers {
|
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) {
|
public static <T, U> U mapIfNotNull(final T value, final Function<T, U> map) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import java.util.List;
|
|||||||
|
|
||||||
public interface ISearchController {
|
public interface ISearchController {
|
||||||
|
|
||||||
KeyValuePair getById(final String id);
|
KeyValuePair getById(final long id);
|
||||||
|
|
||||||
List<KeyValuePair> searchLike(final String term);
|
List<KeyValuePair> searchLike(final String term);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user