push add ac fucntions and handle doplicate adding to the then or if items

This commit is contained in:
ashrafzarkanisala
2024-11-19 00:54:43 +03:00
parent 54143b3ba9
commit 57b8f6b03e
60 changed files with 1392 additions and 24 deletions
@@ -2,6 +2,8 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_com
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_space_model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/enum/device_types.dart';
@@ -69,6 +71,7 @@ class AllDevicesModel {
int? batteryLevel;
String? productName;
List<DeviceSpaceModel>? spaces;
List<DeviceFunction>? _deviceFunctions;
AllDevicesModel({
this.room,
@@ -215,6 +218,28 @@ SOS
return tempIcon;
}
List<DeviceFunction> get deviceFunctions {
_deviceFunctions ??= _getDeviceFunctions();
return _deviceFunctions!;
}
List<DeviceFunction> _getDeviceFunctions() {
switch (productType) {
case 'AC':
return [
SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
ModeFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
TempSetFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
LevelFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
];
// other product types
default:
return [];
}
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
if (room != null) {
+12 -4
View File
@@ -17,13 +17,21 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
void _onAddToIfContainer(AddToIfContainer event, Emitter<RoutineState> emit) {
final updatedIfItems = List<Map<String, String>>.from(state.ifItems)..add(event.item);
emit(state.copyWith(ifItems: updatedIfItems));
if (!_isDuplicate(state.ifItems, event.item)) {
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems)..add(event.item);
emit(state.copyWith(ifItems: updatedIfItems));
}
}
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) {
final updatedThenItems = List<Map<String, String>>.from(state.thenItems)..add(event.item);
emit(state.copyWith(thenItems: updatedThenItems));
if (!_isDuplicate(state.thenItems, event.item)) {
final updatedThenItems = List<Map<String, dynamic>>.from(state.thenItems)..add(event.item);
emit(state.copyWith(thenItems: updatedThenItems));
}
}
bool _isDuplicate(List<Map<String, dynamic>> items, Map<String, dynamic> newItem) {
return items.any((item) => item['imagePath'] == newItem['imagePath'] && item['title'] == newItem['title']);
}
Future<void> _onLoadScenes(LoadScenes event, Emitter<RoutineState> emit) async {
+2 -2
View File
@@ -8,7 +8,7 @@ abstract class RoutineEvent extends Equatable {
}
class AddToIfContainer extends RoutineEvent {
final Map<String, String> item;
final Map<String, dynamic> item;
const AddToIfContainer(this.item);
@@ -17,7 +17,7 @@ class AddToIfContainer extends RoutineEvent {
}
class AddToThenContainer extends RoutineEvent {
final Map<String, String> item;
final Map<String, dynamic> item;
const AddToThenContainer(this.item);
+4 -4
View File
@@ -1,8 +1,8 @@
part of 'routine_bloc.dart';
class RoutineState extends Equatable {
final List<Map<String, String>> ifItems;
final List<Map<String, String>> thenItems;
final List<Map<String, dynamic>> ifItems;
final List<Map<String, dynamic>> thenItems;
final List<Map<String, String>> availableCards;
final List<ScenesModel> scenes;
final List<ScenesModel> automations;
@@ -20,8 +20,8 @@ class RoutineState extends Equatable {
});
RoutineState copyWith({
List<Map<String, String>>? ifItems,
List<Map<String, String>>? thenItems,
List<Map<String, dynamic>>? ifItems,
List<Map<String, dynamic>>? thenItems,
List<ScenesModel>? scenes,
List<ScenesModel>? automations,
bool? isLoading,
@@ -0,0 +1,9 @@
enum AcValuesEnums {
Cooling,
Heating,
Ventilation,
}
enum TempModes { hot, cold, wind }
enum FanSpeeds { auto, low, middle, high }
+190
View File
@@ -0,0 +1,190 @@
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/routiens/bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/utils/color_manager.dart';
mixin ACHelper {
Future<Map<String, dynamic>?> showACFunctionsDialog(BuildContext context, List<DeviceFunction<dynamic>> functions) {
List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
String? selectedFunction;
dynamic selectedValue;
return showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Container(
width: selectedFunction != null ? 600 : 360,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'AC Functions',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 50),
child: Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
),
Flexible(
child: Row(
children: [
Expanded(
child: ListView.separated(
shrinkWrap: true,
itemCount: acFunctions.length,
separatorBuilder: (context, index) => Divider(),
itemBuilder: (context, index) {
final function = acFunctions[index];
return ListTile(
leading: Image.asset(function.icon, width: 24, height: 24),
title: Text(function.operationName),
trailing: Icon(Icons.arrow_forward_ios),
onTap: () {
setState(() {
selectedFunction = function.code;
selectedValue = null;
});
},
);
},
),
),
if (selectedFunction != null)
Container(
width: 1,
color: ColorsManager.greyColor,
),
if (selectedFunction != null)
Expanded(
child: ListView.separated(
shrinkWrap: true,
itemCount: acFunctions
.firstWhere((f) => f.code == selectedFunction)
.getOperationalValues()
.length,
separatorBuilder: (context, index) => Divider(),
itemBuilder: (context, index) {
final operationalValue = acFunctions.firstWhere((f) => f.code == selectedFunction)
..getOperationalValues()[index];
return ListTile(
leading: Image.asset(operationalValue.icon, width: 24, height: 24),
title: Text(operationalValue.getOperationalValues()[index].description),
trailing: Radio<dynamic>(
value: operationalValue.getOperationalValues()[index].value,
groupValue: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value;
});
},
),
);
},
),
),
],
),
),
Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Center(
child: Text(
'Cancel',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: ColorsManager.greyColor),
),
),
),
Container(
height: 50,
width: 1,
color: ColorsManager.greyColor,
),
GestureDetector(
onTap: () {
// Handle the confirmation action here
Navigator.pop(context, {
'function': selectedFunction,
'value': selectedValue,
});
},
child: Center(
child: Text(
'Confirm',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
),
),
),
),
],
),
],
),
),
);
},
);
},
);
}
void handleACDeviceDrop(BuildContext context, Map<String, dynamic> data) {
final device = data['device'] as AllDevicesModel;
final acFunctions = device.deviceFunctions;
showACFunctionsDialog(context, acFunctions).then((result) {
if (result != null) {
_addACDeviceToRoutine(context, data, result);
}
});
}
void handleNonACDeviceDrop(BuildContext context, Map<String, dynamic> data) {
context.read<RoutineBloc>().add(AddToThenContainer(data));
}
void _addACDeviceToRoutine(BuildContext context, Map<String, dynamic> deviceData, Map<String, dynamic> functionData) {
final updatedData = {
...deviceData,
'function': functionData['function'],
'value': functionData['value'],
};
context.read<RoutineBloc>().add(AddToThenContainer(updatedData));
_logACFunctionSelection(functionData);
}
void _logACFunctionSelection(Map<String, dynamic> functionData) {
print('Selected AC function: ${functionData['function']}, Value: ${functionData['value']}');
}
}
@@ -0,0 +1,215 @@
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_operational_value.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
abstract class ACFunction extends DeviceFunction<AcStatusModel> {
ACFunction({
required String deviceId,
required String deviceName,
required String code,
required String operationName,
required String icon,
}) : super(
deviceId: deviceId,
deviceName: deviceName,
code: code,
operationName: operationName,
icon: icon,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue);
List<ACOperationalValue> getOperationalValues();
}
class SwitchFunction extends ACFunction {
SwitchFunction({required super.deviceId, required super.deviceName})
: super(
code: 'switch',
operationName: 'Power',
icon: Assets.assetsAcPower,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue) {
return currentStatus.copyWith(acSwitch: newValue as bool);
}
@override
List<ACOperationalValue> getOperationalValues() => [
ACOperationalValue(
icon: Assets.assetsAcPower,
description: "ON",
value: true,
),
ACOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "OFF",
value: false,
),
];
}
class ModeFunction extends ACFunction {
ModeFunction({required super.deviceId, required super.deviceName})
: super(
code: 'mode',
operationName: 'Mode',
icon: Assets.assetsFreezing,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue) {
return currentStatus.copyWith(modeString: (newValue as TempModes).toString().split('.').last);
}
@override
List<ACOperationalValue> getOperationalValues() => [
ACOperationalValue(
icon: Assets.assetsAcCooling,
description: "Cooling",
value: TempModes.cold,
),
ACOperationalValue(
icon: Assets.assetsAcHeating,
description: "Heating",
value: TempModes.hot,
),
ACOperationalValue(
icon: Assets.assetsFanSpeed,
description: "Ventilation",
value: TempModes.wind,
),
];
}
class TempSetFunction extends ACFunction {
final int min;
final int max;
final int step;
TempSetFunction({required super.deviceId, required super.deviceName})
: min = 200,
max = 300,
step = 5,
super(
code: 'temp_set',
operationName: 'Set Temperature',
icon: Assets.assetsTempreture,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue) {
return currentStatus.copyWith(tempSet: newValue as int);
}
@override
List<ACOperationalValue> getOperationalValues() {
List<ACOperationalValue> values = [];
for (int temp = min; temp <= max; temp += step) {
values.add(ACOperationalValue(
icon: Assets.assetsTempreture,
description: "${temp / 10}°C",
value: temp,
));
}
return values;
}
}
class LevelFunction extends ACFunction {
LevelFunction({required super.deviceId, required super.deviceName})
: super(
code: 'level',
operationName: 'Fan Speed',
icon: Assets.assetsFanSpeed,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue) {
return currentStatus.copyWith(fanSpeedsString: (newValue as FanSpeeds).toString().split('.').last);
}
@override
List<ACOperationalValue> getOperationalValues() => [
ACOperationalValue(
icon: Assets.assetsAcFanLow,
description: "LOW",
value: FanSpeeds.low,
),
ACOperationalValue(
icon: Assets.assetsAcFanMiddle,
description: "MIDDLE",
value: FanSpeeds.middle,
),
ACOperationalValue(
icon: Assets.assetsAcFanHigh,
description: "HIGH",
value: FanSpeeds.high,
),
ACOperationalValue(
icon: Assets.assetsAcFanAuto,
description: "AUTO",
value: FanSpeeds.auto,
),
];
}
class ChildLockFunction extends ACFunction {
ChildLockFunction({required super.deviceId, required super.deviceName})
: super(
code: 'child_lock',
operationName: 'Child Lock',
icon: Assets.assetsChildLock,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue) {
return currentStatus.copyWith(childLock: newValue as bool);
}
@override
List<ACOperationalValue> getOperationalValues() => [
ACOperationalValue(
icon: Assets.assetsSceneChildLock,
description: "Lock",
value: true,
),
ACOperationalValue(
icon: Assets.assetsSceneChildUnlock,
description: "Unlock",
value: false,
),
];
}
/*
final deviceId = 'AC001';
final deviceName = 'Living Room AC';
// Initial AC status
var acStatus = AcStatusModel(
uuid: deviceId,
acSwitch: false,
modeString: 'cold',
tempSet: 220,
currentTemp: 250,
fanSpeedsString: 'auto',
childLock: false,
);
// Get all AC functions
final acFunctions = getACFunctions(deviceId, deviceName);
// Example: Turn on the AC
final switchFunction = acFunctions.firstWhere((f) => f is SwitchFunction) as SwitchFunction;
acStatus = switchFunction.execute(acStatus, true);
// Example: Change mode to heat
final modeFunction = acFunctions.firstWhere((f) => f is ModeFunction) as ModeFunction;
acStatus = modeFunction.execute(acStatus, TempModes.hot);
*/
@@ -0,0 +1,11 @@
class ACOperationalValue {
final String icon;
final String description;
final dynamic value;
ACOperationalValue({
required this.icon,
required this.description,
required this.value,
});
}
@@ -0,0 +1,17 @@
abstract class DeviceFunction<T> {
final String deviceId;
final String deviceName;
final String code;
final String operationName;
final String icon;
DeviceFunction({
required this.deviceId,
required this.deviceName,
required this.code,
required this.operationName,
required this.icon,
});
T execute(T currentStatus, dynamic newValue);
}
@@ -0,0 +1,29 @@
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
class RoutineItem {
final AllDevicesModel device;
final String? function;
final dynamic value;
RoutineItem({
required this.device,
this.function,
this.value,
});
Map<String, dynamic> toMap() {
return {
'device': device,
'function': function,
'value': value,
};
}
factory RoutineItem.fromMap(Map<String, dynamic> map) {
return RoutineItem(
device: map['device'] as AllDevicesModel,
function: map['function'],
value: map['value'],
);
}
}
+10 -2
View File
@@ -10,24 +10,32 @@ class DraggableCard extends StatelessWidget {
required this.title,
this.titleColor,
this.isDragged = false,
this.isDisabled = false,
});
final String imagePath;
final String title;
final Color? titleColor;
final bool isDragged;
final bool isDisabled;
@override
Widget build(BuildContext context) {
return Draggable<Map<String, String>>(
Widget card = Draggable<Map<String, String>>(
data: {'key': UniqueKey().toString(), 'imagePath': imagePath, 'title': title},
feedback: Transform.rotate(
angle: -0.1,
child: _buildCardContent(context),
),
childWhenDragging: _buildGreyContainer(),
child: isDragged ? _buildGreyContainer() : _buildCardContent(context),
child: _buildCardContent(context),
);
if (isDisabled) {
card = AbsorbPointer(child: card);
}
return card;
}
Widget _buildCardContent(BuildContext context) {
@@ -28,12 +28,14 @@ class RoutineDevices extends StatelessWidget {
return Wrap(
spacing: 10,
runSpacing: 10,
children: deviceList
.map((device) => DraggableCard(
imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '',
))
.toList(),
children: deviceList.asMap().entries.map((entry) {
final index = entry.key;
final device = entry.value;
return DraggableCard(
imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '',
);
}).toList(),
);
}
return const Center(child: CircularProgressIndicator());
@@ -26,12 +26,14 @@ class ScenesAndAutomations extends StatelessWidget {
return Wrap(
spacing: 10,
runSpacing: 10,
children: scenes
.map((scene) => DraggableCard(
imagePath: Assets.logo,
title: scene.name ?? '',
))
.toList(),
children: scenes.asMap().entries.map((entry) {
final index = entry.key;
final scene = entry.value;
return DraggableCard(
imagePath: Assets.logo,
title: scene.name ?? '',
);
}).toList(),
);
}
return const Center(child: CircularProgressIndicator());
+55
View File
@@ -210,4 +210,59 @@ class Assets {
//assets/icons/routine/delay.svg
static const String delay = 'assets/icons/routine/delay.svg';
// Assets for functions_icons
static const String assetsSensitivityFunction = "assets/icons/functions_icons/sensitivity.svg";
static const String assetsSensitivityOperationIcon = "assets/icons/functions_icons/sesitivity_operation_icon.svg";
static const String assetsAcPower = "assets/icons/functions_icons/ac_power.svg";
static const String assetsAcPowerOFF = "assets/icons/functions_icons/ac_power_off.svg";
static const String assetsChildLock = "assets/icons/functions_icons/child_lock.svg";
static const String assetsFreezing = "assets/icons/functions_icons/freezing.svg";
static const String assetsFanSpeed = "assets/icons/functions_icons/fan_speed.svg";
static const String assetsAcCooling = "assets/icons/functions_icons/ac_cooling.svg";
static const String assetsAcHeating = "assets/icons/functions_icons/ac_heating.svg";
static const String assetsCelsiusDegrees = "assets/icons/functions_icons/celsius_degrees.svg";
static const String assetsTempreture = "assets/icons/functions_icons/tempreture.svg";
static const String assetsAcFanLow = "assets/icons/functions_icons/ac_fan_low.svg";
static const String assetsAcFanMiddle = "assets/icons/functions_icons/ac_fan_middle.svg";
static const String assetsAcFanHigh = "assets/icons/functions_icons/ac_fan_high.svg";
static const String assetsAcFanAuto = "assets/icons/functions_icons/ac_fan_auto.svg";
static const String assetsSceneChildLock = "assets/icons/functions_icons/scene_child_lock.svg";
static const String assetsSceneChildUnlock = "assets/icons/functions_icons/scene_child_unlock.svg";
static const String assetsSceneRefresh = "assets/icons/functions_icons/scene_refresh.svg";
static const String assetsLightCountdown = "assets/icons/functions_icons/light_countdown.svg";
static const String assetsFarDetection = "assets/icons/functions_icons/far_detection.svg";
static const String assetsFarDetectionFunction = "assets/icons/functions_icons/far_detection_function.svg";
static const String assetsIndicator = "assets/icons/functions_icons/indicator.svg";
static const String assetsMotionDetection = "assets/icons/functions_icons/motion_detection.svg";
static const String assetsMotionlessDetection = "assets/icons/functions_icons/motionless_detection.svg";
static const String assetsNobodyTime = "assets/icons/functions_icons/nobody_time.svg";
static const String assetsFactoryReset = "assets/icons/functions_icons/factory_reset.svg";
static const String assetsMasterState = "assets/icons/functions_icons/master_state.svg";
static const String assetsSwitchAlarmSound = "assets/icons/functions_icons/switch_alarm_sound.svg";
static const String assetsResetOff = "assets/icons/functions_icons/reset_off.svg";
// Assets for automation_functions
static const String assetsCardUnlock = "assets/icons/functions_icons/automation_functions/card_unlock.svg";
static const String assetsDoorbell = "assets/icons/functions_icons/automation_functions/doorbell.svg";
static const String assetsDoorlockNormalOpen =
"assets/icons/functions_icons/automation_functions/doorlock_normal_open.svg";
static const String assetsDoubleLock = "assets/icons/functions_icons/automation_functions/double_lock.svg";
static const String assetsFingerprintUnlock =
"assets/icons/functions_icons/automation_functions/fingerprint_unlock.svg";
static const String assetsHijackAlarm = "assets/icons/functions_icons/automation_functions/hijack_alarm.svg";
static const String assetsLockAlarm = "assets/icons/functions_icons/automation_functions/lock_alarm.svg";
static const String assetsPasswordUnlock = "assets/icons/functions_icons/automation_functions/password_unlock.svg";
static const String assetsRemoteUnlockReq = "assets/icons/functions_icons/automation_functions/remote_unlock_req.svg";
static const String assetsRemoteUnlockViaApp =
"assets/icons/functions_icons/automation_functions/remote_unlock_via_app.svg";
static const String assetsResidualElectricity =
"assets/icons/functions_icons/automation_functions/residual_electricity.svg";
static const String assetsTempPasswordUnlock =
"assets/icons/functions_icons/automation_functions/temp_password_unlock.svg";
static const String assetsSelfTestResult = "assets/icons/functions_icons/automation_functions/self_test_result.svg";
static const String assetsPresence = "assets/icons/functions_icons/automation_functions/presence.svg";
static const String assetsMotion = "assets/icons/functions_icons/automation_functions/motion.svg";
static const String assetsCurrentTemp = "assets/icons/functions_icons/automation_functions/current_temp.svg";
static const String assetsPresenceState = "assets/icons/functions_icons/automation_functions/presence_state.svg";
}