Merge branch 'dev' of https://github.com/SyncrowIOT/web into dev

This commit is contained in:
hannathkadher
2025-04-28 10:03:31 +04:00
12 changed files with 178 additions and 148 deletions

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class SearchResetButtons extends StatelessWidget { class SearchResetButtons extends StatelessWidget {
const SearchResetButtons({ const SearchResetButtons({
@ -17,8 +17,10 @@ class SearchResetButtons extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
mainAxisSize: MainAxisSize.min,
children: [ children: [
Column( Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
const SizedBox(height: 25), const SizedBox(height: 25),

View File

@ -72,6 +72,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
child: state is DeviceManagementLoading child: state is DeviceManagementLoading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: Column( : Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Container(
padding: isLargeScreenSize(context) padding: isLargeScreenSize(context)

View File

@ -14,29 +14,29 @@ class DeviceSearchFilters extends StatefulWidget {
class _DeviceSearchFiltersState extends State<DeviceSearchFilters> class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
with HelperResponsiveLayout { with HelperResponsiveLayout {
final _unitNameController = TextEditingController(); late final TextEditingController _unitNameController;
final _productNameController = TextEditingController(); late final TextEditingController _productNameController;
List<Widget> get _widgets => [ @override
_buildSearchField("Space Name", _unitNameController, 200), void initState() {
_buildSearchField("Device Name / Product Name", _productNameController, 300), _unitNameController = TextEditingController();
_buildSearchResetButtons(), _productNameController = TextEditingController();
]; super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (isExtraLargeScreenSize(context)) {
return Row(
children: _widgets
.map((e) => Padding(padding: const EdgeInsets.all(10), child: e))
.toList(),
);
}
return Wrap( return Wrap(
alignment: WrapAlignment.start,
runAlignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 20, spacing: 20,
runSpacing: 10, runSpacing: 10,
children: _widgets, children: [
_buildSearchField("Space Name", _unitNameController, 200),
_buildSearchField("Device Name / Product Name", _productNameController, 300),
_buildSearchResetButtons(),
],
); );
} }

View File

@ -24,11 +24,10 @@ class FlushMountedPresenceSensorChangeValueEvent
extends FlushMountedPresenceSensorEvent { extends FlushMountedPresenceSensorEvent {
final int value; final int value;
final String code; final String code;
final bool isBatchControl;
const FlushMountedPresenceSensorChangeValueEvent({ const FlushMountedPresenceSensorChangeValueEvent({
required this.value, required this.value,
required this.code, required this.code,
this.isBatchControl = false,
}); });
@override @override

View File

@ -75,7 +75,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
), ),
), ),
PresenceUpdateData( PresenceUpdateData(
value: (model.nearDetection / 100).toDouble(), value: (model.nearDetection / 100).clamp(0.0, double.infinity),
title: 'Nearest Detect Dist:', title: 'Nearest Detect Dist:',
description: 'm', description: 'm',
minValue: 0.0, minValue: 0.0,
@ -92,7 +92,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
), ),
), ),
PresenceUpdateData( PresenceUpdateData(
value: (model.farDetection / 100).toDouble(), value: (model.farDetection / 100).clamp(0.0, double.infinity),
title: 'Max Detect Dist:', title: 'Max Detect Dist:',
description: 'm', description: 'm',
minValue: 0.0, minValue: 0.0,
@ -109,7 +109,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
), ),
), ),
PresenceUpdateData( PresenceUpdateData(
value: model.presenceDelay.toDouble(), value: model.sensiReduce.toDouble(),
title: 'Trigger Level:', title: 'Trigger Level:',
minValue: 0, minValue: 0,
maxValue: 3, maxValue: 3,
@ -117,7 +117,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add( action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent( FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds, deviceIds: devicesIds,
code: FlushMountedPresenceSensorModel.codePresenceDelay, code: FlushMountedPresenceSensorModel.codeSensiReduce,
value: value, value: value,
), ),
), ),
@ -137,19 +137,21 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
), ),
), ),
PresenceUpdateData( PresenceUpdateData(
value: (model.sensiReduce.toDouble()), value: (model.presenceDelay / 10).toDouble(),
title: 'Target Confirm Time:', title: 'Target Confirm Time:',
description: 's', description: 's',
minValue: 0, minValue: 0.0,
maxValue: 3, maxValue: 0.5,
steps: 1, steps: 0.1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add( valuesPercision: 1,
FlushMountedPresenceSensorBatchControlEvent( action: (double value) =>
deviceIds: devicesIds, context.read<FlushMountedPresenceSensorBloc>().add(
code: FlushMountedPresenceSensorModel.codeSensiReduce, FlushMountedPresenceSensorBatchControlEvent(
value: value, deviceIds: devicesIds,
), code: FlushMountedPresenceSensorModel.codePresenceDelay,
), value: (value * 10).toInt(),
),
),
), ),
PresenceUpdateData( PresenceUpdateData(
value: ((model.noneDelay / 10).toDouble()), value: ((model.noneDelay / 10).toDouble()),

View File

@ -15,7 +15,7 @@ import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_la
class FlushMountedPresenceSensorControlView extends StatelessWidget class FlushMountedPresenceSensorControlView extends StatelessWidget
with HelperResponsiveLayout { with HelperResponsiveLayout {
const FlushMountedPresenceSensorControlView({super.key, required this.device}); const FlushMountedPresenceSensorControlView({required this.device, super.key});
final AllDevicesModel device; final AllDevicesModel device;
@ -113,7 +113,7 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
), ),
), ),
PresenceUpdateData( PresenceUpdateData(
value: (model.nearDetection / 100).toDouble(), value: (model.nearDetection / 100).clamp(0.0, double.infinity),
title: 'Nearest Detect Dist:', title: 'Nearest Detect Dist:',
description: 'm', description: 'm',
minValue: 0.0, minValue: 0.0,
@ -129,7 +129,7 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
), ),
), ),
PresenceUpdateData( PresenceUpdateData(
value: (model.farDetection / 100).toDouble(), value: (model.farDetection / 100).clamp(0.0, double.infinity),
title: 'Max Detect Dist:', title: 'Max Detect Dist:',
description: 'm', description: 'm',
minValue: 0.0, minValue: 0.0,
@ -145,20 +145,20 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
), ),
), ),
PresenceUpdateData( PresenceUpdateData(
value: (model.presenceDelay.toDouble()), value: model.sensiReduce.toDouble(),
title: 'Trigger Level:', title: 'Trigger Level:',
minValue: 0, minValue: 0,
maxValue: 3, maxValue: 3,
steps: 1, steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add( action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorChangeValueEvent( FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codePresenceDelay, code: FlushMountedPresenceSensorModel.codeSensiReduce,
value: value, value: value,
), ),
), ),
), ),
PresenceUpdateData( PresenceUpdateData(
value: (model.occurDistReduce.toDouble()), value: model.occurDistReduce.toDouble(),
title: 'Indent Level:', title: 'Indent Level:',
minValue: 0, minValue: 0,
maxValue: 3, maxValue: 3,
@ -171,21 +171,23 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
), ),
), ),
PresenceUpdateData( PresenceUpdateData(
value: (model.sensiReduce.toDouble()), value: (model.presenceDelay / 10).toDouble(),
valuesPercision: 1,
title: 'Target Confirm Time:', title: 'Target Confirm Time:',
description: 's', description: 's',
minValue: 0, minValue: 0.0,
maxValue: 3, maxValue: 0.5,
steps: 1, steps: 0.1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add( action: (double value) =>
FlushMountedPresenceSensorChangeValueEvent( context.read<FlushMountedPresenceSensorBloc>().add(
code: FlushMountedPresenceSensorModel.codeSensiReduce, FlushMountedPresenceSensorChangeValueEvent(
value: value, code: FlushMountedPresenceSensorModel.codePresenceDelay,
), value: (value * 10).toInt(),
), ),
),
), ),
PresenceUpdateData( PresenceUpdateData(
value: ((model.noneDelay / 10).toDouble()), value: (model.noneDelay / 10).toDouble(),
description: 's', description: 's',
title: 'Disappe Delay:', title: 'Disappe Delay:',
minValue: 20, minValue: 20,

View File

@ -217,29 +217,31 @@ class SmartPowerBloc extends Bloc<SmartPowerEvent, SmartPowerState> {
try { try {
var status = var status =
await DevicesManagementApi().getPowerClampInfo(event.deviceId); await DevicesManagementApi().getPowerClampInfo(event.deviceId);
deviceStatus = PowerClampModel.fromJson(status); deviceStatus = PowerClampModel.fromJson(status as Map<String, Object?>? ??{});
final phaseADataPoints = deviceStatus.status.phaseA.dataPoints;
final phaseBDataPoints = deviceStatus.status.phaseB.dataPoints;
final phaseCDataPoints = deviceStatus.status.phaseC.dataPoints;
phaseData = [ phaseData = [
{ {
'name': 'Phase A', 'name': 'Phase A',
'voltage': '${deviceStatus.status.phaseA.dataPoints[0].value / 10} V', 'voltage': '${(phaseADataPoints.elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
'current': '${deviceStatus.status.phaseA.dataPoints[1].value / 10} A', 'current': '${(phaseADataPoints.elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
'activePower': '${deviceStatus.status.phaseA.dataPoints[2].value} W', 'activePower': '${phaseADataPoints.elementAtOrNull(2)?.value??'N/A'} W',
'powerFactor': '${deviceStatus.status.phaseA.dataPoints[3].value}', 'powerFactor': '${phaseADataPoints.elementAtOrNull(3)?.value??'N/A'}',
}, },
{ {
'name': 'Phase B', 'name': 'Phase B',
'voltage': '${deviceStatus.status.phaseB.dataPoints[0].value / 10} V', 'voltage': '${(phaseBDataPoints .elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
'current': '${deviceStatus.status.phaseB.dataPoints[1].value / 10} A', 'current': '${(phaseBDataPoints .elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
'activePower': '${deviceStatus.status.phaseB.dataPoints[2].value} W', 'activePower': '${phaseBDataPoints.elementAtOrNull(2)?.value??'N/A'} W',
'powerFactor': '${deviceStatus.status.phaseB.dataPoints[3].value}', 'powerFactor': '${phaseBDataPoints.elementAtOrNull(3)?.value??'N/A'}',
}, },
{ {
'name': 'Phase C', 'name': 'Phase C',
'voltage': '${deviceStatus.status.phaseC.dataPoints[0].value / 10} V', 'voltage': '${(phaseCDataPoints.elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
'current': '${deviceStatus.status.phaseC.dataPoints[1].value / 10} A', 'current': '${(phaseCDataPoints.elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
'activePower': '${deviceStatus.status.phaseC.dataPoints[2].value} W', 'activePower': '${phaseCDataPoints.elementAtOrNull(2)?.value ?? 'N/A'} W',
'powerFactor': '${deviceStatus.status.phaseC.dataPoints[3].value}', 'powerFactor': '${phaseCDataPoints.elementAtOrNull(3)?.value ?? 'N/A'}',
}, },
]; ];
emit(GetDeviceStatus()); emit(GetDeviceStatus());
@ -785,7 +787,7 @@ class SmartPowerBloc extends Bloc<SmartPowerEvent, SmartPowerState> {
void selectDateRange() async { void selectDateRange() async {
DateTime startDate = dateTime!; DateTime startDate = dateTime!;
DateTime endDate = DateTime(startDate.year, startDate.month + 1, 1) DateTime endDate = DateTime(startDate.year, startDate.month + 1, 1)
.subtract(Duration(days: 1)); .subtract(const Duration(days: 1));
String formattedEndDate = DateFormat('dd/MM/yyyy').format(endDate); String formattedEndDate = DateFormat('dd/MM/yyyy').format(endDate);
endChartDate = ' - $formattedEndDate'; endChartDate = ' - $formattedEndDate';
} }

View File

@ -12,9 +12,9 @@ class PowerClampModel {
factory PowerClampModel.fromJson(Map<String, dynamic> json) { factory PowerClampModel.fromJson(Map<String, dynamic> json) {
return PowerClampModel( return PowerClampModel(
productUuid: json['productUuid'], productUuid: json['productUuid'] as String? ?? '',
productType: json['productType'], productType: json['productType'] as String? ?? '',
status: PowerStatus.fromJson(json['status']), status: PowerStatus.fromJson(json['status'] as Map<String, dynamic>? ?? {}),
); );
} }
@ -26,7 +26,7 @@ class PowerClampModel {
return PowerClampModel( return PowerClampModel(
productUuid: productUuid ?? this.productUuid, productUuid: productUuid ?? this.productUuid,
productType: productType ?? this.productType, productType: productType ?? this.productType,
status: statusPower ?? this.status, status: statusPower ?? status,
); );
} }
} }
@ -46,12 +46,10 @@ class PowerStatus {
factory PowerStatus.fromJson(Map<String, dynamic> json) { factory PowerStatus.fromJson(Map<String, dynamic> json) {
return PowerStatus( return PowerStatus(
phaseA: Phase.fromJson(json['phaseA']), phaseA: Phase.fromJson(json['phaseA']as List<dynamic>? ?? []),
phaseB: Phase.fromJson(json['phaseB']), phaseB: Phase.fromJson(json['phaseB']as List<dynamic>? ?? []),
phaseC: Phase.fromJson(json['phaseC']), phaseC: Phase.fromJson(json['phaseC']as List<dynamic>? ?? []),
general: Phase.fromJson(json['general'] general: Phase.fromJson(json['general']as List<dynamic>? ?? []
// List<DataPoint>.from(
// json['general'].map((x) => DataPoint.fromJson(x))),
)); ));
} }
} }
@ -69,30 +67,30 @@ class Phase {
} }
class DataPoint { class DataPoint {
dynamic code; final String? code;
dynamic customName; final String? customName;
dynamic dpId; final int? dpId;
dynamic time; final int? time;
dynamic type; final String? type;
dynamic value; final dynamic value;
DataPoint({ DataPoint({
required this.code, this.code,
required this.customName, this.customName,
required this.dpId, this.dpId,
required this.time, this.time,
required this.type, this.type,
required this.value, this.value,
}); });
factory DataPoint.fromJson(Map<String, dynamic> json) { factory DataPoint.fromJson(Map<String, dynamic> json) {
return DataPoint( return DataPoint(
code: json['code'], code: json['code'] as String?,
customName: json['customName'], customName: json['customName'] as String?,
dpId: json['dpId'], dpId: json['dpId'] as int?,
time: json['time'], time: json['time'] as int?,
type: json['type'], type: json['type'] as String?,
value: json['value'], value: json['value'] as dynamic,
); );
} }
} }

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class EnergyConsumptionPage extends StatefulWidget { class EnergyConsumptionPage extends StatefulWidget {
@ -10,7 +10,8 @@ class EnergyConsumptionPage extends StatefulWidget {
final Widget widget; final Widget widget;
final Function()? onTap; final Function()? onTap;
EnergyConsumptionPage({ const EnergyConsumptionPage({
super.key,
required this.chartData, required this.chartData,
required this.totalConsumption, required this.totalConsumption,
required this.date, required this.date,
@ -91,11 +92,12 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
], ],
), ),
Column( Column(
mainAxisSize: MainAxisSize.min,
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.only(top: 10), padding: const EdgeInsets.only(top: 10),
child: SizedBox( child: SizedBox(
height: MediaQuery.of(context).size.height * 0.11, height: MediaQuery.sizeOf(context).height * 0.09,
child: LineChart( child: LineChart(
LineChartData( LineChartData(
lineTouchData: LineTouchData( lineTouchData: LineTouchData(
@ -151,7 +153,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
child: RotatedBox( child: RotatedBox(
quarterTurns: -1, quarterTurns: -1,
child: Text(_chartData[index].time, child: Text(_chartData[index].time,
style: TextStyle(fontSize: 10)), style: const TextStyle(fontSize: 10)),
), ),
); );
} }
@ -190,8 +192,8 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
spots: _chartData spots: _chartData
.asMap() .asMap()
.entries .entries
.map((entry) => FlSpot(entry.key.toDouble(), .map((entry) => FlSpot(
entry.value.consumption)) entry.key.toDouble(), entry.value.consumption))
.toList(), .toList(),
isCurved: true, isCurved: true,
color: ColorsManager.primaryColor.withOpacity(0.6), color: ColorsManager.primaryColor.withOpacity(0.6),
@ -218,7 +220,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
borderData: FlBorderData( borderData: FlBorderData(
show: false, show: false,
border: Border.all( border: Border.all(
color: Color(0xff023DFE).withOpacity(0.7), color: const Color(0xff023DFE).withOpacity(0.7),
width: 10, width: 10,
), ),
), ),
@ -253,11 +255,9 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
child: InkWell( child: InkWell(
onTap: widget.onTap, onTap: widget.onTap,
child: Center( child: Center(
child: SizedBox( child: Padding(
child: Padding( padding: const EdgeInsets.all(5),
padding: const EdgeInsets.all(5), child: Text(widget.date),
child: Text(widget.date),
),
), ),
), ),
), ),

View File

@ -12,8 +12,7 @@ import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
//Smart Power Clamp //Smart Power Clamp
class SmartPowerDeviceControl extends StatelessWidget class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayout {
with HelperResponsiveLayout {
final String deviceId; final String deviceId;
const SmartPowerDeviceControl({super.key, required this.deviceId}); const SmartPowerDeviceControl({super.key, required this.deviceId});
@ -25,27 +24,27 @@ class SmartPowerDeviceControl extends StatelessWidget
..add(SmartPowerFetchDeviceEvent(deviceId)), ..add(SmartPowerFetchDeviceEvent(deviceId)),
child: BlocBuilder<SmartPowerBloc, SmartPowerState>( child: BlocBuilder<SmartPowerBloc, SmartPowerState>(
builder: (context, state) { builder: (context, state) {
final _blocProvider = BlocProvider.of<SmartPowerBloc>(context); final blocProvider = BlocProvider.of<SmartPowerBloc>(context);
if (state is SmartPowerLoading) { if (state is SmartPowerLoading) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} else if (state is FakeState) { } else if (state is FakeState) {
return _buildStatusControls( return _buildStatusControls(
currentPage: _blocProvider.currentPage, currentPage: blocProvider.currentPage,
context: context, context: context,
blocProvider: _blocProvider, blocProvider: blocProvider,
); );
} else if (state is GetDeviceStatus) { } else if (state is GetDeviceStatus) {
return _buildStatusControls( return _buildStatusControls(
currentPage: _blocProvider.currentPage, currentPage: blocProvider.currentPage,
context: context, context: context,
blocProvider: _blocProvider, blocProvider: blocProvider,
); );
} else if (state is FilterRecordsState) { } else if (state is FilterRecordsState) {
return _buildStatusControls( return _buildStatusControls(
currentPage: _blocProvider.currentPage, currentPage: blocProvider.currentPage,
context: context, context: context,
blocProvider: _blocProvider, blocProvider: blocProvider,
); );
} }
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
@ -60,7 +59,7 @@ class SmartPowerDeviceControl extends StatelessWidget
required SmartPowerBloc blocProvider, required SmartPowerBloc blocProvider,
required int currentPage, required int currentPage,
}) { }) {
PageController _pageController = PageController(initialPage: currentPage); PageController pageController = PageController(initialPage: currentPage);
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 50), padding: const EdgeInsets.symmetric(horizontal: 50),
child: DeviceControlsContainer( child: DeviceControlsContainer(
@ -85,25 +84,31 @@ class SmartPowerDeviceControl extends StatelessWidget
PowerClampInfoCard( PowerClampInfoCard(
iconPath: Assets.powerActiveIcon, iconPath: Assets.powerActiveIcon,
title: 'Active', title: 'Active',
value: blocProvider value: blocProvider.deviceStatus.status.general.dataPoints
.deviceStatus.status.general.dataPoints[2].value .elementAtOrNull(2)
.toString(), ?.value
.toString() ??
'',
unit: '', unit: '',
), ),
PowerClampInfoCard( PowerClampInfoCard(
iconPath: Assets.voltMeterIcon, iconPath: Assets.voltMeterIcon,
title: 'Current', title: 'Current',
value: blocProvider value: blocProvider.deviceStatus.status.general.dataPoints
.deviceStatus.status.general.dataPoints[1].value .elementAtOrNull(1)
.toString(), ?.value
.toString() ??
'',
unit: ' A', unit: ' A',
), ),
PowerClampInfoCard( PowerClampInfoCard(
iconPath: Assets.frequencyIcon, iconPath: Assets.frequencyIcon,
title: 'Frequency', title: 'Frequency',
value: blocProvider value: blocProvider.deviceStatus.status.general.dataPoints
.deviceStatus.status.general.dataPoints[4].value .elementAtOrNull(4)
.toString(), ?.value
.toString() ??
'',
unit: ' Hz', unit: ' Hz',
), ),
], ],
@ -142,7 +147,7 @@ class SmartPowerDeviceControl extends StatelessWidget
icon: const Icon(Icons.arrow_left), icon: const Icon(Icons.arrow_left),
onPressed: () { onPressed: () {
blocProvider.add(SmartPowerArrowPressedEvent(-1)); blocProvider.add(SmartPowerArrowPressedEvent(-1));
_pageController.previousPage( pageController.previousPage(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut, curve: Curves.easeInOut,
); );
@ -162,7 +167,7 @@ class SmartPowerDeviceControl extends StatelessWidget
icon: const Icon(Icons.arrow_right), icon: const Icon(Icons.arrow_right),
onPressed: () { onPressed: () {
blocProvider.add(SmartPowerArrowPressedEvent(1)); blocProvider.add(SmartPowerArrowPressedEvent(1));
_pageController.nextPage( pageController.nextPage(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut, curve: Curves.easeInOut,
); );
@ -177,7 +182,7 @@ class SmartPowerDeviceControl extends StatelessWidget
Expanded( Expanded(
flex: 2, flex: 2,
child: PageView( child: PageView(
controller: _pageController, controller: pageController,
onPageChanged: (int page) { onPageChanged: (int page) {
blocProvider.add(SmartPowerPageChangedEvent(page)); blocProvider.add(SmartPowerPageChangedEvent(page));
}, },
@ -190,8 +195,8 @@ class SmartPowerDeviceControl extends StatelessWidget
blocProvider.add(SelectDateEvent(context: context)); blocProvider.add(SelectDateEvent(context: context));
blocProvider.add(FilterRecordsByDateEvent( blocProvider.add(FilterRecordsByDateEvent(
selectedDate: blocProvider.dateTime!, selectedDate: blocProvider.dateTime!,
viewType: blocProvider viewType:
.views[blocProvider.currentIndex])); blocProvider.views[blocProvider.currentIndex]));
}, },
widget: blocProvider.dateSwitcher(), widget: blocProvider.dateSwitcher(),
chartData: blocProvider.energyDataList.isNotEmpty chartData: blocProvider.energyDataList.isNotEmpty

View File

@ -46,14 +46,14 @@ final class DebouncedBatchControlDevicesService
final BatchControlDevicesService decoratee; final BatchControlDevicesService decoratee;
final Duration debounceDuration; final Duration debounceDuration;
final _pendingRequests = <(List<String> uuids, String code, Object value)>[];
var _isProcessing = false;
DebouncedBatchControlDevicesService({ DebouncedBatchControlDevicesService({
required this.decoratee, required this.decoratee,
this.debounceDuration = const Duration(milliseconds: 800), this.debounceDuration = const Duration(milliseconds: 1500),
}); });
final _pendingRequests = <(List<String> uuids, String code, Object value)>[];
var _isProcessing = false;
@override @override
Future<bool> batchControlDevices({ Future<bool> batchControlDevices({
required List<String> uuids, required List<String> uuids,
@ -68,16 +68,26 @@ final class DebouncedBatchControlDevicesService
await Future.delayed(debounceDuration); await Future.delayed(debounceDuration);
final lastRequest = _pendingRequests.last; final groupedRequests =
<String, (List<String> uuids, String code, Object value)>{};
for (final request in _pendingRequests) {
final (_, requestCode, requestValue) = request;
groupedRequests[requestCode] = request;
}
_pendingRequests.clear(); _pendingRequests.clear();
try { try {
final (lastRequestUuids, lastRequestCode, lastRequestValue) = lastRequest; var allSuccessful = true;
return decoratee.batchControlDevices( for (final request in groupedRequests.values) {
uuids: lastRequestUuids, final (lastRequestUuids, lastRequestCode, lastRequestValue) = request;
code: lastRequestCode, final success = await decoratee.batchControlDevices(
value: lastRequestValue, uuids: lastRequestUuids,
); code: lastRequestCode,
value: lastRequestValue,
);
if (!success) allSuccessful = false;
}
return allSuccessful;
} finally { } finally {
_isProcessing = false; _isProcessing = false;
} }

View File

@ -40,7 +40,7 @@ final class DebouncedControlDeviceService implements ControlDeviceService {
DebouncedControlDeviceService({ DebouncedControlDeviceService({
required this.decoratee, required this.decoratee,
this.debounceDuration = const Duration(milliseconds: 800), this.debounceDuration = const Duration(milliseconds: 1500),
}); });
final _pendingRequests = <(String deviceUuid, Status status)>[]; final _pendingRequests = <(String deviceUuid, Status status)>[];
@ -59,15 +59,24 @@ final class DebouncedControlDeviceService implements ControlDeviceService {
await Future.delayed(debounceDuration); await Future.delayed(debounceDuration);
final lastRequest = _pendingRequests.last; final groupedRequests = <String, (String deviceUuid, Status status)>{};
for (final request in _pendingRequests) {
final (_, requestStatus) = request;
groupedRequests[requestStatus.code] = request;
}
_pendingRequests.clear(); _pendingRequests.clear();
try { try {
final (lastRequestDeviceUuid, lastRequestStatus) = lastRequest; var allSuccessful = true;
return decoratee.controlDevice( for (final request in groupedRequests.values) {
deviceUuid: lastRequestDeviceUuid, final (lastRequestDeviceUuid, lastRequestStatus) = request;
status: lastRequestStatus, final success = await decoratee.controlDevice(
); deviceUuid: lastRequestDeviceUuid,
status: lastRequestStatus,
);
if (!success) allSuccessful = false;
}
return allSuccessful;
} finally { } finally {
_isProcessing = false; _isProcessing = false;
} }