push door lock

This commit is contained in:
ashrafzarkanisala
2024-08-27 00:16:00 +03:00
parent 2dc5a1e7ed
commit f39ce0a72b
13 changed files with 654 additions and 154 deletions

View File

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/ac/view/ac_device_control.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_status_view.dart';
import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_view.dart';
import 'package:syncrow_web/pages/device_managment/living_room_switch/view/living_room_device_control.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart';
@ -19,7 +20,7 @@ mixin RouteControlsBasedCode {
gatewayId: device.uuid!,
);
case 'DL':
return const SizedBox();
return DoorLockView(device: device);
case 'WPS':
return WallSensorControls(device: device);
case 'CPS':

View File

@ -0,0 +1,131 @@
// ignore_for_file: invalid_use_of_visible_for_testing_member
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_event.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_state.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/models/door_lock_status_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
class DoorLockBloc extends Bloc<DoorLockEvent, DoorLockState> {
late DoorLockStatusModel deviceStatus;
final String deviceId;
Timer? _timer;
DoorLockBloc({required this.deviceId}) : super(DoorLockInitial()) {
on<DoorLockFetchStatus>(_onFetchDeviceStatus);
on<DoorLockControl>(_onDoorLockControl);
on<UpdateLockEvent>(_updateLock);
}
FutureOr<void> _onFetchDeviceStatus(
DoorLockFetchStatus event, Emitter<DoorLockState> emit) async {
emit(DoorLockStatusLoading());
try {
final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
DoorLockStatusModel.fromJson(event.deviceId, status.status);
emit(DoorLockStatusLoaded(deviceStatus));
} catch (e) {
emit(DoorLockControlError(e.toString()));
}
}
FutureOr<void> _onDoorLockControl(
DoorLockControl event, Emitter<DoorLockState> emit) async {
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value);
emit(DoorLockStatusLoaded(deviceStatus));
await _runDebounce(
deviceId: event.deviceId,
code: event.code,
value: event.value,
oldValue: oldValue,
emit: emit,
);
}
FutureOr<void> _updateLock(
UpdateLockEvent event, Emitter<DoorLockState> emit) async {
final oldValue = deviceStatus.normalOpenSwitch;
deviceStatus = deviceStatus.copyWith(normalOpenSwitch: event.value);
emit(DoorLockStatusLoaded(deviceStatus));
try {
final response = await DevicesManagementApi.openDoorLock(deviceId);
if (!response) {
_revertValueAndEmit(deviceId, 'normal_open_switch', oldValue, emit);
}
} catch (e) {
_revertValueAndEmit(deviceId, 'normal_open_switch', oldValue, emit);
emit(DoorLockControlError('Error controlling the lock: $e'));
}
}
Future<void> _runDebounce({
required String deviceId,
required String code,
required dynamic value,
required dynamic oldValue,
required Emitter<DoorLockState> emit,
}) async {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(const Duration(seconds: 1), () async {
try {
final response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
if (!response) {
_revertValueAndEmit(deviceId, code, oldValue, emit);
}
} catch (e) {
_revertValueAndEmit(deviceId, code, oldValue, emit);
}
});
}
void _revertValueAndEmit(
String deviceId,
String code,
dynamic oldValue,
Emitter<DoorLockState> emit,
) {
_updateLocalValue(code, oldValue);
emit(DoorLockStatusLoaded(deviceStatus));
emit(const DoorLockControlError('Failed to control the device.'));
}
void _updateLocalValue(String code, dynamic value) {
switch (code) {
case 'reverse_lock':
if (value is bool) {
deviceStatus = deviceStatus.copyWith(reverseLock: value);
}
break;
case 'normal_open_switch':
if (value is bool) {
deviceStatus = deviceStatus.copyWith(normalOpenSwitch: value);
}
break;
default:
break;
}
emit(DoorLockStatusLoaded(deviceStatus));
}
dynamic _getValueByCode(String code) {
switch (code) {
case 'reverse_lock':
return deviceStatus.reverseLock;
case 'normal_open_switch':
return deviceStatus.normalOpenSwitch;
default:
return null;
}
}
}

