mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
Sp 1703 fe build device overview page curtain module (#303)
<!-- Thanks for contributing! Provide a description of your changes below and a general summary in the title Please look at the following checklist to ensure that your PR can be accepted quickly: --> ## Jira Ticket [SP-1703](https://syncrow.atlassian.net/browse/SP-1703) [SP-1704](https://syncrow.atlassian.net/browse/SP-1704) [SP-1706](https://syncrow.atlassian.net/browse/SP-1706) [SP-1705](https://syncrow.atlassian.net/browse/SP-1705) [SP-1707](https://syncrow.atlassian.net/browse/SP-1707) ## Description all about curtain module (UI + logic + integrate with API + control/batch) all is ready ## Type of Change <!--- Put an `x` in all the boxes that apply: --> - [x] ✨ New feature (non-breaking change which adds functionality) - [ ] 🛠️ Bug fix (non-breaking change which fixes an issue) - [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change) - [ ] 🧹 Code refactor - [ ] ✅ Build configuration change - [ ] 📝 Documentation - [ ] 🗑️ Chore [SP-1703]: https://syncrow.atlassian.net/browse/SP-1703?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [SP-1704]: https://syncrow.atlassian.net/browse/SP-1704?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [SP-1706]: https://syncrow.atlassian.net/browse/SP-1706?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [SP-1705]: https://syncrow.atlassian.net/browse/SP-1705?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [SP-1707]: https://syncrow.atlassian.net/browse/SP-1707?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
This commit is contained in:
8
assets/icons/close_curtain.svg
Normal file
8
assets/icons/close_curtain.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg width="23" height="13" viewBox="0 0 23 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1.24512 2.00263V11L1.90308 11.278L7.5311 6.94877C7.82484 6.72277 7.82484 6.27987 7.5311 6.05388L1.90308 1.72461L1.24512 2.00263Z" fill="#023DFE" fill-opacity="0.7"/>
|
||||||
|
<path d="M1.90344 1.7231L1.68312 1.55364C1.31186 1.2681 0.774414 1.53272 0.774414 2.00105V10.9984C0.774414 11.4668 1.31186 11.7315 1.68312 11.4459L1.90344 11.2764V1.7231Z" fill="#023DFE"/>
|
||||||
|
<path d="M12.0646 0.855469H11.5001C11.1883 0.855469 10.9355 1.10819 10.9355 1.41998V11.5813H12.0646C12.3764 11.5813 12.6291 11.3285 12.6291 11.0167V1.41998C12.6291 1.10826 12.3764 0.855469 12.0646 0.855469Z" fill="#023DFE" fill-opacity="0.7"/>
|
||||||
|
<path d="M12.6291 11.0168C12.0056 11.0168 11.5001 10.5113 11.5001 9.88779V0.855469H10.9356C10.6238 0.855469 10.3711 1.10819 10.3711 1.41998V11.5813C10.3711 11.893 10.6238 12.1458 10.9356 12.1458H12.0646C12.3764 12.1458 12.6291 11.893 12.6291 11.5813V11.0168Z" fill="#023DFE"/>
|
||||||
|
<path d="M21.4247 2.01953L16.1094 6.50343L21.4247 11.1061L22.226 10.7315V2.27062L21.4247 2.01953Z" fill="#023DFE" fill-opacity="0.7"/>
|
||||||
|
<path d="M17.3084 6.94723C17.0147 6.7213 17.0147 6.27833 17.3084 6.05233L22.2263 2.26933V2.00108C22.2263 1.53275 21.6889 1.26807 21.3176 1.55367L15.4693 6.05233C15.1756 6.27833 15.1756 6.7213 15.4693 6.94723L21.3176 11.4459C21.6889 11.7314 22.2263 11.4668 22.2263 10.9985V10.7302L17.3084 6.94723Z" fill="#023DFE"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
8
assets/icons/open_curtain.svg
Normal file
8
assets/icons/open_curtain.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg width="22" height="12" viewBox="0 0 22 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15.2227 1.27411V10.2715L15.8806 10.5495L21.5086 6.22025C21.8024 5.99426 21.8024 5.55136 21.5086 5.32536L15.8806 0.996094L15.2227 1.27411Z" fill="#023DFE" fill-opacity="0.7"/>
|
||||||
|
<path d="M15.881 0.994589L15.6607 0.825126C15.2894 0.539589 14.752 0.804208 14.752 1.27254V10.2699C14.752 10.7383 15.2894 11.0029 15.6607 10.7173L15.881 10.5479V0.994589Z" fill="#023DFE"/>
|
||||||
|
<path d="M12.0646 0.128906H11.5001C11.1883 0.128906 10.9355 0.381631 10.9355 0.693418V10.8547H12.0646C12.3764 10.8547 12.6291 10.602 12.6291 10.2902V0.693418C12.6291 0.381699 12.3764 0.128906 12.0646 0.128906Z" fill="#023DFE" fill-opacity="0.7"/>
|
||||||
|
<path d="M12.6291 10.2903C12.0056 10.2903 11.5001 9.78474 11.5001 9.16123V0.128906H10.9356C10.6238 0.128906 10.3711 0.381631 10.3711 0.693418V10.8547C10.3711 11.1665 10.6238 11.4192 10.9356 11.4192H12.0646C12.3764 11.4192 12.6291 11.1665 12.6291 10.8547V10.2903Z" fill="#023DFE"/>
|
||||||
|
<path d="M6.95005 1.29297L1.63477 5.77687L6.95005 10.3795L7.75136 10.005V1.54405L6.95005 1.29297Z" fill="#023DFE" fill-opacity="0.7"/>
|
||||||
|
<path d="M2.83379 6.21871C2.54005 5.99278 2.54005 5.54981 2.83379 5.32382L7.7517 1.54081V1.27257C7.7517 0.804238 7.21426 0.539551 6.843 0.825156L0.994719 5.32382C0.700979 5.54981 0.700979 5.99278 0.994719 6.21871L6.843 10.7174C7.21426 11.0029 7.7517 10.7383 7.7517 10.27V10.0017L2.83379 6.21871Z" fill="#023DFE"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
6
assets/icons/pause_curtain.svg
Normal file
6
assets/icons/pause_curtain.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="10" height="12" viewBox="0 0 10 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.81262 0.277344H8.24811C7.93632 0.277344 7.68359 0.530068 7.68359 0.841855V11.0031H8.81262C9.1244 11.0031 9.37713 10.7504 9.37713 10.4386V0.841855C9.37713 0.530136 9.1244 0.277344 8.81262 0.277344Z" fill="#023DFE" fill-opacity="0.7"/>
|
||||||
|
<path d="M9.37719 10.4387C8.75361 10.4387 8.24816 9.93317 8.24816 9.30967V0.277344H7.68365C7.37187 0.277344 7.11914 0.530068 7.11914 0.841855V11.0031C7.11914 11.3149 7.37187 11.5676 7.68365 11.5676H8.81268C9.12446 11.5676 9.37719 11.3149 9.37719 11.0031V10.4387Z" fill="#023DFE"/>
|
||||||
|
<path d="M2.5548 0.277344H1.99029C1.67851 0.277344 1.42578 0.530068 1.42578 0.841855V11.0031H2.5548C2.86659 11.0031 3.11932 10.7504 3.11932 10.4386V0.841855C3.11932 0.530136 2.86659 0.277344 2.5548 0.277344Z" fill="#023DFE" fill-opacity="0.7"/>
|
||||||
|
<path d="M3.11937 10.4387C2.4958 10.4387 1.99035 9.93317 1.99035 9.30967V0.277344H1.42584C1.11405 0.277344 0.861328 0.530068 0.861328 0.841855V11.0031C0.861328 11.3149 1.11405 11.5676 1.42584 11.5676H2.55486C2.86665 11.5676 3.11937 11.3149 3.11937 11.0031V10.4387Z" fill="#023DFE"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
10
assets/icons/reverse_arrows.svg
Normal file
10
assets/icons/reverse_arrows.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_10119_2631)">
|
||||||
|
<path d="M16.4229 10.9077V14.4803C16.4229 15.1238 15.9015 15.6453 15.2579 15.6453C14.6143 15.6453 14.0928 15.1238 14.0928 14.4803V14.3684C12.644 15.7134 10.7197 16.5 8.65572 16.5C5.42291 16.5 2.52657 14.573 1.27576 11.5914C1.21425 11.4446 1.18535 11.2917 1.18535 11.1417C1.18535 10.6854 1.45378 10.2539 1.89977 10.0661C2.49302 9.81722 3.17574 10.0959 3.4246 10.6901C4.31098 12.804 6.36475 14.1699 8.65572 14.1699C10.3973 14.1699 11.9999 13.3804 13.0578 12.0728H11.6849C11.0413 12.0728 10.5198 11.5513 10.5198 10.9077C10.5198 10.2641 11.0413 9.74265 11.6849 9.74265H15.2574C15.901 9.74265 16.4229 10.2641 16.4229 10.9077ZM5.31572 7.413C5.9593 7.413 6.48078 6.89105 6.48078 6.24794C6.48078 5.60436 5.9593 5.08288 5.31572 5.08288H4.13342C5.18897 3.68388 6.84661 2.83012 8.65572 2.83012C10.9472 2.83012 13.0005 4.1965 13.8873 6.31039C14.1357 6.90364 14.8184 7.18278 15.4117 6.93439C16.0049 6.68554 16.2841 6.00328 16.0357 5.40956C14.7844 2.42701 11.8881 0.5 8.65572 0.5C6.4421 0.5 4.38554 1.40455 2.90824 2.93218V2.67493C2.90824 2.03135 2.3863 1.50987 1.74318 1.50987C1.09961 1.50987 0.578125 2.03135 0.578125 2.67493V6.24794C0.578125 6.55691 0.701155 6.8533 0.919255 7.07234C1.13782 7.2909 1.43375 7.413 1.74318 7.413H5.31572Z" fill="#023DFE" fill-opacity="0.7"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_10119_2631">
|
||||||
|
<rect width="16" height="16" fill="white" transform="translate(0.5 0.5)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -84,9 +84,14 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
statusList.add(Status(code: element['code'], value: element['value']));
|
statusList.add(Status(code: element['code'], value: element['value']));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
deviceStatus =
|
||||||
|
AcStatusModel.fromJson(usersMap['productUuid'], statusList);
|
||||||
|
|
||||||
deviceStatus = AcStatusModel.fromJson(usersMap['productUuid'], statusList);
|
deviceStatus = AcStatusModel.fromJson(usersMap['productUuid'], statusList);
|
||||||
print('Device status updated: ${deviceStatus.acSwitch}');
|
print('Device status updated: ${deviceStatus.acSwitch}');
|
||||||
|
|
||||||
|
|
||||||
if (!isClosed) {
|
if (!isClosed) {
|
||||||
add(AcStatusUpdated(deviceStatus));
|
add(AcStatusUpdated(deviceStatus));
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ import 'package:syncrow_web/pages/device_managment/ceiling_sensor/view/ceiling_s
|
|||||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart';
|
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_status_view.dart';
|
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_status_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart';
|
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/view/curtain_module_batch.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/view/curtain_module_items.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart';
|
||||||
@ -18,6 +20,7 @@ import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_view.dar
|
|||||||
import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_sensor_batch_view.dart';
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_sensor_batch_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/view/one_gang_glass_batch_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/view/one_gang_glass_batch_control_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/one_gang_switch/view/wall_light_batch_control.dart';
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/view/wall_light_batch_control.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart';
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/power_clamp/view/power_clamp_batch_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/power_clamp/view/power_clamp_batch_control_view.dart';
|
||||||
@ -39,8 +42,6 @@ import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heate
|
|||||||
import 'package:syncrow_web/pages/device_managment/water_leak/view/water_leak_batch_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/water_leak/view/water_leak_batch_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/water_leak/view/water_leak_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/water_leak/view/water_leak_control_view.dart';
|
||||||
|
|
||||||
import '../../one_g_glass_switch/view/one_gang_glass_switch_control_view.dart';
|
|
||||||
|
|
||||||
mixin RouteControlsBasedCode {
|
mixin RouteControlsBasedCode {
|
||||||
Widget routeControlsWidgets({required AllDevicesModel device}) {
|
Widget routeControlsWidgets({required AllDevicesModel device}) {
|
||||||
switch (device.productType) {
|
switch (device.productType) {
|
||||||
@ -84,6 +85,10 @@ mixin RouteControlsBasedCode {
|
|||||||
return CurtainStatusControlsView(
|
return CurtainStatusControlsView(
|
||||||
deviceId: device.uuid!,
|
deviceId: device.uuid!,
|
||||||
);
|
);
|
||||||
|
case 'CUR_2':
|
||||||
|
return CurtainModuleItems(
|
||||||
|
deviceId: device.uuid!,
|
||||||
|
);
|
||||||
case 'AC':
|
case 'AC':
|
||||||
return AcDeviceControlsView(device: device);
|
return AcDeviceControlsView(device: device);
|
||||||
case 'WH':
|
case 'WH':
|
||||||
@ -107,7 +112,7 @@ mixin RouteControlsBasedCode {
|
|||||||
case 'SOS':
|
case 'SOS':
|
||||||
return SosDeviceControlsView(device: device);
|
return SosDeviceControlsView(device: device);
|
||||||
|
|
||||||
case 'NCPS':
|
case 'NCPS':
|
||||||
return FlushMountedPresenceSensorControlView(device: device);
|
return FlushMountedPresenceSensorControlView(device: device);
|
||||||
default:
|
default:
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
@ -132,76 +137,140 @@ mixin RouteControlsBasedCode {
|
|||||||
switch (devices.first.productType) {
|
switch (devices.first.productType) {
|
||||||
case '1G':
|
case '1G':
|
||||||
return WallLightBatchControlView(
|
return WallLightBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == '1G')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == '1G')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case '2G':
|
case '2G':
|
||||||
return TwoGangBatchControlView(
|
return TwoGangBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == '2G')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == '2G')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case '3G':
|
case '3G':
|
||||||
return LivingRoomBatchControlsView(
|
return LivingRoomBatchControlsView(
|
||||||
deviceIds: devices.where((e) => (e.productType == '3G')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == '3G')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case '1GT':
|
case '1GT':
|
||||||
return OneGangGlassSwitchBatchControlView(
|
return OneGangGlassSwitchBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == '1GT')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == '1GT')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case '2GT':
|
case '2GT':
|
||||||
return TwoGangGlassSwitchBatchControlView(
|
return TwoGangGlassSwitchBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == '2GT')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == '2GT')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case '3GT':
|
case '3GT':
|
||||||
return ThreeGangGlassSwitchBatchControlView(
|
return ThreeGangGlassSwitchBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == '3GT')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == '3GT')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case 'GW':
|
case 'GW':
|
||||||
return GatewayBatchControlView(
|
return GatewayBatchControlView(
|
||||||
gatewayIds: devices.where((e) => (e.productType == 'GW')).map((e) => e.uuid!).toList(),
|
gatewayIds: devices
|
||||||
|
.where((e) => e.productType == 'GW')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case 'DL':
|
case 'DL':
|
||||||
return DoorLockBatchControlView(
|
return DoorLockBatchControlView(
|
||||||
devicesIds: devices.where((e) => (e.productType == 'DL')).map((e) => e.uuid!).toList());
|
devicesIds: devices
|
||||||
|
.where((e) => e.productType == 'DL')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList());
|
||||||
case 'WPS':
|
case 'WPS':
|
||||||
return WallSensorBatchControlView(
|
return WallSensorBatchControlView(
|
||||||
devicesIds: devices.where((e) => (e.productType == 'WPS')).map((e) => e.uuid!).toList());
|
devicesIds: devices
|
||||||
|
.where((e) => e.productType == 'WPS')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList());
|
||||||
case 'CPS':
|
case 'CPS':
|
||||||
return CeilingSensorBatchControlView(
|
return CeilingSensorBatchControlView(
|
||||||
devicesIds: devices.where((e) => (e.productType == 'CPS')).map((e) => e.uuid!).toList(),
|
devicesIds: devices
|
||||||
|
.where((e) => e.productType == 'CPS')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case 'CUR':
|
case 'CUR':
|
||||||
return CurtainBatchStatusView(
|
return CurtainBatchStatusView(
|
||||||
devicesIds: devices.where((e) => (e.productType == 'CUR')).map((e) => e.uuid!).toList(),
|
devicesIds: devices
|
||||||
|
.where((e) => e.productType == 'CUR')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
case 'CUR_2':
|
||||||
|
return CurtainModuleBatchView(
|
||||||
|
devicesIds: devices
|
||||||
|
.where((e) => e.productType == 'CUR_2')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case 'AC':
|
case 'AC':
|
||||||
return AcDeviceBatchControlView(
|
return AcDeviceBatchControlView(
|
||||||
devicesIds: devices.where((e) => (e.productType == 'AC')).map((e) => e.uuid!).toList());
|
devicesIds: devices
|
||||||
|
.where((e) => e.productType == 'AC')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList());
|
||||||
case 'WH':
|
case 'WH':
|
||||||
return WaterHEaterBatchControlView(
|
return WaterHEaterBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == 'WH')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == 'WH')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case 'DS':
|
case 'DS':
|
||||||
return MainDoorSensorBatchView(
|
return MainDoorSensorBatchView(
|
||||||
devicesIds: devices.where((e) => (e.productType == 'DS')).map((e) => e.uuid!).toList(),
|
devicesIds: devices
|
||||||
|
.where((e) => e.productType == 'DS')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case 'GD':
|
case 'GD':
|
||||||
return GarageDoorBatchControlView(
|
return GarageDoorBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == 'GD')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == 'GD')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case 'WL':
|
case 'WL':
|
||||||
return WaterLeakBatchControlView(
|
return WaterLeakBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == 'WL')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == 'WL')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case 'PC':
|
case 'PC':
|
||||||
return PowerClampBatchControlView(
|
return PowerClampBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == 'PC')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == 'PC')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case 'SOS':
|
case 'SOS':
|
||||||
return SOSBatchControlView(
|
return SOSBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == 'SOS')).map((e) => e.uuid!).toList(),
|
deviceIds: devices
|
||||||
|
.where((e) => e.productType == 'SOS')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
case 'NCPS':
|
case 'NCPS':
|
||||||
return FlushMountedPresenceSensorBatchControlView(
|
return FlushMountedPresenceSensorBatchControlView(
|
||||||
devicesIds: devices.where((e) => (e.productType == 'NCPS')).map((e) => e.uuid!).toList(),
|
devicesIds: devices
|
||||||
|
.where((e) => e.productType == 'NCPS')
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
|
@ -0,0 +1,379 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:firebase_database/firebase_database.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/models/curtain_module_model.dart';
|
||||||
|
import 'package:syncrow_web/services/batch_control_devices_service.dart';
|
||||||
|
import 'package:syncrow_web/services/control_device_service.dart';
|
||||||
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
|
|
||||||
|
part 'curtain_module_event.dart';
|
||||||
|
part 'curtain_module_state.dart';
|
||||||
|
|
||||||
|
class CurtainModuleBloc extends Bloc<CurtainModuleEvent, CurtainModuleState> {
|
||||||
|
final ControlDeviceService controlDeviceService;
|
||||||
|
final BatchControlDevicesService batchControlDevicesService;
|
||||||
|
StreamSubscription<DatabaseEvent>? _firebaseSubscription;
|
||||||
|
|
||||||
|
CurtainModuleBloc({
|
||||||
|
required this.controlDeviceService,
|
||||||
|
required this.batchControlDevicesService,
|
||||||
|
}) : super(CurtainModuleInitial()) {
|
||||||
|
on<FetchCurtainModuleStatusEvent>(_onFetchCurtainModuleStatusEvent);
|
||||||
|
on<SendCurtainPercentToApiEvent>(_onSendCurtainPercentToApiEvent);
|
||||||
|
on<OpenCurtainEvent>(_onOpenCurtainEvent);
|
||||||
|
on<CloseCurtainEvent>(_onCloseCurtainEvent);
|
||||||
|
on<StopCurtainEvent>(_onStopCurtainEvent);
|
||||||
|
on<ChangeTimerControlEvent>(_onChangeTimerControlEvent);
|
||||||
|
on<CurCalibrationEvent>(_onChageCurCalibrationEvent);
|
||||||
|
on<ChangeElecMachineryModeEvent>(_onChangeElecMachineryModeEvent);
|
||||||
|
on<ChangeControlBackEvent>(_onChangeControlBackEvent);
|
||||||
|
on<ChangeControlBackModeEvent>(_onChangeControlBackModeEvent);
|
||||||
|
on<ChangeCurtainModuleStatusEvent>(_onChangeCurtainModuleStatusEvent);
|
||||||
|
//batch
|
||||||
|
on<CurtainModuleFetchBatchStatusEvent>(_onFetchCurtainModuleBatchStatus);
|
||||||
|
on<SendCurtainBatchPercentToApiEvent>(_onSendCurtainBatchPercentToApiEvent);
|
||||||
|
on<OpenCurtainBatchEvent>(_onOpenCurtainBatchEvent);
|
||||||
|
on<CloseCurtainBatchEvent>(_onCloseCurtainBatchEvent);
|
||||||
|
on<StopCurtainBatchEvent>(_onStopCurtainBatchEvent);
|
||||||
|
on<CurtainModuleFactoryReset>(_onFactoryReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onFetchCurtainModuleStatusEvent(
|
||||||
|
FetchCurtainModuleStatusEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
emit(CurtainModuleLoading());
|
||||||
|
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||||
|
final result = Map.fromEntries(
|
||||||
|
status.status.map((element) => MapEntry(element.code, element.value)),
|
||||||
|
);
|
||||||
|
|
||||||
|
emit(CurtainModuleStatusLoaded(
|
||||||
|
curtainModuleStatus: CurtainModuleStatusModel.fromJson(result),
|
||||||
|
));
|
||||||
|
Map<String, dynamic> statusMap = {};
|
||||||
|
final ref =
|
||||||
|
FirebaseDatabase.instance.ref('device-status/${event.deviceId}');
|
||||||
|
final stream = ref.onValue;
|
||||||
|
|
||||||
|
stream.listen((DatabaseEvent DatabaseEvent) async {
|
||||||
|
if (DatabaseEvent.snapshot.value == null) return;
|
||||||
|
|
||||||
|
Map<dynamic, dynamic> usersMap =
|
||||||
|
DatabaseEvent.snapshot.value as Map<dynamic, dynamic>;
|
||||||
|
|
||||||
|
List<Status> statusList = [];
|
||||||
|
|
||||||
|
usersMap['status'].forEach((element) {
|
||||||
|
statusList.add(Status(code: element['code'], value: element['value']));
|
||||||
|
});
|
||||||
|
|
||||||
|
statusMap = {
|
||||||
|
for (final element in statusList) element.code: element.value,
|
||||||
|
};
|
||||||
|
if (!isClosed) {
|
||||||
|
add(
|
||||||
|
ChangeCurtainModuleStatusEvent(
|
||||||
|
deviceId: event.deviceId,
|
||||||
|
status: CurtainModuleStatusModel.fromJson(statusMap),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onChangeCurtainModuleStatusEvent(
|
||||||
|
ChangeCurtainModuleStatusEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
emit(CurtainModuleLoading());
|
||||||
|
emit(CurtainModuleStatusLoaded(curtainModuleStatus: event.status));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onSendCurtainPercentToApiEvent(
|
||||||
|
SendCurtainPercentToApiEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await controlDeviceService.controlDevice(
|
||||||
|
deviceUuid: event.deviceId,
|
||||||
|
status: event.status,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to send control command: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onOpenCurtainEvent(
|
||||||
|
OpenCurtainEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await controlDeviceService.controlDevice(
|
||||||
|
deviceUuid: event.deviceId,
|
||||||
|
status: Status(code: 'control', value: 'open'),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to open curtain: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onCloseCurtainEvent(
|
||||||
|
CloseCurtainEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await controlDeviceService.controlDevice(
|
||||||
|
deviceUuid: event.deviceId,
|
||||||
|
status: Status(code: 'control', value: 'close'),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to close curtain: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onStopCurtainEvent(
|
||||||
|
StopCurtainEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await controlDeviceService.controlDevice(
|
||||||
|
deviceUuid: event.deviceId,
|
||||||
|
status: Status(code: 'control', value: 'stop'),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to stop curtain: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onChangeTimerControlEvent(
|
||||||
|
ChangeTimerControlEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
if (event.timControl < 10 || event.timControl > 120) {
|
||||||
|
emit(const CurtainModuleError(
|
||||||
|
message: 'Timer control value must be between 10 and 120'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await controlDeviceService.controlDevice(
|
||||||
|
deviceUuid: event.deviceId,
|
||||||
|
status: Status(
|
||||||
|
code: 'tr_timecon',
|
||||||
|
value: event.timControl,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to change timer control: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onChageCurCalibrationEvent(
|
||||||
|
CurCalibrationEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await controlDeviceService.controlDevice(
|
||||||
|
deviceUuid: event.deviceId,
|
||||||
|
status: Status(code: 'cur_calibration', value: 'start'),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to start calibration: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onChangeElecMachineryModeEvent(
|
||||||
|
ChangeElecMachineryModeEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await controlDeviceService.controlDevice(
|
||||||
|
deviceUuid: event.deviceId,
|
||||||
|
status: Status(
|
||||||
|
code: 'elec_machinery_mode',
|
||||||
|
value: event.elecMachineryMode,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to change mode: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onChangeControlBackEvent(
|
||||||
|
ChangeControlBackEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await controlDeviceService.controlDevice(
|
||||||
|
deviceUuid: event.deviceId,
|
||||||
|
status: Status(
|
||||||
|
code: 'control_back',
|
||||||
|
value: event.controlBack,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to change control back: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onChangeControlBackModeEvent(
|
||||||
|
ChangeControlBackModeEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await controlDeviceService.controlDevice(
|
||||||
|
deviceUuid: event.deviceId,
|
||||||
|
status: Status(
|
||||||
|
code: 'control_back_mode',
|
||||||
|
value: event.controlBackMode,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(
|
||||||
|
message: 'Failed to change control back mode: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFetchCurtainModuleBatchStatus(
|
||||||
|
CurtainModuleFetchBatchStatusEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
emit(CurtainModuleLoading());
|
||||||
|
try {
|
||||||
|
final status =
|
||||||
|
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||||
|
|
||||||
|
final result = Map.fromEntries(
|
||||||
|
status.status.map((element) => MapEntry(element.code, element.value)),
|
||||||
|
);
|
||||||
|
|
||||||
|
emit(CurtainModuleStatusLoaded(
|
||||||
|
curtainModuleStatus: CurtainModuleStatusModel.fromJson(result),
|
||||||
|
));
|
||||||
|
|
||||||
|
Map<String, dynamic> statusMap = {};
|
||||||
|
final ref = FirebaseDatabase.instance
|
||||||
|
.ref('device-status/${event.devicesIds.first}');
|
||||||
|
final stream = ref.onValue;
|
||||||
|
|
||||||
|
stream.listen((DatabaseEvent DatabaseEvent) async {
|
||||||
|
if (DatabaseEvent.snapshot.value == null) return;
|
||||||
|
|
||||||
|
Map<dynamic, dynamic> usersMap =
|
||||||
|
DatabaseEvent.snapshot.value as Map<dynamic, dynamic>;
|
||||||
|
|
||||||
|
List<Status> statusList = [];
|
||||||
|
|
||||||
|
usersMap['status'].forEach((element) {
|
||||||
|
statusList
|
||||||
|
.add(Status(code: element['code'], value: element['value']));
|
||||||
|
});
|
||||||
|
|
||||||
|
statusMap = {
|
||||||
|
for (final element in statusList) element.code: element.value,
|
||||||
|
};
|
||||||
|
if (!isClosed) {
|
||||||
|
add(
|
||||||
|
ChangeCurtainModuleStatusEvent(
|
||||||
|
deviceId: event.devicesIds.first,
|
||||||
|
status: CurtainModuleStatusModel.fromJson(statusMap),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onSendCurtainBatchPercentToApiEvent(
|
||||||
|
SendCurtainBatchPercentToApiEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await batchControlDevicesService.batchControlDevices(
|
||||||
|
uuids: event.devicesId,
|
||||||
|
code: event.status.code,
|
||||||
|
value: event.status.value,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to send control command: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onOpenCurtainBatchEvent(
|
||||||
|
OpenCurtainBatchEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await batchControlDevicesService.batchControlDevices(
|
||||||
|
uuids: event.devicesId,
|
||||||
|
code: 'control',
|
||||||
|
value: 'open',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to open curtain: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onCloseCurtainBatchEvent(
|
||||||
|
CloseCurtainBatchEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await batchControlDevicesService.batchControlDevices(
|
||||||
|
uuids: event.devicesId,
|
||||||
|
code: 'control',
|
||||||
|
value: 'close',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to close curtain: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onStopCurtainBatchEvent(
|
||||||
|
StopCurtainBatchEvent event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await batchControlDevicesService.batchControlDevices(
|
||||||
|
uuids: event.devicesId,
|
||||||
|
code: 'control',
|
||||||
|
value: 'stop',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: 'Failed to stop curtain: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onFactoryReset(
|
||||||
|
CurtainModuleFactoryReset event,
|
||||||
|
Emitter<CurtainModuleState> emit,
|
||||||
|
) async {
|
||||||
|
emit(CurtainModuleLoading());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryReset,
|
||||||
|
event.deviceId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(const CurtainModuleError(message: 'Failed'));
|
||||||
|
} else {
|
||||||
|
add(
|
||||||
|
FetchCurtainModuleStatusEvent(deviceId: event.deviceId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainModuleError(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() async {
|
||||||
|
await _firebaseSubscription?.cancel();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
part of 'curtain_module_bloc.dart';
|
||||||
|
|
||||||
|
sealed class CurtainModuleEvent extends Equatable {
|
||||||
|
const CurtainModuleEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FetchCurtainModuleStatusEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
const FetchCurtainModuleStatusEvent({required this.deviceId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SendCurtainPercentToApiEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final Status status;
|
||||||
|
|
||||||
|
const SendCurtainPercentToApiEvent({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.status,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, status];
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpenCurtainEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const OpenCurtainEvent({required this.deviceId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CloseCurtainEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const CloseCurtainEvent({required this.deviceId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class StopCurtainEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const StopCurtainEvent({required this.deviceId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChangeTimerControlEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final int timControl;
|
||||||
|
|
||||||
|
const ChangeTimerControlEvent({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.timControl,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, timControl];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurCalibrationEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const CurCalibrationEvent({
|
||||||
|
required this.deviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChangeElecMachineryModeEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final String elecMachineryMode;
|
||||||
|
|
||||||
|
const ChangeElecMachineryModeEvent({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.elecMachineryMode,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, elecMachineryMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChangeControlBackEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final String controlBack;
|
||||||
|
|
||||||
|
const ChangeControlBackEvent({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.controlBack,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, controlBack];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChangeControlBackModeEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final String controlBackMode;
|
||||||
|
|
||||||
|
const ChangeControlBackModeEvent({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.controlBackMode,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, controlBackMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChangeCurtainModuleStatusEvent extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final CurtainModuleStatusModel status;
|
||||||
|
|
||||||
|
const ChangeCurtainModuleStatusEvent({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.status,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, status];
|
||||||
|
}
|
||||||
|
|
||||||
|
///batch
|
||||||
|
class CurtainModuleFetchBatchStatusEvent extends CurtainModuleEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
const CurtainModuleFetchBatchStatusEvent(this.devicesIds);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SendCurtainBatchPercentToApiEvent extends CurtainModuleEvent {
|
||||||
|
final List<String> devicesId;
|
||||||
|
final Status status;
|
||||||
|
|
||||||
|
const SendCurtainBatchPercentToApiEvent({
|
||||||
|
required this.devicesId,
|
||||||
|
required this.status,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesId, status];
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpenCurtainBatchEvent extends CurtainModuleEvent {
|
||||||
|
final List<String> devicesId;
|
||||||
|
|
||||||
|
const OpenCurtainBatchEvent({required this.devicesId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CloseCurtainBatchEvent extends CurtainModuleEvent {
|
||||||
|
final List<String> devicesId;
|
||||||
|
|
||||||
|
const CloseCurtainBatchEvent({required this.devicesId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class StopCurtainBatchEvent extends CurtainModuleEvent {
|
||||||
|
final List<String> devicesId;
|
||||||
|
|
||||||
|
const StopCurtainBatchEvent({required this.devicesId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainModuleFactoryReset extends CurtainModuleEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final FactoryResetModel factoryReset;
|
||||||
|
|
||||||
|
const CurtainModuleFactoryReset(
|
||||||
|
{required this.deviceId, required this.factoryReset});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, factoryReset];
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
part of 'curtain_module_bloc.dart';
|
||||||
|
|
||||||
|
sealed class CurtainModuleState extends Equatable {
|
||||||
|
const CurtainModuleState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainModuleInitial extends CurtainModuleState {}
|
||||||
|
|
||||||
|
class CurtainModuleLoading extends CurtainModuleState {}
|
||||||
|
|
||||||
|
class CurtainModuleError extends CurtainModuleState {
|
||||||
|
final String message;
|
||||||
|
const CurtainModuleError({required this.message});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainModuleStatusLoaded extends CurtainModuleState {
|
||||||
|
final CurtainModuleStatusModel curtainModuleStatus;
|
||||||
|
|
||||||
|
const CurtainModuleStatusLoaded({required this.curtainModuleStatus});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [curtainModuleStatus];
|
||||||
|
}
|
||||||
|
class CurtainModuleStatusUpdated extends CurtainModuleState {
|
||||||
|
final CurtainModuleStatusModel curtainModuleStatus;
|
||||||
|
|
||||||
|
const CurtainModuleStatusUpdated({required this.curtainModuleStatus});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [curtainModuleStatus];
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
enum CurtainModuleControl {
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
stop,
|
||||||
|
}
|
||||||
|
|
||||||
|
// enum CurtainControlBackMode {
|
||||||
|
// foward,
|
||||||
|
// backward,
|
||||||
|
// }
|
||||||
|
|
||||||
|
class CurtainModuleStatusModel {
|
||||||
|
CurtainModuleControl control;
|
||||||
|
int percentControl;
|
||||||
|
String curCalibration;
|
||||||
|
// CurtainControlBackMode controlBackmode;
|
||||||
|
int trTimeControl;
|
||||||
|
String elecMachineryMode;
|
||||||
|
String controlBack;
|
||||||
|
CurtainModuleStatusModel({
|
||||||
|
required this.control,
|
||||||
|
required this.percentControl,
|
||||||
|
required this.curCalibration,
|
||||||
|
// required this.controlBackmode,
|
||||||
|
required this.trTimeControl,
|
||||||
|
required this.controlBack,
|
||||||
|
required this.elecMachineryMode,
|
||||||
|
});
|
||||||
|
factory CurtainModuleStatusModel.zero() => CurtainModuleStatusModel(
|
||||||
|
control: CurtainModuleControl.stop,
|
||||||
|
percentControl: 0,
|
||||||
|
// controlBackmode: CurtainControlBackMode.foward,
|
||||||
|
curCalibration: '',
|
||||||
|
trTimeControl: 0,
|
||||||
|
controlBack: '',
|
||||||
|
elecMachineryMode: '',
|
||||||
|
);
|
||||||
|
|
||||||
|
factory CurtainModuleStatusModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return CurtainModuleStatusModel(
|
||||||
|
control: CurtainModuleControl.values.firstWhere(
|
||||||
|
(e) => e.toString() == json['control'] as String,
|
||||||
|
orElse: () => CurtainModuleControl.stop,
|
||||||
|
),
|
||||||
|
percentControl: json['percent_control'] as int? ?? 0,
|
||||||
|
curCalibration: json['cur_calibration'] as String? ?? '',
|
||||||
|
trTimeControl: json['tr_timecon'] as int? ?? 0,
|
||||||
|
elecMachineryMode: json['elec_machinery_mode'] as String? ?? '',
|
||||||
|
controlBack: json['control_back'] as String? ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/curtain_movment_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart';
|
||||||
|
import 'package:syncrow_web/services/batch_control_devices_service.dart';
|
||||||
|
import 'package:syncrow_web/services/control_device_service.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class CurtainModuleBatchView extends StatelessWidget {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
const CurtainModuleBatchView({
|
||||||
|
super.key,
|
||||||
|
required this.devicesIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => CurtainModuleBloc(
|
||||||
|
controlDeviceService: RemoteControlDeviceService(),
|
||||||
|
batchControlDevicesService: RemoteBatchControlDevicesService())
|
||||||
|
..add(CurtainModuleFetchBatchStatusEvent(devicesIds)),
|
||||||
|
child: _buildStatusControls(context),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 30),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
ControlCurtainMovementWidget(
|
||||||
|
devicesId: devicesIds,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 120,
|
||||||
|
// width: 350,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
// Expanded(
|
||||||
|
// child:
|
||||||
|
FactoryResetWidget(
|
||||||
|
callFactoryReset: () {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
CurtainModuleFactoryReset(
|
||||||
|
deviceId: devicesIds.first,
|
||||||
|
factoryReset:
|
||||||
|
FactoryResetModel(devicesUuid: devicesIds),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// ),
|
||||||
|
// Expanded(
|
||||||
|
// child: IconNameStatusContainer(
|
||||||
|
// isFullIcon: false,
|
||||||
|
// name: 'Firmware Update',
|
||||||
|
// icon: Assets.firmware,
|
||||||
|
// onTap: () {},
|
||||||
|
// status: false,
|
||||||
|
// textColor: ColorsManager.blackColor,
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/curtain_movment_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/prefrences_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart';
|
||||||
|
import 'package:syncrow_web/services/batch_control_devices_service.dart';
|
||||||
|
import 'package:syncrow_web/services/control_device_service.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class CurtainModuleItems extends StatelessWidget with HelperResponsiveLayout {
|
||||||
|
final String deviceId;
|
||||||
|
const CurtainModuleItems({
|
||||||
|
super.key,
|
||||||
|
required this.deviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => CurtainModuleBloc(
|
||||||
|
controlDeviceService: RemoteControlDeviceService(),
|
||||||
|
batchControlDevicesService: RemoteBatchControlDevicesService())
|
||||||
|
..add(FetchCurtainModuleStatusEvent(deviceId: deviceId)),
|
||||||
|
child: BlocBuilder<CurtainModuleBloc, CurtainModuleState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return _buildStatusControls(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 30),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
ControlCurtainMovementWidget(
|
||||||
|
devicesId: [deviceId],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 140,
|
||||||
|
width: 350,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ScheduleControlButton(
|
||||||
|
onTap: () {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => BlocProvider.value(
|
||||||
|
value:
|
||||||
|
BlocProvider.of<CurtainModuleBloc>(context),
|
||||||
|
child: BuildScheduleView(
|
||||||
|
deviceUuid: deviceId,
|
||||||
|
category: 'CUR_2',
|
||||||
|
code: 'control',
|
||||||
|
|
||||||
|
),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
mainText: '',
|
||||||
|
subtitle: 'Scheduling',
|
||||||
|
iconPath: Assets.scheduling,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: BlocBuilder<CurtainModuleBloc, CurtainModuleState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is CurtainModuleLoading) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
} else if (state is CurtainModuleStatusLoaded) {
|
||||||
|
return IconNameStatusContainer(
|
||||||
|
isFullIcon: false,
|
||||||
|
name: 'Preferences',
|
||||||
|
icon: Assets.preferences,
|
||||||
|
onTap: () => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => BlocProvider.value(
|
||||||
|
value: context.read<CurtainModuleBloc>(),
|
||||||
|
child: CurtainModulePrefrencesDialog(
|
||||||
|
curtainModuleBloc:
|
||||||
|
context.watch<CurtainModuleBloc>(),
|
||||||
|
deviceId: deviceId,
|
||||||
|
curtainModuleStatusModel:
|
||||||
|
state.curtainModuleStatus,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
status: false,
|
||||||
|
textColor: ColorsManager.blackColor,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/calibrate_completed_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart';
|
||||||
|
|
||||||
|
class AccurteCalibratingDialog extends StatelessWidget {
|
||||||
|
final String deviceId;
|
||||||
|
final BuildContext parentContext;
|
||||||
|
const AccurteCalibratingDialog({
|
||||||
|
super.key,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.parentContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: AccurateDialogWidget(
|
||||||
|
title: 'Calibrating',
|
||||||
|
body: const NormalTextBodyForDialog(
|
||||||
|
title: '',
|
||||||
|
step1:
|
||||||
|
'1. Click Close Button to make the Curtain run to Full Close and Position.',
|
||||||
|
step2: '2. click Next to complete the Calibration.',
|
||||||
|
),
|
||||||
|
leftOnTap: () => Navigator.of(parentContext).pop(),
|
||||||
|
rightOnTap: () {
|
||||||
|
parentContext.read<CurtainModuleBloc>().add(
|
||||||
|
CurCalibrationEvent(
|
||||||
|
deviceId: deviceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.of(parentContext).pop();
|
||||||
|
showDialog(
|
||||||
|
context: parentContext,
|
||||||
|
builder: (_) => CalibrateCompletedDialog(
|
||||||
|
parentContext: parentContext,
|
||||||
|
deviceId: deviceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_calibrating_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart';
|
||||||
|
|
||||||
|
class AccurateCalibrationDialog extends StatelessWidget {
|
||||||
|
final String deviceId;
|
||||||
|
final BuildContext parentContext;
|
||||||
|
const AccurateCalibrationDialog({
|
||||||
|
super.key,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.parentContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: AccurateDialogWidget(
|
||||||
|
title: 'Accurate Calibration',
|
||||||
|
body: const NormalTextBodyForDialog(
|
||||||
|
title: 'Prepare Calibration:',
|
||||||
|
step1: '1. Run The Curtain to the Fully Open Position,and pause.',
|
||||||
|
step2: '2. click Next to Start accurate calibration.',
|
||||||
|
),
|
||||||
|
leftOnTap: () => Navigator.of(parentContext).pop(),
|
||||||
|
rightOnTap: () {
|
||||||
|
Navigator.of(parentContext).pop();
|
||||||
|
showDialog(
|
||||||
|
context: parentContext,
|
||||||
|
builder: (_) => AccurteCalibratingDialog(
|
||||||
|
deviceId: deviceId,
|
||||||
|
parentContext: parentContext,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class AccurateDialogWidget extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final Widget body;
|
||||||
|
final void Function() leftOnTap;
|
||||||
|
final void Function() rightOnTap;
|
||||||
|
const AccurateDialogWidget({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.body,
|
||||||
|
required this.leftOnTap,
|
||||||
|
required this.rightOnTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 300,
|
||||||
|
width: 400,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
const Divider(
|
||||||
|
indent: 10,
|
||||||
|
endIndent: 10,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: body,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
const Spacer(),
|
||||||
|
const Divider(),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: leftOnTap,
|
||||||
|
child: Container(
|
||||||
|
height: 60,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(
|
||||||
|
color: ColorsManager.grayBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'Cancel',
|
||||||
|
style: TextStyle(color: ColorsManager.grayBorder),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: rightOnTap,
|
||||||
|
child: Container(
|
||||||
|
height: 60,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(
|
||||||
|
color: ColorsManager.grayBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'Next',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class CalibrateCompletedDialog extends StatelessWidget {
|
||||||
|
final BuildContext parentContext;
|
||||||
|
final String deviceId;
|
||||||
|
const CalibrateCompletedDialog({
|
||||||
|
super.key,
|
||||||
|
required this.parentContext,
|
||||||
|
required this.deviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: SizedBox(
|
||||||
|
height: 250,
|
||||||
|
width: 400,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: Text(
|
||||||
|
'Calibration Completed',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
const Divider(
|
||||||
|
indent: 10,
|
||||||
|
endIndent: 10,
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
Icons.check_circle,
|
||||||
|
size: 100,
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Divider(
|
||||||
|
indent: 10,
|
||||||
|
endIndent: 10,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
parentContext.read<CurtainModuleBloc>().add(
|
||||||
|
FetchCurtainModuleStatusEvent(
|
||||||
|
deviceId: deviceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.of(parentContext).pop();
|
||||||
|
Navigator.of(parentContext).pop();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
height: 40,
|
||||||
|
width: double.infinity,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: const Text(
|
||||||
|
'Close',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.grayBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class CurtainActionWidget extends StatelessWidget {
|
||||||
|
final String icon;
|
||||||
|
final void Function() onTap;
|
||||||
|
const CurtainActionWidget({
|
||||||
|
super.key,
|
||||||
|
required this.icon,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: ClipOval(
|
||||||
|
child: Container(
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
child: ClipOval(
|
||||||
|
child: Container(
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
color: ColorsManager.graysColor,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
icon,
|
||||||
|
width: 35,
|
||||||
|
height: 35,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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/device_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/models/curtain_module_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/curtain_action_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class ControlCurtainMovementWidget extends StatelessWidget {
|
||||||
|
final List<String> devicesId;
|
||||||
|
const ControlCurtainMovementWidget({
|
||||||
|
super.key,
|
||||||
|
required this.devicesId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: 550,
|
||||||
|
child: DeviceControlsContainer(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
CurtainActionWidget(
|
||||||
|
icon: Assets.openCurtain,
|
||||||
|
onTap: () {
|
||||||
|
if (devicesId.length == 1) {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
OpenCurtainEvent(deviceId: devicesId.first),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
OpenCurtainBatchEvent(devicesId: devicesId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 30,
|
||||||
|
),
|
||||||
|
CurtainActionWidget(
|
||||||
|
icon: Assets.pauseCurtain,
|
||||||
|
onTap: () {
|
||||||
|
if (devicesId.length == 1) {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
StopCurtainEvent(deviceId: devicesId.first),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
StopCurtainBatchEvent(devicesId: devicesId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 30,
|
||||||
|
),
|
||||||
|
CurtainActionWidget(
|
||||||
|
icon: Assets.closeCurtain,
|
||||||
|
onTap: () {
|
||||||
|
if (devicesId.length == 1) {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
CloseCurtainEvent(deviceId: devicesId.first),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
CloseCurtainBatchEvent(devicesId: devicesId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
BlocBuilder<CurtainModuleBloc, CurtainModuleState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is CurtainModuleError) {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
state.message,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.minBlueDot,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (state is CurtainModuleLoading) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: ColorsManager.minBlueDot,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (state is CurtainModuleInitial) {
|
||||||
|
return const Center(
|
||||||
|
child: Text(
|
||||||
|
'No data available',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.minBlueDot,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (state is CurtainModuleStatusLoaded) {
|
||||||
|
return CurtainSliderWidget(
|
||||||
|
status: state.curtainModuleStatus,
|
||||||
|
devicesId: devicesId,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(
|
||||||
|
child: Text(
|
||||||
|
'Unknown state',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.minBlueDot,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainSliderWidget extends StatefulWidget {
|
||||||
|
final CurtainModuleStatusModel status;
|
||||||
|
final List<String> devicesId;
|
||||||
|
|
||||||
|
const CurtainSliderWidget({
|
||||||
|
super.key,
|
||||||
|
required this.status,
|
||||||
|
required this.devicesId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CurtainSliderWidget> createState() => _CurtainSliderWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CurtainSliderWidgetState extends State<CurtainSliderWidget> {
|
||||||
|
double? _localValue; // For temporary drag state
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// If user is dragging, use local value. Otherwise, use Firebase-synced state
|
||||||
|
final double currentSliderValue =
|
||||||
|
_localValue ?? widget.status.percentControl / 100;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${(currentSliderValue * 100).round()}%',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.minBlueDot,
|
||||||
|
fontSize: 25,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Slider(
|
||||||
|
value: currentSliderValue,
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
divisions: 10, // 10% step
|
||||||
|
activeColor: ColorsManager.minBlueDot,
|
||||||
|
thumbColor: ColorsManager.primaryColor,
|
||||||
|
inactiveColor: ColorsManager.whiteColors,
|
||||||
|
|
||||||
|
// Start dragging — use local control
|
||||||
|
onChangeStart: (_) {
|
||||||
|
setState(() {
|
||||||
|
_localValue = currentSliderValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// While dragging — update temporary value
|
||||||
|
onChanged: (value) {
|
||||||
|
final steppedValue = (value * 10).roundToDouble() / 10;
|
||||||
|
setState(() {
|
||||||
|
_localValue = steppedValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// On release — send API and return to Firebase-controlled state
|
||||||
|
onChangeEnd: (value) {
|
||||||
|
final int targetPercent = (value * 100).round();
|
||||||
|
|
||||||
|
if (widget.devicesId.length == 1) {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
SendCurtainPercentToApiEvent(
|
||||||
|
deviceId: widget.devicesId.first,
|
||||||
|
status: Status(
|
||||||
|
code: 'percent_control',
|
||||||
|
value: targetPercent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
SendCurtainBatchPercentToApiEvent(
|
||||||
|
devicesId: widget.devicesId,
|
||||||
|
status: Status(
|
||||||
|
code: 'percent_control',
|
||||||
|
value: targetPercent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revert back to Firebase-synced stream
|
||||||
|
setState(() {
|
||||||
|
_localValue = null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class NormalTextBodyForDialog extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final String step1;
|
||||||
|
final String step2;
|
||||||
|
|
||||||
|
const NormalTextBodyForDialog({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.step1,
|
||||||
|
required this.step2,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
step1,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
step2,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class NumberInputField extends StatelessWidget {
|
||||||
|
final TextEditingController controller;
|
||||||
|
|
||||||
|
const NumberInputField({super.key, required this.controller});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextField(
|
||||||
|
controller: controller,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
isDense: true,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||||
|
|
||||||
|
class PrefReversCardWidget extends StatelessWidget {
|
||||||
|
final void Function() onTap;
|
||||||
|
final String title;
|
||||||
|
final String body;
|
||||||
|
const PrefReversCardWidget({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.body,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DefaultContainer(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 8,
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.grayBorder,
|
||||||
|
fontSize: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 20,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
borderRadius: const BorderRadius.horizontal(
|
||||||
|
left: Radius.circular(10),
|
||||||
|
right: Radius.circular(10)),
|
||||||
|
border: Border.all(color: ColorsManager.grayBorder)),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.reverseArrows,
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 100,
|
||||||
|
child: Text(
|
||||||
|
body,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/models/curtain_module_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_calibration_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/pref_revers_card_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/quick_calibration_dialog.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||||
|
|
||||||
|
class CurtainModulePrefrencesDialog extends StatelessWidget {
|
||||||
|
final CurtainModuleStatusModel curtainModuleStatusModel;
|
||||||
|
final String deviceId;
|
||||||
|
final CurtainModuleBloc curtainModuleBloc;
|
||||||
|
const CurtainModulePrefrencesDialog({
|
||||||
|
super.key,
|
||||||
|
required this.curtainModuleStatusModel,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.curtainModuleBloc,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
backgroundColor: ColorsManager.CircleImageBackground,
|
||||||
|
contentPadding: const EdgeInsets.all(30),
|
||||||
|
title: const Center(
|
||||||
|
child: Text(
|
||||||
|
'Preferences',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
content: BlocBuilder<CurtainModuleBloc, CurtainModuleState>(
|
||||||
|
bloc: curtainModuleBloc,
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is CurtainModuleLoading) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
} else if (state is CurtainModuleStatusLoaded) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 300,
|
||||||
|
width: 400,
|
||||||
|
child: GridView(
|
||||||
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
childAspectRatio: 1.5,
|
||||||
|
mainAxisSpacing: 10,
|
||||||
|
crossAxisSpacing: 10,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
PrefReversCardWidget(
|
||||||
|
title: state.curtainModuleStatus.controlBack,
|
||||||
|
body: 'Motor Steering',
|
||||||
|
onTap: () {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
ChangeControlBackEvent(
|
||||||
|
deviceId: deviceId,
|
||||||
|
controlBack:
|
||||||
|
state.curtainModuleStatus.controlBack ==
|
||||||
|
'forward'
|
||||||
|
? 'back'
|
||||||
|
: 'forward',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PrefReversCardWidget(
|
||||||
|
title: formatDeviceType(
|
||||||
|
state.curtainModuleStatus.elecMachineryMode),
|
||||||
|
body: 'Motor Mode',
|
||||||
|
onTap: () => context.read<CurtainModuleBloc>().add(
|
||||||
|
ChangeElecMachineryModeEvent(
|
||||||
|
deviceId: deviceId,
|
||||||
|
elecMachineryMode:
|
||||||
|
state.curtainModuleStatus.elecMachineryMode ==
|
||||||
|
'dry_contact'
|
||||||
|
? 'strong_power'
|
||||||
|
: 'dry_contact',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DefaultContainer(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => AccurateCalibrationDialog(
|
||||||
|
deviceId: deviceId,
|
||||||
|
parentContext: context,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text('Accurte Calibration',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DefaultContainer(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => QuickCalibrationDialog(
|
||||||
|
timControl: state.curtainModuleStatus.trTimeControl,
|
||||||
|
deviceId: deviceId,
|
||||||
|
parentContext: context),
|
||||||
|
),
|
||||||
|
child: const Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text('Quick Calibration',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String formatDeviceType(String raw) {
|
||||||
|
return raw
|
||||||
|
.split('_')
|
||||||
|
.map((word) => word.isNotEmpty
|
||||||
|
? '${word[0].toUpperCase()}${word.substring(1)}'
|
||||||
|
: '')
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/calibrate_completed_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/number_input_textfield.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class QuickCalibratingDialog extends StatefulWidget {
|
||||||
|
final int timControl;
|
||||||
|
final String deviceId;
|
||||||
|
final BuildContext parentContext;
|
||||||
|
const QuickCalibratingDialog({
|
||||||
|
super.key,
|
||||||
|
required this.timControl,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.parentContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<QuickCalibratingDialog> createState() => _QuickCalibratingDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _QuickCalibratingDialogState extends State<QuickCalibratingDialog> {
|
||||||
|
late TextEditingController _controller;
|
||||||
|
String? _errorText;
|
||||||
|
|
||||||
|
void _onRightTap() {
|
||||||
|
final value = int.tryParse(_controller.text);
|
||||||
|
|
||||||
|
if (value == null || value < 10 || value > 120) {
|
||||||
|
setState(() {
|
||||||
|
_errorText = 'Number should be between 10 and 120';
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_errorText = null;
|
||||||
|
});
|
||||||
|
widget.parentContext.read<CurtainModuleBloc>().add(
|
||||||
|
ChangeTimerControlEvent(
|
||||||
|
deviceId: widget.deviceId,
|
||||||
|
timControl: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.of(widget.parentContext).pop();
|
||||||
|
showDialog(
|
||||||
|
context: widget.parentContext,
|
||||||
|
builder: (_) => CalibrateCompletedDialog(
|
||||||
|
parentContext: widget.parentContext,
|
||||||
|
deviceId: widget.deviceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_controller = TextEditingController(text: widget.timControl.toString());
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: AccurateDialogWidget(
|
||||||
|
title: 'Calibrating',
|
||||||
|
body: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'1. please Enter the Travel Time:',
|
||||||
|
style: TextStyle(color: ColorsManager.grayBorder),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Container(
|
||||||
|
width: 150,
|
||||||
|
height: 40,
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: NumberInputField(controller: _controller),
|
||||||
|
),
|
||||||
|
const Expanded(
|
||||||
|
child: Text(
|
||||||
|
'seconds',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_errorText != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
|
child: Text(
|
||||||
|
_errorText!,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.red,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
leftOnTap: () => Navigator.of(widget.parentContext).pop(),
|
||||||
|
rightOnTap: _onRightTap,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/quick_calibrating_dialog.dart';
|
||||||
|
|
||||||
|
class QuickCalibrationDialog extends StatelessWidget {
|
||||||
|
final int timControl;
|
||||||
|
final String deviceId;
|
||||||
|
final BuildContext parentContext;
|
||||||
|
const QuickCalibrationDialog({
|
||||||
|
super.key,
|
||||||
|
required this.timControl,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.parentContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: AccurateDialogWidget(
|
||||||
|
title: 'Quick Calibration',
|
||||||
|
body: const NormalTextBodyForDialog(
|
||||||
|
title: 'Prepare Calibration:',
|
||||||
|
step1:
|
||||||
|
'1. Confirm that the curtain is in the fully closed and suspended state.',
|
||||||
|
step2: '2. click Next to Start calibration.',
|
||||||
|
),
|
||||||
|
leftOnTap: () => Navigator.of(parentContext).pop(),
|
||||||
|
rightOnTap: () {
|
||||||
|
Navigator.of(parentContext).pop();
|
||||||
|
showDialog(
|
||||||
|
context: parentContext,
|
||||||
|
builder: (_) => QuickCalibratingDialog(
|
||||||
|
timControl: timControl,
|
||||||
|
deviceId: deviceId,
|
||||||
|
parentContext: parentContext,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -265,7 +265,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
|||||||
category: event.category,
|
category: event.category,
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
time: getTimeStampWithoutSeconds(dateTime).toString(),
|
time: getTimeStampWithoutSeconds(dateTime).toString(),
|
||||||
code: event.category,
|
code: event.code ?? event.category,
|
||||||
value: event.functionOn,
|
value: event.functionOn,
|
||||||
days: event.selectedDays);
|
days: event.selectedDays);
|
||||||
if (success) {
|
if (success) {
|
||||||
|
@ -70,17 +70,19 @@ class ScheduleAddEvent extends ScheduleEvent {
|
|||||||
final String category;
|
final String category;
|
||||||
final String time;
|
final String time;
|
||||||
final List<String> selectedDays;
|
final List<String> selectedDays;
|
||||||
final bool functionOn;
|
final dynamic functionOn;
|
||||||
|
final String? code;
|
||||||
|
|
||||||
const ScheduleAddEvent({
|
const ScheduleAddEvent({
|
||||||
required this.category,
|
required this.category,
|
||||||
required this.time,
|
required this.time,
|
||||||
required this.selectedDays,
|
required this.selectedDays,
|
||||||
required this.functionOn,
|
required this.functionOn,
|
||||||
|
required this.code,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [category, time, selectedDays, functionOn];
|
List<Object?> get props => [category, time, selectedDays, functionOn, code];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScheduleEditEvent extends ScheduleEvent {
|
class ScheduleEditEvent extends ScheduleEvent {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/schedule_device/bloc/schedule_bloc.dart';
|
import 'package:syncrow_web/pages/device_managment/schedule_device/bloc/schedule_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart';
|
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/count_down_inching_view.dart';
|
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/count_down_inching_view.dart';
|
||||||
@ -9,13 +10,19 @@ import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widg
|
|||||||
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart';
|
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_selector.dart';
|
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_selector.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart';
|
import 'package:syncrow_web/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
||||||
|
|
||||||
class BuildScheduleView extends StatelessWidget {
|
class BuildScheduleView extends StatelessWidget {
|
||||||
const BuildScheduleView(
|
const BuildScheduleView({
|
||||||
{super.key, required this.deviceUuid, required this.category});
|
super.key,
|
||||||
|
required this.deviceUuid,
|
||||||
|
required this.category,
|
||||||
|
this.code,
|
||||||
|
});
|
||||||
final String deviceUuid;
|
final String deviceUuid;
|
||||||
final String category;
|
final String category;
|
||||||
|
final String? code;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -57,13 +64,21 @@ class BuildScheduleView extends StatelessWidget {
|
|||||||
final entry = await ScheduleDialogHelper
|
final entry = await ScheduleDialogHelper
|
||||||
.showAddScheduleDialog(
|
.showAddScheduleDialog(
|
||||||
context,
|
context,
|
||||||
schedule: null,
|
schedule: ScheduleEntry(
|
||||||
|
category: category,
|
||||||
|
time: '',
|
||||||
|
function: Status(
|
||||||
|
code: code.toString(), value: null),
|
||||||
|
days: [],
|
||||||
|
),
|
||||||
isEdit: false,
|
isEdit: false,
|
||||||
|
code: code,
|
||||||
);
|
);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
context.read<ScheduleBloc>().add(
|
context.read<ScheduleBloc>().add(
|
||||||
ScheduleAddEvent(
|
ScheduleAddEvent(
|
||||||
category: entry.category,
|
category: category,
|
||||||
|
code: entry.function.code,
|
||||||
time: entry.time,
|
time: entry.time,
|
||||||
functionOn: entry.function.value,
|
functionOn: entry.function.value,
|
||||||
selectedDays: entry.days,
|
selectedDays: entry.days,
|
||||||
@ -74,7 +89,7 @@ class BuildScheduleView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (state.scheduleMode == ScheduleModes.countdown ||
|
if (state.scheduleMode == ScheduleModes.countdown ||
|
||||||
state.scheduleMode == ScheduleModes.inching)
|
state.scheduleMode == ScheduleModes.inching)
|
||||||
CountdownInchingView(
|
CountdownInchingView(
|
||||||
deviceId: deviceUuid,
|
deviceId: deviceUuid,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
@ -162,11 +162,18 @@ class _ScheduleTableView extends StatelessWidget {
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
bool temp;
|
||||||
|
if (schedule.category == 'CUR_2') {
|
||||||
|
temp = schedule.function.value == 'open' ? true : false;
|
||||||
|
} else {
|
||||||
|
temp = schedule.function.value as bool;
|
||||||
|
}
|
||||||
context.read<ScheduleBloc>().add(
|
context.read<ScheduleBloc>().add(
|
||||||
ScheduleUpdateEntryEvent(
|
ScheduleUpdateEntryEvent(
|
||||||
category: schedule.category,
|
category: schedule.category,
|
||||||
scheduleId: schedule.scheduleId,
|
scheduleId: schedule.scheduleId,
|
||||||
functionOn: schedule.function.value,
|
functionOn: temp,
|
||||||
|
// schedule.function.value,
|
||||||
enable: !schedule.enable,
|
enable: !schedule.enable,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -188,7 +195,10 @@ class _ScheduleTableView extends StatelessWidget {
|
|||||||
child: Text(_getSelectedDays(
|
child: Text(_getSelectedDays(
|
||||||
ScheduleModel.parseSelectedDays(schedule.days)))),
|
ScheduleModel.parseSelectedDays(schedule.days)))),
|
||||||
Center(child: Text(formatIsoStringToTime(schedule.time, context))),
|
Center(child: Text(formatIsoStringToTime(schedule.time, context))),
|
||||||
Center(child: Text(schedule.function.value ? 'On' : 'Off')),
|
schedule.category == 'CUR_2'
|
||||||
|
? Center(
|
||||||
|
child: Text(schedule.function.value == true ? 'open' : 'close'))
|
||||||
|
: Center(child: Text(schedule.function.value ? 'On' : 'Off')),
|
||||||
Center(
|
Center(
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
runAlignment: WrapAlignment.center,
|
runAlignment: WrapAlignment.center,
|
||||||
|
@ -4,7 +4,8 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
|||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class DeviceBatchControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
class DeviceBatchControlDialog extends StatelessWidget
|
||||||
|
with RouteControlsBasedCode {
|
||||||
final List<AllDevicesModel> devices;
|
final List<AllDevicesModel> devices;
|
||||||
|
|
||||||
const DeviceBatchControlDialog({super.key, required this.devices});
|
const DeviceBatchControlDialog({super.key, required this.devices});
|
||||||
@ -18,7 +19,7 @@ class DeviceBatchControlDialog extends StatelessWidget with RouteControlsBasedCo
|
|||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
),
|
),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: devices.length < 2 ? 500 : 800,
|
width: devices.length < 2 ? 600 : 800,
|
||||||
// height: context.screenHeight * 0.7,
|
// height: context.screenHeight * 0.7,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -17,6 +17,7 @@ class ScheduleDialogHelper {
|
|||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
ScheduleEntry? schedule,
|
ScheduleEntry? schedule,
|
||||||
bool isEdit = false,
|
bool isEdit = false,
|
||||||
|
String? code,
|
||||||
}) {
|
}) {
|
||||||
final initialTime = schedule != null
|
final initialTime = schedule != null
|
||||||
? _convertStringToTimeOfDay(schedule.time)
|
? _convertStringToTimeOfDay(schedule.time)
|
||||||
@ -96,7 +97,8 @@ class ScheduleDialogHelper {
|
|||||||
setState(() => selectedDays[i] = v);
|
setState(() => selectedDays[i] = v);
|
||||||
}),
|
}),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
_buildFunctionSwitch(ctx, functionOn!, (v) {
|
_buildFunctionSwitch(schedule!.category, ctx, functionOn!,
|
||||||
|
(v) {
|
||||||
setState(() => functionOn = v);
|
setState(() => functionOn = v);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -115,10 +117,21 @@ class ScheduleDialogHelper {
|
|||||||
width: 100,
|
width: 100,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
dynamic temp;
|
||||||
|
if (schedule?.category == 'CUR_2') {
|
||||||
|
temp = functionOn! ? 'open' : 'close';
|
||||||
|
} else {
|
||||||
|
temp = functionOn;
|
||||||
|
}
|
||||||
|
print(temp);
|
||||||
final entry = ScheduleEntry(
|
final entry = ScheduleEntry(
|
||||||
category: schedule?.category ?? 'switch_1',
|
category: schedule?.category ?? 'switch_1',
|
||||||
time: _formatTimeOfDayToISO(selectedTime),
|
time: _formatTimeOfDayToISO(selectedTime),
|
||||||
function: Status(code: 'switch_1', value: functionOn),
|
function: Status(
|
||||||
|
code: code ?? 'switch_1',
|
||||||
|
value: temp,
|
||||||
|
// functionOn,
|
||||||
|
),
|
||||||
days: _convertSelectedDaysToStrings(selectedDays),
|
days: _convertSelectedDaysToStrings(selectedDays),
|
||||||
scheduleId: schedule?.scheduleId,
|
scheduleId: schedule?.scheduleId,
|
||||||
);
|
);
|
||||||
@ -185,7 +198,7 @@ class ScheduleDialogHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Widget _buildFunctionSwitch(
|
static Widget _buildFunctionSwitch(
|
||||||
BuildContext ctx, bool isOn, Function(bool) onChanged) {
|
String categor, BuildContext ctx, bool isOn, Function(bool) onChanged) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
@ -199,14 +212,14 @@ class ScheduleDialogHelper {
|
|||||||
groupValue: isOn,
|
groupValue: isOn,
|
||||||
onChanged: (val) => onChanged(true),
|
onChanged: (val) => onChanged(true),
|
||||||
),
|
),
|
||||||
const Text('On'),
|
Text(categor == 'CUR_2' ? 'open' : 'On'),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Radio<bool>(
|
Radio<bool>(
|
||||||
value: false,
|
value: false,
|
||||||
groupValue: isOn,
|
groupValue: isOn,
|
||||||
onChanged: (val) => onChanged(false),
|
onChanged: (val) => onChanged(false),
|
||||||
),
|
),
|
||||||
const Text('Off'),
|
Text(categor == 'CUR_2' ? 'close' : 'Off'),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,10 @@ class DeviceModel {
|
|||||||
tempIcon = Assets.openedDoor;
|
tempIcon = Assets.openedDoor;
|
||||||
} else if (type == DeviceType.WaterLeak) {
|
} else if (type == DeviceType.WaterLeak) {
|
||||||
tempIcon = Assets.waterLeakNormal;
|
tempIcon = Assets.waterLeakNormal;
|
||||||
|
} else if (type == DeviceType.Curtain2) {
|
||||||
|
tempIcon = Assets.curtainIcon;
|
||||||
|
} else if (type == DeviceType.Curtain) {
|
||||||
|
tempIcon = Assets.curtainIcon;
|
||||||
} else {
|
} else {
|
||||||
tempIcon = Assets.blackLogo;
|
tempIcon = Assets.blackLogo;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,8 @@ abstract interface class BatchControlDevicesService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final class RemoteBatchControlDevicesService implements BatchControlDevicesService {
|
final class RemoteBatchControlDevicesService
|
||||||
|
implements BatchControlDevicesService {
|
||||||
@override
|
@override
|
||||||
Future<bool> batchControlDevices({
|
Future<bool> batchControlDevices({
|
||||||
required List<String> uuids,
|
required List<String> uuids,
|
||||||
|
@ -391,7 +391,7 @@ class DevicesManagementApi {
|
|||||||
required String deviceId,
|
required String deviceId,
|
||||||
required String time,
|
required String time,
|
||||||
required String code,
|
required String code,
|
||||||
required bool value,
|
required dynamic value,
|
||||||
required List<String> days,
|
required List<String> days,
|
||||||
}) async {
|
}) async {
|
||||||
final response = await HTTPService().post(
|
final response = await HTTPService().post(
|
||||||
|
@ -125,6 +125,10 @@ class Assets {
|
|||||||
static const String ac = 'assets/icons/AC.svg';
|
static const String ac = 'assets/icons/AC.svg';
|
||||||
//assets/icons/Curtain.svg
|
//assets/icons/Curtain.svg
|
||||||
static const String curtain = 'assets/icons/Curtain.svg';
|
static const String curtain = 'assets/icons/Curtain.svg';
|
||||||
|
static const String openCurtain = 'assets/icons/open_curtain.svg';
|
||||||
|
static const String pauseCurtain = 'assets/icons/pause_curtain.svg';
|
||||||
|
static const String closeCurtain = 'assets/icons/close_curtain.svg';
|
||||||
|
static const String reverseArrows = 'assets/icons/reverse_arrows.svg';
|
||||||
//assets/icons/doorLock.svg
|
//assets/icons/doorLock.svg
|
||||||
static const String doorLock = 'assets/icons/doorLock.svg';
|
static const String doorLock = 'assets/icons/doorLock.svg';
|
||||||
//assets/icons/Gateway.svg
|
//assets/icons/Gateway.svg
|
||||||
|
@ -3,6 +3,7 @@ enum DeviceType {
|
|||||||
LightBulb,
|
LightBulb,
|
||||||
DoorLock,
|
DoorLock,
|
||||||
Curtain,
|
Curtain,
|
||||||
|
Curtain2,
|
||||||
Blind,
|
Blind,
|
||||||
OneGang,
|
OneGang,
|
||||||
TwoGang,
|
TwoGang,
|
||||||
@ -44,6 +45,7 @@ enum DeviceType {
|
|||||||
|
|
||||||
Map<String, DeviceType> devicesTypesMap = {
|
Map<String, DeviceType> devicesTypesMap = {
|
||||||
"AC": DeviceType.AC,
|
"AC": DeviceType.AC,
|
||||||
|
"CUR_2": DeviceType.Curtain2,
|
||||||
"GW": DeviceType.Gateway,
|
"GW": DeviceType.Gateway,
|
||||||
"CPS": DeviceType.CeilingSensor,
|
"CPS": DeviceType.CeilingSensor,
|
||||||
"DL": DeviceType.DoorLock,
|
"DL": DeviceType.DoorLock,
|
||||||
|
Reference in New Issue
Block a user