Merge pull request #52 from SyncrowIOT/sp_484

Sp 484
This commit is contained in:
Abdullah
2024-09-21 16:40:44 +03:00
committed by GitHub
23 changed files with 1945 additions and 14 deletions

View File

@ -0,0 +1,334 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:flutter/material.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';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
final String whId;
final String switchCode;
WHModel deviceStatus = WHModel(
firstSwitch: false,firstCountDown: 0
);
List<WHModel> deviceStatusList = [];
List<DeviceModel> devicesList = [];
bool allAcsPage = false;
bool allAcsOn = true;
bool allTempSame = true;
int globalTemp = 25;
Timer? _timer;
bool toggleSchedule = true;
List<String> selectedDays = [];
bool createSchedule = false;
bool createCirculate = false;
List<ScheduleModel> listSchedule = [];
DateTime? selectedTime=DateTime.now();
WaterHeaterBloc({required this.whId,required this.switchCode}) : super(WHInitialState()) {
on<WaterHeaterInitial>(_fetchWaterHeaterStatus);
on<WaterHeaterSwitch>(_changeFirstSwitch);
on<SetCounterValue>(_setCounterValue);
on<GetCounterEvent>(_getCounterValue);
on<ToggleDaySelectionEvent>(toggleDaySelection);
on<ScheduleSave>(saveSchedule);
on<GetScheduleEvent>(getSchedule);
on<ToggleScheduleEvent>(toggleChange);
on<DeleteScheduleEvent>(deleteSchedule);
on<TickTimer>(_onTickTimer);
on<OnClose>(_onClose);
on<SelectTimeEvent>(showTime);
}
void _fetchWaterHeaterStatus(WaterHeaterInitial event, Emitter<WaterHeaterState> emit) async {
emit(WHLoadingState());
try {
var response = await DevicesAPI.getDeviceStatus(whId);
List<StatusModel> 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<WaterHeaterState> 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<WaterHeaterState> 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<WaterHeaterState> emit) async {
emit(WHLoadingState());
try {
var response = await DevicesAPI.getDeviceStatus(whId);
List<StatusModel> 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<WaterHeaterState> 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<WaterHeaterState> emit) {
if (event.remainingTime > 0) {
emit(TimerRunInProgress(event.remainingTime));
} else {
_timer?.cancel();
emit(TimerRunComplete());
}
}
//=====================---------- Schedule ----------------------------------------
List<Map<String, String>> 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<void> saveSchedule(ScheduleSave event, Emitter<WaterHeaterState> 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<void> getSchedule(GetScheduleEvent event, Emitter<WaterHeaterState> emit,) async {
try {
emit(WHLoadingState());
final response = await DevicesAPI.getSchedule(
category: switchCode,
deviceId: whId ,
);
List<dynamic> 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<WaterHeaterState> 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<WaterHeaterState> 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<void> toggleDaySelection(
ToggleDaySelectionEvent event,
Emitter<WaterHeaterState> 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));
}
showTime(SelectTimeEvent event, Emitter<WaterHeaterState> emit) async {
final TimeOfDay? timePicked = await showTimePicker(
context: event.context,
initialTime: TimeOfDay.now(),
builder: (context, child) {
return Theme(
data: ThemeData.light().copyWith(
colorScheme: const ColorScheme.light(
primary: ColorsManager.primaryColor,
onSurface: Colors.black,
),
buttonTheme: const ButtonThemeData(
colorScheme: ColorScheme.light(
primary: Colors.green,
),
),
),
child: child!,
);
},
);
}
}

View File

@ -0,0 +1,101 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
abstract class WaterHeaterEvent extends Equatable {
const WaterHeaterEvent();
@override
List<Object> 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<Object> 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<Object> get props => [deviceCode];
}
class SetCounterValue extends WaterHeaterEvent {
final Duration duration;
final String deviceCode;
const SetCounterValue({required this.duration, required this.deviceCode});
@override
List<Object> get props => [duration, deviceCode];
}
class StartTimer extends WaterHeaterEvent {
final int duration;
const StartTimer(this.duration);
@override
List<Object> get props => [duration];
}
class TickTimer extends WaterHeaterEvent {
final int remainingTime;
const TickTimer(this.remainingTime);
@override
List<Object> 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<Object> get props => [toggle,id];
}
class ToggleDaySelectionEvent extends WaterHeaterEvent {
final String key;
const ToggleDaySelectionEvent({required this.key});
@override
List<Object> get props => [key];
}
class DeleteScheduleEvent extends WaterHeaterEvent {
final String id;
const DeleteScheduleEvent({required this.id});
@override
List<Object> get props => [id];
}
class SelectTimeEvent extends WaterHeaterEvent {
final BuildContext context;
final bool isEffective;
const SelectTimeEvent({required this.context, required this.isEffective});
@override
List<Object> get props => [context, isEffective];
}

View File

@ -0,0 +1,113 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/water_heater.dart';
abstract class WaterHeaterState extends Equatable {
const WaterHeaterState();
@override
List<Object> get props => [];
}
class WHInitialState extends WaterHeaterState {}
class WHLoadingState extends WaterHeaterState {}
class WHChangeLoading extends WaterHeaterState {
// final WHStatusModel WHStatusModel;
const WHChangeLoading(
// {required this. WHStatusModel}
);
// @override
// List<Object> get props => [acStatusModel];
}
class WHModifyingState extends WaterHeaterState {
final WHModel whStatusModel;
const WHModifyingState({required this.whStatusModel});
@override
List<Object> get props => [whStatusModel];
}
class GetWHStatusState extends WaterHeaterState {
final WHModel whStatusModel;
const GetWHStatusState({required this.whStatusModel});
@override
List<Object> get props => [whStatusModel];
}
class WHFailedState extends WaterHeaterState {
final String errorMessage;
const WHFailedState({required this.errorMessage});
@override
List<Object> get props => [errorMessage];
}
class LoadingNewSate extends WaterHeaterState {
final WHModel whModel;
const LoadingNewSate({required this.whModel});
@override
List<Object> get props => [whModel];
}
class TimerRunComplete extends WaterHeaterState {}
class UpdateTimerState extends WaterHeaterState {
final int seconds;
const UpdateTimerState({required this.seconds});
@override
List<Object> get props => [seconds];
}
class TimerRunInProgress extends WaterHeaterState {
final int remainingTime;
const TimerRunInProgress(this.remainingTime);
@override
List<Object> 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<Object> get props => [WHModel];
}
class ChangeSwitchStatusEvent extends WaterHeaterState {
final bool value;
final String deviceId;
const ChangeSwitchStatusEvent({required this.value, this.deviceId = ''});
@override
List<Object> get props => [value, deviceId];
}
class ChangeSlidingSegmentState extends WaterHeaterState {
final int value;
const ChangeSlidingSegmentState({required this.value});
@override
List<Object> get props => [value];
}

View File

@ -62,6 +62,8 @@ class DeviceModel {
tempIcon = Assets.oneGang;
} else if (type == DeviceType.TwoGang) {
tempIcon = Assets.twoGang;
}else if (type == DeviceType.WH) {
tempIcon = Assets.waterHeaterIcon;
} else {
tempIcon = Assets.assetsIconsLogo;
}

View File

@ -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<String, dynamic> json, List<StatusModel> 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<String, dynamic> 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<StatusModel> 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,
);
}
}