View File

@ -0,0 +1,39 @@
import 'package:equatable/equatable.dart';
sealed class DoorLockEvent extends Equatable {
const DoorLockEvent();
@override
List<Object> get props => [];
}
class DoorLockFetchStatus extends DoorLockEvent {
final String deviceId;
const DoorLockFetchStatus(this.deviceId);
@override
List<Object> get props => [deviceId];
}
class DoorLockControl extends DoorLockEvent {
final String deviceId;
final String code;
final bool value;
const DoorLockControl({
required this.deviceId,
required this.code,
required this.value,
});
@override
List<Object> get props => [deviceId, code, value];
}
class UpdateLockEvent extends DoorLockEvent {
final bool value;
const UpdateLockEvent({required this.value});
@override
List<Object> get props => [value];
}

View File

@ -0,0 +1,50 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/models/door_lock_status_model.dart';
sealed class DoorLockState extends Equatable {
const DoorLockState();
@override
List<Object> get props => [];
}
class DoorLockInitial extends DoorLockState {}
class DoorLockStatusLoading extends DoorLockState {}
class DoorLockStatusLoaded extends DoorLockState {
final DoorLockStatusModel status;
const DoorLockStatusLoaded(this.status);
@override
List<Object> get props => [status];
}
class DoorLockControlLoading extends DoorLockState {}
class DoorLockControlError extends DoorLockState {
final String message;
const DoorLockControlError(this.message);
@override
List<Object> get props => [message];
}
class LoadingNewSate extends DoorLockState {
final DoorLockStatusModel smartDoorModel;
const LoadingNewSate({required this.smartDoorModel});
@override
List<Object> get props => [smartDoorModel];
}
class UpdateState extends DoorLockState {
final DoorLockStatusModel smartDoorModel;
const UpdateState({required this.smartDoorModel});
@override
List<Object> get props => [smartDoorModel];
}

View File

