import 'dart:async'; import 'package:firebase_database/firebase_database.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart'; import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_state.dart'; import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/services/batch_control_devices_service.dart'; import 'package:syncrow_web/services/control_device_service.dart'; import 'package:syncrow_web/services/devices_mang_api.dart'; class AcBloc extends Bloc { late AcStatusModel deviceStatus; final String deviceId; final ControlDeviceService controlDeviceService; final BatchControlDevicesService batchControlDevicesService; Timer? _countdownTimer; AcBloc({ required this.deviceId, required this.controlDeviceService, required this.batchControlDevicesService, }) : super(AcsInitialState()) { on(_onFetchAcStatus); on(_onFetchAcBatchStatus); on(_onAcControl); on(_onAcBatchControl); on(_onFactoryReset); on(_onAcStatusUpdated); on(_onClose); on(_handleIncreaseTime); on(_handleDecreaseTime); on(_handleUpdateTimer); on(_handleToggleTimer); on(_handleApiCountdownValue); } bool timerActive = false; int scheduledHours = 0; int scheduledMinutes = 0; FutureOr _onFetchAcStatus( AcFetchDeviceStatusEvent event, Emitter emit, ) async { emit(AcsLoadingState()); try { final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status); if (deviceStatus.countdown1 != 0) { final totalMinutes = deviceStatus.countdown1 * 6; scheduledHours = totalMinutes ~/ 60; scheduledMinutes = totalMinutes % 60; timerActive = true; _startCountdownTimer(emit); } emit(ACStatusLoaded( status: deviceStatus, scheduledHours: scheduledHours, scheduledMinutes: scheduledMinutes, isTimerActive: timerActive, )); _listenToChanges(event.deviceId); } catch (e) { emit(AcsFailedState(error: e.toString())); } } void _listenToChanges(deviceId) { try { final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); ref.onValue.listen((DatabaseEvent event) async { if (event.snapshot.value == null) return; final usersMap = event.snapshot.value! as Map; final statusList = []; usersMap['status'].forEach((element) { statusList .add(Status(code: element['code'], value: element['value'])); }); deviceStatus = AcStatusModel.fromJson(usersMap['productUuid'], statusList); if (!isClosed) { add(AcStatusUpdated(deviceStatus)); } }); } catch (_) {} } void _onAcStatusUpdated( AcStatusUpdated event, Emitter emit, ) { deviceStatus = event.deviceStatus; emit(ACStatusLoaded(status: deviceStatus)); } FutureOr _onAcControl( AcControlEvent event, Emitter emit, ) async { emit(AcsLoadingState()); _updateDeviceFunctionFromCode(event.code, event.value); emit(ACStatusLoaded(status: deviceStatus)); try { final success = await controlDeviceService.controlDevice( deviceUuid: event.deviceId, status: Status(code: event.code, value: event.value), ); if (!success) { emit(const AcsFailedState(error: 'Failed to control device')); } } catch (e) { emit(AcsFailedState(error: e.toString())); } } FutureOr _onFetchAcBatchStatus( AcFetchBatchStatusEvent event, Emitter emit, ) async { emit(AcsLoadingState()); try { final status = await DevicesManagementApi().getBatchStatus(event.devicesIds); deviceStatus = AcStatusModel.fromJson(event.devicesIds.first, status.status); emit(ACStatusLoaded(status: deviceStatus)); } catch (e) { emit(AcsFailedState(error: e.toString())); } } FutureOr _onAcBatchControl( AcBatchControlEvent event, Emitter emit, ) async { emit(AcsLoadingState()); _updateDeviceFunctionFromCode(event.code, event.value); emit(ACStatusLoaded(status: deviceStatus)); try { final success = await batchControlDevicesService.batchControlDevices( uuids: event.devicesIds, code: event.code, value: event.value, ); if (!success) { emit(const AcsFailedState(error: 'Failed to control devices')); } } catch (e) { emit(AcsFailedState(error: e.toString())); } } Future _onFactoryReset( AcFactoryResetEvent event, Emitter emit, ) async { emit(AcsLoadingState()); try { final response = await DevicesManagementApi().factoryReset( event.factoryResetModel, event.deviceId, ); if (!response) { emit(const AcsFailedState(error: 'Failed')); } else { add(AcFetchDeviceStatusEvent(event.deviceId)); } } catch (e) { emit(AcsFailedState(error: e.toString())); } } void _onClose( OnClose event, Emitter emit, ) { _countdownTimer?.cancel(); } void _handleIncreaseTime(IncreaseTimeEvent event, Emitter emit) { if (state is! ACStatusLoaded) return; final currentState = state as ACStatusLoaded; var newHours = scheduledHours; var newMinutes = scheduledMinutes + 30; newHours += newMinutes ~/ 60; newMinutes = newMinutes % 60; if (newHours > 23) { newHours = 23; newMinutes = 59; } scheduledHours = newHours; scheduledMinutes = newMinutes; emit(currentState.copyWith( scheduledHours: scheduledHours, scheduledMinutes: scheduledMinutes, )); } void _handleDecreaseTime( DecreaseTimeEvent event, Emitter emit, ) { if (state is! ACStatusLoaded) return; final currentState = state as ACStatusLoaded; var totalMinutes = (scheduledHours * 60) + scheduledMinutes; totalMinutes = (totalMinutes - 30).clamp(0, 1440); scheduledHours = totalMinutes ~/ 60; scheduledMinutes = totalMinutes % 60; emit(currentState.copyWith( scheduledHours: scheduledHours, scheduledMinutes: scheduledMinutes, )); } Future _handleToggleTimer( ToggleScheduleEvent event, Emitter emit, ) async { if (state is! ACStatusLoaded) return; final currentState = state as ACStatusLoaded; timerActive = !timerActive; if (timerActive) { final totalMinutes = scheduledHours * 60 + scheduledMinutes; if (totalMinutes <= 0) { timerActive = false; emit(currentState.copyWith(isTimerActive: timerActive)); return; } try { final scaledValue = totalMinutes ~/ 6; final success = await controlDeviceService.controlDevice( deviceUuid: deviceId, status: Status(code: 'countdown_time', value: scaledValue), ); if (success) { _startCountdownTimer(emit); emit(currentState.copyWith(isTimerActive: timerActive)); } else { timerActive = false; emit(const AcsFailedState(error: 'Failed to set timer')); } } catch (e) { timerActive = false; emit(AcsFailedState(error: e.toString())); } } else { try { final success = await controlDeviceService.controlDevice( deviceUuid: deviceId, status: Status(code: 'countdown_time', value: 0), ); if (success) { _countdownTimer?.cancel(); scheduledHours = 0; scheduledMinutes = 0; emit(currentState.copyWith( isTimerActive: timerActive, scheduledHours: 0, scheduledMinutes: 0, )); } else { emit(const AcsFailedState(error: 'Failed to stop timer')); } } catch (e) { emit(AcsFailedState(error: e.toString())); } } } void _startCountdownTimer(Emitter emit) { _countdownTimer?.cancel(); var totalSeconds = (scheduledHours * 3600) + (scheduledMinutes * 60); _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { if (totalSeconds > 0) { totalSeconds--; scheduledHours = totalSeconds ~/ 3600; scheduledMinutes = (totalSeconds % 3600) ~/ 60; add(UpdateTimerEvent()); } else { _countdownTimer?.cancel(); timerActive = false; scheduledHours = 0; scheduledMinutes = 0; add(TimerCompletedEvent()); } }); } void _handleUpdateTimer( UpdateTimerEvent event, Emitter emit, ) { if (state is ACStatusLoaded) { final currentState = state as ACStatusLoaded; emit(currentState.copyWith( scheduledHours: scheduledHours, scheduledMinutes: scheduledMinutes, isTimerActive: timerActive, )); } } void _handleApiCountdownValue( ApiCountdownValueEvent event, Emitter emit) { if (state is ACStatusLoaded) { final totalMinutes = event.apiValue * 6; scheduledMinutes = totalMinutes % 60; _startCountdownTimer( emit, ); add(UpdateTimerEvent()); } } void _updateDeviceFunctionFromCode(String code, dynamic value) { switch (code) { case 'switch': if (value is bool) { deviceStatus = deviceStatus.copyWith(acSwitch: value); } case 'temp_set': if (value is int) { deviceStatus = deviceStatus.copyWith(tempSet: value); } case 'mode': if (value is String) { deviceStatus = deviceStatus.copyWith(modeString: value); } case 'level': if (value is String) { deviceStatus = deviceStatus.copyWith(fanSpeedsString: value); } case 'child_lock': if (value is bool) { deviceStatus = deviceStatus.copyWith(childLock: value); } case 'countdown_time': if (value is int) { deviceStatus = deviceStatus.copyWith(countdown1: value); } default: break; } } @override Future close() { add(OnClose()); return super.close(); } }