2020-10-06 19:22:40 +03:00
|
|
|
///
|
2023-01-31 10:43:56 +02:00
|
|
|
/// Copyright © 2016-2023 The Thingsboard Authors
|
2020-10-06 19:22:40 +03:00
|
|
|
///
|
|
|
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
/// you may not use this file except in compliance with the License.
|
|
|
|
|
/// You may obtain a copy of the License at
|
|
|
|
|
///
|
|
|
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
///
|
|
|
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
/// See the License for the specific language governing permissions and
|
|
|
|
|
/// limitations under the License.
|
|
|
|
|
///
|
|
|
|
|
|
|
|
|
|
import { Component, forwardRef, Input, OnInit } from '@angular/core';
|
|
|
|
|
import {
|
2020-10-09 16:05:23 +03:00
|
|
|
AbstractControl,
|
2020-10-06 19:22:40 +03:00
|
|
|
ControlValueAccessor,
|
2023-02-02 15:55:06 +02:00
|
|
|
UntypedFormArray,
|
|
|
|
|
UntypedFormBuilder,
|
|
|
|
|
UntypedFormControl,
|
|
|
|
|
UntypedFormGroup,
|
2020-10-06 19:22:40 +03:00
|
|
|
NG_VALIDATORS,
|
|
|
|
|
NG_VALUE_ACCESSOR,
|
|
|
|
|
ValidationErrors,
|
|
|
|
|
Validator,
|
|
|
|
|
Validators
|
|
|
|
|
} from '@angular/forms';
|
2020-10-12 17:29:27 +03:00
|
|
|
import {
|
|
|
|
|
AlarmSchedule,
|
|
|
|
|
AlarmScheduleType,
|
|
|
|
|
AlarmScheduleTypeTranslationMap,
|
2021-01-05 11:37:05 +02:00
|
|
|
dayOfWeekTranslations,
|
|
|
|
|
getAlarmScheduleRangeText,
|
|
|
|
|
timeOfDayToUTCTimestamp,
|
|
|
|
|
utcTimestampToTimeOfDay
|
2020-10-12 17:29:27 +03:00
|
|
|
} from '@shared/models/device.models';
|
2020-10-06 19:22:40 +03:00
|
|
|
import { isDefined, isDefinedAndNotNull } from '@core/utils';
|
2023-02-17 19:24:01 +02:00
|
|
|
import { MatCheckboxChange } from '@angular/material/checkbox';
|
2021-01-05 11:37:05 +02:00
|
|
|
import { getDefaultTimezone } from '@shared/models/time/time.models';
|
2020-10-06 19:22:40 +03:00
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'tb-alarm-schedule',
|
|
|
|
|
templateUrl: './alarm-schedule.component.html',
|
2020-10-09 16:05:23 +03:00
|
|
|
styleUrls: ['./alarm-schedule.component.scss'],
|
2020-10-06 19:22:40 +03:00
|
|
|
providers: [{
|
|
|
|
|
provide: NG_VALUE_ACCESSOR,
|
|
|
|
|
useExisting: forwardRef(() => AlarmScheduleComponent),
|
|
|
|
|
multi: true
|
|
|
|
|
}, {
|
|
|
|
|
provide: NG_VALIDATORS,
|
|
|
|
|
useExisting: forwardRef(() => AlarmScheduleComponent),
|
|
|
|
|
multi: true
|
|
|
|
|
}]
|
|
|
|
|
})
|
|
|
|
|
export class AlarmScheduleComponent implements ControlValueAccessor, Validator, OnInit {
|
|
|
|
|
@Input()
|
|
|
|
|
disabled: boolean;
|
|
|
|
|
|
2023-02-02 15:55:06 +02:00
|
|
|
alarmScheduleForm: UntypedFormGroup;
|
2020-10-06 19:22:40 +03:00
|
|
|
|
|
|
|
|
alarmScheduleTypes = Object.keys(AlarmScheduleType);
|
|
|
|
|
alarmScheduleType = AlarmScheduleType;
|
|
|
|
|
alarmScheduleTypeTranslate = AlarmScheduleTypeTranslationMap;
|
2020-10-12 17:29:27 +03:00
|
|
|
dayOfWeekTranslationsArray = dayOfWeekTranslations;
|
|
|
|
|
|
|
|
|
|
allDays = Array(7).fill(0).map((x, i) => i);
|
|
|
|
|
|
|
|
|
|
firstRowDays = Array(4).fill(0).map((x, i) => i);
|
|
|
|
|
secondRowDays = Array(3).fill(0).map((x, i) => i + 4);
|
|
|
|
|
|
2020-10-06 19:22:40 +03:00
|
|
|
private modelValue: AlarmSchedule;
|
|
|
|
|
|
|
|
|
|
private defaultItems = Array.from({length: 7}, (value, i) => ({
|
|
|
|
|
enabled: true,
|
2020-10-12 17:29:27 +03:00
|
|
|
dayOfWeek: i + 1
|
2020-10-06 19:22:40 +03:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
private propagateChange = (v: any) => { };
|
|
|
|
|
|
2023-02-02 15:55:06 +02:00
|
|
|
constructor(private fb: UntypedFormBuilder) {
|
2020-10-06 19:22:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngOnInit(): void {
|
|
|
|
|
this.alarmScheduleForm = this.fb.group({
|
|
|
|
|
type: [AlarmScheduleType.ANY_TIME, Validators.required],
|
|
|
|
|
timezone: [null, Validators.required],
|
2020-10-12 17:29:27 +03:00
|
|
|
daysOfWeek: this.fb.array(new Array(7).fill(false), this.validateDayOfWeeks),
|
2020-10-06 19:22:40 +03:00
|
|
|
startsOn: [0, Validators.required],
|
|
|
|
|
endsOn: [0, Validators.required],
|
2022-01-10 16:49:15 +02:00
|
|
|
items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i)), this.validateItems),
|
2022-01-11 14:38:07 +02:00
|
|
|
dynamicValue: [null]
|
2020-10-06 19:22:40 +03:00
|
|
|
});
|
2022-01-10 16:49:15 +02:00
|
|
|
|
2020-10-06 19:22:40 +03:00
|
|
|
this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => {
|
2021-03-17 18:38:57 +02:00
|
|
|
const defaultTimezone = getDefaultTimezone();
|
|
|
|
|
this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false});
|
|
|
|
|
this.updateValidators(type, true);
|
|
|
|
|
this.alarmScheduleForm.updateValueAndValidity();
|
2020-10-06 19:22:40 +03:00
|
|
|
});
|
|
|
|
|
this.alarmScheduleForm.valueChanges.subscribe(() => {
|
|
|
|
|
this.updateModel();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-12 17:29:27 +03:00
|
|
|
validateDayOfWeeks(control: AbstractControl): ValidationErrors | null {
|
|
|
|
|
const dayOfWeeks: boolean[] = control.value;
|
|
|
|
|
if (!dayOfWeeks || !dayOfWeeks.length || !dayOfWeeks.find(v => v === true)) {
|
|
|
|
|
return {
|
|
|
|
|
dayOfWeeks: true
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validateItems(control: AbstractControl): ValidationErrors | null {
|
|
|
|
|
const items: any[] = control.value;
|
|
|
|
|
if (!items || !items.length || !items.find(v => v.enabled === true)) {
|
|
|
|
|
return {
|
|
|
|
|
dayOfWeeks: true
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-06 19:22:40 +03:00
|
|
|
registerOnChange(fn: any): void {
|
|
|
|
|
this.propagateChange = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registerOnTouched(fn: any): void {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setDisabledState(isDisabled: boolean): void {
|
|
|
|
|
this.disabled = isDisabled;
|
|
|
|
|
if (this.disabled) {
|
|
|
|
|
this.alarmScheduleForm.disable({emitEvent: false});
|
|
|
|
|
} else {
|
2023-02-27 16:24:50 +02:00
|
|
|
this.updateValidators(this.alarmScheduleForm.get('type').value);
|
2020-10-06 19:22:40 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writeValue(value: AlarmSchedule): void {
|
|
|
|
|
this.modelValue = value;
|
|
|
|
|
if (!isDefinedAndNotNull(this.modelValue)) {
|
|
|
|
|
this.modelValue = {
|
|
|
|
|
type: AlarmScheduleType.ANY_TIME
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
switch (this.modelValue.type) {
|
|
|
|
|
case AlarmScheduleType.SPECIFIC_TIME:
|
|
|
|
|
let daysOfWeek = new Array(7).fill(false);
|
|
|
|
|
if (isDefined(this.modelValue.daysOfWeek)) {
|
|
|
|
|
daysOfWeek = daysOfWeek.map((item, index) => this.modelValue.daysOfWeek.indexOf(index + 1) > -1);
|
|
|
|
|
}
|
|
|
|
|
this.alarmScheduleForm.patchValue({
|
|
|
|
|
type: this.modelValue.type,
|
|
|
|
|
timezone: this.modelValue.timezone,
|
|
|
|
|
daysOfWeek,
|
2020-10-12 17:29:27 +03:00
|
|
|
startsOn: utcTimestampToTimeOfDay(this.modelValue.startsOn),
|
2022-01-11 14:38:07 +02:00
|
|
|
endsOn: utcTimestampToTimeOfDay(this.modelValue.endsOn),
|
|
|
|
|
dynamicValue: this.modelValue.dynamicValue
|
2020-10-06 19:22:40 +03:00
|
|
|
}, {emitEvent: false});
|
|
|
|
|
break;
|
|
|
|
|
case AlarmScheduleType.CUSTOM:
|
|
|
|
|
if (this.modelValue.items) {
|
|
|
|
|
const alarmDays = [];
|
|
|
|
|
this.modelValue.items
|
|
|
|
|
.sort((a, b) => a.dayOfWeek - b.dayOfWeek)
|
|
|
|
|
.forEach((item, index) => {
|
2020-10-09 16:05:23 +03:00
|
|
|
this.disabledSelectedTime(item.enabled, index);
|
2020-10-06 19:22:40 +03:00
|
|
|
alarmDays.push({
|
|
|
|
|
enabled: item.enabled,
|
2020-10-12 17:29:27 +03:00
|
|
|
startsOn: utcTimestampToTimeOfDay(item.startsOn),
|
|
|
|
|
endsOn: utcTimestampToTimeOfDay(item.endsOn)
|
2020-10-06 19:22:40 +03:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
this.alarmScheduleForm.patchValue({
|
|
|
|
|
type: this.modelValue.type,
|
|
|
|
|
timezone: this.modelValue.timezone,
|
2022-01-10 16:49:15 +02:00
|
|
|
items: alarmDays,
|
2022-01-11 14:38:07 +02:00
|
|
|
dynamicValue: this.modelValue.dynamicValue
|
2020-10-06 19:22:40 +03:00
|
|
|
}, {emitEvent: false});
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
this.alarmScheduleForm.patchValue(this.modelValue || undefined, {emitEvent: false});
|
|
|
|
|
}
|
|
|
|
|
this.updateValidators(this.modelValue.type);
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-02 15:55:06 +02:00
|
|
|
validate(control: UntypedFormGroup): ValidationErrors | null {
|
2020-10-06 19:22:40 +03:00
|
|
|
return this.alarmScheduleForm.valid ? null : {
|
|
|
|
|
alarmScheduler: {
|
|
|
|
|
valid: false
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-02 15:55:06 +02:00
|
|
|
weeklyRepeatControl(index: number): UntypedFormControl {
|
|
|
|
|
return (this.alarmScheduleForm.get('daysOfWeek') as UntypedFormArray).at(index) as UntypedFormControl;
|
2020-10-06 19:22:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private updateValidators(type: AlarmScheduleType, changedType = false){
|
|
|
|
|
switch (type){
|
|
|
|
|
case AlarmScheduleType.ANY_TIME:
|
|
|
|
|
this.alarmScheduleForm.get('timezone').disable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('daysOfWeek').disable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('startsOn').disable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('endsOn').disable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('items').disable({emitEvent: false});
|
|
|
|
|
break;
|
|
|
|
|
case AlarmScheduleType.SPECIFIC_TIME:
|
|
|
|
|
this.alarmScheduleForm.get('timezone').enable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('daysOfWeek').enable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('startsOn').enable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('endsOn').enable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('items').disable({emitEvent: false});
|
|
|
|
|
break;
|
|
|
|
|
case AlarmScheduleType.CUSTOM:
|
|
|
|
|
this.alarmScheduleForm.get('timezone').enable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('daysOfWeek').disable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('startsOn').disable({emitEvent: false});
|
|
|
|
|
this.alarmScheduleForm.get('endsOn').disable({emitEvent: false});
|
|
|
|
|
if (changedType) {
|
|
|
|
|
this.alarmScheduleForm.get('items').enable({emitEvent: false});
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private updateModel() {
|
|
|
|
|
const value = this.alarmScheduleForm.value;
|
|
|
|
|
if (this.modelValue) {
|
|
|
|
|
if (isDefined(value.daysOfWeek)) {
|
|
|
|
|
value.daysOfWeek = value.daysOfWeek
|
|
|
|
|
.map((day: boolean, index: number) => day ? index + 1 : null)
|
|
|
|
|
.filter(day => !!day);
|
|
|
|
|
}
|
|
|
|
|
if (isDefined(value.startsOn) && value.startsOn !== 0) {
|
2020-10-12 17:29:27 +03:00
|
|
|
value.startsOn = timeOfDayToUTCTimestamp(value.startsOn);
|
2020-10-06 19:22:40 +03:00
|
|
|
}
|
|
|
|
|
if (isDefined(value.endsOn) && value.endsOn !== 0) {
|
2020-10-12 17:29:27 +03:00
|
|
|
value.endsOn = timeOfDayToUTCTimestamp(value.endsOn);
|
2020-10-06 19:22:40 +03:00
|
|
|
}
|
|
|
|
|
if (isDefined(value.items)){
|
|
|
|
|
value.items = this.alarmScheduleForm.getRawValue().items;
|
|
|
|
|
value.items = value.items.map((item) => {
|
2020-10-12 17:29:27 +03:00
|
|
|
return { ...item, startsOn: timeOfDayToUTCTimestamp(item.startsOn), endsOn: timeOfDayToUTCTimestamp(item.endsOn)};
|
2020-10-06 19:22:40 +03:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
this.modelValue = value;
|
|
|
|
|
this.propagateChange(this.modelValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-02-02 15:55:06 +02:00
|
|
|
private defaultItemsScheduler(index): UntypedFormGroup {
|
2020-10-06 19:22:40 +03:00
|
|
|
return this.fb.group({
|
|
|
|
|
enabled: [true],
|
2020-10-12 17:29:27 +03:00
|
|
|
dayOfWeek: [index + 1],
|
2020-10-06 19:22:40 +03:00
|
|
|
startsOn: [0, Validators.required],
|
|
|
|
|
endsOn: [0, Validators.required]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
changeCustomScheduler($event: MatCheckboxChange, index: number) {
|
|
|
|
|
const value = $event.checked;
|
2020-10-09 16:05:23 +03:00
|
|
|
this.disabledSelectedTime(value, index, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private disabledSelectedTime(enable: boolean, index: number, emitEvent = false) {
|
|
|
|
|
if (enable) {
|
2020-10-06 19:22:40 +03:00
|
|
|
this.itemsSchedulerForm.at(index).get('startsOn').enable({emitEvent: false});
|
2020-10-09 16:05:23 +03:00
|
|
|
this.itemsSchedulerForm.at(index).get('endsOn').enable({emitEvent});
|
2020-10-06 19:22:40 +03:00
|
|
|
} else {
|
|
|
|
|
this.itemsSchedulerForm.at(index).get('startsOn').disable({emitEvent: false});
|
2020-10-09 16:05:23 +03:00
|
|
|
this.itemsSchedulerForm.at(index).get('endsOn').disable({emitEvent});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-02 15:55:06 +02:00
|
|
|
getSchedulerRangeText(control: UntypedFormGroup | AbstractControl): string {
|
2020-10-12 17:29:27 +03:00
|
|
|
return getAlarmScheduleRangeText(control.get('startsOn').value, control.get('endsOn').value);
|
2020-10-06 19:22:40 +03:00
|
|
|
}
|
|
|
|
|
|
2023-02-02 15:55:06 +02:00
|
|
|
get itemsSchedulerForm(): UntypedFormArray {
|
|
|
|
|
return this.alarmScheduleForm.get('items') as UntypedFormArray;
|
2020-10-06 19:22:40 +03:00
|
|
|
}
|
|
|
|
|
}
|