garage door

This commit is contained in:
mohammad
2024-10-06 16:48:17 +03:00
parent 487c7c5514
commit 5e046b1deb
25 changed files with 2860 additions and 48 deletions
@@ -0,0 +1,411 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_state.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
import 'package:syncrow_app/features/devices/model/garage_door_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/services/api/devices_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
final String GDId;
GarageDoorBloc({
required this.GDId,
}) : super(const GarageDoorSensorState()) {
on<GarageDoorInitial>(_fetchStatus);
on<ReportLogsInitial>(fetchLogsForLastMonth);
on<ToggleClosingReminderEvent>(_toggleClosingReminder);
on<ToggleDoorAlarmEvent>(_toggleDoorAlarm);
on<ToggleDaySelectionEvent>(toggleDaySelection);
on<ScheduleSaveapp>(saveSchedule);
on<GetScheduleEvent>(getSchedule);
on<ToggleScheduleEvent>(toggleChange);
on<ToggleCreateScheduleEvent>(toggleCreateSchedule);
on<OnClose>(_onClose);
on<SelectSecondsEvent>(selectSeconds);
on<ToggleDoorEvent>(openCloseGarageDoor);
on<GetCounterEvent>(_getCounterValue);
on<SetCounterValue>(_setCounterValue);
on<TickTimer>(_onTickTimer);
}
void _onClose(OnClose event, Emitter<GarageDoorSensorState> emit) {
_timer?.cancel();
}
Timer? _timer;
bool lowBattery = false;
bool closingReminder = false;
bool doorAlarm = false;
GarageDoorModel deviceStatus = GarageDoorModel(
tr_timecon: 0,
countdown1: 0,
countdownAlarm: 0,
doorContactState: false,
doorControl1: '',
doorState1: '',
switch1: false,
voiceControl1: false,
batteryPercentage: 0,
);
void _fetchStatus(
GarageDoorInitial event, Emitter<GarageDoorSensorState> emit) async {
emit(GarageDoorLoadingState());
try {
var response = await DevicesAPI.getDeviceStatus(GDId);
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
}
deviceStatus = GarageDoorModel.fromJson(
statusModelList,
);
secondSelected = deviceStatus.tr_timecon;
toggleDoor = deviceStatus.switch1;
emit(UpdateState(garageSensor: deviceStatus));
Future.delayed(const Duration(milliseconds: 500));
_listenToChanges();
} catch (e) {
emit(GarageDoorFailedState(errorMessage: e.toString()));
return;
}
}
void _toggleClosingReminder(ToggleClosingReminderEvent event,
Emitter<GarageDoorSensorState> emit) async {
emit(LoadingNewSate(doorSensor: deviceStatus));
try {
closingReminder = event.isClosingReminderEnabled;
emit(UpdateState(garageSensor: deviceStatus));
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: GDId,
code: 'closing_reminder',
value: closingReminder,
),
GDId,
);
} catch (e) {
emit(GarageDoorFailedState(errorMessage: e.toString()));
}
}
void _toggleDoorAlarm(
ToggleDoorAlarmEvent event, Emitter<GarageDoorSensorState> emit) async {
emit(LoadingNewSate(doorSensor: deviceStatus));
try {
doorAlarm = event.isDoorAlarmEnabled;
emit(UpdateState(garageSensor: deviceStatus));
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: GDId,
code: 'door_alarm',
value: doorAlarm,
),
GDId,
);
} catch (e) {
emit(GarageDoorFailedState(errorMessage: e.toString()));
}
}
DeviceReport recordGroups =
DeviceReport(startTime: '0', endTime: '0', data: []);
Future<void> fetchLogsForLastMonth(
ReportLogsInitial event, Emitter<GarageDoorSensorState> emit) async {
DateTime now = DateTime.now();
DateTime lastMonth = DateTime(now.year, now.month - 1, now.day);
int startTime = lastMonth.millisecondsSinceEpoch;
int endTime = now.millisecondsSinceEpoch;
try {
emit(GarageDoorLoadingState());
var response = await DevicesAPI.getReportLogs(
startTime: startTime.toString(),
endTime: endTime.toString(),
deviceUuid: GDId,
code: 'doorcontact_state',
);
recordGroups = response;
emit(UpdateState(garageSensor: deviceStatus));
} on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage = errorData['message'];
emit(GarageDoorFailedState(errorMessage: errorMessage));
}
}
_listenToChanges() {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$GDId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) async {
if (_timer != null) {
await Future.delayed(const Duration(seconds: 2));
}
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList.add(StatusModel(code: element['code'], value: true));
});
deviceStatus = GarageDoorModel.fromJson(statusList);
if (!isClosed) {
// add(
// DoorSensorSwitch(switchD: deviceStatus.doorContactState),
// );
}
});
} catch (_) {}
}
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> toggleDaySelection(
ToggleDaySelectionEvent event,
Emitter<GarageDoorSensorState> emit,
) async {
emit(GarageDoorLoadingState());
if (selectedDays.contains(event.key)) {
selectedDays.remove(event.key);
} else {
selectedDays.add(event.key);
}
emit(ChangeTimeState());
}
Future<void> saveSchedule(
ScheduleSaveapp event,
Emitter<GarageDoorSensorState> emit,
) async {
try {
if (selectedDays.isNotEmpty) {
emit(GarageDoorLoadingState());
await DevicesAPI.postSchedule(
category: 'switch_1',
deviceId: GDId,
time: getTimeStampWithoutSeconds(selectedTime).toString(),
code: 'switch_1',
value: toggleSchedule,
days: selectedDays);
CustomSnackBar.displaySnackBar('Save Successfully');
add(GetScheduleEvent());
} else {
CustomSnackBar.displaySnackBar('Please select days');
}
} catch (e) {
emit(GarageDoorFailedState(errorMessage: e.toString()));
}
}
Future<void> getSchedule(
GetScheduleEvent event,
Emitter<GarageDoorSensorState> emit,
) async {
try {
emit(GarageDoorLoadingState());
final response = await DevicesAPI.getSchedule(
category: 'switch_1',
deviceId: GDId,
);
List<dynamic> jsonData = response;
listSchedule =
jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
} on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage = errorData['message'];
emit(GarageDoorFailedState(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<GarageDoorSensorState> emit) async {
try {
emit(GarageDoorLoadingState());
final response = await DevicesAPI.changeSchedule(
scheduleId: event.id, deviceUuid: GDId, 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(GarageDoorFailedState(errorMessage: errorMessage.toString()));
}
}
Future deleteSchedule(
DeleteScheduleEvent event, Emitter<GarageDoorSensorState> emit) async {
try {
emit(GarageDoorLoadingState());
final response = await DevicesAPI.deleteSchedule(
scheduleId: event.id,
deviceUuid: GDId,
);
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(GarageDoorFailedState(errorMessage: errorMessage.toString()));
}
}
void toggleSelectedIndex(
ToggleSelectedEvent event, Emitter<GarageDoorSensorState> emit) {
emit(GarageDoorLoadingState());
selectedTabIndex = event.index;
emit(ChangeSlidingSegmentState(value: selectedTabIndex));
}
void toggleCreateSchedule(
ToggleCreateScheduleEvent event, Emitter<GarageDoorSensorState> emit) {
emit(GarageDoorLoadingState());
createSchedule = !createSchedule;
selectedDays.clear();
selectedTime = DateTime.now();
emit(UpdateCreateScheduleState(createSchedule));
}
int selectedTabIndex = 0;
bool toggleSchedule = true;
List<String> selectedDays = [];
bool createSchedule = false;
List<ScheduleModel> listSchedule = [];
DateTime? selectedTime = DateTime.now();
int secondSelected = 0;
bool toggleDoor = false;
Future<void> selectSeconds(
SelectSecondsEvent event, Emitter<GarageDoorSensorState> emit) async {
try {
emit(GarageDoorLoadingState());
secondSelected = event.seconds;
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: GDId, code: 'tr_timecon', value: secondSelected),
GDId);
emit(UpdateState(garageSensor: deviceStatus));
} on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage = errorData['message'];
emit(GarageDoorFailedState(errorMessage: errorMessage.toString()));
}
}
openCloseGarageDoor(
ToggleDoorEvent event, Emitter<GarageDoorSensorState> emit) async {
emit(GarageDoorLoadingState());
try {
toggleDoor = !event.toggle;
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: GDId, code: 'switch_1', value: toggleDoor),
GDId);
add(GarageDoorInitial());
emit(UpdateState(garageSensor: deviceStatus));
} on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage = errorData['message'];
emit(GarageDoorFailedState(errorMessage: errorMessage.toString()));
}
}
void _setCounterValue(
SetCounterValue event, Emitter<GarageDoorSensorState> emit) async {
emit(LoadingNewSate(doorSensor: deviceStatus));
int seconds = 0;
try {
seconds = event.duration.inSeconds;
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: GDId, code: 'countdown_1', value: seconds),
GDId);
if (response['success'] ?? false) {
deviceStatus.countdown1 = seconds;
} else {
emit(GarageDoorFailedState(errorMessage: 'Something went wrong'));
return;
}
} catch (e) {
emit(GarageDoorFailedState(errorMessage: e.toString()));
return;
}
if (seconds > 0) {
_onStartTimer(seconds);
} else {
_timer?.cancel();
emit(TimerRunComplete());
}
}
void _getCounterValue(
GetCounterEvent event, Emitter<GarageDoorSensorState> emit) async {
emit(LoadingInitialState());
try {
var response = await DevicesAPI.getDeviceStatus(GDId);
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
}
deviceStatus = GarageDoorModel.fromJson(statusModelList);
if (event.deviceCode == 'countdown_1') {
deviceStatus.countdown1 > 0
? _onStartTimer(deviceStatus.countdown1)
: emit(UpdateTimerState(seconds: deviceStatus.countdown1));
}
} catch (e) {
emit(GarageDoorFailedState(errorMessage: e.toString()));
return;
}
}
void _onStartTimer(int seconds) {
_timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
add(TickTimer(remainingTime: seconds - timer.tick));
});
}
void _onTickTimer(TickTimer event, Emitter<GarageDoorSensorState> emit) {
if (event.remainingTime > 0) {
emit(TimerRunInProgress(event.remainingTime));
} else {
_timer?.cancel();
emit(TimerRunComplete());
}
}
}
@@ -0,0 +1,162 @@
import 'package:equatable/equatable.dart';
abstract class GarageDoorEvent extends Equatable {
const GarageDoorEvent();
@override
List<Object> get props => [];
}
class GarageDoorLoading extends GarageDoorEvent {}
class GarageDoorSwitch extends GarageDoorEvent {
final bool switchD;
final String deviceId;
final String productId;
const GarageDoorSwitch(
{required this.switchD, this.deviceId = '', this.productId = ''});
@override
List<Object> get props => [switchD, deviceId, productId];
}
class GarageDoorUpdated extends GarageDoorEvent {}
class GarageDoorInitial extends GarageDoorEvent {
const GarageDoorInitial();
}
class ReportLogsInitial extends GarageDoorEvent {
const ReportLogsInitial();
}
class GarageDoorChangeStatus extends GarageDoorEvent {}
class GetCounterEvent extends GarageDoorEvent {
final String deviceCode;
const GetCounterEvent({required this.deviceCode});
@override
List<Object> get props => [deviceCode];
}
class ToggleLowBatteryEvent extends GarageDoorEvent {
final bool isLowBatteryEnabled;
const ToggleLowBatteryEvent(this.isLowBatteryEnabled);
@override
List<Object> get props => [isLowBatteryEnabled];
}
class ToggleClosingReminderEvent extends GarageDoorEvent {
final bool isClosingReminderEnabled;
const ToggleClosingReminderEvent(this.isClosingReminderEnabled);
@override
List<Object> get props => [isClosingReminderEnabled];
}
class ToggleDoorAlarmEvent extends GarageDoorEvent {
final bool isDoorAlarmEnabled;
const ToggleDoorAlarmEvent(this.isDoorAlarmEnabled);
@override
List<Object> get props => [isDoorAlarmEnabled];
}
class SetCounterValue extends GarageDoorEvent {
final Duration duration;
final String deviceCode;
const SetCounterValue({required this.duration, required this.deviceCode});
@override
List<Object> get props => [duration, deviceCode];
}
class StartTimer extends GarageDoorEvent {
final int duration;
const StartTimer(this.duration);
@override
List<Object> get props => [duration];
}
class TickTimer extends GarageDoorEvent {
final int remainingTime;
const TickTimer({required this.remainingTime});
@override
List<Object> get props => [remainingTime];
}
class StopTimer extends GarageDoorEvent {}
class OnClose extends GarageDoorEvent {}
//------------------- Schedule ----------=---------
class GetScheduleEvent extends GarageDoorEvent {}
class ScheduleSaveapp extends GarageDoorEvent {}
class ToggleScheduleEvent extends GarageDoorEvent {
final String id;
final bool toggle;
const ToggleScheduleEvent({required this.toggle, required this.id});
@override
List<Object> get props => [toggle, id];
}
class ToggleDaySelectionEvent extends GarageDoorEvent {
final String key;
const ToggleDaySelectionEvent({required this.key});
@override
List<Object> get props => [key];
}
class DeleteScheduleEvent extends GarageDoorEvent {
final String id;
const DeleteScheduleEvent({required this.id});
@override
List<Object> get props => [id];
}
class ToggleSelectedEvent extends GarageDoorEvent {
final int index;
const ToggleSelectedEvent({required this.index});
@override
List<Object> get props => [index];
}
class ToggleCreateScheduleEvent extends GarageDoorEvent {
final int index;
const ToggleCreateScheduleEvent({required this.index});
@override
List<Object> get props => [index];
}
class ChangeFirstWizardSwitchStatusEvent extends GarageDoorEvent {
final bool value;
final String deviceId;
const ChangeFirstWizardSwitchStatusEvent(
{required this.value, this.deviceId = ''});
@override
List<Object> get props => [value, deviceId];
}
class SelectSecondsEvent extends GarageDoorEvent {
final int seconds;
const SelectSecondsEvent({required this.seconds});
@override
List<Object> get props => [seconds];
}
class ToggleDoorEvent extends GarageDoorEvent {
final bool toggle;
const ToggleDoorEvent({required this.toggle});
@override
List<Object> get props => [toggle];
}
@@ -0,0 +1,82 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/garage_door_model.dart';
class GarageDoorSensorState extends Equatable {
const GarageDoorSensorState();
@override
List<Object> get props => [];
}
class GarageDoorInitialState extends GarageDoorSensorState {}
class GarageDoorLoadingState extends GarageDoorSensorState {}
class GarageDoorFailedState extends GarageDoorSensorState {
final String errorMessage;
const GarageDoorFailedState({required this.errorMessage});
@override
List<Object> get props => [errorMessage];
}
class UpdateState extends GarageDoorSensorState {
final GarageDoorModel garageSensor;
const UpdateState({required this.garageSensor});
@override
List<Object> get props => [garageSensor];
}
class LoadingNewSate extends GarageDoorSensorState {
final GarageDoorModel doorSensor;
const LoadingNewSate({required this.doorSensor});
@override
List<Object> get props => [doorSensor];
}
class ChangeSlidingSegmentState extends GarageDoorSensorState {
final int value;
const ChangeSlidingSegmentState({required this.value});
@override
List<Object> get props => [value];
}
class UpdateTimerState extends GarageDoorSensorState {
final int seconds;
const UpdateTimerState({required this.seconds});
@override
List<Object> get props => [seconds];
}
class TimerRunInProgress extends GarageDoorSensorState {
final int remainingTime;
const TimerRunInProgress(this.remainingTime);
@override
List<Object> get props => [remainingTime];
}
class TimerRunComplete extends GarageDoorSensorState {}
class SaveSchedule extends GarageDoorSensorState {}
class IsToggleState extends GarageDoorSensorState {
final bool? onOff;
const IsToggleState({this.onOff});
}
class ChangeTimeState extends GarageDoorSensorState {}
class LoadingInitialState extends GarageDoorSensorState {}
class UpdateCreateScheduleState extends GarageDoorSensorState {
final bool createSchedule;
UpdateCreateScheduleState(this.createSchedule);
}
@@ -1,7 +1,6 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_state.dart';
@@ -31,7 +31,8 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
List<GroupThreeGangModel> groupThreeGangList = [];
bool allSwitchesOn = true;
ThreeGangBloc({required this.threeGangId, required this.switchCode}) : super(InitialState()) {
ThreeGangBloc({required this.threeGangId, required this.switchCode})
: super(InitialState()) {
on<InitialEvent>(_fetchThreeGangStatus);
on<ThreeGangUpdated>(_threeGangUpdated);
on<ChangeFirstSwitchStatusEvent>(_changeFirstSwitch);
@@ -57,7 +58,8 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
on<ToggleCreateScheduleEvent>(toggleCreateSchedule);
}
void _fetchThreeGangStatus(InitialEvent event, Emitter<ThreeGangState> emit) async {
void _fetchThreeGangStatus(
InitialEvent event, Emitter<ThreeGangState> emit) async {
emit(LoadingInitialState());
try {
threeGangGroup = event.groupScreen;
@@ -69,7 +71,8 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
HomeCubit.getInstance().selectedSpace?.id ?? '', '3G');
for (int i = 0; i < devicesList.length; i++) {
var response = await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
var response =
await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
@@ -86,13 +89,16 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
if (groupThreeGangList.isNotEmpty) {
groupThreeGangList.firstWhere((element) {
if (!element.firstSwitch || !element.secondSwitch || !element.thirdSwitch) {
if (!element.firstSwitch ||
!element.secondSwitch ||
!element.thirdSwitch) {
allSwitchesOn = false;
}
return true;
});
}
emit(UpdateGroupState(threeGangList: groupThreeGangList, allSwitches: allSwitchesOn));
emit(UpdateGroupState(
threeGangList: groupThreeGangList, allSwitches: allSwitchesOn));
} else {
var response = await DevicesAPI.getDeviceStatus(threeGangId);
List<StatusModel> statusModelList = [];
@@ -111,18 +117,21 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
_listenToChanges() {
try {
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$threeGangId');
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$threeGangId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) async {
stream.listen((DatabaseEvent event) async {
if (_timer != null) {
await Future.delayed(const Duration(seconds: 2));
}
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList.add(StatusModel(code: element['code'], value: element['value']));
statusList
.add(StatusModel(code: element['code'], value: element['value']));
});
deviceStatus = ThreeGangModel.fromJson(statusList);
@@ -137,7 +146,8 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
emit(UpdateState(threeGangModel: deviceStatus));
}
void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter<ThreeGangState> emit) async {
void _changeFirstSwitch(
ChangeFirstSwitchStatusEvent event, Emitter<ThreeGangState> emit) async {
emit(LoadingNewSate(threeGangModel: deviceStatus));
try {
if (threeGangGroup) {
@@ -146,11 +156,14 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
if (element.deviceId == event.deviceId) {
element.firstSwitch = !event.value;
}
if (!element.firstSwitch || !element.secondSwitch || !element.thirdSwitch) {
if (!element.firstSwitch ||
!element.secondSwitch ||
!element.thirdSwitch) {
allSwitchesValue = false;
}
});
emit(UpdateGroupState(threeGangList: groupThreeGangList, allSwitches: allSwitchesValue));
emit(UpdateGroupState(
threeGangList: groupThreeGangList, allSwitches: allSwitchesValue));
} else {
deviceStatus.firstSwitch = !event.value;
emit(UpdateState(threeGangModel: deviceStatus));
@@ -187,11 +200,14 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
if (element.deviceId == event.deviceId) {
element.secondSwitch = !event.value;
}
if (!element.firstSwitch || !element.secondSwitch || !element.thirdSwitch) {
if (!element.firstSwitch ||
!element.secondSwitch ||
!element.thirdSwitch) {
allSwitchesValue = false;
}
});
emit(UpdateGroupState(threeGangList: groupThreeGangList, allSwitches: allSwitchesValue));
emit(UpdateGroupState(
threeGangList: groupThreeGangList, allSwitches: allSwitchesValue));
} else {
deviceStatus.secondSwitch = !event.value;
emit(UpdateState(threeGangModel: deviceStatus));
@@ -217,7 +233,8 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
}
}
void _changeThirdSwitch(ChangeThirdSwitchStatusEvent event, Emitter<ThreeGangState> emit) async {
void _changeThirdSwitch(
ChangeThirdSwitchStatusEvent event, Emitter<ThreeGangState> emit) async {
emit(LoadingNewSate(threeGangModel: deviceStatus));
try {
if (threeGangGroup) {
@@ -226,11 +243,14 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
if (element.deviceId == event.deviceId) {
element.thirdSwitch = !event.value;
}
if (!element.firstSwitch || !element.secondSwitch || !element.thirdSwitch) {
if (!element.firstSwitch ||
!element.secondSwitch ||
!element.thirdSwitch) {
allSwitchesValue = false;
}
});
emit(UpdateGroupState(threeGangList: groupThreeGangList, allSwitches: allSwitchesValue));
emit(UpdateGroupState(
threeGangList: groupThreeGangList, allSwitches: allSwitchesValue));
} else {
deviceStatus.thirdSwitch = !event.value;
emit(UpdateState(threeGangModel: deviceStatus));
@@ -269,15 +289,21 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
final response = await Future.wait([
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: threeGangId, code: 'switch_1', value: deviceStatus.firstSwitch),
deviceId: threeGangId,
code: 'switch_1',
value: deviceStatus.firstSwitch),
threeGangId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: threeGangId, code: 'switch_2', value: deviceStatus.secondSwitch),
deviceId: threeGangId,
code: 'switch_2',
value: deviceStatus.secondSwitch),
threeGangId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: threeGangId, code: 'switch_3', value: deviceStatus.thirdSwitch),
deviceId: threeGangId,
code: 'switch_3',
value: deviceStatus.thirdSwitch),
threeGangId),
]);
@@ -303,15 +329,21 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
final response = await Future.wait([
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: threeGangId, code: 'switch_1', value: deviceStatus.firstSwitch),
deviceId: threeGangId,
code: 'switch_1',
value: deviceStatus.firstSwitch),
threeGangId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: threeGangId, code: 'switch_2', value: deviceStatus.secondSwitch),
deviceId: threeGangId,
code: 'switch_2',
value: deviceStatus.secondSwitch),
threeGangId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: threeGangId, code: 'switch_3', value: deviceStatus.thirdSwitch),
deviceId: threeGangId,
code: 'switch_3',
value: deviceStatus.thirdSwitch),
threeGangId),
]);
@@ -333,21 +365,28 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
groupThreeGangList[i].secondSwitch = true;
groupThreeGangList[i].thirdSwitch = true;
}
emit(UpdateGroupState(threeGangList: groupThreeGangList, allSwitches: true));
emit(UpdateGroupState(
threeGangList: groupThreeGangList, allSwitches: true));
for (int i = 0; i < groupThreeGangList.length; i++) {
final response = await Future.wait([
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: groupThreeGangList[i].deviceId, code: 'switch_1', value: true),
deviceId: groupThreeGangList[i].deviceId,
code: 'switch_1',
value: true),
groupThreeGangList[i].deviceId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: groupThreeGangList[i].deviceId, code: 'switch_2', value: true),
deviceId: groupThreeGangList[i].deviceId,
code: 'switch_2',
value: true),
groupThreeGangList[i].deviceId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: groupThreeGangList[i].deviceId, code: 'switch_3', value: true),
deviceId: groupThreeGangList[i].deviceId,
code: 'switch_3',
value: true),
groupThreeGangList[i].deviceId),
]);
@@ -363,7 +402,8 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
}
}
void _groupAllOff(GroupAllOffEvent event, Emitter<ThreeGangState> emit) async {
void _groupAllOff(
GroupAllOffEvent event, Emitter<ThreeGangState> emit) async {
emit(LoadingNewSate(threeGangModel: deviceStatus));
try {
for (int i = 0; i < groupThreeGangList.length; i++) {
@@ -371,21 +411,28 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
groupThreeGangList[i].secondSwitch = false;
groupThreeGangList[i].thirdSwitch = false;
}
emit(UpdateGroupState(threeGangList: groupThreeGangList, allSwitches: false));
emit(UpdateGroupState(
threeGangList: groupThreeGangList, allSwitches: false));
for (int i = 0; i < groupThreeGangList.length; i++) {
final response = await Future.wait([
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: groupThreeGangList[i].deviceId, code: 'switch_1', value: false),
deviceId: groupThreeGangList[i].deviceId,
code: 'switch_1',
value: false),
groupThreeGangList[i].deviceId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: groupThreeGangList[i].deviceId, code: 'switch_2', value: false),
deviceId: groupThreeGangList[i].deviceId,
code: 'switch_2',
value: false),
groupThreeGangList[i].deviceId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: groupThreeGangList[i].deviceId, code: 'switch_3', value: false),
deviceId: groupThreeGangList[i].deviceId,
code: 'switch_3',
value: false),
groupThreeGangList[i].deviceId),
]);
@@ -401,17 +448,20 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
}
}
void _changeSliding(ChangeSlidingSegment event, Emitter<ThreeGangState> emit) async {
void _changeSliding(
ChangeSlidingSegment event, Emitter<ThreeGangState> emit) async {
emit(ChangeSlidingSegmentState(value: event.value));
}
void _setCounterValue(SetCounterValue event, Emitter<ThreeGangState> emit) async {
void _setCounterValue(
SetCounterValue event, Emitter<ThreeGangState> emit) async {
emit(LoadingNewSate(threeGangModel: deviceStatus));
int seconds = 0;
try {
seconds = event.duration.inSeconds;
final response = await DevicesAPI.controlDevice(
DeviceControlModel(deviceId: threeGangId, code: event.deviceCode, value: seconds),
DeviceControlModel(
deviceId: threeGangId, code: event.deviceCode, value: seconds),
threeGangId);
if (response['success'] ?? false) {
@@ -438,7 +488,8 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
}
}
void _getCounterValue(GetCounterEvent event, Emitter<ThreeGangState> emit) async {
void _getCounterValue(
GetCounterEvent event, Emitter<ThreeGangState> emit) async {
emit(LoadingInitialState());
try {
var response = await DevicesAPI.getDeviceStatus(threeGangId);
@@ -541,6 +592,8 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
GetScheduleEvent event,
Emitter<ThreeGangState> emit,
) async {
print('getSchedule=${switchCode}');
try {
emit(LoadingInitialState());
final response = await DevicesAPI.getSchedule(
@@ -548,7 +601,8 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
deviceId: threeGangId,
);
List<dynamic> jsonData = response;
listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
listSchedule =
jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
emit(InitialState());
} on DioException catch (e) {
final errorData = e.response!.data;
@@ -559,12 +613,13 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
int? getTimeStampWithoutSeconds(DateTime? dateTime) {
if (dateTime == null) return null;
DateTime dateTimeWithoutSeconds =
DateTime(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute);
DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month,
dateTime.day, dateTime.hour, dateTime.minute);
return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000;
}
Future toggleChange(ToggleScheduleEvent event, Emitter<ThreeGangState> emit) async {
Future toggleChange(
ToggleScheduleEvent event, Emitter<ThreeGangState> emit) async {
try {
emit(LoadingInitialState());
final response = await DevicesAPI.changeSchedule(
@@ -583,7 +638,8 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
}
}
Future deleteSchedule(DeleteScheduleEvent event, Emitter<ThreeGangState> emit) async {
Future deleteSchedule(
DeleteScheduleEvent event, Emitter<ThreeGangState> emit) async {
try {
emit(LoadingInitialState());
final response = await DevicesAPI.deleteSchedule(
@@ -603,13 +659,15 @@ class ThreeGangBloc extends Bloc<ThreeGangEvent, ThreeGangState> {
}
}
void toggleSelectedIndex(ToggleSelectedEvent event, Emitter<ThreeGangState> emit) {
void toggleSelectedIndex(
ToggleSelectedEvent event, Emitter<ThreeGangState> emit) {
emit(LoadingInitialState());
selectedTabIndex = event.index;
emit(ChangeSlidingSegmentState(value: selectedTabIndex));
}
void toggleCreateSchedule(ToggleCreateScheduleEvent event, Emitter<ThreeGangState> emit) {
void toggleCreateSchedule(
ToggleCreateScheduleEvent event, Emitter<ThreeGangState> emit) {
emit(LoadingInitialState());
createSchedule = !createSchedule;
selectedDays.clear();
@@ -0,0 +1,81 @@
import 'package:syncrow_app/features/devices/model/status_model.dart';
class GarageDoorModel {
bool switch1;
bool doorContactState;
int countdown1;
int countdownAlarm;
String doorControl1;
bool voiceControl1;
String doorState1;
int batteryPercentage;
int tr_timecon;
GarageDoorModel({
required this.switch1,
required this.doorContactState,
required this.countdown1,
required this.countdownAlarm,
required this.doorControl1,
required this.voiceControl1,
required this.doorState1,
required this.batteryPercentage,
required this.tr_timecon,
});
factory GarageDoorModel.fromJson(List<StatusModel> jsonList) {
late bool _switch1 = false;
late bool _doorContactState = false;
late int _countdown1 = 0;
late int _countdownAlarm = 0;
late String _doorControl1 = "closed";
late bool _voiceControl1 = false;
late String _doorState1 = "closed";
late int _batteryPercentage = 0;
late int _tr_timecon = 0;
for (var status in jsonList) {
switch (status.code) {
case 'tr_timecon':
_tr_timecon = status.value ?? "closed";
break;
case 'switch_1':
_switch1 = status.value ?? false;
break;
case 'doorcontact_state':
_doorContactState = status.value ?? false;
break;
case 'countdown_1':
_countdown1 = status.value ?? 0;
break;
case 'countdown_alarm':
_countdownAlarm = status.value ?? 0;
break;
case 'door_control_1':
_doorControl1 = status.value ?? "closed";
break;
case 'voice_control_1':
_voiceControl1 = status.value ?? false;
break;
case 'door_state_1':
_doorState1 = status.value ?? "closed";
break;
case 'battery_percentage':
_batteryPercentage = status.value ?? 0;
break;
}
}
return GarageDoorModel(
switch1: _switch1,
doorContactState: _doorContactState,
countdown1: _countdown1,
countdownAlarm: _countdownAlarm,
doorControl1: _doorControl1,
voiceControl1: _voiceControl1,
doorState1: _doorState1,
batteryPercentage: _batteryPercentage,
tr_timecon: _tr_timecon);
}
}
@@ -0,0 +1,204 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/devices/view/widgets/garage_door/garagedialog.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 CreateGarageSchedule extends StatefulWidget {
final List<Map<String, String>> days;
final void Function(List<String> selectedDays)?
selectDays;
final Function(DateTime) onDateTimeChanged;
final void Function(bool isOn)? onToggleChanged;
const CreateGarageSchedule({
Key? key,
required this.days,
required this.selectDays,
required this.onDateTimeChanged,
this.onToggleChanged,
}) : super(key: key);
@override
_CreateGarageScheduleState createState() => _CreateGarageScheduleState();
}
class _CreateGarageScheduleState extends State<CreateGarageSchedule> {
List<String> selectedDays = [];
bool isOn = true;
String selectedControlOption = 'Open';
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
child: Column(
children: <Widget>[
const Divider(
color: ColorsManager.greyColor,
),
SizedBox(
height: 110,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.time,
initialDateTime: DateTime.now(),
onDateTimeChanged: widget.onDateTimeChanged,
),
),
const Divider(
color: ColorsManager.greyColor,
),
const SizedBox(height: 20),
SizedBox(
height: MediaQuery.of(context).size.height * 0.08,
child: ListView(
scrollDirection: Axis.horizontal,
children: widget.days.map((day) {
bool isSelected = selectedDays.contains(day['day']);
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
setState(() {
if (isSelected) {
selectedDays.remove(day['day']);
} else {
selectedDays.add(day['day']!);
}
if (widget.selectDays != null) {
widget.selectDays!(selectedDays);
}
});
},
child: Container(
width: 50,
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(30),
),
child: Center(
child: Text(
day['day']!,
style: TextStyle(
fontSize: 13,
color: isSelected
? Colors.black
: ColorsManager.grayColor,
),
),
),
),
),
);
}).toList(),
),
),
const SizedBox(height: 20),
DefaultContainer(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 50,
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: const BodyMedium(
text: 'Notification',
fontWeight: FontWeight.normal,
),
trailing: Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: true,
onChanged: (value) {
},
applyTheme: true,
)),
),
),
const Divider(
color: ColorsManager.graysColor,
),
SizedBox(
height: 50,
child: InkWell(
onTap: () {
showDialog(
context: context,
builder: (context) {
return GarageDialog(
label2: 'Close',
initialSelectedLabel: selectedControlOption,
cancelTab: () {
Navigator.of(context).pop();
},
confirmTab: () {
setState(() {});
if (selectedControlOption == "Open") {
widget.onToggleChanged!(true);
} else if (selectedControlOption == 'Close') {
widget.onToggleChanged!(false);
}
Navigator.of(context).pop();
},
title: 'Control',
label1: 'Open',
onTapLabel1: (selected) {
setState(() {
selectedControlOption = 'Open';
});
},
onTapLabel2: (selected) {
setState(() {
selectedControlOption = 'Close';
});
},
);
},
);
},
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: const BodyMedium(
text: 'Control',
fontWeight: FontWeight.normal,
),
trailing: SizedBox(
width: 70,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BodyMedium(
fontColor: ColorsManager.textGray,
text: selectedControlOption,
fontWeight: FontWeight.normal,
),
const Icon(
Icons.arrow_forward_ios,
color: ColorsManager.textGray,
size: 17,
)
],
),
)),
),
),
],
),
),
],
),
);
}
}
@@ -0,0 +1,428 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/garage_door_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_preferences_settings.dart';
import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_records_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/garage_door/schedule_garage_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/garage_door/timer_page.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';
class GarageDoorScreen extends StatelessWidget {
final DeviceModel? device;
const GarageDoorScreen({super.key, this.device});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Garage Door Opener',
child: BlocProvider(
create: (context) => GarageDoorBloc(GDId: device?.uuid ?? '')
..add(const GarageDoorInitial()),
child: BlocBuilder<GarageDoorBloc, GarageDoorSensorState>(
builder: (context, state) {
final garageBloc = BlocProvider.of<GarageDoorBloc>(context);
GarageDoorModel model = GarageDoorModel(
tr_timecon: 0,
countdown1: 0,
countdownAlarm: 0,
doorContactState: false,
doorControl1: '',
doorState1: '',
switch1: false,
voiceControl1: false,
batteryPercentage: 0,
);
if (state is LoadingNewSate) {
model = state.doorSensor;
} else if (state is UpdateState) {
model = state.garageSensor;
}
return state is GarageDoorLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {
garageBloc.add(const GarageDoorInitial());
},
child: ListView(
children: [
SizedBox(
height: MediaQuery.sizeOf(context).height * 0.8,
child: Column(
children: [
Expanded(
flex: 4,
child: InkWell(
overlayColor: WidgetStateProperty.all(
Colors.transparent),
onTap: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
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: InkWell(
onTap: () {
garageBloc.add(ToggleDoorEvent(
toggle:
garageBloc.toggleDoor));
},
child: GradientWidget(
doorStatus: garageBloc.toggleDoor,
seconds:
garageBloc.secondSelected,
),
)),
],
),
),
),
Flexible(
child: Row(
children: [
Expanded(
child: DefaultContainer(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
TimerScheduleScreen(
device: device!,
switchCode: 'switch_1',
deviceCode: 'countdown_1',
)),
);
},
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 46, maxWidth: 50),
child: SvgPicture.asset(
Assets.garageSchedule),
),
const SizedBox(
height: 15,
),
const Flexible(
child: FittedBox(
child: BodySmall(
text: 'Schedule',
textAlign: TextAlign.center,
),
),
),
],
),
),
),
const SizedBox(
width: 10,
),
Expanded(
child: DefaultContainer(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => TimerPage(
device: device!,
switchCode: 'switch_1',
deviceCode: 'countdown_1',
)),
);
},
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 46, maxWidth: 50),
child: SvgPicture.asset(
Assets.garageCountdown),
),
const SizedBox(
height: 15,
),
const Flexible(
child: FittedBox(
child: BodySmall(
text: 'Countdown',
textAlign: TextAlign.center,
),
),
),
],
),
),
),
],
),
),
const SizedBox(
height: 10,
),
Flexible(
child: Row(
children: [
Expanded(
child: DefaultContainer(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
GarageRecordsScreen(
GDId: device!.uuid!)),
);
},
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 46, maxWidth: 50),
child: SvgPicture.asset(
Assets.doorRecordsIcon),
),
const SizedBox(
height: 15,
),
const Flexible(
child: FittedBox(
child: BodySmall(
text: 'Records',
textAlign: TextAlign.center,
),
),
),
],
),
),
),
const SizedBox(
width: 10,
),
Expanded(
child: DefaultContainer(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
PreferencesPage(
GDId: device!.uuid!)),
);
},
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 46, maxWidth: 50),
child: SvgPicture.asset(
Assets.garagePreferencesIcon),
),
const SizedBox(
height: 15,
),
const Flexible(
child: FittedBox(
child: BodySmall(
text: 'Preferences',
textAlign: TextAlign.center,
),
),
),
],
),
),
),
],
),
)
],
),
),
],
),
);
},
),
),
);
}
}
class GradientWidget extends StatefulWidget {
const GradientWidget({required this.seconds, required this.doorStatus});
final int seconds;
final bool doorStatus;
@override
State<GradientWidget> createState() => _GradientWidgetState();
}
class _GradientWidgetState extends State<GradientWidget>
with SingleTickerProviderStateMixin {
late ScrollController _scrollController;
late AnimationController _animationController;
late Animation<double> _itemExtentAnimation;
@override
void initState() {
super.initState();
_scrollController = ScrollController();
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: widget.seconds),
);
_itemExtentAnimation = Tween<double>(
begin: widget.doorStatus ? 0 : 15, end: widget.doorStatus ? 15 : 0)
.animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
),
)..addListener(() {
setState(() {});
});
WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.doorStatus) {
_openAnimation();
} else {
_closeAnimation();
}
});
}
void _openAnimation() {
_animationController.forward();
}
void _closeAnimation() {
_animationController.reverse();
}
@override
void dispose() {
_scrollController.dispose();
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: 300,
height: 300,
padding: EdgeInsets.only(right: 10),
child: Stack(
children: [
SvgPicture.asset(
Assets.openGarageIcon,
fit: BoxFit.fill,
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(child: Container()),
Expanded(
flex: 2,
child: Center(
child: Container(
height: 104,
child: ListView.builder(
itemExtent: _itemExtentAnimation.value,
controller: _scrollController,
itemCount: 4,
itemBuilder: (context, index) {
return Center(child: GradientWidget1());
},
),
),
),
),
],
),
],
),
);
}
}
class GradientWidget1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 97,
height: 15,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFF017297),
Color(0xFF024C67),
],
stops: [0.0, 1.0],
),
),
);
}
}
@@ -0,0 +1,341 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_state.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_large.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class PreferencesPage extends StatelessWidget {
final String GDId;
const PreferencesPage({super.key, required this.GDId});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Preferences',
child: BlocProvider(
create: (context) =>
GarageDoorBloc(GDId: GDId)..add(const GarageDoorInitial()),
child: BlocBuilder<GarageDoorBloc, GarageDoorSensorState>(
builder: (context, state) {
final garageDoorBloc = BlocProvider.of<GarageDoorBloc>(context);
return state is GarageDoorLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: Column(
children: [
const Row(
children: [
BodyMedium(
text: 'Warnings',
fontColor: ColorsManager.grayColor,
fontWeight: FontWeight.w700,
),
],
),
DefaultContainer(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 50,
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: const BodyMedium(
text: 'Alarm when door is open',
fontWeight: FontWeight.normal,
),
trailing: Container(
width: 100,
child: Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
Container(
height: 30,
width: 1,
color: ColorsManager.graysColor,
),
Transform.scale(
scale: .8,
child: CupertinoSwitch(
value:
garageDoorBloc.lowBattery,
onChanged: (value) {
// context
// .read<GarageDoorBloc>()
// .add(
// ToggleLowBatteryEvent(
// value));
},
applyTheme: true,
)),
],
),
)),
),
],
),
),
const SizedBox(
height: 10,
),
const Row(
children: [
BodyMedium(
text: 'Other Settings',
fontColor: ColorsManager.grayColor,
fontWeight: FontWeight.w700,
),
],
),
DefaultContainer(
child: InkWell(
onTap: () {
showDialog(
context: context,
builder: (context) {
return SecondDialog(
label2: 'Close',
initialSelectedLabel: garageDoorBloc
.secondSelected
.toString(),
cancelTab: () {
Navigator.of(context).pop();
},
confirmTab: (v) {
garageDoorBloc
.add(SelectSecondsEvent(seconds: v));
Navigator.of(context).pop();
},
title: 'Control',
label1: 'Open',
onTapLabel1: (selected) {},
);
},
);
},
child: SizedBox(
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: const BodyMedium(
text: 'Opening and Closing Time',
fontWeight: FontWeight.normal,
),
trailing: Container(
height: 90,
width: 120,
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
BodyMedium(
fontColor: ColorsManager.textGray,
text:
'${garageDoorBloc.secondSelected.toString()} Seconds',
fontWeight: FontWeight.normal,
),
const Icon(
Icons.arrow_forward_ios,
size: 15,
color: ColorsManager.textGray,
)
],
),
)),
),
),
)
],
);
},
),
));
}
}
class SecondDialog extends StatefulWidget {
final String label1;
final String label2;
final String title;
final Function(String)? onTapLabel1;
final Function()? cancelTab;
final Function(int selectedSecond)? confirmTab;
final String? initialSelectedLabel;
SecondDialog({
required this.label1,
required this.label2,
required this.title,
this.onTapLabel1,
required this.cancelTab,
required this.confirmTab,
this.initialSelectedLabel,
});
@override
_SecondDialogState createState() => _SecondDialogState();
}
class _SecondDialogState extends State<SecondDialog> {
late String _selectedOption;
late int selectedSecond;
@override
void initState() {
super.initState();
// Parse the initialSelectedLabel as an integer. Default to 10 if invalid or not provided.
selectedSecond = int.tryParse(widget.initialSelectedLabel ?? '10') ?? 10;
_selectedOption = widget.initialSelectedLabel ?? '';
}
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const SizedBox(
height: 10,
),
BodyLarge(
text: widget.title,
fontWeight: FontWeight.w700,
fontColor: ColorsManager.primaryColor,
fontSize: 16,
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 15),
child: Divider(
color: ColorsManager.textGray,
),
),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15, bottom: 10),
child: Column(
children: [
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 100,
child: CupertinoPicker(
itemExtent: 40.0,
scrollController: FixedExtentScrollController(
// Set the initial position based on selectedSecond
initialItem: selectedSecond - 10,
),
onSelectedItemChanged: (int index) {
setState(() {
selectedSecond = index + 10;
});
},
children: List<Widget>.generate(111, (int index) {
return Center(
child: BodyLarge(
text: (index + 10).toString().padLeft(2, '0'),
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 30,
color: Colors.blue),
),
);
}),
),
),
const Text('Sec'),
],
),
)
],
),
),
Row(
children: [
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: SizedBox(
child: InkWell(
onTap: widget.cancelTab,
child: const Padding(
padding: EdgeInsets.all(15),
child: Center(
child: Text(
'Cancel',
style: TextStyle(
color: ColorsManager.textGray,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
),
),
),
),
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
left: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: InkWell(
onTap: () {
widget.confirmTab?.call(selectedSecond);
},
child: const Padding(
padding: EdgeInsets.all(15),
child: Center(
child: Text(
'Confirm',
style: TextStyle(
color: ColorsManager.primaryColor,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
)),
))
],
)
],
),
);
}
}
@@ -0,0 +1,122 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_state.dart';
import 'package:syncrow_app/features/devices/model/device_report_model.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/utils/resource_manager/color_manager.dart';
class GarageRecordsScreen extends StatelessWidget {
final String GDId;
const GarageRecordsScreen({super.key, required this.GDId});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Records',
child: BlocProvider(
create: (context) =>
GarageDoorBloc(GDId: GDId)..add(const ReportLogsInitial()),
child: BlocBuilder<GarageDoorBloc, GarageDoorSensorState>(
builder: (context, state) {
final garageDoorBloc = BlocProvider.of<GarageDoorBloc>(context);
final Map<String, List<DeviceEvent>> groupedRecords = {};
if (state is GarageDoorLoadingState) {
return const Center(
child: DefaultContainer(
width: 50, height: 50, child: CircularProgressIndicator()),
);
} else if (state is UpdateState) {
for (var record in garageDoorBloc.recordGroups.data!) {
final DateTime eventDateTime =
DateTime.fromMillisecondsSinceEpoch(record.eventTime!);
final String formattedDate =
DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime);
// Group by formatted date
if (groupedRecords.containsKey(formattedDate)) {
groupedRecords[formattedDate]!.add(record);
} else {
groupedRecords[formattedDate] = [record];
}
}
}
return ListView.builder(
itemCount: groupedRecords.length,
itemBuilder: (context, index) {
final String date = groupedRecords.keys.elementAt(index);
final List<DeviceEvent> recordsForDate = groupedRecords[date]!;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 5, top: 10),
child: Text(
date,
style: const TextStyle(
color: ColorsManager.grayColor,
fontSize: 13,
fontWeight: FontWeight.w700,
),
),
),
DefaultContainer(
child: Column(
children: [
...recordsForDate.asMap().entries.map((entry) {
final int idx = entry.key;
final DeviceEvent record = entry.value;
final DateTime eventDateTime =
DateTime.fromMillisecondsSinceEpoch(
record.eventTime!);
final String formattedTime =
DateFormat('HH:mm:ss').format(eventDateTime);
return Column(
children: [
Container(
child: ListTile(
leading: Icon(
record.value == 'true'
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
color: record.value == 'true'
? Colors.blue
: Colors.grey,
),
title: Text(
record.value == 'true'
? "Opened"
: "Closed",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
subtitle: Text('$formattedTime'),
),
),
if (idx != recordsForDate.length - 1)
const Divider(
color: ColorsManager.graysColor,
),
],
);
}).toList(),
],
),
),
],
);
},
);
}),
),
);
}
}
@@ -0,0 +1,221 @@
import 'package:flutter/material.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/utils/resource_manager/color_manager.dart';
class GarageDialog extends StatefulWidget {
final String label1;
final String label2;
final String title;
final Function(String)? onTapLabel1;
final Function(String)? onTapLabel2;
final Function()? cancelTab;
final Function()? confirmTab;
final String? initialSelectedLabel;
GarageDialog({
required this.label1,
required this.label2,
required this.title,
this.onTapLabel1,
this.onTapLabel2,
required this.cancelTab,
required this.confirmTab,
this.initialSelectedLabel,
});
@override
_GarageDialogState createState() => _GarageDialogState();
}
class _GarageDialogState extends State<GarageDialog> {
late String _selectedOption;
@override
void initState() {
super.initState();
_selectedOption = widget.initialSelectedLabel ?? '';
}
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const SizedBox(
height: 10,
),
BodyLarge(
text: widget.title,
fontWeight: FontWeight.w700,
fontColor: ColorsManager.primaryColor,
fontSize: 16,
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 15),
child: Divider(
color: ColorsManager.textGray,
),
),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: Column(
children: [
_buildCheckboxOption(
label: widget.label1,
onTap: widget.onTapLabel1,
),
_buildCheckboxOption(
label: widget.label2,
onTap: widget.onTapLabel2,
),
],
),
),
Row(
children: [
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: SizedBox(
child: InkWell(
onTap: widget.cancelTab,
child: const Padding(
padding: EdgeInsets.all(15),
child: Center(
child: Text(
'Cancel',
style: TextStyle(
color: ColorsManager.textGray,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
),
),
),
),
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
left: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: InkWell(
onTap: widget.confirmTab,
child: const Padding(
padding: EdgeInsets.all(15),
child: Center(
child: Text(
'Confirm',
style: TextStyle(
color: ColorsManager.primaryColor,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
)),
))
],
)
],
),
);
}
Widget _buildCheckboxOption(
{required String label, Function(String)? onTap}) {
return Padding(
padding: const EdgeInsets.only(bottom: 10, top: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BodyMedium(
text: label,
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400),
),
CircularCheckbox(
value: _selectedOption == label,
onChanged: (bool? value) {
if (value == true) {
setState(() {
_selectedOption = label;
});
if (onTap != null) {
onTap(label);
}
}
},
),
],
),
);
}
}
class CircularCheckbox extends StatefulWidget {
final bool value;
final ValueChanged<bool?> onChanged;
CircularCheckbox({required this.value, required this.onChanged});
@override
_CircularCheckboxState createState() => _CircularCheckboxState();
}
class _CircularCheckboxState extends State<CircularCheckbox> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
widget.onChanged(!widget.value);
},
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: widget.value
? ColorsManager.primaryColorWithOpacity.withOpacity(0.01)
: Colors.grey,
width: 2.0,
),
color: widget.value
? ColorsManager.primaryColorWithOpacity
: Colors.transparent,
),
width: 24.0,
height: 24.0,
child: widget.value
? const Icon(
Icons.check,
color: Colors.white,
size: 16.0,
)
: null,
),
);
}
}
@@ -0,0 +1,163 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/garage_door/create_schedule_garage.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/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
class TimerScheduleScreen extends StatelessWidget {
final DeviceModel device;
final String deviceCode;
final String switchCode;
const TimerScheduleScreen(
{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) =>
GarageDoorBloc(GDId: device.uuid ?? '')..add(GetScheduleEvent()),
child: BlocBuilder<GarageDoorBloc, GarageDoorSensorState>(
builder: (context, state) {
final garageBloc = BlocProvider.of<GarageDoorBloc>(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) {
garageBloc.add(OnClose());
Navigator.pop(context);
}
},
child: DefaultTabController(
length: 2,
child: DefaultScaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const BodyLarge(
text: 'Schedule',
fontColor: ColorsManager.primaryColor,
fontWeight: FontsManager.bold,
),
actions: [
garageBloc.createSchedule == true
? TextButton(
onPressed: () {
garageBloc.add(ScheduleSaveapp());
},
child: const Text('Save'))
: IconButton(
onPressed: () {
garageBloc.add(
const ToggleCreateScheduleEvent(
index: 1));
},
icon: const Icon(Icons.add),
)
],
),
child: state is GarageDoorLoading
? 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)),
),
),
),
Expanded(
child: SizedBox(
child: garageBloc.createSchedule == true
? CreateGarageSchedule(
onToggleChanged: (bool value) {
garageBloc.toggleSchedule = value;
},
onDateTimeChanged:
(DateTime dateTime) {
garageBloc.selectedTime =
dateTime;
},
days: garageBloc.days,
selectDays:
(List<String> selectedDays) {
garageBloc.selectedDays =
selectedDays;
},
)
: Padding(
padding:
const EdgeInsets.only(top: 10),
child: ScheduleListView(
listSchedule:
garageBloc.listSchedule,
onDismissed: (scheduleId) {
garageBloc.listSchedule
.removeWhere((schedule) =>
schedule.scheduleId ==
scheduleId);
garageBloc.add(
DeleteScheduleEvent(
id: scheduleId));
},
onToggleSchedule:
(scheduleId, isEnabled) {
garageBloc
.add(ToggleScheduleEvent(
id: scheduleId,
toggle: isEnabled,
));
},
),
),
),
),
],
),
)));
},
),
),
);
}
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';
}
}
@@ -0,0 +1,148 @@
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/svg.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
class TimerPage extends StatelessWidget {
final DeviceModel device;
final String deviceCode;
final String switchCode;
const TimerPage(
{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) => GarageDoorBloc(GDId: device.uuid ?? '')
..add(GetCounterEvent(deviceCode: deviceCode)),
child: BlocBuilder<GarageDoorBloc, GarageDoorSensorState>(
builder: (context, state) {
final oneGangBloc = BlocProvider.of<GarageDoorBloc>(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) {
oneGangBloc.add(OnClose());
Navigator.pop(context);
}
},
child: DefaultTabController(
length: 2,
child: DefaultScaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const BodyLarge(
text: 'Countdown',
fontColor: ColorsManager.primaryColor,
fontWeight: FontsManager.bold,
),
),
child: state is GarageDoorLoadingState
? const Center(child: CircularProgressIndicator())
: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width,
decoration: const ShapeDecoration(
color: ColorsManager.onPrimaryColor,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(30)),
),
),
),
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) {
oneGangBloc.add(
const SetCounterValue(
deviceCode:
'countdown_1',
duration:
Duration.zero));
} else if (duration !=
Duration.zero) {
oneGangBloc.add(SetCounterValue(
deviceCode: 'countdown_1',
duration: duration));
}
},
child: SvgPicture.asset(countNum > 0
? Assets.pauseIcon
: Assets.playIcon)),
],
),
),
),
],
),
)));
},
),
),
);
}
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';
}
}
@@ -142,8 +142,7 @@ class OneTouchSetting extends StatelessWidget {
bottom: 10, top: 10),
child: InkWell(
onTap: () async {
oneTouchBloc.optionSelected =
'light_mode';
oneTouchBloc.optionSelected = 'light_mode';
final result = await showDialog(
context: context,
builder: (context) {
@@ -10,6 +10,7 @@ import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/door_sensor/door_sensor_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_door_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_Interface.dart';
@@ -187,6 +188,14 @@ void showDeviceInterface(DeviceModel device, BuildContext context) {
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) =>
ThreeTouchInterface(touchSwitch: device)));
case DeviceType.GarageDoor:
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) =>
GarageDoorScreen(device: device)));
break;
default:
}
@@ -137,3 +137,5 @@ class _CreateScheduleState extends State<CreateSchedule> {
);
}
}
+6
View File
@@ -1059,4 +1059,10 @@ class Assets {
"assets/icons/door_notification_setting_icon.svg";
static const String doorRecordsIcon = "assets/icons/door_records_icon.svg";
static const String doorSensorIcon = "assets/icons/door_sensor_icon.svg";
static const String closedGarageIcon = "assets/icons/closed_garage_door.svg";
static const String openGarageIcon = "assets/icons/open_garage_door.svg";
static const String garageCountdown = "assets/icons/garage_countdown.svg";
static const String garagePreferencesIcon = "assets/icons/garage_preferences_icon.svg";
static const String garageSchedule = "assets/icons/garage_schedule.svg";
//open_garage_door.svg
}
+39 -1
View File
@@ -53,6 +53,7 @@ enum DeviceType {
OneTouch,
TowTouch,
ThreeTouch,
GarageDoor,
Other,
}
@@ -83,6 +84,7 @@ Map<String, DeviceType> devicesTypesMap = {
"1GT": DeviceType.OneTouch,
"2GT": DeviceType.TowTouch,
"3GT": DeviceType.ThreeTouch,
"GD": DeviceType.GarageDoor,
};
Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
DeviceType.AC: [
@@ -370,7 +372,6 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
"range": ['power_off', 'power_on', 'last']
})),
],
DeviceType.ThreeTouch: [
FunctionModel(
code: 'switch_1',
@@ -430,6 +431,43 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
"range": ['power_off', 'power_on', 'last']
})),
],
DeviceType.GarageDoor: [
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": 86400, "scale": 0, "step": 1})),
FunctionModel(
code: 'tr_timecon',
type: functionTypesMap['Integer'],
values: ValueModel.fromJson(
{"unit": "s", "min": 0, "max": 120, "scale": 0, "step": 1})),
FunctionModel(
code: 'countdown_alarm',
type: functionTypesMap['Integer'],
values: ValueModel.fromJson(
{"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
FunctionModel(
code: 'door_control_1',
type: functionTypesMap['Enum'],
values: ValueModel.fromJson({
"range": ['open', 'open']
})),
FunctionModel(
code: 'voice_control_1',
type: functionTypesMap['Boolean'],
values: ValueModel.fromJson({})),
FunctionModel(
code: 'door_state_1',
type: functionTypesMap['Enum'],
values: ValueModel.fromJson({
"range": ["unclosed_time", "close_time_alarm", "none"]
})),
],
};
enum TempModes { hot, cold, wind }