Compare commits
5 Commits
4fd64acf09
...
0936a7bc47
| Author | SHA1 | Date | |
|---|---|---|---|
| 0936a7bc47 | |||
| 116158ab1f | |||
| 3da3dea353 | |||
| 9023f57c37 | |||
| b3ecb231c8 |
@ -1,5 +1,5 @@
|
|||||||
<div class="tileContainer">
|
<div class="tileContainer">
|
||||||
|
|
||||||
<app-thing-tile [now]="now" [thing]="thing" *ngFor="let thing of sorted()"></app-thing-tile>
|
<app-thing-tile [now]="now" [thing]="thing" *ngFor="let thing of sorted(); trackBy: Thing.trackBy"></app-thing-tile>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -16,6 +16,8 @@ import {Thing} from '../Thing';
|
|||||||
})
|
})
|
||||||
export class ThingListComponent implements OnInit, OnDestroy {
|
export class ThingListComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
protected readonly Thing = Thing;
|
||||||
|
|
||||||
private readonly subs: Subscription[] = [];
|
private readonly subs: Subscription[] = [];
|
||||||
|
|
||||||
protected now: Date = new Date();
|
protected now: Date = new Date();
|
||||||
|
|||||||
@ -12,10 +12,10 @@
|
|||||||
|
|
||||||
<div class="sliders">
|
<div class="sliders">
|
||||||
<div class="slider" *ngIf="tunable.brightnessPropertyId !== ''">
|
<div class="slider" *ngIf="tunable.brightnessPropertyId !== ''">
|
||||||
<input type="range" [ngModel]="tunable.brightnessProperty?.state?.value" (ngModelChange)="tunableService.setBrightness(tunable, $event)">
|
<app-slider [percent]="tunable.brightnessProperty?.state?.value" (onChange)="tunableService.setBrightness(tunable, $event)" [color0]="dark" [color1]="bright" [colorSlider]="brightness"></app-slider>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider" *ngIf="tunable.coldnessPropertyId !== ''">
|
<div class="slider" *ngIf="tunable.coldnessPropertyId !== ''">
|
||||||
<input type="range" [ngModel]="tunable.coldnessProperty?.state?.value" (ngModelChange)="tunableService.setColdness(tunable, $event)">
|
<app-slider [percent]="tunable.coldnessProperty?.state?.value" (onChange)="tunableService.setColdness(tunable, $event)" [color0]="warm" [color1]="cold"></app-slider>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
|||||||
import {RelativePipe} from "../../api/relative.pipe";
|
import {RelativePipe} from "../../api/relative.pipe";
|
||||||
import {Tunable} from "../Tunable";
|
import {Tunable} from "../Tunable";
|
||||||
import {TunableService} from '../tunable.service';
|
import {TunableService} from '../tunable.service';
|
||||||
|
import {SliderComponent} from '../../shared/slider/slider.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tunable-tile',
|
selector: 'app-tunable-tile',
|
||||||
@ -13,7 +14,8 @@ import {TunableService} from '../tunable.service';
|
|||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
RelativePipe,
|
RelativePipe,
|
||||||
NgClass,
|
NgClass,
|
||||||
FormsModule
|
FormsModule,
|
||||||
|
SliderComponent
|
||||||
],
|
],
|
||||||
templateUrl: './tunable-tile.component.html',
|
templateUrl: './tunable-tile.component.html',
|
||||||
styleUrl: './tunable-tile.component.less'
|
styleUrl: './tunable-tile.component.less'
|
||||||
@ -28,6 +30,16 @@ export class TunableTileComponent {
|
|||||||
@Input()
|
@Input()
|
||||||
tunable!: Tunable;
|
tunable!: Tunable;
|
||||||
|
|
||||||
|
protected readonly brightness = '#FF0000';
|
||||||
|
|
||||||
|
protected readonly dark = '#000000';
|
||||||
|
|
||||||
|
protected readonly bright = '#ffffff';
|
||||||
|
|
||||||
|
protected readonly warm = '#ffe78e';
|
||||||
|
|
||||||
|
protected readonly cold = '#c4eeff';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly tunableService: TunableService,
|
protected readonly tunableService: TunableService,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -0,0 +1,4 @@
|
|||||||
|
<div #outer class="outer" (mousedown)="change($event)" (mousemove)="change($event)" (mouseup)="end($event)" [style.background]="gradient()">
|
||||||
|
<div #slider class="slider" [style.left]="left(percent)" [style.border-color]="colorSlider" [class.ghost]="percentMouse !== null"></div>
|
||||||
|
<div class="slider" [style.left]="left(percentMouse === null ? percent : percentMouse)" [style.border-color]="colorSlider"></div>
|
||||||
|
</div>
|
||||||
23
src/main/angular/src/app/shared/slider/slider.component.less
Normal file
23
src/main/angular/src/app/shared/slider/slider.component.less
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
@import "../../../config";
|
||||||
|
|
||||||
|
.outer {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 2em;
|
||||||
|
border: @border solid black;
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
width: 1.5em;
|
||||||
|
top: 0.2em;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
border: @border solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ghost {
|
||||||
|
opacity: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
75
src/main/angular/src/app/shared/slider/slider.component.ts
Normal file
75
src/main/angular/src/app/shared/slider/slider.component.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import {Component, ElementRef, EventEmitter, Input, Output, ViewChild} from '@angular/core';
|
||||||
|
|
||||||
|
const RATE_LIMIT_MS = 300;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-slider',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './slider.component.html',
|
||||||
|
styleUrl: './slider.component.less'
|
||||||
|
})
|
||||||
|
export class SliderComponent {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
percent!: number;
|
||||||
|
|
||||||
|
protected percentMouse: number | null = null;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
color0!: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
color1!: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
colorSlider!: string;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
onChange = new EventEmitter<number>();
|
||||||
|
|
||||||
|
@ViewChild('outer')
|
||||||
|
outer!: ElementRef;
|
||||||
|
|
||||||
|
@ViewChild('slider')
|
||||||
|
slider!: ElementRef;
|
||||||
|
|
||||||
|
private last: number | null = null;
|
||||||
|
|
||||||
|
protected change($event: MouseEvent) {
|
||||||
|
if ($event.buttons !== 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.apply($event, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected end($event: MouseEvent) {
|
||||||
|
this.apply($event, true);
|
||||||
|
this.percentMouse = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private apply($event: MouseEvent, force: boolean) {
|
||||||
|
const sliderHalf = this.slider.nativeElement.offsetWidth / 2;
|
||||||
|
const offsetWidth = this.outer.nativeElement.offsetWidth;
|
||||||
|
const currentX = $event.clientX;
|
||||||
|
this.percentMouse = Math.max(0, Math.min(100, Math.round(100 * (currentX - sliderHalf) / offsetWidth)));
|
||||||
|
const now = new Date().getTime();
|
||||||
|
if (!force && this.last !== null && now - this.last < RATE_LIMIT_MS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.last = now;
|
||||||
|
this.onChange.emit(this.percentMouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
gradient(): string {
|
||||||
|
return `linear-gradient(to right, ${this.color0}, ${this.color1})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
left(percent: number): string {
|
||||||
|
if (!this.slider) {
|
||||||
|
return "0%";
|
||||||
|
}
|
||||||
|
return `calc(${percent}% - ${this.slider.nativeElement.offsetWidth / 2}px)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -67,7 +67,7 @@ public class DemoService {
|
|||||||
device(eg, "ambiente", "Ambiente", 849, 848, tagDecoration, tagDecorationInside);
|
device(eg, "ambiente", "Ambiente", 849, 848, tagDecoration, tagDecorationInside);
|
||||||
|
|
||||||
final AreaDto wohnzimmer = area(eg, "wohnzimmer", "Wohnzimmer");
|
final AreaDto wohnzimmer = area(eg, "wohnzimmer", "Wohnzimmer");
|
||||||
tunable(wohnzimmer, "spots", "Spots", 28, 828, 2344, 2343, 1825, 1824, tagLight);
|
tunable(wohnzimmer, "spots", "", 28, 828, 2344, 2343, 1825, 1824, tagLight);
|
||||||
shutter(wohnzimmer, "links", "Links", 1048, tagShutter, tagFront);
|
shutter(wohnzimmer, "links", "Links", 1048, tagShutter, tagFront);
|
||||||
shutter(wohnzimmer, "rechts", "Rechts", 1811, tagShutter, tagFront);
|
shutter(wohnzimmer, "rechts", "Rechts", 1811, tagShutter, tagFront);
|
||||||
device(wohnzimmer, "fernseher", "Fernseher", 20, 4, tagDevice, tagMedia, tagVideo, tagConfirm);
|
device(wohnzimmer, "fernseher", "Fernseher", 20, 4, tagDevice, tagMedia, tagVideo, tagConfirm);
|
||||||
@ -76,25 +76,25 @@ public class DemoService {
|
|||||||
device(wohnzimmer, "fensterdekoration", "Fenster", 1823, 1822, tagDecoration, tagDecorationWindow);
|
device(wohnzimmer, "fensterdekoration", "Fenster", 1823, 1822, tagDecoration, tagDecorationWindow);
|
||||||
|
|
||||||
final AreaDto kueche = area(eg, "kueche", "Küche");
|
final AreaDto kueche = area(eg, "kueche", "Küche");
|
||||||
tunable(kueche, "spots", "Spots", 2311, 2304, 2342, 2341, 2321, 2317, tagLight);
|
tunable(kueche, "spots", "", 2311, 2304, 2342, 2341, 2321, 2317, tagLight);
|
||||||
device(kueche, "haengelampe", "Hängelampe", 2313, 2312, tagLight);
|
device(kueche, "haengelampe", "Hängelampe", 2313, 2312, tagLight);
|
||||||
shutter(kueche, "seite", "Seite", 2316, tagShutter, tagSide);
|
shutter(kueche, "seite", "Seite", 2316, tagShutter, tagSide);
|
||||||
shutter(kueche, "theke", "Theke", 2320, tagShutter, tagBack);
|
shutter(kueche, "theke", "Theke", 2320, tagShutter, tagBack);
|
||||||
shutter(kueche, "tuer", "Tür", 2324, tagShutter, tagBack);
|
shutter(kueche, "tuer", "Tür", 2324, tagShutter, tagBack);
|
||||||
|
|
||||||
final AreaDto waschkueche = area(eg, "waschkueche", "Waschküche");
|
final AreaDto waschkueche = area(eg, "waschkueche", "Waschküche");
|
||||||
tunable(waschkueche, "spots", "Spots", 1827, 1826, 1829, 1828, 1831, 1830, tagLight);
|
tunable(waschkueche, "spots", "", 1827, 1826, 1829, 1828, 1831, 1830, tagLight);
|
||||||
shutter(waschkueche, "seite", "Seite", 1820, tagShutter, tagBack);
|
shutter(waschkueche, "seite", "Seite", 1820, tagShutter, tagBack);
|
||||||
|
|
||||||
final AreaDto flurEg = area(eg, "flur", "Flur EG");
|
final AreaDto flurEg = area(eg, "flur", "Flur EG");
|
||||||
tunable(flurEg, "spots", "Spots", 1032, 1294, 2340, 2339, 2327, 2325, tagLight);
|
tunable(flurEg, "spots", "", 1032, 1294, 2340, 2339, 2327, 2325, tagLight);
|
||||||
|
|
||||||
/* OG ----------------------------------------------------------------------------------------- */
|
/* OG ----------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
final AreaDto og = area(null, "og", "OG");
|
final AreaDto og = area(null, "og", "OG");
|
||||||
|
|
||||||
final AreaDto flurOg = area(og, "flur", "Flur OG");
|
final AreaDto flurOg = area(og, "flur", "Flur OG");
|
||||||
tunable(flurOg, "spots", "Spots", 814, 813, 2086, 2070, 2074, 2073, tagLight);
|
tunable(flurOg, "spots", "", 814, 813, 2086, 2070, 2074, 2073, tagLight);
|
||||||
shutter(flurOg, "vorne", "Vorne", 1293, tagShutter, tagFront);
|
shutter(flurOg, "vorne", "Vorne", 1293, tagShutter, tagFront);
|
||||||
shutter(flurOg, "hinten", "Hinten", 2080, 2079, tagShutter, tagBack);
|
shutter(flurOg, "hinten", "Hinten", 2080, 2079, tagShutter, tagBack);
|
||||||
device(flurOg, "ambiente", "Ambiente", 1538, 1539, tagDecoration, tagDecorationInside);
|
device(flurOg, "ambiente", "Ambiente", 1538, 1539, tagDecoration, tagDecorationInside);
|
||||||
@ -102,7 +102,7 @@ public class DemoService {
|
|||||||
|
|
||||||
final AreaDto badOg = area(og, "bad", "Bad");
|
final AreaDto badOg = area(og, "bad", "Bad");
|
||||||
shutter(badOg, "", "", 1308, 1307, tagShutter, tagBack);
|
shutter(badOg, "", "", 1308, 1307, tagShutter, tagBack);
|
||||||
tunable(badOg, "spots", "Spots", 1299, 841, 1320, 1319, 1318, 1317, tagLight);
|
tunable(badOg, "spots", "", 1299, 841, 1320, 1319, 1318, 1317, tagLight);
|
||||||
tunable(badOg, "waschbecken", "Waschbecken", 790, 789, 1320, 1319, 1318, 1317, tagLight);
|
tunable(badOg, "waschbecken", "Waschbecken", 790, 789, 1320, 1319, 1318, 1317, tagLight);
|
||||||
tunable(badOg, "badewanne", "Badewanne", 782, 781, 1320, 1319, 1318, 1317, tagLight);
|
tunable(badOg, "badewanne", "Badewanne", 782, 781, 1320, 1319, 1318, 1317, tagLight);
|
||||||
tunable(badOg, "dusche", "Dusche", 774, 773, 1320, 1319, 1318, 1317, tagLight);
|
tunable(badOg, "dusche", "Dusche", 774, 773, 1320, 1319, 1318, 1317, tagLight);
|
||||||
@ -110,7 +110,7 @@ public class DemoService {
|
|||||||
|
|
||||||
final AreaDto arbeitszimmer = area(og, "arbeitszimmer", "Arbeitszimmer");
|
final AreaDto arbeitszimmer = area(og, "arbeitszimmer", "Arbeitszimmer");
|
||||||
shutter(arbeitszimmer, "", "", 2064, tagShutter, tagBack);
|
shutter(arbeitszimmer, "", "", 2064, tagShutter, tagBack);
|
||||||
tunable(arbeitszimmer, "spots", "Spots", 2058, 2057, 2067, 2069, 2049, 2054, tagLight);
|
tunable(arbeitszimmer, "spots", "", 2058, 2057, 2067, 2069, 2049, 2054, tagLight);
|
||||||
device(arbeitszimmer, "pc", "Patrick PC", 2052, 2051, tagDevice, tagMedia, tagAudio, tagVideo, tagConfirm);
|
device(arbeitszimmer, "pc", "Patrick PC", 2052, 2051, tagDevice, tagMedia, tagAudio, tagVideo, tagConfirm);
|
||||||
device(arbeitszimmer, "schreibtisch", "Patrick Schreibtisch", 2050, 2048, tagDevice, tagMedia, tagAudio, tagVideo, tagConfirm);
|
device(arbeitszimmer, "schreibtisch", "Patrick Schreibtisch", 2050, 2048, tagDevice, tagMedia, tagAudio, tagVideo, tagConfirm);
|
||||||
device(arbeitszimmer, "drucker", "Drucker", 2066, 2065, tagDevice, tagConfirm);
|
device(arbeitszimmer, "drucker", "Drucker", 2066, 2065, tagDevice, tagConfirm);
|
||||||
@ -118,14 +118,14 @@ public class DemoService {
|
|||||||
final AreaDto schlafzimmer = area(og, "schlafzimmer", "Schlafzimmer");
|
final AreaDto schlafzimmer = area(og, "schlafzimmer", "Schlafzimmer");
|
||||||
shutter(schlafzimmer, "links", "Links", 1303, tagShutter, tagFront);
|
shutter(schlafzimmer, "links", "Links", 1303, tagShutter, tagFront);
|
||||||
shutter(schlafzimmer, "rechts", "Rechts", 1304, tagShutter, tagFront);
|
shutter(schlafzimmer, "rechts", "Rechts", 1304, tagShutter, tagFront);
|
||||||
tunable(schlafzimmer, "spots", "Spots", 1309, 829, 1322, 1321, 1316, 1315, tagLight);
|
tunable(schlafzimmer, "spots", "", 1309, 829, 1322, 1321, 1316, 1315, tagLight);
|
||||||
device(schlafzimmer, "loewe", "Löwe", 844, 843, tagDecoration, tagDecorationInside);
|
device(schlafzimmer, "loewe", "Löwe", 844, 843, tagDecoration, tagDecorationInside);
|
||||||
device(schlafzimmer, "sternenhimmel", "Sternenhimmel", 846, 845, tagDecoration, tagDecorationInside);
|
device(schlafzimmer, "sternenhimmel", "Sternenhimmel", 846, 845, tagDecoration, tagDecorationInside);
|
||||||
device(schlafzimmer, "fenster", "Fenster", 2333, 2334, tagDecoration, tagDecorationWindow);
|
device(schlafzimmer, "fenster", "Fenster", 2333, 2334, tagDecoration, tagDecorationWindow);
|
||||||
|
|
||||||
final AreaDto emil = area(og, "emil", "Emil");
|
final AreaDto emil = area(og, "emil", "Emil");
|
||||||
shutter(emil, "", "", 1807, tagShutter, tagFront);
|
shutter(emil, "", "", 1807, tagShutter, tagFront);
|
||||||
tunable(emil, "spots", "Spots", 1796, 1795, 2085, 2084, 2083, 2082, tagLight);
|
tunable(emil, "spots", "", 1796, 1795, 2085, 2084, 2083, 2082, tagLight);
|
||||||
device(emil, "fenster_links", "Fenster Links", 1804, 1803, tagDecoration, tagDecorationWindow);
|
device(emil, "fenster_links", "Fenster Links", 1804, 1803, tagDecoration, tagDecorationWindow);
|
||||||
device(emil, "fenster_rechts", "Fenster Rechts", 1802, 1801, tagDevice);
|
device(emil, "fenster_rechts", "Fenster Rechts", 1802, 1801, tagDevice);
|
||||||
device(emil, "wand_scheune", "Wand Scheune", 2072, 2071, tagDevice);
|
device(emil, "wand_scheune", "Wand Scheune", 2072, 2071, tagDevice);
|
||||||
|
|||||||
@ -87,10 +87,11 @@ public class DeviceService implements IThingService<DeviceDto> {
|
|||||||
|
|
||||||
@EventListener(PropertyDto.class)
|
@EventListener(PropertyDto.class)
|
||||||
public void onPropertyChange(@NonNull final PropertyDto<?> dto) {
|
public void onPropertyChange(@NonNull final PropertyDto<?> dto) {
|
||||||
deviceRepository.findAllByStatePropertyId(dto.getId()).forEach(device -> publish(device, CrudAction.CREATED));
|
deviceRepository.findAllByStatePropertyId(dto.getId()).forEach(device -> publish(device, CrudAction.UPDATED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@SuppressWarnings("SameParameterValue")
|
||||||
private DeviceDto publish(@NonNull final Device device, @NonNull final CrudAction action) {
|
private DeviceDto publish(@NonNull final Device device, @NonNull final CrudAction action) {
|
||||||
final DeviceDto dto = toDto(device);
|
final DeviceDto dto = toDto(device);
|
||||||
log.info("Device {}: {}", action, dto);
|
log.info("Device {}: {}", action, dto);
|
||||||
|
|||||||
@ -115,10 +115,11 @@ public class TunableService implements IThingService<TunableDto> {
|
|||||||
|
|
||||||
@EventListener(PropertyDto.class)
|
@EventListener(PropertyDto.class)
|
||||||
public void onPropertyChange(@NonNull final PropertyDto<?> dto) {
|
public void onPropertyChange(@NonNull final PropertyDto<?> dto) {
|
||||||
tunableRepository.findDistinctByStatePropertyIdOrBrightnessPropertyIdOrColdnessPropertyId(dto.getId(), dto.getId(), dto.getId()).forEach(tunable -> publish(tunable, CrudAction.CREATED));
|
tunableRepository.findDistinctByStatePropertyIdOrBrightnessPropertyIdOrColdnessPropertyId(dto.getId(), dto.getId(), dto.getId()).forEach(tunable -> publish(tunable, CrudAction.UPDATED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@SuppressWarnings("SameParameterValue")
|
||||||
private TunableDto publish(@NonNull final Tunable tunable, @NonNull final CrudAction action) {
|
private TunableDto publish(@NonNull final Tunable tunable, @NonNull final CrudAction action) {
|
||||||
final TunableDto dto = toDto(tunable);
|
final TunableDto dto = toDto(tunable);
|
||||||
log.info("Tunable {}: {}", action, dto);
|
log.info("Tunable {}: {}", action, dto);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user