Compare commits

..

1 Commits

Author SHA1 Message Date
d80f5e1f3a Refactor energy consumption charts to enhance grid data configuration
Updated the grid data for EnergyConsumptionByPhasesChart, EnergyConsumptionPerDeviceChart, and TotalEnergyConsumptionChart to include horizontal line visibility and set a horizontal interval of 250. Removed unused phasesJson constant from TotalEnergyConsumptionChart for cleaner code.
2025-05-15 14:25:13 +03:00
11 changed files with 109 additions and 133 deletions

View File

@ -1,66 +1,27 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
class PhasesEnergyConsumption extends Equatable { class PhasesEnergyConsumption extends Equatable {
final String uuid; final int month;
final DateTime createdAt; final double phaseA;
final DateTime updatedAt; final double phaseB;
final String deviceUuid; final double phaseC;
final DateTime date;
final double energyConsumedKw;
final double energyConsumedA;
final double energyConsumedB;
final double energyConsumedC;
const PhasesEnergyConsumption({ const PhasesEnergyConsumption({
required this.uuid, required this.month,
required this.createdAt, required this.phaseA,
required this.updatedAt, required this.phaseB,
required this.deviceUuid, required this.phaseC,
required this.date,
required this.energyConsumedKw,
required this.energyConsumedA,
required this.energyConsumedB,
required this.energyConsumedC,
}); });
@override @override
List<Object?> get props => [ List<Object?> get props => [month, phaseA, phaseB, phaseC];
uuid,
createdAt,
updatedAt,
deviceUuid,
date,
energyConsumedKw,
energyConsumedA,
energyConsumedB,
energyConsumedC,
];
factory PhasesEnergyConsumption.fromJson(Map<String, dynamic> json) { factory PhasesEnergyConsumption.fromJson(Map<String, dynamic> json) {
return PhasesEnergyConsumption( return PhasesEnergyConsumption(
uuid: json['uuid'] as String, month: json['month'] as int,
createdAt: DateTime.parse(json['createdAt'] as String), phaseA: (json['phaseA'] as num).toDouble(),
updatedAt: DateTime.parse(json['updatedAt'] as String), phaseB: (json['phaseB'] as num).toDouble(),
deviceUuid: json['deviceUuid'] as String, phaseC: (json['phaseC'] as num).toDouble(),
date: DateTime.parse(json['date'] as String),
energyConsumedKw: double.parse(json['energyConsumedKw']),
energyConsumedA: double.parse(json['energyConsumedA']),
energyConsumedB: double.parse(json['energyConsumedB']),
energyConsumedC: double.parse(json['energyConsumedC']),
); );
} }
Map<String, dynamic> toJson() {
return {
'uuid': uuid,
'createdAt': createdAt.toIso8601String(),
'updatedAt': updatedAt.toIso8601String(),
'deviceUuid': deviceUuid,
'date': date.toIso8601String().split('T')[0],
'energyConsumedKw': energyConsumedKw.toString(),
'energyConsumedA': energyConsumedA.toString(),
'energyConsumedB': energyConsumedB.toString(),
'energyConsumedC': energyConsumedC.toString(),
};
}
} }

View File

