Compare commits

..

14 Commits

Author SHA1 Message Date
d1df33b31e Refactor WallSensorBloc to integrate new service dependencies and utilize a factory for instantiation. Enhanced event handling methods for improved error management and real-time status updates from Firebase, including optimized parsing logic for device status values. 2025-06-03 11:15:06 +03:00
6a36405530 Refactor TwoGangSwitchBloc to integrate new service dependencies and utilize a factory for instantiation. Enhanced event handling methods for improved error management and real-time status updates from Firebase, including parsing logic for device status values. 2025-06-03 10:48:01 +03:00
88a7607395 Refactor TwoGangGlassSwitchBloc to integrate new service dependencies and utilize a factory for instantiation. Enhanced event handling methods for improved error management and state updates, including real-time status listening from Firebase. 2025-06-03 10:33:33 +03:00
f58ddf76da Refactor LivingRoomBloc to integrate new service dependencies and utilize a factory for instantiation. Enhanced event handling methods for improved error management and state updates, including real-time status listening from Firebase. 2025-06-03 10:19:10 +03:00
a71a66034c Refactor ThreeGangGlassSwitchBloc to integrate new service dependencies and utilize a factory for instantiation. Enhanced event handling methods for improved error management and state updates. 2025-06-03 09:49:26 +03:00
b06a23cc60 Refactor WallLightSwitchBloc to integrate new service dependencies and utilize a factory for instantiation. Improved event handling methods for better error management and state updates. 2025-06-02 16:40:13 +03:00
5595bb7f25 Refactor OneGangGlassSwitchBloc to utilize new service dependencies and implement a factory for instantiation. Enhanced event handling methods for improved error management and state updates. 2025-06-02 16:35:55 +03:00
77d39bfc53 Refactor CurtainBloc to use new service dependencies and implement a factory for instantiation. Updated event handling methods for improved error management and state updates. 2025-06-02 11:26:30 +03:00
3bd2bd114b migrate CeilingSensorBloc to use the new services. 2025-06-02 11:13:56 +03:00
f98636a2e5 Migrated AcBloc single/batch controls the new services. 2025-06-02 10:44:43 +03:00
19548e99ab indentation and formatting of WaterHeaterBloc. 2025-06-02 10:20:05 +03:00
b60c674496 Created a factory for the WaterHeaterBloc, and injected the necessary dependenices. 2025-06-02 10:12:53 +03:00
6f3dfb607e Extracted single/batch control services creation into a factory for ease of reusablility for the sake of this migration. 2025-06-02 10:11:23 +03:00
62dabf1ce2 Made values in DeviceControlDialog selectable for a better UX. 2025-06-02 10:10:50 +03:00
81 changed files with 1625 additions and 3249 deletions

View File

@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 24C8.79469 24 5.78123 22.7518 3.51469 20.4853C1.24823 18.2188 0 15.2053 0 12C0 8.79469 1.24823 5.78123 3.51469 3.51469C5.78123 1.24823 8.79469 0 12 0C15.2053 0 18.2188 1.24823 20.4853 3.51469C22.7518 5.78123 24 8.79469 24 12C24 15.2053 22.7518 18.2188 20.4853 20.4853C18.2188 22.7518 15.2053 24 12 24ZM12 1.875C9.2955 1.875 6.75291 2.92819 4.84055 4.84055C2.92819 6.75291 1.875 9.2955 1.875 12C1.875 14.7045 2.92819 17.2471 4.84055 19.1595C6.75291 21.0718 9.2955 22.125 12 22.125C14.7045 22.125 17.2471 21.0718 19.1595 19.1595C21.0718 17.2471 22.125 14.7045 22.125 12C22.125 9.2955 21.0718 6.75291 19.1595 4.84055C17.2471 2.92819 14.7045 1.875 12 1.875ZM15.9775 17.3033L12 13.3258L8.02252 17.3033L6.6967 15.9775L10.6742 12L6.6967 8.02252L8.02252 6.6967L12 10.6742L15.9775 6.6967L17.3033 8.02252L13.3258 12L17.3033 15.9775L15.9775 17.3033Z" fill="#999999" fill-opacity="0.7"/>
</svg>

Before

Width:  |  Height:  |  Size: 992 B

View File

@ -1,3 +0,0 @@
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.4854 3.1332L9.8668 0.515228C9.64704 0.295536 9.34903 0.172119 9.03829 0.172119C8.72755 0.172119 8.42953 0.295536 8.20977 0.515228L0.983989 7.74042C0.874797 7.84895 0.788225 7.97806 0.729285 8.12027C0.670346 8.26249 0.640212 8.41499 0.640629 8.56894V11.1875C0.640629 11.4983 0.764094 11.7964 0.983864 12.0161C1.20363 12.2359 1.5017 12.3594 1.8125 12.3594H11.6563C11.8427 12.3594 12.0216 12.2853 12.1534 12.1534C12.2853 12.0216 12.3594 11.8427 12.3594 11.6562C12.3594 11.4698 12.2853 11.2909 12.1534 11.1591C12.0216 11.0272 11.8427 10.9531 11.6563 10.9531H6.32422L12.4854 4.79081C12.5942 4.68199 12.6806 4.55278 12.7395 4.41057C12.7984 4.26836 12.8288 4.11594 12.8288 3.96201C12.8288 3.80807 12.7984 3.65565 12.7395 3.51344C12.6806 3.37123 12.5942 3.24202 12.4854 3.1332ZM4.33204 10.9531H2.04688V8.66796L6.96875 3.74609L9.25391 6.03124L4.33204 10.9531ZM10.25 5.03515L7.96485 2.74999L9.03946 1.67538L11.3246 3.96054L10.25 5.03515Z" fill="#D5D5D5"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -161,7 +161,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
token = await AuthenticationAPI.loginWithEmail(
model: LoginWithEmailModel(
email: event.username.toLowerCase(),
email: event.username,
password: event.password,
),
);

View File