@ -0,0 +1,184 @@
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
class DoorLockStatusModel {
final String uuid;
final int unlockFingerprint;
final int unlockPassword;
final int unlockTemporary;
final int unlockCard;
final String alarmLock;
final int unlockRequest;
final int residualElectricity;
final bool reverseLock;
final int unlockApp;
final bool hijack;
final bool doorbell;
final String unlockOfflinePd;
final String unlockOfflineClear;
final String unlockDoubleKit;
final String remoteNoPdSetkey;
final String remoteNoDpKey;
bool normalOpenSwitch;
DoorLockStatusModel({
required this.uuid,
required this.unlockFingerprint,
required this.unlockPassword,
required this.unlockTemporary,
required this.unlockCard,
required this.alarmLock,
required this.unlockRequest,
required this.residualElectricity,
required this.reverseLock,
required this.unlockApp,
required this.hijack,
required this.doorbell,
required this.unlockOfflinePd,
required this.unlockOfflineClear,
required this.unlockDoubleKit,
required this.remoteNoPdSetkey,
required this.remoteNoDpKey,
required this.normalOpenSwitch,
});
factory DoorLockStatusModel.fromJson(String id, List<Status> jsonList) {
late int unlockFingerprint;
late int unlockPassword;
late int unlockTemporary;
late int unlockCard;
late String alarmLock;
late int unlockRequest;
late int residualElectricity;
late bool reverseLock;
late int unlockApp;
late bool hijack;
late bool doorbell;
late String unlockOfflinePd;
late String unlockOfflineClear;
late String unlockDoubleKit;
late String remoteNoPdSetkey;
late String remoteNoDpKey;
late bool normalOpenSwitch;
for (var status in jsonList) {
switch (status.code) {
case 'unlock_fingerprint':
unlockFingerprint = status.value ?? 0;
break;
case 'unlock_password':
unlockPassword = status.value ?? 0;
break;
case 'unlock_temporary':
unlockTemporary = status.value ?? 0;
break;
case 'unlock_card':
unlockCard = status.value ?? 0;
break;
case 'alarm_lock':
alarmLock = status.value ?? '';
break;
case 'unlock_request':
unlockRequest = status.value ?? 0;
break;
case 'residual_electricity':
residualElectricity = status.value ?? 0;
break;
case 'reverse_lock':
reverseLock = status.value ?? false;
break;
case 'unlock_app':
unlockApp = status.value ?? 0;
break;
case 'hijack':
hijack = status.value ?? false;
break;
case 'doorbell':
doorbell = status.value ?? false;
break;
case 'unlock_offline_pd':
unlockOfflinePd = status.value ?? '';
break;
case 'unlock_offline_clear':
unlockOfflineClear = status.value ?? '';
break;
case 'unlock_double_kit':
unlockDoubleKit = status.value ?? '';
break;
case 'remote_no_pd_setkey':
remoteNoPdSetkey = status.value ?? '';
break;
case 'remote_no_dp_key':
remoteNoDpKey = status.value ?? '';
break;
case 'normal_open_switch':
normalOpenSwitch = status.value ?? false;
break;
default:
break;
}
}
return DoorLockStatusModel(
uuid: id,
unlockFingerprint: unlockFingerprint,
unlockPassword: unlockPassword,
unlockTemporary: unlockTemporary,
unlockCard: unlockCard,
alarmLock: alarmLock,
unlockRequest: unlockRequest,
residualElectricity: residualElectricity,
reverseLock: reverseLock,
unlockApp: unlockApp,
hijack: hijack,
doorbell: doorbell,
unlockOfflinePd: unlockOfflinePd,
unlockOfflineClear: unlockOfflineClear,
unlockDoubleKit: unlockDoubleKit,
remoteNoPdSetkey: remoteNoPdSetkey,
remoteNoDpKey: remoteNoDpKey,
normalOpenSwitch: normalOpenSwitch,
);
}
DoorLockStatusModel copyWith({
String? uuid,
int? unlockFingerprint,
int? unlockPassword,
int? unlockTemporary,
int? unlockCard,
String? alarmLock,
int? unlockRequest,
int? residualElectricity,
bool? reverseLock,
int? unlockApp,
bool? hijack,
bool? doorbell,
String? unlockOfflinePd,
String? unlockOfflineClear,
String? unlockDoubleKit,
String? remoteNoPdSetkey,
String? remoteNoDpKey,
bool? normalOpenSwitch,
}) {
return DoorLockStatusModel(
uuid: uuid ?? this.uuid,
unlockFingerprint: unlockFingerprint ?? this.unlockFingerprint,
unlockPassword: unlockPassword ?? this.unlockPassword,
unlockTemporary: unlockTemporary ?? this.unlockTemporary,
unlockCard: unlockCard ?? this.unlockCard,
alarmLock: alarmLock ?? this.alarmLock,
unlockRequest: unlockRequest ?? this.unlockRequest,
residualElectricity: residualElectricity ?? this.residualElectricity,
reverseLock: reverseLock ?? this.reverseLock,
unlockApp: unlockApp ?? this.unlockApp,
hijack: hijack ?? this.hijack,
doorbell: doorbell ?? this.doorbell,
unlockOfflinePd: unlockOfflinePd ?? this.unlockOfflinePd,
unlockOfflineClear: unlockOfflineClear ?? this.unlockOfflineClear,
unlockDoubleKit: unlockDoubleKit ?? this.unlockDoubleKit,
remoteNoPdSetkey: remoteNoPdSetkey ?? this.remoteNoPdSetkey,
remoteNoDpKey: remoteNoDpKey ?? this.remoteNoDpKey,
normalOpenSwitch: normalOpenSwitch ?? this.normalOpenSwitch,
);
}
}