View File

@ -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:
}

View File

@ -0,0 +1,143 @@
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_large.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class InchingWidget extends StatefulWidget {
const InchingWidget({super.key,this.onDateTimeChanged});
final Function(DateTime)? onDateTimeChanged;
@override
State<InchingWidget> createState() => _InchingWidgetState();
}
class _InchingWidgetState extends State<InchingWidget> {
@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
),
),
],
),
const SizedBox(height: 10,),
const Text('Once enabled this feature, each time the device is turned on, it will automatically turn of after a period time as pre-set.'),
],
),
),
),
const Padding(
padding: EdgeInsets.only(right: 30,left: 30),
child: Divider(),
),
Container(
child: MinuteSecondPicker()),
const Padding(
padding: EdgeInsets.only(right: 30,left: 30),
child: Divider(),
),
],
);
}
}
class MinuteSecondPicker extends StatefulWidget {
const MinuteSecondPicker({super.key});
@override
_MinuteSecondPickerState createState() => _MinuteSecondPickerState();
}
class _MinuteSecondPickerState extends State<MinuteSecondPicker> {
int selectedMinute = 0;
int selectedSecond = 0;
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Minutes picker
Container(
height: 80,
width: 100,
child: CupertinoPicker(
itemExtent: 40.0,
scrollController: FixedExtentScrollController(initialItem: selectedMinute),
onSelectedItemChanged: (int index) {
setState(() {
selectedMinute = index;
});
},
children: List<Widget>.generate(61, (int index) {
return Center(
child: BodyLarge(
text: index.toString().padLeft(2, '0'),
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 30,color: Colors.blue),
),
);
}),
),
),
const Text('M'),
SizedBox(width: 20),
// Seconds picker
Container(
height: 80,
width: 100,
child: CupertinoPicker(
itemExtent: 40.0,
scrollController: FixedExtentScrollController(initialItem: selectedSecond),
onSelectedItemChanged: (int index) {
setState(() {
selectedSecond = index;
});
},
children: List<Widget>.generate(60, (int index) {
return Center(
child: BodyLarge(
text: index.toString().padLeft(2, '0'),
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 30,color: Colors.blue),
),
);
}),
),
),
const Text('S'),
],
),
);
}
}