@ -211,7 +211,6 @@ class _DynamicTableState extends State<DynamicTable> {
onChanged: widget.withSelectAll && widget.data.isNotEmpty
? _toggleSelectAll
: null,
),
);
}
@ -282,7 +281,6 @@ class _DynamicTableState extends State<DynamicTable> {
padding: EdgeInsets.symmetric(
horizontal: index == widget.headers.length - 1 ? 12 : 8.0,
vertical: 4),
child: Text(
title,
style: context.textTheme.titleSmall!.copyWith(
@ -303,7 +301,6 @@ class _DynamicTableState extends State<DynamicTable> {
required int rowIndex,
required int columnIndex,
}) {
bool isBatteryLevel = content.endsWith('%');
double? batteryLevel;
@ -316,7 +313,6 @@ class _DynamicTableState extends State<DynamicTable> {
return _buildSettingsIcon(rowIndex, size);
}
Color? statusColor;
switch (content) {
case 'Effective':

View File

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

View File

@ -0,0 +1,18 @@
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_event.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_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/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/firmware_update.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/constants/assets.dart';
@ -26,8 +26,9 @@ class AcDeviceBatchControlView extends StatelessWidget with HelperResponsiveLayo
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return BlocProvider(
create: (context) =>
AcBloc(deviceId: devicesIds.first)..add(AcFetchBatchStatusEvent(devicesIds)),
create: (context) => AcBlocFactory.create(
deviceId: devicesIds.first,
)..add(AcFetchBatchStatusEvent(devicesIds)),
child: BlocBuilder<AcBloc, AcsState>(
builder: (context, state) {
if (state is ACStatusLoaded) {

View File

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

View File

@ -95,7 +95,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
return const RoutinesView();
}
if (state.createRoutineView) {
return const CreateNewRoutineView();
return CreateNewRoutineView();
}
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(

View File

@ -6,11 +6,9 @@ import 'package:syncrow_web/pages/common/filter/filter_widget.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/device_settings_panel.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/format_date_time.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
import 'package:syncrow_web/utils/style.dart';
@ -60,8 +58,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
'Low Battery ($lowBatteryCount)',
];
final buttonLabel =
(selectedDevices.length > 1) ? 'Batch Control' : 'Control';
final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control';
return Row(
children: [
@ -108,23 +105,18 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
if (selectedDevices.length == 1) {
showDialog(
context: context,
builder: (context) =>
DeviceControlDialog(
builder: (context) => DeviceControlDialog(
device: selectedDevices.first,
),
);
} else if (selectedDevices.length >
1) {
final productTypes =
selectedDevices
.map((device) =>
device.productType)
} else if (selectedDevices.length > 1) {
final productTypes = selectedDevices
.map((device) => device.productType)
.toSet();
if (productTypes.length == 1) {
showDialog(
context: context,
builder: (context) =>
DeviceBatchControlDialog(
builder: (context) => DeviceBatchControlDialog(
devices: selectedDevices,
),
);
@ -138,9 +130,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: isControlButtonEnabled
? Colors.white
: Colors.grey,
color: isControlButtonEnabled ? Colors.white : Colors.grey,
),
),
),
@ -176,40 +166,29 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
'Installation Date and Time',
'Status',
'Last Offline Date and Time',
'Settings'
],
data: devicesToShow.map((device) {
final combinedSpaceNames = device.spaces != null
? device.spaces!
.map((space) => space.spaceName)
.join(' > ') +
? device.spaces!.map((space) => space.spaceName).join(' > ') +
(device.community != null
? ' > ${device.community!.name}'
: '')
: (device.community != null
? device.community!.name
: '');
: (device.community != null ? device.community!.name : '');
return [
device.name ?? '',
device.productName ?? '',
device.uuid ?? '',
(device.spaces != null &&
device.spaces!.isNotEmpty)
(device.spaces != null && device.spaces!.isNotEmpty)
? device.spaces![0].spaceName
: '',
combinedSpaceNames,
device.batteryLevel != null
? '${device.batteryLevel}%'
: '-',
formatDateTime(
DateTime.fromMillisecondsSinceEpoch(
device.batteryLevel != null ? '${device.batteryLevel}%' : '-',
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
(device.createTime ?? 0) * 1000)),
device.online == true ? 'Online' : 'Offline',
formatDateTime(
DateTime.fromMillisecondsSinceEpoch(
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
(device.updateTime ?? 0) * 1000)),
'Settings',
];
}).toList(),
onSelectionChanged: (selectedRows) {
@ -223,10 +202,6 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
.map((device) => device.uuid!)
.toList(),
isEmpty: devicesToShow.isEmpty,
onSettingsPressed: (rowIndex) {
final device = devicesToShow[rowIndex];
showDeviceSettingsSidebar(context, device);
},
),
),
)
@ -238,37 +213,4 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
},
);
}
void showDeviceSettingsSidebar(BuildContext context, AllDevicesModel device) {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: "Device Settings",
transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, anim1, anim2) {
return Align(
alignment: Alignment.centerRight,
child: Material(
child: Container(
width: MediaQuery.of(context).size.width * 0.3,
color: ColorsManager.whiteColors,
child: DeviceSettingsPanel(
device: device,
onClose: () => Navigator.of(context).pop(),
),
),
),
);
},
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(anim1),
child: child,
);
},
);
}
}

View File

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

View File