View File

@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_bloc.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_event.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_state.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/models/door_lock_status_model.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/widget/door_button.dart';
class DoorLockView extends StatelessWidget {
final AllDevicesModel device;
const DoorLockView({super.key, required this.device});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => DoorLockBloc(deviceId: device.uuid!)
..add(DoorLockFetchStatus(device.uuid!)),
child: BlocListener<DoorLockBloc, DoorLockState>(
listener: (context, state) {
if (state is DoorLockControlError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.message)),
);
}
},
child: BlocBuilder<DoorLockBloc, DoorLockState>(
builder: (context, state) {
if (state is DoorLockStatusLoading) {
return const Center(child: CircularProgressIndicator());
} else if (state is DoorLockStatusLoaded) {
return _buildStatusControls(context, state.status);
} else if (state is UpdateState) {
return _buildStatusControls(context, state.smartDoorModel);
} else if (state is DoorLockControlError) {
return Center(child: Text(state.message));
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
),
);
}
Widget _buildStatusControls(
BuildContext context, DoorLockStatusModel status) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
DoorLockButton(
smartDoorModel: status,
doorLock: device,
),
],
),
),
);
}
}

View File

@ -0,0 +1,130 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_bloc.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_event.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/models/door_lock_status_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class DoorLockButton extends StatefulWidget {
const DoorLockButton({
super.key,
required this.doorLock,
required this.smartDoorModel,
});
final AllDevicesModel doorLock;
final DoorLockStatusModel smartDoorModel;
@override
State<DoorLockButton> createState() =>
_DoorLockButtonState(smartDoorModel: smartDoorModel);
}
class _DoorLockButtonState extends State<DoorLockButton>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _animation;
DoorLockStatusModel smartDoorModel;
_DoorLockButtonState({required this.smartDoorModel});
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
_animation = Tween<double>(begin: 0, end: 1).animate(_animationController)
..addListener(() {
setState(() {});
});
if (smartDoorModel.unlockRequest > 0) {
_animationController.forward();
}
}
@override
void didUpdateWidget(covariant DoorLockButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.smartDoorModel.normalOpenSwitch !=
widget.smartDoorModel.normalOpenSwitch) {
setState(() {
smartDoorModel = widget.smartDoorModel;
});
if (smartDoorModel.unlockRequest > 0) {
_animationController.forward(from: 0);
}
}
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: 255,
height: 255,
child: InkWell(
onTap: () {
_animationController.forward(from: 0);
BlocProvider.of<DoorLockBloc>(context)
.add(UpdateLockEvent(value: !smartDoorModel.normalOpenSwitch));
},
child: Container(
width: 255,
height: 255,
decoration: BoxDecoration(
color: const Color(0xFFEBECED),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
blurRadius: 18,
blurStyle: BlurStyle.outer,
),
],
),
child: Stack(
alignment: Alignment.center,
children: [
Container(
margin: const EdgeInsets.all(30),
decoration: const BoxDecoration(
color: Color(0xFFF9F9F9),
shape: BoxShape.circle,
),
child: Center(
child: SvgPicture.asset(
smartDoorModel.normalOpenSwitch
? Assets.doorUnlock
: Assets.lockIcon,
width: 60,
height: 60,
),
),
),
SizedBox.expand(
child: CircularProgressIndicator(
value: _animation.value,
strokeWidth: 8,
backgroundColor: Colors.transparent,
valueColor: const AlwaysStoppedAnimation<Color>(
ColorsManager.primaryColor),
),
),
],
),
),
),
);
}
}

View File