@ -15,7 +15,7 @@ import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_he
import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service_delagate.dart'; import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service_delagate.dart';
import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_energy_management_analytics_devices_service.dart'; import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_energy_management_analytics_devices_service.dart';
import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart'; import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart';
import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/remote_energy_consumption_by_phases_service.dart'; import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/fake_energy_consumption_by_phases_service.dart';
import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/remote_energy_consumption_per_device_service.dart'; import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/remote_energy_consumption_per_device_service.dart';
import 'package:syncrow_web/pages/analytics/services/occupacy/fake_occupacy_service.dart'; import 'package:syncrow_web/pages/analytics/services/occupacy/fake_occupacy_service.dart';
import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/remote_occupancy_heat_map_service.dart'; import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/remote_occupancy_heat_map_service.dart';
@ -57,7 +57,7 @@ class _AnalyticsPageState extends State<AnalyticsPage> {
), ),
BlocProvider( BlocProvider(
create: (context) => EnergyConsumptionByPhasesBloc( create: (context) => EnergyConsumptionByPhasesBloc(
RemoteEnergyConsumptionByPhasesService(_httpService), FakeEnergyConsumptionByPhasesService(),
), ),
), ),
BlocProvider( BlocProvider(

View File

@ -0,0 +1,20 @@
import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart';
abstract final class EnergyConsumptionByPhasesChartHelper {
const EnergyConsumptionByPhasesChartHelper._();
static const fakeData = <PhasesEnergyConsumption>[
PhasesEnergyConsumption(month: 1, phaseA: 200, phaseB: 300, phaseC: 400),
PhasesEnergyConsumption(month: 2, phaseA: 300, phaseB: 400, phaseC: 500),
PhasesEnergyConsumption(month: 3, phaseA: 400, phaseB: 500, phaseC: 600),
PhasesEnergyConsumption(month: 4, phaseA: 100, phaseB: 100, phaseC: 100),
PhasesEnergyConsumption(month: 5, phaseA: 300, phaseB: 400, phaseC: 500),
PhasesEnergyConsumption(month: 6, phaseA: 300, phaseB: 100, phaseC: 400),
PhasesEnergyConsumption(month: 7, phaseA: 300, phaseB: 100, phaseC: 400),
PhasesEnergyConsumption(month: 8, phaseA: 500, phaseB: 100, phaseC: 100),
PhasesEnergyConsumption(month: 9, phaseA: 500, phaseB: 100, phaseC: 200),
PhasesEnergyConsumption(month: 10, phaseA: 100, phaseB: 50, phaseC: 50),
PhasesEnergyConsumption(month: 11, phaseA: 600, phaseB: 750, phaseC: 130),
PhasesEnergyConsumption(month: 12, phaseA: 100, phaseB: 80, phaseC: 100),
];
}

View File

@ -34,26 +34,14 @@ abstract final class FetchEnergyManagementDataHelper {
final datePickerState = context.read<AnalyticsDatePickerBloc>().state; final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
final selectedDate0 = selectedDate ?? datePickerState.monthlyDate; final selectedDate0 = selectedDate ?? datePickerState.monthlyDate;
loadAnalyticsDevices( loadAnalyticsDevices(context, communityUuid: communityId, spaceUuid: spaceId);
context,
communityUuid: communityId,
spaceUuid: spaceId,
selectedDate: selectedDate0,
);
loadTotalEnergyConsumption( loadTotalEnergyConsumption(
context, context,
selectedDate: selectedDate0, selectedDate: selectedDate0,
communityId: communityId, communityId: communityId,
spaceId: spaceId, spaceId: spaceId,
); );
final selectedDevice = getSelectedDevice(context); loadEnergyConsumptionByPhases(context, selectedDate: selectedDate);
if (selectedDevice case final AnalyticsDevice device) {
loadEnergyConsumptionByPhases(
context,
powerClampUuid: device.uuid,
selectedDate: selectedDate0,
);
}
loadEnergyConsumptionPerDevice( loadEnergyConsumptionPerDevice(
context, context,
communityId: communityId, communityId: communityId,
@ -66,12 +54,11 @@ abstract final class FetchEnergyManagementDataHelper {
static void loadEnergyConsumptionByPhases( static void loadEnergyConsumptionByPhases(
BuildContext context, { BuildContext context, {
required String powerClampUuid,
DateTime? selectedDate, DateTime? selectedDate,
}) { }) {
final param = GetEnergyConsumptionByPhasesParam( final param = GetEnergyConsumptionByPhasesParam(
date: selectedDate, startDate: selectedDate,
powerClampUuid: powerClampUuid, spaceId: '',
); );
context.read<EnergyConsumptionByPhasesBloc>().add( context.read<EnergyConsumptionByPhasesBloc>().add(
LoadEnergyConsumptionByPhasesEvent(param: param), LoadEnergyConsumptionByPhasesEvent(param: param),
@ -134,7 +121,6 @@ abstract final class FetchEnergyManagementDataHelper {
BuildContext context, { BuildContext context, {
required String communityUuid, required String communityUuid,
required String spaceUuid, required String spaceUuid,
required DateTime selectedDate,
}) { }) {
context.read<AnalyticsDevicesBloc>().add( context.read<AnalyticsDevicesBloc>().add(
LoadAnalyticsDevicesEvent( LoadAnalyticsDevicesEvent(
@ -142,11 +128,6 @@ abstract final class FetchEnergyManagementDataHelper {
context.read<PowerClampInfoBloc>().add( context.read<PowerClampInfoBloc>().add(
LoadPowerClampInfoEvent(device.uuid), LoadPowerClampInfoEvent(device.uuid),
); );
loadEnergyConsumptionByPhases(
context,
powerClampUuid: device.uuid,
selectedDate: selectedDate,
);
context.read<RealtimeDeviceChangesBloc>().add( context.read<RealtimeDeviceChangesBloc>().add(
RealtimeDeviceChangesStarted(device.uuid), RealtimeDeviceChangesStarted(device.uuid),
); );

View File

@ -1,6 +1,6 @@
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:syncrow_web/pages/analytics/helpers/get_month_name_from_int.dart';
import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart'; import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
@ -18,7 +18,10 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BarChart( return BarChart(
BarChartData( BarChartData(
gridData: EnergyManagementChartsHelper.gridData(), gridData: EnergyManagementChartsHelper.gridData().copyWith(
checkToShowHorizontalLine: (value) => true,
horizontalInterval: 250,
),
borderData: EnergyManagementChartsHelper.borderData(), borderData: EnergyManagementChartsHelper.borderData(),
barTouchData: _barTouchData(context), barTouchData: _barTouchData(context),
titlesData: _titlesData(context), titlesData: _titlesData(context),
@ -31,25 +34,21 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
barRods: [ barRods: [
BarChartRodData( BarChartRodData(
color: ColorsManager.vividBlue.withValues(alpha: 0.1), color: ColorsManager.vividBlue.withValues(alpha: 0.1),
toY: data.energyConsumedA + toY: data.phaseA + data.phaseB + data.phaseC,
data.energyConsumedB +
data.energyConsumedC,
rodStackItems: [ rodStackItems: [
BarChartRodStackItem( BarChartRodStackItem(
0, 0,
data.energyConsumedA, data.phaseA,
ColorsManager.vividBlue.withValues(alpha: 0.8), ColorsManager.vividBlue.withValues(alpha: 0.8),
), ),
BarChartRodStackItem( BarChartRodStackItem(
data.energyConsumedA, data.phaseA,
data.energyConsumedA + data.energyConsumedB, data.phaseA + data.phaseB,
ColorsManager.vividBlue.withValues(alpha: 0.4), ColorsManager.vividBlue.withValues(alpha: 0.4),
), ),
BarChartRodStackItem( BarChartRodStackItem(
data.energyConsumedA + data.energyConsumedB, data.phaseA + data.phaseB,
data.energyConsumedA + data.phaseA + data.phaseB + data.phaseC,
data.energyConsumedB +
data.energyConsumedC,
ColorsManager.vividBlue.withValues(alpha: 0.15), ColorsManager.vividBlue.withValues(alpha: 0.15),
), ),
], ],
@ -95,27 +94,18 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
}) { }) {
final data = energyData; final data = energyData;
final date = DateFormat('dd/MM/yyyy').format(data[group.x.toInt()].date); final month = data[group.x.toInt()].month.getMonthName;
final phaseA = data[group.x.toInt()].energyConsumedA; final phaseA = data[group.x.toInt()].phaseA;
final phaseB = data[group.x.toInt()].energyConsumedB; final phaseB = data[group.x.toInt()].phaseB;
final phaseC = data[group.x.toInt()].energyConsumedC; final phaseC = data[group.x.toInt()].phaseC;
final total = data[group.x.toInt()].energyConsumedKw;
return BarTooltipItem( return BarTooltipItem(
'$date\n', '$month\n',
context.textTheme.bodyMedium!.copyWith( context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.blackColor, color: ColorsManager.blackColor,
fontSize: 14, fontSize: 14,
), ),
textAlign: TextAlign.start,
children: [ children: [
TextSpan(
text: 'Total: $total\n',
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize: 12,
),
),
TextSpan( TextSpan(
text: 'Phase A: $phaseA\n', text: 'Phase A: $phaseA\n',
style: context.textTheme.bodySmall?.copyWith( style: context.textTheme.bodySmall?.copyWith(
@ -157,7 +147,7 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
sideTitles: SideTitles( sideTitles: SideTitles(
showTitles: true, showTitles: true,
getTitlesWidget: (value, _) { getTitlesWidget: (value, _) {
final month = DateFormat('dd/MM').format(energyData[value.toInt()].date); final month = energyData[value.toInt()].month.getMonthName;
return FittedBox( return FittedBox(
alignment: AlignmentDirectional.bottomCenter, alignment: AlignmentDirectional.bottomCenter,
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,

View File

@ -16,7 +16,10 @@ class EnergyConsumptionPerDeviceChart extends StatelessWidget {
context, context,
leftTitlesInterval: 250, leftTitlesInterval: 250,
), ),
gridData: EnergyManagementChartsHelper.gridData(), gridData: EnergyManagementChartsHelper.gridData().copyWith(
checkToShowHorizontalLine: (value) => true,
horizontalInterval: 250,
),
borderData: EnergyManagementChartsHelper.borderData(), borderData: EnergyManagementChartsHelper.borderData(),
lineTouchData: EnergyManagementChartsHelper.lineTouchData(), lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
lineBarsData: chartData.map((e) { lineBarsData: chartData.map((e) {

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/models/power_clamp_energy_status.dart'; import 'package:syncrow_web/pages/analytics/models/power_clamp_energy_status.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_date_picker_bloc/analytics_date_picker_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart';
@ -12,7 +11,6 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/po
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_phases_data_widget.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_phases_data_widget.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart'; import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
import 'package:syncrow_web/pages/device_managment/power_clamp/models/power_clamp_model.dart'; import 'package:syncrow_web/pages/device_managment/power_clamp/models/power_clamp_model.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -134,12 +132,6 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
alignment: AlignmentDirectional.centerEnd, alignment: AlignmentDirectional.centerEnd,
child: AnalyticsDeviceDropdown( child: AnalyticsDeviceDropdown(
onChanged: (value) { onChanged: (value) {
FetchEnergyManagementDataHelper.loadEnergyConsumptionByPhases(
context,
powerClampUuid: value.uuid,
selectedDate:
context.read<AnalyticsDatePickerBloc>().state.monthlyDate,
);
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges( FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(
context, context,
deviceUuid: value.uuid, deviceUuid: value.uuid,

View File

@ -4,15 +4,6 @@ import 'package:syncrow_web/pages/analytics/models/energy_data_model.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
// energy_consumption_chart will return id, name and consumption
const phasesJson = {
"1": {
"phaseOne": 1000,
"phaseTwo": 2000,
"phaseThree": 3000,
}
};
class TotalEnergyConsumptionChart extends StatelessWidget { class TotalEnergyConsumptionChart extends StatelessWidget {
const TotalEnergyConsumptionChart({required this.chartData, super.key}); const TotalEnergyConsumptionChart({required this.chartData, super.key});
@ -23,8 +14,14 @@ class TotalEnergyConsumptionChart extends StatelessWidget {
return Expanded( return Expanded(
child: LineChart( child: LineChart(
LineChartData( LineChartData(
titlesData: EnergyManagementChartsHelper.titlesData(context), titlesData: EnergyManagementChartsHelper.titlesData(
gridData: EnergyManagementChartsHelper.gridData(), context,
leftTitlesInterval: 250,
),
gridData: EnergyManagementChartsHelper.gridData().copyWith(
checkToShowHorizontalLine: (value) => true,
horizontalInterval: 250,
),
borderData: EnergyManagementChartsHelper.borderData(), borderData: EnergyManagementChartsHelper.borderData(),
lineTouchData: EnergyManagementChartsHelper.lineTouchData(), lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
lineBarsData: _lineBarsData, lineBarsData: _lineBarsData,

View File

@ -1,20 +1,24 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
class GetEnergyConsumptionByPhasesParam extends Equatable { class GetEnergyConsumptionByPhasesParam extends Equatable {
final String powerClampUuid; final DateTime? startDate;
final DateTime? date; final DateTime? endDate;
final String? spaceId;
const GetEnergyConsumptionByPhasesParam({ const GetEnergyConsumptionByPhasesParam({
required this.powerClampUuid, this.startDate,
this.date, this.endDate,
this.spaceId,
}); });
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'monthDate': '${date?.year}-${date?.month.toString().padLeft(2, '0')}', 'startDate': startDate?.toIso8601String(),
'endDate': endDate?.toIso8601String(),
'spaceId': spaceId,
}; };
} }
@override @override
List<Object?> get props => [powerClampUuid, date]; List<Object?> get props => [startDate, endDate, spaceId];
} }

View File

@ -0,0 +1,29 @@
import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart';
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_by_phases_param.dart';
import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/energy_consumption_by_phases_service.dart';
class FakeEnergyConsumptionByPhasesService
implements EnergyConsumptionByPhasesService {
@override
Future<List<PhasesEnergyConsumption>> load(
GetEnergyConsumptionByPhasesParam param,
) {
return Future.delayed(
const Duration(milliseconds: 500),
() => const [
PhasesEnergyConsumption(month: 1, phaseA: 200, phaseB: 300, phaseC: 400),
PhasesEnergyConsumption(month: 2, phaseA: 300, phaseB: 400, phaseC: 500),
PhasesEnergyConsumption(month: 3, phaseA: 400, phaseB: 500, phaseC: 600),
PhasesEnergyConsumption(month: 4, phaseA: 100, phaseB: 100, phaseC: 100),
PhasesEnergyConsumption(month: 5, phaseA: 300, phaseB: 400, phaseC: 500),
PhasesEnergyConsumption(month: 6, phaseA: 300, phaseB: 100, phaseC: 400),
PhasesEnergyConsumption(month: 7, phaseA: 300, phaseB: 100, phaseC: 400),
PhasesEnergyConsumption(month: 8, phaseA: 500, phaseB: 100, phaseC: 100),
PhasesEnergyConsumption(month: 9, phaseA: 500, phaseB: 100, phaseC: 200),
PhasesEnergyConsumption(month: 10, phaseA: 100, phaseB: 50, phaseC: 50),
PhasesEnergyConsumption(month: 11, phaseA: 600, phaseB: 750, phaseC: 130),
PhasesEnergyConsumption(month: 12, phaseA: 100, phaseB: 100, phaseC: 100),
],
);
}
}

View File

@ -15,9 +15,8 @@ final class RemoteEnergyConsumptionByPhasesService
) async { ) async {
try { try {
final response = await _httpService.get( final response = await _httpService.get(
path: '/power-clamp/${param.powerClampUuid}/historical', path: 'endpoint',
showServerMessage: true, showServerMessage: true,
queryParameters: param.toJson(),
expectedResponseModel: (data) { expectedResponseModel: (data) {
final json = data as Map<String, dynamic>? ?? {}; final json = data as Map<String, dynamic>? ?? {};
final mappedData = json['data'] as List<dynamic>? ?? []; final mappedData = json['data'] as List<dynamic>? ?? [];
@ -29,7 +28,7 @@ final class RemoteEnergyConsumptionByPhasesService
); );
return response; return response;
} catch (e) { } catch (e) {
throw Exception('Failed to load energy consumption per phase: $e'); throw Exception('Failed to load energy consumption per device: $e');
} }
} }
} }