Compare commits

..

33 Commits

Author SHA1 Message Date
a7080248cb SP-1477 2025-04-27 12:50:20 +03:00
25893e3bbd Merge pull request #90 from SyncrowIOT/sp-1268-rework-v3
SP-1268 Rework
2025-04-24 16:26:31 +03:00
c90f0874c2 Merge pull request #91 from SyncrowIOT/changed_titles_of_devices_view
Changed titles of devices view
2025-04-24 16:26:15 +03:00
214ec78a95 added title to four scene view. 2025-04-24 12:41:45 +03:00
ed9f98e653 Temporarily disable showing the NCPS device from all the all devices api, for release and demo purposes. Feel free to revert this commit once the device is needed back into the application. 2025-04-24 12:40:02 +03:00
a37236c8d2 changed titles of devices views, and responsiness of device card. 2025-04-24 12:12:56 +03:00
1f1a8a7208 SP-1268 2025-04-23 15:43:55 +03:00
e7efd2b3a1 Merge pull request #89 from SyncrowIOT/fix-device-ui-routine
Refactor device name display in Action and Condition models
2025-04-23 10:20:10 +03:00
2b0e504f05 Refactor device name display in Action and Condition models 2025-04-23 10:07:14 +03:00
a656d5981e Merge pull request #88 from SyncrowIOT/Fix-Save-Display-Condition
Fix save display condition
2025-04-22 15:42:42 +03:00
3f49a18130 remove unused code 2025-04-21 15:19:14 +03:00
a51c4d9679 Refactor device name display in RoomPageSwitch widget 2025-04-21 15:00:31 +03:00
caeed8e73f Merge pull request #87 from SyncrowIOT/FE-Complete-the-Device-Name-as
Refactor device name display in RoomPageSwitch widget
2025-04-21 10:43:22 +03:00
dc5ac9be5b Refactor device name display in RoomPageSwitch widget 2025-04-21 10:36:43 +03:00
5c9b30895f Merge pull request #86 from SyncrowIOT/SP-1286-FE-Add-Device-to-Routine-2-Gang
add tow gang and change gang device ui
2025-04-17 16:29:24 +03:00
06b14a3964 add tow gang and change gang device ui 2025-04-17 16:14:48 +03:00
3c92ea3047 Merge pull request #85 from SyncrowIOT/SP-1285-FE-Add-Device-to-Routine
Sp 1285 fe add device to routine
2025-04-17 10:42:06 +03:00
73d28a1800 Refactor Action class to use type annotations for actionExecutor and entityId 2025-04-17 10:41:01 +03:00
5493ca6fb0 Refactor device filtering logic to improve code readability 2025-04-17 10:38:25 +03:00
7005d8ba83 Refactor device filtering logic and improve code readability
- Extracted the logic for filtering implemented devices into a separate method `_getOnlyImplementedDevices`
- Created a set `allowedDevices` to store the allowed device types
- Updated the filtering logic to use the `allowedDevices` set for checking device types
- Removed unnecessary conditions for filtering devices

Fix nullability issues in `Action` model

- Added null checks for `actionExecutor`, `entityId`, `name`, `type`, and `productType` properties in the `fromJson` method of the `Action` model
- Set default values for `actionExecutor` and `entityId` if they are null
- Updated the type casting for `name`, `type`, and `productType` properties to avoid potential nullability issues
2025-04-17 10:16:10 +03:00
cb79627038 Merge pull request #83 from SyncrowIOT/SP-1397-FE-Create-Routine-SceneRoomsTabBarDevicesView-throws-TabBarController-initialization-error
Sp 1397 fe create routine scene rooms tab bar devices view throws tab bar controller initialization error
2025-04-17 09:47:46 +03:00
1cff69d496 change icon 2025-04-17 09:39:58 +03:00
60df77efad add OneGang product type to routine 2025-04-17 09:32:20 +03:00
9e84048f81 Merge pull request #84 from SyncrowIOT:SP-1416
fixed typo
2025-04-16 20:57:45 +04:00
f2412aa867 refactor: extract device fetching logic into a separate method for improved readability 2025-04-16 16:10:36 +03:00
e4768c95aa refactor: rename tab change events and states for consistency, and to match blocs naming convention. 2025-04-16 16:07:30 +03:00
f25b4dbf6d implement SceneDevicesBodyTabBar for improved tab management in SceneDevicesBody. 2025-04-16 15:57:21 +03:00
9eff9ab371 refactor: consolidate device loading and error handling in SceneDevicesBody and implement SceneDevicesList widget 2025-04-16 15:46:56 +03:00
a2e68d6194 fix: restore title in DefaultScaffold for SceneRoomsTabBarDevicesView 2025-04-16 15:25:23 +03:00
94c94b170f fix: correct order of dispose method calls in SceneRoomsTabBarDevicesView 2025-04-16 15:24:47 +03:00
253cf15559 refactor: simplify SceneDevicesBody widget structure and improve loading indicator handling 2025-04-16 15:24:37 +03:00
443eea9421 SP-1397 2025-04-16 15:11:43 +03:00
87a2c08f64 Merge pull request #82 from SyncrowIOT/SP-1416
added client token login
2025-04-16 12:35:31 +04:00
38 changed files with 1487 additions and 656 deletions

View File

@ -0,0 +1,10 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_7302_4118)">
<path d="M11.1049 10.0001L19.7712 1.33381C20.0763 1.02869 20.0763 0.534007 19.7712 0.228929C19.4661 -0.0761485 18.9714 -0.0761876 18.6663 0.228929L9.99999 8.89524L1.33372 0.228929C1.0286 -0.0761876 0.533915 -0.0761876 0.228837 0.228929C-0.0762401 0.534046 -0.0762792 1.02873 0.228837 1.33381L8.89511 10.0001L0.228837 18.6664C-0.0762792 18.9715 -0.0762792 19.4662 0.228837 19.7713C0.381376 19.9238 0.581337 20.0001 0.781297 20.0001C0.981258 20.0001 1.18118 19.9238 1.33376 19.7713L9.99999 11.105L18.6663 19.7713C18.8188 19.9238 19.0188 20.0001 19.2187 20.0001C19.4187 20.0001 19.6186 19.9238 19.7712 19.7713C20.0763 19.4662 20.0763 18.9715 19.7712 18.6664L11.1049 10.0001Z" fill="#999999"/>
</g>
<defs>
<clipPath id="clip0_7302_4118">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 939 B

View File