@ -119,157 +119,4 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
),
);
}
// ////// changing here for devices controls ////
// Widget _buildStatusControls(List<Status> statuses) {
// return GridView.builder(
// padding: const EdgeInsets.symmetric(horizontal: 40),
// shrinkWrap: true,
// physics: const NeverScrollableScrollPhysics(),
// gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 3,
// mainAxisExtent: 133,
// crossAxisSpacing: 12,
// mainAxisSpacing: 12,
// ),
// itemCount: statuses.length,
// itemBuilder: (context, index) {
// final status = statuses[index];
// return Container(
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(20),
// color: ColorsManager.greyColor.withOpacity(0.2),
// border: Border.all(color: ColorsManager.boxDivider),
// ),
// padding: const EdgeInsets.all(16),
// child: _buildControlForStatus(status),
// );
// },
// );
// }
// Widget _buildControlForStatus(Status status) {
// switch (status.type) {
// case OperationDialogType.onOff:
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// if (status.icon.isNotEmpty)
// ClipOval(
// child: Container(
// color: ColorsManager.whiteColors,
// child: SvgPicture.asset(
// status.icon,
// width: 60,
// height: 60,
// fit: BoxFit.cover,
// ),
// )),
// SizedBox(
// height: 20,
// width: 35,
// child: CupertinoSwitch(
// value: status.value ?? false,
// onChanged: (newValue) {
// // Handle toggle change
// },
// ),
// ),
// ],
// ),
// const Spacer(),
// Center(
// child: Text(
// status.name,
// style: const TextStyle(
// fontWeight: FontWeight.bold,
// fontSize: 14,
// ),
// ),
// ),
// ],
// );
// case OperationDialogType.countdown:
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Center(
// child: Text(
// status.name,
// style: const TextStyle(
// fontWeight: FontWeight.bold,
// fontSize: 14,
// ),
// ),
// ),
// const Spacer(),
// IncrementDecrementWidget(
// value: status.value.toString(),
// description: 'hr',
// onIncrement: () {
// // Handle increment
// },
// onDecrement: () {
// // Handle decrement
// },
// ),
// ],
// );
// case OperationDialogType.integerSteps:
// return IncrementDecrementWidget(
// value: status.value.toString(),
// description: 'm',
// onIncrement: () {
// // Handle increment
// },
// onDecrement: () {
// // Handle decrement
// },
// );
// case OperationDialogType.listOfOptions:
// return Wrap(
// children: [
// ...status.options!.map((e) => ClipOval(
// child: SvgPicture.asset(
// e.icon,
// width: 40,
// height: 40,
// fit: BoxFit.cover,
// ),
// ))
// ],
// );
// default:
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// if (status.icon.isNotEmpty)
// ClipOval(
// child: Container(
// color: ColorsManager.whiteColors,
// child: SvgPicture.asset(
// status.icon,
// width: 60,
// height: 60,
// fit: BoxFit.cover,
// ),
// )),
// const Spacer(),
// Text(
// status.value.toString(),
// style: const TextStyle(
// fontSize: 14,
// color: Colors.black54,
// ),
// ),
// ],
// );
// }
// }
}

View File

@ -82,4 +82,15 @@ class DevicesManagementApi {
);
return response;
}
static Future<bool> openDoorLock(String deviceId) async {
final response = await HTTPService().post(
path: ApiEndpoints.openDoorLock.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json['success'] ?? false;
},
);
return response;
}
}

View File

@ -34,4 +34,5 @@ abstract class ApiEndpoints {
static const String deviceControl = '$baseUrl/device/{uuid}/control';
static const String gatewayApi = '/device/gateway/{gatewayUuid}/devices';
static const String openDoorLock = '/door-lock/open/{doorLockUuid}';
}

View File

@ -130,4 +130,11 @@ class Assets {
static const String lightBulb = "assets/icons/Light.svg";
//assets/icons/sensors.svg
static const String sensors = "assets/icons/sensors.svg";
//assets/icons/door_un_look_ic.svg
static const String doorUnlock = 'assets/icons/door_un_look_ic.svg';
//assets/icons/lockIcon.svg
static const String lockIcon = 'assets/icons/lockIcon.svg';
}