diff --git a/assets/icons/preferences.svg b/assets/icons/preferences.svg new file mode 100644 index 00000000..0a7a7fc0 --- /dev/null +++ b/assets/icons/preferences.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/lib/main.dart b/lib/main.dart index 3b861d10..c544f227 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,11 +2,11 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:go_router/go_router.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; -import 'package:go_router/go_router.dart'; import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/utils/app_routes.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart'; @@ -14,8 +14,7 @@ import 'package:syncrow_web/utils/theme/theme.dart'; Future main() async { try { - const environment = - String.fromEnvironment('FLAVOR', defaultValue: 'development'); + const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development'); await dotenv.load(fileName: '.env.$environment'); WidgetsFlutterBinding.ensureInitialized(); initialSetup(); @@ -45,11 +44,9 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - //HomeBloc.fetchUserInfo(); return MultiBlocProvider( providers: [ - BlocProvider( - create: (context) => HomeBloc()..add(const FetchUserInfo())), + BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())), BlocProvider( create: (context) => VisitorPasswordBloc(), ) 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 068f8c89..eca980c6 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 @@ -13,10 +13,15 @@ import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_batch_co import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_view.dart'; 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_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/three_g_glass_switch/view/three_gang_glass_switch_batch_control_view.dart'; +import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/view/living_room_batch_controls.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/view/living_room_device_control.dart'; +import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_batch_control_view.dart'; +import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/view/wall_light_batch_control.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_batch_control.dart'; @@ -24,6 +29,8 @@ import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_ import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heater_batch_control.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heater_device_control.dart'; +import '../../one_g_glass_switch/view/one_gang_glass_switch_control_view.dart'; + mixin RouteControlsBasedCode { Widget routeControlsWidgets({required AllDevicesModel device}) { switch (device.productType) { @@ -39,6 +46,18 @@ mixin RouteControlsBasedCode { return LivingRoomDeviceControlsView( deviceId: device.uuid!, ); + case '1GT': + return OneGangGlassSwitchControlView( + deviceId: device.uuid!, + ); + case '2GT': + return TwoGangGlassSwitchControlView( + deviceId: device.uuid!, + ); + case '3GT': + return ThreeGangGlassSwitchControlView( + deviceId: device.uuid!, + ); case 'GW': return GateWayControlsView( gatewayId: device.uuid!, @@ -86,77 +105,56 @@ 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(), + ); + case '2GT': + return TwoGangGlassSwitchBatchControlView( + 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(), ); 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 '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(), ); default: return const SizedBox(); diff --git a/lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart b/lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart new file mode 100644 index 00000000..111d5014 --- /dev/null +++ b/lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart @@ -0,0 +1,143 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; +import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart'; +import 'package:syncrow_web/services/devices_mang_api.dart'; + +part 'one_gang_glass_switch_event.dart'; +part 'one_gang_glass_switch_state.dart'; + +class OneGangGlassSwitchBloc extends Bloc { + OneGangGlassStatusModel deviceStatus; + Timer? _timer; + + OneGangGlassSwitchBloc({required String deviceId}) + : deviceStatus = OneGangGlassStatusModel(uuid: deviceId, switch1: false, countDown: 0), + super(OneGangGlassSwitchInitial()) { + on(_onFetchDeviceStatus); + on(_onControl); + on(_onBatchControl); + on(_onFetchBatchStatus); + } + + Future _onFetchDeviceStatus( + OneGangGlassSwitchFetchDeviceEvent event, Emitter emit) async { + emit(OneGangGlassSwitchLoading()); + try { + final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); + deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceId, status.status); + emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); + } catch (e) { + emit(OneGangGlassSwitchError(e.toString())); + } + } + + Future _onControl(OneGangGlassSwitchControl event, Emitter emit) async { + final oldValue = _getValueByCode(event.code); + + _updateLocalValue(event.code, event.value); + emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); + + await _runDebounce( + deviceId: event.deviceId, + code: event.code, + value: event.value, + oldValue: oldValue, + emit: emit, + isBatch: false, + ); + } + + Future _onBatchControl(OneGangGlassSwitchBatchControl event, Emitter emit) async { + final oldValue = _getValueByCode(event.code); + + _updateLocalValue(event.code, event.value); + emit(OneGangGlassSwitchBatchStatusLoaded(deviceStatus)); + + await _runDebounce( + deviceId: event.deviceIds, + code: event.code, + value: event.value, + oldValue: oldValue, + emit: emit, + isBatch: true, + ); + } + + Future _onFetchBatchStatus( + OneGangGlassSwitchFetchBatchStatusEvent event, Emitter emit) async { + emit(OneGangGlassSwitchLoading()); + try { + final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); + deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceIds.first, status.status); + emit(OneGangGlassSwitchBatchStatusLoaded(deviceStatus)); + } catch (e) { + emit(OneGangGlassSwitchError(e.toString())); + } + } + + Future _runDebounce({ + required dynamic deviceId, + required String code, + required bool value, + required bool oldValue, + required Emitter emit, + required bool isBatch, + }) async { + late String id; + if (deviceId is List) { + id = deviceId.first; + } else { + id = deviceId; + } + + if (_timer != null) { + _timer!.cancel(); + } + + _timer = Timer(const Duration(milliseconds: 500), () async { + try { + late bool response; + if (isBatch) { + response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); + } else { + response = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); + } + + if (!response) { + _revertValueAndEmit(id, code, oldValue, emit); + } + } catch (e) { + _revertValueAndEmit(id, code, oldValue, emit); + } + }); + } + + void _revertValueAndEmit(String deviceId, String code, bool oldValue, Emitter emit) { + _updateLocalValue(code, oldValue); + emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); + } + + void _updateLocalValue(String code, bool value) { + if (code == 'switch_1') { + deviceStatus = deviceStatus.copyWith(switch1: value); + } + } + + bool _getValueByCode(String code) { + switch (code) { + case 'switch_1': + return deviceStatus.switch1; + default: + return false; + } + } + + @override + Future close() { + _timer?.cancel(); + return super.close(); + } +} diff --git a/lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_event.dart b/lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_event.dart new file mode 100644 index 00000000..993e5a8c --- /dev/null +++ b/lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_event.dart @@ -0,0 +1,40 @@ +part of 'one_gang_glass_switch_bloc.dart'; + +@immutable +abstract class OneGangGlassSwitchEvent {} + +class OneGangGlassSwitchFetchDeviceEvent extends OneGangGlassSwitchEvent { + final String deviceId; + + OneGangGlassSwitchFetchDeviceEvent(this.deviceId); +} + +class OneGangGlassSwitchControl extends OneGangGlassSwitchEvent { + final String deviceId; + final String code; + final bool value; + + OneGangGlassSwitchControl({ + required this.deviceId, + required this.code, + required this.value, + }); +} + +class OneGangGlassSwitchBatchControl extends OneGangGlassSwitchEvent { + final List deviceIds; + final String code; + final bool value; + + OneGangGlassSwitchBatchControl({ + required this.deviceIds, + required this.code, + required this.value, + }); +} + +class OneGangGlassSwitchFetchBatchStatusEvent extends OneGangGlassSwitchEvent { + final List deviceIds; + + OneGangGlassSwitchFetchBatchStatusEvent(this.deviceIds); +} diff --git a/lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_state.dart b/lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_state.dart new file mode 100644 index 00000000..6059d5eb --- /dev/null +++ b/lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_state.dart @@ -0,0 +1,32 @@ +part of 'one_gang_glass_switch_bloc.dart'; + +@immutable +abstract class OneGangGlassSwitchState {} + +class OneGangGlassSwitchInitial extends OneGangGlassSwitchState {} + +class OneGangGlassSwitchLoading extends OneGangGlassSwitchState {} + +class OneGangGlassSwitchStatusLoaded extends OneGangGlassSwitchState { + final OneGangGlassStatusModel status; + + OneGangGlassSwitchStatusLoaded(this.status); +} + +class OneGangGlassSwitchError extends OneGangGlassSwitchState { + final String message; + + OneGangGlassSwitchError(this.message); +} + +class OneGangGlassSwitchBatchStatusLoaded extends OneGangGlassSwitchState { + final OneGangGlassStatusModel status; + + OneGangGlassSwitchBatchStatusLoaded(this.status); +} + +class OneGangGlassSwitchBatchControlError extends OneGangGlassSwitchState { + final String message; + + OneGangGlassSwitchBatchControlError(this.message); +} diff --git a/lib/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart b/lib/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart new file mode 100644 index 00000000..39c96dd0 --- /dev/null +++ b/lib/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart @@ -0,0 +1,50 @@ +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; + +class OneGangGlassStatusModel { + final String uuid; + final bool switch1; + final int countDown; + + OneGangGlassStatusModel({ + required this.uuid, + required this.switch1, + required this.countDown, + }); + + factory OneGangGlassStatusModel.fromJson(String id, List jsonList) { + late bool switch1; + late int countDown; + + for (var status in jsonList) { + switch (status.code) { + case 'switch_1': + switch1 = status.value ?? false; + break; + case 'countdown_1': + countDown = status.value ?? 0; + break; + } + } + + return OneGangGlassStatusModel( + uuid: id, + switch1: switch1, + countDown: countDown, + ); + } + + OneGangGlassStatusModel copyWith({ + String? uuid, + bool? switch1, + int? countDown, + }) { + return OneGangGlassStatusModel( + uuid: uuid ?? this.uuid, + switch1: switch1 ?? this.switch1, + countDown: countDown ?? this.countDown, + ); + } + + @override + String toString() => 'OneGangGlassStatusModel(uuid: $uuid, switch1: $switch1, countDown: $countDown)'; +} diff --git a/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_batch_control_view.dart b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_batch_control_view.dart new file mode 100644 index 00000000..e81f448c --- /dev/null +++ b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_batch_control_view.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart'; +import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart'; +import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart'; +import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class OneGangGlassSwitchBatchControlView extends StatelessWidget with HelperResponsiveLayout { + final List deviceIds; + + const OneGangGlassSwitchBatchControlView({required this.deviceIds, super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => + OneGangGlassSwitchBloc(deviceId: deviceIds.first)..add(OneGangGlassSwitchFetchBatchStatusEvent(deviceIds)), + child: BlocBuilder( + builder: (context, state) { + if (state is OneGangGlassSwitchLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is OneGangGlassSwitchStatusLoaded) { + return _buildStatusControls(context, state.status); + } else if (state is OneGangGlassSwitchError) { + return const Center(child: Text('Error fetching status')); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ), + ); + } + + Widget _buildStatusControls(BuildContext context, OneGangGlassStatusModel status) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge || isExtraLarge + ? 3 + : isMedium + ? 2 + : 1, + mainAxisExtent: 140, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + ToggleWidget( + value: status.switch1, + code: 'switch_1', + deviceId: deviceIds.first, + label: 'Wall Light', + onChange: (value) { + context.read().add( + OneGangGlassSwitchBatchControl( + deviceIds: deviceIds, + code: 'switch_1', + value: value, + ), + ); + }, + ), + FirmwareUpdateWidget( + deviceId: deviceIds.first, + version: 12, // adjust the version according to your requirement + ), + FactoryResetWidget( + callFactoryReset: () {}, + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart new file mode 100644 index 00000000..77b3dc9e --- /dev/null +++ b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart'; +import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout { + final String deviceId; + + const OneGangGlassSwitchControlView({required this.deviceId, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => + OneGangGlassSwitchBloc(deviceId: deviceId)..add(OneGangGlassSwitchFetchDeviceEvent(deviceId)), + child: BlocBuilder( + builder: (context, state) { + if (state is OneGangGlassSwitchLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is OneGangGlassSwitchStatusLoaded) { + return _buildStatusControls(context, state.status); + } else if (state is OneGangGlassSwitchError) { + return const Center(child: Text('Error fetching status')); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ), + ); + } + + Widget _buildStatusControls(BuildContext context, OneGangGlassStatusModel status) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge || isExtraLarge + ? 3 + : isMedium + ? 2 + : 1, + mainAxisExtent: 140, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + ToggleWidget( + value: status.switch1, + code: 'switch_1', + deviceId: deviceId, + label: 'Wall LightÙ‹', + onChange: (value) { + context.read().add( + OneGangGlassSwitchControl( + deviceId: deviceId, + code: 'switch_1', + value: value, + ), + ); + }, + ), + ToggleWidget( + value: false, + code: '', + deviceId: deviceId, + label: 'Preferences', + icon: Assets.preferences, + onChange: (value) {}, + showToggle: false, + ), + ToggleWidget( + value: false, + code: '', + deviceId: deviceId, + label: 'Scheduling', + icon: Assets.scheduling, + onChange: (value) {}, + showToggle: false, + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/shared/toggle_widget.dart b/lib/pages/device_managment/shared/toggle_widget.dart index 052b04a3..ee76d442 100644 --- a/lib/pages/device_managment/shared/toggle_widget.dart +++ b/lib/pages/device_managment/shared/toggle_widget.dart @@ -1,5 +1,4 @@ import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -13,6 +12,7 @@ class ToggleWidget extends StatelessWidget { final String? icon; final Widget? labelWidget; final Function(dynamic value) onChange; + final bool showToggle; const ToggleWidget({ super.key, @@ -23,6 +23,7 @@ class ToggleWidget extends StatelessWidget { required this.onChange, this.icon, this.labelWidget, + this.showToggle = true, }); @override @@ -62,16 +63,17 @@ class ToggleWidget extends StatelessWidget { fit: BoxFit.contain, ), )), - Container( - height: 20, - width: 35, - padding: const EdgeInsets.only(right: 16, top: 10), - child: CupertinoSwitch( - value: value, - activeColor: ColorsManager.dialogBlueTitle, - onChanged: onChange, + if (showToggle) + Container( + height: 20, + width: 35, + padding: const EdgeInsets.only(right: 16, top: 10), + child: CupertinoSwitch( + value: value, + activeColor: ColorsManager.dialogBlueTitle, + onChanged: onChange, + ), ), - ), ], ), ), diff --git a/lib/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart b/lib/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart new file mode 100644 index 00000000..8e3c109e --- /dev/null +++ b/lib/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart @@ -0,0 +1,174 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.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/three_g_glass_switch/models/three_gang_glass_switch.dart'; +import 'package:syncrow_web/services/devices_mang_api.dart'; + +part 'three_gang_glass_switch_event.dart'; +part 'three_gang_glass_switch_state.dart'; + +class ThreeGangGlassSwitchBloc extends Bloc { + ThreeGangGlassStatusModel deviceStatus; + Timer? _timer; + + ThreeGangGlassSwitchBloc({required String deviceId}) + : deviceStatus = ThreeGangGlassStatusModel( + uuid: deviceId, + switch1: false, + countDown1: 0, + switch2: false, + countDown2: 0, + switch3: false, + countDown3: 0), + super(ThreeGangGlassSwitchInitial()) { + on(_onFetchDeviceStatus); + on(_onControl); + on(_onBatchControl); + on(_onFetchBatchStatus); + on(_onFactoryReset); + } + + Future _onFetchDeviceStatus( + ThreeGangGlassSwitchFetchDeviceEvent event, Emitter emit) async { + emit(ThreeGangGlassSwitchLoading()); + try { + final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); + deviceStatus = ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status); + emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); + } catch (e) { + emit(ThreeGangGlassSwitchError(e.toString())); + } + } + + Future _onControl(ThreeGangGlassSwitchControl event, Emitter emit) async { + final oldValue = _getValueByCode(event.code); + + _updateLocalValue(event.code, event.value); + emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); + + await _runDebounce( + deviceId: event.deviceId, + code: event.code, + value: event.value, + oldValue: oldValue, + emit: emit, + isBatch: false, + ); + } + + Future _onBatchControl(ThreeGangGlassSwitchBatchControl event, Emitter emit) async { + final oldValue = _getValueByCode(event.code); + + _updateLocalValue(event.code, event.value); + emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus)); + + await _runDebounce( + deviceId: event.deviceIds, + code: event.code, + value: event.value, + oldValue: oldValue, + emit: emit, + isBatch: true, + ); + } + + Future _onFetchBatchStatus( + ThreeGangGlassSwitchFetchBatchStatusEvent event, Emitter emit) async { + emit(ThreeGangGlassSwitchLoading()); + try { + final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); + deviceStatus = ThreeGangGlassStatusModel.fromJson(event.deviceIds.first, status.status); + emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus)); + } catch (e) { + emit(ThreeGangGlassSwitchError(e.toString())); + } + } + + Future _onFactoryReset(ThreeGangGlassFactoryReset event, Emitter emit) async { + emit(ThreeGangGlassSwitchLoading()); + try { + final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId); + if (!response) { + emit(ThreeGangGlassSwitchError('Failed')); + } else { + emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); + } + } catch (e) { + emit(ThreeGangGlassSwitchError(e.toString())); + } + } + + Future _runDebounce({ + required dynamic deviceId, + required String code, + required bool value, + required bool oldValue, + required Emitter emit, + required bool isBatch, + }) async { + late String id; + if (deviceId is List) { + id = deviceId.first; + } else { + id = deviceId; + } + + if (_timer != null) { + _timer!.cancel(); + } + + _timer = Timer(const Duration(milliseconds: 500), () async { + try { + late bool response; + if (isBatch) { + response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); + } else { + response = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); + } + + if (!response) { + _revertValueAndEmit(id, code, oldValue, emit); + } + } catch (e) { + _revertValueAndEmit(id, code, oldValue, emit); + } + }); + } + + void _revertValueAndEmit(String deviceId, String code, bool oldValue, Emitter emit) { + _updateLocalValue(code, oldValue); + emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); + } + + void _updateLocalValue(String code, bool value) { + if (code == 'switch_1') { + deviceStatus = deviceStatus.copyWith(switch1: value); + } else if (code == 'switch_2') { + deviceStatus = deviceStatus.copyWith(switch2: value); + } else if (code == 'switch_3') { + deviceStatus = deviceStatus.copyWith(switch3: value); + } + } + + bool _getValueByCode(String code) { + switch (code) { + case 'switch_1': + return deviceStatus.switch1; + case 'switch_2': + return deviceStatus.switch2; + case 'switch_3': + return deviceStatus.switch3; + default: + return false; + } + } + + @override + Future close() { + _timer?.cancel(); + return super.close(); + } +} diff --git a/lib/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_event.dart b/lib/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_event.dart new file mode 100644 index 00000000..a40888e7 --- /dev/null +++ b/lib/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_event.dart @@ -0,0 +1,50 @@ +part of 'three_gang_glass_switch_bloc.dart'; + +@immutable +abstract class ThreeGangGlassSwitchEvent {} + +class ThreeGangGlassSwitchFetchDeviceEvent extends ThreeGangGlassSwitchEvent { + final String deviceId; + + ThreeGangGlassSwitchFetchDeviceEvent(this.deviceId); +} + +class ThreeGangGlassSwitchControl extends ThreeGangGlassSwitchEvent { + final String deviceId; + final String code; + final bool value; + + ThreeGangGlassSwitchControl({ + required this.deviceId, + required this.code, + required this.value, + }); +} + +class ThreeGangGlassSwitchBatchControl extends ThreeGangGlassSwitchEvent { + final List deviceIds; + final String code; + final bool value; + + ThreeGangGlassSwitchBatchControl({ + required this.deviceIds, + required this.code, + required this.value, + }); +} + +class ThreeGangGlassSwitchFetchBatchStatusEvent extends ThreeGangGlassSwitchEvent { + final List deviceIds; + + ThreeGangGlassSwitchFetchBatchStatusEvent(this.deviceIds); +} + +class ThreeGangGlassFactoryReset extends ThreeGangGlassSwitchEvent { + final String deviceId; + final FactoryResetModel factoryReset; + + ThreeGangGlassFactoryReset({ + required this.deviceId, + required this.factoryReset, + }); +} diff --git a/lib/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_state.dart b/lib/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_state.dart new file mode 100644 index 00000000..aedb3a9b --- /dev/null +++ b/lib/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_state.dart @@ -0,0 +1,32 @@ +part of 'three_gang_glass_switch_bloc.dart'; + +@immutable +abstract class ThreeGangGlassSwitchState {} + +class ThreeGangGlassSwitchInitial extends ThreeGangGlassSwitchState {} + +class ThreeGangGlassSwitchLoading extends ThreeGangGlassSwitchState {} + +class ThreeGangGlassSwitchStatusLoaded extends ThreeGangGlassSwitchState { + final ThreeGangGlassStatusModel status; + + ThreeGangGlassSwitchStatusLoaded(this.status); +} + +class ThreeGangGlassSwitchError extends ThreeGangGlassSwitchState { + final String message; + + ThreeGangGlassSwitchError(this.message); +} + +class ThreeGangGlassSwitchBatchStatusLoaded extends ThreeGangGlassSwitchState { + final ThreeGangGlassStatusModel status; + + ThreeGangGlassSwitchBatchStatusLoaded(this.status); +} + +class ThreeGangGlassSwitchBatchControlError extends ThreeGangGlassSwitchState { + final String message; + + ThreeGangGlassSwitchBatchControlError(this.message); +} diff --git a/lib/pages/device_managment/three_g_glass_switch/models/three_gang_glass_switch.dart b/lib/pages/device_managment/three_g_glass_switch/models/three_gang_glass_switch.dart new file mode 100644 index 00000000..cec12b7f --- /dev/null +++ b/lib/pages/device_managment/three_g_glass_switch/models/three_gang_glass_switch.dart @@ -0,0 +1,87 @@ +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; + +class ThreeGangGlassStatusModel { + final String uuid; + final bool switch1; + final int countDown1; + final bool switch2; + final int countDown2; + final bool switch3; + final int countDown3; + + ThreeGangGlassStatusModel({ + required this.uuid, + required this.switch1, + required this.countDown1, + required this.switch2, + required this.countDown2, + required this.switch3, + required this.countDown3, + }); + + factory ThreeGangGlassStatusModel.fromJson(String id, List jsonList) { + late bool switch1; + late int countDown1; + late bool switch2; + late int countDown2; + late bool switch3; + late int countDown3; + + for (var status in jsonList) { + switch (status.code) { + case 'switch_1': + switch1 = status.value ?? false; + break; + case 'countdown_1': + countDown1 = status.value ?? 0; + break; + case 'switch_2': + switch2 = status.value ?? false; + break; + case 'countdown_2': + countDown2 = status.value ?? 0; + break; + case 'switch_3': + switch3 = status.value ?? false; + break; + case 'countdown_3': + countDown3 = status.value ?? 0; + break; + } + } + + return ThreeGangGlassStatusModel( + uuid: id, + switch1: switch1, + countDown1: countDown1, + switch2: switch2, + countDown2: countDown2, + switch3: switch3, + countDown3: countDown3, + ); + } + + ThreeGangGlassStatusModel copyWith({ + String? uuid, + bool? switch1, + int? countDown1, + bool? switch2, + int? countDown2, + bool? switch3, + int? countDown3, + }) { + return ThreeGangGlassStatusModel( + uuid: uuid ?? this.uuid, + switch1: switch1 ?? this.switch1, + countDown1: countDown1 ?? this.countDown1, + switch2: switch2 ?? this.switch2, + countDown2: countDown2 ?? this.countDown2, + switch3: switch3 ?? this.switch3, + countDown3: countDown3 ?? this.countDown3, + ); + } + + @override + String toString() => + 'ThreeGangGlassStatusModel(uuid: $uuid, switch1: $switch1, countDown1: $countDown1, switch2: $switch2, countDown2: $countDown2, switch3: $switch3, countDown3: $countDown3)'; +} diff --git a/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_batch_control_view.dart b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_batch_control_view.dart new file mode 100644 index 00000000..f7118ae6 --- /dev/null +++ b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_batch_control_view.dart @@ -0,0 +1,118 @@ +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/shared/batch_control/factory_reset.dart'; +import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart'; +import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; +import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/models/three_gang_glass_switch.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class ThreeGangGlassSwitchBatchControlView extends StatelessWidget with HelperResponsiveLayout { + final List deviceIds; + + const ThreeGangGlassSwitchBatchControlView({required this.deviceIds, super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => ThreeGangGlassSwitchBloc(deviceId: deviceIds.first) + ..add(ThreeGangGlassSwitchFetchBatchStatusEvent(deviceIds)), + child: BlocBuilder( + builder: (context, state) { + if (state is ThreeGangGlassSwitchLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is ThreeGangGlassSwitchBatchStatusLoaded) { + return _buildStatusControls(context, state.status); + } else if (state is ThreeGangGlassSwitchError) { + return const Center(child: Text('Error fetching status')); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ), + ); + } + + Widget _buildStatusControls(BuildContext context, ThreeGangGlassStatusModel status) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge || isExtraLarge + ? 3 + : isMedium + ? 2 + : 1, + mainAxisExtent: 140, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + ToggleWidget( + value: status.switch1, + code: 'switch_1', + deviceId: deviceIds.first, + label: 'Glass Switch 1', + onChange: (value) { + context.read().add( + ThreeGangGlassSwitchBatchControl( + deviceIds: deviceIds, + code: 'switch_1', + value: value, + ), + ); + }, + ), + ToggleWidget( + value: status.switch2, + code: 'switch_2', + deviceId: deviceIds.first, + label: 'Glass Switch 2', + onChange: (value) { + context.read().add( + ThreeGangGlassSwitchBatchControl( + deviceIds: deviceIds, + code: 'switch_2', + value: value, + ), + ); + }, + ), + ToggleWidget( + value: status.switch3, + code: 'switch_3', + deviceId: deviceIds.first, + label: 'Glass Switch 3', + onChange: (value) { + context.read().add( + ThreeGangGlassSwitchBatchControl( + deviceIds: deviceIds, + code: 'switch_3', + value: value, + ), + ); + }, + ), + FirmwareUpdateWidget( + deviceId: deviceIds.first, + version: 12, // adjust the version according to your requirement + ), + FactoryResetWidget( + callFactoryReset: () { + context.read().add( + ThreeGangGlassFactoryReset( + deviceId: status.uuid, + factoryReset: FactoryResetModel(devicesUuid: deviceIds), + ), + ); + }, + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart new file mode 100644 index 00000000..2f14eaac --- /dev/null +++ b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; +import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +import '../models/three_gang_glass_switch.dart'; + +class ThreeGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout { + final String deviceId; + + const ThreeGangGlassSwitchControlView({required this.deviceId, super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => + ThreeGangGlassSwitchBloc(deviceId: deviceId)..add(ThreeGangGlassSwitchFetchDeviceEvent(deviceId)), + child: BlocBuilder( + builder: (context, state) { + if (state is ThreeGangGlassSwitchLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is ThreeGangGlassSwitchStatusLoaded) { + return _buildStatusControls(context, state.status); + } else if (state is ThreeGangGlassSwitchError) { + return const Center(child: Text('Error fetching status')); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ), + ); + } + + Widget _buildStatusControls(BuildContext context, ThreeGangGlassStatusModel status) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge || isExtraLarge + ? 3 + : isMedium + ? 2 + : 1, + mainAxisExtent: 140, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + ToggleWidget( + value: status.switch1, + code: 'switch_1', + deviceId: deviceId, + label: 'Wall Light', + onChange: (value) { + context.read().add( + ThreeGangGlassSwitchControl( + deviceId: deviceId, + code: 'switch_1', + value: value, + ), + ); + }, + ), + ToggleWidget( + value: status.switch2, + code: 'switch_2', + deviceId: deviceId, + label: 'Ceiling Light', + onChange: (value) { + context.read().add( + ThreeGangGlassSwitchControl( + deviceId: deviceId, + code: 'switch_2', + value: value, + ), + ); + }, + ), + ToggleWidget( + value: status.switch3, + code: 'switch_3', + deviceId: deviceId, + label: 'SpotLight', + onChange: (value) { + context.read().add( + ThreeGangGlassSwitchControl( + deviceId: deviceId, + code: 'switch_3', + value: value, + ), + ); + }, + ), + ToggleWidget( + value: false, + code: '', + deviceId: deviceId, + label: 'Preferences', + icon: Assets.preferences, + onChange: (value) {}, + showToggle: false, + ), + ToggleWidget( + value: false, + code: '', + deviceId: deviceId, + label: 'Scheduling', + icon: Assets.scheduling, + onChange: (value) {}, + showToggle: false, + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart b/lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart new file mode 100644 index 00000000..be2ab687 --- /dev/null +++ b/lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart @@ -0,0 +1,164 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.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/two_g_glass_switch/models/two_gang_glass_status_model.dart'; +import 'package:syncrow_web/services/devices_mang_api.dart'; + +part 'two_gang_glass_switch_event.dart'; +part 'two_gang_glass_switch_state.dart'; + +class TwoGangGlassSwitchBloc extends Bloc { + TwoGangGlassStatusModel deviceStatus; + Timer? _timer; + + TwoGangGlassSwitchBloc({required String deviceId}) + : deviceStatus = + TwoGangGlassStatusModel(uuid: deviceId, switch1: false, countDown1: 0, switch2: false, countDown2: 0), + super(TwoGangGlassSwitchInitial()) { + on(_onFetchDeviceStatus); + on(_onControl); + on(_onBatchControl); + on(_onFetchBatchStatus); + on(_onFactoryReset); + } + + Future _onFetchDeviceStatus( + TwoGangGlassSwitchFetchDeviceEvent event, Emitter emit) async { + emit(TwoGangGlassSwitchLoading()); + try { + final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); + deviceStatus = TwoGangGlassStatusModel.fromJson(event.deviceId, status.status); + emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); + } catch (e) { + emit(TwoGangGlassSwitchError(e.toString())); + } + } + + Future _onControl(TwoGangGlassSwitchControl event, Emitter emit) async { + final oldValue = _getValueByCode(event.code); + + _updateLocalValue(event.code, event.value); + emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); + + await _runDebounce( + deviceId: event.deviceId, + code: event.code, + value: event.value, + oldValue: oldValue, + emit: emit, + isBatch: false, + ); + } + + Future _onBatchControl(TwoGangGlassSwitchBatchControl event, Emitter emit) async { + final oldValue = _getValueByCode(event.code); + + _updateLocalValue(event.code, event.value); + emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus)); + + await _runDebounce( + deviceId: event.deviceIds, + code: event.code, + value: event.value, + oldValue: oldValue, + emit: emit, + isBatch: true, + ); + } + + Future _onFetchBatchStatus( + TwoGangGlassSwitchFetchBatchStatusEvent event, Emitter emit) async { + emit(TwoGangGlassSwitchLoading()); + try { + final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); + deviceStatus = TwoGangGlassStatusModel.fromJson(event.deviceIds.first, status.status); + emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus)); + } catch (e) { + emit(TwoGangGlassSwitchError(e.toString())); + } + } + + Future _onFactoryReset(TwoGangGlassFactoryReset event, Emitter emit) async { + emit(TwoGangGlassSwitchLoading()); + try { + final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId); + if (!response) { + emit(TwoGangGlassSwitchError('Failed')); + } else { + emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); + } + } catch (e) { + emit(TwoGangGlassSwitchError(e.toString())); + } + } + + Future _runDebounce({ + required dynamic deviceId, + required String code, + required bool value, + required bool oldValue, + required Emitter emit, + required bool isBatch, + }) async { + late String id; + if (deviceId is List) { + id = deviceId.first; + } else { + id = deviceId; + } + + if (_timer != null) { + _timer!.cancel(); + } + + _timer = Timer(const Duration(milliseconds: 500), () async { + try { + late bool response; + if (isBatch) { + response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); + } else { + response = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); + } + + if (!response) { + _revertValueAndEmit(id, code, oldValue, emit); + } + } catch (e) { + _revertValueAndEmit(id, code, oldValue, emit); + } + }); + } + + void _revertValueAndEmit(String deviceId, String code, bool oldValue, Emitter emit) { + _updateLocalValue(code, oldValue); + emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); + } + + void _updateLocalValue(String code, bool value) { + if (code == 'switch_1') { + deviceStatus = deviceStatus.copyWith(switch1: value); + } else if (code == 'switch_2') { + deviceStatus = deviceStatus.copyWith(switch2: value); + } + } + + bool _getValueByCode(String code) { + switch (code) { + case 'switch_1': + return deviceStatus.switch1; + case 'switch_2': + return deviceStatus.switch2; + default: + return false; + } + } + + @override + Future close() { + _timer?.cancel(); + return super.close(); + } +} diff --git a/lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_event.dart b/lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_event.dart new file mode 100644 index 00000000..f88d61fe --- /dev/null +++ b/lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_event.dart @@ -0,0 +1,50 @@ +part of 'two_gang_glass_switch_bloc.dart'; + +@immutable +abstract class TwoGangGlassSwitchEvent {} + +class TwoGangGlassSwitchFetchDeviceEvent extends TwoGangGlassSwitchEvent { + final String deviceId; + + TwoGangGlassSwitchFetchDeviceEvent(this.deviceId); +} + +class TwoGangGlassSwitchControl extends TwoGangGlassSwitchEvent { + final String deviceId; + final String code; + final bool value; + + TwoGangGlassSwitchControl({ + required this.deviceId, + required this.code, + required this.value, + }); +} + +class TwoGangGlassSwitchBatchControl extends TwoGangGlassSwitchEvent { + final List deviceIds; + final String code; + final bool value; + + TwoGangGlassSwitchBatchControl({ + required this.deviceIds, + required this.code, + required this.value, + }); +} + +class TwoGangGlassSwitchFetchBatchStatusEvent extends TwoGangGlassSwitchEvent { + final List deviceIds; + + TwoGangGlassSwitchFetchBatchStatusEvent(this.deviceIds); +} + +class TwoGangGlassFactoryReset extends TwoGangGlassSwitchEvent { + final String deviceId; + final FactoryResetModel factoryReset; + + TwoGangGlassFactoryReset({ + required this.deviceId, + required this.factoryReset, + }); +} diff --git a/lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_state.dart b/lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_state.dart new file mode 100644 index 00000000..3f95a514 --- /dev/null +++ b/lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_state.dart @@ -0,0 +1,32 @@ +part of 'two_gang_glass_switch_bloc.dart'; + +@immutable +abstract class TwoGangGlassSwitchState {} + +class TwoGangGlassSwitchInitial extends TwoGangGlassSwitchState {} + +class TwoGangGlassSwitchLoading extends TwoGangGlassSwitchState {} + +class TwoGangGlassSwitchStatusLoaded extends TwoGangGlassSwitchState { + final TwoGangGlassStatusModel status; + + TwoGangGlassSwitchStatusLoaded(this.status); +} + +class TwoGangGlassSwitchError extends TwoGangGlassSwitchState { + final String message; + + TwoGangGlassSwitchError(this.message); +} + +class TwoGangGlassSwitchBatchStatusLoaded extends TwoGangGlassSwitchState { + final TwoGangGlassStatusModel status; + + TwoGangGlassSwitchBatchStatusLoaded(this.status); +} + +class TwoGangGlassSwitchBatchControlError extends TwoGangGlassSwitchState { + final String message; + + TwoGangGlassSwitchBatchControlError(this.message); +} diff --git a/lib/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart b/lib/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart new file mode 100644 index 00000000..54d99d74 --- /dev/null +++ b/lib/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart @@ -0,0 +1,69 @@ +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; + +class TwoGangGlassStatusModel { + final String uuid; + final bool switch1; + final int countDown1; + final bool switch2; + final int countDown2; + + TwoGangGlassStatusModel({ + required this.uuid, + required this.switch1, + required this.countDown1, + required this.switch2, + required this.countDown2, + }); + + factory TwoGangGlassStatusModel.fromJson(String id, List jsonList) { + late bool switch1; + late int countDown1; + late bool switch2; + late int countDown2; + + for (var status in jsonList) { + switch (status.code) { + case 'switch_1': + switch1 = status.value ?? false; + break; + case 'countdown_1': + countDown1 = status.value ?? 0; + break; + case 'switch_2': + switch2 = status.value ?? false; + break; + case 'countdown_2': + countDown2 = status.value ?? 0; + break; + } + } + + return TwoGangGlassStatusModel( + uuid: id, + switch1: switch1, + countDown1: countDown1, + switch2: switch2, + countDown2: countDown2, + ); + } + + TwoGangGlassStatusModel copyWith({ + String? uuid, + bool? switch1, + int? countDown1, + bool? switch2, + int? countDown2, + }) { + return TwoGangGlassStatusModel( + uuid: uuid ?? this.uuid, + switch1: switch1 ?? this.switch1, + countDown1: countDown1 ?? this.countDown1, + switch2: switch2 ?? this.switch2, + countDown2: countDown2 ?? this.countDown2, + ); + } + + @override + String toString() => + 'TwoGangGlassStatusModel(uuid: $uuid, switch1: $switch1, countDown1: $countDown1, switch2: $switch2, countDown2: $countDown2)'; +} diff --git a/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_batch_control_view.dart b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_batch_control_view.dart new file mode 100644 index 00000000..d0288ca3 --- /dev/null +++ b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_batch_control_view.dart @@ -0,0 +1,103 @@ +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/shared/batch_control/factory_reset.dart'; +import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart'; +import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; +import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class TwoGangGlassSwitchBatchControlView extends StatelessWidget with HelperResponsiveLayout { + final List deviceIds; + + const TwoGangGlassSwitchBatchControlView({required this.deviceIds, super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => + TwoGangGlassSwitchBloc(deviceId: deviceIds.first)..add(TwoGangGlassSwitchFetchBatchStatusEvent(deviceIds)), + child: BlocBuilder( + builder: (context, state) { + if (state is TwoGangGlassSwitchLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is TwoGangGlassSwitchBatchStatusLoaded) { + return _buildStatusControls(context, state.status); + } else if (state is TwoGangGlassSwitchError) { + return const Center(child: Text('Error fetching status')); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ), + ); + } + + Widget _buildStatusControls(BuildContext context, TwoGangGlassStatusModel status) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge || isExtraLarge + ? 3 + : isMedium + ? 2 + : 1, + mainAxisExtent: 140, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + ToggleWidget( + value: status.switch1, + code: 'switch_1', + deviceId: deviceIds.first, + label: 'Wall Light', + onChange: (value) { + context.read().add( + TwoGangGlassSwitchBatchControl( + deviceIds: deviceIds, + code: 'switch_1', + value: value, + ), + ); + }, + ), + ToggleWidget( + value: status.switch2, + code: 'switch_2', + deviceId: deviceIds.first, + label: 'Ceiling Light', + onChange: (value) { + context.read().add( + TwoGangGlassSwitchBatchControl( + deviceIds: deviceIds, + code: 'switch_2', + value: value, + ), + ); + }, + ), + FirmwareUpdateWidget( + deviceId: deviceIds.first, + version: 12, // adjust the version according to your requirement + ), + FactoryResetWidget( + callFactoryReset: () { + context.read().add( + TwoGangGlassFactoryReset( + deviceId: status.uuid, + factoryReset: FactoryResetModel(devicesUuid: deviceIds), + ), + ); + }, + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart new file mode 100644 index 00000000..72f69763 --- /dev/null +++ b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; +import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class TwoGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout { + final String deviceId; + + const TwoGangGlassSwitchControlView({required this.deviceId, super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => + TwoGangGlassSwitchBloc(deviceId: deviceId)..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)), + child: BlocBuilder( + builder: (context, state) { + if (state is TwoGangGlassSwitchLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is TwoGangGlassSwitchStatusLoaded) { + return _buildStatusControls(context, state.status); + } else if (state is TwoGangGlassSwitchError) { + return const Center(child: Text('Error fetching status')); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ), + ); + } + + Widget _buildStatusControls(BuildContext context, TwoGangGlassStatusModel status) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge || isExtraLarge + ? 3 + : isMedium + ? 2 + : 1, + mainAxisExtent: 140, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + ToggleWidget( + value: status.switch1, + code: 'switch_1', + deviceId: deviceId, + label: 'Wall Light', + onChange: (value) { + context.read().add( + TwoGangGlassSwitchControl( + deviceId: deviceId, + code: 'switch_1', + value: value, + ), + ); + }, + ), + ToggleWidget( + value: status.switch2, + code: 'switch_2', + deviceId: deviceId, + label: 'Ceiling Light', + onChange: (value) { + context.read().add( + TwoGangGlassSwitchControl( + deviceId: deviceId, + code: 'switch_2', + value: value, + ), + ); + }, + ), + ToggleWidget( + value: false, + code: '', + deviceId: deviceId, + label: 'Preferences', + icon: Assets.preferences, + onChange: (value) {}, + showToggle: false, + ), + ToggleWidget( + value: false, + code: '', + deviceId: deviceId, + label: 'Scheduling', + icon: Assets.scheduling, + onChange: (value) {}, + showToggle: false, + ), + ], + ); + } +} diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 3a28a20c..ff1b3c15 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -13,12 +13,10 @@ 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 = - "assets/images/spase_management_icon.svg"; + static const String spaseManagementIcon = "assets/images/spase_management_icon.svg"; static const String devicesIcon = "assets/images/devices_icon.svg"; static const String moveinIcon = "assets/images/movein_icon.svg"; static const String constructionIcon = "assets/images/construction_icon.svg"; @@ -31,15 +29,13 @@ 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"; static const String switchAlarmSound = "assets/icons/switch_alarm_sound.svg"; static const String resetOff = "assets/icons/reset_off.svg"; - static const String sensitivityOperationIcon = - "assets/icons/sesitivity_operation_icon.svg"; + static const String sensitivityOperationIcon = "assets/icons/sesitivity_operation_icon.svg"; static const String motionDetection = "assets/icons/motion_detection.svg"; static const String freezing = "assets/icons/freezing.svg"; static const String indicator = "assets/icons/indicator.svg"; @@ -60,56 +56,35 @@ class Assets { static const String celsiusDegrees = "assets/icons/celsius_degrees.svg"; static const String masterState = "assets/icons/master_state.svg"; static const String acPower = "assets/icons/ac_power.svg"; - static const String farDetectionFunction = - "assets/icons/far_detection_function.svg"; + static const String farDetectionFunction = "assets/icons/far_detection_function.svg"; static const String nobodyTime = "assets/icons/nobody_time.svg"; // Automation functions - static const String tempPasswordUnlock = - "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 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 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 residualElectricity = - "assets/icons/automation_functions/residual_electricity.svg"; - static const String hijackAlarm = - "assets/icons/automation_functions/hijack_alarm.svg"; - static const String passwordUnlock = - "assets/icons/automation_functions/password_unlock.svg"; - static const String remoteUnlockRequest = - "assets/icons/automation_functions/remote_unlock_req.svg"; - static const String cardUnlock = - "assets/icons/automation_functions/card_unlock.svg"; + static const String tempPasswordUnlock = "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 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 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 residualElectricity = "assets/icons/automation_functions/residual_electricity.svg"; + static const String hijackAlarm = "assets/icons/automation_functions/hijack_alarm.svg"; + static const String passwordUnlock = "assets/icons/automation_functions/password_unlock.svg"; + static const String remoteUnlockRequest = "assets/icons/automation_functions/remote_unlock_req.svg"; + static const String cardUnlock = "assets/icons/automation_functions/card_unlock.svg"; static const String motion = "assets/icons/automation_functions/motion.svg"; - static const String fingerprintUnlock = - "assets/icons/automation_functions/fingerprint_unlock.svg"; + static const String fingerprintUnlock = "assets/icons/automation_functions/fingerprint_unlock.svg"; // 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 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 lightPulp = "assets/icons/light_pulb.svg"; static const String acDevice = "assets/icons/ac_device.svg"; @@ -172,4 +147,7 @@ class Assets { //assets/icons/ac_schedule.svg static const String acSchedule = 'assets/icons/ac_schedule.svg'; + + //assets/icons/preferences.svg + static const String preferences = 'assets/icons/preferences.svg'; }