@ -0,0 +1,18 @@
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_event.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/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_update_data.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presense_nobody_time.dart';
@ -23,8 +23,9 @@ class CeilingSensorBatchControlView extends StatelessWidget with HelperResponsiv
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return BlocProvider(
create: (context) => CeilingSensorBloc(deviceId: devicesIds.first)
..add(CeilingFetchDeviceStatusEvent(devicesIds)),
create: (context) => CeilingSensorBlocFactory.create(
deviceId: devicesIds.first,
)..add(CeilingFetchDeviceStatusEvent(devicesIds)),
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
builder: (context, state) {
if (state is CeilingLoadingInitialState || state is CeilingReportsLoadingState) {
@ -110,7 +111,6 @@ class CeilingSensorBatchControlView extends StatelessWidget with HelperResponsiv
),
),
),
// FirmwareUpdateWidget(deviceId: devicesIds.first, version: 4),
FactoryResetWidget(
callFactoryReset: () {
context.read<CeilingSensorBloc>().add(

View File

@ -4,6 +4,7 @@ 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_event.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/shared/sensors_widgets/presence_display_data.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_space_type.dart';
@ -28,8 +29,9 @@ class CeilingSensorControlsView extends StatelessWidget
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return BlocProvider(
create: (context) => CeilingSensorBloc(deviceId: device.uuid ?? '')
..add(CeilingInitialEvent(device.uuid ?? '')),
create: (context) => CeilingSensorBlocFactory.create(
deviceId: device.uuid ?? '',
)..add(CeilingInitialEvent(device.uuid ?? '')),
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
builder: (context, state) {
if (state is CeilingLoadingInitialState ||

View File

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

View File

@ -0,0 +1,18 @@
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,6 +5,7 @@ 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_event.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/firmware_update.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -18,7 +19,7 @@ class CurtainBatchStatusView extends StatelessWidget with HelperResponsiveLayout
Widget build(BuildContext context) {
return BlocProvider(
create: (context) =>
CurtainBloc(deviceId: devicesIds.first)..add(CurtainFetchBatchStatus(devicesIds)),
CurtainBlocFactory.create(deviceId: devicesIds.first)..add(CurtainFetchBatchStatus(devicesIds)),
child: BlocBuilder<CurtainBloc, CurtainState>(
builder: (context, state) {
if (state is CurtainStatusLoading) {

View File

@ -4,6 +4,7 @@ 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_event.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';
class CurtainStatusControlsView extends StatelessWidget
@ -15,7 +16,7 @@ class CurtainStatusControlsView extends StatelessWidget
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => CurtainBloc(deviceId: deviceId)
create: (context) => CurtainBlocFactory.create(deviceId: deviceId)
..add(CurtainFetchDeviceStatus(deviceId)),
child: BlocBuilder<CurtainBloc, CurtainState>(
builder: (context, state) {

View File

@ -1,165 +0,0 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
import 'package:syncrow_web/services/space_mana_api.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
part 'setting_bloc_event.dart';
class SettingDeviceBloc extends Bloc<DeviceSettingEvent, DeviceSettingsState> {
final String deviceId;
SettingDeviceBloc({
required this.deviceId,
}) : super(const DeviceSettingsInitial()) {
on<DeviceSettingInitialInfo>(_fetchDeviceInfo);
on<SettingBlocSaveName>(_saveName);
on<ChangeNameEvent>(_changeName);
on<SettingBlocDeleteDevice>(_deleteDevice);
on<SettingBlocFetchRooms>(_fetchRooms);
on<SettingBlocAssignRoom>(_onAssignDevice);
}
final TextEditingController nameController = TextEditingController();
List<SubSpaceModel> roomsList = [];
bool isEditingName = false;
bool _validateInputs() {
final nameError = _fullNameValidator(nameController.text);
if (nameError != null) {
CustomSnackBar.displaySnackBar(nameError);
return true;
}
return false;
}
String? _fullNameValidator(String? value) {
if (value == null) return 'name is required';
final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim();
if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) {
return 'name must be between 2 and 30 characters long';
}
if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) {
return 'Only alphanumeric characters, space, dash and single quote are allowed';
}
return null;
}
Future<void> _saveName(
SettingBlocSaveName event, Emitter<DeviceSettingsState> emit) async {
if (_validateInputs()) return;
try {
emit(DeviceSettingsLoading());
await DevicesManagementApi.putDeviceName(
deviceId: deviceId, deviceName: nameController.text);
add(DeviceSettingInitialInfo());
CustomSnackBar.displaySnackBar('Save Successfully');
emit(DeviceSettingsUpdate(deviceName: nameController.text));
} catch (e) {
emit(DeviceSettingsError(message: e.toString()));
}
}
Future<void> _fetchDeviceInfo(
DeviceSettingInitialInfo event, Emitter<DeviceSettingsState> emit) async {
try {
emit(DeviceSettingsLoading());
var response = await DevicesManagementApi.getDeviceInfo(deviceId);
DeviceInfoModel deviceInfo = DeviceInfoModel.fromJson(response);
nameController.text = deviceInfo.name;
emit(DeviceSettingsUpdate(
deviceName: nameController.text,
deviceInfo: deviceInfo,
roomsList: roomsList,
));
} catch (e) {
emit(DeviceSettingsError(message: e.toString()));
}
}
bool editName = false;
final FocusNode focusNode = FocusNode();
void _changeName(ChangeNameEvent event, Emitter<DeviceSettingsState> emit) {
emit(DeviceSettingsInitial(
deviceName: nameController.text,
deviceId: deviceId,
isEditingName: event.value ?? false,
editingNameValue: event.value?.toString() ?? '',
deviceInfo: state.deviceInfo,
));
editName = event.value!;
if (editName) {
Future.delayed(const Duration(milliseconds: 500), () {
focusNode.requestFocus();
});
} else {
add(const SettingBlocSaveName());
focusNode.unfocus();
}
emit(DeviceSettingsUpdate(
deviceName: nameController.text,
deviceInfo: state.deviceInfo,
roomsList: roomsList,
));
}
void _deleteDevice(
SettingBlocDeleteDevice event, Emitter<DeviceSettingsState> emit) async {
try {
emit(DeviceSettingsLoading());
await DevicesManagementApi.resetDevice(devicesUuid: deviceId);
CustomSnackBar.displaySnackBar('Reset Successfully');
emit(DeviceSettingsUpdate(
deviceName: nameController.text,
deviceInfo: state.deviceInfo,
roomsList: roomsList,
));
} catch (e) {
emit(DeviceSettingsError(message: e.toString()));
return;
}
}
void _onAssignDevice(
SettingBlocAssignRoom event, Emitter<DeviceSettingsState> emit) async {
try {
emit(DeviceSettingsLoading());
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
await CommunitySpaceManagementApi.assignDeviceToRoom(
communityId: event.communityUuid,
spaceId: event.spaceUuid,
subSpaceId: event.subSpaceUuid,
deviceId: deviceId,
projectId: projectUuid);
add(DeviceSettingInitialInfo());
CustomSnackBar.displaySnackBar('Save Successfully');
emit(DeviceSettingsSaveSelectionSuccess());
} catch (e) {
emit(DeviceSettingsError(message: e.toString()));
return;
}
}
void _fetchRooms(
SettingBlocFetchRooms event, Emitter<DeviceSettingsState> emit) async {
try {
emit(DeviceSettingsLoading());
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
roomsList = await CommunitySpaceManagementApi.getSubSpaceBySpaceId(
communityId: event.communityUuid,
spaceId: event.spaceUuid,
projectId: projectUuid);
emit(DeviceSettingsUpdate(
deviceName: nameController.text,
deviceInfo: state.deviceInfo,
roomsList: roomsList,
));
} catch (e) {
emit(DeviceSettingsError(message: e.toString()));
return;
}
}
}

View File

@ -1,69 +0,0 @@
part of 'setting_bloc_bloc.dart';
abstract class DeviceSettingEvent extends Equatable {
const DeviceSettingEvent();
@override
List<Object?> get props => [];
}
class SettingBlocSaveDeviceName extends DeviceSettingEvent {
final String deviceName;
final String deviceId;
const SettingBlocSaveDeviceName(
{required this.deviceName, required this.deviceId});
@override
List<Object?> get props => [deviceName, deviceId];
}
class SettingBlocStartEditingName extends DeviceSettingEvent {}
class SettingBlocCancelEditingName extends DeviceSettingEvent {}
class SettingBlocChangeEditingNameValue extends DeviceSettingEvent {
final String value;
const SettingBlocChangeEditingNameValue(this.value);
@override
List<Object?> get props => [value];
}
class SettingBlocFetchRooms extends DeviceSettingEvent {
final String communityUuid;
final String spaceUuid;
const SettingBlocFetchRooms(
{required this.communityUuid, required this.spaceUuid});
@override
List<Object?> get props => [communityUuid, spaceUuid];
}
class SettingBlocSaveName extends DeviceSettingEvent {
const SettingBlocSaveName();
}
class DeviceSettingInitialInfo extends DeviceSettingEvent {}
class ChangeNameEvent extends DeviceSettingEvent {
final bool? value;
const ChangeNameEvent({this.value});
}
class SettingBlocDeleteDevice extends DeviceSettingEvent {}
class SettingBlocAssignRoom extends DeviceSettingEvent {
final String communityUuid;
final String spaceUuid;
final String subSpaceUuid;
const SettingBlocAssignRoom({
required this.communityUuid,
required this.spaceUuid,
required this.subSpaceUuid,
});
@override
List<Object?> get props => [spaceUuid, communityUuid, subSpaceUuid];
}

View File

@ -1,81 +0,0 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
abstract class DeviceSettingsState extends Equatable {
const DeviceSettingsState({this.deviceInfo});
final DeviceInfoModel? deviceInfo;
@override
List<Object?> get props => [deviceInfo];
}
class DeviceSettingsInitial extends DeviceSettingsState {
final String deviceName;
final String deviceId;
final bool isEditingName;
final String editingNameValue;
const DeviceSettingsInitial({
this.deviceName = '',
this.deviceId = '',
this.isEditingName = false,
this.editingNameValue = '',
super.deviceInfo,
});
DeviceSettingsInitial copyWith({
String? deviceName,
String? deviceId,
bool? isEditingName,
String? editingNameValue,
}) =>
DeviceSettingsInitial(
deviceName: deviceName ?? this.deviceName,
deviceId: deviceId ?? this.deviceId,
isEditingName: isEditingName ?? this.isEditingName,
editingNameValue: editingNameValue ?? this.editingNameValue,
);
@override
List<Object?> get props =>
[deviceName, deviceId, isEditingName, editingNameValue];
}
class DeviceSettingsLoading extends DeviceSettingsState {}
class DeviceSettingsUpdate extends DeviceSettingsState {
final String? deviceName;
final List<SubSpaceModel> roomsList;
const DeviceSettingsUpdate({
this.deviceName,
super.deviceInfo,
this.roomsList = const [],
});
@override
List<Object?> get props => [deviceName, deviceInfo, roomsList];
}
class DeviceSettingsError extends DeviceSettingsState {
final String message;
const DeviceSettingsError({required this.message});
@override
List<Object?> get props => [message];
}
class DeviceSettingsFetchRooms extends DeviceSettingsState {
final List<SubSpaceModel> roomsList;
const DeviceSettingsFetchRooms({required this.roomsList});
@override
List<Object?> get props => [roomsList];
}
class DeviceSettingsSaveSelectionSuccess extends DeviceSettingsState {}
class ChangeNameState extends DeviceSettingsState {}

View File

@ -1,28 +0,0 @@
import 'package:syncrow_web/utils/constants/assets.dart';
class DeviceIconTypeHelper {
static const Map<String, String> _iconMap = {
'AC': Assets.ac,
'GW': Assets.gateway,
'CPS': Assets.sensors,
'DL': Assets.doorLock,
'WPS': Assets.sensors,
'3G': Assets.gangSwitch,
'2G': Assets.twoGang,
'1G': Assets.oneGang,
'CUR': Assets.curtain,
'WH': Assets.waterHeater,
'DS': Assets.doorSensor,
'1GT': Assets.oneTouchSwitch,
'2GT': Assets.twoTouchSwitch,
'3GT': Assets.threeTouchSwitch,
'GD': Assets.garageDoor,
'WL': Assets.waterLeakNormal,
'NCPS': Assets.sensors,
};
static String getDeviceIconByTypeCode(String? typeCode) {
if (typeCode == null) return Assets.logoHorizontal;
return _iconMap[typeCode] ?? Assets.logoHorizontal;
}
}

View File

@ -1,127 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/sub_space_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/web_layout/default_container.dart';
class DeviceManagementContent extends StatelessWidget {
const DeviceManagementContent({
super.key,
required this.device,
required this.subSpaces,
required this.deviceInfo,
});
final AllDevicesModel device;
final List<SubSpaceModel> subSpaces;
final DeviceInfoModel deviceInfo;
@override
Widget build(BuildContext context) {
Widget infoRow(
{required String label,
required String value,
Widget? trailing,
required Color? valueColor}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: context.theme.textTheme.bodyMedium!.copyWith(
fontSize: 14,
color: ColorsManager.grayColor,
),
),
Expanded(
child: Text(
value,
textAlign: TextAlign.end,
style: context.theme.textTheme.bodyMedium!
.copyWith(fontSize: 14, color: valueColor),
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
trailing ?? const SizedBox.shrink(),
],
),
);
}
return DefaultContainer(
padding: EdgeInsets.zero,
child: Column(
children: [
const SizedBox(height: 5),
Padding(
padding: const EdgeInsets.all(10.0),
child: InkWell(
onTap: () {
showSubSpaceDialog(
context,
communityUuid: device.community!.uuid!,
spaceUuid: device.spaces!.first.uuid!,
subSpaces: subSpaces,
selected: device.subspace!.uuid,
);
},
child: infoRow(
label: 'Sub-Space:',
value: deviceInfo.subspace.subspaceName,
valueColor: ColorsManager.textGray,
trailing: const Icon(
Icons.arrow_forward_ios,
size: 16,
color: ColorsManager.greyColor,
),
),
),
),
const Divider(color: ColorsManager.dividerColor),
Padding(
padding: const EdgeInsets.all(10.0),
child: infoRow(
label: 'Virtual Address:',
value: deviceInfo.productUuid,
valueColor: ColorsManager.blackColor,
trailing: InkWell(
onTap: () {
Clipboard.setData(
ClipboardData(text: device.productUuid ?? ''),
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Virtual Address copied to clipboard'),
),
);
},
child: const Icon(
Icons.copy,
size: 16,
color: ColorsManager.greyColor,
),
),
),
),
const Divider(color: ColorsManager.dividerColor),
Padding(
padding: const EdgeInsets.all(10.0),
child: infoRow(
label: 'MAC Address:',
valueColor: ColorsManager.blackColor,
value: deviceInfo.macAddress,
),
),
const SizedBox(height: 5),
],
),
);
}
}

View File

@ -1,184 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/device_icon_type_helper.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/device_management_content.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/remove_device_widget.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/web_layout/default_container.dart';
class DeviceSettingsPanel extends StatelessWidget {
final VoidCallback? onClose;
final AllDevicesModel device;
const DeviceSettingsPanel({super.key, this.onClose, required this.device});
@override
Widget build(BuildContext context) {
final sectionTitle = context.theme.textTheme.titleMedium!.copyWith(
fontWeight: FontWeight.bold,
color: ColorsManager.grayColor,
);
return BlocProvider(
create: (context) => SettingDeviceBloc(
deviceId: device.uuid ?? '',
)
..add(DeviceSettingInitialInfo())
..add(SettingBlocFetchRooms(
communityUuid: device.community!.uuid!,
spaceUuid: device.spaces!.first.uuid!,
)),
child: Builder(
builder: (context) {
return BlocBuilder<SettingDeviceBloc, DeviceSettingsState>(
builder: (context, state) {
final _bloc = context.read<SettingDeviceBloc>();
final iconPath = DeviceIconTypeHelper.getDeviceIconByTypeCode(
device.productType);
final deviceInfo = state is DeviceSettingsUpdate
? state.deviceInfo ?? DeviceInfoModel.empty()
: DeviceInfoModel.empty();
final subSpaces =
state is DeviceSettingsUpdate ? state.roomsList ?? [] : [];
return Stack(
children: [
Container(
width: MediaQuery.of(context).size.width * 0.3,
color: ColorsManager.grey25,
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 24),
child: ListView(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: SvgPicture.asset(Assets.closeSettingsIcon),
onPressed:
onClose ?? () => Navigator.of(context).pop(),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Device Settings',
style:
context.theme.textTheme.titleLarge!.copyWith(
fontWeight: FontWeight.bold,
color: ColorsManager.primaryColor,
),
),
],
),
const SizedBox(height: 24),
DefaultContainer(
child: Row(
children: [
CircleAvatar(
radius: 40,
backgroundColor:
const Color.fromARGB(177, 213, 213, 213),
child: CircleAvatar(
backgroundColor: ColorsManager.whiteColors,
radius: 36,
child: SvgPicture.asset(
iconPath,
fit: BoxFit.cover,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Device Name:',
style: context.textTheme.bodyMedium!
.copyWith(
color: ColorsManager.grayColor,
),
),
TextFormField(
maxLength: 30,
style: const TextStyle(
color: ColorsManager.blackColor,
),
textAlign: TextAlign.start,
focusNode: _bloc.focusNode,
controller: _bloc.nameController,
enabled: _bloc.editName,
onFieldSubmitted: (value) {
_bloc.add(const ChangeNameEvent(
value: false));
},
decoration: const InputDecoration(
border: InputBorder.none,
fillColor: Colors.white10,
counterText: '',
),
),
],
),
),
const SizedBox(width: 8),
Visibility(
visible: _bloc.editName != true,
replacement: const SizedBox(),
child: GestureDetector(
onTap: () {
_bloc.add(
const ChangeNameEvent(value: true));
},
child: SvgPicture.asset(
Assets.editNameIconSettings,
color: ColorsManager.grayColor,
height: 20,
width: 20,
),
),
)
],
),
),
const SizedBox(height: 32),
Text('Device Management', style: sectionTitle),
DeviceManagementContent(
device: device,
subSpaces: subSpaces.cast<SubSpaceModel>(),
deviceInfo: deviceInfo,
),
const SizedBox(height: 32),
RemoveDeviceWidget(bloc: _bloc),
],
),
),
if (state is DeviceSettingsLoading)
Positioned.fill(
child: Container(
color: Colors.black.withOpacity(0.1),
child: const Center(
child: CircularProgressIndicator(
color: ColorsManager.primaryColor,
),
),
),
),
],
);
},
);
},
),
);
}
}

View File

@ -1,82 +0,0 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/web_layout/default_container.dart';
class RemoveDeviceWidget extends StatelessWidget {
const RemoveDeviceWidget({
super.key,
required SettingDeviceBloc bloc,
}) : _bloc = bloc;
final SettingDeviceBloc _bloc;
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
child: InkWell(
onTap: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(
'Remove Device',
style: context.textTheme.bodyMedium!.copyWith(
fontWeight: FontWeight.w700,
color: ColorsManager.red,
),
),
content: Text(
'Are you sure you want to remove this device?',
style: context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.grayColor,
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'Cancel',
style: context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.grayColor,
),
),
),
TextButton(
onPressed: () {
_bloc.add(SettingBlocDeleteDevice());
Navigator.of(context).pop();
},
child: Text(
'Remove',
style: context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.red,
),
),
),
],
);
},
);
},
child: DefaultContainer(
padding: const EdgeInsets.all(25),
child: Center(
child: Text(
'Remove Device',
style: context.textTheme.bodyMedium!.copyWith(
fontSize: 14,
color: ColorsManager.red,
fontWeight: FontWeight.w700),
),
),
),
),
);
}
}

