Files
syncrow-app/lib/features/devices/bloc/curtain_bloc/curtain_bloc.dart
2025-03-01 15:08:51 +03:00

394 lines
12 KiB
Dart

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/curtain_bloc/curtain_event.dart';
import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_state.dart';
import 'package:syncrow_app/features/devices/model/curtain_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/group_curtain_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 CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
double curtainWidth = 270;
double curtainOpeningSpace = 195;
double blindHeight = 310;
double blindOpeningSpace = 245;
double openPercentage = 0;
bool isMoving = false;
final String curtainId;
CurtainBloc(
this.curtainId,
) : super(CurtainInitial()) {
on<InitCurtain>(_fetchStatus);
on<OpenCurtain>(_onOpenCurtain);
on<CloseCurtain>(_onCloseCurtain);
on<PauseCurtain>(_onPauseCurtain);
on<ChangeFirstWizardSwitchStatusEvent>(_changeFirstWizardSwitch);
on<InitialWizardEvent>(_fetchWizardStatus);
on<GroupAllOffEvent>(_groupAllOff);
on<GroupAllOnEvent>(_groupAllOn);
on<UpdateCurtainEvent>(_updateCurtain);
}
Future<void> _onOpenCurtain(
OpenCurtain event, Emitter<CurtainState> emit) async {
isMoving = true;
while (openPercentage < 100.0) {
if (state is CurtainsClosing) {
_pauseCurtain(emit);
break;
}
emit(CurtainsOpening(
curtainWidth: curtainWidth,
blindHeight: blindHeight,
openPercentage: openPercentage,
));
if (isMoving) {
await Future.delayed(const Duration(milliseconds: 200), () async {
openPercentage += 10.0;
event.deviceType == DeviceType.Curtain
? curtainWidth -= curtainOpeningSpace / 10
: blindHeight -= blindOpeningSpace / 10;
if (openPercentage >= 100.0) {
_pauseCurtain(emit);
}
});
if (openPercentage >= 100.0) {
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'control',
value: 'close',
),
curtainId,
);
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'percent_control',
value: 100,
),
curtainId,
);
}
} else {
_pauseCurtain(emit);
break;
}
}
}
Future<void> _onCloseCurtain(
CloseCurtain event, Emitter<CurtainState> emit) async {
isMoving = true;
while (openPercentage > 0.0) {
if (state is CurtainsOpening) {
_pauseCurtain(emit);
break;
}
emit(CurtainsClosing(
curtainWidth: curtainWidth,
blindHeight: blindHeight,
openPercentage: openPercentage,
));
if (isMoving) {
await Future.delayed(const Duration(milliseconds: 200), () async {
openPercentage -= 10.0;
event.deviceType == DeviceType.Curtain
? curtainWidth += curtainOpeningSpace / 10
: blindHeight += blindOpeningSpace / 10;
if (openPercentage <= 0.0) {
_pauseCurtain(emit);
}
});
if (openPercentage == 0.0) {
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'percent_control',
value: 0,
),
curtainId,
);
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'control',
value: 'open',
),
curtainId,
);
}
} else {
_pauseCurtain(emit);
break;
}
}
}
Future<void> _onPauseCurtain(
PauseCurtain event, Emitter<CurtainState> emit) async {
_pauseCurtain(emit);
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'control',
value: 'stop',
),
curtainId,
);
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: curtainId,
code: 'percent_control',
value: openPercentage.ceil(),
),
curtainId,
);
}
Future<void> _pauseCurtain(Emitter<CurtainState> emit) async {
isMoving = false;
emit(CurtainsPaused(
curtainWidth: curtainWidth,
blindHeight: blindHeight,
openPercentage: openPercentage,
));
}
void _fetchStatus(InitCurtain event, Emitter<CurtainState> emit) async {
try {
emit(CurtainLoadingState());
_listenToChanges(curtainId);
var response = await DevicesAPI.getDeviceStatus(curtainId);
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
}
openPercentage = double.tryParse(statusModelList[1].value.toString())!;
curtainWidth = 270 - (openPercentage / 100) * curtainOpeningSpace;
blindHeight = 310 - (openPercentage / 100) * blindOpeningSpace;
emit(CurtainsOpening(
curtainWidth: curtainWidth,
blindHeight: blindHeight,
openPercentage: openPercentage,
));
} catch (e) {
emit(FailedState());
return;
}
}
StreamSubscription<DatabaseEvent>? _streamSubscription;
void _listenToChanges(curtainId) {
try {
_streamSubscription?.cancel();
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$curtainId');
Stream<DatabaseEvent> stream = ref.onValue;
_streamSubscription = stream.listen((DatabaseEvent event) {
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']));
});
openPercentage = double.tryParse(statusList[1].value.toString())!;
curtainWidth = 270 - (openPercentage / 100) * curtainOpeningSpace;
blindHeight = 310 - (openPercentage / 100) * blindOpeningSpace;
add(UpdateCurtainEvent());
});
} catch (_) {}
}
@override
Future<void> close() async {
_streamSubscription?.cancel();
_streamSubscription = null;
return super.close();
}
// _listenToChanges(curtainId) {
// try {
// print('curtainId=$curtainId');
// DatabaseReference ref =
// FirebaseDatabase.instance.ref('device-status/$curtainId');
// 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> statusModelList = [];
// for (var status in usersMap['status']) {
// statusModelList.add(StatusModel.fromJson(status));
// print('statusModelList==${statusModelList}');
// }
// openPercentage = double.tryParse(statusModelList[1].value.toString())!;
// curtainWidth = 270 - (openPercentage / 100) * curtainOpeningSpace;
// blindHeight = 310 - (openPercentage / 100) * blindOpeningSpace;
// add(UpdateCurtainEvent());
// });
// } catch (_) {}
// }
_updateCurtain(UpdateCurtainEvent event, Emitter<CurtainState> emit) {
curtainWidth = 270 - (openPercentage / 100) * curtainOpeningSpace;
blindHeight = 310 - (openPercentage / 100) * blindOpeningSpace;
emit(CurtainsOpening(
curtainWidth: curtainWidth,
blindHeight: blindHeight,
openPercentage: openPercentage,
));
}
List<GroupCurtainModel> groupList = [];
bool allSwitchesOn = true;
List<DeviceModel> devicesList = [];
CurtainModel deviceStatus = CurtainModel(control: 'stop', percent: 0);
void _fetchWizardStatus(
InitialWizardEvent event, Emitter<CurtainState> emit) async {
emit(LoadingInitialState());
try {
devicesList = [];
groupList = [];
allSwitchesOn = true;
devicesList = await DevicesAPI.getDeviceByGroupName(
HomeCubit.getInstance().selectedSpace?.id ?? '', 'CUR');
for (int i = 0; i < devicesList.length; i++) {
var response =
await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
}
deviceStatus = CurtainModel.fromJson(statusModelList);
groupList.add(GroupCurtainModel(
deviceId: devicesList[i].uuid ?? '',
deviceName: devicesList[i].name ?? '',
firstSwitch: deviceStatus.control,
percentControl: deviceStatus.percent));
}
if (groupList.isNotEmpty) {
groupList.firstWhere((element) {
if (element.percentControl > 1) {
allSwitchesOn = true;
}
return true;
});
}
emit(
UpdateGroupState(curtainList: groupList, allSwitches: allSwitchesOn));
} catch (e) {
emit(FailedState());
return;
}
}
void _changeFirstWizardSwitch(ChangeFirstWizardSwitchStatusEvent event,
Emitter<CurtainState> emit) async {
emit(LoadingNewSate(curtainModel: deviceStatus));
try {
bool allSwitchesValue = true;
// Update the firstSwitch value in the groupList based on the deviceId
groupList.forEach((element) {
if (element.deviceId == event.deviceId) {
element.percentControl =
event.value; // Set the new value from the event
}
if (element.percentControl > 1) {
allSwitchesValue = true; // Check if any switch is not 'open'
}
});
final response = await DevicesAPI.deviceBatchController(
code: 'percent_control',
devicesUuid: [event.deviceId],
value: event.value, // Use the value from the event
);
emit(UpdateGroupState(
curtainList: groupList, allSwitches: allSwitchesValue));
if (response['success']) {
// Optionally add an initial event if needed.
}
} catch (_) {
// Handle the error if needed.
}
}
void _groupAllOn(GroupAllOnEvent event, Emitter<CurtainState> emit) async {
emit(LoadingNewSate(curtainModel: deviceStatus));
try {
for (int i = 0; i < groupList.length; i++) {
groupList[i].percentControl = 100;
}
emit(UpdateGroupState(curtainList: groupList, allSwitches: true));
List<String> allDeviceIds =
groupList.map((device) => device.deviceId).toList();
final response2 = await DevicesAPI.deviceBatchController(
code: 'percent_control',
devicesUuid: allDeviceIds,
value: 100,
);
if (response2['failedResults'].toString() != '[]') {
await Future.delayed(const Duration(milliseconds: 500));
}
} catch (_) {
emit(FailedState());
await Future.delayed(const Duration(milliseconds: 500));
// Optionally add an initial event if needed.
}
}
void _groupAllOff(GroupAllOffEvent event, Emitter<CurtainState> emit) async {
emit(LoadingNewSate(curtainModel: deviceStatus));
try {
for (int i = 0; i < groupList.length; i++) {
groupList[i].percentControl = 0;
}
emit(UpdateGroupState(curtainList: groupList, allSwitches: false));
List<String> allDeviceIds =
groupList.map((device) => device.deviceId).toList();
final response2 = await DevicesAPI.deviceBatchController(
code: 'percent_control',
devicesUuid: allDeviceIds,
value: 0,
);
if (response2['failedResults'].toString() != '[]') {
await Future.delayed(const Duration(milliseconds: 500));
}
} catch (_) {
emit(FailedState());
await Future.delayed(const Duration(milliseconds: 500));
}
}
}