diff --git a/assets/icons/schedule_Inching_icon.svg b/assets/icons/schedule_Inching_icon.svg new file mode 100644 index 0000000..d68c04b --- /dev/null +++ b/assets/icons/schedule_Inching_icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/schedule_celender_icon.svg b/assets/icons/schedule_celender_icon.svg new file mode 100644 index 0000000..8c00844 --- /dev/null +++ b/assets/icons/schedule_celender_icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/icons/schedule_circulate_icon.svg b/assets/icons/schedule_circulate_icon.svg new file mode 100644 index 0000000..d5a3fdc --- /dev/null +++ b/assets/icons/schedule_circulate_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/schedule_time_icon.svg b/assets/icons/schedule_time_icon.svg new file mode 100644 index 0000000..7cfd4b7 --- /dev/null +++ b/assets/icons/schedule_time_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/water_heater_off.svg b/assets/icons/water_heater_off.svg new file mode 100644 index 0000000..40ab6fb --- /dev/null +++ b/assets/icons/water_heater_off.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/water_heater_on.svg b/assets/icons/water_heater_on.svg new file mode 100644 index 0000000..f26cb36 --- /dev/null +++ b/assets/icons/water_heater_on.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/features/devices/bloc/water_heater_bloc/water_heater_bloc.dart b/lib/features/devices/bloc/water_heater_bloc/water_heater_bloc.dart new file mode 100644 index 0000000..3ff1747 --- /dev/null +++ b/lib/features/devices/bloc/water_heater_bloc/water_heater_bloc.dart @@ -0,0 +1,311 @@ +import 'dart:async'; +import 'package:dio/dio.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/water_heater_bloc/water_heater_event.dart'; +import 'package:syncrow_app/features/devices/bloc/water_heater_bloc/water_heater_state.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/schedule_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/features/devices/model/water_heater.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; + +class WaterHeaterBloc extends Bloc { + final String whId; + final String switchCode; + WHModel deviceStatus = WHModel( + firstSwitch: false,firstCountDown: 0 + ); + List deviceStatusList = []; + List devicesList = []; + bool allAcsPage = false; + bool allAcsOn = true; + bool allTempSame = true; + int globalTemp = 25; + Timer? _timer; + + + bool toggleSchedule = true; + List selectedDays = []; + bool createSchedule = false; + bool createCirculate = false; + List listSchedule = []; + DateTime? selectedTime=DateTime.now(); + + WaterHeaterBloc({required this.whId,required this.switchCode}) : super(WHInitialState()) { + on(_fetchWaterHeaterStatus); + on(_changeFirstSwitch); + + on(_setCounterValue); + on(_getCounterValue); + + on(toggleDaySelection); + on(saveSchedule); + on(getSchedule); + on(toggleChange); + on(deleteSchedule); + on(_onTickTimer); + on(_onClose); + } + + void _fetchWaterHeaterStatus(WaterHeaterInitial event, Emitter emit) async { + emit(WHLoadingState()); + try { + var response = await DevicesAPI.getDeviceStatus(whId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = WHModel.fromJson(statusModelList, ); + emit(UpdateState(whModel: deviceStatus)); + Future.delayed(const Duration(milliseconds: 500)); + // _listenToChanges(); + } catch (e) { + emit(WHFailedState(errorMessage: e.toString())); + return; + } + } + + + void _changeFirstSwitch(WaterHeaterSwitch event, Emitter emit) async { + emit(LoadingNewSate(whModel: deviceStatus)); + try { + deviceStatus.firstSwitch = !event.whSwitch; + emit(UpdateState(whModel: deviceStatus)); + if (_timer != null) { + _timer!.cancel(); + } + _timer = Timer(const Duration(milliseconds: 500), () async { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: whId, + code: 'switch_1', + value: deviceStatus.firstSwitch ), + whId); + + if (!response['success']) { + // add(InitialEvent(groupScreen: oneGangGroup)); + } + }); + } catch (_) { + emit(WHFailedState(errorMessage: _.toString())); + } + } + + + + + + //=====================---------- timer ---------------------------------------- + + void _setCounterValue(SetCounterValue event, Emitter emit) async { + emit(LoadingNewSate(whModel: deviceStatus)); + int seconds = 0; + try { + seconds = event.duration.inSeconds; + final response = await DevicesAPI.controlDevice( + DeviceControlModel(deviceId: whId, code: event.deviceCode, value: seconds), + whId); + + if (response['success'] ?? false) { + if (event.deviceCode == 'countdown_1') { + deviceStatus.firstCountDown = seconds; + } + } else { + emit(const WHFailedState(errorMessage: 'Something went wrong')); + return; + } + } catch (e) { + emit(const WHFailedState(errorMessage: 'Something went wrong')); + return; + } + if (seconds > 0) { + _onStartTimer(seconds); + } else { + _timer?.cancel(); + emit(TimerRunComplete()); + } + } + + void _getCounterValue(GetCounterEvent event, Emitter emit) async { + emit(WHLoadingState()); + try { + var response = await DevicesAPI.getDeviceStatus(whId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = WHModel.fromJson(statusModelList); + + if (event.deviceCode == 'countdown_1') { + deviceStatus.firstCountDown > 0 + ? _onStartTimer(deviceStatus.firstCountDown) + : emit(UpdateTimerState(seconds: deviceStatus.firstCountDown)); + } + } catch (e) { + WHFailedState(errorMessage: e.toString()); + return; + } + } + + void _onClose(OnClose event, Emitter emit) { + _timer?.cancel(); + } + + void _onStartTimer(int seconds) { + _timer?.cancel(); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + add(TickTimer(seconds - timer.tick)); + }); + } + + void _onTickTimer(TickTimer event, Emitter emit) { + if (event.remainingTime > 0) { + emit(TimerRunInProgress(event.remainingTime)); + } else { + _timer?.cancel(); + emit(TimerRunComplete()); + } + } + + + + + //=====================---------- Schedule ---------------------------------------- + + + + List> days = [ + {"day": "Sun", "key": "Sun"}, + {"day": "Mon", "key": "Mon"}, + {"day": "Tue", "key": "Tue"}, + {"day": "Wed", "key": "Wed"}, + {"day": "Thu", "key": "Thu"}, + {"day": "Fri", "key": "Fri"}, + {"day": "Sat", "key": "Sat"}, + ]; + + Future saveSchedule(ScheduleSave event, Emitter emit,) async { + try { + if(selectedDays.isNotEmpty) { + emit(WHLoadingState()); + final response = await DevicesAPI.postSchedule( + category: switchCode, + deviceId: whId, + time: getTimeStampWithoutSeconds(selectedTime).toString(), + code: switchCode, + value: toggleSchedule, + days: selectedDays); + CustomSnackBar.displaySnackBar('Save Successfully'); + add(GetScheduleEvent()); + emit(SaveSchedule()); + toggleCreateSchedule(); + }else{ + CustomSnackBar.displaySnackBar('Please select days'); + } + } catch (e) { + emit(WHFailedState(errorMessage:e.toString())); + } + } + + Future getSchedule(GetScheduleEvent event, Emitter emit,) async { + try { + emit(WHLoadingState()); + final response = await DevicesAPI.getSchedule( + category: switchCode, + deviceId: whId , + ); + List jsonData = response; + listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList(); + emit(WHInitialState()); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(WHFailedState(errorMessage: errorMessage.toString())); + } + } + + int? getTimeStampWithoutSeconds(DateTime? dateTime) { + if (dateTime == null) return null; + DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, + dateTime.day, dateTime.hour, dateTime.minute); + return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000; + } + + Future toggleChange( + ToggleScheduleEvent event, Emitter emit) async { + try { + emit(WHLoadingState()); + final response = await DevicesAPI.changeSchedule( + scheduleId: event.id, deviceUuid: whId, enable: event.toggle); + if (response == true) { + add(GetScheduleEvent()); + toggleSchedule = event.toggle; + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(WHFailedState(errorMessage: errorMessage.toString())); + } + } + + Future deleteSchedule( + DeleteScheduleEvent event, Emitter emit) async { + try { + emit(WHLoadingState()); + final response = await DevicesAPI.deleteSchedule( + scheduleId: event.id, + deviceUuid: whId, ); + if (response == true) { + add(GetScheduleEvent()); + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(WHFailedState(errorMessage: errorMessage.toString())); + } + } + + void toggleCreateSchedule() { + emit(WHLoadingState()); + createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime=DateTime.now(); + emit(UpdateCreateScheduleState(createSchedule)); + } + + void toggleCreateCirculate() { + emit(WHLoadingState()); + createCirculate = !createCirculate; + selectedDays.clear(); + selectedTime=DateTime.now(); + emit(UpdateCreateScheduleState(createCirculate)); + } + + Future toggleDaySelection( + ToggleDaySelectionEvent event, + Emitter emit, + ) async { + emit(WHLoadingState()); + if (selectedDays.contains(event.key)) { + selectedDays.remove(event.key); + } else { + selectedDays.add(event.key); + } + emit(ChangeTimeState()); + } + + int selectedTabIndex = 0; + + void toggleSelectedIndex(index) { + emit(WHLoadingState()); + selectedTabIndex = index; + emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + } + +} diff --git a/lib/features/devices/bloc/water_heater_bloc/water_heater_event.dart b/lib/features/devices/bloc/water_heater_bloc/water_heater_event.dart new file mode 100644 index 0000000..c19c04e --- /dev/null +++ b/lib/features/devices/bloc/water_heater_bloc/water_heater_event.dart @@ -0,0 +1,93 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +abstract class WaterHeaterEvent extends Equatable { + const WaterHeaterEvent(); + + @override + List get props => []; +} + +class WaterHeaterLoading extends WaterHeaterEvent {} + +class WaterHeaterSwitch extends WaterHeaterEvent { + final bool whSwitch; + final String deviceId; + final String productId; + const WaterHeaterSwitch({required this.whSwitch, this.deviceId = '', this.productId = ''}); + + @override + List get props => [whSwitch, deviceId, productId]; +} + +class WaterHeaterUpdated extends WaterHeaterEvent {} + +class WaterHeaterInitial extends WaterHeaterEvent { + const WaterHeaterInitial(); +} + +class WaterHeaterChangeStatus extends WaterHeaterEvent {} + + +class GetCounterEvent extends WaterHeaterEvent { + final String deviceCode; + const GetCounterEvent({required this.deviceCode}); + @override + List get props => [deviceCode]; +} + +class SetCounterValue extends WaterHeaterEvent { + final Duration duration; + final String deviceCode; + const SetCounterValue({required this.duration, required this.deviceCode}); + @override + List get props => [duration, deviceCode]; +} + +class StartTimer extends WaterHeaterEvent { + final int duration; + + const StartTimer(this.duration); + + @override + List get props => [duration]; +} + +class TickTimer extends WaterHeaterEvent { + final int remainingTime; + + const TickTimer(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class StopTimer extends WaterHeaterEvent {} + +class OnClose extends WaterHeaterEvent {} + + +//------------------- Schedule ----------=--------- +class GetScheduleEvent extends WaterHeaterEvent {} +class ScheduleSave extends WaterHeaterEvent {} +class ToggleScheduleEvent extends WaterHeaterEvent { + final String id; + final bool toggle; + const ToggleScheduleEvent({required this.toggle,required this.id}); + @override + List get props => [toggle,id]; +} +class ToggleDaySelectionEvent extends WaterHeaterEvent { + + final String key; + + const ToggleDaySelectionEvent({required this.key}); + @override + List get props => [key]; +} +class DeleteScheduleEvent extends WaterHeaterEvent { + final String id; + const DeleteScheduleEvent({required this.id}); + @override + List get props => [id]; +} diff --git a/lib/features/devices/bloc/water_heater_bloc/water_heater_state.dart b/lib/features/devices/bloc/water_heater_bloc/water_heater_state.dart new file mode 100644 index 0000000..a26b51a --- /dev/null +++ b/lib/features/devices/bloc/water_heater_bloc/water_heater_state.dart @@ -0,0 +1,131 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/ac_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/water_heater.dart'; + +abstract class WaterHeaterState extends Equatable { + const WaterHeaterState(); + + @override + List get props => []; +} + +class WHInitialState extends WaterHeaterState {} + +class WHLoadingState extends WaterHeaterState {} + +class WHChangeLoading extends WaterHeaterState { + // final WHStatusModel WHStatusModel; + const WHChangeLoading( + // {required this. WHStatusModel} + ); + + // @override + // List get props => [acStatusModel]; +} + +class WHModifyingState extends WaterHeaterState { + final WHModel whStatusModel; + const WHModifyingState({required this.whStatusModel}); + + @override + List get props => [whStatusModel]; +} + +class GetWHStatusState extends WaterHeaterState { + final WHModel whStatusModel; + const GetWHStatusState({required this.whStatusModel}); + + @override + List get props => [whStatusModel]; +} + +class GetAllAcsStatusState extends WaterHeaterState { + final List allAcsStatues; + final List allAcs; + final bool allOn; + final bool allTempSame; + final int temp; + + const GetAllAcsStatusState( + {required this.allAcsStatues, + required this.allAcs, + required this.allOn, + required this.allTempSame, + required this.temp}); + + @override + List get props => [allAcsStatues, allAcs, allAcs, allTempSame, temp]; +} + +class WHFailedState extends WaterHeaterState { + final String errorMessage; + + const WHFailedState({required this.errorMessage}); + + @override + List get props => [errorMessage]; +} + + +class LoadingNewSate extends WaterHeaterState { + final WHModel whModel; + const LoadingNewSate({required this.whModel}); + + @override + List get props => [whModel]; +} +class TimerRunComplete extends WaterHeaterState {} +class UpdateTimerState extends WaterHeaterState { + final int seconds; + const UpdateTimerState({required this.seconds}); + + @override + List get props => [seconds]; +} + +class TimerRunInProgress extends WaterHeaterState { + final int remainingTime; + + const TimerRunInProgress(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class SaveSchedule extends WaterHeaterState {} +class ChangeTimeState extends WaterHeaterState {} +class UpdateCreateScheduleState extends WaterHeaterState { + final bool createSchedule; + UpdateCreateScheduleState(this.createSchedule); +} +class IsToggleState extends WaterHeaterState { + final bool? onOff; + const IsToggleState({this.onOff}); +} + + +class UpdateState extends WaterHeaterState { + final WHModel whModel; + const UpdateState({required this.whModel}); + + @override + List get props => [WHModel]; +} + +class ChangeSwitchStatusEvent extends WaterHeaterState { + final bool value; + final String deviceId; + const ChangeSwitchStatusEvent({required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class ChangeSlidingSegmentState extends WaterHeaterState { + final int value; + + const ChangeSlidingSegmentState({required this.value}); + + @override + List get props => [value]; +} \ No newline at end of file diff --git a/lib/features/devices/model/water_heater.dart b/lib/features/devices/model/water_heater.dart new file mode 100644 index 0000000..176579a --- /dev/null +++ b/lib/features/devices/model/water_heater.dart @@ -0,0 +1,141 @@ +// +// +// import 'package:syncrow_app/features/devices/model/status_model.dart'; +// +// class WHModel { +// final int activeTime; +// final String category; +// final String categoryName; +// final int createTime; +// final String gatewayId; +// final String icon; +// final String ip; +// final String lat; +// final String lon; +// final String localKey; +// final String model; +// final String name; +// final String nodeId; +// final bool online; +// final String ownerId; +// final bool sub; +// final String timeZone; +// final int updateTime; +// final String uuid; +// final String productUuid; +// final String productType; +// final String permissionType; +// +// WHModel({ +// required this.activeTime, +// required this.category, +// required this.categoryName, +// required this.createTime, +// required this.gatewayId, +// required this.icon, +// required this.ip, +// required this.lat, +// required this.lon, +// required this.localKey, +// required this.model, +// required this.name, +// required this.nodeId, +// required this.online, +// required this.ownerId, +// required this.sub, +// required this.timeZone, +// required this.updateTime, +// required this.uuid, +// required this.productUuid, +// required this.productType, +// required this.permissionType, +// }); +// +// // Factory method to create a SmartDevice object from JSON +// factory WHModel.fromJson(Map json, List statusModelList) { +// return WHModel( +// activeTime: json['activeTime'], +// category: json['category'], +// categoryName: json['categoryName'], +// createTime: json['createTime'], +// gatewayId: json['gatewayId'], +// icon: json['icon'], +// ip: json['ip'], +// lat: json['lat'], +// lon: json['lon'], +// localKey: json['localKey'], +// model: json['model'], +// name: json['name'], +// nodeId: json['nodeId'], +// online: json['online'], +// ownerId: json['ownerId'], +// sub: json['sub'], +// timeZone: json['timeZone'], +// updateTime: json['updateTime'], +// uuid: json['uuid'], +// productUuid: json['productUuid'], +// productType: json['productType'], +// permissionType: json['permissionType'], +// ); +// } +// +// // Method to convert SmartDevice object to JSON +// Map toJson() { +// return { +// 'activeTime': activeTime, +// 'category': category, +// 'categoryName': categoryName, +// 'createTime': createTime, +// 'gatewayId': gatewayId, +// 'icon': icon, +// 'ip': ip, +// 'lat': lat, +// 'lon': lon, +// 'localKey': localKey, +// 'model': model, +// 'name': name, +// 'nodeId': nodeId, +// 'online': online, +// 'ownerId': ownerId, +// 'sub': sub, +// 'timeZone': timeZone, +// 'updateTime': updateTime, +// 'uuid': uuid, +// 'productUuid': productUuid, +// 'productType': productType, +// 'permissionType': permissionType, +// }; +// } +// } + + + +import 'package:syncrow_app/features/devices/model/status_model.dart'; + +class WHModel { + bool firstSwitch; + int firstCountDown; + + WHModel( + {required this.firstSwitch, + required this.firstCountDown, + }); + + factory WHModel.fromJson(List jsonList) { + late bool _switch; + late int _count; + + + for (int i = 0; i < jsonList.length; i++) { + if (jsonList[i].code == 'switch_1') { + _switch = jsonList[i].value ?? false; + } else if (jsonList[i].code == 'countdown_1') { + _count = jsonList[i].value ?? 0; + } + } + return WHModel( + firstSwitch: _switch, + firstCountDown: _count, + ); + } +} diff --git a/lib/features/devices/view/widgets/room_page_switch.dart b/lib/features/devices/view/widgets/room_page_switch.dart index cfb5445..acd7669 100644 --- a/lib/features/devices/view/widgets/room_page_switch.dart +++ b/lib/features/devices/view/widgets/room_page_switch.dart @@ -14,11 +14,11 @@ import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_Interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_screen.dart'; import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_Interface.dart'; -import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_screen.dart'; import 'package:syncrow_app/features/devices/view/widgets/wall_sensor/wall_sensor_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/three_gang/three_gang_interface.dart'; +import 'package:syncrow_app/features/devices/view/widgets/water_heater/water_heater_page.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/helpers/custom_page_route.dart'; @@ -148,6 +148,14 @@ void showDeviceInterface(DeviceModel device, BuildContext context) { PageRouteBuilder( pageBuilder: (context, animation1, animation2) => ThreeGangInterface(gangSwitch: device))); + + + case DeviceType.WH: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + WaterHeaterPage(device: device))); break; default: } diff --git a/lib/features/devices/view/widgets/water_heater/Inching_widget.dart b/lib/features/devices/view/widgets/water_heater/Inching_widget.dart new file mode 100644 index 0000000..753e51d --- /dev/null +++ b/lib/features/devices/view/widgets/water_heater/Inching_widget.dart @@ -0,0 +1,69 @@ + + + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; + +import '../../../../../utils/resource_manager/color_manager.dart'; + + +class InchingWidget extends StatefulWidget { + const InchingWidget({super.key,this.onDateTimeChanged}); + final Function(DateTime)? onDateTimeChanged; + + @override + State createState() => _InchingWidgetState(); +} + +class _InchingWidgetState extends State { + @override + Widget build(BuildContext context) { + bool isOn = true; // Boolean state for the ON/OFF toggle + return Column( + children: [ + DefaultContainer( + child: Container( + padding: EdgeInsets.all(10), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Inching', // Change text based on state + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + CircleAvatar( + backgroundColor: isOn + ? ColorsManager.secondaryColor.withOpacity(0.6) + : Colors.red.withOpacity(0.6), // Change background color based on state + child: const Icon( + Icons.power_settings_new, + color: ColorsManager.onPrimaryColor, // Change icon color + ), + ), + ], + ), + SizedBox(height: 10,), + Text('Once enabled this feature, each time the device is turned on, it will automatically turn of after a period time as pre-set.'), + ], + ), + ), + ), + SizedBox( + height: 110, + child: CupertinoDatePicker( + mode: CupertinoDatePickerMode.time, + initialDateTime: DateTime.now(), + onDateTimeChanged: (selectedTime) { + }, ), + ), + + ], + ); + } +} diff --git a/lib/features/devices/view/widgets/water_heater/circulate_list_view.dart b/lib/features/devices/view/widgets/water_heater/circulate_list_view.dart new file mode 100644 index 0000000..cdc2b9a --- /dev/null +++ b/lib/features/devices/view/widgets/water_heater/circulate_list_view.dart @@ -0,0 +1,125 @@ + + + + + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/model/schedule_model.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/empty_schedule.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/generated/assets.dart'; + + +class CirculateListView extends StatelessWidget { + final List listSchedule; + final Function(String) onDismissed; + final Function(String, bool) onToggleSchedule; + const CirculateListView({ + super.key, + required this.listSchedule, + required this.onDismissed, + required this.onToggleSchedule, + }); + + @override + Widget build(BuildContext context) { + return Center( + child: listSchedule.isNotEmpty + ? SizedBox( + child: ListView.builder( + shrinkWrap: true, + itemCount: listSchedule.length, + itemBuilder: (context, index) { + return Dismissible( + key: Key(listSchedule[index].scheduleId), + background: Container( + padding: const EdgeInsets.only(right: 10), + alignment: AlignmentDirectional.centerEnd, + decoration: const ShapeDecoration( + color: Color(0xFFFF0000), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + ), + child: Padding( + padding: const EdgeInsets.only(bottom: 10, right: 10), + child: SvgPicture.asset( + Assets.assetsDeleteIcon, + width: 20, + height: 22, + ), + ), + ), + direction: DismissDirection.endToStart, + onDismissed: (direction) { + + onDismissed(listSchedule[index].scheduleId); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Schedule removed')), + ); + }, + child: InkWell( + onTap: () { + + }, + child: DefaultContainer( + padding: const EdgeInsets.all(20), + height: MediaQuery.of(context).size.height / 6.4, + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + BodyLarge( + text: '12:30 AM - 01:30 PM', + // text: listSchedule[index].time, + fontWeight: FontWeight.w500, + fontColor: Colors.black, + fontSize: 22, + ), + Text(listSchedule[index].days.join(' ')), + // Text('Function ${listSchedule[index].function.value ? "ON" : "OFF"}'), + Text('Start Duration: 0h 10m End Duration: 0h 15m'), + ], + ), + ), + Expanded( + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyMedium( + text: '', + fontWeight: FontWeight.normal, + ), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: listSchedule[index].enable, + onChanged: (value) { + onToggleSchedule( + listSchedule[index].scheduleId, + value, + ); + }, + applyTheme: true, + ), + ), + ), + ), + ], + ), + ), + ), + ); + }, + ), + ) + : const EmptySchedule() + ); + } +} diff --git a/lib/features/devices/view/widgets/water_heater/circulate_widget.dart b/lib/features/devices/view/widgets/water_heater/circulate_widget.dart new file mode 100644 index 0000000..d8c0282 --- /dev/null +++ b/lib/features/devices/view/widgets/water_heater/circulate_widget.dart @@ -0,0 +1,168 @@ + + + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +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/utils/resource_manager/color_manager.dart'; + +class CirculateWidget extends StatelessWidget { + final bool isStartEndTime; + final DateTime startTime; + final DateTime endTime; + final List> days; + final List selectedDays; + + // Callback functions to handle user interactions + final Function(bool) onToggleStartEndTime; + final Function(DateTime, bool) onTimeChanged; + final Function(String) onDaySelected; + + const CirculateWidget({ + Key? key, + required this.isStartEndTime, + required this.startTime, + required this.endTime, + required this.days, + required this.selectedDays, + required this.onToggleStartEndTime, + required this.onTimeChanged, + required this.onDaySelected, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), + child: Column( + children: [ + // Start/End time toggle buttons + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () { + onToggleStartEndTime(true); // Toggle to Start Time + }, + child: BodyMedium( + text: 'Start', + fontColor: !isStartEndTime ? Colors.black : Colors.blue, + fontSize: 18, + ), + ), + InkWell( + onTap: () { + onToggleStartEndTime(false); // Toggle to End Time + }, + child: BodyMedium( + text: 'End', + fontColor: isStartEndTime ? Colors.black : Colors.blue, + fontSize: 18, + ), + ), + ], + ), + ), + const Divider( + color: ColorsManager.graysColor, + ), + // Time picker based on start/end toggle + SizedBox( + height: 110, + child: CupertinoDatePicker( + mode: CupertinoDatePickerMode.time, + initialDateTime: isStartEndTime ? startTime : endTime, + onDateTimeChanged: (selectedTime) { + onTimeChanged(selectedTime, isStartEndTime); // Notify parent of time change + }, + ), + ), + const Divider( + color: ColorsManager.graysColor, + ), + const SizedBox(height: 20), + Text('Select days to use Smart Mode'), + const SizedBox(height: 20), + // Days selection as horizontal list + SizedBox( + height: MediaQuery.of(context).size.height * 0.10, + child: ListView( + scrollDirection: Axis.horizontal, + children: days.map((day) { + bool isSelected = selectedDays.contains(day['key']); + return Padding( + padding: const EdgeInsets.all(8.0), + child: InkWell( + onTap: () { + onDaySelected(day['key']!); // Notify parent of day selection + }, + child: Container( + width: 70, + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), + decoration: BoxDecoration( + border: Border.all( + color: isSelected ? Colors.black : ColorsManager.grayColor, + ), + color: Colors.transparent, + borderRadius: BorderRadius.circular(55), + ), + child: Center( + child: Text( + day['day']!, + style: TextStyle( + fontSize: 18, + color: isSelected ? Colors.black : ColorsManager.grayColor, + ), + ), + ), + ), + ), + ); + }).toList(), + ), + ), + const SizedBox(height: 20), + const DefaultContainer(child: + Column( + children: [ + Padding( + padding: EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Start Duration'), + Row( + children: [ + Text('0h 0m'), + Icon(Icons.arrow_forward_ios_sharp,color:ColorsManager.graysColor,) + ], + ), + ],), + ), + Divider(color: ColorsManager.graysColor,), + Padding( + padding: EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('End Duration'), + Row( + children: [ + Text('0h 0m'), + Icon(Icons.arrow_forward_ios_sharp,color:ColorsManager.graysColor,) + ], + ), + ],), + ), + ], + ) + ) + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/devices/view/widgets/water_heater/water_heater_page.dart b/lib/features/devices/view/widgets/water_heater/water_heater_page.dart new file mode 100644 index 0000000..ee5a113 --- /dev/null +++ b/lib/features/devices/view/widgets/water_heater/water_heater_page.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/water_heater_bloc/water_heater_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/water_heater_bloc/water_heater_event.dart'; +import 'package:syncrow_app/features/devices/bloc/water_heater_bloc/water_heater_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/water_heater.dart'; +import 'package:syncrow_app/features/devices/view/widgets/water_heater/wh_timer_schedule_screen.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/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +import '../../../../../generated/assets.dart'; + +class WaterHeaterPage extends StatelessWidget { + final DeviceModel? device; + const WaterHeaterPage({super.key, this.device}); + + @override + Widget build(BuildContext context) { + bool value = false; + return DefaultScaffold( + child: BlocProvider( + create: (context) => + WaterHeaterBloc(switchCode: 'switch_1', whId: device?.uuid ?? '') + ..add(const WaterHeaterInitial()), + child: BlocBuilder( + builder: (context, state) { + final waterHeaterBloc = BlocProvider.of(context); + + WHModel whModel = WHModel(firstCountDown: 0, firstSwitch: false); + + + if (state is LoadingNewSate) { + whModel = state.whModel; + } else if (state is UpdateState) { + whModel = state.whModel; + } + return state is WHLoadingState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : RefreshIndicator( + onRefresh: () async { + waterHeaterBloc.add(WaterHeaterInitial( + // groupScreen: device != null ? false : true + )); + }, + child: Center( + child: ListView( + shrinkWrap: true, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InkWell( + overlayColor: MaterialStateProperty.all( + Colors.transparent), + onTap: () { + waterHeaterBloc.add( + WaterHeaterSwitch( + whSwitch:whModel.firstSwitch + ),); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + decoration: BoxDecoration( + // color: Colors.white.withOpacity(0.1), + borderRadius: + BorderRadius.circular(890), + boxShadow: [ + BoxShadow( + color: + Colors.white.withOpacity(0.1), + blurRadius: 24, + offset: const Offset(-5, -5), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: + Colors.black.withOpacity(0.11), + blurRadius: 25, + offset: const Offset(5, 5), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: + Colors.black.withOpacity(0.13), + blurRadius: 30, + offset: const Offset(5, 5), + blurStyle: BlurStyle.inner, + ), + ], + ), + child: SvgPicture.asset( + whModel.firstSwitch + ? Assets.waterHeaterOn + : Assets.waterHeaterOff, + fit: BoxFit.fill, + ), + ), + ], + ), + ), + const SizedBox(height: 80), + SizedBox( + child: Column( + children: [ + Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(100), + ), + child: GestureDetector( + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + WHTimerScheduleScreen( + switchCode: 'switch_1', device: device!, deviceCode: 'countdown_1', + ))); + }, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: + BorderRadius.circular(100), + ), + ), + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: + BorderRadius.circular(100), + ), + child: Center( + child: Icon( + Icons.access_time, + color: ColorsManager + .primaryColorWithOpacity, + size: 25, + ), + ), + ), + ], + ), + ), + ), + const SizedBox(height: 10), + BodySmall( + text: "Timer", + style: context.bodyMedium.copyWith( + color: ColorsManager.textPrimaryColor, + ), + ), + ], + ), + ) + ], + ), + ], + ), + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/water_heater/water_heater_switch.dart b/lib/features/devices/view/widgets/water_heater/water_heater_switch.dart new file mode 100644 index 0000000..727e751 --- /dev/null +++ b/lib/features/devices/view/widgets/water_heater/water_heater_switch.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class WaterHeaterSwitchIcon extends StatelessWidget { + const WaterHeaterSwitchIcon({super.key, required this.value, required this.action}); + + final bool value; + final Function action; + + @override + Widget build(BuildContext context) { + return InkWell( + overlayColor: MaterialStateProperty.all(Colors.transparent), + onTap: () { + action(); + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(190.0), + child: Container( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + blurStyle: BlurStyle.inner, + color: Colors.black.withOpacity(0.1), + spreadRadius: 9, + blurRadius: 2, + offset: const Offset(3, 1), + ), + ], + ), + child: SvgPicture.asset( + value ? Assets.waterHeaterOn : Assets.waterHeaterOff, + fit: BoxFit.fill, + ), + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/water_heater/wh_timer_schedule_screen.dart b/lib/features/devices/view/widgets/water_heater/wh_timer_schedule_screen.dart new file mode 100644 index 0000000..29ac828 --- /dev/null +++ b/lib/features/devices/view/widgets/water_heater/wh_timer_schedule_screen.dart @@ -0,0 +1,411 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/water_heater_bloc/water_heater_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/water_heater_bloc/water_heater_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/water_heater/Inching_widget.dart'; +import 'package:syncrow_app/features/devices/view/widgets/water_heater/circulate_list_view.dart'; +import 'package:syncrow_app/features/devices/view/widgets/water_heater/circulate_widget.dart'; +import 'package:syncrow_app/features/shared_widgets/create_schedule.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/schedule_list.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.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'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; +import '../../../bloc/water_heater_bloc/water_heater_event.dart'; + +class WHTimerScheduleScreen extends StatelessWidget { + final DeviceModel device; + final String deviceCode; + final String switchCode; + const WHTimerScheduleScreen( + {required this.device, + required this.deviceCode, + required this.switchCode, + super.key}); + + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: BlocProvider( + create: (context) => + WaterHeaterBloc(switchCode: switchCode, whId: device.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode)) + ..add(GetScheduleEvent()), + child: BlocBuilder( + builder: (context, state) { + final waterHeaterBloc = BlocProvider.of(context); + Duration duration = Duration.zero; + int countNum = 0; + if (state is UpdateTimerState) { + countNum = state.seconds; + } else if (state is TimerRunInProgress) { + countNum = state.remainingTime; + } else if (state is TimerRunComplete) { + countNum = 0; + } else if (state is LoadingNewSate) { + countNum = 0; + } + return PopScope( + canPop: false, + onPopInvoked: (didPop) { + if (!didPop) { + waterHeaterBloc.add(OnClose()); + Navigator.pop(context); + } + }, + child: DefaultTabController( + length: 4, + child: DefaultScaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: const BodyLarge( + text: 'Schedule', + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, + ), + actions: [ + waterHeaterBloc.createSchedule == true + && waterHeaterBloc.selectedTabIndex == 1 ? TextButton( + onPressed: () { + waterHeaterBloc.add(ScheduleSave()); + }, + child: const Text('Save')) + : waterHeaterBloc.selectedTabIndex == 1 + ? IconButton( + onPressed: () { + waterHeaterBloc.toggleCreateSchedule(); + }, + icon: const Icon(Icons.add), + ) + : const SizedBox(), + + + waterHeaterBloc.createCirculate == true + &&waterHeaterBloc.selectedTabIndex==2 ? TextButton( + onPressed: () { + waterHeaterBloc.add(ScheduleSave()); + }, + child: const Text('Save')) + : waterHeaterBloc.selectedTabIndex == 2 + ? IconButton( + onPressed: () { + waterHeaterBloc.toggleCreateCirculate(); + }, + icon: const Icon(Icons.add), + ) + : const SizedBox(), + ], + ), + child: state is WHLoadingState + ? const Center(child: CircularProgressIndicator()) + : Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + decoration: const ShapeDecoration( + color: ColorsManager.onPrimaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(30)), + ), + ), + child: TabBar( + indicatorPadding: EdgeInsets.zero, + padding: EdgeInsets.zero, + labelPadding: EdgeInsets.zero, + onTap: (value) { + if (value == 0) { + if (waterHeaterBloc.createSchedule == true) { + waterHeaterBloc.toggleCreateSchedule(); + } + waterHeaterBloc.toggleSelectedIndex(value); + } else if (value == 2) { + if (waterHeaterBloc.createCirculate == true) { + waterHeaterBloc.toggleCreateCirculate(); + } + waterHeaterBloc.toggleSelectedIndex(value); + } else { + waterHeaterBloc.toggleSelectedIndex(value); + } + }, + indicatorColor: Colors.white, + dividerHeight: 0, + indicatorSize: TabBarIndicatorSize.tab, + indicator: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20)), + ), + ), + isScrollable: false, + labelColor: Colors.white, // Text color when selected + unselectedLabelColor: ColorsManager.blackColor, // Text color when not selected + tabs: [ + Tab( + icon: SvgPicture.asset( + Assets.scheduleTimeIcon, + color: waterHeaterBloc.selectedTabIndex == 0 + ? Colors.white + : ColorsManager.blackColor, // Change icon color based on selectedIndex + ), + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: BodySmall( + text: 'Countdown', + style: context.bodySmall.copyWith( + color: waterHeaterBloc.selectedTabIndex == 0 + ? Colors.white + : ColorsManager + .blackColor, // Text color based on selectedTabIndex + fontSize: 8, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Tab( + icon: SvgPicture.asset( + Assets.scheduleCelenderIcon, + color: waterHeaterBloc.selectedTabIndex == 1 + ? Colors.white + : ColorsManager.blackColor, + ), + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: Text( + 'Schedule', + style: context.bodySmall.copyWith( + color: waterHeaterBloc.selectedTabIndex == 1 + ? Colors.white + : ColorsManager.blackColor, + fontSize: 8, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Tab( + icon: SvgPicture.asset( + Assets.scheduleCirculateIcon, + color: waterHeaterBloc.selectedTabIndex == 2 + ? Colors.white + : ColorsManager.blackColor, + ), + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: Text( + 'Circulate', + style: context.bodySmall.copyWith( + color: waterHeaterBloc + .selectedTabIndex == + 2 + ? Colors.white + : ColorsManager.blackColor, + fontSize: 8, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Tab( + icon: SvgPicture.asset( + Assets.scheduleInchingIcon, + color: waterHeaterBloc + .selectedTabIndex == + 3 + ? Colors.white + : ColorsManager.blackColor, + ), + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: Text( + 'Inching', + style: context.bodySmall.copyWith( + color: waterHeaterBloc + .selectedTabIndex == + 3 + ? Colors.white + : ColorsManager.blackColor, + fontSize: 8, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ], + )), + Expanded( + child: TabBarView( + physics: + const NeverScrollableScrollPhysics(), // Disable swiping + children: [ + Center( + child: Container( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + countNum > 0 + ? BodyLarge( + text: _formatDuration( + countNum), + fontColor: ColorsManager + .slidingBlueColor, + fontSize: 40, + ) + : CupertinoTimerPicker( + mode: + CupertinoTimerPickerMode + .hm, + onTimerDurationChanged: + (Duration + newDuration) { + duration = newDuration; + }, + ), + GestureDetector( + onTap: () { + if (state + is LoadingNewSate) { + return; + } + if (countNum > 0) { + waterHeaterBloc.add( + SetCounterValue( + deviceCode: + deviceCode, + duration: Duration + .zero)); + } else if (duration != + Duration.zero) { + waterHeaterBloc.add( + SetCounterValue( + deviceCode: + deviceCode, + duration: + duration)); + } + }, + child: SvgPicture.asset( + countNum > 0 + ? Assets.pauseIcon + : Assets.playIcon)), + ], + ), + ), + ), + Column( + mainAxisAlignment: waterHeaterBloc + .listSchedule.isNotEmpty + ? MainAxisAlignment.start + : MainAxisAlignment.center, + children: [ + SizedBox( + child: waterHeaterBloc.createSchedule == true + ? CreateSchedule( + onToggleChanged: (bool isOn) { + waterHeaterBloc.toggleSchedule = isOn; + }, + onDateTimeChanged: (DateTime dateTime) { + waterHeaterBloc.selectedTime = dateTime; + }, + days: waterHeaterBloc.days, + selectDays: (List + selectedDays) { + waterHeaterBloc + .selectedDays = + selectedDays; + }, + ) + : Padding( + padding: const EdgeInsets.only(top: 10), + child: ScheduleListView( + listSchedule: waterHeaterBloc.listSchedule, // Pass the schedule list here + onDismissed: + (scheduleId) { + waterHeaterBloc.listSchedule.removeWhere( + (schedule) => schedule.scheduleId == scheduleId); + waterHeaterBloc.add( + DeleteScheduleEvent(id: scheduleId)); + }, + onToggleSchedule: (scheduleId, isEnabled) { + + waterHeaterBloc.add(ToggleScheduleEvent( + id: scheduleId, toggle: isEnabled, + )); + }, + ), + ), + ), + ], + ), + Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + waterHeaterBloc.createCirculate == true + ? + CirculateWidget( + isStartEndTime: true, + startTime: DateTime.now(), + endTime: DateTime.now(), + days: waterHeaterBloc.days, + selectedDays: [], + onToggleStartEndTime: (c) {}, + onTimeChanged: (x, f) {}, + onDaySelected: (p0) {}, + ):CirculateListView( + listSchedule: waterHeaterBloc.listSchedule, // Pass the schedule list here + onDismissed: (scheduleId) { + waterHeaterBloc.listSchedule.removeWhere((schedule) => schedule.scheduleId == scheduleId); + waterHeaterBloc.add(DeleteScheduleEvent(id: scheduleId)); + }, + onToggleSchedule: (scheduleId, isEnabled) { + waterHeaterBloc.add(ToggleScheduleEvent( + id: scheduleId, toggle: isEnabled, + )); + }, + ) + ], + ), + ), + Column(children: [ + SizedBox(height: 20), + Container(child: InchingWidget(),),],) + ], + ), + ), + ], + ), + ))); + }, + ), + ), + ); + } + + String _formatDuration(int seconds) { + final hours = (seconds ~/ 3600).toString().padLeft(2, '0'); + final minutes = ((seconds % 3600) ~/ 60).toString().padLeft(2, '0'); + final secs = (seconds % 60).toString().padLeft(2, '0'); + return '$hours:$minutes:$secs'; + } +} diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 5370a48..59b833f 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -1041,4 +1041,15 @@ class Assets { static const String twoGang = "assets/icons/2gang.svg"; static const String emptySchedule = "assets/icons/emptySchedule.svg"; + static const String waterHeaterOn = "assets/icons/water_heater_on.svg"; + static const String waterHeaterOff = "assets/icons/water_heater_off.svg"; + + static const String scheduleCelenderIcon = "assets/icons/schedule_celender_icon.svg"; + static const String scheduleCirculateIcon = "assets/icons/schedule_circulate_icon.svg"; + static const String scheduleInchingIcon = "assets/icons/schedule_Inching_icon.svg"; + static const String scheduleTimeIcon = "assets/icons/schedule_time_icon.svg"; + + + + } diff --git a/lib/main.dart b/lib/main.dart index 05f1875..1738eac 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,7 +14,7 @@ void main() { //to catch all the errors in the app and send them to firebase runZonedGuarded(() async { //to load the environment variables - const environment = String.fromEnvironment('FLAVOR', defaultValue: 'production'); + const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development'); await dotenv.load(fileName: '.env.$environment'); // //this is to make the app work with the self-signed certificate diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index ca12999..1e984d6 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -32,14 +32,15 @@ class DevicesAPI { static Future> controlDevice( DeviceControlModel controlModel, String deviceId) async { try { - final response = await _httpService.post( path: ApiEndpoints.controlDevice.replaceAll('{deviceUuid}', deviceId), body: controlModel.toJson(), showServerMessage: false, expectedResponseModel: (json) { + print('controlDevice=$json' ); return json; }, + ); return response; } catch (e) { @@ -75,6 +76,8 @@ class DevicesAPI { path: ApiEndpoints.deviceFunctionsStatus.replaceAll('{deviceUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { + print('json===$json'); + return json; }, ); @@ -278,17 +281,7 @@ class DevicesAPI { required String code, required bool value, required List days, - }) async { - print( { - "category": category, - "time": time, - "function":{ - "code": code, - "value":value, - }, - "days": days - }); final response = await _httpService.post( path: ApiEndpoints.saveSchedule.replaceAll('{deviceUuid}', deviceId), showServerMessage: false, diff --git a/lib/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index a417454..4f3ba8a 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -49,6 +49,7 @@ enum DeviceType { Gateway, CeilingSensor, WallSensor, + WH, Other, } @@ -73,6 +74,7 @@ Map devicesTypesMap = { "2G": DeviceType.TwoGang, "1G": DeviceType.OneGang, "CUR": DeviceType.Curtain, + "WH": DeviceType.WH, }; Map> devicesFunctionsMap = { DeviceType.AC: [ @@ -185,7 +187,6 @@ Map> devicesFunctionsMap = { values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1}) ), ], - DeviceType.ThreeGang: [ FunctionModel( code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), @@ -221,6 +222,43 @@ Map> devicesFunctionsMap = { {"unit": "%", "min": 0, "max": 100, "scale": 0, "step": 1}) ), ], + DeviceType.WH: [ + FunctionModel( + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({}) + ), + FunctionModel( + code: 'countdown_1', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1}) + ), + FunctionModel( + code: 'relay_status', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson( + {"range": [ "off", "on"]}) + ), + FunctionModel( + code: 'switch_backlight', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson( + {}) + ), + FunctionModel( + code: 'switch_inching', + type: functionTypesMap['String'], + values: ValueModel.fromJson( + {"maxlen": 255,}) + ), + FunctionModel( + code: 'cycle_timing', + type: functionTypesMap['Raw'], + values: ValueModel.fromJson( + {"maxlen": 255}) + ), + ], }; enum TempModes { hot, cold, wind } diff --git a/pubspec.yaml b/pubspec.yaml index be84008..942153c 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.3+21 +version: 1.0.3+22 environment: sdk: ">=3.0.6 <4.0.0"