diff --git a/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart b/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart index 0b61e4e7..9cb3adb5 100644 --- a/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart +++ b/lib/pages/routiens/bloc/routine_bloc/routine_bloc.dart @@ -53,7 +53,8 @@ class RoutineBloc extends Bloc { TriggerSwitchTabsEvent event, Emitter emit, ) { - emit(state.copyWith(routineTab: event.isRoutineTab, createRoutineView: false)); + emit(state.copyWith( + routineTab: event.isRoutineTab, createRoutineView: false)); add(ResetRoutineState()); if (event.isRoutineTab) { add(const LoadScenes(spaceId, communityId)); @@ -72,8 +73,8 @@ class RoutineBloc extends Bloc { 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']); + 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; @@ -82,18 +83,21 @@ class RoutineBloc extends Bloc { } if (event.isTabToRun) { - emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false)); + emit(state.copyWith( + ifItems: updatedIfItems, isTabToRun: true, isAutomation: false)); } else { - emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true)); + emit(state.copyWith( + ifItems: updatedIfItems, isTabToRun: false, isAutomation: true)); } } - void _onAddToThenContainer(AddToThenContainer event, Emitter emit) { + 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']); + 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; @@ -104,22 +108,26 @@ class RoutineBloc extends Bloc { emit(state.copyWith(thenItems: currentItems)); } - void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter emit) { + void _onAddFunctionsToRoutine( + AddFunctionToRoutine event, Emitter emit) { try { if (event.functions.isEmpty) return; - List selectedFunction = List.from(event.functions); + 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.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) { + if (selectedFunction[i].functionCode == + currentFunctions[j].functionCode) { currentFunctions[j] = selectedFunction[i]; if (!functionCode.contains(currentFunctions[j].functionCode)) { functionCode.add(currentFunctions[j].functionCode); @@ -129,13 +137,15 @@ class RoutineBloc extends Bloc { } for (int i = 0; i < functionCode.length; i++) { - selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]); + selectedFunction + .removeWhere((code) => code.functionCode == functionCode[i]); } - currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions) - ..addAll(selectedFunction); + 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)); @@ -144,11 +154,13 @@ class RoutineBloc extends Bloc { } } - Future _onLoadScenes(LoadScenes event, Emitter emit) async { + Future _onLoadScenes( + LoadScenes event, Emitter emit) async { emit(state.copyWith(isLoading: true, errorMessage: null)); try { - final scenes = await SceneApi.getScenesByUnitId(event.unitId, event.communityId); + final scenes = + await SceneApi.getScenesByUnitId(event.unitId, event.communityId); emit(state.copyWith( scenes: scenes, isLoading: false, @@ -163,7 +175,8 @@ class RoutineBloc extends Bloc { } } - Future _onLoadAutomation(LoadAutomation event, Emitter emit) async { + Future _onLoadAutomation( + LoadAutomation event, Emitter emit) async { emit(state.copyWith(isLoading: true, errorMessage: null)); try { @@ -191,14 +204,16 @@ class RoutineBloc extends Bloc { } } - FutureOr _onSearchRoutines(SearchRoutines event, Emitter emit) async { + 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) { + FutureOr _onAddSelectedIcon( + AddSelectedIcon event, Emitter emit) { emit(state.copyWith(selectedIcon: event.icon)); } @@ -212,7 +227,8 @@ class RoutineBloc extends Bloc { return actions.last['deviceId'] == 'delay'; } - Future _onCreateScene(CreateSceneEvent event, Emitter emit) async { + Future _onCreateScene( + CreateSceneEvent event, Emitter emit) async { try { // Check if first action is delay if (_isFirstActionDelay(state.thenItems)) { @@ -238,6 +254,7 @@ class RoutineBloc extends Bloc { return functions.map((function) { if (function.functionCode == 'automation') { return CreateSceneAction( + actionType: 'automation', entityId: function.entityId, actionExecutor: function.value, executorProperty: null, @@ -257,6 +274,7 @@ class RoutineBloc extends Bloc { } return CreateSceneAction( + actionType: 'scene', entityId: function.entityId, actionExecutor: 'device_issue', executorProperty: CreateSceneExecutorProperty( @@ -296,7 +314,8 @@ class RoutineBloc extends Bloc { } } - Future _onCreateAutomation(CreateAutomationEvent event, Emitter emit) async { + Future _onCreateAutomation( + CreateAutomationEvent event, Emitter emit) async { try { if (state.routineName == null || state.routineName!.isEmpty) { emit(state.copyWith( @@ -353,6 +372,15 @@ class RoutineBloc extends Bloc { return functions.map((function) { if (function.functionCode == 'automation') { return AutomationAction( + actionType: 'automation', + entityId: function.entityId, + actionExecutor: function.value, + ); + } + + if (function.functionCode == 'tap_to_run') { + return AutomationAction( + actionType: 'scene', entityId: function.entityId, actionExecutor: function.value, ); @@ -411,17 +439,21 @@ class RoutineBloc extends Bloc { } } - FutureOr _onRemoveDragCard(RemoveDragCard event, Emitter emit) { + FutureOr _onRemoveDragCard( + RemoveDragCard event, Emitter emit) { if (event.isFromThen) { final thenItems = List>.from(state.thenItems); - final selectedFunctions = Map>.from(state.selectedFunctions); + final selectedFunctions = + Map>.from(state.selectedFunctions); thenItems.removeAt(event.index); selectedFunctions.remove(event.key); - emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions)); + emit(state.copyWith( + thenItems: thenItems, selectedFunctions: selectedFunctions)); } else { final ifItems = List>.from(state.ifItems); - final selectedFunctions = Map>.from(state.selectedFunctions); + final selectedFunctions = + Map>.from(state.selectedFunctions); ifItems.removeAt(event.index); selectedFunctions.remove(event.key); @@ -432,7 +464,8 @@ class RoutineBloc extends Bloc { isAutomation: false, isTabToRun: false)); } else { - emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions)); + emit(state.copyWith( + ifItems: ifItems, selectedFunctions: selectedFunctions)); } } } @@ -444,18 +477,23 @@ class RoutineBloc extends Bloc { )); } - FutureOr _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter emit) { + FutureOr _onEffectiveTimeEvent( + EffectiveTimePeriodEvent event, Emitter emit) { emit(state.copyWith(effectiveTime: event.effectiveTime)); } - FutureOr _onSetRoutineName(SetRoutineName event, Emitter emit) { + FutureOr _onSetRoutineName( + SetRoutineName event, Emitter emit) { emit(state.copyWith( routineName: event.name, )); } - (List>, List>, Map>) - _createCardData( + ( + List>, + List>, + Map> + ) _createCardData( List actions, List? conditions, Map> currentFunctions, @@ -488,7 +526,8 @@ class RoutineBloc extends Bloc { 'deviceId': condition.entityId, 'title': matchingDevice.name ?? condition.entityId, 'productType': condition.entityType, - 'imagePath': matchingDevice.getDefaultIcon(condition.entityType), + 'imagePath': + matchingDevice.getDefaultIcon(condition.entityType), }; final functions = matchingDevice.functions; @@ -524,8 +563,11 @@ class RoutineBloc extends Bloc { final cardData = { 'entityId': action.entityId, 'uniqueCustomId': const Uuid().v4(), - 'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId, - 'title': action.actionExecutor == 'delay' ? 'Delay' : (matchingDevice.name ?? 'Device'), + 'deviceId': + action.actionExecutor == 'delay' ? 'delay' : action.entityId, + 'title': action.actionExecutor == 'delay' + ? 'Delay' + : (matchingDevice.name ?? 'Device'), 'productType': action.productType, 'imagePath': matchingDevice.getDefaultIcon(action.productType), }; @@ -568,7 +610,8 @@ class RoutineBloc extends Bloc { return (thenItems, ifItems, currentFunctions); } - Future _onGetSceneDetails(GetSceneDetails event, Emitter emit) async { + Future _onGetSceneDetails( + GetSceneDetails event, Emitter emit) async { try { emit(state.copyWith( isLoading: true, @@ -616,10 +659,12 @@ class RoutineBloc extends Bloc { 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, + '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' @@ -654,7 +699,8 @@ class RoutineBloc extends Bloc { ), ); emit(state.copyWith(automationActionExecutor: action.actionExecutor)); - } else if (action.executorProperty != null && action.actionExecutor != 'delay') { + } else if (action.executorProperty != null && + action.actionExecutor != 'delay') { if (!updatedFunctions.containsKey(uniqueCustomId)) { updatedFunctions[uniqueCustomId] = []; } @@ -739,7 +785,8 @@ class RoutineBloc extends Bloc { thenItems: [], )); - final automationDetails = await SceneApi.getAutomationDetails(event.automationId); + final automationDetails = + await SceneApi.getAutomationDetails(event.automationId); final List> thenItems; final List> ifItems; @@ -758,13 +805,16 @@ class RoutineBloc extends Bloc { final cardData = { 'entityId': condition.entityId, 'uniqueCustomId': const Uuid().v4(), - 'deviceId': condition.expr.statusCode == 'delay' ? 'delay' : condition.entityId, + 'deviceId': condition.expr.statusCode == 'delay' + ? 'delay' + : condition.entityId, 'title': condition.expr.statusCode == 'delay' ? 'Delay' : (matchingDevice?.name ?? 'Device'), 'productType': condition.productType, 'functions': matchingDevice?.functions ?? [], - 'imagePath': matchingDevice?.getDefaultIcon(condition.productType) ?? '', + 'imagePath': + matchingDevice?.getDefaultIcon(condition.productType) ?? '', 'device': matchingDevice, }; @@ -801,15 +851,19 @@ class RoutineBloc extends Bloc { final cardData = { 'entityId': action.entityId, 'uniqueCustomId': const Uuid().v4(), - 'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId, - 'title': action.actionExecutor == 'delay' ? 'Delay' : (matchingDevice.name ?? 'Device'), + 'deviceId': + action.actionExecutor == 'delay' ? 'delay' : action.entityId, + 'title': action.actionExecutor == 'delay' + ? 'Delay' + : (matchingDevice.name ?? 'Device'), 'productType': action.productType, 'functions': matchingDevice.functions, 'imagePath': matchingDevice.getDefaultIcon(action.productType), 'device': matchingDevice, }; - if (action.executorProperty != null && action.actionExecutor != 'delay') { + if (action.executorProperty != null && + action.actionExecutor != 'delay') { final functions = matchingDevice.functions; final functionCode = action.executorProperty!.functionCode; for (var function in functions) { @@ -872,7 +926,8 @@ class RoutineBloc extends Bloc { } } - FutureOr _onResetRoutineState(ResetRoutineState event, Emitter emit) { + FutureOr _onResetRoutineState( + ResetRoutineState event, Emitter emit) { emit(state.copyWith( ifItems: [], thenItems: [], @@ -900,7 +955,8 @@ class RoutineBloc extends Bloc { if (state.isTabToRun) { SceneApi.deleteScene(unitUuid: spaceId, sceneId: state.sceneId ?? ''); } else { - SceneApi.deleteAutomation(unitUuid: spaceId, automationId: state.automationId ?? ''); + SceneApi.deleteAutomation( + unitUuid: spaceId, automationId: state.automationId ?? ''); } add(const LoadScenes(spaceId, communityId)); @@ -929,7 +985,8 @@ class RoutineBloc extends Bloc { // } // } - FutureOr _fetchDevices(FetchDevicesInRoutine event, Emitter emit) async { + FutureOr _fetchDevices( + FetchDevicesInRoutine event, Emitter emit) async { emit(state.copyWith(isLoading: true)); try { final devices = await DevicesManagementApi().fetchDevices(); @@ -940,7 +997,8 @@ class RoutineBloc extends Bloc { } } - FutureOr _onUpdateScene(UpdateScene event, Emitter emit) async { + FutureOr _onUpdateScene( + UpdateScene event, Emitter emit) async { try { // Check if first action is delay if (_isFirstActionDelay(state.thenItems)) { @@ -967,6 +1025,7 @@ class RoutineBloc extends Bloc { return functions.map((function) { if (function.functionCode == 'automation') { return CreateSceneAction( + actionType: 'automation', entityId: function.entityId, actionExecutor: function.value, executorProperty: null, @@ -1006,7 +1065,8 @@ class RoutineBloc extends Bloc { actions: actions, ); - final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? ''); + final result = + await SceneApi.updateScene(createSceneModel, state.sceneId ?? ''); if (result['success']) { add(ResetRoutineState()); add(const LoadScenes(spaceId, communityId)); @@ -1025,7 +1085,8 @@ class RoutineBloc extends Bloc { } } - FutureOr _onUpdateAutomation(UpdateAutomation event, Emitter emit) async { + FutureOr _onUpdateAutomation( + UpdateAutomation event, Emitter emit) async { if (_isFirstActionDelay(state.thenItems)) { emit(state.copyWith( errorMessage: 'Cannot have delay as the first action', @@ -1037,7 +1098,8 @@ class RoutineBloc extends Bloc { try { final conditions = state.ifItems .map((item) { - final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; + final functions = + state.selectedFunctions[item['uniqueCustomId']] ?? []; if (functions.isEmpty) return null; final function = functions.first; @@ -1057,18 +1119,21 @@ class RoutineBloc extends Bloc { final actions = state.thenItems .map((item) { - final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; + final functions = + state.selectedFunctions[item['uniqueCustomId']] ?? []; if (functions.isEmpty) return null; final function = functions.first; return AutomationAction( + actionType: 'automation', entityId: function.entityId, actionExecutor: function.actionExecutor, executorProperty: ExecutorProperty( functionCode: function.functionCode, functionValue: function.value, - delaySeconds: - function.functionCode == 'delay' ? (function.value as num).toInt() : null, + delaySeconds: function.functionCode == 'delay' + ? (function.value as num).toInt() + : null, ), ); }) @@ -1079,13 +1144,14 @@ class RoutineBloc extends Bloc { spaceUuid: spaceId, automationName: state.routineName ?? '', decisionExpr: state.selectedAutomationOperator, - effectiveTime: state.effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''), + effectiveTime: + state.effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''), conditions: conditions, actions: actions, ); - final result = - await SceneApi.updateAutomation(createAutomationModel, state.automationId ?? ''); + final result = await SceneApi.updateAutomation( + createAutomationModel, state.automationId ?? ''); if (result['success']) { add(ResetRoutineState()); @@ -1107,6 +1173,7 @@ class RoutineBloc extends Bloc { FutureOr _onSetAutomationActionExecutor( SetAutomationActionExecutor event, Emitter emit) { - emit(state.copyWith(automationActionExecutor: event.automationActionExecutor)); + emit(state.copyWith( + automationActionExecutor: event.automationActionExecutor)); } } diff --git a/lib/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart b/lib/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart index 295328e4..d76c34de 100644 --- a/lib/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart +++ b/lib/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart @@ -137,11 +137,13 @@ class ConditionExpr { class AutomationAction { String entityId; + String? actionType; String actionExecutor; ExecutorProperty? executorProperty; AutomationAction({ required this.entityId, + this.actionType, required this.actionExecutor, this.executorProperty, }); @@ -151,11 +153,13 @@ class AutomationAction { 'entityId': entityId, 'actionExecutor': actionExecutor, 'executorProperty': executorProperty?.toMap(), + 'actionType': actionType }; } factory AutomationAction.fromMap(Map map) { return AutomationAction( + actionType: map['actionType'], entityId: map['entityId'] ?? '', actionExecutor: map['actionExecutor'] ?? '', executorProperty: map['executorProperty'] != null diff --git a/lib/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart b/lib/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart index c669aa9a..81ee1096 100644 --- a/lib/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart +++ b/lib/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart @@ -95,10 +95,12 @@ class CreateSceneModel { class CreateSceneAction { String entityId; + String? actionType; String actionExecutor; CreateSceneExecutorProperty? executorProperty; CreateSceneAction({ + this.actionType, required this.entityId, required this.actionExecutor, required this.executorProperty, @@ -110,6 +112,7 @@ class CreateSceneAction { CreateSceneExecutorProperty? executorProperty, }) { return CreateSceneAction( + actionType: actionType ?? this.actionType, entityId: entityId ?? this.entityId, actionExecutor: actionExecutor ?? this.actionExecutor, executorProperty: executorProperty ?? this.executorProperty, @@ -125,6 +128,7 @@ class CreateSceneAction { }; } else { return { + "actionType": actionType, 'entityId': entityId, 'actionExecutor': actionExecutor, }; @@ -133,6 +137,7 @@ class CreateSceneAction { factory CreateSceneAction.fromMap(Map map) { return CreateSceneAction( + actionType: map['actionType'], entityId: map['entityId'] ?? '', actionExecutor: map['actionExecutor'] ?? '', executorProperty: diff --git a/lib/pages/routiens/widgets/routine_dialogs/automation_dialog.dart b/lib/pages/routiens/widgets/routine_dialogs/automation_dialog.dart index 16a181b6..2950c0cc 100644 --- a/lib/pages/routiens/widgets/routine_dialogs/automation_dialog.dart +++ b/lib/pages/routiens/widgets/routine_dialogs/automation_dialog.dart @@ -23,7 +23,7 @@ class AutomationDialog extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - final isEnabled = state.automationActionExecutor == 'rule_enable'; + final String? initialSelection = state.automationActionExecutor; return Dialog( shape: @@ -40,14 +40,14 @@ class AutomationDialog extends StatelessWidget { leading: SvgPicture.asset(Assets.acPower, width: 24, height: 24), title: const Text('Enable'), - trailing: Radio( - value: true, - groupValue: isEnabled, - onChanged: (bool? value) { - if (value == true) { + trailing: Radio( + value: 'rule_enable', + groupValue: initialSelection, + onChanged: (String? value) { + if (value != null) { context.read().add( - const SetAutomationActionExecutor( - automationActionExecutor: 'rule_enable', + SetAutomationActionExecutor( + automationActionExecutor: value, ), ); } @@ -58,14 +58,14 @@ class AutomationDialog extends StatelessWidget { leading: SvgPicture.asset(Assets.acPowerOff, width: 24, height: 24), title: const Text('Disable'), - trailing: Radio( - value: false, - groupValue: isEnabled, - onChanged: (bool? value) { - if (value == false) { + trailing: Radio( + value: 'rule_disable', + groupValue: initialSelection, + onChanged: (String? value) { + if (value != null) { context.read().add( - const SetAutomationActionExecutor( - automationActionExecutor: 'rule_disable', + SetAutomationActionExecutor( + automationActionExecutor: value, ), ); } @@ -75,23 +75,25 @@ class AutomationDialog extends StatelessWidget { const SizedBox(height: 16), DialogFooter( onConfirm: () { - context.read().add( - AddFunctionToRoutine( - [ - DeviceFunctionData( - entityId: automationId, - functionCode: 'automation', - value: state.automationActionExecutor, - operationName: 'Automation', - ), - ], - uniqueCustomId, - ), - ); - Navigator.of(context).pop(true); + if (state.automationActionExecutor != null) { + context.read().add( + AddFunctionToRoutine( + [ + DeviceFunctionData( + entityId: automationId, + functionCode: 'automation', + value: state.automationActionExecutor, + operationName: 'Automation', + ), + ], + uniqueCustomId, + ), + ); + Navigator.of(context).pop(true); + } }, onCancel: () => Navigator.of(context).pop(false), - isConfirmEnabled: true, + isConfirmEnabled: state.automationActionExecutor != null, dialogWidth: 400, ), ],