From e0049c0aecdd7907ee1e50445f773e247df8637a Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 8 Oct 2024 17:02:24 +0300 Subject: [PATCH] curtain wizard --- assets/icons/1gang_touch.svg | 5 + assets/icons/2gang_touch.svg | 6 + assets/icons/3gang_touch.svg | 7 + .../bloc/curtain_bloc/curtain_bloc.dart | 170 +++++++++++++++++- .../bloc/curtain_bloc/curtain_event.dart | 19 +- .../bloc/curtain_bloc/curtain_state.dart | 26 ++- lib/features/devices/model/curtain_model.dart | 22 +++ .../devices/model/device_category_model.dart | 10 +- lib/features/devices/model/device_model.dart | 6 +- .../devices/model/group_curtain_model.dart | 13 ++ .../view/widgets/curtains/curtains_list.dart | 77 ++++++++ .../widgets/curtains/curtains_wizard.dart | 49 +++++ .../devices/view/widgets/wizard_page.dart | 8 + lib/generated/assets.dart | 3 + lib/services/api/devices_api.dart | 4 +- pubspec.yaml | 2 +- 16 files changed, 412 insertions(+), 15 deletions(-) create mode 100644 assets/icons/1gang_touch.svg create mode 100644 assets/icons/2gang_touch.svg create mode 100644 assets/icons/3gang_touch.svg create mode 100644 lib/features/devices/model/curtain_model.dart create mode 100644 lib/features/devices/model/group_curtain_model.dart create mode 100644 lib/features/devices/view/widgets/curtains/curtains_list.dart create mode 100644 lib/features/devices/view/widgets/curtains/curtains_wizard.dart diff --git a/assets/icons/1gang_touch.svg b/assets/icons/1gang_touch.svg new file mode 100644 index 0000000..23dee22 --- /dev/null +++ b/assets/icons/1gang_touch.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/2gang_touch.svg b/assets/icons/2gang_touch.svg new file mode 100644 index 0000000..9e55aa5 --- /dev/null +++ b/assets/icons/2gang_touch.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/3gang_touch.svg b/assets/icons/3gang_touch.svg new file mode 100644 index 0000000..701fd32 --- /dev/null +++ b/assets/icons/3gang_touch.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/lib/features/devices/bloc/curtain_bloc/curtain_bloc.dart b/lib/features/devices/bloc/curtain_bloc/curtain_bloc.dart index 7e0ac56..15571e5 100644 --- a/lib/features/devices/bloc/curtain_bloc/curtain_bloc.dart +++ b/lib/features/devices/bloc/curtain_bloc/curtain_bloc.dart @@ -1,7 +1,11 @@ import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_event.dart'; import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_state.dart'; +import 'package:syncrow_app/features/devices/model/curtain_model.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/devices/model/group_curtain_model.dart'; import 'package:syncrow_app/features/devices/model/status_model.dart'; import 'package:syncrow_app/services/api/devices_api.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; @@ -22,9 +26,14 @@ class CurtainBloc extends Bloc { on(_onOpenCurtain); on(_onCloseCurtain); on(_onPauseCurtain); + on(_changeFirstWizardSwitch); + on(_fetchOneTouchWizardStatus); + on(_groupAllOff); + on(_groupAllOn); } - Future _onOpenCurtain(OpenCurtain event, Emitter emit) async { + Future _onOpenCurtain( + OpenCurtain event, Emitter emit) async { isMoving = true; while (openPercentage < 100.0) { if (state is CurtainsClosing) { @@ -71,7 +80,8 @@ class CurtainBloc extends Bloc { } } - Future _onCloseCurtain(CloseCurtain event, Emitter emit) async { + Future _onCloseCurtain( + CloseCurtain event, Emitter emit) async { isMoving = true; while (openPercentage > 0.0) { if (state is CurtainsOpening) { @@ -118,7 +128,8 @@ class CurtainBloc extends Bloc { } } - Future _onPauseCurtain(PauseCurtain event, Emitter emit) async { + Future _onPauseCurtain( + PauseCurtain event, Emitter emit) async { _pauseCurtain(emit); await DevicesAPI.controlDevice( DeviceControlModel( @@ -172,4 +183,157 @@ class CurtainBloc extends Bloc { return; } } + + List groupList = []; + bool allSwitchesOn = true; + List devicesList = []; + CurtainModel deviceStatus = CurtainModel( + control: 'stop', + ); + void _fetchOneTouchWizardStatus( + InitialWizardEvent event, Emitter emit) async { + emit(CurtainLoadingState()); + try { + devicesList = []; + groupList = []; + allSwitchesOn = true; + devicesList = await DevicesAPI.getDeviceByGroupName( + HomeCubit.getInstance().selectedSpace?.id ?? '', 'CUR'); + + for (int i = 0; i < devicesList.length; i++) { + var response = + await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? ''); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = CurtainModel.fromJson(statusModelList); + groupList.add(GroupCurtainModel( + deviceId: devicesList[i].uuid ?? '', + deviceName: devicesList[i].name ?? '', + firstSwitch: deviceStatus.control, + percentControl: 0)); + } + + if (groupList.isNotEmpty) { + groupList.firstWhere((element) { + print('object=====${element.firstSwitch}'); + if (element.firstSwitch == 'close') { + allSwitchesOn = false; + } + return true; + }); + } + emit( + UpdateGroupState(curtainList: groupList, allSwitches: allSwitchesOn)); + } catch (e) { + emit(FailedState()); + return; + } + } + + void _changeFirstWizardSwitch(ChangeFirstWizardSwitchStatusEvent event, + Emitter emit) async { + emit(LoadingNewSate(curtainModel: deviceStatus)); + try { + bool allSwitchesValue = true; + + // Update the firstSwitch value in the groupList based on the deviceId + groupList.forEach((element) { + if (element.deviceId == event.deviceId) { + element.firstSwitch = event.value; // Set the new value from the event + } + if (element.firstSwitch != 'open') { + allSwitchesValue = false; // Check if any switch is not 'open' + } + }); + + final response = await DevicesAPI.deviceBatchController( + code: 'control', + devicesUuid: [event.deviceId], + value: event.value, // Use the value from the event + ); + + emit(UpdateGroupState( + curtainList: groupList, allSwitches: allSwitchesValue)); + + if (response['success']) { + // Optionally add an initial event if needed. + } + } catch (_) { + // Handle the error if needed. + } + } + + void _groupAllOn(GroupAllOnEvent event, Emitter emit) async { + emit(LoadingNewSate(curtainModel: deviceStatus)); + try { + // Set all devices to 'open' + for (int i = 0; i < groupList.length; i++) { + groupList[i].firstSwitch = 'open'; + } + + emit(UpdateGroupState(curtainList: groupList, allSwitches: true)); + + List allDeviceIds = + groupList.map((device) => device.deviceId).toList(); + + final response1 = await DevicesAPI.deviceBatchController( + code: 'control', + devicesUuid: allDeviceIds, + value: 'open', // Set the devices to 'open' + ); + + final response2 = await DevicesAPI.deviceBatchController( + code: 'control', + devicesUuid: allDeviceIds, + value: 'open', + ); + + if (response1['failedResults'].toString() != '[]') { + await Future.delayed(const Duration(milliseconds: 500)); + // Handle retry or error if needed. + } + } catch (_) { + emit(FailedState()); + await Future.delayed(const Duration(milliseconds: 500)); + // Optionally add an initial event if needed. + } + } + + void _groupAllOff(GroupAllOffEvent event, Emitter emit) async { + emit(LoadingNewSate(curtainModel: deviceStatus)); + try { + // Set all devices to 'close' + for (int i = 0; i < groupList.length; i++) { + groupList[i].firstSwitch = 'close'; + } + + emit(UpdateGroupState(curtainList: groupList, allSwitches: false)); + + List allDeviceIds = + groupList.map((device) => device.deviceId).toList(); + + final response1 = await DevicesAPI.deviceBatchController( + code: 'control', + devicesUuid: allDeviceIds, + value: 'close', // Set the devices to 'close' + ); + + final response2 = await DevicesAPI.deviceBatchController( + code: 'control', + devicesUuid: allDeviceIds, + value: 'close', + ); + + if (response1['failedResults'].toString() != '[]') { + await Future.delayed(const Duration(milliseconds: 500)); + // Handle retry or error if needed. + } + } catch (_) { + emit(FailedState()); + await Future.delayed(const Duration(milliseconds: 500)); + // Optionally add an initial event if needed. + } + } } diff --git a/lib/features/devices/bloc/curtain_bloc/curtain_event.dart b/lib/features/devices/bloc/curtain_bloc/curtain_event.dart index 799946e..11b7d9e 100644 --- a/lib/features/devices/bloc/curtain_bloc/curtain_event.dart +++ b/lib/features/devices/bloc/curtain_bloc/curtain_event.dart @@ -31,4 +31,21 @@ class CloseCurtain extends CurtainEvent { class InitCurtain extends CurtainEvent {} class PauseCurtain extends CurtainEvent {} -class useCurtainEvent extends CurtainEvent {} \ No newline at end of file +class useCurtainEvent extends CurtainEvent {} +class InitialWizardEvent extends CurtainEvent {} + + +class ChangeFirstWizardSwitchStatusEvent extends CurtainEvent { + final String value; + final String deviceId; + const ChangeFirstWizardSwitchStatusEvent( + {required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + + + +class GroupAllOnEvent extends CurtainEvent {} + +class GroupAllOffEvent extends CurtainEvent {} \ No newline at end of file diff --git a/lib/features/devices/bloc/curtain_bloc/curtain_state.dart b/lib/features/devices/bloc/curtain_bloc/curtain_state.dart index fd94ab1..1ba4982 100644 --- a/lib/features/devices/bloc/curtain_bloc/curtain_state.dart +++ b/lib/features/devices/bloc/curtain_bloc/curtain_state.dart @@ -1,5 +1,7 @@ // curtain_state.dart import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/curtain_model.dart'; +import 'package:syncrow_app/features/devices/model/group_curtain_model.dart'; abstract class CurtainState extends Equatable { const CurtainState(); @@ -10,8 +12,9 @@ abstract class CurtainState extends Equatable { class CurtainInitial extends CurtainState {} -class UpdateCurtain extends CurtainState { +class LoadingInitialState extends CurtainState {} +class UpdateCurtain extends CurtainState { final double curtainWidth; final double blindHeight; final double openPercentage; @@ -73,4 +76,23 @@ class CurtainsPaused extends CurtainState { @override List get props => [curtainWidth, blindHeight, openPercentage]; -} \ No newline at end of file +} + +class UpdateGroupState extends CurtainState { + final List curtainList; + final bool allSwitches; + + const UpdateGroupState( + {required this.curtainList, required this.allSwitches}); + + @override + List get props => [curtainList, allSwitches]; +} + +class LoadingNewSate extends CurtainState { + final CurtainModel curtainModel; + const LoadingNewSate({required this.curtainModel}); + + @override + List get props => [curtainModel]; +} diff --git a/lib/features/devices/model/curtain_model.dart b/lib/features/devices/model/curtain_model.dart new file mode 100644 index 0000000..4150613 --- /dev/null +++ b/lib/features/devices/model/curtain_model.dart @@ -0,0 +1,22 @@ +import 'package:syncrow_app/features/devices/model/status_model.dart'; + +class CurtainModel { + String control; + + CurtainModel({ + required this.control, + }); + + factory CurtainModel.fromJson(List jsonList) { + late String _control; + + for (int i = 0; i < jsonList.length; i++) { + if (jsonList[i].code == 'control') { + _control = jsonList[i].value ?? false; + } + } + return CurtainModel( + control: _control, + ); + } +} diff --git a/lib/features/devices/model/device_category_model.dart b/lib/features/devices/model/device_category_model.dart index ccd2e0f..d5e214e 100644 --- a/lib/features/devices/model/device_category_model.dart +++ b/lib/features/devices/model/device_category_model.dart @@ -42,7 +42,9 @@ class DevicesCategoryModel { : name = json['groupName'], // id = json['groupId'], type = devicesTypesMap[json['groupName']] ?? DeviceType.Other, - icon = deviceTypeIconMap[devicesTypesMap[json['groupName']] ?? DeviceType.Other] ?? '', + icon = deviceTypeIconMap[ + devicesTypesMap[json['groupName']] ?? DeviceType.Other] ?? + '', devices = [], isSelected = false; @@ -65,8 +67,8 @@ Map deviceTypeIconMap = { DeviceType.WH: Assets.waterHeaterIcon, DeviceType.DS: Assets.doorSensorIcon, DeviceType.Other: Assets.assetsIconsAC, - DeviceType.OneTouch: Assets.oneGang, - DeviceType.TowTouch: Assets.twoGang, - DeviceType.ThreeTouch: Assets.assetsIconsGang, + DeviceType.OneTouch: Assets.gang1touch, + DeviceType.TowTouch: Assets.gang2touch, + DeviceType.ThreeTouch: Assets.gang3touch, DeviceType.GarageDoor: Assets.garageIcon }; diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index 799faa3..65bc021 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -67,13 +67,13 @@ class DeviceModel { } else if (type == DeviceType.DS) { tempIcon = Assets.doorSensorIcon; } else if (type == DeviceType.OneTouch) { - tempIcon = Assets.oneGang; + tempIcon = Assets.gang1touch; } else if (type == DeviceType.TowTouch) { - tempIcon = Assets.twoGang; + tempIcon = Assets.gang2touch; } else if (type == DeviceType.GarageDoor) { tempIcon = Assets.garageIcon; } else if (type == DeviceType.ThreeTouch) { - tempIcon = Assets.assetsIcons3GangSwitch; + tempIcon = Assets.gang3touch; } else if (type == DeviceType.WaterLeak) { tempIcon = Assets.waterLeakIcon; } else { diff --git a/lib/features/devices/model/group_curtain_model.dart b/lib/features/devices/model/group_curtain_model.dart new file mode 100644 index 0000000..44329fc --- /dev/null +++ b/lib/features/devices/model/group_curtain_model.dart @@ -0,0 +1,13 @@ +class GroupCurtainModel { + final String deviceId; + final String deviceName; + String firstSwitch; + int percentControl; + + GroupCurtainModel({ + required this.deviceId, + required this.deviceName, + required this.firstSwitch, + required this.percentControl, + }); +} diff --git a/lib/features/devices/view/widgets/curtains/curtains_list.dart b/lib/features/devices/view/widgets/curtains/curtains_list.dart new file mode 100644 index 0000000..d09b2e0 --- /dev/null +++ b/lib/features/devices/view/widgets/curtains/curtains_list.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_event.dart'; +import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_state.dart'; + +import 'package:syncrow_app/features/devices/model/group_curtain_model.dart'; + +import 'package:syncrow_app/features/shared_widgets/devices_default_switch.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; + +class CurtainsList extends StatelessWidget { + const CurtainsList( + {super.key, required this.curtainsList, required this.allSwitches}); + + final List curtainsList; + final bool allSwitches; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 10), + const BodySmall(text: 'All Curtains'), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: allSwitches, + action: () { + BlocProvider.of(context).add(GroupAllOnEvent()); + }, + secondAction: () { + BlocProvider.of(context).add(GroupAllOffEvent()); + }, + ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.all(0), + itemCount: curtainsList.length, + itemBuilder: (context, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + BodySmall(text: curtainsList[index].deviceName), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: curtainsList[index].firstSwitch == 'open', + action: () { + // Toggle between 'open' and 'close' based on current value + final newValue = + curtainsList[index].firstSwitch == 'open' + ? 'close' + : 'open'; + BlocProvider.of(context).add( + ChangeFirstWizardSwitchStatusEvent( + value: newValue, + deviceId: curtainsList[index].deviceId, + ), + ); + }, + ), + ], + ); + }, + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/devices/view/widgets/curtains/curtains_wizard.dart b/lib/features/devices/view/widgets/curtains/curtains_wizard.dart new file mode 100644 index 0000000..a46cf5a --- /dev/null +++ b/lib/features/devices/view/widgets/curtains/curtains_wizard.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_event.dart'; +import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_state.dart'; + +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/group_curtain_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/curtains/curtains_list.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; + +class CurtainsWizard extends StatelessWidget { + const CurtainsWizard({super.key, this.device}); + + final DeviceModel? device; + + @override + Widget build(BuildContext context) { + List groupOneTouchModel = []; + + return DefaultScaffold( + title: 'Curtain', + child: BlocProvider( + create: (context) => + CurtainBloc(device?.uuid ?? '')..add(InitialWizardEvent()), + child: BlocBuilder( + builder: (context, state) { + bool allSwitchesOn = false; + if (state is UpdateGroupState) { + groupOneTouchModel = state.curtainList; + allSwitchesOn = state.allSwitches; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : CurtainsList( + curtainsList: groupOneTouchModel, + allSwitches: allSwitchesOn, + ); + }, + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/wizard_page.dart b/lib/features/devices/view/widgets/wizard_page.dart index 3b62d1c..4409a24 100644 --- a/lib/features/devices/view/widgets/wizard_page.dart +++ b/lib/features/devices/view/widgets/wizard_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:syncrow_app/features/devices/model/device_category_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart'; +import 'package:syncrow_app/features/devices/view/widgets/curtains/curtains_wizard.dart'; import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_wizard.dart'; import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_wizard.dart'; import 'package:syncrow_app/features/devices/view/widgets/one_touch/one_touch_wizard.dart'; @@ -100,6 +101,13 @@ class WizardPage extends StatelessWidget { pageBuilder: (context, animation1, animation2) => const GarageWizard())); } + if (groupsList[index].name == 'CUR') { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + const CurtainsWizard())); + } }, child: DefaultContainer( padding: const EdgeInsets.all(15), diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 49ab0d4..acf46f7 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -1078,5 +1078,8 @@ class Assets { static const String waterLeakIcon = "assets/icons/waterleak_icon.svg"; static const String leakDetectedIcon = "assets/icons/leak_detected.svg"; static const String leakNormalIcon = "assets/icons/leak_normal_icon.svg"; + static const String gang1touch = "assets/icons/1gang_touch.svg"; + static const String gang2touch = "assets/icons/2gang_touch.svg"; + static const String gang3touch = "assets/icons/3gang_touch.svg"; //leakNormalIcon } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index bebdad2..7186eb6 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -384,13 +384,15 @@ class DevicesAPI { static Future deviceBatchController({ List? devicesUuid, String? code, - bool? value, + var value, }) async { + print({"devicesUuid": devicesUuid, "code": code, "value": value}); final response = await _httpService.post( path: ApiEndpoints.controlBatch, body: {"devicesUuid": devicesUuid, "code": code, "value": value}, showServerMessage: true, expectedResponseModel: (json) { + print('json-=-=-=-=-=-${json}'); return json; }, ); diff --git a/pubspec.yaml b/pubspec.yaml index 2eadacd..920357b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: This is the mobile application project, developed with Flutter for # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: "none" # Remove this line if you wish to publish to pub.dev -version: 1.0.4+26 +version: 1.0.4+27 environment: sdk: ">=3.0.6 <4.0.0"