mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 22:57:21 +00:00
607 lines
19 KiB
Dart
607 lines
19 KiB
Dart
import 'dart:async';
|
|
import 'package:bloc/bloc.dart';
|
|
import 'package:equatable/equatable.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
|
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart';
|
|
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
|
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
|
import 'package:syncrow_web/services/control_device_service.dart';
|
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
|
part 'schedule_event.dart';
|
|
part 'schedule_state.dart';
|
|
|
|
class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
|
final String deviceId;
|
|
|
|
ScheduleBloc({
|
|
required this.deviceId,
|
|
}) : super(ScheduleInitial()) {
|
|
on<ScheduleInitializeAddEvent>(_initializeAddSchedule);
|
|
on<ScheduleUpdateSelectedTimeEvent>(_updateSelectedTime);
|
|
on<ScheduleUpdateSelectedDayEvent>(_updateSelectedDay);
|
|
on<ScheduleUpdateFunctionOnEvent>(_updateFunctionOn);
|
|
on<ScheduleGetEvent>(_getSchedule);
|
|
on<ScheduleAddEvent>(_onAddSchedule);
|
|
on<ScheduleEditEvent>(_onEditSchedule);
|
|
on<ScheduleUpdateEntryEvent>(_onUpdateSchedule);
|
|
on<UpdateScheduleModeEvent>(_onUpdateScheduleMode);
|
|
on<UpdateCountdownTimeEvent>(_onUpdateCountdownTime);
|
|
on<UpdateInchingTimeEvent>(_onUpdateInchingTime);
|
|
on<StartScheduleEvent>(_onStartScheduleEvent);
|
|
on<StopScheduleEvent>(_onStopScheduleEvent);
|
|
on<ScheduleDecrementCountdownEvent>(_onDecrementCountdown);
|
|
on<ScheduleFetchStatusEvent>(_fetchStatus);
|
|
on<ScheduleDeleteEvent>(_onDeleteSchedule);
|
|
}
|
|
Timer? _countdownTimer;
|
|
Duration countdownRemaining = Duration.zero;
|
|
|
|
Future<void> _onStopScheduleEvent(
|
|
StopScheduleEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) async {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
|
|
final success = await RemoteControlDeviceService().controlDevice(
|
|
deviceUuid: deviceId,
|
|
status: Status(
|
|
code: 'countdown_1',
|
|
value: 0,
|
|
),
|
|
);
|
|
if (success) {
|
|
_countdownTimer?.cancel();
|
|
if (event.mode == ScheduleModes.countdown) {
|
|
emit(currentState.copyWith(
|
|
countdownHours: 0,
|
|
countdownMinutes: 0,
|
|
isCountdownActive: false,
|
|
countdownRemaining: Duration.zero,
|
|
));
|
|
} else if (event.mode == ScheduleModes.inching) {
|
|
emit(currentState.copyWith(
|
|
inchingHours: 0,
|
|
inchingMinutes: 0,
|
|
isInchingActive: false,
|
|
countdownRemaining: Duration.zero,
|
|
));
|
|
}
|
|
} else {
|
|
emit(const ScheduleError('Failed to stop schedule'));
|
|
}
|
|
}
|
|
}
|
|
|
|
void _onUpdateScheduleMode(
|
|
UpdateScheduleModeEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
emit(currentState.copyWith(
|
|
scheduleMode: event.scheduleMode,
|
|
countdownRemaining: Duration.zero,
|
|
countdownHours: 0,
|
|
countdownMinutes: 0,
|
|
inchingHours: 0,
|
|
inchingMinutes: 0,
|
|
isCountdownActive: false,
|
|
isInchingActive: false,
|
|
));
|
|
}
|
|
}
|
|
|
|
void _onUpdateCountdownTime(
|
|
UpdateCountdownTimeEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
emit(currentState.copyWith(
|
|
countdownSeconds: event.seconds,
|
|
countdownHours: event.hours,
|
|
countdownMinutes: event.minutes,
|
|
inchingHours: 0,
|
|
inchingMinutes: 0,
|
|
countdownRemaining: Duration.zero,
|
|
));
|
|
}
|
|
}
|
|
|
|
void _onUpdateInchingTime(
|
|
UpdateInchingTimeEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
emit(currentState.copyWith(
|
|
inchingHours: event.hours,
|
|
inchingMinutes: event.minutes,
|
|
countdownRemaining: Duration.zero,
|
|
inchingSeconds: 0, // Add this
|
|
));
|
|
}
|
|
}
|
|
|
|
void _initializeAddSchedule(
|
|
ScheduleInitializeAddEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
emit(currentState.copyWith(
|
|
selectedTime: event.selectedTime,
|
|
selectedDays: event.selectedDays ?? List.filled(7, false),
|
|
functionOn: event.functionOn ?? false,
|
|
isEditing: event.isEditing,
|
|
scheduleMode: event.scheduleMode,
|
|
countdownRemaining: Duration.zero,
|
|
));
|
|
} else {
|
|
emit(ScheduleLoaded(
|
|
schedules: const [],
|
|
selectedTime: event.selectedTime,
|
|
selectedDays: event.selectedDays ?? List.filled(7, false),
|
|
functionOn: event.functionOn ?? false,
|
|
isEditing: event.isEditing,
|
|
deviceId: deviceId,
|
|
scheduleMode: event.scheduleMode,
|
|
countdownHours: 0,
|
|
countdownMinutes: 0,
|
|
inchingHours: 0,
|
|
inchingMinutes: 0,
|
|
isCountdownActive: false,
|
|
isInchingActive: false,
|
|
));
|
|
}
|
|
}
|
|
|
|
void _updateSelectedTime(
|
|
ScheduleUpdateSelectedTimeEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
emit(currentState.copyWith(
|
|
selectedTime: event.selectedTime,
|
|
countdownHours: 0,
|
|
countdownMinutes: 0,
|
|
inchingHours: 0,
|
|
inchingMinutes: 0,
|
|
countdownRemaining: Duration.zero,
|
|
));
|
|
}
|
|
}
|
|
|
|
void _updateSelectedDay(
|
|
ScheduleUpdateSelectedDayEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
final updatedDays = List<bool>.from(currentState.selectedDays);
|
|
updatedDays[event.index] = event.value;
|
|
emit(currentState.copyWith(
|
|
selectedDays: updatedDays,
|
|
countdownHours: 0,
|
|
countdownMinutes: 0,
|
|
inchingHours: 0,
|
|
inchingMinutes: 0,
|
|
countdownRemaining: Duration.zero,
|
|
));
|
|
}
|
|
}
|
|
|
|
void _updateFunctionOn(
|
|
ScheduleUpdateFunctionOnEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
emit(currentState.copyWith(
|
|
functionOn: event.isOn,
|
|
countdownHours: 0,
|
|
countdownMinutes: 0,
|
|
inchingHours: 0,
|
|
inchingMinutes: 0,
|
|
countdownRemaining: Duration.zero,
|
|
));
|
|
}
|
|
}
|
|
|
|
Future<void> _getSchedule(
|
|
ScheduleGetEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) async {
|
|
try {
|
|
emit(ScheduleLoading());
|
|
final schedules = await DevicesManagementApi().getDeviceSchedules(
|
|
deviceId,
|
|
event.category,
|
|
);
|
|
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
emit(currentState.copyWith(
|
|
schedules: schedules,
|
|
selectedTime: null,
|
|
selectedDays: List.filled(7, false),
|
|
functionOn: false,
|
|
isEditing: false,
|
|
countdownRemaining: Duration.zero,
|
|
));
|
|
} else {
|
|
emit(ScheduleLoaded(
|
|
schedules: schedules,
|
|
selectedTime: null,
|
|
selectedDays: List.filled(7, false),
|
|
functionOn: false,
|
|
isEditing: false,
|
|
deviceId: deviceId,
|
|
scheduleMode: ScheduleModes.schedule,
|
|
countdownHours: 0,
|
|
countdownMinutes: 0,
|
|
inchingHours: 0,
|
|
inchingMinutes: 0,
|
|
isCountdownActive: false,
|
|
isInchingActive: false,
|
|
));
|
|
}
|
|
} catch (e) {
|
|
emit(ScheduleError('Failed to load schedules: $e'));
|
|
}
|
|
}
|
|
|
|
Future<void> _onAddSchedule(
|
|
ScheduleAddEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) async {
|
|
try {
|
|
if (state is ScheduleLoaded) {
|
|
final dateTime = DateTime.parse(event.time);
|
|
final success = await DevicesManagementApi().postSchedule(
|
|
category: event.category,
|
|
deviceId: deviceId,
|
|
time: getTimeStampWithoutSeconds(dateTime).toString(),
|
|
code: event.code ?? event.category,
|
|
value: event.functionOn,
|
|
days: event.selectedDays);
|
|
if (success) {
|
|
add(ScheduleGetEvent(category: event.category));
|
|
} else {
|
|
emit(const ScheduleError('Failed to add schedule'));
|
|
}
|
|
}
|
|
} catch (e) {
|
|
emit(ScheduleError('Failed to add schedule: $e'));
|
|
}
|
|
}
|
|
|
|
Future<void> _onEditSchedule(
|
|
ScheduleEditEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) async {
|
|
try {
|
|
if (state is ScheduleLoaded) {
|
|
final dateTime = DateTime.parse(event.time);
|
|
Status status = Status(code: '', value: '');
|
|
if (event.category == 'CUR_2') {
|
|
status = status.copyWith(
|
|
code: 'control',
|
|
value: event.functionOn == true ? 'open' : 'close');
|
|
} else {
|
|
status =
|
|
status.copyWith(code: event.category, value: event.functionOn);
|
|
}
|
|
final updatedSchedule = ScheduleEntry(
|
|
scheduleId: event.scheduleId,
|
|
category: event.category,
|
|
time: getTimeStampWithoutSeconds(dateTime).toString(),
|
|
function: status,
|
|
days: event.selectedDays,
|
|
);
|
|
final success = await DevicesManagementApi().editScheduleRecord(
|
|
deviceId,
|
|
updatedSchedule,
|
|
);
|
|
|
|
if (success) {
|
|
add(ScheduleGetEvent(
|
|
category: event.category,
|
|
));
|
|
} else {
|
|
emit(const ScheduleError('Failed to update schedule'));
|
|
}
|
|
}
|
|
} catch (e) {
|
|
emit(ScheduleError('Failed to update schedule: $e'));
|
|
}
|
|
}
|
|
|
|
Future<void> _onUpdateSchedule(
|
|
ScheduleUpdateEntryEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) async {
|
|
try {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
|
|
final updatedSchedules = currentState.schedules.map((schedule) {
|
|
if (schedule.scheduleId == event.scheduleId) {
|
|
return schedule.copyWith(
|
|
function: Status(code: event.category, value: event.functionOn),
|
|
enable: event.enable,
|
|
);
|
|
}
|
|
return schedule;
|
|
}).toList();
|
|
|
|
final success = await DevicesManagementApi().updateScheduleRecord(
|
|
enable: event.enable,
|
|
uuid: deviceId,
|
|
scheduleId: event.scheduleId,
|
|
);
|
|
|
|
if (success) {
|
|
emit(currentState.copyWith(
|
|
schedules: updatedSchedules,
|
|
countdownHours: 0,
|
|
countdownMinutes: 0,
|
|
inchingHours: 0,
|
|
inchingMinutes: 0,
|
|
countdownRemaining: Duration.zero,
|
|
));
|
|
} else {
|
|
emit(const ScheduleError('Failed to update schedule status'));
|
|
}
|
|
}
|
|
} catch (e) {
|
|
emit(ScheduleError('Failed to update schedule: $e'));
|
|
}
|
|
}
|
|
|
|
Future<void> _onDeleteSchedule(
|
|
ScheduleDeleteEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) async {
|
|
try {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
final success = await DevicesManagementApi().deleteScheduleRecord(
|
|
deviceId,
|
|
event.scheduleId,
|
|
);
|
|
|
|
if (success) {
|
|
final updatedSchedules = currentState.schedules
|
|
.where((s) => s.scheduleId != event.scheduleId)
|
|
.toList();
|
|
emit(currentState.copyWith(
|
|
schedules: updatedSchedules,
|
|
countdownHours: 0,
|
|
countdownMinutes: 0,
|
|
inchingHours: 0,
|
|
inchingMinutes: 0,
|
|
countdownRemaining: Duration.zero,
|
|
));
|
|
} else {
|
|
emit(const ScheduleError('Failed to delete schedule'));
|
|
}
|
|
}
|
|
} catch (e) {
|
|
emit(ScheduleError('Failed to delete schedule: $e'));
|
|
}
|
|
}
|
|
|
|
Duration? _currentCountdown;
|
|
|
|
Future<void> _onStartScheduleEvent(
|
|
StartScheduleEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) async {
|
|
if (state is ScheduleLoaded) {
|
|
final totalSeconds =
|
|
Duration(hours: event.hours, minutes: event.minutes).inSeconds;
|
|
final code = event.mode == ScheduleModes.countdown
|
|
? 'countdown_1'
|
|
: 'switch_inching';
|
|
final currentState = state as ScheduleLoaded;
|
|
final duration = Duration(seconds: totalSeconds);
|
|
_currentCountdown = duration;
|
|
emit(currentState.copyWith(
|
|
countdownRemaining: duration,
|
|
schedules: currentState.schedules.map((schedule) {
|
|
if (schedule.function.code == code) {
|
|
return schedule.copyWith(
|
|
function: Status(code: code, value: totalSeconds),
|
|
);
|
|
}
|
|
return schedule;
|
|
}).toList(),
|
|
countdownHours: event.mode == ScheduleModes.countdown ? event.hours : 0,
|
|
));
|
|
|
|
final success = await RemoteControlDeviceService().controlDevice(
|
|
deviceUuid: deviceId,
|
|
status: Status(
|
|
code: code,
|
|
value: totalSeconds,
|
|
),
|
|
);
|
|
|
|
if (success) {
|
|
if (code == 'countdown_1') {
|
|
final countdownDuration = Duration(seconds: totalSeconds);
|
|
|
|
emit(
|
|
currentState.copyWith(
|
|
countdownHours: countdownDuration.inHours,
|
|
countdownMinutes: countdownDuration.inMinutes % 60,
|
|
countdownRemaining: countdownDuration,
|
|
isCountdownActive: true,
|
|
countdownSeconds: countdownDuration.inSeconds,
|
|
),
|
|
);
|
|
|
|
if (countdownDuration.inSeconds > 0) {
|
|
_startCountdownTimer(emit, countdownDuration);
|
|
} else {
|
|
_countdownTimer?.cancel();
|
|
emit(
|
|
currentState.copyWith(
|
|
countdownHours: 0,
|
|
countdownMinutes: 0,
|
|
countdownRemaining: Duration.zero,
|
|
isCountdownActive: false,
|
|
countdownSeconds: 0,
|
|
),
|
|
);
|
|
}
|
|
} else if (code == 'switch_inching') {
|
|
final inchingDuration = Duration(seconds: totalSeconds);
|
|
emit(
|
|
currentState.copyWith(
|
|
inchingHours: inchingDuration.inHours,
|
|
inchingMinutes: inchingDuration.inMinutes % 60,
|
|
isInchingActive: true,
|
|
countdownRemaining: inchingDuration,
|
|
countdownSeconds: inchingDuration.inSeconds,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void _startCountdownTimer(
|
|
Emitter<ScheduleState> emit,
|
|
Duration duration,
|
|
) {
|
|
_countdownTimer?.cancel();
|
|
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
if (_currentCountdown != null && _currentCountdown! > Duration.zero) {
|
|
_currentCountdown = _currentCountdown! - const Duration(seconds: 1);
|
|
countdownRemaining = _currentCountdown!;
|
|
add(const ScheduleDecrementCountdownEvent());
|
|
} else {
|
|
timer.cancel();
|
|
add(StopScheduleEvent(
|
|
mode: _currentCountdown == null
|
|
? ScheduleModes.countdown
|
|
: ScheduleModes.inching,
|
|
deviceId: deviceId,
|
|
));
|
|
}
|
|
});
|
|
}
|
|
|
|
void _onDecrementCountdown(
|
|
ScheduleDecrementCountdownEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) {
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
emit(currentState.copyWith(
|
|
countdownRemaining: countdownRemaining,
|
|
));
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> close() {
|
|
_countdownTimer?.cancel();
|
|
return super.close();
|
|
}
|
|
|
|
Future<void> _fetchStatus(
|
|
ScheduleFetchStatusEvent event,
|
|
Emitter<ScheduleState> emit,
|
|
) async {
|
|
emit(ScheduleLoading());
|
|
|
|
try {
|
|
final status =
|
|
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
|
print(status.status);
|
|
final deviceStatus =
|
|
WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
|
|
|
|
final scheduleMode =
|
|
deviceStatus.countdownHours > 0 || deviceStatus.countdownMinutes > 0
|
|
? ScheduleModes.countdown
|
|
: deviceStatus.inchingHours > 0 || deviceStatus.inchingMinutes > 0
|
|
? ScheduleModes.inching
|
|
: ScheduleModes.schedule;
|
|
final isCountdown = scheduleMode == ScheduleModes.countdown;
|
|
final isInching = scheduleMode == ScheduleModes.inching;
|
|
|
|
Duration? countdownRemaining;
|
|
var isCountdownActive = false;
|
|
var isInchingActive = false;
|
|
|
|
if (isCountdown) {
|
|
countdownRemaining = Duration(
|
|
hours: deviceStatus.countdownHours,
|
|
minutes: deviceStatus.countdownMinutes,
|
|
);
|
|
isCountdownActive = countdownRemaining > Duration.zero;
|
|
} else if (isInching) {
|
|
isInchingActive = Duration(
|
|
hours: deviceStatus.inchingHours,
|
|
minutes: deviceStatus.inchingMinutes,
|
|
) >
|
|
Duration.zero;
|
|
}
|
|
if (state is ScheduleLoaded) {
|
|
final currentState = state as ScheduleLoaded;
|
|
emit(currentState.copyWith(
|
|
scheduleMode: scheduleMode,
|
|
countdownHours: deviceStatus.countdownHours,
|
|
countdownMinutes: deviceStatus.countdownMinutes,
|
|
inchingHours: deviceStatus.inchingHours,
|
|
inchingMinutes: deviceStatus.inchingMinutes,
|
|
isCountdownActive: isCountdownActive,
|
|
isInchingActive: isInchingActive,
|
|
countdownRemaining: countdownRemaining ?? Duration.zero,
|
|
));
|
|
} else {
|
|
emit(ScheduleLoaded(
|
|
schedules: const [],
|
|
selectedTime: null,
|
|
selectedDays: List.filled(7, false),
|
|
functionOn: false,
|
|
isEditing: false,
|
|
deviceId: deviceId,
|
|
scheduleMode: scheduleMode,
|
|
countdownHours: deviceStatus.countdownHours,
|
|
countdownMinutes: deviceStatus.countdownMinutes,
|
|
inchingHours: deviceStatus.inchingHours,
|
|
inchingMinutes: deviceStatus.inchingMinutes,
|
|
isCountdownActive: isCountdownActive,
|
|
isInchingActive: isInchingActive,
|
|
countdownRemaining: countdownRemaining ?? Duration.zero,
|
|
));
|
|
}
|
|
|
|
// if (isCountdownActive && countdownRemaining != null) {
|
|
// _startCountdownTimer(emit, countdownRemaining);
|
|
// }
|
|
} catch (e) {
|
|
emit(ScheduleError('Failed to fetch device status: $e'));
|
|
}
|
|
}
|
|
|
|
String extractTime(String isoDateTime) {
|
|
return isoDateTime.split('T')[1].split('.')[0];
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|