From 14a5d26ef9418dc764f0bd0c60e4dbebbbfd0563 Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 6 May 2025 16:20:05 +0300 Subject: [PATCH 1/2] add new flush sensor device to app --- assets/icons/detection_distance_icon.svg | 17 ++ assets/icons/disappe_delay_icon.svg | 17 ++ assets/icons/indent_level_icon.svg | 13 ++ assets/icons/target_confirm_time_icon.svg | 21 ++ assets/icons/trigger_level_icon.svg | 23 +++ .../flush_sensor_bloc/flush_sensor_bloc.dart | 129 ++++++++++++ .../flush_sensor_bloc/flush_sensor_event.dart | 43 ++++ .../flush_sensor_bloc/flush_sensor_state.dart | 45 ++++ lib/features/devices/model/device_model.dart | 2 + .../devices/model/flush_sensor_model.dart | 100 +++++++++ .../flush_sensor/flush_persence_records.dart | 120 +++++++++++ .../flush_sensor/flush_sensor_interface.dart | 113 ++++++++++ .../flush_sensor/flush_sensor_option.dart | 47 +++++ .../flush_sensor_options_list.dart | 193 ++++++++++++++++++ ...flush_sensor_parameter_control_dialog.dart | 181 ++++++++++++++++ .../flush_sensor/presence_indicator.dart | 31 +++ .../view/widgets/room_page_switch.dart | 8 + lib/generated/assets.dart | 7 + lib/services/api/devices_api.dart | 5 +- lib/utils/resource_manager/color_manager.dart | 4 +- 20 files changed, 1112 insertions(+), 7 deletions(-) create mode 100644 assets/icons/detection_distance_icon.svg create mode 100644 assets/icons/disappe_delay_icon.svg create mode 100644 assets/icons/indent_level_icon.svg create mode 100644 assets/icons/target_confirm_time_icon.svg create mode 100644 assets/icons/trigger_level_icon.svg create mode 100644 lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart create mode 100644 lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart create mode 100644 lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart create mode 100644 lib/features/devices/model/flush_sensor_model.dart create mode 100644 lib/features/devices/view/widgets/flush_sensor/flush_persence_records.dart create mode 100644 lib/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart create mode 100644 lib/features/devices/view/widgets/flush_sensor/flush_sensor_option.dart create mode 100644 lib/features/devices/view/widgets/flush_sensor/flush_sensor_options_list.dart create mode 100644 lib/features/devices/view/widgets/flush_sensor/flush_sensor_parameter_control_dialog.dart create mode 100644 lib/features/devices/view/widgets/flush_sensor/presence_indicator.dart diff --git a/assets/icons/detection_distance_icon.svg b/assets/icons/detection_distance_icon.svg new file mode 100644 index 0000000..9b0faa1 --- /dev/null +++ b/assets/icons/detection_distance_icon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/icons/disappe_delay_icon.svg b/assets/icons/disappe_delay_icon.svg new file mode 100644 index 0000000..c65fc5a --- /dev/null +++ b/assets/icons/disappe_delay_icon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/icons/indent_level_icon.svg b/assets/icons/indent_level_icon.svg new file mode 100644 index 0000000..837cdac --- /dev/null +++ b/assets/icons/indent_level_icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/icons/target_confirm_time_icon.svg b/assets/icons/target_confirm_time_icon.svg new file mode 100644 index 0000000..6114165 --- /dev/null +++ b/assets/icons/target_confirm_time_icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/trigger_level_icon.svg b/assets/icons/trigger_level_icon.svg new file mode 100644 index 0000000..56e343e --- /dev/null +++ b/assets/icons/trigger_level_icon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart new file mode 100644 index 0000000..28fafce --- /dev/null +++ b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart @@ -0,0 +1,129 @@ +import 'dart:async'; +import 'dart:developer'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart'; +import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; + +class FlushSensorBloc extends Bloc { + final String deviceId; + late DeviceModel deviceModel; + late FlushSensorModel deviceStatus; + + FlushSensorBloc({required this.deviceId}) : super(InitialState()) { + on(_fetchFlushSensorStatus); + on(_changeIndicator); + on(_changeValue); + on(_flushSensorUpdated); + on(_getDeviceReports); + } + + void _fetchFlushSensorStatus( + InitialEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + var response = await DevicesAPI.getDeviceStatus(deviceId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = FlushSensorModel.fromJson(statusModelList); + emit(UpdateState(flushSensorModel: deviceStatus)); + _listenToChanges(); + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + StreamSubscription? _streamSubscription; + + void _listenToChanges() { + try { + _streamSubscription?.cancel(); + DatabaseReference ref = + FirebaseDatabase.instance.ref('device-status/$deviceId'); + Stream stream = ref.onValue; + + _streamSubscription = stream.listen((DatabaseEvent event) async { + Map usersMap = + event.snapshot.value as Map; + List statusList = []; + usersMap['status'].forEach((element) { + statusList + .add(StatusModel(code: element['code'], value: element['value'])); + }); + deviceStatus = FlushSensorModel.fromJson(statusList); + if (!isClosed) { + add(WallSensorUpdatedEvent()); + } + }); + } catch (_) { + log( + 'Error listening to changes', + name: 'FlushMountedPresenceSensorBloc._listenToChanges', + ); + } + } + + @override + Future close() async { + _streamSubscription?.cancel(); + _streamSubscription = null; + return super.close(); + } + + _flushSensorUpdated( + WallSensorUpdatedEvent event, Emitter emit) { + emit(UpdateState(flushSensorModel: deviceStatus)); + } + + void _changeIndicator( + ChangeIndicatorEvent event, Emitter emit) async { + emit(LoadingNewSate(flushSensorModel: deviceStatus)); + try { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: deviceId, code: 'indicator', value: !event.value), + deviceId); + + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + emit(UpdateState(flushSensorModel: deviceStatus)); + } + + void _changeValue( + ChangeValueEvent event, Emitter emit) async { + emit(LoadingNewSate(flushSensorModel: deviceStatus)); + try { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: deviceId, code: event.code, value: event.value), + deviceId); + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + emit(UpdateState(flushSensorModel: deviceStatus)); + } + + void _getDeviceReports( + GetDeviceReportsEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + await DevicesAPI.getDeviceReports(deviceId, event.code).then((value) { + emit(DeviceReportsState(deviceReport: value, code: event.code)); + }); + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } +} diff --git a/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart new file mode 100644 index 0000000..99c5aa5 --- /dev/null +++ b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart @@ -0,0 +1,43 @@ +import 'package:equatable/equatable.dart'; + +abstract class FlushSensorEvent extends Equatable { + const FlushSensorEvent(); + + @override + List get props => []; +} + +class LoadingEvent extends FlushSensorEvent {} + +class InitialEvent extends FlushSensorEvent {} + +class WallSensorUpdatedEvent extends FlushSensorEvent {} + +class ChangeIndicatorEvent extends FlushSensorEvent { + final bool value; + const ChangeIndicatorEvent({required this.value}); + + @override + List get props => [value]; +} + +class ChangeValueEvent extends FlushSensorEvent { + final dynamic value; + final String code; + const ChangeValueEvent({required this.value, required this.code}); + + @override + List get props => [value, code]; +} + +class GetDeviceReportsEvent extends FlushSensorEvent { + final String deviceUuid; + final String code; + const GetDeviceReportsEvent({ + required this.deviceUuid, + required this.code, + }); + + @override + List get props => [deviceUuid, code]; +} diff --git a/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart new file mode 100644 index 0000000..5bbac2c --- /dev/null +++ b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart @@ -0,0 +1,45 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/device_report_model.dart'; +import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart'; + +class FlushSensorState extends Equatable { + const FlushSensorState(); + + @override + List get props => []; +} + +class InitialState extends FlushSensorState {} + +class LoadingInitialState extends FlushSensorState {} + +class UpdateState extends FlushSensorState { + final FlushSensorModel flushSensorModel; + const UpdateState({required this.flushSensorModel}); + + @override + List get props => [flushSensorModel]; +} + +class LoadingNewSate extends FlushSensorState { + final FlushSensorModel flushSensorModel; + const LoadingNewSate({required this.flushSensorModel}); + + @override + List get props => [flushSensorModel]; +} + +class FailedState extends FlushSensorState { + final String error; + + const FailedState({required this.error}); + + @override + List get props => [error]; +} + +class DeviceReportsState extends FlushSensorState { + final DeviceReport deviceReport; + final String code; + const DeviceReportsState({required this.deviceReport, required this.code}); +} diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index 9824468..7abeb9e 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -92,6 +92,8 @@ class DeviceModel { tempIcon = Assets.sixSceneHomeIcon; } else if (type == DeviceType.SOS) { tempIcon = Assets.sosHomeIcon; + } else if (type == DeviceType.FlushMountedSensor) { + tempIcon = Assets.sosHomeIcon; } else { tempIcon = Assets.assetsIconsLogo; } diff --git a/lib/features/devices/model/flush_sensor_model.dart b/lib/features/devices/model/flush_sensor_model.dart new file mode 100644 index 0000000..3640c61 --- /dev/null +++ b/lib/features/devices/model/flush_sensor_model.dart @@ -0,0 +1,100 @@ + +import 'package:syncrow_app/features/devices/model/status_model.dart'; + +class FlushSensorModel { + FlushSensorModel({ + 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 FlushSensorModel.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 FlushSensorModel( + presenceState: presenceState, + sensitivity: sensitivity, + nearDetection: nearDetection, + farDetection: farDetection, + checkingResult: checkingResult, + presenceDelay: presenceDelay, + noneDelay: noneDelay, + occurDistReduce: occurDistReduce, + illuminance: illuminance, + sensiReduce: sensiReduce, + ); + } +} diff --git a/lib/features/devices/view/widgets/flush_sensor/flush_persence_records.dart b/lib/features/devices/view/widgets/flush_sensor/flush_persence_records.dart new file mode 100644 index 0000000..a68535f --- /dev/null +++ b/lib/features/devices/view/widgets/flush_sensor/flush_persence_records.dart @@ -0,0 +1,120 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:intl/intl.dart'; +import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart'; +import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart'; +import 'package:syncrow_app/features/devices/model/device_report_model.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class FlushPresenceRecords extends StatelessWidget { + final String deviceId; + final String code; + final String title; + const FlushPresenceRecords( + {super.key, required this.deviceId, required this.code, required this.title}); + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: title, + child: BlocProvider( + create: (context) => FlushSensorBloc(deviceId: deviceId) + ..add(GetDeviceReportsEvent(deviceUuid: deviceId, code: code)), + child: BlocBuilder(builder: (context, state) { + final Map> groupedRecords = {}; + + if (state is LoadingInitialState) { + return const Center( + child: DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()), + ); + } else if (state is DeviceReportsState) { + for (var record in state.deviceReport.data ?? []) { + final DateTime eventDateTime = DateTime.fromMillisecondsSinceEpoch(record.eventTime!); + final String formattedDate = DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime); + + // Group by formatted date + if (groupedRecords.containsKey(formattedDate)) { + groupedRecords[formattedDate]!.add(record); + } else { + groupedRecords[formattedDate] = [record]; + } + } + } + return groupedRecords.isEmpty + ? const Center( + child: Text('No records found'), + ) + : ListView.builder( + itemCount: groupedRecords.length, + itemBuilder: (context, index) { + final String date = groupedRecords.keys.elementAt(index); + final List recordsForDate = groupedRecords[date]!; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 5, top: 10), + child: Text( + date, + style: const TextStyle( + color: ColorsManager.grayColor, + fontSize: 13, + fontWeight: FontWeight.w700, + ), + ), + ), + DefaultContainer( + child: Column( + children: [ + ...recordsForDate.asMap().entries.map((entry) { + final int idx = entry.key; + final DeviceEvent record = entry.value; + final DateTime eventDateTime = + DateTime.fromMillisecondsSinceEpoch(record.eventTime!); + final String formattedTime = + DateFormat('HH:mm:ss').format(eventDateTime); + + return Column( + children: [ + SizedBox( + child: ListTile( + leading: Icon( + record.value == 'true' + ? Icons.radio_button_checked + : Icons.radio_button_unchecked, + color: record.value == 'true' ? Colors.blue : Colors.grey, + ), + title: Text( + record.value == 'true' ? "Opened" : "Closed", + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + subtitle: Text('$formattedTime'), + ), + ), + if (idx != recordsForDate.length - 1) + const Divider( + color: ColorsManager.graysColor, + ), + ], + ); + }).toList(), + ], + ), + ), + ], + ); + }, + ); + }), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart new file mode 100644 index 0000000..085f173 --- /dev/null +++ b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart @@ -0,0 +1,113 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart'; +import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart'; +import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_persence_records.dart'; +import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_sensor_option.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/helpers/misc_string_helpers.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; +part "presence_indicator.dart"; +part "flush_sensor_options_list.dart"; +part 'flush_sensor_parameter_control_dialog.dart'; + +class FlushMountedInterface extends StatelessWidget { + const FlushMountedInterface({super.key, required this.deviceModel}); + final DeviceModel deviceModel; + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => FlushSensorBloc(deviceId: deviceModel.uuid ?? '') + ..add(InitialEvent()), + child: BlocBuilder( + builder: (context, state) { + final bloc = BlocProvider.of(context); + FlushSensorModel flushSensorModel = FlushSensorModel( + presenceState: 'none', + farDetection: 0, + illuminance: 0, + checkingResult: '', + nearDetection: 0, + noneDelay: 0, + occurDistReduce: 0, + presenceDelay: 0, + sensiReduce: 0, + sensitivity: 0); + + if (state is UpdateState) { + flushSensorModel = state.flushSensorModel; + } else if (state is LoadingNewSate) { + flushSensorModel = state.flushSensorModel; + } + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: Scaffold( + backgroundColor: ColorsManager.backgroundColor, + extendBodyBehindAppBar: true, + extendBody: true, + appBar: DeviceAppbar( + deviceName: deviceModel.name!, + deviceUuid: deviceModel.uuid!, + ), + body: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + padding: const EdgeInsets.all(Constants.defaultPadding), + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.assetsImagesBackground, + ), + fit: BoxFit.cover, + opacity: 0.4, + ), + ), + child: state is LoadingInitialState + ? const Center( + child: RefreshProgressIndicator(), + ) + : SafeArea( + child: RefreshIndicator( + onRefresh: () async { + bloc.add(InitialEvent()); + }, + child: ListView(children: [ + SizedBox( + height: MediaQuery.of(context).size.height, + child: Column(children: [ + PresenceIndicator( + state: flushSensorModel.presenceState, + ), + Expanded( + flex: 2, + child: FlushSensorOptionsList( + bloc: bloc, + flushSensorModel: flushSensorModel, + deviceModel: deviceModel, + ), + ) + ]), + ), + ]), + ), + )))); + }), + ); + } +} diff --git a/lib/features/devices/view/widgets/flush_sensor/flush_sensor_option.dart b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_option.dart new file mode 100644 index 0000000..7911bb0 --- /dev/null +++ b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_option.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class FlushSensorOption extends StatelessWidget { + final String icon; + final String label; + final VoidCallback onTap; + final String? sliderValue; + final bool withArrow; + + const FlushSensorOption({ + super.key, + required this.icon, + required this.label, + required this.onTap, + required this.withArrow, + this.sliderValue = '', + }); + + @override + Widget build(BuildContext context) { + return DefaultContainer( + margin: const EdgeInsets.only(bottom: 5), + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20), + onTap: onTap, + child: Row( + children: [ + SvgPicture.asset(icon), + const SizedBox(width: 25), + Expanded( + child: + Text(label, style: Theme.of(context).textTheme.bodyMedium)), + Text(sliderValue!, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: ColorsManager.grayTextColor, + fontWeight: FontWeight.bold)), + withArrow == true + ? const Icon(Icons.arrow_forward_ios, + color: ColorsManager.grayTextColor, size: 15) + : SizedBox(), + ], + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/flush_sensor/flush_sensor_options_list.dart b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_options_list.dart new file mode 100644 index 0000000..38f8ffb --- /dev/null +++ b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_options_list.dart @@ -0,0 +1,193 @@ +part of "flush_sensor_interface.dart"; + +class FlushSensorOptionsList extends StatelessWidget { + final FlushSensorModel flushSensorModel; + final DeviceModel deviceModel; + final FlushSensorBloc bloc; + const FlushSensorOptionsList( + {super.key, + required this.flushSensorModel, + required this.bloc, + required this.deviceModel}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FlushSensorOption( + withArrow: false, + sliderValue: '${flushSensorModel.illuminance.toString()} Lux', + icon: Assets.assetsIconsPresenceSensorAssetsIlluminanceValue, + label: 'Illuminance Value', + onTap: () {}, + ), + FlushSensorOption( + withArrow: false, + icon: Assets.assetsIconsPresenceSensorAssetsIlluminanceRecord, + label: 'Presence Record', + onTap: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => FlushPresenceRecords( + deviceId: deviceModel.uuid!, + code: FlushSensorModel.codePresenceState, + title: 'Illuminance Record'), + )); + }), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Text( + 'Configuration', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.bold), + ), + ), + FlushSensorOption( + withArrow: true, + sliderValue: flushSensorModel.sensitivity.toString(), + icon: Assets.assetsSensitivityFunction, + label: 'Sensitivity', + onTap: () => _showParameterDialog( + min: 0, + max: 9, + value: flushSensorModel.sensitivity.toDouble(), + context: context, + title: 'Sensitivity', + controlCode: FlushSensorModel.codeSensitivity, + step: 1.0, + ), + ), + FlushSensorOption( + withArrow: true, + sliderValue: "${flushSensorModel.nearDetection / 100} m", + icon: Assets.detectionDistanceIcon, + label: 'Min Detection Distance', + onTap: () => _showParameterDialog( + min: 0.0, + max: 9.5, + value: flushSensorModel.nearDetection.toDouble() / 100, + context: context, + title: 'Min Detection Distance', + controlCode: FlushSensorModel.codeNearDetection, + step: 0.1, + description: 'm', + ), + ), + FlushSensorOption( + withArrow: true, + sliderValue: '${flushSensorModel.farDetection / 100} m', + icon: Assets.detectionDistanceIcon, + label: 'Max Detection Distance', + onTap: () => _showParameterDialog( + description: 'm', + min: 0.0, + max: 9.5, + value: flushSensorModel.farDetection / 100, + context: context, + title: 'Max Detection Distance', + controlCode: FlushSensorModel.codeFarDetection, + step: 0.1), + ), + FlushSensorOption( + sliderValue: flushSensorModel.sensiReduce.toString(), + withArrow: true, + icon: Assets.triggerLevelIcon, + label: 'Trigger Level', + onTap: () => _showParameterDialog( + min: 0, + max: 3, + value: flushSensorModel.sensiReduce.toDouble(), + context: context, + title: 'Trigger Level', + controlCode: FlushSensorModel.codeSensiReduce, + step: 1), + ), + FlushSensorOption( + sliderValue: flushSensorModel.occurDistReduce.toString(), + withArrow: true, + icon: Assets.indentLevelIcon, + label: 'Indent Level', + onTap: () => _showParameterDialog( + min: 0, + max: 3, + value: flushSensorModel.occurDistReduce.toDouble(), + context: context, + title: 'Indent Level', + controlCode: FlushSensorModel.codeOccurDistReduce, + step: 1), + ), + FlushSensorOption( + sliderValue: '${flushSensorModel.presenceDelay / 10}', + withArrow: true, + icon: Assets.targetConfirmTimeIcon, + label: 'Target Confirm Time', + onTap: () => _showParameterDialog( + min: 0.0, + max: 0.5, + value: flushSensorModel.presenceDelay.toDouble() / 10, + context: context, + title: 'Target Confirm Time', + controlCode: FlushSensorModel.codePresenceDelay, + step: 0.1), + ), + FlushSensorOption( + sliderValue: '${flushSensorModel.noneDelay.toDouble() / 10}', + withArrow: true, + icon: Assets.disappeDelayIcon, + label: 'Disappe Delay', + onTap: () => _showParameterDialog( + min: 20, + max: 300, + value: flushSensorModel.noneDelay.toDouble() / 10, + context: context, + title: 'Disappe Delay', + controlCode: FlushSensorModel.codeNoneDelay, + step: 1), + ), + ], + ); + } + + void _showParameterDialog({ + required double step, + required BuildContext context, + required String title, + required String controlCode, + required double min, + required double max, + required double value, + String? description = '', + }) async { + var result = await showDialog( + context: context, + builder: (context) { + return FlushParameterControlDialog( + title: title, + sensor: deviceModel, + value: value, + min: min, + max: max, + step: step, + description: description); + }, + ); + + if (result != null) { + if (controlCode == FlushSensorModel.codeNearDetection || + controlCode == FlushSensorModel.codeFarDetection) { + result = (result * 100).toInt(); + } + if (controlCode == FlushSensorModel.codeNoneDelay) { + result = (result * 10).toInt(); + } + if (controlCode == FlushSensorModel.codePresenceDelay) { + result = (result * 10).toInt(); + } + bloc.add( + ChangeValueEvent(value: result, code: controlCode), + ); + } + } +} diff --git a/lib/features/devices/view/widgets/flush_sensor/flush_sensor_parameter_control_dialog.dart b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_parameter_control_dialog.dart new file mode 100644 index 0000000..1b4a052 --- /dev/null +++ b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_parameter_control_dialog.dart @@ -0,0 +1,181 @@ +part of 'flush_sensor_interface.dart'; + +class FlushParameterControlDialog extends StatefulWidget { + final String title; + final String? description; + final DeviceModel sensor; + final double value; + final double min; + final double max; + final double step; + + const FlushParameterControlDialog({ + super.key, + required this.title, + required this.sensor, + required this.value, + required this.min, + required this.max, + required this.step, + this.description = '', + }); + + @override + FlushParameterControlDialogState createState() => + FlushParameterControlDialogState(); +} + +class FlushParameterControlDialogState + extends State { + late double _value; + + @override + void initState() { + super.initState(); + _value = widget.value; + } + + @override + Widget build(BuildContext context) { + final divisions = ((widget.max - widget.min) / widget.step).toInt(); + final decimalPlaces = widget.step % 1 == 0 ? 0 : 1; + return Dialog( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.only(top: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + BodyMedium( + text: widget.title, + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity, + fontWeight: FontsManager.extraBold, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 15, + horizontal: 50, + ), + child: Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, + ), + child: TitleMedium( + text: + "${_value.toStringAsFixed(decimalPlaces)} ${widget.description}", + style: context.titleMedium.copyWith( + color: Colors.black, + fontWeight: FontsManager.bold, + ), + ), + ), + SizedBox( + width: MediaQuery.sizeOf(context).width, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + onPressed: () { + setState(() { + _value = (_value - widget.step) + .clamp(widget.min, widget.max); + _value = + double.parse(_value.toStringAsFixed(decimalPlaces)); + }); + }, + icon: const Icon(Icons.remove, color: Colors.grey), + ), + Flexible( + child: Slider( + value: _value, + onChanged: (value) { + final newValue = + (value / widget.step).round() * widget.step; + setState(() { + _value = newValue.clamp(widget.min, widget.max); + }); + }, + min: widget.min, + max: widget.max, + divisions: divisions, + label: _value.toStringAsFixed(decimalPlaces), + ), + ), + IconButton( + onPressed: () { + setState(() { + _value = (_value + widget.step) + .clamp(widget.min, widget.max); + _value = + double.parse(_value.toStringAsFixed(decimalPlaces)); + }); + }, + icon: const Icon(Icons.add, color: Colors.grey), + ), + ], + ), + ), + Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Center( + child: BodyMedium( + text: 'Cancel', + style: context.bodyMedium + .copyWith(color: ColorsManager.greyColor), + ), + ), + ), + Container( + height: 50, + width: 1, + color: ColorsManager.greyColor, + ), + InkWell( + onTap: () { + if (widget.sensor.isOnline == null) { + CustomSnackBar.displaySnackBar('The device is offline'); + return; + } + if (!widget.sensor.isOnline!) { + CustomSnackBar.displaySnackBar('The device is offline'); + return; + } + Navigator.pop(context, _value); + }, + child: Center( + child: BodyMedium( + text: 'Confirm', + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity), + ), + ), + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/flush_sensor/presence_indicator.dart b/lib/features/devices/view/widgets/flush_sensor/presence_indicator.dart new file mode 100644 index 0000000..746e08f --- /dev/null +++ b/lib/features/devices/view/widgets/flush_sensor/presence_indicator.dart @@ -0,0 +1,31 @@ +part of "flush_sensor_interface.dart"; + +class PresenceIndicator extends StatelessWidget { + const PresenceIndicator({super.key, required this.state}); + final String state; + @override + Widget build(BuildContext context) { + return Expanded( + flex: 1, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + state.toLowerCase() == 'motion' + ? Assets.assetsIconsPresenceSensorAssetsPresenceSensorMotion + : Assets.assetsIconsPresenceSensorAssetsPresence, + width: 100, + height: 100, + ), + const SizedBox( + height: 10, + ), + BodyMedium( + text: StringHelpers.toTitleCase(state), + style: context.bodyMedium.copyWith(fontWeight: FontsManager.bold), + ), + ], + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/room_page_switch.dart b/lib/features/devices/view/widgets/room_page_switch.dart index 9c95700..074a047 100644 --- a/lib/features/devices/view/widgets/room_page_switch.dart +++ b/lib/features/devices/view/widgets/room_page_switch.dart @@ -13,6 +13,7 @@ import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/door_sensor/door_sensor_screen.dart'; +import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/four_scene_switch/four_scene_screen.dart'; import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_door_screen.dart'; import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.dart'; @@ -300,6 +301,13 @@ Future showDeviceInterface( pageBuilder: (context, animation1, animation2) => SosScreen(device: device))); + case DeviceType.FlushMountedSensor: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + FlushMountedInterface(deviceModel: device))); + break; default: } diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 29aef40..53cd6ae 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -1148,4 +1148,11 @@ class Assets { static const String refreshStatusIcon = 'assets/icons/refresh_status_icon.svg'; + static const String detectionDistanceIcon = + 'assets/icons/detection_distance_icon.svg'; + static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg'; + static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg'; + static const String targetConfirmTimeIcon = + 'assets/icons/target_confirm_time_icon.svg'; + static const String disappeDelayIcon = 'assets/icons/disappe_delay_icon.svg'; } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index 4743fe0..35b390e 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -595,10 +595,7 @@ class DevicesAPI { final result = []; for (final device in data) { final mappedDevice = DeviceModel.fromJson(device); - if (mappedDevice.productType?.name != - DeviceType.FlushMountedSensor.name) { - result.add(mappedDevice); - } + result.add(mappedDevice); } return result; }, diff --git a/lib/utils/resource_manager/color_manager.dart b/lib/utils/resource_manager/color_manager.dart index fc792a8..8146d5e 100644 --- a/lib/utils/resource_manager/color_manager.dart +++ b/lib/utils/resource_manager/color_manager.dart @@ -36,7 +36,5 @@ abstract class ColorsManager { static const Color grayButtonColors = Color(0xffCCCCCC); static const Color blueButton = Color(0xff85BDFF); static const Color backgroundGrey = Color(0xFFF3F3F3); - + static const Color grayTextColor = Color(0xFFC4C4C4); } -//background: #F5F5F5;background: #85BDFF; - From 9cc4538dc503b6bf00b49b8b993e2bacece4215f Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 7 May 2025 10:11:33 +0300 Subject: [PATCH 2/2] Add flush sensor icon to assets and enhancement code --- assets/icons/flush_icon.svg | 22 +++++++ .../flush_sensor_bloc/flush_sensor_bloc.dart | 59 ++++++++++--------- .../flush_sensor_bloc/flush_sensor_event.dart | 20 ++++--- .../flush_sensor_bloc/flush_sensor_state.dart | 27 +++++---- lib/features/devices/model/device_model.dart | 2 +- .../flush_sensor/flush_persence_records.dart | 41 +++++++++---- .../flush_sensor/flush_sensor_interface.dart | 27 ++++----- .../flush_sensor_options_list.dart | 2 +- ...flush_sensor_parameter_control_dialog.dart | 8 +-- lib/generated/assets.dart | 1 + 10 files changed, 126 insertions(+), 83 deletions(-) create mode 100644 assets/icons/flush_icon.svg diff --git a/assets/icons/flush_icon.svg b/assets/icons/flush_icon.svg new file mode 100644 index 0000000..7ab3c69 --- /dev/null +++ b/assets/icons/flush_icon.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart index 28fafce..7790881 100644 --- a/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart +++ b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart'; import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart'; import 'package:syncrow_app/features/devices/model/status_model.dart'; @@ -15,17 +16,17 @@ class FlushSensorBloc extends Bloc { late DeviceModel deviceModel; late FlushSensorModel deviceStatus; - FlushSensorBloc({required this.deviceId}) : super(InitialState()) { - on(_fetchFlushSensorStatus); - on(_changeIndicator); - on(_changeValue); - on(_flushSensorUpdated); - on(_getDeviceReports); + FlushSensorBloc({required this.deviceId}) : super(FlushSensorInitialState()) { + on(_fetchFlushSensorStatus); + on(_changeIndicator); + on(_changeValue); + on(_flushSensorUpdated); + on(_getDeviceReports); } void _fetchFlushSensorStatus( - InitialEvent event, Emitter emit) async { - emit(LoadingInitialState()); + FlushSensorInitialEvent event, Emitter emit) async { + emit(FlushSensorLoadingInitialState()); try { var response = await DevicesAPI.getDeviceStatus(deviceId); List statusModelList = []; @@ -33,10 +34,10 @@ class FlushSensorBloc extends Bloc { statusModelList.add(StatusModel.fromJson(status)); } deviceStatus = FlushSensorModel.fromJson(statusModelList); - emit(UpdateState(flushSensorModel: deviceStatus)); + emit(FlushSensorUpdateState(flushSensorModel: deviceStatus)); _listenToChanges(); } catch (e) { - emit(FailedState(error: e.toString())); + emit(FlushSensorFailedState(error: e.toString())); return; } } @@ -60,7 +61,7 @@ class FlushSensorBloc extends Bloc { }); deviceStatus = FlushSensorModel.fromJson(statusList); if (!isClosed) { - add(WallSensorUpdatedEvent()); + add(FlushSensorUpdatedEvent()); } }); } catch (_) { @@ -79,51 +80,53 @@ class FlushSensorBloc extends Bloc { } _flushSensorUpdated( - WallSensorUpdatedEvent event, Emitter emit) { - emit(UpdateState(flushSensorModel: deviceStatus)); + FlushSensorUpdatedEvent event, Emitter emit) { + emit(FlushSensorUpdateState(flushSensorModel: deviceStatus)); } - void _changeIndicator( - ChangeIndicatorEvent event, Emitter emit) async { - emit(LoadingNewSate(flushSensorModel: deviceStatus)); + void _changeIndicator(FlushSensorChangeIndicatorEvent event, + Emitter emit) async { + emit(FlushSensorLoadingNewSate(flushSensorModel: deviceStatus)); try { final response = await DevicesAPI.controlDevice( DeviceControlModel( deviceId: deviceId, code: 'indicator', value: !event.value), deviceId); - } catch (e) { - emit(FailedState(error: e.toString())); + emit(FlushSensorFailedState(error: e.toString())); return; } - emit(UpdateState(flushSensorModel: deviceStatus)); + emit(FlushSensorUpdateState(flushSensorModel: deviceStatus)); } void _changeValue( - ChangeValueEvent event, Emitter emit) async { - emit(LoadingNewSate(flushSensorModel: deviceStatus)); + FlushSensorChangeValueEvent event, Emitter emit) async { + emit(FlushSensorLoadingNewSate(flushSensorModel: deviceStatus)); try { final response = await DevicesAPI.controlDevice( DeviceControlModel( deviceId: deviceId, code: event.code, value: event.value), deviceId); } catch (e) { - emit(FailedState(error: e.toString())); + emit(FlushSensorFailedState(error: e.toString())); return; } - emit(UpdateState(flushSensorModel: deviceStatus)); + emit(FlushSensorUpdateState(flushSensorModel: deviceStatus)); } - void _getDeviceReports( - GetDeviceReportsEvent event, Emitter emit) async { - emit(LoadingInitialState()); + void _getDeviceReports(FlushSensorGetDeviceReportsEvent event, + Emitter emit) async { + emit(FlushSensorLoadingInitialState()); try { await DevicesAPI.getDeviceReports(deviceId, event.code).then((value) { - emit(DeviceReportsState(deviceReport: value, code: event.code)); + emit(FlushSensorDeviceReportsState( + deviceReport: value, code: event.code)); }); } catch (e) { - emit(FailedState(error: e.toString())); + emit(FlushSensorFailedState(error: e.toString())); return; } } + + } diff --git a/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart index 99c5aa5..ddcd0d1 100644 --- a/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart +++ b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart @@ -7,33 +7,35 @@ abstract class FlushSensorEvent extends Equatable { List get props => []; } -class LoadingEvent extends FlushSensorEvent {} +class FlushSensorLoadingEvent extends FlushSensorEvent {} -class InitialEvent extends FlushSensorEvent {} +class FlushSensorInitialEvent extends FlushSensorEvent {} -class WallSensorUpdatedEvent extends FlushSensorEvent {} +class FlushSensorInitialDeviseInfo extends FlushSensorEvent {} -class ChangeIndicatorEvent extends FlushSensorEvent { +class FlushSensorUpdatedEvent extends FlushSensorEvent {} + +class FlushSensorChangeIndicatorEvent extends FlushSensorEvent { final bool value; - const ChangeIndicatorEvent({required this.value}); + const FlushSensorChangeIndicatorEvent({required this.value}); @override List get props => [value]; } -class ChangeValueEvent extends FlushSensorEvent { +class FlushSensorChangeValueEvent extends FlushSensorEvent { final dynamic value; final String code; - const ChangeValueEvent({required this.value, required this.code}); + const FlushSensorChangeValueEvent({required this.value, required this.code}); @override List get props => [value, code]; } -class GetDeviceReportsEvent extends FlushSensorEvent { +class FlushSensorGetDeviceReportsEvent extends FlushSensorEvent { final String deviceUuid; final String code; - const GetDeviceReportsEvent({ + const FlushSensorGetDeviceReportsEvent({ required this.deviceUuid, required this.code, }); diff --git a/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart index 5bbac2c..8fbc563 100644 --- a/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart +++ b/lib/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/device_info_model.dart'; import 'package:syncrow_app/features/devices/model/device_report_model.dart'; import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart'; @@ -9,37 +10,43 @@ class FlushSensorState extends Equatable { List get props => []; } -class InitialState extends FlushSensorState {} +class FlushSensorInitialState extends FlushSensorState {} -class LoadingInitialState extends FlushSensorState {} +class FlushSensorLoadingInitialState extends FlushSensorState {} -class UpdateState extends FlushSensorState { +class FlushSensorUpdateState extends FlushSensorState { final FlushSensorModel flushSensorModel; - const UpdateState({required this.flushSensorModel}); + const FlushSensorUpdateState({required this.flushSensorModel}); @override List get props => [flushSensorModel]; } -class LoadingNewSate extends FlushSensorState { +class FlushSensorLoadingNewSate extends FlushSensorState { final FlushSensorModel flushSensorModel; - const LoadingNewSate({required this.flushSensorModel}); + const FlushSensorLoadingNewSate({required this.flushSensorModel}); @override List get props => [flushSensorModel]; } -class FailedState extends FlushSensorState { +class FlushSensorFailedState extends FlushSensorState { final String error; - const FailedState({required this.error}); + const FlushSensorFailedState({required this.error}); @override List get props => [error]; } -class DeviceReportsState extends FlushSensorState { +class FlushSensorDeviceReportsState extends FlushSensorState { final DeviceReport deviceReport; final String code; - const DeviceReportsState({required this.deviceReport, required this.code}); + const FlushSensorDeviceReportsState( + {required this.deviceReport, required this.code}); +} + +class FlushSensorLoadingDeviceInfo extends FlushSensorState { + final DeviceInfoModel? deviceInfo; + const FlushSensorLoadingDeviceInfo({this.deviceInfo}); } diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index 7abeb9e..79c2b9c 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -93,7 +93,7 @@ class DeviceModel { } else if (type == DeviceType.SOS) { tempIcon = Assets.sosHomeIcon; } else if (type == DeviceType.FlushMountedSensor) { - tempIcon = Assets.sosHomeIcon; + tempIcon = Assets.flushIcon; } else { tempIcon = Assets.assetsIconsLogo; } diff --git a/lib/features/devices/view/widgets/flush_sensor/flush_persence_records.dart b/lib/features/devices/view/widgets/flush_sensor/flush_persence_records.dart index a68535f..a86a160 100644 --- a/lib/features/devices/view/widgets/flush_sensor/flush_persence_records.dart +++ b/lib/features/devices/view/widgets/flush_sensor/flush_persence_records.dart @@ -15,7 +15,10 @@ class FlushPresenceRecords extends StatelessWidget { final String code; final String title; const FlushPresenceRecords( - {super.key, required this.deviceId, required this.code, required this.title}); + {super.key, + required this.deviceId, + required this.code, + required this.title}); @override Widget build(BuildContext context) { @@ -23,18 +26,23 @@ class FlushPresenceRecords extends StatelessWidget { title: title, child: BlocProvider( create: (context) => FlushSensorBloc(deviceId: deviceId) - ..add(GetDeviceReportsEvent(deviceUuid: deviceId, code: code)), - child: BlocBuilder(builder: (context, state) { + ..add(FlushSensorGetDeviceReportsEvent( + deviceUuid: deviceId, code: code)), + child: BlocBuilder( + builder: (context, state) { final Map> groupedRecords = {}; - if (state is LoadingInitialState) { + if (state is FlushSensorLoadingInitialState) { return const Center( - child: DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()), + child: DefaultContainer( + width: 50, height: 50, child: CircularProgressIndicator()), ); - } else if (state is DeviceReportsState) { + } else if (state is FlushSensorDeviceReportsState) { for (var record in state.deviceReport.data ?? []) { - final DateTime eventDateTime = DateTime.fromMillisecondsSinceEpoch(record.eventTime!); - final String formattedDate = DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime); + final DateTime eventDateTime = + DateTime.fromMillisecondsSinceEpoch(record.eventTime!); + final String formattedDate = + DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime); // Group by formatted date if (groupedRecords.containsKey(formattedDate)) { @@ -52,7 +60,8 @@ class FlushPresenceRecords extends StatelessWidget { itemCount: groupedRecords.length, itemBuilder: (context, index) { final String date = groupedRecords.keys.elementAt(index); - final List recordsForDate = groupedRecords[date]!; + final List recordsForDate = + groupedRecords[date]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -75,9 +84,11 @@ class FlushPresenceRecords extends StatelessWidget { final int idx = entry.key; final DeviceEvent record = entry.value; final DateTime eventDateTime = - DateTime.fromMillisecondsSinceEpoch(record.eventTime!); + DateTime.fromMillisecondsSinceEpoch( + record.eventTime!); final String formattedTime = - DateFormat('HH:mm:ss').format(eventDateTime); + DateFormat('HH:mm:ss') + .format(eventDateTime); return Column( children: [ @@ -87,10 +98,14 @@ class FlushPresenceRecords extends StatelessWidget { record.value == 'true' ? Icons.radio_button_checked : Icons.radio_button_unchecked, - color: record.value == 'true' ? Colors.blue : Colors.grey, + color: record.value == 'true' + ? Colors.blue + : Colors.grey, ), title: Text( - record.value == 'true' ? "Opened" : "Closed", + record.value == 'true' + ? "Opened" + : "Closed", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 18, diff --git a/lib/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart index 085f173..341ccc7 100644 --- a/lib/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart +++ b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart @@ -8,9 +8,11 @@ import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart'; +import 'package:syncrow_app/features/devices/view/device_settings/settings_page.dart'; import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart'; import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_persence_records.dart'; import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_sensor_option.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; import 'package:syncrow_app/generated/assets.dart'; @@ -31,7 +33,7 @@ class FlushMountedInterface extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => FlushSensorBloc(deviceId: deviceModel.uuid ?? '') - ..add(InitialEvent()), + ..add(FlushSensorInitialEvent()), child: BlocBuilder( builder: (context, state) { final bloc = BlocProvider.of(context); @@ -47,28 +49,23 @@ class FlushMountedInterface extends StatelessWidget { sensiReduce: 0, sensitivity: 0); - if (state is UpdateState) { + if (state is FlushSensorUpdateState) { flushSensorModel = state.flushSensorModel; - } else if (state is LoadingNewSate) { + } else if (state is FlushSensorLoadingNewSate) { flushSensorModel = state.flushSensorModel; } + return AnnotatedRegion( value: SystemUiOverlayStyle( statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), statusBarIconBrightness: Brightness.light, ), - child: Scaffold( - backgroundColor: ColorsManager.backgroundColor, - extendBodyBehindAppBar: true, - extendBody: true, - appBar: DeviceAppbar( - deviceName: deviceModel.name!, - deviceUuid: deviceModel.uuid!, - ), - body: Container( + child: DefaultScaffold( + title: deviceModel.name!, + + child: Container( width: MediaQuery.sizeOf(context).width, height: MediaQuery.sizeOf(context).height, - padding: const EdgeInsets.all(Constants.defaultPadding), decoration: const BoxDecoration( image: DecorationImage( image: AssetImage( @@ -78,14 +75,14 @@ class FlushMountedInterface extends StatelessWidget { opacity: 0.4, ), ), - child: state is LoadingInitialState + child: state is FlushSensorLoadingInitialState ? const Center( child: RefreshProgressIndicator(), ) : SafeArea( child: RefreshIndicator( onRefresh: () async { - bloc.add(InitialEvent()); + bloc.add(FlushSensorInitialEvent()); }, child: ListView(children: [ SizedBox( diff --git a/lib/features/devices/view/widgets/flush_sensor/flush_sensor_options_list.dart b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_options_list.dart index 38f8ffb..1619d7d 100644 --- a/lib/features/devices/view/widgets/flush_sensor/flush_sensor_options_list.dart +++ b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_options_list.dart @@ -186,7 +186,7 @@ class FlushSensorOptionsList extends StatelessWidget { result = (result * 10).toInt(); } bloc.add( - ChangeValueEvent(value: result, code: controlCode), + FlushSensorChangeValueEvent(value: result, code: controlCode), ); } } diff --git a/lib/features/devices/view/widgets/flush_sensor/flush_sensor_parameter_control_dialog.dart b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_parameter_control_dialog.dart index 1b4a052..ce12324 100644 --- a/lib/features/devices/view/widgets/flush_sensor/flush_sensor_parameter_control_dialog.dart +++ b/lib/features/devices/view/widgets/flush_sensor/flush_sensor_parameter_control_dialog.dart @@ -153,14 +153,10 @@ class FlushParameterControlDialogState ), InkWell( onTap: () { - if (widget.sensor.isOnline == null) { + if (widget.sensor.isOnline == null || !widget.sensor.isOnline!) { CustomSnackBar.displaySnackBar('The device is offline'); return; - } - if (!widget.sensor.isOnline!) { - CustomSnackBar.displaySnackBar('The device is offline'); - return; - } + } Navigator.pop(context, _value); }, child: Center( diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 53cd6ae..700c631 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -1155,4 +1155,5 @@ class Assets { static const String targetConfirmTimeIcon = 'assets/icons/target_confirm_time_icon.svg'; static const String disappeDelayIcon = 'assets/icons/disappe_delay_icon.svg'; + static const String flushIcon = 'assets/icons/flush_icon.svg'; }