mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-15 09:45:25 +00:00
Compare commits
18 Commits
SP-1506-FE
...
SP-1475-FE
Author | SHA1 | Date | |
---|---|---|---|
06b320a75d | |||
a15b5439f0 | |||
4ded7d5202 | |||
0d45a155e3 | |||
17aad13b2a | |||
a849c1dafb | |||
3e3e17019a | |||
b1bae3cb15 | |||
051bf657ed | |||
5191c1e456 | |||
7a073f10aa | |||
900d47faae | |||
e35a7fdc70 | |||
d80f5e1f3a | |||
39d125ac7e | |||
ad15d0e138 | |||
e6d272a60d | |||
8dfe8d10d4 |
@ -10,6 +10,7 @@
|
||||
analyzer:
|
||||
errors:
|
||||
constant_identifier_names: ignore
|
||||
overridden_fields: ignore
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
|
@ -14,7 +14,6 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||
return BlocBuilder<AnalyticsTabBloc, AnalyticsPageTab>(
|
||||
buildWhen: (previous, current) => previous != current,
|
||||
builder: (context, selectedTab) => Column(
|
||||
@ -68,15 +67,21 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
||||
context.read<AnalyticsDatePickerBloc>().add(
|
||||
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
||||
);
|
||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(
|
||||
context,
|
||||
selectedDate: value,
|
||||
communityId:
|
||||
spaceTreeState.selectedCommunities.firstOrNull ??
|
||||
'',
|
||||
spaceId:
|
||||
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
);
|
||||
|
||||
final spaceTreeState =
|
||||
context.read<SpaceTreeBloc>().state;
|
||||
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
||||
FetchEnergyManagementDataHelper
|
||||
.loadEnergyManagementData(
|
||||
context,
|
||||
selectedDate: value,
|
||||
communityId:
|
||||
spaceTreeState.selectedCommunities.firstOrNull ??
|
||||
'',
|
||||
spaceId:
|
||||
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
);
|
||||
}
|
||||
},
|
||||
selectedDate: context
|
||||
.watch<AnalyticsDatePickerBloc>()
|
||||
|
@ -45,7 +45,7 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
child: Container(
|
||||
padding: const EdgeInsetsDirectional.all(20),
|
||||
width: 320,
|
||||
@ -121,6 +121,7 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
||||
}
|
||||
|
||||
Row _buildYearSelector() {
|
||||
final currentYear = DateTime.now().year;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
@ -134,17 +135,35 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
onPressed: () => setState(() => _currentYear = _currentYear - 1),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_currentYear = _currentYear - 1;
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.chevron_left,
|
||||
color: ColorsManager.grey700,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => setState(() => _currentYear = _currentYear + 1),
|
||||
icon: const Icon(
|
||||
onPressed: _currentYear < currentYear
|
||||
? () {
|
||||
setState(() {
|
||||
_currentYear = _currentYear + 1;
|
||||
// Clear selected month if it becomes invalid in the new year
|
||||
if (_currentYear == currentYear &&
|
||||
_selectedMonth != null &&
|
||||
_selectedMonth! > DateTime.now().month - 1) {
|
||||
_selectedMonth = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
: null,
|
||||
icon: Icon(
|
||||
Icons.chevron_right,
|
||||
color: ColorsManager.grey700,
|
||||
color: _currentYear < currentYear
|
||||
? ColorsManager.grey700
|
||||
: ColorsManager.grey700.withValues(alpha: 0.3),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -152,6 +171,9 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
||||
}
|
||||
|
||||
Widget _buildMonthsGrid() {
|
||||
final currentDate = DateTime.now();
|
||||
final isCurrentYear = _currentYear == currentDate.year;
|
||||
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: 12,
|
||||
@ -165,14 +187,18 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final isSelected = _selectedMonth == index;
|
||||
final isFutureMonth = isCurrentYear && index > currentDate.month - 1;
|
||||
|
||||
return InkWell(
|
||||
onTap: () => setState(() => _selectedMonth = index),
|
||||
onTap: isFutureMonth ? null : () => setState(() => _selectedMonth = index),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? ColorsManager.vividBlue.withValues(alpha: 0.7)
|
||||
: const Color(0xFFEDF2F7),
|
||||
: isFutureMonth
|
||||
? ColorsManager.grey700.withValues(alpha: 0.1)
|
||||
: const Color(0xFFEDF2F7),
|
||||
borderRadius:
|
||||
isSelected ? BorderRadius.circular(15) : BorderRadius.zero,
|
||||
),
|
||||
@ -182,7 +208,9 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
||||
fontSize: 12,
|
||||
color: isSelected
|
||||
? ColorsManager.whiteColors
|
||||
: ColorsManager.blackColor.withValues(alpha: 0.8),
|
||||
: isFutureMonth
|
||||
? ColorsManager.blackColor.withValues(alpha: 0.3)
|
||||
: ColorsManager.blackColor.withValues(alpha: 0.8),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
|
@ -20,9 +20,9 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
|
||||
late int _currentYear;
|
||||
|
||||
static final years = List.generate(
|
||||
DateTime.now().year - 2020 + 1,
|
||||
DateTime.now().year - (DateTime.now().year - 5) + 1,
|
||||
(index) => (2020 + index),
|
||||
);
|
||||
).where((year) => year <= DateTime.now().year).toList();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -33,7 +33,7 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
child: Container(
|
||||
padding: const EdgeInsetsDirectional.all(20),
|
||||
width: 320,
|
||||
|
@ -20,7 +20,8 @@ abstract final class EnergyManagementChartsHelper {
|
||||
interval: 1,
|
||||
reservedSize: 32,
|
||||
showTitles: true,
|
||||
maxIncluded: true,
|
||||
maxIncluded: false,
|
||||
minIncluded: false,
|
||||
getTitlesWidget: (value, meta) => Padding(
|
||||
padding: const EdgeInsetsDirectional.only(top: 20.0),
|
||||
child: Text(
|
||||
@ -36,7 +37,8 @@ abstract final class EnergyManagementChartsHelper {
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
maxIncluded: true,
|
||||
maxIncluded: false,
|
||||
minIncluded: false,
|
||||
interval: leftTitlesInterval,
|
||||
reservedSize: 110,
|
||||
getTitlesWidget: (value, meta) => Padding(
|
||||
|
@ -18,7 +18,10 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BarChart(
|
||||
BarChartData(
|
||||
gridData: EnergyManagementChartsHelper.gridData(),
|
||||
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||
checkToShowHorizontalLine: (value) => true,
|
||||
horizontalInterval: 250,
|
||||
),
|
||||
borderData: EnergyManagementChartsHelper.borderData(),
|
||||
barTouchData: _barTouchData(context),
|
||||
titlesData: _titlesData(context),
|
||||
|
@ -16,7 +16,10 @@ class EnergyConsumptionPerDeviceChart extends StatelessWidget {
|
||||
context,
|
||||
leftTitlesInterval: 250,
|
||||
),
|
||||
gridData: EnergyManagementChartsHelper.gridData(),
|
||||
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||
checkToShowHorizontalLine: (value) => true,
|
||||
horizontalInterval: 250,
|
||||
),
|
||||
borderData: EnergyManagementChartsHelper.borderData(),
|
||||
lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
|
||||
lineBarsData: chartData.map((e) {
|
||||
|
@ -12,7 +12,6 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/po
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_phases_data_widget.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/power_clamp/models/power_clamp_model.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
@ -4,15 +4,6 @@ import 'package:syncrow_web/pages/analytics/models/energy_data_model.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
// energy_consumption_chart will return id, name and consumption
|
||||
const phasesJson = {
|
||||
"1": {
|
||||
"phaseOne": 1000,
|
||||
"phaseTwo": 2000,
|
||||
"phaseThree": 3000,
|
||||
}
|
||||
};
|
||||
|
||||
class TotalEnergyConsumptionChart extends StatelessWidget {
|
||||
const TotalEnergyConsumptionChart({required this.chartData, super.key});
|
||||
|
||||
@ -23,8 +14,14 @@ class TotalEnergyConsumptionChart extends StatelessWidget {
|
||||
return Expanded(
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
titlesData: EnergyManagementChartsHelper.titlesData(context),
|
||||
gridData: EnergyManagementChartsHelper.gridData(),
|
||||
titlesData: EnergyManagementChartsHelper.titlesData(
|
||||
context,
|
||||
leftTitlesInterval: 250,
|
||||
),
|
||||
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||
checkToShowHorizontalLine: (value) => true,
|
||||
horizontalInterval: 250,
|
||||
),
|
||||
borderData: EnergyManagementChartsHelper.borderData(),
|
||||
lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
|
||||
lineBarsData: _lineBarsData,
|
||||
|
@ -28,25 +28,13 @@ abstract final class FetchOccupancyDataHelper {
|
||||
loadAnalyticsDevices(context, communityUuid: communityId, spaceUuid: spaceId);
|
||||
final selectedDevice = context.read<AnalyticsDevicesBloc>().state.selectedDevice;
|
||||
|
||||
context.read<OccupancyBloc>().add(
|
||||
LoadOccupancyEvent(
|
||||
GetOccupancyParam(
|
||||
monthDate:
|
||||
'${datePickerState.monthlyDate.year}-${datePickerState.monthlyDate.month}',
|
||||
spaceUuid: spaceId,
|
||||
communityUuid: communityId,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
context.read<OccupancyHeatMapBloc>().add(
|
||||
LoadOccupancyHeatMapEvent(
|
||||
GetOccupancyHeatMapParam(
|
||||
spaceUuid: spaceId,
|
||||
year: datePickerState.yearlyDate,
|
||||
),
|
||||
),
|
||||
);
|
||||
loadOccupancyChartData(
|
||||
context,
|
||||
communityUuid: communityId,
|
||||
spaceUuid: spaceId,
|
||||
date: datePickerState.monthlyDate,
|
||||
);
|
||||
loadHeatMapData(context, spaceUuid: spaceId, year: datePickerState.yearlyDate);
|
||||
|
||||
if (selectedDevice case final AnalyticsDevice device) {
|
||||
context.read<RealtimeDeviceChangesBloc>()
|
||||
@ -57,6 +45,35 @@ abstract final class FetchOccupancyDataHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static void loadHeatMapData(
|
||||
BuildContext context, {
|
||||
required String spaceUuid,
|
||||
required DateTime year,
|
||||
}) {
|
||||
context.read<OccupancyHeatMapBloc>().add(
|
||||
LoadOccupancyHeatMapEvent(
|
||||
GetOccupancyHeatMapParam(spaceUuid: spaceUuid, year: year),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static void loadOccupancyChartData(
|
||||
BuildContext context, {
|
||||
required String communityUuid,
|
||||
required String spaceUuid,
|
||||
required DateTime date,
|
||||
}) {
|
||||
context.read<OccupancyBloc>().add(
|
||||
LoadOccupancyEvent(
|
||||
GetOccupancyParam(
|
||||
monthDate: '${date.year}-${date.month}',
|
||||
spaceUuid: spaceUuid,
|
||||
communityUuid: communityUuid,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static void loadAnalyticsDevices(
|
||||
BuildContext context, {
|
||||
required String communityUuid,
|
||||
|
@ -23,7 +23,14 @@ class OccupancyChart extends StatelessWidget {
|
||||
),
|
||||
borderData: EnergyManagementChartsHelper.borderData(),
|
||||
barTouchData: _barTouchData(context),
|
||||
titlesData: _titlesData(context),
|
||||
titlesData: _titlesData(context).copyWith(
|
||||
leftTitles: _titlesData(context).leftTitles.copyWith(
|
||||
sideTitles: _titlesData(context).leftTitles.sideTitles.copyWith(
|
||||
maxIncluded: true,
|
||||
minIncluded: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
barGroups: List.generate(chartData.length, (index) {
|
||||
final actual = chartData[index];
|
||||
return BarChartGroupData(
|
||||
|
@ -47,11 +47,17 @@ class OccupancyChartBox extends StatelessWidget {
|
||||
context.read<AnalyticsDatePickerBloc>().add(
|
||||
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
||||
);
|
||||
FetchOccupancyDataHelper.loadOccupancyData(
|
||||
context,
|
||||
communityId: spaceTreeState.selectedCommunities.firstOrNull ?? '',
|
||||
spaceId: spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
);
|
||||
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
||||
FetchOccupancyDataHelper.loadOccupancyChartData(
|
||||
context,
|
||||
communityUuid:
|
||||
spaceTreeState.selectedCommunities.firstOrNull ??
|
||||
'',
|
||||
spaceUuid:
|
||||
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
date: value,
|
||||
);
|
||||
}
|
||||
},
|
||||
selectedDate: context
|
||||
.watch<AnalyticsDatePickerBloc>()
|
||||
|
@ -47,12 +47,14 @@ class OccupancyHeatMapBox extends StatelessWidget {
|
||||
context.read<AnalyticsDatePickerBloc>().add(
|
||||
UpdateAnalyticsDatePickerEvent(yearlyDate: value),
|
||||
);
|
||||
FetchOccupancyDataHelper.loadOccupancyData(
|
||||
context,
|
||||
communityId:
|
||||
spaceTreeState.selectedCommunities.firstOrNull ?? '',
|
||||
spaceId: spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
);
|
||||
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
||||
FetchOccupancyDataHelper.loadHeatMapData(
|
||||
context,
|
||||
spaceUuid:
|
||||
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
year: value,
|
||||
);
|
||||
}
|
||||
},
|
||||
datePickerType: DatePickerType.year,
|
||||
selectedDate: context
|
||||
|
@ -39,7 +39,6 @@ class RemoteOccupancyAnalyticsDevicesService implements AnalyticsDevicesService
|
||||
path:
|
||||
'/projects/$projectUuid/communities/${param.communityUuid}/spaces/${param.spaceUuid}/devices',
|
||||
queryParameters: {
|
||||
'requestType': param.requestType.name,
|
||||
'communityUuid': param.communityUuid,
|
||||
'spaceUuid': param.spaceUuid,
|
||||
'productType': param.deviceTypes.first,
|
||||
|
@ -63,7 +63,8 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
}
|
||||
}
|
||||
|
||||
bool _compareListOfLists(List<List<dynamic>> oldList, List<List<dynamic>> newList) {
|
||||
bool _compareListOfLists(
|
||||
List<List<dynamic>> oldList, List<List<dynamic>> newList) {
|
||||
// Check if the old and new lists are the same
|
||||
if (oldList.length != newList.length) return false;
|
||||
|
||||
@ -132,46 +133,60 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
children: [
|
||||
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
||||
...List.generate(widget.headers.length, (index) {
|
||||
return _buildTableHeaderCell(widget.headers[index], index);
|
||||
return _buildTableHeaderCell(
|
||||
widget.headers[index], index);
|
||||
})
|
||||
//...widget.headers.map((header) => _buildTableHeaderCell(header)),
|
||||
],
|
||||
),
|
||||
),
|
||||
widget.isEmpty
|
||||
? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SvgPicture.asset(Assets.emptyTable),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Text(
|
||||
widget.tableName == 'AccessManagement' ? 'No Password ' : 'No Devices',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: ColorsManager.grayColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
? SizedBox(
|
||||
height: widget.size.height * 0.5,
|
||||
width: widget.size.width,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SvgPicture.asset(Assets.emptyTable),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Text(
|
||||
widget.tableName == 'AccessManagement'
|
||||
? 'No Password '
|
||||
: 'No Devices',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color:
|
||||
ColorsManager.grayColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Column(
|
||||
children: List.generate(widget.data.length, (index) {
|
||||
children:
|
||||
List.generate(widget.data.length, (index) {
|
||||
final row = widget.data[index];
|
||||
return Row(
|
||||
children: [
|
||||
if (widget.withCheckBox) _buildRowCheckbox(index, widget.size.height * 0.08),
|
||||
...row.map((cell) => _buildTableCell(cell.toString(), widget.size.height * 0.08)),
|
||||
if (widget.withCheckBox)
|
||||
_buildRowCheckbox(
|
||||
index, widget.size.height * 0.08),
|
||||
...row.map((cell) => _buildTableCell(
|
||||
cell.toString(),
|
||||
widget.size.height * 0.08)),
|
||||
],
|
||||
);
|
||||
}),
|
||||
@ -196,7 +211,9 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
),
|
||||
child: Checkbox(
|
||||
value: _selectAll,
|
||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty ? _toggleSelectAll : null,
|
||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
||||
? _toggleSelectAll
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -238,7 +255,9 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
constraints: const BoxConstraints.expand(height: 40),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: index == widget.headers.length - 1 ? 12 : 8.0, vertical: 4),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: index == widget.headers.length - 1 ? 12 : 8.0,
|
||||
vertical: 4),
|
||||
child: Text(
|
||||
title,
|
||||
style: context.textTheme.titleSmall!.copyWith(
|
||||
|
@ -97,7 +97,8 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
||||
children: [
|
||||
_buildInfoRow('Space Name:',
|
||||
device.spaces?.firstOrNull?.spaceName ?? 'N/A'),
|
||||
_buildInfoRow('Room:', device.subspace?.subspaceName ?? 'N/A'),
|
||||
_buildInfoRow(
|
||||
'Sub space:', device.subspace?.subspaceName ?? 'N/A'),
|
||||
],
|
||||
),
|
||||
TableRow(
|
||||
|
@ -26,8 +26,10 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
|
||||
functionCode: event.functionData.functionCode,
|
||||
operationName: event.functionData.operationName,
|
||||
value: event.functionData.value ?? existingData.value,
|
||||
valueDescription: event.functionData.valueDescription ?? existingData.valueDescription,
|
||||
valueDescription: event.functionData.valueDescription ??
|
||||
existingData.valueDescription,
|
||||
condition: event.functionData.condition ?? existingData.condition,
|
||||
step: event.functionData.step ?? existingData.step,
|
||||
);
|
||||
} else {
|
||||
functions.clear();
|
||||
@ -59,8 +61,10 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> _onSelectFunction(SelectFunction event, Emitter<FunctionBlocState> emit) {
|
||||
FutureOr<void> _onSelectFunction(
|
||||
SelectFunction event, Emitter<FunctionBlocState> emit) {
|
||||
emit(state.copyWith(
|
||||
selectedFunction: event.functionCode, selectedOperationName: event.operationName));
|
||||
selectedFunction: event.functionCode,
|
||||
selectedOperationName: event.operationName));
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,10 @@ abstract class ACFunction extends DeviceFunction<AcStatusModel> {
|
||||
required super.operationName,
|
||||
required super.icon,
|
||||
required this.type,
|
||||
super.step,
|
||||
super.unit,
|
||||
super.max,
|
||||
super.min,
|
||||
});
|
||||
|
||||
List<ACOperationalValue> getOperationalValues();
|
||||
@ -75,26 +79,24 @@ class ModeFunction extends ACFunction {
|
||||
}
|
||||
|
||||
class TempSetFunction extends ACFunction {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
TempSetFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
: min = 160,
|
||||
max = 300,
|
||||
step = 1,
|
||||
super(
|
||||
TempSetFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'temp_set',
|
||||
operationName: 'Set Temperature',
|
||||
icon: Assets.assetsTempreture,
|
||||
type: type,
|
||||
min: 200,
|
||||
max: 300,
|
||||
step: 1,
|
||||
unit: "°C",
|
||||
);
|
||||
|
||||
@override
|
||||
List<ACOperationalValue> getOperationalValues() {
|
||||
List<ACOperationalValue> values = [];
|
||||
for (int temp = min; temp <= max; temp += step) {
|
||||
for (int temp = min!.toInt(); temp <= max!; temp += step!.toInt()) {
|
||||
values.add(ACOperationalValue(
|
||||
icon: Assets.assetsTempreture,
|
||||
description: "${temp / 10}°C",
|
||||
@ -104,7 +106,6 @@ class TempSetFunction extends ACFunction {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
class LevelFunction extends ACFunction {
|
||||
LevelFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
@ -166,9 +167,10 @@ class ChildLockFunction extends ACFunction {
|
||||
}
|
||||
|
||||
class CurrentTempFunction extends ACFunction {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit = "°C";
|
||||
|
||||
CurrentTempFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
@ -185,7 +187,7 @@ class CurrentTempFunction extends ACFunction {
|
||||
@override
|
||||
List<ACOperationalValue> getOperationalValues() {
|
||||
List<ACOperationalValue> values = [];
|
||||
for (int temp = min; temp <= max; temp += step) {
|
||||
for (int temp = min.toInt(); temp <= max; temp += step.toInt()) {
|
||||
values.add(ACOperationalValue(
|
||||
icon: Assets.currentTemp,
|
||||
description: "${temp / 10}°C",
|
||||
|
@ -6,10 +6,12 @@ class CpsOperationalValue {
|
||||
final String description;
|
||||
final dynamic value;
|
||||
|
||||
|
||||
CpsOperationalValue({
|
||||
required this.icon,
|
||||
required this.description,
|
||||
required this.value,
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,9 +96,9 @@ final class CpsSensitivityFunction extends CpsFunctions {
|
||||
icon: Assets.sensitivity,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
static const _images = <String>[
|
||||
Assets.sensitivityFeature1,
|
||||
@ -115,10 +117,10 @@ final class CpsSensitivityFunction extends CpsFunctions {
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final values = <CpsOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min; value <= max; value += step.toInt()) {
|
||||
values.add(
|
||||
CpsOperationalValue(
|
||||
icon: _images[value],
|
||||
icon: _images[value.toInt()],
|
||||
description: '$value',
|
||||
value: value,
|
||||
),
|
||||
@ -142,9 +144,9 @@ final class CpsMovingSpeedFunction extends CpsFunctions {
|
||||
icon: Assets.speedoMeter,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
@ -173,9 +175,9 @@ final class CpsSpatialStaticValueFunction extends CpsFunctions {
|
||||
icon: Assets.spatialStaticValue,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
@ -204,9 +206,9 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions {
|
||||
icon: Assets.spatialMotionValue,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
@ -375,9 +377,9 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions {
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
@ -406,9 +408,9 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions {
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
|
@ -4,6 +4,11 @@ abstract class DeviceFunction<T> {
|
||||
final String code;
|
||||
final String operationName;
|
||||
final String icon;
|
||||
final double? step;
|
||||
final String? unit;
|
||||
final double? max;
|
||||
final double? min;
|
||||
|
||||
|
||||
DeviceFunction({
|
||||
required this.deviceId,
|
||||
@ -11,6 +16,10 @@ abstract class DeviceFunction<T> {
|
||||
required this.code,
|
||||
required this.operationName,
|
||||
required this.icon,
|
||||
this.step,
|
||||
this.unit,
|
||||
this.max,
|
||||
this.min,
|
||||
});
|
||||
}
|
||||
|
||||
@ -22,6 +31,10 @@ class DeviceFunctionData {
|
||||
final dynamic value;
|
||||
final String? condition;
|
||||
final String? valueDescription;
|
||||
final double? step;
|
||||
final String? unit;
|
||||
final double? max;
|
||||
final double? min;
|
||||
|
||||
DeviceFunctionData({
|
||||
required this.entityId,
|
||||
@ -31,6 +44,10 @@ class DeviceFunctionData {
|
||||
required this.value,
|
||||
this.condition,
|
||||
this.valueDescription,
|
||||
this.step,
|
||||
this.unit,
|
||||
this.max,
|
||||
this.min,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@ -42,6 +59,10 @@ class DeviceFunctionData {
|
||||
'value': value,
|
||||
if (condition != null) 'condition': condition,
|
||||
if (valueDescription != null) 'valueDescription': valueDescription,
|
||||
if (step != null) 'step': step,
|
||||
if (unit != null) 'unit': unit,
|
||||
if (max != null) 'max': max,
|
||||
if (min != null) 'min': min,
|
||||
};
|
||||
}
|
||||
|
||||
@ -54,6 +75,10 @@ class DeviceFunctionData {
|
||||
value: json['value'],
|
||||
condition: json['condition'],
|
||||
valueDescription: json['valueDescription'],
|
||||
step: json['step']?.toDouble(),
|
||||
unit: json['unit'],
|
||||
max: json['max']?.toDouble(),
|
||||
min: json['min']?.toDouble(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -68,7 +93,11 @@ class DeviceFunctionData {
|
||||
other.operationName == operationName &&
|
||||
other.value == value &&
|
||||
other.condition == condition &&
|
||||
other.valueDescription == valueDescription;
|
||||
other.valueDescription == valueDescription &&
|
||||
other.step == step &&
|
||||
other.unit == unit &&
|
||||
other.max == max &&
|
||||
other.min == min;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -79,6 +108,10 @@ class DeviceFunctionData {
|
||||
operationName.hashCode ^
|
||||
value.hashCode ^
|
||||
condition.hashCode ^
|
||||
valueDescription.hashCode;
|
||||
valueDescription.hashCode ^
|
||||
step.hashCode ^
|
||||
unit.hashCode ^
|
||||
max.hashCode ^
|
||||
min.hashCode;
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,11 @@ abstract class FlushFunctions
|
||||
}
|
||||
|
||||
class FlushPresenceDelayFunction extends FlushFunctions {
|
||||
final int min;
|
||||
FlushPresenceDelayFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
}) :
|
||||
super(
|
||||
code: FlushMountedPresenceSensorModel.codePresenceState,
|
||||
operationName: 'Presence State',
|
||||
@ -50,9 +49,9 @@ class FlushPresenceDelayFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushSensiReduceFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
FlushSensiReduceFunction({
|
||||
required super.deviceId,
|
||||
@ -80,8 +79,8 @@ class FlushSensiReduceFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushNoneDelayFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final double min;
|
||||
final double max;
|
||||
final String unit;
|
||||
|
||||
FlushNoneDelayFunction({
|
||||
@ -110,9 +109,9 @@ class FlushNoneDelayFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushIlluminanceFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
FlushIlluminanceFunction({
|
||||
required super.deviceId,
|
||||
@ -130,7 +129,7 @@ class FlushIlluminanceFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
List<FlushOperationalValue> values = [];
|
||||
for (int lux = min; lux <= max; lux += step) {
|
||||
for (int lux = min.toInt(); lux <= max; lux += step.toInt()) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.IlluminanceIcon,
|
||||
description: "$lux Lux",
|
||||
@ -142,9 +141,9 @@ class FlushIlluminanceFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushOccurDistReduceFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
FlushOccurDistReduceFunction({
|
||||
required super.deviceId,
|
||||
@ -173,9 +172,9 @@ class FlushOccurDistReduceFunction extends FlushFunctions {
|
||||
|
||||
// ==== then functions ====
|
||||
class FlushSensitivityFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
FlushSensitivityFunction({
|
||||
required super.deviceId,
|
||||
@ -203,9 +202,9 @@ class FlushSensitivityFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushNearDetectionFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final double min;
|
||||
final double max;
|
||||
final int step;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushNearDetectionFunction({
|
||||
@ -225,7 +224,7 @@ class FlushNearDetectionFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min.toDouble(); value <= max; value += step) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
@ -237,9 +236,9 @@ class FlushNearDetectionFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushMaxDetectDistFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushMaxDetectDistFunction({
|
||||
@ -259,7 +258,7 @@ class FlushMaxDetectDistFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min; value <= max; value += step.toInt()) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
@ -271,9 +270,9 @@ class FlushMaxDetectDistFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushTargetConfirmTimeFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushTargetConfirmTimeFunction({
|
||||
@ -293,7 +292,7 @@ class FlushTargetConfirmTimeFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min.toDouble(); value <= max; value += step) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
@ -305,9 +304,9 @@ class FlushTargetConfirmTimeFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushDisappeDelayFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushDisappeDelayFunction({
|
||||
@ -327,7 +326,7 @@ class FlushDisappeDelayFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min.toDouble(); value <= max; value += step) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
@ -339,9 +338,9 @@ class FlushDisappeDelayFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushIndentLevelFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushIndentLevelFunction({
|
||||
@ -361,7 +360,7 @@ class FlushIndentLevelFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min.toDouble(); value <= max; value += step) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
@ -373,9 +372,9 @@ class FlushIndentLevelFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushTriggerLevelFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushTriggerLevelFunction({
|
||||
@ -395,7 +394,7 @@ class FlushTriggerLevelFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min.toDouble(); value <= max; value += step) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
|
@ -20,17 +20,16 @@ abstract class WaterHeaterFunctions
|
||||
}
|
||||
|
||||
class WHRestartStatusFunction extends WaterHeaterFunctions {
|
||||
final int min;
|
||||
WHRestartStatusFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
super(
|
||||
}) : super(
|
||||
code: 'relay_status',
|
||||
operationName: 'Restart Status',
|
||||
icon: Assets.refreshStatusIcon,
|
||||
);
|
||||
|
||||
|
||||
@override
|
||||
List<WaterHeaterOperationalValue> getOperationalValues() {
|
||||
@ -55,13 +54,11 @@ class WHRestartStatusFunction extends WaterHeaterFunctions {
|
||||
}
|
||||
|
||||
class WHSwitchFunction extends WaterHeaterFunctions {
|
||||
final int min;
|
||||
WHSwitchFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
super(
|
||||
}) : super(
|
||||
code: 'switch_1',
|
||||
operationName: 'Switch',
|
||||
icon: Assets.assetsAcPower,
|
||||
@ -104,12 +101,11 @@ class TimerConfirmTimeFunction extends WaterHeaterFunctions {
|
||||
}
|
||||
|
||||
class BacklightFunction extends WaterHeaterFunctions {
|
||||
final int min;
|
||||
BacklightFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
}) :
|
||||
super(
|
||||
code: 'switch_backlight',
|
||||
operationName: 'Backlight',
|
||||
|
@ -4,7 +4,7 @@ import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
abstract class WpsFunctions extends DeviceFunction<WallSensorModel> {
|
||||
final String type;
|
||||
final String type;
|
||||
|
||||
WpsFunctions({
|
||||
required super.deviceId,
|
||||
@ -13,6 +13,10 @@ abstract class WpsFunctions extends DeviceFunction<WallSensorModel> {
|
||||
required super.operationName,
|
||||
required super.icon,
|
||||
required this.type,
|
||||
super.step,
|
||||
super.unit,
|
||||
super.max,
|
||||
super.min,
|
||||
});
|
||||
|
||||
List<WpsOperationalValue> getOperationalValues();
|
||||
@ -20,9 +24,13 @@ abstract class WpsFunctions extends DeviceFunction<WallSensorModel> {
|
||||
|
||||
// For far_detection (75-600cm in 75cm steps)
|
||||
class FarDetectionFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
final double min;
|
||||
@override
|
||||
final double max;
|
||||
@override
|
||||
final double step;
|
||||
@override
|
||||
final String unit;
|
||||
|
||||
FarDetectionFunction(
|
||||
@ -41,7 +49,7 @@ class FarDetectionFunction extends WpsFunctions {
|
||||
@override
|
||||
List<WpsOperationalValue> getOperationalValues() {
|
||||
final values = <WpsOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min; value <= max; value += step.toInt()) {
|
||||
values.add(WpsOperationalValue(
|
||||
icon: Assets.currentDistanceIcon,
|
||||
description: '$value $unit',
|
||||
@ -54,9 +62,9 @@ class FarDetectionFunction extends WpsFunctions {
|
||||
|
||||
// For presence_time (0-65535 minutes)
|
||||
class PresenceTimeFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
PresenceTimeFunction(
|
||||
@ -86,9 +94,9 @@ class PresenceTimeFunction extends WpsFunctions {
|
||||
|
||||
// For motion_sensitivity_value (1-5 levels)
|
||||
class MotionSensitivityFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
MotionSensitivityFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
@ -116,9 +124,9 @@ class MotionSensitivityFunction extends WpsFunctions {
|
||||
}
|
||||
|
||||
class MotionLessSensitivityFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
MotionLessSensitivityFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
@ -171,8 +179,8 @@ class IndicatorFunction extends WpsFunctions {
|
||||
}
|
||||
|
||||
class NoOneTimeFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final double min;
|
||||
final double max;
|
||||
final String unit;
|
||||
|
||||
NoOneTimeFunction(
|
||||
@ -225,9 +233,9 @@ class PresenceStateFunction extends WpsFunctions {
|
||||
}
|
||||
|
||||
class CurrentDistanceFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
CurrentDistanceFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
@ -244,11 +252,10 @@ class CurrentDistanceFunction extends WpsFunctions {
|
||||
@override
|
||||
List<WpsOperationalValue> getOperationalValues() {
|
||||
List<WpsOperationalValue> values = [];
|
||||
for (int cm = min; cm <= max; cm += step) {
|
||||
for (int cm = min.toInt(); cm <= max; cm += step.toInt()) {
|
||||
values.add(WpsOperationalValue(
|
||||
icon: Assets.assetsTempreture,
|
||||
description: "${cm}CM",
|
||||
|
||||
value: cm,
|
||||
));
|
||||
}
|
||||
@ -257,9 +264,9 @@ class CurrentDistanceFunction extends WpsFunctions {
|
||||
}
|
||||
|
||||
class IlluminanceValueFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
IlluminanceValueFunction({
|
||||
required super.deviceId,
|
||||
@ -277,7 +284,7 @@ class IlluminanceValueFunction extends WpsFunctions {
|
||||
@override
|
||||
List<WpsOperationalValue> getOperationalValues() {
|
||||
List<WpsOperationalValue> values = [];
|
||||
for (int lux = min; lux <= max; lux += step) {
|
||||
for (int lux = min.toInt(); lux <= max; lux += step.toInt()) {
|
||||
values.add(WpsOperationalValue(
|
||||
icon: Assets.IlluminanceIcon,
|
||||
description: "$lux Lux",
|
||||
|
297
lib/pages/routines/widgets/custom_routines_textbox.dart
Normal file
297
lib/pages/routines/widgets/custom_routines_textbox.dart
Normal file
@ -0,0 +1,297 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/condition_toggle.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class CustomRoutinesTextbox extends StatefulWidget {
|
||||
final String? currentCondition;
|
||||
final String dialogType;
|
||||
final (double, double) sliderRange;
|
||||
final dynamic displayedValue;
|
||||
final dynamic initialValue;
|
||||
final void Function(String condition) onConditionChanged;
|
||||
final void Function(double value) onTextChanged;
|
||||
final String unit;
|
||||
final double dividendOfRange;
|
||||
final double stepIncreaseAmount;
|
||||
final bool withSpecialChar;
|
||||
|
||||
const CustomRoutinesTextbox({
|
||||
required this.dialogType,
|
||||
required this.sliderRange,
|
||||
required this.displayedValue,
|
||||
required this.initialValue,
|
||||
required this.onConditionChanged,
|
||||
required this.onTextChanged,
|
||||
required this.currentCondition,
|
||||
required this.unit,
|
||||
required this.dividendOfRange,
|
||||
required this.stepIncreaseAmount,
|
||||
required this.withSpecialChar,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CustomRoutinesTextbox> createState() => _CustomRoutinesTextboxState();
|
||||
}
|
||||
|
||||
class _CustomRoutinesTextboxState extends State<CustomRoutinesTextbox> {
|
||||
late final TextEditingController _controller;
|
||||
bool hasError = false;
|
||||
String? errorMessage;
|
||||
|
||||
int getDecimalPlaces(double step) {
|
||||
String stepStr = step.toString();
|
||||
if (stepStr.contains('.')) {
|
||||
List<String> parts = stepStr.split('.');
|
||||
String decimalPart = parts[1];
|
||||
decimalPart = decimalPart.replaceAll(RegExp(r'0+$'), '');
|
||||
return decimalPart.isEmpty ? 0 : decimalPart.length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||
double initialValue;
|
||||
if (widget.initialValue != null &&
|
||||
widget.initialValue is num &&
|
||||
(widget.initialValue as num) == 0) {
|
||||
initialValue = 0.0;
|
||||
} else {
|
||||
initialValue = double.tryParse(widget.displayedValue) ?? 0.0;
|
||||
}
|
||||
_controller = TextEditingController(
|
||||
text: initialValue.toStringAsFixed(decimalPlaces),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _validateInput(String value) {
|
||||
final doubleValue = double.tryParse(value);
|
||||
if (doubleValue == null) {
|
||||
setState(() {
|
||||
errorMessage = "Invalid number";
|
||||
hasError = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
final min = widget.sliderRange.$1;
|
||||
final max = widget.sliderRange.$2;
|
||||
|
||||
if (doubleValue < min) {
|
||||
setState(() {
|
||||
errorMessage = "Value must be at least $min";
|
||||
hasError = true;
|
||||
});
|
||||
} else if (doubleValue > max) {
|
||||
setState(() {
|
||||
errorMessage = "Value must be at most $max";
|
||||
hasError = true;
|
||||
});
|
||||
} else {
|
||||
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||
int factor = pow(10, decimalPlaces).toInt();
|
||||
int scaledStep = (widget.stepIncreaseAmount * factor).round();
|
||||
int scaledValue = (doubleValue * factor).round();
|
||||
|
||||
if (scaledValue % scaledStep != 0) {
|
||||
setState(() {
|
||||
errorMessage = "must be a multiple of ${widget.stepIncreaseAmount}";
|
||||
hasError = true;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
errorMessage = null;
|
||||
hasError = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CustomRoutinesTextbox oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.initialValue != oldWidget.initialValue) {
|
||||
if (widget.initialValue != null &&
|
||||
widget.initialValue is num &&
|
||||
(widget.initialValue as num) == 0) {
|
||||
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||
_controller.text = 0.0.toStringAsFixed(decimalPlaces);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _correctAndUpdateValue(String value) {
|
||||
final doubleValue = double.tryParse(value) ?? 0.0;
|
||||
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||
double rounded = (doubleValue / widget.stepIncreaseAmount).round() *
|
||||
widget.stepIncreaseAmount;
|
||||
rounded = rounded.clamp(widget.sliderRange.$1, widget.sliderRange.$2);
|
||||
rounded = double.parse(rounded.toStringAsFixed(decimalPlaces));
|
||||
|
||||
setState(() {
|
||||
hasError = false;
|
||||
errorMessage = null;
|
||||
});
|
||||
|
||||
_controller.text = rounded.toStringAsFixed(decimalPlaces);
|
||||
_controller.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: _controller.text.length),
|
||||
);
|
||||
widget.onTextChanged(rounded);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||
|
||||
List<TextInputFormatter> formatters = [];
|
||||
if (decimalPlaces == 0) {
|
||||
formatters.add(FilteringTextInputFormatter.digitsOnly);
|
||||
} else {
|
||||
formatters.add(FilteringTextInputFormatter.allow(
|
||||
RegExp(r'^\d*\.?\d{0,' + decimalPlaces.toString() + r'}$'),
|
||||
));
|
||||
}
|
||||
formatters.add(RangeInputFormatter(
|
||||
min: widget.sliderRange.$1,
|
||||
max: widget.sliderRange.$2,
|
||||
));
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (widget.dialogType == 'IF')
|
||||
ConditionToggle(
|
||||
currentCondition: widget.currentCondition,
|
||||
onChanged: widget.onConditionChanged,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 35, vertical: 2),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
'Step: ${widget.stepIncreaseAmount}',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
width: 170,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF8F8F8),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: hasError
|
||||
? Border.all(color: Colors.red, width: 1)
|
||||
: Border.all(
|
||||
color: ColorsManager.lightGrayBorderColor, width: 1),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: ColorsManager.blackColor.withOpacity(0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
controller: _controller,
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: widget.withSpecialChar == true
|
||||
? [FilteringTextInputFormatter.digitsOnly]
|
||||
: null,
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
isDense: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
onChanged: _validateInput,
|
||||
onFieldSubmitted: _correctAndUpdateValue,
|
||||
onTapOutside: (_) =>
|
||||
_correctAndUpdateValue(_controller.text),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
widget.unit,
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.vividBlue,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2.0),
|
||||
child: Text(
|
||||
errorMessage!,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.red,
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Min. ${widget.sliderRange.$1.toInt()}${widget.unit}',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Max. ${widget.sliderRange.$2.toInt()}${widget.unit}',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||
@ -76,24 +77,20 @@ class ACHelper {
|
||||
context: context,
|
||||
acFunctions: acFunctions,
|
||||
device: device,
|
||||
onFunctionSelected: (functionCode, operationName) {
|
||||
onFunctionSelected:
|
||||
(functionCode, operationName) {
|
||||
RoutineTapFunctionHelper.onTapFunction(
|
||||
context,
|
||||
functionCode: functionCode,
|
||||
functionOperationName: operationName,
|
||||
functionValueDescription:
|
||||
selectedFunctionData.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'temp_set',
|
||||
'temp_current',
|
||||
],
|
||||
defaultValue: functionCode == 'temp_set'
|
||||
? 200
|
||||
: functionCode == 'temp_current'
|
||||
? -100
|
||||
: 0,
|
||||
);
|
||||
context,
|
||||
functionCode: functionCode,
|
||||
functionOperationName: operationName,
|
||||
functionValueDescription:
|
||||
selectedFunctionData.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'temp_set',
|
||||
'temp_current',
|
||||
],
|
||||
defaultValue: 0);
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -206,27 +203,61 @@ class ACHelper {
|
||||
required String operationName,
|
||||
bool? removeComparators,
|
||||
}) {
|
||||
final initialVal = selectedFunction == 'temp_set' ? 200 : -100;
|
||||
final selectedFn =
|
||||
acFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
|
||||
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
|
||||
final initialValue = selectedFunctionData?.value ?? initialVal;
|
||||
return _buildTemperatureSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
// Convert stored integer value to display value
|
||||
final displayValue =
|
||||
(selectedFunctionData?.value ?? selectedFn.min ?? 0) / 10;
|
||||
final minValue = selectedFn.min! / 10;
|
||||
final maxValue = selectedFn.max! / 10;
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: true,
|
||||
dividendOfRange: maxValue,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparators: removeComparators,
|
||||
dialogType: selectedFn.type,
|
||||
sliderRange: (minValue, maxValue),
|
||||
displayedValue: displayValue.toStringAsFixed(1),
|
||||
initialValue: displayValue.toDouble(),
|
||||
unit: selectedFn.unit!,
|
||||
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: selectedFn.operationName,
|
||||
condition: condition,
|
||||
value: 0,
|
||||
step: selectedFn.step,
|
||||
unit: selectedFn.unit,
|
||||
max: selectedFn.max,
|
||||
min: selectedFn.min,
|
||||
),
|
||||
),
|
||||
),
|
||||
onTextChanged: (value) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: selectedFn.operationName,
|
||||
value: (value * 10).round(), // Store as integer
|
||||
condition: selectedFunctionData?.condition,
|
||||
step: selectedFn.step,
|
||||
unit: selectedFn.unit,
|
||||
max: selectedFn.max,
|
||||
min: selectedFn.min,
|
||||
),
|
||||
),
|
||||
),
|
||||
stepIncreaseAmount: selectedFn.step! / 10, // Convert step for display
|
||||
);
|
||||
}
|
||||
|
||||
final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
return _buildOperationalValuesList(
|
||||
context: context,
|
||||
values: values,
|
||||
values: selectedFn.getOperationalValues(),
|
||||
selectedValue: selectedFunctionData?.value,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
@ -235,150 +266,151 @@ class ACHelper {
|
||||
);
|
||||
}
|
||||
|
||||
/// Build temperature selector for AC functions dialog
|
||||
static Widget _buildTemperatureSelector({
|
||||
required BuildContext context,
|
||||
required dynamic initialValue,
|
||||
required String? currentCondition,
|
||||
required String selectCode,
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
bool? removeComparators,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (removeComparators != true)
|
||||
_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 temperature selector for AC functions dialog
|
||||
// static Widget _buildTemperatureSelector({
|
||||
// required BuildContext context,
|
||||
// required dynamic initialValue,
|
||||
// required String? currentCondition,
|
||||
// required String selectCode,
|
||||
// AllDevicesModel? device,
|
||||
// required String operationName,
|
||||
// DeviceFunctionData? selectedFunctionData,
|
||||
// bool? removeComparators,
|
||||
// }) {
|
||||
// return Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// if (removeComparators != true)
|
||||
// _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,
|
||||
// /// 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 = ["<", "==", ">"];
|
||||
// // Function(String) onConditionChanged,
|
||||
// ) {
|
||||
// final conditions = ["<", "==", ">"];
|
||||
|
||||
return ToggleButtons(
|
||||
onPressed: (int index) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value ?? selectCode == 'temp_set'
|
||||
? 200
|
||||
: -100,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
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: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
// return ToggleButtons(
|
||||
// onPressed: (int index) {
|
||||
// context.read<FunctionBloc>().add(
|
||||
// AddFunction(
|
||||
// functionData: DeviceFunctionData(
|
||||
// entityId: device?.uuid ?? '',
|
||||
// functionCode: selectCode,
|
||||
// operationName: operationName,
|
||||
// condition: conditions[index],
|
||||
// value: selectedFunctionData?.value ?? selectCode == 'temp_set'
|
||||
// ? 200
|
||||
// : -100,
|
||||
// valueDescription: selectedFunctionData?.valueDescription,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// 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:
|
||||
// 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,
|
||||
) {
|
||||
final initialVal = selectCode == 'temp_set' ? 200 : -100;
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
'${(initialValue ?? initialVal) / 10}°C',
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
// /// Build temperature display for AC functions dialog
|
||||
// static Widget _buildTemperatureDisplay(
|
||||
// BuildContext context,
|
||||
// dynamic initialValue,
|
||||
// AllDevicesModel? device,
|
||||
// String operationName,
|
||||
// DeviceFunctionData? selectedFunctionData,
|
||||
// String selectCode,
|
||||
// ) {
|
||||
// final initialVal = selectCode == 'temp_set' ? 200 : -100;
|
||||
// return Container(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
// decoration: BoxDecoration(
|
||||
// color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||
// borderRadius: BorderRadius.circular(10),
|
||||
// ),
|
||||
// child: Text(
|
||||
// '${(initialValue ?? initialVal) / 10}°C',
|
||||
// style: context.textTheme.headlineMedium!.copyWith(
|
||||
// color: ColorsManager.primaryColorWithOpacity,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
static Widget _buildTemperatureSlider(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
) {
|
||||
return Slider(
|
||||
value: initialValue is int ? initialValue.toDouble() : 200.0,
|
||||
min: selectCode == 'temp_current' ? -100 : 200,
|
||||
max: selectCode == 'temp_current' ? 900 : 300,
|
||||
divisions: 10,
|
||||
label: '${((initialValue ?? 160) / 10).toInt()}°C',
|
||||
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 _buildTemperatureSlider(
|
||||
// BuildContext context,
|
||||
// dynamic initialValue,
|
||||
// AllDevicesModel? device,
|
||||
// String operationName,
|
||||
// DeviceFunctionData? selectedFunctionData,
|
||||
// String selectCode,
|
||||
// ) {
|
||||
// return Slider(
|
||||
// value: initialValue is int ? initialValue.toDouble() : 200.0,
|
||||
// min: selectCode == 'temp_current' ? -100 : 200,
|
||||
// max: selectCode == 'temp_current' ? 900 : 300,
|
||||
// divisions: 10,
|
||||
// label: '${((initialValue ?? 160) / 10).toInt()}°C',
|
||||
// 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({
|
||||
required BuildContext context,
|
||||
@ -414,7 +446,9 @@ class ACHelper {
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Icon(
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
isSelected
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
size: 24,
|
||||
color: isSelected
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
@ -430,7 +464,8 @@ class ACHelper {
|
||||
operationName: operationName,
|
||||
value: value.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
valueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -41,7 +41,8 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_cpsFunctions = widget.functions.whereType<CpsFunctions>().where((function) {
|
||||
_cpsFunctions =
|
||||
widget.functions.whereType<CpsFunctions>().where((function) {
|
||||
if (widget.dialogType == 'THEN') {
|
||||
return function.type == 'THEN' || function.type == 'BOTH';
|
||||
}
|
||||
@ -149,6 +150,7 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
||||
device: widget.device,
|
||||
)
|
||||
: CpsDialogSliderSelector(
|
||||
step: selectedCpsFunctions.step!,
|
||||
operations: operations,
|
||||
selectedFunction: selectedFunction ?? '',
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
|
@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
|
||||
@ -16,6 +17,7 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
required this.dialogType,
|
||||
required this.step,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@ -26,13 +28,16 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final String dialogType;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliderValueSelector(
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: false,
|
||||
currentCondition: selectedFunctionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode),
|
||||
sliderRange:
|
||||
CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode),
|
||||
displayedValue: CpsSliderHelpers.displayText(
|
||||
value: selectedFunctionData.value,
|
||||
functionCode: selectedFunctionData.functionCode,
|
||||
@ -50,7 +55,7 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
||||
onTextChanged: (value) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
@ -64,6 +69,7 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
||||
dividendOfRange: CpsSliderHelpers.dividendOfRange(
|
||||
selectedFunctionData.functionCode,
|
||||
),
|
||||
stepIncreaseAmount: step,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -34,30 +34,33 @@ class CpsFunctionsList extends StatelessWidget {
|
||||
itemBuilder: (context, index) {
|
||||
final function = cpsFunctions[index];
|
||||
return RoutineDialogFunctionListTile(
|
||||
iconPath: function.icon,
|
||||
operationName: function.operationName,
|
||||
onTap: () => RoutineTapFunctionHelper.onTapFunction(
|
||||
context,
|
||||
functionCode: function.code,
|
||||
functionOperationName: function.operationName,
|
||||
functionValueDescription: selectedFunctionData?.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'static_max_dis',
|
||||
'presence_reference',
|
||||
'moving_reference',
|
||||
'perceptual_boundary',
|
||||
'moving_boundary',
|
||||
'moving_rigger_time',
|
||||
'moving_static_time',
|
||||
'none_body_time',
|
||||
'moving_max_dis',
|
||||
'moving_range',
|
||||
'presence_range',
|
||||
if (dialogType == "IF") 'sensitivity',
|
||||
],
|
||||
),
|
||||
);
|
||||
iconPath: function.icon,
|
||||
operationName: function.operationName,
|
||||
onTap: () {
|
||||
RoutineTapFunctionHelper.onTapFunction(
|
||||
context,
|
||||
step: function.step,
|
||||
functionCode: function.code,
|
||||
functionOperationName: function.operationName,
|
||||
functionValueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'static_max_dis',
|
||||
'presence_reference',
|
||||
'moving_reference',
|
||||
'perceptual_boundary',
|
||||
'moving_boundary',
|
||||
'moving_rigger_time',
|
||||
'moving_static_time',
|
||||
'none_body_time',
|
||||
'moving_max_dis',
|
||||
'moving_range',
|
||||
'presence_range',
|
||||
if (dialogType == "IF") 'sensitivity',
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/flush/flush_operational_value.dart';
|
||||
|
||||
@ -21,22 +22,20 @@ class FlushOperationalValuesList extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: values.length,
|
||||
itemBuilder: (context, index) =>
|
||||
_buildValueItem(context, values[index]),
|
||||
);
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: values.length,
|
||||
itemBuilder: (context, index) => _buildValueItem(context, values[index]),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Widget _buildValueItem(BuildContext context, FlushOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SvgPicture.asset(value.icon, width: 25, height: 25),
|
||||
Expanded(child: _buildValueDescription(value)),
|
||||
_buildValueRadio(context, value),
|
||||
],
|
||||
@ -44,9 +43,6 @@ class FlushOperationalValuesList extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Widget _buildValueDescription(FlushOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
@ -60,6 +56,4 @@ class FlushOperationalValuesList extends StatelessWidget {
|
||||
groupValue: selectedValue,
|
||||
onChanged: (_) => onSelect(value));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/flush/flush_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
|
||||
@ -66,7 +67,8 @@ class FlushValueSelectorWidget extends StatelessWidget {
|
||||
if (isDistanceDetection) {
|
||||
initialValue = initialValue / 100;
|
||||
}
|
||||
return SliderValueSelector(
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: true,
|
||||
currentCondition: functionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: sliderRange,
|
||||
@ -83,7 +85,7 @@ class FlushValueSelectorWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) {
|
||||
onTextChanged: (value) {
|
||||
final roundedValue = _roundToStep(value, stepSize);
|
||||
final finalValue =
|
||||
isDistanceDetection ? (roundedValue * 100).toInt() : roundedValue;
|
||||
@ -102,6 +104,7 @@ class FlushValueSelectorWidget extends StatelessWidget {
|
||||
},
|
||||
unit: _unit,
|
||||
dividendOfRange: stepSize,
|
||||
stepIncreaseAmount: stepSize,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ abstract final class RoutineTapFunctionHelper {
|
||||
|
||||
static void onTapFunction(
|
||||
BuildContext context, {
|
||||
double? step,
|
||||
required String functionCode,
|
||||
required String functionOperationName,
|
||||
required String? functionValueDescription,
|
||||
|
@ -4,11 +4,11 @@ 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/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||
@ -87,14 +87,15 @@ class OneGangSwitchHelper {
|
||||
size: 16,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () =>
|
||||
RoutineTapFunctionHelper.onTapFunction(
|
||||
onTap: () => RoutineTapFunctionHelper
|
||||
.onTapFunction(
|
||||
context,
|
||||
functionCode: function.code,
|
||||
functionOperationName:
|
||||
function.operationName,
|
||||
functionValueDescription:
|
||||
selectedFunctionData.valueDescription,
|
||||
selectedFunctionData
|
||||
.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'countdown_1',
|
||||
@ -108,14 +109,16 @@ class OneGangSwitchHelper {
|
||||
if (selectedFunction != null)
|
||||
Expanded(
|
||||
child: _buildValueSelector(
|
||||
context: context,
|
||||
selectedFunction: selectedFunction,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
acFunctions: oneGangFunctions,
|
||||
device: device,
|
||||
operationName: selectedOperationName ?? '',
|
||||
removeComparetors: removeComparetors,
|
||||
),
|
||||
context: context,
|
||||
selectedFunction: selectedFunction,
|
||||
selectedFunctionData:
|
||||
selectedFunctionData,
|
||||
acFunctions: oneGangFunctions,
|
||||
device: device,
|
||||
operationName:
|
||||
selectedOperationName ?? '',
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -172,6 +175,7 @@ class OneGangSwitchHelper {
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
required bool removeComparetors,
|
||||
required String dialogType,
|
||||
}) {
|
||||
if (selectedFunction == 'countdown_1') {
|
||||
final initialValue = selectedFunctionData?.value ?? 0;
|
||||
@ -184,6 +188,7 @@ class OneGangSwitchHelper {
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType,
|
||||
);
|
||||
}
|
||||
final selectedFn = acFunctions.firstWhere(
|
||||
@ -216,93 +221,18 @@ class OneGangSwitchHelper {
|
||||
required String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
required bool removeComparetors,
|
||||
String? dialogType,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (removeComparetors != true)
|
||||
_buildConditionToggle(
|
||||
context,
|
||||
currentCondition,
|
||||
selectCode,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownDisplay(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
selectedFunctionData, selectCode, dialogType!),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
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: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Build temperature display for AC functions dialog
|
||||
static Widget _buildCountDownDisplay(
|
||||
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),
|
||||
),
|
||||
child: Text(
|
||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildCountDownSlider(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
@ -310,38 +240,47 @@ class OneGangSwitchHelper {
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
String dialogType,
|
||||
) {
|
||||
const twelveHoursInSeconds = 43200.0;
|
||||
final operationalValues = SwitchOperationalValue(
|
||||
icon: '',
|
||||
description: "sec",
|
||||
value: 0.0,
|
||||
minValue: 0,
|
||||
maxValue: twelveHoursInSeconds,
|
||||
stepValue: 1,
|
||||
);
|
||||
return Slider(
|
||||
value: (initialValue ?? 0).toDouble(),
|
||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||
divisions:
|
||||
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||
(operationalValues.stepValue ?? 1))
|
||||
.round(),
|
||||
onChanged: (value) {
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: false,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: (0, 43200),
|
||||
displayedValue: (initialValue ?? 0).toString(),
|
||||
initialValue: (initialValue ?? 0).toString(),
|
||||
onConditionChanged: (condition) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
condition: condition,
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onTextChanged: (value) {
|
||||
final roundedValue = value.round();
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: roundedValue,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
unit: 'sec',
|
||||
dividendOfRange: 1,
|
||||
stepIncreaseAmount: 1,
|
||||
);
|
||||
}
|
||||
|
||||
@ -377,7 +316,9 @@ class OneGangSwitchHelper {
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Icon(
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
isSelected
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
size: 24,
|
||||
color: isSelected
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
@ -393,7 +334,8 @@ class OneGangSwitchHelper {
|
||||
operationName: operationName,
|
||||
value: value.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
valueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -4,10 +4,10 @@ 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/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||
@ -86,20 +86,21 @@ class ThreeGangSwitchHelper {
|
||||
size: 16,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () =>
|
||||
RoutineTapFunctionHelper.onTapFunction(
|
||||
onTap: () => RoutineTapFunctionHelper
|
||||
.onTapFunction(
|
||||
context,
|
||||
functionCode: function.code,
|
||||
functionOperationName:
|
||||
function.operationName,
|
||||
functionValueDescription:
|
||||
selectedFunctionData.valueDescription,
|
||||
selectedFunctionData
|
||||
.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'countdown_1',
|
||||
'countdown_2',
|
||||
'countdown_3',
|
||||
],
|
||||
codesToAddIntoFunctionsWithDefaultValue:
|
||||
function.code
|
||||
.startsWith('countdown')
|
||||
? [function.code]
|
||||
: [],
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -109,14 +110,16 @@ class ThreeGangSwitchHelper {
|
||||
if (selectedFunction != null)
|
||||
Expanded(
|
||||
child: _buildValueSelector(
|
||||
context: context,
|
||||
selectedFunction: selectedFunction,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
switchFunctions: switchFunctions,
|
||||
device: device,
|
||||
operationName: selectedOperationName ?? '',
|
||||
removeComparetors: removeComparetors,
|
||||
),
|
||||
context: context,
|
||||
selectedFunction: selectedFunction,
|
||||
selectedFunctionData:
|
||||
selectedFunctionData,
|
||||
switchFunctions: switchFunctions,
|
||||
device: device,
|
||||
operationName:
|
||||
selectedOperationName ?? '',
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -133,14 +136,6 @@ class ThreeGangSwitchHelper {
|
||||
onConfirm: state.addedFunctions.isNotEmpty
|
||||
? () {
|
||||
/// add the functions to the routine bloc
|
||||
// for (var function in state.addedFunctions) {
|
||||
// context.read<RoutineBloc>().add(
|
||||
// AddFunctionToRoutine(
|
||||
// function,
|
||||
// uniqueCustomId,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
context.read<RoutineBloc>().add(
|
||||
AddFunctionToRoutine(
|
||||
state.addedFunctions,
|
||||
@ -173,24 +168,26 @@ class ThreeGangSwitchHelper {
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
required bool removeComparetors,
|
||||
required String dialogType,
|
||||
}) {
|
||||
if (selectedFunction == 'countdown_1' ||
|
||||
selectedFunction == 'countdown_2' ||
|
||||
selectedFunction == 'countdown_3') {
|
||||
final initialValue = selectedFunctionData?.value ?? 0;
|
||||
return _buildTemperatureSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparetors: removeComparetors,
|
||||
);
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType);
|
||||
}
|
||||
|
||||
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final selectedFn =
|
||||
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
return _buildOperationalValuesList(
|
||||
@ -213,93 +210,18 @@ class ThreeGangSwitchHelper {
|
||||
required String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
bool? removeComparetors,
|
||||
required String dialogType,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (removeComparetors != true)
|
||||
_buildConditionToggle(
|
||||
context,
|
||||
currentCondition,
|
||||
selectCode,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownDisplay(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
selectedFunctionData, selectCode, dialogType),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
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: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Build temperature display for AC functions dialog
|
||||
static Widget _buildCountDownDisplay(
|
||||
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),
|
||||
),
|
||||
child: Text(
|
||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildCountDownSlider(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
@ -307,38 +229,47 @@ class ThreeGangSwitchHelper {
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
String dialogType,
|
||||
) {
|
||||
const twelveHoursInSeconds = 43200.0;
|
||||
final operationalValues = SwitchOperationalValue(
|
||||
icon: '',
|
||||
description: "sec",
|
||||
value: 0.0,
|
||||
minValue: 0,
|
||||
maxValue: twelveHoursInSeconds,
|
||||
stepValue: 1,
|
||||
);
|
||||
return Slider(
|
||||
value: (initialValue ?? 0).toDouble(),
|
||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||
divisions:
|
||||
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||
(operationalValues.stepValue ?? 1))
|
||||
.round(),
|
||||
onChanged: (value) {
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: true,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: (0, 43200),
|
||||
displayedValue: (initialValue ?? 0).toString(),
|
||||
initialValue: (initialValue ?? 0).toString(),
|
||||
onConditionChanged: (condition) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
condition: condition,
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onTextChanged: (value) {
|
||||
final roundedValue = value.round();
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: roundedValue,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
unit: 'sec',
|
||||
dividendOfRange: 1,
|
||||
stepIncreaseAmount: 1,
|
||||
);
|
||||
}
|
||||
|
||||
@ -374,7 +305,9 @@ class ThreeGangSwitchHelper {
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Icon(
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
isSelected
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
size: 24,
|
||||
color: isSelected
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
@ -390,7 +323,8 @@ class ThreeGangSwitchHelper {
|
||||
operationName: operationName,
|
||||
value: value.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
valueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||
@ -86,14 +87,15 @@ class TwoGangSwitchHelper {
|
||||
size: 16,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () =>
|
||||
RoutineTapFunctionHelper.onTapFunction(
|
||||
onTap: () => RoutineTapFunctionHelper
|
||||
.onTapFunction(
|
||||
context,
|
||||
functionCode: function.code,
|
||||
functionOperationName:
|
||||
function.operationName,
|
||||
functionValueDescription:
|
||||
selectedFunctionData.valueDescription,
|
||||
selectedFunctionData
|
||||
.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'countdown_1',
|
||||
@ -115,6 +117,7 @@ class TwoGangSwitchHelper {
|
||||
device: device,
|
||||
operationName: selectedOperationName ?? '',
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -172,22 +175,25 @@ class TwoGangSwitchHelper {
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
required bool removeComparetors,
|
||||
required String dialogType,
|
||||
}) {
|
||||
if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') {
|
||||
if (selectedFunction == 'countdown_1' ||
|
||||
selectedFunction == 'countdown_2') {
|
||||
final initialValue = selectedFunctionData?.value ?? 0;
|
||||
return _buildTemperatureSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparetors: removeComparetors,
|
||||
);
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType);
|
||||
}
|
||||
|
||||
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final selectedFn =
|
||||
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
return _buildOperationalValuesList(
|
||||
@ -210,25 +216,13 @@ class TwoGangSwitchHelper {
|
||||
required String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
bool? removeComparetors,
|
||||
String? dialogType,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (removeComparetors != true)
|
||||
_buildConditionToggle(
|
||||
context,
|
||||
currentCondition,
|
||||
selectCode,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownDisplay(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
selectedFunctionData, selectCode, dialogType!),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -269,7 +263,8 @@ class TwoGangSwitchHelper {
|
||||
minHeight: 40.0,
|
||||
minWidth: 40.0,
|
||||
),
|
||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
isSelected:
|
||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
@ -304,38 +299,48 @@ class TwoGangSwitchHelper {
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
String dialogType,
|
||||
) {
|
||||
const twelveHoursInSeconds = 43200.0;
|
||||
final operationalValues = SwitchOperationalValue(
|
||||
icon: '',
|
||||
description: "sec",
|
||||
value: 0.0,
|
||||
minValue: 0,
|
||||
maxValue: twelveHoursInSeconds,
|
||||
stepValue: 1,
|
||||
);
|
||||
return Slider(
|
||||
value: (initialValue ?? 0).toDouble(),
|
||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||
divisions:
|
||||
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||
(operationalValues.stepValue ?? 1))
|
||||
.round(),
|
||||
onChanged: (value) {
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: true,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: (0, 43200),
|
||||
displayedValue: (initialValue ?? 0).toString(),
|
||||
initialValue: (initialValue ?? 0).toString(),
|
||||
onConditionChanged: (condition) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
condition: condition,
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onTextChanged: (value) {
|
||||
final roundedValue =
|
||||
value.round(); // Round to nearest integer (stepSize 1)
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: roundedValue,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
unit: 'sec',
|
||||
dividendOfRange: 1,
|
||||
stepIncreaseAmount: 1,
|
||||
);
|
||||
}
|
||||
|
||||
@ -371,7 +376,9 @@ class TwoGangSwitchHelper {
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Icon(
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
isSelected
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
size: 24,
|
||||
color: isSelected
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
@ -387,7 +394,8 @@ class TwoGangSwitchHelper {
|
||||
operationName: operationName,
|
||||
value: value.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
valueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -4,8 +4,8 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
|
||||
class WpsValueSelectorWidget extends StatelessWidget {
|
||||
final String selectedFunction;
|
||||
@ -27,11 +27,13 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedFn = wpsFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final selectedFn =
|
||||
wpsFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
if (_isSliderFunction(selectedFunction)) {
|
||||
return SliderValueSelector(
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: false,
|
||||
currentCondition: functionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: sliderRange,
|
||||
@ -48,7 +50,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
||||
onTextChanged: (value) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
@ -61,6 +63,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
||||
),
|
||||
unit: _unit,
|
||||
dividendOfRange: 1,
|
||||
stepIncreaseAmount: _steps,
|
||||
);
|
||||
}
|
||||
|
||||
@ -99,4 +102,10 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
||||
'illuminance_value' => 'Lux',
|
||||
_ => '',
|
||||
};
|
||||
double get _steps => switch (functionData.functionCode) {
|
||||
'presence_time' => 1,
|
||||
'dis_current' => 1,
|
||||
'illuminance_value' => 1,
|
||||
_ => 1,
|
||||
};
|
||||
}
|
||||
|
@ -176,6 +176,7 @@ class _WaterHeaterDialogRoutinesState extends State<WaterHeaterDialogRoutines> {
|
||||
functionData: functionData,
|
||||
whFunctions: _waterHeaterFunctions,
|
||||
device: widget.device,
|
||||
dialogType: widget.dialogType,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -2,25 +2,24 @@ 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/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/water_heater/water_heater_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/water_heater/water_heater_operational_values_list.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData functionData;
|
||||
final List<WaterHeaterFunctions> whFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
|
||||
const WaterHeaterValueSelectorWidget({
|
||||
required this.selectedFunction,
|
||||
required this.functionData,
|
||||
required this.whFunctions,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@ -39,22 +38,6 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildConditionToggle(
|
||||
context,
|
||||
functionData.condition,
|
||||
selectedFunction,
|
||||
device,
|
||||
selectedFn.operationName,
|
||||
functionData,
|
||||
),
|
||||
_buildCountDownDisplay(
|
||||
context,
|
||||
functionData.value,
|
||||
device,
|
||||
selectedFn.operationName,
|
||||
functionData,
|
||||
selectedFunction,
|
||||
),
|
||||
_buildCountDownSlider(
|
||||
context,
|
||||
functionData.value,
|
||||
@ -62,6 +45,7 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
||||
selectedFn.operationName,
|
||||
functionData,
|
||||
selectedFunction,
|
||||
dialogType
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
@ -90,28 +74,6 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildCountDownDisplay(
|
||||
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),
|
||||
),
|
||||
child: Text(
|
||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildCountDownSlider(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
@ -119,78 +81,47 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
String dialogType,
|
||||
) {
|
||||
const twelveHoursInSeconds = 43200.0;
|
||||
final operationalValues = SwitchOperationalValue(
|
||||
icon: '',
|
||||
description: "sec",
|
||||
value: 0.0,
|
||||
minValue: 0,
|
||||
maxValue: twelveHoursInSeconds,
|
||||
stepValue: 1,
|
||||
);
|
||||
return Slider(
|
||||
value: (initialValue ?? 0).toDouble(),
|
||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||
divisions: (((operationalValues.maxValue ?? 0) -
|
||||
(operationalValues.minValue ?? 0)) /
|
||||
(operationalValues.stepValue ?? 1))
|
||||
.round(),
|
||||
onChanged: (value) {
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: false,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: (0, 43200),
|
||||
displayedValue: (initialValue ?? 0).toString(),
|
||||
initialValue: (initialValue ?? 0).toString(),
|
||||
onConditionChanged: (condition) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
value: condition,
|
||||
condition: condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onTextChanged: (value) {
|
||||
final roundedValue = value.round();
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: roundedValue,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildConditionToggle(
|
||||
BuildContext context,
|
||||
String? currentCondition,
|
||||
String selectCode,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
) {
|
||||
final conditions = ["<", "==", ">"];
|
||||
|
||||
return ToggleButtons(
|
||||
onPressed: (int index) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
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:
|
||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
unit: 'sec',
|
||||
dividendOfRange: 1,
|
||||
stepIncreaseAmount: 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -41,13 +41,24 @@ class _UserDropdownMenuState extends State<UserDropdownMenu> {
|
||||
_isDropdownOpen = false;
|
||||
});
|
||||
},
|
||||
child: Transform.rotate(
|
||||
angle: _isDropdownOpen ? -1.5708 : 1.5708,
|
||||
child: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 12),
|
||||
if (widget.user != null)
|
||||
Text(
|
||||
'${widget.user!.firstName} ${widget.user!.lastName}',
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Transform.rotate(
|
||||
angle: _isDropdownOpen ? -1.5708 : 1.5708,
|
||||
child: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -92,13 +92,6 @@ class DesktopAppBar extends StatelessWidget {
|
||||
if (rightBody != null) rightBody!,
|
||||
const SizedBox(width: 24),
|
||||
_UserAvatar(),
|
||||
const SizedBox(width: 12),
|
||||
if (user != null)
|
||||
Text(
|
||||
'${user.firstName} ${user.lastName}',
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
UserDropdownMenu(user: user),
|
||||
],
|
||||
);
|
||||
@ -146,14 +139,6 @@ class TabletAppBar extends StatelessWidget {
|
||||
if (rightBody != null) rightBody!,
|
||||
const SizedBox(width: 16),
|
||||
_UserAvatar(),
|
||||
if (user != null) ...[
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'${user.firstName} ${user.lastName}',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: 14),
|
||||
),
|
||||
],
|
||||
UserDropdownMenu(user: user),
|
||||
],
|
||||
);
|
||||
@ -215,14 +200,6 @@ class MobileAppBar extends StatelessWidget {
|
||||
return Row(
|
||||
children: [
|
||||
_UserAvatar(),
|
||||
if (user != null) ...[
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'${user.firstName} ${user.lastName}',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: 14),
|
||||
),
|
||||
],
|
||||
UserDropdownMenu(user: user),
|
||||
],
|
||||
);
|
||||
|
Reference in New Issue
Block a user