Compare commits

..

3 Commits

60 changed files with 1779 additions and 1635 deletions

3
.vscode/launch.json vendored
View File

@ -16,7 +16,6 @@
"3000", "3000",
"-t", "-t",
"lib/main_dev.dart", "lib/main_dev.dart",
"--web-experimental-hot-reload",
], ],
"flutterMode": "debug" "flutterMode": "debug"
@ -36,7 +35,6 @@
"3000", "3000",
"-t", "-t",
"lib/main_staging.dart", "lib/main_staging.dart",
"--web-experimental-hot-reload",
], ],
"flutterMode": "debug" "flutterMode": "debug"
@ -56,7 +54,6 @@
"3000", "3000",
"-t", "-t",
"lib/main.dart", "lib/main.dart",
"--web-experimental-hot-reload",
], ],
"flutterMode": "debug" "flutterMode": "debug"

View File

@ -26,10 +26,10 @@ final class AirQualityDataLoadingStrategy implements AnalyticsDataLoadingStrateg
final spaceTreeBloc = context.read<SpaceTreeBloc>(); final spaceTreeBloc = context.read<SpaceTreeBloc>();
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid); final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
if (isSpaceSelected) { final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
clearData(context); if (hasSelectedSpaces) clearData(context);
return;
} if (isSpaceSelected) return;
spaceTreeBloc spaceTreeBloc
..add(const SpaceTreeClearSelectionEvent()) ..add(const SpaceTreeClearSelectionEvent())
@ -42,18 +42,11 @@ final class AirQualityDataLoadingStrategy implements AnalyticsDataLoadingStrateg
); );
} }
@override
void onChildSpaceSelected(
BuildContext context,
CommunityModel community,
SpaceModel child,
) {
return onSpaceSelected(context, community, child);
}
@override @override
void clearData(BuildContext context) { void clearData(BuildContext context) {
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent()); context.read<SpaceTreeBloc>().add(
const AnalyticsClearAllSpaceTreeSelectionsEvent(),
);
FetchAirQualityDataHelper.clearAllData(context); FetchAirQualityDataHelper.clearAllData(context);
} }
} }

View File

@ -13,10 +13,5 @@ abstract class AnalyticsDataLoadingStrategy {
CommunityModel community, CommunityModel community,
SpaceModel space, SpaceModel space,
); );
void onChildSpaceSelected(
BuildContext context,
CommunityModel community,
SpaceModel child,
);
void clearData(BuildContext context); void clearData(BuildContext context);
} }

View File

@ -14,24 +14,14 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
CommunityModel community, CommunityModel community,
List<SpaceModel> spaces, List<SpaceModel> spaces,
) { ) {
context.read<SpaceTreeBloc>().add( final spaceTreeBloc = context.read<SpaceTreeBloc>();
OnCommunitySelected( final isCommunitySelected =
community.uuid, spaceTreeBloc.state.selectedCommunities.contains(community.uuid);
spaces,
),
);
final spaceTreeState = context.read<SpaceTreeBloc>().state; if (isCommunitySelected) {
if (spaceTreeState.selectedCommunities.contains(community.uuid)) {
clearData(context); clearData(context);
return; return;
} }
FetchEnergyManagementDataHelper.loadEnergyManagementData(
context,
communityId: community.uuid,
spaceId: spaces.isNotEmpty ? spaces.first.uuid ?? '' : '',
);
} }
@override @override
@ -40,7 +30,24 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
CommunityModel community, CommunityModel community,
SpaceModel space, SpaceModel space,
) { ) {
context.read<SpaceTreeBloc>().add( final spaceTreeBloc = context.read<SpaceTreeBloc>();
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
if (isSpaceSelected) {
final firstSelectedSpace = spaceTreeBloc.state.selectedSpaces.first;
final isTheFirstSelectedSpace = firstSelectedSpace == space.uuid;
if (isTheFirstSelectedSpace) {
clearData(context);
}
return;
}
if (hasSelectedSpaces) {
clearData(context);
}
spaceTreeBloc.add(
OnSpaceSelected( OnSpaceSelected(
community, community,
space.uuid ?? '', space.uuid ?? '',
@ -48,13 +55,6 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
), ),
); );
final spaceTreeState = context.read<SpaceTreeBloc>().state;
if (spaceTreeState.selectedCommunities.contains(community.uuid) ||
spaceTreeState.selectedSpaces.contains(space.uuid)) {
clearData(context);
return;
}
FetchEnergyManagementDataHelper.loadEnergyManagementData( FetchEnergyManagementDataHelper.loadEnergyManagementData(
context, context,
communityId: community.uuid, communityId: community.uuid,
@ -62,18 +62,11 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
); );
} }
@override
void onChildSpaceSelected(
BuildContext context,
CommunityModel community,
SpaceModel child,
) {
return onSpaceSelected(context, community, child);
}
@override @override
void clearData(BuildContext context) { void clearData(BuildContext context) {
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent()); context.read<SpaceTreeBloc>().add(
const AnalyticsClearAllSpaceTreeSelectionsEvent(),
);
FetchEnergyManagementDataHelper.clearAllData(context); FetchEnergyManagementDataHelper.clearAllData(context);
} }
} }

View File

@ -26,10 +26,10 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
final spaceTreeBloc = context.read<SpaceTreeBloc>(); final spaceTreeBloc = context.read<SpaceTreeBloc>();
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid); final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
if (isSpaceSelected) { final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
clearData(context); if (hasSelectedSpaces) clearData(context);
return;
} if (isSpaceSelected) return;
spaceTreeBloc spaceTreeBloc
..add(const SpaceTreeClearSelectionEvent()) ..add(const SpaceTreeClearSelectionEvent())
@ -42,18 +42,11 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
); );
} }
@override
void onChildSpaceSelected(
BuildContext context,
CommunityModel community,
SpaceModel child,
) {
return onSpaceSelected(context, community, child);
}
@override @override
void clearData(BuildContext context) { void clearData(BuildContext context) {
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent()); context.read<SpaceTreeBloc>().add(
const AnalyticsClearAllSpaceTreeSelectionsEvent(),
);
FetchOccupancyDataHelper.clearAllData(context); FetchOccupancyDataHelper.clearAllData(context);
} }
} }

View File

@ -21,7 +21,7 @@ class AnalyticsCommunitiesSidebar extends StatelessWidget {
strategy.onSpaceSelected(context, community, space); strategy.onSpaceSelected(context, community, space);
}, },
onSelectChildSpace: (community, child) { onSelectChildSpace: (community, child) {
strategy.onChildSpaceSelected(context, community, child); strategy.onSpaceSelected(context, community, child);
}, },
), ),
); );

View File

