diff --git a/assets/icons/disappe_delay_icon.svg b/assets/icons/disappe_delay_icon.svg new file mode 100644 index 00000000..c65fc5a7 --- /dev/null +++ b/assets/icons/disappe_delay_icon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/icons/indent_level_icon.svg b/assets/icons/indent_level_icon.svg new file mode 100644 index 00000000..837cdac2 --- /dev/null +++ b/assets/icons/indent_level_icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/icons/target_confirm_time_icon.svg b/assets/icons/target_confirm_time_icon.svg new file mode 100644 index 00000000..61141654 --- /dev/null +++ b/assets/icons/target_confirm_time_icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/trigger_level_icon.svg b/assets/icons/trigger_level_icon.svg new file mode 100644 index 00000000..56e343e8 --- /dev/null +++ b/assets/icons/trigger_level_icon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + 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 d07efc9b..485cb0ec 100644 --- a/lib/pages/device_managment/all_devices/models/devices_model.dart +++ b/lib/pages/device_managment/all_devices/models/devices_model.dart @@ -7,6 +7,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart' import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart'; import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/models/flush/flush_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'; @@ -155,13 +156,17 @@ class AllDevicesModel { batteryLevel = int.tryParse(json['battery']?.toString() ?? ''); productName = json['productName']?.toString(); deviceTags = json['deviceTag'] != null && json['deviceTag'] is List - ? (json['deviceTag'] as List).map((tag) => DeviceTagModel.fromJson(tag)).toList() + ? (json['deviceTag'] as List) + .map((tag) => DeviceTagModel.fromJson(tag)) + .toList() : []; deviceSubSpace = json['subspace'] != null ? DeviceSubSpace.fromJson(json['subspace']) : DeviceSubSpace(subspaceName: ''); 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(); } } @@ -209,7 +214,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; @@ -239,6 +245,8 @@ SOS // tempIcon = Assets.gang3touch; } else if (type == DeviceType.WaterLeak) { tempIcon = Assets.waterLeakNormal; + } else if (type == DeviceType.NCPS) { + tempIcon = Assets.sensors; } else { tempIcon = Assets.logoHorizontal; } @@ -254,51 +262,75 @@ SOS switch (productType) { case 'AC': return [ - SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), - ModeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), - TempSetFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), - CurrentTempFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), - LevelFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), - ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + SwitchFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + ModeFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + TempSetFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + CurrentTempFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), + LevelFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + ChildLockFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), ]; 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 ?? '', type: 'BOTH'), - ThreeGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), - ThreeGangSwitch3Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), - ThreeGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), - ThreeGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), - ThreeGangCountdown3Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + ThreeGangSwitch1Function( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + ThreeGangSwitch2Function( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + ThreeGangSwitch3Function( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + ThreeGangCountdown1Function( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + ThreeGangCountdown2Function( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + ThreeGangCountdown3Function( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), ]; case 'WPS': return [ //IF Functions - PresenceStateFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), - CurrentDistanceFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), - IlluminanceValueFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), - PresenceTimeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), + PresenceStateFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), + CurrentDistanceFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), + IlluminanceValueFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), + PresenceTimeFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), //THEN Functions - FarDetectionFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), - MotionSensitivityFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), - MotionLessSensitivityFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), - IndicatorFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), - NoOneTimeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + FarDetectionFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + MotionSensitivityFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + MotionLessSensitivityFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + IndicatorFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), + NoOneTimeFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), ]; case 'GW': return [ @@ -323,6 +355,30 @@ SOS uuid: uuid ?? '', name: name ?? '', ); + case 'NCPS': + return [ + FlushPresenceDelayFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF',), + + FlushIlluminanceFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'), + //THEN Functions + FlushSensitivityFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + FlushNearDetectionFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + FlushMaxDetectDistFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + FlushTargetConfirmTimeFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + FlushDisappeDelayFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + FlushIndentLevelFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + FlushTriggerLevelFunction( + deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), + ]; + default: return []; } @@ -454,5 +510,6 @@ SOS "3GT": DeviceType.ThreeTouch, "GD": DeviceType.GarageDoor, "WL": DeviceType.WaterLeak, + "NCPS": DeviceType.NCPS, }; } diff --git a/lib/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart b/lib/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart index 975af0e8..bf97005d 100644 --- a/lib/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart +++ b/lib/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart @@ -97,3 +97,7 @@ class FlushMountedPresenceSensorModel { ); } } + + + + diff --git a/lib/pages/device_managment/garage_door/bloc/garage_door_bloc.dart b/lib/pages/device_managment/garage_door/bloc/garage_door_bloc.dart index 025de303..9083ffbe 100644 --- a/lib/pages/device_managment/garage_door/bloc/garage_door_bloc.dart +++ b/lib/pages/device_managment/garage_door/bloc/garage_door_bloc.dart @@ -106,14 +106,14 @@ class GarageDoorBloc extends Bloc { ScheduleEntry newSchedule = ScheduleEntry( category: event.category, time: formatTimeOfDayToISO(event.time), - function: Status(code: 'switch_1', value: event.functionOn), + function: Status(code: 'doorcontact_state', value: event.functionOn), days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays), ); bool success = await DevicesManagementApi().addScheduleRecord(newSchedule, deviceId); if (success) { add(FetchGarageDoorSchedulesEvent( - deviceId: deviceId, category: 'switch_1')); + deviceId: deviceId, category: 'doorcontact_state')); } else { emit(GarageDoorLoadedState(status: deviceStatus)); } @@ -149,7 +149,8 @@ class GarageDoorBloc extends Bloc { final updatedSchedules = deviceStatus.schedules?.map((schedule) { if (schedule.scheduleId == event.scheduleId) { return schedule.copyWith( - function: Status(code: 'switch_1', value: event.functionOn), + function: + Status(code: 'doorcontact_state', value: event.functionOn), enable: event.enable, ); } @@ -274,7 +275,8 @@ class GarageDoorBloc extends Bloc { Future _onBatchControl( GarageDoorBatchControlEvent event, Emitter emit) async { - final oldValue = event.code == 'switch_1' ? deviceStatus.switch1 : false; + final oldValue = + event.code == 'doorcontact_state' ? deviceStatus.switch1 : false; _updateLocalValue(event.code, event.value); emit(GarageDoorBatchStatusLoaded(deviceStatus)); @@ -409,7 +411,7 @@ class GarageDoorBloc extends Bloc { void _revertValue( String code, dynamic oldValue, Emitter emit) { switch (code) { - case 'switch_1': + case 'doorcontact_state': if (oldValue is bool) { deviceStatus = deviceStatus.copyWith(switch1: oldValue); } @@ -468,10 +470,11 @@ class GarageDoorBloc extends Bloc { deviceStatus = deviceStatus.copyWith(voiceControl1: value); } break; - case 'door_contact_state': + case 'doorcontact_state': if (value is bool) { deviceStatus = deviceStatus.copyWith(doorContactState: value); } + default: break; } @@ -490,14 +493,14 @@ class GarageDoorBloc extends Bloc { scheduleId: event.scheduleId, category: event.category, time: formatTimeOfDayToISO(event.time), - function: Status(code: 'switch_1', value: event.functionOn), + function: Status(code: 'doorcontact_state', value: event.functionOn), days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays), ); bool success = await DevicesManagementApi() .editScheduleRecord(deviceId, newSchedule); if (success) { add(FetchGarageDoorSchedulesEvent( - deviceId: deviceId, category: 'switch_1')); + deviceId: deviceId, category: 'doorcontact_state')); } else { emit(GarageDoorLoadedState(status: deviceStatus)); } diff --git a/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart b/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart index 886ca9ae..ae2fc9e4 100644 --- a/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart +++ b/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart @@ -14,7 +14,8 @@ import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout { +class GarageDoorControlView extends StatelessWidget + with HelperResponsiveLayout { final String deviceId; const GarageDoorControlView({required this.deviceId, super.key}); @@ -22,7 +23,8 @@ class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => GarageDoorBloc(deviceId: deviceId)..add(GarageDoorInitialEvent(deviceId)), + create: (context) => GarageDoorBloc(deviceId: deviceId) + ..add(GarageDoorInitialEvent(deviceId)), child: BlocBuilder( builder: (context, state) { if (state is GarageDoorLoadingState) { @@ -34,7 +36,9 @@ class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout garageDoorSensor: true, onRowTap: (index) {}, onClose: () { - context.read().add(BackToGarageDoorGridViewEvent()); + context + .read() + .add(BackToGarageDoorGridViewEvent()); }, ); } else if (state is GarageDoorLoadedState) { @@ -71,11 +75,14 @@ class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout children: [ IconNameStatusContainer( isFullIcon: false, - name: status.switch1 ? 'Opened' : 'Closed', - icon: status.switch1 ? Assets.openedDoor : Assets.closedDoor, + name: status.doorContactState ? 'Opened' : 'Closed', + icon: status.doorContactState ? Assets.openedDoor : Assets.closedDoor, onTap: () { context.read().add( - GarageDoorControlEvent(deviceId: status.uuid, value: !status.switch1, code: 'switch_1'), + GarageDoorControlEvent( + deviceId: status.uuid, + value: !status.switch1, + code: 'doorcontact_state'), ); }, status: status.switch1, @@ -84,7 +91,8 @@ class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout IconNameStatusContainer( onTap: () { context.read().add( - FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: 'switch_1'), + FetchGarageDoorSchedulesEvent( + deviceId: deviceId, category: 'doorcontact_state'), ); showDialog( context: context, @@ -107,7 +115,9 @@ class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout children: [ IconButton( onPressed: () { - context.read().add(DecreaseGarageDoorDelayEvent(deviceId: status.uuid)); + context + .read() + .add(DecreaseGarageDoorDelayEvent(deviceId: status.uuid)); }, icon: const Icon( Icons.remove, @@ -125,7 +135,8 @@ class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout ), Text( 'h', - style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor), + style: context.textTheme.bodySmall! + .copyWith(color: ColorsManager.blackColor), ), Text( (status.delay.inMinutes % 60).toString().padLeft(2, '0'), @@ -136,11 +147,14 @@ class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout ), Text( 'm', - style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor), + style: context.textTheme.bodySmall! + .copyWith(color: ColorsManager.blackColor), ), IconButton( onPressed: () { - context.read().add(IncreaseGarageDoorDelayEvent(deviceId: status.uuid)); + context + .read() + .add(IncreaseGarageDoorDelayEvent(deviceId: status.uuid)); }, icon: const Icon( Icons.add, @@ -158,7 +172,9 @@ class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout onChange: (value) { context.read().add( GarageDoorControlEvent( - deviceId: status.uuid, value: value ? status.delay.inSeconds : 0, code: 'countdown_1'), + deviceId: status.uuid, + value: value ? status.delay.inSeconds : 0, + code: 'countdown_1'), ); }, ), @@ -167,7 +183,8 @@ class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout name: 'Records', icon: Assets.records, onTap: () { - context.read().add(FetchGarageDoorRecordsEvent(code: 'switch_1', deviceId: status.uuid)); + context.read().add(FetchGarageDoorRecordsEvent( + code: 'doorcontact_state', deviceId: status.uuid)); }, status: false, textColor: ColorsManager.blackColor, 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 b1afbc12..a94a312b 100644 --- a/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart +++ b/lib/pages/routines/helper/dialog_helper/device_dialog_helper.dart @@ -4,6 +4,7 @@ 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/widgets/routine_dialogs/ac_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_presence_sensor.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_helper.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart'; @@ -116,6 +117,15 @@ class DeviceDialogHelper { deviceSelectedFunctions: deviceSelectedFunctions, device: data['device'], ); + case 'NCPS': + return FlushPresenceSensor.showFlushFunctionsDialog( + context: context, + functions: functions, + uniqueCustomId: data['uniqueCustomId'], + deviceSelectedFunctions: deviceSelectedFunctions, + dialogType: dialogType, + device: data['device'], + ); default: return null; diff --git a/lib/pages/routines/models/flush/flush_functions.dart b/lib/pages/routines/models/flush/flush_functions.dart new file mode 100644 index 00000000..5013c0b8 --- /dev/null +++ b/lib/pages/routines/models/flush/flush_functions.dart @@ -0,0 +1,407 @@ +import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart'; +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/models/flush/flush_operational_value.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +abstract class FlushFunctions + extends DeviceFunction { + final String type; + + FlushFunctions({ + required super.deviceId, + required super.deviceName, + required super.code, + required super.operationName, + required super.icon, + required this.type, + }); + + List getOperationalValues(); +} + +class FlushPresenceDelayFunction extends FlushFunctions { + final int min; + FlushPresenceDelayFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 0, + super( + code: FlushMountedPresenceSensorModel.codePresenceState, + operationName: 'Presence State', + icon: Assets.presenceStateIcon, + ); + + @override + List getOperationalValues() { + return [ + FlushOperationalValue( + icon: Assets.nobodyTime, + description: 'None', + value: "none", + ), + FlushOperationalValue( + icon: Assets.presenceStateIcon, + description: 'Presence', + value: 'presence', + ), + ]; + } +} + +class FlushSensiReduceFunction extends FlushFunctions { + final int min; + final int max; + final int step; + + FlushSensiReduceFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 1, + max = 5, + step = 1, + super( + code: FlushMountedPresenceSensorModel.codeSensiReduce, + operationName: 'Sensitivity Reduction', + icon: Assets.motionlessDetectionSensitivityIcon, + ); + + @override + List getOperationalValues() { + return List.generate( + (max - min) ~/ step + 1, + (index) => FlushOperationalValue( + icon: Assets.currentDistanceIcon, + description: '${min + (index * step)}', + value: min + (index * step), + )); + } +} + +class FlushNoneDelayFunction extends FlushFunctions { + final int min; + final int max; + final String unit; + + FlushNoneDelayFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 10, + max = 10000, + unit = '秒', + super( + code: FlushMountedPresenceSensorModel.codeNoneDelay, + operationName: 'None Delay', + icon: Assets.nobodyTime, + ); + + @override + List getOperationalValues() { + return [ + FlushOperationalValue( + icon: icon, + description: 'Custom $unit', + value: null, + ) + ]; + } +} + +class FlushIlluminanceFunction extends FlushFunctions { + final int min; + final int max; + final int step; + + FlushIlluminanceFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 0, + max = 2000, + step = 0, + super( + code: FlushMountedPresenceSensorModel.codeIlluminance, + operationName: 'Illuminance', + icon: Assets.IlluminanceIcon, + ); + + @override + List getOperationalValues() { + List values = []; + for (int lux = min; lux <= max; lux += step) { + values.add(FlushOperationalValue( + icon: Assets.IlluminanceIcon, + description: "$lux Lux", + value: lux, + )); + } + return values; + } +} + +class FlushOccurDistReduceFunction extends FlushFunctions { + final int min; + final int max; + final int step; + + FlushOccurDistReduceFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 0, + max = 100, + step = 1, + super( + code: FlushMountedPresenceSensorModel.codeOccurDistReduce, + operationName: 'Occurrence Distance Reduction', + icon: Assets.assetsTempreture, + ); + + @override + List getOperationalValues() { + return List.generate( + (max - min) ~/ step + 1, + (index) => FlushOperationalValue( + icon: Assets.assetsTempreture, + description: '${min + (index * step)}', + value: min + (index * step), + )); + } +} + +// ==== then functions ==== +class FlushSensitivityFunction extends FlushFunctions { + final int min; + final int max; + final int step; + + FlushSensitivityFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 1, + max = 9, + step = 1, + super( + code: FlushMountedPresenceSensorModel.codeSensitivity, + operationName: 'Sensitivity', + icon: Assets.sensitivity, + ); + + @override + List getOperationalValues() { + return List.generate( + (max - min) ~/ step + 1, + (index) => FlushOperationalValue( + icon: Assets.motionDetectionSensitivityValueIcon, + description: '${min + (index * step)}', + value: min + (index * step), + )); + } +} + +class FlushNearDetectionFunction extends FlushFunctions { + final int min; + final double max; + final int step; + final String unit; + + FlushNearDetectionFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 0, + max = 9.5, + step = 1, + unit = 'm', + super( + code: FlushMountedPresenceSensorModel.codeNearDetection, + operationName: 'Nearest Detect Dist', + icon: Assets.currentDistanceIcon, + ); + + @override + List getOperationalValues() { + final values = []; + for (var value = min; value <= max; value += step) { + values.add(FlushOperationalValue( + icon: Assets.nobodyTime, + description: '$value $unit', + value: value * 10, + )); + } + return values; + } +} + +class FlushMaxDetectDistFunction extends FlushFunctions { + final int min; + final int max; + final int step; + final String unit; + + FlushMaxDetectDistFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 75, + max = 600, + step = 75, + unit = 'cm', + super( + code: FlushMountedPresenceSensorModel.codeFarDetection, + operationName: 'Max Detect Dist', + icon: Assets.currentDistanceIcon, + ); + + @override + List getOperationalValues() { + final values = []; + for (var value = min; value <= max; value += step) { + values.add(FlushOperationalValue( + icon: Assets.nobodyTime, + description: '$value $unit', + value: value, + )); + } + return values; + } +} + +class FlushTargetConfirmTimeFunction extends FlushFunctions { + final int min; + final int max; + final int step; + final String unit; + + FlushTargetConfirmTimeFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 75, + max = 600, + step = 75, + unit = 'cm', + super( + code: FlushMountedPresenceSensorModel.codePresenceDelay, + operationName: 'Target Confirm Time', + icon: Assets.targetConfirmTimeIcon, + ); + + @override + List getOperationalValues() { + final values = []; + for (var value = min; value <= max; value += step) { + values.add(FlushOperationalValue( + icon: Assets.nobodyTime, + description: '$value $unit', + value: value, + )); + } + return values; + } +} + +class FlushDisappeDelayFunction extends FlushFunctions { + final int min; + final int max; + final int step; + final String unit; + + FlushDisappeDelayFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 75, + max = 600, + step = 75, + unit = 'cm', + super( + code: FlushMountedPresenceSensorModel.codeNoneDelay, + operationName: 'Disappe Delay', + icon: Assets.DisappeDelayIcon, + ); + + @override + List getOperationalValues() { + final values = []; + for (var value = min; value <= max; value += step) { + values.add(FlushOperationalValue( + icon: Assets.nobodyTime, + description: '$value $unit', + value: value, + )); + } + return values; + } +} + +class FlushIndentLevelFunction extends FlushFunctions { + final int min; + final int max; + final int step; + final String unit; + + FlushIndentLevelFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 75, + max = 600, + step = 75, + unit = 'cm', + super( + code: FlushMountedPresenceSensorModel.codeOccurDistReduce, + operationName: 'Indent Level', + icon: Assets.indentLevelIcon, + ); + + @override + List getOperationalValues() { + final values = []; + for (var value = min; value <= max; value += step) { + values.add(FlushOperationalValue( + icon: Assets.nobodyTime, + description: '$value $unit', + value: value, + )); + } + return values; + } +} + +class FlushTriggerLevelFunction extends FlushFunctions { + final int min; + final int max; + final int step; + final String unit; + + FlushTriggerLevelFunction({ + required super.deviceId, + required super.deviceName, + required super.type, + }) : min = 75, + max = 600, + step = 75, + unit = 'cm', + super( + code: FlushMountedPresenceSensorModel.codeSensiReduce, + operationName: 'Trigger Level', + icon: Assets.triggerLevelIcon, + ); + + @override + List getOperationalValues() { + final values = []; + for (var value = min; value <= max; value += step) { + values.add(FlushOperationalValue( + icon: Assets.nobodyTime, + description: '$value $unit', + value: value, + )); + } + return values; + } +} diff --git a/lib/pages/routines/models/flush/flush_operational_value.dart b/lib/pages/routines/models/flush/flush_operational_value.dart new file mode 100644 index 00000000..af96d35b --- /dev/null +++ b/lib/pages/routines/models/flush/flush_operational_value.dart @@ -0,0 +1,11 @@ +class FlushOperationalValue { + final String icon; + final String description; + final dynamic value; + + FlushOperationalValue({ + 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 f7a4ddc1..884c8d10 100644 --- a/lib/pages/routines/widgets/if_container.dart +++ b/lib/pages/routines/widgets/if_container.dart @@ -28,10 +28,12 @@ class IfContainer extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('IF', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold)), if (state.isAutomation && state.ifItems.isNotEmpty) AutomationOperatorSelector( - selectedOperator: state.selectedAutomationOperator), + selectedOperator: + state.selectedAutomationOperator), ], ), const SizedBox(height: 16), @@ -55,16 +57,17 @@ class IfContainer extends StatelessWidget { (index) => GestureDetector( onTap: () async { if (!state.isTabToRun) { - final result = await DeviceDialogHelper.showDeviceDialog( - context: context, - data: state.ifItems[index], - removeComparetors: false, - dialogType: "IF"); + 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)); + context.read().add( + AddToIfContainer( + state.ifItems[index], false)); } else if (![ 'AC', '1G', @@ -73,25 +76,32 @@ class IfContainer extends StatelessWidget { 'WPS', 'GW', 'CPS', - ].contains(state.ifItems[index]['productType'])) { - context - .read() - .add(AddToIfContainer(state.ifItems[index], false)); + 'NCPS' + ].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'])); }, ), )), @@ -112,7 +122,9 @@ 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( dialogType: 'IF', @@ -121,10 +133,14 @@ class IfContainer extends StatelessWidget { removeComparetors: false); if (result != null) { - context.read().add(AddToIfContainer(mutableData, false)); - } else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS'] + context + .read() + .add(AddToIfContainer(mutableData, false)); + } else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS', 'NCPS'] .contains(mutableData['productType'])) { - context.read().add(AddToIfContainer(mutableData, false)); + context + .read() + .add(AddToIfContainer(mutableData, false)); } } } @@ -170,7 +186,9 @@ class AutomationOperatorSelector extends StatelessWidget { ), ), onPressed: () { - context.read().add(const ChangeAutomationOperator(operator: 'or')); + context + .read() + .add(const ChangeAutomationOperator(operator: 'or')); }, ), Container( @@ -196,7 +214,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 3294a73a..9192b422 100644 --- a/lib/pages/routines/widgets/routine_devices.dart +++ b/lib/pages/routines/widgets/routine_devices.dart @@ -17,7 +17,16 @@ class _RoutineDevicesState extends State { context.read().add(FetchDevicesInRoutine()); } - static const _allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS'}; + static const _allowedProductTypes = { + 'AC', + '1G', + '2G', + '3G', + 'WPS', + 'GW', + 'CPS', + 'NCPS' + }; @override Widget build(BuildContext context) { @@ -34,7 +43,8 @@ class _RoutineDevicesState extends State { }); final deviceList = state.devices - .where((device) => _allowedProductTypes.contains(device.productType)) + .where( + (device) => _allowedProductTypes.contains(device.productType)) .toList(); return Wrap( @@ -51,12 +61,16 @@ class _RoutineDevicesState extends State { 'productType': device.productType, 'functions': device.functions, 'uniqueCustomId': '', - 'tag': device.deviceTags!.isNotEmpty ? device.deviceTags![0].name : '', + 'tag': device.deviceTags!.isNotEmpty + ? device.deviceTags![0].name + : '', 'subSpace': device.deviceSubSpace?.subspaceName ?? '', }; 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: deviceData['imagePath'] as String, title: deviceData['title'] as String, diff --git a/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart new file mode 100644 index 00000000..1a96cfbb --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/models/flush/flush_operational_value.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart'; + +class FlushOperationalValuesList extends StatelessWidget { + final List values; + final dynamic selectedValue; + final AllDevicesModel? device; + final String operationName; + final String selectCode; + final ValueChanged onSelect; + const FlushOperationalValuesList({ + required this.values, + required this.selectedValue, + required this.device, + required this.operationName, + required this.selectCode, + required this.onSelect, + super.key, + }); + + @override + Widget build(BuildContext context) { + return ListView.builder( + padding: const EdgeInsets.all(20), + itemCount: values.length, + itemBuilder: (context, index) => + _buildValueItem(context, values[index]), + ); + } + + + + Widget _buildValueItem(BuildContext context, FlushOperationalValue value) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded(child: _buildValueDescription(value)), + _buildValueRadio(context, value), + ], + ), + ); + } + + + + + Widget _buildValueDescription(FlushOperationalValue value) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Text(value.description), + ); + } + + Widget _buildValueRadio(context, FlushOperationalValue value) { + return Radio( + value: value.value, + groupValue: selectedValue, + onChanged: (_) => onSelect(value)); + } + + +} diff --git a/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_presence_sensor.dart b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_presence_sensor.dart new file mode 100644 index 00000000..bf2146ad --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_presence_sensor.dart @@ -0,0 +1,208 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.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/flush/flush_functions.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/flush_presence_sensor/flush_value_selector_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class FlushPresenceSensor extends StatefulWidget { + final List functions; + final AllDevicesModel? device; + final List? deviceSelectedFunctions; + final String? uniqueCustomId; + final String dialogType; + final bool removeComparetors; + + const FlushPresenceSensor({ + super.key, + required this.functions, + this.device, + this.deviceSelectedFunctions, + this.uniqueCustomId, + required this.dialogType, + this.removeComparetors = false, + }); + + static Future?> showFlushFunctionsDialog({ + required BuildContext context, + required List functions, + AllDevicesModel? device, + List? deviceSelectedFunctions, + String? uniqueCustomId, + required String dialogType, + bool removeComparetors = false, + }) async { + return showDialog?>( + context: context, + builder: (context) => FlushPresenceSensor( + functions: functions, + device: device, + deviceSelectedFunctions: deviceSelectedFunctions, + uniqueCustomId: uniqueCustomId, + removeComparetors: removeComparetors, + dialogType: dialogType, + ), + ); + } + + @override + State createState() => _WallPresenceSensorState(); +} + +class _WallPresenceSensorState extends State { + late final List _flushFunctions; + @override + void initState() { + super.initState(); + _flushFunctions = + 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: _flushFunctions.length, + separatorBuilder: (context, index) => const Padding( + padding: EdgeInsets.symmetric(horizontal: 40.0), + child: Divider(color: ColorsManager.dividerColor), + ), + itemBuilder: (context, index) { + final function = _flushFunctions[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: state.selectedOperationName ?? '', + value: null, + ), + ); + + return Expanded( + child: FlushValueSelectorWidget( + selectedFunction: selectedFunction, + functionData: functionData, + flushFunctions: _flushFunctions, + 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, + ); + } +} diff --git a/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_value_selector_widget.dart b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_value_selector_widget.dart new file mode 100644 index 00000000..64f060e5 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_value_selector_widget.dart @@ -0,0 +1,178 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart'; +import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart'; +import 'package:syncrow_web/pages/routines/models/device_functions.dart'; +import 'package:syncrow_web/pages/routines/models/flush/flush_functions.dart'; +import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart'; +import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart'; + +class FlushValueSelectorWidget extends StatelessWidget { + final String selectedFunction; + final DeviceFunctionData functionData; + final List flushFunctions; + final AllDevicesModel? device; + final String dialogType; + final bool removeComparators; + + const FlushValueSelectorWidget({ + required this.selectedFunction, + required this.functionData, + required this.flushFunctions, + required this.device, + required this.dialogType, + required this.removeComparators, + super.key, + }); + + (double, double) get sliderRange => switch (functionData.functionCode) { + FlushMountedPresenceSensorModel.codeOccurDistReduce => (0, 3), + FlushMountedPresenceSensorModel.codeNoneDelay => (200, 3000), + FlushMountedPresenceSensorModel.codeSensiReduce => (0, 3), + FlushMountedPresenceSensorModel.codePresenceDelay => (0, 5), + FlushMountedPresenceSensorModel.codeIlluminance => (0.0, 2000.0), + FlushMountedPresenceSensorModel.codeFarDetection => (0.0, 9.5), + FlushMountedPresenceSensorModel.codeNearDetection => (0.0, 9.5), + _ => (0.0, 100.0), + }; + + double get stepSize => switch (functionData.functionCode) { + FlushMountedPresenceSensorModel.codeNoneDelay => 10.0, + FlushMountedPresenceSensorModel.codeFarDetection => 0.5, + FlushMountedPresenceSensorModel.codeNearDetection => 0.5, + FlushMountedPresenceSensorModel.codePresenceDelay => 1.0, + FlushMountedPresenceSensorModel.codeOccurDistReduce => 1.0, + FlushMountedPresenceSensorModel.codeSensiReduce => 1.0, + _ => 1.0, + }; + + @override + Widget build(BuildContext context) { + final selectedFn = flushFunctions.firstWhere( + (f) => f.code == selectedFunction, + orElse: () => throw Exception('Function $selectedFunction not found'), + ); + + if (_isSliderFunction(selectedFunction)) { + final isNearDetection = + selectedFunction == FlushMountedPresenceSensorModel.codeNearDetection; + final isFarDetection = + selectedFunction == FlushMountedPresenceSensorModel.codeFarDetection; + + final isDistanceDetection = isNearDetection || isFarDetection; + double initialValue = (functionData.value as num?)?.toDouble() ?? 0.0; + + if (isDistanceDetection) { + initialValue = initialValue / 100; + } + return SliderValueSelector( + currentCondition: functionData.condition, + dialogType: dialogType, + sliderRange: sliderRange, + displayedValue: getDisplayText, + initialValue: initialValue, + onConditionChanged: (condition) => context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: functionData.operationName, + condition: condition, + value: functionData.value ?? 0, + ), + ), + ), + onSliderChanged: (value) { + final roundedValue = _roundToStep(value, stepSize); + final finalValue = + isDistanceDetection ? (roundedValue * 100).toInt() : roundedValue; + + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: functionData.operationName, + value: finalValue, + condition: functionData.condition, + ), + ), + ); + }, + unit: _unit, + dividendOfRange: stepSize, + ); + } + + return FlushOperationalValuesList( + values: selectedFn.getOperationalValues(), + selectedValue: functionData.value, + device: device, + operationName: selectedFn.operationName, + selectCode: selectedFunction, + onSelect: (selectedValue) async { + context.read().add( + AddFunction( + functionData: DeviceFunctionData( + entityId: device?.uuid ?? '', + functionCode: selectedFunction, + operationName: functionData.operationName, + value: selectedValue.value, + condition: functionData.condition, + ), + ), + ); + }, + ); + } + + double _roundToStep(double value, double step) { + return (value / step).roundToDouble() * step; + } + + bool _isSliderFunction(String function) => [ + FlushMountedPresenceSensorModel.codeOccurDistReduce, + FlushMountedPresenceSensorModel.codeSensiReduce, + FlushMountedPresenceSensorModel.codeNoneDelay, + FlushMountedPresenceSensorModel.codeIlluminance, + FlushMountedPresenceSensorModel.codePresenceDelay, + FlushMountedPresenceSensorModel.codeFarDetection, + FlushMountedPresenceSensorModel.codeNearDetection, + ].contains(function); + + String get _unit => switch (functionData.functionCode) { + FlushMountedPresenceSensorModel.codeOccurDistReduce => 'Min', + FlushMountedPresenceSensorModel.codeSensiReduce => 'Sec', + FlushMountedPresenceSensorModel.codeNoneDelay => 'Sec', + FlushMountedPresenceSensorModel.codePresenceDelay => 'Sec', + FlushMountedPresenceSensorModel.codeIlluminance => 'Lux', + FlushMountedPresenceSensorModel.codeFarDetection => 'm', + FlushMountedPresenceSensorModel.codeNearDetection => 'm', + _ => '', + }; + + String get getDisplayText { + final num? value = functionData.value; + double displayValue = value?.toDouble() ?? 0.0; + + if (functionData.functionCode == + FlushMountedPresenceSensorModel.codeNearDetection || + functionData.functionCode == + FlushMountedPresenceSensorModel.codeFarDetection) { + displayValue = displayValue / 100; + } + + switch (functionData.functionCode) { + case FlushMountedPresenceSensorModel.codeFarDetection: + case FlushMountedPresenceSensorModel.codeNearDetection: + return displayValue.toStringAsFixed(1); + case FlushMountedPresenceSensorModel.codeOccurDistReduce: + case FlushMountedPresenceSensorModel.codeSensiReduce: + case FlushMountedPresenceSensorModel.codePresenceDelay: + return displayValue.toStringAsFixed(0); + default: + return displayValue.toStringAsFixed(0); + } + } +} diff --git a/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/time_wheel.dart b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/time_wheel.dart new file mode 100644 index 00000000..56f74054 --- /dev/null +++ b/lib/pages/routines/widgets/routine_dialogs/flush_presence_sensor/time_wheel.dart @@ -0,0 +1,169 @@ +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: 3, + 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) { + int total = hours * 3600 + minutes * 60 + seconds; + if (total > 10000) { + hours = 2; + minutes = 46; + seconds = 40; + total = 10000; + WidgetsBinding.instance.addPostFrameCallback((_) { + _hoursController.jumpToItem(hours); + _minutesController.jumpToItem(minutes); + _secondsController.jumpToItem(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, + ), + ), + ], + ); + } +} diff --git a/lib/pages/routines/widgets/then_container.dart b/lib/pages/routines/widgets/then_container.dart index 3266d3b4..1e7e1382 100644 --- a/lib/pages/routines/widgets/then_container.dart +++ b/lib/pages/routines/widgets/then_container.dart @@ -27,7 +27,8 @@ class ThenContainer extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('THEN', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 16), state.isLoading && state.isUpdate == true ? const Center( @@ -40,11 +41,12 @@ class ThenContainer extends StatelessWidget { state.thenItems.length, (index) => GestureDetector( onTap: () async { - if (state.thenItems[index]['deviceId'] == + if (state.thenItems[index] + ['deviceId'] == 'delay') { final result = await DelayHelper - .showDelayPickerDialog( - context, state.thenItems[index]); + .showDelayPickerDialog(context, + state.thenItems[index]); if (result != null) { context @@ -64,14 +66,17 @@ class ThenContainer extends StatelessWidget { context: context, builder: (BuildContext context) => AutomationDialog( - automationName: state.thenItems[index] - ['name'] ?? - 'Automation', - automationId: state.thenItems[index] - ['deviceId'] ?? - '', - uniqueCustomId: state.thenItems[index] - ['uniqueCustomId'], + automationName: + state.thenItems[index] + ['name'] ?? + 'Automation', + automationId: + state.thenItems[index] + ['deviceId'] ?? + '', + uniqueCustomId: + state.thenItems[index] + ['uniqueCustomId'], ), ); @@ -80,11 +85,13 @@ class ThenContainer extends StatelessWidget { .read() .add(AddToThenContainer({ ...state.thenItems[index], - 'imagePath': Assets.automation, - 'title': state.thenItems[index] - ['name'] ?? + 'imagePath': + Assets.automation, + 'title': state.thenItems[index] - ['title'], + ['name'] ?? + state.thenItems[index] + ['title'], })); } return; @@ -109,8 +116,9 @@ class ThenContainer extends StatelessWidget { 'WPS', 'CPS', "GW", - ].contains( - state.thenItems[index]['productType'])) { + "NCPS" + ].contains(state.thenItems[index] + ['productType'])) { context.read().add( AddToThenContainer( state.thenItems[index])); @@ -120,7 +128,9 @@ class ThenContainer extends StatelessWidget { imagePath: state.thenItems[index] ['imagePath'] ?? '', - title: state.thenItems[index]['title'] ?? '', + title: state.thenItems[index] + ['title'] ?? + '', deviceData: state.thenItems[index], padding: const EdgeInsets.symmetric( horizontal: 4, vertical: 8), @@ -157,8 +167,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; } @@ -183,8 +193,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; } @@ -222,7 +232,7 @@ class ThenContainer extends StatelessWidget { dialogType: "THEN"); if (result != null) { context.read().add(AddToThenContainer(mutableData)); - } else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS'] + } else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS', "NCPS"] .contains(mutableData['productType'])) { context.read().add(AddToThenContainer(mutableData)); } diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index dd933789..43f7bd92 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -13,7 +13,8 @@ class Assets { static const String rightLine = "assets/images/right_line.png"; static const String google = "assets/images/google.svg"; static const String facebook = "assets/images/facebook.svg"; - static const String invisiblePassword = "assets/images/Password_invisible.svg"; + static const String invisiblePassword = + "assets/images/Password_invisible.svg"; static const String visiblePassword = "assets/images/password_visible.svg"; static const String accessIcon = "assets/images/access_icon.svg"; static const String spaseManagementIcon = @@ -30,7 +31,8 @@ class Assets { static const String emptyTable = "assets/images/empty_table.svg"; // General assets - static const String motionlessDetection = "assets/icons/motionless_detection.svg"; + static const String motionlessDetection = + "assets/icons/motionless_detection.svg"; static const String acHeating = "assets/icons/ac_heating.svg"; static const String acPowerOff = "assets/icons/ac_power_off.svg"; static const String acFanMiddle = "assets/icons/ac_fan_middle.svg"; @@ -67,19 +69,22 @@ class Assets { "assets/icons/automation_functions/temp_password_unlock.svg"; static const String doorlockNormalOpen = "assets/icons/automation_functions/doorlock_normal_open.svg"; - static const String doorbell = "assets/icons/automation_functions/doorbell.svg"; + static const String doorbell = + "assets/icons/automation_functions/doorbell.svg"; static const String remoteUnlockViaApp = "assets/icons/automation_functions/remote_unlock_via_app.svg"; static const String doubleLock = "assets/icons/automation_functions/double_lock.svg"; static const String selfTestResult = "assets/icons/automation_functions/self_test_result.svg"; - static const String lockAlarm = "assets/icons/automation_functions/lock_alarm.svg"; + static const String lockAlarm = + "assets/icons/automation_functions/lock_alarm.svg"; static const String presenceState = "assets/icons/automation_functions/presence_state.svg"; static const String currentTemp = "assets/icons/automation_functions/current_temp.svg"; - static const String presence = "assets/icons/automation_functions/presence.svg"; + static const String presence = + "assets/icons/automation_functions/presence.svg"; static const String residualElectricity = "assets/icons/automation_functions/residual_electricity.svg"; static const String hijackAlarm = @@ -96,12 +101,15 @@ class Assets { // Presence Sensor Assets static const String sensorMotionIcon = "assets/icons/sensor_motion_ic.svg"; - static const String sensorPresenceIcon = "assets/icons/sensor_presence_ic.svg"; + static const String sensorPresenceIcon = + "assets/icons/sensor_presence_ic.svg"; static const String sensorVacantIcon = "assets/icons/sensor_vacant_ic.svg"; static const String illuminanceRecordIcon = "assets/icons/illuminance_record_ic.svg"; - static const String presenceRecordIcon = "assets/icons/presence_record_ic.svg"; - static const String helpDescriptionIcon = "assets/icons/help_description_ic.svg"; + static const String presenceRecordIcon = + "assets/icons/presence_record_ic.svg"; + static const String helpDescriptionIcon = + "assets/icons/help_description_ic.svg"; static const String lightPulp = "assets/icons/light_pulb.svg"; static const String acDevice = "assets/icons/ac_device.svg"; @@ -151,10 +159,12 @@ class Assets { static const String unit = 'assets/icons/unit_icon.svg'; static const String villa = 'assets/icons/villa_icon.svg'; static const String iconEdit = 'assets/icons/icon_edit_icon.svg'; - static const String textFieldSearch = 'assets/icons/textfield_search_icon.svg'; + static const String textFieldSearch = + 'assets/icons/textfield_search_icon.svg'; static const String roundedAddIcon = 'assets/icons/rounded_add_icon.svg'; static const String addIcon = 'assets/icons/add_icon.svg'; - static const String smartThermostatIcon = 'assets/icons/smart_thermostat_icon.svg'; + static const String smartThermostatIcon = + 'assets/icons/smart_thermostat_icon.svg'; static const String smartLightIcon = 'assets/icons/smart_light_icon.svg'; static const String presenceSensor = 'assets/icons/presence_sensor.svg'; static const String Gang3SwitchIcon = 'assets/icons/3_Gang_switch_icon.svg'; @@ -202,7 +212,8 @@ class Assets { //assets/icons/water_leak_normal.svg static const String waterLeakNormal = 'assets/icons/water_leak_normal.svg'; //assets/icons/water_leak_detected.svg - static const String waterLeakDetected = 'assets/icons/water_leak_detected.svg'; + static const String waterLeakDetected = + 'assets/icons/water_leak_detected.svg'; //assets/icons/automation_records.svg static const String automationRecords = 'assets/icons/automation_records.svg'; @@ -273,13 +284,16 @@ class Assets { "assets/icons/functions_icons/sensitivity.svg"; static const String assetsSensitivityOperationIcon = "assets/icons/functions_icons/sesitivity_operation_icon.svg"; - static const String assetsAcPower = "assets/icons/functions_icons/ac_power.svg"; + static const String assetsAcPower = + "assets/icons/functions_icons/ac_power.svg"; static const String assetsAcPowerOFF = "assets/icons/functions_icons/ac_power_off.svg"; static const String assetsChildLock = "assets/icons/functions_icons/child_lock.svg"; - static const String assetsFreezing = "assets/icons/functions_icons/freezing.svg"; - static const String assetsFanSpeed = "assets/icons/functions_icons/fan_speed.svg"; + static const String assetsFreezing = + "assets/icons/functions_icons/freezing.svg"; + static const String assetsFanSpeed = + "assets/icons/functions_icons/fan_speed.svg"; static const String assetsAcCooling = "assets/icons/functions_icons/ac_cooling.svg"; static const String assetsAcHeating = @@ -288,7 +302,8 @@ class Assets { "assets/icons/functions_icons/celsius_degrees.svg"; static const String assetsTempreture = "assets/icons/functions_icons/tempreture.svg"; - static const String assetsAcFanLow = "assets/icons/functions_icons/ac_fan_low.svg"; + static const String assetsAcFanLow = + "assets/icons/functions_icons/ac_fan_low.svg"; static const String assetsAcFanMiddle = "assets/icons/functions_icons/ac_fan_middle.svg"; static const String assetsAcFanHigh = @@ -307,7 +322,8 @@ class Assets { "assets/icons/functions_icons/far_detection.svg"; static const String assetsFarDetectionFunction = "assets/icons/functions_icons/far_detection_function.svg"; - static const String assetsIndicator = "assets/icons/functions_icons/indicator.svg"; + static const String assetsIndicator = + "assets/icons/functions_icons/indicator.svg"; static const String assetsMotionDetection = "assets/icons/functions_icons/motion_detection.svg"; static const String assetsMotionlessDetection = @@ -320,7 +336,8 @@ class Assets { "assets/icons/functions_icons/master_state.svg"; static const String assetsSwitchAlarmSound = "assets/icons/functions_icons/switch_alarm_sound.svg"; - static const String assetsResetOff = "assets/icons/functions_icons/reset_off.svg"; + static const String assetsResetOff = + "assets/icons/functions_icons/reset_off.svg"; // Assets for automation_functions static const String assetsCardUnlock = @@ -364,12 +381,14 @@ class Assets { static const String activeUser = 'assets/icons/active_user.svg'; static const String deActiveUser = 'assets/icons/deactive_user.svg'; static const String invitedIcon = 'assets/icons/invited_icon.svg'; - static const String rectangleCheckBox = 'assets/icons/rectangle_check_box.png'; + static const String rectangleCheckBox = + 'assets/icons/rectangle_check_box.png'; static const String CheckBoxChecked = 'assets/icons/box_checked.png'; static const String emptyBox = 'assets/icons/empty_box.png'; static const String completeProcessIcon = 'assets/icons/compleate_process_icon.svg'; - static const String currentProcessIcon = 'assets/icons/current_process_icon.svg'; + static const String currentProcessIcon = + 'assets/icons/current_process_icon.svg'; static const String uncomplete_ProcessIcon = 'assets/icons/uncompleate_process_icon.svg'; static const String wrongProcessIcon = 'assets/icons/wrong_process_icon.svg'; @@ -390,9 +409,11 @@ class Assets { static const String successIcon = 'assets/icons/success_icon.svg'; static const String spaceLocationIcon = 'assets/icons/spaseLocationIcon.svg'; static const String scenesPlayIcon = 'assets/icons/scenesPlayIcon.png'; - static const String scenesPlayIconCheck = 'assets/icons/scenesPlayIconCheck.png'; + static const String scenesPlayIconCheck = + 'assets/icons/scenesPlayIconCheck.png'; static const String presenceStateIcon = 'assets/icons/presence_state.svg'; - static const String currentDistanceIcon = 'assets/icons/current_distance_icon.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 = @@ -415,28 +436,48 @@ class Assets { static const String cpsMode4 = 'assets/icons/cps_mode4.svg'; static const String closeToMotion = 'assets/icons/close_to_motion.svg'; static const String farAwayMotion = 'assets/icons/far_away_motion.svg'; - static const String communicationFault = 'assets/icons/communication_fault.svg'; + static const String communicationFault = + 'assets/icons/communication_fault.svg'; static const String radarFault = 'assets/icons/radar_fault.svg'; - static const String selfTestingSuccess = 'assets/icons/self_testing_success.svg'; - static const String selfTestingFailure = 'assets/icons/self_testing_failure.svg'; - static const String selfTestingTimeout = 'assets/icons/self_testing_timeout.svg'; + static const String selfTestingSuccess = + 'assets/icons/self_testing_success.svg'; + static const String selfTestingFailure = + 'assets/icons/self_testing_failure.svg'; + static const String selfTestingTimeout = + 'assets/icons/self_testing_timeout.svg'; static const String movingSpeed = 'assets/icons/moving_speed.svg'; static const String boundary = 'assets/icons/boundary.svg'; static const String motionMeter = 'assets/icons/motion_meter.svg'; - static const String spatialStaticValue = 'assets/icons/spatial_static_value.svg'; - static const String spatialMotionValue = 'assets/icons/spatial_motion_value.svg'; + static const String spatialStaticValue = + 'assets/icons/spatial_static_value.svg'; + static const String spatialMotionValue = + 'assets/icons/spatial_motion_value.svg'; static const String presenceJudgementThrshold = 'assets/icons/presence_judgement_threshold.svg'; static const String spaceType = 'assets/icons/space_type.svg'; static const String sportsPara = 'assets/icons/sports_para.svg'; - static const String sensitivityFeature1 = 'assets/icons/sensitivity_feature_1.svg'; - static const String sensitivityFeature2 = 'assets/icons/sensitivity_feature_2.svg'; - static const String sensitivityFeature3 = 'assets/icons/sensitivity_feature_3.svg'; - static const String sensitivityFeature4 = 'assets/icons/sensitivity_feature_4.svg'; - static const String sensitivityFeature5 = 'assets/icons/sensitivity_feature_5.svg'; - static const String sensitivityFeature6 = 'assets/icons/sensitivity_feature_6.svg'; - static const String sensitivityFeature7 = 'assets/icons/sensitivity_feature_7.svg'; - static const String sensitivityFeature8 = 'assets/icons/sensitivity_feature_8.svg'; - static const String sensitivityFeature9 = 'assets/icons/sensitivity_feature_9.svg'; + static const String sensitivityFeature1 = + 'assets/icons/sensitivity_feature_1.svg'; + static const String sensitivityFeature2 = + 'assets/icons/sensitivity_feature_2.svg'; + static const String sensitivityFeature3 = + 'assets/icons/sensitivity_feature_3.svg'; + static const String sensitivityFeature4 = + 'assets/icons/sensitivity_feature_4.svg'; + static const String sensitivityFeature5 = + 'assets/icons/sensitivity_feature_5.svg'; + static const String sensitivityFeature6 = + 'assets/icons/sensitivity_feature_6.svg'; + static const String sensitivityFeature7 = + 'assets/icons/sensitivity_feature_7.svg'; + static const String sensitivityFeature8 = + 'assets/icons/sensitivity_feature_8.svg'; + static const String sensitivityFeature9 = + 'assets/icons/sensitivity_feature_9.svg'; static const String deviceTagIcon = 'assets/icons/device_tag_ic.svg'; + static const String targetConfirmTimeIcon = + 'assets/icons/target_confirm_time_icon.svg'; + static const String DisappeDelayIcon = 'assets/icons/disappe_delay_icon.svg'; + static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg'; + static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg'; } diff --git a/lib/utils/enum/device_types.dart b/lib/utils/enum/device_types.dart index 7050f13a..7ad8e02c 100644 --- a/lib/utils/enum/device_types.dart +++ b/lib/utils/enum/device_types.dart @@ -17,6 +17,7 @@ enum DeviceType { ThreeTouch, GarageDoor, WaterLeak, + NCPS, DoorSensor, Other, } @@ -56,5 +57,6 @@ Map devicesTypesMap = { "2GT": DeviceType.TwoGang, "3GT": DeviceType.ThreeGang, 'GD': DeviceType.GarageDoor, - 'WL': DeviceType.WaterLeak + 'WL': DeviceType.WaterLeak, + 'NCPS': DeviceType.NCPS, }; diff --git a/pubspec.lock b/pubspec.lock index a0cbfaad..fb0d6a22 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -26,7 +26,7 @@ packages: source: hosted version: "2.11.0" bloc: - dependency: transitive + dependency: "direct main" description: name: bloc sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"