power clamp

This commit is contained in:
mohammad
2024-10-17 16:04:11 +03:00
parent 82ddc499ac
commit aa27ecf908
8 changed files with 640 additions and 139 deletions

View File

@ -0,0 +1,216 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.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/power_clamp_bloc/power_clamp_event.dart';
import 'package:syncrow_app/features/devices/bloc/power_clamp_bloc/power_clamp_state.dart';
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
import 'package:syncrow_app/features/devices/model/power_clamp_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/power_clamp/power_chart.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
class PowerClampBloc extends Bloc<PowerClampEvent, PowerClampState> {
final String PCId;
PowerClampBloc({
required this.PCId,
}) : super(const PowerClampState()) {
on<PowerClampInitial>(_fetchStatus);
on<ReportLogsInitial>(fetchLogsForLastMonth);
on<FetchEnergyData>(_mapReportToEnergyData);
on<SelectDateEvent>(selectTimeOfLinePassword);
}
//SelectDateEvent
Timer? _timer;
DateTime? dateTime = DateTime.now();
String formattedDate = DateFormat('yyyy/MM/dd').format(DateTime.now());
bool lowBattery = false;
bool closingReminder = false;
bool doorAlarm = false;
PowerClampModel deviceStatus =
PowerClampModel(doorContactState: false, batteryPercentage: 0);
void _fetchStatus(
PowerClampInitial event, Emitter<PowerClampState> emit) async {
emit(PowerClampLoadingState());
try {
var response = await DevicesAPI.getDeviceStatus(PCId);
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
}
deviceStatus = PowerClampModel.fromJson(
statusModelList,
);
emit(UpdateState(powerClampModel: deviceStatus));
Future.delayed(const Duration(milliseconds: 500));
// _listenToChanges();
} catch (e) {
emit(PowerClampFailedState(errorMessage: e.toString()));
return;
}
}
DeviceReport recordGroups =
DeviceReport(startTime: '0', endTime: '0', data: []);
Future<void> fetchLogsForLastMonth(
ReportLogsInitial event, Emitter<PowerClampState> emit) async {
DateTime now = DateTime.now();
DateTime lastMonth = DateTime(now.year, now.month - 1, now.day);
int startTime = lastMonth.millisecondsSinceEpoch;
int endTime = now.millisecondsSinceEpoch;
try {
emit(PowerClampLoadingState());
var response = await DevicesAPI.getReportLogs(
startTime: startTime.toString(),
endTime: endTime.toString(),
deviceUuid: PCId,
code: event.code!,
);
recordGroups = response;
emit(UpdateState(powerClampModel: deviceStatus));
add(FetchEnergyData()); // Trigger mapping to EnergyData
} on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage = errorData['message'];
}
}
_listenToChanges() {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$PCId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) async {
if (_timer != null) {
await Future.delayed(const Duration(seconds: 2));
}
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList.add(StatusModel(code: element['code'], value: true));
});
deviceStatus = PowerClampModel.fromJson(statusList);
if (!isClosed) {
add(
PowerClampSwitch(switchD: deviceStatus.doorContactState),
);
}
});
} catch (_) {}
}
// New Function: Convert the device report data into EnergyData and emit it.
void _mapReportToEnergyData(
FetchEnergyData event, Emitter<PowerClampState> emit) {
try {
List<EnergyData> energyDataList = recordGroups.data
?.map((event) {
if (event.code == "VoltageA" && event.eventTime != null) {
// Convert eventTime to readable format
DateTime eventDateTime =
DateTime.fromMillisecondsSinceEpoch(event.eventTime!);
String formattedTime =
"${eventDateTime.hour}:${eventDateTime.minute.toString().padLeft(2, '0')} ${eventDateTime.hour >= 12 ? 'PM' : 'AM'}";
double value = double.tryParse(event.value ?? "0") ?? 0;
return EnergyData(
formattedTime, value / 1000); // Assume kWh format
}
return null;
})
.where((data) => data != null)
.cast<EnergyData>()
.toList() ??
[];
emit(EnergyDataState(energyData: energyDataList));
} catch (e) {
emit(PowerClampFailedState(errorMessage: e.toString()));
}
}
Future<void> selectTimeOfLinePassword(
SelectDateEvent event, Emitter<PowerClampState> emit) async {
emit(ChangeTimeState());
final DateTime? picked = await showDatePicker(
context: event.context,
initialDate: DateTime.now(),
firstDate: DateTime(1905),
lastDate: DateTime(2101),
);
if (picked != null) {
final selectedDateTime = DateTime(
picked.year,
picked.month,
picked.day,
0,
0,
);
final selectedTimestamp = DateTime(
selectedDateTime.year,
selectedDateTime.month,
selectedDateTime.day,
selectedDateTime.hour,
selectedDateTime.minute,
).millisecondsSinceEpoch ~/
1000; // Divide by 1000 to remove milliseconds
DateTime dateTime =
selectedDateTime; // Assuming this is your DateTime object
formattedDate = DateFormat('yyyy/MM/dd').format(dateTime);
emit(DateSelectedState());
}
}
int currentIndex = 0;
final List<String> views = ['Day', 'Month', 'Year'];
Widget dateSwitcher() {
void switchView(int direction) {
currentIndex = (currentIndex + direction + views.length) % views.length;
}
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.arrow_left),
onPressed: () {
setState(() {
switchView(-1);
});
},
),
Text(
views[currentIndex],
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
),
IconButton(
icon: Icon(Icons.arrow_right),
onPressed: () {
setState(() {
switchView(1);
});
},
),
],
);
},
);
}
}