@ -0,0 +1,15 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.5419 20.2424C11.1182 19.3486 10.0545 18.9678 9.16647 19.3935L1.38334 23.1268C0.591819 23.3098 0 24.0197 0 24.8727V33.8466C0 34.8381 0.797668 35.6413 1.78229 35.6413C2.76668 35.6413 3.56412 34.8381 3.56412 33.8466V27.6644L9.16647 30.3516C9.41376 30.4704 9.67449 30.5264 9.93112 30.5264C10.5974 30.5264 11.2359 30.1484 11.5419 29.5025C11.9643 28.6076 11.5875 27.5367 10.699 27.1103L6.03295 24.8725L10.699 22.6346C11.5877 22.2085 11.9643 21.1376 11.5419 20.2424Z" fill="#D5D5D5"/>
<path d="M20.6084 17.6894C20.8655 17.6894 21.1257 17.6334 21.3737 17.5146L32.0481 12.3951C32.9366 11.9685 33.3141 10.8974 32.891 10.0024C32.4682 9.10864 31.4052 8.72813 30.5156 9.15359L19.8408 14.2736C18.9522 14.6999 18.5754 15.7706 18.998 16.6658C19.3038 17.3112 19.9423 17.6894 20.6084 17.6894Z" fill="#D5D5D5"/>
<path d="M42.7227 7.27445L48.1841 4.65449V10.9114C48.1841 11.9027 48.9818 12.7061 49.9659 12.7061C50.9503 12.7061 51.7478 11.9027 51.7478 10.9114V4.62192L57.2779 7.27445C57.5259 7.39303 57.7862 7.44922 58.0433 7.44922C58.7091 7.44922 59.3478 7.07124 59.6534 6.42537C60.0763 5.53041 59.6994 4.45953 58.8106 4.03316L50.7663 0.174427C50.2813 -0.0581424 49.7189 -0.0581424 49.2338 0.174427L41.19 4.03316C40.3015 4.45953 39.9244 5.53018 40.3472 6.42537C40.771 7.32101 41.8362 7.70198 42.7227 7.27445Z" fill="#D5D5D5"/>
<path d="M67.9533 12.3944L78.6281 17.5146C78.875 17.6334 79.1361 17.6894 79.3932 17.6894C80.059 17.6894 80.6978 17.3114 81.0034 16.6655C81.426 15.7706 81.0494 14.6997 80.1606 14.2733L69.4856 9.1529C68.5982 8.72721 67.5345 9.10726 67.1105 10.0018C66.6875 10.8967 67.0645 11.9676 67.9533 12.3944Z" fill="#D5D5D5"/>
<path d="M81.539 34.8099C82.4275 34.3837 82.8046 33.3128 82.3815 32.4177C81.9587 31.5232 80.8946 31.1424 80.0065 31.5695L73.167 34.8509L51.7477 24.577V16.5765C51.7477 15.5846 50.9503 14.7818 49.9659 14.7818C48.9817 14.7818 48.1841 15.5848 48.1841 16.5765V24.6096L26.8346 34.85L20.6999 31.9073C19.8093 31.481 18.7482 31.8612 18.3244 32.7564C17.9016 33.6514 18.2787 34.7225 19.1672 35.1489L25.8031 38.3319V61.6684L19.0593 64.9031C18.1707 65.3295 17.7939 66.4001 18.2165 67.2955C18.5221 67.9412 19.1606 68.3192 19.8266 68.3192C20.0837 68.3192 20.3447 68.2632 20.5917 68.1444L26.8349 65.1499L48.1841 75.3903V81.5075C48.1841 82.4993 48.9817 83.3022 49.9659 83.3022C50.9503 83.3022 51.7477 82.499 51.7477 81.5075V75.4233L73.1657 65.1501L79.6524 68.2616C79.9002 68.3802 80.1607 68.4364 80.4175 68.4364C81.0834 68.4364 81.7221 68.0584 82.0277 67.4125C82.4508 66.5175 82.0735 65.4467 81.1849 65.0201L74.1977 61.6687V38.3319L81.539 34.8099ZM70.634 62.3879L51.7479 71.4467V49.0994L70.634 40.0402V62.3879ZM29.4788 61.8939C29.4472 61.8269 29.4053 61.7691 29.3668 61.7081V40.0404L48.1841 49.0663V71.4141L29.6409 62.5198C29.6239 62.3086 29.5745 62.0964 29.4788 61.8939ZM50.0003 27.7151L69.0207 36.8383L50.0003 45.9611L30.9799 36.8383L50.0003 27.7151Z" fill="#D5D5D5"/>
<path d="M78.6271 82.486L67.9525 87.6062C67.0639 88.0328 66.6864 89.1036 67.1095 89.9979C67.4153 90.644 68.054 91.0215 68.7199 91.0215C68.9767 91.0215 69.2379 90.9656 69.485 90.8477L80.1598 85.7273C81.0483 85.3009 81.4252 84.2295 81.0025 83.3346C80.579 82.4401 79.5149 82.0616 78.6271 82.486Z" fill="#D5D5D5"/>
<path d="M57.2782 92.7266L51.748 95.3793V88.227C51.748 87.2357 50.9506 86.4323 49.9662 86.4323C48.982 86.4323 48.1844 87.2357 48.1844 88.227V95.3468L42.723 92.7266C41.8335 92.3023 40.7713 92.6807 40.3475 93.5759C39.9246 94.4709 40.3015 95.5413 41.1903 95.9676L49.2346 99.8259C49.4769 99.9417 49.7387 100 50.0008 100C50.2629 100 50.5248 99.9417 50.7671 99.8259L58.8114 95.9676C59.6999 95.5413 60.077 94.4709 59.6541 93.5759C59.2304 92.6807 58.166 92.3032 57.2782 92.7266Z" fill="#D5D5D5"/>
<path d="M32.0476 87.6061L21.3728 82.4859C20.4863 82.0605 19.4217 82.4394 18.9975 83.3343C18.5749 84.2293 18.9517 85.3006 19.8403 85.727L30.5153 90.8474C30.7622 90.9653 31.0233 91.0213 31.2802 91.0213C31.946 91.0213 32.5847 90.6438 32.8903 89.9977C33.3132 89.1036 32.9361 88.0328 32.0476 87.6061Z" fill="#D5D5D5"/>
<path d="M11.5419 70.4974C11.1182 69.6029 10.0545 69.224 9.16647 69.6488L3.56412 72.3355V65.6178C3.56412 64.6265 2.76668 63.823 1.78229 63.823C0.797668 63.823 0 64.6265 0 65.6178V74.5919C0 74.8162 0.0457694 75.0284 0.120686 75.2263C0.156892 75.8784 0.53193 76.4651 1.12215 76.7486L9.16647 80.6073C9.41376 80.7254 9.67449 80.7818 9.93112 80.7818C10.5974 80.7818 11.2359 80.4038 11.5419 79.7582C11.9643 78.8632 11.5875 77.7924 10.699 77.3655L6.03295 75.1281L10.699 72.8903C11.5877 72.4637 11.9643 71.3923 11.5419 70.4974Z" fill="#D5D5D5"/>
<path d="M1.78229 56.8226C2.76668 56.8226 3.56412 56.0193 3.56412 55.0274V44.4374C3.56412 43.4461 2.76668 42.6427 1.78229 42.6427C0.797668 42.6427 0 43.4461 0 44.4374V55.0274C0.00022771 56.0193 0.797896 56.8226 1.78229 56.8226Z" fill="#D5D5D5"/>
<path d="M98.6173 23.1268L90.8342 19.3935C89.9443 18.9667 88.8825 19.3479 88.4589 20.2424C88.0363 21.1378 88.4132 22.2084 89.3017 22.6351L93.9677 24.8729L89.3017 27.1108C88.4132 27.5371 88.0363 28.6078 88.4589 29.503C88.7645 30.1488 89.4032 30.5268 90.0691 30.5268C90.3262 30.5268 90.5873 30.4709 90.8342 30.3521L96.4365 27.6649V33.847C96.4365 34.8385 97.234 35.6418 98.2184 35.6418C99.2027 35.6418 100 34.8385 100 33.847V24.8731C100 24.0197 99.4088 23.3098 98.6173 23.1268Z" fill="#D5D5D5"/>
<path d="M98.2184 42.642C97.2337 42.642 96.4365 43.4455 96.4365 44.4367V55.0267C96.4365 56.0187 97.234 56.8214 98.2184 56.8214C99.2027 56.8214 100 56.0184 100 55.0267V44.4367C100 43.4455 99.203 42.642 98.2184 42.642Z" fill="#D5D5D5"/>
<path d="M98.2184 63.8228C97.2337 63.8228 96.4365 64.6263 96.4365 65.6175V72.3352L90.8342 69.6485C89.9443 69.2231 88.8825 69.6022 88.4589 70.4971C88.0363 71.3921 88.4132 72.4634 89.3017 72.8898L93.9677 75.1277L89.3017 77.3651C88.4132 77.7919 88.0363 78.8628 88.4589 79.7577C88.7645 80.4034 89.4032 80.7814 90.0691 80.7814C90.3262 80.7814 90.5873 80.7249 90.8342 80.6068L98.8785 76.7481C99.4687 76.4646 99.8438 75.8779 99.88 75.2258C99.9546 75.0279 100 74.8157 100 74.5914V65.6173C100 64.6263 99.203 63.8228 98.2184 63.8228Z" fill="#D5D5D5"/>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,10 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_7302_4149)">
<path d="M19.9969 4.94279C19.986 4.83232 19.943 4.72146 19.8674 4.62924C19.8382 4.59358 20.1367 4.89431 15.4141 0.17176C15.3015 0.0591838 15.1487 8.30277e-05 14.9996 0.00012209C14.6443 0.00012209 2.11177 0.00012209 1.75779 0.00012209C0.788544 0.00012209 0 0.788666 0 1.75791V18.2421C0 19.2113 0.788544 19.9998 1.75779 19.9998H18.2419C19.2112 19.9998 19.9997 19.2113 19.9997 18.2421C19.9998 4.01624 20.0013 4.98771 19.9969 4.94279ZM13.4764 1.17198V5.00005C13.4764 5.32314 13.2136 5.58598 12.8905 5.58598H12.1483V1.17198H13.4764ZM10.9765 1.17198V5.58598H5.54684C5.22376 5.58598 4.96091 5.32314 4.96091 5.00005V1.17198H10.9765ZM13.4764 12.539H4.96091V11.7968C4.96091 11.4738 5.22376 11.2109 5.54684 11.2109H12.8905C13.2136 11.2109 13.4764 11.4738 13.4764 11.7968V12.539ZM4.96091 18.828V13.7109H13.4764V18.828H4.96091ZM18.8279 18.2421C18.8279 18.5651 18.5651 18.828 18.242 18.828H14.6483C14.6483 18.0641 14.6483 12.6676 14.6483 11.7968C14.6483 10.8276 13.8597 10.039 12.8905 10.039H5.54684C4.57759 10.039 3.78905 10.8276 3.78905 11.7968V18.828H1.75783C1.43475 18.828 1.1719 18.5651 1.1719 18.2421V1.75791C1.1719 1.43483 1.43475 1.17198 1.75783 1.17198H3.78905V5.00005C3.78905 5.9693 4.57759 6.75784 5.54684 6.75784H12.8905C13.8597 6.75784 14.6483 5.9693 14.6483 5.00005V1.17198H14.7571L18.8279 5.24278V18.2421Z" fill="#023DFE" fill-opacity="0.6"/>
</g>
<defs>
<clipPath id="clip0_7302_4149">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -22,7 +22,7 @@ import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_e
import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart';
import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart';
import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart';
import 'package:syncrow_app/features/scene/view/scene_view.dart';
import 'package:syncrow_app/features/scene/view/routines_view.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/navigation/navigation_service.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
@ -580,7 +580,7 @@ class HomeCubit extends Cubit<HomeState> {
create: (context) => DevicesCubit.getInstance(),
child: const DevicesViewBody(),
),
const SceneView(),
const RoutinesView(),
const MenuView(),
];

