From 506068474b5a1cdadd639a52d232d50d6a69cdd0 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Sun, 21 Apr 2024 16:13:51 +0300 Subject: [PATCH 1/5] Add FunctionType enum and ValueModel class - Added FunctionType enum with values Boolean, Enum, Integer, Raw, String. - Introduced ValueModel class to handle unit, min, max, scale, and step values for functions in FunctionModel. --- .../devices/model/function_model.dart | 44 +++++- lib/utils/resource_manager/constants.dart | 130 +++++++++++++----- 2 files changed, 137 insertions(+), 37 deletions(-) diff --git a/lib/features/devices/model/function_model.dart b/lib/features/devices/model/function_model.dart index b3d04d2..82391ec 100644 --- a/lib/features/devices/model/function_model.dart +++ b/lib/features/devices/model/function_model.dart @@ -1,7 +1,9 @@ +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + class FunctionModel { String? code; - String? type; - String? values; + FunctionType? type; + ValueModel? values; FunctionModel({ required this.code, @@ -25,3 +27,41 @@ class FunctionModel { }; } } + +//"values": "{\"unit\":\"\",\"min\":1,\"max\":10,\"scale\":0,\"step\":1}", + +class ValueModel { + String? unit; + int? min; + int? max; + int? scale; + int? step; + + ValueModel({ + required this.unit, + required this.min, + required this.max, + required this.scale, + required this.step, + }); + + factory ValueModel.fromJson(Map json) { + return ValueModel( + unit: json['unit'], + min: json['min'], + max: json['max'], + scale: json['scale'], + step: json['step'], + ); + } + + Map toJson() { + return { + 'unit': unit, + 'min': min, + 'max': max, + 'scale': scale, + 'step': step, + }; + } +} diff --git a/lib/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index 213b6b1..e1748dd 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -50,7 +50,15 @@ enum DeviceType { // DL awu7anehyu5q1iu8 // WPS awarhusb // 3G 1a6vgvyi +enum FunctionType { Boolean, Enum, Integer, Raw, String } +Map functionTypesMap = { + "Boolean": FunctionType.Boolean, + "Enum": FunctionType.Enum, + "Integer": FunctionType.Integer, + "Raw": FunctionType.Raw, + "String": FunctionType.String, +}; Map devicesTypesMap = { "wzdcrqh0": DeviceType.AC, "wp8ticoo2bhumwgb": DeviceType.Gateway, @@ -61,75 +69,127 @@ Map devicesTypesMap = { }; Map> devicesFunctionsMap = { DeviceType.AC: [ - FunctionModel(code: 'switch', type: 'Boolean', values: '{}'), FunctionModel( - code: 'mode', type: 'Enum', values: '{"range":["cold","hot","wind"]}'), + code: 'switch', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'mode', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ["cold", "hot", "wind"] + })), FunctionModel( code: 'temp_set', - type: 'Integer', - values: '{"unit":"℃","min":200,"max":300,"scale":1,"step":5}'), + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "℃", "min": 200, "max": 300, "scale": 1, "step": 5})), FunctionModel( code: 'level', - type: 'Enum', - values: '{"range":["low","middle","high","auto"]}'), - FunctionModel(code: 'child_lock', type: 'Boolean', values: '{}'), + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ["low", "middle", "high", "auto"] + })), + FunctionModel( + code: 'child_lock', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), ], DeviceType.Gateway: [ - FunctionModel(code: 'switch_alarm_sound', type: 'Boolean', values: '{}'), + FunctionModel( + code: 'switch_alarm_sound', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'master_state', - type: 'Enum', - values: '{"range":["normal","alarm"]}'), - FunctionModel(code: 'factory_reset', type: 'Boolean', values: '{}'), + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ["normal", "alarm"] + })), FunctionModel( - code: 'alarm_active', type: 'String', values: '{"maxlen":255}'), + code: 'factory_reset', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'alarm_active', + type: functionTypesMap['String'], + values: ValueModel.fromJson({"maxlen": 255})), ], DeviceType.CeilingSensor: [ FunctionModel( code: 'sensitivity', - type: 'Integer', - values: '{"unit":"","min":1,"max":10,"scale":0,"step":1}'), + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "", "min": 1, "max": 10, "scale": 0, "step": 1})), ], DeviceType.DoorLock: [ - FunctionModel(code: 'remote_no_pd_setkey', type: 'Raw', values: '{}'), - FunctionModel(code: 'remote_no_dp_key', type: 'Raw', values: '{}'), - FunctionModel(code: 'normal_open_switch', type: 'Boolean', values: '{}'), + FunctionModel( + code: 'remote_no_pd_setkey', + type: functionTypesMap['Raw'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'remote_no_dp_key', + type: functionTypesMap['Raw'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'normal_open_switch', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), ], DeviceType.WallSensor: [ FunctionModel( code: 'far_detection', - type: 'Integer', - values: '{"unit":"cm","min":75,"max":600,"scale":0,"step":75}'), + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "cm", "min": 75, "max": 600, "scale": 0, "step": 75})), FunctionModel( code: 'presence_time', - type: 'Integer', - values: '{"unit":"Min","min":0,"max":65535,"scale":0,"step":1}'), + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "Min", "min": 0, "max": 65535, "scale": 0, "step": 1})), FunctionModel( code: 'motion_sensitivity_value', - type: 'Integer', - values: '{"unit":"","min":1,"max":5,"scale":0,"step":1}'), + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})), FunctionModel( code: 'motionless_sensitivity', - type: 'Integer', - values: '{"unit":"","min":1,"max":5,"scale":0,"step":1}'), - FunctionModel(code: 'indicator', type: 'Boolean', values: '{}'), + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})), + FunctionModel( + code: 'indicator', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), ], DeviceType.ThreeGang: [ - FunctionModel(code: 'switch_1', type: 'Boolean', values: '{}'), - FunctionModel(code: 'switch_2', type: 'Boolean', values: '{}'), - FunctionModel(code: 'switch_3', type: 'Boolean', values: '{}'), + FunctionModel( + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'switch_2', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'switch_3', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', - type: 'Integer', - values: '{"unit":"s","min":0,"max":43200,"scale":0,"step":1}'), + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_2', - type: 'Integer', - values: '{"unit":"s","min":0,"max":43200,"scale":0,"step":1}'), + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_3', - type: 'Integer', - values: '{"unit":"s","min":0,"max":43200,"scale":0,"step":1}'), + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), ], }; From 2eeee63e1dbe33363b6d81b7e190519e72e87c83 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Mon, 22 Apr 2024 09:14:02 +0300 Subject: [PATCH 2/5] Update presence sensor interface and add parameter dialog - Add parameter dialog for adjusting sensitivity in presence sensor interface. - Implement toTitleCase helper method in StringHelpers class. - Update UI elements in the presence sensor interface for better user interaction. --- .../ceiling_sensor_interface.dart | 229 +++++++++++++++++- .../devices/view/widgets/wizard_switches.dart | 5 +- lib/utils/helpers/misc_string_helpers.dart | 7 + 3 files changed, 235 insertions(+), 6 deletions(-) diff --git a/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart b/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart index 575cbee..40c7ac6 100644 --- a/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart +++ b/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart @@ -3,13 +3,17 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/helpers/misc_string_helpers.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; @@ -58,13 +62,58 @@ class CeilingSensorInterface extends StatelessWidget { Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - SvgPicture.asset( - Assets.presenceSensorAssetsPresenceSensorMotion, - width: 100, - height: 100, + InkWell( + onTap: () { + if ((ceilingSensor.isOnline ?? false) == false) { + return; + } + showDialog( + context: context, + builder: (context) => ParameterDialog( + title: 'Sensitivity', + sensor: ceilingSensor, + value: ceilingSensor.status + .firstWhere((element) => + element.code == 'sensitivity') + .value as int, + min: ceilingSensor.functions + .firstWhere((element) => + element.code == 'sensitivity') + .values + ?.min ?? + 0, + max: ceilingSensor.functions + .firstWhere((element) => + element.code == 'sensitivity') + .values + ?.max ?? + 0, + ), + ); + }, + child: SvgPicture.asset( + Assets.presenceSensorAssetsPresenceSensorMotion, + width: 100, + height: 100, + colorFilter: ColorFilter.mode( + (ceilingSensor.isOnline ?? false) + ? ColorsManager.primaryColor + : Colors.grey.withOpacity(0.9), + BlendMode.srcIn, + ), + ), + ), + const SizedBox( + height: 10, ), BodyMedium( - text: 'Motion', + text: (ceilingSensor.isOnline ?? false) + ? StringHelpers.toTitleCase(ceilingSensor.status + .firstWhere((element) => + element.code == 'presence_state') + .value + .toString()) + : "Offline", style: context.bodyMedium.copyWith( fontWeight: FontsManager.bold, ), @@ -209,3 +258,173 @@ var ceilingSensorButtons = [ 'page': null, }, ]; + +class ParameterDialog extends StatefulWidget { + final String title; + final DeviceModel sensor; + final int value; + final int min; + final int max; + + const ParameterDialog({ + super.key, + required this.title, + required this.sensor, + required this.value, + required this.min, + required this.max, + }); + + @override + _ParameterDialogState createState() => _ParameterDialogState(); +} + +class _ParameterDialogState extends State { + late int _value; + + @override + void initState() { + super.initState(); + _value = widget.value; + } + + @override + Widget build(BuildContext context) { + return Dialog( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.only(top: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + BodyMedium( + text: widget.title, + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity, + fontWeight: FontsManager.extraBold, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 15, + horizontal: 50, + ), + child: Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, + ), + child: TitleMedium( + text: _value.toString(), + style: context.titleMedium.copyWith( + color: Colors.black, + fontWeight: FontsManager.bold, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + onPressed: () { + setState(() { + _value = (_value - 1).clamp(widget.min, widget.max); + }); + }, + icon: const Icon( + Icons.remove, + color: Colors.grey, + ), + ), + Slider( + value: _value.toDouble(), + onChanged: (value) { + setState(() { + _value = value.toInt(); + }); + }, + min: widget.min.toDouble(), + max: widget.max.toDouble(), + label: _value.toString(), + inactiveColor: ColorsManager.greyColor, + ), + IconButton( + onPressed: () { + setState(() { + _value = (_value + 1).clamp(widget.min, widget.max); + }); + }, + icon: const Icon( + Icons.add, + color: Colors.grey, + ), + ), + ], + ), + Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Center( + child: BodyMedium( + text: 'Cancel', + style: context.bodyMedium + .copyWith(color: ColorsManager.greyColor), + ), + ), + ), + Container( + height: 50, + width: 1, + color: ColorsManager.greyColor, + ), + InkWell( + onTap: () { + Navigator.pop(context, _value); + if (widget.sensor.isOnline == null) { + return; + } + if (!widget.sensor.isOnline!) { + debugPrint('Device is offline'); + return; + } + + DevicesCubit.getInstance().deviceControl( + DeviceControlModel( + deviceId: widget.sensor.id, + code: 'sensitivity', + value: widget.value), + widget.sensor.id ?? ''); + }, + child: Center( + child: BodyMedium( + text: 'Confirm', + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity), + ), + ), + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/wizard_switches.dart b/lib/features/devices/view/widgets/wizard_switches.dart index a00d19a..7ff490c 100644 --- a/lib/features/devices/view/widgets/wizard_switches.dart +++ b/lib/features/devices/view/widgets/wizard_switches.dart @@ -35,7 +35,10 @@ class WizartSwitches extends StatelessWidget { onTap: () { // DevicesCubit.getInstance().selectCategory(index); //Navigate to the chosen category view without animation - + if (DevicesCubit.getInstance().chosenCategoryView == + null) { + return; + } Navigator.push(context, CustomPageRoute(builder: (context) { return DevicesCubit.getInstance() diff --git a/lib/utils/helpers/misc_string_helpers.dart b/lib/utils/helpers/misc_string_helpers.dart index d9f9dc3..b0b71aa 100644 --- a/lib/utils/helpers/misc_string_helpers.dart +++ b/lib/utils/helpers/misc_string_helpers.dart @@ -61,4 +61,11 @@ class StringHelpers { return 0; } } + + static String toTitleCase(String text) { + return text + .split(' ') + .map((word) => word[0].toUpperCase() + word.substring(1)) + .join(' '); + } } From 072a043aaa1f8334ec8d5c5c26cc27de693ef099 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Mon, 22 Apr 2024 10:13:40 +0300 Subject: [PATCH 3/5] Moved wall sensor files --- .../presence_sensor/parameters_list.dart | 349 ------------------ .../presence_sensors/parameters_list.dart | 324 ++++++++++++++++ .../presence_indicator.dart | 0 .../wall_sensor_interface.dart | 3 + 4 files changed, 327 insertions(+), 349 deletions(-) delete mode 100644 lib/features/devices/view/widgets/presence_sensor/parameters_list.dart create mode 100644 lib/features/devices/view/widgets/presence_sensors/parameters_list.dart rename lib/features/devices/view/widgets/{presence_sensor => presence_sensors}/presence_indicator.dart (100%) rename lib/features/devices/view/widgets/{presence_sensor => presence_sensors}/wall_sensor_interface.dart (93%) diff --git a/lib/features/devices/view/widgets/presence_sensor/parameters_list.dart b/lib/features/devices/view/widgets/presence_sensor/parameters_list.dart deleted file mode 100644 index 14b5203..0000000 --- a/lib/features/devices/view/widgets/presence_sensor/parameters_list.dart +++ /dev/null @@ -1,349 +0,0 @@ -part of "wall_sensor_interface.dart"; - -class ParametersList extends StatelessWidget { - const ParametersList({ - super.key, - required this.wallSensor, - }); - - final DeviceModel wallSensor; - @override - Widget build(BuildContext context) { - return Expanded( - flex: 7, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisSize: MainAxisSize.min, - children: List.generate( - wallSensorButtons.length, - (index) { - if (index == 3) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - DefaultContainer( - padding: const EdgeInsets.symmetric( - vertical: 12, horizontal: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - InkWell( - onTap: () { - showParameterDialog( - context, - 'Motion Detection Sensitivity', - wallSensor, - 5, - 0, - 10, - ); - }, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.min, - children: [ - const BodySmall( - text: 'Motion\nDetectionn\nSensitivity', - textAlign: TextAlign.center), - BodyLarge( - text: '5', - style: context.bodyLarge - .copyWith(fontWeight: FontsManager.bold), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Container( - width: 1, - height: 45, - color: ColorsManager.greyColor, - ), - ), - Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const BodySmall( - text: 'Motionless\nDetection\nSensitivity', - textAlign: TextAlign.center), - BodyLarge( - text: '5', - style: context.bodyLarge.copyWith( - fontWeight: FontsManager.bold, - ), - ), - ], - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Container( - width: 1, - height: 45, - color: ColorsManager.greyColor, - ), - ), - Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const BodySmall( - text: 'Far\nDetection', - textAlign: TextAlign.center), - BodyLarge( - text: '600cm', - style: context.bodyLarge.copyWith( - fontWeight: FontsManager.bold, - ), - ), - ], - ), - ], - ), - ), - listItem(wallSensorButtons[index], context, wallSensor) - ], - ); - } - return listItem(wallSensorButtons[index], context, wallSensor); - }, - ), - ), - ), - ); - } - - final List> wallSensorButtons = const [ - { - 'icon': Assets.presenceSensorAssetsTime, - 'title': 'Presence Time', - 'value': 0, - 'unit': 'min', - 'page': null, - }, - { - 'icon': Assets.presenceSensorAssetsDistance, - 'title': 'Current Distance', - 'value': 279, - 'unit': 'cm', - 'dialog': null, - }, - { - 'icon': Assets.presenceSensorAssetsIlluminanceValue, - 'title': 'Illuminance Value', - 'value': 0, - 'unit': 'Lux', - 'page': null, - }, - { - 'icon': Assets.presenceSensorAssetsEmpty, - 'title': 'Nobody Time', - 'value': 10, - 'unit': 'sec', - 'dialog': null, - }, - { - 'icon': Assets.presenceSensorAssetsIndicator, - 'title': 'Indicator', - 'page': null, - }, - { - 'icon': Assets.presenceSensorAssetsRecord, - 'title': 'Presence Record', - 'page': null, - }, - { - 'icon': Assets.presenceSensorAssetsIlluminanceRecord, - 'title': 'Illuminance Record', - 'page': null, - }, - ]; -} - -Widget listItem( - Map wallSensorButton, - BuildContext context, - DeviceModel wallSensor, -) { - return DefaultContainer( - margin: const EdgeInsets.only(bottom: 5), - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20), - onTap: () { - if (wallSensorButton['page'] != null) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => wallSensorButton['page'] as Widget, - ), - ); - } - }, - child: Row( - children: [ - SvgPicture.asset( - wallSensorButton['icon'] as String, - ), - const SizedBox( - width: 25, - ), - BodyMedium(text: wallSensorButton['title'] as String), - if (wallSensorButton['value'] != null) const Spacer(), - if (wallSensorButton['value'] != null) - BodyMedium( - text: '${wallSensorButton['value']}${wallSensorButton['unit']}', - style: context.bodyMedium.copyWith(color: ColorsManager.greyColor), - ), - if (wallSensorButton['value'] != null) - if (wallSensorButton['title'] == 'Indicator') - Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Transform.scale( - scale: .8, - child: CupertinoSwitch( - value: false, - onChanged: (value) {}, - applyTheme: true, - )), - ], - ), - ), - // CustomSwitch(device: wallSensor), - if (wallSensorButton['title'] == 'Nobody Time') - const Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Icon( - Icons.arrow_forward_ios, - color: ColorsManager.greyColor, - size: 15, - ), - ], - ), - ], - ), - ); -} - -showParameterDialog( - BuildContext context, - String title, - DeviceModel wallSensor, - int value, - int min, - int max, -) { - showDialog( - context: context, - builder: (context) => Dialog( - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(20), - ), - padding: const EdgeInsets.only(top: 20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - BodyMedium( - text: title, - style: context.bodyMedium.copyWith( - color: ColorsManager.primaryColorWithOpacity, - fontWeight: FontsManager.extraBold, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 15, - horizontal: 50, - ), - child: Container( - height: 1, - width: double.infinity, - color: ColorsManager.greyColor, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 10, - ), - child: TitleMedium( - text: value.toString(), - style: context.titleMedium.copyWith( - color: Colors.black, - fontWeight: FontsManager.bold, - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // Icon(Icons.minus), - IconButton( - onPressed: () { - value--; - }, - icon: const Icon( - Icons.remove, - color: Colors.grey, - ), - ), - Slider( - value: value.toDouble(), - onChanged: (value) { - // value = value; - }, - min: 0, - max: 10, - label: value.toString(), - inactiveColor: ColorsManager.greyColor, - ), - IconButton( - onPressed: () { - value++; - }, - icon: const Icon( - Icons.add, - color: Colors.grey, - ), - ), - ], - ), - Container( - height: 1, - width: double.infinity, - color: ColorsManager.greyColor, - ), - Row( - children: [ - Expanded( - child: Center( - child: BodyMedium( - text: 'Cancel', - style: context.bodyMedium - .copyWith(color: ColorsManager.greyColor), - ), - )), - Container( - height: 50, - width: 1, - color: ColorsManager.greyColor, - ), - Expanded( - child: Center( - child: BodyMedium( - text: 'Confirm', - style: context.bodyMedium.copyWith( - color: ColorsManager.primaryColorWithOpacity), - ), - )), - ], - ) - ], - ), - ), - )); -} diff --git a/lib/features/devices/view/widgets/presence_sensors/parameters_list.dart b/lib/features/devices/view/widgets/presence_sensors/parameters_list.dart new file mode 100644 index 0000000..621ec5b --- /dev/null +++ b/lib/features/devices/view/widgets/presence_sensors/parameters_list.dart @@ -0,0 +1,324 @@ +part of "wall_sensor_interface.dart"; + +class ParametersList extends StatelessWidget { + const ParametersList({ + super.key, + required this.wallSensor, + }); + + final DeviceModel wallSensor; + @override + Widget build(BuildContext context) { + return Expanded( + flex: 7, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: List.generate( + wallSensorButtons.length, + (index) { + if (index == 3) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + DefaultContainer( + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + InkWell( + onTap: () { + if ((wallSensor.isOnline ?? false) == false) { + return; + } + String controlCode = 'motion_sensitivity_value'; + showDialog( + context: context, + builder: (context) => ParameterControlDialog( + title: 'Motion Detection Sensitivity', + sensor: wallSensor, + controlCode: controlCode, + value: wallSensor.status + .firstWhere((element) => + element.code == controlCode) + .value as int, + min: wallSensor.functions + .firstWhere((element) => + element.code == controlCode) + .values + ?.min ?? + 0, + max: wallSensor.functions + .firstWhere((element) => + element.code == controlCode) + .values + ?.max ?? + 0, + ), + ); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + const BodySmall( + text: 'Motion\nDetection\nSensitivity', + textAlign: TextAlign.center), + BodyLarge( + text: wallSensor.status + .firstWhere((element) => + element.code == + 'motion_sensitivity_value') + .value + .toString(), + style: context.bodyLarge + .copyWith(fontWeight: FontsManager.bold), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Container( + width: 1, + height: 45, + color: ColorsManager.greyColor, + ), + ), + InkWell( + onTap: () { + if ((wallSensor.isOnline ?? false) == false) { + return; + } + String controlCode = 'motionless_sensitivity'; + showDialog( + context: context, + builder: (context) => ParameterControlDialog( + title: 'Motionless Detection Sensitivity', + sensor: wallSensor, + controlCode: controlCode, + value: wallSensor.status + .firstWhere((element) => + element.code == controlCode) + .value as int, + min: wallSensor.functions + .firstWhere((element) => + element.code == controlCode) + .values + ?.min ?? + 0, + max: wallSensor.functions + .firstWhere((element) => + element.code == controlCode) + .values + ?.max ?? + 0, + ), + ); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const BodySmall( + text: 'Motionless\nDetection\nSensitivity', + textAlign: TextAlign.center), + BodyLarge( + text: wallSensor.status + .firstWhere((element) => + element.code == + 'motionless_sensitivity') + .value + .toString(), + style: context.bodyLarge.copyWith( + fontWeight: FontsManager.bold, + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Container( + width: 1, + height: 45, + color: ColorsManager.greyColor, + ), + ), + InkWell( + onTap: () { + if ((wallSensor.isOnline ?? false) == false) { + return; + } + String controlCode = 'far_detection'; + showDialog( + context: context, + builder: (context) => ParameterControlDialog( + title: 'Far Detection', + sensor: wallSensor, + controlCode: controlCode, + value: wallSensor.status + .firstWhere((element) => + element.code == controlCode) + .value as int, + min: wallSensor.functions + .firstWhere((element) => + element.code == controlCode) + .values + ?.min ?? + 0, + max: wallSensor.functions + .firstWhere((element) => + element.code == controlCode) + .values + ?.max ?? + 0, + ), + ); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const BodySmall( + text: 'Far\nDetection', + textAlign: TextAlign.center), + BodyLarge( + text: + '${wallSensor.status.firstWhere((element) => element.code == 'far_detection').value.toString()}${wallSensor.functions.firstWhere((element) => element.code == 'far_detection').values?.unit ?? ''}', + style: context.bodyLarge.copyWith( + fontWeight: FontsManager.bold, + ), + ), + ], + ), + ), + ], + ), + ), + listItem(wallSensorButtons[index], context, wallSensor) + ], + ); + } + return listItem(wallSensorButtons[index], context, wallSensor); + }, + ), + ), + ), + ); + } + + final List> wallSensorButtons = const [ + { + 'icon': Assets.presenceSensorAssetsTime, + 'title': 'Presence Time', + 'value': 0, + 'unit': 'min', + 'page': null, + }, + { + 'icon': Assets.presenceSensorAssetsDistance, + 'title': 'Current Distance', + 'value': 279, + 'unit': 'cm', + 'dialog': null, + }, + { + 'icon': Assets.presenceSensorAssetsIlluminanceValue, + 'title': 'Illuminance Value', + 'value': 0, + 'unit': 'Lux', + 'page': null, + }, + { + 'icon': Assets.presenceSensorAssetsEmpty, + 'title': 'Nobody Time', + 'value': 10, + 'unit': 'sec', + 'dialog': null, + }, + { + 'icon': Assets.presenceSensorAssetsIndicator, + 'title': 'Indicator', + 'page': null, + }, + { + 'icon': Assets.presenceSensorAssetsRecord, + 'title': 'Presence Record', + 'page': null, + }, + { + 'icon': Assets.presenceSensorAssetsIlluminanceRecord, + 'title': 'Illuminance Record', + 'page': null, + }, + ]; +} + +Widget listItem( + Map wallSensorButton, + BuildContext context, + DeviceModel wallSensor, +) { + return DefaultContainer( + margin: const EdgeInsets.only(bottom: 5), + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20), + onTap: () { + if (wallSensorButton['page'] != null) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => wallSensorButton['page'] as Widget, + ), + ); + } + }, + child: Row( + children: [ + SvgPicture.asset( + wallSensorButton['icon'] as String, + ), + const SizedBox( + width: 25, + ), + BodyMedium(text: wallSensorButton['title'] as String), + if (wallSensorButton['value'] != null) const Spacer(), + if (wallSensorButton['value'] != null) + BodyMedium( + text: '${wallSensorButton['value']}${wallSensorButton['unit']}', + style: context.bodyMedium.copyWith(color: ColorsManager.greyColor), + ), + if (wallSensorButton['value'] != null) + if (wallSensorButton['title'] == 'Indicator') + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: false, + onChanged: (value) {}, + applyTheme: true, + )), + ], + ), + ), + // CustomSwitch(device: wallSensor), + if (wallSensorButton['title'] == 'Nobody Time') + const Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 15, + ), + ], + ), + ], + ), + ); +} diff --git a/lib/features/devices/view/widgets/presence_sensor/presence_indicator.dart b/lib/features/devices/view/widgets/presence_sensors/presence_indicator.dart similarity index 100% rename from lib/features/devices/view/widgets/presence_sensor/presence_indicator.dart rename to lib/features/devices/view/widgets/presence_sensors/presence_indicator.dart diff --git a/lib/features/devices/view/widgets/presence_sensor/wall_sensor_interface.dart b/lib/features/devices/view/widgets/presence_sensors/wall_sensor_interface.dart similarity index 93% rename from lib/features/devices/view/widgets/presence_sensor/wall_sensor_interface.dart rename to lib/features/devices/view/widgets/presence_sensors/wall_sensor_interface.dart index 50bb721..a9d6d0d 100644 --- a/lib/features/devices/view/widgets/presence_sensor/wall_sensor_interface.dart +++ b/lib/features/devices/view/widgets/presence_sensors/wall_sensor_interface.dart @@ -2,6 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; @@ -16,6 +18,7 @@ import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; part "parameters_list.dart"; part "presence_indicator.dart"; +part "parameter_control_dialog.dart"; class WallMountedInterface extends StatelessWidget { const WallMountedInterface({super.key, required this.wallSensor}); From 00e3c13d03967589d289e25d068a7531ba8db62d Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Mon, 22 Apr 2024 10:14:11 +0300 Subject: [PATCH 4/5] Add ParameterControlDialog for managing sensor parameters This commit adds a new ParameterControlDialog widget to manage sensor parameters in the presence sensors feature. The dialog allows users to adjust sensitivity settings for ceiling sensors. --- .../ceiling_sensor_interface.dart | 189 +----------------- .../parameter_control_dialog.dart | 173 ++++++++++++++++ 2 files changed, 182 insertions(+), 180 deletions(-) create mode 100644 lib/features/devices/view/widgets/presence_sensors/parameter_control_dialog.dart diff --git a/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart b/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart index 40c7ac6..590c234 100644 --- a/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart +++ b/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart @@ -1,16 +1,11 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; -import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; -import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/helpers/misc_string_helpers.dart'; @@ -18,6 +13,8 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; +import 'wall_sensor_interface.dart'; + class CeilingSensorInterface extends StatelessWidget { const CeilingSensorInterface({super.key, required this.ceilingSensor}); @@ -67,24 +64,26 @@ class CeilingSensorInterface extends StatelessWidget { if ((ceilingSensor.isOnline ?? false) == false) { return; } + String controlCode = 'sensitivity'; showDialog( context: context, - builder: (context) => ParameterDialog( + builder: (context) => ParameterControlDialog( title: 'Sensitivity', sensor: ceilingSensor, + controlCode: controlCode, value: ceilingSensor.status - .firstWhere((element) => - element.code == 'sensitivity') + .firstWhere( + (element) => element.code == controlCode) .value as int, min: ceilingSensor.functions .firstWhere((element) => - element.code == 'sensitivity') + element.code == controlCode) .values ?.min ?? 0, max: ceilingSensor.functions .firstWhere((element) => - element.code == 'sensitivity') + element.code == controlCode) .values ?.max ?? 0, @@ -258,173 +257,3 @@ var ceilingSensorButtons = [ 'page': null, }, ]; - -class ParameterDialog extends StatefulWidget { - final String title; - final DeviceModel sensor; - final int value; - final int min; - final int max; - - const ParameterDialog({ - super.key, - required this.title, - required this.sensor, - required this.value, - required this.min, - required this.max, - }); - - @override - _ParameterDialogState createState() => _ParameterDialogState(); -} - -class _ParameterDialogState extends State { - late int _value; - - @override - void initState() { - super.initState(); - _value = widget.value; - } - - @override - Widget build(BuildContext context) { - return Dialog( - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(20), - ), - padding: const EdgeInsets.only(top: 20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - BodyMedium( - text: widget.title, - style: context.bodyMedium.copyWith( - color: ColorsManager.primaryColorWithOpacity, - fontWeight: FontsManager.extraBold, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 15, - horizontal: 50, - ), - child: Container( - height: 1, - width: double.infinity, - color: ColorsManager.greyColor, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 10, - ), - child: TitleMedium( - text: _value.toString(), - style: context.titleMedium.copyWith( - color: Colors.black, - fontWeight: FontsManager.bold, - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - onPressed: () { - setState(() { - _value = (_value - 1).clamp(widget.min, widget.max); - }); - }, - icon: const Icon( - Icons.remove, - color: Colors.grey, - ), - ), - Slider( - value: _value.toDouble(), - onChanged: (value) { - setState(() { - _value = value.toInt(); - }); - }, - min: widget.min.toDouble(), - max: widget.max.toDouble(), - label: _value.toString(), - inactiveColor: ColorsManager.greyColor, - ), - IconButton( - onPressed: () { - setState(() { - _value = (_value + 1).clamp(widget.min, widget.max); - }); - }, - icon: const Icon( - Icons.add, - color: Colors.grey, - ), - ), - ], - ), - Container( - height: 1, - width: double.infinity, - color: ColorsManager.greyColor, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - InkWell( - onTap: () { - Navigator.pop(context); - }, - child: Center( - child: BodyMedium( - text: 'Cancel', - style: context.bodyMedium - .copyWith(color: ColorsManager.greyColor), - ), - ), - ), - Container( - height: 50, - width: 1, - color: ColorsManager.greyColor, - ), - InkWell( - onTap: () { - Navigator.pop(context, _value); - if (widget.sensor.isOnline == null) { - return; - } - if (!widget.sensor.isOnline!) { - debugPrint('Device is offline'); - return; - } - - DevicesCubit.getInstance().deviceControl( - DeviceControlModel( - deviceId: widget.sensor.id, - code: 'sensitivity', - value: widget.value), - widget.sensor.id ?? ''); - }, - child: Center( - child: BodyMedium( - text: 'Confirm', - style: context.bodyMedium.copyWith( - color: ColorsManager.primaryColorWithOpacity), - ), - ), - ), - ], - ) - ], - ), - ), - ); - } -} diff --git a/lib/features/devices/view/widgets/presence_sensors/parameter_control_dialog.dart b/lib/features/devices/view/widgets/presence_sensors/parameter_control_dialog.dart new file mode 100644 index 0000000..1d22095 --- /dev/null +++ b/lib/features/devices/view/widgets/presence_sensors/parameter_control_dialog.dart @@ -0,0 +1,173 @@ +part of 'wall_sensor_interface.dart'; + +class ParameterControlDialog extends StatefulWidget { + final String title; + final DeviceModel sensor; + final int value; + final int min; + final int max; + final String controlCode; + + const ParameterControlDialog({ + super.key, + required this.title, + required this.sensor, + required this.value, + required this.min, + required this.max, + required this.controlCode, + }); + + @override + ParameterControlDialogState createState() => ParameterControlDialogState(); +} + +class ParameterControlDialogState extends State { + late int _value; + + @override + void initState() { + super.initState(); + _value = widget.value; + } + + @override + Widget build(BuildContext context) { + return Dialog( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.only(top: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + BodyMedium( + text: widget.title, + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity, + fontWeight: FontsManager.extraBold, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 15, + horizontal: 50, + ), + child: Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, + ), + child: TitleMedium( + text: _value.toString(), + style: context.titleMedium.copyWith( + color: Colors.black, + fontWeight: FontsManager.bold, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + onPressed: () { + setState(() { + _value = (_value - 1).clamp(widget.min, widget.max); + }); + }, + icon: const Icon( + Icons.remove, + color: Colors.grey, + ), + ), + Slider( + value: _value.toDouble(), + onChanged: (value) { + setState(() { + _value = value.toInt(); + }); + }, + min: widget.min.toDouble(), + max: widget.max.toDouble(), + label: _value.toString(), + inactiveColor: ColorsManager.greyColor, + ), + IconButton( + onPressed: () { + setState(() { + _value = (_value + 1).clamp(widget.min, widget.max); + }); + }, + icon: const Icon( + Icons.add, + color: Colors.grey, + ), + ), + ], + ), + Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Center( + child: BodyMedium( + text: 'Cancel', + style: context.bodyMedium + .copyWith(color: ColorsManager.greyColor), + ), + ), + ), + Container( + height: 50, + width: 1, + color: ColorsManager.greyColor, + ), + InkWell( + onTap: () { + Navigator.pop(context, _value); + if (widget.sensor.isOnline == null) { + return; + } + if (!widget.sensor.isOnline!) { + debugPrint('Device is offline'); + return; + } + + DevicesCubit.getInstance().deviceControl( + DeviceControlModel( + deviceId: widget.sensor.id, + code: widget.controlCode, + value: widget.value), + widget.sensor.id ?? ''); + }, + child: Center( + child: BodyMedium( + text: 'Confirm', + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity), + ), + ), + ), + ], + ) + ], + ), + ), + ); + } +} From 54982ba777b20ecb53dce75e23feff9500e99c9c Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Mon, 22 Apr 2024 11:49:21 +0300 Subject: [PATCH 5/5] Configured ceiling presence sensor interface components configured CPS UI to check for the state and the connectivity of the device. reflected the data from the API to the UI --- .../ceiling_sensor_interface.dart | 43 ++++--- .../presence_sensors/parameters_list.dart | 110 ++++++++++++------ .../presence_sensors/presence_indicator.dart | 14 ++- .../wall_sensor_interface.dart | 5 +- .../view/widgets/room_page_switch.dart | 2 +- 5 files changed, 116 insertions(+), 58 deletions(-) diff --git a/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart b/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart index 590c234..238cdc3 100644 --- a/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart +++ b/lib/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart @@ -21,6 +21,10 @@ class CeilingSensorInterface extends StatelessWidget { final DeviceModel ceilingSensor; @override Widget build(BuildContext context) { + String state = ceilingSensor.status + .firstWhere((element) => element.code == "presence_state") + .value + .toString(); return AnnotatedRegion( value: SystemUiOverlayStyle( statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), @@ -62,6 +66,14 @@ class CeilingSensorInterface extends StatelessWidget { InkWell( onTap: () { if ((ceilingSensor.isOnline ?? false) == false) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Device is offline', + ), + backgroundColor: Colors.red, + ), + ); return; } String controlCode = 'sensitivity'; @@ -91,28 +103,31 @@ class CeilingSensorInterface extends StatelessWidget { ); }, child: SvgPicture.asset( - Assets.presenceSensorAssetsPresenceSensorMotion, + state == 'presence' + ? Assets.presenceSensorAssetsPresence + : Assets.presenceSensorAssetsPresenceSensorMotion, width: 100, height: 100, - colorFilter: ColorFilter.mode( - (ceilingSensor.isOnline ?? false) - ? ColorsManager.primaryColor - : Colors.grey.withOpacity(0.9), - BlendMode.srcIn, - ), + // colorFilter: ColorFilter.mode( + // (ceilingSensor.isOnline ?? false) + // ? ColorsManager.primaryColor + // : Colors.grey.withOpacity(0.9), + // BlendMode.srcIn, + // ), ), ), const SizedBox( height: 10, ), BodyMedium( - text: (ceilingSensor.isOnline ?? false) - ? StringHelpers.toTitleCase(ceilingSensor.status - .firstWhere((element) => - element.code == 'presence_state') - .value - .toString()) - : "Offline", + text: StringHelpers.toTitleCase(state), + // (ceilingSensor.isOnline ?? false) + // ? StringHelpers.toTitleCase(ceilingSensor.status + // .firstWhere((element) => + // element.code == 'presence_state') + // .value + // .toString()) + // : "Offline", style: context.bodyMedium.copyWith( fontWeight: FontsManager.bold, ), diff --git a/lib/features/devices/view/widgets/presence_sensors/parameters_list.dart b/lib/features/devices/view/widgets/presence_sensors/parameters_list.dart index 621ec5b..f155064 100644 --- a/lib/features/devices/view/widgets/presence_sensors/parameters_list.dart +++ b/lib/features/devices/view/widgets/presence_sensors/parameters_list.dart @@ -210,49 +210,43 @@ class ParametersList extends StatelessWidget { ); } +//{"result":{"productId":"awarhusb","productType":"WPS","status":[{"code":"presence_state","value":"none"},{"code":"far_detection","value":75},{"code":"presence_time","value":0},{"code":"motion_sensitivity_value","value":5},{"code":"motionless_sensitivity","value":5},{"code":"dis_current","value":214},{"code":"illuminance_value","value":231},{"code":"indicator","value":true}]},"success":true} final List> wallSensorButtons = const [ { 'icon': Assets.presenceSensorAssetsTime, 'title': 'Presence Time', - 'value': 0, - 'unit': 'min', - 'page': null, + 'code': 'presence_time', }, { 'icon': Assets.presenceSensorAssetsDistance, 'title': 'Current Distance', - 'value': 279, - 'unit': 'cm', - 'dialog': null, + 'code': 'dis_current', }, { 'icon': Assets.presenceSensorAssetsIlluminanceValue, 'title': 'Illuminance Value', - 'value': 0, - 'unit': 'Lux', - 'page': null, + 'code': 'illuminance_value', }, { 'icon': Assets.presenceSensorAssetsEmpty, 'title': 'Nobody Time', - 'value': 10, - 'unit': 'sec', - 'dialog': null, + 'code': null, + //TODO: Implement the nobody time }, { 'icon': Assets.presenceSensorAssetsIndicator, 'title': 'Indicator', - 'page': null, + 'code': 'indicator', }, { 'icon': Assets.presenceSensorAssetsRecord, 'title': 'Presence Record', - 'page': null, + 'code': null }, { 'icon': Assets.presenceSensorAssetsIlluminanceRecord, 'title': 'Illuminance Record', - 'page': null, + 'code': null }, ]; } @@ -262,6 +256,18 @@ Widget listItem( BuildContext context, DeviceModel wallSensor, ) { + String? unit; + dynamic value; + if (wallSensorButton['code'] != null) { + if (wallSensor.status + .any((element) => element.code == wallSensorButton['code'] as String)) { + unit = unitsMap[wallSensorButton['code'] as String]; + value = wallSensor.status + .firstWhere( + (element) => element.code == wallSensorButton['code'] as String) + .value; + } + } return DefaultContainer( margin: const EdgeInsets.only(bottom: 5), padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20), @@ -283,30 +289,49 @@ Widget listItem( const SizedBox( width: 25, ), - BodyMedium(text: wallSensorButton['title'] as String), - if (wallSensorButton['value'] != null) const Spacer(), - if (wallSensorButton['value'] != null) - BodyMedium( - text: '${wallSensorButton['value']}${wallSensorButton['unit']}', - style: context.bodyMedium.copyWith(color: ColorsManager.greyColor), - ), - if (wallSensorButton['value'] != null) - if (wallSensorButton['title'] == 'Indicator') - Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Transform.scale( - scale: .8, - child: CupertinoSwitch( - value: false, - onChanged: (value) {}, - applyTheme: true, - )), - ], - ), + BodyMedium( + text: wallSensorButton['title'] as String, + ), + if (wallSensorButton['code'] != null) const Spacer(), + if (wallSensorButton['code'] != null) + if (wallSensorButton['title'] != 'Indicator') + BodyMedium( + text: '${value ?? 'N/A'}${unit ?? ''}', + style: + context.bodyMedium.copyWith(color: ColorsManager.greyColor), ), - // CustomSwitch(device: wallSensor), + if (wallSensorButton['title'] == 'Indicator') + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: value ?? false, + onChanged: (value) { + if (wallSensor.isOnline ?? false) { + DevicesCubit.getInstance().deviceControl( + DeviceControlModel( + deviceId: wallSensor.id, + code: 'indicator', + value: value, + ), + wallSensor.id ?? ''); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Device is offline'), + backgroundColor: Colors.red, + ), + ); + } + }, + applyTheme: true, + )), + ], + ), + ), if (wallSensorButton['title'] == 'Nobody Time') const Row( mainAxisAlignment: MainAxisAlignment.end, @@ -322,3 +347,12 @@ Widget listItem( ), ); } + +Map unitsMap = { + 'presence_time': 's', + 'dis_current': 'cm', + 'illuminance_value': 'lux', + 'far_detection': 'cm', + 'motion_sensitivity_value': '', + 'motionless_sensitivity': '', +}; diff --git a/lib/features/devices/view/widgets/presence_sensors/presence_indicator.dart b/lib/features/devices/view/widgets/presence_sensors/presence_indicator.dart index 39c4d42..ad21528 100644 --- a/lib/features/devices/view/widgets/presence_sensors/presence_indicator.dart +++ b/lib/features/devices/view/widgets/presence_sensors/presence_indicator.dart @@ -1,17 +1,23 @@ part of "wall_sensor_interface.dart"; class PresenceIndicator extends StatelessWidget { - const PresenceIndicator({super.key}); - + const PresenceIndicator({super.key, required this.wallSensor}); + final DeviceModel wallSensor; @override Widget build(BuildContext context) { + String state = wallSensor.status + .firstWhere((element) => element.code == "presence_state") + .value + .toString(); return Expanded( flex: 6, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SvgPicture.asset( - Assets.presenceSensorAssetsPresence, + state == 'presence' + ? Assets.presenceSensorAssetsPresence + : Assets.presenceSensorAssetsPresenceSensorMotion, width: 100, height: 100, ), @@ -19,7 +25,7 @@ class PresenceIndicator extends StatelessWidget { height: 10, ), BodyMedium( - text: 'Presence', + text: StringHelpers.toTitleCase(state), style: context.bodyMedium.copyWith(fontWeight: FontsManager.bold), ), ], diff --git a/lib/features/devices/view/widgets/presence_sensors/wall_sensor_interface.dart b/lib/features/devices/view/widgets/presence_sensors/wall_sensor_interface.dart index a9d6d0d..5c1ee4f 100644 --- a/lib/features/devices/view/widgets/presence_sensors/wall_sensor_interface.dart +++ b/lib/features/devices/view/widgets/presence_sensors/wall_sensor_interface.dart @@ -12,6 +12,7 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/helpers/misc_string_helpers.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; @@ -59,7 +60,9 @@ class WallMountedInterface extends StatelessWidget { ), child: Column( children: [ - const PresenceIndicator(), + PresenceIndicator( + wallSensor: wallSensor, + ), ParametersList(wallSensor: wallSensor), ], ), diff --git a/lib/features/devices/view/widgets/room_page_switch.dart b/lib/features/devices/view/widgets/room_page_switch.dart index ed21e5c..49588ab 100644 --- a/lib/features/devices/view/widgets/room_page_switch.dart +++ b/lib/features/devices/view/widgets/room_page_switch.dart @@ -12,7 +12,7 @@ import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/ac_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface.dart'; -import 'package:syncrow_app/features/devices/view/widgets/presence_sensor/wall_sensor_interface.dart'; +import 'package:syncrow_app/features/devices/view/widgets/presence_sensors/wall_sensor_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/presence_sensors/ceiling_sensor_interface.dart';