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_web/pages/device_managment/all_devices/models/device_status.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/services/devices_mang_api.dart'; class AcBloc extends Bloc { late AcStatusModel deviceStatus; final String deviceId; Timer? _timer; AcBloc({required this.deviceId}) : super(AcsInitialState()) { on(_onFetchAcStatus); on(_onFetchAcBatchStatus); on(_onAcControl); on(_onAcBatchControl); on(_onFactoryReset); on(_onAcStatusUpdated); } FutureOr _onFetchAcStatus( AcFetchDeviceStatusEvent event, Emitter emit) async { emit(AcsLoadingState()); try { final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status); _listenToChanges(event.deviceId); emit(ACStatusLoaded(deviceStatus)); } catch (e) { emit(AcsFailedState(error: e.toString())); } } _listenToChanges(deviceId) { try { DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); Stream stream = ref.onValue; stream.listen((DatabaseEvent event) async { if (event.snapshot.value == null) return; if (_timer != null) { await Future.delayed(const Duration(seconds: 1)); } Map usersMap = event.snapshot.value as Map; List 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(deviceStatus)); } // Future testFirebaseConnection() async { // // Reference to a test node in your database // final testRef = FirebaseDatabase.instance.ref("test"); // // Write a test value // await testRef.set("Hello, Firebase!"); // // Listen for changes on the test node // testRef.onValue.listen((DatabaseEvent event) { // final data = event.snapshot.value; // print("Data from Firebase: $data"); // // If you see "Hello, Firebase!" printed in your console, it means the connection works. // }); // } FutureOr _onAcControl( AcControlEvent event, Emitter emit) async { final oldValue = _getValueByCode(event.code); _updateLocalValue(event.code, event.value, emit); emit(ACStatusLoaded(deviceStatus)); await _runDebounce( isBatch: false, deviceId: event.deviceId, code: event.code, value: event.value, oldValue: oldValue, emit: emit, ); } Future _runDebounce({ required dynamic deviceId, required String code, required dynamic value, required dynamic oldValue, required Emitter emit, required bool isBatch, }) async { late String id; if (deviceId is List) { id = deviceId.first; } else { id = deviceId; } if (_timer != null) { _timer!.cancel(); } _timer = Timer(const Duration(seconds: 1), () async { try { late bool response; if (isBatch) { response = await DevicesManagementApi() .deviceBatchControl(deviceId, code, value); } else { response = await DevicesManagementApi() .deviceControl(deviceId, Status(code: code, value: value)); } if (!response) { _revertValueAndEmit(id, code, oldValue, emit); } } catch (e) { if (e is DioException && e.response != null) { debugPrint('Error response: ${e.response?.data}'); } _revertValueAndEmit(id, code, oldValue, emit); } }); } void _revertValueAndEmit( String deviceId, String code, dynamic oldValue, Emitter emit) { _updateLocalValue(code, oldValue, emit); emit(ACStatusLoaded(deviceStatus)); } void _updateLocalValue(String code, dynamic value, Emitter emit) { switch (code) { case 'switch': if (value is bool) { deviceStatus = deviceStatus.copyWith(acSwitch: value); } break; case 'temp_set': if (value is int) { deviceStatus = deviceStatus.copyWith(tempSet: value); } break; case 'mode': if (value is String) { deviceStatus = deviceStatus.copyWith( modeString: value, ); } break; case 'level': if (value is String) { deviceStatus = deviceStatus.copyWith( fanSpeedsString: value, ); } break; case 'child_lock': if (value is bool) { deviceStatus = deviceStatus.copyWith(childLock: value); } break; default: break; } emit(ACStatusLoaded(deviceStatus)); } dynamic _getValueByCode(String code) { switch (code) { case 'switch': return deviceStatus.acSwitch; case 'temp_set': return deviceStatus.tempSet; case 'mode': return deviceStatus.modeString; case 'level': return deviceStatus.fanSpeedsString; case 'child_lock': return deviceStatus.childLock; default: return null; } } 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(deviceStatus)); } catch (e) { emit(AcsFailedState(error: e.toString())); } } FutureOr _onAcBatchControl( AcBatchControlEvent event, Emitter emit) async { final oldValue = _getValueByCode(event.code); _updateLocalValue(event.code, event.value, emit); emit(ACStatusLoaded(deviceStatus)); await _runDebounce( isBatch: true, deviceId: event.devicesIds, code: event.code, value: event.value, oldValue: oldValue, emit: emit, ); } FutureOr _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())); } } }