View File

@ -1,183 +0,0 @@
class DeviceInfoModel {
final int activeTime;
final String category;
final String categoryName;
final int createTime;
final String gatewayId;
final String icon;
final String ip;
final String lat;
final String localKey;
final String lon;
final String model;
final String name;
final String nodeId;
final bool online;
final String ownerId;
final String productName;
final bool sub;
final String timeZone;
final int updateTime;
final String uuid;
final String productUuid;
final String productType;
final String permissionType;
final String macAddress;
final Subspace subspace;
DeviceInfoModel({
required this.activeTime,
required this.category,
required this.categoryName,
required this.createTime,
required this.gatewayId,
required this.icon,
required this.ip,
required this.lat,
required this.localKey,
required this.lon,
required this.model,
required this.name,
required this.nodeId,
required this.online,
required this.ownerId,
required this.productName,
required this.sub,
required this.timeZone,
required this.updateTime,
required this.uuid,
required this.productUuid,
required this.productType,
required this.permissionType,
required this.macAddress,
required this.subspace,
});
factory DeviceInfoModel.fromJson(Map<String, dynamic> json) {
return DeviceInfoModel(
activeTime: json['activeTime'] as int? ?? 0,
category: json['category'] ?? '',
categoryName: json['categoryName'] as String? ?? '',
createTime: json['createTime'] as int? ?? 0,
gatewayId: json['gatewayId'] as String? ?? '',
icon: json['icon'] as String? ?? '',
ip: json['ip'] as String? ?? '',
lat: json['lat'] as String? ?? '',
localKey: json['localKey'] as String? ?? '',
lon: json['lon'] as String? ?? '',
model: json['model'] as String? ?? '',
name: json['name'] as String? ?? '',
nodeId: json['nodeId'] as String? ?? '',
online: json['online'] as bool? ?? false,
ownerId: json['ownerId'] as String? ?? '',
productName: json['productName'] as String? ?? '',
sub: json['sub'] as bool? ?? false,
timeZone: json['timeZone'] as String? ?? '',
updateTime: json['updateTime'] as int? ?? 0,
uuid: json['uuid'] as String? ?? '',
productUuid: json['productUuid'] as String? ?? '',
productType: json['productType'] as String? ?? '',
permissionType: json['permissionType'] as String? ?? '',
macAddress: json['macAddress'] as String? ?? '',
subspace:
Subspace.fromJson(json['subspace'] as Map<String, dynamic>? ?? {}),
);
}
Map<String, dynamic> toJson() {
return {
'activeTime': activeTime,
'category': category,
'categoryName': categoryName,
'createTime': createTime,
'gatewayId': gatewayId,
'icon': icon,
'ip': ip,
'lat': lat,
'localKey': localKey,
'lon': lon,
'model': model,
'name': name,
'nodeId': nodeId,
'online': online,
'ownerId': ownerId,
'productName': productName,
'sub': sub,
'timeZone': timeZone,
'updateTime': updateTime,
'uuid': uuid,
'productUuid': productUuid,
'productType': productType,
'permissionType': permissionType,
'macAddress': macAddress,
'subspace': subspace.toJson(),
};
}
static DeviceInfoModel empty() {
return DeviceInfoModel(
activeTime: 0,
category: '',
categoryName: '',
createTime: 0,
gatewayId: '',
icon: '',
ip: '',
lat: '',
localKey: '',
lon: '',
model: '',
name: '',
nodeId: '',
online: false,
ownerId: '',
productName: '',
sub: false,
timeZone: '',
updateTime: 0,
uuid: '',
productUuid: '',
productType: '',
permissionType: '',
macAddress: '',
subspace: Subspace(
uuid: '',
createdAt: '',
updatedAt: '',
subspaceName: '',
),
);
}
}
class Subspace {
final String uuid;
final String createdAt;
final String updatedAt;
final String subspaceName;
Subspace({
required this.uuid,
required this.createdAt,
required this.updatedAt,
required this.subspaceName,
});
factory Subspace.fromJson(Map<String, dynamic> json) {
return Subspace(
uuid: json['uuid'] as String? ?? '',
createdAt: json['createdAt'] as String? ?? '',
updatedAt: json['updatedAt'] as String? ?? '',
subspaceName: json['subspaceName'] as String? ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'uuid': uuid,
'createdAt': createdAt,
'updatedAt': updatedAt,
'subspaceName': subspaceName,
};
}
}