@ -1,27 +1,21 @@
import 'dart:async'; import 'dart:async';
import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.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/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_event.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_state.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/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'; import 'package:syncrow_web/services/devices_mang_api.dart';
class AcBloc extends Bloc<AcsEvent, AcsState> { class AcBloc extends Bloc<AcsEvent, AcsState> {
late AcStatusModel deviceStatus; late AcStatusModel deviceStatus;
final String deviceId; final String deviceId;
final ControlDeviceService controlDeviceService; Timer? _timer;
final BatchControlDevicesService batchControlDevicesService;
Timer? _countdownTimer; Timer? _countdownTimer;
AcBloc({ AcBloc({required this.deviceId}) : super(AcsInitialState()) {
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(AcsInitialState()) {
on<AcFetchDeviceStatusEvent>(_onFetchAcStatus); on<AcFetchDeviceStatusEvent>(_onFetchAcStatus);
on<AcFetchBatchStatusEvent>(_onFetchAcBatchStatus); on<AcFetchBatchStatusEvent>(_onFetchAcBatchStatus);
on<AcControlEvent>(_onAcControl); on<AcControlEvent>(_onAcControl);
@ -40,14 +34,14 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
int scheduledMinutes = 0; int scheduledMinutes = 0;
FutureOr<void> _onFetchAcStatus( FutureOr<void> _onFetchAcStatus(
AcFetchDeviceStatusEvent event, AcFetchDeviceStatusEvent event, Emitter<AcsState> emit) async {
Emitter<AcsState> emit,
) async {
emit(AcsLoadingState()); emit(AcsLoadingState());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status); deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status);
if (deviceStatus.countdown1 != 0) { if (deviceStatus.countdown1 != 0) {
// Convert API value to minutes
final totalMinutes = deviceStatus.countdown1 * 6; final totalMinutes = deviceStatus.countdown1 * 6;
scheduledHours = totalMinutes ~/ 60; scheduledHours = totalMinutes ~/ 60;
scheduledMinutes = totalMinutes % 60; scheduledMinutes = totalMinutes % 60;
@ -68,24 +62,30 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
} }
} }
void _listenToChanges(deviceId) { _listenToChanges(deviceId) {
try { try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); DatabaseReference ref =
final stream = ref.onValue; FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) async { stream.listen((DatabaseEvent event) async {
if (event.snapshot.value == null) return; if (event.snapshot.value == null) return;
if (_timer != null) {
await Future.delayed(const Duration(seconds: 1));
}
Map<dynamic, dynamic> usersMap = Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>; event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = []; List<Status> statusList = [];
usersMap['status'].forEach((element) { usersMap['status'].forEach((element) {
statusList.add(Status(code: element['code'], value: element['value'])); statusList
.add(Status(code: element['code'], value: element['value']));
}); });
deviceStatus = AcStatusModel.fromJson(usersMap['productUuid'], statusList); deviceStatus =
AcStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) { if (!isClosed) {
add(AcStatusUpdated(deviceStatus)); add(AcStatusUpdated(deviceStatus));
} }
@ -93,44 +93,146 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
} catch (_) {} } catch (_) {}
} }
void _onAcStatusUpdated( void _onAcStatusUpdated(AcStatusUpdated event, Emitter<AcsState> emit) {
AcStatusUpdated event,
Emitter<AcsState> emit,
) {
deviceStatus = event.deviceStatus; deviceStatus = event.deviceStatus;
emit(ACStatusLoaded(status: deviceStatus)); emit(ACStatusLoaded(status: deviceStatus));
} }
FutureOr<void> _onAcControl( FutureOr<void> _onAcControl(
AcControlEvent event, AcControlEvent event, Emitter<AcsState> emit) async {
Emitter<AcsState> emit, final oldValue = _getValueByCode(event.code);
) async {
emit(AcsLoadingState()); _updateLocalValue(event.code, event.value, emit);
_updateDeviceFunctionFromCode(event.code, event.value);
emit(ACStatusLoaded(status: deviceStatus)); emit(ACStatusLoaded(status: deviceStatus));
try { await _runDebounce(
final success = await controlDeviceService.controlDevice( isBatch: false,
deviceUuid: event.deviceId, deviceId: event.deviceId,
status: Status(code: event.code, value: event.value), code: event.code,
value: event.value,
oldValue: oldValue,
emit: emit,
); );
}
if (!success) { Future<void> _runDebounce({
emit(const AcsFailedState(error: 'Failed to control device')); required dynamic deviceId,
required String code,
required dynamic value,
required dynamic oldValue,
required Emitter<AcsState> 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) { } catch (e) {
emit(AcsFailedState(error: e.toString())); 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<AcsState> emit) {
_updateLocalValue(code, oldValue, emit);
emit(ACStatusLoaded(status: deviceStatus));
}
void _updateLocalValue(String code, dynamic value, Emitter<AcsState> 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);
}
case 'countdown_time':
if (value is int) {
deviceStatus = deviceStatus.copyWith(countdown1: value);
}
break;
default:
break;
}
emit(ACStatusLoaded(status: 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;
case 'countdown_time':
return deviceStatus.countdown1;
default:
return null;
} }
} }
FutureOr<void> _onFetchAcBatchStatus( FutureOr<void> _onFetchAcBatchStatus(
AcFetchBatchStatusEvent event, AcFetchBatchStatusEvent event, Emitter<AcsState> emit) async {
Emitter<AcsState> emit,
) async {
emit(AcsLoadingState()); emit(AcsLoadingState());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.devicesIds); final status =
deviceStatus = AcStatusModel.fromJson(event.devicesIds.first, status.status); await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus =
AcStatusModel.fromJson(event.devicesIds.first, status.status);
emit(ACStatusLoaded(status: deviceStatus)); emit(ACStatusLoaded(status: deviceStatus));
} catch (e) { } catch (e) {
emit(AcsFailedState(error: e.toString())); emit(AcsFailedState(error: e.toString()));
@ -138,32 +240,25 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
} }
FutureOr<void> _onAcBatchControl( FutureOr<void> _onAcBatchControl(
AcBatchControlEvent event, AcBatchControlEvent event, Emitter<AcsState> emit) async {
Emitter<AcsState> emit, final oldValue = _getValueByCode(event.code);
) async {
emit(AcsLoadingState()); _updateLocalValue(event.code, event.value, emit);
_updateDeviceFunctionFromCode(event.code, event.value);
emit(ACStatusLoaded(status: deviceStatus)); emit(ACStatusLoaded(status: deviceStatus));
try { await _runDebounce(
final success = await batchControlDevicesService.batchControlDevices( isBatch: true,
uuids: event.devicesIds, deviceId: event.devicesIds,
code: event.code, code: event.code,
value: event.value, value: event.value,
oldValue: oldValue,
emit: emit,
); );
if (!success) {
emit(const AcsFailedState(error: 'Failed to control devices'));
}
} catch (e) {
emit(AcsFailedState(error: e.toString()));
}
} }
Future<void> _onFactoryReset( FutureOr<void> _onFactoryReset(
AcFactoryResetEvent event, AcFactoryResetEvent event, Emitter<AcsState> emit) async {
Emitter<AcsState> emit,
) async {
emit(AcsLoadingState()); emit(AcsLoadingState());
try { try {
final response = await DevicesManagementApi().factoryReset( final response = await DevicesManagementApi().factoryReset(
@ -180,11 +275,9 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
} }
} }
void _onClose( void _onClose(OnClose event, Emitter<AcsState> emit) {
OnClose event,
Emitter<AcsState> emit,
) {
_countdownTimer?.cancel(); _countdownTimer?.cancel();
_timer?.cancel();
} }
void _handleIncreaseTime(IncreaseTimeEvent event, Emitter<AcsState> emit) { void _handleIncreaseTime(IncreaseTimeEvent event, Emitter<AcsState> emit) {
@ -207,10 +300,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
)); ));
} }
void _handleDecreaseTime( void _handleDecreaseTime(DecreaseTimeEvent event, Emitter<AcsState> emit) {
DecreaseTimeEvent event,
Emitter<AcsState> emit,
) {
if (state is! ACStatusLoaded) return; if (state is! ACStatusLoaded) return;
final currentState = state as ACStatusLoaded; final currentState = state as ACStatusLoaded;
int totalMinutes = (scheduledHours * 60) + scheduledMinutes; int totalMinutes = (scheduledHours * 60) + scheduledMinutes;
@ -225,9 +315,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
} }
Future<void> _handleToggleTimer( Future<void> _handleToggleTimer(
ToggleScheduleEvent event, ToggleScheduleEvent event, Emitter<AcsState> emit) async {
Emitter<AcsState> emit,
) async {
if (state is! ACStatusLoaded) return; if (state is! ACStatusLoaded) return;
final currentState = state as ACStatusLoaded; final currentState = state as ACStatusLoaded;
@ -243,30 +331,29 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
try { try {
final scaledValue = totalMinutes ~/ 6; final scaledValue = totalMinutes ~/ 6;
final success = await controlDeviceService.controlDevice( await _runDebounce(
deviceUuid: deviceId, isBatch: false,
status: Status(code: 'countdown_time', value: scaledValue), deviceId: deviceId,
code: 'countdown_time',
value: scaledValue,
oldValue: scaledValue,
emit: emit,
); );
if (success) {
_startCountdownTimer(emit); _startCountdownTimer(emit);
emit(currentState.copyWith(isTimerActive: timerActive)); emit(currentState.copyWith(isTimerActive: timerActive));
} else {
timerActive = false;
emit(const AcsFailedState(error: 'Failed to set timer'));
}
} catch (e) { } catch (e) {
timerActive = false; timerActive = false;
emit(AcsFailedState(error: e.toString())); emit(AcsFailedState(error: e.toString()));
} }
} else { } else {
try { await _runDebounce(
final success = await controlDeviceService.controlDevice( isBatch: false,
deviceUuid: deviceId, deviceId: deviceId,
status: Status(code: 'countdown_time', value: 0), code: 'countdown_time',
value: 0,
oldValue: 0,
emit: emit,
); );
if (success) {
_countdownTimer?.cancel(); _countdownTimer?.cancel();
scheduledHours = 0; scheduledHours = 0;
scheduledMinutes = 0; scheduledMinutes = 0;
@ -275,12 +362,6 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
scheduledHours: 0, scheduledHours: 0,
scheduledMinutes: 0, scheduledMinutes: 0,
)); ));
} else {
emit(const AcsFailedState(error: 'Failed to stop timer'));
}
} catch (e) {
emit(AcsFailedState(error: e.toString()));
}
} }
} }
@ -304,10 +385,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
}); });
} }
void _handleUpdateTimer( void _handleUpdateTimer(UpdateTimerEvent event, Emitter<AcsState> emit) {
UpdateTimerEvent event,
Emitter<AcsState> emit,
) {
if (state is ACStatusLoaded) { if (state is ACStatusLoaded) {
final currentState = state as ACStatusLoaded; final currentState = state as ACStatusLoaded;
emit(currentState.copyWith( emit(currentState.copyWith(
@ -322,6 +400,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
ApiCountdownValueEvent event, Emitter<AcsState> emit) { ApiCountdownValueEvent event, Emitter<AcsState> emit) {
if (state is ACStatusLoaded) { if (state is ACStatusLoaded) {
final totalMinutes = event.apiValue * 6; final totalMinutes = event.apiValue * 6;
final scheduledHours = totalMinutes ~/ 60;
scheduledMinutes = totalMinutes % 60; scheduledMinutes = totalMinutes % 60;
_startCountdownTimer( _startCountdownTimer(
emit, emit,
@ -330,43 +409,6 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
} }
} }
void _updateDeviceFunctionFromCode(String code, dynamic value) {
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;
case 'countdown_time':
if (value is int) {
deviceStatus = deviceStatus.copyWith(countdown1: value);
}
break;
default:
break;
}
}
@override @override
Future<void> close() { Future<void> close() {
add(OnClose()); add(OnClose());

View File

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

View File

@ -3,12 +3,12 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart'; import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.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/bloc/ac_state.dart';
import 'package:syncrow_web/pages/device_managment/ac/factories/ac_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_ac_mode.dart'; import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_ac_mode.dart';
import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_current_temp.dart'; import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_current_temp.dart';
import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_fan_speed.dart'; import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_fan_speed.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.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/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
@ -26,9 +26,8 @@ class AcDeviceBatchControlView extends StatelessWidget with HelperResponsiveLayo
final isLarge = isLargeScreenSize(context); final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context); final isMedium = isMediumScreenSize(context);
return BlocProvider( return BlocProvider(
create: (context) => AcBlocFactory.create( create: (context) =>
deviceId: devicesIds.first, AcBloc(deviceId: devicesIds.first)..add(AcFetchBatchStatusEvent(devicesIds)),
)..add(AcFetchBatchStatusEvent(devicesIds)),
child: BlocBuilder<AcBloc, AcsState>( child: BlocBuilder<AcBloc, AcsState>(
builder: (context, state) { builder: (context, state) {
if (state is ACStatusLoaded) { if (state is ACStatusLoaded) {

View File

@ -3,7 +3,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart'; import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.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/bloc/ac_state.dart';
import 'package:syncrow_web/pages/device_managment/ac/factories/ac_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/ac/view/control_list/ac_mode.dart'; import 'package:syncrow_web/pages/device_managment/ac/view/control_list/ac_mode.dart';
import 'package:syncrow_web/pages/device_managment/ac/view/control_list/current_temp.dart'; import 'package:syncrow_web/pages/device_managment/ac/view/control_list/current_temp.dart';
import 'package:syncrow_web/pages/device_managment/ac/view/control_list/fan_speed.dart'; import 'package:syncrow_web/pages/device_managment/ac/view/control_list/fan_speed.dart';
@ -25,9 +24,8 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
final isMedium = isMediumScreenSize(context); final isMedium = isMediumScreenSize(context);
return BlocProvider( return BlocProvider(
create: (context) => AcBlocFactory.create( create: (context) => AcBloc(deviceId: device.uuid!)
deviceId: device.uuid!, ..add(AcFetchDeviceStatusEvent(device.uuid!)),
)..add(AcFetchDeviceStatusEvent(device.uuid!)),
child: BlocBuilder<AcBloc, AcsState>( child: BlocBuilder<AcBloc, AcsState>(
builder: (context, state) { builder: (context, state) {
final acBloc = BlocProvider.of<AcBloc>(context); final acBloc = BlocProvider.of<AcBloc>(context);

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.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/all_devices/models/device_status.dart';
@ -5,21 +7,14 @@ import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_e
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_state.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_state.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/help_description.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/help_description.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';
class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> { class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
final String deviceId; final String deviceId;
final ControlDeviceService controlDeviceService;
final BatchControlDevicesService batchControlDevicesService;
late CeilingSensorModel deviceStatus; late CeilingSensorModel deviceStatus;
Timer? _timer;
CeilingSensorBloc({ CeilingSensorBloc({required this.deviceId}) : super(CeilingInitialState()) {
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(CeilingInitialState()) {
on<CeilingInitialEvent>(_fetchCeilingSensorStatus); on<CeilingInitialEvent>(_fetchCeilingSensorStatus);
on<CeilingFetchDeviceStatusEvent>(_fetchCeilingSensorBatchControl); on<CeilingFetchDeviceStatusEvent>(_fetchCeilingSensorBatchControl);
on<CeilingChangeValueEvent>(_changeValue); on<CeilingChangeValueEvent>(_changeValue);
@ -31,34 +26,35 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _fetchCeilingSensorStatus( void _fetchCeilingSensorStatus(
CeilingInitialEvent event, CeilingInitialEvent event, Emitter<CeilingSensorState> emit) async {
Emitter<CeilingSensorState> emit,
) async {
emit(CeilingLoadingInitialState()); emit(CeilingLoadingInitialState());
try { try {
final response = await DevicesManagementApi().getDeviceStatus(event.deviceId); var response =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = CeilingSensorModel.fromJson(response.status); deviceStatus = CeilingSensorModel.fromJson(response.status);
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus)); emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
_listenToChanges(event.deviceId); _listenToChanges(event.deviceId);
} catch (e) { } catch (e) {
emit(CeilingFailedState(error: e.toString())); emit(CeilingFailedState(error: e.toString()));
return;
} }
} }
void _listenToChanges(String deviceId) { _listenToChanges(deviceId) {
try { try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); DatabaseReference ref =
final stream = ref.onValue; FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) { stream.listen((DatabaseEvent event) {
if (event.snapshot.value == null) return; Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
final usersMap = event.snapshot.value as Map<dynamic, dynamic>;
final statusList = <Status>[];
List<Status> statusList = [];
usersMap['status'].forEach((element) { usersMap['status'].forEach((element) {
statusList.add(Status(code: element['code'], value: element['value'])); statusList
.add(Status(code: element['code'], value: element['value']));
}); });
deviceStatus = CeilingSensorModel.fromJson(statusList); deviceStatus = CeilingSensorModel.fromJson(statusList);
@ -69,127 +65,149 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
} catch (_) {} } catch (_) {}
} }
void _onStatusUpdated( void _onStatusUpdated(StatusUpdated event, Emitter<CeilingSensorState> emit) {
StatusUpdated event,
Emitter<CeilingSensorState> emit,
) {
deviceStatus = event.deviceStatus; deviceStatus = event.deviceStatus;
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus)); emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
} }
Future<void> _changeValue( void _changeValue(
CeilingChangeValueEvent event, CeilingChangeValueEvent event, Emitter<CeilingSensorState> emit) async {
Emitter<CeilingSensorState> emit,
) async {
emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus)); emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus));
_updateDeviceFunctionFromCode(event.code, event.value); if (event.code == 'sensitivity') {
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus)); deviceStatus.sensitivity = event.value;
} else if (event.code == 'none_body_time') {
try { deviceStatus.noBodyTime = event.value;
await controlDeviceService.controlDevice( } else if (event.code == 'moving_max_dis') {
deviceUuid: deviceId, deviceStatus.maxDistance = event.value;
status: Status(code: event.code, value: event.value), } else if (event.code == 'scene') {
); deviceStatus.spaceType = getSpaceType(event.value);
} catch (e) {
emit(CeilingFailedState(error: e.toString()));
} }
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
await _runDeBouncer(
deviceId: deviceId,
code: event.code,
value: event.value,
emit: emit,
isBatch: false,
);
} }
Future<void> _onBatchControl( Future<void> _onBatchControl(
CeilingBatchControlEvent event, CeilingBatchControlEvent event, Emitter<CeilingSensorState> emit) async {
Emitter<CeilingSensorState> emit,
) async {
emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus)); emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus));
_updateDeviceFunctionFromCode(event.code, event.value); if (event.code == 'sensitivity') {
deviceStatus.sensitivity = event.value;
} else if (event.code == 'none_body_time') {
deviceStatus.noBodyTime = event.value;
} else if (event.code == 'moving_max_dis') {
deviceStatus.maxDistance = event.value;
} else if (event.code == 'scene') {
deviceStatus.spaceType = getSpaceType(event.value);
}
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus)); emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
await _runDeBouncer(
try { deviceId: event.deviceIds,
final success = await batchControlDevicesService.batchControlDevices(
uuids: event.deviceIds,
code: event.code, code: event.code,
value: event.value, value: event.value,
emit: emit,
isBatch: true,
); );
if (!success) {
emit(const CeilingFailedState(error: 'Failed to control devices'));
}
} catch (e) {
emit(CeilingFailedState(error: e.toString()));
}
} }
void _updateDeviceFunctionFromCode(String code, dynamic value) { _runDeBouncer({
switch (code) { required dynamic deviceId,
case 'sensitivity': required String code,
deviceStatus.sensitivity = value; required dynamic value,
break; required Emitter<CeilingSensorState> emit,
case 'none_body_time': required bool isBatch,
deviceStatus.noBodyTime = value; }) {
break; late String id;
case 'moving_max_dis':
deviceStatus.maxDistance = value; if (deviceId is List) {
break; id = deviceId.first;
case 'scene': } else {
deviceStatus.spaceType = getSpaceType(value); id = deviceId;
break;
default:
break;
}
} }
Future<void> _getDeviceReports( if (_timer != null) {
GetCeilingDeviceReportsEvent event, _timer!.cancel();
Emitter<CeilingSensorState> emit, }
) async { _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) {
add(CeilingInitialEvent(id));
}
if (response == true && code == 'scene') {
emit(CeilingLoadingInitialState());
await Future.delayed(const Duration(seconds: 1));
add(CeilingInitialEvent(id));
}
} catch (_) {
await Future.delayed(const Duration(milliseconds: 500));
add(CeilingInitialEvent(id));
}
});
}
FutureOr<void> _getDeviceReports(GetCeilingDeviceReportsEvent event,
Emitter<CeilingSensorState> emit) async {
if (event.code.isEmpty) { if (event.code.isEmpty) {
emit(ShowCeilingDescriptionState(description: reportString)); emit(ShowCeilingDescriptionState(description: reportString));
return; return;
} } else {
emit(CeilingReportsLoadingState()); emit(CeilingReportsLoadingState());
// final from = DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch;
// final to = DateTime.now().millisecondsSinceEpoch;
try { try {
final value = await DevicesManagementApi.getDeviceReports( // await DevicesManagementApi.getDeviceReportsByDate(deviceId, event.code, from.toString(), to.toString())
deviceId, await DevicesManagementApi.getDeviceReports(deviceId, event.code)
event.code, .then((value) {
);
emit(CeilingReportsState(deviceReport: value)); emit(CeilingReportsState(deviceReport: value));
});
} catch (e) { } catch (e) {
emit(CeilingReportsFailedState(error: e.toString())); emit(CeilingReportsFailedState(error: e.toString()));
return;
}
} }
} }
void _showDescription( void _showDescription(
ShowCeilingDescriptionEvent event, ShowCeilingDescriptionEvent event, Emitter<CeilingSensorState> emit) {
Emitter<CeilingSensorState> emit,
) {
emit(ShowCeilingDescriptionState(description: event.description)); emit(ShowCeilingDescriptionState(description: event.description));
} }
void _backToGridView( void _backToGridView(
BackToCeilingGridViewEvent event, BackToCeilingGridViewEvent event, Emitter<CeilingSensorState> emit) {
Emitter<CeilingSensorState> emit,
) {
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus)); emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
} }
Future<void> _fetchCeilingSensorBatchControl( FutureOr<void> _fetchCeilingSensorBatchControl(
CeilingFetchDeviceStatusEvent event, CeilingFetchDeviceStatusEvent event,
Emitter<CeilingSensorState> emit, Emitter<CeilingSensorState> emit) async {
) async {
emit(CeilingLoadingInitialState()); emit(CeilingLoadingInitialState());
try { try {
final response = await DevicesManagementApi().getBatchStatus(event.devicesIds); var response =
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = CeilingSensorModel.fromJson(response.status); deviceStatus = CeilingSensorModel.fromJson(response.status);
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus)); emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
} catch (e) { } catch (e) {
emit(CeilingFailedState(error: e.toString())); emit(CeilingFailedState(error: e.toString()));
return;
} }
} }
Future<void> _onFactoryReset( FutureOr<void> _onFactoryReset(
CeilingFactoryResetEvent event, CeilingFactoryResetEvent event, Emitter<CeilingSensorState> emit) async {
Emitter<CeilingSensorState> emit,
) async {
emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus)); emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus));
try { try {
final response = await DevicesManagementApi().factoryReset( final response = await DevicesManagementApi().factoryReset(

View File

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

View File

@ -4,9 +4,9 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_re
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_bloc.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_bloc.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_event.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_event.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_state.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_state.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/factories/ceiling_sensor_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.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/sensors_widgets/presence_space_type.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_space_type.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presense_nobody_time.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presense_nobody_time.dart';
@ -23,9 +23,8 @@ class CeilingSensorBatchControlView extends StatelessWidget with HelperResponsiv
final isLarge = isLargeScreenSize(context); final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context); final isMedium = isMediumScreenSize(context);
return BlocProvider( return BlocProvider(
create: (context) => CeilingSensorBlocFactory.create( create: (context) => CeilingSensorBloc(deviceId: devicesIds.first)
deviceId: devicesIds.first, ..add(CeilingFetchDeviceStatusEvent(devicesIds)),
)..add(CeilingFetchDeviceStatusEvent(devicesIds)),
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>( child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
builder: (context, state) { builder: (context, state) {
if (state is CeilingLoadingInitialState || state is CeilingReportsLoadingState) { if (state is CeilingLoadingInitialState || state is CeilingReportsLoadingState) {
@ -111,6 +110,7 @@ class CeilingSensorBatchControlView extends StatelessWidget with HelperResponsiv
), ),
), ),
), ),
// FirmwareUpdateWidget(deviceId: devicesIds.first, version: 4),
FactoryResetWidget( FactoryResetWidget(
callFactoryReset: () { callFactoryReset: () {
context.read<CeilingSensorBloc>().add( context.read<CeilingSensorBloc>().add(

View File

@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_bloc.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_bloc.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_event.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_event.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_state.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_state.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/factories/ceiling_sensor_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart'; import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_display_data.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_display_data.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_space_type.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_space_type.dart';
@ -29,9 +28,8 @@ class CeilingSensorControlsView extends StatelessWidget
final isLarge = isLargeScreenSize(context); final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context); final isMedium = isMediumScreenSize(context);
return BlocProvider( return BlocProvider(
create: (context) => CeilingSensorBlocFactory.create( create: (context) => CeilingSensorBloc(deviceId: device.uuid ?? '')
deviceId: device.uuid ?? '', ..add(CeilingInitialEvent(device.uuid ?? '')),
)..add(CeilingInitialEvent(device.uuid ?? '')),
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>( child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
builder: (context, state) { builder: (context, state) {
if (state is CeilingLoadingInitialState || if (state is CeilingLoadingInitialState ||

View File

@ -1,25 +1,17 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart';
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.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';
class CurtainBloc extends Bloc<CurtainEvent, CurtainState> { class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
late bool deviceStatus; late bool deviceStatus;
final String deviceId; final String deviceId;
final ControlDeviceService controlDeviceService; Timer? _timer;
final BatchControlDevicesService batchControlDevicesService;
CurtainBloc({ CurtainBloc({required this.deviceId}) : super(CurtainInitial()) {
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(CurtainInitial()) {
on<CurtainFetchDeviceStatus>(_onFetchDeviceStatus); on<CurtainFetchDeviceStatus>(_onFetchDeviceStatus);
on<CurtainFetchBatchStatus>(_onFetchBatchStatus); on<CurtainFetchBatchStatus>(_onFetchBatchStatus);
on<CurtainControl>(_onCurtainControl); on<CurtainControl>(_onCurtainControl);
@ -28,31 +20,32 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( FutureOr<void> _onFetchDeviceStatus(
CurtainFetchDeviceStatus event, CurtainFetchDeviceStatus event, Emitter<CurtainState> emit) async {
Emitter<CurtainState> emit,
) async {
emit(CurtainStatusLoading()); emit(CurtainStatusLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
_listenToChanges(event.deviceId, emit); await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId);
deviceStatus = _checkStatus(status.status[0].value); deviceStatus = _checkStatus(status.status[0].value);
emit(CurtainStatusLoaded(deviceStatus)); emit(CurtainStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(CurtainError(e.toString())); emit(CurtainError(e.toString()));
} }
} }
void _listenToChanges(String deviceId, Emitter<CurtainState> emit) { void _listenToChanges(String deviceId) {
try { try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); DatabaseReference ref =
final stream = ref.onValue; FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) { stream.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?; final data = event.snapshot.value as Map<dynamic, dynamic>?;
if (data == null) return; if (data == null) return;
final statusList = <Status>[]; List<Status> statusList = [];
if (data['status'] != null) { if (data['status'] != null) {
for (var element in data['status']) { for (var element in data['status']) {
statusList.add( statusList.add(
@ -64,7 +57,7 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
} }
} }
if (statusList.isNotEmpty) { if (statusList.isNotEmpty) {
final newStatus = _checkStatus(statusList[0].value); bool newStatus = _checkStatus(statusList[0].value);
if (newStatus != deviceStatus) { if (newStatus != deviceStatus) {
deviceStatus = newStatus; deviceStatus = newStatus;
if (!isClosed) { if (!isClosed) {
@ -78,32 +71,76 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
} }
} }
void _onStatusUpdated( void _onStatusUpdated(StatusUpdated event, Emitter<CurtainState> emit) {
StatusUpdated event,
Emitter<CurtainState> emit,
) {
emit(CurtainStatusLoading()); emit(CurtainStatusLoading());
deviceStatus = event.deviceStatus; deviceStatus = event.deviceStatus;
emit(CurtainStatusLoaded(deviceStatus)); emit(CurtainStatusLoaded(deviceStatus));
} }
Future<void> _onCurtainControl( FutureOr<void> _onCurtainControl(
CurtainControl event, CurtainControl event, Emitter<CurtainState> emit) async {
Emitter<CurtainState> emit, final oldValue = deviceStatus;
) async {
emit(CurtainStatusLoading());
_updateLocalValue(event.value, emit); _updateLocalValue(event.value, emit);
try { emit(CurtainStatusLoaded(deviceStatus));
final controlValue = event.value ? 'open' : 'close';
await controlDeviceService.controlDevice( await _runDebounce(
deviceUuid: event.deviceId, deviceId: event.deviceId,
status: Status(code: event.code, value: controlValue), code: event.code,
value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: false,
); );
} catch (e) {
_updateLocalValue(!event.value, emit);
emit(CurtainControlError(e.toString()));
} }
Future<void> _runDebounce({
required dynamic deviceId,
required String code,
required bool value,
required bool oldValue,
required Emitter<CurtainState> 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 {
final controlValue = value ? 'open' : 'close';
late bool response;
if (isBatch) {
response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, controlValue);
} else {
response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: controlValue));
}
if (!response) {
_revertValueAndEmit(id, oldValue, emit);
}
} catch (e) {
_revertValueAndEmit(id, oldValue, emit);
}
});
}
void _revertValueAndEmit(
String deviceId, bool oldValue, Emitter<CurtainState> emit) {
_updateLocalValue(oldValue, emit);
emit(CurtainStatusLoaded(deviceStatus));
emit(const CurtainControlError('Failed to control the device.'));
} }
void _updateLocalValue(bool value, Emitter<CurtainState> emit) { void _updateLocalValue(bool value, Emitter<CurtainState> emit) {
@ -115,44 +152,41 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
return command.toLowerCase() == 'open'; return command.toLowerCase() == 'open';
} }
Future<void> _onFetchBatchStatus( FutureOr<void> _onFetchBatchStatus(
CurtainFetchBatchStatus event, CurtainFetchBatchStatus event, Emitter<CurtainState> emit) async {
Emitter<CurtainState> emit,
) async {
emit(CurtainStatusLoading()); emit(CurtainStatusLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.devicesIds); final status =
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = _checkStatus(status.status[0].value); deviceStatus = _checkStatus(status.status[0].value);
emit(CurtainStatusLoaded(deviceStatus)); emit(CurtainStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(CurtainError(e.toString())); emit(CurtainError(e.toString()));
} }
} }
Future<void> _onCurtainBatchControl( FutureOr<void> _onCurtainBatchControl(
CurtainBatchControl event, CurtainBatchControl event, Emitter<CurtainState> emit) async {
Emitter<CurtainState> emit, final oldValue = deviceStatus;
) async {
emit(CurtainStatusLoading());
_updateLocalValue(event.value, emit); _updateLocalValue(event.value, emit);
try { emit(CurtainStatusLoaded(deviceStatus));
final controlValue = event.value ? 'open' : 'stop';
await batchControlDevicesService.batchControlDevices( await _runDebounce(
uuids: event.devicesIds, deviceId: event.devicesIds,
code: event.code, code: event.code,
value: controlValue, value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: true,
); );
} catch (e) {
_updateLocalValue(!event.value, emit);
emit(CurtainControlError(e.toString()));
}
} }
Future<void> _onFactoryReset( FutureOr<void> _onFactoryReset(
CurtainFactoryReset event, CurtainFactoryReset event, Emitter<CurtainState> emit) async {
Emitter<CurtainState> emit,
) async {
emit(CurtainStatusLoading()); emit(CurtainStatusLoading());
try { try {
final response = await DevicesManagementApi().factoryReset( final response = await DevicesManagementApi().factoryReset(

View File

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

View File

@ -5,7 +5,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_re
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_bloc.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_bloc.dart';
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart';
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart';
import 'package:syncrow_web/pages/device_managment/curtain/factories/curtain_bloc_factory.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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -19,7 +18,7 @@ class CurtainBatchStatusView extends StatelessWidget with HelperResponsiveLayout
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) =>
CurtainBlocFactory.create(deviceId: devicesIds.first)..add(CurtainFetchBatchStatus(devicesIds)), CurtainBloc(deviceId: devicesIds.first)..add(CurtainFetchBatchStatus(devicesIds)),
child: BlocBuilder<CurtainBloc, CurtainState>( child: BlocBuilder<CurtainBloc, CurtainState>(
builder: (context, state) { builder: (context, state) {
if (state is CurtainStatusLoading) { if (state is CurtainStatusLoading) {

View File

@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/common/curtain_toggle.dart';
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_bloc.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_bloc.dart';
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart';
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart';
import 'package:syncrow_web/pages/device_managment/curtain/factories/curtain_bloc_factory.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 CurtainStatusControlsView extends StatelessWidget class CurtainStatusControlsView extends StatelessWidget
@ -16,7 +15,7 @@ class CurtainStatusControlsView extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => CurtainBlocFactory.create(deviceId: deviceId) create: (context) => CurtainBloc(deviceId: deviceId)
..add(CurtainFetchDeviceStatus(deviceId)), ..add(CurtainFetchDeviceStatus(deviceId)),
child: BlocBuilder<CurtainBloc, CurtainState>( child: BlocBuilder<CurtainBloc, CurtainState>(
builder: (context, state) { builder: (context, state) {

View File

@ -1,18 +0,0 @@
import 'package:syncrow_web/services/batch_control_devices_service.dart';
import 'package:syncrow_web/services/control_device_service.dart';
abstract final class DeviceBlocDependenciesFactory {
const DeviceBlocDependenciesFactory._();
static ControlDeviceService createControlDeviceService() {
return DebouncedControlDeviceService(
decoratee: RemoteControlDeviceService(),
);
}
static BatchControlDevicesService createBatchControlDevicesService() {
return DebouncedBatchControlDevicesService(
decoratee: RemoteBatchControlDevicesService(),
);
}
}

View File

@ -1,5 +1,6 @@
import 'package:syncrow_web/pages/device_managment/factories/device_bloc_dependencies_factory.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart'; import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart';
import 'package:syncrow_web/services/batch_control_devices_service.dart';
import 'package:syncrow_web/services/control_device_service.dart';
abstract final class FlushMountedPresenceSensorBlocFactory { abstract final class FlushMountedPresenceSensorBlocFactory {
const FlushMountedPresenceSensorBlocFactory._(); const FlushMountedPresenceSensorBlocFactory._();
@ -9,8 +10,12 @@ abstract final class FlushMountedPresenceSensorBlocFactory {
}) { }) {
return FlushMountedPresenceSensorBloc( return FlushMountedPresenceSensorBloc(
deviceId: deviceId, deviceId: deviceId,
controlDeviceService: DeviceBlocDependenciesFactory.createControlDeviceService(), controlDeviceService: DebouncedControlDeviceService(
batchControlDevicesService: DeviceBlocDependenciesFactory.createBatchControlDevicesService(), decoratee: RemoteControlDeviceService(),
),
batchControlDevicesService: DebouncedBatchControlDevicesService(
decoratee: RemoteBatchControlDevicesService(),
),
); );
} }
} }

View File

@ -1,13 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/foundation.dart'; import 'package:meta/meta.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_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';
part 'one_gang_glass_switch_event.dart'; part 'one_gang_glass_switch_event.dart';
@ -15,16 +13,13 @@ part 'one_gang_glass_switch_state.dart';
class OneGangGlassSwitchBloc class OneGangGlassSwitchBloc
extends Bloc<OneGangGlassSwitchEvent, OneGangGlassSwitchState> { extends Bloc<OneGangGlassSwitchEvent, OneGangGlassSwitchState> {
late OneGangGlassStatusModel deviceStatus; OneGangGlassStatusModel deviceStatus;
final String deviceId; Timer? _timer;
final ControlDeviceService controlDeviceService;
final BatchControlDevicesService batchControlDevicesService;
OneGangGlassSwitchBloc({ OneGangGlassSwitchBloc({required String deviceId})
required this.deviceId, : deviceStatus = OneGangGlassStatusModel(
required this.controlDeviceService, uuid: deviceId, switch1: false, countDown: 0),
required this.batchControlDevicesService, super(OneGangGlassSwitchInitial()) {
}) : super(OneGangGlassSwitchInitial()) {
on<OneGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus); on<OneGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<OneGangGlassSwitchControl>(_onControl); on<OneGangGlassSwitchControl>(_onControl);
on<OneGangGlassSwitchBatchControl>(_onBatchControl); on<OneGangGlassSwitchBatchControl>(_onBatchControl);
@ -33,140 +28,160 @@ class OneGangGlassSwitchBloc
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( Future<void> _onFetchDeviceStatus(OneGangGlassSwitchFetchDeviceEvent event,
OneGangGlassSwitchFetchDeviceEvent event, Emitter<OneGangGlassSwitchState> emit) async {
Emitter<OneGangGlassSwitchState> emit,
) async {
emit(OneGangGlassSwitchLoading()); emit(OneGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
_listenToChanges(event.deviceId, emit); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceId, status.status); _listenToChanges(event.deviceId);
deviceStatus =
OneGangGlassStatusModel.fromJson(event.deviceId, status.status);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(OneGangGlassSwitchError(e.toString())); emit(OneGangGlassSwitchError(e.toString()));
} }
} }
void _listenToChanges( _listenToChanges(deviceId) {
String deviceId,
Emitter<OneGangGlassSwitchState> emit,
) {
try { try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); DatabaseReference ref =
final stream = ref.onValue; FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) { stream.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?; Map<dynamic, dynamic> usersMap =
if (data == null) return; event.snapshot.value as Map<dynamic, dynamic>;
final statusList = <Status>[]; List<Status> statusList = [];
if (data['status'] != null) { usersMap['status'].forEach((element) {
for (var element in data['status']) { statusList
statusList.add( .add(Status(code: element['code'], value: element['value']));
Status( });
code: element['code'].toString(),
value: element['value'].toString(), deviceStatus = OneGangGlassStatusModel.fromJson(
), usersMap['productUuid'], statusList);
);
}
}
if (statusList.isNotEmpty) {
final newStatus = OneGangGlassStatusModel.fromJson(deviceId, statusList);
if (newStatus != deviceStatus) {
deviceStatus = newStatus;
if (!isClosed) { if (!isClosed) {
add(StatusUpdated(deviceStatus)); add(StatusUpdated(deviceStatus));
} }
}
}
}); });
} catch (e) { } catch (_) {}
emit(OneGangGlassSwitchError('Failed to listen to changes: $e'));
}
} }
void _onStatusUpdated( void _onStatusUpdated(
StatusUpdated event, StatusUpdated event, Emitter<OneGangGlassSwitchState> emit) {
Emitter<OneGangGlassSwitchState> emit,
) {
emit(OneGangGlassSwitchLoading());
deviceStatus = event.deviceStatus; deviceStatus = event.deviceStatus;
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} }
Future<void> _onControl( Future<void> _onControl(OneGangGlassSwitchControl event,
OneGangGlassSwitchControl event, Emitter<OneGangGlassSwitchState> emit) async {
Emitter<OneGangGlassSwitchState> emit, final oldValue = _getValueByCode(event.code);
) async {
emit(OneGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
try { await _runDebounce(
await controlDeviceService.controlDevice( deviceId: event.deviceId,
deviceUuid: event.deviceId,
status: Status(code: event.code, value: event.value),
);
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(OneGangGlassSwitchError(e.toString()));
}
}
Future<void> _onBatchControl(
OneGangGlassSwitchBatchControl event,
Emitter<OneGangGlassSwitchState> emit,
) async {
emit(OneGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
try {
await batchControlDevicesService.batchControlDevices(
uuids: event.deviceIds,
code: event.code, code: event.code,
value: event.value, value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: false,
); );
}
Future<void> _onFactoryReset(OneGangGlassFactoryResetEvent event,
Emitter<OneGangGlassSwitchState> emit) async {
emit(OneGangGlassSwitchLoading());
try {
final response = await DevicesManagementApi()
.factoryReset(event.factoryReset, event.deviceId);
if (!response) {
emit(OneGangGlassSwitchError('Failed to reset device'));
} else {
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
}
} catch (e) { } catch (e) {
_updateLocalValue(event.code, !event.value);
emit(OneGangGlassSwitchError(e.toString())); emit(OneGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _onBatchControl(OneGangGlassSwitchBatchControl event,
Emitter<OneGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
await _runDebounce(
deviceId: event.deviceIds,
code: event.code,
value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: true,
);
}
Future<void> _onFetchBatchStatus( Future<void> _onFetchBatchStatus(
OneGangGlassSwitchFetchBatchStatusEvent event, OneGangGlassSwitchFetchBatchStatusEvent event,
Emitter<OneGangGlassSwitchState> emit, Emitter<OneGangGlassSwitchState> emit) async {
) async {
emit(OneGangGlassSwitchLoading()); emit(OneGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); final status =
deviceStatus = await DevicesManagementApi().getBatchStatus(event.deviceIds);
OneGangGlassStatusModel.fromJson(event.deviceIds.first, status.status); deviceStatus = OneGangGlassStatusModel.fromJson(
event.deviceIds.first, status.status);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(OneGangGlassSwitchError(e.toString())); emit(OneGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _onFactoryReset( Future<void> _runDebounce({
OneGangGlassFactoryResetEvent event, required dynamic deviceId,
Emitter<OneGangGlassSwitchState> emit, required String code,
) async { required bool value,
emit(OneGangGlassSwitchLoading()); required bool oldValue,
try { required Emitter<OneGangGlassSwitchState> emit,
final response = await DevicesManagementApi().factoryReset( required bool isBatch,
event.factoryReset, }) async {
event.deviceId, late String id;
); if (deviceId is List) {
if (!response) { id = deviceId.first;
emit(OneGangGlassSwitchError('Failed to reset device'));
} else { } else {
add(OneGangGlassSwitchFetchDeviceEvent(event.deviceId)); id = deviceId;
}
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(const Duration(milliseconds: 500), () 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) { } catch (e) {
emit(OneGangGlassSwitchError(e.toString())); _revertValueAndEmit(id, code, oldValue, emit);
} }
});
}
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
Emitter<OneGangGlassSwitchState> emit) {
_updateLocalValue(code, oldValue);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} }
void _updateLocalValue(String code, bool value) { void _updateLocalValue(String code, bool value) {
@ -174,4 +189,19 @@ class OneGangGlassSwitchBloc
deviceStatus = deviceStatus.copyWith(switch1: value); deviceStatus = deviceStatus.copyWith(switch1: value);
} }
} }
bool _getValueByCode(String code) {
switch (code) {
case 'switch_1':
return deviceStatus.switch1;
default:
return false;
}
}
@override
Future<void> close() {
_timer?.cancel();
return super.close();
}
} }

View File

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

View File

@ -2,9 +2,9 @@ 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/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/factories/one_gang_glass_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.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/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.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';
@ -16,7 +16,7 @@ class OneGangGlassSwitchBatchControlView extends StatelessWidget with HelperResp
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => OneGangGlassSwitchBlocFactory.create(deviceId: deviceIds.first) create: (context) => OneGangGlassSwitchBloc(deviceId: deviceIds.first)
..add(OneGangGlassSwitchFetchBatchStatusEvent(deviceIds)), ..add(OneGangGlassSwitchFetchBatchStatusEvent(deviceIds)),
child: BlocBuilder<OneGangGlassSwitchBloc, OneGangGlassSwitchState>( child: BlocBuilder<OneGangGlassSwitchBloc, OneGangGlassSwitchState>(
builder: (context, state) { builder: (context, state) {

View File

@ -1,7 +1,6 @@
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/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/factories/one_gang_glass_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.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/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
@ -10,13 +9,13 @@ import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_la
class OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout { class OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout {
final String deviceId; final String deviceId;
const OneGangGlassSwitchControlView({required this.deviceId, super.key}); const OneGangGlassSwitchControlView({required this.deviceId, Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) =>
OneGangGlassSwitchBlocFactory.create(deviceId: deviceId)..add(OneGangGlassSwitchFetchDeviceEvent(deviceId)), OneGangGlassSwitchBloc(deviceId: deviceId)..add(OneGangGlassSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<OneGangGlassSwitchBloc, OneGangGlassSwitchState>( child: BlocBuilder<OneGangGlassSwitchBloc, OneGangGlassSwitchState>(
builder: (context, state) { builder: (context, state) {
if (state is OneGangGlassSwitchLoading) { if (state is OneGangGlassSwitchLoading) {

View File

@ -6,21 +6,12 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_sta
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_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';
class WallLightSwitchBloc extends Bloc<WallLightSwitchEvent, WallLightSwitchState> { class WallLightSwitchBloc
late WallLightStatusModel deviceStatus; extends Bloc<WallLightSwitchEvent, WallLightSwitchState> {
final String deviceId; WallLightSwitchBloc({required this.deviceId})
final ControlDeviceService controlDeviceService; : super(WallLightSwitchInitial()) {
final BatchControlDevicesService batchControlDevicesService;
WallLightSwitchBloc({
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(WallLightSwitchInitial()) {
on<WallLightSwitchFetchDeviceEvent>(_onFetchDeviceStatus); on<WallLightSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<WallLightSwitchControl>(_onControl); on<WallLightSwitchControl>(_onControl);
on<WallLightSwitchFetchBatchEvent>(_onFetchBatchStatus); on<WallLightSwitchFetchBatchEvent>(_onFetchBatchStatus);
@ -29,114 +20,143 @@ class WallLightSwitchBloc extends Bloc<WallLightSwitchEvent, WallLightSwitchStat
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( late WallLightStatusModel deviceStatus;
WallLightSwitchFetchDeviceEvent event, final String deviceId;
Emitter<WallLightSwitchState> emit, Timer? _timer;
) async {
FutureOr<void> _onFetchDeviceStatus(WallLightSwitchFetchDeviceEvent event,
Emitter<WallLightSwitchState> emit) async {
emit(WallLightSwitchLoading()); emit(WallLightSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
_listenToChanges(event.deviceId, emit); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = WallLightStatusModel.fromJson(event.deviceId, status.status);
deviceStatus =
WallLightStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(WallLightSwitchStatusLoaded(deviceStatus)); emit(WallLightSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(WallLightSwitchError(e.toString())); emit(WallLightSwitchError(e.toString()));
} }
} }
void _listenToChanges( _listenToChanges(deviceId) {
String deviceId,
Emitter<WallLightSwitchState> emit,
) {
try { try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); DatabaseReference ref =
final stream = ref.onValue; FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) { stream.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?; Map<dynamic, dynamic> usersMap =
if (data == null) return; event.snapshot.value as Map<dynamic, dynamic>;
final statusList = <Status>[]; List<Status> statusList = [];
if (data['status'] != null) { usersMap['status'].forEach((element) {
for (var element in data['status']) { statusList
statusList.add( .add(Status(code: element['code'], value: element['value']));
Status( });
code: element['code'].toString(),
value: element['value'].toString(), deviceStatus =
), WallLightStatusModel.fromJson(usersMap['productUuid'], statusList);
);
}
}
if (statusList.isNotEmpty) {
final newStatus = WallLightStatusModel.fromJson(deviceId, statusList);
if (newStatus != deviceStatus) {
deviceStatus = newStatus;
if (!isClosed) { if (!isClosed) {
add(StatusUpdated(deviceStatus)); add(StatusUpdated(deviceStatus));
} }
}
}
}); });
} catch (e) { } catch (_) {}
emit(WallLightSwitchError('Failed to listen to changes: $e'));
}
} }
void _onStatusUpdated( void _onStatusUpdated(
StatusUpdated event, StatusUpdated event, Emitter<WallLightSwitchState> emit) {
Emitter<WallLightSwitchState> emit,
) {
emit(WallLightSwitchLoading());
deviceStatus = event.deviceStatus; deviceStatus = event.deviceStatus;
emit(WallLightSwitchStatusLoaded(deviceStatus)); emit(WallLightSwitchStatusLoaded(deviceStatus));
} }
Future<void> _onControl( FutureOr<void> _onControl(
WallLightSwitchControl event, WallLightSwitchControl event, Emitter<WallLightSwitchState> emit) async {
Emitter<WallLightSwitchState> emit, final oldValue = _getValueByCode(event.code);
) async {
emit(WallLightSwitchLoading());
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(WallLightSwitchStatusLoaded(deviceStatus)); emit(WallLightSwitchStatusLoaded(deviceStatus));
try { await _runDebounce(
await controlDeviceService.controlDevice( deviceId: event.deviceId,
deviceUuid: event.deviceId,
status: Status(code: event.code, value: event.value),
);
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(WallLightSwitchError(e.toString()));
}
}
Future<void> _onBatchControl(
WallLightSwitchBatchControl event,
Emitter<WallLightSwitchState> emit,
) async {
emit(WallLightSwitchLoading());
_updateLocalValue(event.code, event.value);
emit(WallLightSwitchStatusLoaded(deviceStatus));
try {
await batchControlDevicesService.batchControlDevices(
uuids: event.devicesIds,
code: event.code, code: event.code,
value: event.value, value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: false,
); );
}
Future<void> _runDebounce({
required dynamic deviceId,
required String code,
required bool value,
required bool oldValue,
required Emitter<WallLightSwitchState> 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(milliseconds: 500), () 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) { } catch (e) {
_updateLocalValue(event.code, !event.value); _revertValueAndEmit(id, code, oldValue, emit);
emit(WallLightSwitchError(e.toString())); }
});
}
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
Emitter<WallLightSwitchState> emit) {
_updateLocalValue(code, oldValue);
emit(WallLightSwitchStatusLoaded(deviceStatus));
}
void _updateLocalValue(String code, bool value) {
if (code == 'switch_1') {
deviceStatus = deviceStatus.copyWith(switch1: value);
} }
} }
Future<void> _onFetchBatchStatus( bool _getValueByCode(String code) {
WallLightSwitchFetchBatchEvent event, switch (code) {
Emitter<WallLightSwitchState> emit, case 'switch_1':
) async { return deviceStatus.switch1;
default:
return false;
}
}
Future<void> _onFetchBatchStatus(WallLightSwitchFetchBatchEvent event,
Emitter<WallLightSwitchState> emit) async {
emit(WallLightSwitchLoading()); emit(WallLightSwitchLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.devicesIds); final status =
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = deviceStatus =
WallLightStatusModel.fromJson(event.devicesIds.first, status.status); WallLightStatusModel.fromJson(event.devicesIds.first, status.status);
emit(WallLightSwitchStatusLoaded(deviceStatus)); emit(WallLightSwitchStatusLoaded(deviceStatus));
@ -145,10 +165,32 @@ class WallLightSwitchBloc extends Bloc<WallLightSwitchEvent, WallLightSwitchStat
} }
} }
Future<void> _onFactoryReset( @override
WallLightFactoryReset event, Future<void> close() {
Emitter<WallLightSwitchState> emit, _timer?.cancel();
) async { return super.close();
}
FutureOr<void> _onBatchControl(WallLightSwitchBatchControl event,
Emitter<WallLightSwitchState> emit) async {
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value);
emit(WallLightSwitchStatusLoaded(deviceStatus));
await _runDebounce(
deviceId: event.devicesIds,
code: event.code,
value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: true,
);
}
FutureOr<void> _onFactoryReset(
WallLightFactoryReset event, Emitter<WallLightSwitchState> emit) async {
emit(WallLightSwitchLoading()); emit(WallLightSwitchLoading());
try { try {
final response = await DevicesManagementApi().factoryReset( final response = await DevicesManagementApi().factoryReset(
@ -156,18 +198,12 @@ class WallLightSwitchBloc extends Bloc<WallLightSwitchEvent, WallLightSwitchStat
event.deviceId, event.deviceId,
); );
if (!response) { if (!response) {
emit(WallLightSwitchError('Failed to reset device')); emit(WallLightSwitchError('Failed'));
} else { } else {
add(WallLightSwitchFetchDeviceEvent(event.deviceId)); emit(WallLightSwitchStatusLoaded(deviceStatus));
} }
} catch (e) { } catch (e) {
emit(WallLightSwitchError(e.toString())); emit(WallLightSwitchError(e.toString()));
} }
} }
void _updateLocalValue(String code, bool value) {
if (code == 'switch_1') {
deviceStatus = deviceStatus.copyWith(switch1: value);
}
}
} }

View File

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

View File

@ -4,9 +4,9 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_re
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/factories/wall_light_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.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/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.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';
@ -18,7 +18,7 @@ class WallLightBatchControlView extends StatelessWidget with HelperResponsiveLay
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => WallLightSwitchBlocFactory.create(deviceId: deviceIds.first) create: (context) => WallLightSwitchBloc(deviceId: deviceIds.first)
..add(WallLightSwitchFetchBatchEvent(deviceIds)), ..add(WallLightSwitchFetchBatchEvent(deviceIds)),
child: BlocBuilder<WallLightSwitchBloc, WallLightSwitchState>( child: BlocBuilder<WallLightSwitchBloc, WallLightSwitchState>(
builder: (context, state) { builder: (context, state) {

View File

@ -3,7 +3,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/factories/wall_light_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -16,7 +15,7 @@ class WallLightDeviceControl extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => WallLightSwitchBlocFactory.create(deviceId: deviceId) create: (context) => WallLightSwitchBloc(deviceId: deviceId)
..add(WallLightSwitchFetchDeviceEvent(deviceId)), ..add(WallLightSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<WallLightSwitchBloc, WallLightSwitchState>( child: BlocBuilder<WallLightSwitchBloc, WallLightSwitchState>(
builder: (context, state) { builder: (context, state) {

View File

@ -157,7 +157,7 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
), ),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
SelectableText( Text(
value, value,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,

View File

@ -1,14 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:equatable/equatable.dart'; import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/foundation.dart'; import 'package:meta/meta.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/models/three_gang_glass_switch.dart'; import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/models/three_gang_glass_switch.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';
part 'three_gang_glass_switch_event.dart'; part 'three_gang_glass_switch_event.dart';
@ -16,16 +13,19 @@ part 'three_gang_glass_switch_state.dart';
class ThreeGangGlassSwitchBloc class ThreeGangGlassSwitchBloc
extends Bloc<ThreeGangGlassSwitchEvent, ThreeGangGlassSwitchState> { extends Bloc<ThreeGangGlassSwitchEvent, ThreeGangGlassSwitchState> {
late ThreeGangGlassStatusModel deviceStatus; ThreeGangGlassStatusModel deviceStatus;
final String deviceId; Timer? _timer;
final ControlDeviceService controlDeviceService;
final BatchControlDevicesService batchControlDevicesService;
ThreeGangGlassSwitchBloc({ ThreeGangGlassSwitchBloc({required String deviceId})
required this.deviceId, : deviceStatus = ThreeGangGlassStatusModel(
required this.controlDeviceService, uuid: deviceId,
required this.batchControlDevicesService, switch1: false,
}) : super(ThreeGangGlassSwitchInitial()) { countDown1: 0,
switch2: false,
countDown2: 0,
switch3: false,
countDown3: 0),
super(ThreeGangGlassSwitchInitial()) {
on<ThreeGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus); on<ThreeGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<ThreeGangGlassSwitchControl>(_onControl); on<ThreeGangGlassSwitchControl>(_onControl);
on<ThreeGangGlassSwitchBatchControl>(_onBatchControl); on<ThreeGangGlassSwitchBatchControl>(_onBatchControl);
@ -34,154 +34,188 @@ class ThreeGangGlassSwitchBloc
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( Future<void> _onFetchDeviceStatus(ThreeGangGlassSwitchFetchDeviceEvent event,
ThreeGangGlassSwitchFetchDeviceEvent event, Emitter<ThreeGangGlassSwitchState> emit) async {
Emitter<ThreeGangGlassSwitchState> emit,
) async {
emit(ThreeGangGlassSwitchLoading()); emit(ThreeGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
_listenToChanges(event.deviceId, emit); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = deviceStatus =
ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status); ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(ThreeGangGlassSwitchError(e.toString())); emit(ThreeGangGlassSwitchError(e.toString()));
} }
} }
void _listenToChanges( _listenToChanges(deviceId) {
String deviceId,
Emitter<ThreeGangGlassSwitchState> emit,
) {
try { try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); DatabaseReference ref =
final stream = ref.onValue; FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) { stream.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?; Map<dynamic, dynamic> usersMap =
if (data == null) return; event.snapshot.value as Map<dynamic, dynamic>;
final statusList = <Status>[]; List<Status> statusList = [];
if (data['status'] != null) { usersMap['status'].forEach((element) {
for (var element in data['status']) { statusList
statusList.add( .add(Status(code: element['code'], value: element['value']));
Status( });
code: element['code'].toString(),
value: element['value'].toString(), deviceStatus = ThreeGangGlassStatusModel.fromJson(
), usersMap['productUuid'], statusList);
);
}
}
if (statusList.isNotEmpty) {
final newStatus = ThreeGangGlassStatusModel.fromJson(deviceId, statusList);
if (newStatus != deviceStatus) {
deviceStatus = newStatus;
if (!isClosed) { if (!isClosed) {
add(StatusUpdated(deviceStatus)); add(StatusUpdated(deviceStatus));
} }
}
}
}); });
} catch (e) { } catch (_) {}
emit(ThreeGangGlassSwitchError('Failed to listen to changes: $e'));
}
} }
void _onStatusUpdated( void _onStatusUpdated(
StatusUpdated event, StatusUpdated event, Emitter<ThreeGangGlassSwitchState> emit) {
Emitter<ThreeGangGlassSwitchState> emit,
) {
emit(ThreeGangGlassSwitchLoading());
deviceStatus = event.deviceStatus; deviceStatus = event.deviceStatus;
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
} }
Future<void> _onControl( Future<void> _onControl(ThreeGangGlassSwitchControl event,
ThreeGangGlassSwitchControl event, Emitter<ThreeGangGlassSwitchState> emit) async {
Emitter<ThreeGangGlassSwitchState> emit, final oldValue = _getValueByCode(event.code);
) async {
emit(ThreeGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
try { await _runDebounce(
await controlDeviceService.controlDevice( deviceId: event.deviceId,
deviceUuid: event.deviceId, code: event.code,
status: Status(code: event.code, value: event.value), value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: false,
); );
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(ThreeGangGlassSwitchError(e.toString()));
}
} }
Future<void> _onBatchControl( Future<void> _onBatchControl(ThreeGangGlassSwitchBatchControl event,
ThreeGangGlassSwitchBatchControl event, Emitter<ThreeGangGlassSwitchState> emit) async {
Emitter<ThreeGangGlassSwitchState> emit, final oldValue = _getValueByCode(event.code);
) async {
emit(ThreeGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus));
try { await _runDebounce(
await batchControlDevicesService.batchControlDevices( deviceId: event.deviceIds,
uuids: event.deviceIds,
code: event.code, code: event.code,
value: event.value, value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: true,
); );
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(ThreeGangGlassSwitchError(e.toString()));
}
} }
Future<void> _onFetchBatchStatus( Future<void> _onFetchBatchStatus(
ThreeGangGlassSwitchFetchBatchStatusEvent event, ThreeGangGlassSwitchFetchBatchStatusEvent event,
Emitter<ThreeGangGlassSwitchState> emit, Emitter<ThreeGangGlassSwitchState> emit) async {
) async {
emit(ThreeGangGlassSwitchLoading()); emit(ThreeGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); final status =
deviceStatus = await DevicesManagementApi().getBatchStatus(event.deviceIds);
ThreeGangGlassStatusModel.fromJson(event.deviceIds.first, status.status); deviceStatus = ThreeGangGlassStatusModel.fromJson(
event.deviceIds.first, status.status);
emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(ThreeGangGlassSwitchError(e.toString())); emit(ThreeGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _onFactoryReset( Future<void> _onFactoryReset(ThreeGangGlassFactoryReset event,
ThreeGangGlassFactoryReset event, Emitter<ThreeGangGlassSwitchState> emit) async {
Emitter<ThreeGangGlassSwitchState> emit,
) async {
emit(ThreeGangGlassSwitchLoading()); emit(ThreeGangGlassSwitchLoading());
try { try {
final response = await DevicesManagementApi().factoryReset( final response = await DevicesManagementApi()
event.factoryReset, .factoryReset(event.factoryReset, event.deviceId);
event.deviceId,
);
if (!response) { if (!response) {
emit(ThreeGangGlassSwitchError('Failed to reset device')); emit(ThreeGangGlassSwitchError('Failed'));
} else { } else {
add(ThreeGangGlassSwitchFetchDeviceEvent(event.deviceId)); emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
} }
} catch (e) { } catch (e) {
emit(ThreeGangGlassSwitchError(e.toString())); emit(ThreeGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _runDebounce({
required dynamic deviceId,
required String code,
required bool value,
required bool oldValue,
required Emitter<ThreeGangGlassSwitchState> 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(milliseconds: 500), () 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) {
_revertValueAndEmit(id, code, oldValue, emit);
}
});
}
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
Emitter<ThreeGangGlassSwitchState> emit) {
_updateLocalValue(code, oldValue);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
}
void _updateLocalValue(String code, bool value) { void _updateLocalValue(String code, bool value) {
if (code == 'switch_1') {
deviceStatus = deviceStatus.copyWith(switch1: value);
} else if (code == 'switch_2') {
deviceStatus = deviceStatus.copyWith(switch2: value);
} else if (code == 'switch_3') {
deviceStatus = deviceStatus.copyWith(switch3: value);
}
}
bool _getValueByCode(String code) {
switch (code) { switch (code) {
case 'switch_1': case 'switch_1':
deviceStatus = deviceStatus.copyWith(switch1: value); return deviceStatus.switch1;
break;
case 'switch_2': case 'switch_2':
deviceStatus = deviceStatus.copyWith(switch2: value); return deviceStatus.switch2;
break;
case 'switch_3': case 'switch_3':
deviceStatus = deviceStatus.copyWith(switch3: value); return deviceStatus.switch3;
break; default:
return false;
} }
} }
@override
Future<void> close() {
_timer?.cancel();
return super.close();
}
} }

View File

@ -1,10 +1,7 @@
part of 'three_gang_glass_switch_bloc.dart'; part of 'three_gang_glass_switch_bloc.dart';
@immutable @immutable
abstract class ThreeGangGlassSwitchEvent extends Equatable { abstract class ThreeGangGlassSwitchEvent {}
@override
List<Object?> get props => [];
}
class ThreeGangGlassSwitchFetchDeviceEvent extends ThreeGangGlassSwitchEvent { class ThreeGangGlassSwitchFetchDeviceEvent extends ThreeGangGlassSwitchEvent {
final String deviceId; final String deviceId;
@ -22,9 +19,6 @@ class ThreeGangGlassSwitchControl extends ThreeGangGlassSwitchEvent {
required this.code, required this.code,
required this.value, required this.value,
}); });
@override
List<Object?> get props => [deviceId, code, value];
} }
class ThreeGangGlassSwitchBatchControl extends ThreeGangGlassSwitchEvent { class ThreeGangGlassSwitchBatchControl extends ThreeGangGlassSwitchEvent {
@ -37,9 +31,6 @@ class ThreeGangGlassSwitchBatchControl extends ThreeGangGlassSwitchEvent {
required this.code, required this.code,
required this.value, required this.value,
}); });
@override
List<Object?> get props => [deviceIds, code, value];
} }
class ThreeGangGlassSwitchFetchBatchStatusEvent class ThreeGangGlassSwitchFetchBatchStatusEvent
@ -47,9 +38,6 @@ class ThreeGangGlassSwitchFetchBatchStatusEvent
final List<String> deviceIds; final List<String> deviceIds;
ThreeGangGlassSwitchFetchBatchStatusEvent(this.deviceIds); ThreeGangGlassSwitchFetchBatchStatusEvent(this.deviceIds);
@override
List<Object?> get props => [deviceIds];
} }
class ThreeGangGlassFactoryReset extends ThreeGangGlassSwitchEvent { class ThreeGangGlassFactoryReset extends ThreeGangGlassSwitchEvent {
@ -60,9 +48,6 @@ class ThreeGangGlassFactoryReset extends ThreeGangGlassSwitchEvent {
required this.deviceId, required this.deviceId,
required this.factoryReset, required this.factoryReset,
}); });
@override
List<Object?> get props => [deviceId, factoryReset];
} }
class StatusUpdated extends ThreeGangGlassSwitchEvent { class StatusUpdated extends ThreeGangGlassSwitchEvent {

View File

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

View File

@ -5,7 +5,6 @@ import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_
// 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/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/factories/three_gang_glass_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/models/three_gang_glass_switch.dart'; import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/models/three_gang_glass_switch.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';
@ -17,7 +16,7 @@ class ThreeGangGlassSwitchBatchControlView extends StatelessWidget with HelperRe
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => ThreeGangGlassSwitchBlocFactory.create(deviceId: deviceIds.first) create: (context) => ThreeGangGlassSwitchBloc(deviceId: deviceIds.first)
..add(ThreeGangGlassSwitchFetchBatchStatusEvent(deviceIds)), ..add(ThreeGangGlassSwitchFetchBatchStatusEvent(deviceIds)),
child: BlocBuilder<ThreeGangGlassSwitchBloc, ThreeGangGlassSwitchState>( child: BlocBuilder<ThreeGangGlassSwitchBloc, ThreeGangGlassSwitchState>(
builder: (context, state) { builder: (context, state) {

View File

@ -2,7 +2,6 @@ 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/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/factories/three_gang_glass_switch_bloc_factory.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';
@ -17,7 +16,7 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget with HelperRespons
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) =>
ThreeGangGlassSwitchBlocFactory.create(deviceId: deviceId)..add(ThreeGangGlassSwitchFetchDeviceEvent(deviceId)), ThreeGangGlassSwitchBloc(deviceId: deviceId)..add(ThreeGangGlassSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<ThreeGangGlassSwitchBloc, ThreeGangGlassSwitchState>( child: BlocBuilder<ThreeGangGlassSwitchBloc, ThreeGangGlassSwitchState>(
builder: (context, state) { builder: (context, state) {
if (state is ThreeGangGlassSwitchLoading) { if (state is ThreeGangGlassSwitchLoading) {

View File

@ -1,14 +1,12 @@
import 'dart:async'; // ignore_for_file: invalid_use_of_visible_for_testing_member
import 'dart:developer';
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_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';
part 'living_room_event.dart'; part 'living_room_event.dart';
@ -17,14 +15,9 @@ part 'living_room_state.dart';
class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> { class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
late LivingRoomStatusModel deviceStatus; late LivingRoomStatusModel deviceStatus;
final String deviceId; final String deviceId;
final ControlDeviceService controlDeviceService; Timer? _timer;
final BatchControlDevicesService batchControlDevicesService;
LivingRoomBloc({ LivingRoomBloc({required this.deviceId}) : super(LivingRoomInitial()) {
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(LivingRoomInitial()) {
on<LivingRoomFetchDeviceStatusEvent>(_onFetchDeviceStatus); on<LivingRoomFetchDeviceStatusEvent>(_onFetchDeviceStatus);
on<LivingRoomControl>(_livingRoomControl); on<LivingRoomControl>(_livingRoomControl);
on<LivingRoomBatchControl>(_livingRoomBatchControl); on<LivingRoomBatchControl>(_livingRoomBatchControl);
@ -33,108 +26,156 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( FutureOr<void> _onFetchDeviceStatus(LivingRoomFetchDeviceStatusEvent event,
LivingRoomFetchDeviceStatusEvent event, Emitter<LivingRoomState> emit) async {
Emitter<LivingRoomState> emit,
) async {
emit(LivingRoomDeviceStatusLoading()); emit(LivingRoomDeviceStatusLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
LivingRoomStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(deviceId); _listenToChanges(deviceId);
deviceStatus = LivingRoomStatusModel.fromJson(event.deviceId, status.status);
emit(LivingRoomDeviceStatusLoaded(deviceStatus)); emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(LivingRoomDeviceManagementError(e.toString())); emit(LivingRoomDeviceManagementError(e.toString()));
} }
} }
void _listenToChanges(String deviceId) { FutureOr<void> _livingRoomControl(
try { LivingRoomControl event, Emitter<LivingRoomState> emit) async {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); final oldValue = _getValueByCode(event.code);
ref.onValue.listen((event) {
final eventsMap = event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
eventsMap['status'].forEach((element) {
statusList.add(
Status(code: element['code'], value: element['value']),
);
});
deviceStatus = LivingRoomStatusModel.fromJson(deviceId, statusList);
add(StatusUpdated(deviceStatus));
});
} catch (_) {
log('Error listening to changes');
}
}
void _onStatusUpdated(
StatusUpdated event,
Emitter<LivingRoomState> emit,
) {
deviceStatus = event.deviceStatus;
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
}
Future<void> _livingRoomControl(
LivingRoomControl event,
Emitter<LivingRoomState> emit,
) async {
emit(LivingRoomDeviceStatusLoading());
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(LivingRoomDeviceStatusLoaded(deviceStatus)); emit(LivingRoomDeviceStatusLoaded(deviceStatus));
try { await _runDebounce(
await controlDeviceService.controlDevice( deviceId: event.deviceId,
deviceUuid: event.deviceId,
status: Status(code: event.code, value: event.value),
);
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(LivingRoomDeviceManagementError(e.toString()));
}
}
Future<void> _livingRoomBatchControl(
LivingRoomBatchControl event,
Emitter<LivingRoomState> emit,
) async {
emit(LivingRoomDeviceStatusLoading());
_updateLocalValue(event.code, event.value);
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
try {
await batchControlDevicesService.batchControlDevices(
uuids: event.devicesIds,
code: event.code, code: event.code,
value: event.value, value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: false,
); );
}
Future<void> _runDebounce({
required dynamic deviceId,
required String code,
required dynamic value,
required dynamic oldValue,
required Emitter<LivingRoomState> 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) { } catch (e) {
_updateLocalValue(event.code, !event.value); _revertValueAndEmit(id, code, oldValue, emit);
emit(LivingRoomDeviceManagementError(e.toString())); }
});
}
void _revertValueAndEmit(String deviceId, String code, dynamic oldValue,
Emitter<LivingRoomState> emit) {
_updateLocalValue(code, oldValue);
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
}
void _updateLocalValue(String code, dynamic value) {
switch (code) {
case 'switch_1':
if (value is bool) {
deviceStatus = deviceStatus.copyWith(switch1: value);
}
break;
case 'switch_2':
if (value is bool) {
deviceStatus = deviceStatus.copyWith(switch2: value);
}
break;
case 'switch_3':
if (value is bool) {
deviceStatus = deviceStatus.copyWith(switch3: value);
}
break;
default:
break;
}
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
}
dynamic _getValueByCode(String code) {
switch (code) {
case 'switch_1':
return deviceStatus.switch1;
case 'switch_2':
return deviceStatus.switch2;
case 'switch_3':
return deviceStatus.switch3;
default:
return null;
} }
} }
Future<void> _livingRoomFetchBatchControl( FutureOr<void> _livingRoomFetchBatchControl(
LivingRoomFetchBatchEvent event, LivingRoomFetchBatchEvent event, Emitter<LivingRoomState> emit) async {
Emitter<LivingRoomState> emit,
) async {
emit(LivingRoomDeviceStatusLoading()); emit(LivingRoomDeviceStatusLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.devicesIds); final status =
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = deviceStatus =
LivingRoomStatusModel.fromJson(event.devicesIds.first, status.status); LivingRoomStatusModel.fromJson(event.devicesIds.first, status.status);
// for (var deviceId in event.devicesIds) {
// _listenToChanges(deviceId);
// }
emit(LivingRoomDeviceStatusLoaded(deviceStatus)); emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(LivingRoomDeviceManagementError(e.toString())); emit(LivingRoomDeviceManagementError(e.toString()));
} }
} }
Future<void> _livingRoomFactoryReset( FutureOr<void> _livingRoomBatchControl(
LivingRoomFactoryResetEvent event, LivingRoomBatchControl event, Emitter<LivingRoomState> emit) async {
Emitter<LivingRoomState> emit, final oldValue = _getValueByCode(event.code);
) async {
_updateLocalValue(event.code, event.value);
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
await _runDebounce(
deviceId: event.devicesIds,
code: event.code,
value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: true,
);
}
FutureOr<void> _livingRoomFactoryReset(
LivingRoomFactoryResetEvent event, Emitter<LivingRoomState> emit) async {
emit(LivingRoomDeviceStatusLoading()); emit(LivingRoomDeviceStatusLoading());
try { try {
final response = await DevicesManagementApi().factoryReset( final response = await DevicesManagementApi().factoryReset(
@ -142,28 +183,42 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
event.uuid, event.uuid,
); );
if (!response) { if (!response) {
emit(const LivingRoomDeviceManagementError('Failed to reset device')); emit(const LivingRoomDeviceManagementError('Failed'));
} else { } else {
add(LivingRoomFetchDeviceStatusEvent(event.uuid)); emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} }
} catch (e) { } catch (e) {
emit(LivingRoomDeviceManagementError(e.toString())); emit(LivingRoomDeviceManagementError(e.toString()));
} }
} }
void _updateLocalValue(String code, dynamic value) { _listenToChanges(deviceId) {
if (value is! bool) return; try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
switch (code) { stream.listen((DatabaseEvent event) {
case 'switch_1': Map<dynamic, dynamic> usersMap =
deviceStatus = deviceStatus.copyWith(switch1: value); event.snapshot.value as Map<dynamic, dynamic>;
break;
case 'switch_2': List<Status> statusList = [];
deviceStatus = deviceStatus.copyWith(switch2: value); usersMap['status'].forEach((element) {
break; statusList
case 'switch_3': .add(Status(code: element['code'], value: element['value']));
deviceStatus = deviceStatus.copyWith(switch3: value); });
break;
} deviceStatus =
LivingRoomStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<LivingRoomState> emit) {
deviceStatus = event.deviceStatus;
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} }
} }

View File

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

View File

@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_re
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/three_gang_switch/bloc/living_room_bloc.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart';
import 'package:syncrow_web/pages/device_managment/three_gang_switch/factories/living_room_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -18,7 +17,7 @@ class LivingRoomBatchControlsView extends StatelessWidget with HelperResponsiveL
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) =>
LivingRoomBlocFactory.create(deviceId: deviceIds.first)..add(LivingRoomFetchBatchEvent(deviceIds)), LivingRoomBloc(deviceId: deviceIds.first)..add(LivingRoomFetchBatchEvent(deviceIds)),
child: BlocBuilder<LivingRoomBloc, LivingRoomState>( child: BlocBuilder<LivingRoomBloc, LivingRoomState>(
builder: (context, state) { builder: (context, state) {
if (state is LivingRoomDeviceStatusLoading) { if (state is LivingRoomDeviceStatusLoading) {

View File

@ -1,7 +1,6 @@
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/three_gang_switch/bloc/living_room_bloc.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart';
import 'package:syncrow_web/pages/device_managment/three_gang_switch/factories/living_room_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -15,7 +14,7 @@ class LivingRoomDeviceControlsView extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => LivingRoomBlocFactory.create(deviceId: deviceId) create: (context) => LivingRoomBloc(deviceId: deviceId)
..add(LivingRoomFetchDeviceStatusEvent(deviceId)), ..add(LivingRoomFetchDeviceStatusEvent(deviceId)),
child: BlocBuilder<LivingRoomBloc, LivingRoomState>( child: BlocBuilder<LivingRoomBloc, LivingRoomState>(
builder: (context, state) { builder: (context, state) {

View File

@ -1,33 +1,26 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/foundation.dart'; import 'package:meta/meta.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_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';
part 'two_gang_glass_switch_event.dart'; part 'two_gang_glass_switch_event.dart';
part 'two_gang_glass_switch_state.dart'; part 'two_gang_glass_switch_state.dart';
class TwoGangGlassSwitchBloc class TwoGangGlassSwitchBloc
extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> { extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> {
final String deviceId; TwoGangGlassStatusModel deviceStatus;
final ControlDeviceService controlDeviceService; Timer? _timer;
final BatchControlDevicesService batchControlDevicesService; TwoGangGlassSwitchBloc({required String deviceId})
: deviceStatus = TwoGangGlassStatusModel(
late TwoGangGlassStatusModel deviceStatus; uuid: deviceId,
switch1: false,
TwoGangGlassSwitchBloc({ countDown1: 0,
required this.deviceId, switch2: false,
required this.controlDeviceService, countDown2: 0),
required this.batchControlDevicesService, super(TwoGangGlassSwitchInitial()) {
}) : super(TwoGangGlassSwitchInitial()) {
on<TwoGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus); on<TwoGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<TwoGangGlassSwitchControl>(_onControl); on<TwoGangGlassSwitchControl>(_onControl);
on<TwoGangGlassSwitchBatchControl>(_onBatchControl); on<TwoGangGlassSwitchBatchControl>(_onBatchControl);
@ -36,14 +29,14 @@ class TwoGangGlassSwitchBloc
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( Future<void> _onFetchDeviceStatus(TwoGangGlassSwitchFetchDeviceEvent event,
TwoGangGlassSwitchFetchDeviceEvent event, Emitter<TwoGangGlassSwitchState> emit) async {
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading()); emit(TwoGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
deviceStatus = TwoGangGlassStatusModel.fromJson(event.deviceId, status.status); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
TwoGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId); _listenToChanges(event.deviceId);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
@ -53,121 +46,200 @@ class TwoGangGlassSwitchBloc
void _listenToChanges(String deviceId) { void _listenToChanges(String deviceId) {
try { try {
final ref = FirebaseDatabase.instance.ref( DatabaseReference ref =
'device-status/$deviceId', FirebaseDatabase.instance.ref('device-status/$deviceId');
); ref.onValue.listen((DatabaseEvent event) {
if (event.snapshot.value == null) return;
ref.onValue.listen((event) {
final eventsMap = event.snapshot.value as Map<dynamic, dynamic>;
Map<dynamic, dynamic> data =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = []; List<Status> statusList = [];
eventsMap['status'].forEach((element) {
statusList.add(Status(code: element['code'], value: element['value'])); data['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
}); });
deviceStatus = TwoGangGlassStatusModel.fromJson(deviceId, statusList); // Parse the new status and add the event
add(StatusUpdated(deviceStatus)); final updatedStatus =
TwoGangGlassStatusModel.fromJson(data['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(updatedStatus));
}
}); });
} catch (_) { } catch (e) {
log( // Handle errors and emit an error state if necessary
'Error listening to changes', if (!isClosed) {
name: 'TwoGangGlassSwitchBloc._listenToChanges', // add(TwoGangGlassSwitchError('Error listening to updates: $e'));
); }
} }
} }
Future<void> _onControl( Future<void> _onControl(TwoGangGlassSwitchControl event,
TwoGangGlassSwitchControl event, Emitter<TwoGangGlassSwitchState> emit) async {
Emitter<TwoGangGlassSwitchState> emit, final oldValue = _getValueByCode(event.code);
) async {
emit(TwoGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
try { await _runDebounce(
await controlDeviceService.controlDevice( deviceId: event.deviceId,
deviceUuid: event.deviceId, code: event.code,
status: Status(code: event.code, value: event.value), value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: false,
); );
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(TwoGangGlassSwitchError(e.toString()));
}
} }
Future<void> _onBatchControl( Future<void> _onBatchControl(TwoGangGlassSwitchBatchControl event,
TwoGangGlassSwitchBatchControl event, Emitter<TwoGangGlassSwitchState> emit) async {
Emitter<TwoGangGlassSwitchState> emit, final oldValue = _getValueByCode(event.code);
) async {
emit(TwoGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus));
try { await _runDebounce(
await batchControlDevicesService.batchControlDevices( deviceId: event.deviceIds,
uuids: event.deviceIds,
code: event.code, code: event.code,
value: event.value, value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: true,
); );
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(TwoGangGlassSwitchError(e.toString()));
}
} }
Future<void> _onFetchBatchStatus( Future<void> _onFetchBatchStatus(
TwoGangGlassSwitchFetchBatchStatusEvent event, TwoGangGlassSwitchFetchBatchStatusEvent event,
Emitter<TwoGangGlassSwitchState> emit, Emitter<TwoGangGlassSwitchState> emit) async {
) async {
emit(TwoGangGlassSwitchLoading()); emit(TwoGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); final status =
await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = TwoGangGlassStatusModel.fromJson( deviceStatus = TwoGangGlassStatusModel.fromJson(
event.deviceIds.first, event.deviceIds.first, status.status);
status.status,
);
emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(TwoGangGlassSwitchError(e.toString())); emit(TwoGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _onFactoryReset( Future<void> _onFactoryReset(TwoGangGlassFactoryReset event,
TwoGangGlassFactoryReset event, Emitter<TwoGangGlassSwitchState> emit) async {
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading()); emit(TwoGangGlassSwitchLoading());
try { try {
final response = await DevicesManagementApi().factoryReset( final response = await DevicesManagementApi()
event.factoryReset, .factoryReset(event.factoryReset, event.deviceId);
event.deviceId,
);
if (!response) { if (!response) {
emit(TwoGangGlassSwitchError('Failed to reset device')); emit(TwoGangGlassSwitchError('Failed'));
} else { } else {
add(TwoGangGlassSwitchFetchDeviceEvent(event.deviceId)); emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
} }
} catch (e) { } catch (e) {
emit(TwoGangGlassSwitchError(e.toString())); emit(TwoGangGlassSwitchError(e.toString()));
} }
} }
void _onStatusUpdated( Future<void> _runDebounce({
StatusUpdated event, required dynamic deviceId,
Emitter<TwoGangGlassSwitchState> emit, required String code,
) { required bool value,
deviceStatus = event.deviceStatus; required bool oldValue,
required Emitter<TwoGangGlassSwitchState> 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(milliseconds: 500), () 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) {
_revertValueAndEmit(id, code, oldValue, emit);
}
});
}
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
Emitter<TwoGangGlassSwitchState> emit) {
_updateLocalValue(code, oldValue);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
} }
void _updateLocalValue(String code, bool value) { void _updateLocalValue(String code, bool value) {
if (code == 'switch_1') {
deviceStatus = deviceStatus.copyWith(switch1: value);
} else if (code == 'switch_2') {
deviceStatus = deviceStatus.copyWith(switch2: value);
}
}
bool _getValueByCode(String code) {
switch (code) { switch (code) {
case 'switch_1': case 'switch_1':
deviceStatus = deviceStatus.copyWith(switch1: value); return deviceStatus.switch1;
break;
case 'switch_2': case 'switch_2':
deviceStatus = deviceStatus.copyWith(switch2: value); return deviceStatus.switch2;
break; default:
return false;
} }
} }
@override
Future<void> close() {
_timer?.cancel();
return super.close();
}
// _listenToChanges(deviceId) {
// try {
// DatabaseReference ref =
// FirebaseDatabase.instance.ref('device-status/$deviceId');
// Stream<DatabaseEvent> stream = ref.onValue;
// stream.listen((DatabaseEvent event) {
// Map<dynamic, dynamic> usersMap =
// event.snapshot.value as Map<dynamic, dynamic>;
// List<Status> statusList = [];
// usersMap['status'].forEach((element) {
// statusList
// .add(Status(code: element['code'], value: element['value']));
// });
// deviceStatus = TwoGangGlassStatusModel.fromJson(
// usersMap['productUuid'], statusList);
// if (!isClosed) {
// add(StatusUpdated(deviceStatus));
// }
// });
// } catch (_) {}
// }
void _onStatusUpdated(
StatusUpdated event, Emitter<TwoGangGlassSwitchState> emit) {
// Update the local deviceStatus with the new status from the event
deviceStatus = event.deviceStatus;
// Emit the new state with the updated status
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
}
} }

View File

@ -1,17 +1,12 @@
part of 'two_gang_glass_switch_bloc.dart'; part of 'two_gang_glass_switch_bloc.dart';
@immutable @immutable
abstract class TwoGangGlassSwitchEvent extends Equatable { abstract class TwoGangGlassSwitchEvent {}
const TwoGangGlassSwitchEvent();
}
class TwoGangGlassSwitchFetchDeviceEvent extends TwoGangGlassSwitchEvent { class TwoGangGlassSwitchFetchDeviceEvent extends TwoGangGlassSwitchEvent {
final String deviceId; final String deviceId;
const TwoGangGlassSwitchFetchDeviceEvent(this.deviceId); TwoGangGlassSwitchFetchDeviceEvent(this.deviceId);
@override
List<Object> get props => [deviceId];
} }
class TwoGangGlassSwitchControl extends TwoGangGlassSwitchEvent { class TwoGangGlassSwitchControl extends TwoGangGlassSwitchEvent {
@ -19,14 +14,11 @@ class TwoGangGlassSwitchControl extends TwoGangGlassSwitchEvent {
final String code; final String code;
final bool value; final bool value;
const TwoGangGlassSwitchControl({ TwoGangGlassSwitchControl({
required this.deviceId, required this.deviceId,
required this.code, required this.code,
required this.value, required this.value,
}); });
@override
List<Object> get props => [deviceId, code, value];
} }
class TwoGangGlassSwitchBatchControl extends TwoGangGlassSwitchEvent { class TwoGangGlassSwitchBatchControl extends TwoGangGlassSwitchEvent {
@ -34,43 +26,33 @@ class TwoGangGlassSwitchBatchControl extends TwoGangGlassSwitchEvent {
final String code; final String code;
final bool value; final bool value;
const TwoGangGlassSwitchBatchControl({ TwoGangGlassSwitchBatchControl({
required this.deviceIds, required this.deviceIds,
required this.code, required this.code,
required this.value, required this.value,
}); });
@override
List<Object> get props => [deviceIds, code, value];
} }
class TwoGangGlassSwitchFetchBatchStatusEvent extends TwoGangGlassSwitchEvent { class TwoGangGlassSwitchFetchBatchStatusEvent extends TwoGangGlassSwitchEvent {
final List<String> deviceIds; final List<String> deviceIds;
const TwoGangGlassSwitchFetchBatchStatusEvent(this.deviceIds); TwoGangGlassSwitchFetchBatchStatusEvent(this.deviceIds);
@override
List<Object> get props => [deviceIds];
} }
class TwoGangGlassFactoryReset extends TwoGangGlassSwitchEvent { class TwoGangGlassFactoryReset extends TwoGangGlassSwitchEvent {
final String deviceId; final String deviceId;
final FactoryResetModel factoryReset; final FactoryResetModel factoryReset;
const TwoGangGlassFactoryReset({ TwoGangGlassFactoryReset({
required this.deviceId, required this.deviceId,
required this.factoryReset, required this.factoryReset,
}); });
@override
List<Object> get props => [deviceId, factoryReset];
} }
class StatusUpdated extends TwoGangGlassSwitchEvent { class StatusUpdated extends TwoGangGlassSwitchEvent {
final TwoGangGlassStatusModel deviceStatus; final TwoGangGlassStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
const StatusUpdated(this.deviceStatus);
@override @override
List<Object> get props => [deviceStatus]; List<Object> get props => [deviceStatus];
} }

View File

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

View File

@ -5,7 +5,6 @@ import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_
// 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/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/factories/two_gang_glass_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.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';
@ -17,7 +16,7 @@ class TwoGangGlassSwitchBatchControlView extends StatelessWidget with HelperResp
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => TwoGangGlassSwitchBlocFactory.create(deviceId: deviceIds.first) create: (context) => TwoGangGlassSwitchBloc(deviceId: deviceIds.first)
..add(TwoGangGlassSwitchFetchBatchStatusEvent(deviceIds)), ..add(TwoGangGlassSwitchFetchBatchStatusEvent(deviceIds)),
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>( child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
builder: (context, state) { builder: (context, state) {

View File

@ -2,7 +2,6 @@ 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/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/factories/two_gang_glass_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_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';
@ -16,7 +15,7 @@ class TwoGangGlassSwitchControlView extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => TwoGangGlassSwitchBlocFactory.create(deviceId: deviceId) create: (context) => TwoGangGlassSwitchBloc(deviceId: deviceId)
..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)), ..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>( child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
builder: (context, state) { builder: (context, state) {

View File

@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@ -7,22 +6,10 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_sta
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_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';
class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> { class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
final String deviceId; TwoGangSwitchBloc({required this.deviceId}) : super(TwoGangSwitchInitial()) {
final ControlDeviceService controlDeviceService;
final BatchControlDevicesService batchControlDevicesService;
late TwoGangStatusModel deviceStatus;
TwoGangSwitchBloc({
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(TwoGangSwitchInitial()) {
on<TwoGangSwitchFetchDeviceEvent>(_onFetchDeviceStatus); on<TwoGangSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<TwoGangSwitchControl>(_onControl); on<TwoGangSwitchControl>(_onControl);
on<TwoGangSwitchFetchBatchEvent>(_onFetchBatchStatus); on<TwoGangSwitchFetchBatchEvent>(_onFetchBatchStatus);
@ -31,13 +18,16 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( late TwoGangStatusModel deviceStatus;
TwoGangSwitchFetchDeviceEvent event, final String deviceId;
Emitter<TwoGangSwitchState> emit, Timer? _timer;
) async {
FutureOr<void> _onFetchDeviceStatus(TwoGangSwitchFetchDeviceEvent event,
Emitter<TwoGangSwitchState> emit) async {
emit(TwoGangSwitchLoading()); emit(TwoGangSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = TwoGangStatusModel.fromJson(event.deviceId, status.status); deviceStatus = TwoGangStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId); _listenToChanges(event.deviceId);
emit(TwoGangSwitchStatusLoaded(deviceStatus)); emit(TwoGangSwitchStatusLoaded(deviceStatus));
@ -46,91 +36,131 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
} }
} }
void _listenToChanges(String deviceId) { FutureOr<void> _onControl(
try { TwoGangSwitchControl event, Emitter<TwoGangSwitchState> emit) async {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); final oldValue = _getValueByCode(event.code);
ref.onValue.listen((event) {
final eventsMap = event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
eventsMap['status'].forEach((element) {
statusList.add(
Status(code: element['code'], value: element['value']),
);
});
deviceStatus = TwoGangStatusModel.fromJson(deviceId, statusList);
add(StatusUpdated(deviceStatus));
});
} catch (_) {
log(
'Error listening to changes',
name: 'TwoGangSwitchBloc._listenToChanges',
);
}
}
Future<void> _onControl(
TwoGangSwitchControl event,
Emitter<TwoGangSwitchState> emit,
) async {
emit(TwoGangSwitchLoading());
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(TwoGangSwitchStatusLoaded(deviceStatus)); emit(TwoGangSwitchStatusLoaded(deviceStatus));
try { await _runDebounce(
await controlDeviceService.controlDevice( deviceId: event.deviceId,
deviceUuid: event.deviceId,
status: Status(code: event.code, value: event.value),
);
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(TwoGangSwitchError(e.toString()));
}
}
Future<void> _onBatchControl(
TwoGangSwitchBatchControl event,
Emitter<TwoGangSwitchState> emit,
) async {
emit(TwoGangSwitchLoading());
_updateLocalValue(event.code, event.value);
emit(TwoGangSwitchStatusLoaded(deviceStatus));
try {
await batchControlDevicesService.batchControlDevices(
uuids: event.deviceId,
code: event.code, code: event.code,
value: event.value, value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: false,
); );
}
Future<void> _runDebounce({
required dynamic deviceId,
required String code,
required bool value,
required bool oldValue,
required Emitter<TwoGangSwitchState> 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(milliseconds: 500), () 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) { } catch (e) {
_updateLocalValue(event.code, !event.value); _revertValueAndEmit(id, code, oldValue, emit);
emit(TwoGangSwitchError(e.toString())); }
});
}
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
Emitter<TwoGangSwitchState> emit) {
_updateLocalValue(code, oldValue);
emit(TwoGangSwitchStatusLoaded(deviceStatus));
}
void _updateLocalValue(String code, bool value) {
if (code == 'switch_1') {
deviceStatus = deviceStatus.copyWith(switch1: value);
}
if (code == 'switch_2') {
deviceStatus = deviceStatus.copyWith(switch2: value);
} }
} }
Future<void> _onFetchBatchStatus( bool _getValueByCode(String code) {
TwoGangSwitchFetchBatchEvent event, switch (code) {
Emitter<TwoGangSwitchState> emit, case 'switch_1':
) async { return deviceStatus.switch1;
case 'switch_2':
return deviceStatus.switch2;
default:
return false;
}
}
Future<void> _onFetchBatchStatus(TwoGangSwitchFetchBatchEvent event,
Emitter<TwoGangSwitchState> emit) async {
emit(TwoGangSwitchLoading()); emit(TwoGangSwitchLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.devicesIds); final status =
deviceStatus = TwoGangStatusModel.fromJson( await DevicesManagementApi().getBatchStatus(event.devicesIds);
event.devicesIds.first, deviceStatus =
status.status, TwoGangStatusModel.fromJson(event.devicesIds.first, status.status);
);
emit(TwoGangSwitchStatusLoaded(deviceStatus)); emit(TwoGangSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(TwoGangSwitchError(e.toString())); emit(TwoGangSwitchError(e.toString()));
} }
} }
Future<void> _onFactoryReset( @override
TwoGangFactoryReset event, Future<void> close() {
Emitter<TwoGangSwitchState> emit, _timer?.cancel();
) async { return super.close();
}
FutureOr<void> _onBatchControl(
TwoGangSwitchBatchControl event, Emitter<TwoGangSwitchState> emit) async {
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value);
emit(TwoGangSwitchStatusLoaded(deviceStatus));
await _runDebounce(
deviceId: event.deviceId,
code: event.code,
value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: true,
);
}
FutureOr<void> _onFactoryReset(
TwoGangFactoryReset event, Emitter<TwoGangSwitchState> emit) async {
emit(TwoGangSwitchLoading()); emit(TwoGangSwitchLoading());
try { try {
final response = await DevicesManagementApi().factoryReset( final response = await DevicesManagementApi().factoryReset(
@ -138,31 +168,42 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
event.deviceId, event.deviceId,
); );
if (!response) { if (!response) {
emit(TwoGangSwitchError('Failed to reset device')); emit(TwoGangSwitchError('Failed'));
} else { } else {
add(TwoGangSwitchFetchDeviceEvent(event.deviceId)); emit(TwoGangSwitchStatusLoaded(deviceStatus));
} }
} catch (e) { } catch (e) {
emit(TwoGangSwitchError(e.toString())); emit(TwoGangSwitchError(e.toString()));
} }
} }
void _onStatusUpdated( _listenToChanges(deviceId) {
StatusUpdated event, try {
Emitter<TwoGangSwitchState> emit, DatabaseReference ref =
) { FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
TwoGangStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<TwoGangSwitchState> emit) {
deviceStatus = event.deviceStatus; deviceStatus = event.deviceStatus;
emit(TwoGangSwitchStatusLoaded(deviceStatus)); emit(TwoGangSwitchStatusLoaded(deviceStatus));
} }
void _updateLocalValue(String code, bool value) {
switch (code) {
case 'switch_1':
deviceStatus = deviceStatus.copyWith(switch1: value);
break;
case 'switch_2':
deviceStatus = deviceStatus.copyWith(switch2: value);
break;
}
}
} }

View File

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

View File

@ -24,16 +24,16 @@ class TwoGangStatusModel {
for (var status in jsonList) { for (var status in jsonList) {
switch (status.code) { switch (status.code) {
case 'switch_1': case 'switch_1':
switch1 = bool.tryParse(status.value.toString()) ?? false; switch1 = status.value ?? false;
break; break;
case 'countdown_1': case 'countdown_1':
countDown = int.tryParse(status.value.toString()) ?? 0; countDown = status.value ?? 0;
break; break;
case 'switch_2': case 'switch_2':
switch2 = bool.tryParse(status.value.toString()) ?? false; switch2 = status.value ?? false;
break; break;
case 'countdown_2': case 'countdown_2':
countDown2 = int.tryParse(status.value.toString()) ?? 0; countDown2 = status.value ?? 0;
break; break;
} }
} }

View File

@ -2,11 +2,11 @@ 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/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.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/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/factories/two_gang_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.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';
@ -18,7 +18,7 @@ class TwoGangBatchControlView extends StatelessWidget with HelperResponsiveLayou
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => TwoGangSwitchBlocFactory.create(deviceId: deviceIds.first) create: (context) => TwoGangSwitchBloc(deviceId: deviceIds.first)
..add(TwoGangSwitchFetchBatchEvent(deviceIds)), ..add(TwoGangSwitchFetchBatchEvent(deviceIds)),
child: BlocBuilder<TwoGangSwitchBloc, TwoGangSwitchState>( child: BlocBuilder<TwoGangSwitchBloc, TwoGangSwitchState>(
builder: (context, state) { builder: (context, state) {

View File

@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/factories/two_gang_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.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';
@ -16,7 +15,7 @@ class TwoGangDeviceControlView extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => TwoGangSwitchBlocFactory.create(deviceId: deviceId) create: (context) => TwoGangSwitchBloc(deviceId: deviceId)
..add(TwoGangSwitchFetchDeviceEvent(deviceId)), ..add(TwoGangSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<TwoGangSwitchBloc, TwoGangSwitchState>( child: BlocBuilder<TwoGangSwitchBloc, TwoGangSwitchState>(
builder: (context, state) { builder: (context, state) {

View File

@ -1,28 +1,18 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_state.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_state.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_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';
class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> { class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
final String deviceId; final String deviceId;
final ControlDeviceService controlDeviceService;
final BatchControlDevicesService batchControlDevicesService;
late WallSensorModel deviceStatus; late WallSensorModel deviceStatus;
Timer? _timer;
WallSensorBloc({ WallSensorBloc({required this.deviceId}) : super(WallSensorInitialState()) {
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(WallSensorInitialState()) {
on<WallSensorFetchStatusEvent>(_fetchWallSensorStatus); on<WallSensorFetchStatusEvent>(_fetchWallSensorStatus);
on<WallSensorFetchBatchStatusEvent>(_fetchWallSensorBatchControl); on<WallSensorFetchBatchStatusEvent>(_fetchWallSensorBatchControl);
on<WallSensorChangeValueEvent>(_changeValue); on<WallSensorChangeValueEvent>(_changeValue);
@ -34,28 +24,28 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
on<WallSensorRealtimeUpdateEvent>(_onRealtimeUpdate); on<WallSensorRealtimeUpdateEvent>(_onRealtimeUpdate);
} }
Future<void> _fetchWallSensorStatus( void _fetchWallSensorStatus(
WallSensorFetchStatusEvent event, WallSensorFetchStatusEvent event, Emitter<WallSensorState> emit) async {
Emitter<WallSensorState> emit,
) async {
emit(WallSensorLoadingInitialState()); emit(WallSensorLoadingInitialState());
try { try {
final response = await DevicesManagementApi().getDeviceStatus(deviceId); var response = await DevicesManagementApi().getDeviceStatus(deviceId);
deviceStatus = WallSensorModel.fromJson(response.status); deviceStatus = WallSensorModel.fromJson(response.status);
_listenToChanges(deviceId);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
_listenToChanges(deviceId);
} catch (e) { } catch (e) {
emit(WallSensorFailedState(error: e.toString())); emit(WallSensorFailedState(error: e.toString()));
return;
} }
} }
Future<void> _fetchWallSensorBatchControl( // Fetch batch status
FutureOr<void> _fetchWallSensorBatchControl(
WallSensorFetchBatchStatusEvent event, WallSensorFetchBatchStatusEvent event,
Emitter<WallSensorState> emit, Emitter<WallSensorState> emit) async {
) async {
emit(WallSensorLoadingInitialState()); emit(WallSensorLoadingInitialState());
try { try {
final response = await DevicesManagementApi().getBatchStatus(event.devicesIds); var response =
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = WallSensorModel.fromJson(response.status); deviceStatus = WallSensorModel.fromJson(response.status);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
} catch (e) { } catch (e) {
@ -64,105 +54,132 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
} }
void _listenToChanges(String deviceId) { void _listenToChanges(String deviceId) {
try { DatabaseReference ref =
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); FirebaseDatabase.instance.ref('device-status/$deviceId');
ref.onValue.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?;
if (data == null) return;
ref.onValue.listen((event) { final statusList = (data['status'] as List?)
final eventsMap = event.snapshot.value as Map<dynamic, dynamic>; ?.map((e) => Status(code: e['code'], value: e['value']))
.toList();
List<Status> statusList = []; if (statusList != null) {
eventsMap['status'].forEach((element) { final updatedDeviceStatus = WallSensorModel.fromJson(statusList);
statusList.add(
Status(code: element['code'], value: element['value']),
);
});
deviceStatus = WallSensorModel.fromJson(statusList);
if (!isClosed) { if (!isClosed) {
add(WallSensorRealtimeUpdateEvent(deviceStatus)); add(WallSensorRealtimeUpdateEvent(updatedDeviceStatus));
}
} }
}); });
} catch (_) {
log(
'Error listening to changes',
name: 'WallSensorBloc._listenToChanges',
);
}
} }
Future<void> _changeValue(
WallSensorChangeValueEvent event, void _changeValue(
Emitter<WallSensorState> emit, WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
) async {
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus)); emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
_updateLocalValue(event.code, event.value); if (event.code == 'far_detection') {
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); deviceStatus.farDetection = event.value;
} else if (event.code == 'motionless_sensitivity') {
try { deviceStatus.motionlessSensitivity = event.value;
await controlDeviceService.controlDevice( } else if (event.code == 'motion_sensitivity_value') {
deviceUuid: deviceId, deviceStatus.motionSensitivity = event.value;
status: Status(code: event.code, value: event.value), } else if (event.code == 'no_one_time') {
); deviceStatus.noBodyTime = event.value;
} catch (e) {
_updateLocalValue(event.code, event.value == 0 ? 1 : 0);
emit(WallSensorFailedState(error: e.toString()));
} }
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
await _runDeBouncer(
deviceId: deviceId,
code: event.code,
value: event.value,
isBatch: false,
emit: emit,
);
} }
Future<void> _onBatchControl( Future<void> _onBatchControl(
WallSensorBatchControlEvent event, WallSensorBatchControlEvent event, Emitter<WallSensorState> emit) async {
Emitter<WallSensorState> emit,
) async {
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus)); emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
_updateLocalValue(event.code, event.value); if (event.code == 'far_detection') {
deviceStatus.farDetection = event.value;
} else if (event.code == 'motionless_sensitivity') {
deviceStatus.motionlessSensitivity = event.value;
} else if (event.code == 'motion_sensitivity_value') {
deviceStatus.motionSensitivity = event.value;
} else if (event.code == 'no_one_time') {
deviceStatus.noBodyTime = event.value;
}
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
await _runDeBouncer(
try { deviceId: event.deviceIds,
await batchControlDevicesService.batchControlDevices(
uuids: event.deviceIds,
code: event.code, code: event.code,
value: event.value, value: event.value,
emit: emit,
isBatch: true,
); );
} catch (e) {
_updateLocalValue(event.code, !event.value);
emit(WallSensorFailedState(error: e.toString()));
}
} }
Future<void> _getDeviceReports( _runDeBouncer({
GetDeviceReportsEvent event, required dynamic deviceId,
Emitter<WallSensorState> emit, required String code,
) async { required dynamic value,
emit(DeviceReportsLoadingState()); required Emitter<WallSensorState> emit,
required bool isBatch,
}) {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(const Duration(seconds: 1), () async {
try { try {
final reports = await DevicesManagementApi.getDeviceReports( late bool response;
deviceId, if (isBatch) {
event.code, response = await DevicesManagementApi()
); .deviceBatchControl(deviceId, code, value);
emit(DeviceReportsState(deviceReport: reports, code: event.code)); } else {
response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
}
if (!response) {
add(WallSensorFetchStatusEvent());
}
} catch (_) {
await Future.delayed(const Duration(milliseconds: 500));
add(WallSensorFetchStatusEvent());
}
});
}
FutureOr<void> _getDeviceReports(
GetDeviceReportsEvent event, Emitter<WallSensorState> emit) async {
emit(DeviceReportsLoadingState());
// final from = DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch;
// final to = DateTime.now().millisecondsSinceEpoch;
try {
// await DevicesManagementApi.getDeviceReportsByDate(
// deviceId, event.code, from.toString(), to.toString())
await DevicesManagementApi.getDeviceReports(deviceId, event.code)
.then((value) {
emit(DeviceReportsState(deviceReport: value, code: event.code));
});
} catch (e) { } catch (e) {
emit(DeviceReportsFailedState(error: e.toString())); emit(DeviceReportsFailedState(error: e.toString()));
return;
} }
} }
void _showDescription( void _showDescription(
ShowDescriptionEvent event, ShowDescriptionEvent event, Emitter<WallSensorState> emit) {
Emitter<WallSensorState> emit,
) {
emit(WallSensorShowDescriptionState(description: event.description)); emit(WallSensorShowDescriptionState(description: event.description));
} }
void _backToGridView( void _backToGridView(
BackToGridViewEvent event, BackToGridViewEvent event, Emitter<WallSensorState> emit) {
Emitter<WallSensorState> emit,
) {
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
} }
Future<void> _onFactoryReset( FutureOr<void> _onFactoryReset(
WallSensorFactoryResetEvent event, WallSensorFactoryResetEvent event, Emitter<WallSensorState> emit) async {
Emitter<WallSensorState> emit,
) async {
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus)); emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
try { try {
final response = await DevicesManagementApi().factoryReset( final response = await DevicesManagementApi().factoryReset(
@ -170,9 +187,9 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
event.deviceId, event.deviceId,
); );
if (!response) { if (!response) {
emit(const WallSensorFailedState(error: 'Failed to reset device')); emit(const WallSensorFailedState(error: 'Failed'));
} else { } else {
add(WallSensorFetchStatusEvent()); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
} }
} catch (e) { } catch (e) {
emit(WallSensorFailedState(error: e.toString())); emit(WallSensorFailedState(error: e.toString()));
@ -183,23 +200,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
WallSensorRealtimeUpdateEvent event, WallSensorRealtimeUpdateEvent event,
Emitter<WallSensorState> emit, Emitter<WallSensorState> emit,
) { ) {
emit(WallSensorUpdateState(wallSensorModel: event.deviceStatus)); deviceStatus = event.deviceStatus;
} emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
void _updateLocalValue(String code, dynamic value) {
switch (code) {
case 'far_detection':
deviceStatus.farDetection = value;
break;
case 'motionless_sensitivity':
deviceStatus.motionlessSensitivity = value;
break;
case 'motion_sensitivity_value':
deviceStatus.motionSensitivity = value;
break;
case 'no_one_time':
deviceStatus.noBodyTime = value;
break;
}
} }
} }

View File

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

View File

@ -7,7 +7,6 @@ import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presen
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_bloc.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_bloc.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_state.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_state.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/factories/wall_sensor_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.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';
@ -22,7 +21,7 @@ class WallSensorBatchControlView extends StatelessWidget with HelperResponsiveLa
final isLarge = isLargeScreenSize(context); final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context); final isMedium = isMediumScreenSize(context);
return BlocProvider( return BlocProvider(
create: (context) => WallSensorBlocFactory.create(deviceId: devicesIds.first) create: (context) => WallSensorBloc(deviceId: devicesIds.first)
..add(WallSensorFetchBatchStatusEvent(devicesIds)), ..add(WallSensorFetchBatchStatusEvent(devicesIds)),
child: BlocBuilder<WallSensorBloc, WallSensorState>( child: BlocBuilder<WallSensorBloc, WallSensorState>(
builder: (context, state) { builder: (context, state) {

View File

@ -10,7 +10,6 @@ import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presen
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_static_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_static_widget.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_status.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_status.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/factories/wall_sensor_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_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';
@ -27,7 +26,7 @@ class WallSensorControlsView extends StatelessWidget with HelperResponsiveLayout
final isMedium = isMediumScreenSize(context); final isMedium = isMediumScreenSize(context);
return BlocProvider( return BlocProvider(
create: (context) => create: (context) =>
WallSensorBlocFactory.create(deviceId: device.uuid!)..add(WallSensorFetchStatusEvent()), WallSensorBloc(deviceId: device.uuid!)..add(WallSensorFetchStatusEvent()),
child: BlocBuilder<WallSensorBloc, WallSensorState>( child: BlocBuilder<WallSensorBloc, WallSensorState>(
builder: (context, state) { builder: (context, state) {
if (state is WallSensorLoadingInitialState || state is DeviceReportsLoadingState) { if (state is WallSensorLoadingInitialState || state is DeviceReportsLoadingState) {

View File

@ -1,3 +1,5 @@
// water_heater_bloc.dart
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
@ -8,8 +10,6 @@ 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,17 +17,7 @@ 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> {
late WaterHeaterStatusModel deviceStatus; WaterHeaterBloc() : super(WaterHeaterInitial()) {
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);
@ -39,6 +29,7 @@ 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);
@ -47,7 +38,11 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
void _initializeAddSchedule( late WaterHeaterStatusModel deviceStatus;
Timer? _countdownTimer;
// Timer? _inchingTimer;
FutureOr<void> _initializeAddSchedule(
InitializeAddScheduleEvent event, InitializeAddScheduleEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) { ) {
@ -69,7 +64,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
void _updateSelectedTime( FutureOr<void> _updateSelectedTime(
UpdateSelectedTimeEvent event, UpdateSelectedTimeEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) { ) {
@ -78,7 +73,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
emit(currentState.copyWith(selectedTime: event.selectedTime)); emit(currentState.copyWith(selectedTime: event.selectedTime));
} }
void _updateSelectedDay( FutureOr<void> _updateSelectedDay(
UpdateSelectedDayEvent event, UpdateSelectedDayEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) { ) {
@ -89,7 +84,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
selectedDays: updatedDays, selectedTime: currentState.selectedTime)); selectedDays: updatedDays, selectedTime: currentState.selectedTime));
} }
void _updateFunctionOn( FutureOr<void> _updateFunctionOn(
UpdateFunctionOnEvent event, UpdateFunctionOnEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) { ) {
@ -98,18 +93,16 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
functionOn: event.isOn, selectedTime: currentState.selectedTime)); functionOn: event.isOn, selectedTime: currentState.selectedTime));
} }
Future<void> _updateScheduleEvent( FutureOr<void> _updateScheduleEvent(
UpdateScheduleEvent event, UpdateScheduleEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) async { ) async {
final currentState = state; final currentState = state;
if (currentState is WaterHeaterDeviceStatusLoaded) { if (currentState is WaterHeaterDeviceStatusLoaded) {
if (event.scheduleMode == ScheduleModes.schedule) { if (event.scheduleMode == ScheduleModes.schedule) {
emit( emit(currentState.copyWith(
currentState.copyWith(
scheduleMode: ScheduleModes.schedule, scheduleMode: ScheduleModes.schedule,
), ));
);
} }
if (event.scheduleMode == ScheduleModes.countdown) { if (event.scheduleMode == ScheduleModes.countdown) {
final countdownRemaining = final countdownRemaining =
@ -123,88 +116,87 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
countdownRemaining: countdownRemaining, countdownRemaining: countdownRemaining,
)); ));
if (!currentState.isCountdownActive! && countdownRemaining > Duration.zero) { if (!currentState.isCountdownActive! &&
countdownRemaining > Duration.zero) {
_startCountdownTimer(emit, countdownRemaining); _startCountdownTimer(emit, countdownRemaining);
} }
} else if (event.scheduleMode == ScheduleModes.inching) { } else if (event.scheduleMode == ScheduleModes.inching) {
final inchingDuration = Duration(hours: event.hours, minutes: event.minutes); final inchingDuration =
Duration(hours: event.hours, minutes: event.minutes);
emit( emit(currentState.copyWith(
currentState.copyWith(
scheduleMode: ScheduleModes.inching, scheduleMode: ScheduleModes.inching,
inchingHours: inchingDuration.inHours, inchingHours: inchingDuration.inHours,
inchingMinutes: inchingDuration.inMinutes % 60, inchingMinutes: inchingDuration.inMinutes % 60,
isInchingActive: currentState.isInchingActive, isInchingActive: currentState.isInchingActive,
), ));
);
} }
} }
} }
Future<void> _controlWaterHeater( FutureOr<void> _controlWaterHeater(
ToggleWaterHeaterEvent event, ToggleWaterHeaterEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) async { ) async {
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( emit(currentState.copyWith(
currentState.copyWith(
status: deviceStatus, status: deviceStatus,
), ));
);
final success = await controlDeviceService.controlDevice( final success = await _runDebounce(
deviceUuid: event.deviceId, deviceId: event.deviceId,
status: Status(
code: event.code, code: event.code,
value: event.value, value: event.value,
), oldValue: oldValue,
emit: emit,
isBatch: false,
); );
if (success) { if (success) {
if (event.code == "countdown_1") { if (event.code == "countdown_1") {
final countdownDuration = Duration(seconds: event.value); final countdownDuration = Duration(seconds: event.value);
emit( emit(currentState.copyWith(
currentState.copyWith(
countdownHours: countdownDuration.inHours, countdownHours: countdownDuration.inHours,
countdownMinutes: countdownDuration.inMinutes % 60, countdownMinutes: countdownDuration.inMinutes % 60,
countdownRemaining: countdownDuration, countdownRemaining: countdownDuration,
isCountdownActive: true, isCountdownActive: true,
), ));
);
if (countdownDuration.inSeconds > 0) { if (countdownDuration.inSeconds > 0) {
_startCountdownTimer(emit, countdownDuration); _startCountdownTimer(emit, countdownDuration);
} else { } else {
_countdownTimer?.cancel(); _countdownTimer?.cancel();
emit( emit(currentState.copyWith(
currentState.copyWith(
countdownHours: 0, countdownHours: 0,
countdownMinutes: 0, countdownMinutes: 0,
countdownRemaining: Duration.zero, countdownRemaining: Duration.zero,
isCountdownActive: false, isCountdownActive: false,
), ));
);
} }
} else if (event.code == "switch_inching") { } else if (event.code == "switch_inching") {
final inchingDuration = Duration(seconds: event.value); final inchingDuration = Duration(seconds: event.value);
emit( //if (inchingDuration.inSeconds > 0) {
currentState.copyWith( // _startInchingTimer(emit, inchingDuration);
// } else {
emit(currentState.copyWith(
inchingHours: inchingDuration.inHours, inchingHours: inchingDuration.inHours,
inchingMinutes: inchingDuration.inMinutes % 60, inchingMinutes: inchingDuration.inMinutes % 60,
isInchingActive: true, isInchingActive: true,
), ));
); // }
} }
} }
} }
} }
Future<void> _stopScheduleEvent( FutureOr<void> _stopScheduleEvent(
StopScheduleEvent event, StopScheduleEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) async { ) async {
@ -215,28 +207,25 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
_countdownTimer?.cancel(); _countdownTimer?.cancel();
if (isCountDown) { if (isCountDown) {
emit( emit(currentState.copyWith(
currentState.copyWith(
countdownHours: 0, countdownHours: 0,
countdownMinutes: 0, countdownMinutes: 0,
countdownRemaining: Duration.zero, countdownRemaining: Duration.zero,
isCountdownActive: false, isCountdownActive: false,
), ));
);
} else if (currentState.scheduleMode == ScheduleModes.inching) { } else if (currentState.scheduleMode == ScheduleModes.inching) {
emit( emit(currentState.copyWith(
currentState.copyWith(
inchingHours: 0, inchingHours: 0,
inchingMinutes: 0, inchingMinutes: 0,
isInchingActive: false, isInchingActive: false,
), ));
);
} }
try { try {
final status = await DevicesManagementApi().deviceControl( final status = await DevicesManagementApi().deviceControl(
event.deviceId, event.deviceId,
Status(code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0), Status(
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.'));
@ -247,15 +236,17 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
Future<void> _fetchWaterHeaterStatus( FutureOr<void> _fetchWaterHeaterStatus(
WaterHeaterFetchStatusEvent event, WaterHeaterFetchStatusEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) async { ) async {
emit(WaterHeaterLoadingState()); emit(WaterHeaterLoadingState());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
deviceStatus = WaterHeaterStatusModel.fromJson(event.deviceId, status.status); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
if (deviceStatus.scheduleMode == ScheduleModes.countdown) { if (deviceStatus.scheduleMode == ScheduleModes.countdown) {
final countdownRemaining = Duration( final countdownRemaining = Duration(
@ -297,6 +288,7 @@ 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,
@ -324,7 +316,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
void _listenToChanges(deviceId) { _listenToChanges(deviceId) {
try { try {
DatabaseReference ref = DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId'); FirebaseDatabase.instance.ref('device-status/$deviceId');
@ -336,11 +328,12 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
List<Status> statusList = []; List<Status> statusList = [];
usersMap['status'].forEach((element) { usersMap['status'].forEach((element) {
statusList.add(Status(code: element['code'], value: element['value'])); statusList
.add(Status(code: element['code'], value: element['value']));
}); });
deviceStatus = deviceStatus = WaterHeaterStatusModel.fromJson(
WaterHeaterStatusModel.fromJson(usersMap['productUuid'], statusList); usersMap['productUuid'], statusList);
if (!isClosed) { if (!isClosed) {
add(StatusUpdated(deviceStatus)); add(StatusUpdated(deviceStatus));
} }
@ -348,10 +341,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} catch (_) {} } catch (_) {}
} }
void _onStatusUpdated( void _onStatusUpdated(StatusUpdated event, Emitter<WaterHeaterState> emit) {
StatusUpdated event,
Emitter<WaterHeaterState> emit,
) {
deviceStatus = event.deviceStatus; deviceStatus = event.deviceStatus;
emit(WaterHeaterDeviceStatusLoaded(deviceStatus)); emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
} }
@ -362,13 +352,23 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
) { ) {
_countdownTimer?.cancel(); _countdownTimer?.cancel();
_countdownTimer = Timer.periodic( _countdownTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
const Duration(minutes: 1), add(DecrementCountdownEvent());
(timer) => add(DecrementCountdownEvent()), });
);
} }
void _onDecrementCountdown( // void _startInchingTimer(
// Emitter<WaterHeaterState> emit,
// Duration inchingDuration,
// ) {
// _inchingTimer?.cancel();
// _inchingTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
// add(DecrementInchingEvent());
// });
// }
_onDecrementCountdown(
DecrementCountdownEvent event, DecrementCountdownEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) { ) {
@ -382,29 +382,105 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
if (newRemaining <= Duration.zero) { if (newRemaining <= Duration.zero) {
_countdownTimer?.cancel(); _countdownTimer?.cancel();
emit( emit(currentState.copyWith(
currentState.copyWith(
countdownHours: 0, countdownHours: 0,
countdownMinutes: 0, countdownMinutes: 0,
isCountdownActive: false, isCountdownActive: false,
countdownRemaining: Duration.zero, countdownRemaining: Duration.zero,
), ));
);
return; return;
} }
final totalSeconds = newRemaining.inSeconds; int totalSeconds = newRemaining.inSeconds;
final newHours = totalSeconds ~/ 3600;
final newMinutes = (totalSeconds % 3600) ~/ 60;
emit( int newHours = totalSeconds ~/ 3600;
currentState.copyWith( int newMinutes = (totalSeconds % 3600) ~/ 60;
emit(currentState.copyWith(
countdownHours: newHours, countdownHours: newHours,
countdownMinutes: newMinutes, countdownMinutes: newMinutes,
countdownRemaining: newRemaining, countdownRemaining: newRemaining,
), ));
}
}
}
// 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,
));
} }
} }
@ -429,12 +505,14 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
dynamic _getValueByCode(String code) { dynamic _getValueByCode(String code) {
return switch (code) { switch (code) {
'switch_1' => deviceStatus.heaterSwitch, case 'switch_1':
'countdown_1' => return deviceStatus.heaterSwitch;
(deviceStatus.countdownHours * 60) + deviceStatus.countdownMinutes, case 'countdown_1':
_ => null, return deviceStatus.countdownHours * 60 + deviceStatus.countdownMinutes;
}; default:
return null;
}
} }
@override @override
@ -443,17 +521,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
return super.close(); return super.close();
} }
Future<void> _getSchedule( FutureOr<void> _getSchedule(
GetSchedulesEvent event, GetSchedulesEvent event, Emitter<WaterHeaterState> emit) async {
Emitter<WaterHeaterState> emit,
) async {
emit(ScheduleLoadingState()); emit(ScheduleLoadingState());
try { try {
final schedules = await DevicesManagementApi().getDeviceSchedules( List<ScheduleModel> schedules = await DevicesManagementApi()
deviceStatus.uuid, .getDeviceSchedules(deviceStatus.uuid, event.category);
event.category,
);
emit(WaterHeaterDeviceStatusLoaded( emit(WaterHeaterDeviceStatusLoaded(
deviceStatus, deviceStatus,
@ -461,6 +535,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
scheduleMode: ScheduleModes.schedule, scheduleMode: ScheduleModes.schedule,
)); ));
} catch (e) { } catch (e) {
//(const WaterHeaterFailedState(error: 'Failed to fetch schedules.'));
emit(WaterHeaterDeviceStatusLoaded( emit(WaterHeaterDeviceStatusLoaded(
deviceStatus, deviceStatus,
schedules: const [], schedules: const [],
@ -468,7 +543,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
Future<void> _onAddSchedule( FutureOr<void> _onAddSchedule(
AddScheduleEvent event, AddScheduleEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) async { ) async {
@ -482,6 +557,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays), days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
); );
// emit(ScheduleLoadingState());
bool success = await DevicesManagementApi() bool success = await DevicesManagementApi()
.addScheduleRecord(newSchedule, currentState.status.uuid); .addScheduleRecord(newSchedule, currentState.status.uuid);
@ -489,14 +566,13 @@ 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.'));
} }
} }
} }
Future<void> _onEditSchedule( FutureOr<void> _onEditSchedule(EditWaterHeaterScheduleEvent event,
EditWaterHeaterScheduleEvent event, Emitter<WaterHeaterState> emit) async {
Emitter<WaterHeaterState> emit,
) async {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
@ -508,6 +584,8 @@ 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,
@ -517,11 +595,12 @@ 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.'));
} }
} }
} }
Future<void> _onUpdateSchedule( FutureOr<void> _onUpdateSchedule(
UpdateScheduleEntryEvent event, UpdateScheduleEntryEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) async { ) async {
@ -548,17 +627,20 @@ 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.'));
} }
} }
} }
Future<void> _onDeleteSchedule( FutureOr<void> _onDeleteSchedule(
DeleteScheduleEvent event, DeleteScheduleEvent event,
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) async { ) async {
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);
@ -570,22 +652,20 @@ 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.'));
} }
} }
} }
Future<void> _batchFetchWaterHeater( FutureOr<void> _batchFetchWaterHeater(FetchWaterHeaterBatchStatusEvent event,
FetchWaterHeaterBatchStatusEvent event, Emitter<WaterHeaterState> emit) async {
Emitter<WaterHeaterState> emit,
) async {
emit(WaterHeaterLoadingState()); emit(WaterHeaterLoadingState());
try { try {
final status = await DevicesManagementApi().getBatchStatus( final status =
event.devicesUuid, await DevicesManagementApi().getBatchStatus(event.devicesUuid);
); deviceStatus = WaterHeaterStatusModel.fromJson(
deviceStatus = event.devicesUuid.first, status.status);
WaterHeaterStatusModel.fromJson(event.devicesUuid.first, status.status);
emit(WaterHeaterDeviceStatusLoaded(deviceStatus)); emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
@ -593,8 +673,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
} }
} }
Future<void> _batchControlWaterHeater( FutureOr<void> _batchControlWaterHeater(ControlWaterHeaterBatchEvent event,
ControlWaterHeaterBatchEvent event, Emitter<WaterHeaterState> emit) async { Emitter<WaterHeaterState> emit) async {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
@ -606,10 +686,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
status: deviceStatus, status: deviceStatus,
)); ));
final success = await batchControlDevicesService.batchControlDevices( final success = await _runDebounce(
uuids: event.devicesUuid, deviceId: 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

@ -1,3 +1,5 @@
// water_heater_state.dart
part of 'water_heater_bloc.dart'; part of 'water_heater_bloc.dart';
sealed class WaterHeaterState extends Equatable { sealed class WaterHeaterState extends Equatable {

View File

@ -1,18 +0,0 @@
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,16 +1,15 @@
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 class WaterHEaterBatchControlView extends StatelessWidget with HelperResponsiveLayout {
with HelperResponsiveLayout {
const WaterHEaterBatchControlView({super.key, required this.deviceIds}); const WaterHEaterBatchControlView({super.key, required this.deviceIds});
final List<String> deviceIds; final List<String> deviceIds;
@ -18,9 +17,8 @@ class WaterHEaterBatchControlView extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => WaterHeaterBlocFactory.create( create: (context) =>
deviceId: deviceIds.first, WaterHeaterBloc()..add(FetchWaterHeaterBatchStatusEvent(devicesUuid: deviceIds)),
)..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,7 +5,6 @@ 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';
@ -22,9 +21,8 @@ class WaterHeaterDeviceControlView extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => WaterHeaterBlocFactory.create( create: (context) =>
deviceId: device.uuid ?? '', WaterHeaterBloc()..add(WaterHeaterFetchStatusEvent(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) {
@ -35,7 +33,8 @@ 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(height: 200, child: Center(child: SizedBox())); return const SizedBox(
height: 200, child: Center(child: SizedBox()));
} }
}, },
)); ));

View File

@ -12,53 +12,22 @@ class ConditionToggle extends StatelessWidget {
}); });
static const _conditions = ["<", "==", ">"]; static const _conditions = ["<", "==", ">"];
static const _icons = [
Icons.chevron_left,
Icons.drag_handle,
Icons.chevron_right
];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final selectedIndex = _conditions.indexOf(currentCondition ?? "=="); return ToggleButtons(
onPressed: (index) => onChanged(_conditions[index]),
return Container( borderRadius: const BorderRadius.all(Radius.circular(8)),
height: 30, selectedBorderColor: ColorsManager.primaryColorWithOpacity,
width: MediaQuery.of(context).size.width * 0.1, selectedColor: Colors.white,
decoration: BoxDecoration( fillColor: ColorsManager.primaryColorWithOpacity,
color: ColorsManager.softGray.withOpacity(0.5), color: ColorsManager.primaryColorWithOpacity,
borderRadius: BorderRadius.circular(50), constraints: const BoxConstraints(
), minHeight: 40.0,
clipBehavior: Clip.antiAlias, minWidth: 40.0,
child: Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(_conditions.length, (index) {
final isSelected = index == selectedIndex;
return Expanded(
child: InkWell(
onTap: () => onChanged(_conditions[index]),
child: AnimatedContainer(
duration: const Duration(milliseconds: 180),
curve: Curves.ease,
decoration: BoxDecoration(
color:
isSelected ? ColorsManager.vividBlue : Colors.transparent,
),
child: Center(
child: Icon(
_icons[index],
size: 20,
color: isSelected
? ColorsManager.whiteColors
: ColorsManager.blackColor,
weight: isSelected ? 700 : 500,
),
),
),
),
);
}),
), ),
isSelected: _conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: _conditions.map((c) => Text(c)).toList(),
); );
} }
} }

View File

@ -24,6 +24,9 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
on<PaginationEvent>(_fetchPaginationSpaces); on<PaginationEvent>(_fetchPaginationSpaces);
on<DebouncedSearchEvent>(_onDebouncedSearch); on<DebouncedSearchEvent>(_onDebouncedSearch);
on<SpaceTreeClearSelectionEvent>(_onSpaceTreeClearSelectionEvent); on<SpaceTreeClearSelectionEvent>(_onSpaceTreeClearSelectionEvent);
on<AnalyticsClearAllSpaceTreeSelectionsEvent>(
_onAnalyticsClearAllSpaceTreeSelectionsEvent,
);
} }
Timer _timer = Timer(const Duration(microseconds: 0), () {}); Timer _timer = Timer(const Duration(microseconds: 0), () {});
@ -493,6 +496,20 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
); );
} }
void _onAnalyticsClearAllSpaceTreeSelectionsEvent(
AnalyticsClearAllSpaceTreeSelectionsEvent event,
Emitter<SpaceTreeState> emit,
) async {
emit(
state.copyWith(
selectedCommunities: [],
selectedCommunityAndSpaces: {},
selectedSpaces: [],
soldCheck: [],
),
);
}
@override @override
Future<void> close() async { Future<void> close() async {
_timer.cancel(); _timer.cancel();

View File

@ -112,3 +112,7 @@ class ClearCachedData extends SpaceTreeEvent {}
class SpaceTreeClearSelectionEvent extends SpaceTreeEvent { class SpaceTreeClearSelectionEvent extends SpaceTreeEvent {
const SpaceTreeClearSelectionEvent(); const SpaceTreeClearSelectionEvent();
} }
final class AnalyticsClearAllSpaceTreeSelectionsEvent extends SpaceTreeEvent {
const AnalyticsClearAllSpaceTreeSelectionsEvent();
}