View File

@ -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<ScheduleModel> 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()
);
}
}

View File

@ -0,0 +1,177 @@
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<Map<String, String>> days;
final List<String> selectedDays;
// Callback functions to handle user interactions
final Function(bool) onToggleStartEndTime;
final Function(DateTime, bool) onTimeChanged;
final Function(String) onDaySelected;
final Function()? endDuration;
final Function()? startDuration;
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,
required this.endDuration,
required this.startDuration,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
child: Column(
children: <Widget>[
// 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),
DefaultContainer(child:
Column(
children: [
InkWell(
onTap: startDuration,
child: const 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,)
],
),
],),
),
),
const Divider(color: ColorsManager.graysColor,),
InkWell(
onTap: endDuration,
child: const 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,)
],
),
],),
),
),
],
)
)
],
),
);
}
}

View File

@ -0,0 +1,181 @@
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/generated/assets.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class WaterHeaterPage extends StatelessWidget {
final DeviceModel? device;
const WaterHeaterPage({super.key, this.device});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
child: BlocProvider(
create: (context) =>
WaterHeaterBloc(switchCode: 'switch_1', whId: device?.uuid ?? '')
..add(const WaterHeaterInitial()),
child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
builder: (context, state) {
final waterHeaterBloc = BlocProvider.of<WaterHeaterBloc>(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(const WaterHeaterInitial());
},
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,
),
),
],
),
)
],
),
],
),
),
);
},
),
),
);
}
}

View File

@ -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,
),
),
),
);
}
}

View File

@ -0,0 +1,416 @@
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 'package:syncrow_app/features/devices/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<WaterHeaterBloc, WaterHeaterState>(
builder: (context, state) {
final waterHeaterBloc = BlocProvider.of<WaterHeaterBloc>(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 {
if (waterHeaterBloc.createSchedule == true) {
waterHeaterBloc.toggleCreateSchedule();
}
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<String>
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(
endDuration: () {
waterHeaterBloc.add(SelectTimeEvent(context: context, isEffective: false));
},
startDuration: () {
waterHeaterBloc.add(SelectTimeEvent(context: context, isEffective: false));
},
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';
}
}

View File

@ -1041,4 +1041,16 @@ 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";
static const String waterHeaterIcon = "assets/icons/water_heater_icon.svg";
}

View File

@ -32,7 +32,6 @@ class DevicesAPI {
static Future<Map<String, dynamic>> controlDevice(
DeviceControlModel controlModel, String deviceId) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.controlDevice.replaceAll('{deviceUuid}', deviceId),
body: controlModel.toJson(),
@ -40,6 +39,7 @@ class DevicesAPI {
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
@ -278,17 +278,7 @@ class DevicesAPI {
required String code,
required bool value,
required List<String> 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,

View File

@ -49,6 +49,7 @@ enum DeviceType {
Gateway,
CeilingSensor,
WallSensor,
WH,
Other,
}
@ -73,6 +74,7 @@ Map<String, DeviceType> devicesTypesMap = {
"2G": DeviceType.TwoGang,
"1G": DeviceType.OneGang,
"CUR": DeviceType.Curtain,
"WH": DeviceType.WH,
};
Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
DeviceType.AC: [
@ -185,7 +187,6 @@ Map<DeviceType, List<FunctionModel>> 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<DeviceType, List<FunctionModel>> 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 }