Compare commits
No commits in common. "master" and "deploy---2022-10-25---11-23" have entirely different histories.
master
...
deploy---2
@ -4,8 +4,7 @@ spring.datasource.url=jdbc:h2:./Homeautomation;AUTO_SERVER=TRUE
|
|||||||
spring.datasource.driverClassName=org.h2.Driver
|
spring.datasource.driverClassName=org.h2.Driver
|
||||||
spring.datasource.username=sa
|
spring.datasource.username=sa
|
||||||
spring.datasource.password=password
|
spring.datasource.password=password
|
||||||
#-
|
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||||
spring.jackson.serialization.indent_output=true
|
|
||||||
#-
|
#-
|
||||||
spring.jpa.hibernate.ddl-auto=create
|
spring.jpa.hibernate.ddl-auto=create
|
||||||
#-
|
#-
|
||||||
|
|||||||
18
pom.xml
18
pom.xml
@ -9,14 +9,14 @@
|
|||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>3.3.3</version>
|
<version>2.7.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -41,10 +41,6 @@
|
|||||||
<groupId>com.h2database</groupId>
|
<groupId>com.h2database</groupId>
|
||||||
<artifactId>h2</artifactId>
|
<artifactId>h2</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.postgresql</groupId>
|
|
||||||
<artifactId>postgresql</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
@ -58,7 +54,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.calimero</groupId>
|
<groupId>com.github.calimero</groupId>
|
||||||
<artifactId>calimero-core</artifactId>
|
<artifactId>calimero-core</artifactId>
|
||||||
<version>2.5.1</version>
|
<version>2.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.luckycatlabs</groupId>
|
<groupId>com.luckycatlabs</groupId>
|
||||||
@ -66,12 +62,6 @@
|
|||||||
<version>1.2</version>
|
<version>1.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|||||||
@ -125,8 +125,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"cli": {
|
|
||||||
"analytics": false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,82 +0,0 @@
|
|||||||
export class Duration {
|
|
||||||
|
|
||||||
static readonly ZERO: Duration = new Duration('0');
|
|
||||||
|
|
||||||
readonly totalSeconds: number;
|
|
||||||
|
|
||||||
readonly days: number;
|
|
||||||
|
|
||||||
readonly hours: number;
|
|
||||||
|
|
||||||
readonly minutes: number;
|
|
||||||
|
|
||||||
readonly seconds: number;
|
|
||||||
|
|
||||||
readonly code: string;
|
|
||||||
|
|
||||||
readonly zero: boolean;
|
|
||||||
|
|
||||||
private constructor(input: string) {
|
|
||||||
this.totalSeconds = Duration.parse(input);
|
|
||||||
const sign = Math.sign(this.totalSeconds);
|
|
||||||
const abs = Math.abs(this.totalSeconds);
|
|
||||||
this.days = sign * Math.floor(abs / (24 * 60 * 60));
|
|
||||||
this.hours = sign * Math.floor(abs / (60 * 60)) % 24;
|
|
||||||
this.minutes = sign * Math.floor(abs / 60) % 60;
|
|
||||||
this.seconds = sign * Math.floor(abs) % 60;
|
|
||||||
this.zero = this.totalSeconds === 0;
|
|
||||||
this.code = this.buildCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
static ofCode(code: string) {
|
|
||||||
return new Duration(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static parse(input: string) {
|
|
||||||
const regex = /(?<signs>[+-]*)(?<value>\d+(?:[.,]\d+)?)(?<unit>d|h|m|s|ms)/g;
|
|
||||||
let totalSeconds = 0;
|
|
||||||
for (const match of input.matchAll(regex)) {
|
|
||||||
if (!match?.groups) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const sign = ((match.groups["signs"].replace(/\++/g, '').length % 2) === 1) ? -1 : 1;
|
|
||||||
const value = sign * parseInt(match.groups["value"]);
|
|
||||||
switch (match.groups["unit"]) {
|
|
||||||
case 'd':
|
|
||||||
totalSeconds += value * 24 * 60 * 60;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
totalSeconds += value * 60 * 60;
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
totalSeconds += value * 60;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
totalSeconds += value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return totalSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildCode() {
|
|
||||||
if (this.zero) {
|
|
||||||
return '0';
|
|
||||||
}
|
|
||||||
let code = "";
|
|
||||||
if (this.days != 0) {
|
|
||||||
code += this.days + "d";
|
|
||||||
}
|
|
||||||
if (this.hours != 0) {
|
|
||||||
code += this.hours + "h";
|
|
||||||
}
|
|
||||||
if (this.minutes != 0) {
|
|
||||||
code += this.minutes + "m";
|
|
||||||
}
|
|
||||||
if (this.seconds != 0) {
|
|
||||||
code += this.seconds + "s";
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,9 +1,28 @@
|
|||||||
|
import {prefix} from "../helpers";
|
||||||
|
|
||||||
export class Timestamp {
|
export class Timestamp {
|
||||||
|
|
||||||
|
public readonly WEEKDAY: string[] = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"];
|
||||||
|
|
||||||
|
public readonly dayName;
|
||||||
|
|
||||||
|
public readonly timeString;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
readonly date: Date,
|
readonly date: Date,
|
||||||
) {
|
) {
|
||||||
// -
|
const now = new Date();
|
||||||
|
const minutes: string = prefix(this.date.getMinutes(), '0', 2);
|
||||||
|
if (date.getDate() === now.getDate()) {
|
||||||
|
this.dayName = "Heute";
|
||||||
|
this.timeString = date.getHours() + ":" + minutes;
|
||||||
|
} else if (date.getDate() === now.getDate() + 1) {
|
||||||
|
this.dayName = "Morgen";
|
||||||
|
this.timeString = date.getHours() + ":" + minutes;
|
||||||
|
} else {
|
||||||
|
this.dayName = this.WEEKDAY[date.getDay()];
|
||||||
|
this.timeString = date.getHours() + ":" + minutes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fromDateOrNull(date: Date | null): Timestamp | null {
|
public static fromDateOrNull(date: Date | null): Timestamp | null {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {NO_OP} from "./api.service";
|
import {NO_OP} from "./api.service";
|
||||||
import {Page} from "./Page";
|
import {Page} from "./Page";
|
||||||
import {FromJson, IEntity, Next} from "./types";
|
import {Error, FromJson, IEntity, Next} from "./types";
|
||||||
import {environment} from "../../environments/environment";
|
import {environment} from "../../environments/environment";
|
||||||
import {HttpClient} from "@angular/common/http";
|
import {HttpClient} from "@angular/common/http";
|
||||||
import {map} from "rxjs/operators";
|
import {map} from "rxjs/operators";
|
||||||
@ -19,40 +19,40 @@ export class AbstractRepositoryService<Entity extends IEntity, Creator, Filter>
|
|||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
create(dto: Creator, next: Next<Entity> = NO_OP) {
|
create(dto: Creator, next: Next<Entity> = NO_OP, error: Error = NO_OP) {
|
||||||
this._post(['create'], dto, this.fromJson, next);
|
this._post(['create'], dto, this.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(entity: Entity, key: string, value: any, next: Next<Entity> = NO_OP) {
|
set(entity: Entity, key: string, value: any, next: Next<Entity> = NO_OP, error: Error = NO_OP) {
|
||||||
this._put(['put', entity.id, key], value, this.fromJson, next);
|
this._put(['put', entity.id, key], value, this.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(id: number, next: Next<Entity> = NO_OP) {
|
get(id: number, next: Next<Entity> = NO_OP, error: Error = NO_OP) {
|
||||||
this._get(['get', id], this.fromJson, next);
|
this._get(['get', id], this.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
filter(filter: Filter, next: Next<Page<Entity>> = NO_OP) {
|
filter(filter: Filter, next: Next<Page<Entity>> = NO_OP, error: Error = NO_OP) {
|
||||||
this._post(['filter'], filter, Page.fromJsonGenerator(this.fromJson), next);
|
this._post(['filter'], filter, Page.fromJsonGenerator(this.fromJson), next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(id: number, next: Next<Entity> = NO_OP) {
|
delete(id: number, next: Next<Entity> = NO_OP, error: Error = NO_OP) {
|
||||||
this._delete(['delete', id], this.fromJson, next);
|
this._delete(['delete', id], this.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _get<T>(path: any[], fromJson: FromJson<T>, next: Next<T>): void {
|
private _get<T>(path: any[], fromJson: FromJson<T>, next: Next<T>, error: Error): void {
|
||||||
this.http.get<any>(this._url(path)).pipe(map(fromJson)).subscribe(next);
|
this.http.get<any>(this._url(path)).pipe(map(fromJson)).subscribe(next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _put<T>(path: any[], data: any, fromJson: FromJson<T>, next: Next<T>): void {
|
private _put<T>(path: any[], data: any, fromJson: FromJson<T>, next: Next<T>, error: Error): void {
|
||||||
this.http.put<any>(this._url(path), data).pipe(map(fromJson)).subscribe(next);
|
this.http.put<any>(this._url(path), data).pipe(map(fromJson)).subscribe(next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _post<T>(path: any[], data: any, fromJson: FromJson<T>, next: Next<T>): void {
|
private _post<T>(path: any[], data: any, fromJson: FromJson<T>, next: Next<T>, error: Error): void {
|
||||||
this.http.post<any>(this._url(path), data).pipe(map(fromJson)).subscribe(next);
|
this.http.post<any>(this._url(path), data).pipe(map(fromJson)).subscribe(next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _delete<T>(path: any[], fromJson: FromJson<T>, next: Next<T>): void {
|
private _delete<T>(path: any[], fromJson: FromJson<T>, next: Next<T>, error: Error): void {
|
||||||
this.http.delete<any>(this._url(path)).pipe(map(fromJson)).subscribe(next);
|
this.http.delete<any>(this._url(path)).pipe(map(fromJson)).subscribe(next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _url(path: any[]): string {
|
private _url(path: any[]): string {
|
||||||
|
|||||||
@ -16,6 +16,13 @@ export function NO_COMPARE<T>(a: T, b: T): number {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function errorInterceptor(errorHandler: (error: any) => void): ((error: any) => void) {
|
||||||
|
return error => {
|
||||||
|
console.error(error);
|
||||||
|
errorHandler(error);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
@ -43,36 +50,36 @@ export class ApiService {
|
|||||||
this.updateSubject.pipe(filter(update => update.type === type), map(update => Update.convert(update, fromJson))).subscribe(next);
|
this.updateSubject.pipe(filter(update => update.type === type), map(update => Update.convert(update, fromJson))).subscribe(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
getReturnItem<T>(path: string, fromJson: (json: any) => T, next: (item: T) => void = NO_OP) {
|
getReturnItem<T>(path: string, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) {
|
||||||
this.http.get<any>(this.restUrl(path)).pipe(map(fromJson)).subscribe(next);
|
this.http.get<any>(this.restUrl(path)).pipe(map(fromJson)).subscribe(next, errorInterceptor(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
getList<T>(path: string, fromJson: (json: any) => T, compare: (a: T, b: T) => number = NO_COMPARE, next: (list: T[]) => void = NO_OP) {
|
getList<T>(path: string, fromJson: (json: any) => T, compare: (a: T, b: T) => number = NO_COMPARE, next: (list: T[]) => void = NO_OP, error: (error: any) => void = NO_OP) {
|
||||||
this.http.get<any[]>(this.restUrl(path)).pipe(map(list => list.map(fromJson).sort(compare))).subscribe(next);
|
this.http.get<any[]>(this.restUrl(path)).pipe(map(list => list.map(fromJson).sort(compare))).subscribe(next, errorInterceptor(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
postReturnNone<T>(path: string, data: any, next: (_: void) => void = NO_OP) {
|
postReturnNone<T>(path: string, data: any, next: (_: void) => void = NO_OP, error: (error: any) => void = NO_OP) {
|
||||||
this.http.post<any>(this.restUrl(path), data).subscribe(next);
|
this.http.post<any>(this.restUrl(path), data).subscribe(next, errorInterceptor(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
postReturnItem<T>(path: string, data: any, fromJson: (json: any) => T, next: (item: T) => void = NO_OP) {
|
postReturnItem<T>(path: string, data: any, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) {
|
||||||
this.http.post<any>(this.restUrl(path), data).pipe(map(fromJson)).subscribe(next);
|
this.http.post<any>(this.restUrl(path), data).pipe(map(fromJson)).subscribe(next, errorInterceptor(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
putReturnItem<T>(path: string, data: any, fromJson: (json: any) => T, next: (item: T) => void = NO_OP) {
|
putReturnItem<T>(path: string, data: any, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) {
|
||||||
this.http.put<any>(this.restUrl(path), data).pipe(map(fromJson)).subscribe(next);
|
this.http.put<any>(this.restUrl(path), data).pipe(map(fromJson)).subscribe(next, errorInterceptor(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
postReturnPage<T>(path: string, data: any, fromJson: (json: any) => T, next: (page: Page<T>) => void = NO_OP) {
|
postReturnPage<T>(path: string, data: any, fromJson: (json: any) => T, next: (page: Page<T>) => void = NO_OP, error: (error: any) => void = NO_OP) {
|
||||||
this.http.post<any>(this.restUrl(path), data).pipe(map(Page.fromJsonGenerator(fromJson))).subscribe(next);
|
this.http.post<any>(this.restUrl(path), data).pipe(map(Page.fromJsonGenerator(fromJson))).subscribe(next, errorInterceptor(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteReturnItem<T>(path: string, fromJson: (json: any) => T, next: (item: T) => void = NO_OP) {
|
deleteReturnItem<T>(path: string, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) {
|
||||||
this.http.delete<any>(this.restUrl(path)).pipe(map(fromJson)).subscribe(next);
|
this.http.delete<any>(this.restUrl(path)).pipe(map(fromJson)).subscribe(next, errorInterceptor(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
postReturnList<T>(path: string, data: any, fromJson: (json: any) => T, next: (list: T[]) => void = NO_OP) {
|
postReturnList<T>(path: string, data: any, fromJson: (json: any) => T, next: (list: T[]) => void = NO_OP, error: (error: any) => void = NO_OP) {
|
||||||
this.http.post<any>(this.restUrl(path), data).pipe(map(list => list.map(fromJson))).subscribe(next);
|
this.http.post<any>(this.restUrl(path), data).pipe(map(list => list.map(fromJson))).subscribe(next, errorInterceptor(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(path: string, next: Next<void>): void {
|
delete(path: string, next: Next<void>): void {
|
||||||
|
|||||||
16
src/main/angular/src/app/api/area/area.service.spec.ts
Normal file
16
src/main/angular/src/app/api/area/area.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {AreaService} from './area.service';
|
||||||
|
|
||||||
|
describe('AreaService', () => {
|
||||||
|
let service: AreaService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(AreaService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -16,8 +16,8 @@ export class AreaService implements ISearchService {
|
|||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(next: (list: Area[]) => void, compare: (a: Area, b: Area) => number = NO_COMPARE): void {
|
findAll(next: (list: Area[]) => void, compare: (a: Area, b: Area) => number = NO_COMPARE, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getList("area/findAll", Area.fromJson, compare, next);
|
this.api.getList("area/findAll", Area.fromJson, compare, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(next: (area: Update<Area>) => void): void {
|
subscribe(next: (area: Update<Area>) => void): void {
|
||||||
@ -25,15 +25,15 @@ export class AreaService implements ISearchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
||||||
this.api.getReturnItem("area/searchById/" + id, SearchResult.fromJson, next);
|
this.api.getReturnItem("area/searchById/" + id, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
search(term: string, next: (results: SearchResult[]) => void = NO_OP): void {
|
search(term: string, next: (results: SearchResult[]) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnList("area/searchLike", term, SearchResult.fromJson, next);
|
this.api.postReturnList("area/searchLike", term, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(area: Area, key: string, value: any, next: (item: Area) => void = NO_OP): void {
|
set(area: Area, key: string, value: any, next: (item: Area) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnItem("area/set/" + area.id + "/" + key, value, Area.fromJson, next);
|
this.api.postReturnItem("area/set/" + area.id + "/" + key, value, Area.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
import {validateDateAllowNull, validateNumberNotNull, validateStringNullToEmpty} from "../validators";
|
|
||||||
|
|
||||||
export class Astro {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly zenith: number,
|
|
||||||
readonly sunrise: Date,
|
|
||||||
readonly sunset: Date,
|
|
||||||
readonly sunriseName: string,
|
|
||||||
readonly sunsetName: string,
|
|
||||||
) {
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: any): Astro {
|
|
||||||
return new Astro(
|
|
||||||
validateNumberNotNull(json['zenith']),
|
|
||||||
validateDateAllowNull(json['sunrise']),
|
|
||||||
validateDateAllowNull(json['sunset']),
|
|
||||||
validateStringNullToEmpty(json['sunriseName']),
|
|
||||||
validateStringNullToEmpty(json['sunsetName']),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -25,14 +25,4 @@ export class BulkEntry {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static compareName(a: BulkEntry, b: BulkEntry) {
|
|
||||||
if (!a.property) {
|
|
||||||
return +1;
|
|
||||||
}
|
|
||||||
if (!b.property) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return a.property.title.localeCompare(b.property.title);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,48 +30,40 @@ export class BulkService implements ISearchService {
|
|||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(next: (list: Bulk[]) => void, compare: (a: Bulk, b: Bulk) => number = NO_COMPARE): void {
|
findAll(next: (list: Bulk[]) => void, compare: (a: Bulk, b: Bulk) => number = NO_COMPARE, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getList("bulk/findAll", Bulk.fromJson, compare, next);
|
this.api.getList("bulk/findAll", Bulk.fromJson, compare, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(next: (bulk: Update<Bulk>) => void): void {
|
subscribe(next: (bulk: Update<Bulk>) => void): void {
|
||||||
this.api.subscribe("BulkDto", Bulk.fromJson, next);
|
this.api.subscribe("BulkDto", Bulk.fromJson, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
getById(id: number, next: (results: Bulk) => void): void {
|
getById(id: number, next: (results: Bulk) => void, error?: (error: any) => void): void {
|
||||||
this.api.getReturnItem("bulk/getById/" + id, Bulk.fromJson, next);
|
this.api.getReturnItem("bulk/getById/" + id, Bulk.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
searchById(id: number, next: (results: SearchResult) => void): void {
|
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
||||||
this.api.getReturnItem("bulk/searchById/" + id, SearchResult.fromJson, next);
|
this.api.getReturnItem("bulk/searchById/" + id, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
search(term: string, next: (results: SearchResult[]) => void = NO_OP): void {
|
search(term: string, next: (results: SearchResult[]) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnList("bulk/searchLike", term, SearchResult.fromJson, next);
|
this.api.postReturnList("bulk/searchLike", term, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(bulk: Bulk, key: string, value: any, next: (item: Bulk) => void = NO_OP): void {
|
set(bulk: Bulk, key: string, value: any, next: (item: Bulk) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnItem("bulk/set/" + bulk.id + "/" + key, value, Bulk.fromJson, next);
|
this.api.postReturnItem("bulk/set/" + bulk.id + "/" + key, value, Bulk.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
create(next: (item: Bulk) => void): void {
|
create(next: (item: Bulk) => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnItem("bulk/create", new BulkCreate("Neu", true), Bulk.fromJson, next);
|
this.api.postReturnItem("bulk/create/", new BulkCreate("Neu", true), Bulk.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(bulk: Bulk, next: () => void): void {
|
delete(bulk: Bulk, next: () => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getReturnItem("bulk/delete/" + bulk.id, _ => _, next);
|
this.api.getReturnItem("bulk/delete/" + bulk.id, _ => _, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
run(bulk: Bulk, next?: () => void): void {
|
setEntry(entry: BulkEntry, key: string, value: any, next: (result: BulkEntry) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getReturnItem("bulk/run/" + bulk.id, _ => _, next);
|
this.api.putReturnItem("BulkEntry/" + entry.id + "/set/" + key, value, next, error);
|
||||||
}
|
|
||||||
|
|
||||||
duplicate(bulk: Bulk, next: (item: Bulk) => void): void {
|
|
||||||
this.api.getReturnItem("bulk/duplicate/" + bulk.id, _ => _, next);
|
|
||||||
}
|
|
||||||
|
|
||||||
setEntry(entry: BulkEntry, key: string, value: any, next: (result: BulkEntry) => void = NO_OP): void {
|
|
||||||
this.api.putReturnItem("BulkEntry/" + entry.id + "/set/" + key, value, next);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteEntry(entry: BulkEntry, next: Next<void>): void {
|
deleteEntry(entry: BulkEntry, next: Next<void>): void {
|
||||||
|
|||||||
16
src/main/angular/src/app/api/channel/channel.service.spec.ts
Normal file
16
src/main/angular/src/app/api/channel/channel.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {ChannelService} from './channel.service';
|
||||||
|
|
||||||
|
describe('ChannelService', () => {
|
||||||
|
let service: ChannelService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ChannelService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -16,8 +16,8 @@ export class ChannelService implements ISearchService {
|
|||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(next: (list: Channel[]) => void, compare: (a: Channel, b: Channel) => number = NO_COMPARE): void {
|
findAll(next: (list: Channel[]) => void, compare: (a: Channel, b: Channel) => number = NO_COMPARE, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getList("channel/findAll", Channel.fromJson, compare, next);
|
this.api.getList("channel/findAll", Channel.fromJson, compare, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(next: (channel: Update<Channel>) => void): void {
|
subscribe(next: (channel: Update<Channel>) => void): void {
|
||||||
@ -25,15 +25,15 @@ export class ChannelService implements ISearchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
||||||
this.api.getReturnItem("channel/searchById/" + id, SearchResult.fromJson, next);
|
this.api.getReturnItem("channel/searchById/" + id, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
search(term: string, next: (results: SearchResult[]) => void = NO_OP): void {
|
search(term: string, next: (results: SearchResult[]) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnList("channel/searchLike", term, SearchResult.fromJson, next);
|
this.api.postReturnList("channel/searchLike", term, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(channel: Channel, key: string, value: any, next: (item: Channel) => void = NO_OP): void {
|
set(channel: Channel, key: string, value: any, next: (item: Channel) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnItem("channel/set/" + channel.id + "/" + key, value, Channel.fromJson, next);
|
this.api.postReturnItem("channel/set/" + channel.id + "/" + key, value, Channel.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,48 +62,12 @@ export abstract class Device {
|
|||||||
return a.position - b.position;
|
return a.position - b.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static compareTitle(a: Device, b: Device): number {
|
|
||||||
return a.title.localeCompare(b.title);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract updateProperty(property: Property): void;
|
abstract updateProperty(property: Property): void;
|
||||||
|
|
||||||
static filterByAreaIdAndRoomId(areaId: number | null, roomId: number | null): (_: Device) => boolean {
|
static filterByAreaIdAndRoomId(areaId: number | null, roomId: number | null): (_: Device) => boolean {
|
||||||
return d => d.areaId === areaId && d.roomId === roomId;
|
return d => d.areaId === areaId && d.roomId === roomId;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSwitchClassList(): object {
|
|
||||||
if (!(this instanceof DeviceSwitch)) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
const value: number | null | undefined = (this as DeviceSwitch).stateProperty?.readChannel?.value;
|
|
||||||
return {
|
|
||||||
deviceSwitchOnBack: value === 1,
|
|
||||||
deviceSwitchOffBack: value === 0,
|
|
||||||
disabledBack: value === null || value === undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getStateSceneClassList(): object {
|
|
||||||
if (!(this instanceof DeviceStateScene)) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
return this.getSwitchClassList();
|
|
||||||
}
|
|
||||||
|
|
||||||
getShutterClassList(): object {
|
|
||||||
if (!(this instanceof DeviceShutter)) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
const value: number | null | undefined = (this as DeviceShutter).positionProperty?.readChannel?.value;
|
|
||||||
return {
|
|
||||||
deviceShutterOpenBack: value === 0,
|
|
||||||
deviceShutterIntermediateBack: value !== null && value !== undefined && value > 0 && value < 100,
|
|
||||||
deviceShutterClosedBack: value === 100,
|
|
||||||
disabledBack: value === null || value === undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DeviceSwitch extends Device {
|
export class DeviceSwitch extends Device {
|
||||||
|
|||||||
16
src/main/angular/src/app/api/device/device.service.spec.ts
Normal file
16
src/main/angular/src/app/api/device/device.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {DeviceService} from './device.service';
|
||||||
|
|
||||||
|
describe('DeviceService', () => {
|
||||||
|
let service: DeviceService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(DeviceService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -20,24 +20,24 @@ export class DeviceService {
|
|||||||
this.api.subscribe("DeviceDto", Device.fromJson, next);
|
this.api.subscribe("DeviceDto", Device.fromJson, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(next: (list: Device[]) => void, compare: (a: Device, b: Device) => number = NO_COMPARE): 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);
|
this.api.getList("device/findAll", Device.fromJson, compare, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(device: Device, key: string, value: any, next?: (item: Device) => void): void {
|
set(device: Device, key: string, value: any, next: (item: Device) => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnItem("device/set/" + device.id + "/" + key, value, Device.fromJson, next);
|
this.api.postReturnItem("device/set/" + device.id + "/" + key, value, Device.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
setDeviceSwitch(device: Device, key: string, value: any, next: (item: Device) => 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);
|
this.api.postReturnItem("device/set/" + device.id + "/DeviceSwitch/" + key, value, Device.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
setDeviceStateScene(device: Device, key: string, value: any, next: (item: Device) => 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);
|
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): void {
|
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);
|
this.api.postReturnItem("device/set/" + device.id + "/DeviceShutter/" + key, value, Device.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSwitchState(d: Device, value: boolean): void {
|
setSwitchState(d: Device, value: boolean): void {
|
||||||
@ -64,16 +64,16 @@ export class DeviceService {
|
|||||||
this.propertyService.set(device.positionProperty, "value", value);
|
this.propertyService.set(device.positionProperty, "value", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
getById(id: number, next: (item: Device) => void): void {
|
getById(id: number, next: (item: Device) => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getReturnItem("device/searchById/" + id, Device.fromJson, next);
|
this.api.getReturnItem("device/searchById/" + id, Device.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
create(type: string, next: (item: Device) => void): void {
|
create(type: string, next: (item: Device) => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnItem("device/create", type, Device.fromJson, next);
|
this.api.postReturnItem("device/create/", type, Device.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(device: Device, next: () => void): void {
|
delete(device: Device, next: () => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getReturnItem("device/delete/" + device.id, _ => _, next);
|
this.api.getReturnItem("device/delete/" + device.id, _ => _, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,37 +1,20 @@
|
|||||||
import {undefinedOrNull, validateListOrEmpty, validateNumberNotNull, validateStringEmptyToNull, validateStringNotEmptyNotNull} from "../validators";
|
import {validateDateAllowNull, validateListOrEmpty, validateNumberAllowNull, validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
|
||||||
import {Channel} from "../channel/Channel";
|
import {Channel} from "../channel/Channel";
|
||||||
import {SearchResult} from "../SearchResult";
|
import {SearchResult} from "../SearchResult";
|
||||||
import {environment} from "../../../environments/environment";
|
|
||||||
|
|
||||||
export enum PropertyType {
|
|
||||||
BOOLEAN = 'BOOLEAN',
|
|
||||||
SHUTTER = 'SHUTTER',
|
|
||||||
BRIGHTNESS_PERCENT = 'BRIGHTNESS_PERCENT',
|
|
||||||
COLOR_TEMPERATURE = 'COLOR_TEMPERATURE',
|
|
||||||
LUX = 'LUX',
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Property {
|
export class Property {
|
||||||
|
|
||||||
readonly hrefId: string | null;
|
|
||||||
|
|
||||||
readonly hrefSlug: string | null;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public id: number,
|
public id: number,
|
||||||
public type: PropertyType,
|
public type: string,
|
||||||
public title: string,
|
public title: string,
|
||||||
public slug: string | null,
|
public value: number | null,
|
||||||
|
public timestamp: Date | null,
|
||||||
public readChannel: Channel | null,
|
public readChannel: Channel | null,
|
||||||
public writeChannel: Channel | null,
|
public writeChannel: Channel | null,
|
||||||
public usages: SearchResult[],
|
public usages: SearchResult[],
|
||||||
) {
|
) {
|
||||||
if (writeChannel || true) {
|
// nothing
|
||||||
this.hrefId = environment.restBase + '/property/write/id/' + id + '/'
|
|
||||||
if (slug) {
|
|
||||||
this.hrefSlug = environment.restBase + '/property/write/slug/' + slug + '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJsonAllowNull(json: any): Property | null {
|
static fromJsonAllowNull(json: any): Property | null {
|
||||||
@ -44,9 +27,10 @@ export class Property {
|
|||||||
static fromJson(json: any): Property {
|
static fromJson(json: any): Property {
|
||||||
return new Property(
|
return new Property(
|
||||||
validateNumberNotNull(json['id']),
|
validateNumberNotNull(json['id']),
|
||||||
validateStringNotEmptyNotNull(json['type']) as PropertyType,
|
validateStringNotEmptyNotNull(json['type']),
|
||||||
validateStringNotEmptyNotNull(json['title']),
|
validateStringNotEmptyNotNull(json['title']),
|
||||||
validateStringEmptyToNull(json['slug']),
|
validateNumberAllowNull(json['value']),
|
||||||
|
validateDateAllowNull(json['timestamp']),
|
||||||
Channel.fromJsonAllowNull(json['readChannel']),
|
Channel.fromJsonAllowNull(json['readChannel']),
|
||||||
Channel.fromJsonAllowNull(json['writeChannel']),
|
Channel.fromJsonAllowNull(json['writeChannel']),
|
||||||
validateListOrEmpty(json['usages'], SearchResult.fromJson),
|
validateListOrEmpty(json['usages'], SearchResult.fromJson),
|
||||||
@ -71,32 +55,4 @@ export class Property {
|
|||||||
}
|
}
|
||||||
return a.title.localeCompare(b.title);
|
return a.title.localeCompare(b.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
getStateClassList() {
|
|
||||||
const value = this.readChannel?.value;
|
|
||||||
switch (this.type) {
|
|
||||||
case PropertyType.BOOLEAN:
|
|
||||||
return {
|
|
||||||
propertyStateBooleanUnknown: undefinedOrNull(value),
|
|
||||||
propertyStateBooleanTrue: value > 0,
|
|
||||||
propertyStateBooleanFalse: value === 0,
|
|
||||||
};
|
|
||||||
case PropertyType.SHUTTER:
|
|
||||||
return {
|
|
||||||
propertyStatePercentUnknown: undefinedOrNull(value),
|
|
||||||
propertyStatePercentActive: value === 0,
|
|
||||||
propertyStatePercentBetween: value > 0 && value < 100,
|
|
||||||
propertyStatePercentInactive: value === 100,
|
|
||||||
};
|
|
||||||
case PropertyType.BRIGHTNESS_PERCENT:
|
|
||||||
return {
|
|
||||||
propertyStatePercentUnknown: undefinedOrNull(value),
|
|
||||||
propertyStatePercentActive: value === 100,
|
|
||||||
propertyStatePercentBetween: value > 0 && value < 100,
|
|
||||||
propertyStatePercentInactive: value === 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {PropertyService} from './property.service';
|
||||||
|
|
||||||
|
describe('PropertyService', () => {
|
||||||
|
let service: PropertyService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(PropertyService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -17,8 +17,8 @@ export class PropertyService implements ISearchService {
|
|||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(next: (list: Property[]) => void, compare: (a: Property, b: Property) => number = NO_COMPARE): 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);
|
this.api.getList("property/findAll", Property.fromJson, compare, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(next: (property: Update<Property>) => void): void {
|
subscribe(next: (property: Update<Property>) => void): void {
|
||||||
@ -26,19 +26,19 @@ export class PropertyService implements ISearchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
||||||
this.api.getReturnItem("property/searchById/" + id, SearchResult.fromJson, next);
|
this.api.getReturnItem("property/searchById/" + id, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
search(term: string, next: (results: SearchResult[]) => void = NO_OP): void {
|
search(term: string, next: (results: SearchResult[]) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnList("property/searchLike", term, SearchResult.fromJson, next);
|
this.api.postReturnList("property/searchLike", term, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(property: Property, key: string, value: any, next: (item: Property) => 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.postReturnItem("property/set/" + property.id + "/" + key, value, Property.fromJson, next);
|
this.api.postReturnItem("property/set/" + property.id + "/" + key, value, Property.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
create(next: (item: Property) => void): void {
|
create(next: (item: Property) => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getReturnItem("property/create", Property.fromJson, next);
|
this.api.getReturnItem("property/create/", Property.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(property: Property, next: Next<void>): void {
|
delete(property: Property, next: Next<void>): void {
|
||||||
|
|||||||
16
src/main/angular/src/app/api/room/room.service.spec.ts
Normal file
16
src/main/angular/src/app/api/room/room.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {RoomService} from './room.service';
|
||||||
|
|
||||||
|
describe('RoomService', () => {
|
||||||
|
let service: RoomService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(RoomService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -16,8 +16,8 @@ export class RoomService implements ISearchService {
|
|||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(next: (list: Room[]) => void, compare: (a: Room, b: Room) => number = NO_COMPARE): void {
|
findAll(next: (list: Room[]) => void, compare: (a: Room, b: Room) => number = NO_COMPARE, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getList("room/findAll", Room.fromJson, compare, next);
|
this.api.getList("room/findAll", Room.fromJson, compare, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(next: (room: Update<Room>) => void): void {
|
subscribe(next: (room: Update<Room>) => void): void {
|
||||||
@ -25,15 +25,15 @@ export class RoomService implements ISearchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
||||||
this.api.getReturnItem("room/searchById/" + id, SearchResult.fromJson, next);
|
this.api.getReturnItem("room/searchById/" + id, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
search(term: string, next: (results: SearchResult[]) => void = NO_OP): void {
|
search(term: string, next: (results: SearchResult[]) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnList("room/searchLike", term, SearchResult.fromJson, next);
|
this.api.postReturnList("room/searchLike", term, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(room: Room, key: string, value: any, next: (item: Room) => void = NO_OP): void {
|
set(room: Room, key: string, value: any, next: (item: Room) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnItem("room/set/" + room.id + "/" + key, value, Room.fromJson, next);
|
this.api.postReturnItem("room/set/" + room.id + "/" + key, value, Room.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -16,8 +16,8 @@ export class SceneService implements ISearchService {
|
|||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(next: (list: Scene[]) => void, compare: (a: Scene, b: Scene) => number = NO_COMPARE): void {
|
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);
|
this.api.getList("scene/findAll", Scene.fromJson, compare, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(next: (scene: Update<Scene>) => void): void {
|
subscribe(next: (scene: Update<Scene>) => void): void {
|
||||||
@ -25,15 +25,15 @@ export class SceneService implements ISearchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
searchById(id: number, next: (results: SearchResult) => void, error: (error: any) => void): void {
|
||||||
this.api.getReturnItem("scene/searchById/" + id, SearchResult.fromJson, next);
|
this.api.getReturnItem("scene/searchById/" + id, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
search(term: string, next: (results: SearchResult[]) => void = NO_OP): void {
|
search(term: string, next: (results: SearchResult[]) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnList("scene/searchLike", term, SearchResult.fromJson, next);
|
this.api.postReturnList("scene/searchLike", term, SearchResult.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(scene: Scene, key: string, value: any, next: (item: Scene) => void = NO_OP): void {
|
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);
|
this.api.postReturnItem("scene/set/" + scene.id + "/" + key, value, Scene.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,18 +3,13 @@ import {ScheduleEntry} from "./entry/ScheduleEntry";
|
|||||||
|
|
||||||
export class Schedule {
|
export class Schedule {
|
||||||
|
|
||||||
readonly next?: ScheduleEntry;
|
|
||||||
|
|
||||||
readonly last?: ScheduleEntry;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly id: number,
|
public id: number,
|
||||||
readonly enabled: boolean,
|
public enabled: boolean,
|
||||||
readonly title: string,
|
public title: string,
|
||||||
readonly entries: ScheduleEntry[],
|
public entries: ScheduleEntry[],
|
||||||
) {
|
) {
|
||||||
this.next = entries.filter(e => e.nextFuzzyTimestamp).sort((a, b) => a.nextFuzzyTimestamp.date.getTime() - b.nextFuzzyTimestamp.date.getTime())[0];
|
// nothing
|
||||||
this.last = entries.filter(e => e.lastFuzzyTimestamp).sort((a, b) => b.lastFuzzyTimestamp.date.getTime() - a.lastFuzzyTimestamp.date.getTime())[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: any): Schedule {
|
static fromJson(json: any): Schedule {
|
||||||
@ -22,7 +17,7 @@ export class Schedule {
|
|||||||
validateNumberNotNull(json['id']),
|
validateNumberNotNull(json['id']),
|
||||||
validateBooleanNotNull(json['enabled']),
|
validateBooleanNotNull(json['enabled']),
|
||||||
validateStringNotEmptyNotNull(json['title']),
|
validateStringNotEmptyNotNull(json['title']),
|
||||||
validateListOrEmpty(json['entries'], ScheduleEntry.fromJson, ScheduleEntry.comparePosition),
|
validateListOrEmpty(json['entries'], ScheduleEntry.fromJson, ScheduleEntry.compare),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +25,10 @@ export class Schedule {
|
|||||||
return item.id;
|
return item.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static compareId(a: Schedule, b: Schedule): number {
|
||||||
|
return a.id - b.id;
|
||||||
|
}
|
||||||
|
|
||||||
public static compareName(a: Schedule, b: Schedule): number {
|
public static compareName(a: Schedule, b: Schedule): number {
|
||||||
return a.title.localeCompare(b.title);
|
return a.title.localeCompare(b.title);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,69 +1,43 @@
|
|||||||
import {validateBooleanNotNull, validateDateAllowNull, validateNumberAllowNull, validateNumberNotNull, validateStringNotEmptyNotNull} from "../../validators";
|
import {validateBooleanNotNull, validateDateAllowNull, validateNumberNotNull, validateStringNotEmptyNotNull} from "../../validators";
|
||||||
import {Timestamp} from "../../Timestamp";
|
import {Timestamp} from "../../Timestamp";
|
||||||
import {Property} from "../../property/Property";
|
import {Property} from "../../property/Property";
|
||||||
import {Bulk} from "../../bulk/Bulk";
|
import {Bulk} from "../../bulk/Bulk";
|
||||||
import {formatNumber} from "@angular/common";
|
|
||||||
|
function getDaySeconds(date: Date): number {
|
||||||
|
return date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
export class ScheduleEntry {
|
export class ScheduleEntry {
|
||||||
|
|
||||||
readonly todo: string;
|
|
||||||
|
|
||||||
readonly dayMinute: number;
|
|
||||||
|
|
||||||
readonly anyWeekday: boolean;
|
|
||||||
|
|
||||||
readonly executable: boolean;
|
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
readonly id: number,
|
public id: number,
|
||||||
readonly position: number,
|
public enabled: boolean,
|
||||||
readonly enabled: boolean,
|
public monday: boolean,
|
||||||
readonly monday: boolean,
|
public tuesday: boolean,
|
||||||
readonly tuesday: boolean,
|
public wednesday: boolean,
|
||||||
readonly wednesday: boolean,
|
public thursday: boolean,
|
||||||
readonly thursday: boolean,
|
public friday: boolean,
|
||||||
readonly friday: boolean,
|
public saturday: boolean,
|
||||||
readonly saturday: boolean,
|
public sunday: boolean,
|
||||||
readonly sunday: boolean,
|
public type: string,
|
||||||
readonly type: string,
|
public zenith: number,
|
||||||
readonly zenith: number,
|
public hour: number,
|
||||||
readonly hour: number,
|
public minute: number,
|
||||||
readonly minute: number,
|
public second: number,
|
||||||
readonly second: number,
|
public fuzzySeconds: number,
|
||||||
readonly fuzzySeconds: number,
|
public lastClearTimestamp: Timestamp | null,
|
||||||
readonly skip: number,
|
public nextClearTimestamp: Timestamp | null,
|
||||||
readonly lastClearTimestamp: Timestamp | null,
|
public nextFuzzyTimestamp: Timestamp | null,
|
||||||
readonly nextClearTimestamp: Timestamp | null,
|
public property: Property | null,
|
||||||
readonly nextFuzzyTimestamp: Timestamp | null,
|
public value: number,
|
||||||
readonly lastFuzzyTimestamp: Timestamp | null,
|
public bulk: Bulk | null,
|
||||||
readonly property: Property | null,
|
|
||||||
readonly value: number,
|
|
||||||
readonly bulk: Bulk | null,
|
|
||||||
) {
|
) {
|
||||||
this.dayMinute = hour * 60 + minute;
|
// nothing
|
||||||
this.anyWeekday = this.monday || this.tuesday || this.wednesday || this.thursday || this.friday || this.saturday || this.sunday;
|
|
||||||
this.executable = this.enabled && this.anyWeekday && !!this.bulk;
|
|
||||||
this.todo = this.getToDo();
|
|
||||||
}
|
|
||||||
|
|
||||||
getToDo(): string {
|
|
||||||
let result: string[] = [];
|
|
||||||
if (!this.enabled) {
|
|
||||||
result.push("inaktiv");
|
|
||||||
}
|
|
||||||
if (!this.bulk) {
|
|
||||||
result.push("keine Aktion");
|
|
||||||
}
|
|
||||||
if (!this.anyWeekday) {
|
|
||||||
result.push("kein Wochentag");
|
|
||||||
}
|
|
||||||
return result.join(", ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: any): ScheduleEntry {
|
static fromJson(json: any): ScheduleEntry {
|
||||||
return new ScheduleEntry(
|
return new ScheduleEntry(
|
||||||
validateNumberNotNull(json['id']),
|
validateNumberNotNull(json['id']),
|
||||||
validateNumberNotNull(json['position']),
|
|
||||||
validateBooleanNotNull(json['enabled']),
|
validateBooleanNotNull(json['enabled']),
|
||||||
validateBooleanNotNull(json['monday']),
|
validateBooleanNotNull(json['monday']),
|
||||||
validateBooleanNotNull(json['tuesday']),
|
validateBooleanNotNull(json['tuesday']),
|
||||||
@ -78,11 +52,9 @@ export class ScheduleEntry {
|
|||||||
validateNumberNotNull(json['minute']),
|
validateNumberNotNull(json['minute']),
|
||||||
validateNumberNotNull(json['second']),
|
validateNumberNotNull(json['second']),
|
||||||
validateNumberNotNull(json['fuzzySeconds']),
|
validateNumberNotNull(json['fuzzySeconds']),
|
||||||
validateNumberAllowNull(json['skip']) || 0,
|
|
||||||
Timestamp.fromDateOrNull(validateDateAllowNull(json['lastClearTimestamp'])),
|
Timestamp.fromDateOrNull(validateDateAllowNull(json['lastClearTimestamp'])),
|
||||||
Timestamp.fromDateOrNull(validateDateAllowNull(json['nextClearTimestamp'])),
|
Timestamp.fromDateOrNull(validateDateAllowNull(json['nextClearTimestamp'])),
|
||||||
Timestamp.fromDateOrNull(validateDateAllowNull(json['nextFuzzyTimestamp'])),
|
Timestamp.fromDateOrNull(validateDateAllowNull(json['nextFuzzyTimestamp'])),
|
||||||
Timestamp.fromDateOrNull(validateDateAllowNull(json['lastFuzzyTimestamp'])),
|
|
||||||
Property.fromJsonAllowNull(json['property']),
|
Property.fromJsonAllowNull(json['property']),
|
||||||
validateNumberNotNull(json['value']),
|
validateNumberNotNull(json['value']),
|
||||||
Bulk.fromJsonOrNull(json['bulk']),
|
Bulk.fromJsonOrNull(json['bulk']),
|
||||||
@ -93,12 +65,13 @@ export class ScheduleEntry {
|
|||||||
return item.id;
|
return item.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static comparePosition(a: ScheduleEntry, b: ScheduleEntry): number {
|
public static compare(a: ScheduleEntry, b: ScheduleEntry): number {
|
||||||
return a.position - b.position;
|
if (a.nextFuzzyTimestamp === null) {
|
||||||
}
|
return +1;
|
||||||
|
} else if (b.nextFuzzyTimestamp === null) {
|
||||||
get time(): string {
|
return -1;
|
||||||
return formatNumber(this.hour, 'de-DE', '2.0-0') + ':' + formatNumber(this.minute, 'de-DE', '2.0-0');
|
}
|
||||||
|
return getDaySeconds(a.nextFuzzyTimestamp.date) - getDaySeconds(b.nextFuzzyTimestamp.date);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
export class Zenith {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly title: string,
|
|
||||||
readonly value: number,
|
|
||||||
readonly sunrise: boolean,
|
|
||||||
readonly sunset: boolean,
|
|
||||||
) {
|
|
||||||
// -
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {ScheduleEntryService} from './schedule-entry.service';
|
||||||
|
|
||||||
|
describe('ScheduleEntryService', () => {
|
||||||
|
let service: ScheduleEntryService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ScheduleEntryService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -2,7 +2,6 @@ import {Injectable} from '@angular/core';
|
|||||||
import {ScheduleEntry} from "./ScheduleEntry";
|
import {ScheduleEntry} from "./ScheduleEntry";
|
||||||
import {ApiService, NO_OP} from "../../api.service";
|
import {ApiService, NO_OP} from "../../api.service";
|
||||||
import {Schedule} from "../Schedule";
|
import {Schedule} from "../Schedule";
|
||||||
import {Next} from "../../types";
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -15,16 +14,20 @@ export class ScheduleEntryService {
|
|||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
create(schedule: Schedule, next: Next<ScheduleEntry> = NO_OP): void {
|
findAll(next: (list: ScheduleEntry[]) => void, compare: (a: ScheduleEntry, b: ScheduleEntry) => number, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getReturnItem("schedule/entry/create/" + schedule.id, ScheduleEntry.fromJson, next);
|
this.api.getList("scheduleEntry/findAll", ScheduleEntry.fromJson, compare, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(entry: ScheduleEntry, key: string, value: any, next: Next<ScheduleEntry> = NO_OP): void {
|
set(entry: ScheduleEntry, key: string, value: any, next: (item: ScheduleEntry) => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnItem("schedule/entry/set/" + entry.id + "/" + key, value, ScheduleEntry.fromJson, next);
|
this.api.postReturnItem("schedule/entry/set/" + entry.id + "/" + key, value, ScheduleEntry.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(entry: ScheduleEntry, next: Next<ScheduleEntry> = NO_OP): void {
|
create(schedule: Schedule, next: (item: ScheduleEntry) => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getReturnItem("schedule/entry/delete/" + entry.id, _ => _, next);
|
this.api.getReturnItem("schedule/entry/create/" + schedule.id, ScheduleEntry.fromJson, next, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(entry: ScheduleEntry, next: () => void, error: (error: any) => void = NO_OP): void {
|
||||||
|
this.api.getReturnItem("schedule/entry/delete/" + entry.id, _ => _, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {ScheduleService} from './schedule.service';
|
||||||
|
|
||||||
|
describe('ScheduleService', () => {
|
||||||
|
let service: ScheduleService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ScheduleService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,8 +1,6 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {ApiService, NO_OP} from "../api.service";
|
import {ApiService, NO_OP} from "../api.service";
|
||||||
import {Schedule} from "./Schedule";
|
import {Schedule} from "./Schedule";
|
||||||
import {Next} from "../types";
|
|
||||||
import {Update} from "../Update";
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -15,28 +13,24 @@ export class ScheduleService {
|
|||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(next: Next<Update<Schedule>>): void {
|
findAll(next: (list: Schedule[]) => void, compare: (a: Schedule, b: Schedule) => number, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.subscribe("ScheduleDto", Schedule.fromJson, next);
|
this.api.getList("schedule/findAll", Schedule.fromJson, compare, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(next: Next<Schedule[]>, compare?: (a: Schedule, b: Schedule) => number): void {
|
set(schedule: Schedule, key: string, value: any, next: (item: Schedule) => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getList("schedule/findAll", Schedule.fromJson, compare, next);
|
this.api.postReturnItem("schedule/set/" + schedule.id + "/" + key, value, Schedule.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
getById(id: number, next: (item: Schedule) => void): void {
|
getById(id: number, next: (item: Schedule) => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getReturnItem("schedule/searchById/" + id, Schedule.fromJson, next);
|
this.api.getReturnItem("schedule/searchById/" + id, Schedule.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
create(next: Next<Schedule> = NO_OP): void {
|
create(next: (item: Schedule) => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.getReturnItem("schedule/create", Schedule.fromJson, next);
|
this.api.getReturnItem("schedule/create/", Schedule.fromJson, next, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(schedule: Schedule, key: string, value: any, next: Next<Schedule> = NO_OP): void {
|
delete(schedule: Schedule, next: () => void, error: (error: any) => void = NO_OP): void {
|
||||||
this.api.postReturnItem("schedule/set/" + schedule.id + "/" + key, value, Schedule.fromJson, next);
|
this.api.getReturnItem("schedule/delete/" + schedule.id, _ => _, next, error);
|
||||||
}
|
|
||||||
|
|
||||||
delete(schedule: Schedule, next: Next<Schedule> = NO_OP): void {
|
|
||||||
this.api.getReturnItem("schedule/delete/" + schedule.id, _ => _, next);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,65 +0,0 @@
|
|||||||
import {Inject, Injectable, LOCALE_ID} from '@angular/core';
|
|
||||||
import {DatePipe} from "@angular/common";
|
|
||||||
import {Timestamp} from "./Timestamp";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class TimeService {
|
|
||||||
|
|
||||||
readonly datePipe: DatePipe = new DatePipe(this.locale);
|
|
||||||
|
|
||||||
private _now: Date = new Date();
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@Inject(LOCALE_ID) readonly locale: string,
|
|
||||||
) {
|
|
||||||
// -
|
|
||||||
}
|
|
||||||
|
|
||||||
get now(): Date {
|
|
||||||
return this._now;
|
|
||||||
}
|
|
||||||
|
|
||||||
relativeDate(timestamp: Timestamp | undefined) {
|
|
||||||
const date: Date = timestamp?.date;
|
|
||||||
if (date === undefined || date === null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
const relativeName = this.relativeCalendarDaysName(date);
|
|
||||||
return relativeName + " " + this.datePipe.transform(date, 'HH:mm');
|
|
||||||
}
|
|
||||||
|
|
||||||
relativeCalendarDaysName(date: Date): string {
|
|
||||||
const prefix = this.relativeCalendarDaysPrefix(date);
|
|
||||||
const weekday = date.toLocaleDateString(this.locale, {weekday: 'long'});
|
|
||||||
return prefix + ", " + weekday;
|
|
||||||
}
|
|
||||||
|
|
||||||
private relativeCalendarDaysPrefix(date: Date): string {
|
|
||||||
const days = this.calendarDays(date);
|
|
||||||
if (days < -2) {
|
|
||||||
return "Vor " + -days + " Tagen";
|
|
||||||
} else if (days === -2) {
|
|
||||||
return "Vorgestern";
|
|
||||||
} else if (days === -1) {
|
|
||||||
return "Gestern";
|
|
||||||
} else if (days === 1) {
|
|
||||||
return "Morgen";
|
|
||||||
} else if (days === 2) {
|
|
||||||
return "Übermorgen";
|
|
||||||
} else if (days > 2) {
|
|
||||||
return "In " + days + " Tagen";
|
|
||||||
}
|
|
||||||
return "Heute";
|
|
||||||
}
|
|
||||||
|
|
||||||
private calendarDays(date: Date) {
|
|
||||||
const DAY_MS = 1000 * 60 * 60 * 24;
|
|
||||||
const aMidnight = new Date(this._now.getFullYear(), this._now.getMonth(), this._now.getDate());
|
|
||||||
const bMidnight = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
||||||
const milliseconds = bMidnight.getTime() - aMidnight.getTime();
|
|
||||||
return Math.floor(milliseconds / DAY_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -87,7 +87,3 @@ export function validateMap<T>(json: any, valueFromJson: (json: any) => T): Map<
|
|||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function undefinedOrNull(value: any) {
|
|
||||||
return value === undefined || value === null;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,17 +1,20 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {RouterModule, Routes} from '@angular/router';
|
import {RouterModule, Routes} from '@angular/router';
|
||||||
import {ScheduleListComponent} from "./pages/schedule/list/schedule-list.component";
|
import {ScheduleListComponent} from "./pages/schedule-list/schedule-list.component";
|
||||||
import {ScheduleEditorComponent} from "./pages/schedule/editor/schedule-editor.component";
|
import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
||||||
import {DeviceListComponent} from "./pages/device/list/device-list.component";
|
import {DeviceAllListComponent} from "./pages/device-all-list/device-all-list.component";
|
||||||
import {DeviceEditorComponent} from "./pages/device/editor/device-editor.component";
|
import {DeviceComponent} from "./pages/device/device.component";
|
||||||
import {PropertyListComponent} from "./pages/property/list/property-list.component";
|
import {PropertyListComponent} from "./pages/property-list/property-list.component";
|
||||||
import {ChannelListComponent} from "./pages/channel/list/channel-list.component";
|
import {ChannelListComponent} from "./pages/channel-list/channel-list.component";
|
||||||
import {BulkListComponent} from "./pages/bulk/list/bulk-list.component";
|
import {AreaListComponent} from "./pages/mobile/device-tree/area-list/area-list.component";
|
||||||
import {BulkEditorComponent} from "./pages/bulk/editor/bulk-editor.component";
|
import {RoomListComponent} from "./pages/mobile/device-tree/room-list/room-list.component";
|
||||||
|
import {DeviceListComponent} from "./pages/mobile/device-tree/device-list/device-list.component";
|
||||||
|
import {BulkListComponent} from "./pages/bulk/bulk-list/bulk-list.component";
|
||||||
|
import {BulkComponent} from "./pages/bulk/bulk/bulk.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: 'Device', component: DeviceEditorComponent},
|
{path: 'Device', component: DeviceComponent},
|
||||||
{path: 'DeviceList', component: DeviceListComponent},
|
{path: 'DeviceAllList', component: DeviceAllListComponent},
|
||||||
|
|
||||||
// {path: 'Channel', component: ChannelComponent},
|
// {path: 'Channel', component: ChannelComponent},
|
||||||
{path: 'ChannelList', component: ChannelListComponent},
|
{path: 'ChannelList', component: ChannelListComponent},
|
||||||
@ -19,13 +22,17 @@ const routes: Routes = [
|
|||||||
// {path: 'Property', component: PropertyComponent},
|
// {path: 'Property', component: PropertyComponent},
|
||||||
{path: 'PropertyList', component: PropertyListComponent},
|
{path: 'PropertyList', component: PropertyListComponent},
|
||||||
|
|
||||||
{path: 'Schedule', component: ScheduleEditorComponent},
|
{path: 'Schedule', component: ScheduleComponent},
|
||||||
{path: 'ScheduleList', component: ScheduleListComponent},
|
{path: 'ScheduleList', component: ScheduleListComponent},
|
||||||
|
|
||||||
{path: 'Bulk', component: BulkEditorComponent},
|
{path: 'Bulk', component: BulkComponent},
|
||||||
{path: 'BulkList', component: BulkListComponent},
|
{path: 'BulkList', component: BulkListComponent},
|
||||||
|
|
||||||
{path: '**', redirectTo: '/DeviceList;type=DeviceSwitch'},
|
{path: 'AreaList', component: AreaListComponent},
|
||||||
|
{path: 'RoomList', component: RoomListComponent},
|
||||||
|
{path: 'DeviceList', component: DeviceListComponent},
|
||||||
|
|
||||||
|
{path: '**', redirectTo: '/ScheduleList'},
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|||||||
@ -1,27 +1,22 @@
|
|||||||
<div class="menubar">
|
<div class="menubar">
|
||||||
|
|
||||||
<div class="item" [routerLink]="['/DeviceList', {'type': 'DeviceSwitch'}]" [class.itemActive]="isRouteActive('/DeviceList', 'DeviceSwitch')">
|
|
||||||
Geräte
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item" [routerLink]="['/DeviceList', {'type': 'DeviceShutter'}]" [class.itemActive]="isRouteActive('/DeviceList', 'DeviceShutter')">
|
|
||||||
Rollläden
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item" routerLink="/BulkList" routerLinkActive="itemActive">
|
|
||||||
Stapel
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item" routerLink="/ScheduleList" routerLinkActive="itemActive">
|
<div class="item" routerLink="/ScheduleList" routerLinkActive="itemActive">
|
||||||
Zeitpläne
|
Zeitpläne
|
||||||
</div>
|
</div>
|
||||||
|
<div class="item" routerLink="/DeviceAllList" routerLinkActive="itemActive">
|
||||||
|
Geräte
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item" routerLink="/BulkList" routerLinkActive="itemActive">
|
||||||
|
Massenverarbeitung
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="item itemSecondary" routerLink="/PropertyList" routerLinkActive="itemActive">
|
<div class="item itemSecondary" routerLink="/PropertyList" routerLinkActive="itemActive">
|
||||||
P
|
Eigenschaften
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="item itemSecondary" routerLink="/ChannelList" routerLinkActive="itemActive">
|
<div class="item itemSecondary" routerLink="/ChannelList" routerLinkActive="itemActive">
|
||||||
C
|
Kanäle
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
@import "../config";
|
|
||||||
|
|
||||||
.menubar {
|
.menubar {
|
||||||
border-bottom: @border solid black;
|
border-bottom: 1px solid black;
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
float: left;
|
float: left;
|
||||||
padding: @padding;
|
padding: 10px;
|
||||||
border-right: @border solid black;
|
border-right: 1px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.itemSecondary {
|
.itemSecondary {
|
||||||
float: right;
|
float: right;
|
||||||
border-left: @border solid black;
|
border-left: 1px solid black;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {Params, Router, UrlSegment} from "@angular/router";
|
import {DataService} from "./data.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@ -9,27 +9,10 @@ import {Params, Router, UrlSegment} from "@angular/router";
|
|||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'angular';
|
title = 'angular';
|
||||||
|
|
||||||
private url: UrlSegment[];
|
|
||||||
|
|
||||||
private params: Params;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly router: Router,
|
readonly dataService: DataService,
|
||||||
) {
|
) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
isRouteActive(base: string, type: string): boolean {
|
|
||||||
const parts = this.router.url.split(';');
|
|
||||||
if (parts[0] === base) {
|
|
||||||
for (let i = 1; i < parts.length; i++) {
|
|
||||||
const param = parts[i].split('=');
|
|
||||||
if (param[0] === 'type' && param[1] === type) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,52 +1,43 @@
|
|||||||
import {LOCALE_ID, NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
|
|
||||||
import {AppRoutingModule} from './app-routing.module';
|
import {AppRoutingModule} from './app-routing.module';
|
||||||
import {AppComponent} from './app.component';
|
import {AppComponent} from './app.component';
|
||||||
import {HttpClientModule} from "@angular/common/http";
|
import {HttpClientModule} from "@angular/common/http";
|
||||||
import {FormsModule} from "@angular/forms";
|
import {FormsModule} from "@angular/forms";
|
||||||
import {TextComponent} from './shared/text/text.component';
|
import {EditFieldComponent} from './shared/edit-field/edit-field.component';
|
||||||
import {ScheduleListComponent} from './pages/schedule/list/schedule-list.component';
|
import {ScheduleListComponent} from './pages/schedule-list/schedule-list.component';
|
||||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||||
import {NumberComponent} from './shared/number/number.component';
|
import {NumberComponent} from './shared/number/number.component';
|
||||||
import {ScheduleEditorComponent} from "./pages/schedule/editor/schedule-editor.component";
|
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 {DeviceAllListComponent} from './pages/device-all-list/device-all-list.component';
|
||||||
import {DeviceEditorComponent} from './pages/device/editor/device-editor.component';
|
import {DeviceComponent} from './pages/device/device.component';
|
||||||
import {PropertyListComponent} from './pages/property/list/property-list.component';
|
import {PropertyListComponent} from './pages/property-list/property-list.component';
|
||||||
import {ChannelListComponent} from './pages/channel/list/channel-list.component';
|
import {ChannelListComponent} from './pages/channel-list/channel-list.component';
|
||||||
import {BulkListComponent} from './pages/bulk/list/bulk-list.component';
|
import {AreaListComponent} from './pages/mobile/device-tree/area-list/area-list.component';
|
||||||
import {BulkEditorComponent} from './pages/bulk/editor/bulk-editor.component';
|
import {RoomListComponent} from './pages/mobile/device-tree/room-list/room-list.component';
|
||||||
import {LeftPadDirective} from './pipes/left-pad.directive';
|
import {DeviceListComponent} from './pages/mobile/device-tree/device-list/device-list.component';
|
||||||
import {EntryValueComponent} from './shared/entry-value/entry-value.component';
|
import {BulkListComponent} from './pages/bulk/bulk-list/bulk-list.component';
|
||||||
|
import {BulkComponent} from './pages/bulk/bulk/bulk.component';
|
||||||
import {registerLocaleData} from '@angular/common';
|
|
||||||
import localeDe from '@angular/common/locales/de';
|
|
||||||
import localeDeExtra from '@angular/common/locales/extra/de';
|
|
||||||
import {BoolComponent} from './shared/bool/bool.component';
|
|
||||||
import {DurationComponent} from './shared/duration/duration.component';
|
|
||||||
|
|
||||||
registerLocaleData(localeDe, 'de-DE', localeDeExtra);
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
TextComponent,
|
EditFieldComponent,
|
||||||
ScheduleEditorComponent,
|
ScheduleComponent,
|
||||||
ScheduleListComponent,
|
ScheduleListComponent,
|
||||||
NumberComponent,
|
NumberComponent,
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
DeviceListComponent,
|
DeviceAllListComponent,
|
||||||
DeviceEditorComponent,
|
DeviceComponent,
|
||||||
PropertyListComponent,
|
PropertyListComponent,
|
||||||
ChannelListComponent,
|
ChannelListComponent,
|
||||||
|
AreaListComponent,
|
||||||
|
RoomListComponent,
|
||||||
DeviceListComponent,
|
DeviceListComponent,
|
||||||
BulkListComponent,
|
BulkListComponent,
|
||||||
BulkEditorComponent,
|
BulkComponent,
|
||||||
LeftPadDirective,
|
|
||||||
EntryValueComponent,
|
|
||||||
BoolComponent,
|
|
||||||
DurationComponent,
|
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -55,9 +46,7 @@ registerLocaleData(localeDe, 'de-DE', localeDeExtra);
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
FontAwesomeModule,
|
FontAwesomeModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [],
|
||||||
{provide: LOCALE_ID, useValue: 'de-DE'}
|
|
||||||
],
|
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
|
|||||||
16
src/main/angular/src/app/data.service.spec.ts
Normal file
16
src/main/angular/src/app/data.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {DataService} from './data.service';
|
||||||
|
|
||||||
|
describe('DataService', () => {
|
||||||
|
let service: DataService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(DataService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
33
src/main/angular/src/app/data.service.ts
Normal file
33
src/main/angular/src/app/data.service.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {Schedule} from "./api/schedule/Schedule";
|
||||||
|
import {Device} from "./api/device/Device";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DataService {
|
||||||
|
|
||||||
|
private _schedule?: Schedule;
|
||||||
|
|
||||||
|
get schedule(): Schedule | undefined {
|
||||||
|
return this._schedule;
|
||||||
|
}
|
||||||
|
|
||||||
|
set schedule(schedule: Schedule | undefined) {
|
||||||
|
this._schedule = schedule;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _device?: Device;
|
||||||
|
|
||||||
|
get device(): Device | undefined {
|
||||||
|
return this._device;
|
||||||
|
}
|
||||||
|
|
||||||
|
set device(device: Device | undefined) {
|
||||||
|
this._device = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th> </th>
|
||||||
|
<th>Bezeichnung</th>
|
||||||
|
<th> </th>
|
||||||
|
</tr>
|
||||||
|
<tr *ngFor="let bulk of bulks; trackBy: Bulk.trackBy">
|
||||||
|
|
||||||
|
<td class="boolean" (click)="set(bulk, 'enabled', !bulk.enabled)" [class.true]="bulk.enabled" [class.false]="!bulk.enabled">
|
||||||
|
<fa-icon *ngIf="bulk.enabled" [icon]="faCheckCircle"></fa-icon>
|
||||||
|
<fa-icon *ngIf="!bulk.enabled" [icon]="faCircle"></fa-icon>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td [routerLink]="['/Bulk', {id: bulk.id}]">
|
||||||
|
{{bulk.name}}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="delete" (click)="delete(bulk)">
|
||||||
|
<fa-icon title="Löschen" [icon]="faTimes"></fa-icon>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<button (click)="create()">+ Hinzufügen</button>
|
||||||
|
</p>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {BulkListComponent} from './bulk-list.component';
|
||||||
|
|
||||||
|
describe('BulkListComponent', () => {
|
||||||
|
let component: BulkListComponent;
|
||||||
|
let fixture: ComponentFixture<BulkListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [BulkListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BulkListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {BulkService} from "../../../api/bulk/BulkService";
|
import {BulkService} from "../../../api/bulk/BulkService";
|
||||||
import {Bulk} from "../../../api/bulk/Bulk";
|
import {Bulk} from "../../../api/bulk/Bulk";
|
||||||
import {faCheckCircle, faCircle, faCopy, faPlayCircle, faTimesCircle} from "@fortawesome/free-regular-svg-icons";
|
import {faCheckCircle, faCircle, faTimesCircle} from "@fortawesome/free-regular-svg-icons";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-bulk-list',
|
selector: 'app-bulk-list',
|
||||||
@ -16,8 +16,6 @@ export class BulkListComponent implements OnInit {
|
|||||||
|
|
||||||
readonly faTimes = faTimesCircle;
|
readonly faTimes = faTimesCircle;
|
||||||
|
|
||||||
readonly faPlay = faPlayCircle;
|
|
||||||
|
|
||||||
readonly Bulk = Bulk;
|
readonly Bulk = Bulk;
|
||||||
|
|
||||||
bulks: Bulk[] = [];
|
bulks: Bulk[] = [];
|
||||||
@ -54,21 +52,10 @@ export class BulkListComponent implements OnInit {
|
|||||||
this.bulkService.create(bulk => this.addOrReplace(bulk));
|
this.bulkService.create(bulk => this.addOrReplace(bulk));
|
||||||
}
|
}
|
||||||
|
|
||||||
run(bulk: Bulk): void {
|
|
||||||
if (confirm("Zeitplan \"" + bulk.name + "\" wirklich ausführen?")) {
|
|
||||||
this.bulkService.run(bulk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
duplicate(bulk: Bulk): void {
|
|
||||||
this.bulkService.duplicate(bulk, bulk => this.addOrReplace(bulk));
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(bulk: Bulk): void {
|
delete(bulk: Bulk): void {
|
||||||
if (confirm("Zeitplan \"" + bulk.name + "\" wirklich löschen?")) {
|
if (confirm("Zeitplan \"" + bulk.name + "\" wirklich löschen?")) {
|
||||||
this.bulkService.delete(bulk, () => this.remove(bulk));
|
this.bulkService.delete(bulk, () => this.remove(bulk));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly faCopy = faCopy;
|
|
||||||
}
|
}
|
||||||
@ -1,9 +1,7 @@
|
|||||||
<ng-container *ngIf="bulk">
|
<ng-container *ngIf="bulk">
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
<app-text [initial]="bulk.name" (valueChange)="set(bulk, 'name', $event)"></app-text>
|
<app-edit-field [initial]="bulk.name" (valueChange)="set(bulk, 'name', $event)"></app-edit-field>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Eigenschaft</th>
|
<th>Eigenschaft</th>
|
||||||
@ -11,7 +9,7 @@
|
|||||||
<th> </th>
|
<th> </th>
|
||||||
</tr>
|
</tr>
|
||||||
<ng-container *ngIf="bulk">
|
<ng-container *ngIf="bulk">
|
||||||
<tr *ngFor="let entry of bulk.entries.sort(BulkEntry.compareName)">
|
<tr *ngFor="let entry of bulk.entries">
|
||||||
<td>
|
<td>
|
||||||
<app-search [searchService]="propertyService" [allowEmpty]="false" [initial]="entry.property?.id" (valueChange)="setEntry(entry, 'property', $event)"></app-search>
|
<app-search [searchService]="propertyService" [allowEmpty]="false" [initial]="entry.property?.id" (valueChange)="setEntry(entry, 'property', $event)"></app-search>
|
||||||
</td>
|
</td>
|
||||||
@ -22,7 +20,12 @@
|
|||||||
</td>
|
</td>
|
||||||
<td *ngSwitchCase="'SHUTTER'" [class.true]="entry.value === 0" [class.false]="entry.value === 100" [class.tristate]="0 < entry.value && entry.value < 100">
|
<td *ngSwitchCase="'SHUTTER'" [class.true]="entry.value === 0" [class.false]="entry.value === 100" [class.tristate]="0 < entry.value && entry.value < 100">
|
||||||
<select [ngModel]="entry.value" (ngModelChange)="setEntry(entry, 'value', $event)">
|
<select [ngModel]="entry.value" (ngModelChange)="setEntry(entry, 'value', $event)">
|
||||||
<option *ngFor="let _ of [].constructor(21); let value = index" [ngValue]="value * 5">{{value * 5}}%</option>
|
<option [ngValue]="0">100% Offen</option>
|
||||||
|
<option [ngValue]="35"> 50%</option>
|
||||||
|
<option [ngValue]="55"> 75%</option>
|
||||||
|
<option [ngValue]="75"> 90% Sonnenschutz</option>
|
||||||
|
<option [ngValue]="85">100% Schlitze</option>
|
||||||
|
<option [ngValue]="100">100% Geschlossen</option>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td *ngSwitchCase="'BRIGHTNESS_PERCENT'" [class.true]="entry.value" [class.false]="!entry.value" [class.tristate]="0 < entry.value && entry.value < 100">
|
<td *ngSwitchCase="'BRIGHTNESS_PERCENT'" [class.true]="entry.value" [class.false]="!entry.value" [class.tristate]="0 < entry.value && entry.value < 100">
|
||||||
@ -53,6 +56,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<button (click)="create()">+ Hinzufügen</button>
|
<p>
|
||||||
|
<button (click)="create()">+ Hinzufügen</button>
|
||||||
|
</p>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {BulkComponent} from './bulk.component';
|
||||||
|
|
||||||
|
describe('BulkComponent', () => {
|
||||||
|
let component: BulkComponent;
|
||||||
|
let fixture: ComponentFixture<BulkComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [BulkComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BulkComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -7,13 +7,11 @@ import {BulkEntry} from "../../../api/bulk/BulkEntry";
|
|||||||
import {faTimesCircle} from "@fortawesome/free-regular-svg-icons";
|
import {faTimesCircle} from "@fortawesome/free-regular-svg-icons";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-bulk-editor',
|
selector: 'app-bulk',
|
||||||
templateUrl: './bulk-editor.component.html',
|
templateUrl: './bulk.component.html',
|
||||||
styleUrls: ['./bulk-editor.component.less']
|
styleUrls: ['./bulk.component.less']
|
||||||
})
|
})
|
||||||
export class BulkEditorComponent implements OnInit {
|
export class BulkComponent implements OnInit {
|
||||||
|
|
||||||
protected readonly BulkEntry = BulkEntry;
|
|
||||||
|
|
||||||
readonly faTimes = faTimesCircle;
|
readonly faTimes = faTimesCircle;
|
||||||
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
<div class="tiles">
|
|
||||||
<div class="tile" *ngFor="let bulk of bulks.sort(Bulk.compareName); trackBy: Bulk.trackBy">
|
|
||||||
<div class="tileHead disabledBack" [class.enabledBack]="bulk.enabled">
|
|
||||||
|
|
||||||
<div (click)="set(bulk, 'enabled', !bulk.enabled)">
|
|
||||||
<fa-icon *ngIf="bulk.enabled" [icon]="faCheckCircle"></fa-icon>
|
|
||||||
<fa-icon *ngIf="!bulk.enabled" [icon]="faCircle"></fa-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flexGrow" [routerLink]="['/Bulk', {id: bulk.id}]">
|
|
||||||
{{ bulk.name }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileHeadRight" (click)="run(bulk)">
|
|
||||||
<fa-icon title="Ausführen" [icon]="faPlay"></fa-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileHeadRight" (click)="duplicate(bulk)">
|
|
||||||
<fa-icon title="Duplizieren" [icon]="faCopy"></fa-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileHeadDelete" (click)="delete(bulk)">
|
|
||||||
<fa-icon title="Löschen" [icon]="faTimes"></fa-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="config">
|
|
||||||
<button (click)="create()">+ Hinzufügen</button>
|
|
||||||
</div>
|
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Titel</th>
|
||||||
|
<th>Typ</th>
|
||||||
|
<th colspan="3">Adresse</th>
|
||||||
|
<th>DPT</th>
|
||||||
|
<th colspan="2">Wert</th>
|
||||||
|
</tr>
|
||||||
|
<tr *ngFor="let channel of channels">
|
||||||
|
<td>{{channel.title}}</td>
|
||||||
|
<td>{{channel.type}}</td>
|
||||||
|
<ng-container *ngIf="channel.type === 'KnxGroup'">
|
||||||
|
<td class="first number">{{asKnxGroup(channel).addresMain}} / </td>
|
||||||
|
<td class="middle number">{{asKnxGroup(channel).addresMid}} / </td>
|
||||||
|
<td class="last number">{{asKnxGroup(channel).addresSub}}</td>
|
||||||
|
<td class="number">{{asKnxGroup(channel).dpt}}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="channel.type === 'Logic'">
|
||||||
|
<td colspan="4">{{asLogic(channel).operator}}</td>
|
||||||
|
</ng-container>
|
||||||
|
<td class="number">{{channel.value}}</td>
|
||||||
|
<td>{{channel.timestamp | date:'yyyy-MM-dd HH:mm:ss'}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {ChannelListComponent} from './channel-list.component';
|
||||||
|
|
||||||
|
describe('ChannelListComponent', () => {
|
||||||
|
let component: ChannelListComponent;
|
||||||
|
let fixture: ComponentFixture<ChannelListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ChannelListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ChannelListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {ChannelService} from "../../../api/channel/channel.service";
|
import {ChannelService} from "../../api/channel/channel.service";
|
||||||
import {Channel, KnxGroup, Logic} from "../../../api/channel/Channel";
|
import {Channel, KnxGroup, Logic} from "../../api/channel/Channel";
|
||||||
import {Update} from "../../../api/Update";
|
import {Update} from "../../api/Update";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-channel-list',
|
selector: 'app-channel-list',
|
||||||
@ -1,24 +0,0 @@
|
|||||||
<div class="tiles">
|
|
||||||
<div class="tile" *ngFor="let channel of channels">
|
|
||||||
<div>
|
|
||||||
{{ channel.title }}
|
|
||||||
</div>
|
|
||||||
<div class="left">
|
|
||||||
{{ channel.type }}
|
|
||||||
</div>
|
|
||||||
<ng-container *ngIf="channel.type === 'KnxGroup'">
|
|
||||||
<div class="left">
|
|
||||||
{{ asKnxGroup(channel).addresMain }} / {{ asKnxGroup(channel).addresMid }} / {{ asKnxGroup(channel).addresSub }}
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
DPT {{ asKnxGroup(channel).dpt }}
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
<div class="timestamp">
|
|
||||||
{{ channel.timestamp | date:'yyyy-MM-dd HH:mm:ss' || '-' }}
|
|
||||||
</div>
|
|
||||||
<div class="value">
|
|
||||||
{{ channel.value || '-' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
@import "../../../../config";
|
|
||||||
|
|
||||||
div {
|
|
||||||
padding: @padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timestamp {
|
|
||||||
clear: left;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
clear: right;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
<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.comparePosition); trackBy: Device.trackBy">
|
||||||
|
<ng-container [ngSwitch]="device.type">
|
||||||
|
|
||||||
|
<div class="device" *ngSwitchCase="'DeviceSwitch'" [ngClass]="getSwitchClassList(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">
|
||||||
|
<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)="deviceService.setSwitchState(device, false)"/>
|
||||||
|
</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="title">
|
||||||
|
{{device.title}}
|
||||||
|
</div>
|
||||||
|
<div class="edit" [routerLink]="['/Device', {id: device.id}]">
|
||||||
|
<fa-icon [icon]="faEdit"></fa-icon>
|
||||||
|
</div>
|
||||||
|
<div class="controls">
|
||||||
|
<div class="control button" (click)="deviceService.setShutterPosition(device, 0)">
|
||||||
|
<span class="center">Auf</span>
|
||||||
|
</div>
|
||||||
|
<div class="control button" (click)="deviceService.setShutterPosition(device, 40)">
|
||||||
|
<span class="center">50%</span>
|
||||||
|
</div>
|
||||||
|
<div class="control button" (click)="deviceService.setShutterPosition(device, 75)">
|
||||||
|
<span class="center">90%</span>
|
||||||
|
</div>
|
||||||
|
<div class="control button" (click)="deviceService.setShutterPosition(device, 85)">
|
||||||
|
<span class="center">Schlitze</span>
|
||||||
|
</div>
|
||||||
|
<div class="control button" (click)="deviceService.setShutterPosition(device, 100)">
|
||||||
|
<span class="center">Zu</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
.device {
|
||||||
|
padding: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
float: left;
|
||||||
|
width: 400px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
float: left;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
clear: both;
|
||||||
|
|
||||||
|
.control {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
background-color: lightblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control:hover {
|
||||||
|
background-color: lightskyblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.switchOn {
|
||||||
|
background-color: palegreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switchOff {
|
||||||
|
background-color: indianred;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switchUnknown {
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shutterOpen {
|
||||||
|
background-color: palegreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shutterBetween {
|
||||||
|
background-color: yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shutterClosed {
|
||||||
|
background-color: indianred;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shutterUnknown {
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {DeviceAllListComponent} from './device-all-list.component';
|
||||||
|
|
||||||
|
describe('DeviceAllListComponent', () => {
|
||||||
|
let component: DeviceAllListComponent;
|
||||||
|
let fixture: ComponentFixture<DeviceAllListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [DeviceAllListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DeviceAllListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {DeviceService} from "../../api/device/device.service";
|
||||||
|
import {PropertyService} from "../../api/property/property.service";
|
||||||
|
import {Device, DeviceShutter, DeviceStateScene, DeviceSwitch} from "../../api/device/Device";
|
||||||
|
import {faEdit} from '@fortawesome/free-regular-svg-icons';
|
||||||
|
import {Scene} from "../../api/scene/Scene";
|
||||||
|
import {SceneService} from "../../api/scene/scene.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-device-all-list',
|
||||||
|
templateUrl: './device-all-list.component.html',
|
||||||
|
styleUrls: ['./device-all-list.component.less']
|
||||||
|
})
|
||||||
|
export class DeviceAllListComponent implements OnInit {
|
||||||
|
|
||||||
|
readonly Device = Device;
|
||||||
|
|
||||||
|
readonly faEdit = faEdit;
|
||||||
|
|
||||||
|
devices: Device[] = [];
|
||||||
|
|
||||||
|
scenes: Scene[] = [];
|
||||||
|
|
||||||
|
createType: string = "DeviceSwitch";
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly deviceService: DeviceService,
|
||||||
|
readonly sceneService: SceneService,
|
||||||
|
readonly propertyService: PropertyService,
|
||||||
|
) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
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.sceneService.subscribe(update => this.updateScene(update.payload, update.existing));
|
||||||
|
this.sceneService.findAll(scenes => this.scenes = scenes);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(): void {
|
||||||
|
this.deviceService.create(this.createType, device => this.updateDevice(device, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateDevice(device: Device, existing: boolean): void {
|
||||||
|
const index: number = this.devices.findIndex(d => d.id === device.id);
|
||||||
|
if (index >= 0) {
|
||||||
|
if (existing) {
|
||||||
|
this.devices[index] = device;
|
||||||
|
} else {
|
||||||
|
this.devices.splice(index, 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (existing) {
|
||||||
|
this.devices.push(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
const value: number | null | undefined = (device as DeviceSwitch).stateProperty?.value;
|
||||||
|
return {
|
||||||
|
switchOn: value === 1,
|
||||||
|
switchOff: value === 0,
|
||||||
|
switchUnknown: value === null || value === undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getStateSceneClassList(device: Device): object {
|
||||||
|
return this.getSwitchClassList(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
getShutterClassList(device: Device): object {
|
||||||
|
const value: number | null | undefined = (device as DeviceShutter).positionProperty?.value;
|
||||||
|
return {
|
||||||
|
shutterOpen: value === 0,
|
||||||
|
shutterBetween: value !== null && value !== undefined && value > 0 && value < 100,
|
||||||
|
shutterClosed: value === 100,
|
||||||
|
shutterUnknown: value === null || value === undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -7,7 +7,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<td>
|
<td>
|
||||||
<app-text [initial]="deviceSwitch.title" (valueChange)="set('title', $event)"></app-text>
|
<app-edit-field [initial]="deviceSwitch.title" (valueChange)="set('title', $event)"></app-edit-field>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<td>
|
<td>
|
||||||
<app-text [initial]="deviceStateScene.title" (valueChange)="set('title', $event)"></app-text>
|
<app-edit-field [initial]="deviceStateScene.title" (valueChange)="set('title', $event)"></app-edit-field>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -47,7 +47,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<td>
|
<td>
|
||||||
<app-text [initial]="deviceShutter.title" (valueChange)="set('title', $event)"></app-text>
|
<app-edit-field [initial]="deviceShutter.title" (valueChange)="set('title', $event)"></app-edit-field>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {DeviceComponent} from './device.component';
|
||||||
|
|
||||||
|
describe('DeviceComponent', () => {
|
||||||
|
let component: DeviceComponent;
|
||||||
|
let fixture: ComponentFixture<DeviceComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [DeviceComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DeviceComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,15 +1,16 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
import {PropertyService} from "../../../api/property/property.service";
|
import {DataService} from "../../data.service";
|
||||||
import {Device, DeviceShutter, DeviceStateScene, DeviceSwitch} from "../../../api/device/Device";
|
import {PropertyService} from "../../api/property/property.service";
|
||||||
import {DeviceService} from "../../../api/device/device.service";
|
import {Device, DeviceShutter, DeviceStateScene, DeviceSwitch} from "../../api/device/Device";
|
||||||
|
import {DeviceService} from "../../api/device/device.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-device-editor',
|
selector: 'app-device',
|
||||||
templateUrl: './device-editor.component.html',
|
templateUrl: './device.component.html',
|
||||||
styleUrls: ['./device-editor.component.less']
|
styleUrls: ['./device.component.less']
|
||||||
})
|
})
|
||||||
export class DeviceEditorComponent implements OnInit {
|
export class DeviceComponent implements OnInit {
|
||||||
|
|
||||||
device!: Device;
|
device!: Device;
|
||||||
|
|
||||||
@ -23,12 +24,14 @@ export class DeviceEditorComponent implements OnInit {
|
|||||||
readonly router: Router,
|
readonly router: Router,
|
||||||
readonly activatedRoute: ActivatedRoute,
|
readonly activatedRoute: ActivatedRoute,
|
||||||
readonly deviceService: DeviceService,
|
readonly deviceService: DeviceService,
|
||||||
|
readonly dataService: DataService,
|
||||||
readonly propertyService: PropertyService,
|
readonly propertyService: PropertyService,
|
||||||
) {
|
) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.dataService.device = undefined;
|
||||||
this.activatedRoute.params.subscribe(params => this.deviceService.getById(params['id'], device => this.setDevice(device)));
|
this.activatedRoute.params.subscribe(params => this.deviceService.getById(params['id'], device => this.setDevice(device)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +48,7 @@ export class DeviceEditorComponent implements OnInit {
|
|||||||
this.deviceShutter = device as DeviceShutter;
|
this.deviceShutter = device as DeviceShutter;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
this.dataService.device = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
set(key: string, value: any): void {
|
set(key: string, value: any): void {
|
||||||
@ -53,7 +57,7 @@ export class DeviceEditorComponent implements OnInit {
|
|||||||
|
|
||||||
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", {type: this.device.type}]));
|
this.deviceService.delete(this.device, () => this.router.navigate(["/DeviceAllList"]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
<div class="tiles">
|
|
||||||
|
|
||||||
<ng-container *ngFor="let device of devices; trackBy: Device.trackBy">
|
|
||||||
|
|
||||||
<div class="tile" *ngIf="device.type == 'DeviceSwitch'" [ngClass]="device.getSwitchClassList()">
|
|
||||||
<div class="tileHead">
|
|
||||||
<div class="flexGrow">
|
|
||||||
<app-text [initial]="device.title" (valueChange)="set(device, 'title', $event)"></app-text>
|
|
||||||
</div>
|
|
||||||
<div class="tileHeadRight" [routerLink]="['/Device', {id: device.id}]">
|
|
||||||
<fa-icon [icon]="faEdit"></fa-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="tileBody">
|
|
||||||
<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)="deviceService.setSwitchState(device, false)"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tile" *ngIf="device.type === 'DeviceStateScene'" [ngClass]="device.getStateSceneClassList()">
|
|
||||||
<div class="tileHead">
|
|
||||||
<div class="flexGrow">
|
|
||||||
<app-text [initial]="device.title" (valueChange)="set(device, 'title', $event)"></app-text>
|
|
||||||
</div>
|
|
||||||
<div class="tileHeadRight" [routerLink]="['/Device', {id: device.id}]">
|
|
||||||
<fa-icon [icon]="faEdit"></fa-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="tileBody">
|
|
||||||
<div class="control" *ngFor="let scene of getStateScenes(device)" (click)="deviceService.setStateScene(device, scene.number)">
|
|
||||||
{{ scene.title }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tile" *ngIf="device.type === 'DeviceShutter'" [ngClass]="device.getShutterClassList()">
|
|
||||||
<div class="tileHead">
|
|
||||||
<div class="flexGrow">
|
|
||||||
<app-text [initial]="device.title" (valueChange)="set(device, 'title', $event)"></app-text>
|
|
||||||
</div>
|
|
||||||
<div class="tileHeadRight" [routerLink]="['/Device', {id: device.id}]">
|
|
||||||
<fa-icon [icon]="faEdit"></fa-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="tileBody">
|
|
||||||
<div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 0)">
|
|
||||||
Auf
|
|
||||||
</div>
|
|
||||||
<div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 20)">
|
|
||||||
20%
|
|
||||||
</div>
|
|
||||||
<div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 30)">
|
|
||||||
30%
|
|
||||||
</div>
|
|
||||||
<div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 40)">
|
|
||||||
40%
|
|
||||||
</div>
|
|
||||||
<div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 50)">
|
|
||||||
50%
|
|
||||||
</div>
|
|
||||||
<div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 60)">
|
|
||||||
60%
|
|
||||||
</div>
|
|
||||||
<div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 70)">
|
|
||||||
70%
|
|
||||||
</div>
|
|
||||||
<div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 80)">
|
|
||||||
80%
|
|
||||||
</div>
|
|
||||||
<div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 90)">
|
|
||||||
90%
|
|
||||||
</div>
|
|
||||||
<div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 100)">
|
|
||||||
Zu
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="config">
|
|
||||||
<button (click)="create()">+ Hinzufügen</button>
|
|
||||||
</div>
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
@import "../../../../config";
|
|
||||||
|
|
||||||
.control {
|
|
||||||
float: left;
|
|
||||||
width: 4em;
|
|
||||||
aspect-ratio: 1;
|
|
||||||
margin: @margin;
|
|
||||||
border-radius: 25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlShutter {
|
|
||||||
width: 17.3%;
|
|
||||||
padding-top: 1.1em;
|
|
||||||
text-align: center;
|
|
||||||
margin: calc(@margin / 2);
|
|
||||||
background-color: lightsteelblue;
|
|
||||||
}
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
|
||||||
import {DeviceService} from "../../../api/device/device.service";
|
|
||||||
import {PropertyService} from "../../../api/property/property.service";
|
|
||||||
import {Device, DeviceStateScene} from "../../../api/device/Device";
|
|
||||||
import {faEdit} from '@fortawesome/free-regular-svg-icons';
|
|
||||||
import {Scene} from "../../../api/scene/Scene";
|
|
||||||
import {SceneService} from "../../../api/scene/scene.service";
|
|
||||||
import {ActivatedRoute} from "@angular/router";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-device-list',
|
|
||||||
templateUrl: './device-list.component.html',
|
|
||||||
styleUrls: ['./device-list.component.less']
|
|
||||||
})
|
|
||||||
export class DeviceListComponent implements OnInit {
|
|
||||||
|
|
||||||
readonly Device = Device;
|
|
||||||
|
|
||||||
readonly faEdit = faEdit;
|
|
||||||
|
|
||||||
devices: Device[] = [];
|
|
||||||
|
|
||||||
typeFilter: string;
|
|
||||||
|
|
||||||
scenes: Scene[] = [];
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly deviceService: DeviceService,
|
|
||||||
readonly sceneService: SceneService,
|
|
||||||
readonly propertyService: PropertyService,
|
|
||||||
readonly activatedRoute: ActivatedRoute,
|
|
||||||
) {
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.activatedRoute.params.subscribe(params => {
|
|
||||||
this.typeFilter = params['type'];
|
|
||||||
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.filter(d => !this.typeFilter || this.typeFilter == d.type).sort(Device.compareTitle));
|
|
||||||
|
|
||||||
this.sceneService.subscribe(update => this.updateScene(update.payload, update.existing));
|
|
||||||
this.sceneService.findAll(scenes => this.scenes = scenes);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
create(): void {
|
|
||||||
this.deviceService.create(this.typeFilter, device => this.updateDevice(device, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateDevice(device: Device, existing: boolean): void {
|
|
||||||
const index: number = this.devices.findIndex(d => d.id === device.id);
|
|
||||||
if (index >= 0) {
|
|
||||||
if (existing) {
|
|
||||||
this.devices[index] = device;
|
|
||||||
} else {
|
|
||||||
this.devices.splice(index, 1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (existing) {
|
|
||||||
this.devices.push(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getStateScenes(device: Device): Scene[] {
|
|
||||||
const casted: DeviceStateScene = device as DeviceStateScene;
|
|
||||||
return casted.sceneNumbers.map(sceneNumber => this.scenes.find(scene => scene.number === sceneNumber)).filter(scene => scene !== undefined).map(s => s as Scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
set(device: Device, key: string, value: any): void {
|
|
||||||
this.deviceService.set(device, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<div class="list">
|
||||||
|
|
||||||
|
<div class="entry">
|
||||||
|
Haus
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="entry area" *ngFor="let area of areas" [routerLink]="['/RoomList', {id: area.id}]">
|
||||||
|
{{area.title}}
|
||||||
|
<fa-icon class="icon" [icon]="faArrowAltCircleRight"></fa-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="entry device" *ngFor="let device of devices">
|
||||||
|
{{device.title}}
|
||||||
|
<fa-icon class="icon" [icon]="faPlayCircle"></fa-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {AreaListComponent} from './area-list.component';
|
||||||
|
|
||||||
|
describe('AreaListComponent', () => {
|
||||||
|
let component: AreaListComponent;
|
||||||
|
let fixture: ComponentFixture<AreaListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [AreaListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AreaListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {Area} from "../../../../api/area/Area";
|
||||||
|
import {Device} from "../../../../api/device/Device";
|
||||||
|
import {faArrowAltCircleRight, faPlayCircle} from '@fortawesome/free-regular-svg-icons';
|
||||||
|
import {STUB_AREAS, STUB_DEVICES} from "../../../../api/STUB";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-area-list',
|
||||||
|
templateUrl: './area-list.component.html',
|
||||||
|
styleUrls: ['../device-tree.less']
|
||||||
|
})
|
||||||
|
export class AreaListComponent implements OnInit {
|
||||||
|
|
||||||
|
readonly faArrowAltCircleRight = faArrowAltCircleRight;
|
||||||
|
|
||||||
|
readonly faPlayCircle = faPlayCircle;
|
||||||
|
|
||||||
|
areas: Area[] = STUB_AREAS.sort(Area.comparePosition);
|
||||||
|
|
||||||
|
devices: Device[] = STUB_DEVICES.filter(d => d.areaId === null && d.roomId === null).sort(Device.comparePosition);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<div class="list devices" *ngIf="room !== undefined">
|
||||||
|
|
||||||
|
<div class="entry back" [routerLink]="['/RoomList', {id: room.areaId}]">
|
||||||
|
<fa-icon [icon]="faArrowAltCircleLeft"></fa-icon>
|
||||||
|
|
|
||||||
|
{{room.title}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="entry device {{device.type}}" *ngFor="let device of devices">
|
||||||
|
|
||||||
|
{{device.title}}
|
||||||
|
|
||||||
|
<ng-container *ngIf="device.type === 'DeviceSwitch'">
|
||||||
|
<fa-icon class="icon" [icon]="faTimesCircle"></fa-icon>
|
||||||
|
<fa-icon class="icon" [icon]="faCheckCircle"></fa-icon>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="device.type === 'DeviceStateScene'">
|
||||||
|
<fa-icon class="icon" [icon]="faPlayCircle"></fa-icon>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {DeviceListComponent} from './device-list.component';
|
||||||
|
|
||||||
|
describe('DeviceListComponent', () => {
|
||||||
|
let component: DeviceListComponent;
|
||||||
|
let fixture: ComponentFixture<DeviceListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [DeviceListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DeviceListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {ActivatedRoute} from "@angular/router";
|
||||||
|
import {Device} from "../../../../api/device/Device";
|
||||||
|
import {faArrowAltCircleLeft, faArrowAltCircleRight, faCheckCircle, faPlayCircle, faTimesCircle} from '@fortawesome/free-regular-svg-icons';
|
||||||
|
import {STUB_DEVICES, STUB_ROOMS} from "../../../../api/STUB";
|
||||||
|
import {Room} from "../../../../api/room/Room";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-device-list',
|
||||||
|
templateUrl: './device-list.component.html',
|
||||||
|
styleUrls: ['../device-tree.less']
|
||||||
|
})
|
||||||
|
export class DeviceListComponent implements OnInit {
|
||||||
|
|
||||||
|
readonly faArrowAltCircleLeft = faArrowAltCircleLeft;
|
||||||
|
|
||||||
|
readonly faArrowAltCircleRight = faArrowAltCircleRight;
|
||||||
|
|
||||||
|
readonly faPlayCircle = faPlayCircle;
|
||||||
|
|
||||||
|
readonly faCheckCircle = faCheckCircle;
|
||||||
|
|
||||||
|
readonly faTimesCircle = faTimesCircle;
|
||||||
|
|
||||||
|
room?: Room;
|
||||||
|
|
||||||
|
devices: Device[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly route: ActivatedRoute,
|
||||||
|
) {
|
||||||
|
this.route.paramMap.subscribe(params => {
|
||||||
|
const roomIdStr: string | null = params.get("roomId");
|
||||||
|
if (roomIdStr != null) {
|
||||||
|
const roomId: number = parseInt(roomIdStr);
|
||||||
|
this.room = STUB_ROOMS.find(Room.filterById(roomId));
|
||||||
|
if (this.room) {
|
||||||
|
this.devices = STUB_DEVICES.filter(Device.filterByAreaIdAndRoomId(this.room.areaId, this.room.id)).sort(Device.comparePosition);
|
||||||
|
} else {
|
||||||
|
this.devices = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
.list {
|
||||||
|
font-size: 10vmin;
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
padding: 3vmin;
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
float: right;
|
||||||
|
margin-left: 3vmin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.area {
|
||||||
|
background-color: lightsteelblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.area:active {
|
||||||
|
background-color: cornflowerblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device {
|
||||||
|
background-color: lightcyan;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DeviceSwitch {
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device:active {
|
||||||
|
background-color: darkcyan;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<div class="list rooms" *ngIf="area !== undefined">
|
||||||
|
<div class="entry back" [routerLink]="['/AreaList']">
|
||||||
|
<fa-icon [icon]="faArrowAltCircleLeft"></fa-icon>
|
||||||
|
|
|
||||||
|
{{area.title}}
|
||||||
|
</div>
|
||||||
|
<div class="entry room" *ngFor="let room of rooms" [routerLink]="['/DeviceList', {roomId: room.id}]">
|
||||||
|
{{room.title}}
|
||||||
|
<fa-icon class="icon" [icon]="faArrowAltCircleRight"></fa-icon>
|
||||||
|
</div>
|
||||||
|
<div class="entry device" *ngFor="let device of devices">
|
||||||
|
{{device.title}}
|
||||||
|
<fa-icon class="icon" [icon]="faPlayCircle"></fa-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {RoomListComponent} from './room-list.component';
|
||||||
|
|
||||||
|
describe('RoomListComponent', () => {
|
||||||
|
let component: RoomListComponent;
|
||||||
|
let fixture: ComponentFixture<RoomListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [RoomListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(RoomListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {ActivatedRoute} from "@angular/router";
|
||||||
|
import {Room} from "../../../../api/room/Room";
|
||||||
|
import {Device} from "../../../../api/device/Device";
|
||||||
|
import {faArrowAltCircleLeft, faArrowAltCircleRight, faPlayCircle} from '@fortawesome/free-regular-svg-icons';
|
||||||
|
import {STUB_AREAS, STUB_DEVICES, STUB_ROOMS} from "../../../../api/STUB";
|
||||||
|
import {Area} from "../../../../api/area/Area";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-room-list',
|
||||||
|
templateUrl: './room-list.component.html',
|
||||||
|
styleUrls: ['../device-tree.less']
|
||||||
|
})
|
||||||
|
export class RoomListComponent implements OnInit {
|
||||||
|
|
||||||
|
readonly faArrowAltCircleLeft = faArrowAltCircleLeft;
|
||||||
|
|
||||||
|
readonly faArrowAltCircleRight = faArrowAltCircleRight;
|
||||||
|
|
||||||
|
readonly faPlayCircle = faPlayCircle;
|
||||||
|
|
||||||
|
area?: Area;
|
||||||
|
|
||||||
|
rooms: Room[] = [];
|
||||||
|
|
||||||
|
devices: Device[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly route: ActivatedRoute,
|
||||||
|
) {
|
||||||
|
this.route.paramMap.subscribe(params => {
|
||||||
|
const areaIdStr: string | null = params.get("id");
|
||||||
|
if (areaIdStr != null) {
|
||||||
|
const areaId: number = parseInt(areaIdStr);
|
||||||
|
this.area = STUB_AREAS.find(Area.filterById(areaId));
|
||||||
|
this.rooms = STUB_ROOMS.filter(Room.filterByAreaId(areaId)).sort(Room.comparePosition);
|
||||||
|
this.devices = STUB_DEVICES.filter(Device.filterByAreaIdAndRoomId(areaId, null)).sort(Device.comparePosition);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
<ng-template #empty>
|
||||||
|
<td class="empty">-</td>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="config">
|
||||||
|
<button (click)="create()">+ Hinzufügen</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Bezeichnung</th>
|
||||||
|
<th>Typ</th>
|
||||||
|
<th>Wert</th>
|
||||||
|
<th>Zeitstempel</th>
|
||||||
|
<th>Lesekanal</th>
|
||||||
|
<th>Schreibkanal</th>
|
||||||
|
<th>
|
||||||
|
<fa-icon title="Löschen" [icon]="faTimes"></fa-icon>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<ng-container *ngFor="let property of properties.sort(Property.compareTypeThenTitle)">
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<app-edit-field [initial]="property.title" (valueChange)="edit(property, 'title', $event)"></app-edit-field>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<select [(ngModel)]="property.type" (ngModelChange)="edit(property, 'type', property.type)">
|
||||||
|
<option value="BOOLEAN">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 else empty">
|
||||||
|
<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"}}
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<td *ngIf="property.timestamp !== null else empty">
|
||||||
|
{{property.timestamp | date:'yyyy-MM-dd HH:mm:ss'}}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="full">
|
||||||
|
<app-search [searchService]="channelService" [initial]="property.readChannel?.id" (valueChange)="set(property, 'readChannel', $event)"></app-search>
|
||||||
|
</td>
|
||||||
|
<td class="full">
|
||||||
|
<app-search [searchService]="channelService" [initial]="property.writeChannel?.id" (valueChange)="set(property, 'writeChannel', $event)"></app-search>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="delete">
|
||||||
|
<fa-icon title="Löschen" *ngIf="property.usages.length === 0" [icon]="faTimes" (click)="delete(property)"></fa-icon>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
|
</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();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,11 +1,10 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {Property, PropertyType} from "../../../api/property/Property";
|
import {Property} from "../../api/property/Property";
|
||||||
import {PropertyService} from "../../../api/property/property.service";
|
import {PropertyService} from "../../api/property/property.service";
|
||||||
import {Scene} from "../../../api/scene/Scene";
|
import {Scene} from "../../api/scene/Scene";
|
||||||
import {SceneService} from "../../../api/scene/scene.service";
|
import {SceneService} from "../../api/scene/scene.service";
|
||||||
import {ChannelService} from "../../../api/channel/channel.service";
|
import {ChannelService} from "../../api/channel/channel.service";
|
||||||
import {faTimesCircle} from "@fortawesome/free-regular-svg-icons";
|
import {faTimesCircle} from "@fortawesome/free-regular-svg-icons";
|
||||||
import {environment} from 'src/environments/environment';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-property-list',
|
selector: 'app-property-list',
|
||||||
@ -14,16 +13,12 @@ import {environment} from 'src/environments/environment';
|
|||||||
})
|
})
|
||||||
export class PropertyListComponent implements OnInit {
|
export class PropertyListComponent implements OnInit {
|
||||||
|
|
||||||
readonly environment = environment;
|
|
||||||
|
|
||||||
readonly faTimes = faTimesCircle;
|
readonly faTimes = faTimesCircle;
|
||||||
|
|
||||||
protected booleans: Property[] = [];
|
|
||||||
|
|
||||||
protected shutters: Property[] = [];
|
|
||||||
|
|
||||||
Property = Property;
|
Property = Property;
|
||||||
|
|
||||||
|
properties: Property[] = [];
|
||||||
|
|
||||||
scenes: Scene[] = [];
|
scenes: Scene[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -35,10 +30,7 @@ export class PropertyListComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.propertyService.findAll(properties => {
|
this.propertyService.findAll(properties => this.properties = properties, Property.compareTypeThenTitle);
|
||||||
this.booleans = properties.filter(p => p.type === PropertyType.BOOLEAN).sort(Property.compareTypeThenTitle);
|
|
||||||
this.shutters = properties.filter(p => p.type === PropertyType.SHUTTER).sort(Property.compareTypeThenTitle);
|
|
||||||
}, Property.compareTypeThenTitle);
|
|
||||||
this.propertyService.subscribe(update => this.updateProperty(update.payload, update.existing));
|
this.propertyService.subscribe(update => this.updateProperty(update.payload, update.existing));
|
||||||
|
|
||||||
this.sceneService.findAll(scenes => this.scenes = scenes, Scene.compareNumber);
|
this.sceneService.findAll(scenes => this.scenes = scenes, Scene.compareNumber);
|
||||||
@ -54,23 +46,15 @@ export class PropertyListComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateProperty(property: Property, existing: boolean): void {
|
private updateProperty(property: Property, existing: boolean): void {
|
||||||
if (property.type === PropertyType.BOOLEAN) {
|
const index: number = this.properties.findIndex(p => p.id === property.id);
|
||||||
this.updateProperty2(this.booleans, property, existing);
|
|
||||||
} else if (property.type === PropertyType.SHUTTER) {
|
|
||||||
this.updateProperty2(this.shutters, property, existing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateProperty2(properties: Property[], property: Property, existing: boolean) {
|
|
||||||
const index: number = properties.findIndex(p => p.id === property.id);
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
if (existing) {
|
if (existing) {
|
||||||
properties[index] = property;
|
this.properties[index] = property;
|
||||||
} else {
|
} else {
|
||||||
properties.slice(index, 1);
|
this.properties.slice(index, 1);
|
||||||
}
|
}
|
||||||
} else if (existing) {
|
} else if (existing) {
|
||||||
properties.push(property);
|
this.properties.push(property);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +72,7 @@ export class PropertyListComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
findScene(property: Property): Scene | undefined {
|
findScene(property: Property): Scene | undefined {
|
||||||
return this.scenes.find(s => s.id === property.readChannel?.value);
|
return this.scenes.find(s => s.id === property.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(property: Property, key: string, value: any): void {
|
set(property: Property, key: string, value: any): void {
|
||||||
@ -101,22 +85,8 @@ export class PropertyListComponent implements OnInit {
|
|||||||
|
|
||||||
delete(property: Property): void {
|
delete(property: Property): void {
|
||||||
if (confirm(`Eigenschaft "${property.title}" wirklich löschen?`)) {
|
if (confirm(`Eigenschaft "${property.title}" wirklich löschen?`)) {
|
||||||
this.propertyService.delete(property, () => {
|
this.propertyService.delete(property, () => this.properties.splice(this.properties.findIndex(p => p.id === property.id), 1));
|
||||||
this.delete2(this.booleans, property);
|
|
||||||
this.delete2(this.shutters, property);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private delete2(properties: Property[], property: Property) {
|
|
||||||
const index = properties.findIndex(p => p.id === property.id);
|
|
||||||
if (index >= 0) {
|
|
||||||
properties.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
listLists(): Property[][] {
|
|
||||||
return [this.shutters, this.booleans];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,42 +0,0 @@
|
|||||||
<ng-template #empty>
|
|
||||||
<td class="empty">-</td>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<div class="tiles">
|
|
||||||
|
|
||||||
<div *ngFor="let list of listLists()">
|
|
||||||
<div class="tile" *ngFor="let property of list">
|
|
||||||
|
|
||||||
<div class="tileHead" [ngClass]="property.getStateClassList()">
|
|
||||||
<app-text [initial]="property.title" (valueChange)="edit(property, 'title', $event)"></app-text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileBody">
|
|
||||||
|
|
||||||
<select [(ngModel)]="property.type" (ngModelChange)="edit(property, 'type', property.type)">
|
|
||||||
<option value="BOOLEAN">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>
|
|
||||||
|
|
||||||
<app-search [searchService]="channelService" [initial]="property.readChannel?.id" (valueChange)="set(property, 'readChannel', $event)"></app-search>
|
|
||||||
|
|
||||||
<app-search [searchService]="channelService" [initial]="property.writeChannel?.id" (valueChange)="set(property, 'writeChannel', $event)"></app-search>
|
|
||||||
|
|
||||||
<div class="delete">
|
|
||||||
<fa-icon title="Löschen" *ngIf="property.usages.length === 0" [icon]="faTimes" (click)="delete(property)"></fa-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="config">
|
|
||||||
<button (click)="create()">+ Hinzufügen</button>
|
|
||||||
</div>
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
@import "../../../../config";
|
|
||||||
|
|
||||||
.propertyStateBooleanUnknown {
|
|
||||||
background-color: @COLOR_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
.propertyStateBooleanTrue {
|
|
||||||
background-color: @COLOR_ACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
.propertyStateBooleanFalse {
|
|
||||||
background-color: @COLOR_INACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
.propertyStatePercentUnknown {
|
|
||||||
background-color: @COLOR_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
.propertyStatePercentActive {
|
|
||||||
background-color: @COLOR_ACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
.propertyStatePercentBetween {
|
|
||||||
background-color: @COLOR_BETWEEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
.propertyStatePercentInactive {
|
|
||||||
background-color: @COLOR_INACTIVE;
|
|
||||||
}
|
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th> </th>
|
||||||
|
<th>Bezeichnung</th>
|
||||||
|
<th> </th>
|
||||||
|
</tr>
|
||||||
|
<tr *ngFor="let schedule of schedules; trackBy: Schedule.trackBy">
|
||||||
|
|
||||||
|
<td class="boolean" (click)="set(schedule, 'enabled', !schedule.enabled)" [class.true]="schedule.enabled" [class.false]="!schedule.enabled">
|
||||||
|
<fa-icon *ngIf="schedule.enabled" [icon]="faCheckCircle"></fa-icon>
|
||||||
|
<fa-icon *ngIf="!schedule.enabled" [icon]="faCircle"></fa-icon>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td [routerLink]="['/Schedule', {id: schedule.id}]">
|
||||||
|
{{schedule.title}}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="delete" (click)="delete(schedule)">
|
||||||
|
<fa-icon title="Löschen" [icon]="faTimes"></fa-icon>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<button (click)="create()">+ Hinzufügen</button>
|
||||||
|
</p>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
select {
|
||||||
|
background-color: transparent;
|
||||||
|
border-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: lightblue;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {ScheduleListComponent} from './schedule-list.component';
|
||||||
|
|
||||||
|
describe('ScheduleListComponent', () => {
|
||||||
|
let component: ScheduleListComponent;
|
||||||
|
let fixture: ComponentFixture<ScheduleListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ScheduleListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ScheduleListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {ScheduleService} from "../../api/schedule/schedule.service";
|
||||||
|
import {Schedule} from "../../api/schedule/Schedule";
|
||||||
|
import {faCheckCircle, faCircle, faTimesCircle} from '@fortawesome/free-regular-svg-icons';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-schedule-list',
|
||||||
|
templateUrl: './schedule-list.component.html',
|
||||||
|
styleUrls: ['./schedule-list.component.less']
|
||||||
|
})
|
||||||
|
export class ScheduleListComponent implements OnInit {
|
||||||
|
|
||||||
|
readonly faCheckCircle = faCheckCircle;
|
||||||
|
|
||||||
|
readonly faCircle = faCircle;
|
||||||
|
|
||||||
|
readonly faTimes = faTimesCircle;
|
||||||
|
|
||||||
|
readonly Schedule = Schedule;
|
||||||
|
|
||||||
|
schedules: Schedule[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly scheduleService: ScheduleService,
|
||||||
|
) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.scheduleService.findAll(schedules => this.schedules = schedules, Schedule.compareName);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(schedule: Schedule, key: string, value: any): void {
|
||||||
|
this.scheduleService.set(schedule, key, value, schedule => this.addOrReplace(schedule));
|
||||||
|
}
|
||||||
|
|
||||||
|
private addOrReplace(schedule: Schedule): void {
|
||||||
|
const index: number = this.schedules.findIndex(s => s.id === schedule.id);
|
||||||
|
if (index < 0) {
|
||||||
|
this.schedules.push(schedule);
|
||||||
|
} else {
|
||||||
|
this.schedules[index] = schedule;
|
||||||
|
}
|
||||||
|
this.schedules = this.schedules.sort(Schedule.compareName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private remove(schedule: Schedule): void {
|
||||||
|
this.schedules.splice(this.schedules.findIndex(s => s.id === schedule.id), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(): void {
|
||||||
|
this.scheduleService.create(schedule => this.addOrReplace(schedule));
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(schedule: Schedule): void {
|
||||||
|
if (confirm("Zeitplan \"" + schedule.title + "\" wirklich löschen?")) {
|
||||||
|
this.scheduleService.delete(schedule, () => this.remove(schedule));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,126 +0,0 @@
|
|||||||
<ng-container *ngIf="schedule">
|
|
||||||
|
|
||||||
<div id="title">
|
|
||||||
<app-text [initial]="schedule.title" (valueChange)="scheduleService.set(schedule, 'title', $event)"></app-text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tiles">
|
|
||||||
|
|
||||||
<div class="tile" *ngFor="let entry of schedule.entries; trackBy: ScheduleEntry.trackBy">
|
|
||||||
|
|
||||||
<div class="tileHead disabledBack" [class.enabledBack]="entry.executable" [class.skipBack]="entry.skip">
|
|
||||||
<div class="enabled" (click)="set(entry, 'enabled', !entry.enabled)">
|
|
||||||
<fa-icon *ngIf="entry.enabled" [icon]="faCheckCircle"></fa-icon>
|
|
||||||
<fa-icon *ngIf="!entry.enabled" [icon]="faCircle"></fa-icon>
|
|
||||||
</div>
|
|
||||||
<div class="flexGrow">
|
|
||||||
<select [ngModel]="entry.bulk?.id" (ngModelChange)="entryService.set(entry, 'bulk', $event)">
|
|
||||||
<option [ngValue]="null">-</option>
|
|
||||||
<option [ngValue]="bulk.id" *ngFor="let bulk of bulks.sort(Bulk.compareName)">{{ bulk.name }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="tileHeadDelete">
|
|
||||||
<fa-icon [icon]="faTimesCircle" (click)="delete(entry)"></fa-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileBodyFlex">
|
|
||||||
<div class="weekdays">
|
|
||||||
<div>
|
|
||||||
<app-bool label="Mo" [value]="entry.monday" (onChange)="entryService.set(entry, 'monday', $event)"></app-bool>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<app-bool label="Di" [value]="entry.tuesday" (onChange)="entryService.set(entry, 'tuesday', $event)"></app-bool>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<app-bool label="Mi" [value]="entry.wednesday" (onChange)="entryService.set(entry, 'wednesday', $event)"></app-bool>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<app-bool label="Do" [value]="entry.thursday" (onChange)="entryService.set(entry, 'thursday', $event)"></app-bool>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<app-bool label="Fr" [value]="entry.friday" (onChange)="entryService.set(entry, 'friday', $event)"></app-bool>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<app-bool label="Sa" [value]="entry.saturday" (onChange)="entryService.set(entry, 'saturday', $event)"></app-bool>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<app-bool label="So" [value]="entry.sunday" (onChange)="entryService.set(entry, 'sunday', $event)"></app-bool>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modes">
|
|
||||||
<div class="_inner_">
|
|
||||||
<div>
|
|
||||||
<app-bool icon="faClock" [value]="entry.type === 'TIME'" (onChange)="entryService.set(entry, 'type', 'TIME')"></app-bool>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<app-bool icon="faArrowAltCircleUp" [value]="entry.type === 'SUNRISE'" (onChange)="entryService.set(entry, 'type', 'SUNRISE')"></app-bool>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<app-bool icon="faArrowAltCircleDown" [value]="entry.type === 'SUNSET'" (onChange)="entryService.set(entry, 'type', 'SUNSET')"></app-bool>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileBodyFlex" *ngIf="entry.type === 'TIME'">
|
|
||||||
<div class="flexGrow time">
|
|
||||||
<button class="buttonPlus" (click)="dayMinuteAdd(entry, +60)">+</button>
|
|
||||||
<button class="buttonMinus" (click)="dayMinuteAdd(entry, -60)">-</button>
|
|
||||||
<input type="time" [ngModel]="entry.time" (ngModelChange)="timeFromString(entry, $event)">
|
|
||||||
<button class="buttonPlus" (click)="dayMinuteAdd(entry, +1)">+</button>
|
|
||||||
<button class="buttonMinus" (click)="dayMinuteAdd(entry, -1)">-</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileBodyFlex" *ngIf="entry.type === 'SUNRISE' || entry.type === 'SUNSET'">
|
|
||||||
<div class="flexGrow sun">
|
|
||||||
<div *ngFor="let zenith of getZenithEntries(entry.type)">
|
|
||||||
<app-bool [label]="zenith.title" [value]="entry.zenith === zenith.value" (onChange)="entryService.set(entry, 'zenith', zenith.value)"></app-bool>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="number" min="45" max="120" [ngModel]="entry.zenith" (ngModelChange)="entryService.set(entry, 'zenith', $event || 0)">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileBodyFlex">
|
|
||||||
<div class="flexHalf">
|
|
||||||
<div class="flexIcon">
|
|
||||||
<img class="icon" src="assets/dice.svg" alt="+/-" title="Zufallsabweichung +/-">
|
|
||||||
</div>
|
|
||||||
<div class="flexIconInput">
|
|
||||||
<app-duration [duration]="Duration.ofCode(entry.fuzzySeconds + 's')" [inputClass]="entry.fuzzySeconds ? 'fuzzyBack' : ''" [min]="Duration.ofCode('')" (onChange)="entryService.set(entry, 'fuzzySeconds', $event.totalSeconds)"></app-duration>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flexHalf">
|
|
||||||
<div class="flexIcon">
|
|
||||||
<img class="icon" src="assets/skip.svg" alt="Überspringen">
|
|
||||||
</div>
|
|
||||||
<div class="flexIconInput flexInputLast">
|
|
||||||
<input type="number" min="0" [class.skipBack]="entry.skip" [ngModel]="entry.skip" (ngModelChange)="entryService.set(entry, 'skip', $event || 0)">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileBodyFlex">
|
|
||||||
<div class="flexGrow timestamp" [class.skipFont]="entry.skip" *ngIf="entry.executable">
|
|
||||||
{{ timeService.relativeDate(entry.nextFuzzyTimestamp) }}
|
|
||||||
<span [class.fuzzyFont]="entry.fuzzySeconds" *ngIf="entry.fuzzySeconds">
|
|
||||||
(eig: {{ entry.nextClearTimestamp.date | date:'HH:mm' }})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flexGrow inactive" *ngIf="entry.todo">
|
|
||||||
{{ entry.todo }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="config">
|
|
||||||
<button (click)="create()">+ Hinzufügen</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</ng-container>
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
@import "../../../../config";
|
|
||||||
|
|
||||||
@time_input_width: 35%;
|
|
||||||
|
|
||||||
#title {
|
|
||||||
margin: @margin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weekdays {
|
|
||||||
float: left;
|
|
||||||
width: 75%;
|
|
||||||
border-radius: @border-radius;
|
|
||||||
|
|
||||||
div {
|
|
||||||
float: left;
|
|
||||||
width: 14.2857%;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.modes {
|
|
||||||
float: left;
|
|
||||||
width: 25%;
|
|
||||||
|
|
||||||
._inner_ {
|
|
||||||
margin-left: @margin;
|
|
||||||
border-radius: @border-radius;
|
|
||||||
|
|
||||||
div {
|
|
||||||
float: left;
|
|
||||||
width: 33.3333%;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.time {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: @time_input_width;
|
|
||||||
text-align: center;
|
|
||||||
margin-right: @margin;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: calc(((100% - @time_input_width) - 4 * @margin) / 4);
|
|
||||||
margin-right: @margin;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.sun {
|
|
||||||
|
|
||||||
div {
|
|
||||||
float: left;
|
|
||||||
width: 20%;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.timestamp {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inactive {
|
|
||||||
color: gray;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
@ -1,139 +0,0 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
|
||||||
import {ScheduleService} from "../../../api/schedule/schedule.service";
|
|
||||||
import {Schedule} from "../../../api/schedule/Schedule";
|
|
||||||
import {ScheduleEntry} from "../../../api/schedule/entry/ScheduleEntry";
|
|
||||||
import {ScheduleEntryService} from "../../../api/schedule/entry/schedule-entry.service";
|
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
|
||||||
import {Update} from "../../../api/Update";
|
|
||||||
import {NO_OP} from "../../../api/api.service";
|
|
||||||
|
|
||||||
import {Duration} from "../../../api/Duration";
|
|
||||||
import {Bulk} from "../../../api/bulk/Bulk";
|
|
||||||
import {BulkService} from "../../../api/bulk/BulkService";
|
|
||||||
import {Zenith} from "../../../api/schedule/entry/Zenith";
|
|
||||||
import {TimeService} from "../../../api/time.service";
|
|
||||||
import {faCheckCircle, faCircle, faTimesCircle} from "@fortawesome/free-regular-svg-icons";
|
|
||||||
|
|
||||||
const DAY_MINUTES: number = 24 * 60;
|
|
||||||
|
|
||||||
const ZENITH_ENTRIES: Zenith[] = [
|
|
||||||
new Zenith("Astr.", 107, true, true),
|
|
||||||
new Zenith("Naut.", 102, true, true),
|
|
||||||
new Zenith("Bürg.", 96, true, true),
|
|
||||||
new Zenith("Aufg.", 90.8, true, false),
|
|
||||||
new Zenith("Unterg.", 90.8, false, true),
|
|
||||||
];
|
|
||||||
|
|
||||||
const ZENITH_SUNRISE = ZENITH_ENTRIES.filter(zenith => zenith.sunrise);
|
|
||||||
|
|
||||||
const ZENITH_SUNSET = ZENITH_ENTRIES.filter(zenith => zenith.sunset).reverse();
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-schedule-editor',
|
|
||||||
templateUrl: './schedule-editor.component.html',
|
|
||||||
styleUrls: ['./schedule-editor.component.less']
|
|
||||||
})
|
|
||||||
export class ScheduleEditorComponent implements OnInit {
|
|
||||||
|
|
||||||
protected readonly faCheckCircle = faCheckCircle;
|
|
||||||
|
|
||||||
protected readonly faTimesCircle = faTimesCircle;
|
|
||||||
|
|
||||||
protected readonly faCircle = faCircle;
|
|
||||||
|
|
||||||
protected readonly ScheduleEntry = ScheduleEntry;
|
|
||||||
|
|
||||||
protected readonly Schedule = Schedule;
|
|
||||||
|
|
||||||
protected readonly Duration = Duration;
|
|
||||||
|
|
||||||
protected readonly Bulk = Bulk;
|
|
||||||
|
|
||||||
protected readonly expanded: number[] = [];
|
|
||||||
|
|
||||||
protected now: Date = new Date(Date.now());
|
|
||||||
|
|
||||||
protected schedule!: Schedule;
|
|
||||||
|
|
||||||
protected bulks: Bulk[] = [];
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly router: Router,
|
|
||||||
readonly activatedRoute: ActivatedRoute,
|
|
||||||
readonly scheduleService: ScheduleService,
|
|
||||||
readonly entryService: ScheduleEntryService,
|
|
||||||
readonly bulkService: BulkService,
|
|
||||||
readonly timeService: TimeService,
|
|
||||||
) {
|
|
||||||
// -
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.scheduleService.subscribe(update => this.update(update));
|
|
||||||
this.bulkService.findAll(list => this.bulks = list);
|
|
||||||
this.activatedRoute.params.subscribe(params => this.scheduleService.getById(params['id'], schedule => this.setSchedule(schedule)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private update(update: Update<Schedule>): void {
|
|
||||||
if (!this.schedule || this.schedule.id !== update.payload.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!update.existing) {
|
|
||||||
this.router.navigate(['/ScheduleList']);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setSchedule(update.payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
private setSchedule(schedule: Schedule) {
|
|
||||||
this.schedule = schedule;
|
|
||||||
this.expanded.length = 0;
|
|
||||||
this.expanded.push(...schedule.entries.map(e => e.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
create(): void {
|
|
||||||
this.entryService.create(this.schedule, NO_OP);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(entry: ScheduleEntry): void {
|
|
||||||
if (confirm("Eintrag \"" + this.timeService.relativeDate(entry.nextClearTimestamp) + " +/-" + entry.fuzzySeconds + "\" wirklich löschen?")) {
|
|
||||||
this.entryService.delete(entry, NO_OP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set(entry: ScheduleEntry | null, key: string, value: any): void {
|
|
||||||
if (entry) {
|
|
||||||
this.entryService.set(entry, key, value, NO_OP);
|
|
||||||
} else {
|
|
||||||
this.scheduleService.set(this.schedule, key, value, NO_OP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
timeFromString(entry: ScheduleEntry, time: string) {
|
|
||||||
const parts = time.split(':');
|
|
||||||
const hour = parseInt(parts[0]);
|
|
||||||
const minute = parseInt(parts[1]);
|
|
||||||
let second = 0;
|
|
||||||
if (parts.length === 3) {
|
|
||||||
second = parseInt(parts[2]);
|
|
||||||
}
|
|
||||||
const daySecond = (hour * 24 + minute) * 60 + second;
|
|
||||||
this.entryService.set(entry, 'daySecond', daySecond);
|
|
||||||
}
|
|
||||||
|
|
||||||
dayMinuteAdd(entry: ScheduleEntry, minutes: number) {
|
|
||||||
let newMinutes = entry.dayMinute + minutes;
|
|
||||||
while (newMinutes < 0 || newMinutes >= DAY_MINUTES) {
|
|
||||||
newMinutes = (newMinutes + DAY_MINUTES) % DAY_MINUTES;
|
|
||||||
}
|
|
||||||
this.entryService.set(entry, 'daySecond', newMinutes * 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
getZenithEntries(type: string) {
|
|
||||||
if (type === 'SUNRISE') {
|
|
||||||
return ZENITH_SUNRISE;
|
|
||||||
}
|
|
||||||
return ZENITH_SUNSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
<div class="tiles">
|
|
||||||
|
|
||||||
<div class="tile" *ngFor="let schedule of schedules; trackBy: Schedule.trackBy">
|
|
||||||
|
|
||||||
<div class="tileHead disabledBack" [class.enabledBack]="schedule.next" [class.skipBack]="schedule.next?.skip">
|
|
||||||
|
|
||||||
<div class="enabled" (click)="set(schedule, 'enabled', !schedule.enabled)">
|
|
||||||
<fa-icon *ngIf="schedule.enabled" [icon]="faCheckCircle"></fa-icon>
|
|
||||||
<fa-icon *ngIf="!schedule.enabled" [icon]="faCircle"></fa-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flexGrow" [routerLink]="['/Schedule', {id: schedule.id}]">
|
|
||||||
{{ schedule.title }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileHeadRight" (click)="skip(schedule.next)" *ngIf="schedule.next">
|
|
||||||
<img class="icon" src="assets/skip.svg" [alt]="'Über.'">{{ schedule.next?.skip }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileHeadRight" (click)="delete(schedule)">
|
|
||||||
<fa-icon title="Löschen" [icon]="faTimes"></fa-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileBody next">
|
|
||||||
<div class="timestamp">
|
|
||||||
<ng-container *ngIf="schedule.next">{{ timeService.relativeDate(schedule.next?.nextFuzzyTimestamp) }}</ng-container>
|
|
||||||
</div>
|
|
||||||
<div class="bulk" [class.timestampBulkEmpty]="!schedule.next?.bulk" *ngIf="schedule.next?.bulk">
|
|
||||||
{{ schedule.next?.bulk?.name }}
|
|
||||||
<fa-icon [icon]="faPlayCircle" (click)="execute(schedule.next?.bulk)"></fa-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tileBody last">
|
|
||||||
<div class="timestamp">
|
|
||||||
<ng-container *ngIf="schedule.last">{{ timeService.relativeDate(schedule.last?.lastFuzzyTimestamp) }}</ng-container>
|
|
||||||
<ng-container *ngIf="!schedule.last">- - -</ng-container>
|
|
||||||
</div>
|
|
||||||
<div class="bulk" [class.timestampBulkEmpty]="!schedule.last?.bulk" *ngIf="schedule.last?.bulk">
|
|
||||||
{{ schedule.last?.bulk?.name }}
|
|
||||||
<fa-icon [icon]="faPlayCircle" (click)="execute(schedule.last?.bulk)"></fa-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="config">
|
|
||||||
<button (click)="create()">+ Hinzufügen</button>
|
|
||||||
</div>
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
@import "../../../../config";
|
|
||||||
|
|
||||||
.last {
|
|
||||||
color: gray;
|
|
||||||
font-size: 60%;
|
|
||||||
border-top: @border solid gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timestamp {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bulk {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
|
||||||
import {ScheduleService} from "../../../api/schedule/schedule.service";
|
|
||||||
import {Schedule} from "../../../api/schedule/Schedule";
|
|
||||||
import {faCheckCircle, faCircle, faPlayCircle, faTimesCircle} from '@fortawesome/free-regular-svg-icons';
|
|
||||||
import {NO_OP} from "../../../api/api.service";
|
|
||||||
import {Update} from "../../../api/Update";
|
|
||||||
import {ScheduleEntryService} from "../../../api/schedule/entry/schedule-entry.service";
|
|
||||||
import {ScheduleEntry} from "../../../api/schedule/entry/ScheduleEntry";
|
|
||||||
import {Bulk} from "../../../api/bulk/Bulk";
|
|
||||||
import {BulkService} from "../../../api/bulk/BulkService";
|
|
||||||
import {TimeService} from "../../../api/time.service";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-schedule-list',
|
|
||||||
templateUrl: './schedule-list.component.html',
|
|
||||||
styleUrls: ['./schedule-list.component.less']
|
|
||||||
})
|
|
||||||
export class ScheduleListComponent implements OnInit {
|
|
||||||
|
|
||||||
readonly faCheckCircle = faCheckCircle;
|
|
||||||
|
|
||||||
readonly faCircle = faCircle;
|
|
||||||
|
|
||||||
readonly faTimes = faTimesCircle;
|
|
||||||
|
|
||||||
readonly Schedule = Schedule;
|
|
||||||
|
|
||||||
schedules: Schedule[] = [];
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly scheduleService: ScheduleService,
|
|
||||||
readonly entryService: ScheduleEntryService,
|
|
||||||
readonly bulkService: BulkService,
|
|
||||||
readonly timeService: TimeService,
|
|
||||||
) {
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.scheduleService.subscribe(update => this.update(update));
|
|
||||||
this.scheduleService.findAll(schedules => this.schedules = schedules, Schedule.compareName);
|
|
||||||
}
|
|
||||||
|
|
||||||
set(schedule: Schedule, key: string, value: any): void {
|
|
||||||
this.scheduleService.set(schedule, key, value, NO_OP);
|
|
||||||
}
|
|
||||||
|
|
||||||
private update(update: Update<Schedule>): void {
|
|
||||||
const index: number = this.schedules.findIndex(s => s.id === update.payload.id);
|
|
||||||
if (index < 0) {
|
|
||||||
this.schedules.push(update.payload);
|
|
||||||
} else if (update.existing) {
|
|
||||||
this.schedules[index] = update.payload;
|
|
||||||
} else {
|
|
||||||
this.schedules.splice(index, 1);
|
|
||||||
}
|
|
||||||
this.schedules = this.schedules.sort(Schedule.compareName)
|
|
||||||
}
|
|
||||||
|
|
||||||
private remove(schedule: Schedule): void {
|
|
||||||
this.schedules.splice(this.schedules.findIndex(s => s.id === schedule.id), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
create(): void {
|
|
||||||
this.scheduleService.create(NO_OP);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(schedule: Schedule): void {
|
|
||||||
if (confirm("Zeitplan \"" + schedule.title + "\" wirklich löschen?")) {
|
|
||||||
this.scheduleService.delete(schedule, NO_OP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
skip(entry: ScheduleEntry) {
|
|
||||||
let skip = entry.skip + 1;
|
|
||||||
if (skip > 7) {
|
|
||||||
skip = 0;
|
|
||||||
}
|
|
||||||
this.entryService.set(entry, 'skip', skip);
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(bulk: Bulk | undefined) {
|
|
||||||
if (bulk && confirm("Stapel Ausführen?\n\n" + bulk.name)) {
|
|
||||||
this.bulkService.run(bulk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected readonly faPlayCircle = faPlayCircle;
|
|
||||||
}
|
|
||||||
187
src/main/angular/src/app/pages/schedule/schedule.component.html
Normal file
187
src/main/angular/src/app/pages/schedule/schedule.component.html
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
<ng-container *ngIf="schedule">
|
||||||
|
|
||||||
|
<ng-template #boolean let-entry="entry" let-value="value" let-key="key">
|
||||||
|
<td class="boolean" (click)="set(entry, key, !value)" [class.true]="value" [class.false]="!value">
|
||||||
|
<fa-icon *ngIf="value" [icon]="faCheckCircle"></fa-icon>
|
||||||
|
<fa-icon *ngIf="!value" [icon]="faCircle"></fa-icon>
|
||||||
|
</td>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr class="header">
|
||||||
|
<ng-container *ngTemplateOutlet="boolean;context:{schedule: schedule, value: schedule.enabled, key:'enabled'}"></ng-container>
|
||||||
|
<td colspan="24">
|
||||||
|
<app-edit-field [initial]="schedule.title" (valueChange)="set(null, 'title', $event)"></app-edit-field>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr [class.disabled]="!schedule.enabled">
|
||||||
|
<th> </th>
|
||||||
|
<th>Mo</th>
|
||||||
|
<th>Di</th>
|
||||||
|
<th>Mi</th>
|
||||||
|
<th>Do</th>
|
||||||
|
<th>Fr</th>
|
||||||
|
<th>Sa</th>
|
||||||
|
<th>So</th>
|
||||||
|
<th>Typ</th>
|
||||||
|
<th>Sonnenstand</th>
|
||||||
|
<th colspan="3">Uhrzeit</th>
|
||||||
|
<th>Unschärfe</th>
|
||||||
|
<th colspan="6">Nächste Ausführung</th>
|
||||||
|
<th colspan="2">Eingeschaft setzen</th>
|
||||||
|
<th>Massenverarbeitung</th>
|
||||||
|
<th> </th>
|
||||||
|
</tr>
|
||||||
|
<tr *ngFor="let entry of schedule.entries" [class.disabled]="entry.nextClearTimestamp === null">
|
||||||
|
<ng-container *ngTemplateOutlet="boolean;context:{entry: entry, value: entry.enabled, key:'enabled'}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="boolean;context:{entry: entry, value: entry.monday, key:'monday'}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="boolean;context:{entry: entry, value: entry.tuesday, key:'tuesday'}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="boolean;context:{entry: entry, value: entry.wednesday, key:'wednesday'}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="boolean;context:{entry: entry, value: entry.thursday, key:'thursday'}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="boolean;context:{entry: entry, value: entry.friday, key:'friday'}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="boolean;context:{entry: entry, value: entry.saturday, key:'saturday'}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="boolean;context:{entry: entry, value: entry.sunday, key:'sunday'}"></ng-container>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<select [(ngModel)]="entry.type" (ngModelChange)="set(entry, 'type', entry.type)">
|
||||||
|
<option value="TIME">Uhrzeit</option>
|
||||||
|
<option value="SUNRISE">Sonnenaufgang</option>
|
||||||
|
<option value="SUNSET">Sonnenuntergang</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<ng-container *ngIf="entry.type === 'SUNRISE' || entry.type === 'SUNSET'">
|
||||||
|
<td>
|
||||||
|
<select [(ngModel)]="entry.zenith" (ngModelChange)="set(entry, 'zenith', entry.zenith)">
|
||||||
|
<option value="87">
|
||||||
|
[ 87°]
|
||||||
|
<ng-container *ngIf="entry.type === 'SUNRISE'">Nach Sonnenaufgang</ng-container>
|
||||||
|
<ng-container *ngIf="entry.type === 'SUNSET'">Vor Sonnenuntergang</ng-container>
|
||||||
|
</option>
|
||||||
|
<option value="90.8333">
|
||||||
|
[ 90°]
|
||||||
|
<ng-container *ngIf="entry.type === 'SUNRISE'">Sonnenaufgang</ng-container>
|
||||||
|
<ng-container *ngIf="entry.type === 'SUNSET'">Sonnenuntergang</ng-container>
|
||||||
|
</option>
|
||||||
|
<option value="93">[ 93°]</option>
|
||||||
|
<option value="96">[ 96°] Bürgerliche Dämmerung</option>
|
||||||
|
<option value="99">[ 99°]</option>
|
||||||
|
<option value="102">[102°] Nautische Dämmerung</option>
|
||||||
|
<option value="105">[105°]</option>
|
||||||
|
<option value="108">[108°] Astronomische Dämmerung</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<td *ngIf="entry.type !== 'SUNRISE' && entry.type !== 'SUNSET'" class="empty"></td>
|
||||||
|
|
||||||
|
<ng-container *ngIf="entry.type === 'TIME'">
|
||||||
|
<td class="first">
|
||||||
|
<select [(ngModel)]="entry.hour" (ngModelChange)="set(entry, 'hour', entry.hour)">
|
||||||
|
<option *ngFor="let _ of [].constructor(24); let value = index" [ngValue]="value">{{value}}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="middle">:</td>
|
||||||
|
<td class="last">
|
||||||
|
<select [(ngModel)]="entry.minute" (ngModelChange)="set(entry, 'minute', entry.minute)">
|
||||||
|
<option *ngFor="let _ of [].constructor(60); let value = index" [ngValue]="value">{{value | number:'2.0'}}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<td *ngIf="entry.type !== 'TIME'" colspan="3" class="empty"></td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<select [(ngModel)]="entry.fuzzySeconds" (ngModelChange)="set(entry, 'fuzzySeconds', entry.fuzzySeconds)">
|
||||||
|
<option [ngValue]="0">Keine</option>
|
||||||
|
<option [ngValue]="60">1 Minute</option>
|
||||||
|
<option [ngValue]="300">5 Minuten</option>
|
||||||
|
<option [ngValue]="600">10 Minuten</option>
|
||||||
|
<option [ngValue]="1800">30 Minuten</option>
|
||||||
|
<option [ngValue]="3600">1 Stunde</option>
|
||||||
|
<option [ngValue]="7200">2 Stunden</option>
|
||||||
|
<option [ngValue]="10800">3 Stunden</option>
|
||||||
|
<option [ngValue]="21600">6 Stunden</option>
|
||||||
|
<option [ngValue]="43200">12 Stunden</option>
|
||||||
|
<option [ngValue]="86400">1 Tag</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<ng-container *ngIf="entry.nextClearTimestamp">
|
||||||
|
<td class="number first" [class.empty]="entry.fuzzySeconds > 0">{{entry.nextClearTimestamp.dayName}}</td>
|
||||||
|
<td class="number middle" [class.empty]="entry.fuzzySeconds > 0">: </td>
|
||||||
|
<td class="number last" [class.empty]="entry.fuzzySeconds > 0">{{entry.nextClearTimestamp.timeString}}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="!entry.nextClearTimestamp">
|
||||||
|
<td class="empty first"></td>
|
||||||
|
<td class="empty middle"></td>
|
||||||
|
<td class="empty last"></td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="entry.nextFuzzyTimestamp && entry.fuzzySeconds > 0">
|
||||||
|
<td class="number first">{{entry.nextFuzzyTimestamp.dayName}}</td>
|
||||||
|
<td class="number middle">: </td>
|
||||||
|
<td class="number last">{{entry.nextFuzzyTimestamp.timeString}}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="!entry.nextFuzzyTimestamp || entry.fuzzySeconds <= 0">
|
||||||
|
<td class="empty first"></td>
|
||||||
|
<td class="empty middle"></td>
|
||||||
|
<td class="empty last"></td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<app-search [searchService]="propertyService" [initial]="entry.property?.id" (valueChange)="set(entry, 'property', $event)"></app-search>
|
||||||
|
</td>
|
||||||
|
<ng-container [ngSwitch]="entry.property?.type">
|
||||||
|
<td *ngSwitchCase="'BOOLEAN'" [class.true]="entry.value" [class.false]="!entry.value" (click)="set(entry, 'value', entry.value > 0 ? 0 : 1)">
|
||||||
|
{{entry.value ? "An" : "Aus"}}
|
||||||
|
</td>
|
||||||
|
<td *ngSwitchCase="'SHUTTER'" [class.true]="entry.value === 0" [class.false]="entry.value === 100" [class.tristate]="0 < entry.value && entry.value < 100">
|
||||||
|
<select [(ngModel)]="entry.value" (ngModelChange)="set(entry, 'value', entry.value)">
|
||||||
|
<option [ngValue]="0">100% Offen</option>
|
||||||
|
<option [ngValue]="35"> 50%</option>
|
||||||
|
<option [ngValue]="55"> 75%</option>
|
||||||
|
<option [ngValue]="75"> 90% Sonnenschutz</option>
|
||||||
|
<option [ngValue]="85">100% Schlitze</option>
|
||||||
|
<option [ngValue]="100">100% Geschlossen</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td *ngSwitchCase="'BRIGHTNESS_PERCENT'" [class.true]="entry.value" [class.false]="!entry.value" [class.tristate]="0 < entry.value && entry.value < 100">
|
||||||
|
<select [(ngModel)]="entry.value" (ngModelChange)="set(entry, 'value', entry.value)">
|
||||||
|
<option *ngFor="let _ of [].constructor(21); let value = index" [ngValue]="value * 5">{{value * 5}}%</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td *ngSwitchCase="'COLOR_TEMPERATURE'" [class.true]="entry.value" [class.false]="!entry.value" [class.tristate]="0 < entry.value && entry.value < 100">
|
||||||
|
<select [(ngModel)]="entry.value" (ngModelChange)="set(entry, 'value', entry.value)">
|
||||||
|
<option *ngFor="let _ of [].constructor(21); let value = index" [ngValue]="value * 5">{{value * 5}}%</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td *ngSwitchCase="'LUX'" [class.true]="entry.value" [class.false]="!entry.value" [class.tristate]="0 < entry.value && entry.value < 100">
|
||||||
|
<select [(ngModel)]="entry.value" (ngModelChange)="set(entry, 'value', entry.value)">
|
||||||
|
<option *ngFor="let _ of [].constructor(21); let value = index" [ngValue]="value * 5">{{value * 5}}%</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td *ngSwitchCase="'SCENE'">
|
||||||
|
<select [(ngModel)]="entry.value" (ngModelChange)="set(entry, 'value', entry.value)">
|
||||||
|
<option *ngFor="let scene of scenes" [ngValue]="scene.number">#{{scene.number | number:'2.0-0'}} {{scene.title}}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td *ngSwitchDefault class="empty">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<app-search [searchService]="bulkService" [initial]="entry.bulk?.id" (valueChange)="set(entry, 'bulk', $event)"></app-search>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="delete" (click)="delete(entry)">
|
||||||
|
<fa-icon title="Löschen" [icon]="faTimes"></fa-icon>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<button (click)="create()">+ Hinzufügen</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
select {
|
||||||
|
background-color: transparent;
|
||||||
|
border-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: lightblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.header {
|
||||||
|
|
||||||
|
th:not(:first-child), td:not(:first-child) {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {ScheduleComponent} from './schedule.component';
|
||||||
|
|
||||||
|
describe('ScheduleComponent', () => {
|
||||||
|
let component: ScheduleComponent;
|
||||||
|
let fixture: ComponentFixture<ScheduleComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ScheduleComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ScheduleComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {ScheduleService} from "../../api/schedule/schedule.service";
|
||||||
|
import {Schedule} from "../../api/schedule/Schedule";
|
||||||
|
import {ScheduleEntry} from "../../api/schedule/entry/ScheduleEntry";
|
||||||
|
import {ScheduleEntryService} from "../../api/schedule/entry/schedule-entry.service";
|
||||||
|
import {faCheckCircle, faCircle, faTimesCircle} from '@fortawesome/free-regular-svg-icons';
|
||||||
|
import {ActivatedRoute} from "@angular/router";
|
||||||
|
import {DataService} from "../../data.service";
|
||||||
|
import {PropertyService} from "../../api/property/property.service";
|
||||||
|
import {Scene} from "../../api/scene/Scene";
|
||||||
|
import {SceneService} from "../../api/scene/scene.service";
|
||||||
|
import {BulkService} from "../../api/bulk/BulkService";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-schedule',
|
||||||
|
templateUrl: './schedule.component.html',
|
||||||
|
styleUrls: ['./schedule.component.less']
|
||||||
|
})
|
||||||
|
export class ScheduleComponent implements OnInit {
|
||||||
|
|
||||||
|
readonly faCheckCircle = faCheckCircle;
|
||||||
|
|
||||||
|
readonly faCircle = faCircle;
|
||||||
|
|
||||||
|
readonly faTimes = faTimesCircle;
|
||||||
|
|
||||||
|
readonly Schedule = Schedule;
|
||||||
|
|
||||||
|
schedule!: Schedule;
|
||||||
|
|
||||||
|
scenes: Scene[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly activatedRoute: ActivatedRoute,
|
||||||
|
readonly scheduleService: ScheduleService,
|
||||||
|
readonly scheduleEntryService: ScheduleEntryService,
|
||||||
|
readonly propertyService: PropertyService,
|
||||||
|
readonly bulkService: BulkService,
|
||||||
|
readonly sceneService: SceneService,
|
||||||
|
readonly dataService: DataService,
|
||||||
|
) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.dataService.schedule = undefined;
|
||||||
|
this.sceneService.findAll(scenes => this.scenes = scenes);
|
||||||
|
this.activatedRoute.params.subscribe(params => this.scheduleService.getById(params['id'], schedule => this.setSchedule(schedule)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private setSchedule(schedule: Schedule): void {
|
||||||
|
this.schedule = schedule;
|
||||||
|
this.dataService.schedule = schedule;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(entry: ScheduleEntry | null, key: string, value: any): void {
|
||||||
|
if (entry) {
|
||||||
|
this.scheduleEntryService.set(entry, key, value, entry => this.addOrReplace(entry));
|
||||||
|
} else {
|
||||||
|
this.scheduleService.set(this.schedule, key, value, schedule => this.setSchedule(schedule));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addOrReplace(entry: ScheduleEntry): void {
|
||||||
|
const index: number = this.schedule.entries.findIndex(s => s.id === entry.id);
|
||||||
|
if (index < 0) {
|
||||||
|
this.schedule.entries.push(entry);
|
||||||
|
} else {
|
||||||
|
this.schedule.entries[index] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private remove(entry: ScheduleEntry): void {
|
||||||
|
this.schedule.entries.splice(this.schedule.entries.findIndex(e => e.id === entry.id), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(): void {
|
||||||
|
this.scheduleEntryService.create(this.schedule, entry => this.addOrReplace(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(entry: ScheduleEntry): void {
|
||||||
|
if (confirm("Eintrag \"" + entry.nextClearTimestamp?.timeString + " +/-" + entry.fuzzySeconds + "\" wirklich löschen?")) {
|
||||||
|
this.scheduleEntryService.delete(entry, () => this.remove(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import {Pipe, PipeTransform} from '@angular/core';
|
|
||||||
|
|
||||||
@Pipe({
|
|
||||||
name: 'leftPad',
|
|
||||||
})
|
|
||||||
export class LeftPadDirective implements PipeTransform {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
transform(value: any, count: number): any {
|
|
||||||
let result = "" + value;
|
|
||||||
const rest: number = count - result.length;
|
|
||||||
if (rest > 0) {
|
|
||||||
result = " ".repeat(rest) + result;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
<div type="checkbox" [style.background-color]="color()" (click)="onChange.emit(!value)">
|
|
||||||
<ng-container *ngIf="label">
|
|
||||||
{{ label }}
|
|
||||||
</ng-container>
|
|
||||||
<ng-container *ngIf="!label">
|
|
||||||
<ng-container *ngIf="icon">
|
|
||||||
<fa-icon *ngIf="icon === 'faCircle'" [icon]="faCircle"></fa-icon>
|
|
||||||
<fa-icon *ngIf="icon === 'faCheckCircle'" [icon]="faCheckCircle"></fa-icon>
|
|
||||||
<fa-icon *ngIf="icon === 'faClock'" [icon]="faClock"></fa-icon>
|
|
||||||
<fa-icon *ngIf="icon === 'faArrowAltCircleUp'" [icon]="faArrowAltCircleUp"></fa-icon>
|
|
||||||
<fa-icon *ngIf="icon === 'faArrowAltCircleDown'" [icon]="faArrowAltCircleDown"></fa-icon>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container *ngIf="!icon">
|
|
||||||
<fa-icon *ngIf="value" [icon]="faCheckCircle"></fa-icon>
|
|
||||||
<fa-icon *ngIf="!value" [icon]="faCircle"></fa-icon>
|
|
||||||
</ng-container>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
div {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding-top: 0.1em;
|
|
||||||
padding-bottom: 0.1em;
|
|
||||||
text-align: center;
|
|
||||||
background-color: gray;
|
|
||||||
}
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
|
||||||
import {faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCircle, faClock} from "@fortawesome/free-regular-svg-icons";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-bool',
|
|
||||||
templateUrl: './bool.component.html',
|
|
||||||
styleUrls: ['./bool.component.less']
|
|
||||||
})
|
|
||||||
export class BoolComponent implements OnInit {
|
|
||||||
|
|
||||||
protected readonly faCheckCircle = faCheckCircle;
|
|
||||||
|
|
||||||
protected readonly faCircle = faCircle;
|
|
||||||
|
|
||||||
protected readonly faClock = faClock;
|
|
||||||
|
|
||||||
protected readonly faArrowAltCircleUp = faArrowAltCircleUp;
|
|
||||||
|
|
||||||
protected readonly faArrowAltCircleDown = faArrowAltCircleDown;
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
icon: string | undefined = undefined;
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
colorActive: string = '#8FBC8FFF';
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
colorInactive: string = '#8c8c8c';
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
label: string | undefined = undefined;
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
value: boolean | undefined = undefined;
|
|
||||||
|
|
||||||
@Output()
|
|
||||||
onChange: EventEmitter<boolean> = new EventEmitter();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
color(): string {
|
|
||||||
if (this.value === true) {
|
|
||||||
return this.colorActive;
|
|
||||||
} else if (this.value === false) {
|
|
||||||
return this.colorInactive;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user