View File

@ -1,35 +0,0 @@
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
class SubSpaceModel {
final String? id;
final String? name;
List<DeviceModel>? devices;
SubSpaceModel({
required this.id,
required this.name,
required this.devices,
});
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'devices': devices?.map((device) => device.toJson()).toList(),
};
}
factory SubSpaceModel.fromJson(Map<String, dynamic> json) {
List<DeviceModel> devices = [];
if (json['devices'] != null) {
for (var device in json['devices']) {
devices.add(DeviceModel.fromJson(device));
}
}
return SubSpaceModel(
id: json['uuid'] as String? ?? '',
name: json['subspaceName'] as String? ?? '',
devices: devices.isNotEmpty ? devices : null as List<DeviceModel>?,
);
}
}

View File

@ -1,115 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/subspace_dialog_buttons.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class SubSpaceDialog extends StatefulWidget {
final List<SubSpaceModel> subSpaces;
final String? selected;
final void Function(SubSpaceModel?) onConfirmed;
const SubSpaceDialog({
Key? key,
required this.subSpaces,
this.selected,
required this.onConfirmed,
}) : super(key: key);
@override
State<SubSpaceDialog> createState() => _SubSpaceDialogState();
}
class _SubSpaceDialogState extends State<SubSpaceDialog> {
String? _selectedId;
@override
void initState() {
super.initState();
_selectedId = widget.selected;
}
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: ColorsManager.whiteColors,
insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 60),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),
),
child: Container(
width: MediaQuery.of(context).size.width * 0.35,
padding: const EdgeInsets.fromLTRB(0, 24, 0, 0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Sub-Space',
style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w700,
color: ColorsManager.blueColor,
fontSize: 20),
),
const Divider(),
const SizedBox(height: 10),
...widget.subSpaces.map((space) {
return RadioListTile<String>(
value: space.id!,
groupValue: _selectedId,
onChanged: (value) {
setState(() {
_selectedId = value;
});
},
activeColor: Color(0xFF2962FF),
title: Text(
space.name ?? 'Unnamed Sub-Space',
style: context.textTheme.bodyMedium?.copyWith(
fontSize: 15,
color: ColorsManager.grayColor,
fontWeight: FontWeight.w400,
),
),
controlAffinity: ListTileControlAffinity.trailing,
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
);
}).toList(),
const SizedBox(height: 12),
const Divider(height: 1, thickness: 1),
SubSpaceDialogButtons(selectedId: _selectedId, widget: widget),
],
),
),
);
}
}
void showSubSpaceDialog(
BuildContext context, {
required List<SubSpaceModel> subSpaces,
String? selected,
required String communityUuid,
required String spaceUuid,
}) {
showDialog(
context: context,
barrierDismissible: true,
builder: (ctx) => SubSpaceDialog(
subSpaces: subSpaces,
selected: selected,
onConfirmed: (selectedModel) {
if (selectedModel != null) {
context.read<SettingDeviceBloc>().add(
SettingBlocAssignRoom(
communityUuid: communityUuid,
spaceUuid: spaceUuid,
subSpaceUuid: selectedModel.id ?? '',
),
);
}
},
),
);
}

