UNPKG

lavva.exalushome

Version:

Library implementing communication and abstraction layers for ExalusHome system

973 lines 69.8 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { Api } from "../../Api"; import { DependencyContainer } from "../../DependencyContainer"; import { Guid } from "../../Guid"; import { Helpers } from "../../Helpers"; import { DeviceResponseType, DeviceTaskType } from "../Devices/IDevice"; import { EnergyMeasurementParameter } from "../Devices/IDeviceState"; import { DeviceControlFeature } from "../Devices/IDeviceTask"; import { LightRGBW, SetBlindMicroventilation, SetBlindOpenCloseTime, SetBlindPosition, SetBlindPositionSimple, SetFacadePosition, SetGatePositionPrecise, SetLightBrightness, SetLightBrightnessDynamicly, SetLightColor, SetLightTemperature, SetTemperature, TurnOnWithTime } from "../Devices/Tasks/Tasks"; import { UpdatesProvider } from "../Updates/UpdatesProvider"; import { ConditionInfoArgument, DeviceStateComparisonMethod, DeviceStateComparisonParams } from "./LeftArgumentTypes"; import { LockType, NotifyType } from "./SceneTaskTypes"; import { ConditionsTypes, DeviceStateType, HandledType, SceneTaskTypeNum, SupportedTaskTypes, UnsupportedScenesDeviceState } from "./Scenes"; import { ScenesService } from "./ScenesService"; export class SequenceBuilder { constructor(sequenceName, sequenceIcon = "Cube") { this._result = null; this._tmp = new SequenceRequestData(); this._tmpTask = null; this._prevSeqGuid = ""; this._currentSeqGuid = ""; this._editingExistingSequence = false; this._rawExistingSequenceTasks = null; //Saved in case when we remove first argument by method RemoveArgument this._sequenceName = ""; this._sequenceIcon = ""; this._service = Api.Get(ScenesService.ServiceName); if (sequenceName) { this._sequenceName = sequenceName; this._sequenceIcon = sequenceIcon; this._tmp.Guid = Guid.NewGuid(); } } /** * Sets current (new or in edit) sequence name * @param name name */ SetSequenceName(name) { const findLastArgumentAndEditNameInNotifyTask = (sequence) => { if (sequence.RightArgumentType == ArgumentTypeNum.Null) { sequence.Tasks.ControllerTasks.where(t => t.TaskType == SceneTaskTypeNum.NotifyTask).toArray().forEach(t => { t.Title = name; }); } else return findLastArgumentAndEditNameInNotifyTask(sequence.RightArgument); }; this._sequenceName = name; if (this._editingExistingSequence && this._result != null) { this._result.Name = name; //Find notify task and update topic (name) findLastArgumentAndEditNameInNotifyTask(this._result); } } /** * Sets current (new or in edit) sequence icon * @param icon icon */ SetSequenceIcon(icon) { this._sequenceIcon = icon; if (this._editingExistingSequence && this._result != null) { this._result.Icon = icon; } } /** * Gets next sequence from sequence chain */ GetNext() { if (this._result == null) return null; if (this._currentSeqGuid == "") { this._currentSeqGuid = this._result.Guid; return this._service.MapSequence(JSON.parse(JSON.stringify(this._result)), true); } else { const result = this.FindArgumentWithGuid(this._result, this._currentSeqGuid); if (result == null) { return null; } else { if (result.RightArgumentType == ArgumentTypeNum.Condition) { this._prevSeqGuid = this._currentSeqGuid; this._currentSeqGuid = result.RightArgument.Guid; return this._service.MapSequence(JSON.parse(JSON.stringify(result.RightArgument)), true); } else return null; } } } /** * Method allows to clone builder with all sqeuence atributes and tasks. * Function re-generate all guids in chain and reset navigation to first element. * @returns cloned builder (new object) */ CloneCurrentBuilder() { const regenerateArgumentsGuids = (builder) => { const regenerateGuids = (sequence) => { sequence.Guid = Guid.NewGuid(); if (sequence.RightArgumentType == ArgumentTypeNum.Condition) { regenerateGuids(sequence.RightArgument); } }; if (builder._result != null) regenerateGuids(builder._result); if (this._tmp != null) this._tmp.Guid = Guid.NewGuid(); }; var clonedBuilder = new SequenceBuilder(); clonedBuilder._currentSeqGuid = ""; clonedBuilder._prevSeqGuid = ""; clonedBuilder._editingExistingSequence = structuredClone(this._editingExistingSequence); clonedBuilder._rawExistingSequenceTasks = structuredClone(this._rawExistingSequenceTasks); clonedBuilder._result = structuredClone(this._result); clonedBuilder._sequenceIcon = structuredClone(this._sequenceIcon); clonedBuilder._sequenceName = structuredClone(this._sequenceName); ; clonedBuilder._tmp = structuredClone(this._tmp); regenerateArgumentsGuids(clonedBuilder); return clonedBuilder; } /** * Gets previous sequence from sequence chain */ GetPrevious() { let prevGuid = ""; const findArgumentWithGuidAndReturnPrevGuid = (sequence, sequenceGuid) => { if (sequence.Guid == sequenceGuid) { return { sequence: sequence, prevGuid: prevGuid }; } else if (sequence.RightArgumentType == ArgumentTypeNum.Condition) { prevGuid = sequence.Guid; return findArgumentWithGuidAndReturnPrevGuid(sequence.RightArgument, sequenceGuid); } else return null; }; if (this._result == null) return null; if (this._currentSeqGuid == "" || this._prevSeqGuid == "") return null; const result = findArgumentWithGuidAndReturnPrevGuid(this._result, this._prevSeqGuid); if (result == null) { return null; } else { this._currentSeqGuid = result.sequence.Guid; this._prevSeqGuid = result.prevGuid; return this._service.MapSequence(JSON.parse(JSON.stringify(result.sequence)), true); } } /** * Gets sequence by guid * @param sequenceGuid sequence guid */ GetByGuid(sequenceGuid) { if (this._result == null) return null; const arg = this.FindArgumentWithGuid(this._result, sequenceGuid); if (arg == null) return null; return this._service.MapSequence(arg); } EditCurrentSequence(sequence) { const findLastArgumentAndGetRawTasks = (sequence) => { if (sequence.RightArgumentType == ArgumentTypeNum.Null) { this._rawExistingSequenceTasks = JSON.parse(JSON.stringify(sequence.Tasks)); } else return findLastArgumentAndGetRawTasks(sequence.RightArgument); }; this._editingExistingSequence = true; findLastArgumentAndGetRawTasks(sequence._rawRequestData); if (Object.hasOwn(sequence._rawRequestData, "Condition")) { this._result = sequence._rawRequestData.Condition; } else { this._result = sequence._rawRequestData; } //Getting raw tasks from last argument return { GetNext: () => this.GetNext(), GetPrevious: () => this.GetPrevious(), GetByGuid: (sequenceGuid) => this.GetByGuid(sequenceGuid), GetAllowedArguments: () => this.GetAllowedArguments(), RemoveSequence: (sequenceGuid) => this.RemoveSequenceByGuid(sequenceGuid), ArgumentAsAstronomicalClockWithOffset: (arg, atMeetCondition, comparasion) => this.ArgumentAsAstronomicalClockWithOffset(arg, atMeetCondition, comparasion), ArgumentAsDaysOfWeek: (arg, atMeetCondition, comparison) => this.ArgumentAsDaysOfWeek(arg, atMeetCondition, comparison), ArgumentAsDeviceState: (arg, atMeetCondition, atMeetConditionTimeout, comparison = ConditionsTypes.Equal) => this.ArgumentAsDeviceState(arg, atMeetCondition, atMeetConditionTimeout, comparison), ArgumentAsTime: (arg, atMeetCondition, comparison = ConditionsTypes.Equal) => this.ArgumentAsTime(arg, atMeetCondition, comparison), ArgumentAsTimeSpan: (arg, comparison) => this.ArgumentAsTimeSpan(arg, comparison), ArgumentAsSceneExecuted: (arg) => this.ArgumentAsSceneExecuted(arg), RemoveTask: (taskGuid) => this.RemoveTask(taskGuid), }; } /** * Function creates argument of type AstronomicalClockWithOffse in the argument chain * @param arg * @returns function that adds argument to end of chain */ ArgumentAsAstronomicalClockWithOffset(arg, atMeetCondition, comparasion) { this._tmp.Guid = Guid.NewGuid(); this._tmp.HandledType = HandledType.AstronomicalClockWithOffset; this._tmp.LeftArgumentType = ArgumentTypeNum.ArgumentAsAstronomicalClockWithOffset; this._tmp.AtMeetCondition = atMeetCondition; if (atMeetCondition && (comparasion != null && comparasion != ConditionsTypes.Equal)) { throw new BadParametersScenesBuilderException("AtmeetCondition supports only comparation type 'Equal'."); } if (arg.Offset < -3600 || arg.Offset > 3600) throw new BadParametersScenesBuilderException("Offset must be in range between -3600 and 3600 seconds."); if (atMeetCondition) this._tmp.ConditionType = ConditionsTypes.Equal; else if (comparasion != null) this._tmp.ConditionType = comparasion; else throw new BadParametersScenesBuilderException("Comparasion type must be provided if atMeetCondition is false!"); this._tmp.LeftArgument.Argument = arg; this._tmp.LeftArgument.HandledType = ArgumentTypeNum.ArgumentAsAstronomicalClockWithOffset; return { AddArgument: () => this.AddSequence(), EditArgument: (sequenceGuid) => this.EditSequence(sequenceGuid) }; } ArgumentAsActionWithEmptyCondition() { this._tmp.Guid = Guid.NewGuid(); this._tmp.HandledType = HandledType.Unknown; this._tmp.LeftArgumentType = ArgumentTypeNum.Null; this._tmp.ConditionType = ConditionsTypes.Equal; this._tmp.LeftArgument.Argument = null; this._tmp.LeftArgument.HandledType = ArgumentTypeNum.Null; this._tmp.Name = this._sequenceName; this._tmp.Icon = this._sequenceIcon; this._tmp.AtMeetCondition = false; return { AddArgument: () => this.AddSequence(), EditArgument: (sequenceGuid) => this.EditSequence(sequenceGuid) }; } /** * Function creates argument of type DayOfWeekArgument in the argument chain * @param arg * @param atMeetCondition * @param comparison * @returns function that adds argument to end of chain */ ArgumentAsDaysOfWeek(arg, atMeetCondition, comparison = ConditionsTypes.Equal) { this._tmp.Guid = Guid.NewGuid(); this._tmp.HandledType = HandledType.DaysOfWeek; this._tmp.LeftArgumentType = ArgumentTypeNum.ArgumentAsDaysOfWeek; this._tmp.ConditionType = comparison; this._tmp.LeftArgument.Argument = arg; this._tmp.LeftArgument.HandledType = ArgumentTypeNum.ArgumentAsDaysOfWeek; this._tmp.AtMeetCondition = atMeetCondition; return { AddArgument: () => this.AddSequence(), EditArgument: (sequenceGuid) => this.EditSequence(sequenceGuid) }; } /** * Function creates argument of type SimpleTimeSpanArgument in the argument chain * @param arg * @param comparison * @returns function that adds argument to end of chain */ ArgumentAsTimeSpan(arg, comparison = ConditionsTypes.Equal) { this._tmp.Guid = Guid.NewGuid(); this._tmp.HandledType = HandledType.Timer; this._tmp.LeftArgumentType = ArgumentTypeNum.ArgumentAsTimeSpan; this._tmp.ConditionType = comparison; this._tmp.AtMeetCondition = true; this._tmp.LeftArgument.Argument = arg; this._tmp.LeftArgument.HandledType = ArgumentTypeNum.ArgumentAsTimeSpan; return { AddArgument: () => this.AddSequence(), EditArgument: (sequenceGuid) => this.EditSequence(sequenceGuid) }; } /** * Function creates argument of type ArgumentAsTime in the argument chain * @param arg * @param atMeetCondition * @param comparison * @returns function that adds argument to end of chain */ ArgumentAsTime(arg, atMeetCondition, comparison = ConditionsTypes.Equal) { this._tmp.Guid = Guid.NewGuid(); this._tmp.HandledType = HandledType.Time; this._tmp.LeftArgumentType = ArgumentTypeNum.ArgumentAsTime; this._tmp.ConditionType = comparison; this._tmp.AtMeetCondition = atMeetCondition; this._tmp.LeftArgument.Argument = arg; this._tmp.LeftArgument.HandledType = ArgumentTypeNum.ArgumentAsTime; return { AddArgument: () => this.AddSequence(), EditArgument: (sequenceGuid) => this.EditSequence(sequenceGuid) }; } /** * Function creates argument of type DeviceStateArgument in the argument chain * @param arg * @param atMeetCondition * @param atMeetConditionTimeout * @param comparison * @returns function that adds argument to end of chain */ ArgumentAsDeviceState(arg, atMeetCondition, atMeetConditionTimeout, comparison = ConditionsTypes.Equal) { this._tmp.Guid = Guid.NewGuid(); this._tmp.HandledType = HandledType.DeviceState; this._tmp.LeftArgumentType = ArgumentTypeNum.ArgumentAsDeviceState; this._tmp.ConditionType = comparison; this._tmp.AtMeetCondition = atMeetCondition; this._tmp.ConditionTimeout = atMeetConditionTimeout; this._tmp.LeftArgument.Argument = this.GenerateDeviceStateArgument(arg); this._tmp.LeftArgument.HandledType = ArgumentTypeNum.ArgumentAsDeviceState; return { AddArgument: () => this.AddSequence(), EditArgument: (sequenceGuid) => this.EditSequence(sequenceGuid) }; } /** * Function creates argument of type ArgumentAsSceneExecuted - this argument waits until given scene will be executed, then returns 'true' and allow to execute check rest of arguments in chain. * @param arg */ ArgumentAsSceneExecuted(arg) { this._tmp.Guid = Guid.NewGuid(); this._tmp.HandledType = HandledType.SceneExecuted; this._tmp.LeftArgumentType = ArgumentTypeNum.ArgumentAsConditionInfo; this._tmp.ConditionType = ConditionsTypes.Equal; this._tmp.AtMeetCondition = true; const argumentData = new ConditionInfoArgument(); argumentData.DeviceGuid = arg.DeviceGuid; this._tmp.LeftArgument.Argument = argumentData; this._tmp.LeftArgument.HandledType = ArgumentTypeNum.ArgumentAsConditionInfo; return { AddArgument: () => this.AddSequence(), EditArgument: (sequenceGuid) => this.EditSequence(sequenceGuid) }; } GenerateDeviceStateArgument(arg) { const result = new LeftArgumentDeviceStateRequestData(); result.Channel = arg.GetCheckDeviceState().Channel; result.ComparisonMethod = arg.ComparisonMethod; let rawParams = {}; let paramsIterable = arg.GetComparisonParams().entries(); for (const [param, paramVal] of paramsIterable) { rawParams = Object.assign(Object.assign({}, rawParams), { [DeviceStateComparisonParams[param]]: paramVal.Value }); } result.ComparisonParams = rawParams; result.DeviceGuid = arg.DeviceGuid; result.DeviceStateType = this.MapResponseTypeToDeviceStateType(arg.Type); switch (arg.Type) { case DeviceResponseType.BatteryState: { result.CheckedDeviceState.State = arg.GetCheckDeviceState().State; result.CheckedDeviceState.Percentage = arg.GetCheckDeviceState().Percentage; break; } case DeviceResponseType.BinarySensorState: { result.CheckedDeviceState.State = arg.GetCheckDeviceState().State; break; } case DeviceResponseType.BlindPosition: { result.CheckedDeviceState.Position = arg.GetCheckDeviceState().Position; break; } case DeviceResponseType.LightBrightness: { result.CheckedDeviceState.Brightness = arg.GetCheckDeviceState().Brightness; break; } case DeviceResponseType.MeasuredBrightness: { result.CheckedDeviceState.Brightness = arg.GetCheckDeviceState().Brightness; break; } case DeviceResponseType.RemoteButtonState: { result.CheckedDeviceState.State = arg.GetCheckDeviceState().State; break; } case DeviceResponseType.ChannelOnOffState: { result.CheckedDeviceState.State = arg.GetCheckDeviceState().State; break; } case DeviceResponseType.DoorBellState: { result.CheckedDeviceState.State = arg.GetCheckDeviceState().State; break; } case DeviceResponseType.MeasuredEnergy: { let rawParams = {}; let paramsIterable = arg.GetCheckDeviceState().MeasurementParameters.entries(); for (const [param, paramVal] of paramsIterable) { rawParams = Object.assign(Object.assign({}, rawParams), { [EnergyMeasurementParameter[param]]: paramVal }); } result.CheckedDeviceState.MeasurementParameters = rawParams; break; } case DeviceResponseType.FloodSensorState: { result.CheckedDeviceState.State = arg.GetCheckDeviceState().State; break; } case DeviceResponseType.GatePosition: { result.CheckedDeviceState.Position = arg.GetCheckDeviceState().Position; result.CheckedDeviceState.PositionType = arg.GetCheckDeviceState().PositionType; break; } case DeviceResponseType.LightColor: { const st = arg.GetCheckDeviceState(); result.CheckedDeviceState.R = st.R; result.CheckedDeviceState.G = st.G; result.CheckedDeviceState.B = st.B; break; } case DeviceResponseType.LightWarmth: { result.CheckedDeviceState.Temperature = arg.GetCheckDeviceState().Temperature; break; } case DeviceResponseType.ReedState: { result.CheckedDeviceState.State = arg.GetCheckDeviceState().State; break; } case DeviceResponseType.SmokeSensorState: throw new UnsupportedScenesDeviceState("Unsupported device state!"); case DeviceResponseType.MeasuredTemperature: { result.CheckedDeviceState.Temperature = arg.GetCheckDeviceState().Temperature; break; } case DeviceResponseType.HumiditySensorState: { result.CheckedDeviceState.Humidity = arg.GetCheckDeviceState().Humidity; break; } case DeviceResponseType.BlindRemoteButtonState: { result.CheckedDeviceState.State = arg.GetCheckDeviceState().State; break; } case DeviceResponseType.LightRGBWState: { let st = arg.GetCheckDeviceState(); result.CheckedDeviceState.R = st.R; result.CheckedDeviceState.G = st.G; result.CheckedDeviceState.B = st.B; result.CheckedDeviceState.W = st.W; break; } case DeviceResponseType.FacadeRemoteButtonState: { result.CheckedDeviceState.State = arg.GetCheckDeviceState().State; break; } case DeviceResponseType.MovementSensorState: { let st = arg.GetCheckDeviceState(); result.CheckedDeviceState.Movement = st.Movement; result.CheckedDeviceState.Intensity = st.Intensity; break; } case DeviceResponseType.PressureSensorState: { result.CheckedDeviceState.Pressure = arg.GetCheckDeviceState().Pressure; result.CheckedDeviceState.PressureType = arg.GetCheckDeviceState().PressureType; break; } case DeviceResponseType.WindSpeedState: { result.CheckedDeviceState.Value = arg.GetCheckDeviceState().Value; break; } case DeviceResponseType.CurrentWindThreshold: { result.CheckedDeviceState.WindThreshold = arg.GetCheckDeviceState().WindThreshold; break; } case DeviceResponseType.Unknown: result.CheckedDeviceState = {}; break; default: throw new UnsupportedScenesDeviceState("Unsupported device state!"); } return result; } MapResponseTypeToDeviceStateType(responseType) { switch (responseType) { case DeviceResponseType.BatteryState: return DeviceStateType.BatteryState; case DeviceResponseType.BinarySensorState: return DeviceStateType.BinarySensor; case DeviceResponseType.BlindPosition: return DeviceStateType.BlindPosition; case DeviceResponseType.LightBrightness: return DeviceStateType.LightBrightness; case DeviceResponseType.RemoteButtonState: return DeviceStateType.ButtonState; case DeviceResponseType.ChannelOnOffState: return DeviceStateType.ChannelOnOff; case DeviceResponseType.DoorBellState: return DeviceStateType.DoorBell; case DeviceResponseType.MeasuredEnergy: return DeviceStateType.Energy; case DeviceResponseType.FloodSensorState: return DeviceStateType.FloodSensor; case DeviceResponseType.GatePosition: return DeviceStateType.GatePosition; case DeviceResponseType.MeasuredBrightness: return DeviceStateType.Brightness; case DeviceResponseType.LightColor: return DeviceStateType.LightColor; case DeviceResponseType.LightWarmth: return DeviceStateType.LightTemperature; case DeviceResponseType.ReedState: return DeviceStateType.ReedState; case DeviceResponseType.SmokeSensorState: return DeviceStateType.SmokeSensor; case DeviceResponseType.MeasuredTemperature: return DeviceStateType.Temperature; case DeviceResponseType.HumiditySensorState: return DeviceStateType.Humidity; case DeviceResponseType.BlindRemoteButtonState: return DeviceStateType.BlindsControlButton; case DeviceResponseType.FacadeRemoteButtonState: return DeviceStateType.FacadeControlButton; case DeviceResponseType.MovementSensorState: return DeviceStateType.Movement; case DeviceResponseType.PressureSensorState: return DeviceStateType.AirPressure; case DeviceResponseType.WindSpeedState: return DeviceStateType.WindSpeed; case DeviceResponseType.CurrentWindThreshold: return DeviceStateType.WindThreshold; default: return DeviceStateType.Unknown; } } /** * Function adds argument to the end of argument chain * @returns Functions that coud create another argument, function that coud commit argument chain, current argument data. */ AddSequence() { const findLastRightArgumentAndAddContition = (sequence, newData) => { if (sequence.RightArgumentType == ArgumentTypeNum.Null) { sequence.RightArgumentType = ArgumentTypeNum.Condition; sequence.RightArgument = newData; if (this._editingExistingSequence) { if (this._rawExistingSequenceTasks != null) { newData.Tasks = JSON.parse(JSON.stringify(this._rawExistingSequenceTasks)); sequence.Tasks = new TasksRequestData(); } } } else return findLastRightArgumentAndAddContition(sequence.RightArgument, newData); }; //result null - root sequence if (this._result == null) { this._tmp.Name = this._sequenceName; this._tmp.Icon = this._sequenceIcon; this._result = this._tmp; this._result.AtMeetCondition = true; } else findLastRightArgumentAndAddContition(this._result, this._tmp); const sequneceData = this._service.MapSequence(JSON.parse(JSON.stringify(this._tmp))); this._tmp = new SequenceRequestData(); return { GetNext: () => this.GetNext(), GetPrevious: () => this.GetPrevious(), GetByGuid: (sequenceGuid) => this.GetByGuid(sequenceGuid), GetAllowedArguments: () => this.GetAllowedArguments(), RemoveSequenceByGuid: (argumentGuid) => this.RemoveSequenceByGuid(argumentGuid), Commit: () => this.Commit(), ArgumentAsActionWithEmptyCondition: () => this.ArgumentAsActionWithEmptyCondition(), ArgumentAsAstronomicalClockWithOffset: (arg, atMeetCondition, comparasion) => this.ArgumentAsAstronomicalClockWithOffset(arg, atMeetCondition, comparasion), ArgumentAsDaysOfWeek: (arg, atMeetCondition, comparison) => this.ArgumentAsDaysOfWeek(arg, atMeetCondition, comparison), ArgumentAsDeviceState: (arg, atMeetCondition, atMeetConditionTimeout, comparison = ConditionsTypes.Equal) => this.ArgumentAsDeviceState(arg, atMeetCondition, atMeetConditionTimeout, comparison), ArgumentAsTime: (arg, atMeetCondition, comparison = ConditionsTypes.Equal) => this.ArgumentAsTime(arg, atMeetCondition, comparison), ArgumentAsTimeSpan: (arg, comparison) => this.ArgumentAsTimeSpan(arg, comparison), ArgumentAsSceneExecuted: (arg) => this.ArgumentAsSceneExecuted(arg), SequenceData: sequneceData }; } EditSequence(sequenceGuid) { const findLastArgumentWithGuidAndEditCondition = (sequence, newData) => { if (sequence.Guid == sequenceGuid && sequence.ObjectType == ArgumentTypeNum.Condition) { //Remap properties (needs in this format to keep reference to this._result, we changing only sequence properties without deattach root) newData is reference to this._tmp sequence.AtMeetCondition = newData.AtMeetCondition; sequence.ConditionTimeout = newData.ConditionTimeout; sequence.ConditionType = newData.ConditionType; sequence.ExecuteOnce = newData.ExecuteOnce; sequence.HandledType = newData.HandledType; sequence.IsDisabled = newData.IsDisabled; sequence.LeftArgument = newData.LeftArgument; sequence.LeftArgumentType = newData.LeftArgumentType; sequence.ObjectType = newData.ObjectType; sequence.Tasks = newData.Tasks; //Keep settings that are not edited in this scope newData.Guid = sequence.Guid; newData.Icon = sequence.Icon; newData.Name = sequence.Name; } else if (sequence.RightArgumentType == ArgumentTypeNum.Condition) return findLastArgumentWithGuidAndEditCondition(sequence.RightArgument, newData); else throw new SequenceNotFoundScenesBuilderException(`Cannot edit sequence with given GUID ${sequenceGuid} - sequence not foud in chain`); }; if (this._result == null) throw new SequenceNotFoundScenesBuilderException(`Cannot edit sequence with given GUID ${sequenceGuid} - sequence not initialized (no root element)`); findLastArgumentWithGuidAndEditCondition(this._result, this._tmp); const sequneceData = this._service.MapSequence(JSON.parse(JSON.stringify(this._tmp))); this._tmp = new SequenceRequestData(); console.log(`new tmp guid: ${this._tmp.Guid}`); if (!this._result.AtMeetCondition) this._result.AtMeetCondition = true; return { GetNext: () => this.GetNext(), GetPrevious: () => this.GetPrevious(), GetByGuid: (sequenceGuid) => this.GetByGuid(sequenceGuid), GetAllowedArguments: () => this.GetAllowedArguments(), RemoveSequenceByGuid: (sequenceGuid) => this.RemoveSequenceByGuid(sequenceGuid), Commit: () => this.Commit(), ArgumentAsActionWithEmptyCondition: () => this.ArgumentAsActionWithEmptyCondition(), ArgumentAsAstronomicalClockWithOffset: (arg, atMeetCondition, comparasion) => this.ArgumentAsAstronomicalClockWithOffset(arg, atMeetCondition, comparasion), ArgumentAsDaysOfWeek: (arg, atMeetCondition, comparison) => this.ArgumentAsDaysOfWeek(arg, atMeetCondition, comparison), ArgumentAsDeviceState: (arg, atMeetCondition, atMeetConditionTimeout, comparison = ConditionsTypes.Equal) => this.ArgumentAsDeviceState(arg, atMeetCondition, atMeetConditionTimeout, comparison), ArgumentAsTime: (arg, atMeetCondition, comparison = ConditionsTypes.Equal) => this.ArgumentAsTime(arg, atMeetCondition, comparison), ArgumentAsTimeSpan: (arg, comparison) => this.ArgumentAsTimeSpan(arg, comparison), ArgumentAsSceneExecuted: (arg) => this.ArgumentAsSceneExecuted(arg), SequenceData: sequneceData }; } RemoveSequenceByGuid(sequenceGuid) { let refToHead; const fixNavigation = (sequence, sequenceGuid) => { //navigation never used, don't need fix if (this._currentSeqGuid == "" && this._prevSeqGuid == "") return; if (this._currentSeqGuid == sequenceGuid) { this._currentSeqGuid = sequence.RightArgumentType == ArgumentTypeNum.Condition ? sequence.RightArgument.Guid : ""; this._prevSeqGuid = refToHead != null ? refToHead.Guid : ""; } else if (this._prevSeqGuid == sequenceGuid) { this._prevSeqGuid = refToHead != null ? refToHead.Guid : ""; } }; const findRightArgumentAndRemoveArg = (sequence, sequenceGuid) => { //Argument to remove if (sequence.Guid == sequenceGuid) { //no ref to head - root argument if (refToHead == null) this._result = null; else { refToHead.RightArgument = sequence.RightArgument; refToHead.RightArgumentType = sequence.RightArgumentType; } fixNavigation(sequence, sequenceGuid); } else { refToHead = sequence; if (sequence.RightArgumentType == ArgumentTypeNum.Null) throw new SequenceNotFoundScenesBuilderException(`Cannot remove sequence, sequence with given guid ${sequenceGuid} not found!`); return findRightArgumentAndRemoveArg(sequence.RightArgument, sequenceGuid); } }; if (this._result == null) throw new SequenceNotFoundScenesBuilderException("Cannot remove sequence, sequence not configured!"); //Removing root argument if (this._result.Guid == sequenceGuid && this._result.RightArgumentType != ArgumentTypeNum.Null) { this._result.RightArgument.Name = this._result.Name; this._result.RightArgument.Icon = this._result.Icon; this._result.RightArgument.Guid = this._result.Guid; this._result = this._result.RightArgument; this._result.AtMeetCondition = true; fixNavigation(this._result, sequenceGuid); } else { //Removing nested arguments findRightArgumentAndRemoveArg(this._result, sequenceGuid); } return { GetNext: () => this.GetNext(), GetPrevious: () => this.GetPrevious(), GetByGuid: (sequenceGuid) => this.GetByGuid(sequenceGuid), GetAllowedArguments: () => this.GetAllowedArguments(), RemoveSequenceByGuid: (sequenceGuid) => this.RemoveSequenceByGuid(sequenceGuid), Commit: () => this.Commit(), ArgumentAsActionWithEmptyCondition: () => this.ArgumentAsActionWithEmptyCondition(), ArgumentAsAstronomicalClockWithOffset: (arg, atMeetCondition, comparasion) => this.ArgumentAsAstronomicalClockWithOffset(arg, atMeetCondition, comparasion), ArgumentAsDaysOfWeek: (arg, atMeetCondition, comparison) => this.ArgumentAsDaysOfWeek(arg, atMeetCondition, comparison), ArgumentAsDeviceState: (arg, atMeetCondition, atMeetConditionTimeout, comparison = ConditionsTypes.Equal) => this.ArgumentAsDeviceState(arg, atMeetCondition, atMeetConditionTimeout, comparison), ArgumentAsTime: (arg, atMeetCondition, comparison = ConditionsTypes.Equal) => this.ArgumentAsTime(arg, atMeetCondition, comparison), ArgumentAsTimeSpan: (arg, comparison) => this.ArgumentAsTimeSpan(arg, comparison), ArgumentAsSceneExecuted: (arg) => this.ArgumentAsSceneExecuted(arg), SequenceData: null }; } /** * Functions commits modifications of arguments chain * @returns */ Commit() { const findLastRightArgumentAndMoveTasks = (sequence) => { //Last arg if (sequence.RightArgumentType == ArgumentTypeNum.Null) { if (this._rawExistingSequenceTasks != null) sequence.Tasks = this._rawExistingSequenceTasks; } else return findLastRightArgumentAndMoveTasks(sequence.RightArgument); }; if (this._result == null) throw new SequenceNotFoundScenesBuilderException("Sequence are not initialized! Not found root sequence."); //Move tasks to last argument - required while editing (removing or adding argument) if (this._editingExistingSequence) findLastRightArgumentAndMoveTasks(this._result); return { GetSupportedTypesOfTasks: () => this.GetSupportedTaskTypesAsync(), DeviceTask: (task) => this.DeviceTask(task), DelayTask: (delay) => this.DelayTask(delay), NotifyTask: (notification) => this.NotifyTask(notification), LockExecutionTask: (lockTask) => this.LockExecutionTask(lockTask), RemoveTask: (taskGuid) => this.RemoveTask(taskGuid), Build: () => this.Build(), BuildToISequence: () => this._service.MapSequence(JSON.parse(JSON.stringify(this._result))), ValidateScene: () => this.ValidateScene(), }; } DeviceTask(task) { this._tmpTask = this.ParseDeviceTask(task); if (this._result == null) throw new Error("No context!"); return { AddTask: () => this.AddTask(), EditTask: (taskId) => this.EditTask(taskId), TaskId: Helpers.GenerateMd5(JSON.stringify(this._tmpTask)), }; } ParseDeviceTask(task) { const tsk = new DeviceTaskRequestData(); tsk.Channel = task.Channel; tsk.DeviceGuid = task.DeviceGuid; tsk.ControlFeature = this.MapTaskTypeToControlFeature(task.TaskType); tsk.Data = {}; tsk.Data.Channel = task.Channel; if (task.TaskType == DeviceTaskType.SetBlindPosition || task instanceof SetBlindPosition) { tsk.Data.Position = task.Position; tsk.Data.Action = task.Action; } else if (task.TaskType == DeviceTaskType.SetBlindPositionSimple || task instanceof SetBlindPositionSimple) { tsk.Data.Action = task.Action; tsk.Data.Position = 0; } else if (task.TaskType == DeviceTaskType.SetLightColor || task instanceof SetLightColor) { tsk.Data.R = task.R; tsk.Data.G = task.G; tsk.Data.B = task.B; } else if (task.TaskType == DeviceTaskType.SetLightTemperature || task instanceof SetLightTemperature) { tsk.Data.Temperature = task.Temperature; } else if (task.TaskType == DeviceTaskType.SetLightBrightness || task instanceof SetLightBrightness) { tsk.Data.Brightness = task.Brightness; } else if (task.TaskType == DeviceTaskType.SetTemperature || task instanceof SetTemperature) { tsk.Data.Temperature = task.Temperature; } else if (task.TaskType == DeviceTaskType.TurnOnWithTime || task instanceof TurnOnWithTime) { tsk.Data.SwitchOffDelaySeconds = task.SwitchOffDelaySeconds; } else if (task.TaskType == DeviceTaskType.SetLightBrightnessDynamicly || task instanceof SetLightBrightnessDynamicly) { tsk.Data.Brightness = task.Brightness; } else if (task.TaskType == DeviceTaskType.SetBlindOpenCloseTime || task instanceof SetBlindOpenCloseTime) { tsk.Data.CloseTime = task.Data.CloseTime; tsk.Data.OpenTime = task.Data.OpenTime; } else if (task.TaskType == DeviceTaskType.SetBlindMicroventilation || task instanceof SetBlindMicroventilation) { tsk.Data.Position = task.Position; } else if (task.TaskType == DeviceTaskType.SetFacadePosition || task instanceof SetFacadePosition) { tsk.Data.FacadeAction = task.FacadeAction; tsk.Data.Position = task.Position; tsk.Data.Tilt = task.Tilt; } else if (task.TaskType == DeviceTaskType.LightRGBW || task instanceof LightRGBW) { tsk.Data.R = task.Data.R; tsk.Data.G = task.Data.G; tsk.Data.B = task.Data.B; tsk.Data.W = task.Data.W; tsk.Data.Brightness = task.Data.Brightness; } else if (task.TaskType == DeviceTaskType.PreciseGateControl || task instanceof SetGatePositionPrecise) { tsk.Data.Position = task.Position; tsk.Data.GateControlAction = task.GateControlAction; } //else // throw new Error(`Unsupported device task, task type: ${task.TaskType} task object: ${task}!`); return tsk; } DelayTask(delay) { const task = new DelayTaskRequestData(); task.Delay = delay.Delay.Value; this._tmpTask = task; if (this._result == null) throw new Error("No context!"); return { AddTask: () => this.AddTask(), EditTask: (taskGuid) => this.EditTask(taskGuid), TaskId: Helpers.GenerateMd5(JSON.stringify(task)), }; } NotifyTask(notification) { if (notification.NotifyType == NotifyType.Email) throw new UnsupportedArgumentScenesBuilderException("NotifyType 'Email' is not supported!"); if (notification.NotifyType == NotifyType.None) throw new Error("NotifyType mus be set!"); if (notification.Message == "") throw new BadParametersScenesBuilderException("Message must be set!"); if (notification.Message.length > 1000) throw new BadParametersScenesBuilderException("Message is too long! Max length is 1000 characters."); const task = new NotifyTaskRequestData(); task.NotifyType = notification.NotifyType; task.Message = notification.Message; task.NotificationClients = notification.NotificationClients; task.Title = this._sequenceName; this._tmpTask = task; if (this._result == null) throw new Error("No context!"); return { AddTask: () => this.AddTask(), EditTask: (taskGuid) => this.EditTask(taskGuid), TaskId: Helpers.GenerateMd5(JSON.stringify(task)), }; } LockExecutionTask(lockTask) { const task = new LockTaskTaskRequestData(); task.LockType = lockTask.LockType; if (task.LockType == LockType.Timeout) { if (task.LockEndHour != "00:00:00") throw new UnsupportedArgumentScenesBuilderException("Parameter LockEndHour at lock type `Timeout` must be default!"); } task.LockStartHour = lockTask.LockStartHour.Value; task.LockEndHour = lockTask.LockEndHour.Value; task.ResetLockScenesGuids = lockTask.ResetLockScenesGuids; this._tmpTask = task; if (this._result == null) throw new Error("No context!"); return { AddTask: () => this.AddTask(), EditTask: (taskGuid) => this.EditTask(taskGuid), TaskId: Helpers.GenerateMd5(JSON.stringify(task)), }; } AddTask() { const findLastRightArgumentAndAddTask = (sequence) => { var _a; //Last arg if (sequence.RightArgumentType == ArgumentTypeNum.Null) { if (this._tmpTask == null) throw Error("No task context!"); if (sequence.Tasks.DevicesTasks === undefined || sequence.Tasks.DevicesTasks === null) sequence.Tasks.DevicesTasks = []; if (this._tmpTask instanceof DeviceTaskRequestData) sequence.Tasks.DevicesTasks.push(structuredClone(this._tmpTask)); sequence.Tasks.ControllerTasks.push(structuredClone(this._tmpTask)); this._tmpTask = null; (_a = DependencyContainer.Log) === null || _a === void 0 ? void 0 : _a.Debug(`Task added to sequence: \n${JSON.stringify(this._tmpTask)} \ntasks on a list: \n${JSON.stringify(sequence.Tasks)}`); } else return findLastRightArgumentAndAddTask(sequence.RightArgument); }; if (this._result == null) throw new Error("No context!"); findLastRightArgumentAndAddTask(this._result); return { GetSupportedTypesOfTasks: () => this.GetSupportedTaskTypesAsync(), DeviceTask: (task) => this.DeviceTask(task), DelayTask: (delay) => this.DelayTask(delay), NotifyTask: (notification) => this.NotifyTask(notification), LockExecutionTask: (lockTask) => this.LockExecutionTask(lockTask), RemoveTask: (taskId) => this.RemoveTask(taskId), Build: () => this.Build(), BuildToISequence: () => this._service.MapSequence(JSON.parse(JSON.stringify(this._result))), ValidateScene: () => this.ValidateScene(), }; } EditTask(taskId) { const findLastRightArgumentAndEditTask = (sequence) => { var _a; //Last arg if (sequence.RightArgumentType == ArgumentTypeNum.Null) { if (this._tmpTask == null) throw Error("No task context!"); if (sequence.Tasks.ControllerTasks == null || sequence.Tasks.ControllerTasks.length == 0) throw new TaskNotFoundScenesBuilderException("Task not found, cannot edit!"); if (this._tmpTask instanceof DeviceTaskRequestData && sequence.Tasks.DevicesTasks != null) sequence.Tasks.DevicesTasks = sequence.Tasks.DevicesTasks.map(dTask => Helpers.GenerateMd5(JSON.stringify(dTask)) == taskId ? structuredClone(this._tmpTask) : dTask); sequence.Tasks.ControllerTasks = sequence.Tasks.ControllerTasks.map(cTask => Helpers.GenerateMd5(JSON.stringify(cTask)) == taskId ? structuredClone(this._tmpTask) : cTask); this._tmpTask = null; (_a = DependencyContainer.Log) === null || _a === void 0 ? void 0 : _a.Debug(`Task edited in sequence: \n${JSON.stringify(this._tmpTask)} \ntasks on a list: \n${JSON.stringify(sequence.Tasks)}`); } else return findLastRightArgumentAndEditTask(sequence.RightArgument); }; if (this._result == null) throw new Error("No context!"); findLastRightArgumentAndEditTask(this._result); return { GetSupportedTypesOfTasks: () => this.GetSupportedTaskTypesAsync(), DeviceTask: (task) => this.DeviceTask(task), DelayTask: (delay) => this.DelayTask(delay), NotifyTask: (notification) => this.NotifyTask(notification), LockExecutionTask: (lockTask) => this.LockExecutionTask(lockTask), RemoveTask: (taskGuid) => this.RemoveTask(taskGuid), Build: () => this.Build(), BuildToISequence: () => this._service.MapSequence(JSON.parse(JSON.stringify(this._result))), ValidateScene: () => this.ValidateScene(), }; } RemoveTask(taskId) { const findLastRightArgumentAndRemoveTask = (sequence, taskId) => { var _a, _b; //Last arg if (sequence.RightArgumentType == ArgumentTypeNum.Null) { const newTasks = sequence.Tasks.ControllerTasks.filter(t => Helpers.GenerateMd5(JSON.stringify(t)) != taskId); if (newTasks == null) sequence.Tasks.ControllerTasks = []; else sequence.Tasks.ControllerTasks = newTasks; if ((_b = (_a = sequence.Tasks) === null || _a === void 0 ? void 0 : _a.DevicesTasks) === null || _b === void 0 ? void 0 : _b.any()) { const newTasks = sequence.Tasks.DevicesTasks.filter(t => Helpers.GenerateMd5(JSON.stringify(t)) != taskId); if (newTasks == null) sequence.Tasks.DevicesTasks = []; else sequence.Tasks.DevicesTasks = newTasks; } } else return findLastRightArgumentAndRemoveTask(sequence.RightArgument, taskId); }; if (this._result == null) throw Error("Cannot remove task, sequence not configured!"); findLastRightArgumentAndRemoveTask(this._result, taskId); } MapTaskTypeToControlFeature(taskType) { switch (taskType) { case DeviceTaskType.Unknown: throw new BadParametersScenesBuilderException("Unsupported task type!"); case DeviceTaskType.SetBlindPosition: return DeviceControlFeature.SetBlindPosition; case DeviceTaskType.SetBlindPositionSimple: return DeviceControlFeature.SetBlindPosition; case DeviceTaskType.SetBlindMicroventilation: return DeviceControlFeature.SetBlindMicroventilation; case DeviceTaskType.TurnOff: return DeviceControlFeature.TurnOff; case DeviceTaskType.TurnOn: return DeviceControlFeature.TurnOn; case DeviceTaskType.TurnOnWithTime: return DeviceControlFeature.TurnOnWithTimeout; case DeviceTaskType.TogleState: return DeviceControlFeature.ToggleState; case DeviceTaskType.SetLightBrightnessDynamicly: return DeviceControlFeature.SetLightBrightnessDynamicly; case DeviceTaskType.SetLightBrightness: return DeviceControlFeature.SetLightBrightness; case DeviceTaskType.SetLightColor: return DeviceControlFeature.SetLightColor; case DeviceTaskType.SetLightTemperature: return DeviceControlFeature.SetLightTemperature; case DeviceTaskType.PairDevice: throw new BadParametersScenesBuilderException("Unsupported task type!"); case DeviceTaskType.UnpairDevice: throw new BadParametersScenesBuilderException("Unsupported task type!"); case DeviceTaskType.IdentifyDevice: return DeviceControlFeature.IdentifyDevice; case DeviceTaskType.GetChannelsState: return DeviceContr