View File

@ -169,18 +169,20 @@ class DeviceManagerBloc extends Bloc<DeviceManagerEvent, DeviceManagerState> {
}
}
_getOnlyImplementedDevices(List<DeviceModel> devices) {
List<DeviceModel> implementedDevices = [];
for (int i = 0; i < devices.length; i++) {
if (devices[i].productType == DeviceType.AC ||
devices[i].productType == DeviceType.DoorLock ||
devices[i].productType == DeviceType.Gateway ||
devices[i].productType == DeviceType.WallSensor ||
devices[i].productType == DeviceType.CeilingSensor ||
devices[i].productType == DeviceType.ThreeGang) {
implementedDevices.add(devices[i]);
}
}
return implementedDevices;
List<DeviceModel> _getOnlyImplementedDevices(List<DeviceModel> devices) {
const allowedDeviceTypes = {
DeviceType.AC,
DeviceType.DoorLock,
DeviceType.Gateway,
DeviceType.WallSensor,
DeviceType.CeilingSensor,
DeviceType.ThreeGang,
DeviceType.OneGang,
DeviceType.TwoGang
};
return devices
.where((device) => allowedDeviceTypes.contains(device.productType))
.toList();
}
}

View File

@ -1,6 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/6_scene_switch_bloc/6_scene_bloc.dart';
@ -31,7 +29,7 @@ class SixSceneScreen extends StatelessWidget {
..add(const SexSceneSwitchInitial()),
child: BlocBuilder<SixSceneBloc, SixSceneState>(
builder: (context, state) {
final _bloc = BlocProvider.of<SixSceneBloc>(context);
final bloc = BlocProvider.of<SixSceneBloc>(context);
SixSceneModel model = SixSceneModel(
scene_1: '',
scene_2: '',
@ -48,7 +46,7 @@ class SixSceneScreen extends StatelessWidget {
model = state.device;
}
return DefaultScaffold(
title: _bloc.deviceInfo.name,
title: device?.name ?? '6 Scene Switch',
actions: [
InkWell(
onTap: () async {
@ -58,9 +56,9 @@ class SixSceneScreen extends StatelessWidget {
),
);
if (val == null) {
_bloc.add(const SixSceneInitialInfo());
_bloc.add(const SixSceneInitial());
_bloc.add(const SexSceneSwitchInitial());
bloc.add(const SixSceneInitialInfo());
bloc.add(const SixSceneInitial());
bloc.add(const SexSceneSwitchInitial());
}
},
child: SvgPicture.asset(Assets.assetsIconsSettings),
@ -77,7 +75,7 @@ class SixSceneScreen extends StatelessWidget {
)
: RefreshIndicator(
onRefresh: () async {
_bloc.add(const SixSceneInitial());
bloc.add(const SixSceneInitial());
},
child: ListView(
children: [
@ -92,22 +90,22 @@ class SixSceneScreen extends StatelessWidget {
switch4Title: model.scene_4,
switch5Title: model.scene_5,
switch6Title: model.scene_6,
switch1Down: _bloc.deviceStatus.switch_backlight
switch1Down: bloc.deviceStatus.switch_backlight
? Assets.switchOn
: Assets.switchOff,
switch1Up: _bloc.deviceStatus.switch_backlight
switch1Up: bloc.deviceStatus.switch_backlight
? Assets.switchOn
: Assets.switchOff,
switch2Down: _bloc.deviceStatus.switch_backlight
switch2Down: bloc.deviceStatus.switch_backlight
? Assets.switchOn
: Assets.switchOff,
switch2Up: _bloc.deviceStatus.switch_backlight
switch2Up: bloc.deviceStatus.switch_backlight
? Assets.switchOn
: Assets.switchOff,
switch3Up: _bloc.deviceStatus.switch_backlight
switch3Up: bloc.deviceStatus.switch_backlight
? Assets.switchOn
: Assets.switchOff,
switch3Down: _bloc.deviceStatus.switch_backlight
switch3Down: bloc.deviceStatus.switch_backlight
? Assets.switchOn
: Assets.switchOff,
onSwitch3DownTap: () {
@ -135,7 +133,7 @@ class SixSceneScreen extends StatelessWidget {
Expanded(
child: DefaultContainer(
onTap: () {
_bloc.add(ChangeSwitchStatusEvent());
bloc.add(ChangeSwitchStatusEvent());
},
child: Column(
crossAxisAlignment:
@ -197,9 +195,9 @@ class SixSceneScreen extends StatelessWidget {
);
if (value == true) {
Future.delayed(
const Duration(
milliseconds: 200), () {
_bloc.add(
const Duration(milliseconds: 200),
() {
bloc.add(
const SexSceneSwitchInitial());
});
}

View File

@ -22,14 +22,15 @@ class DoorSensorScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Door Sensor',
title: device?.name,
child: BlocProvider(
create: (context) =>
DoorSensorBloc(DSId: device?.uuid ?? '')..add(const DoorSensorInitial()),
child: BlocBuilder<DoorSensorBloc, DoorSensorState>(
builder: (context, state) {
final doorSensorBloc = BlocProvider.of<DoorSensorBloc>(context);
DoorSensorModel model = DoorSensorModel(batteryPercentage: 0, doorContactState: false);
DoorSensorModel model =
DoorSensorModel(batteryPercentage: 0, doorContactState: false);
if (state is LoadingNewSate) {
model = state.doorSensor;
} else if (state is UpdateState) {
@ -37,8 +38,8 @@ class DoorSensorScreen extends StatelessWidget {
}
return state is DoorSensorLoadingState
? const Center(
child:
DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()),
child: DefaultContainer(
width: 50, height: 50, child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {
@ -56,7 +57,8 @@ class DoorSensorScreen extends StatelessWidget {
Expanded(
flex: 4,
child: InkWell(
overlayColor: WidgetStateProperty.all(Colors.transparent),
overlayColor:
WidgetStateProperty.all(Colors.transparent),
onTap: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
@ -107,17 +109,21 @@ class DoorSensorScreen extends StatelessWidget {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
DoorRecordsScreen(DSId: device!.uuid!)),
DoorRecordsScreen(
DSId: device!.uuid!)),
);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints:
const BoxConstraints(maxHeight: 46, maxWidth: 50),
child: SvgPicture.asset(Assets.doorRecordsIcon),
constraints: const BoxConstraints(
maxHeight: 46, maxWidth: 50),
child: SvgPicture.asset(
Assets.doorRecordsIcon),
),
const SizedBox(
height: 15,
@ -143,18 +149,21 @@ class DoorSensorScreen extends StatelessWidget {
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => NotificationSettingsPage()),
builder: (context) =>
NotificationSettingsPage()),
);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints:
const BoxConstraints(maxHeight: 46, maxWidth: 50),
child:
SvgPicture.asset(Assets.doorNotificationSetting),
constraints: const BoxConstraints(
maxHeight: 46, maxWidth: 50),
child: SvgPicture.asset(
Assets.doorNotificationSetting),
),
const SizedBox(
height: 15,

View File

@ -47,7 +47,7 @@ class FourSceneScreen extends StatelessWidget {
}
return DefaultScaffold(
title: _bloc.deviceInfo.name,
title: device?.name ?? '4 Scene',
actions: [
InkWell(
onTap: () async {
@ -165,7 +165,7 @@ class FourSceneScreen extends StatelessWidget {
Navigator.of(context).pop();
},
confirmTab:
(switchSelected) async {
(switchSelected) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>

View File

@ -21,7 +21,8 @@ class GateWayView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => GatewayBloc()..add(GatewayInitial(gatewayId: gatewayObj.uuid ?? '')),
create: (context) =>
GatewayBloc()..add(GatewayInitial(gatewayId: gatewayObj.uuid ?? '')),
child: BlocBuilder<GatewayBloc, GatewayState>(builder: (context, state) {
List<DeviceModel> devicesList = [];
if (state is UpdateGatewayState) {
@ -37,7 +38,7 @@ class GateWayView extends StatelessWidget {
extendBodyBehindAppBar: true,
extendBody: true,
appBar: DeviceAppbar(
deviceName: 'Gateway',
deviceName: gatewayObj.name ?? 'Gateway',
deviceUuid: gatewayObj.uuid!,
),
body: Container(
@ -60,7 +61,7 @@ class GateWayView extends StatelessWidget {
},
child: ListView(
children: [
Container(
SizedBox(
height: MediaQuery.of(context).size.height,
child: SafeArea(
child: Column(
@ -90,7 +91,8 @@ class GateWayView extends StatelessWidget {
),
]),
),
if (devicesList.isEmpty && state is UpdateGatewayState)
if (devicesList.isEmpty &&
state is UpdateGatewayState)
Container(
width: MediaQuery.sizeOf(context).width,
alignment: AlignmentDirectional.center,
@ -115,7 +117,8 @@ class GateWayView extends StatelessWidget {
margin: const EdgeInsets.only(top: 20),
height: 50,
width: 50,
child: const RefreshProgressIndicator()),
child:
const RefreshProgressIndicator()),
)
: Expanded(
child: GridView.builder(
@ -130,7 +133,8 @@ class GateWayView extends StatelessWidget {
shrinkWrap: true,
itemCount: devicesList.length,
itemBuilder: (context, index) {
return RoomPageSwitch(device: devicesList[index]);
return RoomPageSwitch(
device: devicesList[index]);
},
),
)

View File

@ -51,7 +51,7 @@ class _PowerClampPageState extends State<PowerClampPage> {
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Power Clamp',
title: widget.device?.name ?? 'Power Clamp',
child: BlocProvider(
create: (context) => PowerClampBloc(PCId: widget.device?.uuid ?? '')
..add(const PowerClampInitial()),
@ -153,41 +153,6 @@ class _PowerClampPageState extends State<PowerClampPage> {
];
}
List<Widget> _buildPowerClampCards(PowerClampModel model,
List<EnergyData> chartData, PowerClampBloc blocProvider) {
return [
_buildPowerClampCard(
phaseType: '',
title: 'Total Energy \nConsumption',
phase: model.status.general,
isGeneral: true,
chartData: chartData,
blocProvider: blocProvider,
),
_buildPowerClampCard(
phaseType: 'Phase A consumption',
title: 'Phase A Energy \nConsumption',
phase: model.status.phaseA,
chartData: chartData,
blocProvider: blocProvider,
),
_buildPowerClampCard(
phaseType: 'Phase B consumption',
title: 'Phase B Energy \nConsumption',
phase: model.status.phaseB,
chartData: chartData,
blocProvider: blocProvider,
),
_buildPowerClampCard(
phaseType: 'Phase C consumption',
title: 'Phase C Energy \nConsumption',
phase: model.status.phaseC,
chartData: chartData,
blocProvider: blocProvider,
),
];
}
Widget _buildPowerClampCard({
required String title,
required String phaseType,
@ -209,10 +174,8 @@ class _PowerClampPageState extends State<PowerClampPage> {
blocProvider.add(SelectDateEvent(context: context));
},
totalActiveGeneral: isGeneral ? _getValueOrNA(phase.dataPoints, 2) : null,
totalCurrentGeneral:
isGeneral ? _getValueOrNA(phase.dataPoints, 1) : null,
totalFrequencyGeneral:
isGeneral ? _getValueOrNA(phase.dataPoints, 4) : null,
totalCurrentGeneral: isGeneral ? _getValueOrNA(phase.dataPoints, 1) : null,
totalFrequencyGeneral: isGeneral ? _getValueOrNA(phase.dataPoints, 4) : null,
totalFactor: !isGeneral ? _getValueOrNA(phase.dataPoints, 3) : null,
totalActive: !isGeneral ? _getValueOrNA(phase.dataPoints, 2) : null,
totalCurrent: !isGeneral ? _getValueOrNA(phase.dataPoints, 1) : null,
@ -223,9 +186,7 @@ class _PowerClampPageState extends State<PowerClampPage> {
}
String _getValueOrNA(List<DataPoint> dataPoints, int index) {
return dataPoints.length > index
? dataPoints[index].value.toString()
: 'N/A';
return dataPoints.length > index ? dataPoints[index].value.toString() : 'N/A';
}
Widget _buildPageIndicator() {
@ -240,8 +201,7 @@ class _PowerClampPageState extends State<PowerClampPage> {
height: 10.0,
width: _currentPage == index ? 10.0 : 10.0,
decoration: BoxDecoration(
color:
_currentPage == index ? Colors.grey : ColorsManager.greyColor,
color: _currentPage == index ? Colors.grey : ColorsManager.greyColor,
borderRadius: BorderRadius.circular(5.0),
),
);

View File

@ -10,6 +10,7 @@ import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/6_scene_switch/six_scene_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/door_sensor/door_sensor_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/four_scene_switch/four_scene_screen.dart';
@ -19,14 +20,13 @@ import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface
import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_Interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/one_touch/one_touch_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/power_clamp/power_clamp_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/three_gang/three_gang_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/three_touch/three_touch_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_Interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/two_touch/two_touch_Interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/wall_sensor/wall_sensor_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/three_gang/three_gang_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/water_heater/water_heater_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/water_leak/water_leak_screen.dart';
import 'package:syncrow_app/features/shared_widgets/custom_switch.dart';
@ -73,34 +73,30 @@ class RoomPageSwitch extends StatelessWidget {
? CustomSwitch(
device: device,
)
: const SizedBox(),
: const SizedBox.shrink(),
],
),
Flexible(
child: FittedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
device.name ?? "",
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: context.bodyLarge.copyWith(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.grey,
),
),
Text(
device.subspace!.subspaceName ?? '',
overflow: TextOverflow.ellipsis,
style: context.bodySmall.copyWith(
fontWeight: FontWeight.w400,
fontSize: 10,
color: Colors.grey,
),
),
],
Text(
device.name ?? '',
textScaler: TextScaler.linear(1),
style: context.bodyLarge.copyWith(
fontWeight: FontWeight.bold,
color: Colors.grey,
fontSize: 14,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
device.subspace!.subspaceName ?? '',
overflow: TextOverflow.ellipsis,
textScaler: TextScaler.linear(1),
style: context.bodySmall.copyWith(
fontWeight: FontWeight.w400,
fontSize: 9,
color: Colors.grey,
),
),
),
@ -119,8 +115,6 @@ Future<void> showDeviceInterface(
required BuildContext context,
required isAllDevices,
List<DeviceModel>? allDevices}) async {
print('object-----${device.uuid} ${device.name}');
final devicesCubit = context.read<DevicesCubit>();
switch (device.productType) {

View File

@ -5,7 +5,6 @@ import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/sos_model.dart';
import 'package:syncrow_app/features/devices/view/device_settings/settings_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_alarm_management_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_records_screen.dart';
@ -29,17 +28,8 @@ class SosScreen extends StatelessWidget {
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final sensor = BlocProvider.of<SosBloc>(context);
SosModel model = SosModel(
batteryPercentage: 0,
sosContactState: '',
);
if (state is LoadingNewSate) {
model = state.sosSensor;
} else if (state is UpdateState) {
model = state.sensor;
}
return DefaultScaffold(
title: sensor.deviceInfo.name,
title: device?.name,
actions: [
InkWell(
onTap: () async {
@ -82,38 +72,33 @@ class SosScreen extends StatelessWidget {
Expanded(
flex: 4,
child: InkWell(
overlayColor: MaterialStateProperty.all(
Colors.transparent),
overlayColor:
WidgetStateProperty.all(Colors.transparent),
onTap: () {
// Add functionality for the main SOS button here
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(890),
borderRadius: BorderRadius.circular(890),
boxShadow: [
BoxShadow(
color:
Colors.white.withOpacity(0.1),
color: Colors.white.withOpacity(0.1),
blurRadius: 24,
offset: const Offset(-5, -5),
blurStyle: BlurStyle.outer,
),
BoxShadow(
color: Colors.black
.withOpacity(0.11),
color: Colors.black.withOpacity(0.11),
blurRadius: 25,
offset: const Offset(5, 5),
blurStyle: BlurStyle.outer,
),
BoxShadow(
color: Colors.black
.withOpacity(0.13),
color: Colors.black.withOpacity(0.13),
blurRadius: 30,
offset: const Offset(5, 5),
blurStyle: BlurStyle.inner,
@ -140,9 +125,8 @@ class SosScreen extends StatelessWidget {
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
SosRecordsScreen(
sosId: device!.uuid!),
builder: (context) => SosRecordsScreen(
sosId: device!.uuid!),
),
);
},
@ -197,8 +181,8 @@ class SosScreen extends StatelessWidget {
maxHeight: 46,
maxWidth: 50,
),
child: SvgPicture.asset(Assets
.doorNotificationSetting),
child: SvgPicture.asset(
Assets.doorNotificationSetting),
),
const SizedBox(height: 15),
const Flexible(

View File

@ -9,7 +9,10 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class GangSwitch extends StatelessWidget {
const GangSwitch(
{super.key, required this.threeGangSwitch, required this.value, required this.action});
{super.key,
required this.threeGangSwitch,
required this.value,
required this.action});
final DeviceModel threeGangSwitch;
final bool value;

View File

@ -21,7 +21,7 @@ class WaterHeaterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Water heater',
title: device?.name?? 'Water heater',
child: BlocProvider(
create: (context) => WaterHeaterBloc(switchCode: 'switch_1', whId: device?.uuid ?? '')
..add(const WaterHeaterInitial()),

View File

@ -22,7 +22,7 @@ class WaterLeakScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Water Leak Sensor',
title: device?.name ?? 'Water Leak',
child: BlocProvider(
create: (context) => WaterLeakBloc(WLId: device?.uuid ?? '')
..add(const WaterLeakInitial()),

View File

@ -1,5 +1,3 @@
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_event.dart';
@ -7,19 +5,31 @@ import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart';
class TabBarBloc extends Bloc<TabBarEvent, TabBarState> {
final DeviceManagerBloc deviceManagerBloc;
TabBarBloc(this.deviceManagerBloc) : super(const Initial()) {
on<TabChanged>(_handleTabChanged);
TabBarBloc(this.deviceManagerBloc) : super(const TabBarInitialState()) {
on<TabBarTabChangedEvent>(_onTabBarTabChangedEvent);
}
FutureOr<void> _handleTabChanged(
TabChanged event, Emitter<TabBarState> emit) {
final DeviceManagerBloc deviceManagerBloc;
void _onTabBarTabChangedEvent(
TabBarTabChangedEvent event,
Emitter<TabBarState> emit,
) {
_getDevices(event);
emit(
TabBarTabSelectedState(
roomId: event.roomId,
selectedTabIndex: event.selectedIndex,
),
);
}
void _getDevices(TabBarTabChangedEvent event) {
if (event.roomId == "-1") {
deviceManagerBloc.add(FetchAllDevices());
} else {
deviceManagerBloc.add(FetchDevicesByRoomId(event.roomId,event.unit));
deviceManagerBloc.add(FetchDevicesByRoomId(event.roomId, event.unit));
}
emit(TabSelected(
roomId: event.roomId, selectedTabIndex: event.selectedIndex));
}
}

View File

@ -4,10 +4,14 @@ abstract class TabBarEvent {
const TabBarEvent();
}
class TabChanged extends TabBarEvent {
class TabBarTabChangedEvent extends TabBarEvent {
const TabBarTabChangedEvent({
required this.selectedIndex,
required this.roomId,
required this.unit,
});
final int selectedIndex;
final String roomId;
final SpaceModel unit;
const TabChanged(
{required this.selectedIndex, required this.roomId, required this.unit});
}

View File

@ -2,12 +2,16 @@ abstract class TabBarState {
const TabBarState();
}
class Initial extends TabBarState {
const Initial();
class TabBarInitialState extends TabBarState {
const TabBarInitialState();
}
class TabSelected extends TabBarState {
class TabBarTabSelectedState extends TabBarState {
const TabBarTabSelectedState({
required this.roomId,
required this.selectedTabIndex,
});
final int selectedTabIndex;
final String roomId;
const TabSelected({required this.roomId, required this.selectedTabIndex});
}

View File

@ -0,0 +1,78 @@
import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/generated/assets.dart';
class OneGangHelperFunctions {
static List<SceneStaticFunction> oneGangHelperFunctions(
String deviceId, String deviceName, functionValue) {
return [
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'Light Switch',
code: 'switch_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'Light CountDown',
code: 'countdown_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.countdown,
operationalValues: [
SceneOperationalValue(icon: '', value: 0),
],
),
];
}
static List<SceneStaticFunction> oneGangAutomationFunctions(
String deviceId, String deviceName, functionValue) {
return [
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'Light Switch',
code: 'switch_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'Light CountDown',
code: 'countdown_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.integerSteps,
operationalValues: [
SceneOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
),
],
),
];
}
}

View File

@ -10,7 +10,7 @@ class ThreeGangHelperFunctions {
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'Light 1 Switch',
operationName: 'L - Light Switch',
code: 'switch_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
@ -21,11 +21,23 @@ class ThreeGangHelperFunctions {
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'L - Light Countdown',
code: 'countdown_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.countdown,
operationalValues: [
SceneOperationalValue(icon: '', value: 0),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'Light 2 Switch',
operationName: 'M - Light Switch',
code: 'switch_2',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
@ -36,11 +48,23 @@ class ThreeGangHelperFunctions {
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'M - Light Countdown',
code: 'countdown_2',
functionValue: functionValue,
operationDialogType: OperationDialogType.countdown,
operationalValues: [
SceneOperationalValue(icon: '', value: 0),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'Light 3 Switch',
operationName: 'R - Light Switch',
code: 'switch_3',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
@ -55,31 +79,7 @@ class ThreeGangHelperFunctions {
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'Light 1 CountDown',
code: 'countdown_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.countdown,
operationalValues: [
SceneOperationalValue(icon: '', value: 0),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'Light 2 CountDown',
code: 'countdown_2',
functionValue: functionValue,
operationDialogType: OperationDialogType.countdown,
operationalValues: [
SceneOperationalValue(icon: '', value: 0),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'Light 3 CountDown',
operationName: 'R - Light Countdown',
code: 'countdown_3',
functionValue: functionValue,
operationDialogType: OperationDialogType.countdown,
@ -97,7 +97,7 @@ class ThreeGangHelperFunctions {
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'Light 1 Switch',
operationName: 'L - Light Switch',
code: 'switch_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
@ -108,41 +108,11 @@ class ThreeGangHelperFunctions {
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'Light 2 Switch',
code: 'switch_2',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'Light 3 Switch',
code: 'switch_3',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'Light 1 CountDown',
operationName: 'L - Light Countdown',
code: 'countdown_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.integerSteps,
@ -157,11 +127,26 @@ class ThreeGangHelperFunctions {
),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'M - Light Switch',
code: 'switch_2',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'Light 2 CountDown',
operationName: 'M - Light Countdown',
code: 'countdown_2',
functionValue: functionValue,
operationDialogType: OperationDialogType.integerSteps,
@ -176,11 +161,26 @@ class ThreeGangHelperFunctions {
),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'R - Light Switch',
code: 'switch_3',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'Light 3 CountDown',
operationName: 'R - Light Countdown',
code: 'countdown_3',
functionValue: functionValue,
operationDialogType: OperationDialogType.integerSteps,

View File

@ -0,0 +1,139 @@
import 'package:syncrow_app/features/scene/enum/operation_dialog_type.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/generated/assets.dart';
class TowGangHelperFunctions {
static List<SceneStaticFunction> towGangHelperFunctions(
String deviceId, String deviceName, functionValue) {
return [
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'L - Light Switch',
code: 'switch_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'L - Light CountDown',
code: 'countdown_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.countdown,
operationalValues: [
SceneOperationalValue(icon: '', value: 0),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'R - Light Switch',
code: 'switch_2',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'R - Light CountDown',
code: 'countdown_2',
functionValue: functionValue,
operationDialogType: OperationDialogType.countdown,
operationalValues: [
SceneOperationalValue(icon: '', value: 0),
],
),
];
}
static List<SceneStaticFunction> towGangAutomationFunctions(
String deviceId, String deviceName, functionValue) {
return [
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'L - Light Switch',
code: 'switch_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'R - Light CountDown',
code: 'countdown_1',
functionValue: functionValue,
operationDialogType: OperationDialogType.integerSteps,
operationalValues: [
SceneOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
operationName: 'R - Light Switch',
code: 'switch_2',
functionValue: functionValue,
operationDialogType: OperationDialogType.onOff,
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
operationName: 'R - Light CountDown',
code: 'countdown_2',
functionValue: functionValue,
operationDialogType: OperationDialogType.integerSteps,
operationalValues: [
SceneOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
),
],
),
];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -74,6 +74,9 @@ class Action {
ExecutorProperty? executorProperty;
String? name;
String? type;
final String productType;
final String deviceName;
Action({
required this.actionExecutor,
@ -81,6 +84,8 @@ class Action {
this.executorProperty,
this.name,
this.type,
required this.productType,
required this.deviceName,
});
String toRawJson() => json.encode(toJson());
@ -88,10 +93,12 @@ class Action {
static Action? fromJson(Map<String, dynamic> json) {
if (json['name'] != null && json['type'] != null) {
return Action(
actionExecutor: json["actionExecutor"],
entityId: json["entityId"],
name: json['name'],
type: json['type'],
actionExecutor: json["actionExecutor"] as String,
entityId: json["entityId"] as String,
name: json['name'] as String?,
type: json['type'] as String?,
productType: json['productType'] as String,
deviceName: json['deviceName'] as String,
);
}
if (json["executorProperty"] == null) {
@ -99,9 +106,11 @@ class Action {
}
return Action(
actionExecutor: json["actionExecutor"],
entityId: json["entityId"],
actionExecutor: json["actionExecutor"] as String,
entityId: json["entityId"] as String,
executorProperty: ExecutorProperty.fromJson(json["executorProperty"]),
productType: json['productType'] as String,
deviceName: json['deviceName'] as String,
);
}
@ -142,12 +151,16 @@ class Condition {
final String entityId;
final String entityType;
final Expr expr;
final String productType;
final String deviceName;
Condition({
required this.code,
required this.entityId,
required this.entityType,
required this.expr,
required this.productType,
required this.deviceName,
});
factory Condition.fromRawJson(String str) =>
@ -160,6 +173,8 @@ class Condition {
entityId: json["entityId"],
entityType: json["entityType"],
expr: Expr.fromJson(json["expr"]),
productType: json['productType'] as String,
deviceName: json['deviceName'] as String,
);
Map<String, dynamic> toJson() => {

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
@ -11,11 +12,10 @@ import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/light_divider.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/navigation/navigate_to_route.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class DeviceFunctionsView extends StatelessWidget
@ -51,22 +51,14 @@ class DeviceFunctionsView extends StatelessWidget
.add(AddTaskEvent(isAutomation: isAutomation));
navigateToRoute(context, Routes.sceneTasksRoute);
},
child: BodyMedium(
text: 'Save',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.secondaryColor.withOpacity(0.6),
),
child: SvgPicture.asset(Assets.saveRoutinesIcon),
),
],
leading: TextButton(
onPressed: () {
_cancelOperation(context, device, isAutomation);
},
child: BodyMedium(
text: 'Cancel',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.textPrimaryColor.withOpacity(0.6),
),
child: SvgPicture.asset(Assets.cancelIcon),
),
leadingWidth: 80,
padding: EdgeInsets.zero,
@ -75,66 +67,127 @@ class DeviceFunctionsView extends StatelessWidget
itemCount: functions.length,
padding: const EdgeInsets.only(top: 24.0),
itemBuilder: (context, index) {
return DefaultContainer(
padding: index == 0
? const EdgeInsets.only(top: 8)
: index == functions.length - 1
? const EdgeInsets.only(bottom: 8)
: EdgeInsets.zero,
margin: EdgeInsets.zero,
borderRadius: index == 0
? const BorderRadius.only(
topLeft: Radius.circular(20), topRight: Radius.circular(20))
: index == functions.length - 1
? const BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20))
: BorderRadius.zero,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) {
return SceneListTile(
iconsSize: 22,
minLeadingWidth: 20,
assetPath: functions[index].icon,
titleString: functions[index].operationName,
trailingWidget: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
if (device.productType!.name.toString() == 'ThreeGang' ||
device.productType!.name.toString() == 'TwoGang') {
final bool isFirstInPair = index % 2 == 0;
final bool isLastInPair =
index % 2 == 1 || index == functions.length - 1;
final bool isLastItem = index == functions.length - 1;
return Column(mainAxisSize: MainAxisSize.min, children: [
if (isFirstInPair && index != 0) const SizedBox(height: 16),
DefaultContainer(
padding: EdgeInsets.only(
top: isFirstInPair ? 8 : 0,
bottom: isLastInPair ? 8 : 0,
),
margin: EdgeInsets.zero,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(isFirstInPair ? 20 : 0),
topRight: Radius.circular(isFirstInPair ? 20 : 0),
bottomLeft: Radius.circular(isLastInPair ? 20 : 0),
bottomRight: Radius.circular(isLastInPair ? 20 : 0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) {
return SceneListTile(
iconsSize: 22,
minLeadingWidth: 20,
assetPath: functions[index].icon,
titleString: functions[index].operationName,
trailingWidget: const Icon(
Icons.arrow_forward_ios_rounded,
color: ColorsManager.greyColor,
size: 16,
),
],
),
onPressed: () {
if (isAutomation) {
_showAutomationDialog(
context,
functions[index],
device,
);
} else {
_showTabToRunDialog(
context,
functions[index],
device,
);
}
onPressed: () {
if (isAutomation) {
_showAutomationDialog(
context, functions[index], device);
} else {
_showTabToRunDialog(
context, functions[index], device);
}
},
);
},
);
},
),
if (isFirstInPair && !isLastItem)
SizedBox(
width: context.width * 0.8,
child: const LightDivider(),
),
],
),
index != functions.length - 1
? SizedBox(
width: context.width * 0.8, child: const LightDivider())
: const SizedBox(),
],
),
);
)
]);
} else {
return DefaultContainer(
padding: index == 0
? const EdgeInsets.only(top: 8)
: index == functions.length - 1
? const EdgeInsets.only(bottom: 8)
: EdgeInsets.zero,
margin: EdgeInsets.zero,
borderRadius: index == 0 && index == functions.length - 1
? const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
)
: index == 0
? const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
)
: index == functions.length - 1
? const BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
)
: BorderRadius.zero,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) {
return SceneListTile(
iconsSize: 22,
minLeadingWidth: 20,
assetPath: functions[index].icon,
titleString: functions[index].operationName,
trailingWidget: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.arrow_forward_ios_rounded,
color: ColorsManager.greyColor,
size: 16,
),
],
),
onPressed: () {
if (isAutomation) {
_showAutomationDialog(
context, functions[index], device);
} else {
_showTabToRunDialog(
context, functions[index], device);
}
},
);
}),
index != functions.length - 1
? SizedBox(
width: context.width * 0.8,
child: const LightDivider())
: const SizedBox(),
],
));
}
},
),
);

View File

@ -0,0 +1,117 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart';
import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart';
import 'package:syncrow_app/features/scene/helper/scene_bloc_factory.dart';
import 'package:syncrow_app/features/scene/widgets/empty_routines_widget.dart';
import 'package:syncrow_app/features/scene/widgets/routines_expansion_tile.dart';
import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_header.dart';
import 'package:syncrow_app/features/shared_widgets/app_loading_indicator.dart';
import 'package:syncrow_app/utils/context_extension.dart';
class RoutinesView extends StatelessWidget {
const RoutinesView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SceneBlocFactory.create(
pageType: false,
homeCubit: HomeCubit.getInstance(),
),
child: BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) {
final selectedSpace = HomeCubit.getInstance().selectedSpace;
if (state is DeleteSceneSuccess) {
if (state.success) _loadScenesAndAutomations(context, selectedSpace);
}
if (state is CreateSceneWithTasks) {
if (state.success) {
_loadScenesAndAutomations(context, selectedSpace);
context.read<SmartSceneSelectBloc>().add(const SmartSceneClearEvent());
}
}
return BlocListener<SceneBloc, SceneState>(
listener: (context, state) {
if (state is SceneTriggerSuccess) {
context.showCustomSnackbar(
message: 'Scene ${state.sceneName} triggered successfully!',
);
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SceneHeader(),
const SizedBox(height: 8),
Expanded(
child: BlocBuilder<SceneBloc, SceneState>(
builder: (context, state) {
if (state is SceneLoading) {
return const AppLoadingIndicator();
}
if (state is SceneError) {
return Center(
child: Text(state.message),
);
}
if (state is SceneLoaded) {
final scenes = state.scenes;
final automationList = state.automationList;
final routinesIsEmpty =
scenes.isEmpty && automationList.isEmpty;
if (routinesIsEmpty) return const EmptyRoutinesWidget();
return Theme(
data: Theme.of(context).copyWith(
dividerColor: Colors.transparent,
),
child: Expanded(
child: ListView(
children: [
RoutinesExpansionTile(
title: 'Tap to run routines',
emptyRoutinesMessage:
'No scenes have been added yet',
routines: state.scenes,
loadingStates: state.loadingStates,
loadingSceneId: state.loadingSceneId,
),
RoutinesExpansionTile(
title: 'Automation',
emptyRoutinesMessage:
'No automations have been added yet',
routines: state.automationList,
loadingStates: state.loadingStates,
loadingSceneId: state.loadingSceneId,
),
const SizedBox(height: 15),
],
),
),
);
}
return const SizedBox.shrink();
},
),
),
],
),
);
},
),
);
}
void _loadScenesAndAutomations(BuildContext context, SpaceModel? selectedSpace) {
context.read<SceneBloc>()
..add(LoadScenes(selectedSpace!.id, selectedSpace, showInDevice: false))
..add(LoadAutomation(selectedSpace.id, selectedSpace.community.uuid));
}
}

View File

@ -20,82 +20,69 @@ class SceneRoomsTabBarDevicesView extends StatefulWidget {
_SceneRoomsTabBarDevicesViewState();
}
class _SceneRoomsTabBarDevicesViewState
extends State<SceneRoomsTabBarDevicesView>
class _SceneRoomsTabBarDevicesViewState extends State<SceneRoomsTabBarDevicesView>
with SingleTickerProviderStateMixin {
late final TabController _tabController;
List<SubSpaceModel>? rooms = [];
late final SpaceModel selectedSpace;
var rooms = <SubSpaceModel>[];
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
selectedSpace = HomeCubit.getInstance().selectedSpace!;
selectedSpace = HomeCubit.getInstance().selectedSpace!;
rooms = List.from(selectedSpace.subspaces ?? []);
rooms = List.from(selectedSpace.subspaces);
final defaultSubSpaceModel = SubSpaceModel(
name: 'All Devices',
devices: context.read<DevicesCubit>().allDevices,
id: '-1',
);
if (rooms != null && rooms!.isNotEmpty) {
if (rooms![0].id != '-1') {
rooms?.insert(
0,
SubSpaceModel(
name: 'All Devices',
devices: context.read<DevicesCubit>().allDevices,
id: '-1',
),
);
}
} else {
rooms = [
SubSpaceModel(
name: 'All Devices',
devices: context.read<DevicesCubit>().allDevices,
id: '-1',
)
];
if (rooms.isNotEmpty) {
final isFirstRoomIdValid = rooms[0].id != '-1';
if (isFirstRoomIdValid) {
rooms.insert(0, defaultSubSpaceModel);
}
_tabController = TabController(length: rooms!.length, vsync: this);
_tabController.addListener(_handleTabSwitched);
setState(() {});
});
}
void _handleTabSwitched() {
if (_tabController.indexIsChanging) {
final value = _tabController.index;
/// select tab
context.read<TabBarBloc>().add(TabChanged(
selectedIndex: value,
roomId: rooms?[value].id ?? '',
unit: selectedSpace));
return;
} else {
rooms = [defaultSubSpaceModel];
}
_tabController = TabController(length: rooms.length, vsync: this);
_tabController.addListener(_handleTabSwitched);
}
@override
void dispose() {
super.dispose();
_tabController.dispose();
_tabController.removeListener(() {});
_tabController.dispose();
}
void _handleTabSwitched() {
if (_tabController.indexIsChanging) {
final index = _tabController.index;
context.read<TabBarBloc>().add(
TabBarTabChangedEvent(
selectedIndex: index,
roomId: rooms[index].id ?? '',
unit: selectedSpace,
),
);
return;
}
}
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: StringsManager.createScene,
padding: EdgeInsets.zero,
padding: EdgeInsetsDirectional.zero,
leading: IconButton(
onPressed: () {
navigateToRoute(context, Routes.sceneTasksRoute);
},
icon: const Icon(
Icons.arrow_back_ios,
),
onPressed: () => navigateToRoute(context, Routes.sceneTasksRoute),
icon: const Icon(Icons.arrow_back_ios),
),
title: StringsManager.createScene,
child: SceneDevicesBody(tabController: _tabController, rooms: rooms),
);
}

View File

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class EmptyRoutinesWidget extends StatelessWidget {
const EmptyRoutinesWidget({super.key});
@override
Widget build(BuildContext context) {
return Expanded(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
spacing: 16,
children: [
SvgPicture.asset(
'assets/icons/empty_routines.svg',
colorFilter: ColorFilter.mode(
ColorsManager.textGray,
BlendMode.srcIn,
),
),
BodyMedium(
text: 'No Routines yet',
fontColor: ColorsManager.textGray,
),
],
),
),
);
}
}

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/scene/model/scenes_model.dart';
import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_grid_view.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class RoutinesExpansionTile extends StatelessWidget {
const RoutinesExpansionTile({
super.key,
required this.title,
required this.emptyRoutinesMessage,
required this.routines,
required this.loadingStates,
this.loadingSceneId,
});
final String title;
final String emptyRoutinesMessage;
final List<ScenesModel> routines;
final Map<String, bool> loadingStates;
final String? loadingSceneId;
@override
Widget build(BuildContext context) {
return ExpansionTile(
tilePadding: const EdgeInsets.symmetric(
horizontal: 6,
),
initiallyExpanded: true,
iconColor: ColorsManager.grayColor,
title: BodyMedium(
text: title,
),
childrenPadding: EdgeInsetsDirectional.only(bottom: 10),
children: [
Visibility(
visible: routines.isNotEmpty,
replacement: Center(
child: BodyMedium(
text: emptyRoutinesMessage,
textAlign: TextAlign.center,
),
),
child: SceneGrid(
scenes: routines,
loadingSceneId: loadingSceneId,
disablePlayButton: false,
loadingStates: loadingStates,
),
),
],
);
}
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_state.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
@ -8,60 +7,44 @@ import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart'
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart';
import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart';
import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/features/scene/widgets/scene_devices/scene_devices_body_tab_bar.dart';
import 'package:syncrow_app/features/scene/widgets/scene_devices/scene_devices_list.dart';
import 'package:syncrow_app/features/shared_widgets/app_loading_indicator.dart';
class SceneDevicesBody extends StatelessWidget {
const SceneDevicesBody({
super.key,
required TabController tabController,
required this.tabController,
required this.rooms,
}) : _tabController = tabController;
super.key,
});
final TabController _tabController;
final List<SubSpaceModel>? rooms;
final TabController tabController;
final List<SubSpaceModel> rooms;
@override
Widget build(BuildContext context) {
final isAutomationDeviceStatus =
((ModalRoute.of(context)?.settings.arguments as SceneSettingsRouteArguments?)?.sceneType ==
CreateSceneEnum.deviceStatusChanges.name);
return BlocBuilder<TabBarBloc, TabBarState>(
builder: (context, tabState) {
builder: (context, state) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
TabBar(
controller: _tabController,
dividerColor: Colors.transparent,
indicatorColor: Colors.transparent,
tabs: [
...rooms!.map((e) => Tab(
child: BodyLarge(
text: e.name ?? '',
textAlign: TextAlign.start,
style: context.bodyLarge.copyWith(
color: (tabState is TabSelected) && tabState.roomId == e.id
? ColorsManager.textPrimaryColor
: ColorsManager.textPrimaryColor.withOpacity(0.2),
),
),
)),
],
isScrollable: true,
tabAlignment: TabAlignment.start,
SceneDevicesBodyTabBar(
tabController: tabController,
rooms: rooms,
selectedRoomId: state is TabBarTabSelectedState ? state.roomId : '-1',
),
Expanded(
child: TabBarView(
controller: _tabController,
physics: const NeverScrollableScrollPhysics(),
children:
rooms!.map((e) => _buildRoomTab(e, context, isAutomationDeviceStatus)).toList(),
controller: tabController,
children: rooms
.map(
(room) => _buildRoomTab(
room,
_isAutomationDeviceStatus(context),
),
)
.toList(),
),
),
],
@ -70,52 +53,46 @@ class SceneDevicesBody extends StatelessWidget {
);
}
Widget _buildRoomTab(SubSpaceModel room, BuildContext context, bool isAutomationDeviceStatus) {
bool _isAutomationDeviceStatus(BuildContext context) {
final routeArguments =
ModalRoute.of(context)?.settings.arguments as SceneSettingsRouteArguments?;
final deviceStatusChangesScene = CreateSceneEnum.deviceStatusChanges.name;
final sceneType = routeArguments?.sceneType;
return sceneType == deviceStatusChangesScene;
}
Widget _buildRoomTab(
SubSpaceModel room,
bool isAutomationDeviceStatus,
) {
return BlocBuilder<DeviceManagerBloc, DeviceManagerState>(
builder: (context, state) {
if (state.loading && state.devices == null) {
return const Center(child: CircularProgressIndicator());
} else if (state.devices != null && state.devices!.isNotEmpty) {
return ListView.builder(
itemCount: state.devices!.length,
itemBuilder: (context, index) {
final device = state.devices![index];
return DefaultContainer(
child: SceneListTile(
minLeadingWidth: 40,
leadingWidget: SvgPicture.asset(device.icon ?? ''),
titleWidget: BodyMedium(
text: device.name ?? '',
style: context.titleSmall.copyWith(
color: ColorsManager.secondaryTextColor,
fontWeight: FontWeight.w400,
fontSize: 20,
),
),
trailingWidget: const Icon(
Icons.arrow_forward_ios_rounded,
color: ColorsManager.greyColor,
size: 16,
),
onPressed: () {
Navigator.pushNamed(
context,
Routes.deviceFunctionsRoute,
arguments: {
"device": device,
"isAutomationDeviceStatus": isAutomationDeviceStatus
},
);
},
),
);
},
);
} else if (state.error != null) {
return const Center(child: Text('Something went wrong'));
} else {
return const SizedBox();
}
final isLoading = state.loading && state.devices == null;
final hasData =
state.devices != null && (state.devices?.isNotEmpty ?? false);
final hasError = state.error != null;
final widgets = <bool, Widget>{
isLoading: const AppLoadingIndicator(),
hasError: Center(child: Text('${state.error}')),
hasData: SceneDevicesList(
devices: state.devices ?? [],
isAutomationDeviceStatus: isAutomationDeviceStatus,
),
};
final invalidWidgetEntry = MapEntry(
true,
Center(child: Text('This subspace has no devices')),
);
final validWidgetEntry = widgets.entries.firstWhere(
(entry) => entry.key == true,
orElse: () => invalidWidgetEntry,
);
return validWidgetEntry.value;
},
);
}

View File

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SceneDevicesBodyTabBar extends StatelessWidget {
const SceneDevicesBodyTabBar({
required this.tabController,
required this.rooms,
required this.selectedRoomId,
super.key,
});
final String selectedRoomId;
final TabController tabController;
final List<SubSpaceModel> rooms;
@override
Widget build(BuildContext context) {
return TabBar(
controller: tabController,
dividerColor: Colors.transparent,
indicatorColor: Colors.transparent,
isScrollable: true,
tabAlignment: TabAlignment.start,
tabs: rooms.map((e) {
final isSelected = selectedRoomId == e.id;
return Tab(
child: BodyLarge(
text: e.name ?? '',
textAlign: TextAlign.start,
style: context.bodyLarge.copyWith(
color: isSelected
? ColorsManager.textPrimaryColor
: ColorsManager.textPrimaryColor.withValues(
alpha: 0.2,
),
),
),
);
}).toList(),
);
}
}

View File

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SceneDevicesList extends StatelessWidget {
const SceneDevicesList({
required this.isAutomationDeviceStatus,
required this.devices,
super.key,
});
final List<DeviceModel> devices;
final bool isAutomationDeviceStatus;
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: devices.length,
itemBuilder: (context, index) => _buildDeviceTile(context, devices[index]),
);
}
Widget _buildDeviceTile(
BuildContext context,
DeviceModel device,
) {
return DefaultContainer(
child: SceneListTile(
minLeadingWidth: 40,
leadingWidget: SvgPicture.asset(device.icon ?? ''),
titleWidget: BodyMedium(
text: device.name ?? '',
style: context.titleSmall.copyWith(
color: ColorsManager.secondaryTextColor,
fontWeight: FontWeight.w400,
fontSize: 20,
),
),
trailingWidget: const Icon(
Icons.arrow_forward_ios_rounded,
color: ColorsManager.greyColor,
size: 16,
),
onPressed: () => _navigateToDeviceFunctions(context, device),
),
);
}
void _navigateToDeviceFunctions(
BuildContext context,
DeviceModel device,
) {
Navigator.of(context).pushNamed(
Routes.deviceFunctionsRoute,
arguments: {
"device": device,
"isAutomationDeviceStatus": isAutomationDeviceStatus,
},
);
}
}

View File

@ -1,7 +1,5 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
class SceneHeader extends StatelessWidget {
@ -19,10 +17,6 @@ class SceneHeader extends StatelessWidget {
fontWeight: FontWeight.bold,
),
),
// SizedBox(height: 20),
// BodySmall(
// text: StringsManager.tapToRunRoutine,
// ),
],
);
}

View File

@ -59,13 +59,16 @@ class DefaultScaffold extends StatelessWidget {
height: height ?? MediaQuery.sizeOf(context).height,
padding: padding ??
const EdgeInsets.symmetric(horizontal: Constants.defaultPadding),
decoration: const BoxDecoration(
image: DecorationImage(
decoration: BoxDecoration(
image: const DecorationImage(
colorFilter: ColorFilter.mode(
ColorsManager.backgroundGrey,
BlendMode.srcIn,
),
image: AssetImage(
Assets.assetsImagesBackground,
),
fit: BoxFit.cover,
opacity: 0.4,
),
),
child: SafeArea(child: child),

View File

@ -1142,5 +1142,9 @@ class Assets {
static const String toggleSwitchSmall = 'assets/icons/toggleSwitchSmall.svg';
static const String offToggleSwitchSmall = 'assets/icons/offToggleSwitchSmall.svg';
static const String saveRoutinesIcon = 'assets/icons/save_routines_icon.svg';
static const String cancelIcon = 'assets/icons/cancel_icon.svg';
}

View File

@ -3,8 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/model/community_model.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/app_layout/view/app_layout.dart';
import 'package:syncrow_app/features/auth/view/otp_view.dart';
import 'package:syncrow_app/features/auth/view/login_view.dart';
import 'package:syncrow_app/features/auth/view/otp_view.dart';
import 'package:syncrow_app/features/auth/view/sign_up_view.dart';
import 'package:syncrow_app/features/dashboard/view/dashboard_view.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart';
@ -17,11 +17,12 @@ import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart'
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart';
import 'package:syncrow_app/features/scene/view/device_functions_view.dart';
import 'package:syncrow_app/features/scene/view/scene_auto_settings.dart';
import 'package:syncrow_app/features/scene/view/scene_tasks_view.dart';
import 'package:syncrow_app/features/scene/view/scene_rooms_tabbar.dart';
import 'package:syncrow_app/features/scene/view/scene_tasks_view.dart';
import 'package:syncrow_app/features/scene/view/scene_view.dart';
import 'package:syncrow_app/features/scene/view/smart_automation_select_route.dart';
import 'package:syncrow_app/features/splash/view/splash_view.dart';
import 'routing_constants.dart';
class Router {
@ -88,7 +89,7 @@ class Router {
BlocProvider(
create: (BuildContext context) =>
TabBarBloc(context.read<DeviceManagerBloc>())
..add(TabChanged(
..add(TabBarTabChangedEvent(
selectedIndex: 0,
roomId: '-1',
unit: SpaceModel(

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'package:syncrow_app/features/devices/model/device_category_model.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
@ -7,6 +8,8 @@ import 'package:syncrow_app/features/devices/model/device_report_model.dart';
import 'package:syncrow_app/features/devices/model/function_model.dart';
import 'package:syncrow_app/services/api/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
import '../../features/devices/model/create_temporary_password_model.dart';
class DevicesAPI {
@ -99,7 +102,7 @@ class DevicesAPI {
static Future<Map<String, dynamic>> getPowerClampStatus(
String deviceId) async {
final response = await _httpService.get(
path: ApiEndpoints.deviceFunctionsStatus.replaceAll('{powerClampUuid}', deviceId),
path: ApiEndpoints.deviceFunctionsStatus.replaceAll('{deviceUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
@ -580,7 +583,7 @@ class DevicesAPI {
path: path,
showServerMessage: false,
expectedResponseModel: (json) {
final data = json['data'];
final data = json['data'] as List<dynamic>?;
if (data == null || data.isEmpty) {
return <DeviceModel>[];
@ -588,9 +591,14 @@ class DevicesAPI {
if (json == null || json.isEmpty || json == []) {
return <DeviceModel>[];
}
return data
.map<DeviceModel>((device) => DeviceModel.fromJson(device))
.toList();
final result = <DeviceModel>[];
for (final device in data) {
final mappedDevice = DeviceModel.fromJson(device);
if (mappedDevice.productType?.name != DeviceType.FlushMountedSensor.name) {
result.add(mappedDevice);
}
}
return result;
},
);

View File

@ -35,6 +35,7 @@ abstract class ColorsManager {
static const Color blueColor1 = Color(0xff0A7AFF);
static const Color grayButtonColors = Color(0xffCCCCCC);
static const Color blueButton = Color(0xff85BDFF);
static const Color backgroundGrey = Color(0xFFF3F3F3);
}
//background: #F5F5F5;background: #85BDFF;

View File

@ -62,6 +62,7 @@ enum DeviceType {
SixScene,
SOS,
Other,
FlushMountedSensor,
}
enum FunctionType { Boolean, Enum, Integer, Raw, String }
@ -96,6 +97,7 @@ Map<String, DeviceType> devicesTypesMap = {
"4S": DeviceType.FourScene,
"6S": DeviceType.SixScene,
"SOS": DeviceType.SOS,
"NCPS": DeviceType.FlushMountedSensor,
};
Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {