Created a factory for the WaterHeaterBloc, and injected the necessary dependenices.

This commit is contained in:
Faris Armoush
2025-06-02 10:12:46 +03:00
parent 6f3dfb607e
commit b60c674496
4 changed files with 76 additions and 163 deletions

View File

@ -10,6 +10,8 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_sta
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.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/schedule_model.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.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'; import 'package:syncrow_web/services/devices_mang_api.dart';
import 'package:syncrow_web/utils/format_date_time.dart'; import 'package:syncrow_web/utils/format_date_time.dart';
@ -17,7 +19,17 @@ part 'water_heater_event.dart';
part 'water_heater_state.dart'; part 'water_heater_state.dart';
class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> { class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
WaterHeaterBloc() : super(WaterHeaterInitial()) { late WaterHeaterStatusModel deviceStatus;
final String deviceId;
final ControlDeviceService controlDeviceService;
final BatchControlDevicesService batchControlDevicesService;
Timer? _countdownTimer;
WaterHeaterBloc({
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(WaterHeaterInitial()) {
on<WaterHeaterFetchStatusEvent>(_fetchWaterHeaterStatus); on<WaterHeaterFetchStatusEvent>(_fetchWaterHeaterStatus);
on<ToggleWaterHeaterEvent>(_controlWaterHeater); on<ToggleWaterHeaterEvent>(_controlWaterHeater);
on<FetchWaterHeaterBatchStatusEvent>(_batchFetchWaterHeater); on<FetchWaterHeaterBatchStatusEvent>(_batchFetchWaterHeater);
@ -29,7 +41,6 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
on<UpdateSelectedTimeEvent>(_updateSelectedTime); on<UpdateSelectedTimeEvent>(_updateSelectedTime);
on<UpdateSelectedDayEvent>(_updateSelectedDay); on<UpdateSelectedDayEvent>(_updateSelectedDay);
on<UpdateFunctionOnEvent>(_updateFunctionOn); on<UpdateFunctionOnEvent>(_updateFunctionOn);
on<GetSchedulesEvent>(_getSchedule); on<GetSchedulesEvent>(_getSchedule);
on<AddScheduleEvent>(_onAddSchedule); on<AddScheduleEvent>(_onAddSchedule);
on<EditWaterHeaterScheduleEvent>(_onEditSchedule); on<EditWaterHeaterScheduleEvent>(_onEditSchedule);
@ -38,10 +49,6 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
late WaterHeaterStatusModel deviceStatus;
Timer? _countdownTimer;
// Timer? _inchingTimer;
FutureOr<void> _initializeAddSchedule( FutureOr<void> _initializeAddSchedule(
InitializeAddScheduleEvent event, InitializeAddScheduleEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
@ -116,13 +123,11 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
countdownRemaining: countdownRemaining, countdownRemaining: countdownRemaining,
)); ));
if (!currentState.isCountdownActive! && if (!currentState.isCountdownActive! && countdownRemaining > Duration.zero) {
countdownRemaining > Duration.zero) {
_startCountdownTimer(emit, countdownRemaining); _startCountdownTimer(emit, countdownRemaining);
} }
} else if (event.scheduleMode == ScheduleModes.inching) { } else if (event.scheduleMode == ScheduleModes.inching) {
final inchingDuration = final inchingDuration = Duration(hours: event.hours, minutes: event.minutes);
Duration(hours: event.hours, minutes: event.minutes);
emit(currentState.copyWith( emit(currentState.copyWith(
scheduleMode: ScheduleModes.inching, scheduleMode: ScheduleModes.inching,
@ -141,21 +146,18 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(currentState.copyWith( emit(currentState.copyWith(
status: deviceStatus, status: deviceStatus,
)); ));
final success = await _runDebounce( final success = await controlDeviceService.controlDevice(
deviceId: event.deviceId, deviceUuid: event.deviceId,
code: event.code, status: Status(
value: event.value, code: event.code,
oldValue: oldValue, value: event.value,
emit: emit, ),
isBatch: false,
); );
if (success) { if (success) {
@ -182,15 +184,11 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} else if (event.code == "switch_inching") { } else if (event.code == "switch_inching") {
final inchingDuration = Duration(seconds: event.value); final inchingDuration = Duration(seconds: event.value);
//if (inchingDuration.inSeconds > 0) {
// _startInchingTimer(emit, inchingDuration);
// } else {
emit(currentState.copyWith( emit(currentState.copyWith(
inchingHours: inchingDuration.inHours, inchingHours: inchingDuration.inHours,
inchingMinutes: inchingDuration.inMinutes % 60, inchingMinutes: inchingDuration.inMinutes % 60,
isInchingActive: true, isInchingActive: true,
)); ));
// }
} }
} }
} }
@ -224,8 +222,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
try { try {
final status = await DevicesManagementApi().deviceControl( final status = await DevicesManagementApi().deviceControl(
event.deviceId, event.deviceId,
Status( Status(code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0),
code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0),
); );
if (!status) { if (!status) {
emit(const WaterHeaterFailedState(error: 'Failed to stop schedule.')); emit(const WaterHeaterFailedState(error: 'Failed to stop schedule.'));
@ -243,10 +240,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
emit(WaterHeaterLoadingState()); emit(WaterHeaterLoadingState());
try { try {
final status = final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
await DevicesManagementApi().getDeviceStatus(event.deviceId); deviceStatus = WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
deviceStatus =
WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
if (deviceStatus.scheduleMode == ScheduleModes.countdown) { if (deviceStatus.scheduleMode == ScheduleModes.countdown) {
final countdownRemaining = Duration( final countdownRemaining = Duration(
@ -288,7 +283,6 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
inchingMinutes: deviceStatus.inchingMinutes, inchingMinutes: deviceStatus.inchingMinutes,
isInchingActive: true, isInchingActive: true,
)); ));
//_startInchingTimer(emit, inchingDuration);
} else { } else {
emit(WaterHeaterDeviceStatusLoaded( emit(WaterHeaterDeviceStatusLoaded(
deviceStatus, deviceStatus,
@ -316,7 +310,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
_listenToChanges(deviceId) { void _listenToChanges(deviceId) {
try { try {
DatabaseReference ref = DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId'); FirebaseDatabase.instance.ref('device-status/$deviceId');
@ -328,12 +322,11 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
List<Status> statusList = []; List<Status> statusList = [];
usersMap['status'].forEach((element) { usersMap['status'].forEach((element) {
statusList statusList.add(Status(code: element['code'], value: element['value']));
.add(Status(code: element['code'], value: element['value']));
}); });
deviceStatus = WaterHeaterStatusModel.fromJson( deviceStatus =
usersMap['productUuid'], statusList); WaterHeaterStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) { if (!isClosed) {
add(StatusUpdated(deviceStatus)); add(StatusUpdated(deviceStatus));
} }
@ -357,17 +350,6 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
}); });
} }
// void _startInchingTimer(
// Emitter<WaterHeaterState> emit,
// Duration inchingDuration,
// ) {
// _inchingTimer?.cancel();
// _inchingTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
// add(DecrementInchingEvent());
// });
// }
_onDecrementCountdown( _onDecrementCountdown(
DecrementCountdownEvent event, DecrementCountdownEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
@ -405,85 +387,6 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
// FutureOr<void> _onDecrementInching(
// DecrementInchingEvent event,
// Emitter<WaterHeaterState> emit,
// ) {
// if (state is WaterHeaterDeviceStatusLoaded) {
// final currentState = state as WaterHeaterDeviceStatusLoaded;
// if (currentState.inchingHours > 0 || currentState.inchingMinutes > 0) {
// final newRemaining = Duration(
// hours: currentState.inchingHours,
// minutes: currentState.inchingMinutes,
// ) -
// const Duration(minutes: 1);
// if (newRemaining <= Duration.zero) {
// _inchingTimer?.cancel();
// emit(currentState.copyWith(
// inchingHours: 0,
// inchingMinutes: 0,
// isInchingActive: false,
// ));
// } else {
// emit(currentState.copyWith(
// inchingHours: newRemaining.inHours,
// inchingMinutes: newRemaining.inMinutes % 60,
// ));
// }
// }
// }
// }
Future<bool> _runDebounce({
required dynamic deviceId,
required String code,
required dynamic value,
required dynamic oldValue,
required Emitter<WaterHeaterState> emit,
required bool isBatch,
}) async {
try {
late bool status;
await Future.delayed(const Duration(milliseconds: 500));
if (isBatch) {
status = await DevicesManagementApi().deviceBatchControl(
deviceId,
code,
value,
);
} else {
status = await DevicesManagementApi().deviceControl(
deviceId,
Status(code: code, value: value),
);
}
if (!status) {
_revertValue(code, oldValue, emit.call);
return false;
} else {
return true;
}
} catch (e) {
_revertValue(code, oldValue, emit.call);
return false;
}
}
void _revertValue(String code, dynamic oldValue,
void Function(WaterHeaterState state) emit) {
_updateLocalValue(code, oldValue);
if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded;
emit(currentState.copyWith(
status: deviceStatus,
));
}
}
void _updateLocalValue(String code, dynamic value) { void _updateLocalValue(String code, dynamic value) {
switch (code) { switch (code) {
case 'switch_1': case 'switch_1':
@ -505,14 +408,12 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
dynamic _getValueByCode(String code) { dynamic _getValueByCode(String code) {
switch (code) { return switch (code) {
case 'switch_1': 'switch_1' => deviceStatus.heaterSwitch,
return deviceStatus.heaterSwitch; 'countdown_1' =>
case 'countdown_1': (deviceStatus.countdownHours * 60) + deviceStatus.countdownMinutes,
return deviceStatus.countdownHours * 60 + deviceStatus.countdownMinutes; _ => null,
default: };
return null;
}
} }
@override @override
@ -571,8 +472,10 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
FutureOr<void> _onEditSchedule(EditWaterHeaterScheduleEvent event, FutureOr<void> _onEditSchedule(
Emitter<WaterHeaterState> emit) async { EditWaterHeaterScheduleEvent event,
Emitter<WaterHeaterState> emit,
) async {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
@ -584,8 +487,6 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays), days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
); );
// emit(ScheduleLoadingState());
bool success = await DevicesManagementApi().editScheduleRecord( bool success = await DevicesManagementApi().editScheduleRecord(
currentState.status.uuid, currentState.status.uuid,
newSchedule, newSchedule,
@ -595,7 +496,6 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
add(GetSchedulesEvent(category: 'switch_1', uuid: deviceStatus.uuid)); add(GetSchedulesEvent(category: 'switch_1', uuid: deviceStatus.uuid));
} else { } else {
emit(currentState); emit(currentState);
//emit(const WaterHeaterFailedState(error: 'Failed to add schedule.'));
} }
} }
} }
@ -627,7 +527,6 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
emit(currentState.copyWith(schedules: updatedSchedules)); emit(currentState.copyWith(schedules: updatedSchedules));
} else { } else {
emit(currentState); emit(currentState);
// emit(const WaterHeaterFailedState(error: 'Failed to update schedule.'));
} }
} }
} }
@ -639,8 +538,6 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
// emit(ScheduleLoadingState());
bool success = await DevicesManagementApi() bool success = await DevicesManagementApi()
.deleteScheduleRecord(currentState.status.uuid, event.scheduleId); .deleteScheduleRecord(currentState.status.uuid, event.scheduleId);
@ -652,20 +549,18 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
emit(currentState.copyWith(schedules: updatedSchedules)); emit(currentState.copyWith(schedules: updatedSchedules));
} else { } else {
emit(currentState); emit(currentState);
// emit(const WaterHeaterFailedState(error: 'Failed to delete schedule.'));
} }
} }
} }
FutureOr<void> _batchFetchWaterHeater(FetchWaterHeaterBatchStatusEvent event, FutureOr<void> _batchFetchWaterHeater(
Emitter<WaterHeaterState> emit) async { FetchWaterHeaterBatchStatusEvent event, Emitter<WaterHeaterState> emit) async {
emit(WaterHeaterLoadingState()); emit(WaterHeaterLoadingState());
try { try {
final status = final status = await DevicesManagementApi().getBatchStatus(event.devicesUuid);
await DevicesManagementApi().getBatchStatus(event.devicesUuid); deviceStatus =
deviceStatus = WaterHeaterStatusModel.fromJson( WaterHeaterStatusModel.fromJson(event.devicesUuid.first, status.status);
event.devicesUuid.first, status.status);
emit(WaterHeaterDeviceStatusLoaded(deviceStatus)); emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
@ -673,8 +568,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
FutureOr<void> _batchControlWaterHeater(ControlWaterHeaterBatchEvent event, FutureOr<void> _batchControlWaterHeater(
Emitter<WaterHeaterState> emit) async { ControlWaterHeaterBatchEvent event, Emitter<WaterHeaterState> emit) async {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
@ -686,13 +581,10 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
status: deviceStatus, status: deviceStatus,
)); ));
final success = await _runDebounce( final success = await batchControlDevicesService.batchControlDevices(
deviceId: event.devicesUuid, uuids: event.devicesUuid,
code: event.code, code: event.code,
value: event.value, value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: true,
); );
if (success) { if (success) {

View File

@ -0,0 +1,18 @@
import 'package:syncrow_web/pages/device_managment/factories/device_bloc_dependencies_factory.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
abstract final class WaterHeaterBlocFactory {
const WaterHeaterBlocFactory._();
static WaterHeaterBloc create({
required String deviceId,
}) {
return WaterHeaterBloc(
deviceId: deviceId,
controlDeviceService:
DeviceBlocDependenciesFactory.createControlDeviceService(),
batchControlDevicesService:
DeviceBlocDependenciesFactory.createBatchControlDevicesService(),
);
}
}

View File

@ -1,15 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart'; import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
// import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart'; // import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/factories/water_heater_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class WaterHEaterBatchControlView extends StatelessWidget with HelperResponsiveLayout { class WaterHEaterBatchControlView extends StatelessWidget
with HelperResponsiveLayout {
const WaterHEaterBatchControlView({super.key, required this.deviceIds}); const WaterHEaterBatchControlView({super.key, required this.deviceIds});
final List<String> deviceIds; final List<String> deviceIds;
@ -17,8 +18,9 @@ class WaterHEaterBatchControlView extends StatelessWidget with HelperResponsiveL
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) => WaterHeaterBlocFactory.create(
WaterHeaterBloc()..add(FetchWaterHeaterBatchStatusEvent(devicesUuid: deviceIds)), deviceId: deviceIds.first,
)..add(FetchWaterHeaterBatchStatusEvent(devicesUuid: deviceIds)),
child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>( child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
builder: (context, state) { builder: (context, state) {
if (state is WaterHeaterLoadingState) { if (state is WaterHeaterLoadingState) {

View File

@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/factories/water_heater_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedual_view.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedual_view.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
@ -21,8 +22,9 @@ class WaterHeaterDeviceControlView extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) => WaterHeaterBlocFactory.create(
WaterHeaterBloc()..add(WaterHeaterFetchStatusEvent(device.uuid!)), deviceId: device.uuid ?? '',
)..add(WaterHeaterFetchStatusEvent(device.uuid!)),
child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>( child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
builder: (context, state) { builder: (context, state) {
if (state is WaterHeaterLoadingState) { if (state is WaterHeaterLoadingState) {
@ -33,8 +35,7 @@ class WaterHeaterDeviceControlView extends StatelessWidget
state is WaterHeaterBatchFailedState) { state is WaterHeaterBatchFailedState) {
return const Center(child: Text('Error fetching status')); return const Center(child: Text('Error fetching status'));
} else { } else {
return const SizedBox( return const SizedBox(height: 200, child: Center(child: SizedBox()));
height: 200, child: Center(child: SizedBox()));
} }
}, },
)); ));