push water leak device

This commit is contained in:
ashrafzarkanisala
2024-10-07 11:26:30 +03:00
parent 2b5249e985
commit 9ea8e3be2b
13 changed files with 772 additions and 27 deletions

View File

@ -30,6 +30,8 @@ import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_
import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heater_batch_control.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heater_device_control.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/view/water_leak_batch_control_view.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/view/water_leak_control_view.dart';
import '../../one_g_glass_switch/view/one_gang_glass_switch_control_view.dart';
@ -88,6 +90,10 @@ mixin RouteControlsBasedCode {
return GarageDoorControlView(
deviceId: device.uuid!,
);
case 'WL':
return WaterLeakView(
deviceId: device.uuid!,
);
default:
return const SizedBox();
}
@ -211,6 +217,13 @@ mixin RouteControlsBasedCode {
.map((e) => e.uuid!)
.toList(),
);
case 'WL':
return WaterLeakBatchControlView(
deviceIds: devices
.where((e) => (e.productType == 'WL'))
.map((e) => e.uuid!)
.toList(),
);
default:
return const SizedBox();
}

View File

@ -14,6 +14,7 @@ class ReportsTable extends StatelessWidget {
bool? hideValueShowDescription;
bool? mainDoorSensor;
bool? garageDoorSensor;
bool? waterLeak;
ReportsTable({
super.key,
@ -25,6 +26,7 @@ class ReportsTable extends StatelessWidget {
this.hideValueShowDescription,
this.mainDoorSensor,
this.garageDoorSensor,
this.waterLeak,
});
@override
@ -55,7 +57,8 @@ class ReportsTable extends StatelessWidget {
DeviceEvent data = entry.value;
// Parse eventTime into Date and Time
DateTime eventDateTime = DateTime.fromMillisecondsSinceEpoch(data.eventTime!);
DateTime eventDateTime =
DateTime.fromMillisecondsSinceEpoch(data.eventTime!);
String date = DateFormat('dd/MM/yyyy').format(eventDateTime);
String time = DateFormat('HH:mm').format(eventDateTime);
@ -63,8 +66,12 @@ class ReportsTable extends StatelessWidget {
if (hideValueShowDescription == true) {
if (mainDoorSensor != null && mainDoorSensor == true) {
value = data.value == 'true' ? 'Open' : 'Close';
} else if (garageDoorSensor != null && garageDoorSensor == true) {
} else if (garageDoorSensor != null &&
garageDoorSensor == true) {
value = data.value == 'true' ? 'Opened' : 'Closed';
} else if (waterLeak != null && waterLeak == true) {
value =
data.value == 'normal' ? 'Normal' : 'Leak Detected';
} else {
value = '${data.value!} ${thirdColumnDescription ?? ''}';
}

View File

@ -0,0 +1,173 @@
import 'package:bloc/bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_event.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_state.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/model/water_leak_status_model.dart';
import 'dart:async';
import 'package:syncrow_web/services/devices_mang_api.dart';
class WaterLeakBloc extends Bloc<WaterLeakEvent, WaterLeakState> {
WaterLeakStatusModel? deviceStatus;
Timer? _timer;
final String deviceId;
WaterLeakBloc(this.deviceId) : super(WaterLeakInitialState()) {
on<FetchWaterLeakStatusEvent>(_onFetchWaterLeakStatus);
on<WaterLeakControlEvent>(_onControl);
on<WaterLeakBatchControlEvent>(_onBatchControl);
on<FetchWaterLeakBatchStatusEvent>(_onFetchBatchStatus);
on<FetchWaterLeakReportsEvent>(_onFetchWaterLeakReports);
on<WaterLeakFactoryResetEvent>(_onFactoryReset);
}
Future<void> _onFetchWaterLeakStatus(
FetchWaterLeakStatusEvent event, Emitter<WaterLeakState> emit) async {
emit(WaterLeakLoadingState());
try {
final response =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = WaterLeakStatusModel.fromJson(deviceId, response.status);
emit(WaterLeakLoadedState(deviceStatus!));
} catch (e) {
emit(WaterLeakErrorState(e.toString()));
}
}
Future<void> _onControl(
WaterLeakControlEvent event, Emitter<WaterLeakState> emit) async {
final oldValue = deviceStatus!.watersensorState;
_updateLocalValue(event.code, event.value);
emit(WaterLeakLoadedState(deviceStatus!));
await _runDebounce(
deviceId: event.deviceId,
code: event.code,
value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: false,
);
}
Future<void> _onFactoryReset(
WaterLeakFactoryResetEvent event, Emitter<WaterLeakState> emit) async {
emit(WaterLeakLoadingState());
try {
final response = await DevicesManagementApi().factoryReset(
event.factoryReset,
event.deviceId,
);
if (response) {
emit(WaterLeakInitialState());
} else {
emit(const WaterLeakErrorState('Factory reset failed'));
}
} catch (e) {
emit(WaterLeakErrorState(e.toString()));
}
}
Future<void> _onBatchControl(
WaterLeakBatchControlEvent event, Emitter<WaterLeakState> emit) async {
final oldValue = deviceStatus!.watersensorState;
_updateLocalValue(event.code, event.value);
emit(WaterLeakBatchStatusLoadedState(deviceStatus!));
await _runDebounce(
deviceId: event.deviceIds,
code: event.code,
value: event.value,
oldValue: oldValue,
emit: emit,
isBatch: true,
);
}
Future<void> _onFetchBatchStatus(FetchWaterLeakBatchStatusEvent event,
Emitter<WaterLeakState> emit) async {
emit(WaterLeakLoadingState());
try {
final response =
await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = WaterLeakStatusModel.fromJson(deviceId, response.status);
emit(WaterLeakBatchStatusLoadedState(deviceStatus!));
} catch (e) {
emit(WaterLeakErrorState(e.toString()));
}
}
Future<void> _runDebounce({
required dynamic deviceId,
required String code,
required dynamic value,
required dynamic oldValue,
required Emitter<WaterLeakState> emit,
required bool isBatch,
}) async {
late String id;
if (deviceId is List) {
id = deviceId.first;
} else {
id = deviceId;
}
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(const Duration(milliseconds: 500), () async {
try {
late bool response;
if (isBatch) {
response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else {
response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
}
if (!response) {
_revertValueAndEmit(id, code, oldValue, emit);
}
} catch (e) {
_revertValueAndEmit(id, code, oldValue, emit);
}
});
}
void _updateLocalValue(String code, dynamic value) {
if (code == 'watersensor_state') {
deviceStatus = deviceStatus!.copyWith(watersensorState: value);
} else if (code == 'battery_percentage') {
deviceStatus = deviceStatus!.copyWith(batteryPercentage: value);
}
}
void _revertValueAndEmit(String deviceId, String code, dynamic oldValue,
Emitter<WaterLeakState> emit) {
_updateLocalValue(code, oldValue);
emit(WaterLeakLoadedState(deviceStatus!));
}
Future<void> _onFetchWaterLeakReports(
FetchWaterLeakReportsEvent event, Emitter<WaterLeakState> emit) async {
emit(WaterLeakReportsLoadingState());
try {
final from = DateTime.now()
.subtract(const Duration(days: 30))
.millisecondsSinceEpoch;
final to = DateTime.now().millisecondsSinceEpoch;
final DeviceReport records =
await DevicesManagementApi.getDeviceReportsByDate(
event.deviceId, event.code, from.toString(), to.toString());
emit(WaterLeakReportsLoadedState(records));
} catch (e) {
emit(WaterLeakReportsFailedState(e.toString()));
}
}
}

View File

@ -0,0 +1,87 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
abstract class WaterLeakEvent extends Equatable {
const WaterLeakEvent();
@override
List<Object> get props => [];
}
class FetchWaterLeakStatusEvent extends WaterLeakEvent {
final String deviceId;
const FetchWaterLeakStatusEvent(this.deviceId);
@override
List<Object> get props => [deviceId];
}
class WaterLeakControlEvent extends WaterLeakEvent {
final String deviceId;
final String code;
final dynamic value;
const WaterLeakControlEvent({
required this.deviceId,
required this.code,
required this.value,
});
@override
List<Object> get props => [deviceId, code, value];
}
class WaterLeakBatchControlEvent extends WaterLeakEvent {
final List<String> deviceIds;
final String code;
final dynamic value;
const WaterLeakBatchControlEvent({
required this.deviceIds,
required this.code,
required this.value,
});
@override
List<Object> get props => [deviceIds, code, value];
}
class FetchWaterLeakBatchStatusEvent extends WaterLeakEvent {
final List<String> deviceIds;
const FetchWaterLeakBatchStatusEvent(this.deviceIds);
@override
List<Object> get props => [deviceIds];
}
class FetchWaterLeakReportsEvent extends WaterLeakEvent {
final String deviceId;
final String code;
final int from;
final int to;
const FetchWaterLeakReportsEvent({
required this.deviceId,
required this.code,
required this.from,
required this.to,
});
@override
List<Object> get props => [deviceId, code, from, to];
}
class WaterLeakFactoryResetEvent extends WaterLeakEvent {
final String deviceId;
final FactoryResetModel factoryReset;
const WaterLeakFactoryResetEvent({
required this.deviceId,
required this.factoryReset,
});
@override
List<Object> get props => [deviceId];
}

View File

@ -0,0 +1,61 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/model/water_leak_status_model.dart';
abstract class WaterLeakState extends Equatable {
const WaterLeakState();
@override
List<Object> get props => [];
}
class WaterLeakInitialState extends WaterLeakState {}
class WaterLeakLoadingState extends WaterLeakState {}
class WaterLeakLoadedState extends WaterLeakState {
final WaterLeakStatusModel status;
const WaterLeakLoadedState(this.status);
@override
List<Object> get props => [status];
}
class WaterLeakBatchStatusLoadedState extends WaterLeakState {
final WaterLeakStatusModel status;
const WaterLeakBatchStatusLoadedState(this.status);
@override
List<Object> get props => [status];
}
class WaterLeakErrorState extends WaterLeakState {
final String message;
const WaterLeakErrorState(this.message);
@override
List<Object> get props => [message];
}
class WaterLeakReportsLoadingState extends WaterLeakState {}
class WaterLeakReportsLoadedState extends WaterLeakState {
final DeviceReport deviceReport;
const WaterLeakReportsLoadedState(this.deviceReport);
@override
List<Object> get props => [deviceReport];
}
class WaterLeakReportsFailedState extends WaterLeakState {
final String error;
const WaterLeakReportsFailedState(this.error);
@override
List<Object> get props => [error];
}

View File

@ -0,0 +1,48 @@
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
class WaterLeakStatusModel {
final String productUuid;
final String productType;
final String watersensorState;
final int batteryPercentage;
WaterLeakStatusModel({
required this.productUuid,
required this.productType,
required this.watersensorState,
required this.batteryPercentage,
});
factory WaterLeakStatusModel.fromJson(String id, List<Status> jsonList) {
late String watersensorState;
late int batteryPercentage;
for (var i = 0; i < jsonList.length; i++) {
if (jsonList[i].code == 'watersensor_state') {
watersensorState = jsonList[i].value;
} else if (jsonList[i].code == 'battery_percentage') {
batteryPercentage = jsonList[i].value;
}
}
return WaterLeakStatusModel(
productUuid: id,
productType: 'WL',
watersensorState: watersensorState,
batteryPercentage: batteryPercentage,
);
}
WaterLeakStatusModel copyWith({
String? productUuid,
String? productType,
String? watersensorState,
int? batteryPercentage,
}) {
return WaterLeakStatusModel(
productUuid: productUuid ?? this.productUuid,
productType: productType ?? this.productType,
watersensorState: watersensorState ?? this.watersensorState,
batteryPercentage: batteryPercentage ?? this.batteryPercentage,
);
}
}

View File

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_bloc.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_event.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_state.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/model/water_leak_status_model.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class WaterLeakBatchControlView extends StatelessWidget
with HelperResponsiveLayout {
final List<String> deviceIds;
const WaterLeakBatchControlView({Key? key, required this.deviceIds})
: super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => WaterLeakBloc(deviceIds.first)
..add(FetchWaterLeakBatchStatusEvent(deviceIds)),
child: BlocBuilder<WaterLeakBloc, WaterLeakState>(
builder: (context, state) {
if (state is WaterLeakLoadingState) {
return const Center(child: CircularProgressIndicator());
} else if (state is WaterLeakBatchStatusLoadedState) {
return _buildStatusControls(context, state.status);
} else if (state is WaterLeakErrorState) {
return Center(child: Text('Error: ${state.message}'));
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
);
}
Widget _buildStatusControls(
BuildContext context, WaterLeakStatusModel status) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 170,
height: 140,
child: FirmwareUpdateWidget(deviceId: deviceIds.first, version: 2)),
const SizedBox(
width: 12,
),
SizedBox(
width: 170,
height: 140,
child: FactoryResetWidget(
callFactoryReset: () {
context.read<WaterLeakBloc>().add(WaterLeakFactoryResetEvent(
deviceId: deviceIds.first,
factoryReset: FactoryResetModel(devicesUuid: deviceIds)));
},
),
),
],
);
}
}

View File

@ -0,0 +1,128 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_control_view.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_bloc.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_event.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_state.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/widgets/water_leak_notifi_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class WaterLeakView extends StatelessWidget with HelperResponsiveLayout {
final String deviceId;
const WaterLeakView({Key? key, required this.deviceId}) : super(key: key);
@override
Widget build(BuildContext context) {
final isExtraLarge = isExtraLargeScreenSize(context);
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return BlocProvider(
create: (context) =>
WaterLeakBloc(deviceId)..add(FetchWaterLeakStatusEvent(deviceId)),
child: BlocBuilder<WaterLeakBloc, WaterLeakState>(
builder: (context, state) {
if (state is WaterLeakLoadingState) {
return const Center(child: CircularProgressIndicator());
} else if (state is WaterLeakLoadedState) {
return GridView(
shrinkWrap: true,
padding: const EdgeInsets.symmetric(horizontal: 50),
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: isExtraLarge || isLarge
? 3
: isMedium
? 2
: 1,
mainAxisExtent: 140,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
children: [
IconNameStatusContainer(
isFullIcon: false,
name: state.status.watersensorState == 'normal'
? 'Normal'
: 'Leak Detection',
icon: state.status.watersensorState == 'normal'
? Assets.waterLeakNormal
: Assets.waterLeakDetected,
onTap: () {},
status: state.status.watersensorState == 'normal',
textColor: state.status.watersensorState == 'normal'
? ColorsManager.blackColor
: ColorsManager.red,
),
IconNameStatusContainer(
isFullIcon: false,
name: 'Records',
icon: Assets.records,
onTap: () {
context
.read<WaterLeakBloc>()
.add(FetchWaterLeakReportsEvent(
deviceId: deviceId,
code: 'watersensor_state',
from: DateTime.now()
.subtract(const Duration(days: 30))
.millisecondsSinceEpoch,
to: DateTime.now().millisecondsSinceEpoch,
));
},
status: false,
textColor: ColorsManager.blackColor,
),
IconNameStatusContainer(
isFullIcon: false,
name: 'Automation Record',
icon: Assets.automationRecords,
onTap: () {},
status: false,
textColor: ColorsManager.blackColor,
),
IconNameStatusContainer(
isFullIcon: false,
name: 'Notifications\nSettings',
icon: Assets.mainDoorNotifi,
onTap: () {
showDialog(
context: context,
builder: (context) => const WaterLeakNotificationDialog(),
);
},
status: false,
textColor: ColorsManager.blackColor,
paddingAmount: 14,
),
],
);
} else if (state is WaterLeakReportsLoadingState) {
return const Center(child: CircularProgressIndicator());
} else if (state is WaterLeakReportsLoadedState) {
return ReportsTable(
report: state.deviceReport,
hideValueShowDescription: true,
waterLeak: true,
onRowTap: (index) {},
onClose: () {
context
.read<WaterLeakBloc>()
.add(FetchWaterLeakStatusEvent(deviceId));
},
);
} else if (state is WaterLeakReportsFailedState) {
return Center(child: Text('Error: ${state.error}'));
} else if (state is WaterLeakErrorState) {
return Center(child: Text('Error: ${state.message}'));
} else {
return const Center(child: Text('No data available'));
}
},
),
);
}
}

View File

@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class WaterLeakNotificationDialog extends StatelessWidget {
const WaterLeakNotificationDialog({super.key});
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.white,
insetPadding: const EdgeInsets.all(20),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: SizedBox(
width: 400,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(),
Text(
'Notification Settings',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 22,
color: ColorsManager.dialogBlueTitle,
),
),
Container(
width: 25,
decoration: BoxDecoration(
color: Colors.transparent,
shape: BoxShape.circle,
border: Border.all(
color: Colors.grey,
width: 1.0,
),
),
child: IconButton(
padding: EdgeInsets.all(1),
icon: const Icon(
Icons.close,
color: Colors.grey,
size: 18,
),
onPressed: () {
Navigator.of(context).pop();
},
),
),
],
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ToggleWidget(
value: true,
code: 'notification',
deviceId: '',
label: 'Low Battery',
onChange: (v) {},
icon: '-1',
),
ToggleWidget(
value: true,
code: 'notification',
deviceId: '',
label: 'Water Leakage',
onChange: (v) {},
icon: '-1',
),
],
),
],
),
),
),
),
);
}
}