mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
Merged with dev
This commit is contained in:
@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ac_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart';
|
||||
@ -98,6 +99,15 @@ class DeviceDialogHelper {
|
||||
deviceSelectedFunctions: deviceSelectedFunctions,
|
||||
uniqueCustomId: data['uniqueCustomId'],
|
||||
removeComparetors: removeComparetors);
|
||||
case 'CPS':
|
||||
return CeilingSensorHelper.showCeilingSensorDialog(
|
||||
context: context,
|
||||
functions: functions,
|
||||
device: data['device'],
|
||||
deviceSelectedFunctions: deviceSelectedFunctions,
|
||||
uniqueCustomId: data['uniqueCustomId'],
|
||||
dialogType: dialogType,
|
||||
);
|
||||
case 'GW':
|
||||
return GatewayHelper.showGatewayFunctionsDialog(
|
||||
context: context,
|
||||
|
889
lib/pages/routines/models/ceiling_presence_sensor_functions.dart
Normal file
889
lib/pages/routines/models/ceiling_presence_sensor_functions.dart
Normal file
@ -0,0 +1,889 @@
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class CpsOperationalValue {
|
||||
final String icon;
|
||||
final String description;
|
||||
final dynamic value;
|
||||
|
||||
CpsOperationalValue({
|
||||
required this.icon,
|
||||
required this.description,
|
||||
required this.value,
|
||||
});
|
||||
}
|
||||
|
||||
abstract class CpsFunctions extends DeviceFunction<CpsOperationalValue> {
|
||||
CpsFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.code,
|
||||
required super.operationName,
|
||||
required super.icon,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
final String type;
|
||||
|
||||
List<CpsOperationalValue> getOperationalValues();
|
||||
}
|
||||
|
||||
final class CpsRadarSwitchFunction extends CpsFunctions {
|
||||
CpsRadarSwitchFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'radar_switch',
|
||||
operationName: 'Radar Switch',
|
||||
icon: Assets.acPower,
|
||||
);
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() => [
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsAcPower,
|
||||
description: "ON",
|
||||
value: true,
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsAcPowerOFF,
|
||||
description: "OFF",
|
||||
value: false,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
final class CpsSpatialParameterSwitchFunction extends CpsFunctions {
|
||||
CpsSpatialParameterSwitchFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'space_para_switch',
|
||||
operationName: 'Spatial Parameter Switch',
|
||||
icon: Assets.acPower,
|
||||
);
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() => [
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsAcPower,
|
||||
description: "ON",
|
||||
value: true,
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsAcPowerOFF,
|
||||
description: "OFF",
|
||||
value: false,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
final class CpsSensitivityFunction extends CpsFunctions {
|
||||
CpsSensitivityFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 1,
|
||||
max = 10,
|
||||
step = 1,
|
||||
super(
|
||||
code: 'sensitivity',
|
||||
operationName: 'Sensitivity',
|
||||
icon: Assets.sensitivity,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
static const _images = <String>[
|
||||
Assets.sensitivityFeature1,
|
||||
Assets.sensitivityFeature1,
|
||||
Assets.sensitivityFeature2,
|
||||
Assets.sensitivityFeature3,
|
||||
Assets.sensitivityFeature4,
|
||||
Assets.sensitivityFeature5,
|
||||
Assets.sensitivityFeature6,
|
||||
Assets.sensitivityFeature7,
|
||||
Assets.sensitivityFeature8,
|
||||
Assets.sensitivityFeature9,
|
||||
Assets.sensitivityFeature9,
|
||||
];
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final values = <CpsOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
values.add(
|
||||
CpsOperationalValue(
|
||||
icon: _images[value],
|
||||
description: '$value',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMovingSpeedFunction extends CpsFunctions {
|
||||
CpsMovingSpeedFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
max = 32,
|
||||
step = 1,
|
||||
super(
|
||||
code: 'moving_speed',
|
||||
operationName: 'Moving Speed',
|
||||
icon: Assets.speedoMeter,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return List.generate(
|
||||
(max - min) ~/ step + 1,
|
||||
(index) => CpsOperationalValue(
|
||||
icon: Assets.speedoMeter,
|
||||
description: '${min + (index * step)}',
|
||||
value: min + (index * step),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsSpatialStaticValueFunction extends CpsFunctions {
|
||||
CpsSpatialStaticValueFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
max = 255,
|
||||
step = 1,
|
||||
super(
|
||||
code: 'space_static_val',
|
||||
operationName: 'Spacial Static Value',
|
||||
icon: Assets.spatialStaticValue,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return List.generate(
|
||||
(max - min) ~/ step + 1,
|
||||
(index) => CpsOperationalValue(
|
||||
icon: Assets.spatialStaticValue,
|
||||
description: '${min + (index * step)}',
|
||||
value: min + (index * step),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsSpatialMotionValueFunction extends CpsFunctions {
|
||||
CpsSpatialMotionValueFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
max = 255,
|
||||
step = 1,
|
||||
super(
|
||||
code: 'space_move_val',
|
||||
operationName: 'Spatial Motion Value',
|
||||
icon: Assets.spatialMotionValue,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return List.generate(
|
||||
(max - min) ~/ step + 1,
|
||||
(index) => CpsOperationalValue(
|
||||
icon: Assets.spatialMotionValue,
|
||||
description: '${min + (index * step)}',
|
||||
value: min + (index * step),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions {
|
||||
CpsMaxDistanceOfDetectionFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 10.0,
|
||||
step = 0.5,
|
||||
super(
|
||||
code: 'moving_max_dis',
|
||||
operationName: 'Maximum Distance Of Detection',
|
||||
icon: Assets.currentDistanceIcon,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.currentDistanceIcon,
|
||||
description: '${value.toStringAsFixed(1)} M',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions {
|
||||
CpsMaxDistanceOfStaticDetectionFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 10.0,
|
||||
step = 0.5,
|
||||
super(
|
||||
code: 'static_max_dis',
|
||||
operationName: 'Maximum Distance Of Static Detection',
|
||||
icon: Assets.currentDistanceIcon,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.currentDistanceIcon,
|
||||
description: '${value.toStringAsFixed(1)} M',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsDetectionRangeFunction extends CpsFunctions {
|
||||
CpsDetectionRangeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 25.5,
|
||||
step = 0.1,
|
||||
super(
|
||||
code: 'moving_range',
|
||||
operationName: 'Detection Range',
|
||||
icon: Assets.farDetection,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.farDetection,
|
||||
description: '${value.toStringAsFixed(1)} M',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions {
|
||||
CpsDistanceOfMovingObjectsFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 25.5,
|
||||
step = 0.1,
|
||||
super(
|
||||
code: 'presence_range',
|
||||
operationName: 'Distance Of Moving Objects',
|
||||
icon: Assets.currentDistanceIcon,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.currentDistanceIcon,
|
||||
description: '${value.toStringAsFixed(1)} M',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions {
|
||||
CpsPresenceJudgementThrsholdFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
max = 255,
|
||||
step = 5,
|
||||
super(
|
||||
code: 'presence_reference',
|
||||
operationName: 'Presence Judgement Threshold',
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return List.generate(
|
||||
(max - min) ~/ step + 1,
|
||||
(index) => CpsOperationalValue(
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
description: '${min + (index * step)}',
|
||||
value: min + (index * step),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions {
|
||||
CpsMotionAmplitudeTriggerThresholdFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
max = 255,
|
||||
step = 5,
|
||||
super(
|
||||
code: 'moving_reference',
|
||||
operationName: 'Motion Amplitude Trigger Threshold',
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return List.generate(
|
||||
(max - min) ~/ step + 1,
|
||||
(index) => CpsOperationalValue(
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
description: '${min + (index * step)}',
|
||||
value: min + (index * step),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsPerpetualBoundaryFunction extends CpsFunctions {
|
||||
CpsPerpetualBoundaryFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.00,
|
||||
max = 5.00,
|
||||
step = 0.50,
|
||||
super(
|
||||
code: 'perceptual_boundary',
|
||||
operationName: 'Perpetual Boundary',
|
||||
icon: Assets.boundary,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.boundary,
|
||||
description: '${value.toStringAsFixed(1)}M',
|
||||
value: value + 1200,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMotionTriggerBoundaryFunction extends CpsFunctions {
|
||||
CpsMotionTriggerBoundaryFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 5.0,
|
||||
step = 0.5,
|
||||
super(
|
||||
code: 'moving_boundary',
|
||||
operationName: 'Motion Trigger Boundary',
|
||||
icon: Assets.motionMeter,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.motionMeter,
|
||||
description: '${value.toStringAsFixed(1)} M',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMotionTriggerTimeFunction extends CpsFunctions {
|
||||
CpsMotionTriggerTimeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 2.0,
|
||||
step = 0.1,
|
||||
super(
|
||||
code: 'moving_rigger_time',
|
||||
operationName: 'Motion Trigger Time',
|
||||
icon: Assets.motionMeter,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.motionMeter,
|
||||
description: '${value.toStringAsFixed(3)} sec',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMotionToStaticTimeFunction extends CpsFunctions {
|
||||
CpsMotionToStaticTimeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 50.0,
|
||||
step = 1.0,
|
||||
super(
|
||||
code: 'moving_static_time',
|
||||
operationName: 'Motion To Static Time',
|
||||
icon: Assets.motionMeter,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.motionMeter,
|
||||
description: '${value.toStringAsFixed(0)} sec',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions {
|
||||
CpsEnteringNoBodyStateTimeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 300.0,
|
||||
step = 5.0,
|
||||
super(
|
||||
code: 'none_body_time',
|
||||
operationName: 'Entering Nobody State Time',
|
||||
icon: Assets.motionMeter,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.motionMeter,
|
||||
description: '${value.toStringAsFixed(0)} sec',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsSelfTestResultFunctions extends CpsFunctions {
|
||||
CpsSelfTestResultFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'checking_result',
|
||||
operationName: 'Self-Test Result',
|
||||
icon: Assets.selfTestResult,
|
||||
);
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
description: 'Self Testing',
|
||||
icon: Assets.selfTestResult,
|
||||
value: 'check',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Self Testing Success',
|
||||
icon: Assets.selfTestingSuccess,
|
||||
value: 'check_success',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Self Testing Failure',
|
||||
icon: Assets.selfTestingFailure,
|
||||
value: 'check_failure',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Self Testing Timeout',
|
||||
icon: Assets.selfTestingTimeout,
|
||||
value: 'check_timeout',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Communication Fault',
|
||||
icon: Assets.communicationFault,
|
||||
value: 'communication_fault',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Radar Fault',
|
||||
icon: Assets.radarFault,
|
||||
value: 'radar_fault',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsNobodyTimeFunction extends CpsFunctions {
|
||||
CpsNobodyTimeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'nobody_time',
|
||||
operationName: 'Entering Nobody Time',
|
||||
icon: Assets.assetsNobodyTime,
|
||||
);
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: 'None',
|
||||
value: 'none',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '10sec',
|
||||
value: '10s',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '30sec',
|
||||
value: '30s',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '1min',
|
||||
value: '1min',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '2min',
|
||||
value: '2min',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '5min',
|
||||
value: '5min',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '10min',
|
||||
value: '10min',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '30min',
|
||||
value: '30min',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '1hour',
|
||||
value: '1hr',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMovementFunctions extends CpsFunctions {
|
||||
CpsMovementFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'body_movement',
|
||||
operationName: 'Movement',
|
||||
icon: Assets.motion,
|
||||
);
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
description: 'None',
|
||||
icon: Assets.nobodyTime,
|
||||
value: 'none',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Close To',
|
||||
icon: Assets.closeToMotion,
|
||||
value: 'close_to',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Far Away',
|
||||
icon: Assets.farAwayMotion,
|
||||
value: 'far_away',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsCustomModeFunction extends CpsFunctions {
|
||||
CpsCustomModeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'custom_mode',
|
||||
operationName: 'Custom Mode',
|
||||
icon: Assets.cpsCustomMode,
|
||||
);
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
icon: Assets.cpsMode1,
|
||||
description: 'Mode 1',
|
||||
value: 'mode1',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.cpsMode2,
|
||||
description: 'Mode 2',
|
||||
value: 'mode2',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.cpsMode3,
|
||||
description: 'Mode 3',
|
||||
value: 'mode3',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.cpsMode4,
|
||||
description: 'Mode 4',
|
||||
value: 'mode4',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsSpaceTypeFunctions extends CpsFunctions {
|
||||
CpsSpaceTypeFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'scene',
|
||||
operationName: 'Space Type',
|
||||
icon: Assets.spaceType,
|
||||
);
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
description: 'Office',
|
||||
icon: Assets.office,
|
||||
value: 'office',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Parlour',
|
||||
icon: Assets.parlour,
|
||||
value: 'parlour',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Bathroom',
|
||||
icon: Assets.bathroom,
|
||||
value: 'bathroom',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Bedroom',
|
||||
icon: Assets.bedroom,
|
||||
value: 'bedroom',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'DIY',
|
||||
icon: Assets.dyi,
|
||||
value: 'dyi',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class CpsPresenceStatusFunctions extends CpsFunctions {
|
||||
CpsPresenceStatusFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'presence_state',
|
||||
operationName: 'Presence Status',
|
||||
icon: Assets.presenceSensor,
|
||||
);
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: 'None',
|
||||
value: 'none',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.presenceState,
|
||||
description: 'Presence',
|
||||
value: 'presence',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.motion,
|
||||
description: 'Motion',
|
||||
value: 'motion',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsSportsParaFunction extends CpsFunctions {
|
||||
CpsSportsParaFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
super(
|
||||
code: 'sports_para',
|
||||
operationName: 'Sports Para',
|
||||
icon: Assets.sportsPara,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.motionMeter,
|
||||
description: value.toStringAsFixed(0),
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
33
lib/pages/routines/widgets/condition_toggle.dart
Normal file
33
lib/pages/routines/widgets/condition_toggle.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ConditionToggle extends StatelessWidget {
|
||||
final String? currentCondition;
|
||||
final void Function(String condition) onChanged;
|
||||
|
||||
const ConditionToggle({
|
||||
required this.onChanged,
|
||||
this.currentCondition,
|
||||
super.key,
|
||||
});
|
||||
|
||||
static const _conditions = ["<", "==", ">"];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ToggleButtons(
|
||||
onPressed: (index) => onChanged(_conditions[index]),
|
||||
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(),
|
||||
);
|
||||
}
|
||||
}
|
@ -8,12 +8,12 @@ class DialogFooter extends StatelessWidget {
|
||||
final int? dialogWidth;
|
||||
|
||||
const DialogFooter({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.onCancel,
|
||||
required this.onConfirm,
|
||||
required this.isConfirmEnabled,
|
||||
this.dialogWidth,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -28,21 +28,19 @@ class DialogFooter extends StatelessWidget {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildFooterButton(
|
||||
context,
|
||||
'Cancel',
|
||||
onCancel,
|
||||
),
|
||||
_buildFooterButton(
|
||||
context: context,
|
||||
text: 'Cancel',
|
||||
onTap: onCancel,
|
||||
),
|
||||
if (isConfirmEnabled) ...[
|
||||
Container(width: 1, height: 50, color: ColorsManager.greyColor),
|
||||
Expanded(
|
||||
child: _buildFooterButton(
|
||||
context,
|
||||
'Confirm',
|
||||
onConfirm,
|
||||
),
|
||||
_buildFooterButton(
|
||||
context: context,
|
||||
text: 'Confirm',
|
||||
onTap: onConfirm,
|
||||
textColor:
|
||||
isConfirmEnabled ? ColorsManager.primaryColorWithOpacity : Colors.red,
|
||||
),
|
||||
],
|
||||
],
|
||||
@ -50,24 +48,24 @@ class DialogFooter extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFooterButton(
|
||||
BuildContext context,
|
||||
String text,
|
||||
VoidCallback? onTap,
|
||||
) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
height: 50,
|
||||
child: Center(
|
||||
child: Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: text == 'Confirm'
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
Widget _buildFooterButton({
|
||||
required BuildContext context,
|
||||
required String text,
|
||||
required VoidCallback? onTap,
|
||||
Color? textColor,
|
||||
}) {
|
||||
return Expanded(
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: ColorsManager.primaryColorWithOpacity,
|
||||
disabledForegroundColor: ColorsManager.primaryColor,
|
||||
),
|
||||
onPressed: onTap,
|
||||
child: Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: textColor ?? ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
35
lib/pages/routines/widgets/function_slider.dart
Normal file
35
lib/pages/routines/widgets/function_slider.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FunctionSlider extends StatelessWidget {
|
||||
final dynamic initialValue;
|
||||
final (double min, double max) range;
|
||||
final void Function(double value) onChanged;
|
||||
final double dividendOfRange;
|
||||
|
||||
const FunctionSlider({
|
||||
required this.onChanged,
|
||||
required this.initialValue,
|
||||
required this.range,
|
||||
required this.dividendOfRange,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final (min, max) = range;
|
||||
final bool isValidRange = max > min;
|
||||
final double value = initialValue is int
|
||||
? (initialValue as int).toDouble()
|
||||
: (initialValue as double);
|
||||
|
||||
final int? divisions = isValidRange ? ((max - min) / dividendOfRange).round() : null;
|
||||
|
||||
return Slider(
|
||||
value: value.clamp(min, max),
|
||||
min: min,
|
||||
max: max,
|
||||
divisions: divisions,
|
||||
onChanged: isValidRange ? onChanged : null,
|
||||
);
|
||||
}
|
||||
}
|
@ -70,8 +70,9 @@ class IfContainer extends StatelessWidget {
|
||||
'1G',
|
||||
'2G',
|
||||
'3G',
|
||||
'WPS'
|
||||
'GW',
|
||||
'WPS',
|
||||
'GW',
|
||||
'CPS',
|
||||
].contains(state.ifItems[index]['productType'])) {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
@ -121,7 +122,7 @@ class IfContainer extends StatelessWidget {
|
||||
|
||||
if (result != null) {
|
||||
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW']
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS']
|
||||
.contains(mutableData['productType'])) {
|
||||
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class _RoutineDevicesState extends State<RoutineDevices> {
|
||||
context.read<RoutineBloc>().add(FetchDevicesInRoutine());
|
||||
}
|
||||
|
||||
static const _allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW'};
|
||||
static const _allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS'};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -0,0 +1,219 @@
|
||||
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/bloc/routine_bloc/routine_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/dialog_footer.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart';
|
||||
|
||||
class CeilingSensorDialog extends StatefulWidget {
|
||||
const CeilingSensorDialog({
|
||||
required this.uniqueCustomId,
|
||||
required this.functions,
|
||||
required this.deviceSelectedFunctions,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String? uniqueCustomId;
|
||||
final List<DeviceFunction> functions;
|
||||
final List<DeviceFunctionData> deviceSelectedFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
|
||||
@override
|
||||
State<CeilingSensorDialog> createState() => _CeilingSensorDialogState();
|
||||
}
|
||||
|
||||
class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
||||
late final List<CpsFunctions> _cpsFunctions;
|
||||
late final String _dialogHeaderText;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_cpsFunctions = widget.functions.whereType<CpsFunctions>().where((function) {
|
||||
if (widget.dialogType == 'THEN') {
|
||||
return function.type == 'THEN' || function.type == 'BOTH';
|
||||
}
|
||||
return function.type == 'IF' || function.type == 'BOTH';
|
||||
}).toList();
|
||||
|
||||
final isIfDialog = widget.dialogType == 'IF';
|
||||
_dialogHeaderText = isIfDialog ? 'Conditions' : 'Functions';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
|
||||
builder: (context, state) {
|
||||
final selectedFunction = state.selectedFunction;
|
||||
|
||||
return Container(
|
||||
width: selectedFunction != null ? 600 : 360,
|
||||
height: 450,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DialogHeader('Presence Sensor $_dialogHeaderText'),
|
||||
Expanded(child: _buildMainContent(context, state)),
|
||||
DialogFooter(
|
||||
onCancel: () => Navigator.pop(context),
|
||||
onConfirm: state.addedFunctions.isNotEmpty
|
||||
? () {
|
||||
final functions = _updateValuesForAddedFunctions(
|
||||
state.addedFunctions,
|
||||
);
|
||||
context.read<RoutineBloc>().add(
|
||||
AddFunctionToRoutine(
|
||||
functions,
|
||||
'${widget.uniqueCustomId}',
|
||||
),
|
||||
);
|
||||
|
||||
Navigator.pop(context, {
|
||||
'deviceId': widget.functions.first.deviceId,
|
||||
});
|
||||
}
|
||||
: null,
|
||||
isConfirmEnabled: selectedFunction != null,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMainContent(BuildContext context, FunctionBlocState state) {
|
||||
final selectedFunction = state.selectedFunction;
|
||||
final selectedOperationName = state.selectedOperationName;
|
||||
final selectedFunctionData = state.addedFunctions.firstWhere(
|
||||
(f) => f.functionCode == selectedFunction,
|
||||
orElse: () => DeviceFunctionData(
|
||||
entityId: '',
|
||||
functionCode: selectedFunction ?? '',
|
||||
operationName: '',
|
||||
value: null,
|
||||
),
|
||||
);
|
||||
final selectedCpsFunctions = _cpsFunctions.firstWhere(
|
||||
(f) => f.code == selectedFunction,
|
||||
orElse: () => CpsMovementFunctions(
|
||||
deviceId: '',
|
||||
deviceName: '',
|
||||
type: '',
|
||||
),
|
||||
);
|
||||
final operations = selectedCpsFunctions.getOperationalValues();
|
||||
final isSensitivityFunction = selectedFunction == 'sensitivity';
|
||||
final isToggleFunction = isSensitivityFunction
|
||||
? widget.dialogType == 'THEN'
|
||||
: CeilingSensorHelper.toggleCodes.contains(selectedFunction);
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
CpsFunctionsList(cpsFunctions: _cpsFunctions),
|
||||
if (state.selectedFunction != null)
|
||||
Expanded(
|
||||
child: isToggleFunction
|
||||
? CpsDialogValueSelector(
|
||||
operations: operations,
|
||||
selectedFunction: selectedFunction ?? '',
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
cpsFunctions: _cpsFunctions,
|
||||
operationName: selectedOperationName ?? '',
|
||||
device: widget.device,
|
||||
)
|
||||
: CpsDialogSliderSelector(
|
||||
operations: operations,
|
||||
selectedFunction: selectedFunction ?? '',
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
cpsFunctions: _cpsFunctions,
|
||||
operationName: selectedOperationName ?? '',
|
||||
device: widget.device,
|
||||
dialogType: widget.dialogType,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static const _mappableSteppedFunctions = <String>{
|
||||
'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',
|
||||
};
|
||||
|
||||
List<DeviceFunctionData> _updateValuesForAddedFunctions(
|
||||
List<DeviceFunctionData> addedFunctions,
|
||||
) {
|
||||
return addedFunctions.map((function) {
|
||||
final shouldMapValue = _mappableSteppedFunctions.contains(
|
||||
function.functionCode,
|
||||
);
|
||||
if (shouldMapValue) {
|
||||
final mappedValue = _mapSteppedValue(
|
||||
value: function.value,
|
||||
inputStep: CpsSliderHelpers.dividendOfRange(function.functionCode),
|
||||
inputRange: CpsSliderHelpers.sliderRange(function.functionCode),
|
||||
outputRange: CpsSliderHelpers.mappedRange(function.functionCode),
|
||||
);
|
||||
return DeviceFunctionData(
|
||||
value: mappedValue,
|
||||
entityId: function.entityId,
|
||||
functionCode: function.functionCode,
|
||||
operationName: function.operationName,
|
||||
condition: function.condition,
|
||||
actionExecutor: function.actionExecutor,
|
||||
valueDescription: function.valueDescription,
|
||||
);
|
||||
}
|
||||
return function;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
int _mapSteppedValue({
|
||||
required (double min, double max) inputRange,
|
||||
required double inputStep,
|
||||
required (double min, double max, double dividend) outputRange,
|
||||
required double value,
|
||||
}) {
|
||||
final (inputMin, inputMax) = inputRange;
|
||||
final (outputMin, outputMax, outputStep) = outputRange;
|
||||
|
||||
final clampedValue = value.clamp(inputMin, inputMax);
|
||||
|
||||
final stepsFromMin = ((clampedValue - inputMin) / inputStep).round();
|
||||
|
||||
final mappedValue = outputMin + (stepsFromMin * outputStep);
|
||||
|
||||
return mappedValue.clamp(outputMin, outputMax).round();
|
||||
}
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
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/models/ceiling_presence_sensor_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart';
|
||||
|
||||
abstract final class CeilingSensorHelper {
|
||||
const CeilingSensorHelper._();
|
||||
|
||||
static Future<Map<String, dynamic>?> showCeilingSensorDialog({
|
||||
required BuildContext context,
|
||||
required String? uniqueCustomId,
|
||||
required List<DeviceFunction> functions,
|
||||
required List<DeviceFunctionData> deviceSelectedFunctions,
|
||||
required AllDevicesModel? device,
|
||||
required String dialogType,
|
||||
}) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (context) => BlocProvider(
|
||||
create: (context) => FunctionBloc()
|
||||
..add(
|
||||
InitializeFunctions(deviceSelectedFunctions),
|
||||
),
|
||||
child: CeilingSensorDialog(
|
||||
uniqueCustomId: uniqueCustomId,
|
||||
functions: functions,
|
||||
deviceSelectedFunctions: deviceSelectedFunctions,
|
||||
device: device,
|
||||
dialogType: dialogType,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static List<DeviceFunction<CpsOperationalValue>> getCeilingSensorFunctions({
|
||||
required String uuid,
|
||||
required String name,
|
||||
}) {
|
||||
return [
|
||||
CpsRadarSwitchFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsSpatialParameterSwitchFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsSensitivityFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMovingSpeedFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsSpatialStaticValueFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsSpatialMotionValueFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsMaxDistanceOfDetectionFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMaxDistanceOfStaticDetectionFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsDetectionRangeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsDistanceOfMovingObjectsFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsPresenceJudgementThrsholdFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMotionAmplitudeTriggerThresholdFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsPerpetualBoundaryFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMotionTriggerBoundaryFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMotionTriggerTimeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMotionToStaticTimeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsEnteringNoBodyStateTimeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsSelfTestResultFunctions(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsNobodyTimeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMovementFunctions(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsCustomModeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsSpaceTypeFunctions(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsPresenceStatusFunctions(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsSportsParaFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
static const toggleCodes = <String>{
|
||||
'radar_switch',
|
||||
'space_para_switch',
|
||||
'checking_result',
|
||||
'nobody_time',
|
||||
'body_movement',
|
||||
'custom_mode',
|
||||
'scene',
|
||||
'presence_state',
|
||||
};
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
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/models/ceiling_presence_sensor_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.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';
|
||||
|
||||
class CpsDialogSliderSelector extends StatelessWidget {
|
||||
const CpsDialogSliderSelector({
|
||||
required this.operations,
|
||||
required this.selectedFunction,
|
||||
required this.selectedFunctionData,
|
||||
required this.cpsFunctions,
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
required this.dialogType,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<CpsOperationalValue> operations;
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData selectedFunctionData;
|
||||
final List<CpsFunctions> cpsFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final String dialogType;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliderValueSelector(
|
||||
currentCondition: selectedFunctionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode),
|
||||
displayedValue: CpsSliderHelpers.displayText(
|
||||
value: selectedFunctionData.value,
|
||||
functionCode: selectedFunctionData.functionCode,
|
||||
),
|
||||
initialValue: selectedFunctionData.value ?? 0,
|
||||
unit: CpsSliderHelpers.unit(selectedFunctionData.functionCode),
|
||||
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: operationName,
|
||||
condition: condition,
|
||||
value: selectedFunctionData.value,
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: operationName,
|
||||
value: double.parse(value.toStringAsFixed(2)),
|
||||
condition: selectedFunctionData.condition,
|
||||
),
|
||||
),
|
||||
),
|
||||
dividendOfRange: CpsSliderHelpers.dividendOfRange(
|
||||
selectedFunctionData.functionCode,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
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/models/ceiling_presence_sensor_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_selection_list_tile.dart';
|
||||
|
||||
class CpsDialogValueSelector extends StatelessWidget {
|
||||
const CpsDialogValueSelector({
|
||||
required this.operations,
|
||||
required this.selectedFunction,
|
||||
required this.selectedFunctionData,
|
||||
required this.cpsFunctions,
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<CpsOperationalValue> operations;
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData? selectedFunctionData;
|
||||
final List<CpsFunctions> cpsFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
itemCount: operations.length,
|
||||
itemBuilder: (context, index) {
|
||||
final operation = operations[index];
|
||||
final isSelected = selectedFunctionData?.value == operation.value;
|
||||
return RoutineDialogSelectionListTile(
|
||||
iconPath: operation.icon,
|
||||
description: operation.description,
|
||||
isSelected: isSelected,
|
||||
onTap: () {
|
||||
if (!isSelected) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: operationName,
|
||||
value: operation.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
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/widgets/routine_dialog_function_list_tile.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class CpsFunctionsList extends StatelessWidget {
|
||||
const CpsFunctionsList({required this.cpsFunctions, super.key});
|
||||
|
||||
final List<CpsFunctions> cpsFunctions;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 360,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: false,
|
||||
itemCount: cpsFunctions.length,
|
||||
separatorBuilder: (context, index) => const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 40.0),
|
||||
child: Divider(color: ColorsManager.dividerColor),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final function = cpsFunctions[index];
|
||||
return RoutineDialogFunctionListTile(
|
||||
iconPath: function.icon,
|
||||
operationName: function.operationName,
|
||||
onTap: () => context.read<FunctionBloc>().add(
|
||||
SelectFunction(
|
||||
functionCode: function.code,
|
||||
operationName: function.operationName,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
abstract final class CpsSliderHelpers {
|
||||
static (double min, double max, double step) mappedRange(String functionCode) {
|
||||
final (defaultMin, defaultMax) = sliderRange(functionCode);
|
||||
final defaultDivdidend = dividendOfRange(functionCode);
|
||||
return switch (functionCode) {
|
||||
'static_max_dis' => (0, 500, 50),
|
||||
'presence_reference' => (0, 255, 5),
|
||||
'moving_reference' => (0, 255, 5),
|
||||
'perceptual_boundary' => (0, 500, 50),
|
||||
'moving_boundary' => (0, 500, 50),
|
||||
'moving_rigger_time' => (0, 2000, 100),
|
||||
'moving_static_time' => (0, 60000, 1000),
|
||||
'none_body_time' => (0, 300000, 5000),
|
||||
'moving_max_dis' => (0, 500, 50),
|
||||
'moving_range' => (0, 255, 5),
|
||||
'presence_range' => (0, 255, 5),
|
||||
_ => (defaultMin, defaultMax, defaultDivdidend),
|
||||
};
|
||||
}
|
||||
|
||||
static (double min, double max) sliderRange(String functionCode) =>
|
||||
switch (functionCode) {
|
||||
'moving_speed' => (0, 32),
|
||||
'sensitivity' => (0, 10),
|
||||
'space_static_val' => (0, 255),
|
||||
'space_move_val' => (0, 255),
|
||||
'moving_max_dis' => (0, 10),
|
||||
'static_max_dis' => (0, 10),
|
||||
'moving_range' => (0, 25.5),
|
||||
'presence_range' => (0, 25.5),
|
||||
'presence_judgement_threshold' => (0, 255),
|
||||
'motion_amplitude_trigger_threshold' => (0, 255),
|
||||
'perceptual_boundary' => (0, 5),
|
||||
'moving_boundary' => (0, 5),
|
||||
'moving_rigger_time' => (0, 2),
|
||||
'moving_static_time' => (0, 50),
|
||||
'none_body_time' => (0, 300),
|
||||
'sports_para' => (0, 100),
|
||||
_ => (0, 300),
|
||||
};
|
||||
|
||||
static double dividendOfRange(String functionCode) => switch (functionCode) {
|
||||
'presence_reference' => 5,
|
||||
'moving_reference' => 5,
|
||||
'moving_max_dis' => 0.5,
|
||||
'static_max_dis' => 0.5,
|
||||
'moving_range' => 0.1,
|
||||
'presence_range' => 0.1,
|
||||
'perceptual_boundary' => 0.5,
|
||||
'moving_boundary' => 0.5,
|
||||
'moving_rigger_time' => 0.1,
|
||||
'moving_static_time' => 1.0,
|
||||
'none_body_time' => 5.0,
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
static String unit(String functionCode) => switch (functionCode) {
|
||||
'moving_max_dis' ||
|
||||
'static_max_dis' ||
|
||||
'moving_range' ||
|
||||
'presence_range' ||
|
||||
'perceptual_boundary' ||
|
||||
'moving_boundary' =>
|
||||
'M',
|
||||
'moving_rigger_time' || 'moving_static_time' || 'none_body_time' => 'sec',
|
||||
_ => '',
|
||||
};
|
||||
|
||||
static String displayText({
|
||||
required dynamic value,
|
||||
required String functionCode,
|
||||
}) {
|
||||
final parsedValue = double.tryParse('$value');
|
||||
|
||||
return switch (functionCode) {
|
||||
'moving_max_dis' ||
|
||||
'static_max_dis' ||
|
||||
'moving_range' ||
|
||||
'presence_range' ||
|
||||
'perceptual_boundary' ||
|
||||
'moving_boundary' =>
|
||||
parsedValue?.toStringAsFixed(1) ?? '0',
|
||||
'moving_rigger_time' => parsedValue?.toStringAsFixed(2) ?? '0',
|
||||
'moving_static_time' ||
|
||||
'none_body_time' =>
|
||||
parsedValue?.toStringAsFixed(2) ?? '0',
|
||||
_ => '${parsedValue?.toStringAsFixed(0) ?? 0}',
|
||||
};
|
||||
}
|
||||
}
|
@ -1,17 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/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/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.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/wall_sensor/time_wheel.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
|
||||
class WallPresenceSensor extends StatefulWidget {
|
||||
final List<DeviceFunction> functions;
|
||||
@ -63,8 +62,7 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_wpsFunctions =
|
||||
widget.functions.whereType<WpsFunctions>().where((function) {
|
||||
_wpsFunctions = widget.functions.whereType<WpsFunctions>().where((function) {
|
||||
if (widget.dialogType == 'THEN') {
|
||||
return function.type == 'THEN' || function.type == 'BOTH';
|
||||
}
|
||||
@ -176,10 +174,10 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
||||
);
|
||||
|
||||
return Expanded(
|
||||
child: _ValueSelector(
|
||||
child: WpsValueSelectorWidget(
|
||||
selectedFunction: selectedFunction,
|
||||
functionData: functionData,
|
||||
acFunctions: _wpsFunctions,
|
||||
wpsFunctions: _wpsFunctions,
|
||||
device: widget.device,
|
||||
dialogType: widget.dialogType!,
|
||||
removeComparators: widget.removeComparetors,
|
||||
@ -208,342 +206,3 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ValueSelector extends StatelessWidget {
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData functionData;
|
||||
final List<WpsFunctions> acFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
final bool removeComparators;
|
||||
|
||||
const _ValueSelector({
|
||||
required this.selectedFunction,
|
||||
required this.functionData,
|
||||
required this.acFunctions,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
required this.removeComparators,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedFn =
|
||||
acFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
if (_isSliderFunction(selectedFunction)) {
|
||||
return _SliderValueSelector(
|
||||
selectedFunction: selectedFunction,
|
||||
functionData: functionData,
|
||||
device: device,
|
||||
dialogType: dialogType,
|
||||
);
|
||||
}
|
||||
|
||||
return _OperationalValuesList(
|
||||
values: values,
|
||||
selectedValue: functionData.value,
|
||||
device: device,
|
||||
operationName: selectedFn.operationName,
|
||||
selectCode: selectedFunction,
|
||||
);
|
||||
}
|
||||
|
||||
bool _isSliderFunction(String function) =>
|
||||
['dis_current', 'presence_time', 'illuminance_value'].contains(function);
|
||||
}
|
||||
|
||||
class _SliderValueSelector extends StatelessWidget {
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData functionData;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
|
||||
const _SliderValueSelector({
|
||||
required this.selectedFunction,
|
||||
required this.functionData,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final initialValue = functionData.value ?? 250;
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
_ConditionToggle(
|
||||
currentCondition: functionData.condition,
|
||||
selectCode: selectedFunction,
|
||||
device: device,
|
||||
operationName: functionData.operationName,
|
||||
selectedValue: functionData.value,
|
||||
),
|
||||
_ValueDisplay(
|
||||
value: initialValue,
|
||||
functionCode: selectedFunction,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_FunctionSlider(
|
||||
initialValue: initialValue,
|
||||
functionCode: selectedFunction,
|
||||
functionData: functionData,
|
||||
device: device,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ConditionToggle extends StatelessWidget {
|
||||
final String? currentCondition;
|
||||
final String selectCode;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final dynamic selectedValue;
|
||||
|
||||
const _ConditionToggle({
|
||||
this.currentCondition,
|
||||
required this.selectCode,
|
||||
this.device,
|
||||
required this.operationName,
|
||||
this.selectedValue,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const conditions = ["<", "==", ">"];
|
||||
return ToggleButtons(
|
||||
onPressed: (index) => _updateCondition(context, conditions[index]),
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateCondition(BuildContext context, String condition) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: condition,
|
||||
value: selectedValue,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ValueDisplay extends StatelessWidget {
|
||||
final dynamic value;
|
||||
final String functionCode;
|
||||
|
||||
const _ValueDisplay({
|
||||
required this.value,
|
||||
required this.functionCode,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
_getDisplayText(),
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _getDisplayText() {
|
||||
final intValue = (value as num?)?.toInt() ?? 0;
|
||||
switch (functionCode) {
|
||||
case 'presence_time':
|
||||
return '$intValue Min';
|
||||
case 'dis_current':
|
||||
return '$intValue CM';
|
||||
case 'illuminance_value':
|
||||
return '$intValue Lux';
|
||||
default:
|
||||
return '$intValue';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _FunctionSlider extends StatelessWidget {
|
||||
final dynamic initialValue;
|
||||
final String functionCode;
|
||||
final DeviceFunctionData functionData;
|
||||
final AllDevicesModel? device;
|
||||
|
||||
const _FunctionSlider({
|
||||
required this.initialValue,
|
||||
required this.functionCode,
|
||||
required this.functionData,
|
||||
required this.device,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final (min, max) = _getSliderRange();
|
||||
return Slider(
|
||||
value: initialValue is int ? initialValue.toDouble() : min,
|
||||
min: min,
|
||||
max: max,
|
||||
divisions: (max - min).toInt(),
|
||||
onChanged: (value) => _updateValue(context, value.toInt()),
|
||||
);
|
||||
}
|
||||
|
||||
(double, double) _getSliderRange() {
|
||||
switch (functionCode) {
|
||||
case 'presence_time':
|
||||
return (0, 65535);
|
||||
case 'dis_current':
|
||||
return (1, 600);
|
||||
case 'illuminance_value':
|
||||
return (0, 10000);
|
||||
default:
|
||||
return (200, 300);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateValue(BuildContext context, int value) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: functionCode,
|
||||
operationName: functionData.operationName,
|
||||
value: value,
|
||||
condition: functionData.condition,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OperationalValuesList extends StatelessWidget {
|
||||
final List<WpsOperationalValue> values;
|
||||
final dynamic selectedValue;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final String selectCode;
|
||||
|
||||
const _OperationalValuesList({
|
||||
required this.values,
|
||||
required this.selectedValue,
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
required this.selectCode,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return operationName == 'Nobody Time'
|
||||
? _buildTimeWheel(context)
|
||||
: ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: values.length,
|
||||
itemBuilder: (context, index) =>
|
||||
_buildValueItem(context, values[index]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTimeWheel(BuildContext context) {
|
||||
final currentTotalSeconds = selectedValue as int? ?? 0;
|
||||
return TimeWheelPicker(
|
||||
initialHours: currentTotalSeconds ~/ 3600,
|
||||
initialMinutes: (currentTotalSeconds % 3600) ~/ 60,
|
||||
initialSeconds: currentTotalSeconds % 60,
|
||||
onTimeChanged: (h, m, s) => _updateTotalSeconds(context, h, m, s),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueItem(BuildContext context, WpsOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_buildValueIcon(context, value),
|
||||
Expanded(child: _buildValueDescription(value)),
|
||||
_buildValueRadio(context, value),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueIcon(context, WpsOperationalValue value) {
|
||||
return Column(
|
||||
children: [
|
||||
if (_shouldShowTextDescription)
|
||||
Text(value.description.replaceAll("cm", '')),
|
||||
SvgPicture.asset(value.icon, width: 25, height: 25),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
bool get _shouldShowTextDescription =>
|
||||
operationName == 'Far Detection' ||
|
||||
operationName == 'Motionless Detection Sensitivity';
|
||||
|
||||
Widget _buildValueDescription(WpsOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Text(value.description),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueRadio(context, WpsOperationalValue value) {
|
||||
return Radio<dynamic>(
|
||||
value: value.value,
|
||||
groupValue: selectedValue,
|
||||
onChanged: (_) => _selectValue(context, value.value),
|
||||
);
|
||||
}
|
||||
|
||||
void _selectValue(BuildContext context, dynamic value) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateTotalSeconds(BuildContext context, int h, int m, int s) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: h * 3600 + m * 60 + s,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,114 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/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_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart';
|
||||
|
||||
class WpsOperationalValuesList extends StatelessWidget {
|
||||
final List<WpsOperationalValue> values;
|
||||
final dynamic selectedValue;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final String selectCode;
|
||||
|
||||
const WpsOperationalValuesList({
|
||||
required this.values,
|
||||
required this.selectedValue,
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
required this.selectCode,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return operationName == 'Nobody Time'
|
||||
? _buildTimeWheel(context)
|
||||
: ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: values.length,
|
||||
itemBuilder: (context, index) => _buildValueItem(context, values[index]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTimeWheel(BuildContext context) {
|
||||
final currentTotalSeconds = selectedValue as int? ?? 0;
|
||||
return TimeWheelPicker(
|
||||
initialHours: currentTotalSeconds ~/ 3600,
|
||||
initialMinutes: (currentTotalSeconds % 3600) ~/ 60,
|
||||
initialSeconds: currentTotalSeconds % 60,
|
||||
onTimeChanged: (h, m, s) => _updateTotalSeconds(context, h, m, s),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueItem(BuildContext context, WpsOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_buildValueIcon(context, value),
|
||||
Expanded(child: _buildValueDescription(value)),
|
||||
_buildValueRadio(context, value),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueIcon(context, WpsOperationalValue value) {
|
||||
return Column(
|
||||
children: [
|
||||
if (_shouldShowTextDescription) Text(value.description.replaceAll("cm", '')),
|
||||
SvgPicture.asset(value.icon, width: 25, height: 25),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
bool get _shouldShowTextDescription =>
|
||||
operationName == 'Far Detection' ||
|
||||
operationName == 'Motionless Detection Sensitivity';
|
||||
|
||||
Widget _buildValueDescription(WpsOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Text(value.description),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueRadio(context, WpsOperationalValue value) {
|
||||
return Radio<dynamic>(
|
||||
value: value.value,
|
||||
groupValue: selectedValue,
|
||||
onChanged: (_) => _selectValue(context, value.value),
|
||||
);
|
||||
}
|
||||
|
||||
void _selectValue(BuildContext context, dynamic value) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateTotalSeconds(BuildContext context, int h, int m, int s) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: h * 3600 + m * 60 + s,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
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/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.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;
|
||||
final DeviceFunctionData functionData;
|
||||
final List<WpsFunctions> wpsFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
final bool removeComparators;
|
||||
|
||||
const WpsValueSelectorWidget({
|
||||
required this.selectedFunction,
|
||||
required this.functionData,
|
||||
required this.wpsFunctions,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
required this.removeComparators,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedFn = wpsFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
if (_isSliderFunction(selectedFunction)) {
|
||||
return SliderValueSelector(
|
||||
currentCondition: functionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: sliderRange,
|
||||
displayedValue: getDisplayText,
|
||||
initialValue: functionData.value ?? 250,
|
||||
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: functionData.operationName,
|
||||
condition: condition,
|
||||
value: functionData.value,
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: functionData.operationName,
|
||||
value: value.toInt(),
|
||||
condition: functionData.condition,
|
||||
),
|
||||
),
|
||||
),
|
||||
unit: _unit,
|
||||
dividendOfRange: 1,
|
||||
);
|
||||
}
|
||||
|
||||
return WpsOperationalValuesList(
|
||||
values: values,
|
||||
selectedValue: functionData.value,
|
||||
device: device,
|
||||
operationName: selectedFn.operationName,
|
||||
selectCode: selectedFunction,
|
||||
);
|
||||
}
|
||||
|
||||
bool _isSliderFunction(String function) =>
|
||||
['dis_current', 'presence_time', 'illuminance_value'].contains(function);
|
||||
|
||||
(double, double) get sliderRange => switch (functionData.functionCode) {
|
||||
'presence_time' => (0, 65535),
|
||||
'dis_current' => (0, 600),
|
||||
'illuminance_value' => (0, 10000),
|
||||
_ => (200, 300),
|
||||
};
|
||||
|
||||
String get getDisplayText {
|
||||
final intValue = int.tryParse('${functionData.value ?? ''}');
|
||||
return switch (functionData.functionCode) {
|
||||
'presence_time' => '${intValue ?? '0'}',
|
||||
'dis_current' => '${intValue ?? '250'}',
|
||||
'illuminance_value' => '${intValue ?? '0'}',
|
||||
_ => '$intValue',
|
||||
};
|
||||
}
|
||||
|
||||
String get _unit => switch (functionData.functionCode) {
|
||||
'presence_time' => 'Min',
|
||||
'dis_current' => 'CM',
|
||||
'illuminance_value' => 'Lux',
|
||||
_ => '',
|
||||
};
|
||||
}
|
81
lib/pages/routines/widgets/slider_value_selector.dart
Normal file
81
lib/pages/routines/widgets/slider_value_selector.dart
Normal file
@ -0,0 +1,81 @@
|
||||
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/function_slider.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/value_display.dart';
|
||||
|
||||
class SliderValueSelector extends StatelessWidget {
|
||||
final String? currentCondition;
|
||||
final String dialogType;
|
||||
final (double, double) sliderRange;
|
||||
final String displayedValue;
|
||||
final Object? initialValue;
|
||||
final void Function(String condition) onConditionChanged;
|
||||
final void Function(double value) onSliderChanged;
|
||||
final String unit;
|
||||
final double dividendOfRange;
|
||||
|
||||
const SliderValueSelector({
|
||||
required this.dialogType,
|
||||
required this.sliderRange,
|
||||
required this.displayedValue,
|
||||
required this.initialValue,
|
||||
required this.onConditionChanged,
|
||||
required this.onSliderChanged,
|
||||
required this.currentCondition,
|
||||
required this.unit,
|
||||
required this.dividendOfRange,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
spacing: 16,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (dialogType == 'IF')
|
||||
ConditionToggle(
|
||||
currentCondition: currentCondition,
|
||||
onChanged: onConditionChanged,
|
||||
),
|
||||
ValueDisplay(
|
||||
value: initialValue,
|
||||
label: displayedValue,
|
||||
unit: unit,
|
||||
),
|
||||
FunctionSlider(
|
||||
initialValue: initialValue,
|
||||
range: sliderRange,
|
||||
onChanged: onSliderChanged,
|
||||
dividendOfRange: dividendOfRange,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RangeInputFormatter extends TextInputFormatter {
|
||||
const RangeInputFormatter({required this.min, required this.max});
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue,
|
||||
TextEditingValue newValue,
|
||||
) {
|
||||
final text = newValue.text;
|
||||
if (text.isEmpty) {
|
||||
return newValue;
|
||||
}
|
||||
|
||||
final value = double.tryParse(text);
|
||||
if (value == null || value < min || value > max) {
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
}
|
@ -3,10 +3,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/automation_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/delay_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/helper/dialog_helper/device_dialog_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dragable_card.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/automation_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/delay_dialog.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
@ -27,8 +27,7 @@ class ThenContainer extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('THEN',
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 16),
|
||||
state.isLoading && state.isUpdate == true
|
||||
? const Center(
|
||||
@ -41,12 +40,11 @@ class ThenContainer extends StatelessWidget {
|
||||
state.thenItems.length,
|
||||
(index) => GestureDetector(
|
||||
onTap: () async {
|
||||
if (state.thenItems[index]
|
||||
['deviceId'] ==
|
||||
if (state.thenItems[index]['deviceId'] ==
|
||||
'delay') {
|
||||
final result = await DelayHelper
|
||||
.showDelayPickerDialog(context,
|
||||
state.thenItems[index]);
|
||||
.showDelayPickerDialog(
|
||||
context, state.thenItems[index]);
|
||||
|
||||
if (result != null) {
|
||||
context
|
||||
@ -66,17 +64,14 @@ class ThenContainer extends StatelessWidget {
|
||||
context: context,
|
||||
builder: (BuildContext context) =>
|
||||
AutomationDialog(
|
||||
automationName:
|
||||
state.thenItems[index]
|
||||
['name'] ??
|
||||
'Automation',
|
||||
automationId:
|
||||
state.thenItems[index]
|
||||
['deviceId'] ??
|
||||
'',
|
||||
uniqueCustomId:
|
||||
state.thenItems[index]
|
||||
['uniqueCustomId'],
|
||||
automationName: state.thenItems[index]
|
||||
['name'] ??
|
||||
'Automation',
|
||||
automationId: state.thenItems[index]
|
||||
['deviceId'] ??
|
||||
'',
|
||||
uniqueCustomId: state.thenItems[index]
|
||||
['uniqueCustomId'],
|
||||
),
|
||||
);
|
||||
|
||||
@ -85,13 +80,11 @@ class ThenContainer extends StatelessWidget {
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToThenContainer({
|
||||
...state.thenItems[index],
|
||||
'imagePath':
|
||||
Assets.automation,
|
||||
'title':
|
||||
'imagePath': Assets.automation,
|
||||
'title': state.thenItems[index]
|
||||
['name'] ??
|
||||
state.thenItems[index]
|
||||
['name'] ??
|
||||
state.thenItems[index]
|
||||
['title'],
|
||||
['title'],
|
||||
}));
|
||||
}
|
||||
return;
|
||||
@ -114,9 +107,10 @@ class ThenContainer extends StatelessWidget {
|
||||
'2G',
|
||||
'3G',
|
||||
'WPS',
|
||||
'CPS',
|
||||
"GW",
|
||||
].contains(state.thenItems[index]
|
||||
['productType'])) {
|
||||
].contains(
|
||||
state.thenItems[index]['productType'])) {
|
||||
context.read<RoutineBloc>().add(
|
||||
AddToThenContainer(
|
||||
state.thenItems[index]));
|
||||
@ -126,9 +120,7 @@ class ThenContainer extends StatelessWidget {
|
||||
imagePath: state.thenItems[index]
|
||||
['imagePath'] ??
|
||||
'',
|
||||
title: state.thenItems[index]
|
||||
['title'] ??
|
||||
'',
|
||||
title: state.thenItems[index]['title'] ?? '',
|
||||
deviceData: state.thenItems[index],
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4, vertical: 8),
|
||||
@ -165,8 +157,8 @@ class ThenContainer extends StatelessWidget {
|
||||
}
|
||||
|
||||
if (mutableData['type'] == 'automation') {
|
||||
int index = state.thenItems.indexWhere(
|
||||
(item) => item['deviceId'] == mutableData['deviceId']);
|
||||
int index = state.thenItems
|
||||
.indexWhere((item) => item['deviceId'] == mutableData['deviceId']);
|
||||
if (index != -1) {
|
||||
return;
|
||||
}
|
||||
@ -191,8 +183,8 @@ class ThenContainer extends StatelessWidget {
|
||||
}
|
||||
|
||||
if (mutableData['type'] == 'tap_to_run' && state.isAutomation) {
|
||||
int index = state.thenItems.indexWhere(
|
||||
(item) => item['deviceId'] == mutableData['deviceId']);
|
||||
int index = state.thenItems
|
||||
.indexWhere((item) => item['deviceId'] == mutableData['deviceId']);
|
||||
if (index != -1) {
|
||||
return;
|
||||
}
|
||||
@ -230,7 +222,7 @@ class ThenContainer extends StatelessWidget {
|
||||
dialogType: "THEN");
|
||||
if (result != null) {
|
||||
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW']
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS']
|
||||
.contains(mutableData['productType'])) {
|
||||
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
|
||||
}
|
||||
|
33
lib/pages/routines/widgets/value_display.dart
Normal file
33
lib/pages/routines/widgets/value_display.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class ValueDisplay extends StatelessWidget {
|
||||
final dynamic value;
|
||||
final String label;
|
||||
final String unit;
|
||||
|
||||
const ValueDisplay({
|
||||
required this.value,
|
||||
required this.label,
|
||||
super.key,
|
||||
required this.unit,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
'$label $unit ',
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user