View File

@ -0,0 +1,109 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
abstract class PowerClampEvent extends Equatable {
const PowerClampEvent();
@override
List<Object> get props => [];
}
class PowerClampLoading extends PowerClampEvent {}
class PowerClampSwitch extends PowerClampEvent {
final bool switchD;
final String deviceId;
final String productId;
const PowerClampSwitch(
{required this.switchD, this.deviceId = '', this.productId = ''});
@override
List<Object> get props => [switchD, deviceId, productId];
}
class PowerClampUpdated extends PowerClampEvent {}
class FetchEnergyData extends PowerClampEvent {}
class SelectDateEvent extends PowerClampEvent {
BuildContext context;
SelectDateEvent({required this.context});
}
class PowerClampInitial extends PowerClampEvent {
const PowerClampInitial();
}
class ReportLogsInitial extends PowerClampEvent {
final String? code;
const ReportLogsInitial({required this.code});
@override
List<Object> get props => [code!];
}
class PowerClampChangeStatus extends PowerClampEvent {}
class GetCounterEvent extends PowerClampEvent {
final String deviceCode;
const GetCounterEvent({required this.deviceCode});
@override
List<Object> get props => [deviceCode];
}
class ToggleLowBatteryEvent extends PowerClampEvent {
final bool isLowBatteryEnabled;
const ToggleLowBatteryEvent(this.isLowBatteryEnabled);
@override
List<Object> get props => [isLowBatteryEnabled];
}
class ToggleClosingReminderEvent extends PowerClampEvent {
final bool isClosingReminderEnabled;
const ToggleClosingReminderEvent(this.isClosingReminderEnabled);
@override
List<Object> get props => [isClosingReminderEnabled];
}
class ToggleDoorAlarmEvent extends PowerClampEvent {
final bool isDoorAlarmEnabled;
const ToggleDoorAlarmEvent(this.isDoorAlarmEnabled);
@override
List<Object> get props => [isDoorAlarmEnabled];
}
class SetCounterValue extends PowerClampEvent {
final Duration duration;
final String deviceCode;
const SetCounterValue({required this.duration, required this.deviceCode});
@override
List<Object> get props => [duration, deviceCode];
}
class StartTimer extends PowerClampEvent {
final int duration;
const StartTimer(this.duration);
@override
List<Object> get props => [duration];
}
class TickTimer extends PowerClampEvent {
final int remainingTime;
const TickTimer(this.remainingTime);
@override
List<Object> get props => [remainingTime];
}
class StopTimer extends PowerClampEvent {}
class OnClose extends PowerClampEvent {}

View File

@ -0,0 +1,48 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/power_clamp_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/power_clamp/power_chart.dart';
class PowerClampState extends Equatable {
const PowerClampState();
@override
List<Object> get props => [];
}
class PowerClampInitialState extends PowerClampState {}
class PowerClampLoadingState extends PowerClampState {}
class ChangeTimeState extends PowerClampState {}
class DateSelectedState extends PowerClampState {}
//DateSelectedState
class PowerClampFailedState extends PowerClampState {
final String errorMessage;
const PowerClampFailedState({required this.errorMessage});
@override
List<Object> get props => [errorMessage];
}
class UpdateState extends PowerClampState {
final PowerClampModel powerClampModel;
const UpdateState({required this.powerClampModel});
@override
List<Object> get props => [powerClampModel];
}
class LoadingNewSate extends PowerClampState {
final PowerClampModel powerClampModel;
const LoadingNewSate({required this.powerClampModel});
@override
List<Object> get props => [powerClampModel];
}
class EnergyDataState extends PowerClampState {
final List<EnergyData> energyData;
const EnergyDataState({required this.energyData});
}

