From 05b31805107c29ce27ede2af21b5921718d09f64 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:22:41 +0300 Subject: [PATCH 01/16] Created `ControlDeviceService` interface and its remote and debounced implementation. --- lib/services/control_device_service.dart | 80 ++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 lib/services/control_device_service.dart diff --git a/lib/services/control_device_service.dart b/lib/services/control_device_service.dart new file mode 100644 index 00000000..b115990d --- /dev/null +++ b/lib/services/control_device_service.dart @@ -0,0 +1,80 @@ +import 'dart:developer'; + +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +abstract interface class ControlDeviceService { + Future controlDevice({ + required String deviceUuid, + required Status status, + }); +} + +final class RemoteControlDeviceService implements ControlDeviceService { + @override + Future controlDevice({ + required String deviceUuid, + required Status status, + }) async { + try { + final response = await HTTPService().post( + path: ApiEndpoints.deviceControl.replaceAll('{uuid}', deviceUuid), + body: status.toMap(), + showServerMessage: true, + expectedResponseModel: (json) { + return (json['success'] as bool?) ?? false; + }, + ); + return response; + } catch (e) { + log('Error fetching $e', name: 'ControlDeviceService'); + return false; + } + } +} + +final class DebouncedControlDeviceService implements ControlDeviceService { + final ControlDeviceService decoratee; + final Duration _debounceDuration; + final List<(String deviceUuid, Status status)> _pendingRequests = []; + bool _isProcessing = false; + + DebouncedControlDeviceService({ + required this.decoratee, + Duration debounceDuration = const Duration(milliseconds: 1500), + }) : _debounceDuration = debounceDuration; + + @override + Future controlDevice({ + required String deviceUuid, + required Status status, + }) async { + _pendingRequests.add((deviceUuid, status)); + + if (_isProcessing) { + log( + 'Request added to queue', + name: 'DebouncedControlDeviceService', + ); + return false; + } + + _isProcessing = true; + + await Future.delayed(_debounceDuration); + + final lastRequest = _pendingRequests.last; + _pendingRequests.clear(); + + try { + final ( lastRequestDeviceUuid, lastRequestStatus) = lastRequest; + return decoratee.controlDevice( + deviceUuid: lastRequestDeviceUuid, + status: lastRequestStatus, + ); + } finally { + _isProcessing = false; + } + } +} From 8c637e40ff7c3325ee18547d736842f7a932fabe Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:23:00 +0300 Subject: [PATCH 02/16] Created `FlushMountedPresenceSensorModel` model. --- .../flush_mounted_presence_sensor_model.dart | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 lib/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart new file mode 100644 index 00000000..975af0e8 --- /dev/null +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart @@ -0,0 +1,99 @@ +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; + +class FlushMountedPresenceSensorModel { + FlushMountedPresenceSensorModel({ + required this.presenceState, + required this.farDetection, + required this.illuminance, + required this.sensitivity, + required this.occurDistReduce, + required this.noneDelay, + required this.presenceDelay, + required this.nearDetection, + required this.sensiReduce, + required this.checkingResult, + }); + + static const String codePresenceState = 'presence_state'; + static const String codeSensitivity = 'sensitivity'; + static const String codeNearDetection = 'near_detection'; + static const String codeFarDetection = 'far_detection'; + static const String codeCheckingResult = 'checking_result'; + static const String codePresenceDelay = 'presence_delay'; + static const String codeNoneDelay = 'none_delay'; + static const String codeOccurDistReduce = 'occur_dist_reduce'; + static const String codeIlluminance = 'illum_value'; + static const String codeSensiReduce = 'sensi_reduce'; + + String presenceState; + int sensitivity; + int nearDetection; + int farDetection; + String checkingResult; + int presenceDelay; + int noneDelay; + int occurDistReduce; + int illuminance; + int sensiReduce; + + factory FlushMountedPresenceSensorModel.fromJson(List jsonList) { + String presenceState = 'none'; + int sensitivity = 0; + int nearDetection = 0; + int farDetection = 0; + String checkingResult = 'none'; + int presenceDelay = 0; + int noneDelay = 0; + int occurDistReduce = 0; + int illuminance = 0; + int sensiReduce = 0; + + for (var status in jsonList) { + switch (status.code) { + case codePresenceState: + presenceState = status.value ?? 'presence'; + break; + case codeSensitivity: + sensitivity = status.value ?? 0; + break; + case codeNearDetection: + nearDetection = status.value ?? 0; + break; + case codeFarDetection: + farDetection = status.value ?? 0; + break; + case codeCheckingResult: + checkingResult = status.value ?? 'check_success'; + break; + case codePresenceDelay: + presenceDelay = status.value ?? 0; + break; + case codeNoneDelay: + noneDelay = status.value ?? 0; + break; + case codeOccurDistReduce: + occurDistReduce = status.value ?? 0; + break; + case codeIlluminance: + illuminance = status.value ?? 0; + break; + case codeSensiReduce: + sensiReduce = status.value ?? 0; + break; + } + } + + return FlushMountedPresenceSensorModel( + presenceState: presenceState, + sensitivity: sensitivity, + nearDetection: nearDetection, + farDetection: farDetection, + checkingResult: checkingResult, + presenceDelay: presenceDelay, + noneDelay: noneDelay, + occurDistReduce: occurDistReduce, + illuminance: illuminance, + sensiReduce: sensiReduce, + ); + } +} From 367d6717e72780173f8be30a321e35867b0d69b7 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:23:28 +0300 Subject: [PATCH 03/16] Refactor `PresenceUpdateData` widget to support decimal values. --- .../shared/sensors_widgets/presence_update_data.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/pages/device_managment/shared/sensors_widgets/presence_update_data.dart b/lib/pages/device_managment/shared/sensors_widgets/presence_update_data.dart index 16596a1f..4b4d5562 100644 --- a/lib/pages/device_managment/shared/sensors_widgets/presence_update_data.dart +++ b/lib/pages/device_managment/shared/sensors_widgets/presence_update_data.dart @@ -13,6 +13,7 @@ class PresenceUpdateData extends StatefulWidget { required this.maxValue, required this.steps, this.description, + this.valuesPercision = 0, }); final String title; @@ -22,6 +23,7 @@ class PresenceUpdateData extends StatefulWidget { final double steps; final Function action; final String? description; + final int valuesPercision; @override State createState() => _CurrentTempState(); @@ -45,7 +47,7 @@ class _CurrentTempState extends State { } void _onValueChanged(double newValue) { - widget.action(newValue.toInt()); + widget.action(newValue); } @override @@ -66,7 +68,7 @@ class _CurrentTempState extends State { color: ColorsManager.blackColor, fontWeight: FontWeight.w400, fontSize: 10), ), IncrementDecrementWidget( - value: widget.value.toString(), + value: widget.value.toStringAsFixed(widget.valuesPercision), description: widget.description ?? '', descriptionColor: ColorsManager.blackColor, onIncrement: () { From 1975a1b6f466349af147330c0ccaee6029f79cb8 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:24:14 +0300 Subject: [PATCH 04/16] created `FlushMountedPresenceSensorBloc`, events, and states for device management. --- .../flush_mounted_presence_sensor_bloc.dart | 266 ++++++++++++++++++ .../flush_mounted_presence_sensor_event.dart | 84 ++++++ .../flush_mounted_presence_sensor_state.dart | 76 +++++ 3 files changed, 426 insertions(+) create mode 100644 lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart create mode 100644 lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_event.dart create mode 100644 lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_state.dart diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart new file mode 100644 index 00000000..06be2666 --- /dev/null +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart @@ -0,0 +1,266 @@ +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:syncrow_web/pages/device_managment/all_devices/models/device_reports.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/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart'; +import 'package:syncrow_web/services/control_device_service.dart'; +import 'package:syncrow_web/services/devices_mang_api.dart'; + +part 'flush_mounted_presence_sensor_event.dart'; +part 'flush_mounted_presence_sensor_state.dart'; + +class FlushMountedPresenceSensorBloc + extends Bloc { + final String deviceId; + late FlushMountedPresenceSensorModel deviceStatus; + + final ControlDeviceService controlDeviceService; + + FlushMountedPresenceSensorBloc({ + required this.deviceId, + required this.controlDeviceService, + }) : super(FlushMountedPresenceSensorInitialState()) { + on( + _onFlushMountedPresenceSensorFetchStatusEvent, + ); + on( + _onFlushMountedPresenceSensorFetchBatchStatusEvent); + on( + _onFlushMountedPresenceSensorChangeValueEvent, + ); + on( + _onFlushMountedPresenceSensorBatchControlEvent, + ); + on( + _onFlushMountedPresenceSensorGetDeviceReportsEvent); + on( + _onFlushMountedPresenceSensorShowDescriptionEvent, + ); + on( + _onFlushMountedPresenceSensorBackToGridViewEvent, + ); + on( + _onFlushMountedPresenceSensorFactoryResetEvent, + ); + } + + void _onFlushMountedPresenceSensorFetchStatusEvent( + FlushMountedPresenceSensorFetchStatusEvent event, + Emitter emit, + ) async { + emit(FlushMountedPresenceSensorLoadingInitialState()); + try { + final response = await DevicesManagementApi().getDeviceStatus(deviceId); + deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status); + emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); + _listenToChanges(emit, deviceId); + } catch (e) { + emit(FlushMountedPresenceSensorFailedState(error: e.toString())); + return; + } + } + + Future _onFlushMountedPresenceSensorFetchBatchStatusEvent( + FlushMountedPresenceSensorFetchBatchStatusEvent event, + Emitter emit, + ) async { + emit(FlushMountedPresenceSensorLoadingInitialState()); + try { + var response = await DevicesManagementApi().getBatchStatus(event.devicesIds); + deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status); + emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); + } catch (e) { + emit(FlushMountedPresenceSensorFailedState(error: e.toString())); + } + } + + void _listenToChanges( + Emitter emit, + String deviceId, + ) { + try { + final ref = FirebaseDatabase.instance.ref( + 'device-status/$deviceId', + ); + + ref.onValue.listen((DatabaseEvent event) { + Map usersMap = + event.snapshot.value as Map; + List statusList = []; + + usersMap['status'].forEach((element) { + statusList.add(Status(code: element['code'], value: element['value'])); + }); + + deviceStatus = FlushMountedPresenceSensorModel.fromJson(statusList); + emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus)); + }); + } catch (_) {} + } + + void _onFlushMountedPresenceSensorChangeValueEvent( + FlushMountedPresenceSensorChangeValueEvent event, + Emitter emit, + ) async { + emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus)); + switch (event.code) { + case FlushMountedPresenceSensorModel.codeFarDetection: + deviceStatus.farDetection = event.value; + break; + case FlushMountedPresenceSensorModel.codeSensitivity: + log('updated sensitivity: ${deviceStatus.sensitivity}-${event.value}'); + deviceStatus.sensitivity = event.value; + break; + case FlushMountedPresenceSensorModel.codeNoneDelay: + log('updated none delay: ${deviceStatus.noneDelay}-${event.value}'); + deviceStatus.noneDelay = event.value; + break; + case FlushMountedPresenceSensorModel.codePresenceDelay: + deviceStatus.presenceDelay = event.value; + break; + case FlushMountedPresenceSensorModel.codeNearDetection: + deviceStatus.nearDetection = event.value; + break; + case FlushMountedPresenceSensorModel.codeOccurDistReduce: + deviceStatus.occurDistReduce = event.value; + break; + case FlushMountedPresenceSensorModel.codeSensiReduce: + deviceStatus.sensiReduce = event.value; + break; + default: + break; + } + emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); + await _runDeBouncer( + deviceId: deviceId, + code: event.code, + value: event.value, + isBatch: false, + emit: emit, + ); + } + + Future _onFlushMountedPresenceSensorBatchControlEvent( + FlushMountedPresenceSensorBatchControlEvent event, + Emitter emit, + ) async { + emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus)); + switch (event.code) { + case FlushMountedPresenceSensorModel.codeFarDetection: + deviceStatus.farDetection = event.value; + break; + case FlushMountedPresenceSensorModel.codeSensitivity: + deviceStatus.sensitivity = event.value; + break; + case FlushMountedPresenceSensorModel.codeNoneDelay: + deviceStatus.noneDelay = event.value; + break; + case FlushMountedPresenceSensorModel.codePresenceDelay: + deviceStatus.presenceDelay = event.value; + break; + case FlushMountedPresenceSensorModel.codeNearDetection: + deviceStatus.nearDetection = event.value; + break; + case FlushMountedPresenceSensorModel.codeOccurDistReduce: + deviceStatus.occurDistReduce = event.value; + break; + case FlushMountedPresenceSensorModel.codeSensiReduce: + deviceStatus.sensiReduce = event.value; + break; + default: + break; + } + emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); + await _runDeBouncer( + deviceId: event.deviceIds, + code: event.code, + value: event.value, + emit: emit, + isBatch: true, + ); + } + + Future _runDeBouncer({ + required dynamic deviceId, + required String code, + required dynamic value, + required Emitter emit, + required bool isBatch, + }) async { + try { + if (isBatch) { + await DevicesManagementApi().deviceBatchControl(deviceId, code, value); + } else { + await controlDeviceService.controlDevice( + deviceUuid: deviceId, + status: Status(code: code, value: value), + ); + } + } catch (_) { + await Future.delayed(const Duration(milliseconds: 500)); + add(FlushMountedPresenceSensorFetchStatusEvent()); + } + } + + Future _onFlushMountedPresenceSensorGetDeviceReportsEvent( + FlushMountedPresenceSensorGetDeviceReportsEvent event, + Emitter emit, + ) async { + emit(FlushMountedPresenceSensorDeviceReportsLoadingState()); + + try { + await DevicesManagementApi.getDeviceReports(deviceId, event.code) + .then((value) { + emit(FlushMountedPresenceSensorDeviceReportsState( + deviceReport: value, code: event.code)); + }); + } catch (e) { + emit(FlushMountedPresenceSensorDeviceReportsFailedState(error: e.toString())); + return; + } + } + + void _onFlushMountedPresenceSensorShowDescriptionEvent( + FlushMountedPresenceSensorShowDescriptionEvent event, + Emitter emit, + ) { + emit(FlushMountedPresenceSensorShowDescriptionState( + description: event.description)); + } + + void _onFlushMountedPresenceSensorBackToGridViewEvent( + FlushMountedPresenceSensorBackToGridViewEvent event, + Emitter emit, + ) { + emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); + } + + Future _onFlushMountedPresenceSensorFactoryResetEvent( + FlushMountedPresenceSensorFactoryResetEvent event, + Emitter emit, + ) async { + emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus)); + try { + final response = await DevicesManagementApi().factoryReset( + event.factoryReset, + event.deviceId, + ); + if (!response) { + emit( + const FlushMountedPresenceSensorFailedState( + error: 'Something went wrong with factory reset, please try again', + ), + ); + } else { + emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); + } + } catch (e) { + emit(FlushMountedPresenceSensorFailedState(error: e.toString())); + } + } +} diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_event.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_event.dart new file mode 100644 index 00000000..f1636300 --- /dev/null +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_event.dart @@ -0,0 +1,84 @@ +part of 'flush_mounted_presence_sensor_bloc.dart'; + +sealed class FlushMountedPresenceSensorEvent extends Equatable { + const FlushMountedPresenceSensorEvent(); + + @override + List get props => []; +} + +class FlushMountedPresenceSensorFetchStatusEvent + extends FlushMountedPresenceSensorEvent {} + +class FlushMountedPresenceSensorChangeValueEvent + extends FlushMountedPresenceSensorEvent { + final int value; + final String code; + final bool isBatchControl; + const FlushMountedPresenceSensorChangeValueEvent({ + required this.value, + required this.code, + this.isBatchControl = false, + }); + + @override + List get props => [value, code]; +} + +class FlushMountedPresenceSensorFetchBatchStatusEvent + extends FlushMountedPresenceSensorEvent { + final List devicesIds; + const FlushMountedPresenceSensorFetchBatchStatusEvent(this.devicesIds); + + @override + List get props => [devicesIds]; +} + +class FlushMountedPresenceSensorGetDeviceReportsEvent + extends FlushMountedPresenceSensorEvent { + final String deviceUuid; + final String code; + const FlushMountedPresenceSensorGetDeviceReportsEvent({ + required this.deviceUuid, + required this.code, + }); + + @override + List get props => [deviceUuid, code]; +} + +class FlushMountedPresenceSensorShowDescriptionEvent + extends FlushMountedPresenceSensorEvent { + final String description; + const FlushMountedPresenceSensorShowDescriptionEvent({required this.description}); +} + +class FlushMountedPresenceSensorBackToGridViewEvent + extends FlushMountedPresenceSensorEvent {} + +class FlushMountedPresenceSensorBatchControlEvent + extends FlushMountedPresenceSensorEvent { + final List deviceIds; + final String code; + final dynamic value; + + const FlushMountedPresenceSensorBatchControlEvent({ + required this.deviceIds, + required this.code, + required this.value, + }); + + @override + List get props => [deviceIds, code, value]; +} + +class FlushMountedPresenceSensorFactoryResetEvent + extends FlushMountedPresenceSensorEvent { + final String deviceId; + final FactoryResetModel factoryReset; + + const FlushMountedPresenceSensorFactoryResetEvent({ + required this.deviceId, + required this.factoryReset, + }); +} diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_state.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_state.dart new file mode 100644 index 00000000..0fef07f2 --- /dev/null +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_state.dart @@ -0,0 +1,76 @@ +part of 'flush_mounted_presence_sensor_bloc.dart'; + +sealed class FlushMountedPresenceSensorState extends Equatable { + const FlushMountedPresenceSensorState(); + + @override + List get props => []; +} + +class FlushMountedPresenceSensorInitialState + extends FlushMountedPresenceSensorState {} + +class FlushMountedPresenceSensorLoadingInitialState + extends FlushMountedPresenceSensorState {} + +class FlushMountedPresenceSensorUpdateState extends FlushMountedPresenceSensorState { + final FlushMountedPresenceSensorModel model; + const FlushMountedPresenceSensorUpdateState({required this.model}); + + @override + List get props => [model]; +} + +class FlushMountedPresenceSensorLoadingNewSate + extends FlushMountedPresenceSensorState { + final FlushMountedPresenceSensorModel model; + const FlushMountedPresenceSensorLoadingNewSate({required this.model}); + + @override + List get props => [model]; +} + +class FlushMountedPresenceSensorFailedState extends FlushMountedPresenceSensorState { + final String error; + + const FlushMountedPresenceSensorFailedState({required this.error}); + + @override + List get props => [error]; +} + +class FlushMountedPresenceSensorDeviceReportsLoadingState + extends FlushMountedPresenceSensorState {} + +class FlushMountedPresenceSensorDeviceReportsState + extends FlushMountedPresenceSensorState { + const FlushMountedPresenceSensorDeviceReportsState({ + required this.deviceReport, + required this.code, + }); + + final DeviceReport deviceReport; + final String code; + + @override + List get props => [deviceReport, code]; +} + +class FlushMountedPresenceSensorDeviceReportsFailedState + extends FlushMountedPresenceSensorState { + const FlushMountedPresenceSensorDeviceReportsFailedState({required this.error}); + + final String error; + + @override + List get props => [error]; +} + +class FlushMountedPresenceSensorShowDescriptionState + extends FlushMountedPresenceSensorState { + const FlushMountedPresenceSensorShowDescriptionState({required this.description}); + + final String description; + @override + List get props => [description]; +} From fb8ccdf0a67043f4203fd3519f50de5693f999d2 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:24:38 +0300 Subject: [PATCH 05/16] Add FlushMountedPresenceSensorControlView for managing presence sensor settings --- .../helper/route_controls_based_code.dart | 4 + ..._mounted_presence_sensor_control_view.dart | 225 ++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart diff --git a/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart b/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart index e38ac582..81405451 100644 --- a/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart +++ b/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart @@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_st import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart'; import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart'; +import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_batch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_control_view.dart'; import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_batch_control.dart'; @@ -104,6 +105,9 @@ mixin RouteControlsBasedCode { ); case 'SOS': return SosDeviceControlsView(device: device); + + case 'NCPS': + return FlushMountedPresenceSensorControlView(device: device); default: return const SizedBox(); } diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart new file mode 100644 index 00000000..5a355067 --- /dev/null +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart @@ -0,0 +1,225 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_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_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/shared/table/description_view.dart'; +import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart'; +import 'package:syncrow_web/services/control_device_service.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class FlushMountedPresenceSensorControlView extends StatelessWidget + with HelperResponsiveLayout { + const FlushMountedPresenceSensorControlView({super.key, required this.device}); + + final AllDevicesModel device; + + @override + Widget build(BuildContext context) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); + return BlocProvider( + create: (context) => FlushMountedPresenceSensorBloc( + deviceId: device.uuid!, + controlDeviceService: DebouncedControlDeviceService( + decoratee: RemoteControlDeviceService(), + ), + )..add(FlushMountedPresenceSensorFetchStatusEvent()), + child: BlocBuilder( + builder: (context, state) { + if (state is FlushMountedPresenceSensorLoadingInitialState || + state is FlushMountedPresenceSensorDeviceReportsLoadingState) { + return const Center(child: CircularProgressIndicator()); + } else if (state is FlushMountedPresenceSensorUpdateState) { + return _buildGridView( + context, state.model, isExtraLarge, isLarge, isMedium); + } else if (state is FlushMountedPresenceSensorDeviceReportsState) { + return ReportsTable( + report: state.deviceReport, + thirdColumnTitle: + state.code == 'illuminance_value' ? "Value" : 'Status', + thirdColumnDescription: + state.code == 'illuminance_value' ? "Lux" : null, + onRowTap: (index) {}, + onClose: () { + context + .read() + .add(FlushMountedPresenceSensorBackToGridViewEvent()); + }, + ); + } else if (state is FlushMountedPresenceSensorShowDescriptionState) { + return DescriptionView( + description: state.description, + onClose: () { + context + .read() + .add(FlushMountedPresenceSensorBackToGridViewEvent()); + }, + ); + } else if (state is FlushMountedPresenceSensorDeviceReportsFailedState) { + final model = + context.read().deviceStatus; + return _buildGridView( + context, + model, + isExtraLarge, + isLarge, + isMedium, + ); + } + return const Center( + child: Text('Error fetching status', textAlign: TextAlign.center), + ); + }, + ), + ); + } + + Widget _buildGridView( + BuildContext context, + FlushMountedPresenceSensorModel model, + bool isExtraLarge, + bool isLarge, + bool isMedium, + ) { + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge || isExtraLarge + ? 3 + : isMedium + ? 2 + : 1, + mainAxisExtent: 140, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + PresenceState( + value: model.presenceState, + ), + PresenceDisplayValue( + value: model.illuminance.toString(), + postfix: 'Lux', + description: 'Illuminance Value', + ), + PresenceUpdateData( + value: model.sensitivity.toDouble(), + title: 'Sensitivity:', + minValue: 0, + maxValue: 9, + steps: 1, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeSensitivity, + value: value, + ), + ), + ), + PresenceUpdateData( + value: (model.nearDetection).toDouble(), + title: 'Nearest Detect Dist:', + description: 'm', + minValue: 0.0, + maxValue: 950, + steps: 10, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeNearDetection, + value: (value).toInt(), + ), + ), + ), + PresenceUpdateData( + value: (model.farDetection).toDouble(), + title: 'Max Detect Dist:', + description: 'm', + minValue: 0.0, + maxValue: 950, + steps: 10, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeFarDetection, + value: (value).toInt(), + ), + ), + ), + PresenceUpdateData( + value: (model.presenceDelay.toDouble()), + title: 'Trigger Level:', + minValue: 0, + maxValue: 3, + steps: 1, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codePresenceDelay, + value: value, + ), + ), + ), + PresenceUpdateData( + value: (model.occurDistReduce.toDouble()), + title: 'Indent Level:', + minValue: 0, + maxValue: 3, + steps: 1, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeOccurDistReduce, + value: value, + ), + ), + ), + PresenceUpdateData( + value: (model.sensiReduce.toDouble()), + title: 'Target Confirm Time:', + description: 's', + minValue: 0, + maxValue: 3, + steps: 1, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeSensiReduce, + value: value, + ), + ), + ), + PresenceUpdateData( + value: ((model.noneDelay).toDouble()), + description: 's', + title: 'Disappe Delay:', + minValue: 0, + maxValue: 3000, + steps: 10, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeNoneDelay, + value: value, + ), + ), + ), + GestureDetector( + onTap: () => context.read().add( + FlushMountedPresenceSensorGetDeviceReportsEvent( + code: 'presence_state', + deviceUuid: device.uuid!, + ), + ), + child: const PresenceStaticWidget( + icon: Assets.presenceRecordIcon, + description: 'Presence Record', + ), + ), + ], + ); + } +} From 8c960bd5f11872ac7bd42c3ffa0c9e8594a31859 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:26:17 +0300 Subject: [PATCH 06/16] created `BatchControlDevicesService` interface. --- lib/services/batch_control_devices_service.dart | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 lib/services/batch_control_devices_service.dart diff --git a/lib/services/batch_control_devices_service.dart b/lib/services/batch_control_devices_service.dart new file mode 100644 index 00000000..3049ec7e --- /dev/null +++ b/lib/services/batch_control_devices_service.dart @@ -0,0 +1,7 @@ +abstract interface class BatchControlDevicesService { + Future batchControlDevices({ + required List uuids, + required String code, + required Object value, + }); +} From 24130be66589ad110e100e52caa4b7ddb3c50ba0 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:26:55 +0300 Subject: [PATCH 07/16] organized instances in bloc. --- .../bloc/flush_mounted_presence_sensor_bloc.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart index 06be2666..d7720937 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart @@ -17,10 +17,9 @@ part 'flush_mounted_presence_sensor_state.dart'; class FlushMountedPresenceSensorBloc extends Bloc { final String deviceId; - late FlushMountedPresenceSensorModel deviceStatus; - final ControlDeviceService controlDeviceService; + late FlushMountedPresenceSensorModel deviceStatus; FlushMountedPresenceSensorBloc({ required this.deviceId, required this.controlDeviceService, From b11d4186fb9efc55e5b2a587515c9d50b17075c3 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:31:26 +0300 Subject: [PATCH 08/16] Add BatchControlDevicesService integration to FlushMountedPresenceSensorBloc and ControlView --- .../flush_mounted_presence_sensor_bloc.dart | 9 +- ..._mounted_presence_sensor_control_view.dart | 4 + .../batch_control_devices_service.dart | 83 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart index d7720937..98cfa977 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart @@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_rep 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/flush_mounted_presence_sensor/models/flush_mounted_presence_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'; @@ -18,11 +19,13 @@ class FlushMountedPresenceSensorBloc extends Bloc { final String deviceId; final ControlDeviceService controlDeviceService; + final BatchControlDevicesService batchControlDevicesService; late FlushMountedPresenceSensorModel deviceStatus; FlushMountedPresenceSensorBloc({ required this.deviceId, required this.controlDeviceService, + required this.batchControlDevicesService, }) : super(FlushMountedPresenceSensorInitialState()) { on( _onFlushMountedPresenceSensorFetchStatusEvent, @@ -193,7 +196,11 @@ class FlushMountedPresenceSensorBloc }) async { try { if (isBatch) { - await DevicesManagementApi().deviceBatchControl(deviceId, code, value); + await batchControlDevicesService.batchControlDevices( + uuids: deviceId, + code: code, + value: value, + ); } else { await controlDeviceService.controlDevice( deviceUuid: deviceId, diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart index 5a355067..c02b4141 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart @@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presen import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart'; import 'package:syncrow_web/pages/device_managment/shared/table/description_view.dart'; import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart'; +import 'package:syncrow_web/services/batch_control_devices_service.dart'; import 'package:syncrow_web/services/control_device_service.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; @@ -30,6 +31,9 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget controlDeviceService: DebouncedControlDeviceService( decoratee: RemoteControlDeviceService(), ), + batchControlDevicesService: DebouncedBatchControlDevicesService( + decoratee: RemoteBatchControlDevicesService(), + ), )..add(FlushMountedPresenceSensorFetchStatusEvent()), child: BlocBuilder( diff --git a/lib/services/batch_control_devices_service.dart b/lib/services/batch_control_devices_service.dart index 3049ec7e..c8d0bfc2 100644 --- a/lib/services/batch_control_devices_service.dart +++ b/lib/services/batch_control_devices_service.dart @@ -1,3 +1,8 @@ +import 'dart:developer'; + +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + abstract interface class BatchControlDevicesService { Future batchControlDevices({ required List uuids, @@ -5,3 +10,81 @@ abstract interface class BatchControlDevicesService { required Object value, }); } + +class RemoteBatchControlDevicesService implements BatchControlDevicesService { + @override + Future batchControlDevices({ + required List uuids, + required String code, + required Object value, + }) async { + try { + final body = { + 'devicesUuid': uuids, + 'code': code, + 'value': value, + 'operationType': 'COMMAND', + }; + + final response = await HTTPService().post( + path: ApiEndpoints.deviceBatchControl, + body: body, + showServerMessage: true, + expectedResponseModel: (json) => (json['success'] as bool?) ?? false, + ); + + return response; + } catch (e) { + log('Error fetching $e', name: 'BatchControlDevicesService'); + return false; + } + } +} + +final class DebouncedBatchControlDevicesService + implements BatchControlDevicesService { + final BatchControlDevicesService decoratee; + final Duration _debounceDuration; + final List<(List uuids, String code, Object value)> _pendingRequests = []; + bool _isProcessing = false; + + DebouncedBatchControlDevicesService({ + required this.decoratee, + Duration debounceDuration = const Duration(milliseconds: 1500), + }) : _debounceDuration = debounceDuration; + + @override + Future batchControlDevices({ + required List uuids, + required String code, + required Object value, + }) async { + _pendingRequests.add((uuids, code, value)); + + if (_isProcessing) { + log( + 'Request added to queue', + name: 'DebouncedBatchControlDevicesService', + ); + return false; + } + + _isProcessing = true; + + await Future.delayed(_debounceDuration); + + final lastRequest = _pendingRequests.last; + _pendingRequests.clear(); + + try { + final (lastRequestUuids, lastRequestCode, lastRequestValue) = lastRequest; + return decoratee.batchControlDevices( + uuids: lastRequestUuids, + code: lastRequestCode, + value: lastRequestValue, + ); + } finally { + _isProcessing = false; + } + } +} From 42d6b64e588a96210a131dd050ea6cc63739234c Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:32:49 +0300 Subject: [PATCH 09/16] Refactor FlushMountedPresenceSensorBloc to replace _runDeBouncer with _controlDevice for handling device control logic --- .../bloc/flush_mounted_presence_sensor_bloc.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart index 98cfa977..f15c14d6 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart @@ -138,7 +138,7 @@ class FlushMountedPresenceSensorBloc break; } emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); - await _runDeBouncer( + await _controlDevice( deviceId: deviceId, code: event.code, value: event.value, @@ -178,7 +178,7 @@ class FlushMountedPresenceSensorBloc break; } emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); - await _runDeBouncer( + await _controlDevice( deviceId: event.deviceIds, code: event.code, value: event.value, @@ -187,7 +187,7 @@ class FlushMountedPresenceSensorBloc ); } - Future _runDeBouncer({ + Future _controlDevice({ required dynamic deviceId, required String code, required dynamic value, From 91f93d439528eab3479a53580a81ce3f35d52c2a Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:44:11 +0300 Subject: [PATCH 10/16] Refactor FlushMountedPresenceSensorBloc to streamline device control logic and remove redundant code --- .../flush_mounted_presence_sensor_bloc.dart | 141 +++++++----------- 1 file changed, 51 insertions(+), 90 deletions(-) diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart index f15c14d6..a3c15aa0 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:developer'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; @@ -110,41 +109,16 @@ class FlushMountedPresenceSensorBloc Emitter emit, ) async { emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus)); - switch (event.code) { - case FlushMountedPresenceSensorModel.codeFarDetection: - deviceStatus.farDetection = event.value; - break; - case FlushMountedPresenceSensorModel.codeSensitivity: - log('updated sensitivity: ${deviceStatus.sensitivity}-${event.value}'); - deviceStatus.sensitivity = event.value; - break; - case FlushMountedPresenceSensorModel.codeNoneDelay: - log('updated none delay: ${deviceStatus.noneDelay}-${event.value}'); - deviceStatus.noneDelay = event.value; - break; - case FlushMountedPresenceSensorModel.codePresenceDelay: - deviceStatus.presenceDelay = event.value; - break; - case FlushMountedPresenceSensorModel.codeNearDetection: - deviceStatus.nearDetection = event.value; - break; - case FlushMountedPresenceSensorModel.codeOccurDistReduce: - deviceStatus.occurDistReduce = event.value; - break; - case FlushMountedPresenceSensorModel.codeSensiReduce: - deviceStatus.sensiReduce = event.value; - break; - default: - break; - } + _updateDeviceFunctionFromCode(event.code, event.value); emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); - await _controlDevice( - deviceId: deviceId, - code: event.code, - value: event.value, - isBatch: false, - emit: emit, - ); + try { + await controlDeviceService.controlDevice( + deviceUuid: deviceId, + status: Status(code: event.code, value: event.value), + ); + } catch (_) { + await _reloadDeviceStatus(); + } } Future _onFlushMountedPresenceSensorBatchControlEvent( @@ -152,67 +126,54 @@ class FlushMountedPresenceSensorBloc Emitter emit, ) async { emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus)); - switch (event.code) { - case FlushMountedPresenceSensorModel.codeFarDetection: - deviceStatus.farDetection = event.value; - break; - case FlushMountedPresenceSensorModel.codeSensitivity: - deviceStatus.sensitivity = event.value; - break; - case FlushMountedPresenceSensorModel.codeNoneDelay: - deviceStatus.noneDelay = event.value; - break; - case FlushMountedPresenceSensorModel.codePresenceDelay: - deviceStatus.presenceDelay = event.value; - break; - case FlushMountedPresenceSensorModel.codeNearDetection: - deviceStatus.nearDetection = event.value; - break; - case FlushMountedPresenceSensorModel.codeOccurDistReduce: - deviceStatus.occurDistReduce = event.value; - break; - case FlushMountedPresenceSensorModel.codeSensiReduce: - deviceStatus.sensiReduce = event.value; - break; - default: - break; - } + _updateDeviceFunctionFromCode(event.code, event.value); emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); - await _controlDevice( - deviceId: event.deviceIds, - code: event.code, - value: event.value, - emit: emit, - isBatch: true, - ); + + try { + await batchControlDevicesService.batchControlDevices( + uuids: event.deviceIds, + code: event.code, + value: event.value, + ); + } catch (_) { + await _reloadDeviceStatus(); + } } - Future _controlDevice({ - required dynamic deviceId, - required String code, - required dynamic value, - required Emitter emit, - required bool isBatch, - }) async { - try { - if (isBatch) { - await batchControlDevicesService.batchControlDevices( - uuids: deviceId, - code: code, - value: value, - ); - } else { - await controlDeviceService.controlDevice( - deviceUuid: deviceId, - status: Status(code: code, value: value), - ); - } - } catch (_) { - await Future.delayed(const Duration(milliseconds: 500)); - add(FlushMountedPresenceSensorFetchStatusEvent()); + void _updateDeviceFunctionFromCode(String code, int value) { + switch (code) { + case FlushMountedPresenceSensorModel.codeFarDetection: + deviceStatus.farDetection = value; + break; + case FlushMountedPresenceSensorModel.codeSensitivity: + deviceStatus.sensitivity = value; + break; + case FlushMountedPresenceSensorModel.codeNoneDelay: + deviceStatus.noneDelay = value; + break; + case FlushMountedPresenceSensorModel.codePresenceDelay: + deviceStatus.presenceDelay = value; + break; + case FlushMountedPresenceSensorModel.codeNearDetection: + deviceStatus.nearDetection = value; + break; + case FlushMountedPresenceSensorModel.codeOccurDistReduce: + deviceStatus.occurDistReduce = value; + break; + case FlushMountedPresenceSensorModel.codeSensiReduce: + deviceStatus.sensiReduce = value; + break; + default: + return; } } + Future _reloadDeviceStatus() async { + await Future.delayed(const Duration(milliseconds: 500), () { + add(FlushMountedPresenceSensorFetchStatusEvent()); + }); + } + Future _onFlushMountedPresenceSensorGetDeviceReportsEvent( FlushMountedPresenceSensorGetDeviceReportsEvent event, Emitter emit, From 7adce3b94ca4e3f963756f430a512c3fc57b2094 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:44:44 +0300 Subject: [PATCH 11/16] Refactor _onFlushMountedPresenceSensorFetchBatchStatusEvent to use final for response variable --- .../bloc/flush_mounted_presence_sensor_bloc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart index a3c15aa0..9b62cb4e 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart @@ -72,7 +72,7 @@ class FlushMountedPresenceSensorBloc ) async { emit(FlushMountedPresenceSensorLoadingInitialState()); try { - var response = await DevicesManagementApi().getBatchStatus(event.devicesIds); + final response = await DevicesManagementApi().getBatchStatus(event.devicesIds); deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status); emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus)); } catch (e) { From 46860619a07f58e4a0e0e57cb35696c9e3ab03a3 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:48:24 +0300 Subject: [PATCH 12/16] Add bloc dependency to pubspec.yaml --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index fd7ed797..ec8660b6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -60,6 +60,7 @@ dependencies: firebase_core: ^3.11.0 firebase_crashlytics: ^4.3.2 firebase_database: ^11.3.2 + bloc: ^8.1.4 dev_dependencies: From 2bb7a6950a6e1cc7d74e6db66e408efad1f85c40 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 10:52:08 +0300 Subject: [PATCH 13/16] Refactor debounce duration handling in BatchControlDevicesService and ControlDeviceService --- .../batch_control_devices_service.dart | 23 ++++++++----------- lib/services/control_device_service.dart | 23 ++++++++----------- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/lib/services/batch_control_devices_service.dart b/lib/services/batch_control_devices_service.dart index c8d0bfc2..76dbe480 100644 --- a/lib/services/batch_control_devices_service.dart +++ b/lib/services/batch_control_devices_service.dart @@ -11,7 +11,7 @@ abstract interface class BatchControlDevicesService { }); } -class RemoteBatchControlDevicesService implements BatchControlDevicesService { +final class RemoteBatchControlDevicesService implements BatchControlDevicesService { @override Future batchControlDevices({ required List uuids, @@ -44,14 +44,15 @@ class RemoteBatchControlDevicesService implements BatchControlDevicesService { final class DebouncedBatchControlDevicesService implements BatchControlDevicesService { final BatchControlDevicesService decoratee; - final Duration _debounceDuration; - final List<(List uuids, String code, Object value)> _pendingRequests = []; - bool _isProcessing = false; + final Duration debounceDuration; + + final _pendingRequests = <(List uuids, String code, Object value)>[]; + var _isProcessing = false; DebouncedBatchControlDevicesService({ required this.decoratee, - Duration debounceDuration = const Duration(milliseconds: 1500), - }) : _debounceDuration = debounceDuration; + this.debounceDuration = const Duration(milliseconds: 1500), + }); @override Future batchControlDevices({ @@ -61,17 +62,11 @@ final class DebouncedBatchControlDevicesService }) async { _pendingRequests.add((uuids, code, value)); - if (_isProcessing) { - log( - 'Request added to queue', - name: 'DebouncedBatchControlDevicesService', - ); - return false; - } + if (_isProcessing) return false; _isProcessing = true; - await Future.delayed(_debounceDuration); + await Future.delayed(debounceDuration); final lastRequest = _pendingRequests.last; _pendingRequests.clear(); diff --git a/lib/services/control_device_service.dart b/lib/services/control_device_service.dart index b115990d..ab04a398 100644 --- a/lib/services/control_device_service.dart +++ b/lib/services/control_device_service.dart @@ -36,14 +36,15 @@ final class RemoteControlDeviceService implements ControlDeviceService { final class DebouncedControlDeviceService implements ControlDeviceService { final ControlDeviceService decoratee; - final Duration _debounceDuration; - final List<(String deviceUuid, Status status)> _pendingRequests = []; - bool _isProcessing = false; + final Duration debounceDuration; DebouncedControlDeviceService({ required this.decoratee, - Duration debounceDuration = const Duration(milliseconds: 1500), - }) : _debounceDuration = debounceDuration; + this.debounceDuration = const Duration(milliseconds: 1500), + }); + + final _pendingRequests = <(String deviceUuid, Status status)>[]; + var _isProcessing = false; @override Future controlDevice({ @@ -52,23 +53,17 @@ final class DebouncedControlDeviceService implements ControlDeviceService { }) async { _pendingRequests.add((deviceUuid, status)); - if (_isProcessing) { - log( - 'Request added to queue', - name: 'DebouncedControlDeviceService', - ); - return false; - } + if (_isProcessing) return false; _isProcessing = true; - await Future.delayed(_debounceDuration); + await Future.delayed(debounceDuration); final lastRequest = _pendingRequests.last; _pendingRequests.clear(); try { - final ( lastRequestDeviceUuid, lastRequestStatus) = lastRequest; + final (lastRequestDeviceUuid, lastRequestStatus) = lastRequest; return decoratee.controlDevice( deviceUuid: lastRequestDeviceUuid, status: lastRequestStatus, From 4a5176cf2238b1e2a04a769c7d43e14b22476458 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 12:11:13 +0300 Subject: [PATCH 14/16] Refactor presence update data handling for improved precision and scaling --- ...ed_presence_sensor_batch_control_view.dart | 187 ++++++++++++++++++ ..._mounted_presence_sensor_control_view.dart | 52 ++--- .../sensors_widgets/presence_update_data.dart | 8 +- 3 files changed, 221 insertions(+), 26 deletions(-) create mode 100644 lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart new file mode 100644 index 00000000..0ef4cdbc --- /dev/null +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart @@ -0,0 +1,187 @@ +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/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart'; +import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart'; +import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart'; +import 'package:syncrow_web/services/batch_control_devices_service.dart'; +import 'package:syncrow_web/services/control_device_service.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class FlushMountedPresenceSensorBatchControlView extends StatelessWidget + with HelperResponsiveLayout { + const FlushMountedPresenceSensorBatchControlView({ + required this.devicesIds, + super.key, + }); + + final List devicesIds; + + @override + Widget build(BuildContext context) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); + return BlocProvider( + create: (context) => FlushMountedPresenceSensorBloc( + deviceId: devicesIds.first, + controlDeviceService: DebouncedControlDeviceService( + decoratee: RemoteControlDeviceService(), + ), + batchControlDevicesService: DebouncedBatchControlDevicesService( + decoratee: RemoteBatchControlDevicesService(), + ), + )..add(FlushMountedPresenceSensorFetchStatusEvent()), + child: BlocBuilder( + builder: (context, state) { + if (state is FlushMountedPresenceSensorLoadingInitialState || + state is FlushMountedPresenceSensorDeviceReportsLoadingState) { + return const Center(child: CircularProgressIndicator()); + } else if (state is FlushMountedPresenceSensorUpdateState) { + return _buildGridView( + context, state.model, isExtraLarge, isLarge, isMedium); + } + return const Center(child: Text('Error fetching status')); + }, + ), + ); + } + + Widget _buildGridView( + BuildContext context, + FlushMountedPresenceSensorModel model, + bool isExtraLarge, + bool isLarge, + bool isMedium, + ) { + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge || isExtraLarge + ? 3 + : isMedium + ? 2 + : 1, + mainAxisExtent: 140, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + PresenceUpdateData( + value: model.sensitivity.toDouble(), + title: 'Sensitivity:', + minValue: 0, + maxValue: 9, + steps: 1, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeSensitivity, + value: value, + ), + ), + ), + PresenceUpdateData( + value: (model.nearDetection / 100).toDouble(), + title: 'Nearest Detect Dist:', + description: 'm', + minValue: 0.0, + maxValue: 9.5, + steps: 0.1, + valuesPercision: 1, + action: (double value) => + context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeNearDetection, + value: (value * 100).toInt(), + ), + ), + ), + PresenceUpdateData( + value: (model.farDetection / 100).toDouble(), + title: 'Max Detect Dist:', + description: 'm', + minValue: 0.0, + maxValue: 9.5, + steps: 0.1, + valuesPercision: 1, + action: (double value) => + context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeFarDetection, + value: (value * 100).toInt(), + ), + ), + ), + PresenceUpdateData( + value: model.presenceDelay.toDouble(), + title: 'Trigger Level:', + minValue: 0, + maxValue: 3, + steps: 1, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codePresenceDelay, + value: value, + ), + ), + ), + PresenceUpdateData( + value: (model.occurDistReduce.toDouble()), + title: 'Indent Level:', + minValue: 0, + maxValue: 3, + steps: 1, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeOccurDistReduce, + value: value, + ), + ), + ), + PresenceUpdateData( + value: (model.sensiReduce.toDouble()), + title: 'Target Confirm Time:', + description: 's', + minValue: 0, + maxValue: 3, + steps: 1, + action: (int value) => context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeSensiReduce, + value: value, + ), + ), + ), + PresenceUpdateData( + value: ((model.noneDelay / 10).toDouble()), + description: 's', + title: 'Disappe Delay:', + minValue: 20, + maxValue: 300, + steps: 1, + action: (double value) => + context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeNoneDelay, + value: (value * 10).round(), + ), + ), + ), + FactoryResetWidget( + callFactoryReset: () { + context.read().add( + FlushMountedPresenceSensorFactoryResetEvent( + deviceId: devicesIds.first, + factoryReset: FactoryResetModel(devicesUuid: devicesIds), + ), + ); + }, + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart index c02b4141..dab887b2 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart @@ -130,32 +130,36 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget ), ), PresenceUpdateData( - value: (model.nearDetection).toDouble(), + value: (model.nearDetection / 100).toDouble(), title: 'Nearest Detect Dist:', description: 'm', minValue: 0.0, - maxValue: 950, - steps: 10, - action: (int value) => context.read().add( - FlushMountedPresenceSensorChangeValueEvent( - code: FlushMountedPresenceSensorModel.codeNearDetection, - value: (value).toInt(), - ), - ), + maxValue: 9.5, + steps: 0.1, + valuesPercision: 1, + action: (double value) => + context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeNearDetection, + value: (value * 100).toInt(), + ), + ), ), PresenceUpdateData( - value: (model.farDetection).toDouble(), + value: (model.farDetection / 100).toDouble(), title: 'Max Detect Dist:', description: 'm', minValue: 0.0, - maxValue: 950, - steps: 10, - action: (int value) => context.read().add( - FlushMountedPresenceSensorChangeValueEvent( - code: FlushMountedPresenceSensorModel.codeFarDetection, - value: (value).toInt(), - ), - ), + maxValue: 9.5, + steps: 0.1, + valuesPercision: 1, + action: (double value) => + context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeFarDetection, + value: (value * 100).toInt(), + ), + ), ), PresenceUpdateData( value: (model.presenceDelay.toDouble()), @@ -198,16 +202,16 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget ), ), PresenceUpdateData( - value: ((model.noneDelay).toDouble()), + value: ((model.noneDelay / 10).toDouble()), description: 's', title: 'Disappe Delay:', - minValue: 0, - maxValue: 3000, - steps: 10, - action: (int value) => context.read().add( + minValue: 20, + maxValue: 300, + steps: 1, + action: (double value) => context.read().add( FlushMountedPresenceSensorChangeValueEvent( code: FlushMountedPresenceSensorModel.codeNoneDelay, - value: value, + value: (value * 10).round(), ), ), ), diff --git a/lib/pages/device_managment/shared/sensors_widgets/presence_update_data.dart b/lib/pages/device_managment/shared/sensors_widgets/presence_update_data.dart index 4b4d5562..e535612d 100644 --- a/lib/pages/device_managment/shared/sensors_widgets/presence_update_data.dart +++ b/lib/pages/device_managment/shared/sensors_widgets/presence_update_data.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/increament_decreament.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; class PresenceUpdateData extends StatefulWidget { const PresenceUpdateData({ @@ -64,8 +65,11 @@ class _CurrentTempState extends State { children: [ Text( widget.title, - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: ColorsManager.blackColor, fontWeight: FontWeight.w400, fontSize: 10), + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.blackColor, + fontWeight: FontWeight.w400, + fontSize: 10, + ), ), IncrementDecrementWidget( value: widget.value.toStringAsFixed(widget.valuesPercision), From 86164e746a77e81ffe27cdc8b430cffda28c68f1 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 12:15:50 +0300 Subject: [PATCH 15/16] Refactor FlushMountedPresenceSensorBloc creation to use factory method and streamline dependency injection --- ..._mounted_presence_sensor_bloc_factory.dart | 21 ++++++++ ...ed_presence_sensor_batch_control_view.dart | 25 +++------- ..._mounted_presence_sensor_control_view.dart | 48 +++++++------------ 3 files changed, 44 insertions(+), 50 deletions(-) create mode 100644 lib/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart new file mode 100644 index 00000000..537189af --- /dev/null +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart @@ -0,0 +1,21 @@ +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._(); + + static FlushMountedPresenceSensorBloc create({ + required String deviceId, + }) { + return FlushMountedPresenceSensorBloc( + deviceId: '', + controlDeviceService: DebouncedControlDeviceService( + decoratee: RemoteControlDeviceService(), + ), + batchControlDevicesService: DebouncedBatchControlDevicesService( + decoratee: RemoteBatchControlDevicesService(), + ), + )..add(FlushMountedPresenceSensorFetchStatusEvent()); + } +} diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart index 0ef4cdbc..7f1e726c 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart @@ -2,11 +2,10 @@ 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/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart'; import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart'; -import 'package:syncrow_web/services/batch_control_devices_service.dart'; -import 'package:syncrow_web/services/control_device_service.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; class FlushMountedPresenceSensorBatchControlView extends StatelessWidget @@ -20,19 +19,10 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget @override Widget build(BuildContext context) { - final isExtraLarge = isExtraLargeScreenSize(context); - final isLarge = isLargeScreenSize(context); - final isMedium = isMediumScreenSize(context); return BlocProvider( - create: (context) => FlushMountedPresenceSensorBloc( + create: (context) => FlushMountedPresenceSensorBlocFactory.create( deviceId: devicesIds.first, - controlDeviceService: DebouncedControlDeviceService( - decoratee: RemoteControlDeviceService(), - ), - batchControlDevicesService: DebouncedBatchControlDevicesService( - decoratee: RemoteBatchControlDevicesService(), - ), - )..add(FlushMountedPresenceSensorFetchStatusEvent()), + ), child: BlocBuilder( builder: (context, state) { @@ -40,8 +30,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget state is FlushMountedPresenceSensorDeviceReportsLoadingState) { return const Center(child: CircularProgressIndicator()); } else if (state is FlushMountedPresenceSensorUpdateState) { - return _buildGridView( - context, state.model, isExtraLarge, isLarge, isMedium); + return _buildGridView(context, state.model); } return const Center(child: Text('Error fetching status')); }, @@ -52,10 +41,10 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget Widget _buildGridView( BuildContext context, FlushMountedPresenceSensorModel model, - bool isExtraLarge, - bool isLarge, - bool isMedium, ) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); return GridView( padding: const EdgeInsets.symmetric(horizontal: 50), shrinkWrap: true, diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart index dab887b2..bdefa507 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_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_static_widget.dart'; @@ -9,8 +10,6 @@ import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presen import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart'; import 'package:syncrow_web/pages/device_managment/shared/table/description_view.dart'; import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart'; -import 'package:syncrow_web/services/batch_control_devices_service.dart'; -import 'package:syncrow_web/services/control_device_service.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; @@ -22,19 +21,10 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget @override Widget build(BuildContext context) { - final isExtraLarge = isExtraLargeScreenSize(context); - final isLarge = isLargeScreenSize(context); - final isMedium = isMediumScreenSize(context); return BlocProvider( - create: (context) => FlushMountedPresenceSensorBloc( - deviceId: device.uuid!, - controlDeviceService: DebouncedControlDeviceService( - decoratee: RemoteControlDeviceService(), - ), - batchControlDevicesService: DebouncedBatchControlDevicesService( - decoratee: RemoteBatchControlDevicesService(), - ), - )..add(FlushMountedPresenceSensorFetchStatusEvent()), + create: (context) => FlushMountedPresenceSensorBlocFactory.create( + deviceId: device.uuid ?? '-1', + ), child: BlocBuilder( builder: (context, state) { @@ -42,8 +32,7 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget state is FlushMountedPresenceSensorDeviceReportsLoadingState) { return const Center(child: CircularProgressIndicator()); } else if (state is FlushMountedPresenceSensorUpdateState) { - return _buildGridView( - context, state.model, isExtraLarge, isLarge, isMedium); + return _buildGridView(context, state.model); } else if (state is FlushMountedPresenceSensorDeviceReportsState) { return ReportsTable( report: state.deviceReport, @@ -70,13 +59,7 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget } else if (state is FlushMountedPresenceSensorDeviceReportsFailedState) { final model = context.read().deviceStatus; - return _buildGridView( - context, - model, - isExtraLarge, - isLarge, - isMedium, - ); + return _buildGridView(context, model); } return const Center( child: Text('Error fetching status', textAlign: TextAlign.center), @@ -89,10 +72,10 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget Widget _buildGridView( BuildContext context, FlushMountedPresenceSensorModel model, - bool isExtraLarge, - bool isLarge, - bool isMedium, ) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); return GridView( padding: const EdgeInsets.symmetric(horizontal: 50), shrinkWrap: true, @@ -208,12 +191,13 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget minValue: 20, maxValue: 300, steps: 1, - action: (double value) => context.read().add( - FlushMountedPresenceSensorChangeValueEvent( - code: FlushMountedPresenceSensorModel.codeNoneDelay, - value: (value * 10).round(), - ), - ), + action: (double value) => + context.read().add( + FlushMountedPresenceSensorChangeValueEvent( + code: FlushMountedPresenceSensorModel.codeNoneDelay, + value: (value * 10).round(), + ), + ), ), GestureDetector( onTap: () => context.read().add( From e1a24651307b67943c2ac4df6e051953acdcb735 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 23 Apr 2025 13:11:25 +0300 Subject: [PATCH 16/16] Fix deviceId assignment in FlushMountedPresenceSensorBlocFactory and update _listenToChanges method for async handling --- .../flush_mounted_presence_sensor_bloc.dart | 30 +++++++++++-------- ..._mounted_presence_sensor_bloc_factory.dart | 2 +- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart index 9b62cb4e..0bc15cd2 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; @@ -80,28 +81,31 @@ class FlushMountedPresenceSensorBloc } } - void _listenToChanges( + Future _listenToChanges( Emitter emit, String deviceId, - ) { - try { - final ref = FirebaseDatabase.instance.ref( - 'device-status/$deviceId', - ); + ) async { + final ref = FirebaseDatabase.instance.ref( + 'device-status/$deviceId', + ); - ref.onValue.listen((DatabaseEvent event) { + await ref.onValue.listen( + (DatabaseEvent event) async { Map usersMap = event.snapshot.value as Map; List statusList = []; - - usersMap['status'].forEach((element) { + + (usersMap['status'] as List?)?.forEach((element) { statusList.add(Status(code: element['code'], value: element['value'])); }); - + deviceStatus = FlushMountedPresenceSensorModel.fromJson(statusList); - emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus)); - }); - } catch (_) {} + if (!emit.isDone) { + emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus)); + } + }, + onError: (error) => log(error.toString(), name: 'FirebaseDatabaseError'), + ).asFuture(); } void _onFlushMountedPresenceSensorChangeValueEvent( diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart index 537189af..f1342eec 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart @@ -9,7 +9,7 @@ abstract final class FlushMountedPresenceSensorBlocFactory { required String deviceId, }) { return FlushMountedPresenceSensorBloc( - deviceId: '', + deviceId: deviceId, controlDeviceService: DebouncedControlDeviceService( decoratee: RemoteControlDeviceService(), ),