From 7661f54427b5e35d2e2ac7ffbd44e379f82a5c39 Mon Sep 17 00:00:00 2001 From: ashrafzarkanisala Date: Fri, 4 Oct 2024 17:07:00 +0300 Subject: [PATCH] push 1G , 2G glass switches --- lib/main.dart | 9 +- .../helper/route_controls_based_code.dart | 80 ++++----- .../bloc/one_gang_glass_switch_bloc.dart | 143 +++++++++++++++ .../bloc/one_gang_glass_switch_event.dart | 40 +++++ .../bloc/one_gang_glass_switch_state.dart | 32 ++++ .../models/once_gang_glass_status_model.dart | 50 ++++++ .../one_gang_glass_batch_control_view.dart | 80 +++++++++ .../one_gang_glass_switch_control_view.dart | 71 ++++++++ .../bloc/two_gang_glass_switch_bloc.dart | 164 ++++++++++++++++++ .../bloc/two_gang_glass_switch_event.dart | 50 ++++++ .../bloc/two_gang_glass_switch_state.dart | 32 ++++ .../models/two_gang_glass_status_model.dart | 69 ++++++++ ..._gang_glass_switch_batch_control_view.dart | 103 +++++++++++ .../two_gang_glass_switch_control_view.dart | 86 +++++++++ 14 files changed, 959 insertions(+), 50 deletions(-) create mode 100644 lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart create mode 100644 lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_event.dart create mode 100644 lib/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_state.dart create mode 100644 lib/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart create mode 100644 lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_batch_control_view.dart create mode 100644 lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart create mode 100644 lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart create mode 100644 lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_event.dart create mode 100644 lib/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_state.dart create mode 100644 lib/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart create mode 100644 lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_batch_control_view.dart create mode 100644 lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart 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..03c1f03c 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,13 @@ 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_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 +27,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 +44,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 LivingRoomClassSwitchControlView( + // deviceId: device.uuid!, + // ); case 'GW': return GateWayControlsView( gatewayId: device.uuid!, @@ -86,77 +103,52 @@ 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 '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..8403185f --- /dev/null +++ b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart @@ -0,0 +1,71 @@ +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/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, + ), + ); + }, + ), + ], + ); + } +} 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..8d1b9c75 --- /dev/null +++ b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart @@ -0,0 +1,86 @@ +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/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, + ), + ); + }, + ), + ], + ); + } +}