View File

@ -0,0 +1,28 @@
import 'package:syncrow_app/features/devices/model/status_model.dart';
class PowerClampModel {
dynamic doorContactState;
dynamic batteryPercentage;
PowerClampModel({
required this.doorContactState,
required this.batteryPercentage,
});
factory PowerClampModel.fromJson(List<StatusModel> jsonList) {
late dynamic _doorContactState;
late dynamic _batteryPercentage;
for (int i = 0; i < jsonList.length; i++) {
if (jsonList[i].code == 'VoltageA') {
_doorContactState = jsonList[i].value ?? false;
} else if (jsonList[i].code == 'CurrentA') {
_batteryPercentage = jsonList[i].value ?? 0;
}
}
return PowerClampModel(
doorContactState: _doorContactState,
batteryPercentage: _batteryPercentage,
);
}
}

View File

@ -120,6 +120,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
),
lineBarsData: [
LineChartBarData(
preventCurveOvershootingThreshold: 0.1,
curveSmoothness: 0.5,
preventCurveOverShooting: true,
aboveBarData: BarAreaData(),
@ -148,7 +149,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
show: false,
),
isStrokeCapRound: true,
barWidth: 6,
barWidth: 2,
),
],
borderData: FlBorderData(

View File

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_event.dart';
import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_state.dart';
import 'package:syncrow_app/features/devices/bloc/power_clamp_bloc/power_clamp_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/power_clamp_bloc/power_clamp_event.dart';
import 'package:syncrow_app/features/devices/bloc/power_clamp_bloc/power_clamp_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/door_sensor_model.dart';
import 'package:syncrow_app/features/devices/model/power_clamp_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/power_clamp/power_chart.dart';
import 'package:syncrow_app/features/devices/view/widgets/power_clamp/power_info_card.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
@ -34,19 +34,26 @@ class _PowerClampPageState extends State<PowerClampPage> {
return DefaultScaffold(
title: 'Power Clamp',
child: BlocProvider(
create: (context) => DoorSensorBloc(DSId: widget.device?.uuid ?? '')
..add(const DoorSensorInitial()),
child: BlocBuilder<DoorSensorBloc, DoorSensorState>(
create: (context) => PowerClampBloc(PCId: widget.device?.uuid ?? '')
..add(const PowerClampInitial())
..add(ReportLogsInitial(code: 'VoltageA')),
child: BlocBuilder<PowerClampBloc, PowerClampState>(
builder: (context, state) {
final doorSensorBloc = BlocProvider.of<DoorSensorBloc>(context);
DoorSensorModel model =
DoorSensorModel(batteryPercentage: 0, doorContactState: false);
final _blocProvider = BlocProvider.of<PowerClampBloc>(context);
PowerClampModel model =
PowerClampModel(batteryPercentage: 0, doorContactState: false);
List<EnergyData> chartData = [];
if (state is LoadingNewSate) {
model = state.doorSensor;
model = state.powerClampModel;
} else if (state is UpdateState) {
model = state.doorSensor;
model = state.powerClampModel;
} else if (state is EnergyDataState) {
chartData = state.energyData;
print(chartData);
}
return state is DoorSensorLoadingState
return state is PowerClampLoadingState
? const Center(
child: DefaultContainer(
width: 50,
@ -58,7 +65,7 @@ class _PowerClampPageState extends State<PowerClampPage> {
Expanded(
child: RefreshIndicator(
onRefresh: () async {
doorSensorBloc.add(const DoorSensorInitial());
_blocProvider.add(const PowerClampInitial());
},
child: Padding(
padding: EdgeInsets.only(top: 25),
@ -68,6 +75,8 @@ class _PowerClampPageState extends State<PowerClampPage> {
setState(() {
_currentPage = page;
});
_blocProvider.add(
const ReportLogsInitial(code: 'VoltageA'));
},
itemBuilder: (context, index) {
return DefaultContainer(
@ -106,15 +115,16 @@ class _PowerClampPageState extends State<PowerClampPage> {
),
],
),
SizedBox(
const SizedBox(
height: 20,
),
Row(
const Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
PowerClampInfoCard(
iconPath: Assets.powerActiveIcon,
iconPath:
Assets.powerActiveIcon,
title: 'Active',
value: '700',
unit: ' w',
@ -175,28 +185,80 @@ class _PowerClampPageState extends State<PowerClampPage> {
],
),
SizedBox(
height: 310,
height: 290,
child: EnergyConsumptionPage(
chartData: [
EnergyData('12:00 AM', 4.0),
EnergyData('01:00 AM', 3.5),
EnergyData('02:00 AM', 3.8),
EnergyData('03:00 AM', 3.2),
EnergyData('04:00 AM', 4.0),
EnergyData('05:00 AM', 3.4),
EnergyData('06:00 AM', 3.2),
EnergyData('07:00 AM', 3.5),
EnergyData('08:00 AM', 3.8),
EnergyData('09:00 AM', 3.6),
EnergyData('10:00 AM', 3.9),
EnergyData('11:00 AM', 4.0),
chartData: chartData.isNotEmpty
? chartData
: [
EnergyData(
'12:00 AM', 4.0),
EnergyData(
'01:00 AM', 3.5),
EnergyData(
'02:00 AM', 3.8),
EnergyData(
'03:00 AM', 3.2),
EnergyData(
'04:00 AM', 4.0),
EnergyData(
'05:00 AM', 3.4),
EnergyData(
'06:00 AM', 3.2),
EnergyData(
'07:00 AM', 3.5),
EnergyData(
'08:00 AM', 3.8),
EnergyData(
'09:00 AM', 3.6),
EnergyData(
'10:00 AM', 3.9),
EnergyData(
'11:00 AM', 4.0),
],
totalConsumption: 100.0,
totalConsumption: chartData.fold(
0,
(sum, data) =>
sum + data.consumption),
date: '10/08/2024',
),
),
],
SizedBox(
height: 5,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
DefaultContainer(
padding: EdgeInsets.all(0),
color: ColorsManager.grayBox,
child: SizedBox(
child: _blocProvider
.dateSwitcher(),
)),
InkWell(
onTap: () {
_blocProvider.add(
SelectDateEvent(
context: context));
},
child: DefaultContainer(
color:
ColorsManager.grayBox,
child: SizedBox(
child: Padding(
padding:
const EdgeInsets
.all(5),
child: Text(
_blocProvider
.formattedDate),
),
)),
),
],
)
]),
),
);
},

View File

@ -164,15 +164,14 @@ void showDeviceInterface(DeviceModel device, BuildContext context) {
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) =>
WaterHeaterPage(device: device)));
// case DeviceType.DS:
// Navigator.push(
// context,
// PageRouteBuilder(
// pageBuilder: (context, animation1, animation2) =>
// DoorSensorScreen(device: device)));
case DeviceType.DS:
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) =>
DoorSensorScreen(device: device)));
case DeviceType.PC:
Navigator.push(
context,
PageRouteBuilder(

View File

@ -55,7 +55,7 @@ enum DeviceType {
ThreeTouch,
GarageDoor,
WaterLeak,
PC,
Other,
}
@ -87,6 +87,7 @@ Map<String, DeviceType> devicesTypesMap = {
"3GT": DeviceType.ThreeTouch,
"GD": DeviceType.GarageDoor,
"WL": DeviceType.WaterLeak,
"PC": DeviceType.PC,
};
Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
DeviceType.AC: [
@ -471,6 +472,43 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
})),
],
DeviceType.WaterLeak: [],
DeviceType.PC: [
FunctionModel(
code: 'switch_1',
type: functionTypesMap['Boolean'],
values: ValueModel.fromJson({})),
FunctionModel(
code: 'countdown_1',
type: functionTypesMap['Integer'],
values: ValueModel.fromJson(
{"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
FunctionModel(
code: 'tr_timecon',
type: functionTypesMap['Integer'],
values: ValueModel.fromJson(
{"unit": "s", "min": 0, "max": 120, "scale": 0, "step": 1})),
FunctionModel(
code: 'countdown_alarm',
type: functionTypesMap['Integer'],
values: ValueModel.fromJson(
{"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
FunctionModel(
code: 'door_control_1',
type: functionTypesMap['Enum'],
values: ValueModel.fromJson({
"range": ['open', 'open']
})),
FunctionModel(
code: 'voice_control_1',
type: functionTypesMap['Boolean'],
values: ValueModel.fromJson({})),
FunctionModel(
code: 'door_state_1',
type: functionTypesMap['Enum'],
values: ValueModel.fromJson({
"range": ["unclosed_time", "close_time_alarm", "none"]
})),
],
};
enum TempModes { hot, cold, wind }