diff --git a/assets/icons/close_curtain.svg b/assets/icons/close_curtain.svg new file mode 100644 index 00000000..53f9e03b --- /dev/null +++ b/assets/icons/close_curtain.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/open_curtain.svg b/assets/icons/open_curtain.svg new file mode 100644 index 00000000..715773a5 --- /dev/null +++ b/assets/icons/open_curtain.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/pause_curtain.svg b/assets/icons/pause_curtain.svg new file mode 100644 index 00000000..8f90ea4f --- /dev/null +++ b/assets/icons/pause_curtain.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/reverse_arrows.svg b/assets/icons/reverse_arrows.svg new file mode 100644 index 00000000..fe119c39 --- /dev/null +++ b/assets/icons/reverse_arrows.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/images/completed_done.svg b/assets/images/completed_done.svg new file mode 100644 index 00000000..759f0cba --- /dev/null +++ b/assets/images/completed_done.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/pages/analytics/models/analytics_device.dart b/lib/pages/analytics/models/analytics_device.dart index 869de23f..6571eae4 100644 --- a/lib/pages/analytics/models/analytics_device.dart +++ b/lib/pages/analytics/models/analytics_device.dart @@ -39,8 +39,12 @@ class AnalyticsDevice { ? ProductDevice.fromJson(json['productDevice'] as Map) : null, spaceUuid: json['spaceUuid'] as String?, - latitude: json['lat'] != null ? double.parse(json['lat'] as String? ?? '0.0') : null, - longitude: json['lon'] != null ? double.parse(json['lon'] as String? ?? '0.0') : null, + latitude: json['lat'] != null && json['lat'] != '' + ? double.tryParse(json['lat']?.toString() ?? '0.0') + : null, + longitude: json['lon'] != null && json['lon'] != '' + ? double.tryParse(json['lon']?.toString() ?? '0.0') + : null, ); } } diff --git a/lib/pages/analytics/modules/air_quality/blocs/air_quality_distribution/air_quality_distribution_bloc.dart b/lib/pages/analytics/modules/air_quality/blocs/air_quality_distribution/air_quality_distribution_bloc.dart index 40d51d2b..455dff23 100644 --- a/lib/pages/analytics/modules/air_quality/blocs/air_quality_distribution/air_quality_distribution_bloc.dart +++ b/lib/pages/analytics/modules/air_quality/blocs/air_quality_distribution/air_quality_distribution_bloc.dart @@ -46,11 +46,11 @@ class AirQualityDistributionBloc } } - Future _onClearAirQualityDistribution( + void _onClearAirQualityDistribution( ClearAirQualityDistribution event, Emitter emit, - ) async { - emit(const AirQualityDistributionState()); + ) { + emit(AirQualityDistributionState(selectedAqiType: state.selectedAqiType)); } void _onUpdateAqiTypeEvent( diff --git a/lib/pages/analytics/modules/air_quality/blocs/range_of_aqi/range_of_aqi_bloc.dart b/lib/pages/analytics/modules/air_quality/blocs/range_of_aqi/range_of_aqi_bloc.dart index 88c3715e..326a87a2 100644 --- a/lib/pages/analytics/modules/air_quality/blocs/range_of_aqi/range_of_aqi_bloc.dart +++ b/lib/pages/analytics/modules/air_quality/blocs/range_of_aqi/range_of_aqi_bloc.dart @@ -75,6 +75,6 @@ class RangeOfAqiBloc extends Bloc { ClearRangeOfAqiEvent event, Emitter emit, ) { - emit(const RangeOfAqiState()); + emit(RangeOfAqiState(selectedAqiType: state.selectedAqiType)); } } diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart index f7be6ee3..7b6b113a 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart @@ -34,6 +34,7 @@ class AqiDistributionChartTitle extends StatelessWidget { alignment: AlignmentDirectional.centerEnd, fit: BoxFit.scaleDown, child: AqiTypeDropdown( + selectedAqiType: context.watch().state.selectedAqiType, onChanged: (value) { if (value != null) { final bloc = context.read(); diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_type_dropdown.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_type_dropdown.dart index 6640c717..8233fe5a 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/aqi_type_dropdown.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_type_dropdown.dart @@ -18,19 +18,20 @@ enum AqiType { } class AqiTypeDropdown extends StatefulWidget { - const AqiTypeDropdown({super.key, required this.onChanged}); + const AqiTypeDropdown({ + required this.onChanged, + this.selectedAqiType, + super.key, + }); final ValueChanged onChanged; + final AqiType? selectedAqiType; @override State createState() => _AqiTypeDropdownState(); } class _AqiTypeDropdownState extends State { - AqiType? _selectedItem = AqiType.aqi; - - void _updateSelectedItem(AqiType? item) => setState(() => _selectedItem = item); - @override Widget build(BuildContext context) { return Container( @@ -41,8 +42,8 @@ class _AqiTypeDropdownState extends State { width: 1, ), ), - child: DropdownButton( - value: _selectedItem, + child: DropdownButton( + value: widget.selectedAqiType, isDense: true, borderRadius: BorderRadius.circular(16), dropdownColor: ColorsManager.whiteColors, @@ -59,10 +60,7 @@ class _AqiTypeDropdownState extends State { items: AqiType.values .map((e) => DropdownMenuItem(value: e, child: Text(e.value))) .toList(), - onChanged: (value) { - _updateSelectedItem(value); - widget.onChanged(value); - }, + onChanged: widget.onChanged, ), ); } diff --git a/lib/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart_title.dart b/lib/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart_title.dart index 1b0da288..421fbb13 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart_title.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart_title.dart @@ -63,15 +63,15 @@ class RangeOfAqiChartTitle extends StatelessWidget { fit: BoxFit.scaleDown, alignment: AlignmentDirectional.centerEnd, child: AqiTypeDropdown( + selectedAqiType: context.watch().state.selectedAqiType, onChanged: (value) { final spaceTreeState = context.read().state; final spaceUuid = spaceTreeState.selectedSpaces.firstOrNull; - - if (spaceUuid == null) return; - if (value != null) { context.read().add(UpdateAqiTypeEvent(value)); } + + if (spaceUuid == null) return; }, ), ), diff --git a/lib/pages/device_managment/ac/bloc/ac_bloc.dart b/lib/pages/device_managment/ac/bloc/ac_bloc.dart index 2ac26b41..85e119f1 100644 --- a/lib/pages/device_managment/ac/bloc/ac_bloc.dart +++ b/lib/pages/device_managment/ac/bloc/ac_bloc.dart @@ -84,9 +84,14 @@ class AcBloc extends Bloc { statusList.add(Status(code: element['code'], value: element['value'])); }); + + deviceStatus = + AcStatusModel.fromJson(usersMap['productUuid'], statusList); + deviceStatus = AcStatusModel.fromJson(usersMap['productUuid'], statusList); print('Device status updated: ${deviceStatus.acSwitch}'); + if (!isClosed) { add(AcStatusUpdated(deviceStatus)); } diff --git a/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart b/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart index 98b0c195..2c42caa6 100644 --- a/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart +++ b/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart @@ -16,11 +16,12 @@ class DeviceManagementBloc int _onlineCount = 0; int _offlineCount = 0; int _lowBatteryCount = 0; - List _selectedDevices = []; + final List _selectedDevices = []; List _filteredDevices = []; String currentProductName = ''; String? currentCommunity; String? currentUnitName; + String subSpaceName = ''; DeviceManagementBloc() : super(DeviceManagementInitial()) { on(_onFetchDevices); @@ -31,25 +32,26 @@ class DeviceManagementBloc on(_onResetFilters); on(_onResetSelectedDevices); on(_onUpdateSelection); + on(_onUpdateDeviceName); + on(_onUpdateSubSpaceName); } Future _onFetchDevices( FetchDevices event, Emitter emit) async { emit(DeviceManagementLoading()); try { - List devices = []; + var devices = []; _devices.clear(); - var spaceBloc = event.context.read(); + final spaceBloc = event.context.read(); final projectUuid = await ProjectManager.getProjectUUID() ?? ''; if (spaceBloc.state.selectedCommunities.isEmpty) { - devices = - await DevicesManagementApi().fetchDevices('', '', projectUuid); + devices = await DevicesManagementApi().fetchDevices('', '', projectUuid); } else { - for (var community in spaceBloc.state.selectedCommunities) { - List spacesList = + for (final community in spaceBloc.state.selectedCommunities) { + final spacesList = spaceBloc.state.selectedCommunityAndSpaces[community] ?? []; - for (var space in spacesList) { + for (final space in spacesList) { devices.addAll(await DevicesManagementApi() .fetchDevices(community, space, projectUuid)); } @@ -74,7 +76,7 @@ class DeviceManagementBloc } } - void _onFilterDevices( + Future _onFilterDevices( FilterDevices event, Emitter emit) async { if (_devices.isNotEmpty) { _filteredDevices = List.from(_devices.where((device) { @@ -156,8 +158,7 @@ class DeviceManagementBloc add(FilterDevices(_getFilterFromIndex(_selectedIndex))); } - void _onSelectDevice( - SelectDevice event, Emitter emit) { + void _onSelectDevice(SelectDevice event, Emitter emit) { final selectedUuid = event.selectedDevice.uuid; if (_selectedDevices.any((device) => device.uuid == selectedUuid)) { @@ -166,9 +167,9 @@ class DeviceManagementBloc _selectedDevices.add(event.selectedDevice); } - List clonedSelectedDevices = List.from(_selectedDevices); + final clonedSelectedDevices = List.from(_selectedDevices); - bool isControlButtonEnabled = + final isControlButtonEnabled = _checkIfControlButtonEnabled(clonedSelectedDevices); if (state is DeviceManagementLoaded) { @@ -198,8 +199,8 @@ class DeviceManagementBloc void _onUpdateSelection( UpdateSelection event, Emitter emit) { - List selectedDevices = []; - List devicesToSelectFrom = []; + final selectedDevices = []; + var devicesToSelectFrom = []; if (state is DeviceManagementLoaded) { devicesToSelectFrom = (state as DeviceManagementLoaded).devices; @@ -207,7 +208,7 @@ class DeviceManagementBloc devicesToSelectFrom = (state as DeviceManagementFiltered).filteredDevices; } - for (int i = 0; i < event.selectedRows.length; i++) { + for (var i = 0; i < event.selectedRows.length; i++) { if (event.selectedRows[i]) { selectedDevices.add(devicesToSelectFrom[i]); } @@ -253,8 +254,7 @@ class DeviceManagementBloc _onlineCount = _devices.where((device) => device.online == true).length; _offlineCount = _devices.where((device) => device.online == false).length; _lowBatteryCount = _devices - .where((device) => - device.batteryLevel != null && device.batteryLevel! < 20) + .where((device) => device.batteryLevel != null && device.batteryLevel! < 20) .length; } @@ -270,8 +270,8 @@ class DeviceManagementBloc return 'All'; } } - void _onSearchDevices( - SearchDevices event, Emitter emit) { + + void _onSearchDevices(SearchDevices event, Emitter emit) { if ((event.community == null || event.community!.isEmpty) && (event.unitName == null || event.unitName!.isEmpty) && (event.deviceNameOrProductName == null || @@ -300,7 +300,7 @@ class DeviceManagementBloc currentCommunity = event.community; currentUnitName = event.unitName; - List devicesToSearch = _devices; + final devicesToSearch = _devices; if (devicesToSearch.isNotEmpty) { final searchText = event.deviceNameOrProductName?.toLowerCase() ?? ''; @@ -343,5 +343,134 @@ class DeviceManagementBloc } } + void _onUpdateDeviceName( + UpdateDeviceName event, Emitter emit) { + final devices = _devices.map((device) { + if (device.uuid == event.deviceId) { + final modifiedDevice = device.copyWith(name: event.newName); + _selectedDevices.removeWhere((device) => device.uuid == event.deviceId); + _selectedDevices.add(modifiedDevice); + return modifiedDevice; + } + return device; + }).toList(); + + final filteredDevices = _filteredDevices.map((device) { + if (device.uuid == event.deviceId) { + final modifiedDevice = device.copyWith(name: event.newName); + _selectedDevices.removeWhere((device) => device.uuid == event.deviceId); + _selectedDevices.add(modifiedDevice); + return modifiedDevice; + } + return device; + }).toList(); + + _devices = devices; + _filteredDevices = filteredDevices; + + if (state is DeviceManagementLoaded) { + final loaded = state as DeviceManagementLoaded; + final selectedDevices01 = _selectedDevices.map((device) { + if (device.uuid == event.deviceId) { + final modifiedDevice = device.copyWith(name: event.newName); + return modifiedDevice; + } + return device; + }).toList(); + emit(DeviceManagementLoaded( + devices: devices, + selectedIndex: loaded.selectedIndex, + onlineCount: loaded.onlineCount, + offlineCount: loaded.offlineCount, + lowBatteryCount: loaded.lowBatteryCount, + selectedDevice: selectedDevices01, + isControlButtonEnabled: loaded.isControlButtonEnabled, + )); + } else if (state is DeviceManagementFiltered) { + final filtered = state as DeviceManagementFiltered; + final selectedDevices01 = filtered.selectedDevice?.map((device) { + if (device.uuid == event.deviceId) { + final modifiedDevice = device.copyWith(name: event.newName); + return modifiedDevice; + } + return device; + }).toList(); + emit(DeviceManagementFiltered( + filteredDevices: filteredDevices, + selectedIndex: filtered.selectedIndex, + onlineCount: filtered.onlineCount, + offlineCount: filtered.offlineCount, + lowBatteryCount: filtered.lowBatteryCount, + selectedDevice: selectedDevices01, + isControlButtonEnabled: filtered.isControlButtonEnabled, + )); + } + } + + void _onUpdateSubSpaceName( + UpdateSubSpaceName event, Emitter emit) { + final devices = _devices.map((device) { + if (device.uuid == event.deviceId) { + return device.copyWith( + subspace: + device.subspace?.copyWith(subspaceName: event.newSubSpaceName)); + } + return device; + }).toList(); + + final filteredDevices = _filteredDevices.map((device) { + if (device.uuid == event.deviceId) { + return device.copyWith( + subspace: + device.subspace?.copyWith(subspaceName: event.newSubSpaceName)); + } + return device; + }).toList(); + + _devices = devices; + _filteredDevices = filteredDevices; + + if (state is DeviceManagementLoaded) { + final loaded = state as DeviceManagementLoaded; + final selectedDevices = loaded.selectedDevice?.map((device) { + if (device.uuid == event.deviceId) { + return device.copyWith( + subspace: + device.subspace?.copyWith(subspaceName: event.newSubSpaceName)); + } + return device; + }).toList(); + emit(DeviceManagementLoaded( + devices: _devices, + selectedIndex: loaded.selectedIndex, + onlineCount: loaded.onlineCount, + offlineCount: loaded.offlineCount, + lowBatteryCount: loaded.lowBatteryCount, + selectedDevice: selectedDevices, + isControlButtonEnabled: loaded.isControlButtonEnabled, + )); + } else if (state is DeviceManagementFiltered) { + // final filtered = state as DeviceManagementFiltered; + // emit(DeviceManagementFiltered( + // filteredDevices: _filteredDevices, + // selectedIndex: filtered.selectedIndex, + // onlineCount: filtered.onlineCount, + // offlineCount: filtered.offlineCount, + // lowBatteryCount: filtered.lowBatteryCount, + // selectedDevice: filtered.selectedDevice, + // isControlButtonEnabled: filtered.isControlButtonEnabled, + // )); + } + } + + void changeSubspaceName( + String deviceId, String newSubSpaceName, String subspaceId) { + add(UpdateSubSpaceName( + deviceId: deviceId, + newSubSpaceName: newSubSpaceName, + subspaceId: subspaceId, + )); + } + List get selectedDevices => _selectedDevices; } diff --git a/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_event.dart b/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_event.dart index 5292de0e..e3b3acac 100644 --- a/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_event.dart +++ b/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_event.dart @@ -70,3 +70,21 @@ class UpdateSelection extends DeviceManagementEvent { const UpdateSelection(this.selectedRows); } + +class UpdateDeviceName extends DeviceManagementEvent { + final String deviceId; + final String newName; + + const UpdateDeviceName({required this.deviceId, required this.newName}); +} + +class UpdateSubSpaceName extends DeviceManagementEvent { + final String deviceId; + final String newSubSpaceName; + final String subspaceId; + + const UpdateSubSpaceName( + {required this.deviceId, + required this.newSubSpaceName, + required this.subspaceId}); +} diff --git a/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart b/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart index 5586a310..08bca73c 100644 --- a/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart +++ b/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart @@ -7,6 +7,8 @@ import 'package:syncrow_web/pages/device_managment/ceiling_sensor/view/ceiling_s import 'package:syncrow_web/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart'; import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_status_view.dart'; import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/view/curtain_module_batch.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/view/curtain_module_items.dart'; import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart'; import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart'; @@ -18,6 +20,7 @@ import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_view.dar import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_control_view.dart'; import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_sensor_batch_view.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/view/one_gang_glass_batch_control_view.dart'; +import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/view/wall_light_batch_control.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart'; import 'package:syncrow_web/pages/device_managment/power_clamp/view/power_clamp_batch_control_view.dart'; @@ -39,8 +42,6 @@ import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heate import 'package:syncrow_web/pages/device_managment/water_leak/view/water_leak_batch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/water_leak/view/water_leak_control_view.dart'; -import '../../one_g_glass_switch/view/one_gang_glass_switch_control_view.dart'; - mixin RouteControlsBasedCode { Widget routeControlsWidgets({required AllDevicesModel device}) { switch (device.productType) { @@ -84,6 +85,10 @@ mixin RouteControlsBasedCode { return CurtainStatusControlsView( deviceId: device.uuid!, ); + case 'CUR_2': + return CurtainModuleItems( + deviceId: device.uuid!, + ); case 'AC': return AcDeviceControlsView(device: device); case 'WH': @@ -107,7 +112,7 @@ mixin RouteControlsBasedCode { case 'SOS': return SosDeviceControlsView(device: device); - case 'NCPS': + case 'NCPS': return FlushMountedPresenceSensorControlView(device: device); default: return const SizedBox(); @@ -132,76 +137,140 @@ mixin RouteControlsBasedCode { switch (devices.first.productType) { case '1G': return WallLightBatchControlView( - deviceIds: devices.where((e) => (e.productType == '1G')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == '1G') + .map((e) => e.uuid!) + .toList(), ); case '2G': return TwoGangBatchControlView( - deviceIds: devices.where((e) => (e.productType == '2G')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == '2G') + .map((e) => e.uuid!) + .toList(), ); case '3G': return LivingRoomBatchControlsView( - deviceIds: devices.where((e) => (e.productType == '3G')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == '3G') + .map((e) => e.uuid!) + .toList(), ); case '1GT': return OneGangGlassSwitchBatchControlView( - deviceIds: devices.where((e) => (e.productType == '1GT')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == '1GT') + .map((e) => e.uuid!) + .toList(), ); case '2GT': return TwoGangGlassSwitchBatchControlView( - deviceIds: devices.where((e) => (e.productType == '2GT')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == '2GT') + .map((e) => e.uuid!) + .toList(), ); case '3GT': return ThreeGangGlassSwitchBatchControlView( - deviceIds: devices.where((e) => (e.productType == '3GT')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == '3GT') + .map((e) => e.uuid!) + .toList(), ); case 'GW': return GatewayBatchControlView( - gatewayIds: devices.where((e) => (e.productType == 'GW')).map((e) => e.uuid!).toList(), + gatewayIds: devices + .where((e) => e.productType == 'GW') + .map((e) => e.uuid!) + .toList(), ); case 'DL': return DoorLockBatchControlView( - devicesIds: devices.where((e) => (e.productType == 'DL')).map((e) => e.uuid!).toList()); + devicesIds: devices + .where((e) => e.productType == 'DL') + .map((e) => e.uuid!) + .toList()); case 'WPS': return WallSensorBatchControlView( - devicesIds: devices.where((e) => (e.productType == 'WPS')).map((e) => e.uuid!).toList()); + devicesIds: devices + .where((e) => e.productType == 'WPS') + .map((e) => e.uuid!) + .toList()); case 'CPS': return CeilingSensorBatchControlView( - devicesIds: devices.where((e) => (e.productType == 'CPS')).map((e) => e.uuid!).toList(), + devicesIds: devices + .where((e) => e.productType == 'CPS') + .map((e) => e.uuid!) + .toList(), ); case 'CUR': return CurtainBatchStatusView( - devicesIds: devices.where((e) => (e.productType == 'CUR')).map((e) => e.uuid!).toList(), + devicesIds: devices + .where((e) => e.productType == 'CUR') + .map((e) => e.uuid!) + .toList(), + ); + case 'CUR_2': + return CurtainModuleBatchView( + devicesIds: devices + .where((e) => e.productType == 'CUR_2') + .map((e) => e.uuid!) + .toList(), ); case 'AC': return AcDeviceBatchControlView( - devicesIds: devices.where((e) => (e.productType == 'AC')).map((e) => e.uuid!).toList()); + devicesIds: devices + .where((e) => e.productType == 'AC') + .map((e) => e.uuid!) + .toList()); case 'WH': return WaterHEaterBatchControlView( - deviceIds: devices.where((e) => (e.productType == 'WH')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == 'WH') + .map((e) => e.uuid!) + .toList(), ); case 'DS': return MainDoorSensorBatchView( - devicesIds: devices.where((e) => (e.productType == 'DS')).map((e) => e.uuid!).toList(), + devicesIds: devices + .where((e) => e.productType == 'DS') + .map((e) => e.uuid!) + .toList(), ); case 'GD': return GarageDoorBatchControlView( - deviceIds: devices.where((e) => (e.productType == 'GD')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == 'GD') + .map((e) => e.uuid!) + .toList(), ); case 'WL': return WaterLeakBatchControlView( - deviceIds: devices.where((e) => (e.productType == 'WL')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == 'WL') + .map((e) => e.uuid!) + .toList(), ); case 'PC': return PowerClampBatchControlView( - deviceIds: devices.where((e) => (e.productType == 'PC')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == 'PC') + .map((e) => e.uuid!) + .toList(), ); case 'SOS': return SOSBatchControlView( - deviceIds: devices.where((e) => (e.productType == 'SOS')).map((e) => e.uuid!).toList(), + deviceIds: devices + .where((e) => e.productType == 'SOS') + .map((e) => e.uuid!) + .toList(), ); case 'NCPS': return FlushMountedPresenceSensorBatchControlView( - devicesIds: devices.where((e) => (e.productType == 'NCPS')).map((e) => e.uuid!).toList(), + devicesIds: devices + .where((e) => e.productType == 'NCPS') + .map((e) => e.uuid!) + .toList(), ); default: return const SizedBox(); diff --git a/lib/pages/device_managment/all_devices/models/device_status.dart b/lib/pages/device_managment/all_devices/models/device_status.dart index b78f2a30..f4efe36b 100644 --- a/lib/pages/device_managment/all_devices/models/device_status.dart +++ b/lib/pages/device_managment/all_devices/models/device_status.dart @@ -57,6 +57,16 @@ class Status { }; } + Status copyWith({ + String? code, + dynamic value, + }) { + return Status( + code: code ?? this.code, + value: value ?? this.value, + ); + } + factory Status.fromJson(String source) => Status.fromMap(json.decode(source)); String toJson() => json.encode(toMap()); diff --git a/lib/pages/device_managment/all_devices/models/device_subspace.model.dart b/lib/pages/device_managment/all_devices/models/device_subspace.model.dart index dc2386de..5d5f44bf 100644 --- a/lib/pages/device_managment/all_devices/models/device_subspace.model.dart +++ b/lib/pages/device_managment/all_devices/models/device_subspace.model.dart @@ -44,4 +44,20 @@ class DeviceSubspace { static List> listToJson(List subspaces) { return subspaces.map((subspace) => subspace.toJson()).toList(); } + + DeviceSubspace copyWith({ + String? uuid, + DateTime? createdAt, + DateTime? updatedAt, + String? subspaceName, + bool? disabled, + }) { + return DeviceSubspace( + uuid: uuid ?? this.uuid, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + subspaceName: subspaceName ?? this.subspaceName, + disabled: disabled ?? this.disabled, + ); + } } 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 e491214d..21fd1193 100644 --- a/lib/pages/device_managment/all_devices/models/devices_model.dart +++ b/lib/pages/device_managment/all_devices/models/devices_model.dart @@ -588,4 +588,72 @@ SOS "NCPS": DeviceType.NCPS, "PC": DeviceType.PC, }; + + AllDevicesModel copyWith({ + DevicesModelRoom? room, + DeviceSubspace? subspace, + DevicesModelUnit? unit, + DeviceCommunityModel? community, + String? productUuid, + String? productType, + String? permissionType, + int? activeTime, + String? category, + String? categoryName, + int? createTime, + String? gatewayId, + String? icon, + String? ip, + String? lat, + String? localKey, + String? lon, + String? model, + String? name, + String? nodeId, + bool? online, + String? ownerId, + bool? sub, + String? timeZone, + int? updateTime, + String? uuid, + int? batteryLevel, + String? productName, + List? spaces, + List? deviceTags, + DeviceSubSpace? deviceSubSpace, + }) { + return AllDevicesModel( + room: room ?? this.room, + subspace: subspace ?? this.subspace, + unit: unit ?? this.unit, + community: community ?? this.community, + productUuid: productUuid ?? this.productUuid, + productType: productType ?? this.productType, + permissionType: permissionType ?? this.permissionType, + activeTime: activeTime ?? this.activeTime, + category: category ?? this.category, + categoryName: categoryName ?? this.categoryName, + createTime: createTime ?? this.createTime, + gatewayId: gatewayId ?? this.gatewayId, + icon: icon ?? this.icon, + ip: ip ?? this.ip, + lat: lat ?? this.lat, + localKey: localKey ?? this.localKey, + lon: lon ?? this.lon, + model: model ?? this.model, + name: name ?? this.name, + nodeId: nodeId ?? this.nodeId, + online: online ?? this.online, + ownerId: ownerId ?? this.ownerId, + sub: sub ?? this.sub, + timeZone: timeZone ?? this.timeZone, + updateTime: updateTime ?? this.updateTime, + uuid: uuid ?? this.uuid, + batteryLevel: batteryLevel ?? this.batteryLevel, + productName: productName ?? this.productName, + spaces: spaces ?? this.spaces, + deviceTags: deviceTags ?? this.deviceTags, + deviceSubSpace: deviceSubSpace ?? this.deviceSubSpace, + ); + } } diff --git a/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart b/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart index c865a5dc..7ca33d1d 100644 --- a/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart +++ b/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart @@ -23,6 +23,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { @override Widget build(BuildContext context) { return BlocBuilder( + buildWhen: (previous, current) => previous != current, builder: (context, state) { List devicesToShow = []; int selectedIndex = 0; @@ -31,7 +32,6 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { int lowBatteryCount = 0; bool isControlButtonEnabled = false; List selectedDevices = []; - if (state is DeviceManagementLoaded) { devicesToShow = state.devices; selectedIndex = state.selectedIndex; @@ -111,6 +111,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { onPressed: isControlButtonEnabled ? () { if (isAnyDeviceOffline) { + ScaffoldMessenger.of(context) + .clearSnackBars(); ScaffoldMessenger.of(context) .showSnackBar( const SnackBar( @@ -190,7 +192,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { 'Product Name', 'Device ID', 'Space Name', - 'location', + 'Location', 'Battery Level', 'Installation Date and Time', 'Status', @@ -242,7 +244,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { .map((device) => device.uuid!) .toList(), isEmpty: devicesToShow.isEmpty, - onSettingsPressed: (rowIndex) { + onSettingsPressed: (rowIndex) async { final device = devicesToShow[rowIndex]; showDeviceSettingsSidebar(context, device); }, @@ -264,7 +266,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { barrierDismissible: true, barrierLabel: "Device Settings", transitionDuration: const Duration(milliseconds: 300), - pageBuilder: (context, anim1, anim2) { + pageBuilder: (_, anim1, anim2) { return Align( alignment: Alignment.centerRight, child: Material( @@ -274,6 +276,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { child: DeviceSettingsPanel( device: device, onClose: () => Navigator.of(context).pop(), + deviceManagementBloc: context.read(), ), ), ), diff --git a/lib/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart b/lib/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart new file mode 100644 index 00000000..b40d7ea6 --- /dev/null +++ b/lib/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart @@ -0,0 +1,379 @@ +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/models/curtain_module_model.dart'; +import 'package:syncrow_web/services/batch_control_devices_service.dart'; +import 'package:syncrow_web/services/control_device_service.dart'; +import 'package:syncrow_web/services/devices_mang_api.dart'; + +part 'curtain_module_event.dart'; +part 'curtain_module_state.dart'; + +class CurtainModuleBloc extends Bloc { + final ControlDeviceService controlDeviceService; + final BatchControlDevicesService batchControlDevicesService; + StreamSubscription? _firebaseSubscription; + + CurtainModuleBloc({ + required this.controlDeviceService, + required this.batchControlDevicesService, + }) : super(CurtainModuleInitial()) { + on(_onFetchCurtainModuleStatusEvent); + on(_onSendCurtainPercentToApiEvent); + on(_onOpenCurtainEvent); + on(_onCloseCurtainEvent); + on(_onStopCurtainEvent); + on(_onChangeTimerControlEvent); + on(_onChageCurCalibrationEvent); + on(_onChangeElecMachineryModeEvent); + on(_onChangeControlBackEvent); + on(_onChangeControlBackModeEvent); + on(_onChangeCurtainModuleStatusEvent); + //batch + on(_onFetchCurtainModuleBatchStatus); + on(_onSendCurtainBatchPercentToApiEvent); + on(_onOpenCurtainBatchEvent); + on(_onCloseCurtainBatchEvent); + on(_onStopCurtainBatchEvent); + on(_onFactoryReset); + } + + Future _onFetchCurtainModuleStatusEvent( + FetchCurtainModuleStatusEvent event, + Emitter emit, + ) async { + emit(CurtainModuleLoading()); + final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); + final result = Map.fromEntries( + status.status.map((element) => MapEntry(element.code, element.value)), + ); + + emit(CurtainModuleStatusLoaded( + curtainModuleStatus: CurtainModuleStatusModel.fromJson(result), + )); + Map statusMap = {}; + final ref = + FirebaseDatabase.instance.ref('device-status/${event.deviceId}'); + final stream = ref.onValue; + + stream.listen((DatabaseEvent DatabaseEvent) async { + if (DatabaseEvent.snapshot.value == null) return; + + Map usersMap = + DatabaseEvent.snapshot.value as Map; + + List statusList = []; + + usersMap['status'].forEach((element) { + statusList.add(Status(code: element['code'], value: element['value'])); + }); + + statusMap = { + for (final element in statusList) element.code: element.value, + }; + if (!isClosed) { + add( + ChangeCurtainModuleStatusEvent( + deviceId: event.deviceId, + status: CurtainModuleStatusModel.fromJson(statusMap), + ), + ); + } + }); + } + + Future _onChangeCurtainModuleStatusEvent( + ChangeCurtainModuleStatusEvent event, + Emitter emit, + ) async { + emit(CurtainModuleLoading()); + emit(CurtainModuleStatusLoaded(curtainModuleStatus: event.status)); + } + + Future _onSendCurtainPercentToApiEvent( + SendCurtainPercentToApiEvent event, + Emitter emit, + ) async { + try { + await controlDeviceService.controlDevice( + deviceUuid: event.deviceId, + status: event.status, + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to send control command: $e')); + } + } + + Future _onOpenCurtainEvent( + OpenCurtainEvent event, + Emitter emit, + ) async { + try { + await controlDeviceService.controlDevice( + deviceUuid: event.deviceId, + status: Status(code: 'control', value: 'open'), + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to open curtain: $e')); + } + } + + Future _onCloseCurtainEvent( + CloseCurtainEvent event, + Emitter emit, + ) async { + try { + await controlDeviceService.controlDevice( + deviceUuid: event.deviceId, + status: Status(code: 'control', value: 'close'), + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to close curtain: $e')); + } + } + + Future _onStopCurtainEvent( + StopCurtainEvent event, + Emitter emit, + ) async { + try { + await controlDeviceService.controlDevice( + deviceUuid: event.deviceId, + status: Status(code: 'control', value: 'stop'), + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to stop curtain: $e')); + } + } + + Future _onChangeTimerControlEvent( + ChangeTimerControlEvent event, + Emitter emit, + ) async { + try { + if (event.timControl < 10 || event.timControl > 120) { + emit(const CurtainModuleError( + message: 'Timer control value must be between 10 and 120')); + return; + } + await controlDeviceService.controlDevice( + deviceUuid: event.deviceId, + status: Status( + code: 'tr_timecon', + value: event.timControl, + ), + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to change timer control: $e')); + } + } + + Future _onChageCurCalibrationEvent( + CurCalibrationEvent event, + Emitter emit, + ) async { + try { + await controlDeviceService.controlDevice( + deviceUuid: event.deviceId, + status: Status(code: 'cur_calibration', value: 'start'), + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to start calibration: $e')); + } + } + + Future _onChangeElecMachineryModeEvent( + ChangeElecMachineryModeEvent event, + Emitter emit, + ) async { + try { + await controlDeviceService.controlDevice( + deviceUuid: event.deviceId, + status: Status( + code: 'elec_machinery_mode', + value: event.elecMachineryMode, + ), + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to change mode: $e')); + } + } + + Future _onChangeControlBackEvent( + ChangeControlBackEvent event, + Emitter emit, + ) async { + try { + await controlDeviceService.controlDevice( + deviceUuid: event.deviceId, + status: Status( + code: 'control_back', + value: event.controlBack, + ), + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to change control back: $e')); + } + } + + Future _onChangeControlBackModeEvent( + ChangeControlBackModeEvent event, + Emitter emit, + ) async { + try { + await controlDeviceService.controlDevice( + deviceUuid: event.deviceId, + status: Status( + code: 'control_back_mode', + value: event.controlBackMode, + ), + ); + } catch (e) { + emit(CurtainModuleError( + message: 'Failed to change control back mode: $e')); + } + } + + FutureOr _onFetchCurtainModuleBatchStatus( + CurtainModuleFetchBatchStatusEvent event, + Emitter emit, + ) async { + emit(CurtainModuleLoading()); + try { + final status = + await DevicesManagementApi().getBatchStatus(event.devicesIds); + + final result = Map.fromEntries( + status.status.map((element) => MapEntry(element.code, element.value)), + ); + + emit(CurtainModuleStatusLoaded( + curtainModuleStatus: CurtainModuleStatusModel.fromJson(result), + )); + + Map statusMap = {}; + final ref = FirebaseDatabase.instance + .ref('device-status/${event.devicesIds.first}'); + final stream = ref.onValue; + + stream.listen((DatabaseEvent DatabaseEvent) async { + if (DatabaseEvent.snapshot.value == null) return; + + Map usersMap = + DatabaseEvent.snapshot.value as Map; + + List statusList = []; + + usersMap['status'].forEach((element) { + statusList + .add(Status(code: element['code'], value: element['value'])); + }); + + statusMap = { + for (final element in statusList) element.code: element.value, + }; + if (!isClosed) { + add( + ChangeCurtainModuleStatusEvent( + deviceId: event.devicesIds.first, + status: CurtainModuleStatusModel.fromJson(statusMap), + ), + ); + } + }); + } catch (e) { + emit(CurtainModuleError(message: e.toString())); + } + } + + Future _onSendCurtainBatchPercentToApiEvent( + SendCurtainBatchPercentToApiEvent event, + Emitter emit, + ) async { + try { + await batchControlDevicesService.batchControlDevices( + uuids: event.devicesId, + code: event.status.code, + value: event.status.value, + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to send control command: $e')); + } + } + + Future _onOpenCurtainBatchEvent( + OpenCurtainBatchEvent event, + Emitter emit, + ) async { + try { + await batchControlDevicesService.batchControlDevices( + uuids: event.devicesId, + code: 'control', + value: 'open', + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to open curtain: $e')); + } + } + + Future _onCloseCurtainBatchEvent( + CloseCurtainBatchEvent event, + Emitter emit, + ) async { + try { + await batchControlDevicesService.batchControlDevices( + uuids: event.devicesId, + code: 'control', + value: 'close', + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to close curtain: $e')); + } + } + + Future _onStopCurtainBatchEvent( + StopCurtainBatchEvent event, + Emitter emit, + ) async { + try { + await batchControlDevicesService.batchControlDevices( + uuids: event.devicesId, + code: 'control', + value: 'stop', + ); + } catch (e) { + emit(CurtainModuleError(message: 'Failed to stop curtain: $e')); + } + } + + Future _onFactoryReset( + CurtainModuleFactoryReset event, + Emitter emit, + ) async { + emit(CurtainModuleLoading()); + try { + final response = await DevicesManagementApi().factoryReset( + event.factoryReset, + event.deviceId, + ); + if (!response) { + emit(const CurtainModuleError(message: 'Failed')); + } else { + add( + FetchCurtainModuleStatusEvent(deviceId: event.deviceId), + ); + } + } catch (e) { + emit(CurtainModuleError(message: e.toString())); + } + } + + @override + Future close() async { + await _firebaseSubscription?.cancel(); + return super.close(); + } +} diff --git a/lib/pages/device_managment/curtain_module/bloc/curtain_module_event.dart b/lib/pages/device_managment/curtain_module/bloc/curtain_module_event.dart new file mode 100644 index 00000000..4eec030d --- /dev/null +++ b/lib/pages/device_managment/curtain_module/bloc/curtain_module_event.dart @@ -0,0 +1,193 @@ +part of 'curtain_module_bloc.dart'; + +sealed class CurtainModuleEvent extends Equatable { + const CurtainModuleEvent(); + + @override + List get props => []; +} + +class FetchCurtainModuleStatusEvent extends CurtainModuleEvent { + final String deviceId; + const FetchCurtainModuleStatusEvent({required this.deviceId}); + + @override + List get props => [deviceId]; +} + +class SendCurtainPercentToApiEvent extends CurtainModuleEvent { + final String deviceId; + final Status status; + + const SendCurtainPercentToApiEvent({ + required this.deviceId, + required this.status, + }); + + @override + List get props => [deviceId, status]; +} + +class OpenCurtainEvent extends CurtainModuleEvent { + final String deviceId; + + const OpenCurtainEvent({required this.deviceId}); + + @override + List get props => [deviceId]; +} + +class CloseCurtainEvent extends CurtainModuleEvent { + final String deviceId; + + const CloseCurtainEvent({required this.deviceId}); + + @override + List get props => [deviceId]; +} + +class StopCurtainEvent extends CurtainModuleEvent { + final String deviceId; + + const StopCurtainEvent({required this.deviceId}); + + @override + List get props => [deviceId]; +} + +class ChangeTimerControlEvent extends CurtainModuleEvent { + final String deviceId; + final int timControl; + + const ChangeTimerControlEvent({ + required this.deviceId, + required this.timControl, + }); + + @override + List get props => [deviceId, timControl]; +} + +class CurCalibrationEvent extends CurtainModuleEvent { + final String deviceId; + + const CurCalibrationEvent({ + required this.deviceId, + }); + + @override + List get props => [deviceId]; +} + +class ChangeElecMachineryModeEvent extends CurtainModuleEvent { + final String deviceId; + final String elecMachineryMode; + + const ChangeElecMachineryModeEvent({ + required this.deviceId, + required this.elecMachineryMode, + }); + + @override + List get props => [deviceId, elecMachineryMode]; +} + +class ChangeControlBackEvent extends CurtainModuleEvent { + final String deviceId; + final String controlBack; + + const ChangeControlBackEvent({ + required this.deviceId, + required this.controlBack, + }); + + @override + List get props => [deviceId, controlBack]; +} + +class ChangeControlBackModeEvent extends CurtainModuleEvent { + final String deviceId; + final String controlBackMode; + + const ChangeControlBackModeEvent({ + required this.deviceId, + required this.controlBackMode, + }); + + @override + List get props => [deviceId, controlBackMode]; +} + +class ChangeCurtainModuleStatusEvent extends CurtainModuleEvent { + final String deviceId; + final CurtainModuleStatusModel status; + + const ChangeCurtainModuleStatusEvent({ + required this.deviceId, + required this.status, + }); + + @override + List get props => [deviceId, status]; +} + +///batch +class CurtainModuleFetchBatchStatusEvent extends CurtainModuleEvent { + final List devicesIds; + + const CurtainModuleFetchBatchStatusEvent(this.devicesIds); + + @override + List get props => [devicesIds]; +} + +class SendCurtainBatchPercentToApiEvent extends CurtainModuleEvent { + final List devicesId; + final Status status; + + const SendCurtainBatchPercentToApiEvent({ + required this.devicesId, + required this.status, + }); + + @override + List get props => [devicesId, status]; +} + +class OpenCurtainBatchEvent extends CurtainModuleEvent { + final List devicesId; + + const OpenCurtainBatchEvent({required this.devicesId}); + + @override + List get props => [devicesId]; +} + +class CloseCurtainBatchEvent extends CurtainModuleEvent { + final List devicesId; + + const CloseCurtainBatchEvent({required this.devicesId}); + + @override + List get props => [devicesId]; +} + +class StopCurtainBatchEvent extends CurtainModuleEvent { + final List devicesId; + + const StopCurtainBatchEvent({required this.devicesId}); + + @override + List get props => [devicesId]; +} + +class CurtainModuleFactoryReset extends CurtainModuleEvent { + final String deviceId; + final FactoryResetModel factoryReset; + + const CurtainModuleFactoryReset( + {required this.deviceId, required this.factoryReset}); + + @override + List get props => [deviceId, factoryReset]; +} diff --git a/lib/pages/device_managment/curtain_module/bloc/curtain_module_state.dart b/lib/pages/device_managment/curtain_module/bloc/curtain_module_state.dart new file mode 100644 index 00000000..02ef9279 --- /dev/null +++ b/lib/pages/device_managment/curtain_module/bloc/curtain_module_state.dart @@ -0,0 +1,37 @@ +part of 'curtain_module_bloc.dart'; + +sealed class CurtainModuleState extends Equatable { + const CurtainModuleState(); + + @override + List get props => []; +} + +class CurtainModuleInitial extends CurtainModuleState {} + +class CurtainModuleLoading extends CurtainModuleState {} + +class CurtainModuleError extends CurtainModuleState { + final String message; + const CurtainModuleError({required this.message}); + + @override + List get props => [message]; +} + +class CurtainModuleStatusLoaded extends CurtainModuleState { + final CurtainModuleStatusModel curtainModuleStatus; + + const CurtainModuleStatusLoaded({required this.curtainModuleStatus}); + + @override + List get props => [curtainModuleStatus]; +} +class CurtainModuleStatusUpdated extends CurtainModuleState { + final CurtainModuleStatusModel curtainModuleStatus; + + const CurtainModuleStatusUpdated({required this.curtainModuleStatus}); + + @override + List get props => [curtainModuleStatus]; +} diff --git a/lib/pages/device_managment/curtain_module/models/curtain_module_model.dart b/lib/pages/device_managment/curtain_module/models/curtain_module_model.dart new file mode 100644 index 00000000..0b6d23fb --- /dev/null +++ b/lib/pages/device_managment/curtain_module/models/curtain_module_model.dart @@ -0,0 +1,53 @@ + +enum CurtainModuleControl { + open, + close, + stop, +} + +// enum CurtainControlBackMode { +// foward, +// backward, +// } + +class CurtainModuleStatusModel { + CurtainModuleControl control; + int percentControl; + String curCalibration; + // CurtainControlBackMode controlBackmode; + int trTimeControl; + String elecMachineryMode; + String controlBack; + CurtainModuleStatusModel({ + required this.control, + required this.percentControl, + required this.curCalibration, + // required this.controlBackmode, + required this.trTimeControl, + required this.controlBack, + required this.elecMachineryMode, + }); + factory CurtainModuleStatusModel.zero() => CurtainModuleStatusModel( + control: CurtainModuleControl.stop, + percentControl: 0, + // controlBackmode: CurtainControlBackMode.foward, + curCalibration: '', + trTimeControl: 0, + controlBack: '', + elecMachineryMode: '', + ); + + factory CurtainModuleStatusModel.fromJson(Map json) { + return CurtainModuleStatusModel( + control: CurtainModuleControl.values.firstWhere( + (e) => e.toString() == json['control'] as String, + orElse: () => CurtainModuleControl.stop, + ), + percentControl: json['percent_control'] as int? ?? 0, + curCalibration: json['cur_calibration'] as String? ?? '', + trTimeControl: json['tr_timecon'] as int? ?? 0, + elecMachineryMode: json['elec_machinery_mode'] as String? ?? '', + controlBack: json['control_back'] as String? ?? '', + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/view/curtain_module_batch.dart b/lib/pages/device_managment/curtain_module/view/curtain_module_batch.dart new file mode 100644 index 00000000..bd28cd8a --- /dev/null +++ b/lib/pages/device_managment/curtain_module/view/curtain_module_batch.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/curtain_movment_widget.dart'; +import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart'; +import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart'; +import 'package:syncrow_web/services/batch_control_devices_service.dart'; +import 'package:syncrow_web/services/control_device_service.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class CurtainModuleBatchView extends StatelessWidget { + final List devicesIds; + const CurtainModuleBatchView({ + super.key, + required this.devicesIds, + }); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => CurtainModuleBloc( + controlDeviceService: RemoteControlDeviceService(), + batchControlDevicesService: RemoteBatchControlDevicesService()) + ..add(CurtainModuleFetchBatchStatusEvent(devicesIds)), + child: _buildStatusControls(context), + ); + } + + Widget _buildStatusControls(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 30), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ControlCurtainMovementWidget( + devicesId: devicesIds, + ), + const SizedBox( + height: 10, + ), + SizedBox( + height: 120, + // width: 350, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Expanded( + // child: + FactoryResetWidget( + callFactoryReset: () { + context.read().add( + CurtainModuleFactoryReset( + deviceId: devicesIds.first, + factoryReset: + FactoryResetModel(devicesUuid: devicesIds), + ), + ); + }, + ), + // ), + // Expanded( + // child: IconNameStatusContainer( + // isFullIcon: false, + // name: 'Firmware Update', + // icon: Assets.firmware, + // onTap: () {}, + // status: false, + // textColor: ColorsManager.blackColor, + // ), + // ) + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/view/curtain_module_items.dart b/lib/pages/device_managment/curtain_module/view/curtain_module_items.dart new file mode 100644 index 00000000..82c812ce --- /dev/null +++ b/lib/pages/device_managment/curtain_module/view/curtain_module_items.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/curtain_movment_widget.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/prefrences_dialog.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart'; +import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart'; +import 'package:syncrow_web/services/batch_control_devices_service.dart'; +import 'package:syncrow_web/services/control_device_service.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class CurtainModuleItems extends StatelessWidget with HelperResponsiveLayout { + final String deviceId; + const CurtainModuleItems({ + super.key, + required this.deviceId, + }); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => CurtainModuleBloc( + controlDeviceService: RemoteControlDeviceService(), + batchControlDevicesService: RemoteBatchControlDevicesService()) + ..add(FetchCurtainModuleStatusEvent(deviceId: deviceId)), + child: BlocBuilder( + builder: (context, state) { + return _buildStatusControls(context); + }, + ), + ); + } + + Widget _buildStatusControls(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 30), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ControlCurtainMovementWidget( + devicesId: [deviceId], + ), + const SizedBox( + height: 10, + ), + SizedBox( + height: 140, + width: 350, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: ScheduleControlButton( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: + BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + category: 'CUR_2', + code: 'control', + + ), + )); + }, + mainText: '', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, + ), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: BlocBuilder( + builder: (context, state) { + if (state is CurtainModuleLoading) { + return const Center( + child: CircularProgressIndicator(), + ); + } else if (state is CurtainModuleStatusLoaded) { + return IconNameStatusContainer( + isFullIcon: false, + name: 'Preferences', + icon: Assets.preferences, + onTap: () => showDialog( + context: context, + builder: (_) => BlocProvider.value( + value: context.read(), + child: CurtainModulePrefrencesDialog( + curtainModuleBloc: + context.watch(), + deviceId: deviceId, + curtainModuleStatusModel: + state.curtainModuleStatus, + ), + ), + ), + status: false, + textColor: ColorsManager.blackColor, + ); + } else { + return const SizedBox(); + } + }, + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/accurate_calibrating_dialog.dart b/lib/pages/device_managment/curtain_module/widgets/accurate_calibrating_dialog.dart new file mode 100644 index 00000000..0d3a1a92 --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/accurate_calibrating_dialog.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/calibrate_completed_dialog.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class AccurteCalibratingDialog extends StatelessWidget { + final String deviceId; + final BuildContext parentContext; + const AccurteCalibratingDialog({ + super.key, + required this.deviceId, + required this.parentContext, + }); + + @override + Widget build(_) { + return AlertDialog( + backgroundColor: ColorsManager.whiteColors, + contentPadding: EdgeInsets.zero, + content: AccurateDialogWidget( + title: 'Calibrating', + body: const NormalTextBodyForDialog( + title: '', + step1: + 'Click Close Button to make the Curtain run to Full Close and Position.', + step2: 'click Next to complete the Calibration.', + ), + leftOnTap: () => Navigator.of(parentContext).pop(), + rightOnTap: () { + parentContext.read().add( + CurCalibrationEvent( + deviceId: deviceId, + ), + ); + Navigator.of(parentContext).pop(); + showDialog( + context: parentContext, + builder: (_) => CalibrateCompletedDialog( + parentContext: parentContext, + deviceId: deviceId, + ), + ); + }, + ), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/accurate_calibration_dialog.dart b/lib/pages/device_managment/curtain_module/widgets/accurate_calibration_dialog.dart new file mode 100644 index 00000000..7124639d --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/accurate_calibration_dialog.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_calibrating_dialog.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class AccurateCalibrationDialog extends StatelessWidget { + final String deviceId; + final BuildContext parentContext; + const AccurateCalibrationDialog({ + super.key, + required this.deviceId, + required this.parentContext, + }); + + @override + Widget build(_) { + return AlertDialog( + backgroundColor: ColorsManager.whiteColors, + contentPadding: EdgeInsets.zero, + content: AccurateDialogWidget( + title: 'Accurate Calibration', + body: const NormalTextBodyForDialog( + title: 'Prepare Calibration:', + step1: 'Run The Curtain to the Fully Open Position,and pause.', + step2: 'click Next to Start accurate calibration.', + ), + leftOnTap: () => Navigator.of(parentContext).pop(), + rightOnTap: () { + Navigator.of(parentContext).pop(); + showDialog( + context: parentContext, + builder: (_) => AccurteCalibratingDialog( + deviceId: deviceId, + parentContext: parentContext, + ), + ); + }, + ), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart b/lib/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart new file mode 100644 index 00000000..0d6ea90c --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class AccurateDialogWidget extends StatelessWidget { + final String title; + final Widget body; + final void Function() leftOnTap; + final void Function() rightOnTap; + const AccurateDialogWidget({ + super.key, + required this.title, + required this.body, + required this.leftOnTap, + required this.rightOnTap, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 250, + width: 500, + child: Column( + children: [ + Expanded( + flex: 3, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(10), + child: Text( + title, + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: ColorsManager.dialogBlueTitle, + ), + ), + ), + const Divider( + indent: 60, + endIndent: 60, + ), + ], + ), + ), + Expanded( + flex: 5, + child: body, + ), + Expanded( + flex: 2, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Expanded(child: Divider()), + Row( + children: [ + Expanded( + child: InkWell( + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(26), + ), + onTap: leftOnTap, + child: Container( + height: 40, + alignment: Alignment.center, + decoration: const BoxDecoration( + border: Border( + right: BorderSide( + color: ColorsManager.grayBorder, + ), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(26), + ), + ), + child: const Text( + 'Cancel', + style: TextStyle(color: ColorsManager.grayBorder), + ), + ), + ), + ), + Expanded( + child: InkWell( + borderRadius: const BorderRadius.only( + bottomRight: Radius.circular(26), + ), + onTap: rightOnTap, + child: Container( + height: 40, + alignment: Alignment.center, + decoration: const BoxDecoration( + border: Border( + right: BorderSide( + color: ColorsManager.grayBorder, + ), + ), + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(26), + ), + ), + child: const Text( + 'Next', + style: TextStyle( + color: ColorsManager.blueColor, + ), + ), + ), + ), + ) + ], + ) + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/calibrate_completed_dialog.dart b/lib/pages/device_managment/curtain_module/widgets/calibrate_completed_dialog.dart new file mode 100644 index 00000000..bd0cbb37 --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/calibrate_completed_dialog.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class CalibrateCompletedDialog extends StatelessWidget { + final BuildContext parentContext; + final String deviceId; + const CalibrateCompletedDialog({ + super.key, + required this.parentContext, + required this.deviceId, + }); + + @override + Widget build(_) { + return AlertDialog( + contentPadding: EdgeInsets.zero, + content: SizedBox( + height: 250, + width: 400, + child: Column( + children: [ + Expanded( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(10), + child: Text( + 'Calibration Completed', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: ColorsManager.dialogBlueTitle, + ), + ), + ), + const SizedBox(height: 5), + const Divider( + indent: 10, + endIndent: 10, + ), + ], + ), + ), + Expanded( + child: SvgPicture.asset(Assets.completedDoneIcon), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Divider( + indent: 10, + endIndent: 10, + ), + InkWell( + onTap: () { + parentContext.read().add( + FetchCurtainModuleStatusEvent( + deviceId: deviceId, + ), + ); + Navigator.of(parentContext).pop(); + Navigator.of(parentContext).pop(); + }, + child: Container( + height: 40, + width: double.infinity, + alignment: Alignment.center, + child: const Text( + 'Close', + style: TextStyle( + color: ColorsManager.grayBorder, + ), + ), + ), + ) + ], + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/curtain_action_widget.dart b/lib/pages/device_managment/curtain_module/widgets/curtain_action_widget.dart new file mode 100644 index 00000000..8c2ff81c --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/curtain_action_widget.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CurtainActionWidget extends StatelessWidget { + final String icon; + final void Function() onTap; + const CurtainActionWidget({ + super.key, + required this.icon, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: ClipOval( + child: Container( + height: 60, + width: 60, + padding: const EdgeInsets.all(8), + color: ColorsManager.whiteColors, + child: ClipOval( + child: Container( + height: 60, + width: 60, + padding: const EdgeInsets.all(8), + color: ColorsManager.graysColor, + child: SvgPicture.asset( + icon, + width: 35, + height: 35, + fit: BoxFit.contain, + ), + ), + ), + )), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/curtain_movment_widget.dart b/lib/pages/device_managment/curtain_module/widgets/curtain_movment_widget.dart new file mode 100644 index 00000000..e98ff11d --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/curtain_movment_widget.dart @@ -0,0 +1,219 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/models/curtain_module_model.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/curtain_action_widget.dart'; +import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class ControlCurtainMovementWidget extends StatelessWidget { + final List devicesId; + const ControlCurtainMovementWidget({ + super.key, + required this.devicesId, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 550, + child: DeviceControlsContainer( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CurtainActionWidget( + icon: Assets.openCurtain, + onTap: () { + if (devicesId.length == 1) { + context.read().add( + OpenCurtainEvent(deviceId: devicesId.first), + ); + } else { + context.read().add( + OpenCurtainBatchEvent(devicesId: devicesId), + ); + } + }, + ), + const SizedBox( + width: 30, + ), + CurtainActionWidget( + icon: Assets.pauseCurtain, + onTap: () { + if (devicesId.length == 1) { + context.read().add( + StopCurtainEvent(deviceId: devicesId.first), + ); + } else { + context.read().add( + StopCurtainBatchEvent(devicesId: devicesId), + ); + } + }, + ), + const SizedBox( + width: 30, + ), + CurtainActionWidget( + icon: Assets.closeCurtain, + onTap: () { + if (devicesId.length == 1) { + context.read().add( + CloseCurtainEvent(deviceId: devicesId.first), + ); + } else { + context.read().add( + CloseCurtainBatchEvent(devicesId: devicesId), + ); + } + }, + ), + BlocBuilder( + builder: (context, state) { + if (state is CurtainModuleError) { + return Center( + child: Text( + state.message, + style: const TextStyle( + color: ColorsManager.minBlueDot, + fontSize: 16, + ), + ), + ); + } else if (state is CurtainModuleLoading) { + return const Center( + child: CircularProgressIndicator( + color: ColorsManager.minBlueDot, + ), + ); + } else if (state is CurtainModuleInitial) { + return const Center( + child: Text( + 'No data available', + style: TextStyle( + color: ColorsManager.minBlueDot, + fontSize: 16, + ), + ), + ); + } else if (state is CurtainModuleStatusLoaded) { + return CurtainSliderWidget( + status: state.curtainModuleStatus, + devicesId: devicesId, + ); + } else { + return const Center( + child: Text( + 'Unknown state', + style: TextStyle( + color: ColorsManager.minBlueDot, + fontSize: 16, + ), + ), + ); + } + }, + ) + ], + ), + ), + ); + } +} + +class CurtainSliderWidget extends StatefulWidget { + final CurtainModuleStatusModel status; + final List devicesId; + + const CurtainSliderWidget({ + super.key, + required this.status, + required this.devicesId, + }); + + @override + State createState() => _CurtainSliderWidgetState(); +} + +class _CurtainSliderWidgetState extends State { + double? _localValue; // For temporary drag state + + @override + Widget build(BuildContext context) { + // If user is dragging, use local value. Otherwise, use Firebase-synced state + final double currentSliderValue = + _localValue ?? widget.status.percentControl / 100; + + return Column( + children: [ + Text( + '${(currentSliderValue * 100).round()}%', + style: const TextStyle( + color: ColorsManager.minBlueDot, + fontSize: 25, + fontWeight: FontWeight.bold, + ), + ), + Slider( + value: currentSliderValue, + min: 0, + max: 1, + divisions: 10, // 10% step + activeColor: ColorsManager.minBlueDot, + thumbColor: ColorsManager.primaryColor, + inactiveColor: ColorsManager.whiteColors, + + // Start dragging — use local control + onChangeStart: (_) { + setState(() { + _localValue = currentSliderValue; + }); + }, + + // While dragging — update temporary value + onChanged: (value) { + final steppedValue = (value * 10).roundToDouble() / 10; + setState(() { + _localValue = steppedValue; + }); + }, + + // On release — send API and return to Firebase-controlled state + onChangeEnd: (value) { + final int targetPercent = (value * 100).round(); + + if (widget.devicesId.length == 1) { + context.read().add( + SendCurtainPercentToApiEvent( + deviceId: widget.devicesId.first, + status: Status( + code: 'percent_control', + value: targetPercent, + ), + ), + ); + } else { + context.read().add( + SendCurtainBatchPercentToApiEvent( + devicesId: widget.devicesId, + status: Status( + code: 'percent_control', + value: targetPercent, + ), + ), + ); + } + + // Revert back to Firebase-synced stream + setState(() { + _localValue = null; + }); + }, + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart b/lib/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart new file mode 100644 index 00000000..fa293ec6 --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class NormalTextBodyForDialog extends StatelessWidget { + final String title; + final String step1; + final String step2; + + const NormalTextBodyForDialog({ + super.key, + required this.title, + required this.step1, + required this.step2, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsetsGeometry.only(left: 15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (title.isEmpty) + const SizedBox() + else + Expanded( + child: Text( + title, + style: const TextStyle( + color: ColorsManager.grayColor, + fontSize: 15, + ), + ), + ), + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + width: 10, + ), + const Text('1. ', + style: TextStyle( + color: ColorsManager.grayColor, + fontSize: 15, + )), + SizedBox( + width: 450, + child: Text( + step1, + style: const TextStyle( + color: ColorsManager.grayColor, + fontSize: 15, + ), + ), + ), + ], + ), + ), + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + width: 10, + ), + const Text('2. ', + style: TextStyle( + color: ColorsManager.grayColor, + fontSize: 15, + )), + Text( + step2, + style: const TextStyle( + color: ColorsManager.grayColor, + fontSize: 15, + ), + ), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/number_input_textfield.dart b/lib/pages/device_managment/curtain_module/widgets/number_input_textfield.dart new file mode 100644 index 00000000..428f6531 --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/number_input_textfield.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class NumberInputField extends StatelessWidget { + final TextEditingController controller; + + const NumberInputField({super.key, required this.controller}); + + @override + Widget build(BuildContext context) { + return TextField( + controller: controller, + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + decoration: const InputDecoration( + border: InputBorder.none, + isDense: true, + contentPadding: EdgeInsets.zero, + ), + style: const TextStyle( + fontSize: 15, + color: ColorsManager.blackColor, + ), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/pref_revers_card_widget.dart b/lib/pages/device_managment/curtain_module/widgets/pref_revers_card_widget.dart new file mode 100644 index 00000000..85c45d27 --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/pref_revers_card_widget.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/web_layout/default_container.dart'; + +class PrefReversCardWidget extends StatelessWidget { + final void Function() onTap; + final String title; + final String body; + const PrefReversCardWidget({ + super.key, + required this.title, + required this.body, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return DefaultContainer( + padding: const EdgeInsets.all(18), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 8, + child: Text( + title, + style: const TextStyle( + color: ColorsManager.grayBorder, + fontSize: 15, + ), + ), + ), + const SizedBox( + width: 20, + ), + Expanded( + flex: 2, + child: InkWell( + onTap: onTap, + child: Container( + padding: const EdgeInsets.all(3), + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + borderRadius: const BorderRadius.horizontal( + left: Radius.circular(10), + right: Radius.circular(10)), + border: Border.all(color: ColorsManager.grayBorder)), + child: SvgPicture.asset( + Assets.reverseArrows, + height: 15, + ), + ), + ), + ) + ], + ), + SizedBox( + width: 100, + child: Text( + body, + style: const TextStyle( + color: ColorsManager.blackColor, + fontWeight: FontWeight.w500, + fontSize: 18, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/prefrences_dialog.dart b/lib/pages/device_managment/curtain_module/widgets/prefrences_dialog.dart new file mode 100644 index 00000000..35844c05 --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/prefrences_dialog.dart @@ -0,0 +1,149 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/models/curtain_module_model.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_calibration_dialog.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/pref_revers_card_widget.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/quick_calibration_dialog.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/web_layout/default_container.dart'; + +class CurtainModulePrefrencesDialog extends StatelessWidget { + final CurtainModuleStatusModel curtainModuleStatusModel; + final String deviceId; + final CurtainModuleBloc curtainModuleBloc; + const CurtainModulePrefrencesDialog({ + super.key, + required this.curtainModuleStatusModel, + required this.deviceId, + required this.curtainModuleBloc, + }); + + @override + Widget build(_) { + return AlertDialog( + backgroundColor: ColorsManager.CircleImageBackground, + contentPadding: const EdgeInsets.all(20), + title: Center( + child: Text( + 'Preferences', + style: TextStyle( + color: ColorsManager.dialogBlueTitle, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + )), + content: BlocBuilder( + bloc: curtainModuleBloc, + builder: (context, state) { + if (state is CurtainModuleLoading) { + return const Center( + child: CircularProgressIndicator(), + ); + } else if (state is CurtainModuleStatusLoaded) { + return SizedBox( + height: 300, + width: 400, + child: GridView( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 1.5, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + ), + children: [ + PrefReversCardWidget( + title: state.curtainModuleStatus.controlBack, + body: 'Motor Steering', + onTap: () { + context.read().add( + ChangeControlBackEvent( + deviceId: deviceId, + controlBack: + state.curtainModuleStatus.controlBack == + 'forward' + ? 'back' + : 'forward', + ), + ); + }, + ), + PrefReversCardWidget( + title: formatDeviceType( + state.curtainModuleStatus.elecMachineryMode), + body: 'Motor Mode', + onTap: () => context.read().add( + ChangeElecMachineryModeEvent( + deviceId: deviceId, + elecMachineryMode: + state.curtainModuleStatus.elecMachineryMode == + 'dry_contact' + ? 'strong_power' + : 'dry_contact', + ), + ), + ), + DefaultContainer( + padding: const EdgeInsets.all(12), + child: InkWell( + onTap: () => showDialog( + context: context, + builder: (_) => AccurateCalibrationDialog( + deviceId: deviceId, + parentContext: context, + ), + ), + child: const Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text('Accurte Calibration', + style: TextStyle( + fontSize: 18, + color: ColorsManager.blackColor, + )), + ], + ), + ), + ), + DefaultContainer( + padding: const EdgeInsets.all(12), + child: InkWell( + onTap: () => showDialog( + context: context, + builder: (_) => QuickCalibrationDialog( + timControl: state.curtainModuleStatus.trTimeControl, + deviceId: deviceId, + parentContext: context), + ), + child: const Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text('Quick Calibration', + style: TextStyle( + fontSize: 18, + color: ColorsManager.blackColor, + )), + ], + ), + ), + ), + ], + ), + ); + } else { + return const SizedBox(); + } + }, + ), + ); + } + + String formatDeviceType(String raw) { + return raw + .split('_') + .map((word) => word.isNotEmpty + ? '${word[0].toUpperCase()}${word.substring(1)}' + : '') + .join(' '); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/quick_calibrating_dialog.dart b/lib/pages/device_managment/curtain_module/widgets/quick_calibrating_dialog.dart new file mode 100644 index 00000000..6fc9adf2 --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/quick_calibrating_dialog.dart @@ -0,0 +1,149 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/calibrate_completed_dialog.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/number_input_textfield.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class QuickCalibratingDialog extends StatefulWidget { + final int timControl; + final String deviceId; + final BuildContext parentContext; + const QuickCalibratingDialog({ + super.key, + required this.timControl, + required this.deviceId, + required this.parentContext, + }); + + @override + State createState() => _QuickCalibratingDialogState(); +} + +class _QuickCalibratingDialogState extends State { + late TextEditingController _controller; + String? _errorText; + + void _onRightTap() { + final value = int.tryParse(_controller.text); + + if (value == null || value < 10 || value > 120) { + setState(() { + _errorText = 'Number should be between 10 and 120'; + }); + return; + } + + setState(() { + _errorText = null; + }); + widget.parentContext.read().add( + ChangeTimerControlEvent( + deviceId: widget.deviceId, + timControl: value, + ), + ); + Navigator.of(widget.parentContext).pop(); + showDialog( + context: widget.parentContext, + builder: (_) => CalibrateCompletedDialog( + parentContext: widget.parentContext, + deviceId: widget.deviceId, + ), + ); + } + + @override + void initState() { + _controller = TextEditingController(text: widget.timControl.toString()); + super.initState(); + } + + @override + Widget build(_) { + return AlertDialog( + backgroundColor: ColorsManager.whiteColors, + contentPadding: EdgeInsets.zero, + content: AccurateDialogWidget( + title: 'Calibrating', + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Expanded( + child: Align( + alignment: Alignment.topCenter, + child: Padding( + padding: EdgeInsets.only(right: 75), + child: Text( + '1.please Enter the Travel Time:', + style: TextStyle(color: ColorsManager.lightGrayColor), + ), + ), + ), + ), + Expanded( + child: Align( + alignment: Alignment.center, + child: Container( + width: 130, + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + color: ColorsManager.neutralGray.withValues( + alpha: 0.5, + ), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsetsGeometry.only(left: 5), + child: NumberInputField(controller: _controller)), + ), + Expanded( + child: Text( + 'seconds', + style: TextStyle( + fontSize: 12, + color: ColorsManager.dialogBlueTitle, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ), + ), + ), + if (_errorText != null) + Expanded( + child: Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + _errorText!, + style: const TextStyle( + color: ColorsManager.red, + fontSize: 14, + ), + ), + ), + ), + const Expanded( + child: Align( + alignment: Alignment.bottomCenter, + child: Text( + '2.click Next to Complete the calibration', + style: TextStyle(color: ColorsManager.lightGrayColor), + ), + ), + ) + ], + ), + leftOnTap: () => Navigator.of(widget.parentContext).pop(), + rightOnTap: _onRightTap, + ), + ); + } +} diff --git a/lib/pages/device_managment/curtain_module/widgets/quick_calibration_dialog.dart b/lib/pages/device_managment/curtain_module/widgets/quick_calibration_dialog.dart new file mode 100644 index 00000000..06b386c8 --- /dev/null +++ b/lib/pages/device_managment/curtain_module/widgets/quick_calibration_dialog.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart'; +import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/quick_calibrating_dialog.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class QuickCalibrationDialog extends StatelessWidget { + final int timControl; + final String deviceId; + final BuildContext parentContext; + const QuickCalibrationDialog({ + super.key, + required this.timControl, + required this.deviceId, + required this.parentContext, + }); + + @override + Widget build(_) { + return AlertDialog( + backgroundColor: ColorsManager.whiteColors, + contentPadding: EdgeInsets.zero, + content: AccurateDialogWidget( + title: 'Quick Calibration', + body: const NormalTextBodyForDialog( + title: 'Prepare Calibration:', + step1: + 'Confirm that the curtain is in the fully closed and suspended state.', + step2: 'click Next to Start calibration.', + ), + leftOnTap: () => Navigator.of(parentContext).pop(), + rightOnTap: () { + Navigator.of(parentContext).pop(); + showDialog( + context: parentContext, + builder: (_) => QuickCalibratingDialog( + timControl: timControl, + deviceId: deviceId, + parentContext: parentContext, + ), + ); + }, + ), + ); + } +} diff --git a/lib/pages/device_managment/device_setting/device_management_content.dart b/lib/pages/device_managment/device_setting/device_management_content.dart index a087e5bb..c73899fc 100644 --- a/lib/pages/device_managment/device_setting/device_management_content.dart +++ b/lib/pages/device_managment/device_setting/device_management_content.dart @@ -19,11 +19,14 @@ class DeviceManagementContent extends StatelessWidget { required this.device, required this.subSpaces, required this.deviceInfo, + required this.deviceManagementBloc, }); final AllDevicesModel device; final List subSpaces; final DeviceInfoModel deviceInfo; + final DeviceManagementBloc deviceManagementBloc; + @override Widget build(BuildContext context) { @@ -87,6 +90,11 @@ class DeviceManagementContent extends StatelessWidget { ), ); }); + + deviceManagementBloc.add(UpdateSubSpaceName( + subspaceId: selectedSubSpace.id!, + deviceId: device.uuid!, + newSubSpaceName: selectedSubSpace.name ?? '')); } }, child: infoRow( diff --git a/lib/pages/device_managment/device_setting/device_settings_panel.dart b/lib/pages/device_managment/device_setting/device_settings_panel.dart index 48458b3b..0856b5d0 100644 --- a/lib/pages/device_managment/device_setting/device_settings_panel.dart +++ b/lib/pages/device_managment/device_setting/device_settings_panel.dart @@ -1,13 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart'; import 'package:syncrow_web/pages/device_managment/device_setting/device_icon_type_helper.dart'; import 'package:syncrow_web/pages/device_managment/device_setting/device_management_content.dart'; import 'package:syncrow_web/pages/device_managment/device_setting/remove_device_widget.dart'; import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart'; -import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart'; import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -17,7 +19,13 @@ import 'package:syncrow_web/web_layout/default_container.dart'; class DeviceSettingsPanel extends StatelessWidget { final VoidCallback? onClose; final AllDevicesModel device; - const DeviceSettingsPanel({super.key, this.onClose, required this.device}); + final DeviceManagementBloc deviceManagementBloc; + const DeviceSettingsPanel({ + super.key, + this.onClose, + required this.device, + required this.deviceManagementBloc, + }); @override Widget build(BuildContext context) { @@ -71,10 +79,10 @@ class DeviceSettingsPanel extends StatelessWidget { 'Device Settings', style: context.theme.textTheme.titleLarge! .copyWith( - fontWeight: FontWeight.w700, + fontWeight: FontWeight.w700, color: ColorsManager.vividBlue .withOpacity(0.7), - fontSize: 24), + fontSize: 24), ), ], ), @@ -134,8 +142,14 @@ class DeviceSettingsPanel extends StatelessWidget { onFieldSubmitted: (value) { _bloc.add(const ChangeNameEvent( value: false)); + deviceManagementBloc + ..add(UpdateDeviceName( + deviceId: device.uuid!, + newName: _bloc + .nameController + .text))..add(ResetSelectedDevices()); }, - decoration: InputDecoration( + decoration:const InputDecoration( isDense: true, contentPadding: EdgeInsets.zero, border: InputBorder.none, @@ -157,7 +171,7 @@ class DeviceSettingsPanel extends StatelessWidget { onTap: () { _bloc.add( const ChangeNameEvent( - value: true)); + value: true)); }, child: SvgPicture.asset( Assets @@ -190,6 +204,7 @@ class DeviceSettingsPanel extends StatelessWidget { device: device, subSpaces: subSpaces.cast(), deviceInfo: deviceInfo, + deviceManagementBloc: deviceManagementBloc, ), const SizedBox(height: 32), RemoveDeviceWidget(bloc: _bloc), diff --git a/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart b/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart index fbf7ae64..0db1445f 100644 --- a/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart +++ b/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart @@ -265,7 +265,7 @@ class ScheduleBloc extends Bloc { category: event.category, deviceId: deviceId, time: getTimeStampWithoutSeconds(dateTime).toString(), - code: event.category, + code: event.code ?? event.category, value: event.functionOn, days: event.selectedDays); if (success) { @@ -286,11 +286,20 @@ class ScheduleBloc extends Bloc { try { if (state is ScheduleLoaded) { final dateTime = DateTime.parse(event.time); + Status status = Status(code: '', value: ''); + if (event.category == 'CUR_2') { + status = status.copyWith( + code: 'control', + value: event.functionOn == true ? 'open' : 'close'); + } else { + status = + status.copyWith(code: event.category, value: event.functionOn); + } final updatedSchedule = ScheduleEntry( scheduleId: event.scheduleId, category: event.category, time: getTimeStampWithoutSeconds(dateTime).toString(), - function: Status(code: event.category, value: event.functionOn), + function: status, days: event.selectedDays, ); final success = await DevicesManagementApi().editScheduleRecord( diff --git a/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart b/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart index 0b9ec581..a28b8757 100644 --- a/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart +++ b/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart @@ -70,17 +70,19 @@ class ScheduleAddEvent extends ScheduleEvent { final String category; final String time; final List selectedDays; - final bool functionOn; + final dynamic functionOn; + final String? code; const ScheduleAddEvent({ required this.category, required this.time, required this.selectedDays, required this.functionOn, + required this.code, }); @override - List get props => [category, time, selectedDays, functionOn]; + List get props => [category, time, selectedDays, functionOn, code]; } class ScheduleEditEvent extends ScheduleEvent { diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart index 47534d37..52a5c56f 100644 --- a/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/schedule_device/bloc/schedule_bloc.dart'; import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart'; import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/count_down_inching_view.dart'; @@ -9,13 +10,19 @@ import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widg import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart'; import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_selector.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; class BuildScheduleView extends StatelessWidget { - const BuildScheduleView( - {super.key, required this.deviceUuid, required this.category}); + const BuildScheduleView({ + super.key, + required this.deviceUuid, + required this.category, + this.code, + }); final String deviceUuid; final String category; + final String? code; @override Widget build(BuildContext context) { @@ -45,9 +52,12 @@ class BuildScheduleView extends StatelessWidget { children: [ const ScheduleHeader(), const SizedBox(height: 20), - ScheduleModeSelector( - currentMode: state.scheduleMode, - ), + if (category == 'CUR_2') + const SizedBox() + else + ScheduleModeSelector( + currentMode: state.scheduleMode, + ), const SizedBox(height: 20), if (state.scheduleMode == ScheduleModes.schedule) ScheduleManagementUI( @@ -57,13 +67,21 @@ class BuildScheduleView extends StatelessWidget { final entry = await ScheduleDialogHelper .showAddScheduleDialog( context, - schedule: null, + schedule: ScheduleEntry( + category: category, + time: '', + function: Status( + code: code.toString(), value: null), + days: [], + ), isEdit: false, + code: code, ); if (entry != null) { context.read().add( ScheduleAddEvent( - category: entry.category, + category: category, + code: entry.function.code, time: entry.time, functionOn: entry.function.value, selectedDays: entry.days, @@ -74,7 +92,7 @@ class BuildScheduleView extends StatelessWidget { ), if (state.scheduleMode == ScheduleModes.countdown || state.scheduleMode == ScheduleModes.inching) - CountdownInchingView( + CountdownInchingView( deviceId: deviceUuid, ), const SizedBox(height: 20), diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart index 98ae0515..84d8e1f5 100644 --- a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart @@ -162,11 +162,18 @@ class _ScheduleTableView extends StatelessWidget { child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { + bool temp; + if (schedule.category == 'CUR_2') { + temp = schedule.function.value == 'open' ? true : false; + } else { + temp = schedule.function.value as bool; + } context.read().add( ScheduleUpdateEntryEvent( category: schedule.category, scheduleId: schedule.scheduleId, - functionOn: schedule.function.value, + functionOn: temp, + // schedule.function.value, enable: !schedule.enable, ), ); @@ -188,7 +195,10 @@ class _ScheduleTableView extends StatelessWidget { child: Text(_getSelectedDays( ScheduleModel.parseSelectedDays(schedule.days)))), Center(child: Text(formatIsoStringToTime(schedule.time, context))), - Center(child: Text(schedule.function.value ? 'On' : 'Off')), + if (schedule.category == 'CUR_2') + Center(child: Text(schedule.function.value)) + else + Center(child: Text(schedule.function.value ? 'On' : 'Off')), Center( child: Wrap( runAlignment: WrapAlignment.center, @@ -202,12 +212,20 @@ class _ScheduleTableView extends StatelessWidget { isEdit: true, ).then((updatedSchedule) { if (updatedSchedule != null) { + bool temp; + if (schedule.category == 'CUR_2') { + updatedSchedule.function.value == 'open' + ? temp = true + : temp = false; + } else { + temp = updatedSchedule.function.value; + } context.read().add( ScheduleEditEvent( scheduleId: schedule.scheduleId, category: schedule.category, time: updatedSchedule.time, - functionOn: updatedSchedule.function.value, + functionOn: temp, selectedDays: updatedSchedule.days), ); } diff --git a/lib/pages/device_managment/shared/device_batch_control_dialog.dart b/lib/pages/device_managment/shared/device_batch_control_dialog.dart index f2dc68f5..c7ea6c71 100644 --- a/lib/pages/device_managment/shared/device_batch_control_dialog.dart +++ b/lib/pages/device_managment/shared/device_batch_control_dialog.dart @@ -4,7 +4,8 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; -class DeviceBatchControlDialog extends StatelessWidget with RouteControlsBasedCode { +class DeviceBatchControlDialog extends StatelessWidget + with RouteControlsBasedCode { final List devices; const DeviceBatchControlDialog({super.key, required this.devices}); @@ -18,7 +19,7 @@ class DeviceBatchControlDialog extends StatelessWidget with RouteControlsBasedCo borderRadius: BorderRadius.circular(20), ), child: SizedBox( - width: devices.length < 2 ? 500 : 800, + width: devices.length < 2 ? 600 : 800, // height: context.screenHeight * 0.7, child: SingleChildScrollView( child: Padding( diff --git a/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart b/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart index ae7feac9..f55b32ab 100644 --- a/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart +++ b/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart @@ -17,14 +17,21 @@ class ScheduleDialogHelper { BuildContext context, { ScheduleEntry? schedule, bool isEdit = false, + String? code, }) { + bool temp; + if (schedule?.category == 'CUR_2') { + temp = schedule!.function.value == 'open' ? true : false; + } else { + temp = schedule!.function.value; + } final initialTime = schedule != null ? _convertStringToTimeOfDay(schedule.time) : TimeOfDay.now(); final initialDays = schedule != null ? _convertDaysStringToBooleans(schedule.days) : List.filled(7, false); - bool? functionOn = schedule?.function.value ?? true; + bool? functionOn = temp; TimeOfDay selectedTime = initialTime; List selectedDays = List.of(initialDays); @@ -96,7 +103,8 @@ class ScheduleDialogHelper { setState(() => selectedDays[i] = v); }), const SizedBox(height: 16), - _buildFunctionSwitch(ctx, functionOn!, (v) { + _buildFunctionSwitch(schedule!.category, ctx, functionOn!, + (v) { setState(() => functionOn = v); }), ], @@ -115,10 +123,21 @@ class ScheduleDialogHelper { width: 100, child: ElevatedButton( onPressed: () { + dynamic temp; + if (schedule?.category == 'CUR_2') { + temp = functionOn! ? 'open' : 'close'; + } else { + temp = functionOn; + } + print(temp); final entry = ScheduleEntry( category: schedule?.category ?? 'switch_1', time: _formatTimeOfDayToISO(selectedTime), - function: Status(code: 'switch_1', value: functionOn), + function: Status( + code: code ?? 'switch_1', + value: temp, + // functionOn, + ), days: _convertSelectedDaysToStrings(selectedDays), scheduleId: schedule?.scheduleId, ); @@ -185,7 +204,7 @@ class ScheduleDialogHelper { } static Widget _buildFunctionSwitch( - BuildContext ctx, bool isOn, Function(bool) onChanged) { + String categor, BuildContext ctx, bool isOn, Function(bool) onChanged) { return Row( children: [ Text( @@ -199,14 +218,14 @@ class ScheduleDialogHelper { groupValue: isOn, onChanged: (val) => onChanged(true), ), - const Text('On'), + Text(categor == 'CUR_2' ? 'open' : 'On'), const SizedBox(width: 10), Radio( value: false, groupValue: isOn, onChanged: (val) => onChanged(false), ), - const Text('Off'), + Text(categor == 'CUR_2' ? 'close' : 'Off'), ], ); } diff --git a/lib/pages/routines/widgets/routine_dialogs/curtain_dialog.dart b/lib/pages/routines/widgets/routine_dialogs/curtain_dialog.dart index bdf8660d..64295e2a 100644 --- a/lib/pages/routines/widgets/routine_dialogs/curtain_dialog.dart +++ b/lib/pages/routines/widgets/routine_dialogs/curtain_dialog.dart @@ -58,7 +58,9 @@ class CurtainHelper { child: Column( mainAxisSize: MainAxisSize.min, children: [ - const DialogHeader('AC Functions'), + DialogHeader(dialogType == 'THEN' + ? 'Curtain Functions' + : 'Curtain Conditions'), Expanded( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, diff --git a/lib/pages/spaces_management/all_spaces/model/product_model.dart b/lib/pages/spaces_management/all_spaces/model/product_model.dart index 8f905032..a7b23d0f 100644 --- a/lib/pages/spaces_management/all_spaces/model/product_model.dart +++ b/lib/pages/spaces_management/all_spaces/model/product_model.dart @@ -58,11 +58,14 @@ class ProductModel { '3G': Assets.Gang3SwitchIcon, '3GT': Assets.threeTouchSwitch, 'CUR': Assets.curtain, + 'CUR_2': Assets.curtain, 'GD': Assets.garageDoor, 'GW': Assets.SmartGatewayIcon, 'DL': Assets.DoorLockIcon, 'WL': Assets.waterLeakSensor, 'WH': Assets.waterHeater, + 'WM': Assets.waterLeakSensor, + 'SOS': Assets.sos, 'AC': Assets.ac, 'CPS': Assets.presenceSensor, 'PC': Assets.powerClamp, diff --git a/lib/pages/visitor_password/model/device_model.dart b/lib/pages/visitor_password/model/device_model.dart index f9711eed..75d00350 100644 --- a/lib/pages/visitor_password/model/device_model.dart +++ b/lib/pages/visitor_password/model/device_model.dart @@ -80,6 +80,10 @@ class DeviceModel { tempIcon = Assets.openedDoor; } else if (type == DeviceType.WaterLeak) { tempIcon = Assets.waterLeakNormal; + } else if (type == DeviceType.Curtain2) { + tempIcon = Assets.curtainIcon; + } else if (type == DeviceType.Curtain) { + tempIcon = Assets.curtainIcon; } else { tempIcon = Assets.blackLogo; } diff --git a/lib/pages/visitor_password/view/visitor_password_dialog.dart b/lib/pages/visitor_password/view/visitor_password_dialog.dart index d1fb172a..5362f448 100644 --- a/lib/pages/visitor_password/view/visitor_password_dialog.dart +++ b/lib/pages/visitor_password/view/visitor_password_dialog.dart @@ -2,10 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/date_time_widget.dart'; -import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart'; @@ -23,8 +21,8 @@ class VisitorPasswordDialog extends StatelessWidget { @override Widget build(BuildContext context) { - Size size = MediaQuery.of(context).size; - var text = Theme.of(context) + final size = MediaQuery.of(context).size; + final text = Theme.of(context) .textTheme .bodySmall! .copyWith(color: Colors.black, fontSize: 13); @@ -41,8 +39,7 @@ class VisitorPasswordDialog extends StatelessWidget { title: 'Sent Successfully', widgeta: Column( children: [ - if (visitorBloc - .passwordStatus!.failedOperations.isNotEmpty) + if (visitorBloc.passwordStatus!.failedOperations.isNotEmpty) Column( children: [ const Text('Failed Devices'), @@ -56,22 +53,19 @@ class VisitorPasswordDialog extends StatelessWidget { .passwordStatus!.failedOperations.length, itemBuilder: (context, index) { return Container( - margin: EdgeInsets.all(5), + margin: const EdgeInsets.all(5), decoration: containerDecoration, height: 45, child: Center( - child: Text(visitorBloc - .passwordStatus! - .failedOperations[index] - .deviceName)), + child: Text(visitorBloc.passwordStatus! + .failedOperations[index].deviceName)), ); }, ), ), ], ), - if (visitorBloc - .passwordStatus!.successOperations.isNotEmpty) + if (visitorBloc.passwordStatus!.successOperations.isNotEmpty) Column( children: [ const Text('Success Devices'), @@ -85,14 +79,12 @@ class VisitorPasswordDialog extends StatelessWidget { .passwordStatus!.successOperations.length, itemBuilder: (context, index) { return Container( - margin: EdgeInsets.all(5), + margin: const EdgeInsets.all(5), decoration: containerDecoration, height: 45, child: Center( - child: Text(visitorBloc - .passwordStatus! - .successOperations[index] - .deviceName)), + child: Text(visitorBloc.passwordStatus! + .successOperations[index].deviceName)), ); }, ), @@ -115,16 +107,14 @@ class VisitorPasswordDialog extends StatelessWidget { child: BlocBuilder( builder: (BuildContext context, VisitorPasswordState state) { final visitorBloc = BlocProvider.of(context); - bool isRepeat = + final isRepeat = state is IsRepeatState ? state.repeat : visitorBloc.repeat; return AlertDialog( backgroundColor: Colors.white, title: Text( 'Create visitor password', style: Theme.of(context).textTheme.headlineLarge!.copyWith( - fontWeight: FontWeight.w400, - fontSize: 24, - color: Colors.black), + fontWeight: FontWeight.w400, fontSize: 24, color: Colors.black), ), content: state is LoadingInitialState ? const Center(child: CircularProgressIndicator()) @@ -310,14 +300,12 @@ class VisitorPasswordDialog extends StatelessWidget { visitorBloc.accessTypeSelected == 'Offline Password') { visitorBloc.add(SelectTimeEvent( - context: context, - isEffective: false)); + context: context, isEffective: false)); } else { - visitorBloc.add( - SelectTimeVisitorPassword( - context: context, - isStart: false, - isRepeat: false)); + visitorBloc.add(SelectTimeVisitorPassword( + context: context, + isStart: false, + isRepeat: false)); } }, startTime: () { @@ -326,31 +314,28 @@ class VisitorPasswordDialog extends StatelessWidget { visitorBloc.accessTypeSelected == 'Offline Password') { visitorBloc.add(SelectTimeEvent( - context: context, - isEffective: true)); + context: context, isEffective: true)); } else { - visitorBloc.add( - SelectTimeVisitorPassword( - context: context, - isStart: true, - isRepeat: false)); + visitorBloc.add(SelectTimeVisitorPassword( + context: context, + isStart: true, + isRepeat: false)); } }, - firstString: (visitorBloc - .usageFrequencySelected == - 'Periodic' && - visitorBloc.accessTypeSelected == - 'Offline Password') - ? visitorBloc.effectiveTime - : visitorBloc.startTimeAccess - .toString(), + firstString: + (visitorBloc.usageFrequencySelected == + 'Periodic' && + visitorBloc.accessTypeSelected == + 'Offline Password') + ? visitorBloc.effectiveTime + : visitorBloc.startTimeAccess, secondString: (visitorBloc .usageFrequencySelected == 'Periodic' && visitorBloc.accessTypeSelected == 'Offline Password') ? visitorBloc.expirationTime - : visitorBloc.endTimeAccess.toString(), + : visitorBloc.endTimeAccess, icon: Assets.calendarIcon), const SizedBox( height: 10, @@ -410,8 +395,7 @@ class VisitorPasswordDialog extends StatelessWidget { child: CupertinoSwitch( value: visitorBloc.repeat, onChanged: (value) { - visitorBloc - .add(ToggleRepeatEvent()); + visitorBloc.add(ToggleRepeatEvent()); }, applyTheme: true, ), @@ -442,8 +426,7 @@ class VisitorPasswordDialog extends StatelessWidget { }, ).then((listDevice) { if (listDevice != null) { - visitorBloc.selectedDevices = - listDevice; + visitorBloc.selectedDevices = listDevice; } }); }, @@ -455,8 +438,7 @@ class VisitorPasswordDialog extends StatelessWidget { .bodySmall! .copyWith( fontWeight: FontWeight.w400, - color: - ColorsManager.whiteColors, + color: ColorsManager.whiteColors, fontSize: 12), ), ), @@ -495,37 +477,30 @@ class VisitorPasswordDialog extends StatelessWidget { onPressed: () { if (visitorBloc.forgetFormKey.currentState!.validate()) { if (visitorBloc.selectedDevices.isNotEmpty) { - if (visitorBloc.usageFrequencySelected == - 'One-Time' && - visitorBloc.accessTypeSelected == - 'Offline Password') { + if (visitorBloc.usageFrequencySelected == 'One-Time' && + visitorBloc.accessTypeSelected == 'Offline Password') { setPasswordFunction(context, size, visitorBloc); } else if (visitorBloc.usageFrequencySelected == 'Periodic' && - visitorBloc.accessTypeSelected == - 'Offline Password') { + visitorBloc.accessTypeSelected == 'Offline Password') { if (visitorBloc.expirationTime != 'End Time' && visitorBloc.effectiveTime != 'Start Time') { setPasswordFunction(context, size, visitorBloc); } else { visitorBloc.stateDialog( context: context, - message: - 'Please select Access Period to continue', + message: 'Please select Access Period to continue', title: 'Access Period'); } - } else if (visitorBloc.endTimeAccess.toString() != - 'End Time' && - visitorBloc.startTimeAccess.toString() != - 'Start Time') { + } else if (visitorBloc.endTimeAccess != 'End Time' && + visitorBloc.startTimeAccess != 'Start Time') { if (visitorBloc.effectiveTimeTimeStamp != null && visitorBloc.expirationTimeTimeStamp != null) { if (isRepeat == true) { if (visitorBloc.expirationTime != 'End Time' && visitorBloc.effectiveTime != 'Start Time' && visitorBloc.selectedDays.isNotEmpty) { - setPasswordFunction( - context, size, visitorBloc); + setPasswordFunction(context, size, visitorBloc); } else { visitorBloc.stateDialog( context: context, @@ -539,15 +514,13 @@ class VisitorPasswordDialog extends StatelessWidget { } else { visitorBloc.stateDialog( context: context, - message: - 'Please select Access Period to continue', + message: 'Please select Access Period to continue', title: 'Access Period'); } } else { visitorBloc.stateDialog( context: context, - message: - 'Please select Access Period to continue', + message: 'Please select Access Period to continue', title: 'Access Period'); } } else { @@ -593,17 +566,17 @@ class VisitorPasswordDialog extends StatelessWidget { alignment: Alignment.center, content: SizedBox( height: size.height * 0.25, - child: Center( - child: - CircularProgressIndicator(), // Display a loading spinner + child: const Center( + child: CircularProgressIndicator(), // Display a loading spinner ), ), ); } else { return AlertDialog( alignment: Alignment.center, + backgroundColor: Colors.white, content: SizedBox( - height: size.height * 0.25, + height: size.height * 0.13, child: Column( children: [ Column( @@ -617,13 +590,16 @@ class VisitorPasswordDialog extends StatelessWidget { width: 35, ), ), + const SizedBox( + height: 20, + ), Text( 'Set Password', style: Theme.of(context) .textTheme .headlineLarge! .copyWith( - fontSize: 30, + fontSize: 24, fontWeight: FontWeight.w400, color: Colors.black, ), @@ -631,15 +607,6 @@ class VisitorPasswordDialog extends StatelessWidget { ], ), const SizedBox(width: 15), - Text( - 'This action will update all of the selected\n door locks passwords in the property.\n\nAre you sure you want to continue?', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: ColorsManager.grayColor, - fontWeight: FontWeight.w400, - fontSize: 18, - ), - ), ], ), ), @@ -668,12 +635,12 @@ class VisitorPasswordDialog extends StatelessWidget { decoration: containerDecoration, width: size.width * 0.1, child: DefaultButton( + backgroundColor: Color(0xff023DFE), borderRadius: 8, onPressed: () { Navigator.pop(context); if (visitorBloc.usageFrequencySelected == 'One-Time' && - visitorBloc.accessTypeSelected == - 'Online Password') { + visitorBloc.accessTypeSelected == 'Online Password') { visitorBloc.add(OnlineOneTimePasswordEvent( context: context, passwordName: visitorBloc.userNameController.text, @@ -681,8 +648,7 @@ class VisitorPasswordDialog extends StatelessWidget { )); } else if (visitorBloc.usageFrequencySelected == 'Periodic' && - visitorBloc.accessTypeSelected == - 'Online Password') { + visitorBloc.accessTypeSelected == 'Online Password') { visitorBloc.add(OnlineMultipleTimePasswordEvent( passwordName: visitorBloc.userNameController.text, email: visitorBloc.emailController.text, @@ -693,8 +659,7 @@ class VisitorPasswordDialog extends StatelessWidget { )); } else if (visitorBloc.usageFrequencySelected == 'One-Time' && - visitorBloc.accessTypeSelected == - 'Offline Password') { + visitorBloc.accessTypeSelected == 'Offline Password') { visitorBloc.add(OfflineOneTimePasswordEvent( context: context, passwordName: visitorBloc.userNameController.text, @@ -702,8 +667,7 @@ class VisitorPasswordDialog extends StatelessWidget { )); } else if (visitorBloc.usageFrequencySelected == 'Periodic' && - visitorBloc.accessTypeSelected == - 'Offline Password') { + visitorBloc.accessTypeSelected == 'Offline Password') { visitorBloc.add(OfflineMultipleTimePasswordEvent( passwordName: visitorBloc.userNameController.text, email: visitorBloc.emailController.text, @@ -715,7 +679,7 @@ class VisitorPasswordDialog extends StatelessWidget { } }, child: Text( - 'Ok', + 'Confirm', style: Theme.of(context).textTheme.bodySmall!.copyWith( fontWeight: FontWeight.w400, color: ColorsManager.whiteColors, diff --git a/lib/services/batch_control_devices_service.dart b/lib/services/batch_control_devices_service.dart index f78cdef4..16542c8c 100644 --- a/lib/services/batch_control_devices_service.dart +++ b/lib/services/batch_control_devices_service.dart @@ -11,7 +11,8 @@ abstract interface class BatchControlDevicesService { }); } -final class RemoteBatchControlDevicesService implements BatchControlDevicesService { +final class RemoteBatchControlDevicesService + implements BatchControlDevicesService { @override Future batchControlDevices({ required List uuids, diff --git a/lib/services/devices_mang_api.dart b/lib/services/devices_mang_api.dart index 6fb27daf..709d6855 100644 --- a/lib/services/devices_mang_api.dart +++ b/lib/services/devices_mang_api.dart @@ -393,7 +393,7 @@ class DevicesManagementApi { required String deviceId, required String time, required String code, - required bool value, + required dynamic value, required List days, }) async { final response = await HTTPService().post( diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 50170ed9..40fca1fa 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -83,7 +83,5 @@ abstract class ColorsManager { static const Color maxPurpleDot = Color(0xFF5F00BD); static const Color minBlue = Color(0xFF93AAFD); static const Color minBlueDot = Color(0xFF023DFE); - static const Color grey25 = Color(0xFFF9F9F9); - - + static const Color grey25 = Color(0xFFF9F9F9); } diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 226766b3..87f6e73f 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -14,8 +14,7 @@ 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 = @@ -34,8 +33,7 @@ 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'; @@ -72,22 +70,19 @@ 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 = @@ -104,15 +99,12 @@ 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'; @@ -125,6 +117,10 @@ class Assets { static const String ac = 'assets/icons/AC.svg'; //assets/icons/Curtain.svg static const String curtain = 'assets/icons/Curtain.svg'; + static const String openCurtain = 'assets/icons/open_curtain.svg'; + static const String pauseCurtain = 'assets/icons/pause_curtain.svg'; + static const String closeCurtain = 'assets/icons/close_curtain.svg'; + static const String reverseArrows = 'assets/icons/reverse_arrows.svg'; //assets/icons/doorLock.svg static const String doorLock = 'assets/icons/doorLock.svg'; //assets/icons/Gateway.svg @@ -162,12 +158,10 @@ 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'; @@ -215,8 +209,7 @@ 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'; @@ -287,16 +280,13 @@ 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 = @@ -305,8 +295,7 @@ 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 = @@ -325,8 +314,7 @@ 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 = @@ -339,8 +327,7 @@ 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 = @@ -384,14 +371,13 @@ 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 completedDoneIcon = 'assets/images/completed_done.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'; @@ -412,11 +398,9 @@ 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 = @@ -439,44 +423,29 @@ 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'; @@ -484,13 +453,10 @@ class Assets { static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg'; static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg'; static const String blankCalendar = 'assets/icons/blank_calendar.svg'; - static const String refreshStatusIcon = - 'assets/icons/refresh_status_icon.svg'; - static const String energyConsumedIcon = - 'assets/icons/energy_consumed_icon.svg'; + static const String refreshStatusIcon = 'assets/icons/refresh_status_icon.svg'; + static const String energyConsumedIcon = 'assets/icons/energy_consumed_icon.svg'; - static const String closeSettingsIcon = - 'assets/icons/close_settings_icon.svg'; + static const String closeSettingsIcon = 'assets/icons/close_settings_icon.svg'; static const String editNameIconSettings = 'assets/icons/edit_name_icon_settings.svg'; @@ -501,7 +467,8 @@ class Assets { static const String aqiAirQuality = 'assets/icons/aqi_air_quality.svg'; static const String temperatureAqiSidebar = 'assets/icons/thermometer.svg'; static const String humidityAqiSidebar = 'assets/icons/humidity.svg'; - static const String autocadOccupancyImage = 'assets/images/autocad_occupancy_image.png'; + static const String autocadOccupancyImage = + 'assets/images/autocad_occupancy_image.png'; static const String emptyBarredChart = 'assets/icons/empty_barred_chart.svg'; static const String emptyEnergyManagementChart = 'assets/icons/empty_energy_management_chart.svg'; diff --git a/lib/utils/enum/device_types.dart b/lib/utils/enum/device_types.dart index 9bfd322f..947e63aa 100644 --- a/lib/utils/enum/device_types.dart +++ b/lib/utils/enum/device_types.dart @@ -3,6 +3,7 @@ enum DeviceType { LightBulb, DoorLock, Curtain, + Curtain2, Blind, OneGang, TwoGang, @@ -44,6 +45,7 @@ enum DeviceType { Map devicesTypesMap = { "AC": DeviceType.AC, + "CUR_2": DeviceType.Curtain2, "GW": DeviceType.Gateway, "CPS": DeviceType.CeilingSensor, "DL": DeviceType.DoorLock,