From 60b51657a6a1e4db5b6e132463f92064eeeb8731 Mon Sep 17 00:00:00 2001 From: ashrafzarkanisala Date: Tue, 17 Sep 2024 00:22:51 +0300 Subject: [PATCH] working on water heater --- .../bloc/device_managment_bloc.dart | 8 ++ .../helper/route_controls_based_code.dart | 3 + .../all_devices/models/fake_door_sensor.dart | 33 +++++ .../models/fake_heater_device.dart | 33 +++++ .../widgets/living_toggle_widget.dart | 4 +- .../water_heater/bloc/water_heater_bloc.dart | 132 ++++++++++++++++++ .../water_heater/bloc/water_heater_event.dart | 49 +++++++ .../water_heater/bloc/water_heater_state.dart | 76 ++++++++++ .../models/water_heater_status_model.dart | 83 +++++++++++ .../view/water_heater_device_control.dart | 73 ++++++++++ 10 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 lib/pages/device_managment/all_devices/models/fake_door_sensor.dart create mode 100644 lib/pages/device_managment/all_devices/models/fake_heater_device.dart create mode 100644 lib/pages/device_managment/water_heater/bloc/water_heater_bloc.dart create mode 100644 lib/pages/device_managment/water_heater/bloc/water_heater_event.dart create mode 100644 lib/pages/device_managment/water_heater/bloc/water_heater_state.dart create mode 100644 lib/pages/device_managment/water_heater/models/water_heater_status_model.dart create mode 100644 lib/pages/device_managment/water_heater/view/water_heater_device_control.dart diff --git a/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart b/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart index 70c65fcc..c3104c65 100644 --- a/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart +++ b/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart @@ -1,7 +1,10 @@ import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/fake_door_sensor.dart'; import 'package:syncrow_web/services/devices_mang_api.dart'; + +import '../models/fake_heater_device.dart'; part 'device_managment_event.dart'; part 'device_managment_state.dart'; @@ -31,6 +34,11 @@ class DeviceManagementBloc try { final devices = await DevicesManagementApi().fetchDevices(); _selectedDevices.clear(); + + /// add fake device for heater + devices.insert(0, fakeWaterHeaterDevice); + devices.insert(1, fakeMainDoorSensor); + //// _devices = devices; _filteredDevices = devices; _calculateDeviceCounts(); 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 9c893983..2872ee6d 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 @@ -19,6 +19,7 @@ import 'package:syncrow_web/pages/device_managment/two_gang_switch/view/wall_lig 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'; import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heater_device_control.dart'; mixin RouteControlsBasedCode { Widget routeControlsWidgets({required AllDevicesModel device}) { @@ -53,6 +54,8 @@ mixin RouteControlsBasedCode { ); case 'AC': return AcDeviceControlsView(device: device); + case 'WH': + return WaterHeaterDeviceControl(device: device,); default: return const SizedBox(); } diff --git a/lib/pages/device_managment/all_devices/models/fake_door_sensor.dart b/lib/pages/device_managment/all_devices/models/fake_door_sensor.dart new file mode 100644 index 00000000..6682de18 --- /dev/null +++ b/lib/pages/device_managment/all_devices/models/fake_door_sensor.dart @@ -0,0 +1,33 @@ +import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart'; + +AllDevicesModel fakeMainDoorSensor = AllDevicesModel( + room: DevicesModelRoom( + uuid: "12de8f60-7104-4726-b5f8-ea426c0c7c3d", name: "Main Hall"), + unit: DevicesModelUnit( + uuid: "08fd3dcf-d13a-40db-970d-d0ce893df30e", name: "Entrance Unit 1"), + productUuid: "fake-uuid-main-door-sensor", + productType: "DS", + permissionType: "CONTROLLABLE", + activeTime: 1722178888, + category: "sensor", + categoryName: "Door Sensor", + createTime: 1722178888, + gatewayId: "b49df7395gfd8c19047krmk", + icon: "smart/icon/bay1642572935122vdsS/2b2f5fffaa5bbf81c3164fc313df2023.png", + ip: "", + lat: "31.92", + localKey: "A/43+:7M", + lon: "35.85", + model: "D03ZLSDSA2", + name: "Main Door Sensor", + nodeId: "70a523ffece8a7f9", + online: true, + ownerId: "199300932", + sub: true, + timeZone: "+03:00", + updateTime: 1723627123, + uuid: "9c32dac5-ce0c-4c85-b45c-8e16511174cg", + batteryLevel: 85, +); diff --git a/lib/pages/device_managment/all_devices/models/fake_heater_device.dart b/lib/pages/device_managment/all_devices/models/fake_heater_device.dart new file mode 100644 index 00000000..8ce1bb90 --- /dev/null +++ b/lib/pages/device_managment/all_devices/models/fake_heater_device.dart @@ -0,0 +1,33 @@ +import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart'; + +AllDevicesModel fakeWaterHeaterDevice = AllDevicesModel( + room: DevicesModelRoom( + uuid: "75ea7d60-5104-4726-b5f8-ea426c0c6a1b", name: "Kitchen"), + unit: DevicesModelUnit( + uuid: "04fd1dcf-f24a-40db-970d-d0be884ed30f", name: "Flat 101"), + productUuid: "fake-uuid-kitchen-water-heater", + productType: "WH", + permissionType: "CONTROLLABLE", + activeTime: 1722173778, + category: "kg", + categoryName: "Water Heater", + createTime: 1722173778, + gatewayId: "bf0294123ed2c19067skrk", + icon: "smart/icon/bay1642572935385vcsA/2b1f5efbaa5bbf81c3164fa312cf2032.png", + ip: "", + lat: "31.97", + localKey: "T/39+:9M", + lon: "35.89", + model: "S01ZLSWBSA3", + name: "Kitchen Water Heater", + nodeId: "60a423fffed5a7f6", + online: true, + ownerId: "199200732", + sub: true, + timeZone: "+03:00", + updateTime: 1723626515, + uuid: "5b31dae4-ce9c-4c70-b52b-7e150654sdf56", + batteryLevel: null, +); diff --git a/lib/pages/device_managment/three_gang_switch/widgets/living_toggle_widget.dart b/lib/pages/device_managment/three_gang_switch/widgets/living_toggle_widget.dart index 25c14511..71eba26f 100644 --- a/lib/pages/device_managment/three_gang_switch/widgets/living_toggle_widget.dart +++ b/lib/pages/device_managment/three_gang_switch/widgets/living_toggle_widget.dart @@ -10,6 +10,7 @@ class ToggleWidget extends StatelessWidget { final String code; final String deviceId; final String label; + final String? icon; final Function(dynamic value) onChange; const ToggleWidget({ @@ -19,6 +20,7 @@ class ToggleWidget extends StatelessWidget { required this.deviceId, required this.label, required this.onChange, + this.icon, }); @override @@ -42,7 +44,7 @@ class ToggleWidget extends StatelessWidget { child: Container( color: ColorsManager.whiteColors, child: SvgPicture.asset( - Assets.lightPulp, + icon ?? Assets.lightPulp, width: 60, height: 60, fit: BoxFit.cover, diff --git a/lib/pages/device_managment/water_heater/bloc/water_heater_bloc.dart b/lib/pages/device_managment/water_heater/bloc/water_heater_bloc.dart new file mode 100644 index 00000000..47632653 --- /dev/null +++ b/lib/pages/device_managment/water_heater/bloc/water_heater_bloc.dart @@ -0,0 +1,132 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; + +part 'water_heater_event.dart'; +part 'water_heater_state.dart'; + +class WaterHeaterBloc extends Bloc { + WaterHeaterBloc() : super(WaterHeaterInitial()) { + on(_fetchWaterHeaterStatus); + on(_controlWaterHeater); + on(_updateScheduleEvent); + on(_stopScheduleEvent); + } + + late WaterHeaterStatusModel deviceStatus; + Timer? _timer; + + FutureOr _updateScheduleEvent( + UpdateScheduleEvent event, + Emitter emit, + ) { + emit(WaterHeaterScheduleState( + scheduleType: event.scheduleType, + hours: event.hours, + minutes: event.minutes, + isActive: true, + )); + } + + FutureOr _stopScheduleEvent( + StopScheduleEvent event, + Emitter emit, + ) { + if (state is WaterHeaterScheduleState) { + final currentState = state as WaterHeaterScheduleState; + emit(WaterHeaterScheduleState( + scheduleType: currentState.scheduleType, + hours: currentState.hours, + minutes: currentState.minutes, + isActive: false, + )); + } + } + + FutureOr _controlWaterHeater( + ToggleWaterHeaterEvent event, Emitter emit) async { + final oldValue = deviceStatus.heaterSwitch; + + _updateLocalValue(event.isOn); + + emit(WaterHeaterToggleState(isOn: event.isOn)); + + await _runDebounce( + deviceId: event.deviceId, + value: event.isOn, + oldValue: oldValue, + emit: emit, + ); + } + + Future _runDebounce({ + required String deviceId, + required bool value, + required bool oldValue, + required Emitter emit, + }) async { + if (_timer != null) { + _timer!.cancel(); + } + + _timer = Timer(const Duration(milliseconds: 500), () async { + try { + //// TODO: implement control + + // final status = await DevicesManagementApi().deviceControl( + // deviceId, Status(value: value, code: 'heaterSwitch')); + + // if (!status) { + // _revertValueAndEmit(deviceId, oldValue, emit); + // } + } catch (e) { + _revertValueAndEmit(deviceId, oldValue, emit); + } + }); + } + + void _revertValueAndEmit( + String deviceId, bool oldValue, Emitter emit) { + _updateLocalValue(oldValue); + emit(WaterHeaterToggleState(isOn: oldValue)); + } + + void _updateLocalValue(bool value) { + deviceStatus = deviceStatus.copyWith(heaterSwitch: value); + } + + FutureOr _fetchWaterHeaterStatus( + WaterHeaterFetchStatusEvent event, Emitter emit) async { + emit(WaterHeaterLoadingState()); + + try { + // final status = + // await DevicesManagementApi().getDeviceStatus(event.deviceId); + // deviceStatus = + // WaterHeaterStatusModel.fromJson(event.deviceId, status.status); + + final List fakeStatusList = [ + Status(code: 'switch', value: true), + Status(code: 'schedule_mode', value: 'countdown'), + Status(code: 'countdown_hours', value: 6), + Status(code: 'countdown_minutes', value: 23), + ]; + + final fakeWaterHeaterStatus = + WaterHeaterStatusModel.fromJson(event.deviceId, fakeStatusList); + + emit(WaterHeaterDeviceStatusLoaded(fakeWaterHeaterStatus)); + } catch (e) { + emit(WaterHeaterFailedState(error: e.toString())); + } + } + + @override + Future close() { + _timer?.cancel(); + return super.close(); + } +} diff --git a/lib/pages/device_managment/water_heater/bloc/water_heater_event.dart b/lib/pages/device_managment/water_heater/bloc/water_heater_event.dart new file mode 100644 index 00000000..3adf23dd --- /dev/null +++ b/lib/pages/device_managment/water_heater/bloc/water_heater_event.dart @@ -0,0 +1,49 @@ +part of 'water_heater_bloc.dart'; + +sealed class WaterHeaterEvent extends Equatable { + const WaterHeaterEvent(); + + @override + List get props => []; +} + +final class ToggleWaterHeaterEvent extends WaterHeaterEvent { + final bool isOn; + final String deviceId; + + const ToggleWaterHeaterEvent({required this.isOn, required this.deviceId}); + + @override + List get props => [isOn]; +} + +final class UpdateScheduleEvent extends WaterHeaterEvent { + final ScheduleType scheduleType; + final int hours; + final int minutes; + + const UpdateScheduleEvent(this.scheduleType, this.hours, this.minutes); + + @override + List get props => [scheduleType, hours, minutes]; +} + +final class StopScheduleEvent extends WaterHeaterEvent {} + +class WaterHeaterFetchStatusEvent extends WaterHeaterEvent { + final String deviceId; + + const WaterHeaterFetchStatusEvent(this.deviceId); + + @override + List get props => [deviceId]; +} + +class WaterHeaterFetchBatchStatusEvent extends WaterHeaterEvent { + final String deviceId; + + const WaterHeaterFetchBatchStatusEvent(this.deviceId); + + @override + List get props => [deviceId]; +} diff --git a/lib/pages/device_managment/water_heater/bloc/water_heater_state.dart b/lib/pages/device_managment/water_heater/bloc/water_heater_state.dart new file mode 100644 index 00000000..bc9ac051 --- /dev/null +++ b/lib/pages/device_managment/water_heater/bloc/water_heater_state.dart @@ -0,0 +1,76 @@ +part of 'water_heater_bloc.dart'; + +enum ScheduleType { countdown, schedule, circulate, inching } + +sealed class WaterHeaterState extends Equatable { + const WaterHeaterState(); + + @override + List get props => []; +} + +final class WaterHeaterInitial extends WaterHeaterState {} + +final class WaterHeaterToggleState extends WaterHeaterState { + final bool isOn; + + const WaterHeaterToggleState({required this.isOn}); + + @override + List get props => [isOn]; +} + +final class WaterHeaterScheduleState extends WaterHeaterState { + final ScheduleType scheduleType; + final int hours; + final int minutes; + final bool isActive; + + const WaterHeaterScheduleState({ + required this.scheduleType, + required this.hours, + required this.minutes, + required this.isActive, + }); + + @override + List get props => [scheduleType, hours, minutes, isActive]; +} + +final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState { + final WaterHeaterStatusModel status; + + const WaterHeaterDeviceStatusLoaded(this.status); + + @override + List get props => [status]; +} + +final class WaterHeaterBatchStatusLoaded extends WaterHeaterState { + final WaterHeaterStatusModel status; + + const WaterHeaterBatchStatusLoaded(this.status); + + @override + List get props => [status]; +} + +final class WaterHeaterFailedState extends WaterHeaterState { + final String error; + + const WaterHeaterFailedState({required this.error}); + + @override + List get props => [error]; +} + +final class WaterHeaterBatchFailedState extends WaterHeaterState { + final String error; + + const WaterHeaterBatchFailedState({required this.error}); + + @override + List get props => [error]; +} + +final class WaterHeaterLoadingState extends WaterHeaterState {} diff --git a/lib/pages/device_managment/water_heater/models/water_heater_status_model.dart b/lib/pages/device_managment/water_heater/models/water_heater_status_model.dart new file mode 100644 index 00000000..b4a02a02 --- /dev/null +++ b/lib/pages/device_managment/water_heater/models/water_heater_status_model.dart @@ -0,0 +1,83 @@ +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; + +enum ScheduleModes { countdown, schedule, circulate, inching } + +class WaterHeaterStatusModel { + final String uuid; + final bool heaterSwitch; + final String scheduleModeString; + final int countdownHours; + final int countdownMinutes; + final ScheduleModes scheduleMode; + + WaterHeaterStatusModel({ + required this.uuid, + required this.heaterSwitch, + required this.scheduleModeString, + required this.countdownHours, + required this.countdownMinutes, + }) : scheduleMode = getScheduleMode(scheduleModeString); + + factory WaterHeaterStatusModel.fromJson(String id, List jsonList) { + late bool heaterSwitch; + late String scheduleMode; + late int countdownHours; + late int countdownMinutes; + + for (var status in jsonList) { + switch (status.code) { + case 'switch': + heaterSwitch = status.value ?? false; + break; + case 'schedule_mode': + scheduleMode = status.value ?? 'countdown'; + break; + case 'countdown_hours': + countdownHours = status.value ?? 0; + break; + case 'countdown_minutes': + countdownMinutes = status.value ?? 0; + break; + } + } + + return WaterHeaterStatusModel( + uuid: id, + heaterSwitch: heaterSwitch, + scheduleModeString: scheduleMode, + countdownHours: countdownHours, + countdownMinutes: countdownMinutes, + ); + } + + WaterHeaterStatusModel copyWith({ + String? uuid, + bool? heaterSwitch, + String? scheduleModeString, + int? countdownHours, + int? countdownMinutes, + }) { + return WaterHeaterStatusModel( + uuid: uuid ?? this.uuid, + heaterSwitch: heaterSwitch ?? this.heaterSwitch, + scheduleModeString: scheduleModeString ?? this.scheduleModeString, + countdownHours: countdownHours ?? this.countdownHours, + countdownMinutes: countdownMinutes ?? this.countdownMinutes, + ); + } + + static ScheduleModes getScheduleMode(String value) { + switch (value) { + case 'countdown': + return ScheduleModes.countdown; + case 'schedule': + return ScheduleModes.schedule; + case 'circulate': + return ScheduleModes.circulate; + case 'inching': + return ScheduleModes.inching; + default: + return ScheduleModes.countdown; + } + } +} diff --git a/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart b/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart new file mode 100644 index 00000000..715854ad --- /dev/null +++ b/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/widgets/living_toggle_widget.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; +import 'package:syncrow_web/pages/visitor_password/model/device_model.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class WaterHeaterDeviceControl extends StatelessWidget + with HelperResponsiveLayout { + const WaterHeaterDeviceControl({super.key, required this.device}); + + final AllDevicesModel device; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => + WaterHeaterBloc()..add(WaterHeaterFetchStatusEvent(device.uuid!)), + child: BlocBuilder( + builder: (context, state) { + if (state is WaterHeaterLoadingState) { + return const Center(child: CircularProgressIndicator()); + } else if (state is WaterHeaterDeviceStatusLoaded) { + return _buildStatusControls(context, state.status); + } else if (state is WaterHeaterFailedState || + state is WaterHeaterBatchFailedState) { + return const Center(child: Text('Error fetching status')); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + )); + } + + Widget _buildStatusControls( + BuildContext context, WaterHeaterStatusModel 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: [ + const SizedBox(), + ToggleWidget( + deviceId: device.uuid!, + code: 'water_heater', + value: false, + label: 'Water Heater', + onChange: (value) { + context.read().add(ToggleWaterHeaterEvent( + deviceId: device.uuid!, + isOn: value, + )); + }, + ), + ], + ); + } +}