import 'dart:async'; import 'package:firebase_database/firebase_database.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/acs_bloc/acs_event.dart'; import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_state.dart'; import 'package:syncrow_app/features/devices/model/ac_model.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/model/status_model.dart'; import 'package:syncrow_app/services/api/devices_api.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; class ACsBloc extends Bloc { final String acId; AcStatusModel deviceStatus = AcStatusModel( countdown1: 0, uuid: '', acSwitch: true, modeString: 'hot', tempSet: 300, currentTemp: 315, fanSpeedsString: 'low', childLock: false); List deviceStatusList = []; List devicesList = []; bool allAcsPage = false; bool allAcsOn = true; bool allTempSame = true; int globalTemp = 250; Timer? _timer; ACsBloc({required this.acId}) : super(AcsInitialState()) { on(_fetchAcsStatus); on(_changeAcSwitch); on(_increaseCoolTo); on(_decreaseCoolTo); on(_changeLockValue); on(_changeAcMode); on(_changeFanSpeed); on(_changeAllAcSwitch); on(_increaseAllTemp); on(_decreaseAllTemp); on(_onAcUpdated); on(_onClose); on(_getCounterValue); on(_setCounterValue); on(_onTickTimer); // on(_setTimeOutAlarm); } void _fetchAcsStatus(AcsInitial event, Emitter emit) async { emit(AcsLoadingState()); try { allAcsPage = event.allAcs; if (event.allAcs) { await _getAllAcs(); emit(GetAllAcsStatusState( allAcsStatues: deviceStatusList, allAcs: devicesList, allOn: allAcsOn, allTempSame: allTempSame, temp: globalTemp)); } else { var response = await DevicesAPI.getDeviceStatus(acId); List statusModelList = []; for (var status in response['status']) { statusModelList.add(StatusModel.fromJson(status)); } deviceStatus = AcStatusModel.fromJson(response['productUuid'], statusModelList); emit(GetAcStatusState(acStatusModel: deviceStatus)); Future.delayed(const Duration(milliseconds: 500)); _listenToChanges(acId); } } catch (e) { emit(AcsFailedState(errorMessage: e.toString())); return; } } StreamSubscription? _streamSubscription; void _listenToChanges(acId) { try { _streamSubscription?.cancel(); DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$acId'); Stream stream = ref.onValue; _streamSubscription = stream.listen((DatabaseEvent event) { Map usersMap = event.snapshot.value as Map; List statusList = []; usersMap['status'].forEach((element) { statusList.add(StatusModel(code: element['code'], value: element['value'])); }); deviceStatus = AcStatusModel.fromJson(usersMap['productUuid'], statusList); add(AcUpdated()); }); } catch (_) {} } @override Future close() async { _streamSubscription?.cancel(); _streamSubscription = null; return super.close(); } _onAcUpdated(AcUpdated event, Emitter emit) { emit(GetAcStatusState(acStatusModel: deviceStatus)); } _getAllAcs() async { deviceStatusList = []; devicesList = []; devicesList = await DevicesAPI.getDeviceByGroupName( HomeCubit.getInstance().selectedSpace?.id ?? '', 'AC'); for (int i = 0; i < devicesList.length; i++) { _listenToChanges(devicesList[i].uuid); var response = await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? ''); List statusModelList = []; for (var status in response['status']) { statusModelList.add(StatusModel.fromJson(status)); } deviceStatusList.add(AcStatusModel.fromJson(devicesList[i].uuid ?? '', statusModelList)); } _setAllAcsTempsAndSwitches(); } void _changeAcSwitch(AcSwitch event, Emitter emit) async { final acSwitchValue = !event.acSwitch; if (allAcsPage) { emit(AcsLoadingState()); for (AcStatusModel ac in deviceStatusList) { if (ac.uuid == event.deviceId) { ac.acSwitch = acSwitchValue; } } _setAllAcsTempsAndSwitches(); _emitAcsStatus(emit); } else { emit(AcChangeLoading(acStatusModel: deviceStatus)); deviceStatus.acSwitch = acSwitchValue; emit(AcModifyingState(acStatusModel: deviceStatus)); } await _runDeBouncerForOneDevice(deviceId: event.deviceId, code: 'switch', value: acSwitchValue); } void _changeAllAcSwitch(ChangeAllSwitch event, Emitter emit) async { emit(AcsLoadingState()); if (deviceStatusList.length == devicesList.length) { for (int i = 0; i < deviceStatusList.length; i++) { deviceStatusList[i].acSwitch = event.value; } } _setAllAcsTempsAndSwitches(); _emitAcsStatus(emit); _runDeBouncerForAllAcs(code: 'switch', value: event.value); } void _increaseAllTemp(IncreaseAllTemp event, Emitter emit) async { emit(AcsLoadingState()); double tempValue = event.value + 0.5; int value = (tempValue * 10).toInt(); if (!_checkTemperatureValue(tempValue, emit)) { return; } if (deviceStatusList.length == devicesList.length) { for (int i = 0; i < deviceStatusList.length; i++) { deviceStatusList[i].tempSet = value; } } _setAllAcsTempsAndSwitches(); _emitAcsStatus(emit); _runDeBouncerForAllAcs(code: 'temp_set', value: value); } void _decreaseAllTemp(DecreaseAllTemp event, Emitter emit) async { emit(AcsLoadingState()); double tempValue = event.value - 0.5; int value = (tempValue * 10).toInt(); if (!_checkTemperatureValue(tempValue, emit)) { return; } if (deviceStatusList.length == devicesList.length) { for (int i = 0; i < deviceStatusList.length; i++) { deviceStatusList[i].tempSet = value; } } _setAllAcsTempsAndSwitches(); _emitAcsStatus(emit); _runDeBouncerForAllAcs(code: 'temp_set', value: value); } void _changeLockValue(ChangeLock event, Emitter emit) async { emit(AcChangeLoading(acStatusModel: deviceStatus)); final lockValue = !event.lockBool; deviceStatus.childLock = lockValue; emit(AcModifyingState(acStatusModel: deviceStatus)); await _runDeBouncerForOneDevice(deviceId: acId, code: 'child_lock', value: lockValue); } void _increaseCoolTo(IncreaseCoolToTemp event, Emitter emit) async { emit(AcChangeLoading(acStatusModel: deviceStatus)); double tempValue = event.value + 0.5; int value = (tempValue * 10).toInt(); if (!_checkTemperatureValue(tempValue, emit)) { return; } if (allAcsPage) { emit(AcsLoadingState()); for (AcStatusModel ac in deviceStatusList) { if (ac.uuid == event.deviceId) { ac.tempSet = value; } } _setAllAcsTempsAndSwitches(); _emitAcsStatus(emit); } else { emit(AcChangeLoading(acStatusModel: deviceStatus)); deviceStatus.tempSet = value; emit(AcModifyingState(acStatusModel: deviceStatus)); } await _runDeBouncerForOneDevice(deviceId: event.deviceId, code: 'temp_set', value: value); } void _decreaseCoolTo(DecreaseCoolToTemp event, Emitter emit) async { emit(AcChangeLoading(acStatusModel: deviceStatus)); double tempValue = event.value - 0.5; int value = (tempValue * 10).toInt(); if (!_checkTemperatureValue(tempValue, emit)) { return; } if (allAcsPage) { emit(AcsLoadingState()); for (AcStatusModel ac in deviceStatusList) { if (ac.uuid == event.deviceId) { ac.tempSet = value; } } _setAllAcsTempsAndSwitches(); _emitAcsStatus(emit); } else { emit(AcChangeLoading(acStatusModel: deviceStatus)); deviceStatus.tempSet = value; emit(AcModifyingState(acStatusModel: deviceStatus)); } await _runDeBouncerForOneDevice(deviceId: event.deviceId, code: 'temp_set', value: value); } void _changeAcMode(ChangeAcMode event, Emitter emit) async { final tempMode = tempModesMap[getNextItem(tempModesMap, event.tempModes)]!; if (allAcsPage) { emit(AcsLoadingState()); for (AcStatusModel ac in deviceStatusList) { if (ac.uuid == event.deviceId) { ac.modeString = getACModeString(tempMode); ac.acMode = AcStatusModel.getACMode(getACModeString(tempMode)); } } _emitAcsStatus(emit); } else { emit(AcChangeLoading(acStatusModel: deviceStatus)); deviceStatus.modeString = getACModeString(tempMode); deviceStatus.acMode = AcStatusModel.getACMode(getACModeString(tempMode)); emit(AcModifyingState(acStatusModel: deviceStatus)); } await _runDeBouncerForOneDevice( deviceId: event.deviceId, code: 'mode', value: getACModeString(tempMode)); } void _changeFanSpeed(ChangeFanSpeed event, Emitter emit) async { emit(AcChangeLoading(acStatusModel: deviceStatus)); final fanSpeed = event.fanSpeeds; if (allAcsPage) { emit(AcsLoadingState()); for (AcStatusModel ac in deviceStatusList) { if (ac.uuid == event.deviceId) { ac.fanSpeedsString = getNextFanSpeedKey(fanSpeed); ac.acFanSpeed = AcStatusModel.getFanSpeed(getNextFanSpeedKey(fanSpeed)); } } _emitAcsStatus(emit); } else { emit(AcChangeLoading(acStatusModel: deviceStatus)); deviceStatus.fanSpeedsString = getNextFanSpeedKey(fanSpeed); deviceStatus.acFanSpeed = AcStatusModel.getFanSpeed(getNextFanSpeedKey(fanSpeed)); emit(AcModifyingState(acStatusModel: deviceStatus)); } await _runDeBouncerForOneDevice( deviceId: event.deviceId, code: 'level', value: getNextFanSpeedKey(fanSpeed)); } String getACModeString(TempModes value) { if (value == TempModes.cold) { return 'cold'; } else if (value == TempModes.hot) { return 'hot'; } else if (value == TempModes.wind) { return 'wind'; } else { return 'cold'; } } void _setAllAcsTempsAndSwitches() { allAcsOn = true; allTempSame = true; if (deviceStatusList.isNotEmpty) { int temp = deviceStatusList.first.tempSet; for (var element in deviceStatusList) { if (!element.acSwitch) { allAcsOn = false; } if (element.tempSet != temp) { allTempSame = false; } } if (allTempSame) { globalTemp = temp; } } } _runDeBouncerForAllAcs({required String code, required dynamic value}) { if (_timer != null) { _timer!.cancel(); } _timer = Timer(const Duration(seconds: 1), () async { if (deviceStatusList.length == devicesList.length) { for (int i = 0; i < deviceStatusList.length; i++) { try { await DevicesAPI.controlDevice( DeviceControlModel(deviceId: devicesList[i].uuid, code: code, value: value), devicesList[i].uuid ?? ''); } catch (_) { await Future.delayed(const Duration(milliseconds: 500)); add(const AcsInitial(allAcs: true)); } } } }); } _runDeBouncerForOneDevice({ required String deviceId, required String code, required dynamic value, }) { if (_timer != null) { _timer!.cancel(); } _timer = Timer(const Duration(seconds: 1), () async { try { final response = await DevicesAPI.controlDevice( DeviceControlModel(deviceId: allAcsPage ? deviceId : acId, code: code, value: value), allAcsPage ? deviceId : acId); if (!response['success']) { add(AcsInitial(allAcs: allAcsPage)); } } catch (_) { await Future.delayed(const Duration(milliseconds: 500)); add(AcsInitial(allAcs: allAcsPage)); } }); } bool _checkTemperatureValue(double value, Emitter emit) { if (value >= 20 && value <= 30) { return true; } else { emit(const AcsFailedState(errorMessage: 'The temperature must be between 20 and 30')); emit(GetAllAcsStatusState( allAcsStatues: deviceStatusList, allAcs: devicesList, allOn: allAcsOn, allTempSame: allTempSame, temp: globalTemp)); return false; } } _emitAcsStatus(Emitter emit) { emit(GetAllAcsStatusState( allAcsStatues: deviceStatusList, allAcs: devicesList, allOn: allAcsOn, allTempSame: allTempSame, temp: globalTemp)); } void _setCounterValue(SetCounterValue event, Emitter emit) async { emit(AcsLoadingState()); int seconds = 0; try { seconds = event.seconds; final response = await DevicesAPI.controlDevice( DeviceControlModel(deviceId: acId, code: 'countdown_time', value: event.duration), acId); if (response['success'] ?? false) { deviceStatus.countdown1 = seconds; } else { emit(const AcsFailedState(errorMessage: 'Something went wrong')); return; } } catch (e) { emit(AcsFailedState(errorMessage: e.toString())); return; } if (event.duration > 0) { _onStartTimer(seconds); } else { _timer?.cancel(); emit(TimerRunComplete()); } } void _getCounterValue(GetCounterEvent event, Emitter emit) async { emit(AcsLoadingState()); var response = await DevicesAPI.getDeviceStatus(acId); List statusModelList = []; for (var status in response['status']) { statusModelList.add(StatusModel.fromJson(status)); } deviceStatus = AcStatusModel.fromJson(response['productUuid'], statusModelList); deviceStatus.countdown1; var duration; if (deviceStatus.countdown1 == 5) { duration = const Duration(minutes: 30); var countNum = duration.inSeconds; _onStartTimer(countNum); } else if (deviceStatus.countdown1 > 5) { duration = Duration(minutes: deviceStatus.countdown1 * 6); var countNum = duration.inSeconds; _onStartTimer(countNum); } else { _timer?.cancel(); emit(TimerRunComplete()); } } 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 emit) { if (event.remainingTime > 0) { emit(TimerRunInProgress(event.remainingTime)); } else { _timer?.cancel(); emit(TimerRunComplete()); } } void _onClose(OnClose event, Emitter emit) { _timer?.cancel(); // Cancel the timer } }