View File

@ -1,114 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/sub_space_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class SubSpaceDialogButtons extends StatelessWidget {
const SubSpaceDialogButtons({
super.key,
required String? selectedId,
required this.widget,
}) : _selectedId = selectedId;
final String? _selectedId;
final SubSpaceDialog widget;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 50,
child: Row(
children: [
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.dividerColor,
width: 0.5,
),
),
),
child: TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'Cancel',
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.textGray,
fontSize: 18,
fontWeight: FontWeight.w500,
),
),
),
),
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
left: BorderSide(
color: ColorsManager.dividerColor,
width: 0.5,
),
),
),
child: TextButton(
onPressed: _selectedId == null
? null
: () {
final selectedModel = widget.subSpaces.firstWhere(
(space) => space.id == _selectedId,
orElse: () =>
SubSpaceModel(id: null, name: '', devices: []));
widget.onConfirmed(selectedModel);
Navigator.of(context).pop();
},
child: Text(
'Confirm',
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.secondaryColor,
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
),
),
),
],
),
);
}
}
void showSubSpaceDialog(
BuildContext context, {
required List<SubSpaceModel> subSpaces,
String? selected,
required String communityUuid,
required String spaceUuid,
}) {
showDialog(
context: context,
barrierDismissible: true,
builder: (ctx) => SubSpaceDialog(
subSpaces: subSpaces,
selected: selected,
onConfirmed: (selectedModel) {
if (selectedModel != null) {
context.read<SettingDeviceBloc>().add(
SettingBlocAssignRoom(
communityUuid: communityUuid,
spaceUuid: spaceUuid,
subSpaceUuid: selectedModel.id ?? '',
),
);
}
},
),
);
}

View File

@ -0,0 +1,18 @@
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,6 +1,5 @@
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/services/batch_control_devices_service.dart';
import 'package:syncrow_web/services/control_device_service.dart';
abstract final class FlushMountedPresenceSensorBlocFactory {
const FlushMountedPresenceSensorBlocFactory._();
@ -10,12 +9,8 @@ abstract final class FlushMountedPresenceSensorBlocFactory {
}) {
return FlushMountedPresenceSensorBloc(
deviceId: deviceId,
controlDeviceService: DebouncedControlDeviceService(
decoratee: RemoteControlDeviceService(),
),
batchControlDevicesService: DebouncedBatchControlDevicesService(
decoratee: RemoteBatchControlDevicesService(),
),
controlDeviceService: DeviceBlocDependenciesFactory.createControlDeviceService(),
batchControlDevicesService: DeviceBlocDependenciesFactory.createBatchControlDevicesService(),
);
}
}

View File

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

