diff --git a/assets/icons/functions_icons/automation_functions/card_unlock.svg b/assets/icons/functions_icons/automation_functions/card_unlock.svg new file mode 100644 index 0000000..dd77680 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/card_unlock.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/current_temp.svg b/assets/icons/functions_icons/automation_functions/current_temp.svg new file mode 100644 index 0000000..42cceb2 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/current_temp.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/doorbell.svg b/assets/icons/functions_icons/automation_functions/doorbell.svg new file mode 100644 index 0000000..1dc515a --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/doorbell.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/doorlock_normal_open.svg b/assets/icons/functions_icons/automation_functions/doorlock_normal_open.svg new file mode 100644 index 0000000..8f4a590 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/doorlock_normal_open.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/double_lock.svg b/assets/icons/functions_icons/automation_functions/double_lock.svg new file mode 100644 index 0000000..d8ad971 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/double_lock.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/fingerprint_unlock.svg b/assets/icons/functions_icons/automation_functions/fingerprint_unlock.svg new file mode 100644 index 0000000..f9f5b84 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/fingerprint_unlock.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/hijack_alarm.svg b/assets/icons/functions_icons/automation_functions/hijack_alarm.svg new file mode 100644 index 0000000..e32997f --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/hijack_alarm.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/lock_alarm.svg b/assets/icons/functions_icons/automation_functions/lock_alarm.svg new file mode 100644 index 0000000..8bd2dee --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/lock_alarm.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/motion.svg b/assets/icons/functions_icons/automation_functions/motion.svg new file mode 100644 index 0000000..8d69463 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/motion.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/password_unlock.svg b/assets/icons/functions_icons/automation_functions/password_unlock.svg new file mode 100644 index 0000000..1920b69 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/password_unlock.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/presence.svg b/assets/icons/functions_icons/automation_functions/presence.svg new file mode 100644 index 0000000..d71a474 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/presence.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/presence_state.svg b/assets/icons/functions_icons/automation_functions/presence_state.svg new file mode 100644 index 0000000..d5de48e --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/presence_state.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/remote_unlock_req.svg b/assets/icons/functions_icons/automation_functions/remote_unlock_req.svg new file mode 100644 index 0000000..da128ff --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/remote_unlock_req.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/remote_unlock_via_app.svg b/assets/icons/functions_icons/automation_functions/remote_unlock_via_app.svg new file mode 100644 index 0000000..39fc859 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/remote_unlock_via_app.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/residual_electricity.svg b/assets/icons/functions_icons/automation_functions/residual_electricity.svg new file mode 100644 index 0000000..6a5b612 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/residual_electricity.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/functions_icons/automation_functions/self_test_result.svg b/assets/icons/functions_icons/automation_functions/self_test_result.svg new file mode 100644 index 0000000..8739327 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/self_test_result.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/functions_icons/automation_functions/temp_password_unlock.svg b/assets/icons/functions_icons/automation_functions/temp_password_unlock.svg new file mode 100644 index 0000000..98d7573 --- /dev/null +++ b/assets/icons/functions_icons/automation_functions/temp_password_unlock.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 15d1180..ccc9375 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -44,9 +44,9 @@ PODS: - FirebaseCoreInternal (~> 10.0) - GoogleUtilities/Environment (~> 7.12) - GoogleUtilities/Logger (~> 7.12) - - FirebaseCoreExtension (10.27.0): + - FirebaseCoreExtension (10.29.0): - FirebaseCore (~> 10.0) - - FirebaseCoreInternal (10.27.0): + - FirebaseCoreInternal (10.29.0): - "GoogleUtilities/NSData+zlib (~> 7.8)" - FirebaseCrashlytics (10.20.0): - FirebaseCore (~> 10.5) @@ -56,12 +56,12 @@ PODS: - GoogleUtilities/Environment (~> 7.8) - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (~> 2.1) - - FirebaseInstallations (10.27.0): + - FirebaseInstallations (10.29.0): - FirebaseCore (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8) - PromisesObjC (~> 2.1) - - FirebaseSessions (10.27.0): + - FirebaseSessions (10.29.0): - FirebaseCore (~> 10.5) - FirebaseCoreExtension (~> 10.0) - FirebaseInstallations (~> 10.0) @@ -277,11 +277,11 @@ SPEC CHECKSUMS: firebase_crashlytics: 012078b4eec6fc9716f97ba3da0f0e44a04e95b1 FirebaseAnalytics: a2731bf3670747ce8f65368b118d18aa8e368246 FirebaseCore: 28045c1560a2600d284b9c45a904fe322dc890b6 - FirebaseCoreExtension: 4ec89dd0c6de93d6becde32122d68b7c35f6bf5d - FirebaseCoreInternal: 4b297a2d56063dbea2c1d0d04222d44a8d058862 + FirebaseCoreExtension: 705ca5b14bf71d2564a0ddc677df1fc86ffa600f + FirebaseCoreInternal: df84dd300b561c27d5571684f389bf60b0a5c934 FirebaseCrashlytics: 81530595edb6d99f1918f723a6c33766a24a4c86 - FirebaseInstallations: 766dabca09fd94aef922538aaf144cc4a6fb6869 - FirebaseSessions: 2fdf949f9e58295a57703ae8f2efc44f9fa3aa16 + FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd + FirebaseSessions: dbd14adac65ce996228652c1fc3a3f576bdf3ecc Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_localization: f43b18844a2b3d2c71fd64f04ffd6b1e64dd54d4 flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be diff --git a/lib/features/app_layout/bloc/home_cubit.dart b/lib/features/app_layout/bloc/home_cubit.dart index a964a53..9168283 100644 --- a/lib/features/app_layout/bloc/home_cubit.dart +++ b/lib/features/app_layout/bloc/home_cubit.dart @@ -17,10 +17,16 @@ import 'package:syncrow_app/features/devices/model/status_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/devices_view_body.dart'; import 'package:syncrow_app/features/menu/view/menu_view.dart'; +import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart'; +import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; +import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart'; import 'package:syncrow_app/features/scene/view/create_scene_view.dart'; +import 'package:syncrow_app/features/scene/view/scene_tasks_view.dart'; import 'package:syncrow_app/features/scene/view/scene_view.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/navigation/navigation_service.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/services/api/devices_api.dart'; import 'package:syncrow_app/services/api/profile_api.dart'; import 'package:syncrow_app/services/api/spaces_api.dart'; @@ -52,13 +58,12 @@ class HomeCubit extends Cubit { return _instance!; } - - Future fetchUserInfo() async { + Future fetchUserInfo() async { try { - var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); user = await ProfileApi().fetchUserInfo(uuid); emit(HomeUserInfoLoaded(user!)); // Emit state after fetching user info - } catch (e) { return; } @@ -77,9 +82,12 @@ class HomeCubit extends Cubit { selectedSpace = null; selectedRoom = null; pageIndex = 0; - OneSignal.User.pushSubscription.removeObserver((stateChanges) => oneSignalSubscriptionObserver); - OneSignal.Notifications.removePermissionObserver((permission) => oneSignalPermissionObserver); - OneSignal.Notifications.removeClickListener((event) => oneSignalClickListenerObserver); + OneSignal.User.pushSubscription + .removeObserver((stateChanges) => oneSignalSubscriptionObserver); + OneSignal.Notifications.removePermissionObserver( + (permission) => oneSignalPermissionObserver); + OneSignal.Notifications.removeClickListener( + (event) => oneSignalClickListenerObserver); return super.close(); } @@ -121,7 +129,9 @@ class HomeCubit extends Cubit { return; } - var userUuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? ''; + var userUuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? + ''; if (userUuid.isNotEmpty) { await OneSignal.login(userUuid); } @@ -129,21 +139,24 @@ class HomeCubit extends Cubit { await OneSignal.User.pushSubscription.optIn(); //this function will be called once a user is subscribed - oneSignalSubscriptionObserver = OneSignal.User.pushSubscription.addObserver((state) async { + oneSignalSubscriptionObserver = + OneSignal.User.pushSubscription.addObserver((state) async { if (state.current.optedIn) { await _sendSubscriptionId(); } }); // Send the player id when a user allows notifications - oneSignalPermissionObserver = OneSignal.Notifications.addPermissionObserver((state) async { + oneSignalPermissionObserver = + OneSignal.Notifications.addPermissionObserver((state) async { await _sendSubscriptionId(); }); //check if the player id is sent, if not send it again await _sendSubscriptionId(); - oneSignalClickListenerObserver = OneSignal.Notifications.addClickListener((event) async { + oneSignalClickListenerObserver = + OneSignal.Notifications.addClickListener((event) async { //Once the user clicks on the notification }); } catch (err) { @@ -230,7 +243,9 @@ class HomeCubit extends Cubit { Future joinAUnit(String code) async { try { - var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? ''; + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? + ''; Map body = {'userUuid': uuid, 'inviteCode': code}; final success = await SpacesAPI.joinUnit(body); @@ -348,15 +363,28 @@ class HomeCubit extends Cubit { size: 32, ), style: ButtonStyle( - foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor), + foregroundColor: + WidgetStateProperty.all(ColorsManager.textPrimaryColor), ), onPressed: () { - Navigator.push( + Navigator.pushNamed( NavigationService.navigatorKey.currentContext!, - CustomPageRoute( - builder: (context) => const CreateSceneView(), + Routes.sceneTasksRoute, + arguments: SceneSettingsRouteArguments( + sceneType: '', + sceneId: '', + sceneName: '', ), ); + NavigationService.navigatorKey.currentContext! + .read() + .add(const ClearTaskListEvent()); + NavigationService.navigatorKey.currentContext! + .read() + .add(const SceneTypeEvent(CreateSceneEnum.none)); + NavigationService.navigatorKey.currentContext! + .read() + .add(const SmartSceneClearEvent()); }, ), IconButton( @@ -365,7 +393,8 @@ class HomeCubit extends Cubit { size: 28, ), style: ButtonStyle( - foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor), + foregroundColor: + WidgetStateProperty.all(ColorsManager.textPrimaryColor), ), onPressed: () {}, ), @@ -398,7 +427,8 @@ class HomeCubit extends Cubit { }; static var bottomNavItems = [ - defaultBottomNavBarItem(icon: Assets.assetsIconsDashboard, label: 'Dashboard'), + defaultBottomNavBarItem( + icon: Assets.assetsIconsDashboard, label: 'Dashboard'), // defaultBottomNavBarItem(icon: Assets.assetsIconslayout, label: 'Layout'), defaultBottomNavBarItem(icon: Assets.assetsIconsDevices, label: 'Devices'), defaultBottomNavBarItem(icon: Assets.assetsIconsRoutines, label: 'Routine'), @@ -424,7 +454,8 @@ class HomeCubit extends Cubit { void updateDevice(String deviceId) async { try { - final response = await DevicesAPI.firmwareDevice(deviceId: deviceId, firmwareVersion: '0'); + final response = await DevicesAPI.firmwareDevice( + deviceId: deviceId, firmwareVersion: '0'); if (response['success'] ?? false) { CustomSnackBar.displaySnackBar('No updates available'); } @@ -432,7 +463,8 @@ class HomeCubit extends Cubit { } } -BottomNavigationBarItem defaultBottomNavBarItem({required String icon, required String label}) { +BottomNavigationBarItem defaultBottomNavBarItem( + {required String icon, required String label}) { return BottomNavigationBarItem( icon: SvgPicture.asset(icon), activeIcon: SvgPicture.asset( diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index 4a822be..82fac11 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -45,7 +45,8 @@ class DeviceModel { if (type == DeviceType.LightBulb) { tempIcon = Assets.assetsIconsLight; - } else if (type == DeviceType.CeilingSensor || type == DeviceType.WallSensor) { + } else if (type == DeviceType.CeilingSensor || + type == DeviceType.WallSensor) { tempIcon = Assets.assetsIconsSensors; } else if (type == DeviceType.AC) { tempIcon = Assets.assetsIconsAC; @@ -93,5 +94,6 @@ class DeviceModel { }; } - List getFunctions(DeviceType type) => devicesFunctionsMap[productType] ?? []; + List getFunctions(DeviceType type) => + devicesFunctionsMap[productType] ?? []; } diff --git a/lib/features/devices/view/widgets/scene_listview.dart b/lib/features/devices/view/widgets/scene_listview.dart index b44fdbe..cbea49d 100644 --- a/lib/features/devices/view/widgets/scene_listview.dart +++ b/lib/features/devices/view/widgets/scene_listview.dart @@ -1,6 +1,3 @@ - - - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; @@ -12,7 +9,6 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dar import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/navigation/routing_constants.dart'; - class SceneListview extends StatelessWidget { final List scenes; final String? loadingSceneId; @@ -24,8 +20,7 @@ class SceneListview extends StatelessWidget { @override Widget build(BuildContext context) { - return - ListView.builder( + return ListView.builder( shrinkWrap: true, scrollDirection: Axis.horizontal, itemCount: scenes.length, @@ -34,7 +29,7 @@ class SceneListview extends StatelessWidget { final isLoading = loadingSceneId == scene.id; return Container( padding: const EdgeInsets.only(right: 10), - child:DefaultContainer( + child: DefaultContainer( onTap: () { Navigator.pushNamed( context, @@ -45,10 +40,11 @@ class SceneListview extends StatelessWidget { sceneName: scene.name, ), ); - BlocProvider.of(context).add(FetchSceneTasksEvent(sceneId: scene.id)); + BlocProvider.of(context) + .add(FetchSceneTasksEvent(sceneId: scene.id)); }, - child:SizedBox( - width: MediaQuery.of(context).size.width*0.4, + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.4, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -73,8 +69,7 @@ class SceneListview extends StatelessWidget { ], ), ), - ) - ); + )); }, ); } diff --git a/lib/features/scene/bloc/create_scene/create_scene_bloc.dart b/lib/features/scene/bloc/create_scene/create_scene_bloc.dart index f314088..6ebfcae 100644 --- a/lib/features/scene/bloc/create_scene/create_scene_bloc.dart +++ b/lib/features/scene/bloc/create_scene/create_scene_bloc.dart @@ -1,10 +1,17 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart'; +import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; import 'package:syncrow_app/features/scene/helper/scene_operations_data_helper.dart'; +import 'package:syncrow_app/features/scene/model/create_automation_model.dart'; import 'package:syncrow_app/features/scene/model/create_scene_model.dart'; import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; +import 'package:syncrow_app/navigation/navigation_service.dart'; import 'package:syncrow_app/services/api/scene_api.dart'; part 'create_scene_event.dart'; @@ -25,23 +32,65 @@ class CreateSceneBloc extends Bloc on(_removeFromSelectedValueById); on(_deleteScene); on(_updateTaskValue); + on(_selectConditionRule); + on(_sceneTypeEvent); + on(_onEffectiveTimeEvent); } + CreateSceneEnum sceneType = CreateSceneEnum.none; + + /// tab to run values and list List tasksList = []; List tempTasksList = []; final Map selectedValues = {}; + /// automation values and list + List automationTasksList = []; + List automationTempTasksList = []; + final Map automationSelectedValues = {}; + final Map automationComparatorValues = {}; + String conditionRule = 'or'; + EffectiveTime? effectiveTime; + FutureOr _onAddSceneTask( AddTaskEvent event, Emitter emit) { - final copyList = List.from(tempTasksList); - tasksList.addAll(copyList); - tempTasksList.clear(); - selectedValues.clear(); - emit(AddSceneTask(tasksList: tasksList)); + emit(CreateSceneLoading()); + if (event.isAutomation == true) { + final copyList = List.from(automationTempTasksList); + automationTasksList.addAll(copyList); + automationTempTasksList.clear(); + automationSelectedValues.clear(); + automationComparatorValues.clear(); + emit(AddSceneTask( + automationTasksList: automationTasksList, + tasksList: tasksList, + condition: conditionRule, + )); + } else { + final copyList = List.from(tempTasksList); + tasksList.addAll(copyList); + tempTasksList.clear(); + selectedValues.clear(); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + } } FutureOr _onTempHoldSceneTask( TempHoldSceneTasksEvent event, Emitter emit) { + if (event.isAutomation == true) { + addToTempAutomationTaskList(event, emit); + } else { + addToTempTaskList(event, emit); + } + } + + void addToTempTaskList( + TempHoldSceneTasksEvent event, Emitter emit) { + emit(CreateSceneLoading()); bool updated = false; for (var element in tempTasksList) { if (element.code == event.deviceControlModel.code) { @@ -53,6 +102,7 @@ class CreateSceneBloc extends Bloc code: event.deviceControlModel.code ?? '', deviceId: event.deviceId, functionValue: event.deviceControlModel.value, + operationDialogType: event.operationType, operationalValues: [ SceneOperationalValue( value: event.deviceControlModel.value, @@ -67,12 +117,12 @@ class CreateSceneBloc extends Bloc } } if (!updated) { - // Add new function if not found var newElement = SceneStaticFunction( operationName: event.operation, deviceName: event.deviceName, icon: event.icon, code: event.deviceControlModel.code ?? '', + operationDialogType: event.operationType, deviceId: event.deviceId, functionValue: event.deviceControlModel.value, operationalValues: [ @@ -86,40 +136,161 @@ class CreateSceneBloc extends Bloc selectedValues[newElement.code] = event.deviceControlModel.value; } - emit(TempHoldSceneTask(tempTasksList: tempTasksList)); - emit(AddSceneTask(tasksList: tasksList)); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + } + + void addToTempAutomationTaskList( + TempHoldSceneTasksEvent event, Emitter emit) { + emit(CreateSceneLoading()); + bool updated = false; + for (var element in automationTempTasksList) { + if (element.code == event.deviceControlModel.code) { + // Update the existing function with new values + var updatedElement = element.copyWith( + operationName: event.operation, + deviceName: event.deviceName, + icon: event.icon, + code: event.deviceControlModel.code ?? '', + deviceId: event.deviceId, + functionValue: event.deviceControlModel.value, + operationDialogType: event.operationType, + operationalValues: [ + SceneOperationalValue( + value: event.deviceControlModel.value, + icon: '', + ), + ], + comparator: automationComparatorValues[element.code], + ); + automationTempTasksList[automationTempTasksList.indexOf(element)] = + updatedElement; + automationSelectedValues[updatedElement.code] = + event.deviceControlModel.value; + updated = true; + break; + } + } + if (!updated) { + var newElement = SceneStaticFunction( + operationName: event.operation, + deviceName: event.deviceName, + icon: event.icon, + code: event.deviceControlModel.code ?? '', + operationDialogType: event.operationType, + deviceId: event.deviceId, + functionValue: event.deviceControlModel.value, + operationalValues: [ + SceneOperationalValue( + value: event.deviceControlModel.value, + icon: '', + ), + ], + comparator: + automationComparatorValues[event.deviceControlModel.code] ?? '==', + ); + automationTempTasksList.add(newElement); + automationSelectedValues[newElement.code] = + event.deviceControlModel.value; + } + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); } FutureOr _selectedValue( SelectedValueEvent event, Emitter emit) { - selectedValues[event.code] = event.value; + if (event.isAutomation == true) { + automationSelectedValues[event.code] = event.value; + automationComparatorValues[event.code] = event.comparator ?? '=='; + + // Update the comparator value for the specific task in automationTasksList + for (int i = 0; i < automationTasksList.length; i++) { + if (automationTasksList[i].code == event.code) { + automationTasksList[i] = automationTasksList[i].copyWith( + comparator: event.comparator ?? '==', + functionValue: event.value, + ); + break; + } + } + } else { + selectedValues[event.code] = event.value; + } emit(SelectedTaskValueState(value: event.value)); - emit(AddSceneTask(tasksList: tasksList)); + emit(AddSceneTask( + tasksList: List.from(tasksList), + automationTasksList: List.from( + automationTasksList, + ), + condition: conditionRule, + )); } FutureOr _removeTaskById( RemoveTaskByIdEvent event, Emitter emit) { emit(CreateSceneLoading()); + if (event.isAutomation == true) { + for (var element in automationTasksList) { + if (element.uniqueCustomId == event.taskId) { + automationTasksList.remove(element); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + break; + } + } + } else { + for (var element in tasksList) { + if (element.uniqueCustomId == event.taskId) { + tasksList.remove(element); - for (var element in tasksList) { - if (element.uniqueCustomId == event.taskId) { - tasksList.remove(element); - - emit(AddSceneTask(tasksList: tasksList)); - break; + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + break; + } } } } FutureOr _removeTempTaskById( RemoveTempTaskByIdEvent event, Emitter emit) { - for (var element in tempTasksList) { - if (element.code == event.code) { - tempTasksList.remove(element); + emit(CreateSceneLoading()); + if (event.isAutomation == true) { + for (var element in automationTempTasksList) { + if (element.uniqueCustomId == event.code) { + automationTempTasksList.remove(element); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + break; + } + } + } else { + for (var element in tempTasksList) { + if (element.code == event.code) { + tempTasksList.remove(element); - emit(AddSceneTask(tasksList: tasksList)); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); - break; + break; + } } } } @@ -128,27 +299,53 @@ class CreateSceneBloc extends Bloc CreateSceneWithTasksEvent event, Emitter emit) async { emit(CreateSceneLoading()); try { - final response = event.updateScene - ? await SceneApi.updateScene(event.createSceneModel, event.sceneId) - : await SceneApi.createScene(event.createSceneModel); + dynamic response; + if (event.createSceneModel != null) { + response = event.updateScene + ? await SceneApi.updateScene(event.createSceneModel!, event.sceneId) + : await SceneApi.createScene(event.createSceneModel!); + } else if (event.createAutomationModel != null) { + response = event.updateScene + ? await SceneApi.updateAutomation( + event.createAutomationModel!, event.sceneId) + : await SceneApi.createAutomation(event.createAutomationModel!); + } + if (response['success'] == true) { tasksList.clear(); tempTasksList.clear(); selectedValues.clear(); + automationTasksList.clear(); + automationTempTasksList.clear(); + automationSelectedValues.clear(); + automationComparatorValues.clear(); + effectiveTime = null; + sceneType = CreateSceneEnum.none; + conditionRule = 'or'; emit(const CreateSceneWithTasks(success: true)); } else { emit(const CreateSceneError(message: 'Something went wrong')); } } catch (e) { emit(const CreateSceneError(message: 'Something went wrong')); - emit(AddSceneTask(tasksList: tasksList)); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); } } FutureOr _clearTaskList( ClearTaskListEvent event, Emitter emit) { + emit(CreateSceneLoading()); + automationTasksList.clear(); tasksList.clear(); - emit(AddSceneTask(tasksList: tasksList)); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); } FutureOr _fetchSceneTasks( @@ -156,13 +353,46 @@ class CreateSceneBloc extends Bloc emit(CreateSceneLoading()); try { - final response = await SceneApi.getSceneDetails(event.sceneId); + final response = event.isAutomation + ? await SceneApi.getAutomationDetails(event.sceneId) + : await SceneApi.getSceneDetails(event.sceneId); if (response.id.isNotEmpty) { - tasksList = List.from( - getTaskListFunctionsFromApi(actions: response.actions)); - emit(AddSceneTask( - tasksList: tasksList, - )); + if (event.isAutomation) { + automationTasksList = List.from( + getTaskListFunctionsFromApi( + actions: [], + isAutomation: true, + conditions: response.conditions)); + tasksList = List.from( + getTaskListFunctionsFromApi( + actions: response.actions, isAutomation: false)); + + conditionRule = response.decisionExpr ?? conditionRule; + + if (response.effectiveTime != null) { + BlocProvider.of( + NavigationService.navigatorKey.currentState!.context) + .add(SetCustomTime(response.effectiveTime!.start, + response.effectiveTime!.end)); + BlocProvider.of( + NavigationService.navigatorKey.currentState!.context) + .add(ToggleDay(response.effectiveTime!.loops)); + } + + emit(AddSceneTask( + automationTasksList: automationTasksList, + tasksList: tasksList, + condition: conditionRule, + )); + } else { + tasksList = List.from( + getTaskListFunctionsFromApi( + actions: response.actions, isAutomation: false)); + emit(AddSceneTask( + tasksList: tasksList, + condition: conditionRule, + )); + } } else { emit(const CreateSceneError(message: 'Something went wrong')); } @@ -173,17 +403,51 @@ class CreateSceneBloc extends Bloc FutureOr _clearTempTaskList( ClearTempTaskListEvent event, Emitter emit) { - tempTasksList.clear(); - selectedValues.clear(); - emit(AddSceneTask(tasksList: tempTasksList)); + emit(CreateSceneLoading()); + if (event.isAutomation == true) { + automationTempTasksList.clear(); + automationSelectedValues.clear(); + automationComparatorValues.clear(); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + } else { + tempTasksList.clear(); + selectedValues.clear(); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + } } FutureOr _removeFromSelectedValueById( RemoveFromSelectedValueById event, Emitter emit) { - if (selectedValues.containsKey(event.code)) { - selectedValues.remove(event.code); - emit(const SelectedTaskValueState(value: null)); - emit(AddSceneTask(tasksList: tasksList)); + emit(CreateSceneLoading()); + if (event.isAutomation == true) { + if (automationSelectedValues.containsKey(event.code)) { + automationSelectedValues.remove(event.code); + automationComparatorValues.remove(event.code); + emit(const SelectedTaskValueState(value: null)); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + } + } else { + if (selectedValues.containsKey(event.code)) { + selectedValues.remove(event.code); + emit(const SelectedTaskValueState(value: null)); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + } } } @@ -192,8 +456,12 @@ class CreateSceneBloc extends Bloc emit(DeleteSceneLoading()); try { - final response = await SceneApi.deleteScene( - sceneId: event.sceneId, unitUuid: event.unitUuid); + final response = + sceneType.name == CreateSceneEnum.deviceStatusChanges.name + ? await SceneApi.deleteAutomation( + automationId: event.sceneId, unitUuid: event.unitUuid) + : await SceneApi.deleteScene( + sceneId: event.sceneId, unitUuid: event.unitUuid); if (response == true) { emit(const DeleteSceneSuccess(true)); } else { @@ -206,14 +474,66 @@ class CreateSceneBloc extends Bloc FutureOr _updateTaskValue( UpdateTaskEvent event, Emitter emit) { - for (var i = 0; i < tasksList.length; i++) { - if (tasksList[i].uniqueCustomId == event.taskId) { - tasksList[i] = tasksList[i].copyWith( - functionValue: event.newValue, - ); - break; + emit(CreateSceneLoading()); + if (event.isAutomation == true) { + for (var i = 0; i < automationTasksList.length; i++) { + if (automationTasksList[i].uniqueCustomId == event.taskId) { + automationTasksList[i] = automationTasksList[i].copyWith( + functionValue: event.newValue, + ); + break; + } + } + } else { + for (var i = 0; i < tasksList.length; i++) { + if (tasksList[i].uniqueCustomId == event.taskId) { + tasksList[i] = tasksList[i].copyWith( + functionValue: event.newValue, + ); + break; + } } } - emit(AddSceneTask(tasksList: tasksList)); + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + } + + FutureOr _selectConditionRule( + SelectConditionEvent event, Emitter emit) { + emit(CreateSceneInitial()); + if (event.condition.contains('any')) { + conditionRule = 'or'; + } else { + conditionRule = 'and'; + } + + emit(AddSceneTask( + tasksList: tasksList, + automationTasksList: automationTasksList, + condition: conditionRule, + )); + } + + FutureOr _sceneTypeEvent( + SceneTypeEvent event, Emitter emit) { + emit(CreateSceneInitial()); + + if (event.type == CreateSceneEnum.tabToRun) { + sceneType = CreateSceneEnum.tabToRun; + } else if (event.type == CreateSceneEnum.deviceStatusChanges) { + sceneType = CreateSceneEnum.deviceStatusChanges; + } else { + sceneType = CreateSceneEnum.none; + } + + emit(SceneTypeState(event.type)); + } + + FutureOr _onEffectiveTimeEvent( + EffectiveTimePeriodEvent event, Emitter emit) { + effectiveTime = event.period; } } diff --git a/lib/features/scene/bloc/create_scene/create_scene_event.dart b/lib/features/scene/bloc/create_scene/create_scene_event.dart index 60fd098..74046bd 100644 --- a/lib/features/scene/bloc/create_scene/create_scene_event.dart +++ b/lib/features/scene/bloc/create_scene/create_scene_event.dart @@ -8,6 +8,9 @@ sealed class CreateSceneEvent extends Equatable { } class AddTaskEvent extends CreateSceneEvent { + const AddTaskEvent({this.isAutomation}); + final bool? isAutomation; + @override List get props => []; } @@ -19,6 +22,8 @@ class TempHoldSceneTasksEvent extends CreateSceneEvent { final String operation; final String deviceName; final String uniqueId; + final bool? isAutomation; + final OperationDialogType operationType; const TempHoldSceneTasksEvent({ required this.deviceControlModel, @@ -27,6 +32,8 @@ class TempHoldSceneTasksEvent extends CreateSceneEvent { required this.operation, required this.deviceName, required this.uniqueId, + this.isAutomation, + required this.operationType, }); @override @@ -38,23 +45,37 @@ class TempHoldSceneTasksEvent extends CreateSceneEvent { operation, uniqueId, deviceName, - icon + icon, ]; } class UpdateTaskEvent extends CreateSceneEvent { final String taskId; final dynamic newValue; - const UpdateTaskEvent({required this.taskId, required this.newValue}); + final bool? isAutomation; + const UpdateTaskEvent({ + required this.taskId, + required this.newValue, + this.isAutomation, + }); @override List get props => [taskId, newValue]; } class SelectedValueEvent extends CreateSceneEvent { final dynamic value; + final dynamic automationValue; final String code; + final bool? isAutomation; + final String? comparator; - const SelectedValueEvent({this.value, required this.code}); + const SelectedValueEvent({ + this.value, + required this.code, + this.isAutomation, + this.automationValue, + this.comparator, + }); @override List get props => [value!, code]; @@ -62,8 +83,11 @@ class SelectedValueEvent extends CreateSceneEvent { class RemoveTaskByIdEvent extends CreateSceneEvent { final String taskId; - - const RemoveTaskByIdEvent({required this.taskId}); + final bool? isAutomation; + const RemoveTaskByIdEvent({ + required this.taskId, + this.isAutomation, + }); @override List get props => [taskId]; @@ -71,7 +95,8 @@ class RemoveTaskByIdEvent extends CreateSceneEvent { class RemoveTempTaskByIdEvent extends CreateSceneEvent { final String code; - const RemoveTempTaskByIdEvent({required this.code}); + final bool? isAutomation; + const RemoveTempTaskByIdEvent({required this.code, this.isAutomation}); @override List get props => [code]; @@ -79,36 +104,42 @@ class RemoveTempTaskByIdEvent extends CreateSceneEvent { class RemoveFromSelectedValueById extends CreateSceneEvent { final String code; - - const RemoveFromSelectedValueById({required this.code}); + final bool? isAutomation; + const RemoveFromSelectedValueById({required this.code, this.isAutomation}); @override List get props => [code]; } class CreateSceneWithTasksEvent extends CreateSceneEvent { - final CreateSceneModel createSceneModel; + final CreateSceneModel? createSceneModel; final bool updateScene; final String sceneId; + final CreateAutomationModel? createAutomationModel; + //final bool isAutomation; const CreateSceneWithTasksEvent({ required this.createSceneModel, required this.updateScene, required this.sceneId, + required this.createAutomationModel, + // required this.isAutomation, }); @override - List get props => [createSceneModel]; + List get props => []; } class ClearTaskListEvent extends CreateSceneEvent { - const ClearTaskListEvent(); + const ClearTaskListEvent({this.isAutomation}); + final bool? isAutomation; @override List get props => []; } class ClearTempTaskListEvent extends CreateSceneEvent { - const ClearTempTaskListEvent(); + const ClearTempTaskListEvent({this.isAutomation}); + final bool? isAutomation; @override List get props => []; @@ -116,10 +147,13 @@ class ClearTempTaskListEvent extends CreateSceneEvent { class FetchSceneTasksEvent extends CreateSceneEvent { final String sceneId; - const FetchSceneTasksEvent({required this.sceneId}); + final bool isAutomation; + + const FetchSceneTasksEvent( + {this.isAutomation = false, required this.sceneId}); @override - List get props => []; + List get props => [sceneId, isAutomation]; } class DeleteSceneEvent extends CreateSceneEvent { @@ -133,3 +167,19 @@ class DeleteSceneEvent extends CreateSceneEvent { @override List get props => [sceneId, unitUuid]; } + +class SelectConditionEvent extends CreateSceneEvent { + final String condition; + + const SelectConditionEvent(this.condition); +} + +class SceneTypeEvent extends CreateSceneEvent { + final CreateSceneEnum type; + const SceneTypeEvent(this.type); +} + +class EffectiveTimePeriodEvent extends CreateSceneEvent { + final EffectiveTime period; + const EffectiveTimePeriodEvent(this.period); +} diff --git a/lib/features/scene/bloc/create_scene/create_scene_state.dart b/lib/features/scene/bloc/create_scene/create_scene_state.dart index 3cc9da1..4108738 100644 --- a/lib/features/scene/bloc/create_scene/create_scene_state.dart +++ b/lib/features/scene/bloc/create_scene/create_scene_state.dart @@ -21,7 +21,10 @@ class CreateSceneError extends CreateSceneState { class AddSceneTask extends CreateSceneState { final List tasksList; - const AddSceneTask({required this.tasksList}); + final List? automationTasksList; + final String? condition; + const AddSceneTask( + {required this.tasksList, this.automationTasksList, this.condition}); @override List get props => [tasksList]; @@ -29,7 +32,9 @@ class AddSceneTask extends CreateSceneState { class TempHoldSceneTask extends CreateSceneState { final List tempTasksList; - const TempHoldSceneTask({required this.tempTasksList}); + final List? automationTempTasksList; + const TempHoldSceneTask( + {required this.tempTasksList, this.automationTempTasksList}); @override List get props => [tempTasksList]; @@ -68,3 +73,14 @@ class DeleteSceneError extends CreateSceneState { } class DeleteSceneLoading extends CreateSceneState {} + +class ConditionSelectedState extends CreateSceneState { + final String condition; + + const ConditionSelectedState(this.condition); +} + +class SceneTypeState extends CreateSceneState { + final CreateSceneEnum type; + const SceneTypeState(this.type); +} diff --git a/lib/features/scene/bloc/effective_period/effect_period_bloc.dart b/lib/features/scene/bloc/effective_period/effect_period_bloc.dart new file mode 100644 index 0000000..7fa071c --- /dev/null +++ b/lib/features/scene/bloc/effective_period/effect_period_bloc.dart @@ -0,0 +1,86 @@ +import 'package:bloc/bloc.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_state.dart'; +import 'package:syncrow_app/features/scene/enum/effective_period_options.dart'; +import 'package:syncrow_app/features/scene/model/create_automation_model.dart'; +import 'package:syncrow_app/navigation/navigation_service.dart'; + +class EffectPeriodBloc extends Bloc { + EffectPeriodBloc() : super(EffectPeriodState.initial()) { + on(_onSetPeriod); + on(_onToggleDay); + on(_onSetCustomTime); + } + + void _onSetPeriod(SetPeriod event, Emitter emit) { + String startTime = ''; + String endTime = ''; + + switch (event.period) { + case EnumEffectivePeriodOptions.allDay: + startTime = '00:00'; + endTime = '23:59'; + break; + case EnumEffectivePeriodOptions.daytime: + startTime = '06:00'; + endTime = '18:00'; + break; + case EnumEffectivePeriodOptions.night: + startTime = '18:00'; + endTime = '06:00'; + break; + case EnumEffectivePeriodOptions.custom: + startTime = state.customStartTime ?? '00:00'; + endTime = state.customEndTime ?? '23:59'; + break; + default: + break; + } + + // Update CreateSceneBloc + BlocProvider.of( + NavigationService.navigatorKey.currentState!.context) + .add(EffectiveTimePeriodEvent(EffectiveTime( + start: startTime, end: endTime, loops: state.selectedDaysBinary))); + + emit(state.copyWith(selectedPeriod: event.period)); + } + + void _onToggleDay(ToggleDay event, Emitter emit) { + final daysList = state.selectedDaysBinary.split(''); + final dayIndex = _getDayIndex(event.day); + if (daysList[dayIndex] == '1') { + daysList[dayIndex] = '0'; + } else { + daysList[dayIndex] = '1'; + } + final newDaysBinary = daysList.join(); + emit(state.copyWith(selectedDaysBinary: newDaysBinary)); + + BlocProvider.of( + NavigationService.navigatorKey.currentState!.context) + .add(EffectiveTimePeriodEvent(EffectiveTime( + start: state.customStartTime ?? '00:00', + end: state.customEndTime ?? '23:59', + loops: newDaysBinary))); + } + + void _onSetCustomTime(SetCustomTime event, Emitter emit) { + emit(state.copyWith( + customStartTime: event.startTime, customEndTime: event.endTime)); + + BlocProvider.of( + NavigationService.navigatorKey.currentState!.context) + .add(EffectiveTimePeriodEvent(EffectiveTime( + start: event.startTime, + end: event.endTime, + loops: state.selectedDaysBinary))); + } + + int _getDayIndex(String day) { + const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; + return days.indexOf(day); + } +} diff --git a/lib/features/scene/bloc/effective_period/effect_period_event.dart b/lib/features/scene/bloc/effective_period/effect_period_event.dart new file mode 100644 index 0000000..809bf82 --- /dev/null +++ b/lib/features/scene/bloc/effective_period/effect_period_event.dart @@ -0,0 +1,37 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/scene/enum/effective_period_options.dart'; + +abstract class EffectPeriodEvent extends Equatable { + const EffectPeriodEvent(); + + @override + List get props => []; +} + +class SetPeriod extends EffectPeriodEvent { + final EnumEffectivePeriodOptions period; + + const SetPeriod(this.period); + + @override + List get props => [period]; +} + +class ToggleDay extends EffectPeriodEvent { + final String day; + + const ToggleDay(this.day); + + @override + List get props => [day]; +} + +class SetCustomTime extends EffectPeriodEvent { + final String startTime; + final String endTime; + + const SetCustomTime(this.startTime, this.endTime); + + @override + List get props => [startTime, endTime]; +} diff --git a/lib/features/scene/bloc/effective_period/effect_period_state.dart b/lib/features/scene/bloc/effective_period/effect_period_state.dart new file mode 100644 index 0000000..029652b --- /dev/null +++ b/lib/features/scene/bloc/effective_period/effect_period_state.dart @@ -0,0 +1,43 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/scene/enum/effective_period_options.dart'; + +class EffectPeriodState extends Equatable { + final EnumEffectivePeriodOptions selectedPeriod; + final String selectedDaysBinary; + final String? customStartTime; + final String? customEndTime; + + const EffectPeriodState({ + required this.selectedPeriod, + required this.selectedDaysBinary, + this.customStartTime, + this.customEndTime, + }); + + factory EffectPeriodState.initial() { + return const EffectPeriodState( + selectedPeriod: EnumEffectivePeriodOptions.allDay, + selectedDaysBinary: "1111111", // All days selected + customStartTime: "00:00", + customEndTime: "23:59", + ); + } + + EffectPeriodState copyWith({ + EnumEffectivePeriodOptions? selectedPeriod, + String? selectedDaysBinary, + String? customStartTime, + String? customEndTime, + }) { + return EffectPeriodState( + selectedPeriod: selectedPeriod ?? this.selectedPeriod, + selectedDaysBinary: selectedDaysBinary ?? this.selectedDaysBinary, + customStartTime: customStartTime ?? this.customStartTime, + customEndTime: customEndTime ?? this.customEndTime, + ); + } + + @override + List get props => + [selectedPeriod, selectedDaysBinary, customStartTime, customEndTime]; +} diff --git a/lib/features/scene/bloc/scene_bloc/scene_bloc.dart b/lib/features/scene/bloc/scene_bloc/scene_bloc.dart index 98b36cc..047e9cb 100644 --- a/lib/features/scene/bloc/scene_bloc/scene_bloc.dart +++ b/lib/features/scene/bloc/scene_bloc/scene_bloc.dart @@ -11,34 +11,95 @@ part 'scene_state.dart'; class SceneBloc extends Bloc { SceneBloc() : super(SceneInitial()) { on(_onLoadScenes); + on(_onLoadAutomation); on(_onSceneTrigger); + on(_onUpdateAutomationStatus); } + List scenes = []; + List automationList = []; + Future _onLoadScenes(LoadScenes event, Emitter emit) async { emit(SceneLoading()); try { if (event.unitId.isNotEmpty) { - final scenes = await SceneApi.getScenesByUnitId(event.unitId); - emit(SceneLoaded(scenes)); + scenes = await SceneApi.getScenesByUnitId(event.unitId); + emit(SceneLoaded(scenes, automationList)); } else { - const SceneError(message: ''); + emit(const SceneError(message: 'Unit ID is empty')); } } catch (e) { emit(const SceneError(message: 'Something went wrong')); } } - Future _onSceneTrigger(SceneTrigger event, Emitter emit) async { + Future _onLoadAutomation( + LoadAutomation event, Emitter emit) async { + emit(SceneLoading()); + + try { + if (event.unitId.isNotEmpty) { + automationList = await SceneApi.getAutomationByUnitId(event.unitId); + emit(SceneLoaded(scenes, automationList)); + } else { + emit(const SceneError(message: 'Unit ID is empty')); + } + } catch (e) { + emit(const SceneError(message: 'Something went wrong')); + } + } + + Future _onSceneTrigger( + SceneTrigger event, Emitter emit) async { final currentState = state; if (currentState is SceneLoaded) { - emit(SceneLoaded(currentState.scenes, loadingSceneId: event.sceneId)); + emit(SceneLoaded( + currentState.scenes, + currentState.automationList, + loadingSceneId: event.sceneId, + )); try { final success = await SceneApi.triggerScene(event.sceneId); if (success) { emit(SceneTriggerSuccess(event.name)); - emit(SceneLoaded(currentState.scenes)); + emit(SceneLoaded(currentState.scenes, currentState.automationList)); + } else { + emit(const SceneError(message: 'Something went wrong')); + } + } catch (e) { + emit(const SceneError(message: 'Something went wrong')); + } + } + } + + Future _onUpdateAutomationStatus( + UpdateAutomationStatus event, Emitter emit) async { + final currentState = state; + if (currentState is SceneLoaded) { + final newLoadingStates = + Map.from(currentState.loadingStates) + ..[event.automationId] = true; + + emit(SceneLoaded( + currentState.scenes, + currentState.automationList, + loadingStates: newLoadingStates, + )); + + try { + final success = await SceneApi.updateAutomationStatus( + event.automationId, event.automationStatusUpdate); + if (success) { + automationList = await SceneApi.getAutomationByUnitId( + event.automationStatusUpdate.unitUuid); + newLoadingStates[event.automationId] = false; + emit(SceneLoaded( + currentState.scenes, + automationList, + loadingStates: newLoadingStates, + )); } else { emit(const SceneError(message: 'Something went wrong')); } diff --git a/lib/features/scene/bloc/scene_bloc/scene_event.dart b/lib/features/scene/bloc/scene_bloc/scene_event.dart index 7412afc..d580ed4 100644 --- a/lib/features/scene/bloc/scene_bloc/scene_event.dart +++ b/lib/features/scene/bloc/scene_bloc/scene_event.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/scene/model/update_automation.dart'; abstract class SceneEvent extends Equatable { const SceneEvent(); @@ -16,6 +17,15 @@ class LoadScenes extends SceneEvent { List get props => [unitId]; } +class LoadAutomation extends SceneEvent { + final String unitId; + + const LoadAutomation(this.unitId); + + @override + List get props => [unitId]; +} + class SceneTrigger extends SceneEvent { final String sceneId; final String name; @@ -25,3 +35,15 @@ class SceneTrigger extends SceneEvent { @override List get props => [sceneId]; } + +//updateAutomationStatus +class UpdateAutomationStatus extends SceneEvent { + final String automationId; + final AutomationStatusUpdate automationStatusUpdate; + + const UpdateAutomationStatus( + {required this.automationStatusUpdate, required this.automationId}); + + @override + List get props => [automationStatusUpdate]; +} diff --git a/lib/features/scene/bloc/scene_bloc/scene_state.dart b/lib/features/scene/bloc/scene_bloc/scene_state.dart index 9685416..0b6ac85 100644 --- a/lib/features/scene/bloc/scene_bloc/scene_state.dart +++ b/lib/features/scene/bloc/scene_bloc/scene_state.dart @@ -13,12 +13,16 @@ class SceneLoading extends SceneState {} class SceneLoaded extends SceneState { final List scenes; + final List automationList; final String? loadingSceneId; + final Map loadingStates; - const SceneLoaded(this.scenes, {this.loadingSceneId}); + const SceneLoaded(this.scenes, this.automationList, + {this.loadingSceneId, this.loadingStates = const {}}); @override - List get props => [scenes, loadingSceneId]; + List get props => + [scenes, loadingSceneId, automationList, loadingStates]; } class SceneError extends SceneState { @@ -38,3 +42,7 @@ class SceneTriggerSuccess extends SceneState { @override List get props => [sceneName]; } + +class UpdateAutomationStatusLoading extends SceneState { + const UpdateAutomationStatusLoading(); +} diff --git a/lib/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart b/lib/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart new file mode 100644 index 0000000..daf9e75 --- /dev/null +++ b/lib/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart @@ -0,0 +1,30 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/scene/model/smart_scene_enable.dart'; + +part 'smart_scene_select_dart_event.dart'; +part 'smart_scene_select_dart_state.dart'; + +class SmartSceneSelectBloc + extends Bloc { + SmartSceneSelectBloc() : super(SmartSceneSelectInitial()) { + on(_onSmartSceneEnable); + on(_smartSceneClear); + } + + SmartSceneEnable? smartSceneEnable; + + FutureOr _onSmartSceneEnable( + SmartSceneEnableEvent event, Emitter emit) { + smartSceneEnable = event.smartSceneEnable; + emit(SmartSceneSelected(smartSceneEnable: smartSceneEnable!)); + } + + FutureOr _smartSceneClear( + SmartSceneClearEvent event, Emitter emit) { + smartSceneEnable = null; + emit(SmartSceneSelectInitial()); + } +} \ No newline at end of file diff --git a/lib/features/scene/bloc/smart_scene/smart_scene_select_dart_event.dart b/lib/features/scene/bloc/smart_scene/smart_scene_select_dart_event.dart new file mode 100644 index 0000000..ecdf30a --- /dev/null +++ b/lib/features/scene/bloc/smart_scene/smart_scene_select_dart_event.dart @@ -0,0 +1,21 @@ +part of 'smart_scene_select_dart_bloc.dart'; + +sealed class SmartSceneSelectEvent extends Equatable { + const SmartSceneSelectEvent(); + + @override + List get props => []; +} + +class SmartSceneEnableEvent extends SmartSceneSelectEvent { + final SmartSceneEnable smartSceneEnable; + + const SmartSceneEnableEvent(this.smartSceneEnable); + + @override + List get props => [smartSceneEnable]; +} + +class SmartSceneClearEvent extends SmartSceneSelectEvent { + const SmartSceneClearEvent(); +} diff --git a/lib/features/scene/bloc/smart_scene/smart_scene_select_dart_state.dart b/lib/features/scene/bloc/smart_scene/smart_scene_select_dart_state.dart new file mode 100644 index 0000000..d29b748 --- /dev/null +++ b/lib/features/scene/bloc/smart_scene/smart_scene_select_dart_state.dart @@ -0,0 +1,19 @@ +part of 'smart_scene_select_dart_bloc.dart'; + +sealed class SmartSceneSelectState extends Equatable { + const SmartSceneSelectState(); + + @override + List get props => []; +} + +final class SmartSceneSelectInitial extends SmartSceneSelectState {} + +class SmartSceneSelected extends SmartSceneSelectState { + final SmartSceneEnable smartSceneEnable; + + const SmartSceneSelected({required this.smartSceneEnable}); + + @override + List get props => [smartSceneEnable]; +} diff --git a/lib/features/scene/enum/create_scene_enum.dart b/lib/features/scene/enum/create_scene_enum.dart index 6a65abc..5eaf0fb 100644 --- a/lib/features/scene/enum/create_scene_enum.dart +++ b/lib/features/scene/enum/create_scene_enum.dart @@ -1,4 +1,5 @@ enum CreateSceneEnum { tabToRun, deviceStatusChanges, + none, } diff --git a/lib/features/scene/enum/effective_period_options.dart b/lib/features/scene/enum/effective_period_options.dart new file mode 100644 index 0000000..97877b6 --- /dev/null +++ b/lib/features/scene/enum/effective_period_options.dart @@ -0,0 +1,7 @@ +enum EnumEffectivePeriodOptions { + allDay, + daytime, + night, + custom, + none, +} diff --git a/lib/features/scene/enum/operation_dialog_type.dart b/lib/features/scene/enum/operation_dialog_type.dart new file mode 100644 index 0000000..b1829b1 --- /dev/null +++ b/lib/features/scene/enum/operation_dialog_type.dart @@ -0,0 +1,9 @@ +enum OperationDialogType { + countdown, + delay, + temperature, + onOff, + integerSteps, + listOfOptions, + none, +} diff --git a/lib/features/scene/helper/effect_period_helper.dart b/lib/features/scene/helper/effect_period_helper.dart new file mode 100644 index 0000000..2b54fa8 --- /dev/null +++ b/lib/features/scene/helper/effect_period_helper.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart'; +import 'package:syncrow_app/features/scene/enum/effective_period_options.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:time_picker_spinner/time_picker_spinner.dart'; + +class EffectPeriodHelper { + static Future?> showCustomTimePicker( + BuildContext context) async { + String selectedStartTime = "00:00"; + String selectedEndTime = "23:59"; + PageController pageController = PageController(initialPage: 0); + + DateTime startDateTime = DateTime(2022, 1, 1, 0, 0); + DateTime endDateTime = DateTime(2022, 1, 1, 23, 59); + + context.customAlertDialog( + alertBody: SizedBox( + height: 250, + child: PageView( + controller: pageController, + physics: const NeverScrollableScrollPhysics(), + children: [ + _buildTimePickerPage( + context: context, + pageController: pageController, + isStartTime: true, + time: startDateTime, + onTimeChange: (time) { + selectedStartTime = + "${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}"; + }, + ), + _buildTimePickerPage( + context: context, + pageController: pageController, + isStartTime: false, + time: endDateTime, + onTimeChange: (time) { + selectedEndTime = + "${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}"; + }, + ), + ], + ), + ), + title: "Custom", + onConfirm: () { + context.read().add( + SetCustomTime(selectedStartTime, selectedEndTime), + ); + context.read().add( + const SetPeriod(EnumEffectivePeriodOptions.custom), + ); + + Navigator.of(context).pop(); + }, + ); + return null; + } + + static Widget _buildTimePickerPage({ + required BuildContext context, + required PageController pageController, + required bool isStartTime, + required DateTime time, + required Function(DateTime) onTimeChange, + }) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (!isStartTime) + TextButton( + onPressed: () { + pageController.previousPage( + duration: const Duration(milliseconds: 300), + curve: Curves.easeIn, + ); + }, + child: const BodyMedium(text: "Start"), + ), + TextButton( + onPressed: () {}, + child: BodyMedium( + text: isStartTime ? "Start" : "End", + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColor, + ), + ), + ), + if (isStartTime) + TextButton( + onPressed: () { + pageController.nextPage( + duration: const Duration(milliseconds: 300), + curve: Curves.easeIn, + ); + }, + child: const BodyMedium(text: "End"), + ), + ], + ), + ), + TimePickerSpinner( + is24HourMode: false, + normalTextStyle: const TextStyle( + fontSize: 24, + color: Colors.grey, + ), + highlightedTextStyle: const TextStyle( + fontSize: 24, + color: ColorsManager.primaryColor, + ), + spacing: 20, + itemHeight: 50, + isForce2Digits: true, + time: time, + onTimeChange: onTimeChange, + ), + const SizedBox(height: 16), + ], + ); + } + + static String formatEnumValue(EnumEffectivePeriodOptions value) { + switch (value) { + case EnumEffectivePeriodOptions.allDay: + return "All Day"; + case EnumEffectivePeriodOptions.daytime: + return "Daytime"; + case EnumEffectivePeriodOptions.night: + return "Night"; + case EnumEffectivePeriodOptions.custom: + return "Custom"; + case EnumEffectivePeriodOptions.none: + return "None"; + default: + return ""; + } +} +} diff --git a/lib/features/scene/helper/functions_per_device/ac_functions.dart b/lib/features/scene/helper/functions_per_device/ac_functions.dart new file mode 100644 index 0000000..35b47bd --- /dev/null +++ b/lib/features/scene/helper/functions_per_device/ac_functions.dart @@ -0,0 +1,271 @@ +import 'package:syncrow_app/features/scene/enum/ac_values.dart'; +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; +import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class ACFunctionsHelper { + static List tabToRunAcFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsAcPower, + operationName: 'Power', + code: 'switch', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, + description: "ON", + value: true, + ), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "OFF", + value: false, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsFreezing, + operationName: 'Mode', + code: 'mode', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcCooling, + description: AcValuesEnums.Cooling.name, + value: TempModes.cold.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcHeating, + description: AcValuesEnums.Heating.name, + value: TempModes.hot.name, + ), + SceneOperationalValue( + icon: Assets.assetsFanSpeed, + description: AcValuesEnums.Ventilation.name, + value: TempModes.wind.name, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsTempreture, + operationName: 'Set Temperature', + code: 'temp_set', + functionValue: functionValue, + operationDialogType: OperationDialogType.temperature, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsCelsiusDegrees, + value: 0, + description: 'COOL TO', + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsFanSpeed, + operationName: 'Fan Speed', + code: 'level', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcFanLow, + description: ValueACRange.LOW.name, + value: FanSpeeds.low.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcFanMiddle, + description: ValueACRange.MIDDLE.name, + value: FanSpeeds.middle.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcFanHigh, + description: ValueACRange.HIGH.name, + value: FanSpeeds.high.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcFanAuto, + description: ValueACRange.AUTO.name, + value: FanSpeeds.auto.name, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsChildLock, + operationName: 'Child Lock', + code: 'child_lock', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsSceneChildLock, + description: 'Lock', + value: true, + ), + SceneOperationalValue( + icon: Assets.assetsSceneChildUnlock, + description: 'Unlock', + value: false, + ), + ], + ), + ]; + } + + static List automationAcFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsAcPower, + operationName: 'Power', + code: 'switch', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, + description: "ON", + value: true, + ), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "OFF", + value: false, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsFreezing, + operationName: 'Mode', + code: 'mode', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcCooling, + description: AcValuesEnums.Cooling.name, + value: TempModes.cold.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcHeating, + description: AcValuesEnums.Heating.name, + value: TempModes.hot.name, + ), + SceneOperationalValue( + icon: Assets.assetsFanSpeed, + description: AcValuesEnums.Ventilation.name, + value: TempModes.wind.name, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsTempreture, + operationName: 'Set Temperature', + code: 'temp_set', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsCelsiusDegrees, + value: 0.0, + description: '°C', + minValue: 20, + maxValue: 30, + stepValue: 0.5, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsCurrentTemp, + operationName: 'Current Temperature', + code: 'temp_current', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsCelsiusDegrees, + value: 0.0, + description: '°C', + minValue: -9.9, + maxValue: 99.9, + stepValue: 0.1, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsFanSpeed, + operationName: 'Fan Speed', + code: 'level', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcFanLow, + description: ValueACRange.LOW.name, + value: FanSpeeds.low.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcFanMiddle, + description: ValueACRange.MIDDLE.name, + value: FanSpeeds.middle.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcFanHigh, + description: ValueACRange.HIGH.name, + value: FanSpeeds.high.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcFanAuto, + description: ValueACRange.AUTO.name, + value: FanSpeeds.auto.name, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsChildLock, + operationName: 'Child Lock', + code: 'child_lock', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsSceneChildLock, + description: 'Lock', + value: true, + ), + SceneOperationalValue( + icon: Assets.assetsSceneChildUnlock, + description: 'Unlock', + value: false, + ), + ], + ), + ]; + } +} diff --git a/lib/features/scene/helper/functions_per_device/door_lock_functions.dart b/lib/features/scene/helper/functions_per_device/door_lock_functions.dart new file mode 100644 index 0000000..81f3589 --- /dev/null +++ b/lib/features/scene/helper/functions_per_device/door_lock_functions.dart @@ -0,0 +1,267 @@ +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; +import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class DoorLockHelperFunctions { + /// tab to run functions + static List doorLockTapToRunFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsIconsDoorLock, + operationName: 'Set Door lock Normal Open', + functionValue: functionValue, + code: 'normal_open_switch', + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), + ], + ), + ]; + } + + //// automation functions + static List doorLockAutomationFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsFingerprintUnlock, + operationName: 'Fingerprint Unlock', + functionValue: functionValue, + code: 'unlock_fingerprint', + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0.0, + maxValue: 999, + stepValue: 1.0, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsPasswordUnlock, + operationName: 'Password Unlock', + functionValue: functionValue, + code: 'unlock_password', + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0.0, + maxValue: 999, + stepValue: 1.0, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsCardUnlock, + operationName: 'Card Unlock', + functionValue: functionValue, + code: 'unlock_card', + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0.0, + maxValue: 999, + stepValue: 1.0, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsLockAlarm, + operationName: 'Lock Alarm', + functionValue: functionValue, + code: 'alarm_lock', + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsFingerprintUnlock, + description: "Fingerprint Mismatch", + value: 0, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsRemoteUnlockReq, + operationName: 'Remote Unlock Request', + functionValue: functionValue, + code: 'unlock_request', + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0, + maxValue: 90, + stepValue: 1, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsResidualElectricity, + operationName: 'Residual Electricity', + functionValue: functionValue, + code: 'residual_electricity', + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0, + maxValue: 100, + stepValue: 1, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsDoubleLock, + operationName: 'Double Lock', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + code: 'reverse_lock', + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, + description: "ON", + value: true, + ), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "OFF", + value: false, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsRemoteUnlockViaApp, + operationName: 'Remote Unlock Via App', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + code: 'unlock_app', + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0, + maxValue: 999, + stepValue: 1, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsHijackAlarm, + operationName: 'Hijack Alarm', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + code: 'hijack', + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, + description: "ON", + value: true, + ), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "OFF", + value: false, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsDoorlockNormalOpen, + operationName: 'Set Door Lock Normal Open', + functionValue: functionValue, + code: 'normal_open_switch', + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, + description: "ON", + value: true, + ), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "OFF", + value: false, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsDoorlockNormalOpen, + operationName: 'Doorbell', + functionValue: functionValue, + code: 'doorbell', + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, + description: "ON", + value: true, + ), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "OFF", + value: false, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsTempPasswordUnlock, + operationName: 'Temporary Password Unlock', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + code: 'unlock_temporary', + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0, + maxValue: 999, + stepValue: 1, + ), + ], + ), + ]; + } +} diff --git a/lib/features/scene/helper/functions_per_device/gateway_functions.dart b/lib/features/scene/helper/functions_per_device/gateway_functions.dart new file mode 100644 index 0000000..7a356a7 --- /dev/null +++ b/lib/features/scene/helper/functions_per_device/gateway_functions.dart @@ -0,0 +1,62 @@ +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; +import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class GatewayHelperFunctions { + static List tabToRunGatewayFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsSwitchAlarmSound, + operationName: 'Switch Alarm Sound', + code: 'switch_alarm_sound', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsMasterState, + operationName: 'Master State', + code: 'master_state', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, + description: "Alarm", + value: 'alarm', + ), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "Normal", + value: 'normal', + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsFactoryReset, + operationName: 'Factory Reset', + code: 'factory_reset', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsSceneRefresh, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsResetOff, description: "OFF", value: false), + ], + ), + ]; + } +} diff --git a/lib/features/scene/helper/functions_per_device/human_presence_functions.dart b/lib/features/scene/helper/functions_per_device/human_presence_functions.dart new file mode 100644 index 0000000..d6f6070 --- /dev/null +++ b/lib/features/scene/helper/functions_per_device/human_presence_functions.dart @@ -0,0 +1,260 @@ +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; +import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class HumanPresenceHelperFunctions { + static List tabToRunHumanPresenceFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsFarDetection, + operationName: 'Far Detection', + code: 'far_detection', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 75, + description: '75cm', + iconValue: '75', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 150, + description: '150cm', + iconValue: '150', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 225, + description: '225cm', + iconValue: '225', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 300, + description: '300cm', + iconValue: '300', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 375, + description: '375cm', + iconValue: '375', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 450, + description: '450cm', + iconValue: '450', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 525, + description: '525cm', + iconValue: '525', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 600, + description: '600cm', + iconValue: '600', + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsMotionDetection, + operationName: 'Motion Detection Sensitivity', + code: 'motion_sensitivity_value', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 1, + description: 1.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 2, + description: 2.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 3, + description: 3.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 4, + description: 4.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 5, + description: 5.toString(), + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsMotionlessDetection, + operationName: 'Motionless Detection Sensitivity', + code: 'motionless_sensitivity', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + iconValue: '1', + icon: Assets.assetsFarDetectionFunction, + value: 1, + description: '1', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 2, + description: '2', + iconValue: '2', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 3, + description: '3', + iconValue: '3', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 4, + description: '4', + iconValue: '4', + ), + SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: 5, + description: '5', + iconValue: '5', + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsIndicator, + operationName: 'Indicator', + code: 'indicator', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), + ], + ), + ]; + } + + static List automationHumanPresenceFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsPresenceState, + operationName: 'Presence State', + code: 'presence_state', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsIconsPresenceSensorAssetsEmpty, + value: 'none', + description: 'None', + ), + SceneOperationalValue( + icon: Assets.assetsPresence, + value: 'presence', + description: 'Presence', + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsIconsPresenceSensorAssetsDistance, + operationName: 'Current Distance', + code: 'dis_current', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + value: 0.0, + description: "CM", + minValue: 1, + maxValue: 600, + stepValue: 1, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsIconsPresenceSensorAssetsIlluminanceValue, + operationName: 'Illuminance Value', + code: 'illuminance_value', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + value: 0.0, + description: "Lux", + minValue: 0, + maxValue: 10000, + stepValue: 1, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsIndicator, + operationName: 'Indicator', + code: 'indicator', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsIconsPresenceSensorAssetsTime, + operationName: 'Presence Time', + code: 'presence_time', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + value: 0.0, + minValue: 0.0, + maxValue: 65535, + stepValue: 1, + description: 'min'), + ], + ), + ]; + } +} diff --git a/lib/features/scene/helper/functions_per_device/presence_sensor.dart b/lib/features/scene/helper/functions_per_device/presence_sensor.dart new file mode 100644 index 0000000..25354e6 --- /dev/null +++ b/lib/features/scene/helper/functions_per_device/presence_sensor.dart @@ -0,0 +1,139 @@ +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; +import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class PresenceSensorHelperFunctions { + static List tabToRunPresenceSensorFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsSensitivityFunction, + operationName: 'Sensitivity', + code: 'sensitivity', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 1, + description: 1.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 2, + description: 2.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 3, + description: 3.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 4, + description: 4.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 5, + description: 5.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 6, + description: 6.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 7, + description: 7.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 8, + description: 8.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 9, + description: 9.toString(), + ), + SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: 10, + description: 10.toString(), + ), + ], + ), + ]; + } + + static List automationPresenceSensorFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsIconsSensors, + operationName: 'Presence Status', + code: 'presence_state', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsIconsPresenceSensorAssetsEmpty, + value: 'none', + description: 'None', + ), + // SceneOperationalValue( + // icon: Assets.assetsPresence, + // value: 'presence', + // description: 'Presence', + // ), + // SceneOperationalValue( + // icon: Assets.assetsMotion, + // value: 'motion', + // description: 'Motion', + // ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsSensitivityFunction, + operationName: 'Sensitivity', + code: 'sensitivity', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0, + maxValue: 10, + stepValue: 1, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsSelfTestResult, + operationName: 'Self-Test Result', + code: 'checking_result', + functionValue: functionValue, + operationDialogType: OperationDialogType.listOfOptions, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsSelfTestResult, + value: 'check_success', + description: 'Self Testing Success', + ), + ], + ), + ]; + } +} diff --git a/lib/features/scene/helper/functions_per_device/three_gang_functions.dart b/lib/features/scene/helper/functions_per_device/three_gang_functions.dart new file mode 100644 index 0000000..88eaef3 --- /dev/null +++ b/lib/features/scene/helper/functions_per_device/three_gang_functions.dart @@ -0,0 +1,200 @@ +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; +import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class ThreeGangHelperFunctions { + static List threeGangHelperFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsAcPower, + operationName: 'Light 1 Switch', + code: 'switch_1', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsAcPower, + operationName: 'Light 2 Switch', + code: 'switch_2', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsAcPower, + operationName: 'Light 3 Switch', + code: 'switch_3', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsLightCountdown, + operationName: 'Light 1 CountDown', + code: 'countdown_1', + functionValue: functionValue, + operationDialogType: OperationDialogType.countdown, + operationalValues: [ + SceneOperationalValue(icon: '', value: 0), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsLightCountdown, + operationName: 'Light 2 CountDown', + code: 'countdown_2', + functionValue: functionValue, + operationDialogType: OperationDialogType.countdown, + operationalValues: [ + SceneOperationalValue(icon: '', value: 0), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsLightCountdown, + operationName: 'Light 3 CountDown', + code: 'countdown_3', + functionValue: functionValue, + operationDialogType: OperationDialogType.countdown, + operationalValues: [ + SceneOperationalValue(icon: '', value: 0), + ], + ), + ]; + } + + static List threeGangAutomationFunctions( + String deviceId, String deviceName, functionValue) { + return [ + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsAcPower, + operationName: 'Light 1 Switch', + code: 'switch_1', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsAcPower, + operationName: 'Light 2 Switch', + code: 'switch_2', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsAcPower, + operationName: 'Light 3 Switch', + code: 'switch_3', + functionValue: functionValue, + operationDialogType: OperationDialogType.onOff, + operationalValues: [ + SceneOperationalValue( + icon: Assets.assetsAcPower, description: "ON", value: true), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsLightCountdown, + operationName: 'Light 1 CountDown', + code: 'countdown_1', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "sec", + value: 0.0, + minValue: 0, + maxValue: 43200, + stepValue: 1, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsLightCountdown, + operationName: 'Light 2 CountDown', + code: 'countdown_2', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "sec", + value: 0.0, + minValue: 0, + maxValue: 43200, + stepValue: 1, + ), + ], + ), + SceneStaticFunction( + deviceId: deviceId, + deviceName: deviceName, + icon: Assets.assetsLightCountdown, + operationName: 'Light 3 CountDown', + code: 'countdown_3', + functionValue: functionValue, + operationDialogType: OperationDialogType.integerSteps, + operationalValues: [ + SceneOperationalValue( + icon: '', + description: "sec", + value: 0.0, + minValue: 0, + maxValue: 43200, + stepValue: 1, + ), + ], + ), + ]; + } +} diff --git a/lib/features/scene/helper/scene_logic_helper.dart b/lib/features/scene/helper/scene_logic_helper.dart index 616e3fd..b1d3086 100644 --- a/lib/features/scene/helper/scene_logic_helper.dart +++ b/lib/features/scene/helper/scene_logic_helper.dart @@ -2,13 +2,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; - +import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart'; +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; +import 'package:syncrow_app/features/scene/model/create_automation_model.dart'; import 'package:syncrow_app/features/scene/model/create_scene_model.dart'; - import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; - import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_countdown.dart'; import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_functions_body.dart'; +import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_slider_steps.dart'; import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_temperature_body.dart'; import 'package:syncrow_app/utils/context_extension.dart'; @@ -20,84 +21,185 @@ mixin SceneLogicHelper { } void handleSaveButtonPress( - BuildContext context, - TextEditingController sceneNameController, - List tasks, { + BuildContext context, { required bool updateScene, required String sceneId, + required bool isAutomation, + required TextEditingController sceneName, + required List actions, + required List conditions, }) { - if (isOnlyDelayOrDelayLast(tasks)) { - // Show snackbar indicating restriction - Navigator.pop(context); - context.showCustomSnackbar( - message: 'A single delay or delay-last operations are NOT allowed.', - icon: const Icon( - Icons.error, - color: Colors.red, - ), - ); - } else { - final createSceneModel = CreateSceneModel( - unitUuid: HomeCubit.getInstance().selectedSpace!.id ?? '', - sceneName: sceneNameController.text, - decisionExpr: 'and', - actions: List.generate( - tasks.length, - (index) { - final task = tasks[index]; - if (task.deviceId == 'delay') { - return CreateSceneAction( - entityId: tasks[index].deviceId, - actionExecutor: 'delay', - executorProperty: CreateSceneExecutorProperty( - functionCode: '', - functionValue: '', - delaySeconds: task.functionValue, + final sceneBloc = context.read(); + final smartSceneBloc = context.read(); + + if (isAutomation) { + // Handle Automation Creation + if (isOnlyDelayOrDelayLast(actions)) { + Navigator.pop(context); + context.showCustomSnackbar( + message: 'A single delay or delay-last operations are NOT allowed.', + icon: const Icon( + Icons.error, + color: Colors.red, + ), + ); + } else { + final createAutomationModel = CreateAutomationModel( + unitUuid: HomeCubit.getInstance().selectedSpace!.id ?? '', + automationName: sceneName.text, + decisionExpr: sceneBloc.conditionRule, + effectiveTime: sceneBloc.effectiveTime ?? + EffectiveTime(start: '00:00', end: '23:59', loops: '1111111'), + conditions: List.generate( + conditions.length, + (index) { + final task = conditions[index]; + return CreateCondition( + code: index + 1, + entityId: task.deviceId, + entityType: 'device_report', + expr: ConditionExpr( + statusCode: task.code, + comparator: task.comparator ?? '==', + statusValue: task.functionValue, ), ); - } - return CreateSceneAction( - entityId: task.deviceId, - actionExecutor: 'device_issue', - executorProperty: CreateSceneExecutorProperty( - functionCode: task.code, - functionValue: task.functionValue, - delaySeconds: 0, - ), - ); - }, - ), - ); - context.read().add(CreateSceneWithTasksEvent( - createSceneModel: createSceneModel, - updateScene: updateScene, - sceneId: sceneId, - )); - - Navigator.pop(context); + }, + ), + actions: [ + ...List.generate( + actions.length, + (index) { + final task = actions[index]; + if (task.deviceId == 'delay') { + return CreateSceneAction( + entityId: actions[index].deviceId, + actionExecutor: 'delay', + executorProperty: CreateSceneExecutorProperty( + functionCode: '', + functionValue: '', + delaySeconds: task.functionValue, + ), + ); + } + return CreateSceneAction( + entityId: task.deviceId, + actionExecutor: 'device_issue', + executorProperty: CreateSceneExecutorProperty( + functionCode: task.code, + functionValue: task.functionValue, + delaySeconds: 0, + ), + ); + }, + ), + if (smartSceneBloc.smartSceneEnable != null) + CreateSceneAction( + entityId: smartSceneBloc.smartSceneEnable?.entityId ?? '', + actionExecutor: + smartSceneBloc.smartSceneEnable?.actionExecutor ?? '', + executorProperty: null) + ], + ); + sceneBloc.add(CreateSceneWithTasksEvent( + createSceneModel: null, + updateScene: updateScene, + sceneId: sceneId, + createAutomationModel: createAutomationModel, + )); + Navigator.pop(context); + } + } else { + if (isOnlyDelayOrDelayLast(actions)) { + Navigator.pop(context); + context.showCustomSnackbar( + message: 'A single delay or delay-last operations are NOT allowed.', + icon: const Icon( + Icons.error, + color: Colors.red, + ), + ); + } else { + // Handle Scene Creation + final createSceneModel = CreateSceneModel( + unitUuid: HomeCubit.getInstance().selectedSpace!.id ?? '', + sceneName: sceneName.text, + decisionExpr: 'and', + actions: [ + ...List.generate( + actions.length, + (index) { + final task = actions[index]; + if (task.deviceId == 'delay') { + return CreateSceneAction( + entityId: actions[index].deviceId, + actionExecutor: 'delay', + executorProperty: CreateSceneExecutorProperty( + functionCode: '', + functionValue: '', + delaySeconds: task.functionValue, + ), + ); + } + return CreateSceneAction( + entityId: task.deviceId, + actionExecutor: 'device_issue', + executorProperty: CreateSceneExecutorProperty( + functionCode: task.code, + functionValue: task.functionValue, + delaySeconds: 0, + ), + ); + }, + ), + if (smartSceneBloc.smartSceneEnable != null) + CreateSceneAction( + entityId: smartSceneBloc.smartSceneEnable?.entityId ?? '', + actionExecutor: + smartSceneBloc.smartSceneEnable?.actionExecutor ?? '', + executorProperty: null) + ], + ); + sceneBloc.add(CreateSceneWithTasksEvent( + createSceneModel: createSceneModel, + createAutomationModel: null, + updateScene: updateScene, + sceneId: sceneId, + )); + Navigator.pop(context); + } } } Widget getTheCorrectDialogBody( - SceneStaticFunction taskItem, - ) { - if (taskItem.code.contains('temp_set')) { + SceneStaticFunction taskItem, dynamic functionValue, + {required bool isAutomation}) { + if (taskItem.operationDialogType == OperationDialogType.temperature) { return AlertDialogTemperatureBody( taskItem: taskItem, - functionValue: taskItem.functionValue, + functionValue: functionValue ?? taskItem.functionValue, ); - } else if (taskItem.code.contains('countdown') || - taskItem.deviceId.contains('delay')) { + } else if ((taskItem.operationDialogType == + OperationDialogType.countdown) || + (taskItem.operationDialogType == OperationDialogType.delay)) { return AlertDialogCountdown( - durationValue: taskItem.functionValue, - functionValue: taskItem.functionValue, + durationValue: taskItem.functionValue ?? 0, + functionValue: functionValue ?? taskItem.functionValue, function: taskItem, ); + } else if (taskItem.operationDialogType == + OperationDialogType.integerSteps) { + return AlertDialogSliderSteps( + taskItem: taskItem, + functionValue: functionValue ?? taskItem.functionValue, + isAutomation: isAutomation, + ); } return AlertDialogFunctionsOperationsBody( taskItem: taskItem, - functionValue: taskItem.functionValue, + functionValue: functionValue ?? taskItem.functionValue, + isAutomation: isAutomation, ); } } diff --git a/lib/features/scene/helper/scene_operations_data_helper.dart b/lib/features/scene/helper/scene_operations_data_helper.dart index 5c8cda0..4b75b78 100644 --- a/lib/features/scene/helper/scene_operations_data_helper.dart +++ b/lib/features/scene/helper/scene_operations_data_helper.dart @@ -1,658 +1,166 @@ import 'package:syncrow_app/features/devices/model/function_model.dart'; import 'package:syncrow_app/features/scene/enum/ac_values.dart'; +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; +import 'package:syncrow_app/features/scene/helper/functions_per_device/ac_functions.dart'; +import 'package:syncrow_app/features/scene/helper/functions_per_device/door_lock_functions.dart'; +import 'package:syncrow_app/features/scene/helper/functions_per_device/gateway_functions.dart'; +import 'package:syncrow_app/features/scene/helper/functions_per_device/human_presence_functions.dart'; +import 'package:syncrow_app/features/scene/helper/functions_per_device/presence_sensor.dart'; +import 'package:syncrow_app/features/scene/helper/functions_per_device/three_gang_functions.dart'; import 'package:syncrow_app/features/scene/model/scene_details_model.dart'; import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; mixin SceneOperationsDataHelper { + final Map, String, String, dynamic, bool)> + _functionMap = { + DeviceType.LightBulb: lightBulbFunctions, + DeviceType.CeilingSensor: ceilingSensorFunctions, + DeviceType.WallSensor: wallSensorFunctions, + DeviceType.AC: acFunctions, + DeviceType.DoorLock: doorLockFunctions, + DeviceType.Curtain: curtainFunctions, + DeviceType.ThreeGang: threeGangFunctions, + DeviceType.Gateway: gatewayFunctions, + }; + + final Map _titleMap = { + DeviceType.LightBulb: 'Light Bulb Functions', + DeviceType.CeilingSensor: 'Presence Sensor Functions', + DeviceType.WallSensor: 'Human Presence Sensor Functions', + DeviceType.AC: 'AC Functions', + DeviceType.DoorLock: 'Door Lock Functions', + DeviceType.Curtain: 'Curtain Functions', + DeviceType.ThreeGang: '3G Light Switch Functions', + DeviceType.Gateway: 'Gateway Functions', + }; + List getFunctionsWithIcons({ DeviceType? type, required List functions, required String deviceId, required String deviceName, + required bool isAutomation, }) { - switch (type) { - case DeviceType.LightBulb: - return lightBulbFunctions( - functions: functions, - deviceId: deviceId, - deviceName: deviceName, - functionValue: null, - ); - case DeviceType.CeilingSensor: - return ceilingSensorFunctions( - functions: functions, - deviceId: deviceId, - deviceName: deviceName, - functionValue: null, - ); - case DeviceType.WallSensor: - return wallSensorFunctions( - functions: functions, - deviceId: deviceId, - deviceName: deviceName, - functionValue: null, - ); - case DeviceType.AC: - return acFunctions( - functions: functions, - deviceId: deviceId, - deviceName: deviceName, - functionValue: null, - ); - case DeviceType.DoorLock: - return doorLockFunctions( - functions: functions, - deviceId: deviceId, - deviceName: deviceName, - functionValue: null, - ); - case DeviceType.Curtain: - return curtainFunctions( - functions: functions, - deviceId: deviceId, - deviceName: deviceName, - functionValue: null, - ); - case DeviceType.ThreeGang: - return threeGangFunctions( - functions: functions, - deviceId: deviceId, - deviceName: deviceName, - functionValue: null, - ); - case DeviceType.Gateway: - return gatewayFunctions( - functions: functions, - deviceId: deviceId, - deviceName: deviceName, - functionValue: null, - ); - default: - return lightBulbFunctions( - functions: functions, - deviceId: deviceId, - deviceName: deviceName, - functionValue: null, - ); - } + final functionValue = null; + return _functionMap[type]?.call( + functions, deviceId, deviceName, functionValue, isAutomation) ?? + lightBulbFunctions( + functions, deviceId, deviceName, functionValue, isAutomation); } String getTitle({DeviceType? type}) { - switch (type) { - case DeviceType.LightBulb: - return 'Light Bulb Functions'; - case DeviceType.CeilingSensor: - return 'Presence Sensor Functions'; - case DeviceType.WallSensor: - return 'Human Presence Sensor Functions'; - case DeviceType.AC: - return 'AC Functions'; - case DeviceType.DoorLock: - return 'Door Lock Functions'; - case DeviceType.Curtain: - return 'Curtain Functions'; - case DeviceType.ThreeGang: - return '3G Light Switch Functions'; - case DeviceType.Gateway: - return 'Gateway Functions'; - default: - return ''; + return _titleMap[type] ?? ''; + } + + static List ceilingSensorFunctions( + List functions, + String deviceId, + String deviceName, + dynamic functionValue, + bool isAutomation) { + if (isAutomation) { + return PresenceSensorHelperFunctions.automationPresenceSensorFunctions( + deviceId, deviceName, functionValue); } + return PresenceSensorHelperFunctions.tabToRunPresenceSensorFunctions( + deviceId, deviceName, functionValue); } - /// presence sensor - List ceilingSensorFunctions({ - required List functions, - required String deviceId, - required String deviceName, - required dynamic functionValue, - }) { - return [ - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsSensitivityFunction, - operationName: 'Sensitivity', - code: 'sensitivity', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 1, - description: 1.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 2, - description: 2.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 3, - description: 3.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 4, - description: 4.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 5, - description: 5.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 6, - description: 6.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 7, - description: 7.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 8, - description: 8.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 9, - description: 9.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 10, - description: 10.toString(), - ), - ], - ), - ]; - } - - List curtainFunctions( - {required List functions, - required String deviceId, - required String deviceName, - required functionValue}) { + static List curtainFunctions( + List functions, + String deviceId, + String deviceName, + dynamic functionValue, + bool isAutomation) { return []; } - List doorLockFunctions({ - required List functions, - required String deviceId, - required String deviceName, - required functionValue, - }) { - return [ - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsIconsDoorLock, - operationName: 'Set Door lock Normal Open', - functionValue: functionValue, - code: 'normal_open_switch', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), - ], - ), - ]; + static List doorLockFunctions( + List functions, + String deviceId, + String deviceName, + dynamic functionValue, + bool isAutomation) { + if (isAutomation) { + return DoorLockHelperFunctions.doorLockAutomationFunctions( + deviceId, deviceName, functionValue); + } + return DoorLockHelperFunctions.doorLockTapToRunFunctions( + deviceId, deviceName, functionValue); } - List wallSensorFunctions({ - required List functions, - required String deviceId, - required String deviceName, - required functionValue, - }) { - return [ - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsFarDetection, - operationName: 'Far Detection', - code: 'far_detection', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 75, - description: '75cm', - iconValue: '75', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 150, - description: '150cm', - iconValue: '150', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 225, - description: '225cm', - iconValue: '225', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 300, - description: '300cm', - iconValue: '300', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 375, - description: '375cm', - iconValue: '375', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 450, - description: '450cm', - iconValue: '450', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 525, - description: '525cm', - iconValue: '525', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 600, - description: '600cm', - iconValue: '600', - ), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsMotionDetection, - operationName: 'Motion Detection Sensitivity', - code: 'motion_sensitivity_value', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 1, - description: 1.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 2, - description: 2.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 3, - description: 3.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 4, - description: 4.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 5, - description: 5.toString(), - ), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsMotionlessDetection, - operationName: 'Motionless Detection Sensitivity', - code: 'motionless_sensitivity', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - iconValue: '1', - icon: Assets.assetsFarDetectionFunction, - value: 1, - description: '1', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 2, - description: '2', - iconValue: '2', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 3, - description: '3', - iconValue: '3', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 4, - description: '4', - iconValue: '4', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 5, - description: '5', - iconValue: '5', - ), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsIndicator, - operationName: 'Indicator', - code: 'indicator', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsNobodyTime, - operationName: 'Nobody Time', - code: 'presence_time', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - ), - ]; + static List wallSensorFunctions( + List functions, + String deviceId, + String deviceName, + dynamic functionValue, + bool isAutomation) { + if (isAutomation) { + return HumanPresenceHelperFunctions.automationHumanPresenceFunctions( + deviceId, deviceName, functionValue); + } + return HumanPresenceHelperFunctions.tabToRunHumanPresenceFunctions( + deviceId, deviceName, functionValue); } - List lightBulbFunctions( - {required List functions, - required String deviceId, - required String deviceName, - required functionValue}) { + static List lightBulbFunctions( + List functions, + String deviceId, + String deviceName, + dynamic functionValue, + bool isAutomation) { return []; } - List gatewayFunctions({ - required List functions, - required String deviceId, - required String deviceName, - required functionValue, - }) { - return [ - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsSwitchAlarmSound, - operationName: 'Switch Alarm Sound', - code: 'switch_alarm_sound', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsMasterState, - operationName: 'Master State', - code: 'master_state', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, - description: "Alarm", - value: 'alarm', - ), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "Normal", - value: 'normal', - ), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsFactoryReset, - operationName: 'Factory Reset', - code: 'factory_reset', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSceneRefresh, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsResetOff, description: "OFF", value: false), - ], - ), - ]; + static List gatewayFunctions( + List functions, + String deviceId, + String deviceName, + dynamic functionValue, + bool isAutomation) { + return GatewayHelperFunctions.tabToRunGatewayFunctions( + deviceId, deviceName, functionValue); } - List threeGangFunctions({ - required List functions, - required String deviceId, - required String deviceName, - required functionValue, - }) { - return [ - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsAcPower, - operationName: 'Light 1 Switch', - code: 'switch_1', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsAcPower, - operationName: 'Light 2 Switch', - code: 'switch_2', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsAcPower, - operationName: 'Light 3 Switch', - code: 'switch_3', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, description: "OFF", value: false), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsLightCountdown, - operationName: 'Light 1 CountDown', - code: 'countdown_1', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsLightCountdown, - operationName: 'Light 2 CountDown', - code: 'countdown_2', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsLightCountdown, - operationName: 'Light 3 CountDown', - code: 'countdown_3', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - ), - ]; + static List threeGangFunctions( + List functions, + String deviceId, + String deviceName, + dynamic functionValue, + bool isAutomation) { + if (isAutomation) { + return ThreeGangHelperFunctions.threeGangAutomationFunctions( + deviceId, deviceName, functionValue); + } + return ThreeGangHelperFunctions.threeGangHelperFunctions( + deviceId, deviceName, functionValue); } - /// smart ac thermostat - List acFunctions({ - required List functions, - required String deviceId, - required String deviceName, - required functionValue, - }) { - return [ - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsAcPower, - operationName: 'Power', - code: 'switch', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, - description: "ON", - value: true, - ), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false, - ), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsFreezing, - operationName: 'Mode', - code: 'mode', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcCooling, - description: AcValuesEnums.Cooling.name, - value: TempModes.cold.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcHeating, - description: AcValuesEnums.Heating.name, - value: TempModes.hot.name, - ), - SceneOperationalValue( - icon: Assets.assetsFanSpeed, - description: AcValuesEnums.Ventilation.name, - value: TempModes.wind.name, - ), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsTempreture, - operationName: 'Set Temperature', - code: 'temp_set', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsCelsiusDegrees, - value: 0, - description: 'COOL TO', - ), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsFanSpeed, - operationName: 'Fan Speed', - code: 'level', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcFanLow, - description: ValueACRange.LOW.name, - value: FanSpeeds.low.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcFanMiddle, - description: ValueACRange.MIDDLE.name, - value: FanSpeeds.middle.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcFanHigh, - description: ValueACRange.HIGH.name, - value: FanSpeeds.high.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcFanAuto, - description: ValueACRange.AUTO.name, - value: FanSpeeds.auto.name, - ), - ], - ), - SceneStaticFunction( - deviceId: deviceId, - deviceName: deviceName, - icon: Assets.assetsChildLock, - operationName: 'Child Lock', - code: 'child_lock', - functionValue: functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSceneChildLock, - description: 'Lock', - value: true, - ), - SceneOperationalValue( - icon: Assets.assetsSceneChildUnlock, - description: 'Unlock', - value: false, - ), - ], - ), - ]; + static List acFunctions( + List functions, + String deviceId, + String deviceName, + dynamic functionValue, + bool isAutomation) { + if (isAutomation) { + return ACFunctionsHelper.automationAcFunctions( + deviceId, deviceName, functionValue); + } + return ACFunctionsHelper.tabToRunAcFunctions( + deviceId, deviceName, functionValue); } -///////&************ END of get function with icons for device *************&&//////// - /// - /// List getTaskListFunctionsFromApi({ required List actions, + required bool isAutomation, + List? conditions, }) { List functions = []; + // Handle actions for (var action in actions) { if (action.entityId == 'delay') { functions.add( @@ -662,6 +170,7 @@ mixin SceneOperationsDataHelper { deviceIcon: Assets.delay, icon: Assets.delay, operationName: 'delay', + operationDialogType: OperationDialogType.delay, functionValue: action.executorProperty.delaySeconds, code: '', operationalValues: [ @@ -673,617 +182,973 @@ mixin SceneOperationsDataHelper { ], ), ); + } else { + functions + .add(_mapExecutorPropertyToSceneFunction(action, isAutomation)); } - ExecutorProperty executorProperty = action.executorProperty; + } - switch (executorProperty.functionCode) { - case 'sensitivity': - functions.add( - SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsSensitivityFunction, - operationName: 'Sensitivity', - code: 'sensitivity', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 1, - description: 1.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 2, - description: 2.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 3, - description: 3.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 4, - description: 4.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 5, - description: 5.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 6, - description: 6.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 7, - description: 7.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 8, - description: 8.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 9, - description: 9.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 10, - description: 10.toString(), - ), - ], - ), - ); - break; - case 'normal_open_switch': - functions.add( - SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'WIFI LOCK PRO', - deviceIcon: Assets.assetsIconsDoorLock, - icon: Assets.assetsIconsDoorLock, - operationName: 'Set Door lock Normal Open', - functionValue: executorProperty.functionValue, - code: 'normal_open_switch', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false, - ), - ], - ), - ); - break; - case 'far_detection': - functions.add( - SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Human Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsFarDetection, - operationName: 'Far Detection', - functionValue: executorProperty.functionValue, - code: 'far_detection', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 75, - description: '75cm', - iconValue: '75', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 150, - description: '150cm', - iconValue: '150', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 225, - description: '225cm', - iconValue: '225', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 300, - description: '300cm', - iconValue: '300', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 375, - description: '375cm', - iconValue: '375', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 450, - description: '450cm', - iconValue: '450', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 525, - description: '525cm', - iconValue: '525', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 600, - description: '600cm', - iconValue: '600', - ), - ], - ), - ); - break; - case 'motion_sensitivity_value': - functions.add( - SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Human Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsMotionDetection, - operationName: 'Motion Detection Sensitivity', - functionValue: executorProperty.functionValue, - code: 'motion_sensitivity_value', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 1, - description: 1.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 2, - description: 2.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 3, - description: 3.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 4, - description: 4.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 5, - description: 5.toString(), - ), - ], - ), - ); - break; - case 'motionless_sensitivity': - functions.add( - SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Human Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsMotionlessDetection, - operationName: 'Motionless Detection Sensitivity', - functionValue: executorProperty.functionValue, - code: 'motion_sensitivity_value', - operationalValues: [ - SceneOperationalValue( - iconValue: '1', - icon: Assets.assetsFarDetectionFunction, - value: 1, - description: '1', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 2, - description: '2', - iconValue: '2', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 3, - description: '3', - iconValue: '3', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 4, - description: '4', - iconValue: '4', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 5, - description: '5', - iconValue: '5', - ), - ], - ), - ); - break; - case 'indicator': - functions.add( - SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Human Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsIndicator, - operationName: 'Indicator', - functionValue: executorProperty.functionValue, - code: 'indicator', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false, - ), - ], - ), - ); - break; - case 'presence_time': - functions.add( - SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Human Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsNobodyTime, - operationName: 'Nobody Time', - functionValue: executorProperty.functionValue, - code: 'presence_time', - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - ), - ); - break; - case 'switch_alarm_sound': - functions.add( - SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Multi-Mode Gateway Z-W-B', - deviceIcon: Assets.assetsIconsGateway, - icon: Assets.assetsSwitchAlarmSound, - operationName: 'Switch Alarm Sound', - functionValue: executorProperty.functionValue, - code: 'switch_alarm_sound', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, - description: "ON", - value: true, - ), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false), - ], - ), - ); - break; - case 'master_state': - functions.add( - SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Multi-Mode Gateway Z-W-B', - deviceIcon: Assets.assetsIconsGateway, - icon: Assets.assetsMasterState, - operationName: 'Master State', - functionValue: executorProperty.functionValue, - code: 'master_state', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, - description: "Alarm", - value: 'alarm', - ), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "Normal", - value: 'normal', - ), - ], - ), - ); - break; - case 'factory_reset': - functions.add( - SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Multi-Mode Gateway Z-W-B', - deviceIcon: Assets.assetsIconsGateway, - icon: Assets.assetsFactoryReset, - operationName: 'Reset Factory', - functionValue: executorProperty.functionValue, - code: 'factory_reset', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSceneRefresh, - description: "ON", - value: true), - SceneOperationalValue( - icon: Assets.assetsResetOff, - description: "OFF", - value: false, - ), - ], - ), - ); - break; - case 'switch_1': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsAcPower, - operationName: 'Light 1 Switch', - code: 'switch_1', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, - description: "ON", - value: true, - ), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false), - SceneOperationalValue( - icon: Assets.assetsSceneRefresh, - description: "Reverse Switch", - value: null, - ), - ], - )); - break; - case 'switch_2': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsAcPower, - operationName: 'Light 2 Switch', - code: 'switch_2', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false), - SceneOperationalValue( - icon: Assets.assetsSceneRefresh, - description: "Reverse Switch", - value: null, - ), - ], - )); - break; - case 'switch_3': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsAcPower, - operationName: 'Light 3 Switch', - code: 'switch_3', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false), - SceneOperationalValue( - icon: Assets.assetsSceneRefresh, - description: "Reverse Switch", - value: null, - ), - ], - )); - break; - case 'countdown_1': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsLightCountdown, - operationName: 'Light 1 CountDown', - code: 'countdown_1', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - )); - break; - case 'countdown_2': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsLightCountdown, - operationName: 'Light 2 CountDown', - code: 'countdown_2', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - )); - break; - case 'countdown_3': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsLightCountdown, - operationName: 'Light 3 CountDown', - code: 'countdown_3', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - )); - break; - case 'switch': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Smart AC Thermostat - Grey - Model A', - deviceIcon: Assets.assetsIconsAC, - icon: Assets.assetsAcPower, - operationName: 'Power', - code: 'switch', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, - description: "ON", - value: true, - ), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false, - ), - ], - )); - break; - case 'temp_set': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Smart AC Thermostat - Grey - Model A', - deviceIcon: Assets.assetsIconsAC, - icon: Assets.assetsTempreture, - operationName: 'Set Temperature', - code: 'temp_set', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsCelsiusDegrees, - value: 0, - description: 'COOL TO', - ), - ], - )); - break; - case 'mode': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Smart AC Thermostat - Grey - Model A', - deviceIcon: Assets.assetsIconsAC, - icon: Assets.assetsFreezing, - operationName: 'Mode', - code: 'mode', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcCooling, - description: AcValuesEnums.Cooling.name, - value: TempModes.cold.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcHeating, - description: AcValuesEnums.Heating.name, - value: TempModes.hot.name, - ), - SceneOperationalValue( - icon: Assets.assetsFanSpeed, - description: AcValuesEnums.Ventilation.name, - value: TempModes.wind.name, - ), - ], - )); - break; - case 'level': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Smart AC Thermostat - Grey - Model A', - deviceIcon: Assets.assetsIconsAC, - icon: Assets.assetsFanSpeed, - operationName: 'Fan Speed', - code: 'level', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcFanLow, - description: ValueACRange.LOW.name, - value: FanSpeeds.low.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcFanMiddle, - description: ValueACRange.MIDDLE.name, - value: FanSpeeds.middle.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcFanHigh, - description: ValueACRange.HIGH.name, - value: FanSpeeds.high.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcFanAuto, - description: ValueACRange.AUTO.name, - value: FanSpeeds.auto.name, - ), - ], - )); - break; - case 'child_lock': - functions.add(SceneStaticFunction( - deviceId: action.entityId, - deviceName: 'Smart AC Thermostat - Grey - Model A', - deviceIcon: Assets.assetsIconsAC, - icon: Assets.assetsChildLock, - operationName: 'Child Lock', - code: 'child_lock', - functionValue: executorProperty.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSceneChildLock, - description: 'Lock', - value: true, - ), - SceneOperationalValue( - icon: Assets.assetsSceneChildUnlock, - description: 'Unlock', - value: false, - ), - ], - )); - break; - default: + // Handle conditions + if (conditions != null) { + for (var condition in conditions) { + // Create a dummy Action from Condition to reuse _mapExecutorPropertyToSceneFunction + Action dummyAction = Action( + actionExecutor: 'device_report', + entityId: condition.entityId, + executorProperty: ExecutorProperty( + functionCode: condition.expr.statusCode, + functionValue: condition.expr.statusValue, + ), + ); + var conditionFunction = _mapExecutorPropertyToSceneFunction( + dummyAction, + isAutomation, + comparator: condition.expr.comparator, + ); + functions.add(conditionFunction); } } return functions; } - ///// END of get fucntion for once device based on the CODE ***** /////// - /// - /// - /// + SceneStaticFunction _mapExecutorPropertyToSceneFunction( + Action action, + bool isAutomation, { + String? comparator, + }) { + final executorProperty = action.executorProperty; + + final Map + functionMap = { + 'sensitivity': _createSensitivityFunction, + 'normal_open_switch': _createNormalOpenSwitchFunction, + 'unlock_fingerprint': _createUnlockFingerprintFunction, + 'unlock_password': _createUnlockPasswordFunction, + 'unlock_card': _createUnlockCardFunction, + 'alarm_lock': _createAlarmLockFunction, + 'unlock_request': _createUnlockRequestFunction, + 'residual_electricity': _createResidualElectricityFunction, + 'reverse_lock': _createReverseLockFunction, + 'unlock_app': _createUnlockAppFunction, + 'hijack': _createHijackFunction, + 'doorbell': _createDoorbellFunction, + 'unlock_temporary': _createUnlockTemporaryFunction, + 'far_detection': _createFarDetectionFunction, + 'motion_sensitivity_value': _createMotionSensitivityFunction, + 'motionless_sensitivity': _createMotionlessSensitivityFunction, + 'indicator': _createIndicatorFunction, + 'presence_time': _createPresenceTimeFunction, + 'presence_state': _createPresenceStateFunction, + 'dis_current': _createDisCurrentFunction, + 'illuminance_value': _createIlluminanceValueFunction, + 'checking_result': _createCheckingResultFunction, + 'switch': _createSwitchFunction, + 'temp_set': _createTempSetFunction, + 'temp_current': _createTempCurrentFunction, + 'mode': _createModeFunction, + 'level': _createLevelFunction, + 'child_lock': _createChildLockFunction, + 'switch_1': _createSwitch1Function, + 'switch_2': _createSwitch2Function, + 'switch_3': _createSwitch3Function, + 'countdown_1': _createCountdown1Function, + 'countdown_2': _createCountdown2Function, + 'countdown_3': _createCountdown3Function, + 'switch_alarm_sound': _createSwitchAlarmSoundFunction, + 'master_state': _createMasterStateFunction, + 'factory_reset': _createFactoryResetFunction, + }; + + final functionCode = executorProperty.functionCode ?? ''; + final createFunction = functionMap[functionCode]; + if (createFunction != null) { + return createFunction(action, isAutomation, comparator); + } else { + throw ArgumentError('Unsupported function code: $functionCode'); + } + } + + SceneStaticFunction _createSceneFunction( + Action action, + String deviceName, + String icon, + String operationName, + OperationDialogType operationDialogType, + List operationalValues, + bool isAutomation, [ + String? comparator, + ]) { + final functionValue = action.executorProperty.functionValue; + return SceneStaticFunction( + deviceId: action.entityId, + deviceName: deviceName, + deviceIcon: icon, + icon: icon, + operationName: operationName, + functionValue: functionValue, + code: action.executorProperty.functionCode ?? '', + operationDialogType: operationDialogType, + operationalValues: operationalValues, + comparator: comparator ?? '==', + ); + } + + SceneStaticFunction _createSensitivityFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Presence Sensor', + Assets.assetsIconsSensors, + 'Sensitivity', + isAutomation + ? OperationDialogType.integerSteps + : OperationDialogType.listOfOptions, + isAutomation ? _createIntegerStepsOptions() : _createSensitivityOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createNormalOpenSwitchFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Set Door lock Normal Open', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + List _createIntegerStepsOptions() { + return [ + SceneOperationalValue( + icon: '', + value: 0.0, + description: "CM", + minValue: 1, + maxValue: 600, + stepValue: 1, + ), + ]; + } + + List _createSensitivityOptions() { + return List.generate( + 10, + (index) => SceneOperationalValue( + icon: Assets.assetsSensitivityOperationIcon, + value: index + 1, + description: (index + 1).toString(), + ), + ); + } + + List _createOnOffOptions() { + return [ + SceneOperationalValue( + icon: Assets.assetsAcPower, + description: "ON", + value: true, + ), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "OFF", + value: false, + ), + ]; + } + + SceneStaticFunction _createUnlockFingerprintFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Fingerprint Unlock', + OperationDialogType.integerSteps, + _createFingerprintUnlockOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createUnlockPasswordFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Password Unlock', + OperationDialogType.integerSteps, + _createPasswordUnlockOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createUnlockCardFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Card Unlock', + OperationDialogType.integerSteps, + _createCardUnlockOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createAlarmLockFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Lock Alarm', + OperationDialogType.listOfOptions, + _createLockAlarmOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createUnlockRequestFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Remote Unlock Request', + OperationDialogType.integerSteps, + _createUnlockRequestOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createResidualElectricityFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Residual Electricity', + OperationDialogType.integerSteps, + _createResidualElectricityOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createReverseLockFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Double Lock', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createUnlockAppFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Remote Unlock Via App', + OperationDialogType.integerSteps, + _createUnlockAppOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createHijackFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Hijack Alarm', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createDoorbellFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Doorbell', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createUnlockTemporaryFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'WIFI LOCK PRO', + Assets.assetsIconsDoorLock, + 'Temporary Password Unlock', + OperationDialogType.integerSteps, + _createTemporaryPasswordUnlockOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createFarDetectionFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Human Presence Sensor', + Assets.assetsIconsSensors, + 'Far Detection', + OperationDialogType.listOfOptions, + _createFarDetectionOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createMotionSensitivityFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Human Presence Sensor', + Assets.assetsIconsSensors, + 'Motion Detection Sensitivity', + OperationDialogType.listOfOptions, + _createSensitivityOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createMotionlessSensitivityFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Human Presence Sensor', + Assets.assetsIconsSensors, + 'Motionless Detection Sensitivity', + OperationDialogType.listOfOptions, + _createSensitivityOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createIndicatorFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Human Presence Sensor', + Assets.assetsIconsSensors, + 'Indicator', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createPresenceTimeFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Human Presence Sensor', + Assets.assetsIconsSensors, + 'Nobody Time', + OperationDialogType.countdown, + _createCountdownOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createPresenceStateFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Human Presence Sensor', + Assets.assetsIconsSensors, + 'Presence State', + OperationDialogType.listOfOptions, + _createPresenceStateOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createDisCurrentFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Human Presence Sensor', + Assets.assetsIconsSensors, + 'Current Distance', + isAutomation + ? OperationDialogType.integerSteps + : OperationDialogType.countdown, + _createCurrentDistanceOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createIlluminanceValueFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Human Presence Sensor', + Assets.assetsIconsSensors, + 'Illuminance Value', + OperationDialogType.integerSteps, + _createIlluminanceValueOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createCheckingResultFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Human Presence Sensor', + Assets.assetsIconsSensors, + 'Self-Test Result', + OperationDialogType.listOfOptions, + _createSelfTestResultOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createSwitchFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Smart AC Thermostat - Grey - Model A', + Assets.assetsIconsAC, + 'Power', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createTempSetFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Smart AC Thermostat - Grey - Model A', + Assets.assetsIconsAC, + 'Set Temperature', + isAutomation + ? OperationDialogType.integerSteps + : OperationDialogType.temperature, + isAutomation + ? _createAutomationTemperatureOptions() + : _createTemperatureOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createTempCurrentFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Smart AC Thermostat - Grey - Model A', + Assets.assetsIconsAC, + 'Current Temperature', + OperationDialogType.integerSteps, + _createCurrentTemperatureOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createModeFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Smart AC Thermostat - Grey - Model A', + Assets.assetsIconsAC, + 'Mode', + OperationDialogType.listOfOptions, + _createAcModeOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createLevelFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Smart AC Thermostat - Grey - Model A', + Assets.assetsIconsAC, + 'Fan Speed', + OperationDialogType.listOfOptions, + _createFanSpeedOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createChildLockFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Smart AC Thermostat - Grey - Model A', + Assets.assetsIconsAC, + 'Child Lock', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createSwitch1Function( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + '3 Gang Button Switch L-L', + Assets.assetsIcons3GangSwitch, + 'Light 1 Switch', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createSwitch2Function( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + '3 Gang Button Switch L-L', + Assets.assetsIcons3GangSwitch, + 'Light 2 Switch', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createSwitch3Function( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + '3 Gang Button Switch L-L', + Assets.assetsIcons3GangSwitch, + 'Light 3 Switch', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createCountdown1Function( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + '3 Gang Button Switch L-L', + Assets.assetsIcons3GangSwitch, + 'Light 1 CountDown', + isAutomation + ? OperationDialogType.integerSteps + : OperationDialogType.countdown, + isAutomation + ? _createAutomationCountDownOptions() + : _createCountdownOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createCountdown2Function( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + '3 Gang Button Switch L-L', + Assets.assetsIcons3GangSwitch, + 'Light 2 CountDown', + isAutomation + ? OperationDialogType.integerSteps + : OperationDialogType.countdown, + isAutomation + ? _createAutomationCountDownOptions() + : _createCountdownOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createCountdown3Function( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + '3 Gang Button Switch L-L', + Assets.assetsIcons3GangSwitch, + 'Light 3 CountDown', + isAutomation + ? OperationDialogType.integerSteps + : OperationDialogType.countdown, + isAutomation + ? _createAutomationCountDownOptions() + : _createCountdownOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createSwitchAlarmSoundFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Gateway', + Assets.assetsIconsGateway, + 'Switch Alarm Sound', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createMasterStateFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Gateway', + Assets.assetsIconsGateway, + 'Master State', + OperationDialogType.listOfOptions, + _createMasterStateOptions(), + isAutomation, + comparator, + ); + } + + SceneStaticFunction _createFactoryResetFunction( + Action action, bool isAutomation, String? comparator) { + return _createSceneFunction( + action, + 'Gateway', + Assets.assetsIconsGateway, + 'Factory Reset', + OperationDialogType.onOff, + _createOnOffOptions(), + isAutomation, + comparator, + ); + } + + List _createFingerprintUnlockOptions() { + return [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0.0, + maxValue: 999, + stepValue: 1.0, + ), + ]; + } + + List _createPasswordUnlockOptions() { + return [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0.0, + maxValue: 999, + stepValue: 1.0, + ), + ]; + } + + List _createCardUnlockOptions() { + return [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0.0, + maxValue: 999, + stepValue: 1.0, + ), + ]; + } + + List _createLockAlarmOptions() { + return [ + SceneOperationalValue( + icon: Assets.assetsFingerprintUnlock, + description: "Fingerprint Mismatch", + value: 0, + ), + ]; + } + + List _createUnlockRequestOptions() { + return [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0, + maxValue: 90, + stepValue: 1, + ), + ]; + } + + List _createResidualElectricityOptions() { + return [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0, + maxValue: 100, + stepValue: 1, + ), + ]; + } + + List _createUnlockAppOptions() { + return [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0, + maxValue: 999, + stepValue: 1, + ), + ]; + } + + List _createTemporaryPasswordUnlockOptions() { + return [ + SceneOperationalValue( + icon: '', + description: "", + value: 0.0, + minValue: 0, + maxValue: 999, + stepValue: 1, + ), + ]; + } + + List _createPresenceStateOptions() { + return [ + SceneOperationalValue( + icon: Assets.assetsIconsPresenceSensorAssetsEmpty, + value: 'none', + description: 'None', + ), + SceneOperationalValue( + icon: Assets.assetsPresence, + value: 'presence', + description: 'Presence', + ), + ]; + } + + List _createCurrentDistanceOptions() { + return [ + SceneOperationalValue( + icon: '', + value: 0.0, + description: "CM", + minValue: 1, + maxValue: 600, + stepValue: 1, + ), + ]; + } + + List _createIlluminanceValueOptions() { + return [ + SceneOperationalValue( + icon: '', + value: 0.0, + description: "Lux", + minValue: 0, + maxValue: 10000, + stepValue: 1, + ), + ]; + } + + List _createSelfTestResultOptions() { + return [ + SceneOperationalValue( + icon: Assets.assetsSelfTestResult, + value: 'check_success', + description: 'Self Testing Success', + ), + ]; + } + + List _createAutomationTemperatureOptions() { + return [ + SceneOperationalValue( + icon: Assets.assetsCelsiusDegrees, + value: 0.0, + description: '°C', + minValue: 20, + maxValue: 30, + stepValue: 0.5, + ), + ]; + } + + List _createAutomationCountDownOptions() { + return [ + SceneOperationalValue( + icon: '', + description: "sec", + value: 0.0, + minValue: 0, + maxValue: 43200, + stepValue: 1, + ), + ]; + } + + List _createFarDetectionOptions() { + final distances = [75, 150, 225, 300, 375, 450, 525, 600]; + return distances + .map( + (distance) => SceneOperationalValue( + icon: Assets.assetsFarDetectionFunction, + value: distance, + description: '${distance}cm', + iconValue: distance.toString(), + ), + ) + .toList(); + } + + List _createTemperatureOptions() { + return [ + SceneOperationalValue( + icon: Assets.assetsCelsiusDegrees, + value: 0, + description: 'COOL TO', + ), + ]; + } + + List _createMasterStateOptions() { + return [ + SceneOperationalValue( + icon: Assets.assetsAcPower, + description: "Alarm", + value: 'alarm', + ), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "Normal", + value: 'normal', + ), + ]; + } + + List _createAcModeOptions() { + return [ + SceneOperationalValue( + icon: Assets.assetsAcCooling, + description: AcValuesEnums.Cooling.name, + value: TempModes.cold.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcHeating, + description: AcValuesEnums.Heating.name, + value: TempModes.hot.name, + ), + SceneOperationalValue( + icon: Assets.assetsFanSpeed, + description: AcValuesEnums.Ventilation.name, + value: TempModes.wind.name, + ), + ]; + } + + List _createFanSpeedOptions() { + return [ + SceneOperationalValue( + icon: Assets.assetsAcFanLow, + description: ValueACRange.LOW.name, + value: FanSpeeds.low.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcFanMiddle, + description: ValueACRange.MIDDLE.name, + value: FanSpeeds.middle.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcFanHigh, + description: ValueACRange.HIGH.name, + value: FanSpeeds.high.name, + ), + SceneOperationalValue( + icon: Assets.assetsAcFanAuto, + description: ValueACRange.AUTO.name, + value: FanSpeeds.auto.name, + ), + ]; + } + + List _createCountdownOptions() { + return [ + SceneOperationalValue( + icon: '', + value: 0, + ), + ]; + } List getOperationsForOneFunction({ required String deviceId, required SceneStaticFunction taskItem, + required bool isAutomation, }) { - List functions = []; if (deviceId.contains('delay')) { - functions.add( + return [ SceneStaticFunction( deviceId: taskItem.deviceId, deviceName: 'delay', @@ -1292,6 +1157,7 @@ mixin SceneOperationsDataHelper { operationName: 'delay', functionValue: taskItem.functionValue, code: '', + operationDialogType: OperationDialogType.delay, operationalValues: [ SceneOperationalValue( icon: '', @@ -1300,588 +1166,35 @@ mixin SceneOperationsDataHelper { ), ], ), - ); - return functions; - } - switch (taskItem.code) { - case 'sensitivity': - functions.add( - SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsSensitivityFunction, - operationName: 'Sensitivity', - code: 'sensitivity', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 1, - description: 1.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 2, - description: 2.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 3, - description: 3.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 4, - description: 4.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 5, - description: 5.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 6, - description: 6.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 7, - description: 7.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 8, - description: 8.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 9, - description: 9.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 10, - description: 10.toString(), - ), - ], - ), - ); - break; - case 'normal_open_switch': - functions.add( - SceneStaticFunction( - deviceId: deviceId, - deviceName: 'WIFI LOCK PRO', - deviceIcon: Assets.assetsIconsDoorLock, - icon: Assets.assetsIconsDoorLock, - operationName: 'Set Door lock Normal Open', - functionValue: taskItem.functionValue, - code: 'normal_open_switch', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false, - ), - ], - ), - ); - break; - case 'far_detection': - functions.add( - SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Human Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsFarDetection, - operationName: 'Far Detection', - functionValue: taskItem.functionValue, - code: 'far_detection', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 75, - description: '75cm', - iconValue: '75', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 150, - description: '150cm', - iconValue: '150', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 225, - description: '225cm', - iconValue: '225', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 300, - description: '300cm', - iconValue: '300', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 375, - description: '375cm', - iconValue: '375', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 450, - description: '450cm', - iconValue: '450', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 525, - description: '525cm', - iconValue: '525', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 600, - description: '600cm', - iconValue: '600', - ), - ], - ), - ); - break; - case 'motion_sensitivity_value': - functions.add( - SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Human Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsMotionDetection, - operationName: 'Motion Detection Sensitivity', - functionValue: taskItem.functionValue, - code: 'motion_sensitivity_value', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 1, - description: 1.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 2, - description: 2.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 3, - description: 3.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 4, - description: 4.toString(), - ), - SceneOperationalValue( - icon: Assets.assetsSensitivityOperationIcon, - value: 5, - description: 5.toString(), - ), - ], - ), - ); - break; - case 'motionless_sensitivity': - functions.add( - SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Human Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsMotionlessDetection, - operationName: 'Motionless Detection Sensitivity', - functionValue: taskItem.functionValue, - code: 'motion_sensitivity_value', - operationalValues: [ - SceneOperationalValue( - iconValue: '1', - icon: Assets.assetsFarDetectionFunction, - value: 1, - description: '1', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 2, - description: '2', - iconValue: '2', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 3, - description: '3', - iconValue: '3', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 4, - description: '4', - iconValue: '4', - ), - SceneOperationalValue( - icon: Assets.assetsFarDetectionFunction, - value: 5, - description: '5', - iconValue: '5', - ), - ], - ), - ); - break; - case 'indicator': - functions.add( - SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Human Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsIndicator, - operationName: 'Indicator', - functionValue: taskItem.functionValue, - code: 'indicator', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false, - ), - ], - ), - ); - break; - case 'presence_time': - functions.add( - SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Human Presence Sensor', - deviceIcon: Assets.assetsIconsSensors, - icon: Assets.assetsNobodyTime, - operationName: 'Nobody Time', - functionValue: taskItem.functionValue, - code: 'presence_time', - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - ), - ); - break; - case 'switch_alarm_sound': - functions.add( - SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Multi-Mode Gateway Z-W-B', - deviceIcon: Assets.assetsIconsGateway, - icon: Assets.assetsSwitchAlarmSound, - operationName: 'Switch Alarm Sound', - functionValue: taskItem.functionValue, - code: 'switch_alarm_sound', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, - description: "ON", - value: true, - ), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false), - ], - ), - ); - break; - case 'master_state': - functions.add( - SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Multi-Mode Gateway Z-W-B', - deviceIcon: Assets.assetsIconsGateway, - icon: Assets.assetsMasterState, - operationName: 'Master State', - functionValue: taskItem.functionValue, - code: 'master_state', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, - description: "Alarm", - value: 'alarm', - ), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "Normal", - value: 'normal', - ), - ], - ), - ); - break; - case 'factory_reset': - functions.add( - SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Multi-Mode Gateway Z-W-B', - deviceIcon: Assets.assetsIconsGateway, - icon: Assets.assetsFactoryReset, - operationName: 'Reset Factory', - functionValue: taskItem.functionValue, - code: 'factory_reset', - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSceneRefresh, - description: "ON", - value: true), - SceneOperationalValue( - icon: Assets.assetsResetOff, - description: "OFF", - value: false, - ), - ], - ), - ); - break; - case 'switch_1': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsAcPower, - operationName: 'Light 1 Switch', - code: 'switch_1', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, - description: "ON", - value: true, - ), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false), - ], - )); - break; - case 'switch_2': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsAcPower, - operationName: 'Light 2 Switch', - code: 'switch_2', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false), - ], - )); - break; - case 'switch_3': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsAcPower, - operationName: 'Light 3 Switch', - code: 'switch_3', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, description: "ON", value: true), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false), - ], - )); - break; - case 'countdown_1': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsLightCountdown, - operationName: 'Light 1 CountDown', - code: 'countdown_1', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - )); - break; - case 'countdown_2': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsLightCountdown, - operationName: 'Light 2 CountDown', - code: 'countdown_2', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - )); - break; - case 'countdown_3': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: '3 Gang Button Switch L-L', - deviceIcon: Assets.assetsIcons3GangSwitch, - icon: Assets.assetsLightCountdown, - operationName: 'Light 3 CountDown', - code: 'countdown_3', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue(icon: '', value: 0), - ], - )); - break; - case 'switch': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Smart AC Thermostat - Grey - Model A', - deviceIcon: Assets.assetsIconsAC, - icon: Assets.assetsAcPower, - operationName: 'Power', - code: 'switch', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcPower, - description: "ON", - value: true, - ), - SceneOperationalValue( - icon: Assets.assetsAcPowerOFF, - description: "OFF", - value: false, - ), - ], - )); - break; - case 'temp_set': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Smart AC Thermostat - Grey - Model A', - deviceIcon: Assets.assetsIconsAC, - icon: Assets.assetsTempreture, - operationName: 'Set Temperature', - code: 'temp_set', - functionValue: taskItem.functionValue != null - ? ((taskItem.functionValue / 10) as double).toInt() - : null, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsCelsiusDegrees, - value: 0, - description: 'COOL TO', - ), - ], - )); - break; - case 'mode': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Smart AC Thermostat - Grey - Model A', - deviceIcon: Assets.assetsIconsAC, - icon: Assets.assetsFreezing, - operationName: 'Mode', - code: 'mode', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcCooling, - description: AcValuesEnums.Cooling.name, - value: TempModes.cold.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcHeating, - description: AcValuesEnums.Heating.name, - value: TempModes.hot.name, - ), - SceneOperationalValue( - icon: Assets.assetsFanSpeed, - description: AcValuesEnums.Ventilation.name, - value: TempModes.wind.name, - ), - ], - )); - break; - case 'level': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Smart AC Thermostat - Grey - Model A', - deviceIcon: Assets.assetsIconsAC, - icon: Assets.assetsFanSpeed, - operationName: 'Fan Speed', - code: 'level', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsAcFanLow, - description: ValueACRange.LOW.name, - value: FanSpeeds.low.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcFanMiddle, - description: ValueACRange.MIDDLE.name, - value: FanSpeeds.middle.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcFanHigh, - description: ValueACRange.HIGH.name, - value: FanSpeeds.high.name, - ), - SceneOperationalValue( - icon: Assets.assetsAcFanAuto, - description: ValueACRange.AUTO.name, - value: FanSpeeds.auto.name, - ), - ], - )); - break; - case 'child_lock': - functions.add(SceneStaticFunction( - deviceId: deviceId, - deviceName: 'Smart AC Thermostat - Grey - Model A', - deviceIcon: Assets.assetsIconsAC, - icon: Assets.assetsChildLock, - operationName: 'Child Lock', - code: 'child_lock', - functionValue: taskItem.functionValue, - operationalValues: [ - SceneOperationalValue( - icon: Assets.assetsSceneChildLock, - description: 'Lock', - value: true, - ), - SceneOperationalValue( - icon: Assets.assetsSceneChildUnlock, - description: 'Unlock', - value: false, - ), - ], - )); - break; - default: + ]; } - return functions; + return [ + _mapExecutorPropertyToSceneFunction( + Action( + entityId: deviceId, + executorProperty: ExecutorProperty( + functionCode: taskItem.code, + functionValue: taskItem.functionValue, + ), + actionExecutor: '', + ), + isAutomation, + comparator: taskItem.comparator, + ), + ]; + } + + List _createCurrentTemperatureOptions() { + return [ + SceneOperationalValue( + icon: Assets.assetsCelsiusDegrees, + value: 0.0, + description: '°C', + minValue: -9.9, + maxValue: 99.9, + stepValue: 0.1, + ), + ]; } } diff --git a/lib/features/scene/model/create_automation_model.dart b/lib/features/scene/model/create_automation_model.dart new file mode 100644 index 0000000..a349dc2 --- /dev/null +++ b/lib/features/scene/model/create_automation_model.dart @@ -0,0 +1,172 @@ +import 'dart:convert'; + +import 'package:syncrow_app/features/scene/model/create_scene_model.dart'; + +class CreateAutomationModel { + String unitUuid; + String automationName; + String decisionExpr; + EffectiveTime effectiveTime; + List conditions; + List actions; + + CreateAutomationModel({ + required this.unitUuid, + required this.automationName, + required this.decisionExpr, + required this.effectiveTime, + required this.conditions, + required this.actions, + }); + + CreateAutomationModel copyWith({ + String? unitUuid, + String? automationName, + String? decisionExpr, + EffectiveTime? effectiveTime, + List? conditions, + List? actions, + }) { + return CreateAutomationModel( + unitUuid: unitUuid ?? this.unitUuid, + automationName: automationName ?? this.automationName, + decisionExpr: decisionExpr ?? this.decisionExpr, + effectiveTime: effectiveTime ?? this.effectiveTime, + conditions: conditions ?? this.conditions, + actions: actions ?? this.actions, + ); + } + + Map toMap([String? automationId]) { + return { + if (automationId == null) 'unitUuid': unitUuid, + 'automationName': automationName, + 'decisionExpr': decisionExpr, + 'effectiveTime': effectiveTime.toMap(), + 'conditions': conditions.map((x) => x.toMap()).toList(), + 'actions': actions.map((x) => x.toMap()).toList(), + }; + } + + factory CreateAutomationModel.fromMap(Map map) { + return CreateAutomationModel( + unitUuid: map['unitUuid'] ?? '', + automationName: map['automationName'] ?? '', + decisionExpr: map['decisionExpr'] ?? '', + effectiveTime: EffectiveTime.fromMap(map['effectiveTime']), + conditions: List.from( + map['conditions']?.map((x) => CreateCondition.fromMap(x))), + actions: List.from( + map['actions']?.map((x) => CreateSceneAction.fromMap(x))), + ); + } + + String toJson([String? automationId]) => json.encode(toMap(automationId)); + + factory CreateAutomationModel.fromJson(String source) => + CreateAutomationModel.fromMap(json.decode(source)); + + @override + String toString() { + return 'CreateAutomationModel(unitUuid: $unitUuid, automationName: $automationName, decisionExpr: $decisionExpr, effectiveTime: $effectiveTime, conditions: $conditions, actions: $actions)'; + } +} + +class EffectiveTime { + String start; + String end; + String loops; + + EffectiveTime({ + required this.start, + required this.end, + required this.loops, + }); + + Map toMap() { + return { + 'start': start, + 'end': end, + 'loops': loops, + }; + } + + factory EffectiveTime.fromMap(Map map) { + return EffectiveTime( + start: map['start'] ?? '', + end: map['end'] ?? '', + loops: map['loops'] ?? '', + ); + } + + @override + String toString() => 'EffectiveTime(start: $start, end: $end, loops: $loops)'; +} + +class CreateCondition { + int code; + String entityId; + String entityType; + ConditionExpr expr; + + CreateCondition({ + required this.code, + required this.entityId, + required this.entityType, + required this.expr, + }); + + Map toMap() { + return { + 'code': code, + 'entityId': entityId, + 'entityType': entityType, + 'expr': expr.toMap(), + }; + } + + factory CreateCondition.fromMap(Map map) { + return CreateCondition( + code: map['code'] ?? 0, + entityId: map['entityId'] ?? '', + entityType: map['entityType'] ?? '', + expr: ConditionExpr.fromMap(map['expr']), + ); + } + + @override + String toString() => + 'CreateCondition(code: $code, entityId: $entityId, entityType: $entityType, expr: $expr)'; +} + +class ConditionExpr { + String statusCode; + String comparator; + dynamic statusValue; + + ConditionExpr({ + required this.statusCode, + required this.comparator, + required this.statusValue, + }); + + Map toMap() { + return { + 'statusCode': statusCode, + 'comparator': comparator, + 'statusValue': statusValue, + }; + } + + factory ConditionExpr.fromMap(Map map) { + return ConditionExpr( + statusCode: map['statusCode'] ?? '', + comparator: map['comparator'] ?? '', + statusValue: map['statusValue'], + ); + } + + @override + String toString() => + 'ConditionExpr(statusCode: $statusCode, comparator: $comparator, statusValue: $statusValue)'; +} diff --git a/lib/features/scene/model/create_scene_model.dart b/lib/features/scene/model/create_scene_model.dart index 53dc344..0d19345 100644 --- a/lib/features/scene/model/create_scene_model.dart +++ b/lib/features/scene/model/create_scene_model.dart @@ -3,29 +3,11 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; class CreateSceneModel { - /* -{ - "unitUuid": "string", - "sceneName": "string", - "decisionExpr": "string", - "actions": [ - { - "entityId": "string", - "actionExecutor": "string", - "executorProperty": { - "functionCode": "string", - "functionValue": {}, - "delaySeconds": 0 - } - } - ] -} - */ - String unitUuid; String sceneName; String decisionExpr; List actions; + CreateSceneModel({ required this.unitUuid, required this.sceneName, @@ -99,7 +81,8 @@ class CreateSceneModel { class CreateSceneAction { String entityId; String actionExecutor; - CreateSceneExecutorProperty executorProperty; + CreateSceneExecutorProperty? executorProperty; + CreateSceneAction({ required this.entityId, required this.actionExecutor, @@ -122,7 +105,8 @@ class CreateSceneAction { return { 'entityId': entityId, 'actionExecutor': actionExecutor, - 'executorProperty': executorProperty.toMap(), + if (executorProperty != null) + 'executorProperty': executorProperty?.toMap(actionExecutor), }; } @@ -163,6 +147,7 @@ class CreateSceneExecutorProperty { String functionCode; dynamic functionValue; int delaySeconds; + CreateSceneExecutorProperty({ required this.functionCode, required this.functionValue, @@ -181,12 +166,14 @@ class CreateSceneExecutorProperty { ); } - Map toMap() { - return { - if (functionCode.isNotEmpty == true) 'functionCode': functionCode, - if (functionValue != '') 'functionValue': functionValue, - if (delaySeconds > 0) 'delaySeconds': delaySeconds, - }; + Map toMap(String actionExecutor) { + final map = {}; + if (functionCode.isNotEmpty) map['functionCode'] = functionCode; + if (functionValue != null) map['functionValue'] = functionValue; + if (actionExecutor == 'delay' && delaySeconds > 0) { + map['delaySeconds'] = delaySeconds; + } + return map; } factory CreateSceneExecutorProperty.fromMap(Map map) { @@ -197,7 +184,7 @@ class CreateSceneExecutorProperty { ); } - String toJson() => json.encode(toMap()); + String toJson(String actionExecutor) => json.encode(toMap(actionExecutor)); factory CreateSceneExecutorProperty.fromJson(String source) => CreateSceneExecutorProperty.fromMap(json.decode(source)); diff --git a/lib/features/scene/model/scene_details_model.dart b/lib/features/scene/model/scene_details_model.dart index 5ed5d1c..3c40476 100644 --- a/lib/features/scene/model/scene_details_model.dart +++ b/lib/features/scene/model/scene_details_model.dart @@ -6,6 +6,9 @@ class SceneDetailsModel { final String status; final String type; final List actions; + final List? conditions; + final String? decisionExpr; + final EffectiveTime? effectiveTime; SceneDetailsModel({ required this.id, @@ -13,6 +16,9 @@ class SceneDetailsModel { required this.status, required this.type, required this.actions, + this.conditions, + this.decisionExpr, + this.effectiveTime, }); factory SceneDetailsModel.fromRawJson(String str) => @@ -26,8 +32,20 @@ class SceneDetailsModel { name: json["name"], status: json["status"], type: json["type"], - actions: - List.from(json["actions"].map((x) => Action.fromJson(x))), + actions: (json["actions"] as List) + .map((x) => Action.fromJson(x)) + .where((x) => x != null) + .toList() + .cast(), + conditions: json["conditions"] != null + ? (json["conditions"] as List) + .map((x) => Condition.fromJson(x)) + .toList() + : null, + decisionExpr: json["decisionExpr"], + effectiveTime: json["effectiveTime"] != null + ? EffectiveTime.fromJson(json["effectiveTime"]) + : null, ); Map toJson() => { @@ -36,6 +54,11 @@ class SceneDetailsModel { "status": status, "type": type, "actions": List.from(actions.map((x) => x.toJson())), + "conditions": conditions != null + ? List.from(conditions!.map((x) => x.toJson())) + : null, + "decisionExpr": decisionExpr, + "effectiveTime": effectiveTime?.toJson(), }; } @@ -50,15 +73,19 @@ class Action { required this.executorProperty, }); - factory Action.fromRawJson(String str) => Action.fromJson(json.decode(str)); - String toRawJson() => json.encode(toJson()); - factory Action.fromJson(Map json) => Action( - actionExecutor: json["actionExecutor"], - entityId: json["entityId"], - executorProperty: ExecutorProperty.fromJson(json["executorProperty"]), - ); + static Action? fromJson(Map json) { + if (json["executorProperty"] == null) { + return null; // Return null if executorProperty is not present + } + + return Action( + actionExecutor: json["actionExecutor"], + entityId: json["entityId"], + executorProperty: ExecutorProperty.fromJson(json["executorProperty"]), + ); + } Map toJson() => { "actionExecutor": actionExecutor, @@ -91,3 +118,93 @@ class ExecutorProperty { "delaySeconds": delaySeconds, }; } + +class Condition { + final int code; + final String entityId; + final String entityType; + final Expr expr; + + Condition({ + required this.code, + required this.entityId, + required this.entityType, + required this.expr, + }); + + factory Condition.fromRawJson(String str) => + Condition.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory Condition.fromJson(Map json) => Condition( + code: json["code"], + entityId: json["entityId"], + entityType: json["entityType"], + expr: Expr.fromJson(json["expr"]), + ); + + Map toJson() => { + "code": code, + "entityId": entityId, + "entityType": entityType, + "expr": expr.toJson(), + }; +} + +class Expr { + final String comparator; + final String statusCode; + final dynamic statusValue; + + Expr({ + required this.comparator, + required this.statusCode, + required this.statusValue, + }); + + factory Expr.fromRawJson(String str) => Expr.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory Expr.fromJson(Map json) => Expr( + comparator: json["comparator"], + statusCode: json["statusCode"], + statusValue: json["statusValue"], + ); + + Map toJson() => { + "comparator": comparator, + "statusCode": statusCode, + "statusValue": statusValue, + }; +} + +class EffectiveTime { + final String start; + final String end; + final String loops; + + EffectiveTime({ + required this.start, + required this.end, + required this.loops, + }); + + factory EffectiveTime.fromRawJson(String str) => + EffectiveTime.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory EffectiveTime.fromJson(Map json) => EffectiveTime( + start: json["start"], + end: json["end"], + loops: json["loops"], + ); + + Map toJson() => { + "start": start, + "end": end, + "loops": loops, + }; +} diff --git a/lib/features/scene/model/scene_static_function.dart b/lib/features/scene/model/scene_static_function.dart index 43f7f09..f73e130 100644 --- a/lib/features/scene/model/scene_static_function.dart +++ b/lib/features/scene/model/scene_static_function.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; import 'package:uuid/uuid.dart'; class SceneStaticFunction { @@ -13,6 +14,8 @@ class SceneStaticFunction { final String uniqueCustomId; final dynamic functionValue; final String? deviceIcon; + final OperationDialogType operationDialogType; + final String? comparator; SceneStaticFunction({ required this.icon, @@ -23,6 +26,8 @@ class SceneStaticFunction { required this.operationName, required this.functionValue, this.deviceIcon, + required this.operationDialogType, + this.comparator, }) : uniqueCustomId = const Uuid().v4(); SceneStaticFunction copyWith({ @@ -35,6 +40,8 @@ class SceneStaticFunction { dynamic functionValue, String? deviceIcon, String? deviceName, + OperationDialogType? operationDialogType, + String? comparator, }) { return SceneStaticFunction( icon: icon ?? this.icon, @@ -45,6 +52,8 @@ class SceneStaticFunction { operationName: operationName ?? this.operationName, functionValue: functionValue ?? this.functionValue, deviceIcon: deviceIcon ?? this.deviceIcon, + operationDialogType: operationDialogType ?? this.operationDialogType, + comparator: comparator ?? this.comparator, ); } @@ -57,8 +66,9 @@ class SceneStaticFunction { 'deviceId': deviceId, 'operationName': operationName, 'functionValue': functionValue, - 'deviceIcon': deviceIcon - + 'deviceIcon': deviceIcon, + 'operationDialogType': operationDialogType.name, + 'comparator': comparator }; } @@ -74,6 +84,10 @@ class SceneStaticFunction { operationName: map['operationName'] ?? '', functionValue: map['functionValue'] ?? '', deviceIcon: map['deviceIcon'] ?? '', + operationDialogType: map['operationDialogType'] != null + ? OperationDialogType.values.byName(map['operationDialogType']) + : OperationDialogType.none, + comparator: map['comparator'], ); } @@ -84,7 +98,7 @@ class SceneStaticFunction { @override String toString() { - return 'SceneStaticFunction(icon: $icon, name: $deviceName, code: $code, operationalValues: $operationalValues, deviceId: $deviceId, operationName: $operationName, functionValue: $functionValue, deviceIcon: $deviceIcon)'; + return 'SceneStaticFunction(icon: $icon, name: $deviceName, code: $code, operationalValues: $operationalValues, deviceId: $deviceId, operationName: $operationName, functionValue: $functionValue, deviceIcon: $deviceIcon, operationDialogType: $operationDialogType, comparator: $comparator)'; } @override @@ -98,6 +112,8 @@ class SceneStaticFunction { other.operationName == operationName && other.functionValue == functionValue && other.deviceIcon == deviceIcon && + other.comparator == comparator && + other.operationDialogType == operationDialogType && listEquals(other.operationalValues, operationalValues) && other.deviceId == deviceId; } @@ -111,6 +127,8 @@ class SceneStaticFunction { operationName.hashCode ^ functionValue.hashCode ^ deviceIcon.hashCode ^ + comparator.hashCode ^ + operationDialogType.hashCode ^ operationalValues.hashCode; } } @@ -120,12 +138,18 @@ class SceneOperationalValue { final dynamic value; final String? description; final String? iconValue; + final double? minValue; + final double? maxValue; + final double? stepValue; SceneOperationalValue({ required this.icon, required this.value, this.description, this.iconValue, + this.minValue, + this.maxValue, + this.stepValue, }); SceneOperationalValue copyWith({ @@ -133,12 +157,18 @@ class SceneOperationalValue { dynamic value, String? description, String? iconValue, + double? minValue, + double? maxValue, + double? stepValue, }) { return SceneOperationalValue( icon: icon ?? this.icon, value: value ?? this.value, description: description ?? this.description, iconValue: iconValue ?? this.iconValue, + minValue: minValue ?? this.minValue, + maxValue: maxValue ?? this.maxValue, + stepValue: stepValue ?? this.stepValue, ); } @@ -147,7 +177,10 @@ class SceneOperationalValue { 'icon': icon, 'value': value, 'description': description, - 'iconValue': iconValue + 'iconValue': iconValue, + 'minValue': minValue, + 'maxValue': maxValue, + 'stepValue': stepValue }; } @@ -157,6 +190,9 @@ class SceneOperationalValue { value: map['value'], description: map['description'], iconValue: map['iconValue'] ?? '', + minValue: map['minValue'], + maxValue: map['maxValue'], + stepValue: map['stepValue'], ); } @@ -167,7 +203,7 @@ class SceneOperationalValue { @override String toString() => - 'StaticFunctionOperationHelper(icon: $icon, value: $value, description: $description, iconValue: $iconValue)'; + 'StaticFunctionOperationHelper(icon: $icon, value: $value, description: $description, iconValue: $iconValue, minValue: $minValue, maxValue: $maxValue, stepValue: $stepValue)'; @override bool operator ==(Object other) { @@ -177,6 +213,9 @@ class SceneOperationalValue { other.icon == icon && other.description == description && other.iconValue == iconValue && + other.minValue == minValue && + other.maxValue == maxValue && + other.stepValue == stepValue && other.value == value; } @@ -186,5 +225,8 @@ class SceneOperationalValue { value.hashCode ^ description.hashCode ^ iconValue.hashCode ^ + minValue.hashCode ^ + maxValue.hashCode ^ + stepValue.hashCode ^ description.hashCode; } diff --git a/lib/features/scene/model/scenes_model.dart b/lib/features/scene/model/scenes_model.dart index 5d0b44c..4c0066c 100644 --- a/lib/features/scene/model/scenes_model.dart +++ b/lib/features/scene/model/scenes_model.dart @@ -3,8 +3,8 @@ import 'dart:convert'; class ScenesModel { final String id; final String name; - final Status status; - final Type type; + final String status; + final String type; ScenesModel({ required this.id, @@ -20,35 +20,15 @@ class ScenesModel { factory ScenesModel.fromJson(Map json) => ScenesModel( id: json["id"], - name: json["name"], - status: statusValues.map[json["status"]]!, - type: typeValues.map[json["type"]]!, + name: json["name"] ?? '', + status: json["status"] ?? '', + type: json["type"] ?? '', ); Map toJson() => { "id": id, "name": name, - "status": statusValues.reverse[status], - "type": typeValues.reverse[type], + "status": status, + "type": type, }; } - -enum Status { ENABLE } - -final statusValues = EnumValues({"enable": Status.ENABLE}); - -enum Type { TAP_TO_RUN } - -final typeValues = EnumValues({"tap_to_run": Type.TAP_TO_RUN}); - -class EnumValues { - Map map; - late Map reverseMap; - - EnumValues(this.map); - - Map get reverse { - reverseMap = map.map((k, v) => MapEntry(v, k)); - return reverseMap; - } -} diff --git a/lib/features/scene/model/smart_scene_enable.dart b/lib/features/scene/model/smart_scene_enable.dart new file mode 100644 index 0000000..c855925 --- /dev/null +++ b/lib/features/scene/model/smart_scene_enable.dart @@ -0,0 +1,23 @@ +class SmartSceneEnable { + final String entityId; + final String actionExecutor; + + SmartSceneEnable({ + required this.entityId, + required this.actionExecutor, + }); + + factory SmartSceneEnable.fromJson(Map json) { + return SmartSceneEnable( + entityId: json['entityId'], + actionExecutor: json['actionExecutor'], + ); + } + + Map toJson() { + return { + 'entityId': entityId, + 'actionExecutor': actionExecutor, + }; + } +} diff --git a/lib/features/scene/model/update_automation.dart b/lib/features/scene/model/update_automation.dart new file mode 100644 index 0000000..d15d191 --- /dev/null +++ b/lib/features/scene/model/update_automation.dart @@ -0,0 +1,38 @@ +import 'dart:convert'; + +class AutomationStatusUpdate { + final String unitUuid; + final bool isEnable; + + AutomationStatusUpdate({ + required this.unitUuid, + required this.isEnable, + }); + + factory AutomationStatusUpdate.fromRawJson(String str) => + AutomationStatusUpdate.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory AutomationStatusUpdate.fromJson(Map json) => + AutomationStatusUpdate( + unitUuid: json["unitUuid"], + isEnable: json["isEnable"], + ); + + Map toJson() => { + "unitUuid": unitUuid, + "isEnable": isEnable, + }; + + factory AutomationStatusUpdate.fromMap(Map map) => + AutomationStatusUpdate( + unitUuid: map["unitUuid"], + isEnable: map["isEnable"], + ); + + Map toMap() => { + "unitUuid": unitUuid, + "isEnable": isEnable, + }; +} diff --git a/lib/features/scene/view/create_scene_view.dart b/lib/features/scene/view/create_scene_view.dart index e19d3ea..283f87f 100644 --- a/lib/features/scene/view/create_scene_view.dart +++ b/lib/features/scene/view/create_scene_view.dart @@ -6,10 +6,9 @@ import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments. import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; - import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/navigation/navigate_to_route.dart'; import 'package:syncrow_app/navigation/routing_constants.dart'; - import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; class CreateSceneView extends StatelessWidget { @@ -22,7 +21,7 @@ class CreateSceneView extends StatelessWidget { padding: const EdgeInsets.only(top: 24), leading: IconButton( onPressed: () { - Navigator.pop(context); + navigateToRoute(context, Routes.sceneTasksRoute); }, icon: const Icon( Icons.arrow_back_ios, @@ -48,6 +47,9 @@ class CreateSceneView extends StatelessWidget { ), ); context.read().add(const ClearTaskListEvent()); + context + .read() + .add(const SceneTypeEvent(CreateSceneEnum.tabToRun)); }, ), DefaultContainer( @@ -58,7 +60,22 @@ class CreateSceneView extends StatelessWidget { titleString: StringsManager.whenDeviceStatusChanges, subtitleString: StringsManager.whenUnusualActivityIsDetected, ), - onTap: () {}, + onTap: () { + Navigator.pushNamed( + context, + Routes.sceneControlDevicesRoute, + arguments: SceneSettingsRouteArguments( + sceneType: CreateSceneEnum.deviceStatusChanges.name, + sceneId: '', + sceneName: '', + ), + ); + context + .read() + .add(const ClearTaskListEvent(isAutomation: true)); + context.read().add( + const SceneTypeEvent(CreateSceneEnum.deviceStatusChanges)); + }, ), ], ), diff --git a/lib/features/scene/view/device_functions_view.dart b/lib/features/scene/view/device_functions_view.dart index cd2b781..bb9c48f 100644 --- a/lib/features/scene/view/device_functions_view.dart +++ b/lib/features/scene/view/device_functions_view.dart @@ -1,33 +1,37 @@ import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; +import 'package:syncrow_app/features/scene/helper/scene_logic_helper.dart'; import 'package:syncrow_app/features/scene/helper/scene_operations_data_helper.dart'; import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; -import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_countdown.dart'; -import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_functions_body.dart'; -import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_temperature_body.dart'; import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; import 'package:syncrow_app/features/shared_widgets/light_divider.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/navigation/navigate_to_route.dart'; import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class DeviceFunctionsView extends StatelessWidget - with SceneOperationsDataHelper { - const DeviceFunctionsView({super.key}); + with SceneOperationsDataHelper, SceneLogicHelper { + DeviceFunctionsView({super.key}); @override Widget build(BuildContext context) { /// this whole widget needs a refactor later /// /// static functions based on type - final device = ModalRoute.of(context)?.settings.arguments as DeviceModel; + final device = (ModalRoute.of(context)?.settings.arguments as Map)['device'] + as DeviceModel; + + final isAutomation = (ModalRoute.of(context)?.settings.arguments + as Map)['isAutomationDeviceStatus'] as bool; /// static custom functions based on type /// used for now until later backend fixes @@ -38,6 +42,7 @@ class DeviceFunctionsView extends StatelessWidget functions: device.functions, deviceId: device.uuid ?? '', deviceName: device.name ?? '', + isAutomation: isAutomation, ); } @@ -46,10 +51,10 @@ class DeviceFunctionsView extends StatelessWidget actions: [ TextButton( onPressed: () { - context.read().add(AddTaskEvent()); - Navigator.popUntil(context, (route) { - return route.settings.name == Routes.sceneTasksRoute; - }); + context + .read() + .add(AddTaskEvent(isAutomation: isAutomation)); + navigateToRoute(context, Routes.sceneTasksRoute); }, child: BodyMedium( text: 'Save', @@ -60,6 +65,16 @@ class DeviceFunctionsView extends StatelessWidget ], leading: TextButton( onPressed: () { + final automationSelectedValue = + context.read().automationSelectedValues; + for (var element in device.functions) { + if (automationSelectedValue.containsKey(element.code)) { + context.read().add(RemoveTempTaskByIdEvent( + code: element.code!, isAutomation: true)); + context.read().add(RemoveFromSelectedValueById( + code: element.code!, isAutomation: true)); + } + } final selectedValue = context.read().selectedValues; for (var element in device.functions) { @@ -83,136 +98,180 @@ class DeviceFunctionsView extends StatelessWidget ), leadingWidth: 80, padding: EdgeInsets.zero, - child: Padding( + child: ListView.builder( + shrinkWrap: true, + itemCount: functions.length, padding: const EdgeInsets.only(top: 24.0), - child: ListView.builder( - shrinkWrap: true, - itemCount: functions.length, - padding: EdgeInsets.zero, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - return DefaultContainer( - padding: index == 0 - ? const EdgeInsets.only(top: 8) - : index == functions.length - 1 - ? const EdgeInsets.only(bottom: 8) - : EdgeInsets.zero, - margin: EdgeInsets.zero, - borderRadius: index == 0 - ? const BorderRadius.only( - topLeft: Radius.circular(20), - topRight: Radius.circular(20)) - : index == functions.length - 1 - ? const BorderRadius.only( - bottomLeft: Radius.circular(20), - bottomRight: Radius.circular(20)) - : BorderRadius.zero, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - BlocBuilder( - builder: (context, state) { - return SceneListTile( - iconsSize: 22, - minLeadingWidth: 20, - assetPath: functions[index].icon, - titleString: functions[index].operationName, - trailingWidget: const Row( - mainAxisSize: MainAxisSize.min, - children: [ - /// selected value or the default value - // BodyMedium(text: ), - Icon( - Icons.arrow_forward_ios_rounded, - color: ColorsManager.greyColor, - size: 16, - ), - ], - ), - onPressed: () { - final functionValues = context - .read() - .selectedValues[functions[index].code]; - - context.customAlertDialog( - alertBody: functions[index].code == 'temp_set' - ? AlertDialogTemperatureBody( - taskItem: functions[index], - functionValue: functionValues, - ) - : (functions[index] - .code - .contains('countdown') || - functions[index] - .code - .contains('presence_time')) - ? AlertDialogCountdown( - durationValue: functions[index] - .operationalValues - .first - .value, - function: functions[index], - functionValue: functionValues, - ) - : AlertDialogFunctionsOperationsBody( - taskItem: functions[index], - functionValue: functionValues, - ), - title: functions[index].operationName, - onConfirm: () { - final selectedValue = context - .read() - .selectedValues[functions[index].code]; - if (selectedValue == null) { - return; - } - context - .read() - .add(TempHoldSceneTasksEvent( - deviceControlModel: DeviceControlModel( - deviceId: device.uuid, - code: functions[index].code, - value: selectedValue, - ), - deviceId: device.uuid ?? '', - operation: functions[index].operationName, - icon: device.icon ?? '', - deviceName: device.name ?? '', - uniqueId: functions[index].uniqueCustomId, - )); - Navigator.pop(context); - }, - onDismiss: () { - final tempTaskList = context - .read() - .tempTasksList; - for (var element in tempTaskList) { - if (element.code == functions[index].code) { - context.read().add( - RemoveTempTaskByIdEvent( - code: functions[index].code)); - context.read().add( - RemoveFromSelectedValueById( - code: functions[index].code)); - } - } - Navigator.pop(context); - }, + itemBuilder: (context, index) { + return DefaultContainer( + padding: index == 0 + ? const EdgeInsets.only(top: 8) + : index == functions.length - 1 + ? const EdgeInsets.only(bottom: 8) + : EdgeInsets.zero, + margin: EdgeInsets.zero, + borderRadius: index == 0 + ? const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20)) + : index == functions.length - 1 + ? const BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20)) + : BorderRadius.zero, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + BlocBuilder( + builder: (context, state) { + return SceneListTile( + iconsSize: 22, + minLeadingWidth: 20, + assetPath: functions[index].icon, + titleString: functions[index].operationName, + trailingWidget: const Row( + mainAxisSize: MainAxisSize.min, + children: [ + /// selected value or the default value + // BodyMedium(text: ), + Icon( + Icons.arrow_forward_ios_rounded, + color: ColorsManager.greyColor, + size: 16, + ), + ], + ), + onPressed: () { + if (isAutomation) { + _showAutomationDialog( + context, + functions[index], + device, ); - }, - ); - }, - ), - index != functions.length - 1 - ? SizedBox( - width: context.width * 0.8, - child: const LightDivider()) - : const SizedBox(), - ], - ), - ); - }, - ), + } else { + _showTabToRunDialog( + context, + functions[index], + device, + ); + } + }, + ); + }, + ), + index != functions.length - 1 + ? SizedBox( + width: context.width * 0.8, + child: const LightDivider()) + : const SizedBox(), + ], + ), + ); + }, )); } + + void _showTabToRunDialog( + BuildContext context, + SceneStaticFunction function, + DeviceModel device, + ) { + final functionValues = + context.read().selectedValues[function.code]; + + context.customAlertDialog( + alertBody: getTheCorrectDialogBody( + function, + functionValues, + isAutomation: false, + ), + title: function.operationName, + onConfirm: () { + final selectedValue = + context.read().selectedValues[function.code]; + if (selectedValue == null) { + return; + } + context.read().add(TempHoldSceneTasksEvent( + deviceControlModel: DeviceControlModel( + deviceId: device.uuid, + code: function.code, + value: selectedValue, + ), + deviceId: device.uuid ?? '', + operation: function.operationName, + icon: device.icon ?? '', + deviceName: device.name ?? '', + uniqueId: function.uniqueCustomId, + operationType: function.operationDialogType, + )); + Navigator.pop(context); + }, + onDismiss: () { + final tempTaskList = context.read().tempTasksList; + for (var element in tempTaskList) { + if (element.code == function.code) { + context + .read() + .add(RemoveTempTaskByIdEvent(code: function.code)); + context + .read() + .add(RemoveFromSelectedValueById(code: function.code)); + } + } + Navigator.pop(context); + }, + ); + } + + void _showAutomationDialog( + BuildContext context, SceneStaticFunction function, DeviceModel device) { + final automationFunctionValues = + context.read().automationSelectedValues[function.code]; + + context.customAlertDialog( + alertBody: getTheCorrectDialogBody( + function, + automationFunctionValues, + isAutomation: true, + ), + title: function.operationName, + onConfirm: () { + final automationFunctionValues = context + .read() + .automationSelectedValues[function.code]; + if (automationFunctionValues == null) { + return; + } + context.read().add(TempHoldSceneTasksEvent( + deviceControlModel: DeviceControlModel( + deviceId: device.uuid, + code: function.code, + value: automationFunctionValues, + ), + deviceId: device.uuid ?? '', + operation: function.operationName, + icon: device.icon ?? '', + deviceName: device.name ?? '', + uniqueId: function.uniqueCustomId, + operationType: function.operationDialogType, + isAutomation: true, + )); + Navigator.pop(context); + }, + onDismiss: () { + final automationTempTaskList = + context.read().automationTempTasksList; + for (var element in automationTempTaskList) { + if (element.code == function.code) { + context.read().add(RemoveTempTaskByIdEvent( + code: function.code, isAutomation: true)); + context.read().add(RemoveFromSelectedValueById( + code: function.code, isAutomation: true)); + } + } + Navigator.pop(context); + }, + ); + } } diff --git a/lib/features/scene/view/scene_auto_settings.dart b/lib/features/scene/view/scene_auto_settings.dart new file mode 100644 index 0000000..5bc8d44 --- /dev/null +++ b/lib/features/scene/view/scene_auto_settings.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/scene/view/scene_tasks_view.dart'; +import 'package:syncrow_app/features/scene/widgets/effective_period_setting/effective_period_bottom_sheet.dart'; +import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +class SceneAutoSettings extends StatelessWidget { + const SceneAutoSettings({super.key}); + + @override + Widget build(BuildContext context) { + final sceneSettings = + ModalRoute.of(context)!.settings.arguments as Map? ?? + {}; + final sceneId = sceneSettings['sceneId'] as String? ?? ''; + final isAutomation = sceneSettings['isAutomation'] as bool? ?? false; + final sceneName = sceneSettings['sceneName'] as String? ?? ''; + + return Scaffold( + backgroundColor: ColorsManager.backgroundColor, + appBar: AppBar( + centerTitle: true, + backgroundColor: ColorsManager.backgroundColor, + title: const BodyLarge( + text: "Settings", + fontColor: ColorsManager.secondaryColor, + fontWeight: FontsManager.bold, + ), + ), + body: DefaultContainer( + padding: EdgeInsets.zero, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Visibility( + visible: isAutomation == true, + child: SceneListTile( + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + titleString: "Effective Period", + trailingWidget: const Icon(Icons.arrow_forward_ios_rounded), + onPressed: () { + context.customBottomSheet( + child: const EffectPeriodBottomSheetContent(), + ); + }, + ), + ), + Visibility( + visible: sceneName.isNotEmpty, + child: DeleteBottomSheetContent( + isAutomation: isAutomation, + sceneId: sceneId, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/scene/view/scene_rooms_tabbar.dart b/lib/features/scene/view/scene_rooms_tabbar.dart index 64143b1..a97d668 100644 --- a/lib/features/scene/view/scene_rooms_tabbar.dart +++ b/lib/features/scene/view/scene_rooms_tabbar.dart @@ -8,6 +8,8 @@ import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart import 'package:syncrow_app/features/scene/widgets/scene_devices/scene_devices_body.dart'; import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/navigation/navigate_to_route.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; @@ -16,10 +18,12 @@ class SceneRoomsTabBarDevicesView extends StatefulWidget { const SceneRoomsTabBarDevicesView({super.key}); @override - State createState() => _SceneRoomsTabBarDevicesViewState(); + State createState() => + _SceneRoomsTabBarDevicesViewState(); } -class _SceneRoomsTabBarDevicesViewState extends State +class _SceneRoomsTabBarDevicesViewState + extends State with SingleTickerProviderStateMixin { late final TabController _tabController; List? rooms = []; @@ -41,7 +45,8 @@ class _SceneRoomsTabBarDevicesViewState extends State() - .add(TabChanged(selectedIndex: value, roomId: rooms?[value].id ?? '')); + context.read().add( + TabChanged(selectedIndex: value, roomId: rooms?[value].id ?? '')); return; } } @@ -72,8 +76,7 @@ class _SceneRoomsTabBarDevicesViewState extends State( - listener: (context, state) { - if (state is DeleteSceneSuccess) { - if (state.success) { - Navigator.pop(context); - Navigator.pop(context); - BlocProvider.of(context).add( - LoadScenes(HomeCubit.getInstance().selectedSpace!.id!)); - } - } - }, - builder: (context, state) { - return DefaultContainer( - onTap: () { - context.read().add(DeleteSceneEvent( - sceneId: sceneId, - unitUuid: - HomeCubit.getInstance().selectedSpace!.id!, - )); - }, - child: SceneListTile( - titleString: StringsManager.deleteScene, - leadingWidget: (state is DeleteSceneLoading) - ? const SizedBox( - height: 24, - width: 24, - child: CircularProgressIndicator()) - : SvgPicture.asset( - Assets.assetsDeleteIcon, - color: ColorsManager.red, - ), - )); - }, - ), - ], + padding: EdgeInsets.zero, + child: BlocConsumer( + listener: (context, state) { + if (state is DeleteSceneSuccess) { + if (state.success) { + navigateToRoute(context, Routes.homeRoute); + BlocProvider.of(context).add( + LoadScenes(HomeCubit.getInstance().selectedSpace!.id!)); + BlocProvider.of(context).add( + LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!)); + } + } + }, + builder: (context, state) { + return DefaultContainer( + onTap: () { + context.read().add(DeleteSceneEvent( + sceneId: sceneId, + unitUuid: HomeCubit.getInstance().selectedSpace!.id!, + )); + }, + child: SceneListTile( + padding: const EdgeInsets.symmetric(horizontal: 8), + titleString: isAutomation + ? StringsManager.deleteAutomation + : StringsManager.deleteScene, + leadingWidget: (state is DeleteSceneLoading) + ? const SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator()) + : SvgPicture.asset( + Assets.assetsDeleteIcon, + color: ColorsManager.red, + ), + )); + }, )); } } diff --git a/lib/features/scene/view/scene_view.dart b/lib/features/scene/view/scene_view.dart index 6c09439..ce297ce 100644 --- a/lib/features/scene/view/scene_view.dart +++ b/lib/features/scene/view/scene_view.dart @@ -5,11 +5,13 @@ import 'package:syncrow_app/features/devices/view/widgets/scene_listview.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart'; +import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart'; import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_grid_view.dart'; import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_header.dart'; import 'package:syncrow_app/features/shared_widgets/create_unit.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class SceneView extends StatelessWidget { final bool pageType; @@ -19,19 +21,27 @@ class SceneView extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => SceneBloc() - ..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? '')), + ..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? '')) + ..add(LoadAutomation(HomeCubit.getInstance().selectedSpace?.id ?? '')), child: BlocBuilder( builder: (context, state) { if (state is DeleteSceneSuccess) { if (state.success) { BlocProvider.of(context) .add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!)); + BlocProvider.of(context).add( + LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!)); } } if (state is CreateSceneWithTasks) { if (state.success == true) { BlocProvider.of(context) .add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!)); + BlocProvider.of(context).add( + LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!)); + context + .read() + .add(const SmartSceneClearEvent()); } } return BlocListener( @@ -63,29 +73,90 @@ class SceneView extends StatelessWidget { ); } if (state is SceneLoaded) { - if (state.scenes.isNotEmpty) { - return pageType == false - ? Expanded( - child: SceneGrid( - scenes: state.scenes, - loadingSceneId: state.loadingSceneId, - ), - ) - : Expanded( + final scenes = state.scenes; + final automationList = state.automationList; + + return pageType + ? Expanded( child: SceneListview( - scenes: state.scenes, - loadingSceneId: state.loadingSceneId, - ) - ); - } else { - return const Expanded( - child: Center( - child: BodyMedium( - text: 'No scenes have been added yet', - ), - ), - ); - } + scenes: scenes, + loadingSceneId: state.loadingSceneId, + )) + : Expanded( + child: ListView( + children: [ + Theme( + data: ThemeData().copyWith( + dividerColor: Colors.transparent), + child: ExpansionTile( + tilePadding: + const EdgeInsets.symmetric( + horizontal: 6), + initiallyExpanded: true, + iconColor: ColorsManager.grayColor, + title: const BodyMedium( + text: 'Tap to run routines'), + children: [ + scenes.isNotEmpty + ? SceneGrid( + scenes: scenes, + loadingSceneId: + state.loadingSceneId, + disablePlayButton: false, + loadingStates: state + .loadingStates, // Add this line + ) + : const Center( + child: BodyMedium( + text: + 'No scenes have been added yet', + ), + ), + const SizedBox( + height: 10, + ), + ], + ), + ), + Theme( + data: ThemeData().copyWith( + dividerColor: Colors.transparent), + child: ExpansionTile( + initiallyExpanded: true, + iconColor: ColorsManager.grayColor, + tilePadding: + const EdgeInsets.symmetric( + horizontal: 6), + title: const BodyMedium( + text: 'Automation'), + children: [ + automationList.isNotEmpty + ? SceneGrid( + scenes: automationList, + loadingSceneId: + state.loadingSceneId, + disablePlayButton: true, + loadingStates: state + .loadingStates, // Add this line + ) + : const Center( + child: BodyMedium( + text: + 'No automations have been added yet', + ), + ), + const SizedBox( + height: 10, + ), + ], + ), + ), + const SizedBox( + height: 15, + ), + ], + ), + ); } return const SizedBox(); }, diff --git a/lib/features/scene/view/smart_automation_select_route.dart b/lib/features/scene/view/smart_automation_select_route.dart new file mode 100644 index 0000000..6b059ef --- /dev/null +++ b/lib/features/scene/view/smart_automation_select_route.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; +import 'package:syncrow_app/features/scene/widgets/select_smart_scene/smart_enable_autoamtion.dart'; +import 'package:syncrow_app/features/scene/widgets/select_smart_scene/smart_enable_tab_run.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/navigation/navigate_to_route.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class SmartAutomationSelectView extends StatelessWidget { + const SmartAutomationSelectView({super.key}); + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: "Select Smart Scene", + padding: const EdgeInsets.only(top: 24), + leading: IconButton( + onPressed: () { + navigateToRoute(context, Routes.sceneTasksRoute); + }, + icon: const Icon( + Icons.arrow_back_ios, + )), + height: 260, + child: DefaultContainer( + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SceneListTile( + assetPath: Assets.handClickIcon, + titleString: "Tap To Run", + subtitleString: '', + trailingWidget: const Icon( + Icons.arrow_forward_ios_rounded, + color: ColorsManager.greyColor, + ), + onPressed: () { + context.customBottomSheet( + child: const SmartEnableTabRun(), + ); + }, + ), + const Divider( + color: ColorsManager.dividerColor, + ), + SceneListTile( + assetPath: Assets.refreshIcon, + titleString: "Automation", + subtitleString: '', + trailingWidget: const Icon( + Icons.arrow_forward_ios_rounded, + color: ColorsManager.greyColor, + ), + onPressed: () { + context.customBottomSheet( + child: const SmartEnableAutomation(), + ); + }, + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/scene/widgets/alert_dialogs/alert_dialog_countdown.dart b/lib/features/scene/widgets/alert_dialogs/alert_dialog_countdown.dart index 73371a0..35f1d37 100644 --- a/lib/features/scene/widgets/alert_dialogs/alert_dialog_countdown.dart +++ b/lib/features/scene/widgets/alert_dialogs/alert_dialog_countdown.dart @@ -26,6 +26,7 @@ class _AlertDialogCountdownState extends State { @override didChangeDependencies() { super.didChangeDependencies(); + final tempTaskList = context.read().tempTasksList; for (var element in tempTaskList) { diff --git a/lib/features/scene/widgets/alert_dialogs/alert_dialog_functions_body.dart b/lib/features/scene/widgets/alert_dialogs/alert_dialog_functions_body.dart index bf0246c..58ef349 100644 --- a/lib/features/scene/widgets/alert_dialogs/alert_dialog_functions_body.dart +++ b/lib/features/scene/widgets/alert_dialogs/alert_dialog_functions_body.dart @@ -11,10 +11,12 @@ class AlertDialogFunctionsOperationsBody extends StatefulWidget { super.key, this.functionValue, required this.taskItem, + required this.isAutomation, }); dynamic functionValue; final SceneStaticFunction taskItem; + final bool isAutomation; @override State createState() => @@ -26,24 +28,46 @@ class _AlertDialogFunctionsOperationsBodyState @override didChangeDependencies() { super.didChangeDependencies(); - final tempTaskList = context.read().tempTasksList; + if (widget.isAutomation) { + final automationTempTasksList = + context.read().automationTempTasksList; - if (tempTaskList.isNotEmpty) { - for (var element in tempTaskList) { - if (element.code == widget.taskItem.code) { - groupValue = element.functionValue; - } else { - context - .read() - .add(RemoveFromSelectedValueById(code: widget.taskItem.code)); + if (automationTempTasksList.isNotEmpty) { + for (var element in automationTempTasksList) { + if (element.code == widget.taskItem.code) { + groupValue = element.functionValue; + } else { + context.read().add(RemoveFromSelectedValueById( + code: widget.taskItem.code, isAutomation: widget.isAutomation)); + } } } - } - if (widget.functionValue != null) { - setState(() { - groupValue = widget.functionValue; - }); + if (widget.functionValue != null) { + setState(() { + groupValue = widget.functionValue; + }); + } + } else { + final tempTaskList = context.read().tempTasksList; + + if (tempTaskList.isNotEmpty) { + for (var element in tempTaskList) { + if (element.code == widget.taskItem.code) { + groupValue = element.functionValue; + } else { + context + .read() + .add(RemoveFromSelectedValueById(code: widget.taskItem.code)); + } + } + } + + if (widget.functionValue != null) { + setState(() { + groupValue = widget.functionValue; + }); + } } } @@ -76,7 +100,9 @@ class _AlertDialogFunctionsOperationsBodyState groupValue = value; }); context.read().add(SelectedValueEvent( - value: value!, code: widget.taskItem.code)); + value: value!, + code: widget.taskItem.code, + isAutomation: widget.isAutomation)); }, ), onPressed: () { @@ -84,7 +110,9 @@ class _AlertDialogFunctionsOperationsBodyState groupValue = operation.value; }); context.read().add(SelectedValueEvent( - value: groupValue, code: widget.taskItem.code)); + value: groupValue, + code: widget.taskItem.code, + isAutomation: widget.isAutomation)); }, ); }, diff --git a/lib/features/scene/widgets/alert_dialogs/alert_dialog_slider_steps.dart b/lib/features/scene/widgets/alert_dialogs/alert_dialog_slider_steps.dart new file mode 100644 index 0000000..3e986e7 --- /dev/null +++ b/lib/features/scene/widgets/alert_dialogs/alert_dialog_slider_steps.dart @@ -0,0 +1,242 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; +import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class AlertDialogSliderSteps extends StatefulWidget { + const AlertDialogSliderSteps({ + super.key, + this.functionValue, + required this.taskItem, + required this.isAutomation, + }); + + final dynamic functionValue; + final SceneStaticFunction taskItem; + final bool isAutomation; + + @override + State createState() => _AlertDialogSliderStepsState(); +} + +class _AlertDialogSliderStepsState extends State { + double? groupValue; + int selectedToggleIndex = 1; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + final createSceneBloc = context.read(); + + if (widget.isAutomation) { + final automationTempTaskList = createSceneBloc.automationTempTasksList; + final automationComparatorValues = + createSceneBloc.automationComparatorValues; + + for (var element in automationTempTaskList) { + if (element.code == widget.taskItem.code) { + groupValue = element.functionValue; + selectedToggleIndex = + _comparatorToIndex(automationComparatorValues[element.code]); + } + } + } + + if (widget.functionValue != null) { + groupValue = _normalizeValue(widget.functionValue); + } else { + groupValue = + _normalizeValue(widget.taskItem.operationalValues[0].minValue); + } + + if (widget.taskItem.comparator != null) { + selectedToggleIndex = _comparatorToIndex(widget.taskItem.comparator); + } + + setState(() {}); + } + + double _normalizeValue(dynamic value) { + if (((widget.taskItem.code == "temp_set" && value > 200) || + widget.taskItem.code == "temp_current")) { + return (value) / 10; + } + return value.toDouble(); + } + + int _comparatorToIndex(String? comparator) { + switch (comparator) { + case "<": + return 0; + case "==": + return 1; + case ">": + return 2; + default: + return 1; + } + } + + String _indexToComparator(int index) { + switch (index) { + case 0: + return "<"; + case 1: + return "=="; + case 2: + return ">"; + default: + return "=="; + } + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ToggleButtons( + isSelected: [ + selectedToggleIndex == 0, + selectedToggleIndex == 1, + selectedToggleIndex == 2 + ], + onPressed: (index) { + setState(() { + selectedToggleIndex = index; + }); + context.read().add( + SelectedValueEvent( + value: _deNormalizeValue(groupValue), + code: widget.taskItem.code, + isAutomation: widget.isAutomation, + comparator: _indexToComparator(selectedToggleIndex), + ), + ); + }, + borderRadius: BorderRadius.circular(30), + selectedColor: Colors.white, + color: ColorsManager.blackColor, + fillColor: ColorsManager.primaryColorWithOpacity, + borderColor: ColorsManager.greyColor, + constraints: BoxConstraints.tight(const Size(70, 30)), + children: [ + SizedBox( + width: 70, + height: 30, + child: Container( + color: selectedToggleIndex == 0 + ? ColorsManager.primaryColorWithOpacity + : ColorsManager.greyColor, + alignment: Alignment.center, + child: const Text("<"), + ), + ), + SizedBox( + width: 70, + height: 30, + child: Container( + color: selectedToggleIndex == 1 + ? ColorsManager.primaryColorWithOpacity + : ColorsManager.greyColor, + alignment: Alignment.center, + child: const Text("="), + ), + ), + SizedBox( + width: 70, + height: 30, + child: Container( + color: selectedToggleIndex == 2 + ? ColorsManager.primaryColorWithOpacity + : ColorsManager.greyColor, + alignment: Alignment.center, + child: const Text(">"), + ), + ), + ], + ), + const SizedBox(height: 12), + ...widget.taskItem.operationalValues.map( + (operation) => BlocBuilder( + builder: (context, state) { + return Column( + children: [ + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TitleMedium( + text: groupValue != null + ? (groupValue! % 1 == 0 + ? groupValue!.toStringAsFixed(0) + : groupValue!.toStringAsFixed(1)) + : "0", + style: context.titleMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity, + fontSize: 30, + ), + ), + const SizedBox(width: 8), + TitleMedium( + text: operation.description.toString(), + style: context.titleMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity, + fontSize: 30, + ), + ), + ], + ), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Slider( + value: groupValue ?? 0, + min: operation.minValue ?? 0, + max: operation.maxValue ?? 0, + inactiveColor: ColorsManager.primaryColorWithOpacity + .withOpacity(0.2), + divisions: operation.stepValue != null + ? ((operation.maxValue!.toDouble() - + operation.minValue!.toDouble()) / + operation.stepValue!.toDouble()) + .round() + : null, + onChanged: (value) { + setState(() { + groupValue = normalizeValue(value); + }); + context.read().add(SelectedValueEvent( + value: _deNormalizeValue(groupValue), + code: widget.taskItem.code, + isAutomation: widget.isAutomation, + comparator: + _indexToComparator(selectedToggleIndex))); + }, + ), + ), + const SizedBox(height: 12), + ], + ); + }, + ), + ), + ], + ); + } + + double normalizeValue(double value) { + return double.parse(value.toStringAsFixed(1)); + } + + double _deNormalizeValue(double? value) { + if (widget.taskItem.code == "temp_set" || + widget.taskItem.code == "temp_current") { + return (value ?? 0) * 10; + } + return value ?? 0; + } +} diff --git a/lib/features/scene/widgets/alert_dialogs/alert_dialog_temperature_body.dart b/lib/features/scene/widgets/alert_dialogs/alert_dialog_temperature_body.dart index fb662ad..783d8c1 100644 --- a/lib/features/scene/widgets/alert_dialogs/alert_dialog_temperature_body.dart +++ b/lib/features/scene/widgets/alert_dialogs/alert_dialog_temperature_body.dart @@ -14,14 +14,10 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class AlertDialogTemperatureBody extends StatefulWidget { AlertDialogTemperatureBody({ super.key, - //required this.index, - // required this.functions, this.functionValue, required this.taskItem, }); - //final List functions; - // final int index; final SceneStaticFunction taskItem; dynamic functionValue; @@ -32,14 +28,16 @@ class AlertDialogTemperatureBody extends StatefulWidget { class _AlertDialogTemperatureBodyState extends State { + int temperature = 24; + @override - didChangeDependencies() { + void didChangeDependencies() { super.didChangeDependencies(); final tempTaskList = context.read().tempTasksList; for (var element in tempTaskList) { if (element.code == widget.taskItem.code) { - temperature = element.functionValue; + temperature = _normalizeTemperature(element.functionValue); } else { context .read() @@ -48,12 +46,18 @@ class _AlertDialogTemperatureBodyState } if (widget.functionValue != null) { setState(() { - temperature = widget.functionValue; + temperature = _normalizeTemperature(widget.functionValue); }); } } - int temperature = 24; + int _normalizeTemperature(dynamic value) { + if (value is int && value >= 100) { + return value ~/ 10; + } + return value as int? ?? 24; + } + @override Widget build(BuildContext context) { return ListTile( @@ -98,8 +102,7 @@ class _AlertDialogTemperatureBodyState ], ), subtitle: BodyLarge( - text: widget.taskItem.operationalValues[0].description - .toString(), + text: widget.taskItem.operationalValues[0].description.toString(), textAlign: TextAlign.center, ), trailing: IconButton( @@ -110,8 +113,7 @@ class _AlertDialogTemperatureBodyState } }); context.read().add(SelectedValueEvent( - value: temperature * 10, - code: widget.taskItem.code)); + value: temperature * 10, code: widget.taskItem.code)); }, icon: const Icon( Icons.add, diff --git a/lib/features/scene/widgets/bottom_sheet_widget.dart b/lib/features/scene/widgets/bottom_sheet_widget.dart index 1bbd631..518ef86 100644 --- a/lib/features/scene/widgets/bottom_sheet_widget.dart +++ b/lib/features/scene/widgets/bottom_sheet_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; +import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart'; import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_countdown.dart'; import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; @@ -66,7 +67,9 @@ class CustomBottomSheetWidget extends StatelessWidget { size: 16, color: ColorsManager.greyColor, ), - onPressed: () {}, + onPressed: () { + Navigator.pushNamed(context, Routes.smartAutomationSelectRoute); + }, ), SceneListTile( assetPath: Assets.delay, @@ -97,6 +100,7 @@ class CustomBottomSheetWidget extends StatelessWidget { operationName: 'Delay The Action', code: '', functionValue: 0, + operationDialogType: OperationDialogType.delay, operationalValues: [ SceneOperationalValue(icon: '', value: 0), ], @@ -123,8 +127,9 @@ class CustomBottomSheetWidget extends StatelessWidget { icon: Assets.delay, deviceName: 'Delay The Action', uniqueId: functions[0].uniqueCustomId, + operationType: functions[0].operationDialogType, )); - context.read().add(AddTaskEvent()); + context.read().add(const AddTaskEvent()); Navigator.pop(context); Navigator.pop(context); }, diff --git a/lib/features/scene/widgets/create_scene_save_button.dart b/lib/features/scene/widgets/create_scene_save_button.dart index 5a31914..59e375b 100644 --- a/lib/features/scene/widgets/create_scene_save_button.dart +++ b/lib/features/scene/widgets/create_scene_save_button.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; +import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; import 'package:syncrow_app/features/scene/helper/scene_logic_helper.dart'; import 'package:syncrow_app/features/shared_widgets/default_button.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/navigation/navigate_to_route.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; @@ -43,8 +46,7 @@ class _CreateSceneSaveButtonState extends State listener: (context, state) { if (state is CreateSceneWithTasks) { if (state.success == true) { - Navigator.pop(context); - Navigator.pop(context); + navigateToRoute(context, Routes.homeRoute); context.showCustomSnackbar( message: 'Scene created successfully', icon: const Icon( @@ -66,55 +68,59 @@ class _CreateSceneSaveButtonState extends State }, builder: (context, state) { return DefaultButton( - onPressed: widget.sceneName.isNotEmpty - ? () { - final tasks = context.read().tasksList; - handleSaveButtonPress( - context, - sceneNameController, - tasks, - updateScene: true, - sceneId: widget.sceneId, - ); - } - : () { - context.customAlertDialog( - alertBody: Padding( - padding: - const EdgeInsets.only(left: 8, right: 8, bottom: 8), - child: SizedBox( - height: 40, - child: SearchBar( - controller: sceneNameController, - elevation: WidgetStateProperty.all(0), - textStyle: - WidgetStateProperty.all(context.bodyMedium), - hintStyle: WidgetStateProperty.all( - context.bodyMedium.copyWith( - fontSize: 14, - color: ColorsManager.secondaryTextColor), - ), - hintText: 'Enter scene name', - backgroundColor: WidgetStateProperty.all( - ColorsManager.backgroundColor), - ), + onPressed: () { + final sceneBloc = context.read(); + final isAutomation = + sceneBloc.sceneType == CreateSceneEnum.deviceStatusChanges; + + if (widget.sceneName.isNotEmpty) { + handleSaveButtonPress( + context, + sceneName: sceneNameController, + actions: sceneBloc.tasksList, + updateScene: true, + sceneId: widget.sceneId, + isAutomation: isAutomation, + conditions: sceneBloc.automationTasksList, + ); + } else { + context.customAlertDialog( + alertBody: Padding( + padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8), + child: SizedBox( + height: 40, + child: SearchBar( + controller: sceneNameController, + elevation: WidgetStateProperty.all(0), + textStyle: WidgetStateProperty.all(context.bodyMedium), + hintStyle: WidgetStateProperty.all( + context.bodyMedium.copyWith( + fontSize: 14, + color: ColorsManager.secondaryTextColor), ), + hintText: 'Enter scene name', + backgroundColor: WidgetStateProperty.all( + ColorsManager.backgroundColor), ), - title: 'Scene Name', - onConfirm: () { - if (sceneNameController.text.isNotEmpty) { - final tasks = context.read().tasksList; - handleSaveButtonPress( - context, - sceneNameController, - tasks, - updateScene: false, - sceneId: widget.sceneId, - ); - } - }, - ); + ), + ), + title: 'Scene Name', + onConfirm: () { + if (sceneNameController.text.isNotEmpty) { + handleSaveButtonPress( + context, + sceneName: sceneNameController, + actions: sceneBloc.tasksList, + updateScene: false, + sceneId: widget.sceneId, + isAutomation: isAutomation, + conditions: sceneBloc.automationTasksList, + ); + } }, + ); + } + }, customButtonStyle: ButtonStyle( backgroundColor: WidgetStateProperty.all( ColorsManager.primaryColor, diff --git a/lib/features/scene/widgets/effective_period_setting/effective_period_bottom_sheet.dart b/lib/features/scene/widgets/effective_period_setting/effective_period_bottom_sheet.dart new file mode 100644 index 0000000..659f163 --- /dev/null +++ b/lib/features/scene/widgets/effective_period_setting/effective_period_bottom_sheet.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/scene/widgets/effective_period_setting/period_options.dart'; +import 'package:syncrow_app/features/scene/widgets/effective_period_setting/repeat_days.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +import '../../helper/effect_period_helper.dart'; + +class EffectPeriodBottomSheetContent extends StatelessWidget { + const EffectPeriodBottomSheetContent({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: TextButton( + onPressed: () => Navigator.pop(context), + child: BodySmall( + text: 'Save', + style: context.bodySmall.copyWith( + color: ColorsManager.primaryColorWithOpacity), + )), + ), + Expanded( + flex: 2, + child: BodyMedium( + text: 'Effective Period', + fontColor: ColorsManager.primaryColorWithOpacity, + fontWeight: FontsManager.bold, + textAlign: TextAlign.center, + ), + ), + const Spacer(), + ], + ), + const Divider( + color: ColorsManager.backgroundColor, + ), + const PeriodOptions( + showCustomTimePicker: EffectPeriodHelper.showCustomTimePicker, + ), + const SizedBox(height: 16), + const RepeatDays(), + const SizedBox(height: 24), + ], + ), + ); + } +} diff --git a/lib/features/scene/widgets/effective_period_setting/period_options.dart b/lib/features/scene/widgets/effective_period_setting/period_options.dart new file mode 100644 index 0000000..cdc4088 --- /dev/null +++ b/lib/features/scene/widgets/effective_period_setting/period_options.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_state.dart'; +import 'package:syncrow_app/features/scene/enum/effective_period_options.dart'; +import 'package:syncrow_app/features/scene/helper/effect_period_helper.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; + +class PeriodOptions extends StatelessWidget { + final Future?> Function(BuildContext) showCustomTimePicker; + + const PeriodOptions({ + required this.showCustomTimePicker, + super.key, + }); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return Column( + children: [ + _buildRadioOption( + context, EnumEffectivePeriodOptions.allDay, '24 Hours'), + _buildRadioOption(context, EnumEffectivePeriodOptions.daytime, + 'Sunrise to Sunset'), + _buildRadioOption( + context, EnumEffectivePeriodOptions.night, 'Sunset to Sunrise'), + ListTile( + contentPadding: EdgeInsets.zero, + onTap: () => showCustomTimePicker(context), + title: const BodyMedium( + text: 'Custom', + ), + subtitle: + state.customStartTime != null && state.customEndTime != null + ? BodySmall( + text: + '${"${state.customStartTime} AM"} - ${"${state.customEndTime} PM"}', + style: context.bodySmall.copyWith(fontSize: 10), + ) + : BodySmall( + text: '00:00 AM - 11:59 PM', + style: context.bodySmall.copyWith(fontSize: 10), + ), + trailing: Radio( + value: EnumEffectivePeriodOptions.custom, + groupValue: state.selectedPeriod, + onChanged: (value) async { + if (value != null) { + context.read().add(SetPeriod(value)); + } + }, + ), + ), + ], + ); + }, + ); + } + + Widget _buildRadioOption( + BuildContext context, EnumEffectivePeriodOptions value, String subtitle) { + return BlocBuilder( + builder: (context, state) { + return ListTile( + contentPadding: EdgeInsets.zero, + onTap: () { + context.read().add(SetPeriod(value)); + }, + title: BodyMedium(text: EffectPeriodHelper.formatEnumValue(value)), + subtitle: BodySmall( + text: subtitle, + style: context.bodySmall.copyWith(fontSize: 10), + ), + trailing: Radio( + value: value, + groupValue: state.selectedPeriod, + onChanged: (value) { + if (value != null) { + context.read().add(SetPeriod(value)); + } + }, + ), + ); + }, + ); + } +} diff --git a/lib/features/scene/widgets/effective_period_setting/repeat_days.dart b/lib/features/scene/widgets/effective_period_setting/repeat_days.dart new file mode 100644 index 0000000..9f40165 --- /dev/null +++ b/lib/features/scene/widgets/effective_period_setting/repeat_days.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_state.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; + +class RepeatDays extends StatelessWidget { + const RepeatDays({super.key}); + + @override + Widget build(BuildContext context) { + final daysMap = { + 'Mon': 'M', + 'Tue': 'T', + 'Wed': 'W', + 'Thu': 'T', + 'Fri': 'F', + 'Sat': 'S', + 'Sun': 'S', + }; + + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const BodyMedium(text: 'Repeat'), + const SizedBox(width: 8), + BlocBuilder( + builder: (context, state) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: daysMap.entries.map((entry) { + final day = entry.key; + final abbreviation = entry.value; + final dayIndex = _getDayIndex(day); + final isSelected = state.selectedDaysBinary[dayIndex] == '1'; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 3.0), + child: GestureDetector( + onTap: () { + context.read().add(ToggleDay(day)); + }, + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + isSelected ? Colors.grey : Colors.grey.shade300, + width: 1, + ), + ), + child: CircleAvatar( + radius: 15, + backgroundColor: Colors.white, + child: Text( + abbreviation, + style: TextStyle( + fontSize: 16, + color: + isSelected ? Colors.grey : Colors.grey.shade300, + ), + ), + ), + ), + ), + ); + }).toList(), + ); + }, + ), + ], + ); + } + + int _getDayIndex(String day) { + const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; + return days.indexOf(day); + } +} diff --git a/lib/features/scene/widgets/if_then_containers/if_container.dart b/lib/features/scene/widgets/if_then_containers/if_container.dart index 104b255..c7fe028 100644 --- a/lib/features/scene/widgets/if_then_containers/if_container.dart +++ b/lib/features/scene/widgets/if_then_containers/if_container.dart @@ -1,11 +1,22 @@ import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; +import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; +import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart'; +import 'package:syncrow_app/features/scene/view/create_scene_view.dart'; +import 'package:syncrow_app/features/scene/widgets/if_then_containers/then_added_tasks.dart'; import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/light_divider.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/helpers/custom_page_route.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; @@ -16,32 +27,173 @@ class IFDefaultContainer extends StatelessWidget { @override Widget build(BuildContext context) { + final sceneType = context.read().sceneType; return DefaultContainer( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2), child: Column( mainAxisSize: MainAxisSize.min, children: [ SceneListTile( - leadingWidget: BodyLarge( - text: 'IF', - style: context.bodyLarge.copyWith( - fontWeight: FontWeight.bold, - color: ColorsManager.primaryTextColor, + leadingWidget: InkWell( + onTap: () { + if (sceneType.name == + CreateSceneEnum.deviceStatusChanges.name) { + context.customAlertDialog( + hideConfirmButton: true, + alertBody: Column( + children: [ + ListTile( + title: const BodyMedium( + text: "When all conditions are met"), + onTap: () { + context.read().add( + const SelectConditionEvent( + "When all conditions are met")); + Navigator.pop(context); + }, + ), + ListTile( + title: const BodyMedium( + text: "When any condition is met"), + onTap: () { + context.read().add( + const SelectConditionEvent( + "When any condition is met")); + Navigator.pop(context); + }, + ), + ], + ), + title: 'Conditions Rule', + onConfirm: () {}, + ); + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + BodyLarge( + text: 'IF', + style: context.bodyLarge.copyWith( + fontWeight: FontWeight.bold, + color: ColorsManager.primaryTextColor, + ), + ), + Visibility( + visible: sceneType.name == + CreateSceneEnum.deviceStatusChanges.name, + child: BlocBuilder( + builder: (context, state) { + String conditionText = "When any condition is met"; + if (state is AddSceneTask) { + if (state.condition == 'or') { + conditionText = "When any condition is met"; + } else { + conditionText = "When all conditions are met"; + } + } + return SizedBox( + width: context.width * 0.6, + child: Row(children: [ + BodySmall(text: conditionText), + const Icon(Icons.keyboard_arrow_down) + ]), + ); + }, + ), + ), + ], ), ), - trailingWidget: SvgPicture.asset(Assets.addIcon), + trailingWidget: BlocBuilder( + builder: (context, state) { + bool isClickable = false; + if (state is AddSceneTask) { + isClickable = state.automationTasksList?.isNotEmpty ?? false; + } + return GestureDetector( + onTap: isClickable + ? () => Navigator.pushNamed( + context, + Routes.sceneControlDevicesRoute, + arguments: SceneSettingsRouteArguments( + sceneType: + CreateSceneEnum.deviceStatusChanges.name, + sceneId: '', + sceneName: '', + ), + ) + : null, + child: SvgPicture.asset( + Assets.addIcon, + colorFilter: ColorFilter.mode( + isClickable + ? ColorsManager.primaryColor + : ColorsManager.greyColor, + BlendMode.srcIn, + ), + ), + ); + }, + ), padding: EdgeInsets.zero, ), const LightDivider(), - const SceneListTile( - assetPath: Assets.handClickIcon, - titleString: StringsManager.tapToRun, - padding: EdgeInsets.zero, + BlocBuilder( + builder: (context, state) { + if (state is AddSceneTask) { + final automationTasksList = state.automationTasksList; + if (automationTasksList?.isNotEmpty == true) { + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: automationTasksList?.length, + reverse: true, + itemBuilder: (context, index) { + return ThenAddedTasksContainer( + taskItem: automationTasksList![index], + isAutomation: true, + ); + }, + ); + } + } + final sceneType = context.read().sceneType; + if (sceneType.name == CreateSceneEnum.tabToRun.name) { + return const SceneListTile( + padding: EdgeInsets.symmetric(horizontal: 2), + assetPath: Assets.handClickIcon, + titleString: StringsManager.tapToRun, + subtitleString: '', + ); + } + return SceneListTile( + titleString: '+ Add Condition', + textAlign: TextAlign.center, + onPressed: () { + final sceneType = context.read().sceneType; + if (sceneType.name == CreateSceneEnum.none.name) { + Navigator.push( + context, + CustomPageRoute( + builder: (context) => const CreateSceneView())); + } else { + Navigator.pushNamed( + context, + Routes.sceneControlDevicesRoute, + arguments: SceneSettingsRouteArguments( + sceneType: CreateSceneEnum.deviceStatusChanges.name, + sceneId: '', + sceneName: '', + ), + ); + } + }); + }, ) ], ), ); } } - - diff --git a/lib/features/scene/widgets/if_then_containers/then_added_tasks.dart b/lib/features/scene/widgets/if_then_containers/then_added_tasks.dart index a50b2a6..8cbb450 100644 --- a/lib/features/scene/widgets/if_then_containers/then_added_tasks.dart +++ b/lib/features/scene/widgets/if_then_containers/then_added_tasks.dart @@ -13,6 +13,7 @@ import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class ThenAddedTasksContainer extends StatelessWidget @@ -22,17 +23,19 @@ class ThenAddedTasksContainer extends StatelessWidget required this.taskItem, this.sceneId, this.index, + this.isAutomation, }); final SceneStaticFunction taskItem; String? sceneId; int? index; + bool? isAutomation; @override Widget build(BuildContext context) { final createSceneBloc = context.read(); String operationValue = ''; - if (taskItem.code.contains('countdown') || + if ((taskItem.code.contains('countdown') && isAutomation != true) || taskItem.deviceId.contains('delay')) { final functionValue = taskItem.functionValue ?? taskItem.operationalValues.first.value; @@ -40,7 +43,8 @@ class ThenAddedTasksContainer extends StatelessWidget Duration(seconds: int.tryParse(functionValue.toString()) ?? 0); operationValue = "${duration.inHours}h ${duration.inMinutes.remainder(60)}m "; - } else if (taskItem.code.contains('temp_set')) { + } else if (taskItem.code.contains('temp_set') || + taskItem.code.contains('temp_current')) { if (taskItem.functionValue != null || taskItem.functionValue != 0) { operationValue = '${((taskItem.functionValue / 10) as double).round().toString()}°C'; @@ -48,8 +52,13 @@ class ThenAddedTasksContainer extends StatelessWidget operationValue = '0°C'; } } else { - final functionValue = + var functionValue = taskItem.functionValue ?? taskItem.operationalValues.first.value; + + if (functionValue is double) { + functionValue = double.parse(functionValue.toStringAsFixed(1)); + } + operationValue = functionValue.toString(); } return DefaultContainer( @@ -58,28 +67,49 @@ class ThenAddedTasksContainer extends StatelessWidget /// get the task functions functionOperation = List.from(getOperationsForOneFunction( - taskItem: taskItem, deviceId: taskItem.deviceId)); + taskItem: taskItem, + deviceId: taskItem.deviceId, + isAutomation: isAutomation ?? false)); /// show alert dialog based on type context.customAlertDialog( - alertBody: getTheCorrectDialogBody(functionOperation.first), + alertBody: getTheCorrectDialogBody(functionOperation.first, null, + isAutomation: isAutomation ?? false), title: functionOperation.first.operationName, onConfirm: () { final savedCode = functionOperation.first.deviceId.contains('delay') ? 'delay' : functionOperation.first.code; - final selectedValue = createSceneBloc.selectedValues[savedCode]; + if (isAutomation == true) { + final automationSelectedValue = + createSceneBloc.automationSelectedValues[savedCode]; - try { - createSceneBloc.add( - UpdateTaskEvent( - newValue: selectedValue, - taskId: taskItem.uniqueCustomId, - ), - ); - } catch (e) { - debugPrint('Error adding UpdateTaskEvent: $e'); + try { + createSceneBloc.add( + UpdateTaskEvent( + newValue: automationSelectedValue, + taskId: taskItem.uniqueCustomId, + isAutomation: true, + ), + ); + } catch (e) { + debugPrint('Error adding UpdateTaskEvent: $e'); + } + } else { + final selectedValue = createSceneBloc.selectedValues[savedCode]; + + try { + createSceneBloc.add( + UpdateTaskEvent( + newValue: selectedValue, + taskId: taskItem.uniqueCustomId, + ), + ); + } catch (e) { + debugPrint('Error adding UpdateTaskEvent: $e'); + } } + Navigator.pop(context); }, ); @@ -109,16 +139,21 @@ class ThenAddedTasksContainer extends StatelessWidget onDismissed: (direction) { String removeFunctionById = taskItem.uniqueCustomId; - context - .read() - .add(RemoveTaskByIdEvent(taskId: removeFunctionById)); + if (isAutomation == true) { + context.read().add(RemoveTaskByIdEvent( + taskId: removeFunctionById, + isAutomation: true, + )); + } else { + context.read().add(RemoveTaskByIdEvent( + taskId: removeFunctionById, + )); + } String removeFunction = "${taskItem.operationName} with value ${taskItem.operationalValues.first.value}"; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('$removeFunction removed')), - ); + CustomSnackBar.displaySnackBar('$removeFunction removed'); }, child: SceneListTile( padding: EdgeInsets.zero, diff --git a/lib/features/scene/widgets/if_then_containers/then_container.dart b/lib/features/scene/widgets/if_then_containers/then_container.dart index 3c36709..3b322e4 100644 --- a/lib/features/scene/widgets/if_then_containers/then_container.dart +++ b/lib/features/scene/widgets/if_then_containers/then_container.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; +import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; +import 'package:syncrow_app/features/scene/view/create_scene_view.dart'; import 'package:syncrow_app/features/scene/widgets/if_then_containers/then_added_tasks.dart'; import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; import 'package:syncrow_app/features/scene/widgets/bottom_sheet_widget.dart'; @@ -10,6 +12,7 @@ import 'package:syncrow_app/features/shared_widgets/light_divider.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/helpers/custom_page_route.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class ThenDefaultContainer extends StatelessWidget { @@ -35,17 +38,29 @@ class ThenDefaultContainer extends StatelessWidget { color: ColorsManager.primaryTextColor, ), ), - trailingWidget: GestureDetector( - onTap: () => context.customBottomSheet( - child: const CustomBottomSheetWidget(), - ), - child: SvgPicture.asset( - Assets.addIcon, - colorFilter: const ColorFilter.mode( - ColorsManager.primaryColor, - BlendMode.srcIn, - ), - ), + trailingWidget: BlocBuilder( + builder: (context, state) { + bool isClickable = false; + if (state is AddSceneTask) { + isClickable = state.tasksList.isNotEmpty; + } + return GestureDetector( + onTap: isClickable + ? () => context.customBottomSheet( + child: const CustomBottomSheetWidget(), + ) + : null, + child: SvgPicture.asset( + Assets.addIcon, + colorFilter: ColorFilter.mode( + isClickable + ? ColorsManager.primaryColor + : ColorsManager.greyColor, + BlendMode.srcIn, + ), + ), + ); + }, ), padding: EdgeInsets.zero, ), @@ -71,20 +86,23 @@ class ThenDefaultContainer extends StatelessWidget { }, ); } - return SceneListTile( - titleString: '+ Add Task', - textAlign: TextAlign.center, - onPressed: () => context.customBottomSheet( - child: const CustomBottomSheetWidget(), - ), - ); } return SceneListTile( titleString: '+ Add Task', textAlign: TextAlign.center, - onPressed: () => context.customBottomSheet( - child: const CustomBottomSheetWidget(), - ), + onPressed: () { + final sceneType = context.read().sceneType; + if (sceneType.name == CreateSceneEnum.none.name) { + Navigator.push( + context, + CustomPageRoute( + builder: (context) => const CreateSceneView())); + } else { + context.customBottomSheet( + child: const CustomBottomSheetWidget(), + ); + } + }, ); }, ) diff --git a/lib/features/scene/widgets/scene_devices/scene_devices_body.dart b/lib/features/scene/widgets/scene_devices/scene_devices_body.dart index 6ee9ab4..60e97c8 100644 --- a/lib/features/scene/widgets/scene_devices/scene_devices_body.dart +++ b/lib/features/scene/widgets/scene_devices/scene_devices_body.dart @@ -6,6 +6,8 @@ import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_man import 'package:syncrow_app/features/devices/model/room_model.dart'; import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart'; +import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; +import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart'; import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; @@ -26,6 +28,11 @@ class SceneDevicesBody extends StatelessWidget { @override Widget build(BuildContext context) { + final isAutomationDeviceStatus = ((ModalRoute.of(context) + ?.settings + .arguments as SceneSettingsRouteArguments?) + ?.sceneType == + CreateSceneEnum.deviceStatusChanges.name); return BlocBuilder( builder: (context, tabState) { return Column( @@ -56,7 +63,10 @@ class SceneDevicesBody extends StatelessWidget { child: TabBarView( controller: _tabController, physics: const NeverScrollableScrollPhysics(), - children: rooms!.map((e) => _buildRoomTab(e, context)).toList(), + children: rooms! + .map((e) => + _buildRoomTab(e, context, isAutomationDeviceStatus)) + .toList(), ), ), ], @@ -65,7 +75,8 @@ class SceneDevicesBody extends StatelessWidget { ); } - Widget _buildRoomTab(RoomModel room, BuildContext context) { + Widget _buildRoomTab( + RoomModel room, BuildContext context, bool isAutomationDeviceStatus) { return BlocBuilder( builder: (context, state) { if (state.loading && state.devices == null) { @@ -96,7 +107,10 @@ class SceneDevicesBody extends StatelessWidget { Navigator.pushNamed( context, Routes.deviceFunctionsRoute, - arguments: device, + arguments: { + "device": device, + "isAutomationDeviceStatus": isAutomationDeviceStatus + }, ); }, ), diff --git a/lib/features/scene/widgets/scene_view_widget/scene_grid_view.dart b/lib/features/scene/widgets/scene_view_widget/scene_grid_view.dart index e6e5faf..f690fbb 100644 --- a/lib/features/scene/widgets/scene_view_widget/scene_grid_view.dart +++ b/lib/features/scene/widgets/scene_view_widget/scene_grid_view.dart @@ -1,21 +1,25 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_app/features/scene/model/scenes_model.dart'; import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_item.dart'; class SceneGrid extends StatelessWidget { - final List scenes; + final List scenes; final String? loadingSceneId; + final bool disablePlayButton; + final Map loadingStates; + const SceneGrid({ required this.scenes, required this.loadingSceneId, + required this.disablePlayButton, + required this.loadingStates, super.key, }); @override Widget build(BuildContext context) { - return - GridView.builder( + return GridView.builder( shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 12, @@ -26,7 +30,14 @@ class SceneGrid extends StatelessWidget { itemBuilder: (context, index) { final scene = scenes[index]; final isLoading = loadingSceneId == scene.id; - return SceneItem(scene: scene, isLoading: isLoading); + final isSwitchLoading = loadingStates[scene.id] ?? false; + + return SceneItem( + scene: scene, + isLoading: isLoading, + disablePlayButton: disablePlayButton, + isSwitchLoading: isSwitchLoading, + ); }, ); } diff --git a/lib/features/scene/widgets/scene_view_widget/scene_header.dart b/lib/features/scene/widgets/scene_view_widget/scene_header.dart index 788c4c1..1be7542 100644 --- a/lib/features/scene/widgets/scene_view_widget/scene_header.dart +++ b/lib/features/scene/widgets/scene_view_widget/scene_header.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; @@ -20,10 +19,10 @@ class SceneHeader extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - SizedBox(height: 20), - BodySmall( - text: StringsManager.tapToRunRoutine, - ), + // SizedBox(height: 20), + // BodySmall( + // text: StringsManager.tapToRunRoutine, + // ), ], ); } diff --git a/lib/features/scene/widgets/scene_view_widget/scene_item.dart b/lib/features/scene/widgets/scene_view_widget/scene_item.dart index 1830475..0dc4fff 100644 --- a/lib/features/scene/widgets/scene_view_widget/scene_item.dart +++ b/lib/features/scene/widgets/scene_view_widget/scene_item.dart @@ -1,11 +1,14 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart'; import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; import 'package:syncrow_app/features/scene/model/scenes_model.dart'; import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart'; +import 'package:syncrow_app/features/scene/model/update_automation.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; @@ -16,11 +19,15 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class SceneItem extends StatelessWidget { final ScenesModel scene; final bool isLoading; + final bool isSwitchLoading; + final bool disablePlayButton; const SceneItem({ required this.scene, required this.isLoading, super.key, + required this.disablePlayButton, + required this.isSwitchLoading, }); @override @@ -31,16 +38,28 @@ class SceneItem extends StatelessWidget { context, Routes.sceneTasksRoute, arguments: SceneSettingsRouteArguments( - sceneType: CreateSceneEnum.tabToRun.name, + sceneType: disablePlayButton == false + ? CreateSceneEnum.tabToRun.name + : CreateSceneEnum.deviceStatusChanges.name, sceneId: scene.id, sceneName: scene.name, ), ); - BlocProvider.of(context) - .add(FetchSceneTasksEvent(sceneId: scene.id)); + if (disablePlayButton == false) { + BlocProvider.of(context) + .add(const SceneTypeEvent(CreateSceneEnum.tabToRun)); + BlocProvider.of(context).add( + FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false)); + } else { + BlocProvider.of(context) + .add(const SceneTypeEvent(CreateSceneEnum.deviceStatusChanges)); + BlocProvider.of(context) + .add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: true)); + } }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -52,23 +71,45 @@ class SceneItem extends StatelessWidget { Assets.assetsIconsLogo, fit: BoxFit.fill, ), - IconButton( - padding: EdgeInsets.zero, - onPressed: () { - context - .read() - .add(SceneTrigger(scene.id, scene.name)); - }, - icon: isLoading - ? const Center( - child: CircularProgressIndicator(), - ) - : const Icon( - Icons.play_circle, - size: 40, - color: ColorsManager.greyColor, - ), - ), + disablePlayButton == false + ? IconButton( + padding: EdgeInsets.zero, + onPressed: () { + context + .read() + .add(SceneTrigger(scene.id, scene.name)); + }, + icon: isLoading + ? const Center( + child: CircularProgressIndicator(), + ) + : const Icon( + Icons.play_circle, + size: 40, + color: ColorsManager.greyColor, + ), + ) + : isSwitchLoading + ? Center( + child: CircularProgressIndicator( + color: ColorsManager.primaryColorWithOpacity, + ), + ) + : CupertinoSwitch( + activeColor: ColorsManager.primaryColor, + value: scene.status == 'enable' ? true : false, + onChanged: (value) { + context.read().add( + UpdateAutomationStatus( + automationStatusUpdate: + AutomationStatusUpdate( + isEnable: value, + unitUuid: HomeCubit.getInstance() + .selectedSpace! + .id!), + automationId: scene.id)); + }, + ), ], ), const SizedBox(height: 10), diff --git a/lib/features/scene/widgets/select_smart_scene/smart_automation_list.dart b/lib/features/scene/widgets/select_smart_scene/smart_automation_list.dart new file mode 100644 index 0000000..e987f4a --- /dev/null +++ b/lib/features/scene/widgets/select_smart_scene/smart_automation_list.dart @@ -0,0 +1,280 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart'; +import 'package:syncrow_app/features/scene/model/scene_static_function.dart'; +import 'package:syncrow_app/features/scene/model/scenes_model.dart'; +import 'package:syncrow_app/features/scene/model/smart_scene_enable.dart'; +import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class SmartSceneSelectAutomationList extends StatefulWidget { + const SmartSceneSelectAutomationList({ + super.key, + required this.automationList, + }); + + final List automationList; + + @override + State createState() => + _SmartSceneSelectAutomationListState(); +} + +class _SmartSceneSelectAutomationListState + extends State { + final List colorList = _generateDarkColors(100); + + static List _generateDarkColors(int count) { + final random = Random(); + final colors = []; + + while (colors.length < count) { + final color = + Color((random.nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0); + if (_isDark(color)) { + colors.add(color); + } + } + return colors; + } + + static bool _isDark(Color color) { + final brightness = + ((color.red * 299) + (color.green * 587) + (color.blue * 114)) / 1000; + return brightness < 128; + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: TextButton( + onPressed: () => Navigator.pop(context), + child: BodySmall( + text: 'Save', + style: context.bodySmall + .copyWith(color: ColorsManager.primaryColorWithOpacity), + )), + ), + Expanded( + flex: 2, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: BodyMedium( + text: 'Automation', + textAlign: TextAlign.center, + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity, + fontWeight: FontWeight.bold), + ), + ), + ), + const Spacer(), + ], + ), + const Divider( + color: ColorsManager.dividerColor, + ), + Expanded( + child: BlocBuilder( + builder: (context, state) { + final smartSceneEnable = ((state is SmartSceneSelected)) + ? state.smartSceneEnable + : context.read().smartSceneEnable; + + return ListView.builder( + shrinkWrap: true, + itemCount: widget.automationList.length, + itemBuilder: (context, index) { + final automation = widget.automationList[index]; + final isSelected = + smartSceneEnable?.entityId == automation.id; + final descriptionSelected = isSelected + ? (smartSceneEnable?.actionExecutor == 'rule_enable' + ? 'Enable' + : 'Disable') + : automation.status == 'enable' + ? 'Enable' + : 'Disable'; + + return SceneListTile( + padding: const EdgeInsets.symmetric(horizontal: 10), + leadingWidget: CircleAvatar( + backgroundColor: colorList[index % colorList.length], + radius: 15, + child: Text( + index.toString(), + style: context.bodyMedium.copyWith(color: Colors.white), + ), + ), + titleString: automation.name, + trailingWidget: TextButton.icon( + iconAlignment: IconAlignment.end, + onPressed: () { + context.customAlertDialog( + alertBody: EnableDisableAutomationDialog( + automationId: automation.id, + descriptionSelected: descriptionSelected, + ), + title: automation.name, + onConfirm: () { + Navigator.pop(context); + }, + onDismiss: () { + context + .read() + .add(const SmartSceneClearEvent()); + Navigator.pop(context); + }, + ); + }, + label: BodyMedium( + text: _capitalizeFirst(descriptionSelected), + style: context.bodyMedium + .copyWith(color: ColorsManager.greyColor), + ), + icon: const Icon( + Icons.arrow_forward_ios_rounded, + color: ColorsManager.greyColor, + size: 14, + ), + style: const ButtonStyle( + padding: WidgetStatePropertyAll(EdgeInsets.zero), + ), + ), + onPressed: () { + context.customAlertDialog( + alertBody: EnableDisableAutomationDialog( + automationId: automation.id, + descriptionSelected: descriptionSelected, + ), + title: automation.name, + onConfirm: () { + Navigator.pop(context); + }, + onDismiss: () { + context + .read() + .add(const SmartSceneClearEvent()); + Navigator.pop(context); + }, + ); + }, + ); + }, + ); + }, + ), + ), + ], + ); + } + + String _capitalizeFirst(String description) { + if (description.isEmpty) return description; + return '${description[0].toUpperCase()}${description.substring(1)}'; + } +} + +class EnableDisableAutomationDialog extends StatefulWidget { + const EnableDisableAutomationDialog({ + super.key, + required this.automationId, + required this.descriptionSelected, + }); + + final String automationId; + final String descriptionSelected; + + @override + State createState() => + _EnableDisableAutomationDialogState(); +} + +class _EnableDisableAutomationDialogState + extends State { + String? groupValue; + final List values = [ + SceneOperationalValue( + icon: Assets.assetsAcPower, + description: 'Enable', + value: 'rule_enable', + ), + SceneOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: 'Disable', + value: 'rule_disable', + ), + ]; + + @override + void initState() { + super.initState(); + groupValue = + widget.descriptionSelected == 'Enable' ? 'rule_enable' : 'rule_disable'; + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ...values.map( + (operation) => SceneListTile( + iconsSize: 25, + minLeadingWidth: 15, + padding: const EdgeInsets.symmetric(horizontal: 16), + assetPath: operation.icon, + assetHeaderValue: operation.iconValue, + titleString: operation.description.toString(), + textAlign: TextAlign.start, + trailingWidget: Radio( + value: operation.value, + groupValue: groupValue, + onChanged: (value) { + setState(() { + groupValue = value; + }); + context + .read() + .add(SmartSceneEnableEvent( + SmartSceneEnable( + entityId: widget.automationId, + actionExecutor: value!, + ), + )); + }, + ), + onPressed: () { + setState(() { + groupValue = operation.value; + }); + context + .read() + .add(SmartSceneEnableEvent( + SmartSceneEnable( + entityId: widget.automationId, + actionExecutor: operation.value, + ), + )); + }, + ), + ), + ], + ); + }, + ); + } +} diff --git a/lib/features/scene/widgets/select_smart_scene/smart_enable_autoamtion.dart b/lib/features/scene/widgets/select_smart_scene/smart_enable_autoamtion.dart new file mode 100644 index 0000000..d4684f5 --- /dev/null +++ b/lib/features/scene/widgets/select_smart_scene/smart_enable_autoamtion.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart'; + +import 'package:syncrow_app/features/scene/widgets/select_smart_scene/smart_automation_list.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; + +import 'package:syncrow_app/utils/context_extension.dart'; + +class SmartEnableAutomation extends StatelessWidget { + const SmartEnableAutomation({super.key}); + + @override + Widget build(BuildContext context) { + return DefaultContainer( + height: context.height * 0.5, + width: double.infinity, + child: BlocBuilder( + bloc: context.read() + ..add( + LoadAutomation(HomeCubit.getInstance().selectedSpace?.id ?? '')), + builder: (context, state) { + if (state is SceneLoading) { + return const Align( + alignment: Alignment.topCenter, + child: LinearProgressIndicator()); + } + if (state is SceneLoaded) { + return SmartSceneSelectAutomationList( + automationList: state.automationList); + } + return const SizedBox.shrink(); + }, + ), + ); + } +} diff --git a/lib/features/scene/widgets/select_smart_scene/smart_enable_tab_run.dart b/lib/features/scene/widgets/select_smart_scene/smart_enable_tab_run.dart new file mode 100644 index 0000000..37a5e7c --- /dev/null +++ b/lib/features/scene/widgets/select_smart_scene/smart_enable_tab_run.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart'; +import 'package:syncrow_app/features/scene/widgets/select_smart_scene/smart_tab_run_list.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; + +class SmartEnableTabRun extends StatelessWidget { + const SmartEnableTabRun({super.key}); + + @override + Widget build(BuildContext context) { + return DefaultContainer( + height: context.height * 0.5, + width: double.infinity, + child: BlocBuilder( + bloc: context.read() + ..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? '')), + builder: (context, state) { + if (state is SceneLoading) { + return const Align( + alignment: Alignment.topCenter, + child: LinearProgressIndicator()); + } + if (state is SceneLoaded) { + return SmartSceneSelectTabToRunList(scenes: state.scenes); + } + return const SizedBox.shrink(); + }, + ), + ); + } +} + diff --git a/lib/features/scene/widgets/select_smart_scene/smart_tab_run_list.dart b/lib/features/scene/widgets/select_smart_scene/smart_tab_run_list.dart new file mode 100644 index 0000000..15fc2b6 --- /dev/null +++ b/lib/features/scene/widgets/select_smart_scene/smart_tab_run_list.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart'; +import 'package:syncrow_app/features/scene/model/scenes_model.dart'; +import 'package:syncrow_app/features/scene/model/smart_scene_enable.dart'; +import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class SmartSceneSelectTabToRunList extends StatefulWidget { + const SmartSceneSelectTabToRunList({ + super.key, + required this.scenes, + }); + final List scenes; + + @override + State createState() => + _SmartSceneSelectTabToRunListState(); +} + +class _SmartSceneSelectTabToRunListState + extends State { + String groupValue = ''; + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: TextButton( + onPressed: () => Navigator.pop(context), + child: BodySmall( + text: 'Save', + style: context.bodySmall + .copyWith(color: ColorsManager.primaryColorWithOpacity), + )), + ), + Expanded( + flex: 2, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: BodyMedium( + text: 'Tab To Run', + textAlign: TextAlign.center, + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity, + fontWeight: FontWeight.bold), + ), + ), + ), + const Spacer(), + ], + ), + const Divider( + color: ColorsManager.dividerColor, + ), + Expanded( + child: ListView.builder( + shrinkWrap: true, + itemCount: widget.scenes.length, + itemBuilder: (context, index) { + final scene = widget.scenes[index]; + return SceneListTile( + padding: const EdgeInsets.symmetric(horizontal: 10), + leadingWidget: Image.asset( + height: 32, + width: 32, + Assets.assetsIconsLogo, + fit: BoxFit.fill, + ), + titleString: scene.name, + trailingWidget: Radio( + value: scene.id, + groupValue: groupValue, + onChanged: (String? value) { + if (value != null) { + setState(() { + groupValue = value; + }); + context + .read() + .add(SmartSceneEnableEvent(SmartSceneEnable( + entityId: scene.id, + actionExecutor: 'rule_enable', + ))); + } + }), + onPressed: () { + setState(() { + groupValue = scene.id; + }); + context + .read() + .add(SmartSceneEnableEvent(SmartSceneEnable( + entityId: scene.id, + actionExecutor: 'rule_enable', + ))); + }, + ); + }), + ), + ], + ); + } +} diff --git a/lib/features/shared_widgets/default_scaffold.dart b/lib/features/shared_widgets/default_scaffold.dart index 080fbef..a88e828 100644 --- a/lib/features/shared_widgets/default_scaffold.dart +++ b/lib/features/shared_widgets/default_scaffold.dart @@ -17,6 +17,7 @@ class DefaultScaffold extends StatelessWidget { this.padding, this.leading, this.leadingWidth, + this.height, }); final Widget child; @@ -27,6 +28,7 @@ class DefaultScaffold extends StatelessWidget { final EdgeInsetsGeometry? padding; final Widget? leading; final double? leadingWidth; + final double? height; @override Widget build(BuildContext context) { return AnnotatedRegion( @@ -54,8 +56,9 @@ class DefaultScaffold extends StatelessWidget { ), body: Container( width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - padding: padding ?? const EdgeInsets.symmetric(horizontal: Constants.defaultPadding), + height: height ?? MediaQuery.sizeOf(context).height, + padding: padding ?? + const EdgeInsets.symmetric(horizontal: Constants.defaultPadding), decoration: const BoxDecoration( image: DecorationImage( image: AssetImage( diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 3eb3827..caae2da 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -3,7 +3,8 @@ class Assets { /// Assets for assetsFontsAftikaRegular /// assets/fonts/AftikaRegular.ttf - static const String assetsFontsAftikaRegular = "assets/fonts/AftikaRegular.ttf"; + static const String assetsFontsAftikaRegular = + "assets/fonts/AftikaRegular.ttf"; /// Assets for assetsIcons3GangSwitch /// assets/icons/3GangSwitch.svg @@ -19,82 +20,98 @@ class Assets { /// Assets for assetsIconsAutomatedClock /// assets/icons/automated_clock.svg - static const String assetsIconsAutomatedClock = "assets/icons/automated_clock.svg"; + static const String assetsIconsAutomatedClock = + "assets/icons/automated_clock.svg"; static const String acSwitchIcon = "assets/icons/ac_switch_ic.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstChargeddmOff /// assets/icons/battery/dmOff/perOffchargOfflowOffpmOffstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstChargeddmOff = "assets/icons/battery/dmOff/perOffchargOfflowOffpmOffstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstDefaultdmOff /// assets/icons/battery/dmOff/perOffchargOfflowOffpmOffstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOfflowOffpmOffstDefaultdmOff = "assets/icons/battery/dmOff/perOffchargOfflowOffpmOffstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOfflowOffpmOnstChargeddmOff /// assets/icons/battery/dmOff/perOffchargOfflowOffpmOnstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOfflowOffpmOnstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOfflowOffpmOnstChargeddmOff = "assets/icons/battery/dmOff/perOffchargOfflowOffpmOnstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOfflowOnpmOffstDefaultdmOff /// assets/icons/battery/dmOff/perOffchargOfflowOnpmOffstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOfflowOnpmOffstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOfflowOnpmOffstDefaultdmOff = "assets/icons/battery/dmOff/perOffchargOfflowOnpmOffstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOfflowOnpmOnstDefaultdmOff /// assets/icons/battery/dmOff/perOffchargOfflowOnpmOnstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOfflowOnpmOnstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOfflowOnpmOnstDefaultdmOff = "assets/icons/battery/dmOff/perOffchargOfflowOnpmOnstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOnlowOffpmOffstChargeddmOff /// assets/icons/battery/dmOff/perOffchargOnlowOffpmOffstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOnlowOffpmOffstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOnlowOffpmOffstChargeddmOff = "assets/icons/battery/dmOff/perOffchargOnlowOffpmOffstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOnlowOnpmOffstlowBatterydmOff /// assets/icons/battery/dmOff/perOffchargOnlowOnpmOffstlowBatterydmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOnlowOnpmOffstlowBatterydmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOnlowOnpmOffstlowBatterydmOff = "assets/icons/battery/dmOff/perOffchargOnlowOnpmOffstlowBatterydmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOffchargOnlowOnpmOnstlowpmdmOff /// assets/icons/battery/dmOff/perOffchargOnlowOnpmOnstlowpmdmOff.svg - static const String assetsIconsBatteryDmOffPerOffchargOnlowOnpmOnstlowpmdmOff = + static const String + assetsIconsBatteryDmOffPerOffchargOnlowOnpmOnstlowpmdmOff = "assets/icons/battery/dmOff/perOffchargOnlowOnpmOnstlowpmdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstChargeddmOff /// assets/icons/battery/dmOff/perOnchargOfflowOffpmOffstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstChargeddmOff = "assets/icons/battery/dmOff/perOnchargOfflowOffpmOffstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstDefaultdmOff /// assets/icons/battery/dmOff/perOnchargOfflowOffpmOffstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOfflowOffpmOffstDefaultdmOff = "assets/icons/battery/dmOff/perOnchargOfflowOffpmOffstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOfflowOffpmOnstChargeddmOff /// assets/icons/battery/dmOff/perOnchargOfflowOffpmOnstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOfflowOffpmOnstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOfflowOffpmOnstChargeddmOff = "assets/icons/battery/dmOff/perOnchargOfflowOffpmOnstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOfflowOnpmOffstDefaultdmOff /// assets/icons/battery/dmOff/perOnchargOfflowOnpmOffstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOfflowOnpmOffstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOfflowOnpmOffstDefaultdmOff = "assets/icons/battery/dmOff/perOnchargOfflowOnpmOffstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOfflowOnpmOnstDefaultdmOff /// assets/icons/battery/dmOff/perOnchargOfflowOnpmOnstDefaultdmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOfflowOnpmOnstDefaultdmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOfflowOnpmOnstDefaultdmOff = "assets/icons/battery/dmOff/perOnchargOfflowOnpmOnstDefaultdmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOnlowOffpmOffstChargeddmOff /// assets/icons/battery/dmOff/perOnchargOnlowOffpmOffstChargeddmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOnlowOffpmOffstChargeddmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOnlowOffpmOffstChargeddmOff = "assets/icons/battery/dmOff/perOnchargOnlowOffpmOffstChargeddmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOnlowOnpmOffstlowBatterydmOff /// assets/icons/battery/dmOff/perOnchargOnlowOnpmOffstlowBatterydmOff.svg - static const String assetsIconsBatteryDmOffPerOnchargOnlowOnpmOffstlowBatterydmOff = + static const String + assetsIconsBatteryDmOffPerOnchargOnlowOnpmOffstlowBatterydmOff = "assets/icons/battery/dmOff/perOnchargOnlowOnpmOffstlowBatterydmOff.svg"; /// Assets for assetsIconsBatteryDmOffPerOnchargOnlowOnpmOnstlowpmdmOff @@ -104,37 +121,44 @@ class Assets { /// Assets for assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstChargeddmOn /// assets/icons/battery/dmOn/perOffchargOfflowOffpmOffstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstChargeddmOn = "assets/icons/battery/dmOn/perOffchargOfflowOffpmOffstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstDefaultdmOn /// assets/icons/battery/dmOn/perOffchargOfflowOffpmOffstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOfflowOffpmOffstDefaultdmOn = "assets/icons/battery/dmOn/perOffchargOfflowOffpmOffstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOfflowOffpmOnstChargeddmOn /// assets/icons/battery/dmOn/perOffchargOfflowOffpmOnstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOfflowOffpmOnstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOfflowOffpmOnstChargeddmOn = "assets/icons/battery/dmOn/perOffchargOfflowOffpmOnstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOfflowOnpmOffstDefaultdmOn /// assets/icons/battery/dmOn/perOffchargOfflowOnpmOffstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOfflowOnpmOffstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOfflowOnpmOffstDefaultdmOn = "assets/icons/battery/dmOn/perOffchargOfflowOnpmOffstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOfflowOnpmOnstDefaultdmOn /// assets/icons/battery/dmOn/perOffchargOfflowOnpmOnstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOfflowOnpmOnstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOfflowOnpmOnstDefaultdmOn = "assets/icons/battery/dmOn/perOffchargOfflowOnpmOnstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOnlowOffpmOffstChargeddmOn /// assets/icons/battery/dmOn/perOffchargOnlowOffpmOffstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOnlowOffpmOffstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOnlowOffpmOffstChargeddmOn = "assets/icons/battery/dmOn/perOffchargOnlowOffpmOffstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOnlowOnpmOffstlowBatterydmOn /// assets/icons/battery/dmOn/perOffchargOnlowOnpmOffstlowBatterydmOn.svg - static const String assetsIconsBatteryDmOnPerOffchargOnlowOnpmOffstlowBatterydmOn = + static const String + assetsIconsBatteryDmOnPerOffchargOnlowOnpmOffstlowBatterydmOn = "assets/icons/battery/dmOn/perOffchargOnlowOnpmOffstlowBatterydmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOffchargOnlowOnpmOnstlowpmdmOn @@ -144,37 +168,44 @@ class Assets { /// Assets for assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstChargeddmOn /// assets/icons/battery/dmOn/perOnchargOfflowOffpmOffstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstChargeddmOn = "assets/icons/battery/dmOn/perOnchargOfflowOffpmOffstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstDefaultdmOn /// assets/icons/battery/dmOn/perOnchargOfflowOffpmOffstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOfflowOffpmOffstDefaultdmOn = "assets/icons/battery/dmOn/perOnchargOfflowOffpmOffstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOfflowOffpmOnstChargeddmOn /// assets/icons/battery/dmOn/perOnchargOfflowOffpmOnstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOfflowOffpmOnstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOfflowOffpmOnstChargeddmOn = "assets/icons/battery/dmOn/perOnchargOfflowOffpmOnstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOfflowOnpmOffstDefaultdmOn /// assets/icons/battery/dmOn/perOnchargOfflowOnpmOffstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOfflowOnpmOffstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOfflowOnpmOffstDefaultdmOn = "assets/icons/battery/dmOn/perOnchargOfflowOnpmOffstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOfflowOnpmOnstDefaultdmOn /// assets/icons/battery/dmOn/perOnchargOfflowOnpmOnstDefaultdmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOfflowOnpmOnstDefaultdmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOfflowOnpmOnstDefaultdmOn = "assets/icons/battery/dmOn/perOnchargOfflowOnpmOnstDefaultdmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOnlowOffpmOffstChargeddmOn /// assets/icons/battery/dmOn/perOnchargOnlowOffpmOffstChargeddmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOnlowOffpmOffstChargeddmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOnlowOffpmOffstChargeddmOn = "assets/icons/battery/dmOn/perOnchargOnlowOffpmOffstChargeddmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOnlowOnpmOffstlowBatterydmOn /// assets/icons/battery/dmOn/perOnchargOnlowOnpmOffstlowBatterydmOn.svg - static const String assetsIconsBatteryDmOnPerOnchargOnlowOnpmOffstlowBatterydmOn = + static const String + assetsIconsBatteryDmOnPerOnchargOnlowOnpmOffstlowBatterydmOn = "assets/icons/battery/dmOn/perOnchargOnlowOnpmOffstlowBatterydmOn.svg"; /// Assets for assetsIconsBatteryDmOnPerOnchargOnlowOnpmOnstlowpmdmOn @@ -226,7 +257,8 @@ class Assets { /// Assets for assetsIconsDashboardFill /// assets/icons/dashboard-fill.svg - static const String assetsIconsDashboardFill = "assets/icons/dashboard-fill.svg"; + static const String assetsIconsDashboardFill = + "assets/icons/dashboard-fill.svg"; /// Assets for assetsIconsDevices /// assets/icons/Devices.svg @@ -242,7 +274,8 @@ class Assets { /// Assets for assetsIconsDoorLockLinkage /// assets/icons/DoorLockLinkage.svg - static const String assetsIconsDoorLockLinkage = "assets/icons/DoorLockLinkage.svg"; + static const String assetsIconsDoorLockLinkage = + "assets/icons/DoorLockLinkage.svg"; /// Assets for assetsIconsDoorLockLock /// assets/icons/DoorLockLock.svg @@ -250,15 +283,18 @@ class Assets { /// Assets for assetsIconsDoorLockMembers /// assets/icons/DoorLockMembers.svg - static const String assetsIconsDoorLockMembers = "assets/icons/DoorLockMembers.svg"; + static const String assetsIconsDoorLockMembers = + "assets/icons/DoorLockMembers.svg"; /// Assets for assetsIconsDoorLockPassword /// assets/icons/DoorLockPassword.svg - static const String assetsIconsDoorLockPassword = "assets/icons/DoorLockPassword.svg"; + static const String assetsIconsDoorLockPassword = + "assets/icons/DoorLockPassword.svg"; /// Assets for assetsIconsDoorLockRecords /// assets/icons/DoorLockRecords.svg - static const String assetsIconsDoorLockRecords = "assets/icons/DoorLockRecords.svg"; + static const String assetsIconsDoorLockRecords = + "assets/icons/DoorLockRecords.svg"; /// Assets for assetsIconsDoorlockAssetsBatteryIndicator /// assets/icons/doorlock-assets/BatteryIndicator.svg @@ -279,7 +315,8 @@ class Assets { /// assets/icons/doorlock-assets/lockIcon.svg static const String assetsIconsDoorlockAssetsLockIcon = "assets/icons/doorlock-assets/lockIcon.svg"; - static const String doorUnlockIcon = "assets/icons/doorlock-assets/door_un_look_ic.svg"; + static const String doorUnlockIcon = + "assets/icons/doorlock-assets/door_un_look_ic.svg"; /// Assets for assetsIconsDoorlockAssetsMembersManagement /// assets/icons/doorlock-assets/members-management.svg @@ -367,11 +404,13 @@ class Assets { /// Assets for assetsIconsLightSwitchOff /// assets/icons/lightSwitchOff.svg - static const String assetsIconsLightSwitchOff = "assets/icons/lightSwitchOff.svg"; + static const String assetsIconsLightSwitchOff = + "assets/icons/lightSwitchOff.svg"; /// Assets for assetsIconsLightSwitchOn /// assets/icons/lightSwitchOn.svg - static const String assetsIconsLightSwitchOn = "assets/icons/lightSwitchOn.svg"; + static const String assetsIconsLightSwitchOn = + "assets/icons/lightSwitchOn.svg"; /// Assets for assetsIconsLinkageIconsDoorLockAlarm /// assets/icons/linkageIcons/doorLockAlarm.svg @@ -380,7 +419,8 @@ class Assets { /// Assets for assetsIconsLinkTimeLimitedPasswordIcon /// assets/icons/timeLimitedPasswordIcon.svg - static const String timeLimitedPasswordIcon = "assets/icons/timeLimitedPasswordIcon.svg"; + static const String timeLimitedPasswordIcon = + "assets/icons/timeLimitedPasswordIcon.svg"; /// Assets for assetsIconsoneTimePassword /// assets/icons/oneTimePassword.svg @@ -388,7 +428,8 @@ class Assets { /// Assets for assetsIconsTimeLimitedPassword /// assets/icons/timeLimitedPassword.svg - static const String timeLimitedPassword = "assets/icons/timeLimitedPassword.svg"; + static const String timeLimitedPassword = + "assets/icons/timeLimitedPassword.svg"; /// Assets for assetsIconsNoValidPasswords /// assets/icons/noValidPasswords.svg @@ -557,11 +598,13 @@ class Assets { /// Assets for assetsIconsPresenceSensorAssetsParameterSettings /// assets/icons/presence-sensor-assets/space_type_icon.svg - static const String spaceTypeIcon = "assets/icons/presence-sensor-assets/space_type_icon.svg"; + static const String spaceTypeIcon = + "assets/icons/presence-sensor-assets/space_type_icon.svg"; /// Assets for assetsIconsPresenceSensorAssetsParameterSettings /// assets/icons/presence-sensor-assets/space_type_icon.svg - static const String sensitivityIcon = "assets/icons/presence-sensor-assets/Sensitivity.svg"; + static const String sensitivityIcon = + "assets/icons/presence-sensor-assets/Sensitivity.svg"; /// Assets for assetsIconsPresenceSensorAssetsParameterSettings /// assets/icons/presence-sensor-assets/maximum_distance.svg @@ -594,7 +637,8 @@ class Assets { /// Assets for assetsIconsRoutinesFill /// assets/icons/Routines-fill.svg - static const String assetsIconsRoutinesFill = "assets/icons/Routines-fill.svg"; + static const String assetsIconsRoutinesFill = + "assets/icons/Routines-fill.svg"; /// Assets for assetsIconsScan /// assets/icons/Scan.svg @@ -626,7 +670,8 @@ class Assets { /// Assets for assetsIconsSustainability /// assets/icons/sustainability.svg - static const String assetsIconsSustainability = "assets/icons/sustainability.svg"; + static const String assetsIconsSustainability = + "assets/icons/sustainability.svg"; /// Assets for assetsIconsUnlockingMethodsIconsFace /// assets/icons/unlockingMethodsIcons/face.svg @@ -718,7 +763,8 @@ class Assets { /// Assets for assetsImagesHorizintalBlade /// assets/images/HorizintalBlade.png - static const String assetsImagesHorizintalBlade = "assets/images/HorizintalBlade.png"; + static const String assetsImagesHorizintalBlade = + "assets/images/HorizintalBlade.png"; /// Assets for assetsImagesLogo /// assets/images/Logo.svg @@ -726,7 +772,8 @@ class Assets { /// Assets for assetsImagesLogoHorizontal /// assets/images/logo_horizontal.png - static const String assetsImagesLogoHorizontal = "assets/images/logo_horizontal.png"; + static const String assetsImagesLogoHorizontal = + "assets/images/logo_horizontal.png"; /// Assets for assetsImagesPause /// assets/images/Pause.png @@ -756,7 +803,8 @@ class Assets { /// assets/images/Window.png static const String assetsImagesWindow = "assets/images/Window.png"; - static const String assetsSensitivityFunction = "assets/icons/functions_icons/sensitivity.svg"; + static const String assetsSensitivityFunction = + "assets/icons/functions_icons/sensitivity.svg"; //assets/icons/functions_icons/sesitivity_operation_icon.svg static const String assetsSensitivityOperationIcon = @@ -764,59 +812,73 @@ class Assets { //assets/icons/functions_icons/ac_power.svg - static const String assetsAcPower = "assets/icons/functions_icons/ac_power.svg"; + static const String assetsAcPower = + "assets/icons/functions_icons/ac_power.svg"; //assets/icons/functions_icons/ac_power_off.svg - static const String assetsAcPowerOFF = "assets/icons/functions_icons/ac_power_off.svg"; + static const String assetsAcPowerOFF = + "assets/icons/functions_icons/ac_power_off.svg"; //assets/icons/functions_icons/child_lock.svg - static const String assetsChildLock = "assets/icons/functions_icons/child_lock.svg"; + static const String assetsChildLock = + "assets/icons/functions_icons/child_lock.svg"; //assets/icons/functions_icons/cooling.svg - static const String assetsFreezing = "assets/icons/functions_icons/freezing.svg"; + static const String assetsFreezing = + "assets/icons/functions_icons/freezing.svg"; //assets/icons/functions_icons/fan_speed.svg - static const String assetsFanSpeed = "assets/icons/functions_icons/fan_speed.svg"; + static const String assetsFanSpeed = + "assets/icons/functions_icons/fan_speed.svg"; //assets/icons/functions_icons/ac_cooling.svg - static const String assetsAcCooling = "assets/icons/functions_icons/ac_cooling.svg"; + static const String assetsAcCooling = + "assets/icons/functions_icons/ac_cooling.svg"; //assets/icons/functions_icons/ac_heating.svg - static const String assetsAcHeating = "assets/icons/functions_icons/ac_heating.svg"; + static const String assetsAcHeating = + "assets/icons/functions_icons/ac_heating.svg"; //assets/icons/functions_icons/celsius_degrees.svg - static const String assetsCelsiusDegrees = "assets/icons/functions_icons/celsius_degrees.svg"; + static const String assetsCelsiusDegrees = + "assets/icons/functions_icons/celsius_degrees.svg"; //assets/icons/functions_icons/tempreture.svg - static const String assetsTempreture = "assets/icons/functions_icons/tempreture.svg"; + static const String assetsTempreture = + "assets/icons/functions_icons/tempreture.svg"; //assets/icons/functions_icons/ac_fan_low.svg - static const String assetsAcFanLow = "assets/icons/functions_icons/ac_fan_low.svg"; + static const String assetsAcFanLow = + "assets/icons/functions_icons/ac_fan_low.svg"; //assets/icons/functions_icons/ac_fan_middle.svg - static const String assetsAcFanMiddle = "assets/icons/functions_icons/ac_fan_middle.svg"; + static const String assetsAcFanMiddle = + "assets/icons/functions_icons/ac_fan_middle.svg"; //assets/icons/functions_icons/ac_fan_high.svg - static const String assetsAcFanHigh = "assets/icons/functions_icons/ac_fan_high.svg"; + static const String assetsAcFanHigh = + "assets/icons/functions_icons/ac_fan_high.svg"; //assets/icons/functions_icons/ac_fan_auto.svg - static const String assetsAcFanAuto = "assets/icons/functions_icons/ac_fan_auto.svg"; + static const String assetsAcFanAuto = + "assets/icons/functions_icons/ac_fan_auto.svg"; //assets/icons/functions_icons/scene_child_lock.svg - static const String assetsSceneChildLock = "assets/icons/functions_icons/scene_child_lock.svg"; + static const String assetsSceneChildLock = + "assets/icons/functions_icons/scene_child_lock.svg"; //assets/icons/functions_icons/scene_child_unlock.svg @@ -825,15 +887,18 @@ class Assets { //assets/icons/functions_icons/scene_refresh.svg - static const String assetsSceneRefresh = "assets/icons/functions_icons/scene_refresh.svg"; + static const String assetsSceneRefresh = + "assets/icons/functions_icons/scene_refresh.svg"; //assets/icons/functions_icons/light_countdown.svg - static const String assetsLightCountdown = "assets/icons/functions_icons/light_countdown.svg"; + static const String assetsLightCountdown = + "assets/icons/functions_icons/light_countdown.svg"; //assets/icons/functions_icons/far_detection.svg - static const String assetsFarDetection = "assets/icons/functions_icons/far_detection.svg"; + static const String assetsFarDetection = + "assets/icons/functions_icons/far_detection.svg"; //assets/icons/functions_icons/far_detection_function.svg @@ -842,11 +907,13 @@ class Assets { //assets/icons/functions_icons/indicator.svg - static const String assetsIndicator = "assets/icons/functions_icons/indicator.svg"; + static const String assetsIndicator = + "assets/icons/functions_icons/indicator.svg"; //assets/icons/functions_icons/motion_detection.svg - static const String assetsMotionDetection = "assets/icons/functions_icons/motion_detection.svg"; + static const String assetsMotionDetection = + "assets/icons/functions_icons/motion_detection.svg"; //assets/icons/functions_icons/motionless_detection.svg @@ -855,15 +922,18 @@ class Assets { //assets/icons/functions_icons/nobody_time.svg - static const String assetsNobodyTime = "assets/icons/functions_icons/nobody_time.svg"; + static const String assetsNobodyTime = + "assets/icons/functions_icons/nobody_time.svg"; //assets/icons/functions_icons/factory_reset.svg - static const String assetsFactoryReset = "assets/icons/functions_icons/factory_reset.svg"; + static const String assetsFactoryReset = + "assets/icons/functions_icons/factory_reset.svg"; //assets/icons/functions_icons/master_state.svg - static const String assetsMasterState = "assets/icons/functions_icons/master_state.svg"; + static const String assetsMasterState = + "assets/icons/functions_icons/master_state.svg"; //assets/icons/functions_icons/switch_alarm_sound.svg @@ -872,5 +942,91 @@ class Assets { //assets/icons/functions_icons/reset_off.svg - static const String assetsResetOff = "assets/icons/functions_icons/reset_off.svg"; + static const String assetsResetOff = + "assets/icons/functions_icons/reset_off.svg"; + + //assets/icons/functions_icons/automation_functions/card_unlock.svg + + static const String assetsCardUnlock = + "assets/icons/functions_icons/automation_functions/card_unlock.svg"; + + //assets/icons/functions_icons/automation_functions/doorbell.svg + + static const String assetsDoorbell = + "assets/icons/functions_icons/automation_functions/doorbell.svg"; + + //assets/icons/functions_icons/automation_functions/doorlock_normal_open.svg + + static const String assetsDoorlockNormalOpen = + "assets/icons/functions_icons/automation_functions/doorlock_normal_open.svg"; + + //assets/icons/functions_icons/automation_functions/double_lock.svg + + static const String assetsDoubleLock = + "assets/icons/functions_icons/automation_functions/double_lock.svg"; + + //assets/icons/functions_icons/automation_functions/fingerprint_unlock.svg + + static const String assetsFingerprintUnlock = + "assets/icons/functions_icons/automation_functions/fingerprint_unlock.svg"; + + //assets/icons/functions_icons/automation_functions/hijack_alarm.svg + + static const String assetsHijackAlarm = + "assets/icons/functions_icons/automation_functions/hijack_alarm.svg"; + + //assets/icons/functions_icons/automation_functions/lock_alarm.svg + + static const String assetsLockAlarm = + "assets/icons/functions_icons/automation_functions/lock_alarm.svg"; + + //assets/icons/functions_icons/automation_functions/password_unlock.svg + + static const String assetsPasswordUnlock = + "assets/icons/functions_icons/automation_functions/password_unlock.svg"; + + //assets/icons/functions_icons/automation_functions/remote_unlock_req.svg + + static const String assetsRemoteUnlockReq = + "assets/icons/functions_icons/automation_functions/remote_unlock_req.svg"; + + //assets/icons/functions_icons/automation_functions/remote_unlock_via_app.svg + + static const String assetsRemoteUnlockViaApp = + "assets/icons/functions_icons/automation_functions/remote_unlock_via_app.svg"; + + //assets/icons/functions_icons/automation_functions/residual_electricity.svg + + static const String assetsResidualElectricity = + "assets/icons/functions_icons/automation_functions/residual_electricity.svg"; + + //assets/icons/functions_icons/automation_functions/temp_password_unlock.svg + + static const String assetsTempPasswordUnlock = + "assets/icons/functions_icons/automation_functions/temp_password_unlock.svg"; + + //assets/icons/functions_icons/automation_functions/self_test_result.svg + + static const String assetsSelfTestResult = + "assets/icons/functions_icons/automation_functions/self_test_result.svg"; + + ///assets/icons/functions_icons/automation_functions/presence.svg + + static const String assetsPresence = + "assets/icons/functions_icons/automation_functions/presence.svg"; + + //assets/icons/functions_icons/automation_functions/motion.svg + + static const String assetsMotion = + "assets/icons/functions_icons/automation_functions/motion.svg"; + + //assets/icons/functions_icons/automation_functions/current_temp.svg + + static const String assetsCurrentTemp = + "assets/icons/functions_icons/automation_functions/current_temp.svg"; + + //assets/icons/functions_icons/automation_functions/presence_state.svg + + static const String assetsPresenceState = + "assets/icons/functions_icons/automation_functions/presence_state.svg"; } diff --git a/lib/my_app.dart b/lib/my_app.dart index a49fec7..f6d7a5b 100644 --- a/lib/my_app.dart +++ b/lib/my_app.dart @@ -3,6 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart'; import 'package:syncrow_app/navigation/navigation_service.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; @@ -28,7 +30,10 @@ class MyApp extends StatelessWidget { BlocProvider(create: (context) => CreateSceneBloc()), BlocProvider(create: (context) => SceneBloc()), BlocProvider(create: (context) => ProfileBloc()), - + BlocProvider(create: (context) => SmartSceneSelectBloc()), + BlocProvider( + create: (context) => EffectPeriodBloc(), + ), ], child: MaterialApp( navigatorKey: NavigationService.navigatorKey, diff --git a/lib/navigation/navigate_to_route.dart b/lib/navigation/navigate_to_route.dart new file mode 100644 index 0000000..e7c2715 --- /dev/null +++ b/lib/navigation/navigate_to_route.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +void navigateToRoute(BuildContext context, String targetRoute) { + bool routeFound = false; + + Navigator.popUntil(context, (route) { + if (route.settings.name == targetRoute) { + routeFound = true; + return true; + } + return route.isFirst; + }); + + if (!routeFound) { + Navigator.pushNamed(context, targetRoute); + } +} diff --git a/lib/navigation/router.dart b/lib/navigation/router.dart index 751d818..990b7fb 100644 --- a/lib/navigation/router.dart +++ b/lib/navigation/router.dart @@ -14,9 +14,11 @@ import 'package:syncrow_app/features/menu/view/widgets/profile/profile_view.dart import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart'; import 'package:syncrow_app/features/scene/view/device_functions_view.dart'; +import 'package:syncrow_app/features/scene/view/scene_auto_settings.dart'; import 'package:syncrow_app/features/scene/view/scene_tasks_view.dart'; import 'package:syncrow_app/features/scene/view/scene_rooms_tabbar.dart'; import 'package:syncrow_app/features/scene/view/scene_view.dart'; +import 'package:syncrow_app/features/scene/view/smart_automation_select_route.dart'; import 'package:syncrow_app/features/splash/view/splash_view.dart'; import 'routing_constants.dart'; @@ -24,54 +26,67 @@ class Router { static Route generateRoute(RouteSettings settings) { switch (settings.name) { case Routes.splash: - return MaterialPageRoute(builder: (_) => const SplashView(), settings: settings); + return MaterialPageRoute( + builder: (_) => const SplashView(), settings: settings); // case Routes.devicesRoute: // return MaterialPageRoute( // builder: (_) => const DevicesView(), settings: settings); case Routes.profileRoute: - return MaterialPageRoute(builder: (_) => const ProfileView(), settings: settings); + return MaterialPageRoute( + builder: (_) => const ProfileView(), settings: settings); case Routes.sceneRoute: - return MaterialPageRoute(builder: (_) => const SceneView(), settings: settings); + return MaterialPageRoute( + builder: (_) => const SceneView(), settings: settings); case Routes.layoutRoute: - return MaterialPageRoute(builder: (_) => const LayoutPage(), settings: settings); + return MaterialPageRoute( + builder: (_) => const LayoutPage(), settings: settings); case Routes.authLogin: - return MaterialPageRoute(builder: (_) => const LoginView(), settings: settings); + return MaterialPageRoute( + builder: (_) => const LoginView(), settings: settings); case Routes.otpRoute: - return MaterialPageRoute(builder: (_) => const OtpView(), settings: settings); + return MaterialPageRoute( + builder: (_) => const OtpView(), settings: settings); case Routes.authSignUp: - return MaterialPageRoute(builder: (_) => const SignUpView(), settings: settings); + return MaterialPageRoute( + builder: (_) => const SignUpView(), settings: settings); case Routes.dashboardRoute: - return MaterialPageRoute(builder: (_) => const DashboardView(), settings: settings); + return MaterialPageRoute( + builder: (_) => const DashboardView(), settings: settings); case Routes.homeRoute: - return MaterialPageRoute(builder: (_) => const AppLayout(), settings: settings); + return MaterialPageRoute( + builder: (_) => const AppLayout(), settings: settings); case Routes.menuRoute: - return MaterialPageRoute(builder: (_) => const MenuView(), settings: settings); + return MaterialPageRoute( + builder: (_) => const MenuView(), settings: settings); case Routes.createUnit: - return MaterialPageRoute(builder: (_) => const CreateUnitView(), settings: settings); + return MaterialPageRoute( + builder: (_) => const CreateUnitView(), settings: settings); case Routes.sceneTasksRoute: - return MaterialPageRoute(builder: (_) => const SceneTasksView(), settings: settings); + return MaterialPageRoute( + builder: (_) => const SceneTasksView(), settings: settings); case Routes.sceneControlDevicesRoute: return MaterialPageRoute( builder: (_) => MultiBlocProvider( providers: [ BlocProvider( - create: (BuildContext context) => DeviceManagerBloc()..add(FetchAllDevices()), + create: (BuildContext context) => + DeviceManagerBloc()..add(FetchAllDevices()), ), BlocProvider( - create: (BuildContext context) => - TabBarBloc(context.read()) - ..add(const TabChanged(selectedIndex: 0, roomId: '-1')), + create: (BuildContext context) => TabBarBloc( + context.read()) + ..add(const TabChanged(selectedIndex: 0, roomId: '-1')), ), ], child: const SceneRoomsTabBarDevicesView(), @@ -79,7 +94,18 @@ class Router { settings: settings); case Routes.deviceFunctionsRoute: return MaterialPageRoute( - builder: (_) => const DeviceFunctionsView(), + builder: (_) => DeviceFunctionsView(), + settings: settings, + ); + case Routes.sceneAutoSettingsRoute: + return MaterialPageRoute( + builder: (_) => const SceneAutoSettings(), + settings: settings, + ); + + case Routes.smartAutomationSelectRoute: + return MaterialPageRoute( + builder: (_) => const SmartAutomationSelectView(), settings: settings, ); default: diff --git a/lib/navigation/routing_constants.dart b/lib/navigation/routing_constants.dart index 86826ca..b64849f 100644 --- a/lib/navigation/routing_constants.dart +++ b/lib/navigation/routing_constants.dart @@ -19,6 +19,7 @@ class Routes { static const String createUnit = '/create-unit'; static const String sceneTasksRoute = '/scene-tasks'; static const String sceneControlDevicesRoute = '/scene-control-devices'; -static const String deviceFunctionsRoute = '/device-functions'; - + static const String deviceFunctionsRoute = '/device-functions'; + static const String sceneAutoSettingsRoute = '/scene-automation-settings'; + static const String smartAutomationSelectRoute = '/smart-automation-select'; } diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 3d42813..c3c2e8c 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -81,7 +81,8 @@ abstract class ApiEndpoints { static const String controlGroup = '/group/control'; //GET static const String groupBySpace = '/group/{unitUuid}'; - static const String devicesByGroupName = '/group/{unitUuid}/devices/{groupName}'; + static const String devicesByGroupName = + '/group/{unitUuid}/devices/{groupName}'; static const String groupByUuid = '/group/{groupUuid}'; //DELETE @@ -93,7 +94,8 @@ abstract class ApiEndpoints { static const String addDeviceToRoom = '/device/room'; static const String addDeviceToGroup = '/device/group'; static const String controlDevice = '/device/{deviceUuid}/control'; - static const String firmwareDevice = '/device/{deviceUuid}/firmware/{firmwareVersion}'; + static const String firmwareDevice = + '/device/{deviceUuid}/firmware/{firmwareVersion}'; static const String getDevicesByUserId = '/device/user/{userId}'; static const String getDevicesByUnitId = '/device/unit/{unitUuid}'; @@ -102,7 +104,8 @@ abstract class ApiEndpoints { static const String deviceByUuid = '/device/{deviceUuid}'; static const String deviceFunctions = '/device/{deviceUuid}/functions'; static const String gatewayApi = '/device/gateway/{gatewayUuid}/devices'; - static const String deviceFunctionsStatus = '/device/{deviceUuid}/functions/status'; + static const String deviceFunctionsStatus = + '/device/{deviceUuid}/functions/status'; ///Device Permission Module //POST @@ -114,26 +117,42 @@ abstract class ApiEndpoints { static const String assignDeviceToRoom = '/device/room'; - /// Scene API //////////////////// + /// Scene & Automation API //////////////////// /// POST static const String createScene = '/scene/tap-to-run'; static const String triggerScene = '/scene/tap-to-run/trigger/{sceneId}'; + static const String createAutomation = '/automation'; /// GET static const String getUnitScenes = '/scene/tap-to-run/{unitUuid}'; static const String getScene = '/scene/tap-to-run/details/{sceneId}'; + static const String getUnitAutomation = '/automation/{unitUuid}'; + + static const String getAutomationDetails = + '/automation/details/{automationId}'; + /// PUT static const String updateScene = '/scene/tap-to-run/{sceneId}'; + static const String updateAutomation = '/automation/{automationId}'; + + static const String updateAutomationStatus = + '/automation/status/{automationId}'; + /// DELETE static const String deleteScene = '/scene/tap-to-run/{unitUuid}/{sceneId}'; + static const String deleteAutomation = + '/automation/{unitUuid}/{automationId}'; + //////////////////////Door Lock ////////////////////// //online - static const String addTemporaryPassword = '/door-lock/temporary-password/online/{doorLockUuid}'; - static const String getTemporaryPassword = '/door-lock/temporary-password/online/{doorLockUuid}'; + static const String addTemporaryPassword = + '/door-lock/temporary-password/online/{doorLockUuid}'; + static const String getTemporaryPassword = + '/door-lock/temporary-password/online/{doorLockUuid}'; //one-time offline static const String addOneTimeTemporaryPassword = diff --git a/lib/services/api/scene_api.dart b/lib/services/api/scene_api.dart index d28c03d..e26920c 100644 --- a/lib/services/api/scene_api.dart +++ b/lib/services/api/scene_api.dart @@ -1,18 +1,21 @@ +import 'package:syncrow_app/features/scene/model/create_automation_model.dart'; import 'package:syncrow_app/features/scene/model/create_scene_model.dart'; import 'package:syncrow_app/features/scene/model/scene_details_model.dart'; import 'package:syncrow_app/features/scene/model/scenes_model.dart'; +import 'package:syncrow_app/features/scene/model/update_automation.dart'; import 'package:syncrow_app/services/api/api_links_endpoints.dart'; import 'package:syncrow_app/services/api/http_service.dart'; class SceneApi { static final HTTPService _httpService = HTTPService(); +//create scene static Future> createScene( CreateSceneModel createSceneModel) async { try { final response = await _httpService.post( path: ApiEndpoints.createScene, - body: createSceneModel.toJson(), + body: createSceneModel.toMap(), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -24,6 +27,26 @@ class SceneApi { } } +// create automation + static Future> createAutomation( + CreateAutomationModel createAutomationModel) async { + try { + final response = await _httpService.post( + path: ApiEndpoints.createAutomation, + body: createAutomationModel.toMap(), + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } catch (e) { + rethrow; + } + } + + //get scene by unit id + static Future> getScenesByUnitId(String unitId) async { try { final response = await _httpService.get( @@ -43,6 +66,27 @@ class SceneApi { } } + //getAutomation + + static Future> getAutomationByUnitId(String unitId) async { + try { + final response = await _httpService.get( + path: ApiEndpoints.getUnitAutomation.replaceAll('{unitUuid}', unitId), + showServerMessage: false, + expectedResponseModel: (json) { + List scenes = []; + for (var scene in json) { + scenes.add(ScenesModel.fromJson(scene)); + } + return scenes; + }, + ); + return response; + } catch (e) { + rethrow; + } + } + static Future triggerScene(String sceneId) async { try { final response = await _httpService.post( @@ -56,6 +100,38 @@ class SceneApi { } } +//automation details + static Future getAutomationDetails( + String automationId) async { + try { + final response = await _httpService.get( + path: ApiEndpoints.getAutomationDetails + .replaceAll('{automationId}', automationId), + showServerMessage: false, + expectedResponseModel: (json) => SceneDetailsModel.fromJson(json), + ); + return response; + } catch (e) { + rethrow; + } + } + + //updateAutomationStatus + static Future updateAutomationStatus(String automationId, + AutomationStatusUpdate createAutomationEnable) async { + try { + final response = await _httpService.put( + path: ApiEndpoints.updateAutomationStatus + .replaceAll('{automationId}', automationId), + body: createAutomationEnable.toMap(), + expectedResponseModel: (json) => json['success'], + ); + return response; + } catch (e) { + rethrow; + } + } + //getScene static Future getSceneDetails(String sceneId) async { @@ -71,7 +147,7 @@ class SceneApi { } } - //updateScene + //update Scene static updateScene(CreateSceneModel createSceneModel, String sceneId) async { try { final response = await _httpService.put( @@ -88,7 +164,26 @@ class SceneApi { } } - //deleteScene + //update automation + static updateAutomation( + CreateAutomationModel createAutomationModel, String automationId) async { + try { + final response = await _httpService.put( + path: ApiEndpoints.updateAutomation + .replaceAll('{automationId}', automationId), + body: createAutomationModel + .toJson(automationId.isNotEmpty == true ? automationId : null), + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } catch (e) { + rethrow; + } + } + + //delete Scene static Future deleteScene( {required String unitUuid, required String sceneId}) async { @@ -105,4 +200,21 @@ class SceneApi { rethrow; } } + + // delete automation + static Future deleteAutomation( + {required String unitUuid, required String automationId}) async { + try { + final response = await _httpService.delete( + path: ApiEndpoints.deleteAutomation + .replaceAll('{automationId}', automationId) + .replaceAll('{unitUuid}', unitUuid), + showServerMessage: false, + expectedResponseModel: (json) => json['statusCode'] == 200, + ); + return response; + } catch (e) { + rethrow; + } + } } diff --git a/lib/utils/context_extension.dart b/lib/utils/context_extension.dart index 1dc20e9..c9db183 100644 --- a/lib/utils/context_extension.dart +++ b/lib/utils/context_extension.dart @@ -65,6 +65,7 @@ extension ContextExtension on BuildContext { required String title, required VoidCallback onConfirm, VoidCallback? onDismiss, + bool? hideConfirmButton, }) { showDialog( context: this, @@ -110,39 +111,57 @@ extension ContextExtension on BuildContext { width: double.infinity, color: ColorsManager.greyColor, ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - GestureDetector( - onTap: onDismiss ?? - () { - Navigator.pop(context); - }, - child: Center( - child: BodyMedium( - text: 'Cancel', - style: context.bodyMedium - .copyWith(color: ColorsManager.greyColor), + hideConfirmButton != true + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + GestureDetector( + onTap: onDismiss ?? + () { + Navigator.pop(context); + }, + child: Center( + child: BodyMedium( + text: 'Cancel', + style: context.bodyMedium + .copyWith(color: ColorsManager.greyColor), + ), + ), + ), + Container( + height: 50, + width: 1, + color: ColorsManager.greyColor, + ), + GestureDetector( + onTap: onConfirm, + child: Center( + child: BodyMedium( + text: 'Confirm', + style: context.bodyMedium.copyWith( + color: + ColorsManager.primaryColorWithOpacity), + ), + ), + ), + ], + ) + : Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: GestureDetector( + onTap: onDismiss ?? + () { + Navigator.pop(context); + }, + child: Center( + child: BodyMedium( + text: 'Cancel', + style: context.bodyMedium + .copyWith(color: ColorsManager.greyColor), + ), + ), ), ), - ), - Container( - height: 50, - width: 1, - color: ColorsManager.greyColor, - ), - GestureDetector( - onTap: onConfirm, - child: Center( - child: BodyMedium( - text: 'Confirm', - style: context.bodyMedium.copyWith( - color: ColorsManager.primaryColorWithOpacity), - ), - ), - ), - ], - ) ], ), ), diff --git a/lib/utils/resource_manager/strings_manager.dart b/lib/utils/resource_manager/strings_manager.dart index 1548bb5..421a0c3 100644 --- a/lib/utils/resource_manager/strings_manager.dart +++ b/lib/utils/resource_manager/strings_manager.dart @@ -40,4 +40,5 @@ class StringsManager { static const String functions = "Functions"; static const String firstLaunch = "firstLaunch"; static const String deleteScene = 'Delete Scene'; + static const String deleteAutomation = 'Delete Automation'; } diff --git a/pubspec.yaml b/pubspec.yaml index ccc0d42..5bfe40d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -76,6 +76,7 @@ flutter: - assets/icons/MenuIcons/SecurityAndPrivacyIcons/ - assets/icons/curtainsIcon/ - assets/icons/functions_icons/ + - assets/icons/functions_icons/automation_functions/ - .env.development - .env.staging - .env.production