import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:flutter_bloc/flutter_bloc.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/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'; // String spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6'; // String communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9'; class RoutineBloc extends Bloc { RoutineBloc() : super(const RoutineState()) { on(_onAddToIfContainer); on(_onAddToThenContainer); on(_onLoadScenes); on(_onLoadAutomation); on(_onAddFunctionsToRoutine); on(_onSearchRoutines); on(_onAddSelectedIcon); on(_onCreateScene); on(_onRemoveDragCard); on(_changeOperatorOperator); on(_onEffectiveTimeEvent); on(_onCreateAutomation); on(_onSetRoutineName); on(_onResetRoutineState); on(_onGetSceneDetails); on(_onGetAutomationDetails); on(_deleteScene); // on(_deleteAutomation); on(_fetchDevices); on(_onUpdateScene); on(_onUpdateAutomation); on(_triggerSwitchTabsEvent); on(_createNewRoutineViewEvent); on(_resetErrorMessage); on(_onSceneTrigger); on(_onUpdateAutomationStatus); } String selectedSpaceId = ''; String selectedCommunityId = ''; FutureOr _triggerSwitchTabsEvent( TriggerSwitchTabsEvent event, Emitter emit, ) { emit(state.copyWith(routineTab: event.isRoutineTab, createRoutineView: false)); add(ResetRoutineState()); if (event.isRoutineTab) { add(const LoadScenes()); add(const LoadAutomation()); } } _resetErrorMessage( ResetErrorMessage event, Emitter emit, ) { emit(state.copyWith(errorMessage: '')); } FutureOr _createNewRoutineViewEvent( CreateNewRoutineViewEvent event, Emitter emit, ) { emit(state.copyWith(createRoutineView: event.createRoutineView)); } void _onAddToIfContainer(AddToIfContainer event, Emitter emit) { final updatedIfItems = List>.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 emit) { final currentItems = List>.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 emit) { try { if (event.functions.isEmpty) return; // List selectedFunction = List.from(event.functions); Map> currentSelectedFunctions = Map>.from(state.selectedFunctions); // if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) { // List currentFunctions = // List.from(currentSelectedFunctions[event.uniqueCustomId] ?? []); // List 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 _onLoadScenes(LoadScenes event, Emitter emit) async { emit(state.copyWith(isLoading: true, errorMessage: null)); List scenes = []; try { BuildContext context = NavigationService.navigatorKey.currentContext!; var createRoutineBloc = context.read(); final projectUuid = await ProjectManager.getProjectUUID() ?? ''; if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') { var spaceBloc = context.read(); for (var communityId in spaceBloc.state.selectedCommunities) { List 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 _onLoadAutomation(LoadAutomation event, Emitter emit) async { emit(state.copyWith(isLoading: true, errorMessage: null)); List automations = []; final projectId = await ProjectManager.getProjectUUID() ?? ''; BuildContext context = NavigationService.navigatorKey.currentContext!; var createRoutineBloc = context.read(); try { if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') { var spaceBloc = context.read(); for (var communityId in spaceBloc.state.selectedCommunities) { List 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 _onSearchRoutines(SearchRoutines event, Emitter 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 _onAddSelectedIcon(AddSelectedIcon event, Emitter emit) { emit(state.copyWith(selectedIcon: event.icon)); } // bool _isFirstActionDelay(List> actions) { // if (actions.isEmpty) return false; // return actions.first['deviceId'] == 'delay'; // } bool _isLastActionDelay(List> actions) { if (actions.isEmpty) return false; return actions.last['deviceId'] == 'delay'; } Future _onCreateScene(CreateSceneEvent event, Emitter 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(); 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', )); } } catch (e) { emit(state.copyWith( isLoading: false, errorMessage: 'Something went wrong', )); } } Future _onCreateAutomation(CreateAutomationEvent event, Emitter 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(); 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'); } } catch (e) { emit(state.copyWith( isLoading: false, errorMessage: 'Something went wrong', )); CustomSnackBar.redSnackBar('Something went wrong'); } } FutureOr _onRemoveDragCard(RemoveDragCard event, Emitter emit) { if (event.isFromThen) { final thenItems = List>.from(state.thenItems); final selectedFunctions = Map>.from(state.selectedFunctions); thenItems.removeAt(event.index); selectedFunctions.remove(event.key); emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions)); } else { final ifItems = List>.from(state.ifItems); final selectedFunctions = Map>.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 _changeOperatorOperator( ChangeAutomationOperator event, Emitter emit) { emit(state.copyWith( selectedAutomationOperator: event.operator, )); } FutureOr _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter emit) { emit(state.copyWith(effectiveTime: event.effectiveTime)); } FutureOr _onSetRoutineName(SetRoutineName event, Emitter emit) { emit(state.copyWith( routineName: event.name, )); } // ( // List>, // List>, // Map> // ) _createCardData( // List actions, // List? conditions, // Map> currentFunctions, // bool isTabToRun, // ) { // final ifItems = isTabToRun // ? [ // { // 'entityId': 'tab_to_run', // 'uniqueCustomId': const Uuid().v4(), // 'deviceId': 'tab_to_run', // 'title': 'Tab 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 _onGetSceneDetails(GetSceneDetails event, Emitter 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> thenItems; final List> ifItems; final Map> updatedFunctions = Map>.from(state.selectedFunctions); final Map> 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' : action.entityId; if (!deviceCards.containsKey(deviceId)) { deviceCards[deviceId] = { 'entityId': action.entityId, 'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId, 'uniqueCustomId': action.type == 'automation' || action.actionExecutor == 'delay' ? const Uuid().v4() : action.entityId, '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, }; } final cardData = deviceCards[deviceId]!; final uniqueCustomId = cardData['uniqueCustomId'].toString(); if (action.type == 'automation') { if (!updatedFunctions.containsKey(uniqueCustomId)) { updatedFunctions[uniqueCustomId] = []; } 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') { // if (!updatedFunctions.containsKey(uniqueCustomId)) { // updatedFunctions[uniqueCustomId] = []; // } final functions = matchingDevice?.functions; final functionCode = action.executorProperty?.functionCode; for (DeviceFunction function in functions ?? []) { if (function.code == functionCode) { updatedFunctions[const Uuid().v4()]!.add( DeviceFunctionData( entityId: action.entityId, functionCode: functionCode ?? '', value: action.executorProperty?.functionValue, operationName: function.operationName, ), ); break; } } } else if (action.actionExecutor == 'delay') { if (!updatedFunctions.containsKey(uniqueCustomId)) { updatedFunctions[uniqueCustomId] = []; } 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': 'Tab 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 _onResetRoutineState(ResetRoutineState event, Emitter 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 _deleteScene(DeleteScene event, Emitter emit) async { try { final projectId = await ProjectManager.getProjectUUID() ?? ''; emit(state.copyWith(isLoading: true)); BuildContext context = NavigationService.navigatorKey.currentContext!; var spaceBloc = context.read(); 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(); // 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)); } catch (e) { emit(state.copyWith( isLoading: false, errorMessage: 'Failed to delete scene', )); } } // FutureOr _deleteAutomation(DeleteAutomation event, Emitter 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 _fetchDevices(FetchDevicesInRoutine event, Emitter emit) async { emit(state.copyWith(isLoading: true)); try { final projectUuid = await ProjectManager.getProjectUUID() ?? ''; List devices = []; BuildContext context = NavigationService.navigatorKey.currentContext!; var createRoutineBloc = context.read(); var spaceBloc = context.read(); if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') { for (var communityId in spaceBloc.state.selectedCommunities) { List 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 _onUpdateScene(UpdateScene event, Emitter 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 _onUpdateAutomation(UpdateAutomation event, Emitter 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(); 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(LoadAutomation()); add(LoadScenes()); } else { emit(state.copyWith( isLoading: false, errorMessage: result['message'], )); } } catch (e) { emit(state.copyWith( isLoading: false, errorMessage: 'Something went wrong', )); } } Future _onGetAutomationDetails( GetAutomationDetails event, Emitter 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> deviceIfCards = {}; final Map> deviceThenCards = {}; final Map> updatedFunctions = Map>.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 = condition.entityId; 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', }; } 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 = action.actionExecutor == 'delay' ? '${action.entityId}_delay' : action.entityId; 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 ?? '', }; } 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 _onSceneTrigger(SceneTrigger event, Emitter 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 _onUpdateAutomationStatus( UpdateAutomationStatus event, Emitter 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()}', )); } } }