DeviceStateScene + Search blur fix

This commit is contained in:
Patrick Haßel 2021-11-08 17:18:19 +01:00
parent 5a74795569
commit febb198c93
34 changed files with 529 additions and 133 deletions

View File

@ -1,4 +1,4 @@
import {validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators"; import {validateListOrEmpty, validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
import {Property} from "../property/Property"; import {Property} from "../property/Property";
export abstract class Device { export abstract class Device {
@ -21,6 +21,15 @@ export abstract class Device {
type, type,
Property.fromJsonAllowNull(json['stateProperty']), Property.fromJsonAllowNull(json['stateProperty']),
); );
case "DeviceStateScene":
return new DeviceStateScene(
validateNumberNotNull(json['id']),
validateStringNotEmptyNotNull(json['title']),
type,
Property.fromJsonAllowNull(json['stateProperty']),
Property.fromJsonAllowNull(json['sceneProperty']),
validateListOrEmpty(json['sceneNumbers'], parseInt),
);
case "DeviceShutter": case "DeviceShutter":
return new DeviceShutter( return new DeviceShutter(
validateNumberNotNull(json['id']), validateNumberNotNull(json['id']),
@ -67,6 +76,28 @@ export class DeviceSwitch extends Device {
} }
export class DeviceStateScene extends DeviceSwitch {
constructor(
id: number,
title: string,
type: string,
stateProperty: Property | null,
public sceneProperty: Property | null,
public sceneNumbers: number[],
) {
super(id, title, type, stateProperty);
}
updateProperty(property: Property): void {
super.updateProperty(property);
if (this.sceneProperty?.id === property.id) {
this.sceneProperty = property;
}
}
}
export class DeviceShutter extends Device { export class DeviceShutter extends Device {
constructor( constructor(

View File

@ -1,6 +1,8 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {ApiService, NO_COMPARE, NO_OP} from "../api.service"; import {ApiService, NO_COMPARE, NO_OP} from "../api.service";
import {Device} from "./Device"; import {Device, DeviceShutter, DeviceStateScene, DeviceSwitch} from "./Device";
import {Update} from "../Update";
import {PropertyService} from "../property/property.service";
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -9,10 +11,15 @@ export class DeviceService {
constructor( constructor(
readonly api: ApiService, readonly api: ApiService,
readonly propertyService: PropertyService,
) { ) {
// nothing // nothing
} }
subscribe(next: (device: Update<Device>) => void): void {
this.api.subscribe("DeviceDto", Device.fromJson, next);
}
findAll(next: (list: Device[]) => void, compare: (a: Device, b: Device) => number = NO_COMPARE, error: (error: any) => void = NO_OP): void { findAll(next: (list: Device[]) => void, compare: (a: Device, b: Device) => number = NO_COMPARE, error: (error: any) => void = NO_OP): void {
this.api.getList("device/findAll", Device.fromJson, compare, next, error); this.api.getList("device/findAll", Device.fromJson, compare, next, error);
} }
@ -21,14 +28,42 @@ export class DeviceService {
this.api.postReturnItem("device/set/" + device.id + "/" + key, value, Device.fromJson, next, error); this.api.postReturnItem("device/set/" + device.id + "/" + key, value, Device.fromJson, next, error);
} }
setDeviceSwitch(device: Device, key: string, value: any, next: (item: Device) => void, error: (error: any) => void = NO_OP): void { setDeviceSwitch(device: Device, key: string, value: any, next: (item: Device) => void = NO_OP, error: (error: any) => void = NO_OP): void {
this.api.postReturnItem("device/set/" + device.id + "/DeviceSwitch/" + key, value, Device.fromJson, next, error); this.api.postReturnItem("device/set/" + device.id + "/DeviceSwitch/" + key, value, Device.fromJson, next, error);
} }
setDeviceShutter(device: Device, key: string, value: any, next: (item: Device) => void, error: (error: any) => void = NO_OP): void { setDeviceStateScene(device: Device, key: string, value: any, next: (item: Device) => void = NO_OP, error: (error: any) => void = NO_OP): void {
this.api.postReturnItem("device/set/" + device.id + "/DeviceStateScene/" + key, value, Device.fromJson, next, error);
}
setDeviceShutter(device: Device, key: string, value: any, next: (item: Device) => void = NO_OP, error: (error: any) => void = NO_OP): void {
this.api.postReturnItem("device/set/" + device.id + "/DeviceShutter/" + key, value, Device.fromJson, next, error); this.api.postReturnItem("device/set/" + device.id + "/DeviceShutter/" + key, value, Device.fromJson, next, error);
} }
setSwitchState(d: Device, value: boolean): void {
const device: DeviceSwitch = d as DeviceSwitch;
if (!device.stateProperty) {
throw new Error("Property 'stateProperty' not set for: " + device);
}
this.propertyService.set(device.stateProperty, "value", value ? 1 : 0);
}
setStateScene(d: Device, value: number): void {
const device: DeviceStateScene = d as DeviceStateScene;
if (!device.sceneProperty) {
throw new Error("Property 'sceneProperty' not set for: " + device);
}
this.propertyService.set(device.sceneProperty, "value", value);
}
setShutterPosition(d: Device, value: number): void {
const device: DeviceShutter = d as DeviceShutter;
if (!device.positionProperty) {
throw new Error("Property 'positionProperty' not set for: " + device);
}
this.propertyService.set(device.positionProperty, "value", value);
}
getById(id: number, next: (item: Device) => void, error: (error: any) => void = NO_OP): void { getById(id: number, next: (item: Device) => void, error: (error: any) => void = NO_OP): void {
this.api.getItem("device/getById/" + id, Device.fromJson, next, error); this.api.getItem("device/getById/" + id, Device.fromJson, next, error);
} }

View File

@ -1,3 +1,5 @@
import {NO_COMPARE} from "./api.service";
export function validateBooleanNotNull(value: any): boolean { export function validateBooleanNotNull(value: any): boolean {
if (value !== true && value !== false) { if (value !== true && value !== false) {
throw new Error("Not a boolean: " + value); throw new Error("Not a boolean: " + value);
@ -66,7 +68,7 @@ export function validateDateAllowNull(value: any): Date | null {
return validateDateNotNull(value); return validateDateNotNull(value);
} }
export function validateListOrEmpty<T>(json: any, fromJson: (json: any) => T, compare: (a: T, b: T) => number): T[] { export function validateListOrEmpty<T>(json: any, fromJson: (json: any) => T, compare: (a: T, b: T) => number = NO_COMPARE): T[] {
if (!Array.isArray(json)) { if (!Array.isArray(json)) {
return []; return [];
} }

View File

@ -1,5 +1,14 @@
<div class="config">
<select [(ngModel)]="createType">
<option ngValue="DeviceSwitch">Schalter</option>
<option ngValue="DeviceShutter">Rollladen</option>
</select>
<button (click)="create()">+ Hinzufügen</button>
</div>
<ng-container *ngFor="let device of devices.sort(Device.compareTypeThenTitle); trackBy: Device.trackBy"> <ng-container *ngFor="let device of devices.sort(Device.compareTypeThenTitle); trackBy: Device.trackBy">
<ng-container [ngSwitch]="device.type"> <ng-container [ngSwitch]="device.type">
<div class="device" *ngSwitchCase="'DeviceSwitch'" [ngClass]="getSwitchClassList(device)"> <div class="device" *ngSwitchCase="'DeviceSwitch'" [ngClass]="getSwitchClassList(device)">
<div class="title"> <div class="title">
{{device.title}} {{device.title}}
@ -8,10 +17,25 @@
<fa-icon [icon]="faEdit"></fa-icon> <fa-icon [icon]="faEdit"></fa-icon>
</div> </div>
<div class="controls"> <div class="controls">
<img alt="An" class="control" src="assets/switch-on.svg" (click)="setSwitchState(device, true)"/> <img alt="An" class="control" src="assets/switch-on.svg" (click)="deviceService.setSwitchState(device, true)"/>
<img alt="Aus" class="control" src="assets/switch-off.svg" (click)="setSwitchState(device, false)"/> <img alt="Aus" class="control" src="assets/switch-off.svg" (click)="deviceService.setSwitchState(device, false)"/>
</div> </div>
</div> </div>
<div class="device" *ngSwitchCase="'DeviceStateScene'" [ngClass]="getStateSceneClassList(device)">
<div class="title">
{{device.title}}
</div>
<div class="edit" [routerLink]="['/Device', {id: device.id}]">
<fa-icon [icon]="faEdit"></fa-icon>
</div>
<div class="controls">
<div *ngFor="let scene of getStateScenes(device)" class="control button" (click)="deviceService.setStateScene(device, scene.number)">
<span class="center">{{scene.title}}</span>
</div>
</div>
</div>
<div class="device" *ngSwitchCase="'DeviceShutter'" [ngClass]="getShutterClassList(device)"> <div class="device" *ngSwitchCase="'DeviceShutter'" [ngClass]="getShutterClassList(device)">
<div class="title"> <div class="title">
{{device.title}} {{device.title}}
@ -20,30 +44,23 @@
<fa-icon [icon]="faEdit"></fa-icon> <fa-icon [icon]="faEdit"></fa-icon>
</div> </div>
<div class="controls"> <div class="controls">
<div class="control button" (click)="setShutterPosition(device, 0)"> <div class="control button" (click)="deviceService.setShutterPosition(device, 0)">
<span class="center">Auf</span> <span class="center">Auf</span>
</div> </div>
<div class="control button" (click)="setShutterPosition(device, 40)"> <div class="control button" (click)="deviceService.setShutterPosition(device, 40)">
<span class="center">50%</span> <span class="center">50%</span>
</div> </div>
<div class="control button" (click)="setShutterPosition(device, 75)"> <div class="control button" (click)="deviceService.setShutterPosition(device, 75)">
<span class="center">90%</span> <span class="center">90%</span>
</div> </div>
<div class="control button" (click)="setShutterPosition(device, 85)"> <div class="control button" (click)="deviceService.setShutterPosition(device, 85)">
<span class="center">Schlitze</span> <span class="center">Schlitze</span>
</div> </div>
<div class="control button" (click)="setShutterPosition(device, 100)"> <div class="control button" (click)="deviceService.setShutterPosition(device, 100)">
<span class="center">Zu</span> <span class="center">Zu</span>
</div> </div>
</div> </div>
</div> </div>
</ng-container> </ng-container>
</ng-container> </ng-container>
<p>
<select [(ngModel)]="createType">
<option ngValue="DeviceSwitch">Schalter</option>
<option ngValue="DeviceShutter">Rollladen</option>
</select>
<button (click)="create()">+ Hinzufügen</button>
</p>

View File

@ -3,6 +3,12 @@
margin-bottom: 5px; margin-bottom: 5px;
border-radius: 10px; border-radius: 10px;
@media (min-width: 1000px) {
float: left;
width: 400px;
margin-right: 5px;
}
.title { .title {
float: left; float: left;
font-weight: bold; font-weight: bold;
@ -71,3 +77,8 @@
.shutterUnknown { .shutterUnknown {
background-color: gray; background-color: gray;
} }
.config {
clear: both;
margin-bottom: 5px;
}

View File

@ -1,8 +1,10 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {DeviceService} from "../../api/device/device.service"; import {DeviceService} from "../../api/device/device.service";
import {PropertyService} from "../../api/property/property.service"; import {PropertyService} from "../../api/property/property.service";
import {Device, DeviceShutter, DeviceSwitch} from "../../api/device/Device"; import {Device, DeviceShutter, DeviceStateScene, DeviceSwitch} from "../../api/device/Device";
import {faEdit} from '@fortawesome/free-regular-svg-icons'; import {faEdit} from '@fortawesome/free-regular-svg-icons';
import {Scene} from "../../api/scene/Scene";
import {SceneService} from "../../api/scene/scene.service";
@Component({ @Component({
selector: 'app-device-list', selector: 'app-device-list',
@ -17,10 +19,13 @@ export class DeviceListComponent implements OnInit {
devices: Device[] = []; devices: Device[] = [];
scenes: Scene[] = [];
createType: string = "DeviceSwitch"; createType: string = "DeviceSwitch";
constructor( constructor(
readonly deviceService: DeviceService, readonly deviceService: DeviceService,
readonly sceneService: SceneService,
readonly propertyService: PropertyService, readonly propertyService: PropertyService,
) { ) {
// nothing // nothing
@ -28,23 +33,12 @@ export class DeviceListComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.propertyService.subscribe(update => this.devices.forEach(d => d.updateProperty(update.payload))); this.propertyService.subscribe(update => this.devices.forEach(d => d.updateProperty(update.payload)));
this.deviceService.subscribe(update => this.updateDevice(update.payload, update.existing));
this.deviceService.findAll(devices => this.devices = devices); this.deviceService.findAll(devices => this.devices = devices);
}
setSwitchState(d: Device, value: boolean): void { this.sceneService.subscribe(update => this.updateScene(update.payload, update.existing));
const device: DeviceSwitch = d as DeviceSwitch; this.sceneService.findAll(scenes => this.scenes = scenes);
if (!device.stateProperty) {
throw new Error("Property 'stateProperty' not set for: " + device);
}
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 'positionProperty' not set for: " + device);
}
this.propertyService.set(device.positionProperty, "value", value);
} }
create(): void { create(): void {
@ -66,6 +60,20 @@ export class DeviceListComponent implements OnInit {
} }
} }
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);
}
}
getSwitchClassList(device: Device): object { getSwitchClassList(device: Device): object {
const value: number | null | undefined = (device as DeviceSwitch).stateProperty?.value; const value: number | null | undefined = (device as DeviceSwitch).stateProperty?.value;
return { return {
@ -75,6 +83,10 @@ export class DeviceListComponent implements OnInit {
}; };
} }
getStateSceneClassList(device: Device): object {
return this.getSwitchClassList(device);
}
getShutterClassList(device: Device): object { getShutterClassList(device: Device): object {
const value: number | null | undefined = (device as DeviceShutter).positionProperty?.value; const value: number | null | undefined = (device as DeviceShutter).positionProperty?.value;
return { return {
@ -85,4 +97,9 @@ export class DeviceListComponent implements OnInit {
}; };
} }
getStateScenes(d: Device): Scene[] {
const device: DeviceStateScene = d as DeviceStateScene;
return device.sceneNumbers.map(sceneNumber => this.scenes.find(scene => scene.number === sceneNumber)).filter(scene => scene !== undefined).map(s => s as Scene);
}
} }

View File

@ -13,7 +13,30 @@
<tr> <tr>
<th>Eigenschaft</th> <th>Eigenschaft</th>
<td> <td>
<app-search [searchService]="propertyService" [initial]="deviceSwitch.stateProperty?.id" [showKey]="true" (valueChange)="setDeviceSwitch('stateProperty', $event)"></app-search> <app-search [searchService]="propertyService" [initial]="deviceSwitch.stateProperty?.id" [showKey]="true" (valueChange)="deviceService.setDeviceSwitch(device,'stateProperty', $event)"></app-search>
</td>
</tr>
</table>
</ng-container>
<ng-container *ngIf="deviceStateScene">
<table class="vertical">
<tr>
<th>Name</th>
<td>
<app-edit-field [initial]="deviceStateScene.title" (valueChange)="set('title', $event)"></app-edit-field>
</td>
</tr>
<tr>
<th>Status-Eigenschaft</th>
<td>
<app-search [searchService]="propertyService" [initial]="deviceStateScene.stateProperty?.id" [showKey]="true" (valueChange)="deviceService.setDeviceStateScene(device,'stateProperty', $event)"></app-search>
</td>
</tr>
<tr>
<th>Szene-Eigenschaft</th>
<td>
<app-search [searchService]="propertyService" [initial]="deviceStateScene.sceneProperty?.id" [showKey]="true" (valueChange)="deviceService.setDeviceStateScene(device,'sceneProperty', $event)"></app-search>
</td> </td>
</tr> </tr>
</table> </table>
@ -30,7 +53,7 @@
<tr> <tr>
<th>Eigenschaft</th> <th>Eigenschaft</th>
<td> <td>
<app-search [searchService]="propertyService" [initial]="deviceShutter.positionProperty?.id" [showKey]="true" (valueChange)="setDeviceShutter('positionProperty', $event)"></app-search> <app-search [searchService]="propertyService" [initial]="deviceShutter.positionProperty?.id" [showKey]="true" (valueChange)="deviceService.setDeviceShutter(device,'positionProperty', $event)"></app-search>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -2,7 +2,7 @@ import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router"; import {ActivatedRoute, Router} 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 {Device, DeviceShutter, DeviceSwitch} from "../../api/device/Device"; import {Device, DeviceShutter, DeviceStateScene, DeviceSwitch} from "../../api/device/Device";
import {DeviceService} from "../../api/device/device.service"; import {DeviceService} from "../../api/device/device.service";
@Component({ @Component({
@ -16,6 +16,8 @@ export class DeviceComponent implements OnInit {
deviceSwitch: DeviceSwitch | undefined; deviceSwitch: DeviceSwitch | undefined;
deviceStateScene: DeviceStateScene | undefined;
deviceShutter: DeviceShutter | undefined; deviceShutter: DeviceShutter | undefined;
constructor( constructor(
@ -39,6 +41,9 @@ export class DeviceComponent implements OnInit {
case "DeviceSwitch": case "DeviceSwitch":
this.deviceSwitch = device as DeviceSwitch; this.deviceSwitch = device as DeviceSwitch;
break; break;
case "DeviceStateScene":
this.deviceStateScene = device as DeviceStateScene;
break;
case "DeviceShutter": case "DeviceShutter":
this.deviceShutter = device as DeviceShutter; this.deviceShutter = device as DeviceShutter;
break; break;
@ -50,14 +55,6 @@ export class DeviceComponent implements OnInit {
this.deviceService.set(this.device, key, value, device => this.setDevice(device)); this.deviceService.set(this.device, key, value, device => this.setDevice(device));
} }
setDeviceSwitch(key: string, value: any): void {
this.deviceService.setDeviceSwitch(this.device, key, value, device => this.setDevice(device));
}
setDeviceShutter(key: string, value: any): void {
this.deviceService.setDeviceShutter(this.device, key, value, device => this.setDevice(device));
}
delete(): void { delete(): void {
if (confirm(this.getDeviceTypeTitle() + " \"" + this.device.title + "\" wirklich löschen?")) { if (confirm(this.getDeviceTypeTitle() + " \"" + this.device.title + "\" wirklich löschen?")) {
this.deviceService.delete(this.device, () => this.router.navigate(["/DeviceList"])); this.deviceService.delete(this.device, () => this.router.navigate(["/DeviceList"]));

View File

@ -8,14 +8,14 @@
</tr> </tr>
<tr *ngFor="let property of properties.sort(Property.compareTypeThenTitle)"> <tr *ngFor="let property of properties.sort(Property.compareTypeThenTitle)">
<td> <td>
<app-edit-field [initial]="property.title" (valueChange)="setProperty(property, 'title', $event)"></app-edit-field> <app-edit-field [initial]="property.title" (valueChange)="edit(property, 'title', $event)"></app-edit-field>
</td> </td>
<td> <td>
<app-edit-field [initial]="property.name" (valueChange)="setProperty(property, 'name', $event)"></app-edit-field> <app-edit-field [initial]="property.name" (valueChange)="edit(property, 'name', $event)"></app-edit-field>
</td> </td>
<td> <td>
<select [(ngModel)]="property.type" (ngModelChange)="setProperty(property, 'type',property.type)"> <select [(ngModel)]="property.type" (ngModelChange)="edit(property, 'type', property.type)">
<option value="SWITCH">Schalter</option> <option value="BOOLEAN">Schalter</option>
<option value="SHUTTER">Rollladen</option> <option value="SHUTTER">Rollladen</option>
<option value="BRIGHTNESS_PERCENT">Helligkeit [%]</option> <option value="BRIGHTNESS_PERCENT">Helligkeit [%]</option>
<option value="COLOR_TEMPERATURE">Farbtermperatur</option> <option value="COLOR_TEMPERATURE">Farbtermperatur</option>
@ -24,7 +24,7 @@
</select> </select>
</td> </td>
<ng-container *ngIf="property.value !== null"> <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)"> <td *ngIf="property.type === 'BOOLEAN'" class="boolean" [class.true]="property.value" [class.false]="!property.value" (click)="edit(property, 'value', property.value > 0 ? 0 : 1)">
{{property.value ? "An" : "Aus"}} {{property.value ? "An" : "Aus"}}
</td> </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"> <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">

View File

@ -0,0 +1,3 @@
table {
width: 100%;
}

View File

@ -32,12 +32,12 @@ export class PropertyListComponent implements OnInit {
this.sceneService.subscribe(update => this.updateScene(update.payload, update.existing)); this.sceneService.subscribe(update => this.updateScene(update.payload, update.existing));
} }
setProperty(property: Property, key: string, value: any): void { edit(property: Property, key: string, value: any): void {
this.propertyService.set(property, key, value, property => this.updateProperty(property, true)); this.propertyService.set(property, key, value);
} }
setScene(scene: Scene, key: string, value: any): void { setScene(scene: Scene, key: string, value: any): void {
this.sceneService.set(scene, key, value, scene => this.updateScene(scene, true)); this.sceneService.set(scene, key, value);
} }
private updateProperty(property: Property, existing: boolean): void { private updateProperty(property: Property, existing: boolean): void {

View File

@ -132,7 +132,7 @@
<td class="empty last"></td> <td class="empty last"></td>
</ng-container> </ng-container>
<td *ngIf="schedule.property?.type === 'SWITCH'" [class.true]="entry.value" [class.false]="!entry.value" (click)="set(entry, 'value', entry.value > 0 ? 0 : 1)"> <td *ngIf="schedule.property?.type === 'BOOLEAN'" [class.true]="entry.value" [class.false]="!entry.value" (click)="set(entry, 'value', entry.value > 0 ? 0 : 1)">
{{entry.value ? "An" : "Aus"}} {{entry.value ? "An" : "Aus"}}
</td> </td>
<td *ngIf="schedule.property?.type === 'SHUTTER'" [class.true]="entry.value === 0" [class.false]="entry.value === 100" [class.tristate]="0 < entry.value && entry.value < 100"> <td *ngIf="schedule.property?.type === 'SHUTTER'" [class.true]="entry.value === 0" [class.false]="entry.value === 100" [class.tristate]="0 < entry.value && entry.value < 100">

View File

@ -10,19 +10,19 @@
</ng-container> </ng-container>
</div> </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)" (focus)="cancelOnBlur=true" (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" (mousedown)="dontCancelOnBlur()" (click)="select(undefined)">
- -
</div> </div>
<div *ngIf="selected" class="result selected" (click)="select(selected)"> <div *ngIf="selected" class="result selected" (mousedown)="dontCancelOnBlur()" (click)="select(selected)">
{{selected.value}} {{selected.value}}
<ng-container *ngIf="showKey"> <ng-container *ngIf="showKey">
[{{selected.key}}] [{{selected.key}}]
</ng-container> </ng-container>
</div> </div>
<div *ngFor="let result of results" class="result" (click)="select(result)"> <div *ngFor="let result of results" class="result" (mousedown)="dontCancelOnBlur()" (click)="select(result)">
{{result.value}} {{result.value}}
<ng-container *ngIf="showKey"> <ng-container *ngIf="showKey">
[{{result.key}}] [{{result.key}}]

View File

@ -9,7 +9,7 @@ import {ISearchService} from "../../api/ISearchService";
}) })
export class SearchComponent<T> implements OnInit { export class SearchComponent<T> implements OnInit {
private timeout: number | undefined; private changedTimeout: number | undefined;
@ViewChild('input') @ViewChild('input')
input2?: ElementRef; input2?: ElementRef;
@ -43,6 +43,8 @@ export class SearchComponent<T> implements OnInit {
searching: boolean = false; searching: boolean = false;
cancelOnBlur: boolean = true;
constructor() { constructor() {
} }
@ -53,14 +55,14 @@ export class SearchComponent<T> implements OnInit {
} }
changed(): void { changed(): void {
this.clearTimeout(); this.clearChangedTimeout();
this.timeout = setTimeout(() => this.doSearch(), 400); this.changedTimeout = setTimeout(() => this.doSearch(), 400);
} }
private clearTimeout(): void { private clearChangedTimeout(): void {
if (this.timeout) { if (this.changedTimeout) {
clearTimeout(this.timeout); clearTimeout(this.changedTimeout);
this.timeout = undefined; this.changedTimeout = undefined;
} }
} }
@ -80,13 +82,13 @@ export class SearchComponent<T> implements OnInit {
this.doSearch(); this.doSearch();
break; break;
case 'Escape': case 'Escape':
this.cancel(); this.cancelSearch();
break; break;
} }
} }
doSearch(): void { doSearch(): void {
this.clearTimeout(); this.clearChangedTimeout();
if (!this.term) { if (!this.term) {
this.results = []; this.results = [];
} else { } else {
@ -95,13 +97,19 @@ export class SearchComponent<T> implements OnInit {
} }
blur() { blur() {
setTimeout(() => this.cancel(), 100); if (this.cancelOnBlur) {
this.cancelSearch();
}
} }
cancel(): void { cancelSearch(): void {
this.searching = false; this.searching = false;
} }
dontCancelOnBlur(): void {
this.cancelOnBlur = false;
}
select(result: KeyValuePair | undefined): void { select(result: KeyValuePair | undefined): void {
this.searching = false; this.searching = false;
this.selected = result; this.selected = result;

View File

@ -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=scene-width, initial-scale=1"> <meta name="viewport" content="width=device-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>

View File

@ -9,6 +9,7 @@ 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.SceneDto;
import de.ph87.homeautomation.scene.SceneRepository; import de.ph87.homeautomation.scene.SceneRepository;
import de.ph87.homeautomation.scene.SceneWriteService; import de.ph87.homeautomation.scene.SceneWriteService;
import de.ph87.homeautomation.schedule.Schedule; import de.ph87.homeautomation.schedule.Schedule;
@ -48,16 +49,21 @@ public class DemoDataService {
private final SceneWriteService sceneWriteService; private final SceneWriteService sceneWriteService;
public void insertDemoData() { public void insertDemoData() {
final Property fernseher = createProperty("fernseher.eg", PropertyType.SWITCH, "Fernseher", knx(0, 0, 20), knx(0, 0, 4)); final Property eg = createProperty("eg.status", PropertyType.BOOLEAN, "Erdgeschoss", knx(0, 4, 2), null);
final Property verstaerker = createProperty("verstaerker", PropertyType.SWITCH, "Verstärker", knx(0, 3, 57), knx(0, 3, 56)); final Property eg_szene = createProperty("eg.szene", PropertyType.SCENE, "Erdgeschoss Szene", null, knx(0, 0, 1));
final Property garten_steckdosen = createProperty("garten.steckdosen", PropertyType.SWITCH, "Garten Steckdosen", knx(0, 4, 12), knx(0, 4, 11)); final Property og = createProperty("og.status", PropertyType.BOOLEAN, "Obergeschoss", knx(0, 6, 6), null);
final Property terrasse = createProperty("terrasse", PropertyType.SWITCH, "Terrasse Licht", knx(0, 4, 1), knx(0, 4, 0)); final Property og_szene = createProperty("og.szene", PropertyType.SCENE, "Obergeschoss Szene", null, knx(0, 3, 2));
final Property ambiente_eg = createProperty("ambiente.eg", PropertyType.SWITCH, "Ambiente EG", knx(0, 3, 81), knx(0, 3, 80)); final Property fernseher = createProperty("fernseher.eg", PropertyType.BOOLEAN, "Fernseher", knx(0, 0, 20), knx(0, 0, 4));
final Property ambiente_og = createProperty("ambiente.og", PropertyType.SWITCH, "Ambiente OG", knx(0, 6, 2), knx(0, 6, 3)); final Property verstaerker = createProperty("verstaerker", PropertyType.BOOLEAN, "Verstärker", knx(0, 3, 57), knx(0, 3, 56));
final Property garten_steckdosen = createProperty("garten.steckdosen", PropertyType.BOOLEAN, "Garten Steckdosen", knx(0, 4, 12), knx(0, 4, 11));
final Property terrasse = createProperty("terrasse", PropertyType.BOOLEAN, "Terrasse Licht", knx(0, 4, 1), knx(0, 4, 0));
final Property bad_licht = createProperty("bad.licht", PropertyType.SWITCH, "Bad Licht", knx(0, 5, 19), knx(0, 3, 73)); final Property ambiente_eg = createProperty("ambiente.eg", PropertyType.BOOLEAN, "Ambiente EG", knx(0, 3, 81), knx(0, 3, 80));
final Property flur_eg_licht = createProperty("flur.eg.licht", PropertyType.SWITCH, "Flur EG Licht", knx(0, 4, 8), knx(0, 5, 14)); final Property ambiente_og = createProperty("ambiente.og", PropertyType.BOOLEAN, "Ambiente OG", knx(0, 6, 2), knx(0, 6, 3));
final Property bad_licht = createProperty("bad.licht", PropertyType.BOOLEAN, "Bad Licht", knx(0, 5, 19), knx(0, 3, 73));
final Property flur_eg_licht = createProperty("flur.eg.licht", PropertyType.BOOLEAN, "Flur EG Licht", knx(0, 4, 8), knx(0, 5, 14));
final Property wohnzimmer_rollladen = createProperty("wohnzimmer.rollladen", PropertyType.SHUTTER, "Wohnzimmer Rollladen", null, knx(0, 4, 24)); final Property wohnzimmer_rollladen = createProperty("wohnzimmer.rollladen", PropertyType.SHUTTER, "Wohnzimmer Rollladen", null, knx(0, 4, 24));
final Property schlafzimmer_rollladen = createProperty("schlafzimmer_rollladen", PropertyType.SHUTTER, "Schlafzimmer Rollladen", null, knx(0, 3, 3)); final Property schlafzimmer_rollladen = createProperty("schlafzimmer_rollladen", PropertyType.SHUTTER, "Schlafzimmer Rollladen", null, knx(0, 3, 3));
@ -66,7 +72,16 @@ public class DemoDataService {
final Property helligkeit = createProperty("helligkeit", PropertyType.LUX, "Helligkeit", knx(0, 5, 6), null); final Property helligkeit = createProperty("helligkeit", PropertyType.LUX, "Helligkeit", knx(0, 5, 6), null);
final Property szene_haus = createProperty("szene_haus", PropertyType.SCENE, "Szene Haus ", null, knx(0, 0, 21)); final Property szene_haus = createProperty("szene_haus", PropertyType.SCENE, "Szene Haus ", null, knx(0, 0, 21));
if (sceneRepository.count() == 0) {
final SceneDto alles_aus = sceneWriteService.create(1, "Alles AUS");
final SceneDto nachtlicht = sceneWriteService.create(2, "Nachtlicht");
final SceneDto dekoration_aus = sceneWriteService.create(30, "Dekoration AUS");
final SceneDto dekoration_an = sceneWriteService.create(31, "Dekoration AN");
if (deviceRepository.count() == 0) { if (deviceRepository.count() == 0) {
deviceWriteService.createDeviceStateScene(eg, eg_szene, alles_aus);
deviceWriteService.createDeviceStateScene(og, og_szene, alles_aus);
deviceWriteService.createDeviceSwitch(fernseher); deviceWriteService.createDeviceSwitch(fernseher);
deviceWriteService.createDeviceSwitch(verstaerker); deviceWriteService.createDeviceSwitch(verstaerker);
deviceWriteService.createDeviceSwitch(garten_steckdosen); deviceWriteService.createDeviceSwitch(garten_steckdosen);
@ -82,12 +97,6 @@ public class DemoDataService {
deviceWriteService.createDeviceShutter(schlafzimmer_rollladen); deviceWriteService.createDeviceShutter(schlafzimmer_rollladen);
deviceWriteService.createDeviceShutter(flur_og_rollladen); deviceWriteService.createDeviceShutter(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) {

View File

@ -56,6 +56,16 @@ public class DeviceController {
return deviceWriteService.setDeviceSwitch(id, (device, v) -> device.setStateProperty(mapIfNotNull(v, propertyReadService::getByName)), name); return deviceWriteService.setDeviceSwitch(id, (device, v) -> device.setStateProperty(mapIfNotNull(v, propertyReadService::getByName)), name);
} }
@PostMapping("set/{id}/DeviceStateScene/stateProperty")
public DeviceDto setDeviceStateSceneStateProperty(@PathVariable final long id, @RequestBody(required = false) final String name) {
return deviceWriteService.setDeviceStateScene(id, (device, v) -> device.setStateProperty(mapIfNotNull(v, propertyReadService::getByName)), name);
}
@PostMapping("set/{id}/DeviceStateScene/sceneProperty")
public DeviceDto setDeviceStateSceneSceneProperty(@PathVariable final long id, @RequestBody(required = false) final String name) {
return deviceWriteService.setDeviceStateScene(id, (device, v) -> device.setSceneProperty(mapIfNotNull(v, propertyReadService::getByName)), name);
}
@PostMapping("set/{id}/DeviceShutter/positionProperty") @PostMapping("set/{id}/DeviceShutter/positionProperty")
public DeviceDto setDeviceShutterPositionProperty(@PathVariable final long id, @RequestBody(required = false) final String name) { public DeviceDto setDeviceShutterPositionProperty(@PathVariable final long id, @RequestBody(required = false) final String name) {
return deviceWriteService.setDeviceShutter(id, (device, v) -> device.setPositionProperty(mapIfNotNull(v, propertyReadService::getByName)), name); return deviceWriteService.setDeviceShutter(id, (device, v) -> device.setPositionProperty(mapIfNotNull(v, propertyReadService::getByName)), name);

View File

@ -31,6 +31,9 @@ public class DeviceReadService {
if (device instanceof DeviceSwitch) { if (device instanceof DeviceSwitch) {
final DeviceSwitch deviceSwitch = (DeviceSwitch) device; final DeviceSwitch deviceSwitch = (DeviceSwitch) device;
return new DeviceSwitchDto(deviceSwitch, mapIfNotNull(deviceSwitch.getStateProperty(), propertyMapper::toDto)); return new DeviceSwitchDto(deviceSwitch, mapIfNotNull(deviceSwitch.getStateProperty(), propertyMapper::toDto));
} else if (device instanceof DeviceStateScene) {
final DeviceStateScene deviceStateScene = (DeviceStateScene) device;
return new DeviceStateSceneDto(deviceStateScene, mapIfNotNull(deviceStateScene.getStateProperty(), propertyMapper::toDto), mapIfNotNull(deviceStateScene.getSceneProperty(), propertyMapper::toDto));
} else if (device instanceof DeviceShutter) { } else if (device instanceof DeviceShutter) {
final DeviceShutter deviceShutter = (DeviceShutter) device; final DeviceShutter deviceShutter = (DeviceShutter) device;
return new DeviceShutterDto(deviceShutter, mapIfNotNull(deviceShutter.getPositionProperty(), propertyMapper::toDto)); return new DeviceShutterDto(deviceShutter, mapIfNotNull(deviceShutter.getPositionProperty(), propertyMapper::toDto));

View File

@ -1,18 +1,19 @@
package de.ph87.homeautomation.device; package de.ph87.homeautomation.device;
import de.ph87.homeautomation.device.devices.Device; import de.ph87.homeautomation.device.devices.*;
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.Property;
import de.ph87.homeautomation.scene.SceneDto;
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 de.ph87.homeautomation.web.WebSocketService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
@ -24,6 +25,8 @@ public class DeviceWriteService {
private final DeviceReadService deviceReadService; private final DeviceReadService deviceReadService;
private final WebSocketService webSocketService;
public DeviceDto createDeviceSwitch(final Property stateProperty) { public DeviceDto createDeviceSwitch(final Property stateProperty) {
return createDeviceSwitch(null, stateProperty); return createDeviceSwitch(null, stateProperty);
} }
@ -33,7 +36,7 @@ public class DeviceWriteService {
deviceSwitch.setTitle(getTitle(title, stateProperty)); deviceSwitch.setTitle(getTitle(title, stateProperty));
deviceSwitch.setStateProperty(stateProperty); deviceSwitch.setStateProperty(stateProperty);
deviceRepository.save(deviceSwitch); deviceRepository.save(deviceSwitch);
return deviceReadService.toDto(deviceSwitch); return publish(deviceSwitch, true);
} }
public DeviceDto createDeviceShutter(final Property positionProperty) { public DeviceDto createDeviceShutter(final Property positionProperty) {
@ -45,13 +48,27 @@ public class DeviceWriteService {
deviceShutter.setTitle(getTitle(title, positionProperty)); deviceShutter.setTitle(getTitle(title, positionProperty));
deviceShutter.setPositionProperty(positionProperty); deviceShutter.setPositionProperty(positionProperty);
deviceRepository.save(deviceShutter); deviceRepository.save(deviceShutter);
return deviceReadService.toDto(deviceShutter); return publish(deviceShutter, true);
}
public DeviceDto createDeviceStateScene(final Property stateProperty, final Property sceneProperty, final SceneDto... scenes) {
return createDeviceStateScene(null, stateProperty, sceneProperty, scenes);
}
public DeviceDto createDeviceStateScene(final String title, final Property stateProperty, final Property sceneProperty, final SceneDto... scenes) {
final DeviceStateScene deviceStateScene = new DeviceStateScene();
deviceStateScene.setTitle(getTitle(title, stateProperty));
deviceStateScene.setStateProperty(stateProperty);
deviceStateScene.setSceneProperty(sceneProperty);
deviceStateScene.setSceneNumbers(Arrays.stream(scenes).map(SceneDto::getNumber).collect(Collectors.toList()));
deviceRepository.save(deviceStateScene);
return publish(deviceStateScene, true);
} }
public <T> DeviceDto set(final long id, final BiConsumer<Device, T> setter, final T value) { public <T> DeviceDto set(final long id, final BiConsumer<Device, T> setter, final T value) {
final Device device = deviceReadService.getById(id); final Device device = deviceReadService.getById(id);
setter.accept(device, value); setter.accept(device, value);
return deviceReadService.toDto(device); return publish(device, true);
} }
public <T> DeviceDto setDeviceSwitch(final long id, final BiConsumer<DeviceSwitch, T> setter, final T value) { public <T> DeviceDto setDeviceSwitch(final long id, final BiConsumer<DeviceSwitch, T> setter, final T value) {
@ -60,7 +77,16 @@ public class DeviceWriteService {
throw new BadRequestException("Not a DeviceSwitch: %s", device); throw new BadRequestException("Not a DeviceSwitch: %s", device);
} }
setter.accept((DeviceSwitch) device, value); setter.accept((DeviceSwitch) device, value);
return deviceReadService.toDto(device); return publish(device, true);
}
public <T> DeviceDto setDeviceStateScene(final long id, final BiConsumer<DeviceStateScene, T> setter, final T value) {
final Device device = deviceReadService.getById(id);
if (!(device instanceof DeviceStateScene)) {
throw new BadRequestException("Not a DeviceStateScene: %s", device);
}
setter.accept((DeviceStateScene) device, value);
return publish(device, true);
} }
public <T> DeviceDto setDeviceShutter(final long id, final BiConsumer<DeviceShutter, T> setter, final T value) { public <T> DeviceDto setDeviceShutter(final long id, final BiConsumer<DeviceShutter, T> setter, final T value) {
@ -69,14 +95,20 @@ public class DeviceWriteService {
throw new BadRequestException("Not a DeviceShutter: %s", device); throw new BadRequestException("Not a DeviceShutter: %s", device);
} }
setter.accept((DeviceShutter) device, value); setter.accept((DeviceShutter) device, value);
return deviceReadService.toDto(device); return publish(device, true);
} }
public void delete(final long id) { public void delete(final long id) {
deviceRepository.deleteById(id); final Device device = deviceReadService.getById(id);
deviceRepository.delete(device);
publish(device, false);
} }
public DeviceDto create(final String type) { public DeviceDto create(final String type) {
return publish(create2(type), true);
}
private DeviceDto create2(final String type) {
switch (type) { switch (type) {
case "DeviceSwitch": case "DeviceSwitch":
return createDeviceSwitch(null, null); return createDeviceSwitch(null, null);
@ -86,6 +118,15 @@ public class DeviceWriteService {
throw new RuntimeException("Not implemented type: " + type); throw new RuntimeException("Not implemented type: " + type);
} }
private DeviceDto publish(final Device device, final boolean existing) {
return publish(deviceReadService.toDto(device), existing);
}
private DeviceDto publish(final DeviceDto dto, final boolean existing) {
webSocketService.send(dto, existing);
return dto;
}
private String getTitle(final String title, final Property property) { private String getTitle(final String title, final Property property) {
if (title != null) { if (title != null) {
return title; return title;

View File

@ -2,8 +2,10 @@ package de.ph87.homeautomation.device.devices;
import lombok.Getter; import lombok.Getter;
import java.io.Serializable;
@Getter @Getter
public abstract class DeviceDto { public abstract class DeviceDto implements Serializable {
public final long id; public final long id;

View File

@ -0,0 +1,30 @@
package de.ph87.homeautomation.device.devices;
import de.ph87.homeautomation.property.Property;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@ToString
@Entity
public class DeviceStateScene extends Device {
@ManyToOne
private Property stateProperty;
@ManyToOne
private Property sceneProperty;
@ElementCollection(fetch = FetchType.EAGER)
private List<Integer> sceneNumbers = new ArrayList<>();
}

View File

@ -0,0 +1,24 @@
package de.ph87.homeautomation.device.devices;
import de.ph87.homeautomation.property.PropertyDto;
import lombok.Getter;
import java.util.List;
@Getter
public class DeviceStateSceneDto extends DeviceDto {
public final PropertyDto stateProperty;
public final PropertyDto sceneProperty;
public final List<Integer> sceneNumbers;
public DeviceStateSceneDto(final DeviceStateScene device, final PropertyDto stateProperty, final PropertyDto sceneProperty) {
super(device);
this.stateProperty = stateProperty;
this.sceneProperty = sceneProperty;
this.sceneNumbers = device.getSceneNumbers();
}
}

View File

@ -0,0 +1,56 @@
package de.ph87.homeautomation.knx.group;
import lombok.Data;
import java.io.Serializable;
import java.time.ZonedDateTime;
@Data
public class KnxGroupDto implements Serializable {
public final int addressRaw;
public final String addressStr;
public final int dptMain;
public final int dptSub;
public final String name;
public final String description;
public final int puid;
public final boolean ets;
public final KnxTelegram lastTelegram;
public final Double value;
public final ZonedDateTime timestamp;
public final KnxGroupLinkInfo read;
public final byte[] sendValue;
public final KnxGroupLinkInfo send;
public KnxGroupDto(final KnxGroup knxGroup) {
this.addressRaw = knxGroup.getAddressRaw();
this.addressStr = knxGroup.getAddressStr();
this.dptMain = knxGroup.getDptMain();
this.dptSub = knxGroup.getDptSub();
this.name = knxGroup.getName();
this.description = knxGroup.getDescription();
this.puid = knxGroup.getPuid();
this.ets = knxGroup.isEts();
this.lastTelegram = knxGroup.getLastTelegram();
this.value = knxGroup.getValue();
this.timestamp = knxGroup.getTimestamp();
this.read = knxGroup.getRead();
this.sendValue = knxGroup.getSendValue();
this.send = knxGroup.getSend();
}
}

View File

@ -0,0 +1,12 @@
package de.ph87.homeautomation.knx.group;
import org.springframework.stereotype.Service;
@Service
public class KnxGroupMapper {
public KnxGroupDto toDto(final KnxGroup knxGroup) {
return new KnxGroupDto(knxGroup);
}
}

View File

@ -1,6 +1,7 @@
package de.ph87.homeautomation.knx.group; package de.ph87.homeautomation.knx.group;
import de.ph87.homeautomation.channel.ChannelChangedEvent; import de.ph87.homeautomation.channel.ChannelChangedEvent;
import de.ph87.homeautomation.web.WebSocketService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
@ -26,6 +27,10 @@ public class KnxGroupWriteService {
private final ApplicationEventPublisher applicationEventPublisher; private final ApplicationEventPublisher applicationEventPublisher;
private final WebSocketService webSocketService;
private final KnxGroupMapper knxGroupMapper;
public void requestRead(final KnxGroup knxGroup) { public void requestRead(final KnxGroup knxGroup) {
knxGroup.getRead().setNextTimestamp(ZonedDateTime.now()); knxGroup.getRead().setNextTimestamp(ZonedDateTime.now());
log.debug("Requesting read for KnxGroup: {}", knxGroup); log.debug("Requesting read for KnxGroup: {}", knxGroup);
@ -39,11 +44,14 @@ public class KnxGroupWriteService {
((DPTXlatorBoolean) translator).setValue(value == 1.0); ((DPTXlatorBoolean) translator).setValue(value == 1.0);
} else if (translator instanceof DPTXlator8BitUnsigned) { } else if (translator instanceof DPTXlator8BitUnsigned) {
((DPTXlator8BitUnsigned) translator).setValue((int) value); ((DPTXlator8BitUnsigned) translator).setValue((int) value);
} else if (translator instanceof DPTXlatorSceneNumber) {
((DPTXlatorSceneNumber) translator).setValue((int) value - 1);
} else { // TODO implement all DPTXlator... } else { // TODO implement all DPTXlator...
translator.setValue("" + value); translator.setValue("" + value);
} }
knxGroup.getSend().setNextTimestamp(ZonedDateTime.now()); knxGroup.getSend().setNextTimestamp(ZonedDateTime.now());
knxGroup.setSendValue(translator.getData()); knxGroup.setSendValue(translator.getData());
publish(knxGroup);
sendThreadWakeUpEvent(); sendThreadWakeUpEvent();
} catch (KNXFormatException e) { } catch (KNXFormatException e) {
log.error("Failed set value \"{}\" to DptXlator {} for KnxGroup {}", value, translator, knxGroup); log.error("Failed set value \"{}\" to DptXlator {} for KnxGroup {}", value, translator, knxGroup);
@ -84,6 +92,7 @@ public class KnxGroupWriteService {
knxGroup.setValue(valueOptional.get()); knxGroup.setValue(valueOptional.get());
knxGroup.setTimestamp(ZonedDateTime.now()); knxGroup.setTimestamp(ZonedDateTime.now());
log.debug("KnxGroup updated: {}", knxGroup); log.debug("KnxGroup updated: {}", knxGroup);
publish(knxGroup);
applicationEventPublisher.publishEvent(new ChannelChangedEvent(knxGroup)); applicationEventPublisher.publishEvent(new ChannelChangedEvent(knxGroup));
} else { } else {
log.error("Failed to get value from DptXlator {} for KnxGroup {}", translator, knxGroup); log.error("Failed to get value from DptXlator {} for KnxGroup {}", translator, knxGroup);
@ -109,4 +118,10 @@ public class KnxGroupWriteService {
return Optional.empty(); return Optional.empty();
} }
private KnxGroupDto publish(final KnxGroup knxGroup) {
final KnxGroupDto dto = knxGroupMapper.toDto(knxGroup);
webSocketService.send(dto, true);
return dto;
}
} }

View File

@ -32,6 +32,11 @@ public class PropertyController implements ISearchController {
return propertyWriteService.set(id, Property::setName, propertyName); return propertyWriteService.set(id, Property::setName, propertyName);
} }
@PostMapping("set/{id}/title")
public PropertyDto setPropertyTitle(@PathVariable final long id, @RequestBody final String propertyTitle) {
return propertyWriteService.set(id, Property::setTitle, propertyTitle);
}
@PostMapping("set/{id}/value") @PostMapping("set/{id}/value")
public PropertyDto setValue(@PathVariable final long id, @RequestBody final double value) { public PropertyDto setValue(@PathVariable final long id, @RequestBody final double value) {
return propertyWriteService.set(id, propertyWriteService::write, value); return propertyWriteService.set(id, propertyWriteService::write, value);

View File

@ -1,5 +1,5 @@
package de.ph87.homeautomation.property; package de.ph87.homeautomation.property;
public enum PropertyType { public enum PropertyType {
SWITCH, SHUTTER, BRIGHTNESS_PERCENT, COLOR_TEMPERATURE, LUX, SCENE BOOLEAN, SHUTTER, BRIGHTNESS_PERCENT, COLOR_TEMPERATURE, LUX, SCENE
} }

View File

@ -45,7 +45,7 @@ public class PropertyWriteService {
property.setValue(property.getReadChannel().getValue()); property.setValue(property.getReadChannel().getValue());
property.setTimestamp(property.getReadChannel().getTimestamp()); 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); publish(property, true);
} }
); );
} }
@ -53,7 +53,7 @@ public class PropertyWriteService {
public PropertyDto create() { public PropertyDto create() {
final Property entry = new Property(); final Property entry = new Property();
entry.setTitle(generateUnusedName()); entry.setTitle(generateUnusedName());
return propertyMapper.toDto(propertyRepository.save(entry)); return publish(propertyRepository.save(entry), true);
} }
private String generateUnusedName() { private String generateUnusedName() {
@ -68,11 +68,19 @@ public class PropertyWriteService {
public <T> PropertyDto set(final long id, final BiConsumer<Property, T> setter, final T value) { public <T> PropertyDto set(final long id, final BiConsumer<Property, T> setter, final T value) {
final Property property = propertyReadService.getById(id); final Property property = propertyReadService.getById(id);
setter.accept(property, value); setter.accept(property, value);
return propertyMapper.toDto(property); return publish(property, true);
} }
public void delete(final long id) { public void delete(final long id) {
propertyRepository.deleteById(id); final Property property = propertyReadService.getById(id);
propertyRepository.delete(property);
publish(property, false);
}
private PropertyDto publish(final Property property, final boolean existing) {
final PropertyDto dto = propertyMapper.toDto(property);
webSocketService.send(dto, existing);
return dto;
} }
} }

View File

@ -2,8 +2,10 @@ package de.ph87.homeautomation.scene;
import lombok.Getter; import lombok.Getter;
import java.io.Serializable;
@Getter @Getter
public class SceneDto { public class SceneDto implements Serializable {
public final long id; public final long id;

View File

@ -1,6 +1,7 @@
package de.ph87.homeautomation.scene; package de.ph87.homeautomation.scene;
import de.ph87.homeautomation.schedule.ScheduleWriteService; import de.ph87.homeautomation.schedule.ScheduleWriteService;
import de.ph87.homeautomation.web.WebSocketService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -22,18 +23,29 @@ public class SceneWriteService {
private final SceneMapper sceneMapper; private final SceneMapper sceneMapper;
private final WebSocketService webSocketService;
public <T> SceneDto set(final long id, final BiConsumer<Scene, T> setter, final T value) { public <T> SceneDto set(final long id, final BiConsumer<Scene, T> setter, final T value) {
final Scene scene = sceneReadService.getById(id); final Scene scene = sceneReadService.getById(id);
setter.accept(scene, value); setter.accept(scene, value);
return sceneMapper.toDto(scene); return publish(scene, true);
}
public void delete(final long id) {
sceneRepository.deleteById(id);
} }
public SceneDto create(final int number, final String title) { public SceneDto create(final int number, final String title) {
return sceneMapper.toDto(sceneRepository.save(new Scene(number, orElseGet(title, this::generateUnusedTitle)))); final Scene scene = sceneRepository.save(new Scene(number, orElseGet(title, this::generateUnusedTitle)));
return publish(scene, true);
}
public void delete(final long id) {
final Scene scene = sceneReadService.getById(id);
sceneRepository.delete(scene);
publish(scene, false);
}
private SceneDto publish(final Scene scene, final boolean existing) {
final SceneDto dto = sceneMapper.toDto(scene);
webSocketService.send(dto, existing);
return dto;
} }
private String generateUnusedTitle() { private String generateUnusedTitle() {

View File

@ -4,11 +4,12 @@ import de.ph87.homeautomation.property.PropertyDto;
import de.ph87.homeautomation.schedule.entry.ScheduleEntryDto; import de.ph87.homeautomation.schedule.entry.ScheduleEntryDto;
import lombok.Getter; import lombok.Getter;
import java.io.Serializable;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Getter @Getter
public class ScheduleDto { public class ScheduleDto implements Serializable {
public final long id; public final long id;

View File

@ -1,5 +1,6 @@
package de.ph87.homeautomation.schedule; package de.ph87.homeautomation.schedule;
import de.ph87.homeautomation.web.WebSocketService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -24,10 +25,12 @@ public class ScheduleWriteService {
private final ScheduleRepository scheduleRepository; private final ScheduleRepository scheduleRepository;
private final WebSocketService webSocketService;
public ScheduleDto create() { public ScheduleDto create() {
final Schedule entry = new Schedule(); final Schedule entry = new Schedule();
entry.setTitle(generateUnusedName()); entry.setTitle(generateUnusedName());
return scheduleMapper.toDto(scheduleRepository.save(entry)); return publish(scheduleRepository.save(entry), true);
} }
private String generateUnusedName() { private String generateUnusedName() {
@ -43,11 +46,19 @@ public class ScheduleWriteService {
final Schedule schedule = scheduleReadService.getById(id); final Schedule schedule = scheduleReadService.getById(id);
setter.accept(schedule, value); setter.accept(schedule, value);
scheduleCalculationService.calculateSchedule(schedule, ZonedDateTime.now()); scheduleCalculationService.calculateSchedule(schedule, ZonedDateTime.now());
return scheduleMapper.toDto(schedule); return publish(schedule, true);
} }
public void delete(final long id) { public void delete(final long id) {
scheduleRepository.deleteById(id); final Schedule schedule = scheduleReadService.getById(id);
scheduleRepository.delete(schedule);
publish(schedule, false);
}
private ScheduleDto publish(final Schedule schedule, final boolean existing) {
final ScheduleDto dto = scheduleMapper.toDto(schedule);
webSocketService.send(dto, existing);
return dto;
} }
} }

View File

@ -2,10 +2,11 @@ package de.ph87.homeautomation.schedule.entry;
import lombok.Getter; import lombok.Getter;
import java.io.Serializable;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
@Getter @Getter
public class ScheduleEntryDto { public class ScheduleEntryDto implements Serializable {
public final long id; public final long id;

View File

@ -3,6 +3,7 @@ package de.ph87.homeautomation.schedule.entry;
import de.ph87.homeautomation.schedule.Schedule; import de.ph87.homeautomation.schedule.Schedule;
import de.ph87.homeautomation.schedule.ScheduleCalculationService; import de.ph87.homeautomation.schedule.ScheduleCalculationService;
import de.ph87.homeautomation.schedule.ScheduleReadService; import de.ph87.homeautomation.schedule.ScheduleReadService;
import de.ph87.homeautomation.web.WebSocketService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -27,25 +28,34 @@ public class ScheduleEntryWriteService {
private final ScheduleEntryRepository scheduleEntryRepository; private final ScheduleEntryRepository scheduleEntryRepository;
private final WebSocketService webSocketService;
public <T> ScheduleEntryDto set(final long id, final BiConsumer<ScheduleEntry, T> setter, final T value) { public <T> ScheduleEntryDto set(final long id, final BiConsumer<ScheduleEntry, T> setter, final T value) {
final ScheduleEntry entry = scheduleEntryReadService.getById(id); final ScheduleEntry entry = scheduleEntryReadService.getById(id);
setter.accept(entry, value); setter.accept(entry, value);
final Schedule schedule = scheduleReadService.getByEntry(entry); final Schedule schedule = scheduleReadService.getByEntry(entry);
scheduleCalculationService.calculateSchedule(schedule, ZonedDateTime.now()); scheduleCalculationService.calculateSchedule(schedule, ZonedDateTime.now());
return scheduleEntryMapper.toDto(entry); return publish(entry, true);
} }
public ScheduleEntryDto create(final long scheduleId) { public ScheduleEntryDto create(final long scheduleId) {
final Schedule schedule = scheduleReadService.getById(scheduleId); final Schedule schedule = scheduleReadService.getById(scheduleId);
final ScheduleEntry entry = new ScheduleEntry(); final ScheduleEntry entry = new ScheduleEntry();
schedule.getEntries().add(scheduleEntryRepository.save(entry)); schedule.getEntries().add(scheduleEntryRepository.save(entry));
return scheduleEntryMapper.toDto(entry); return publish(entry, true);
} }
public void delete(final long id) { public void delete(final long id) {
final ScheduleEntry entry = scheduleEntryReadService.getById(id); final ScheduleEntry entry = scheduleEntryReadService.getById(id);
scheduleReadService.getByEntry(entry).getEntries().remove(entry); scheduleReadService.getByEntry(entry).getEntries().remove(entry);
scheduleEntryRepository.delete(entry); scheduleEntryRepository.delete(entry);
publish(entry, false);
}
private ScheduleEntryDto publish(final ScheduleEntry entry, final boolean existing) {
final ScheduleEntryDto dto = scheduleEntryMapper.toDto(entry);
webSocketService.send(dto, existing);
return dto;
} }
} }