@ -0,0 +1,18 @@
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: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/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/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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -16,7 +16,7 @@ class OneGangGlassSwitchBatchControlView extends StatelessWidget with HelperResp
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => OneGangGlassSwitchBloc(deviceId: deviceIds.first)
create: (context) => OneGangGlassSwitchBlocFactory.create(deviceId: deviceIds.first)
..add(OneGangGlassSwitchFetchBatchStatusEvent(deviceIds)),
child: BlocBuilder<OneGangGlassSwitchBloc, OneGangGlassSwitchState>(
builder: (context, state) {

View File

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

View File

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

View File

@ -0,0 +1,18 @@
import 'package:syncrow_web/pages/device_managment/factories/device_bloc_dependencies_factory.dart';
import 'package:syncrow_web/pages/device_managment/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_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/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/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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -18,7 +18,7 @@ class WallLightBatchControlView extends StatelessWidget with HelperResponsiveLay
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => WallLightSwitchBloc(deviceId: deviceIds.first)
create: (context) => WallLightSwitchBlocFactory.create(deviceId: deviceIds.first)
..add(WallLightSwitchFetchBatchEvent(deviceIds)),
child: BlocBuilder<WallLightSwitchBloc, WallLightSwitchState>(
builder: (context, state) {

View File

@ -3,6 +3,7 @@ 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_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/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/shared/toggle_widget.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -15,7 +16,7 @@ class WallLightDeviceControl extends StatelessWidget
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => WallLightSwitchBloc(deviceId: deviceId)
create: (context) => WallLightSwitchBlocFactory.create(deviceId: deviceId)
..add(WallLightSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<WallLightSwitchBloc, WallLightSwitchState>(
builder: (context, state) {

View File

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

View File

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

View File

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

View File

@ -0,0 +1,18 @@
import 'package:syncrow_web/pages/device_managment/factories/device_bloc_dependencies_factory.dart';
import 'package:syncrow_web/pages/device_managment/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,6 +5,7 @@ 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/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/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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -16,7 +17,7 @@ class ThreeGangGlassSwitchBatchControlView extends StatelessWidget with HelperRe
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ThreeGangGlassSwitchBloc(deviceId: deviceIds.first)
create: (context) => ThreeGangGlassSwitchBlocFactory.create(deviceId: deviceIds.first)
..add(ThreeGangGlassSwitchFetchBatchStatusEvent(deviceIds)),
child: BlocBuilder<ThreeGangGlassSwitchBloc, ThreeGangGlassSwitchState>(
builder: (context, state) {

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.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/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/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -16,7 +17,7 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget with HelperRespons
Widget build(BuildContext context) {
return BlocProvider(
create: (context) =>
ThreeGangGlassSwitchBloc(deviceId: deviceId)..add(ThreeGangGlassSwitchFetchDeviceEvent(deviceId)),
ThreeGangGlassSwitchBlocFactory.create(deviceId: deviceId)..add(ThreeGangGlassSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<ThreeGangGlassSwitchBloc, ThreeGangGlassSwitchState>(
builder: (context, state) {
if (state is ThreeGangGlassSwitchLoading) {

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,18 @@
import 'package:syncrow_web/pages/device_managment/factories/device_bloc_dependencies_factory.dart';
import 'package:syncrow_web/pages/device_managment/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,6 +5,7 @@ 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/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/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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -16,7 +17,7 @@ class TwoGangGlassSwitchBatchControlView extends StatelessWidget with HelperResp
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => TwoGangGlassSwitchBloc(deviceId: deviceIds.first)
create: (context) => TwoGangGlassSwitchBlocFactory.create(deviceId: deviceIds.first)
..add(TwoGangGlassSwitchFetchBatchStatusEvent(deviceIds)),
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
builder: (context, state) {

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.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/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/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -15,7 +16,7 @@ class TwoGangGlassSwitchControlView extends StatelessWidget
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => TwoGangGlassSwitchBloc(deviceId: deviceId)
create: (context) => TwoGangGlassSwitchBlocFactory.create(deviceId: deviceId)
..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
builder: (context, state) {

View File

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

@ -0,0 +1,18 @@
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) {
switch (status.code) {
case 'switch_1':
switch1 = status.value ?? false;
switch1 = bool.tryParse(status.value.toString()) ?? false;
break;
case 'countdown_1':
countDown = status.value ?? 0;
countDown = int.tryParse(status.value.toString()) ?? 0;
break;
case 'switch_2':
switch2 = status.value ?? false;
switch2 = bool.tryParse(status.value.toString()) ?? false;
break;
case 'countdown_2':
countDown2 = status.value ?? 0;
countDown2 = int.tryParse(status.value.toString()) ?? 0;
break;
}
}

View File

@ -2,11 +2,11 @@ import 'package:flutter/material.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/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/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_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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -18,7 +18,7 @@ class TwoGangBatchControlView extends StatelessWidget with HelperResponsiveLayou
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => TwoGangSwitchBloc(deviceId: deviceIds.first)
create: (context) => TwoGangSwitchBlocFactory.create(deviceId: deviceIds.first)
..add(TwoGangSwitchFetchBatchEvent(deviceIds)),
child: BlocBuilder<TwoGangSwitchBloc, TwoGangSwitchState>(
builder: (context, state) {

View File

@ -4,6 +4,7 @@ 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_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/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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -15,7 +16,7 @@ class TwoGangDeviceControlView extends StatelessWidget
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => TwoGangSwitchBloc(deviceId: deviceId)
create: (context) => TwoGangSwitchBlocFactory.create(deviceId: deviceId)
..add(TwoGangSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<TwoGangSwitchBloc, TwoGangSwitchState>(
builder: (context, state) {

View File

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

@ -0,0 +1,18 @@
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,6 +7,7 @@ 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_event.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/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -21,7 +22,7 @@ class WallSensorBatchControlView extends StatelessWidget with HelperResponsiveLa
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return BlocProvider(
create: (context) => WallSensorBloc(deviceId: devicesIds.first)
create: (context) => WallSensorBlocFactory.create(deviceId: devicesIds.first)
..add(WallSensorFetchBatchStatusEvent(devicesIds)),
child: BlocBuilder<WallSensorBloc, WallSensorState>(
builder: (context, state) {

View File

@ -10,6 +10,7 @@ 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_status.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/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -26,7 +27,7 @@ class WallSensorControlsView extends StatelessWidget with HelperResponsiveLayout
final isMedium = isMediumScreenSize(context);
return BlocProvider(
create: (context) =>
WallSensorBloc(deviceId: device.uuid!)..add(WallSensorFetchStatusEvent()),
WallSensorBlocFactory.create(deviceId: device.uuid!)..add(WallSensorFetchStatusEvent()),
child: BlocBuilder<WallSensorBloc, WallSensorState>(
builder: (context, state) {
if (state is WallSensorLoadingInitialState || state is DeviceReportsLoadingState) {

View File

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

View File

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

View File

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

View File

@ -1,15 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter_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/firmware_update.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/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/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class WaterHEaterBatchControlView extends StatelessWidget with HelperResponsiveLayout {
class WaterHEaterBatchControlView extends StatelessWidget
with HelperResponsiveLayout {
const WaterHEaterBatchControlView({super.key, required this.deviceIds});
final List<String> deviceIds;
@ -17,8 +18,9 @@ class WaterHEaterBatchControlView extends StatelessWidget with HelperResponsiveL
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) =>
WaterHeaterBloc()..add(FetchWaterHeaterBatchStatusEvent(devicesUuid: deviceIds)),
create: (context) => WaterHeaterBlocFactory.create(
deviceId: deviceIds.first,
)..add(FetchWaterHeaterBatchStatusEvent(devicesUuid: deviceIds)),
child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
builder: (context, state) {
if (state is WaterHeaterLoadingState) {

View File

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

View File

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

View File

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

View File

@ -91,8 +91,7 @@ class DevicesManagementApi {
}
}
Future<bool> deviceBatchControl(
List<String> uuids, String code, dynamic value) async {
Future<bool> deviceBatchControl(List<String> uuids, String code, dynamic value) async {
try {
final body = {
'devicesUuid': uuids,
@ -117,8 +116,7 @@ class DevicesManagementApi {
}
}
static Future<List<DeviceModel>> getDevicesByGatewayId(
String gatewayId) async {
static Future<List<DeviceModel>> getDevicesByGatewayId(String gatewayId) async {
final response = await HTTPService().get(
path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId),
showServerMessage: false,
@ -152,9 +150,7 @@ class DevicesManagementApi {
String code,
) async {
final response = await HTTPService().get(
path: ApiEndpoints.getDeviceLogs
.replaceAll('{uuid}', uuid)
.replaceAll('{code}', code),
path: ApiEndpoints.getDeviceLogs.replaceAll('{uuid}', uuid).replaceAll('{code}', code),
showServerMessage: false,
expectedResponseModel: (json) {
return DeviceReport.fromJson(json['data']);
@ -227,8 +223,7 @@ class DevicesManagementApi {
}
}
Future<bool> addScheduleRecord(
ScheduleEntry sendSchedule, String uuid) async {
Future<bool> addScheduleRecord(ScheduleEntry sendSchedule, String uuid) async {
try {
final response = await HTTPService().post(
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
@ -245,8 +240,7 @@ class DevicesManagementApi {
}
}
Future<List<ScheduleModel>> getDeviceSchedules(
String uuid, String category) async {
Future<List<ScheduleModel>> getDeviceSchedules(String uuid, String category) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.getScheduleByDeviceId
@ -269,9 +263,7 @@ class DevicesManagementApi {
}
Future<bool> updateScheduleRecord(
{required bool enable,
required String uuid,
required String scheduleId}) async {
{required bool enable, required String uuid, required String scheduleId}) async {
try {
final response = await HTTPService().put(
path: ApiEndpoints.updateScheduleByDeviceId
@ -292,8 +284,7 @@ class DevicesManagementApi {
}
}
Future<bool> editScheduleRecord(
String uuid, ScheduleEntry newSchedule) async {
Future<bool> editScheduleRecord(String uuid, ScheduleEntry newSchedule) async {
try {
final response = await HTTPService().put(
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
@ -344,46 +335,4 @@ class DevicesManagementApi {
return false;
}
}
static Future<Map<String, dynamic>> putDeviceName(
{required String deviceId, required String deviceName}) async {
try {
final response = await HTTPService().put(
path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId),
body: {"deviceName": deviceName},
expectedResponseModel: (json) {
return json['data'];
},
);
return response;
} catch (e) {
rethrow;
}
}
static Future getDeviceInfo(String deviceId) async {
final response = await HTTPService().get(
path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json['data'] as Map<String, dynamic>;
});
return response;
}
static Future resetDevice({
String? devicesUuid,
}) async {
final response = await HTTPService().post(
path: ApiEndpoints.resetDevice.replaceAll('{deviceUuid}', devicesUuid!),
showServerMessage: false,
body: {
"devicesUuid": [devicesUuid]
},
expectedResponseModel: (json) {
return json;
},
);
return response;
}
}

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
import 'package:syncrow_web/pages/space_tree/model/pagination_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
@ -13,16 +12,14 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
class CommunitySpaceManagementApi {
// Community Management APIs
Future<List<CommunityModel>> fetchCommunities(String projectId,
{int page = 1}) async {
Future<List<CommunityModel>> fetchCommunities(String projectId, {int page = 1}) async {
try {
List<CommunityModel> allCommunities = [];
bool hasNext = true;
while (hasNext) {
await HTTPService().get(
path: ApiEndpoints.getCommunityList
.replaceAll('{projectId}', projectId),
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
queryParameters: {
'page': page,
},
@ -58,14 +55,8 @@ class CommunitySpaceManagementApi {
try {
bool hasNext = false;
await HTTPService().get(
path:
ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
queryParameters: {
'page': page,
'includeSpaces': true,
'size': 25,
'search': search
},
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
queryParameters: {'page': page, 'includeSpaces': true, 'size': 25, 'search': search},
expectedResponseModel: (json) {
try {
List<dynamic> jsonData = json['data'] ?? [];
@ -77,10 +68,7 @@ class CommunitySpaceManagementApi {
page = currentPage + 1;
paginationModel = PaginationModel(
pageNum: page,
hasNext: hasNext,
size: 25,
communities: communityList);
pageNum: page, hasNext: hasNext, size: 25, communities: communityList);
return paginationModel;
} catch (_) {
hasNext = false;
@ -95,8 +83,7 @@ class CommunitySpaceManagementApi {
Future<CommunityModel?> getCommunityById(String communityId) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.getCommunityById
.replaceAll('{communityId}', communityId),
path: ApiEndpoints.getCommunityById.replaceAll('{communityId}', communityId),
expectedResponseModel: (json) {
return CommunityModel.fromJson(json['data']);
},
@ -108,8 +95,7 @@ class CommunitySpaceManagementApi {
}
}
Future<CommunityModel?> createCommunity(
String name, String description, String projectId) async {
Future<CommunityModel?> createCommunity(String name, String description, String projectId) async {
try {
final response = await HTTPService().post(
path: ApiEndpoints.createCommunity.replaceAll('{projectId}', projectId),
@ -128,8 +114,7 @@ class CommunitySpaceManagementApi {
}
}
Future<bool> updateCommunity(
String communityId, String name, String projectId) async {
Future<bool> updateCommunity(String communityId, String name, String projectId) async {
try {
final response = await HTTPService().put(
path: ApiEndpoints.updateCommunity
@ -166,8 +151,7 @@ class CommunitySpaceManagementApi {
}
}
Future<SpacesResponse> fetchSpaces(
String communityId, String projectId) async {
Future<SpacesResponse> fetchSpaces(String communityId, String projectId) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.listSpaces
@ -193,8 +177,7 @@ class CommunitySpaceManagementApi {
}
}
Future<SpaceModel?> getSpace(
String communityId, String spaceId, String projectId) async {
Future<SpaceModel?> getSpace(String communityId, String spaceId, String projectId) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.getSpace
@ -306,8 +289,7 @@ class CommunitySpaceManagementApi {
}
}
Future<bool> deleteSpace(
String communityId, String spaceId, String projectId) async {
Future<bool> deleteSpace(String communityId, String spaceId, String projectId) async {
try {
final response = await HTTPService().delete(
path: ApiEndpoints.deleteSpace
@ -325,17 +307,15 @@ class CommunitySpaceManagementApi {
}
}
Future<List<SpaceModel>> getSpaceHierarchy(
String communityId, String projectId) async {
Future<List<SpaceModel>> getSpaceHierarchy(String communityId, String projectId) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.getSpaceHierarchy
.replaceAll('{communityId}', communityId)
.replaceAll('{projectId}', projectId),
expectedResponseModel: (json) {
final spaceModels = (json['data'] as List)
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
.toList();
final spaceModels =
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
return spaceModels;
},
@ -347,17 +327,15 @@ class CommunitySpaceManagementApi {
}
}
Future<List<SpaceModel>> getSpaceOnlyWithDevices(
{String? communityId, String? projectId}) async {
Future<List<SpaceModel>> getSpaceOnlyWithDevices({String? communityId, String? projectId}) async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.spaceOnlyWithDevices
.replaceAll('{communityId}', communityId!)
.replaceAll('{projectId}', projectId!),
expectedResponseModel: (json) {
final spaceModels = (json['data'] as List)
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
.toList();
final spaceModels =
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
return spaceModels;
},
);
@ -367,59 +345,4 @@ class CommunitySpaceManagementApi {
return [];
}
}
static Future<List<SubSpaceModel>> getSubSpaceBySpaceId(
{required String communityId,
required String spaceId,
required String projectId}) async {
try {
final path = ApiEndpoints.listSubspace
.replaceFirst('{communityUuid}', communityId)
.replaceFirst('{spaceUuid}', spaceId)
.replaceAll('{projectUuid}', projectId);
final response = await HTTPService().get(
path: path,
queryParameters: {"page": 1, "pageSize": 10},
showServerMessage: false,
expectedResponseModel: (json) {
List<SubSpaceModel> rooms = [];
if (json['data'] != null) {
for (var subspace in json['data']) {
rooms.add(SubSpaceModel.fromJson(subspace));
}
}
return rooms;
},
);
return response;
} catch (error, stackTrace) {
return [];
}
}
static Future<Map<String, dynamic>> assignDeviceToRoom(
{required String communityId,
required String spaceId,
required String subSpaceId,
required String deviceId,
required String projectId}) async {
try {
final response = await HTTPService().post(
path: ApiEndpoints.assignDeviceToRoom
.replaceAll('{projectUuid}', projectId)
.replaceAll('{communityUuid}', communityId)
.replaceAll('{spaceUuid}', spaceId)
.replaceAll('{subSpaceUuid}', subSpaceId)
.replaceAll('{deviceUuid}', deviceId),
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
}

View File

@ -83,7 +83,4 @@ abstract class ColorsManager {
static const Color maxPurpleDot = Color(0xFF5F00BD);
static const Color minBlue = Color(0xFF93AAFD);
static const Color minBlueDot = Color(0xFF023DFE);
static const Color grey25 = Color(0xFFF9F9F9);
}

View File

@ -60,12 +60,9 @@ abstract class ApiEndpoints {
'/devices/{uuid}/report-logs?code={code}&startTime={startTime}&endTime={endTime}';
static const String scheduleByDeviceId = '/schedule/{deviceUuid}';
static const String getScheduleByDeviceId =
'/schedule/{deviceUuid}?category={category}';
static const String deleteScheduleByDeviceId =
'/schedule/{deviceUuid}/{scheduleUuid}';
static const String updateScheduleByDeviceId =
'/schedule/enable/{deviceUuid}';
static const String getScheduleByDeviceId = '/schedule/{deviceUuid}?category={category}';
static const String deleteScheduleByDeviceId = '/schedule/{deviceUuid}/{scheduleUuid}';
static const String updateScheduleByDeviceId = '/schedule/enable/{deviceUuid}';
static const String factoryReset = '/devices/batch';
//product
@ -127,13 +124,4 @@ abstract class ApiEndpoints {
'/projects/{projectId}/communities/{communityId}/spaces/{unitUuid}/automations';
static const String spaceOnlyWithDevices =
'/projects/{projectId}/communities/{communityId}/spaces?onlyWithDevices=true';
static const String listSubspace =
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces';
static const String deviceByUuid = '/devices/{deviceUuid}';
static const String resetDevice = '/factory/reset/{deviceUuid}';
static const String assignDeviceToRoom =
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices/{deviceUuid}';
}

View File

@ -452,13 +452,6 @@ class Assets {
'assets/icons/refresh_status_icon.svg';
static const String energyConsumedIcon =
'assets/icons/energy_consumed_icon.svg';
static const String closeSettingsIcon =
'assets/icons/close_settings_icon.svg';
static const String editNameIconSettings =
'assets/icons/edit_name_icon_settings.svg';
static const String locationPin = 'assets/icons/location_pin.svg';
static const String aqiTemperature = 'assets/icons/aqi_temperature.svg';
static const String aqiHumidity = 'assets/icons/aqi_humidity.svg';

View File

@ -1,45 +0,0 @@
import 'package:flutter/material.dart';
class DefaultContainer extends StatelessWidget {
const DefaultContainer({
super.key,
required this.child,
this.height,
this.width,
this.color,
this.boxConstraints,
this.margin,
this.padding,
this.onTap,
this.borderRadius,
});
final double? height;
final double? width;
final Widget child;
final BoxConstraints? boxConstraints;
final EdgeInsets? margin;
final EdgeInsets? padding;
final Color? color;
final Function()? onTap;
final BorderRadius? borderRadius;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(20),
child: Container(
height: height,
width: width,
margin: margin ?? const EdgeInsets.only(right: 3, bottom: 3),
constraints: boxConstraints,
decoration: BoxDecoration(
color: color ?? Colors.white,
borderRadius: borderRadius ?? BorderRadius.circular(20),
),
padding: padding ?? const EdgeInsets.all(10),
child: child,
),
);
}
}