diff --git a/assets/icons/Illuminance_icon.svg b/assets/icons/Illuminance_icon.svg new file mode 100644 index 00000000..379c45f9 --- /dev/null +++ b/assets/icons/Illuminance_icon.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/Indicator_icon.svg b/assets/icons/Indicator_icon.svg new file mode 100644 index 00000000..a542e9fa --- /dev/null +++ b/assets/icons/Indicator_icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/icons/current_distance_icon.svg b/assets/icons/current_distance_icon.svg new file mode 100644 index 00000000..d4c87601 --- /dev/null +++ b/assets/icons/current_distance_icon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/icons/far_detection_icon.svg b/assets/icons/far_detection_icon.svg new file mode 100644 index 00000000..b41b000a --- /dev/null +++ b/assets/icons/far_detection_icon.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/motion_detection_sensitivity_icon.svg b/assets/icons/motion_detection_sensitivity_icon.svg new file mode 100644 index 00000000..cd527f13 --- /dev/null +++ b/assets/icons/motion_detection_sensitivity_icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/motion_detection_sensitivity_value_icon.svg b/assets/icons/motion_detection_sensitivity_value_icon.svg new file mode 100644 index 00000000..21bebd7a --- /dev/null +++ b/assets/icons/motion_detection_sensitivity_value_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/motionless_detection_sensitivity_icon.svg b/assets/icons/motionless_detection_sensitivity_icon.svg new file mode 100644 index 00000000..a1dc2926 --- /dev/null +++ b/assets/icons/motionless_detection_sensitivity_icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/presence_state.svg b/assets/icons/presence_state.svg new file mode 100644 index 00000000..0c5ad042 --- /dev/null +++ b/assets/icons/presence_state.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/presence_time_icon.svg b/assets/icons/presence_time_icon.svg new file mode 100644 index 00000000..d1c22e03 --- /dev/null +++ b/assets/icons/presence_time_icon.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/pages/device_managment/all_devices/models/devices_model.dart b/lib/pages/device_managment/all_devices/models/devices_model.dart index e71d5af8..0a3b346c 100644 --- a/lib/pages/device_managment/all_devices/models/devices_model.dart +++ b/lib/pages/device_managment/all_devices/models/devices_model.dart @@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/three_gang_switch/three_gang_switch.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/two_gang_switch/two_gang_switch.dart'; +import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/enum/device_types.dart'; @@ -148,7 +149,9 @@ class AllDevicesModel { productName = json['productName']?.toString(); if (json['spaces'] != null && json['spaces'] is List) { - spaces = (json['spaces'] as List).map((space) => DeviceSpaceModel.fromJson(space)).toList(); + spaces = (json['spaces'] as List) + .map((space) => DeviceSpaceModel.fromJson(space)) + .toList(); } } @@ -196,7 +199,8 @@ SOS String tempIcon = ''; if (type == DeviceType.LightBulb) { tempIcon = Assets.lightBulb; - } else if (type == DeviceType.CeilingSensor || type == DeviceType.WallSensor) { + } else if (type == DeviceType.CeilingSensor || + type == DeviceType.WallSensor) { tempIcon = Assets.sensors; } else if (type == DeviceType.AC) { tempIcon = Assets.ac; @@ -226,6 +230,8 @@ SOS // tempIcon = Assets.gang3touch; } else if (type == DeviceType.WaterLeak) { tempIcon = Assets.waterLeakNormal; + } else if (type == DeviceType.WaterLeak) { + tempIcon = Assets.waterLeakNormal; } else { tempIcon = Assets.logoHorizontal; } @@ -252,27 +258,62 @@ SOS case '1G': return [ OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''), - OneGangCountdownFunction(deviceId: uuid ?? '', deviceName: name ?? ''), + OneGangCountdownFunction( + deviceId: uuid ?? '', deviceName: name ?? ''), ]; case '2G': return [ TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''), TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''), - TwoGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? ''), - TwoGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? ''), + TwoGangCountdown1Function( + deviceId: uuid ?? '', deviceName: name ?? ''), + TwoGangCountdown2Function( + deviceId: uuid ?? '', deviceName: name ?? ''), ]; case '3G': return [ - ThreeGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''), - ThreeGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''), - ThreeGangSwitch3Function(deviceId: uuid ?? '', deviceName: name ?? ''), - ThreeGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? ''), - ThreeGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? ''), - ThreeGangCountdown3Function(deviceId: uuid ?? '', deviceName: name ?? ''), + ThreeGangSwitch1Function( + deviceId: uuid ?? '', deviceName: name ?? ''), + ThreeGangSwitch2Function( + deviceId: uuid ?? '', deviceName: name ?? ''), + ThreeGangSwitch3Function( + deviceId: uuid ?? '', deviceName: name ?? ''), + ThreeGangCountdown1Function( + deviceId: uuid ?? '', deviceName: name ?? ''), + ThreeGangCountdown2Function( + deviceId: uuid ?? '', deviceName: name ?? ''), + ThreeGangCountdown3Function( + deviceId: uuid ?? '', deviceName: name ?? ''), ]; + case 'WPS': + return [ + //IF Functions + PresenceStateFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + CurrentDistanceFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + IlluminanceValueFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + PresenceTimeFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + //THEN Functions + FarDetectionFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), + MotionSensitivityFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), + MotionLessSensitivityFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), + IndicatorFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + NoOneTimeFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), + + // FarDetectionSliderFunction( + // deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN') + ]; default: return []; } diff --git a/lib/pages/device_managment/water_heater/widgets/count_down_inching_view.dart b/lib/pages/device_managment/water_heater/widgets/count_down_inching_view.dart index 53892c20..9c28d4d6 100644 --- a/lib/pages/device_managment/water_heater/widgets/count_down_inching_view.dart +++ b/lib/pages/device_managment/water_heater/widgets/count_down_inching_view.dart @@ -95,6 +95,77 @@ class CountdownInchingView extends StatelessWidget { ); } + Row _hourMinutesSecondWheel( + BuildContext context, WaterHeaterDeviceStatusLoaded state) { + final isCountDown = + state.scheduleMode?.name == ScheduleModes.countdown.name; + late bool isActive; + if (isCountDown && + state.countdownRemaining != null && + state.isCountdownActive == true) { + isActive = true; + } else if (!isCountDown && + state.countdownRemaining != null && + state.isInchingActive == true) { + isActive = true; + } else { + isActive = false; + } + + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + _buildPickerColumn( + context, + 'h', + isCountDown + ? (state.countdownHours ?? 0) + : (state.inchingHours ?? 0), + 24, (value) { + context.read().add(UpdateScheduleEvent( + scheduleMode: state.scheduleMode ?? ScheduleModes.countdown, + hours: value, + minutes: isCountDown + ? (state.countdownMinutes ?? 0) + : (state.inchingMinutes ?? 0), + )); + }, isActive: isActive), + const SizedBox(width: 10), + _buildPickerColumn( + context, + 'm', + isCountDown + ? (state.countdownMinutes ?? 0) + : (state.inchingMinutes ?? 0), + 60, (value) { + context.read().add(UpdateScheduleEvent( + scheduleMode: state.scheduleMode ?? ScheduleModes.countdown, + hours: isCountDown + ? (state.countdownHours ?? 0) + : (state.inchingHours ?? 0), + minutes: value, + )); + }, isActive: isActive), + const SizedBox(width: 10), + _buildPickerColumn( + context, + 'S', + isCountDown + ? (state.countdownMinutes ?? 0) + : (state.inchingMinutes ?? 0), + 60, (value) { + context.read().add(UpdateScheduleEvent( + scheduleMode: state.scheduleMode ?? ScheduleModes.countdown, + hours: isCountDown + ? (state.countdownHours ?? 0) + : (state.inchingHours ?? 0), + minutes: value, + )); + }, isActive: isActive), + ], + ); + } + Widget _buildPickerColumn( BuildContext context, String label, diff --git a/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart b/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart index 933b35d7..48eaedb9 100644 --- a/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart +++ b/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart @@ -6,21 +6,24 @@ import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_swit import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wall_presence_sensor.dart'; class DeviceDialogHelper { - static Future?> showDeviceDialog( - BuildContext context, - Map data, { + static Future?> showDeviceDialog({ + required BuildContext context, + required String dialogType, + required Map data, required bool removeComparetors, }) async { final functions = data['functions'] as List; try { final result = await _getDialogForDeviceType( - context, - data['productType'], - data, - functions, + dialogType: dialogType, + context: context, + productType: data['productType'], + data: data, + functions: functions, removeComparetors: removeComparetors, ); @@ -34,32 +37,66 @@ class DeviceDialogHelper { return null; } - static Future?> _getDialogForDeviceType(BuildContext context, - String productType, Map data, List functions, - {required bool removeComparetors}) async { + static Future?> _getDialogForDeviceType({ + required String dialogType, + required BuildContext context, + required String productType, + required Map data, + required List functions, + required bool removeComparetors, + }) async { final routineBloc = context.read(); final deviceSelectedFunctions = routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? []; - if (removeComparetors) { + if (removeComparetors && data['productType'] != 'WPS') { //remove the current temp function in the 'if container' functions.removeAt(3); } switch (productType) { case 'AC': - return ACHelper.showACFunctionsDialog(context, functions, data['device'], - deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors); + return ACHelper.showACFunctionsDialog( + context, + functions, + data['device'], + deviceSelectedFunctions, + data['uniqueCustomId'], + removeComparetors); case '1G': - return OneGangSwitchHelper.showSwitchFunctionsDialog(context, functions, data['device'], - deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors); + return OneGangSwitchHelper.showSwitchFunctionsDialog( + context, + functions, + data['device'], + deviceSelectedFunctions, + data['uniqueCustomId'], + removeComparetors); case '2G': - return TwoGangSwitchHelper.showSwitchFunctionsDialog(context, functions, data['device'], - deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors); + return TwoGangSwitchHelper.showSwitchFunctionsDialog( + context, + functions, + data['device'], + deviceSelectedFunctions, + data['uniqueCustomId'], + removeComparetors); case '3G': - return ThreeGangSwitchHelper.showSwitchFunctionsDialog(context, functions, data['device'], - deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors); + return ThreeGangSwitchHelper.showSwitchFunctionsDialog( + context, + functions, + data['device'], + deviceSelectedFunctions, + data['uniqueCustomId'], + removeComparetors); + case 'WPS': + return WallPresenceSensor.showWPSFunctionsDialog( + dialogType: dialogType, + context: context, + functions: functions, + device: data['device'], + deviceSelectedFunctions: deviceSelectedFunctions, + uniqueCustomId: data['uniqueCustomId'], + removeComparetors: removeComparetors); default: return null; } diff --git a/lib/pages/routines/models/wps/wps_functions.dart b/lib/pages/routines/models/wps/wps_functions.dart new file mode 100644 index 00000000..d090686a --- /dev/null +++ b/lib/pages/routines/models/wps/wps_functions.dart @@ -0,0 +1,290 @@ + + +import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart'; +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +abstract class WpsFunctions extends DeviceFunction { + final String type; + + WpsFunctions({ + required super.deviceId, + required super.deviceName, + required super.code, + required super.operationName, + required super.icon, + required this.type, + }); + + List getOperationalValues(); +} + +// For far_detection (75-600cm in 75cm steps) +class FarDetectionFunction extends WpsFunctions { + final int min; + final int max; + final int step; + final String unit; + + FarDetectionFunction( + {required super.deviceId, required super.deviceName, required type}) + : min = 75, + max = 600, + step = 75, + unit = 'cm', + super( + type: type, + code: 'far_detection', + operationName: 'Far Detection', + icon: Assets.farDetectionIcon, + ); + + @override + List getOperationalValues() { + final values = []; + for (var value = min; value <= max; value += step) { + values.add(WpsOperationalValue( + icon: Assets.currentDistanceIcon, + description: '$value $unit', + value: value, + )); + } + return values; + } +} + +// For presence_time (0-65535 minutes) +class PresenceTimeFunction extends WpsFunctions { + final int min; + final int max; + final int step; + final String unit; + + PresenceTimeFunction( + {required super.deviceId, required super.deviceName, required type}) + : min = 0, + max = 65535, + step = 1, + unit = 'Min', + super( + type: type, + code: 'presence_time', + operationName: 'Presence Time', + icon: Assets.presenceTimeIcon, + ); + + @override + List getOperationalValues() { + return [ + WpsOperationalValue( + icon: icon, + description: 'Custom $unit', + value: null, + ) + ]; + } +} + +// For motion_sensitivity_value (1-5 levels) +class MotionSensitivityFunction extends WpsFunctions { + final int min; + final int max; + final int step; + + MotionSensitivityFunction( + {required super.deviceId, required super.deviceName, required type}) + : min = 1, + max = 5, + step = 1, + super( + type: type, + code: 'motion_sensitivity_value', + operationName: 'Motion Detection Sensitivity', + icon: Assets.motionDetectionSensitivityIcon, + ); + + @override + List getOperationalValues() { + return List.generate( + (max - min) ~/ step + 1, + (index) => WpsOperationalValue( + icon: Assets.motionDetectionSensitivityValueIcon, + description: '${min + (index * step)}', + value: min + (index * step), + ), + ); + } +} + +class MotionLessSensitivityFunction extends WpsFunctions { + final int min; + final int max; + final int step; + + MotionLessSensitivityFunction( + {required super.deviceId, required super.deviceName, required type}) + : min = 1, + max = 5, + step = 1, + super( + type: type, + code: 'motionless_sensitivity', + operationName: 'Motionless Detection Sensitivity', + icon: Assets.motionlessDetectionSensitivityIcon, + ); + + @override + List getOperationalValues() { + return List.generate( + (max - min) ~/ step + 1, + (index) => WpsOperationalValue( + icon: Assets.currentDistanceIcon, + description: '${min + (index * step)}', + value: min + (index * step), + ), + ); + } +} + +class IndicatorFunction extends WpsFunctions { + IndicatorFunction( + {required super.deviceId, required super.deviceName, required type}) + : super( + type: type, + code: 'indicator', + operationName: 'Indicator', + icon: Assets.IndicatorIcon, + ); + + @override + List getOperationalValues() => [ + WpsOperationalValue( + icon: Assets.assetsAcPower, + description: "ON", + value: true, + ), + WpsOperationalValue( + icon: Assets.assetsAcPowerOFF, + description: "OFF", + value: false, + ), + ]; +} + +class NoOneTimeFunction extends WpsFunctions { + final int min; + final int max; + final String unit; + + NoOneTimeFunction( + {required super.deviceId, required super.deviceName, required type}) + : min = 10, + max = 10000, + unit = '秒', + super( + type: type, + code: 'no_one_time', + operationName: 'Nobody Time', + icon: Assets.nobodyTime, + ); + + @override + List getOperationalValues() { + return [ + WpsOperationalValue( + icon: icon, + description: 'Custom $unit', + value: null, + ) + ]; + } +} + +class PresenceStateFunction extends WpsFunctions { + PresenceStateFunction( + {required super.deviceId, required super.deviceName, required type}) + : super( + type: type, + code: 'presence_state', + operationName: 'Presence State', + icon: Assets.presenceStateIcon, + ); + + @override + List getOperationalValues() => [ + WpsOperationalValue( + icon: Assets.assetsAcPower, + description: "None", + value: true, + ), + WpsOperationalValue( + icon: Assets.presenceStateIcon, + description: "Presence", + value: false, + ), + ]; +} + +class CurrentDistanceFunction extends WpsFunctions { + final int min; + final int max; + final int step; + + CurrentDistanceFunction( + {required super.deviceId, required super.deviceName, required type}) + : min = 1, + max = 600, + step = 1, + super( + type: type, + code: 'current_distance', + operationName: 'Current Distance', + icon: Assets.currentDistanceIcon, + ); + + @override + List getOperationalValues() { + List values = []; + for (int temp = min; temp <= max; temp += step) { + values.add(WpsOperationalValue( + icon: Assets.assetsTempreture, + description: "${temp}CM", + value: temp, + )); + } + return values; + } +} + +class IlluminanceValueFunction extends WpsFunctions { + final int min; + final int max; + final int step; + + IlluminanceValueFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 0, + max = 10000, + step = 10, + super( + code: 'illuminance_value', + operationName: 'Illuminance Value', + icon: Assets.IlluminanceIcon, + ); + + @override + List getOperationalValues() { + List values = []; + for (int lux = min; lux <= max; lux += step) { + values.add(WpsOperationalValue( + icon: Assets.IlluminanceIcon, + description: "$lux Lux", + value: lux, + )); + } + return values; + } +} diff --git a/lib/pages/routines/models/wps/wps_operational_value.dart b/lib/pages/routines/models/wps/wps_operational_value.dart new file mode 100644 index 00000000..28b05d1f --- /dev/null +++ b/lib/pages/routines/models/wps/wps_operational_value.dart @@ -0,0 +1,11 @@ +class WpsOperationalValue { + final String icon; + final String description; + final dynamic value; + + WpsOperationalValue({ + required this.icon, + required this.description, + required this.value, + }); +} diff --git a/lib/pages/routines/widgets/if_container.dart b/lib/pages/routines/widgets/if_container.dart index d1736240..d6078143 100644 --- a/lib/pages/routines/widgets/if_container.dart +++ b/lib/pages/routines/widgets/if_container.dart @@ -26,7 +26,9 @@ class IfContainer extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text('IF', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + const Text('IF', + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold)), if (state.isAutomation && state.ifItems.isNotEmpty) AutomationOperatorSelector( selectedOperator: state.selectedAutomationOperator), @@ -53,34 +55,47 @@ class IfContainer extends StatelessWidget { (index) => GestureDetector( onTap: () async { if (!state.isTabToRun) { - final result = await DeviceDialogHelper.showDeviceDialog( - context, state.ifItems[index], - removeComparetors: false); + final result = await DeviceDialogHelper + .showDeviceDialog( + context: context, + data: state.ifItems[index], + removeComparetors: false, + dialogType: "IF"); if (result != null) { - context - .read() - .add(AddToIfContainer(state.ifItems[index], false)); - } else if (!['AC', '1G', '2G', '3G'] - .contains(state.ifItems[index]['productType'])) { - context - .read() - .add(AddToIfContainer(state.ifItems[index], false)); + context.read().add( + AddToIfContainer( + state.ifItems[index], false)); + } else if (![ + 'AC', + '1G', + '2G', + '3G', + 'WPS' + ].contains( + state.ifItems[index]['productType'])) { + context.read().add( + AddToIfContainer( + state.ifItems[index], false)); } } }, child: DraggableCard( - imagePath: state.ifItems[index]['imagePath'] ?? '', + imagePath: + state.ifItems[index]['imagePath'] ?? '', title: state.ifItems[index]['title'] ?? '', deviceData: state.ifItems[index], - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), + padding: const EdgeInsets.symmetric( + horizontal: 4, vertical: 8), isFromThen: false, isFromIf: true, onRemove: () { - context.read().add(RemoveDragCard( - index: index, - isFromThen: false, - key: state.ifItems[index]['uniqueCustomId'])); + context.read().add( + RemoveDragCard( + index: index, + isFromThen: false, + key: state.ifItems[index] + ['uniqueCustomId'])); }, ), )), @@ -90,6 +105,7 @@ class IfContainer extends StatelessWidget { ); }, onAcceptWithDetails: (data) async { + print('data.data=${data.data}'); final uniqueCustomId = const Uuid().v4(); final mutableData = Map.from(data.data); @@ -101,15 +117,25 @@ class IfContainer extends StatelessWidget { if (!state.isTabToRun) { if (mutableData['deviceId'] == 'tab_to_run') { - context.read().add(AddToIfContainer(mutableData, true)); + context + .read() + .add(AddToIfContainer(mutableData, true)); } else { - final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData, + final result = await DeviceDialogHelper.showDeviceDialog( + dialogType: 'IF', + context: context, + data: mutableData, removeComparetors: false); if (result != null) { - context.read().add(AddToIfContainer(mutableData, false)); - } else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) { - context.read().add(AddToIfContainer(mutableData, false)); + context + .read() + .add(AddToIfContainer(mutableData, false)); + } else if (!['AC', '1G', '2G', '3G', 'WPS'] + .contains(mutableData['productType'])) { + context + .read() + .add(AddToIfContainer(mutableData, false)); } } } @@ -155,7 +181,9 @@ class AutomationOperatorSelector extends StatelessWidget { ), ), onPressed: () { - context.read().add(const ChangeAutomationOperator(operator: 'or')); + context + .read() + .add(const ChangeAutomationOperator(operator: 'or')); }, ), Container( @@ -181,7 +209,9 @@ class AutomationOperatorSelector extends StatelessWidget { ), ), onPressed: () { - context.read().add(const ChangeAutomationOperator(operator: 'and')); + context + .read() + .add(const ChangeAutomationOperator(operator: 'and')); }, ), ], diff --git a/lib/pages/routines/widgets/routine_devices.dart b/lib/pages/routines/widgets/routine_devices.dart index 21531ad3..2d3f7236 100644 --- a/lib/pages/routines/widgets/routine_devices.dart +++ b/lib/pages/routines/widgets/routine_devices.dart @@ -37,7 +37,8 @@ class _RoutineDevicesState extends State { device.productType == 'AC' || device.productType == '1G' || device.productType == '2G' || - device.productType == '3G') + device.productType == '3G' || + device.productType == 'WPS') .toList(); return Wrap( @@ -46,7 +47,9 @@ class _RoutineDevicesState extends State { children: deviceList.asMap().entries.map((entry) { final device = entry.value; if (state.searchText != null && state.searchText!.isNotEmpty) { - return device.name!.toLowerCase().contains(state.searchText!.toLowerCase()) + return device.name! + .toLowerCase() + .contains(state.searchText!.toLowerCase()) ? DraggableCard( imagePath: device.getDefaultIcon(device.productType), title: device.name ?? '', diff --git a/lib/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart b/lib/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart index f565f07d..9a1511ea 100644 --- a/lib/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart +++ b/lib/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart @@ -22,21 +22,25 @@ class ThreeGangSwitchHelper { String uniqueCustomId, bool removeComparetors, ) async { - List switchFunctions = functions.whereType().toList(); + List switchFunctions = + functions.whereType().toList(); return showDialog?>( context: context, builder: (BuildContext context) { + print('functions: $functions'); + return BlocProvider( - create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])), + create: (_) => FunctionBloc() + ..add(InitializeFunctions(deviceSelectedFunctions ?? [])), child: AlertDialog( contentPadding: EdgeInsets.zero, content: BlocBuilder( builder: (context, state) { final selectedFunction = state.selectedFunction; final selectedOperationName = state.selectedOperationName; - final selectedFunctionData = - state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction, + final selectedFunctionData = state.addedFunctions + .firstWhere((f) => f.functionCode == selectedFunction, orElse: () => DeviceFunctionData( entityId: '', functionCode: selectedFunction ?? '', @@ -83,9 +87,12 @@ class ThreeGangSwitchHelper { color: ColorsManager.textGray, ), onTap: () { - context.read().add(SelectFunction( + context + .read() + .add(SelectFunction( functionCode: function.code, - operationName: function.operationName, + operationName: + function.operationName, )); }, ); @@ -177,7 +184,8 @@ class ThreeGangSwitchHelper { ); } - final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction); + final selectedFn = + switchFunctions.firstWhere((f) => f.code == selectedFunction); final values = selectedFn.getOperationalValues(); return _buildOperationalValuesList( @@ -214,11 +222,11 @@ class ThreeGangSwitchHelper { selectedFunctionData, ), const SizedBox(height: 20), - _buildCountDownDisplay( - context, initialValue, device, operationName, selectedFunctionData, selectCode), + _buildCountDownDisplay(context, initialValue, device, operationName, + selectedFunctionData, selectCode), const SizedBox(height: 20), - _buildCountDownSlider( - context, initialValue, device, operationName, selectedFunctionData, selectCode), + _buildCountDownSlider(context, initialValue, device, operationName, + selectedFunctionData, selectCode), ], ); } @@ -259,7 +267,8 @@ class ThreeGangSwitchHelper { minHeight: 40.0, minWidth: 40.0, ), - isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(), + isSelected: + conditions.map((c) => c == (currentCondition ?? "==")).toList(), children: conditions.map((c) => Text(c)).toList(), ); } @@ -307,7 +316,8 @@ class ThreeGangSwitchHelper { value: (initialValue ?? 0).toDouble(), min: operationalValues.minValue?.toDouble() ?? 0.0, max: operationalValues.maxValue?.toDouble() ?? 0.0, - divisions: (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) / + divisions: (((operationalValues.maxValue ?? 0) - + (operationalValues.minValue ?? 0)) / (operationalValues.stepValue ?? 1)) .round(), onChanged: (value) { @@ -359,9 +369,13 @@ class ThreeGangSwitchHelper { style: context.textTheme.bodyMedium, ), trailing: Icon( - isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, + isSelected + ? Icons.radio_button_checked + : Icons.radio_button_unchecked, size: 24, - color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray, + color: isSelected + ? ColorsManager.primaryColorWithOpacity + : ColorsManager.textGray, ), onTap: () { if (!isSelected) { @@ -373,7 +387,8 @@ class ThreeGangSwitchHelper { operationName: operationName, value: value.value, condition: selectedFunctionData?.condition, - valueDescription: selectedFunctionData?.valueDescription, + valueDescription: + selectedFunctionData?.valueDescription, ), ), ); diff --git a/lib/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart new file mode 100644 index 00000000..a5b24a22 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart @@ -0,0 +1,150 @@ + +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class TimeWheelPicker extends StatefulWidget { + final int initialHours; + final int initialMinutes; + final int initialSeconds; + final Function(int, int, int) onTimeChanged; + + const TimeWheelPicker({ + super.key, + required this.initialHours, + required this.initialMinutes, + required this.initialSeconds, + required this.onTimeChanged, + }); + + @override + State createState() => _TimeWheelPickerState(); +} + +class _TimeWheelPickerState extends State { + late FixedExtentScrollController _hoursController; + late FixedExtentScrollController _minutesController; + late FixedExtentScrollController _secondsController; + + @override + void initState() { + super.initState(); + _hoursController = FixedExtentScrollController(initialItem: widget.initialHours); + _minutesController = FixedExtentScrollController(initialItem: widget.initialMinutes); + _secondsController = FixedExtentScrollController(initialItem: widget.initialSeconds); + } + + @override + void didUpdateWidget(TimeWheelPicker oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.initialHours != widget.initialHours) { + _hoursController.jumpToItem(widget.initialHours); + } + if (oldWidget.initialMinutes != widget.initialMinutes) { + _minutesController.jumpToItem(widget.initialMinutes); + } + if (oldWidget.initialSeconds != widget.initialSeconds) { + _secondsController.jumpToItem(widget.initialSeconds); + } + } + + @override + void dispose() { + _hoursController.dispose(); + _minutesController.dispose(); + _secondsController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildPickerColumn( + label: 'h', + controller: _hoursController, + itemCount: 24, + onChanged: (value) => _handleTimeChange( + value, + _minutesController.selectedItem, + _secondsController.selectedItem, + ), + ), + const SizedBox(width: 5), + _buildPickerColumn( + label: 'm', + controller: _minutesController, + itemCount: 60, + onChanged: (value) => _handleTimeChange( + _hoursController.selectedItem, + value, + _secondsController.selectedItem, + ), + ), + const SizedBox(width: 5), + _buildPickerColumn( + label: 's', + controller: _secondsController, + itemCount: 60, + onChanged: (value) => _handleTimeChange( + _hoursController.selectedItem, + _minutesController.selectedItem, + value, + ), + ), + ], + ); + } + + void _handleTimeChange(int hours, int minutes, int seconds) { + widget.onTimeChanged(hours, minutes, seconds); + } + + Widget _buildPickerColumn({ + required String label, + required FixedExtentScrollController controller, + required int itemCount, + required Function(int) onChanged, + }) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 40, + width: 40, + padding: const EdgeInsets.symmetric(horizontal: 5), + decoration: BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.circular(8), + ), + child: ListWheelScrollView.useDelegate( + controller: controller, + itemExtent: 40.0, + physics: const FixedExtentScrollPhysics(), + onSelectedItemChanged: onChanged, + childDelegate: ListWheelChildBuilderDelegate( + builder: (context, index) => Center( + child: Text( + index.toString().padLeft(2), + style: const TextStyle( + fontSize: 18, + color: ColorsManager.blue1, + ), + ), + ), + childCount: itemCount, + ), + ), + ), + const SizedBox(width: 5), + Text( + label, + style: const TextStyle( + color: ColorsManager.blackColor, + fontSize: 18, + ), + ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wall_presence_sensor.dart b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wall_presence_sensor.dart new file mode 100644 index 00000000..960b22de --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/wall_sensor/wall_presence_sensor.dart @@ -0,0 +1,553 @@ +import 'package:flutter/material.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/routines/bloc/routine_bloc/routine_bloc.dart'; +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart'; +import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart'; +import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart'; +import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; + + +class WallPresenceSensor extends StatefulWidget { + final List functions; + final AllDevicesModel? device; + final List? deviceSelectedFunctions; + final String? uniqueCustomId; + final String? dialogType; + final bool removeComparetors; + + const WallPresenceSensor({ + super.key, + required this.functions, + this.device, + this.deviceSelectedFunctions, + this.uniqueCustomId, + this.dialogType, + this.removeComparetors = false, + }); + + static Future?> showWPSFunctionsDialog({ + required BuildContext context, + required List functions, + AllDevicesModel? device, + List? deviceSelectedFunctions, + String? uniqueCustomId, + String? dialogType, + bool removeComparetors = false, + }) async { + return showDialog?>( + context: context, + builder: (context) => WallPresenceSensor( + functions: functions, + device: device, + deviceSelectedFunctions: deviceSelectedFunctions, + uniqueCustomId: uniqueCustomId, + removeComparetors: removeComparetors, + dialogType: dialogType, + ), + ); + } + + @override + State createState() => _WallPresenceSensorState(); +} + +class _WallPresenceSensorState extends State { + late final List _wpsFunctions; + + @override + void initState() { + super.initState(); + _wpsFunctions = + widget.functions.whereType().where((function) { + if (widget.dialogType == 'THEN') { + return function.type == 'THEN' || function.type == 'BOTH'; + } + return function.type == 'IF' || function.type == 'BOTH'; + }).toList(); + } + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => FunctionBloc() + ..add(InitializeFunctions(widget.deviceSelectedFunctions ?? [])), + child: _buildDialogContent(), + ); + } + + Widget _buildDialogContent() { + return AlertDialog( + contentPadding: EdgeInsets.zero, + content: BlocBuilder( + builder: (context, state) { + final selectedFunction = state.selectedFunction; + return Container( + width: selectedFunction != null ? 600 : 360, + height: 450, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.only(top: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const DialogHeader('Presence Sensor Condition'), + Expanded(child: _buildMainContent(context, state)), + _buildDialogFooter(context, state), + ], + ), + ); + }, + ), + ); + } + + Widget _buildMainContent(BuildContext context, FunctionBlocState state) { + return Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildFunctionList(context), + if (state.selectedFunction != null) _buildValueSelector(context, state), + ], + ); + } + + Widget _buildFunctionList(BuildContext context) { + return SizedBox( + width: 360, + child: ListView.separated( + shrinkWrap: false, + physics: const AlwaysScrollableScrollPhysics(), + itemCount: _wpsFunctions.length, + separatorBuilder: (context, index) => const Padding( + padding: EdgeInsets.symmetric(horizontal: 40.0), + child: Divider(color: ColorsManager.dividerColor), + ), + itemBuilder: (context, index) { + final function = _wpsFunctions[index]; + return ListTile( + leading: SvgPicture.asset( + function.icon, + width: 24, + height: 24, + placeholderBuilder: (context) => const SizedBox( + width: 24, + height: 24, + ), + ), + title: Text( + function.operationName, + style: context.textTheme.bodyMedium, + ), + trailing: const Icon( + Icons.arrow_forward_ios, + size: 16, + color: ColorsManager.textGray, + ), + onTap: () => context.read().add( + SelectFunction( + functionCode: function.code, + operationName: function.operationName, + ), + ), + ); + }, + ), + ); + } + + Widget _buildValueSelector(BuildContext context, FunctionBlocState state) { + final selectedFunction = state.selectedFunction!; + final functionData = state.addedFunctions.firstWhere( + (f) => f.functionCode == selectedFunction, + orElse: () => DeviceFunctionData( + entityId: '', + functionCode: selectedFunction, + operationName: '', + value: null, + ), + ); + + return Expanded( + child: _ValueSelector( + selectedFunction: selectedFunction, + functionData: functionData, + acFunctions: _wpsFunctions, + device: widget.device, + dialogType: widget.dialogType!, + removeComparators: widget.removeComparetors, + ), + ); + } + + Widget _buildDialogFooter(BuildContext context, FunctionBlocState state) { + return DialogFooter( + onCancel: () => Navigator.pop(context), + onConfirm: state.addedFunctions.isNotEmpty + ? () { + context.read().add( + AddFunctionToRoutine( + state.addedFunctions, + widget.uniqueCustomId!, + ), + ); + Navigator.pop( + context, + {'deviceId': widget.functions.first.deviceId}, + ); + } + : null, + isConfirmEnabled: state.selectedFunction != null, + ); + } +} + +class _ValueSelector extends StatelessWidget { + final String selectedFunction; + final DeviceFunctionData functionData; + final List acFunctions; + final AllDevicesModel? device; + final String dialogType; + final bool removeComparators; + + const _ValueSelector({ + required this.selectedFunction, + required this.functionData, + required this.acFunctions, + required this.device, + required this.dialogType, + required this.removeComparators, + }); + + @override + Widget build(BuildContext context) { + final selectedFn = + acFunctions.firstWhere((f) => f.code == selectedFunction); + final values = selectedFn.getOperationalValues(); + + if (_isSliderFunction(selectedFunction)) { + return _SliderValueSelector( + selectedFunction: selectedFunction, + functionData: functionData, + device: device, + dialogType: dialogType, + ); + } + + return _OperationalValuesList( + values: values, + selectedValue: functionData.value, + device: device, + operationName: selectedFn.operationName, + selectCode: selectedFunction, + ); + } + + bool _isSliderFunction(String function) => [ + 'current_distance', + 'presence_time', + 'illuminance_value' + ].contains(function); +} + +class _SliderValueSelector extends StatelessWidget { + final String selectedFunction; + final DeviceFunctionData functionData; + final AllDevicesModel? device; + final String dialogType; + + const _SliderValueSelector({ + required this.selectedFunction, + required this.functionData, + required this.device, + required this.dialogType, + }); + + @override + Widget build(BuildContext context) { + final initialValue = functionData.value ?? 250; + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 20), + _ConditionToggle( + currentCondition: functionData.condition, + selectCode: selectedFunction, + device: device, + operationName: functionData.operationName, + selectedValue: functionData.value, + ), + _ValueDisplay( + value: initialValue, + functionCode: selectedFunction, + ), + const SizedBox(height: 20), + _FunctionSlider( + initialValue: initialValue, + functionCode: selectedFunction, + functionData: functionData, + device: device, + ), + ], + ); + } +} + +class _ConditionToggle extends StatelessWidget { + final String? currentCondition; + final String selectCode; + final AllDevicesModel? device; + final String operationName; + final dynamic selectedValue; + + const _ConditionToggle({ + this.currentCondition, + required this.selectCode, + this.device, + required this.operationName, + this.selectedValue, + }); + + @override + Widget build(BuildContext context) { + const conditions = ["<", "==", ">"]; + return ToggleButtons( + onPressed: (index) => _updateCondition(context, conditions[index]), + borderRadius: const BorderRadius.all(Radius.circular(8)), + selectedBorderColor: ColorsManager.primaryColorWithOpacity, + selectedColor: Colors.white, + fillColor: ColorsManager.primaryColorWithOpacity, + color: ColorsManager.primaryColorWithOpacity, + constraints: const BoxConstraints( + minHeight: 40.0, + minWidth: 40.0, + ), + isSelected: + conditions.map((c) => c == (currentCondition ?? "==")).toList(), + children: conditions.map((c) => Text(c)).toList(), + ); + } + + void _updateCondition(BuildContext context, String condition) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + condition: condition, + value: selectedValue, + ), + ), + ); + } +} + +class _ValueDisplay extends StatelessWidget { + final dynamic value; + final String functionCode; + + const _ValueDisplay({ + required this.value, + required this.functionCode, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + decoration: BoxDecoration( + color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + _getDisplayText(), + style: context.textTheme.headlineMedium!.copyWith( + color: ColorsManager.primaryColorWithOpacity, + ), + ), + ); + } + + String _getDisplayText() { + final intValue = (value as num?)?.toInt() ?? 0; + switch (functionCode) { + case 'presence_time': + return '$intValue Min'; + case 'current_distance': + return '$intValue CM'; + case 'illuminance_value': + return '$intValue Lux'; + default: + return '$intValue'; + } + } +} + +class _FunctionSlider extends StatelessWidget { + final dynamic initialValue; + final String functionCode; + final DeviceFunctionData functionData; + final AllDevicesModel? device; + + const _FunctionSlider({ + required this.initialValue, + required this.functionCode, + required this.functionData, + required this.device, + }); + + @override + Widget build(BuildContext context) { + final (min, max) = _getSliderRange(); + return Slider( + value: initialValue is int ? initialValue.toDouble() : min, + min: min, + max: max, + divisions: (max - min).toInt(), + onChanged: (value) => _updateValue(context, value.toInt()), + ); + } + + (double, double) _getSliderRange() { + switch (functionCode) { + case 'presence_time': + return (0, 65535); + case 'current_distance': + return (1, 600); + case 'illuminance_value': + return (0, 10000); + default: + return (200, 300); + } + } + + void _updateValue(BuildContext context, int value) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: functionCode, + operationName: functionData.operationName, + value: value, + condition: functionData.condition, + ), + ), + ); + } +} + +class _OperationalValuesList extends StatelessWidget { + final List values; + final dynamic selectedValue; + final AllDevicesModel? device; + final String operationName; + final String selectCode; + + const _OperationalValuesList({ + required this.values, + required this.selectedValue, + required this.device, + required this.operationName, + required this.selectCode, + }); + + @override + Widget build(BuildContext context) { + return operationName == 'Nobody Time' + ? _buildTimeWheel(context) + : ListView.builder( + padding: const EdgeInsets.all(20), + itemCount: values.length, + itemBuilder: (context, index) => + _buildValueItem(context, values[index]), + ); + } + + Widget _buildTimeWheel(BuildContext context) { + final currentTotalSeconds = selectedValue as int? ?? 0; + return TimeWheelPicker( + initialHours: currentTotalSeconds ~/ 3600, + initialMinutes: (currentTotalSeconds % 3600) ~/ 60, + initialSeconds: currentTotalSeconds % 60, + onTimeChanged: (h, m, s) => _updateTotalSeconds(context, h, m, s), + ); + } + + Widget _buildValueItem(BuildContext context, WpsOperationalValue value) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _buildValueIcon(context, value), + Expanded(child: _buildValueDescription(value)), + _buildValueRadio(context, value), + ], + ), + ); + } + + Widget _buildValueIcon(context, WpsOperationalValue value) { + return Column( + children: [ + if (_shouldShowTextDescription) + Text(value.description.replaceAll("cm", '')), + SvgPicture.asset(value.icon, width: 25, height: 25), + ], + ); + } + + bool get _shouldShowTextDescription => + operationName == 'Far Detection' || + operationName == 'Motionless Detection Sensitivity'; + + Widget _buildValueDescription(WpsOperationalValue value) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Text(value.description), + ); + } + + Widget _buildValueRadio(context, WpsOperationalValue value) { + return Radio( + value: value.value, + groupValue: selectedValue, + onChanged: (_) => _selectValue(context, value.value), + ); + } + + void _selectValue(BuildContext context, dynamic value) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: value, + ), + ), + ); + } + + void _updateTotalSeconds(BuildContext context, int h, int m, int s) { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectCode, + operationName: operationName, + value: h * 3600 + m * 60 + s, + ), + ), + ); + } +} diff --git a/lib/pages/routines/widgets/then_container.dart b/lib/pages/routines/widgets/then_container.dart index 2fcc62b8..5b26056a 100644 --- a/lib/pages/routines/widgets/then_container.dart +++ b/lib/pages/routines/widgets/then_container.dart @@ -26,7 +26,9 @@ class ThenContainer extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('THEN', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + const Text('THEN', + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 16), state.isLoading && state.isUpdate == true ? const Center( @@ -39,12 +41,17 @@ class ThenContainer extends StatelessWidget { state.thenItems.length, (index) => GestureDetector( onTap: () async { - if (state.thenItems[index]['deviceId'] == 'delay') { - final result = await DelayHelper.showDelayPickerDialog( - context, state.thenItems[index]); + if (state.thenItems[index] + ['deviceId'] == + 'delay') { + final result = await DelayHelper + .showDelayPickerDialog(context, + state.thenItems[index]); if (result != null) { - context.read().add(AddToThenContainer({ + context + .read() + .add(AddToThenContainer({ ...state.thenItems[index], 'imagePath': Assets.delay, 'title': 'Delay', @@ -53,58 +60,86 @@ class ThenContainer extends StatelessWidget { return; } - if (state.thenItems[index]['type'] == 'automation') { + if (state.thenItems[index]['type'] == + 'automation') { final result = await showDialog( context: context, - builder: (BuildContext context) => AutomationDialog( + builder: (BuildContext context) => + AutomationDialog( automationName: - state.thenItems[index]['name'] ?? 'Automation', + state.thenItems[index] + ['name'] ?? + 'Automation', automationId: - state.thenItems[index]['deviceId'] ?? '', - uniqueCustomId: state.thenItems[index] - ['uniqueCustomId'], + state.thenItems[index] + ['deviceId'] ?? + '', + uniqueCustomId: + state.thenItems[index] + ['uniqueCustomId'], ), ); if (result != null) { - context.read().add(AddToThenContainer({ + context + .read() + .add(AddToThenContainer({ ...state.thenItems[index], - 'imagePath': Assets.automation, - 'title': state.thenItems[index]['name'] ?? - state.thenItems[index]['title'], + 'imagePath': + Assets.automation, + 'title': + state.thenItems[index] + ['name'] ?? + state.thenItems[index] + ['title'], })); } return; } - final result = await DeviceDialogHelper.showDeviceDialog( - context, state.thenItems[index], - removeComparetors: true); + final result = await DeviceDialogHelper + .showDeviceDialog( + context: context, + data: state.thenItems[index], + removeComparetors: true, + dialogType: "THEN"); if (result != null) { - context - .read() - .add(AddToThenContainer(state.thenItems[index])); - } else if (!['AC', '1G', '2G', '3G'] - .contains(state.thenItems[index]['productType'])) { - context - .read() - .add(AddToThenContainer(state.thenItems[index])); + context.read().add( + AddToThenContainer( + state.thenItems[index])); + } else if (![ + 'AC', + '1G', + '2G', + '3G', + 'WPS' + ].contains(state.thenItems[index] + ['productType'])) { + context.read().add( + AddToThenContainer( + state.thenItems[index])); } }, child: DraggableCard( - imagePath: state.thenItems[index]['imagePath'] ?? '', - title: state.thenItems[index]['title'] ?? '', + imagePath: state.thenItems[index] + ['imagePath'] ?? + '', + title: state.thenItems[index] + ['title'] ?? + '', deviceData: state.thenItems[index], - padding: - const EdgeInsets.symmetric(horizontal: 4, vertical: 8), + padding: const EdgeInsets.symmetric( + horizontal: 4, vertical: 8), isFromThen: true, isFromIf: false, onRemove: () { - context.read().add(RemoveDragCard( - index: index, - isFromThen: true, - key: state.thenItems[index]['uniqueCustomId'])); + context.read().add( + RemoveDragCard( + index: index, + isFromThen: true, + key: state.thenItems[index] + ['uniqueCustomId'])); }, ), ))), @@ -114,6 +149,8 @@ class ThenContainer extends StatelessWidget { ); }, onAcceptWithDetails: (data) async { + print('data.data THEN=${data.data}'); + final uniqueCustomId = const Uuid().v4(); final mutableData = Map.from(data.data); mutableData['uniqueCustomId'] = uniqueCustomId; @@ -129,8 +166,8 @@ class ThenContainer extends StatelessWidget { } if (mutableData['type'] == 'automation') { - int index = - state.thenItems.indexWhere((item) => item['deviceId'] == mutableData['deviceId']); + int index = state.thenItems.indexWhere( + (item) => item['deviceId'] == mutableData['deviceId']); if (index != -1) { return; } @@ -155,8 +192,8 @@ class ThenContainer extends StatelessWidget { } if (mutableData['type'] == 'tap_to_run' && state.isAutomation) { - int index = - state.thenItems.indexWhere((item) => item['deviceId'] == mutableData['deviceId']); + int index = state.thenItems.indexWhere( + (item) => item['deviceId'] == mutableData['deviceId']); if (index != -1) { return; } @@ -174,7 +211,8 @@ class ThenContainer extends StatelessWidget { } if (mutableData['deviceId'] == 'delay') { - final result = await DelayHelper.showDelayPickerDialog(context, mutableData); + final result = + await DelayHelper.showDelayPickerDialog(context, mutableData); if (result != null) { context.read().add(AddToThenContainer({ @@ -186,11 +224,15 @@ class ThenContainer extends StatelessWidget { return; } - final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData, - removeComparetors: true); + final result = await DeviceDialogHelper.showDeviceDialog( + context: context, + data: mutableData, + removeComparetors: true, + dialogType: "THEN"); if (result != null) { context.read().add(AddToThenContainer(mutableData)); - } else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) { + } else if (!['AC', '1G', '2G', '3G', 'WPS'] + .contains(mutableData['productType'])) { context.read().add(AddToThenContainer(mutableData)); } }, diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 0e7b0bd2..01359f57 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -411,4 +411,21 @@ class Assets { static const String scenesPlayIcon = 'assets/icons/scenesPlayIcon.svg'; static const String scenesPlayIconCheck = 'assets/icons/scenesPlayIconCheck.svg'; + static const String presenceStateIcon = 'assets/icons/presence_state.svg'; + static const String currentDistanceIcon = + 'assets/icons/current_distance_icon.svg'; + + static const String farDetectionIcon = 'assets/icons/far_detection_icon.svg'; + static const String motionDetectionSensitivityIcon = + 'assets/icons/motion_detection_sensitivity_icon.svg'; + + static const String motionlessDetectionSensitivityIcon = + 'assets/icons/motionless_detection_sensitivity_icon.svg'; + + static const String IndicatorIcon = 'assets/icons/Indicator_icon.svg'; + static const String motionDetectionSensitivityValueIcon = 'assets/icons/motion_detection_sensitivity_value_icon.svg'; + static const String presenceTimeIcon = 'assets/icons/presence_time_icon.svg'; + static const String IlluminanceIcon = 'assets/icons/Illuminance_icon.svg'; + + //Illuminance_icon }