mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 22:57:21 +00:00
1453 lines
50 KiB
Dart
1453 lines
50 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:bloc/bloc.dart';
|
|
import 'package:dio/dio.dart';
|
|
import 'package:equatable/equatable.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
|
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
|
|
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
|
|
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
|
|
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_scene_model.dart';
|
|
import 'package:syncrow_web/pages/routines/models/delay/delay_fucntions.dart';
|
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
|
import 'package:syncrow_web/pages/routines/models/routine_details_model.dart';
|
|
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
|
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
|
import 'package:syncrow_web/services/routines_api.dart';
|
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
|
import 'package:syncrow_web/utils/navigation_service.dart';
|
|
import 'package:syncrow_web/utils/snack_bar.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
part 'routine_event.dart';
|
|
part 'routine_state.dart';
|
|
|
|
class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|
RoutineBloc() : super(const RoutineState()) {
|
|
on<AddToIfContainer>(_onAddToIfContainer);
|
|
on<AddToThenContainer>(_onAddToThenContainer);
|
|
on<LoadScenes>(_onLoadScenes);
|
|
on<LoadAutomation>(_onLoadAutomation);
|
|
on<AddFunctionToRoutine>(_onAddFunctionsToRoutine);
|
|
on<SearchRoutines>(_onSearchRoutines);
|
|
on<AddSelectedIcon>(_onAddSelectedIcon);
|
|
on<CreateSceneEvent>(_onCreateScene);
|
|
on<RemoveDragCard>(_onRemoveDragCard);
|
|
on<ChangeAutomationOperator>(_changeOperatorOperator);
|
|
on<EffectiveTimePeriodEvent>(_onEffectiveTimeEvent);
|
|
on<CreateAutomationEvent>(_onCreateAutomation);
|
|
on<SetRoutineName>(_onSetRoutineName);
|
|
on<ResetRoutineState>(_onResetRoutineState);
|
|
on<GetSceneDetails>(_onGetSceneDetails);
|
|
on<GetAutomationDetails>(_onGetAutomationDetails);
|
|
on<DeleteScene>(_deleteScene);
|
|
// on<DeleteAutomation>(_deleteAutomation);
|
|
on<FetchDevicesInRoutine>(_fetchDevices);
|
|
on<UpdateScene>(_onUpdateScene);
|
|
on<UpdateAutomation>(_onUpdateAutomation);
|
|
on<TriggerSwitchTabsEvent>(_triggerSwitchTabsEvent);
|
|
on<CreateNewRoutineViewEvent>(_createNewRoutineViewEvent);
|
|
on<ResetErrorMessage>(_resetErrorMessage);
|
|
on<SceneTrigger>(_onSceneTrigger);
|
|
on<UpdateAutomationStatus>(_onUpdateAutomationStatus);
|
|
}
|
|
String selectedSpaceId = '';
|
|
String selectedCommunityId = '';
|
|
|
|
FutureOr<void> _triggerSwitchTabsEvent(
|
|
TriggerSwitchTabsEvent event,
|
|
Emitter<RoutineState> emit,
|
|
) {
|
|
emit(state.copyWith(
|
|
routineTab: event.isRoutineTab, createRoutineView: false));
|
|
add(ResetRoutineState());
|
|
if (event.isRoutineTab) {
|
|
add(const LoadScenes());
|
|
add(const LoadAutomation());
|
|
}
|
|
}
|
|
|
|
_resetErrorMessage(
|
|
ResetErrorMessage event,
|
|
Emitter<RoutineState> emit,
|
|
) {
|
|
emit(state.copyWith(errorMessage: ''));
|
|
}
|
|
|
|
FutureOr<void> _createNewRoutineViewEvent(
|
|
CreateNewRoutineViewEvent event,
|
|
Emitter<RoutineState> emit,
|
|
) {
|
|
emit(state.copyWith(createRoutineView: event.createRoutineView));
|
|
}
|
|
|
|
void _onAddToIfContainer(AddToIfContainer event, Emitter<RoutineState> emit) {
|
|
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
|
|
|
|
// Find the index of the item in teh current itemsList
|
|
int index = updatedIfItems.indexWhere(
|
|
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
|
// Replace the map if the index is valid
|
|
if (index != -1) {
|
|
updatedIfItems[index] = event.item;
|
|
} else {
|
|
updatedIfItems.add(event.item);
|
|
}
|
|
|
|
if (event.isTabToRun) {
|
|
emit(state.copyWith(
|
|
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
|
|
} else {
|
|
emit(state.copyWith(
|
|
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
|
|
}
|
|
}
|
|
|
|
void _onAddToThenContainer(
|
|
AddToThenContainer event, Emitter<RoutineState> emit) {
|
|
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
|
|
|
|
// Find the index of the item in teh current itemsList
|
|
int index = currentItems.indexWhere(
|
|
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
|
// Replace the map if the index is valid
|
|
if (index != -1) {
|
|
currentItems[index] = event.item;
|
|
} else {
|
|
currentItems.add(event.item);
|
|
}
|
|
|
|
emit(state.copyWith(thenItems: currentItems));
|
|
}
|
|
|
|
void _onAddFunctionsToRoutine(
|
|
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
|
|
try {
|
|
if (event.functions.isEmpty) return;
|
|
|
|
// List<DeviceFunctionData> selectedFunction = List<DeviceFunctionData>.from(event.functions);
|
|
|
|
Map<String, List<DeviceFunctionData>> currentSelectedFunctions =
|
|
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
|
|
|
// if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
|
|
// List<DeviceFunctionData> currentFunctions =
|
|
// List<DeviceFunctionData>.from(currentSelectedFunctions[event.uniqueCustomId] ?? []);
|
|
|
|
// List<String> functionCode = [];
|
|
// for (int i = 0; i < selectedFunction.length; i++) {
|
|
// for (int j = 0; j < currentFunctions.length; j++) {
|
|
// if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) {
|
|
// currentFunctions[j] = selectedFunction[i];
|
|
// if (!functionCode.contains(currentFunctions[j].functionCode)) {
|
|
// functionCode.add(currentFunctions[j].functionCode);
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// for (int i = 0; i < functionCode.length; i++) {
|
|
// selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]);
|
|
// }
|
|
|
|
// currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions)
|
|
// ..addAll(selectedFunction);
|
|
// } else {
|
|
// currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
|
|
// }
|
|
|
|
currentSelectedFunctions[event.uniqueCustomId] =
|
|
List.from(event.functions);
|
|
|
|
emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
|
|
} catch (e) {
|
|
debugPrint('Error adding functions: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> _onLoadScenes(
|
|
LoadScenes event, Emitter<RoutineState> emit) async {
|
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
|
List<ScenesModel> scenes = [];
|
|
try {
|
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
|
if (createRoutineBloc.selectedSpaceId == '' &&
|
|
createRoutineBloc.selectedCommunityId == '') {
|
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
|
List<String> spacesList =
|
|
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
|
for (var spaceId in spacesList) {
|
|
scenes.addAll(
|
|
await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
|
}
|
|
}
|
|
} else {
|
|
scenes.addAll(await SceneApi.getScenes(
|
|
createRoutineBloc.selectedSpaceId,
|
|
createRoutineBloc.selectedCommunityId,
|
|
projectUuid));
|
|
}
|
|
|
|
emit(state.copyWith(
|
|
scenes: scenes,
|
|
isLoading: false,
|
|
));
|
|
} catch (e) {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
loadScenesErrorMessage: 'Failed to load scenes',
|
|
errorMessage: '',
|
|
loadAutomationErrorMessage: '',
|
|
scenes: scenes));
|
|
}
|
|
}
|
|
|
|
Future<void> _onLoadAutomation(
|
|
LoadAutomation event, Emitter<RoutineState> emit) async {
|
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
|
List<ScenesModel> automations = [];
|
|
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
|
|
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
|
try {
|
|
if (createRoutineBloc.selectedSpaceId == '' &&
|
|
createRoutineBloc.selectedCommunityId == '') {
|
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
|
List<String> spacesList =
|
|
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
|
for (var spaceId in spacesList) {
|
|
automations.addAll(
|
|
await SceneApi.getAutomation(spaceId, communityId, projectId));
|
|
}
|
|
}
|
|
} else {
|
|
automations.addAll(await SceneApi.getAutomation(
|
|
createRoutineBloc.selectedSpaceId,
|
|
createRoutineBloc.selectedCommunityId,
|
|
projectId));
|
|
}
|
|
emit(state.copyWith(
|
|
automations: automations,
|
|
isLoading: false,
|
|
));
|
|
} catch (e) {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
loadAutomationErrorMessage: 'Failed to load automations',
|
|
errorMessage: '',
|
|
loadScenesErrorMessage: '',
|
|
automations: automations));
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _onSearchRoutines(
|
|
SearchRoutines event, Emitter<RoutineState> emit) async {
|
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
emit(state.copyWith(isLoading: false, errorMessage: null));
|
|
emit(state.copyWith(searchText: event.query));
|
|
}
|
|
|
|
FutureOr<void> _onAddSelectedIcon(
|
|
AddSelectedIcon event, Emitter<RoutineState> emit) {
|
|
emit(state.copyWith(selectedIcon: event.icon));
|
|
}
|
|
|
|
// bool _isFirstActionDelay(List<Map<String, dynamic>> actions) {
|
|
// if (actions.isEmpty) return false;
|
|
// return actions.first['deviceId'] == 'delay';
|
|
// }
|
|
|
|
bool _isLastActionDelay(List<Map<String, dynamic>> actions) {
|
|
if (actions.isEmpty) return false;
|
|
return actions.last['deviceId'] == 'delay';
|
|
}
|
|
|
|
Future<void> _onCreateScene(
|
|
CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
|
try {
|
|
// Check if first action is delay
|
|
// if (_isFirstActionDelay(state.thenItems)) {
|
|
// emit(state.copyWith(
|
|
// errorMessage: 'Cannot have delay as the first action',
|
|
// isLoading: false,
|
|
// ));
|
|
// return;
|
|
// }
|
|
|
|
if (_isLastActionDelay(state.thenItems)) {
|
|
emit(state.copyWith(
|
|
errorMessage:
|
|
'A delay condition cannot be the only or the last action',
|
|
isLoading: false,
|
|
));
|
|
return;
|
|
}
|
|
|
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
|
|
|
final actions = state.thenItems.expand((item) {
|
|
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
|
return functions.map((function) {
|
|
if (function.functionCode == 'automation') {
|
|
return CreateSceneAction(
|
|
actionType: 'automation',
|
|
entityId: function.entityId,
|
|
actionExecutor: function.value,
|
|
executorProperty: null,
|
|
);
|
|
}
|
|
|
|
if (item['deviceId'] == 'delay') {
|
|
return CreateSceneAction(
|
|
entityId: function.entityId,
|
|
actionExecutor: 'delay',
|
|
executorProperty: CreateSceneExecutorProperty(
|
|
functionCode: '',
|
|
functionValue: '',
|
|
delaySeconds: int.tryParse(function.value.toString()) ?? 0,
|
|
),
|
|
);
|
|
}
|
|
|
|
return CreateSceneAction(
|
|
actionType: 'scene',
|
|
entityId: function.entityId,
|
|
actionExecutor: 'device_issue',
|
|
executorProperty: CreateSceneExecutorProperty(
|
|
functionCode: function.functionCode,
|
|
functionValue: function.value,
|
|
delaySeconds: 0,
|
|
),
|
|
);
|
|
});
|
|
}).toList();
|
|
|
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
|
|
|
final createSceneModel = CreateSceneModel(
|
|
spaceUuid: createRoutineBloc.selectedSpaceId,
|
|
iconId: state.selectedIcon ?? '',
|
|
showInDevice: true,
|
|
sceneName: state.routineName ?? '',
|
|
decisionExpr: 'and',
|
|
actions: actions,
|
|
);
|
|
|
|
final result = await SceneApi.createScene(createSceneModel);
|
|
if (result['success']) {
|
|
add(ResetRoutineState());
|
|
add(const LoadScenes());
|
|
add(const LoadAutomation());
|
|
} else {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: 'Something went wrong',
|
|
));
|
|
}
|
|
} on APIException catch (e) {
|
|
final errorData = e.message;
|
|
String errorMessage = errorData;
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: errorMessage,
|
|
));
|
|
}
|
|
}
|
|
|
|
Future<void> _onCreateAutomation(
|
|
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
|
|
try {
|
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
|
emit(state.copyWith(
|
|
errorMessage: 'Automation name is required',
|
|
));
|
|
CustomSnackBar.redSnackBar('Automation name is required');
|
|
return;
|
|
}
|
|
// if (_isFirstActionDelay(state.thenItems)) {
|
|
// emit(state.copyWith(
|
|
// errorMessage: 'Cannot have delay as the first action',
|
|
// isLoading: false,
|
|
// ));
|
|
// CustomSnackBar.redSnackBar('Cannot have delay as the first action');
|
|
|
|
// return;
|
|
// }
|
|
|
|
if (_isLastActionDelay(state.thenItems)) {
|
|
emit(state.copyWith(
|
|
errorMessage:
|
|
'A delay condition cannot be the only or the last action',
|
|
isLoading: false,
|
|
));
|
|
CustomSnackBar.redSnackBar('Cannot have delay as the last action');
|
|
return;
|
|
}
|
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
|
int i = 0;
|
|
final conditions = state.ifItems.expand((item) {
|
|
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
|
return functions.map((function) {
|
|
return Condition(
|
|
code: i++,
|
|
entityId: function.entityId,
|
|
entityType: 'device_report',
|
|
expr: ConditionExpr(
|
|
statusCode: function.functionCode,
|
|
comparator: function.condition ?? '==',
|
|
statusValue: function.value,
|
|
),
|
|
);
|
|
});
|
|
}).toList();
|
|
|
|
if (conditions.isEmpty) {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: 'At least one condition is required',
|
|
));
|
|
return;
|
|
}
|
|
|
|
final actions = state.thenItems.expand((item) {
|
|
if (item['type'] == 'tap_to_run') {
|
|
return [
|
|
AutomationAction(
|
|
actionType: 'scene',
|
|
entityId: item['deviceId'],
|
|
actionExecutor: 'rule_trigger',
|
|
),
|
|
];
|
|
}
|
|
|
|
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
|
|
|
return functions.map((function) {
|
|
if (function.functionCode == 'automation') {
|
|
return AutomationAction(
|
|
actionType: 'automation',
|
|
entityId: function.entityId,
|
|
actionExecutor: function.value,
|
|
executorProperty: null,
|
|
);
|
|
}
|
|
|
|
if (item['deviceId'] == 'delay') {
|
|
return AutomationAction(
|
|
entityId: function.entityId,
|
|
actionExecutor: 'delay',
|
|
executorProperty: ExecutorProperty(
|
|
delaySeconds: int.tryParse(function.value.toString()) ?? 0,
|
|
),
|
|
);
|
|
}
|
|
|
|
return AutomationAction(
|
|
entityId: function.entityId,
|
|
actionExecutor: 'device_issue',
|
|
executorProperty: ExecutorProperty(
|
|
functionCode: function.functionCode,
|
|
functionValue: function.value,
|
|
),
|
|
);
|
|
});
|
|
}).toList();
|
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
|
|
|
final createAutomationModel = CreateAutomationModel(
|
|
spaceUuid: createRoutineBloc.selectedSpaceId,
|
|
automationName: state.routineName ?? '',
|
|
decisionExpr: state.selectedAutomationOperator,
|
|
effectiveTime: EffectiveTime(
|
|
start: state.effectiveTime?.start ?? '00:00',
|
|
end: state.effectiveTime?.end ?? '23:59',
|
|
loops: state.effectiveTime?.loops ?? '1111111',
|
|
),
|
|
conditions: conditions,
|
|
actions: actions,
|
|
);
|
|
|
|
final result =
|
|
await SceneApi.createAutomation(createAutomationModel, projectUuid);
|
|
if (result['success']) {
|
|
add(ResetRoutineState());
|
|
add(const LoadAutomation());
|
|
add(const LoadScenes());
|
|
} else {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: result['message'],
|
|
));
|
|
CustomSnackBar.redSnackBar('Something went wrong');
|
|
}
|
|
} on APIException catch (e) {
|
|
final errorData = e.message;
|
|
String errorMessage = errorData;
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: errorMessage,
|
|
));
|
|
CustomSnackBar.redSnackBar(errorMessage);
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _onRemoveDragCard(
|
|
RemoveDragCard event, Emitter<RoutineState> emit) {
|
|
if (event.isFromThen) {
|
|
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
|
|
final selectedFunctions =
|
|
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
|
|
|
thenItems.removeAt(event.index);
|
|
selectedFunctions.remove(event.key);
|
|
emit(state.copyWith(
|
|
thenItems: thenItems, selectedFunctions: selectedFunctions));
|
|
} else {
|
|
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
|
|
final selectedFunctions =
|
|
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
|
|
|
ifItems.removeAt(event.index);
|
|
selectedFunctions.remove(event.key);
|
|
if (ifItems.isEmpty && state.thenItems.isEmpty) {
|
|
emit(state.copyWith(
|
|
ifItems: ifItems,
|
|
selectedFunctions: selectedFunctions,
|
|
isAutomation: false,
|
|
isTabToRun: false));
|
|
} else {
|
|
emit(state.copyWith(
|
|
ifItems: ifItems, selectedFunctions: selectedFunctions));
|
|
}
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _changeOperatorOperator(
|
|
ChangeAutomationOperator event, Emitter<RoutineState> emit) {
|
|
emit(state.copyWith(
|
|
selectedAutomationOperator: event.operator,
|
|
));
|
|
}
|
|
|
|
FutureOr<void> _onEffectiveTimeEvent(
|
|
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
|
|
emit(state.copyWith(effectiveTime: event.effectiveTime));
|
|
}
|
|
|
|
FutureOr<void> _onSetRoutineName(
|
|
SetRoutineName event, Emitter<RoutineState> emit) {
|
|
emit(state.copyWith(
|
|
routineName: event.name,
|
|
));
|
|
}
|
|
|
|
// (
|
|
// List<Map<String, dynamic>>,
|
|
// List<Map<String, dynamic>>,
|
|
// Map<String, List<DeviceFunctionData>>
|
|
// ) _createCardData(
|
|
// List<RoutineAction> actions,
|
|
// List<RoutineCondition>? conditions,
|
|
// Map<String, List<DeviceFunctionData>> currentFunctions,
|
|
// bool isTabToRun,
|
|
// ) {
|
|
// final ifItems = isTabToRun
|
|
// ? [
|
|
// {
|
|
// 'entityId': 'tab_to_run',
|
|
// 'uniqueCustomId': const Uuid().v4(),
|
|
// 'deviceId': 'tab_to_run',
|
|
// 'title': 'Tap to run',
|
|
// 'productType': 'tab_to_run',
|
|
// 'imagePath': Assets.tabToRun,
|
|
// }
|
|
// ]
|
|
// : conditions?.map((condition) {
|
|
// final matchingDevice = state.devices.firstWhere(
|
|
// (device) => device.uuid == condition.entityId,
|
|
// orElse: () => AllDevicesModel(
|
|
// uuid: condition.entityId,
|
|
// name: condition.entityId,
|
|
// productType: condition.entityType,
|
|
// ),
|
|
// );
|
|
|
|
// final cardData = {
|
|
// 'entityId': condition.entityId,
|
|
// 'uniqueCustomId': const Uuid().v4(),
|
|
// 'deviceId': condition.entityId,
|
|
// 'title': matchingDevice.name ?? condition.entityId,
|
|
// 'productType': condition.entityType,
|
|
// 'imagePath':
|
|
// matchingDevice.getDefaultIcon(condition.entityType),
|
|
// };
|
|
|
|
// final functions = matchingDevice.functions;
|
|
|
|
// for (var function in functions) {
|
|
// if (function.code == condition.expr.statusCode) {
|
|
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
|
// DeviceFunctionData(
|
|
// entityId: condition.entityId,
|
|
// functionCode: condition.expr.statusCode,
|
|
// value: condition.expr.statusValue,
|
|
// operationName: function.operationName,
|
|
// ),
|
|
// ];
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
// return cardData;
|
|
// }).toList() ??
|
|
// [];
|
|
|
|
// final thenItems = actions.map((action) {
|
|
// final matchingDevice = state.devices.firstWhere(
|
|
// (device) => device.uuid == action.entityId,
|
|
// orElse: () => AllDevicesModel(
|
|
// uuid: action.entityId,
|
|
// name: action.entityId,
|
|
// productType: action.productType,
|
|
// ),
|
|
// );
|
|
|
|
// final cardData = {
|
|
// 'entityId': action.entityId,
|
|
// 'uniqueCustomId': const Uuid().v4(),
|
|
// 'deviceId':
|
|
// action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
|
// 'title': action.actionExecutor == 'delay'
|
|
// ? 'Delay'
|
|
// : (matchingDevice.name ?? 'Device'),
|
|
// 'productType': action.productType,
|
|
// 'imagePath': matchingDevice.getDefaultIcon(action.productType),
|
|
// };
|
|
|
|
// final functions = matchingDevice.functions;
|
|
|
|
// if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
|
// final functionCode = action.executorProperty!.functionCode;
|
|
// for (var function in functions) {
|
|
// if (function.code == functionCode) {
|
|
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
|
// DeviceFunctionData(
|
|
// entityId: action.entityId,
|
|
// functionCode: functionCode ?? '',
|
|
// value: action.executorProperty!.functionValue,
|
|
// operationName: function.operationName,
|
|
// ),
|
|
// ];
|
|
// break;
|
|
// }
|
|
// }
|
|
// } else if (action.actionExecutor == 'delay') {
|
|
// final delayFunction = DelayFunction(
|
|
// deviceId: action.entityId,
|
|
// deviceName: 'Delay',
|
|
// );
|
|
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
|
// DeviceFunctionData(
|
|
// entityId: action.entityId,
|
|
// functionCode: 'delay',
|
|
// value: action.executorProperty?.delaySeconds ?? 0,
|
|
// operationName: delayFunction.operationName,
|
|
// ),
|
|
// ];
|
|
// }
|
|
|
|
// return cardData;
|
|
// }).toList();
|
|
|
|
// return (thenItems, ifItems, currentFunctions);
|
|
// }
|
|
|
|
Future<void> _onGetSceneDetails(
|
|
GetSceneDetails event, Emitter<RoutineState> emit) async {
|
|
try {
|
|
emit(state.copyWith(
|
|
isLoading: true,
|
|
isTabToRun: true,
|
|
isUpdate: true,
|
|
sceneId: event.sceneId,
|
|
routineName: null,
|
|
isAutomation: false,
|
|
selectedFunctions: {},
|
|
ifItems: [],
|
|
thenItems: [],
|
|
errorMessage: '',
|
|
loadScenesErrorMessage: null,
|
|
loadAutomationErrorMessage: null,
|
|
searchText: '',
|
|
selectedIcon: null,
|
|
selectedAutomationOperator: 'or',
|
|
effectiveTime: null,
|
|
));
|
|
|
|
final sceneDetails = await SceneApi.getSceneDetails(event.sceneId);
|
|
|
|
final List<Map<String, dynamic>> thenItems;
|
|
final List<Map<String, dynamic>> ifItems;
|
|
final Map<String, List<DeviceFunctionData>> updatedFunctions =
|
|
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
|
|
|
final Map<String, Map<String, dynamic>> deviceCards = {};
|
|
|
|
for (var action in sceneDetails.actions) {
|
|
AllDevicesModel? matchingDevice;
|
|
for (var device in state.devices) {
|
|
if (device.uuid == action.entityId) {
|
|
matchingDevice = device;
|
|
break;
|
|
}
|
|
}
|
|
|
|
final deviceId = action.type == 'automation'
|
|
? '${action.entityId}_automation'
|
|
: action.actionExecutor == 'delay'
|
|
? '${action.entityId}_delay'
|
|
: const Uuid().v4();
|
|
|
|
// if (!deviceCards.containsKey(deviceId)) {
|
|
deviceCards[deviceId] = {
|
|
'entityId': action.entityId,
|
|
'deviceId':
|
|
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
|
'uniqueCustomId':
|
|
action.type == 'automation' || action.actionExecutor == 'delay'
|
|
? action.entityId
|
|
: const Uuid().v4(),
|
|
'title': action.actionExecutor == 'delay'
|
|
? 'Delay'
|
|
: action.type == 'automation'
|
|
? action.name ?? 'Automation'
|
|
: (matchingDevice?.name ?? 'Device'),
|
|
'productType': action.productType,
|
|
'functions': matchingDevice?.functions,
|
|
'imagePath': action.type == 'automation'
|
|
? Assets.automation
|
|
: action.actionExecutor == 'delay'
|
|
? Assets.delay
|
|
: matchingDevice?.getDefaultIcon(action.productType),
|
|
'device': matchingDevice,
|
|
'name': action.name,
|
|
'type': action.type,
|
|
'tag': matchingDevice?.deviceTags?.isNotEmpty ?? false
|
|
? matchingDevice?.deviceTags![0].name ?? ''
|
|
: '',
|
|
'subSpace': matchingDevice?.deviceSubSpace?.subspaceName ?? '',
|
|
};
|
|
// }
|
|
|
|
final cardData = deviceCards[deviceId]!;
|
|
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
|
|
|
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
|
updatedFunctions[uniqueCustomId] = [];
|
|
}
|
|
|
|
if (action.type == 'automation') {
|
|
updatedFunctions[uniqueCustomId]!.add(
|
|
DeviceFunctionData(
|
|
entityId: action.entityId,
|
|
functionCode: 'automation',
|
|
value: action.actionExecutor,
|
|
operationName: 'Automation',
|
|
),
|
|
);
|
|
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
|
|
} else if (action.executorProperty != null &&
|
|
action.actionExecutor != 'delay') {
|
|
final functions = matchingDevice?.functions ?? [];
|
|
final functionCode = action.executorProperty?.functionCode;
|
|
for (DeviceFunction function in functions) {
|
|
if (function.code == functionCode) {
|
|
updatedFunctions[uniqueCustomId]!.add(
|
|
DeviceFunctionData(
|
|
entityId: action.entityId,
|
|
functionCode: functionCode ?? '',
|
|
value: action.executorProperty?.functionValue,
|
|
operationName: function.operationName,
|
|
),
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
} else if (action.actionExecutor == 'delay') {
|
|
final delayFunction = DelayFunction(
|
|
deviceId: action.entityId,
|
|
deviceName: 'Delay',
|
|
);
|
|
updatedFunctions[uniqueCustomId]!.add(
|
|
DeviceFunctionData(
|
|
entityId: action.entityId,
|
|
functionCode: 'delay',
|
|
value: action.executorProperty?.delaySeconds ?? 0,
|
|
operationName: delayFunction.operationName,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
thenItems = deviceCards.values.toList();
|
|
|
|
ifItems = [
|
|
{
|
|
'entityId': 'tab_to_run',
|
|
'uniqueCustomId': const Uuid().v4(),
|
|
'deviceId': 'tab_to_run',
|
|
'title': 'Tap to run',
|
|
'productType': 'tab_to_run',
|
|
'imagePath': Assets.tabToRun,
|
|
}
|
|
];
|
|
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
routineName: sceneDetails.name,
|
|
selectedIcon: sceneDetails.iconId,
|
|
selectedAutomationOperator: sceneDetails.decisionExpr,
|
|
effectiveTime: sceneDetails.effectiveTime,
|
|
isAutomation: false,
|
|
isTabToRun: event.isTabToRun,
|
|
thenItems: thenItems,
|
|
ifItems: ifItems,
|
|
selectedFunctions: updatedFunctions,
|
|
sceneId: sceneDetails.sceneId,
|
|
));
|
|
} catch (e) {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: 'Failed to load scene details: $e',
|
|
));
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _onResetRoutineState(
|
|
ResetRoutineState event, Emitter<RoutineState> emit) {
|
|
emit(state.copyWith(
|
|
ifItems: [],
|
|
thenItems: [],
|
|
selectedFunctions: {},
|
|
scenes: [],
|
|
sceneId: '',
|
|
automationId: '',
|
|
automations: [],
|
|
isLoading: false,
|
|
errorMessage: '',
|
|
loadScenesErrorMessage: null,
|
|
loadAutomationErrorMessage: null,
|
|
searchText: '',
|
|
selectedIcon: '',
|
|
isTabToRun: false,
|
|
isAutomation: false,
|
|
selectedAutomationOperator: 'or',
|
|
effectiveTime: null,
|
|
routineName: '',
|
|
isUpdate: false,
|
|
createRoutineView: false));
|
|
}
|
|
|
|
FutureOr<void> _deleteScene(
|
|
DeleteScene event, Emitter<RoutineState> emit) async {
|
|
try {
|
|
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
|
|
|
emit(state.copyWith(isLoading: true));
|
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
|
if (state.isTabToRun) {
|
|
await SceneApi.deleteScene(
|
|
unitUuid: spaceBloc.state.selectedSpaces[0],
|
|
sceneId: state.sceneId ?? '');
|
|
} else {
|
|
await SceneApi.deleteAutomation(
|
|
unitUuid: spaceBloc.state.selectedSpaces[0],
|
|
automationId: state.automationId ?? '',
|
|
projectId: projectId);
|
|
}
|
|
// var createRoutineBloc = context.read<CreateRoutineBloc>();
|
|
// if (state.isTabToRun) {
|
|
// await SceneApi.deleteScene(
|
|
// unitUuid: createRoutineBloc.selectedSpaceId,
|
|
// sceneId: state.sceneId ?? '');
|
|
// } else {
|
|
// await SceneApi.deleteAutomation(
|
|
// projectId: projectId,
|
|
// unitUuid: createRoutineBloc.selectedSpaceId,
|
|
// automationId: state.automationId ?? '');
|
|
// }
|
|
|
|
add(const LoadScenes());
|
|
add(const LoadAutomation());
|
|
add(ResetRoutineState());
|
|
emit(state.copyWith(isLoading: false, createRoutineView: false));
|
|
} on APIException catch (e) {
|
|
final errorData = e.message;
|
|
String errorMessage = errorData;
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: errorMessage,
|
|
));
|
|
CustomSnackBar.redSnackBar(errorMessage);
|
|
}
|
|
}
|
|
|
|
// FutureOr<void> _deleteAutomation(DeleteAutomation event, Emitter<RoutineState> emit) {
|
|
// try {
|
|
// emit(state.copyWith(isLoading: true));
|
|
// add(const LoadAutomation(spaceId));
|
|
// add(const LoadScenes(spaceId, communityId));
|
|
// emit(_resetState());
|
|
// } catch (e) {
|
|
// emit(state.copyWith(
|
|
// isLoading: false,
|
|
// errorMessage: 'Failed to delete automation',
|
|
// ));
|
|
// }
|
|
// }
|
|
|
|
FutureOr<void> _fetchDevices(
|
|
FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
|
|
emit(state.copyWith(isLoading: true));
|
|
try {
|
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
|
List<AllDevicesModel> devices = [];
|
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
|
|
|
if (createRoutineBloc.selectedSpaceId == '' &&
|
|
createRoutineBloc.selectedCommunityId == '') {
|
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
|
List<String> spacesList =
|
|
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
|
for (var spaceId in spacesList) {
|
|
devices.addAll(await DevicesManagementApi()
|
|
.fetchDevices(communityId, spaceId, projectUuid));
|
|
}
|
|
}
|
|
} else {
|
|
devices.addAll(await DevicesManagementApi().fetchDevices(
|
|
createRoutineBloc.selectedCommunityId,
|
|
createRoutineBloc.selectedSpaceId,
|
|
projectUuid));
|
|
}
|
|
|
|
emit(state.copyWith(isLoading: false, devices: devices));
|
|
} catch (e) {
|
|
emit(state.copyWith(isLoading: false));
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _onUpdateScene(
|
|
UpdateScene event, Emitter<RoutineState> emit) async {
|
|
try {
|
|
// Check if first action is delay
|
|
// if (_isFirstActionDelay(state.thenItems)) {
|
|
// emit(state.copyWith(
|
|
// errorMessage: 'Cannot have delay as the first action',
|
|
// isLoading: false,
|
|
// ));
|
|
|
|
// return;
|
|
// }
|
|
|
|
if (_isLastActionDelay(state.thenItems)) {
|
|
emit(state.copyWith(
|
|
errorMessage:
|
|
'A delay condition cannot be the only or the last action',
|
|
isLoading: false,
|
|
));
|
|
return;
|
|
}
|
|
|
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
|
|
|
final actions = state.thenItems.expand((item) {
|
|
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
|
return functions.map((function) {
|
|
if (function.functionCode == 'automation') {
|
|
return CreateSceneAction(
|
|
actionType: 'automation',
|
|
entityId: function.entityId,
|
|
actionExecutor: function.value,
|
|
executorProperty: null,
|
|
);
|
|
}
|
|
|
|
if (item['deviceId'] == 'delay') {
|
|
return CreateSceneAction(
|
|
entityId: function.entityId,
|
|
actionExecutor: 'delay',
|
|
executorProperty: CreateSceneExecutorProperty(
|
|
functionCode: '',
|
|
functionValue: '',
|
|
delaySeconds: int.tryParse(function.value.toString()) ?? 0,
|
|
),
|
|
);
|
|
}
|
|
|
|
return CreateSceneAction(
|
|
entityId: function.entityId,
|
|
actionExecutor: 'device_issue',
|
|
executorProperty: CreateSceneExecutorProperty(
|
|
functionCode: function.functionCode,
|
|
functionValue: function.value,
|
|
delaySeconds: 0,
|
|
),
|
|
);
|
|
});
|
|
}).toList();
|
|
|
|
final createSceneModel = CreateSceneModel(
|
|
spaceUuid: state.sceneId ?? '',
|
|
iconId: state.selectedIcon ?? '',
|
|
showInDevice: true,
|
|
sceneName: state.routineName ?? '',
|
|
decisionExpr: 'and',
|
|
actions: actions,
|
|
);
|
|
|
|
final result =
|
|
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
|
if (result['success']) {
|
|
add(ResetRoutineState());
|
|
add(const LoadScenes());
|
|
add(const LoadAutomation());
|
|
} else {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: result['message'],
|
|
));
|
|
}
|
|
} catch (e) {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: 'Something went wrong',
|
|
));
|
|
}
|
|
}
|
|
|
|
FutureOr<void> _onUpdateAutomation(
|
|
UpdateAutomation event, Emitter<RoutineState> emit) async {
|
|
try {
|
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
|
emit(state.copyWith(
|
|
errorMessage: 'Automation name is required',
|
|
));
|
|
return;
|
|
}
|
|
// if (_isFirstActionDelay(state.thenItems)) {
|
|
// emit(state.copyWith(
|
|
// errorMessage: 'Cannot have delay as the first action',
|
|
// isLoading: false,
|
|
// ));
|
|
// return;
|
|
// }
|
|
|
|
if (_isLastActionDelay(state.thenItems)) {
|
|
emit(state.copyWith(
|
|
errorMessage: 'Cannot have delay as the last action',
|
|
isLoading: false,
|
|
));
|
|
return;
|
|
}
|
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
|
int i = 0;
|
|
final conditions = state.ifItems.expand((item) {
|
|
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
|
return functions.map((function) {
|
|
return Condition(
|
|
code: i++,
|
|
entityId: function.entityId,
|
|
entityType: 'device_report',
|
|
expr: ConditionExpr(
|
|
statusCode: function.functionCode,
|
|
comparator: function.condition ?? '==',
|
|
statusValue: function.value,
|
|
),
|
|
);
|
|
});
|
|
}).toList();
|
|
|
|
if (conditions.isEmpty) {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: 'At least one condition is required',
|
|
));
|
|
return;
|
|
}
|
|
|
|
final actions = state.thenItems.expand((item) {
|
|
if (item['type'] == 'tap_to_run' || item['type'] == 'scene') {
|
|
return [
|
|
AutomationAction(
|
|
actionType: 'scene',
|
|
entityId: item['deviceId'],
|
|
actionExecutor: 'rule_trigger',
|
|
),
|
|
];
|
|
}
|
|
|
|
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
|
|
|
return functions.map((function) {
|
|
if (function.functionCode == 'automation') {
|
|
return AutomationAction(
|
|
actionType: 'automation',
|
|
entityId: function.entityId,
|
|
actionExecutor: function.value,
|
|
executorProperty: null,
|
|
);
|
|
}
|
|
|
|
if (item['deviceId'] == 'delay') {
|
|
return AutomationAction(
|
|
entityId: function.entityId,
|
|
actionType: 'automation',
|
|
actionExecutor: 'delay',
|
|
executorProperty: ExecutorProperty(
|
|
delaySeconds: int.tryParse(function.value.toString()) ?? 0,
|
|
),
|
|
);
|
|
}
|
|
|
|
return AutomationAction(
|
|
entityId: function.entityId,
|
|
actionExecutor: 'device_issue',
|
|
actionType: 'automation',
|
|
executorProperty: ExecutorProperty(
|
|
functionCode: function.functionCode,
|
|
functionValue: function.value,
|
|
),
|
|
);
|
|
});
|
|
}).toList();
|
|
|
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
|
var spaceBloc = context.read<CreateRoutineBloc>();
|
|
|
|
final createAutomationModel = CreateAutomationModel(
|
|
spaceUuid: spaceBloc.selectedSpaceId,
|
|
automationName: state.routineName ?? '',
|
|
decisionExpr: state.selectedAutomationOperator,
|
|
effectiveTime: EffectiveTime(
|
|
start: state.effectiveTime?.start ?? '00:00',
|
|
end: state.effectiveTime?.end ?? '23:59',
|
|
loops: state.effectiveTime?.loops ?? '1111111',
|
|
),
|
|
conditions: conditions,
|
|
actions: actions,
|
|
);
|
|
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
|
final result = await SceneApi.updateAutomation(
|
|
createAutomationModel, state.automationId ?? '', projectId);
|
|
|
|
if (result['success']) {
|
|
add(ResetRoutineState());
|
|
add(const LoadAutomation());
|
|
add(const LoadScenes());
|
|
} else {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: result['message'],
|
|
));
|
|
}
|
|
} on APIException catch (e) {
|
|
final errorData = e.message;
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: errorData,
|
|
));
|
|
}
|
|
}
|
|
|
|
Future<void> _onGetAutomationDetails(
|
|
GetAutomationDetails event, Emitter<RoutineState> emit) async {
|
|
try {
|
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
|
emit(state.copyWith(
|
|
isLoading: true,
|
|
isUpdate: true,
|
|
isTabToRun: false,
|
|
automationId: event.automationId,
|
|
isAutomation: true,
|
|
ifItems: [],
|
|
thenItems: [],
|
|
));
|
|
|
|
final automationDetails =
|
|
await SceneApi.getAutomationDetails(event.automationId, projectUuid);
|
|
|
|
final Map<String, Map<String, dynamic>> deviceIfCards = {};
|
|
final Map<String, Map<String, dynamic>> deviceThenCards = {};
|
|
|
|
final Map<String, List<DeviceFunctionData>> updatedFunctions =
|
|
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
|
|
|
for (RoutineCondition condition in automationDetails.conditions ?? []) {
|
|
AllDevicesModel? matchingDevice = state.devices.firstWhere(
|
|
(device) => device.uuid == condition.entityId,
|
|
orElse: () => AllDevicesModel(
|
|
uuid: condition.entityId,
|
|
name: condition.entityId,
|
|
productType: condition.productType,
|
|
),
|
|
);
|
|
|
|
final deviceId = const Uuid().v4();
|
|
|
|
// if (!deviceIfCards.containsKey(deviceId)) {
|
|
deviceIfCards[deviceId] = {
|
|
'entityId': condition.entityId,
|
|
'deviceId': condition.entityId,
|
|
'uniqueCustomId': const Uuid().v4(),
|
|
'title': matchingDevice.name ?? 'Device',
|
|
'productType': condition.productType,
|
|
'functions': matchingDevice.functions,
|
|
'imagePath': matchingDevice.getDefaultIcon(condition.productType),
|
|
'device': matchingDevice,
|
|
'type': 'condition',
|
|
'tag': matchingDevice.deviceTags?.isNotEmpty ?? false
|
|
? matchingDevice.deviceTags![0].name
|
|
: '',
|
|
'subSpace': matchingDevice.deviceSubSpace?.subspaceName ?? '',
|
|
};
|
|
// }
|
|
|
|
final cardData = deviceIfCards[deviceId]!;
|
|
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
|
|
|
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
|
updatedFunctions[uniqueCustomId] = [];
|
|
}
|
|
|
|
final functions = matchingDevice.functions;
|
|
for (var function in functions) {
|
|
if (function.code == condition.expr.statusCode) {
|
|
updatedFunctions[uniqueCustomId]!.add(
|
|
DeviceFunctionData(
|
|
entityId: condition.entityId,
|
|
functionCode: condition.expr.statusCode,
|
|
value: condition.expr.statusValue,
|
|
operationName: function.operationName,
|
|
),
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process actions (thenItems)
|
|
for (var action in automationDetails.actions) {
|
|
AllDevicesModel? matchingDevice = state.devices.firstWhere(
|
|
(device) => device.uuid == action.entityId,
|
|
orElse: () => AllDevicesModel(
|
|
uuid: action.entityId,
|
|
name: action.entityId,
|
|
productType: action.productType,
|
|
),
|
|
);
|
|
|
|
final deviceId = const Uuid().v4();
|
|
|
|
// if (!deviceThenCards.containsKey(deviceId)) {
|
|
deviceThenCards[deviceId] = {
|
|
'entityId': action.entityId,
|
|
'deviceId':
|
|
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
|
'uniqueCustomId': const Uuid().v4(),
|
|
'title': action.actionExecutor == 'delay'
|
|
? 'Delay'
|
|
: (action.type == 'scene' || action.type == 'automation')
|
|
? action.name
|
|
: (matchingDevice.name ?? 'Device'),
|
|
'productType': action.productType,
|
|
'functions': matchingDevice.functions,
|
|
'imagePath': action.actionExecutor == 'delay'
|
|
? Assets.delay
|
|
: action.type == 'automation'
|
|
? Assets.automation
|
|
: matchingDevice.getDefaultIcon(action.productType),
|
|
'device': matchingDevice,
|
|
'type': action.type == 'scene'
|
|
? 'scene'
|
|
: action.type == 'automation'
|
|
? 'automation'
|
|
: 'action',
|
|
'icon': action.icon ?? '',
|
|
'tag': matchingDevice.deviceTags?.isNotEmpty ?? false
|
|
? matchingDevice.deviceTags![0].name
|
|
: '',
|
|
'subSpace': matchingDevice.deviceSubSpace?.subspaceName ?? '',
|
|
};
|
|
// }
|
|
|
|
final cardData = deviceThenCards[deviceId]!;
|
|
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
|
|
|
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
|
updatedFunctions[uniqueCustomId] = [];
|
|
}
|
|
|
|
if (action.executorProperty != null &&
|
|
action.actionExecutor != 'delay') {
|
|
final functions = matchingDevice.functions;
|
|
final functionCode = action.executorProperty!.functionCode;
|
|
for (var function in functions) {
|
|
if (function.code == functionCode) {
|
|
updatedFunctions[uniqueCustomId]!.add(
|
|
DeviceFunctionData(
|
|
entityId: action.entityId,
|
|
functionCode: functionCode ?? '',
|
|
value: action.executorProperty!.functionValue,
|
|
operationName: function.operationName,
|
|
),
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
} else if (action.actionExecutor == 'delay') {
|
|
final delayFunction = DelayFunction(
|
|
deviceId: action.entityId,
|
|
deviceName: 'Delay',
|
|
);
|
|
updatedFunctions[uniqueCustomId]!.add(
|
|
DeviceFunctionData(
|
|
entityId: action.entityId,
|
|
functionCode: 'delay',
|
|
value: action.executorProperty?.delaySeconds ?? 0,
|
|
operationName: delayFunction.operationName,
|
|
),
|
|
);
|
|
} else if (action.actionExecutor == 'rule_disable' ||
|
|
action.actionExecutor == 'rule_enable') {
|
|
updatedFunctions[uniqueCustomId]!.add(
|
|
DeviceFunctionData(
|
|
entityId: action.entityId,
|
|
functionCode: 'automation',
|
|
value: action.actionExecutor,
|
|
operationName: action.name ?? 'Automation',
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
final ifItems = deviceIfCards.values
|
|
.where((card) => card['type'] == 'condition')
|
|
.toList();
|
|
final thenItems = deviceThenCards.values
|
|
.where((card) =>
|
|
card['type'] == 'action' ||
|
|
card['type'] == 'automation' ||
|
|
card['type'] == 'scene')
|
|
.toList();
|
|
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
routineName: automationDetails.name,
|
|
selectedAutomationOperator: automationDetails.decisionExpr,
|
|
effectiveTime: automationDetails.effectiveTime,
|
|
isAutomation: true,
|
|
thenItems: thenItems,
|
|
ifItems: ifItems,
|
|
selectedFunctions: updatedFunctions,
|
|
automationId: automationDetails.automationId,
|
|
));
|
|
} catch (e) {
|
|
emit(state.copyWith(
|
|
isLoading: false,
|
|
errorMessage: 'Failed to load automation details: $e',
|
|
));
|
|
}
|
|
}
|
|
|
|
Future<void> _onSceneTrigger(
|
|
SceneTrigger event, Emitter<RoutineState> emit) async {
|
|
emit(state.copyWith(loadingSceneId: event.sceneId));
|
|
|
|
try {
|
|
final success = await SceneApi.triggerScene(event.sceneId!);
|
|
|
|
if (success) {
|
|
emit(state.copyWith(
|
|
loadingSceneId: null,
|
|
// Add success state if needed
|
|
));
|
|
// Optional: Add delay to show success feedback
|
|
await Future.delayed(const Duration(milliseconds: 500));
|
|
} else {
|
|
emit(state.copyWith(
|
|
loadingSceneId: null,
|
|
errorMessage: 'Trigger failed',
|
|
));
|
|
}
|
|
} catch (e) {
|
|
emit(state.copyWith(
|
|
loadingSceneId: null,
|
|
errorMessage: 'Trigger error: ${e.toString()}',
|
|
));
|
|
}
|
|
}
|
|
|
|
Future<void> _onUpdateAutomationStatus(
|
|
UpdateAutomationStatus event, Emitter<RoutineState> emit) async {
|
|
// Create a new set safely
|
|
final currentLoadingIds = state.loadingAutomationIds;
|
|
final newLoadingIds = {...currentLoadingIds!}..add(event.automationId);
|
|
|
|
emit(state.copyWith(loadingAutomationIds: newLoadingIds));
|
|
|
|
try {
|
|
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
|
final success = await SceneApi.updateAutomationStatus(
|
|
event.automationId, event.automationStatusUpdate, projectId);
|
|
|
|
if (success) {
|
|
final updatedAutomations = await SceneApi.getAutomationByUnitId(
|
|
event.automationStatusUpdate.spaceUuid,
|
|
event.communityId,
|
|
projectId);
|
|
|
|
// Remove from loading set safely
|
|
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
|
..remove(event.automationId);
|
|
|
|
emit(state.copyWith(
|
|
automations: updatedAutomations,
|
|
loadingAutomationIds: updatedLoadingIds,
|
|
));
|
|
} else {
|
|
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
|
..remove(event.automationId);
|
|
emit(state.copyWith(
|
|
loadingAutomationIds: updatedLoadingIds,
|
|
errorMessage: 'Update failed',
|
|
));
|
|
}
|
|
} catch (e) {
|
|
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
|
..remove(event.automationId);
|
|
emit(state.copyWith(
|
|
loadingAutomationIds: updatedLoadingIds,
|
|
errorMessage: 'Update error: ${e.toString()}',
|
|
));
|
|
}
|
|
}
|
|
}
|