finished ac devices

This commit is contained in:
ashrafzarkanisala
2024-11-23 21:51:20 +03:00
parent 5e91d7c03a
commit 7dccfb5a76
25 changed files with 810 additions and 770 deletions

View File

@ -1,8 +1,5 @@
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
enum TempModes { hot, cold, wind }
enum FanSpeeds { auto, low, middle, high }
import 'package:syncrow_web/utils/constants/app_enum.dart';
class AcStatusModel {
final String uuid;

View File

@ -3,9 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart';
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class BatchAcMode extends StatelessWidget {
@ -27,15 +27,19 @@ class BatchAcMode extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildIconContainer(context, TempModes.cold, Assets.freezing, value == TempModes.cold),
_buildIconContainer(context, TempModes.hot, Assets.acSun, value == TempModes.hot),
_buildIconContainer(context, TempModes.wind, Assets.acAirConditioner, value == TempModes.wind),
_buildIconContainer(context, TempModes.cold, Assets.freezing,
value == TempModes.cold),
_buildIconContainer(
context, TempModes.hot, Assets.acSun, value == TempModes.hot),
_buildIconContainer(context, TempModes.wind, Assets.acAirConditioner,
value == TempModes.wind),
],
),
);
}
Widget _buildIconContainer(BuildContext context, TempModes mode, String assetPath, bool isSelected) {
Widget _buildIconContainer(
BuildContext context, TempModes mode, String assetPath, bool isSelected) {
return Flexible(
child: GestureDetector(
onTap: () {

View File

@ -3,9 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart';
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class BatchFanSpeedControl extends StatelessWidget {
@ -30,8 +30,10 @@ class BatchFanSpeedControl extends StatelessWidget {
runSpacing: 8,
spacing: 8,
children: [
_buildIconContainer(context, FanSpeeds.auto, Assets.acFanAuto, value == FanSpeeds.auto),
_buildIconContainer(context, FanSpeeds.low, Assets.acFanLow, value == FanSpeeds.low),
_buildIconContainer(context, FanSpeeds.auto, Assets.acFanAuto,
value == FanSpeeds.auto),
_buildIconContainer(context, FanSpeeds.low, Assets.acFanLow,
value == FanSpeeds.low),
],
),
const SizedBox(height: 8),
@ -39,8 +41,10 @@ class BatchFanSpeedControl extends StatelessWidget {
runSpacing: 8,
spacing: 8,
children: [
_buildIconContainer(context, FanSpeeds.middle, Assets.acFanMiddle, value == FanSpeeds.middle),
_buildIconContainer(context, FanSpeeds.high, Assets.acFanHigh, value == FanSpeeds.high),
_buildIconContainer(context, FanSpeeds.middle, Assets.acFanMiddle,
value == FanSpeeds.middle),
_buildIconContainer(context, FanSpeeds.high, Assets.acFanHigh,
value == FanSpeeds.high),
],
)
],
@ -48,7 +52,8 @@ class BatchFanSpeedControl extends StatelessWidget {
);
}
Widget _buildIconContainer(BuildContext context, FanSpeeds speed, String assetPath, bool isSelected) {
Widget _buildIconContainer(BuildContext context, FanSpeeds speed,
String assetPath, bool isSelected) {
return GestureDetector(
onTap: () {
context.read<AcBloc>().add(

View File

@ -3,9 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart';
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class AcMode extends StatelessWidget {
@ -27,15 +27,19 @@ class AcMode extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildIconContainer(context, TempModes.cold, Assets.freezing, value == TempModes.cold),
_buildIconContainer(context, TempModes.hot, Assets.acSun, value == TempModes.hot),
_buildIconContainer(context, TempModes.wind, Assets.acAirConditioner, value == TempModes.wind),
_buildIconContainer(context, TempModes.cold, Assets.freezing,
value == TempModes.cold),
_buildIconContainer(
context, TempModes.hot, Assets.acSun, value == TempModes.hot),
_buildIconContainer(context, TempModes.wind, Assets.acAirConditioner,
value == TempModes.wind),
],
),
);
}
Widget _buildIconContainer(BuildContext context, TempModes mode, String assetPath, bool isSelected) {
Widget _buildIconContainer(
BuildContext context, TempModes mode, String assetPath, bool isSelected) {
return Flexible(
child: GestureDetector(
onTap: () {

View File

@ -3,9 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart';
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class FanSpeedControl extends StatelessWidget {
@ -29,8 +29,10 @@ class FanSpeedControl extends StatelessWidget {
runSpacing: 8,
spacing: 8,
children: [
_buildIconContainer(context, FanSpeeds.auto, Assets.acFanAuto, value == FanSpeeds.auto),
_buildIconContainer(context, FanSpeeds.low, Assets.acFanLow, value == FanSpeeds.low),
_buildIconContainer(context, FanSpeeds.auto, Assets.acFanAuto,
value == FanSpeeds.auto),
_buildIconContainer(context, FanSpeeds.low, Assets.acFanLow,
value == FanSpeeds.low),
],
),
const SizedBox(height: 8),
@ -38,8 +40,10 @@ class FanSpeedControl extends StatelessWidget {
runSpacing: 8,
spacing: 8,
children: [
_buildIconContainer(context, FanSpeeds.middle, Assets.acFanMiddle, value == FanSpeeds.middle),
_buildIconContainer(context, FanSpeeds.high, Assets.acFanHigh, value == FanSpeeds.high),
_buildIconContainer(context, FanSpeeds.middle, Assets.acFanMiddle,
value == FanSpeeds.middle),
_buildIconContainer(context, FanSpeeds.high, Assets.acFanHigh,
value == FanSpeeds.high),
],
)
],
@ -47,7 +51,8 @@ class FanSpeedControl extends StatelessWidget {
);
}
Widget _buildIconContainer(BuildContext context, FanSpeeds speed, String assetPath, bool isSelected) {
Widget _buildIconContainer(BuildContext context, FanSpeeds speed,
String assetPath, bool isSelected) {
return GestureDetector(
onTap: () {
context.read<AcBloc>().add(

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
part 'functions_bloc_event.dart';
@ -10,120 +11,32 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
FunctionBloc() : super(const FunctionBlocState()) {
on<InitializeFunctions>(_onInitializeFunctions);
on<AddFunction>(_onAddFunction);
on<UpdateFunction>(_onUpdateFunctions);
on<UpdateFunctionValue>(_onUpdateFunctionValue);
on<UpdateFunctionCondition>(_onUpdateFunctionCondition);
on<RemoveFunction>(_onRemoveFunction);
on<SelectFunction>(_onSelectFunction);
}
void _onAddFunction(AddFunction event, Emitter<FunctionBlocState> emit) {
debugPrint('Adding function: ${event.functionData.function}');
final functions = List<DeviceFunctionData>.from(state.functions);
// Find existing function data
final functions = List<DeviceFunctionData>.from(state.addedFunctions);
final existingIndex = functions.indexWhere(
(f) => f.function == event.functionData.function,
(f) => f.functionCode == event.functionData.functionCode,
);
// If function exists, preserve its value and condition
if (existingIndex != -1) {
final existingData = functions[existingIndex];
functions[existingIndex] = DeviceFunctionData(
entityId: event.functionData.entityId,
function: event.functionData.function,
functionCode: event.functionData.functionCode,
operationName: event.functionData.operationName,
value: existingData.value, // Preserve the existing value
condition: existingData.condition, // Preserve the existing condition
value: event.functionData.value ?? existingData.value,
valueDescription: event.functionData.valueDescription ??
existingData.valueDescription,
condition: event.functionData.condition ?? existingData.condition,
);
} else {
functions.add(event.functionData);
}
debugPrint('Functions after add: $functions');
emit(state.copyWith(
functions: functions,
selectedFunction: event.functionData.function,
));
}
void _onUpdateFunctions(
UpdateFunction event, Emitter<FunctionBlocState> emit) {
final functions = state.functions.map((data) {
return data.function == event.functionData.function
? event.functionData
: data;
}).toList();
emit(state.copyWith(functions: functions));
}
void _onUpdateFunctionValue(
UpdateFunctionValue event,
Emitter<FunctionBlocState> emit,
) {
debugPrint('Updating function value: ${event.function} -> ${event.value}');
// Create a new list to ensure state immutability
final functions = List<DeviceFunctionData>.from(state.functions);
// Find the index of the function to update
final functionIndex = functions.indexWhere(
(data) => data.function == event.function,
);
if (functionIndex != -1) {
// Update the existing function data while preserving other fields
final existingData = functions[functionIndex];
functions[functionIndex] = DeviceFunctionData(
entityId: existingData.entityId,
function: existingData.function,
operationName: existingData.operationName,
value: event.value,
condition: existingData.condition,
);
} else {
// If function doesn't exist, add it
functions.add(DeviceFunctionData(
entityId: '',
function: event.function,
operationName: '',
value: event.value,
));
}
debugPrint('Functions after update: $functions');
emit(state.copyWith(functions: functions));
}
void _onUpdateFunctionCondition(
UpdateFunctionCondition event,
Emitter<FunctionBlocState> emit,
) {
final functions = state.functions.map((data) {
if (data.function == event.function) {
return DeviceFunctionData(
entityId: data.entityId,
function: data.function,
operationName: data.operationName,
value: data.value,
condition: event.condition,
);
}
return data;
}).toList();
emit(state.copyWith(functions: functions));
}
void _onRemoveFunction(
RemoveFunction event, Emitter<FunctionBlocState> emit) {
final functions = state.functions
.where((data) => data.function != event.functionCode)
.toList();
emit(state.copyWith(
functions: functions,
selectedFunction: functions.isEmpty ? null : state.selectedFunction,
addedFunctions: functions,
selectedFunction: event.functionData.functionCode,
));
}
@ -131,18 +44,25 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
InitializeFunctions event,
Emitter<FunctionBlocState> emit,
) {
emit(state.copyWith(functions: event.functions));
emit(state.copyWith(addedFunctions: event.functions));
}
DeviceFunctionData? getFunction(String functionCode) {
return state.functions.firstWhere(
(data) => data.function == functionCode,
return state.addedFunctions.firstWhere(
(data) => data.functionCode == functionCode,
orElse: () => DeviceFunctionData(
entityId: '',
function: functionCode,
functionCode: functionCode,
operationName: '',
value: null,
),
);
}
FutureOr<void> _onSelectFunction(
SelectFunction event, Emitter<FunctionBlocState> emit) {
emit(state.copyWith(
selectedFunction: event.functionCode,
selectedOperationName: event.operationName));
}
}

View File

@ -18,48 +18,17 @@ class AddFunction extends FunctionBlocEvent {
List<Object?> get props => [functionData];
}
class UpdateFunction extends FunctionBlocEvent {
final DeviceFunctionData functionData;
const UpdateFunction(this.functionData);
@override
List<Object?> get props => [functionData];
}
class UpdateFunctionValue extends FunctionBlocEvent {
final String function;
final dynamic value;
const UpdateFunctionValue({
required this.function,
required this.value,
});
@override
List<Object?> get props => [function, value];
}
class UpdateFunctionCondition extends FunctionBlocEvent {
final String function;
final String condition;
const UpdateFunctionCondition({
required this.function,
required this.condition,
});
@override
List<Object?> get props => [function, condition];
}
class RemoveFunction extends FunctionBlocEvent {
class SelectFunction extends FunctionBlocEvent {
final String functionCode;
final String operationName;
const RemoveFunction(this.functionCode);
const SelectFunction({
required this.functionCode,
required this.operationName,
});
@override
List<Object?> get props => [functionCode];
List<Object?> get props => [functionCode, operationName];
}
class InitializeFunctions extends FunctionBlocEvent {

View File

@ -1,24 +1,29 @@
part of 'functions_bloc_bloc.dart';
class FunctionBlocState extends Equatable {
final List<DeviceFunctionData> functions;
final List<DeviceFunctionData> addedFunctions;
final String? selectedFunction;
final String? selectedOperationName;
const FunctionBlocState({
this.functions = const [],
this.addedFunctions = const [],
this.selectedFunction,
this.selectedOperationName,
});
FunctionBlocState copyWith({
List<DeviceFunctionData>? functions,
List<DeviceFunctionData>? addedFunctions,
String? selectedFunction,
String? selectedOperationName,
}) {
return FunctionBlocState(
functions: functions ?? this.functions,
addedFunctions: addedFunctions ?? this.addedFunctions,
selectedFunction: selectedFunction ?? this.selectedFunction,
selectedOperationName:
selectedOperationName ?? this.selectedOperationName,
);
}
@override
List<Object?> get props => [functions, selectedFunction];
List<Object?> get props =>
[addedFunctions, selectedFunction, selectedOperationName];
}

View File

@ -1,5 +1,6 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/routine_model.dart';
import 'package:syncrow_web/services/routines_api.dart';
@ -21,32 +22,33 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
void _onAddToIfContainer(AddToIfContainer event, Emitter<RoutineState> emit) {
if (!_isDuplicate(state.ifItems, event.item)) {
// if (!_isDuplicate(state.ifItems, event.item)) {
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems)
..add(event.item);
emit(state.copyWith(ifItems: updatedIfItems));
}
// }
}
void _onAddToThenContainer(
AddToThenContainer event, Emitter<RoutineState> emit) {
if (!_isDuplicate(state.thenItems, event.item)) {
// if (!_isDuplicate(state.thenItems, event.item)) {
final updatedThenItems = List<Map<String, dynamic>>.from(state.thenItems)
..add(event.item);
emit(state.copyWith(thenItems: updatedThenItems));
}
// }
}
void _onAddFunction(AddFunctionToRoutine event, Emitter<RoutineState> emit) {
final functions = List<DeviceFunctionData>.from(state.selectedFunctions);
functions.add(event.function);
debugPrint("******" + functions.toString());
emit(state.copyWith(selectedFunctions: functions));
}
void _onRemoveFunction(RemoveFunction event, Emitter<RoutineState> emit) {
final functions = List<DeviceFunctionData>.from(state.selectedFunctions)
..removeWhere((f) =>
f.function == event.function.function &&
f.functionCode == event.function.functionCode &&
f.value == event.function.value);
emit(state.copyWith(selectedFunctions: functions));
}
@ -55,12 +57,12 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(selectedFunctions: []));
}
bool _isDuplicate(
List<Map<String, dynamic>> items, Map<String, dynamic> newItem) {
return items.any((item) =>
item['imagePath'] == newItem['imagePath'] &&
item['title'] == newItem['title']);
}
// bool _isDuplicate(
// List<Map<String, dynamic>> items, Map<String, dynamic> newItem) {
// return items.any((item) =>
// item['imagePath'] == newItem['imagePath'] &&
// item['title'] == newItem['title']);
// }
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {

View File

@ -1,9 +0,0 @@
enum AcValuesEnums {
Cooling,
Heating,
Ventilation,
}
enum TempModes { hot, cold, wind }
enum FanSpeeds { auto, low, middle, high }

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_operational_value.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
@ -13,42 +15,32 @@ import 'package:syncrow_web/pages/routiens/bloc/functions_bloc/functions_bloc_bl
class ACHelper {
static Future<Map<String, dynamic>?> showACFunctionsDialog(
BuildContext context,
List<DeviceFunction<dynamic>> functions,
List<DeviceFunction> functions,
AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions,
) async {
List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
// Initialize the FunctionBloc with existing functions
final initialFunctions = acFunctions
.map((f) => DeviceFunctionData(
entityId: '',
function: f.code,
operationName: f.operationName,
value: null,
))
.toList();
return showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext context) {
return BlocProvider(
create: (_) =>
FunctionBloc()..add(InitializeFunctions(initialFunctions)),
create: (_) => FunctionBloc()
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) {
final selectedFunction = state.selectedFunction;
final selectedFunctionData = selectedFunction != null
? state.functions.firstWhere(
(f) => f.function == selectedFunction,
final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions
.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
function: selectedFunction,
functionCode: selectedFunction ?? '',
operationName: '',
value: null,
),
)
: null;
));
return Container(
width: selectedFunction != null ? 600 : 360,
@ -70,41 +62,27 @@ class ACHelper {
SizedBox(
width: selectedFunction != null ? 320 : 360,
child: _buildFunctionsList(
context,
acFunctions,
(functionCode) =>
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: '',
function: functionCode,
operationName: '',
value: null,
context: context,
acFunctions: acFunctions,
onFunctionSelected:
(functionCode, operationName) => context
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: functionCode,
operationName: operationName,
)),
),
),
),
// Value selector
if (selectedFunction != null)
Expanded(
child: _buildValueSelector(
context,
selectedFunction,
selectedFunctionData,
(value) => context.read<FunctionBloc>().add(
UpdateFunctionValue(
function: selectedFunction,
value: value,
),
),
(condition) =>
context.read<FunctionBloc>().add(
UpdateFunctionCondition(
function: selectedFunction,
condition: condition,
),
),
acFunctions,
context: context,
selectedFunction: selectedFunction,
selectedFunctionData: selectedFunctionData,
acFunctions: acFunctions,
device: device,
operationName: selectedOperationName ?? '',
),
),
],
@ -114,11 +92,10 @@ class ACHelper {
onCancel: () {
Navigator.pop(context);
},
onConfirm: selectedFunction != null &&
selectedFunctionData?.value != null
onConfirm: state.addedFunctions.isNotEmpty
? () {
/// add the functions to the routine bloc
for (var function in state.functions) {
for (var function in state.addedFunctions) {
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
function,
@ -126,7 +103,9 @@ class ACHelper {
);
}
// Return the device data to be added to the container
Navigator.pop(context);
Navigator.pop(context, {
'deviceId': functions.first.deviceId,
});
}
: null,
isConfirmEnabled: selectedFunction != null,
@ -145,11 +124,11 @@ class ACHelper {
}
/// Build functions list for AC functions dialog
static Widget _buildFunctionsList(
BuildContext context,
List<ACFunction> acFunctions,
Function(String) onFunctionSelected,
) {
static Widget _buildFunctionsList({
required BuildContext context,
required List<ACFunction> acFunctions,
required Function(String, String) onFunctionSelected,
}) {
return ListView.separated(
shrinkWrap: false,
physics: const AlwaysScrollableScrollPhysics(),
@ -182,29 +161,34 @@ class ACHelper {
size: 16,
color: ColorsManager.textGray,
),
onTap: () => onFunctionSelected(function.code),
onTap: () => onFunctionSelected(
function.code,
function.operationName,
),
);
},
);
}
/// Build value selector for AC functions dialog
static Widget _buildValueSelector(
BuildContext context,
String selectedFunction,
DeviceFunctionData? selectedFunctionData,
Function(dynamic) onValueChanged,
Function(String) onConditionChanged,
List<ACFunction> acFunctions,
) {
static Widget _buildValueSelector({
required BuildContext context,
required String selectedFunction,
required DeviceFunctionData? selectedFunctionData,
required List<ACFunction> acFunctions,
AllDevicesModel? device,
required String operationName,
}) {
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
final initialValue = selectedFunctionData?.value ?? 200;
return _buildTemperatureSelector(
context,
initialValue,
selectedFunctionData?.condition,
onValueChanged,
onConditionChanged,
context: context,
initialValue: initialValue,
selectCode: selectedFunction,
currentCondition: selectedFunctionData?.condition,
device: device,
operationName: operationName,
selectedFunctionData: selectedFunctionData,
);
}
@ -213,37 +197,43 @@ class ACHelper {
final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList(
context,
values,
selectedFunctionData?.value,
onValueChanged,
context: context,
values: values,
selectedValue: selectedFunctionData?.value,
device: device,
operationName: operationName,
selectCode: selectedFunction,
selectedFunctionData: selectedFunctionData,
);
}
/// Build temperature selector for AC functions dialog
static Widget _buildTemperatureSelector(
BuildContext context,
dynamic initialValue,
String? currentCondition,
Function(dynamic) onValueChanged,
Function(String) onConditionChanged,
) {
static Widget _buildTemperatureSelector({
required BuildContext context,
required dynamic initialValue,
required String? currentCondition,
required String selectCode,
AllDevicesModel? device,
required String operationName,
DeviceFunctionData? selectedFunctionData,
}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildConditionToggle(
context,
currentCondition,
onConditionChanged,
selectCode,
device,
operationName,
selectedFunctionData,
),
const SizedBox(height: 20),
_buildTemperatureDisplay(context, initialValue),
_buildTemperatureDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
const SizedBox(height: 20),
_buildTemperatureSlider(
context,
initialValue,
onValueChanged,
),
_buildTemperatureSlider(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
],
);
}
@ -252,13 +242,28 @@ class ACHelper {
static Widget _buildConditionToggle(
BuildContext context,
String? currentCondition,
Function(String) onConditionChanged,
String selectCode,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
// Function(String) onConditionChanged,
) {
final conditions = ["<", "==", ">"];
return ToggleButtons(
onPressed: (int index) {
onConditionChanged(conditions[index]);
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
condition: conditions[index],
value: selectedFunctionData?.value,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
},
borderRadius: const BorderRadius.all(Radius.circular(8)),
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
@ -277,7 +282,12 @@ class ACHelper {
/// Build temperature display for AC functions dialog
static Widget _buildTemperatureDisplay(
BuildContext context, dynamic initialValue) {
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
String selectCode) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
@ -296,7 +306,10 @@ class ACHelper {
static Widget _buildTemperatureSlider(
BuildContext context,
dynamic initialValue,
Function(dynamic) onValueChanged,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
String selectCode,
) {
return Slider(
value: initialValue is int ? initialValue.toDouble() : 200.0,
@ -305,17 +318,32 @@ class ACHelper {
divisions: 14,
label: '${((initialValue ?? 200) / 10).toInt()}°C',
onChanged: (value) {
onValueChanged(value.toInt());
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
value: value,
condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
},
);
}
static Widget _buildOperationalValuesList(
BuildContext context,
List<dynamic> values,
dynamic selectedValue,
Function(dynamic) onValueChanged,
) {
static Widget _buildOperationalValuesList({
required BuildContext context,
required List<ACOperationalValue> values,
required dynamic selectedValue,
AllDevicesModel? device,
required String operationName,
required String selectCode,
DeviceFunctionData? selectedFunctionData,
// required Function(dynamic) onValueChanged,
}) {
return ListView.builder(
shrinkWrap: false,
physics: const AlwaysScrollableScrollPhysics(),
@ -349,7 +377,19 @@ class ACHelper {
),
onTap: () {
if (!isSelected) {
onValueChanged(value.value);
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
value: value.value,
condition: selectedFunctionData?.condition,
valueDescription:
selectedFunctionData?.valueDescription,
),
),
);
}
},
);

View File

@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/ac_helper.dart';
import 'package:syncrow_web/pages/routiens/helper/one_gang_switch_helper.dart';
import 'package:syncrow_web/pages/routiens/helper/three_gang_switch_helper.dart';
@ -16,6 +18,7 @@ class DeviceDialogHelper {
final result = await _getDialogForDeviceType(
context,
data['productType'],
data,
functions,
);
@ -32,21 +35,30 @@ class DeviceDialogHelper {
static Future<Map<String, dynamic>?> _getDialogForDeviceType(
BuildContext context,
String productType,
Map<String, dynamic> data,
List<DeviceFunction> functions,
) async {
final routineBloc = context.read<RoutineBloc>();
final deviceSelectedFunctions = routineBloc.state.selectedFunctions
.where((f) => f.entityId == data['deviceId'])
.toList();
switch (productType) {
case 'AC':
return ACHelper.showACFunctionsDialog(context, functions);
return ACHelper.showACFunctionsDialog(
context, functions, data['device'], deviceSelectedFunctions);
case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog(
context, functions);
context, functions, data['device'], deviceSelectedFunctions);
case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog(
context, functions);
case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog(
context, functions);
break;
default:
return null;
}
}
}

View File

@ -1,5 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_operational_value.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/one_gang_switch/one_gang_switch.dart';
@ -11,27 +17,33 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
class OneGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
BuildContext context,
List<DeviceFunction<dynamic>> functions,
List<DeviceFunction> functions,
AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions,
) async {
List<DeviceFunction<dynamic>> switchFunctions = functions
.where(
(f) => f is OneGangSwitchFunction || f is OneGangCountdownFunction)
.toList();
final selectedFunctionNotifier = ValueNotifier<String?>(null);
final selectedValueNotifier = ValueNotifier<dynamic>(null);
final selectedConditionNotifier = ValueNotifier<String?>("<");
final selectedConditionsNotifier =
ValueNotifier<List<bool>>([true, false, false]);
List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
return showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext context) {
return ValueListenableBuilder<String?>(
valueListenable: selectedFunctionNotifier,
builder: (context, selectedFunction, _) {
return AlertDialog(
return BlocProvider(
create: (_) => FunctionBloc()
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog(
contentPadding: EdgeInsets.zero,
content: Container(
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) {
final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions
.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
functionCode: selectedFunction ?? '',
operationName: '',
value: null,
));
return Container(
width: selectedFunction != null ? 600 : 360,
height: 450,
decoration: BoxDecoration(
@ -49,21 +61,13 @@ class OneGangSwitchHelper {
// Left side: Function list
Expanded(
child: ListView.separated(
itemCount: switchFunctions.length,
itemCount: acFunctions.length,
separatorBuilder: (_, __) => const Divider(
color: ColorsManager.dividerColor,
),
itemBuilder: (context, index) {
final function = switchFunctions[index];
return ValueListenableBuilder<String?>(
valueListenable: selectedFunctionNotifier,
builder: (context, selectedFunction, _) {
final isSelected =
selectedFunction == function.code;
final function = acFunctions[index];
return ListTile(
tileColor: isSelected
? Colors.grey.shade100
: null,
leading: SvgPicture.asset(
function.icon,
width: 24,
@ -79,14 +83,13 @@ class OneGangSwitchHelper {
color: ColorsManager.textGray,
),
onTap: () {
selectedFunctionNotifier.value =
function.code;
selectedValueNotifier.value =
function is OneGangCountdownFunction
? 0
: null;
},
);
context
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code,
operationName:
function.operationName,
));
},
);
},
@ -94,28 +97,40 @@ class OneGangSwitchHelper {
),
// Right side: Value selector
if (selectedFunction != null)
ValueListenableBuilder<String?>(
valueListenable: selectedFunctionNotifier,
builder: (context, selectedFunction, _) {
final selectedFn = switchFunctions.firstWhere(
(f) => f.code == selectedFunction,
);
return Expanded(
child: selectedFn is OneGangCountdownFunction
? _buildCountDownSelector(
context,
selectedValueNotifier,
selectedConditionNotifier,
selectedConditionsNotifier,
)
: _buildOperationalValuesList(
context,
selectedFn as BaseSwitchFunction,
selectedValueNotifier,
Expanded(
child: _buildValueSelector(
context: context,
selectedFunction: selectedFunction,
selectedFunctionData: selectedFunctionData,
acFunctions: acFunctions,
device: device,
operationName: selectedOperationName ?? '',
),
);
},
),
// ValueListenableBuilder<String?>(
// valueListenable: selectedFunctionNotifier,
// builder: (context, selectedFunction, _) {
// final selectedFn =
// switchFunctions.firstWhere(
// (f) => f.code == selectedFunction,
// );
// return Expanded(
// child: selectedFn
// is OneGangCountdownFunction
// ? _buildCountDownSelector(
// context,
// selectedValueNotifier,
// selectedConditionNotifier,
// selectedConditionsNotifier,
// )
// : _buildOperationalValuesList(
// context,
// selectedFn as BaseSwitchFunction,
// selectedValueNotifier,
// ),
// );
// },
// ),
],
),
),
@ -125,75 +140,129 @@ class OneGangSwitchHelper {
color: ColorsManager.greyColor,
),
DialogFooter(
onCancel: () => Navigator.pop(context),
onConfirm: selectedFunctionNotifier.value != null &&
selectedValueNotifier.value != null
onCancel: () {
Navigator.pop(context);
},
onConfirm: state.addedFunctions.isNotEmpty
? () {
final selectedFn = switchFunctions.firstWhere(
(f) => f.code == selectedFunctionNotifier.value,
/// add the functions to the routine bloc
for (var function in state.addedFunctions) {
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
function,
),
);
final value = selectedValueNotifier.value;
final functionData = DeviceFunctionData(
entityId: selectedFn.deviceId,
function: selectedFn.code,
operationName: selectedFn.operationName,
value: value,
condition: selectedConditionNotifier.value,
valueDescription:
selectedFn is OneGangCountdownFunction
? '${value} sec'
: ((selectedFn as BaseSwitchFunction)
.getOperationalValues()
.firstWhere((v) => v.value == value)
.description),
);
Navigator.pop(
context, {selectedFn.code: functionData});
}
// Return the device data to be added to the container
Navigator.pop(context, {
'deviceId': functions.first.deviceId,
});
}
: null,
isConfirmEnabled: selectedFunctionNotifier.value != null,
isConfirmEnabled: selectedFunction != null,
),
],
),
);
},
),
);
));
},
);
},
).then((value) {
selectedFunctionNotifier.dispose();
selectedValueNotifier.dispose();
selectedConditionNotifier.dispose();
selectedConditionsNotifier.dispose();
return value;
});
}
static Widget _buildCountDownSelector(
BuildContext context,
ValueNotifier<dynamic> valueNotifier,
ValueNotifier<String?> conditionNotifier,
ValueNotifier<List<bool>> conditionsNotifier,
) {
return ValueListenableBuilder<dynamic>(
valueListenable: valueNotifier,
builder: (context, value, _) {
static Widget _buildValueSelector({
required BuildContext context,
required String selectedFunction,
required DeviceFunctionData? selectedFunctionData,
required List<ACFunction> acFunctions,
AllDevicesModel? device,
required String operationName,
}) {
if (selectedFunction == 'countdown_1') {
final initialValue = selectedFunctionData?.value ?? 200;
return _buildTemperatureSelector(
context: context,
initialValue: initialValue,
selectCode: selectedFunction,
currentCondition: selectedFunctionData?.condition,
device: device,
operationName: operationName,
selectedFunctionData: selectedFunctionData,
);
}
final selectedFn =
acFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList(
context: context,
values: values,
selectedValue: selectedFunctionData?.value,
device: device,
operationName: operationName,
selectCode: selectedFunction,
selectedFunctionData: selectedFunctionData,
);
}
static Widget _buildTemperatureSelector({
required BuildContext context,
required dynamic initialValue,
required String? currentCondition,
required String selectCode,
AllDevicesModel? device,
required String operationName,
DeviceFunctionData? selectedFunctionData,
}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ValueListenableBuilder<List<bool>>(
valueListenable: conditionsNotifier,
builder: (context, selectedConditions, _) {
_buildConditionToggle(
context,
currentCondition,
selectCode,
device,
operationName,
selectedFunctionData,
),
const SizedBox(height: 20),
_buildTemperatureDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
const SizedBox(height: 20),
_buildTemperatureSlider(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
],
);
}
/// Build condition toggle for AC functions dialog
static Widget _buildConditionToggle(
BuildContext context,
String? currentCondition,
String selectCode,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
// Function(String) onConditionChanged,
) {
final conditions = ["<", "==", ">"];
return ToggleButtons(
onPressed: (int index) {
final newConditions = List<bool>.filled(3, false);
newConditions[index] = true;
conditionsNotifier.value = newConditions;
conditionNotifier.value = index == 0
? "<"
: index == 1
? "=="
: ">";
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
condition: conditions[index],
value: selectedFunctionData?.value,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
},
borderRadius: const BorderRadius.all(Radius.circular(8)),
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
@ -204,66 +273,223 @@ class OneGangSwitchHelper {
minHeight: 40.0,
minWidth: 40.0,
),
isSelected: selectedConditions,
children: const [Text("<"), Text("="), Text(">")],
isSelected:
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: conditions.map((c) => Text(c)).toList(),
);
},
}
/// Build temperature display for AC functions dialog
static Widget _buildTemperatureDisplay(
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
String selectCode) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
const SizedBox(height: 20),
Text(
'${value ?? 0} sec',
style: Theme.of(context).textTheme.headlineMedium,
child: Text(
'${initialValue ?? 0} sec',
style: context.textTheme.headlineMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
),
const SizedBox(height: 20),
Slider(
value: (value ?? 0).toDouble(),
),
);
}
static Widget _buildTemperatureSlider(
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
String selectCode,
) {
return Slider(
value: (initialValue ?? 0).toDouble(),
min: 0,
max: 300,
divisions: 300,
onChanged: (newValue) {
valueNotifier.value = newValue.toInt();
},
onChanged: (value) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
value: value,
condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription,
),
),
],
);
},
);
}
static Widget _buildOperationalValuesList(
BuildContext context,
BaseSwitchFunction function,
ValueNotifier<dynamic> valueNotifier,
) {
final values = function.getOperationalValues();
return ValueListenableBuilder<dynamic>(
valueListenable: valueNotifier,
builder: (context, selectedValue, _) {
static Widget _buildOperationalValuesList({
required BuildContext context,
required List<ACOperationalValue> values,
required dynamic selectedValue,
AllDevicesModel? device,
required String operationName,
required String selectCode,
DeviceFunctionData? selectedFunctionData,
// required Function(dynamic) onValueChanged,
}) {
return ListView.builder(
shrinkWrap: false,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: values.length,
itemBuilder: (context, index) {
final value = values[index];
final isSelected = selectedValue == value.value;
return ListTile(
leading: SvgPicture.asset(
value.icon,
width: 24,
height: 24,
placeholderBuilder: (BuildContext context) => Container(
width: 24,
height: 24,
color: Colors.transparent,
),
),
title: Text(
value.description,
style: context.textTheme.bodyMedium,
),
trailing: Radio<dynamic>(
trailing: Icon(
isSelected
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
size: 24,
color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
onTap: () {
if (!isSelected) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
value: value.value,
groupValue: selectedValue,
onChanged: (newValue) {
valueNotifier.value = newValue;
},
condition: selectedFunctionData?.condition,
valueDescription:
selectedFunctionData?.valueDescription,
),
),
);
}
},
);
},
);
}
// static Widget _buildCountDownSelector(
// BuildContext context,
// ValueNotifier<dynamic> valueNotifier,
// ValueNotifier<String?> conditionNotifier,
// ValueNotifier<List<bool>> conditionsNotifier,
// ) {
// return ValueListenableBuilder<dynamic>(
// valueListenable: valueNotifier,
// builder: (context, value, _) {
// return Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// ValueListenableBuilder<List<bool>>(
// valueListenable: conditionsNotifier,
// builder: (context, selectedConditions, _) {
// return ToggleButtons(
// onPressed: (int index) {
// final newConditions = List<bool>.filled(3, false);
// newConditions[index] = true;
// conditionsNotifier.value = newConditions;
// conditionNotifier.value = index == 0
// ? "<"
// : index == 1
// ? "=="
// : ">";
// },
// borderRadius: const BorderRadius.all(Radius.circular(8)),
// selectedBorderColor: ColorsManager.primaryColorWithOpacity,
// selectedColor: Colors.white,
// fillColor: ColorsManager.primaryColorWithOpacity,
// color: ColorsManager.primaryColorWithOpacity,
// constraints: const BoxConstraints(
// minHeight: 40.0,
// minWidth: 40.0,
// ),
// isSelected: selectedConditions,
// children: const [Text("<"), Text("="), Text(">")],
// );
// },
// ),
// const SizedBox(height: 20),
// Text(
// '${value ?? 0} sec',
// style: Theme.of(context).textTheme.headlineMedium,
// ),
// const SizedBox(height: 20),
// Slider(
// value: (value ?? 0).toDouble(),
// min: 0,
// max: 300,
// divisions: 300,
// onChanged: (newValue) {
// valueNotifier.value = newValue.toInt();
// },
// ),
// ],
// );
// },
// );
// }
// static Widget _buildOperationalValuesList(
// BuildContext context,
// BaseSwitchFunction function,
// ValueNotifier<dynamic> valueNotifier,
// ) {
// final values = function.getOperationalValues();
// return ValueListenableBuilder<dynamic>(
// valueListenable: valueNotifier,
// builder: (context, selectedValue, _) {
// return ListView.builder(
// itemCount: values.length,
// itemBuilder: (context, index) {
// final value = values[index];
// return ListTile(
// leading: SvgPicture.asset(
// value.icon,
// width: 24,
// height: 24,
// ),
// title: Text(
// value.description,
// style: context.textTheme.bodyMedium,
// ),
// trailing: Radio<dynamic>(
// value: value.value,
// groupValue: selectedValue,
// onChanged: (newValue) {
// valueNotifier.value = newValue;
// },
// ),
// );
// },
// );
// },
// );
// }
}

View File

@ -143,7 +143,7 @@ class ThreeGangSwitchHelper {
final value = selectedValueNotifier.value;
final functionData = DeviceFunctionData(
entityId: selectedFn.deviceId,
function: selectedFn.code,
functionCode: selectedFn.code,
operationName: selectedFn.operationName,
value: value,
condition: selectedConditionNotifier.value,

View File

@ -138,7 +138,7 @@ class TwoGangSwitchHelper {
final value = selectedValueNotifier.value;
final functionData = DeviceFunctionData(
entityId: selectedFn.deviceId,
function: selectedFn.code,
functionCode: selectedFn.code,
operationName: selectedFn.operationName,
value: value,
condition: selectedConditionNotifier.value,

View File

@ -1,25 +1,17 @@
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_operational_value.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
abstract class ACFunction extends DeviceFunction<AcStatusModel> {
ACFunction({
required String deviceId,
required String deviceName,
required String code,
required String operationName,
required String icon,
}) : super(
deviceId: deviceId,
deviceName: deviceName,
code: code,
operationName: operationName,
icon: icon,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue);
required super.deviceId,
required super.deviceName,
required super.code,
required super.operationName,
required super.icon,
});
List<ACOperationalValue> getOperationalValues();
}
@ -32,11 +24,6 @@ class SwitchFunction extends ACFunction {
icon: Assets.assetsAcPower,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue) {
return currentStatus.copyWith(acSwitch: newValue as bool);
}
@override
List<ACOperationalValue> getOperationalValues() => [
ACOperationalValue(
@ -60,27 +47,22 @@ class ModeFunction extends ACFunction {
icon: Assets.assetsFreezing,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue) {
return currentStatus.copyWith(modeString: (newValue as TempModes).toString().split('.').last);
}
@override
List<ACOperationalValue> getOperationalValues() => [
ACOperationalValue(
icon: Assets.assetsAcCooling,
description: "Cooling",
value: TempModes.cold,
value: TempModes.cold.name,
),
ACOperationalValue(
icon: Assets.assetsAcHeating,
description: "Heating",
value: TempModes.hot,
value: TempModes.hot.name,
),
ACOperationalValue(
icon: Assets.assetsFanSpeed,
description: "Ventilation",
value: TempModes.wind,
value: TempModes.wind.name,
),
];
}
@ -100,11 +82,6 @@ class TempSetFunction extends ACFunction {
icon: Assets.assetsTempreture,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue) {
return currentStatus.copyWith(tempSet: newValue as int);
}
@override
List<ACOperationalValue> getOperationalValues() {
List<ACOperationalValue> values = [];
@ -127,32 +104,27 @@ class LevelFunction extends ACFunction {
icon: Assets.assetsFanSpeed,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue) {
return currentStatus.copyWith(fanSpeedsString: (newValue as FanSpeeds).toString().split('.').last);
}
@override
List<ACOperationalValue> getOperationalValues() => [
ACOperationalValue(
icon: Assets.assetsAcFanLow,
description: "LOW",
value: FanSpeeds.low,
value: FanSpeeds.low.name,
),
ACOperationalValue(
icon: Assets.assetsAcFanMiddle,
description: "MIDDLE",
value: FanSpeeds.middle,
value: FanSpeeds.middle.name,
),
ACOperationalValue(
icon: Assets.assetsAcFanHigh,
description: "HIGH",
value: FanSpeeds.high,
value: FanSpeeds.high.name,
),
ACOperationalValue(
icon: Assets.assetsAcFanAuto,
description: "AUTO",
value: FanSpeeds.auto,
value: FanSpeeds.auto.name,
),
];
}
@ -165,11 +137,6 @@ class ChildLockFunction extends ACFunction {
icon: Assets.assetsChildLock,
);
@override
AcStatusModel execute(AcStatusModel currentStatus, dynamic newValue) {
return currentStatus.copyWith(childLock: newValue as bool);
}
@override
List<ACOperationalValue> getOperationalValues() => [
ACOperationalValue(
@ -184,32 +151,3 @@ class ChildLockFunction extends ACFunction {
),
];
}
/*
final deviceId = 'AC001';
final deviceName = 'Living Room AC';
// Initial AC status
var acStatus = AcStatusModel(
uuid: deviceId,
acSwitch: false,
modeString: 'cold',
tempSet: 220,
currentTemp: 250,
fanSpeedsString: 'auto',
childLock: false,
);
// Get all AC functions
final acFunctions = getACFunctions(deviceId, deviceName);
// Example: Turn on the AC
final switchFunction = acFunctions.firstWhere((f) => f is SwitchFunction) as SwitchFunction;
acStatus = switchFunction.execute(acStatus, true);
// Example: Change mode to heat
final modeFunction = acFunctions.firstWhere((f) => f is ModeFunction) as ModeFunction;
acStatus = modeFunction.execute(acStatus, TempModes.hot);
*/

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
abstract class DeviceFunction<T> {
final String deviceId;
final String deviceName;
@ -12,34 +14,33 @@ abstract class DeviceFunction<T> {
required this.operationName,
required this.icon,
});
T execute(T currentStatus, dynamic newValue);
}
class DeviceFunctionData {
final String entityId;
final String actionExecutor;
final String function;
final String functionCode;
final String operationName;
final dynamic value;
final String? condition;
final String? valueDescription;
final UniqueKey uniqueKey;
DeviceFunctionData({
required this.entityId,
this.actionExecutor = 'function',
required this.function,
this.actionExecutor = 'device_issue',
required this.functionCode,
required this.operationName,
required this.value,
this.condition,
this.valueDescription,
});
}) : uniqueKey = UniqueKey();
Map<String, dynamic> toJson() {
return {
'entityId': entityId,
'actionExecutor': actionExecutor,
'function': function,
'function': functionCode,
'operationName': operationName,
'value': value,
if (condition != null) 'condition': condition,
@ -51,7 +52,7 @@ class DeviceFunctionData {
return DeviceFunctionData(
entityId: json['entityId'],
actionExecutor: json['actionExecutor'] ?? 'function',
function: json['function'],
functionCode: json['function'],
operationName: json['operationName'],
value: json['value'],
condition: json['condition'],
@ -66,7 +67,7 @@ class DeviceFunctionData {
return other is DeviceFunctionData &&
other.entityId == entityId &&
other.actionExecutor == actionExecutor &&
other.function == function &&
other.functionCode == functionCode &&
other.operationName == operationName &&
other.value == value &&
other.condition == condition &&
@ -77,7 +78,7 @@ class DeviceFunctionData {
int get hashCode {
return entityId.hashCode ^
actionExecutor.hashCode ^
function.hashCode ^
functionCode.hashCode ^
operationName.hashCode ^
value.hashCode ^
condition.hashCode ^

View File

@ -10,8 +10,5 @@ abstract class BaseSwitchFunction extends DeviceFunction<bool> {
required super.icon,
});
@override
bool execute(bool currentStatus, dynamic newValue);
List<SwitchOperationalValue> getOperationalValues();
}

View File

@ -10,11 +10,6 @@ class OneGangSwitchFunction extends BaseSwitchFunction {
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
@ -38,11 +33,6 @@ class OneGangCountdownFunction extends BaseSwitchFunction {
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(

View File

@ -10,11 +10,6 @@ class ThreeGangSwitch1Function extends BaseSwitchFunction {
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
@ -39,11 +34,6 @@ class ThreeGangCountdown1Function extends BaseSwitchFunction {
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
@ -65,11 +55,6 @@ class ThreeGangSwitch2Function extends BaseSwitchFunction {
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
@ -94,11 +79,6 @@ class ThreeGangCountdown2Function extends BaseSwitchFunction {
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
@ -120,11 +100,6 @@ class ThreeGangSwitch3Function extends BaseSwitchFunction {
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
@ -149,11 +124,6 @@ class ThreeGangCountdown3Function extends BaseSwitchFunction {
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(

View File

@ -10,11 +10,6 @@ class TwoGangSwitch1Function extends BaseSwitchFunction {
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
@ -31,20 +26,13 @@ class TwoGangSwitch1Function extends BaseSwitchFunction {
}
class TwoGangSwitch2Function extends BaseSwitchFunction {
TwoGangSwitch2Function({required String deviceId, required String deviceName})
TwoGangSwitch2Function({required super.deviceId, required super.deviceName})
: super(
deviceId: deviceId,
deviceName: deviceName,
code: 'switch_2',
operationName: 'Light 2 Switch',
icon: Assets.assetsAcPower,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
@ -69,11 +57,6 @@ class TwoGangCountdown1Function extends BaseSwitchFunction {
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(
@ -96,11 +79,6 @@ class TwoGangCountdown2Function extends BaseSwitchFunction {
icon: Assets.assetsLightCountdown,
);
@override
bool execute(bool currentStatus, dynamic newValue) {
return newValue as bool;
}
@override
List<SwitchOperationalValue> getOperationalValues() => [
SwitchOperationalValue(

View File

@ -43,16 +43,16 @@ class DraggableCard extends StatelessWidget {
BuildContext context, List<DeviceFunctionData> deviceFunctions) {
return Card(
color: ColorsManager.whiteColors,
child: SizedBox(
child: Container(
padding: const EdgeInsets.all(8),
width: 90,
height: deviceFunctions.isEmpty ? 123 : null,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 123,
child: Column(
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
@ -88,50 +88,25 @@ class DraggableCard extends StatelessWidget {
),
],
),
),
if (deviceFunctions.isNotEmpty) ...[
const Divider(height: 1),
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4),
itemCount: deviceFunctions.length,
itemBuilder: (context, index) {
final function = deviceFunctions[index];
return Row(
if (deviceFunctions.isNotEmpty)
// const Divider(height: 1),
...deviceFunctions.map((function) => Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Text(
'${function.operationName}: ${function.valueDescription}',
'${function.operationName}: ${function.value}',
style: context.textTheme.bodySmall?.copyWith(
fontSize: 9,
color: ColorsManager.textGray,
height: 1.2,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
InkWell(
onTap: () {
context.read<RoutineBloc>().add(
RemoveFunction(function),
);
},
child: const Padding(
padding: EdgeInsets.all(2),
child: Icon(
Icons.close,
size: 12,
color: ColorsManager.textGray,
),
),
),
],
);
},
),
],
)),
],
),
),

View File

@ -9,9 +9,6 @@ class RoutineDevices extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Get the RoutineBloc instance from the parent
final routineBloc = context.read<RoutineBloc>();
return BlocProvider(
create: (context) => DeviceManagementBloc()..add(FetchDevices()),
child: BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
@ -26,9 +23,7 @@ class RoutineDevices extends StatelessWidget {
.toList();
// Provide the RoutineBloc to the child widgets
return BlocProvider.value(
value: routineBloc,
child: BlocBuilder<RoutineBloc, RoutineState>(
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, routineState) {
return Wrap(
spacing: 10,
@ -39,9 +34,8 @@ class RoutineDevices extends StatelessWidget {
imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '',
deviceData: {
'key': UniqueKey().toString(),
'imagePath':
device.getDefaultIcon(device.productType),
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
@ -51,7 +45,6 @@ class RoutineDevices extends StatelessWidget {
}).toList(),
);
},
),
);
}
return const Center(child: CircularProgressIndicator());

View File

@ -30,7 +30,7 @@ class ThenContainer extends StatelessWidget {
runSpacing: 8,
children: state.thenItems
.map((item) => DraggableCard(
key: Key(item['key']!),
// key: Key(item['key']!),
imagePath: item['imagePath']!,
title: item['title']!,
deviceData: item,
@ -45,6 +45,18 @@ class ThenContainer extends StatelessWidget {
onAccept: (data) async {
final result =
await DeviceDialogHelper.showDeviceDialog(context, data);
// if (result != null) {
// for (var function in routineBloc.state.selectedFunctions) {
// routineBloc.add(AddToThenContainer(
// {
// 'item': function,
// 'imagePath': data['imagePath'],
// 'title': data['name'],
// }
// ));
// }
// }
if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer(data));
} else if (!['AC', '1G', '2G', '3G']

View File

@ -97,3 +97,9 @@ extension AccessStatusExtension on AccessStatus {
enum TempModes { hot, cold, wind }
enum FanSpeeds { auto, low, middle, high }
enum AcValuesEnums {
Cooling,
Heating,
Ventilation,
}