mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-11 15:47:44 +00:00
Compare commits
7 Commits
SP-1509-FE
...
SP-1506-FE
Author | SHA1 | Date | |
---|---|---|---|
c07b53107e | |||
5279020d08 | |||
da481536c4 | |||
f21366268a | |||
c3aef736fd | |||
d45ff262c7 | |||
a9d6c6f4ee |
@ -1,27 +1,66 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
class PhasesEnergyConsumption extends Equatable {
|
class PhasesEnergyConsumption extends Equatable {
|
||||||
final int month;
|
final String uuid;
|
||||||
final double phaseA;
|
final DateTime createdAt;
|
||||||
final double phaseB;
|
final DateTime updatedAt;
|
||||||
final double phaseC;
|
final String deviceUuid;
|
||||||
|
final DateTime date;
|
||||||
|
final double energyConsumedKw;
|
||||||
|
final double energyConsumedA;
|
||||||
|
final double energyConsumedB;
|
||||||
|
final double energyConsumedC;
|
||||||
|
|
||||||
const PhasesEnergyConsumption({
|
const PhasesEnergyConsumption({
|
||||||
required this.month,
|
required this.uuid,
|
||||||
required this.phaseA,
|
required this.createdAt,
|
||||||
required this.phaseB,
|
required this.updatedAt,
|
||||||
required this.phaseC,
|
required this.deviceUuid,
|
||||||
|
required this.date,
|
||||||
|
required this.energyConsumedKw,
|
||||||
|
required this.energyConsumedA,
|
||||||
|
required this.energyConsumedB,
|
||||||
|
required this.energyConsumedC,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [month, phaseA, phaseB, phaseC];
|
List<Object?> get props => [
|
||||||
|
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(
|
||||||
month: json['month'] as int,
|
uuid: json['uuid'] as String,
|
||||||
phaseA: (json['phaseA'] as num).toDouble(),
|
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||||
phaseB: (json['phaseB'] as num).toDouble(),
|
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||||
phaseC: (json['phaseC'] as num).toDouble(),
|
deviceUuid: json['deviceUuid'] as String,
|
||||||
|
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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@ 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/fake_energy_consumption_by_phases_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_per_device/fake_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';
|
||||||
import 'package:syncrow_web/pages/analytics/services/power_clamp_info/remote_power_clamp_info_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/power_clamp_info/remote_power_clamp_info_service.dart';
|
||||||
@ -57,12 +57,12 @@ class _AnalyticsPageState extends State<AnalyticsPage> {
|
|||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => EnergyConsumptionByPhasesBloc(
|
create: (context) => EnergyConsumptionByPhasesBloc(
|
||||||
FakeEnergyConsumptionByPhasesService(),
|
RemoteEnergyConsumptionByPhasesService(_httpService),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => EnergyConsumptionPerDeviceBloc(
|
create: (context) => EnergyConsumptionPerDeviceBloc(
|
||||||
FakeEnergyConsumptionPerDeviceService(),
|
RemoteEnergyConsumptionPerDeviceService(_httpService),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
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),
|
|
||||||
];
|
|
||||||
}
|
|
@ -34,26 +34,44 @@ 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(context, communityUuid: communityId, spaceUuid: spaceId);
|
loadAnalyticsDevices(
|
||||||
|
context,
|
||||||
|
communityUuid: communityId,
|
||||||
|
spaceUuid: spaceId,
|
||||||
|
selectedDate: selectedDate0,
|
||||||
|
);
|
||||||
loadTotalEnergyConsumption(
|
loadTotalEnergyConsumption(
|
||||||
context,
|
context,
|
||||||
selectedDate: selectedDate0,
|
selectedDate: selectedDate0,
|
||||||
communityId: communityId,
|
communityId: communityId,
|
||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
);
|
);
|
||||||
loadEnergyConsumptionByPhases(context, selectedDate: selectedDate);
|
final selectedDevice = getSelectedDevice(context);
|
||||||
loadEnergyConsumptionPerDevice(context);
|
if (selectedDevice case final AnalyticsDevice device) {
|
||||||
|
loadEnergyConsumptionByPhases(
|
||||||
|
context,
|
||||||
|
powerClampUuid: device.uuid,
|
||||||
|
selectedDate: selectedDate0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
loadEnergyConsumptionPerDevice(
|
||||||
|
context,
|
||||||
|
communityId: communityId,
|
||||||
|
spaceId: spaceId,
|
||||||
|
selectedDate: selectedDate0,
|
||||||
|
);
|
||||||
loadRealtimeDeviceChanges(context);
|
loadRealtimeDeviceChanges(context);
|
||||||
loadPowerClampInfo(context);
|
loadPowerClampInfo(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void loadEnergyConsumptionByPhases(
|
static void loadEnergyConsumptionByPhases(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
|
required String powerClampUuid,
|
||||||
DateTime? selectedDate,
|
DateTime? selectedDate,
|
||||||
}) {
|
}) {
|
||||||
final param = GetEnergyConsumptionByPhasesParam(
|
final param = GetEnergyConsumptionByPhasesParam(
|
||||||
startDate: selectedDate,
|
date: selectedDate,
|
||||||
spaceId: '',
|
powerClampUuid: powerClampUuid,
|
||||||
);
|
);
|
||||||
context.read<EnergyConsumptionByPhasesBloc>().add(
|
context.read<EnergyConsumptionByPhasesBloc>().add(
|
||||||
LoadEnergyConsumptionByPhasesEvent(param: param),
|
LoadEnergyConsumptionByPhasesEvent(param: param),
|
||||||
@ -76,10 +94,19 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void loadEnergyConsumptionPerDevice(BuildContext context) {
|
static void loadEnergyConsumptionPerDevice(
|
||||||
const param = GetEnergyConsumptionPerDeviceParam();
|
BuildContext context, {
|
||||||
|
DateTime? selectedDate,
|
||||||
|
required String communityId,
|
||||||
|
required String spaceId,
|
||||||
|
}) {
|
||||||
|
final param = GetEnergyConsumptionPerDeviceParam(
|
||||||
|
spaceId: spaceId,
|
||||||
|
communityId: communityId,
|
||||||
|
monthDate: selectedDate,
|
||||||
|
);
|
||||||
context.read<EnergyConsumptionPerDeviceBloc>().add(
|
context.read<EnergyConsumptionPerDeviceBloc>().add(
|
||||||
const LoadEnergyConsumptionPerDeviceEvent(param),
|
LoadEnergyConsumptionPerDeviceEvent(param),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +134,7 @@ 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(
|
||||||
@ -114,6 +142,11 @@ 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),
|
||||||
);
|
);
|
||||||
|
@ -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:syncrow_web/pages/analytics/helpers/get_month_name_from_int.dart';
|
import 'package:intl/intl.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';
|
||||||
@ -31,21 +31,25 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
barRods: [
|
barRods: [
|
||||||
BarChartRodData(
|
BarChartRodData(
|
||||||
color: ColorsManager.vividBlue.withValues(alpha: 0.1),
|
color: ColorsManager.vividBlue.withValues(alpha: 0.1),
|
||||||
toY: data.phaseA + data.phaseB + data.phaseC,
|
toY: data.energyConsumedA +
|
||||||
|
data.energyConsumedB +
|
||||||
|
data.energyConsumedC,
|
||||||
rodStackItems: [
|
rodStackItems: [
|
||||||
BarChartRodStackItem(
|
BarChartRodStackItem(
|
||||||
0,
|
0,
|
||||||
data.phaseA,
|
data.energyConsumedA,
|
||||||
ColorsManager.vividBlue.withValues(alpha: 0.8),
|
ColorsManager.vividBlue.withValues(alpha: 0.8),
|
||||||
),
|
),
|
||||||
BarChartRodStackItem(
|
BarChartRodStackItem(
|
||||||
data.phaseA,
|
data.energyConsumedA,
|
||||||
data.phaseA + data.phaseB,
|
data.energyConsumedA + data.energyConsumedB,
|
||||||
ColorsManager.vividBlue.withValues(alpha: 0.4),
|
ColorsManager.vividBlue.withValues(alpha: 0.4),
|
||||||
),
|
),
|
||||||
BarChartRodStackItem(
|
BarChartRodStackItem(
|
||||||
data.phaseA + data.phaseB,
|
data.energyConsumedA + data.energyConsumedB,
|
||||||
data.phaseA + data.phaseB + data.phaseC,
|
data.energyConsumedA +
|
||||||
|
data.energyConsumedB +
|
||||||
|
data.energyConsumedC,
|
||||||
ColorsManager.vividBlue.withValues(alpha: 0.15),
|
ColorsManager.vividBlue.withValues(alpha: 0.15),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -91,18 +95,27 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
}) {
|
}) {
|
||||||
final data = energyData;
|
final data = energyData;
|
||||||
|
|
||||||
final month = data[group.x.toInt()].month.getMonthName;
|
final date = DateFormat('dd/MM/yyyy').format(data[group.x.toInt()].date);
|
||||||
final phaseA = data[group.x.toInt()].phaseA;
|
final phaseA = data[group.x.toInt()].energyConsumedA;
|
||||||
final phaseB = data[group.x.toInt()].phaseB;
|
final phaseB = data[group.x.toInt()].energyConsumedB;
|
||||||
final phaseC = data[group.x.toInt()].phaseC;
|
final phaseC = data[group.x.toInt()].energyConsumedC;
|
||||||
|
final total = data[group.x.toInt()].energyConsumedKw;
|
||||||
|
|
||||||
return BarTooltipItem(
|
return BarTooltipItem(
|
||||||
'$month\n',
|
'$date\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(
|
||||||
@ -144,7 +157,7 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
sideTitles: SideTitles(
|
sideTitles: SideTitles(
|
||||||
showTitles: true,
|
showTitles: true,
|
||||||
getTitlesWidget: (value, _) {
|
getTitlesWidget: (value, _) {
|
||||||
final month = energyData[value.toInt()].month.getMonthName;
|
final month = DateFormat('dd/MM').format(energyData[value.toInt()].date);
|
||||||
return FittedBox(
|
return FittedBox(
|
||||||
alignment: AlignmentDirectional.bottomCenter,
|
alignment: AlignmentDirectional.bottomCenter,
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
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';
|
||||||
@ -11,6 +12,7 @@ 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';
|
||||||
@ -132,6 +134,12 @@ 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,
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class HeatMapTooltip extends StatelessWidget {
|
||||||
|
const HeatMapTooltip({
|
||||||
|
required this.date,
|
||||||
|
required this.value,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final DateTime date;
|
||||||
|
final int value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: AlignmentDirectional.topStart,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.grey700,
|
||||||
|
borderRadius: BorderRadius.circular(3),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
DateFormat('MMM d, yyyy').format(date),
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 2, thickness: 1),
|
||||||
|
Text(
|
||||||
|
'$value Occupants',
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/heat_map_tooltip.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_painter.dart';
|
||||||
|
|
||||||
|
class InteractiveHeatMap extends StatefulWidget {
|
||||||
|
const InteractiveHeatMap({
|
||||||
|
required this.items,
|
||||||
|
required this.maxValue,
|
||||||
|
required this.cellSize,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<OccupancyPaintItem> items;
|
||||||
|
final int maxValue;
|
||||||
|
final double cellSize;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<InteractiveHeatMap> createState() => _InteractiveHeatMapState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InteractiveHeatMapState extends State<InteractiveHeatMap> {
|
||||||
|
OccupancyPaintItem? _hoveredItem;
|
||||||
|
OverlayEntry? _overlayEntry;
|
||||||
|
final LayerLink _layerLink = LayerLink();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_removeOverlay();
|
||||||
|
_overlayEntry?.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _removeOverlay() {
|
||||||
|
_overlayEntry?.remove();
|
||||||
|
_overlayEntry = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showTooltip(OccupancyPaintItem item, Offset localPosition) {
|
||||||
|
_removeOverlay();
|
||||||
|
|
||||||
|
final column = item.index ~/ 7;
|
||||||
|
final row = item.index % 7;
|
||||||
|
final x = column * widget.cellSize;
|
||||||
|
final y = row * widget.cellSize;
|
||||||
|
|
||||||
|
_overlayEntry = OverlayEntry(
|
||||||
|
builder: (context) => Positioned(
|
||||||
|
child: CompositedTransformFollower(
|
||||||
|
link: _layerLink,
|
||||||
|
offset: Offset(x + widget.cellSize, y),
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Transform.translate(
|
||||||
|
offset: Offset(-(widget.cellSize * 2.5), -50),
|
||||||
|
child: HeatMapTooltip(date: item.date, value: item.value),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Overlay.of(context).insert(_overlayEntry!);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return CompositedTransformTarget(
|
||||||
|
link: _layerLink,
|
||||||
|
child: MouseRegion(
|
||||||
|
onHover: (event) {
|
||||||
|
final column = event.localPosition.dx ~/ widget.cellSize;
|
||||||
|
final row = event.localPosition.dy ~/ widget.cellSize;
|
||||||
|
final index = column * 7 + row;
|
||||||
|
|
||||||
|
if (index >= 0 && index < widget.items.length) {
|
||||||
|
final item = widget.items[index];
|
||||||
|
if (_hoveredItem != item) {
|
||||||
|
setState(() => _hoveredItem = item);
|
||||||
|
_showTooltip(item, event.localPosition);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_removeOverlay();
|
||||||
|
setState(() => _hoveredItem = null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onExit: (_) {
|
||||||
|
_removeOverlay();
|
||||||
|
setState(() => _hoveredItem = null);
|
||||||
|
},
|
||||||
|
child: CustomPaint(
|
||||||
|
isComplex: true,
|
||||||
|
size: _painterSize,
|
||||||
|
painter: OccupancyPainter(
|
||||||
|
items: widget.items,
|
||||||
|
maxValue: widget.maxValue,
|
||||||
|
hoveredItem: _hoveredItem,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Size get _painterSize {
|
||||||
|
final height = 7 * widget.cellSize;
|
||||||
|
final width = widget.items.length ~/ 7 * widget.cellSize;
|
||||||
|
return Size(width, height);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:math' as math show max;
|
import 'dart:math' as math show max;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/interactive_heat_map.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_days.dart';
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_days.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_gradient.dart';
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_gradient.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_months.dart';
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_months.dart';
|
||||||
@ -28,7 +29,7 @@ class OccupancyHeatMap extends StatelessWidget {
|
|||||||
return List.generate(_totalWeeks * 7, (index) {
|
return List.generate(_totalWeeks * 7, (index) {
|
||||||
final date = startDate.add(Duration(days: index));
|
final date = startDate.add(Duration(days: index));
|
||||||
final value = heatMapData[date] ?? 0;
|
final value = heatMapData[date] ?? 0;
|
||||||
return OccupancyPaintItem(index: index, value: value);
|
return OccupancyPaintItem(index: index, value: value, date: date);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,15 +59,13 @@ class OccupancyHeatMap extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const OccupancyHeatMapDays(cellSize: _cellSize),
|
const OccupancyHeatMapDays(cellSize: _cellSize),
|
||||||
CustomPaint(
|
SizedBox(
|
||||||
size: const Size(_totalWeeks * _cellSize, 7 * _cellSize),
|
width: _totalWeeks * _cellSize,
|
||||||
child: CustomPaint(
|
height: 7 * _cellSize,
|
||||||
isComplex: true,
|
child: InteractiveHeatMap(
|
||||||
size: const Size(_totalWeeks * _cellSize, 7 * _cellSize),
|
|
||||||
painter: OccupancyPainter(
|
|
||||||
items: paintItems,
|
items: paintItems,
|
||||||
maxValue: _maxValue,
|
maxValue: _maxValue,
|
||||||
),
|
cellSize: _cellSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -4,18 +4,25 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
|||||||
class OccupancyPaintItem {
|
class OccupancyPaintItem {
|
||||||
final int index;
|
final int index;
|
||||||
final int value;
|
final int value;
|
||||||
|
final DateTime date;
|
||||||
|
|
||||||
const OccupancyPaintItem({required this.index, required this.value});
|
const OccupancyPaintItem({
|
||||||
|
required this.index,
|
||||||
|
required this.value,
|
||||||
|
required this.date,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class OccupancyPainter extends CustomPainter {
|
class OccupancyPainter extends CustomPainter {
|
||||||
OccupancyPainter({
|
OccupancyPainter({
|
||||||
required this.items,
|
required this.items,
|
||||||
required this.maxValue,
|
required this.maxValue,
|
||||||
|
this.hoveredItem,
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<OccupancyPaintItem> items;
|
final List<OccupancyPaintItem> items;
|
||||||
final int maxValue;
|
final int maxValue;
|
||||||
|
final OccupancyPaintItem? hoveredItem;
|
||||||
|
|
||||||
static const double cellSize = 16.0;
|
static const double cellSize = 16.0;
|
||||||
|
|
||||||
@ -25,6 +32,10 @@ class OccupancyPainter extends CustomPainter {
|
|||||||
final Paint borderPaint = Paint()
|
final Paint borderPaint = Paint()
|
||||||
..color = ColorsManager.grayBorder.withValues(alpha: 0.4)
|
..color = ColorsManager.grayBorder.withValues(alpha: 0.4)
|
||||||
..style = PaintingStyle.stroke;
|
..style = PaintingStyle.stroke;
|
||||||
|
final Paint hoveredBorderPaint = Paint()
|
||||||
|
..color = Colors.black
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 1.5;
|
||||||
|
|
||||||
for (final item in items) {
|
for (final item in items) {
|
||||||
final column = item.index ~/ 7;
|
final column = item.index ~/ 7;
|
||||||
@ -37,6 +48,10 @@ class OccupancyPainter extends CustomPainter {
|
|||||||
final rect = Rect.fromLTWH(x, y, cellSize, cellSize);
|
final rect = Rect.fromLTWH(x, y, cellSize, cellSize);
|
||||||
canvas.drawRect(rect, fillPaint);
|
canvas.drawRect(rect, fillPaint);
|
||||||
|
|
||||||
|
// Highlight the hovered item
|
||||||
|
if (hoveredItem != null && hoveredItem!.index == item.index) {
|
||||||
|
canvas.drawRect(rect, hoveredBorderPaint);
|
||||||
|
} else {
|
||||||
_drawDashedLine(
|
_drawDashedLine(
|
||||||
canvas,
|
canvas,
|
||||||
Offset(x, y),
|
Offset(x, y),
|
||||||
@ -51,8 +66,9 @@ class OccupancyPainter extends CustomPainter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
canvas.drawLine(Offset(x, y), Offset(x, y + cellSize), borderPaint);
|
canvas.drawLine(Offset(x, y), Offset(x, y + cellSize), borderPaint);
|
||||||
canvas.drawLine(
|
canvas.drawLine(Offset(x + cellSize, y), Offset(x + cellSize, y + cellSize),
|
||||||
Offset(x + cellSize, y), Offset(x + cellSize, y + cellSize), borderPaint);
|
borderPaint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,5 +96,6 @@ class OccupancyPainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
bool shouldRepaint(covariant OccupancyPainter oldDelegate) =>
|
||||||
|
oldDelegate.hoveredItem != hoveredItem;
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,20 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
class GetEnergyConsumptionByPhasesParam extends Equatable {
|
class GetEnergyConsumptionByPhasesParam extends Equatable {
|
||||||
final DateTime? startDate;
|
final String powerClampUuid;
|
||||||
final DateTime? endDate;
|
final DateTime? date;
|
||||||
final String? spaceId;
|
|
||||||
|
|
||||||
const GetEnergyConsumptionByPhasesParam({
|
const GetEnergyConsumptionByPhasesParam({
|
||||||
this.startDate,
|
required this.powerClampUuid,
|
||||||
this.endDate,
|
this.date,
|
||||||
this.spaceId,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'startDate': startDate?.toIso8601String(),
|
'monthDate': '${date?.year}-${date?.month.toString().padLeft(2, '0')}',
|
||||||
'endDate': endDate?.toIso8601String(),
|
|
||||||
'spaceId': spaceId,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [startDate, endDate, spaceId];
|
List<Object?> get props => [powerClampUuid, date];
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
class GetEnergyConsumptionPerDeviceParam {
|
class GetEnergyConsumptionPerDeviceParam {
|
||||||
const GetEnergyConsumptionPerDeviceParam();
|
const GetEnergyConsumptionPerDeviceParam({
|
||||||
|
this.monthDate,
|
||||||
|
this.spaceId,
|
||||||
|
this.communityId,
|
||||||
|
});
|
||||||
|
|
||||||
|
final DateTime? monthDate;
|
||||||
|
final String? spaceId;
|
||||||
|
final String? communityId;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'monthDate':
|
||||||
|
'${monthDate?.year}-${monthDate?.month.toString().padLeft(2, '0')}',
|
||||||
|
if (spaceId == null || spaceId == null) 'spaceUuid': spaceId,
|
||||||
|
'communityUuid': communityId,
|
||||||
|
'groupByDevice': true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ class GetTotalEnergyConsumptionParam {
|
|||||||
return {
|
return {
|
||||||
'monthDate':
|
'monthDate':
|
||||||
'${monthDate?.year}-${monthDate?.month.toString().padLeft(2, '0')}',
|
'${monthDate?.year}-${monthDate?.month.toString().padLeft(2, '0')}',
|
||||||
if (communityId == null || communityId!.isEmpty) 'spaceUuid': spaceId,
|
if (spaceId == null || spaceId == null) 'spaceUuid': spaceId,
|
||||||
'communityUuid': communityId,
|
'communityUuid': communityId,
|
||||||
'groupByDevice': false,
|
'groupByDevice': false,
|
||||||
};
|
};
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
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),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,8 +15,9 @@ final class RemoteEnergyConsumptionByPhasesService
|
|||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.get(
|
final response = await _httpService.get(
|
||||||
path: 'endpoint',
|
path: '/power-clamp/${param.powerClampUuid}/historical',
|
||||||
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>? ?? [];
|
||||||
@ -28,7 +29,7 @@ final class RemoteEnergyConsumptionByPhasesService
|
|||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Exception('Failed to load energy consumption per device: $e');
|
throw Exception('Failed to load energy consumption per phase: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/models/device_energy_data_model.dart';
|
import 'package:syncrow_web/pages/analytics/models/device_energy_data_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/models/energy_data_model.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_per_device_param.dart';
|
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_per_device_param.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/energy_consumption_per_device_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/energy_consumption_per_device_service.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
@ -15,16 +17,10 @@ class RemoteEnergyConsumptionPerDeviceService
|
|||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.get(
|
final response = await _httpService.get(
|
||||||
path: 'endpoint',
|
path: '/power-clamp/historical',
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
expectedResponseModel: (data) {
|
queryParameters: param.toJson(),
|
||||||
final json = data as Map<String, dynamic>? ?? {};
|
expectedResponseModel: _EnergyConsumptionPerDeviceMapper.map,
|
||||||
final mappedData = json['data'] as List<dynamic>? ?? [];
|
|
||||||
return mappedData.map((e) {
|
|
||||||
final jsonData = e as Map<String, dynamic>;
|
|
||||||
return DeviceEnergyDataModel.fromJson(jsonData);
|
|
||||||
}).toList();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -32,3 +28,33 @@ class RemoteEnergyConsumptionPerDeviceService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract final class _EnergyConsumptionPerDeviceMapper {
|
||||||
|
const _EnergyConsumptionPerDeviceMapper._();
|
||||||
|
static List<DeviceEnergyDataModel> map(dynamic data) {
|
||||||
|
final json = data as Map<String, dynamic>? ?? {};
|
||||||
|
final mappedData = json['data'] as List<dynamic>? ?? [];
|
||||||
|
return mappedData.where((e) {
|
||||||
|
final deviceData = (e as Map<String, dynamic>)['data'] as List<dynamic>? ?? [];
|
||||||
|
return deviceData.isNotEmpty;
|
||||||
|
}).map((e) {
|
||||||
|
final deviceData = e as Map<String, dynamic>;
|
||||||
|
final energyData = deviceData['data'] as List<dynamic>;
|
||||||
|
|
||||||
|
return DeviceEnergyDataModel(
|
||||||
|
deviceId: deviceData['deviceUuid'] as String,
|
||||||
|
deviceName: deviceData['deviceName'] as String,
|
||||||
|
color: Color((DateTime.now().microsecondsSinceEpoch +
|
||||||
|
deviceData['deviceUuid'].hashCode) |
|
||||||
|
0xFF000000),
|
||||||
|
energy: energyData.map((data) {
|
||||||
|
final energyJson = data as Map<String, dynamic>;
|
||||||
|
return EnergyDataModel(
|
||||||
|
date: DateTime.parse(energyJson['date'] as String),
|
||||||
|
value: double.parse(energyJson['total_energy_consumed_kw'] as String),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user