mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
15
assets/icons/empty_records.svg
Normal file
15
assets/icons/empty_records.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.92516 12.1457C6.6709 11.6094 6.03272 11.3809 5.49988 11.6364L0.830003 13.8763C0.355091 13.9861 0 14.4121 0 14.9238V20.3082C0 20.9031 0.478601 21.385 1.06937 21.385C1.66001 21.385 2.13847 20.9031 2.13847 20.3082V16.5989L5.49988 18.2112C5.64826 18.2825 5.80469 18.3161 5.95867 18.3161C6.35844 18.3161 6.74154 18.0893 6.92516 17.7017C7.1786 17.1648 6.95249 16.5222 6.41937 16.2664L3.61977 14.9237L6.41937 13.581C6.95262 13.3253 7.1786 12.6828 6.92516 12.1457Z" fill="#D5D5D5"/>
|
||||
<path d="M12.3651 10.6137C12.5194 10.6137 12.6755 10.5802 12.8243 10.5089L19.2289 7.43717C19.762 7.1812 19.9886 6.53854 19.7347 6.00156C19.481 5.46528 18.8432 5.23697 18.3094 5.49225L11.9046 8.56423C11.3714 8.82006 11.1453 9.46245 11.3989 9.99956C11.5824 10.3868 11.9655 10.6137 12.3651 10.6137Z" fill="#D5D5D5"/>
|
||||
<path d="M25.6338 4.36467L28.9106 2.79269V6.54683C28.9106 7.14161 29.3893 7.62367 29.9797 7.62367C30.5704 7.62367 31.0488 7.14161 31.0488 6.54683V2.77315L34.3669 4.36467C34.5157 4.43582 34.6719 4.46953 34.8261 4.46953C35.2256 4.46953 35.6089 4.24274 35.7922 3.85522C36.0459 3.31825 35.8198 2.67572 35.2866 2.41989L30.46 0.104656C30.169 -0.0348854 29.8315 -0.0348854 29.5405 0.104656L24.7142 2.41989C24.1811 2.67572 23.9548 3.31811 24.2085 3.85522C24.4628 4.39261 25.1019 4.62119 25.6338 4.36467Z" fill="#D5D5D5"/>
|
||||
<path d="M40.7719 7.43681L47.1768 10.5089C47.3249 10.5802 47.4816 10.6138 47.6358 10.6138C48.0353 10.6138 48.4186 10.387 48.6019 9.99948C48.8555 9.4625 48.6295 8.81998 48.0963 8.56415L41.6912 5.49189C41.1588 5.23648 40.5206 5.46451 40.2662 6.0012C40.0124 6.53818 40.2386 7.1807 40.7719 7.43681Z" fill="#D5D5D5"/>
|
||||
<path d="M48.9237 20.886C49.4568 20.6303 49.683 19.9878 49.4292 19.4506C49.1755 18.9139 48.537 18.6855 48.0042 18.9417L43.9005 20.9106L31.0489 14.7463V9.94598C31.0489 9.35079 30.5704 8.86914 29.9798 8.86914C29.3893 8.86914 28.9107 9.35093 28.9107 9.94598V14.7658L16.101 20.9101L12.4202 19.1445C11.8858 18.8886 11.2492 19.1168 10.9949 19.6539C10.7412 20.1909 10.9674 20.8335 11.5006 21.0894L15.4821 22.9992V37.0011L11.4358 38.9419C10.9027 39.1977 10.6766 39.8401 10.9301 40.3774C11.1135 40.7648 11.4966 40.9915 11.8962 40.9915C12.0505 40.9915 12.2071 40.958 12.3553 40.8867L16.1012 39.09L28.9107 45.2342V48.9046C28.9107 49.4996 29.3893 49.9814 29.9798 49.9814C30.5704 49.9814 31.0489 49.4995 31.0489 48.9046V45.254L43.8996 39.0901L47.7917 40.957C47.9404 41.0282 48.0967 41.0619 48.2508 41.0619C48.6503 41.0619 49.0335 40.8351 49.2169 40.4476C49.4707 39.9106 49.2443 39.2681 48.7112 39.0121L44.5188 37.0013V22.9992L48.9237 20.886ZM42.3806 37.4328L31.049 42.8681V29.4597L42.3806 24.0241V37.4328ZM17.6875 37.1364C17.6685 37.0962 17.6434 37.0615 17.6203 37.0249V24.0243L28.9107 29.4399V42.8485L17.7848 37.5119C17.7746 37.3852 17.7449 37.2579 17.6875 37.1364ZM30.0004 16.6291L41.4126 22.103L30.0004 27.5767L18.5882 22.103L30.0004 16.6291Z" fill="#D5D5D5"/>
|
||||
<path d="M47.1767 49.4916L40.772 52.5637C40.2389 52.8196 40.0123 53.4622 40.2662 53.9987C40.4497 54.3864 40.8329 54.6129 41.2324 54.6129C41.3865 54.6129 41.5432 54.5793 41.6915 54.5086L48.0964 51.4363C48.6295 51.1805 48.8556 50.5377 48.602 50.0007C48.3479 49.464 47.7094 49.237 47.1767 49.4916Z" fill="#D5D5D5"/>
|
||||
<path d="M34.367 55.636L31.0489 57.2276V52.9362C31.0489 52.3414 30.5704 51.8594 29.9798 51.8594C29.3893 51.8594 28.9107 52.3414 28.9107 52.9362V57.2081L25.6338 55.636C25.1002 55.3814 24.4628 55.6084 24.2086 56.1455C23.9548 56.6825 24.181 57.3248 24.7142 57.5806L29.5408 59.8956C29.6862 59.965 29.8433 60 30.0005 60C30.1578 60 30.3149 59.965 30.4603 59.8956L35.2869 57.5806C35.82 57.3248 36.0462 56.6825 35.7925 56.1455C35.5383 55.6084 34.8997 55.3819 34.367 55.636Z" fill="#D5D5D5"/>
|
||||
<path d="M19.2289 52.5636L12.824 49.4914C12.2922 49.2362 11.6534 49.4635 11.3989 50.0005C11.1453 50.5375 11.3714 51.1803 11.9046 51.4361L18.3096 54.5083C18.4577 54.5791 18.6144 54.6127 18.7685 54.6127C19.168 54.6127 19.5512 54.3861 19.7346 53.9985C19.9883 53.4621 19.762 52.8195 19.2289 52.5636Z" fill="#D5D5D5"/>
|
||||
<path d="M6.92516 42.2985C6.6709 41.7618 6.03272 41.5345 5.49988 41.7894L2.13847 43.4014V39.3708C2.13847 38.776 1.66001 38.2939 1.06937 38.2939C0.478601 38.2939 0 38.776 0 39.3708V44.7553C0 44.8898 0.0274617 45.0171 0.0724116 45.1359C0.0941352 45.5271 0.319158 45.8792 0.673293 46.0493L5.49988 48.3645C5.64826 48.4354 5.80469 48.4692 5.95867 48.4692C6.35844 48.4692 6.74154 48.2424 6.92516 47.855C7.1786 47.3181 6.95249 46.6755 6.41937 46.4194L3.61977 45.077L6.41937 43.7343C6.95262 43.4783 7.1786 42.8355 6.92516 42.2985Z" fill="#D5D5D5"/>
|
||||
<path d="M1.06937 34.0934C1.66001 34.0934 2.13847 33.6114 2.13847 33.0163V26.6623C2.13847 26.0675 1.66001 25.5854 1.06937 25.5854C0.478601 25.5854 0 26.0675 0 26.6623V33.0163C0.000136626 33.6114 0.478738 34.0934 1.06937 34.0934Z" fill="#D5D5D5"/>
|
||||
<path d="M59.1707 13.8762L54.5008 11.6362C53.9669 11.3801 53.3298 11.6088 53.0757 12.1455C52.8221 12.6828 53.0482 13.3252 53.5813 13.5811L56.3809 14.9239L53.5813 16.2666C53.0482 16.5224 52.8221 17.1648 53.0757 17.7019C53.259 18.0894 53.6422 18.3162 54.0417 18.3162C54.196 18.3162 54.3527 18.2826 54.5008 18.2113L57.8622 16.599V20.3083C57.8622 20.9032 58.3407 21.3852 58.9313 21.3852C59.5219 21.3852 60.0003 20.9032 60.0003 20.3083V14.924C60.0003 14.4119 59.6456 13.986 59.1707 13.8762Z" fill="#D5D5D5"/>
|
||||
<path d="M58.9314 25.5854C58.3406 25.5854 57.8623 26.0675 57.8623 26.6623V33.0163C57.8623 33.6114 58.3408 34.0931 58.9314 34.0931C59.522 34.0931 60.0004 33.6113 60.0004 33.0163V26.6623C60.0004 26.0675 59.5222 25.5854 58.9314 25.5854Z" fill="#D5D5D5"/>
|
||||
<path d="M58.9313 38.2935C58.3405 38.2935 57.8622 38.7755 57.8622 39.3703V43.4009L54.5008 41.7889C53.9669 41.5336 53.3298 41.7611 53.0757 42.2981C52.8221 42.835 53.0482 43.4778 53.5813 43.7337L56.3809 45.0764L53.5813 46.4188C53.0482 46.6749 52.8221 47.3174 53.0757 47.8544C53.259 48.2418 53.6422 48.4686 54.0417 48.4686C54.196 48.4686 54.3527 48.4347 54.5008 48.3639L59.3274 46.0486C59.6815 45.8785 59.9065 45.5265 59.9283 45.1353C59.9731 45.0165 60.0003 44.8892 60.0003 44.7546V39.3702C60.0003 38.7755 59.5221 38.2935 58.9313 38.2935Z" fill="#D5D5D5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 6.0 KiB |
24
assets/icons/open_close_door.svg
Normal file
24
assets/icons/open_close_door.svg
Normal file
@ -0,0 +1,24 @@
|
||||
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="30" cy="30" r="30" fill="white"/>
|
||||
<g filter="url(#filter0_i_2351_2399)">
|
||||
<path d="M16 16.1817C16 13.6934 17.8282 11.5608 20.3041 11.3129C24.581 10.8846 27.9929 10.9034 32.2213 11.3239C34.7091 11.5713 36.5548 13.7089 36.5548 16.209V43.7835C36.5548 46.246 34.7637 48.3665 32.3165 48.6408C28.0557 49.1183 24.5855 49.1222 20.2426 48.6376C17.7933 48.3643 16 46.2435 16 43.7791V16.1817Z" fill="#EAF6FF"/>
|
||||
</g>
|
||||
<path d="M33.5761 34.3577C33.7051 34.3577 33.8341 34.3086 33.9325 34.2102C34.1293 34.0133 34.1293 33.6943 33.9325 33.4975C32.2479 31.813 32.2479 29.0721 33.9325 27.3875C34.1293 27.1907 34.1293 26.8717 33.9325 26.6749C33.7356 26.4782 33.4166 26.4782 33.2198 26.6749C31.1423 28.7525 31.1423 32.1327 33.2198 34.2103C33.3182 34.3086 33.4472 34.3577 33.5761 34.3577Z" fill="#8AC9FE"/>
|
||||
<path d="M34.6358 32.4001C34.744 32.4001 34.8522 32.3588 34.9348 32.2762C35.0999 32.1111 35.0999 31.8434 34.9347 31.6783C34.6046 31.3483 34.4228 30.9093 34.4228 30.4424C34.4228 29.9756 34.6046 29.5367 34.9347 29.2066C35.0999 29.0415 35.0999 28.7738 34.9348 28.6087C34.7696 28.4436 34.5019 28.4436 34.3369 28.6087C33.847 29.0985 33.5772 29.7497 33.5772 30.4424C33.5772 31.1352 33.847 31.7864 34.3369 32.2763C34.4193 32.3587 34.5276 32.4001 34.6358 32.4001Z" fill="#8AC9FE"/>
|
||||
<path d="M27.1713 41.5347L27.1713 43.4539C27.1713 43.8359 26.8617 44.1455 26.4797 44.1455C26.0976 44.1455 25.7881 43.8359 25.7881 43.4539L25.7881 41.5347C25.7881 41.1527 26.0976 40.8431 26.4797 40.8431C26.8617 40.8431 27.1713 41.1527 27.1713 41.5347Z" fill="#B3DAFE"/>
|
||||
<path d="M37.5342 19.7964C37.5342 18.9119 38.1118 18.1138 38.9777 17.9335C40.3924 17.639 41.5238 17.6511 42.9209 17.9388C43.7961 18.1191 44.3858 18.9219 44.3858 19.8155V42.0421C44.3858 42.9071 43.8339 43.6929 42.9918 43.8912C41.5575 44.2289 40.3936 44.2319 38.9318 43.8895C38.0881 43.6918 37.5342 42.9055 37.5342 42.039V19.7964Z" fill="#EAF6FF"/>
|
||||
<path d="M39.9952 34.3577C39.8662 34.3577 39.7372 34.3086 39.6388 34.2102C39.442 34.0133 39.442 33.6943 39.6388 33.4975C41.3234 31.813 41.3234 29.0721 39.6388 27.3875C39.442 27.1907 39.442 26.8717 39.6388 26.6749C39.8357 26.4782 40.1547 26.4782 40.3515 26.6749C42.429 28.7525 42.429 32.1327 40.3515 34.2103C40.2531 34.3086 40.1241 34.3577 39.9952 34.3577Z" fill="#8AC9FE"/>
|
||||
<path d="M38.9355 32.4001C38.8273 32.4001 38.7191 32.3588 38.6365 32.2762C38.4714 32.1111 38.4714 31.8434 38.6366 31.6783C38.9666 31.3483 39.1485 30.9093 39.1485 30.4424C39.1485 29.9756 38.9666 29.5367 38.6366 29.2066C38.4714 29.0415 38.4714 28.7738 38.6365 28.6087C38.8017 28.4436 39.0694 28.4436 39.2344 28.6087C39.7243 29.0985 39.9941 29.7497 39.9941 30.4424C39.9941 31.1352 39.7243 31.7864 39.2344 32.2763C39.1519 32.3587 39.0437 32.4001 38.9355 32.4001Z" fill="#8AC9FE"/>
|
||||
<defs>
|
||||
<filter id="filter0_i_2351_2399" x="15" y="11" width="21.5547" height="38" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="-1"/>
|
||||
<feGaussianBlur stdDeviation="1.5"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.538295 0 0 0 0 0.538295 0 0 0 0 0.538295 0 0 0 0.3 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_2351_2399"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
19
assets/icons/open_close_records.svg
Normal file
19
assets/icons/open_close_records.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="30" cy="30" r="30" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.6277 34.7848L12.0206 21.9742C11.9545 21.7877 12.0527 21.581 12.2393 21.5129C18.2964 19.3343 25.1218 17.3082 30.9563 14.8187C31.2171 14.6542 31.4217 14.8167 31.534 15.1276L38.7725 33.7858L40.7381 39.2523C40.8043 39.4388 40.7081 39.6475 40.5215 39.7137L26.6702 44.6947L21.2709 46.6366C21.0843 46.7048 20.8777 46.6065 20.8096 46.4199L16.6277 34.7848Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.4689 30.5338L18.1684 17.1174C18.1343 16.9208 18.2667 16.7342 18.4613 16.7001L34.3042 13.9839L37.9325 13.3861C38.1311 13.2999 38.454 13.4001 38.5302 13.8435L42.4493 33.4146L43.4301 39.1398C43.4642 39.3345 43.3318 39.523 43.1372 39.5571L28.6301 42.0446L22.974 43.0155C22.7795 43.0477 22.5909 42.9152 22.5568 42.7206L20.4689 30.5338Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.9063 26.5779V39.4768C24.9063 39.6835 25.0748 39.854 25.2814 39.854H31.2684H40.1315H41.4051H46.624C46.8305 39.854 46.999 39.6835 46.999 39.4768V33.4166V16.6761V16.5016H43.1882C42.8212 16.5016 42.059 16.5537 42.059 15.7213L42.0509 12H25.2813C25.0748 12 24.9062 12.1685 24.9062 12.3752V15.5949V16.957V26.5779H24.9063Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.5492 19.3423C16.4031 20.0645 14.277 20.7806 12.2393 21.5128C12.0527 21.581 11.9545 21.7876 12.0206 21.9742L16.6277 34.7848L20.8096 46.4199C20.8778 46.6065 21.0844 46.7048 21.2709 46.6365L26.6702 44.6947L38.9732 40.2713L18.5492 19.3423Z" fill="#B5C4CF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.9076 15.5947L18.4613 16.7C18.2667 16.7341 18.1343 16.9207 18.1684 17.1173L18.5495 19.342L20.469 30.5337L22.5569 42.7205C22.591 42.9151 22.7795 43.0474 22.9741 43.0154L28.6301 42.0445L38.9735 40.2711L41.4064 39.8538H40.1328L24.9076 15.5947Z" fill="#D7E7EC"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.999 33.4166V16.6761V16.5016V16.4434C46.999 16.3351 46.9528 16.317 46.7864 16.1545L42.6727 12.2307C42.4782 12.0461 42.4461 12 42.3318 12H42.0509H25.2813C25.0748 12 24.9062 12.1685 24.9062 12.3752V39.4768C24.9062 39.6835 25.0748 39.854 25.2813 39.854H46.6239C46.8305 39.854 46.999 39.6835 46.999 39.4768V33.4166Z" fill="#EDF3F4"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.9077 15.5947L23.3975 15.8535V40.8629C23.3975 41.0716 23.566 41.2401 23.7725 41.2401H29.7595H33.3216L38.9736 40.2712L41.4065 39.8539H40.1329H38.5665H31.2698H25.2828C25.0762 39.8539 24.9077 39.6834 24.9077 39.4768C24.9077 31.5167 24.9077 23.5547 24.9077 15.5947Z" fill="#B5C4CF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.469 30.5336L18.5495 19.3418C18.03 19.5163 17.5126 19.6908 16.9971 19.8654L17.2358 21.2556L19.1552 32.4473L21.2452 44.6341C21.2772 44.8286 21.4658 44.961 21.6603 44.929L27.3164 43.9581L30 43.4987L38.0829 40.59L37.9867 40.4395L33.3214 41.2399L28.6302 42.0444L22.9741 43.0153C22.7796 43.0474 22.591 42.915 22.5569 42.7204L20.469 30.5336Z" fill="#9AAFB7"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.9998 33.4166V16.6761V16.5016C46.9998 16.299 46.9337 16.297 46.7873 16.1545L42.6736 12.2307C42.4791 12.0461 42.447 12 42.3326 12H42.0518H37.8779C42.9784 18.8286 43.2692 31.1377 39.4845 38.8209C39.31 39.178 39.1275 39.521 38.937 39.854H40.1323H41.4059H46.6248C46.8313 39.854 46.9998 39.6835 46.9998 39.4768V33.4166Z" fill="#D7E7EC"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M42.0607 15.7213C42.0607 16.5537 42.8229 16.5016 43.19 16.5016H47.0008V16.4434C47.0008 16.3351 46.9546 16.317 46.7882 16.1545L42.6745 12.2307C42.48 12.0461 42.4479 12 42.3336 12H42.0527L42.0607 15.7213Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.7882 16.1545L42.6745 12.2307C42.48 12.0461 42.4479 12 42.3336 12H42.0527L42.0607 15.7212C42.0607 16.5537 42.8229 16.5015 43.19 16.5015H47.0008V16.4433C47.0008 16.3351 46.9546 16.317 46.7882 16.1545Z" fill="#B5C4CF"/>
|
||||
<path d="M43.6094 20.2037H28.5086C28.2169 20.2037 27.9805 19.9671 27.9805 19.6753C27.9805 19.3835 28.2169 19.147 28.5086 19.147H43.6094C43.9011 19.147 44.1376 19.3835 44.1376 19.6753C44.1376 19.9671 43.9012 20.2037 43.6094 20.2037Z" fill="#9AAFB7"/>
|
||||
<path d="M43.6094 23.9566H28.5086C28.2169 23.9566 27.9805 23.7201 27.9805 23.4283C27.9805 23.1364 28.2169 22.8999 28.5086 22.8999H43.6094C43.9011 22.8999 44.1376 23.1364 44.1376 23.4283C44.1376 23.7201 43.9012 23.9566 43.6094 23.9566Z" fill="#9AAFB7"/>
|
||||
<path d="M43.6094 27.7105H28.5086C28.2169 27.7105 27.9805 27.474 27.9805 27.1822C27.9805 26.8903 28.2169 26.6538 28.5086 26.6538H43.6094C43.9011 26.6538 44.1376 26.8903 44.1376 27.1822C44.1376 27.474 43.9012 27.7105 43.6094 27.7105Z" fill="#9AAFB7"/>
|
||||
<path d="M43.6094 31.4634H28.5086C28.2169 31.4634 27.9805 31.2269 27.9805 30.9351C27.9805 30.6433 28.2169 30.4067 28.5086 30.4067H43.6094C43.9011 30.4067 44.1376 30.6433 44.1376 30.9351C44.1376 31.2269 43.9012 31.4634 43.6094 31.4634Z" fill="#9AAFB7"/>
|
||||
<path d="M43.6094 35.2169H28.5086C28.2169 35.2169 27.9805 34.9803 27.9805 34.6885C27.9805 34.3967 28.2169 34.1602 28.5086 34.1602H43.6094C43.9011 34.1602 44.1376 34.3967 44.1376 34.6885C44.1376 34.9803 43.9012 35.2169 43.6094 35.2169Z" fill="#9AAFB7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
22
assets/icons/water_heater.svg
Normal file
22
assets/icons/water_heater.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<svg width="25" height="36" viewBox="0 0 25 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.67036 31.6755C9.37913 31.6755 9.14303 31.9116 9.14303 32.2029V33.5343C9.14303 34.3122 8.5101 34.9452 7.73219 34.9452H6.41014C6.11891 34.9452 5.88281 35.1813 5.88281 35.4725C5.88281 35.7637 6.11891 35.9998 6.41014 35.9998H7.73219C9.09163 35.9998 10.1977 34.8938 10.1977 33.5343V32.2029C10.1977 31.9116 9.96158 31.6755 9.67036 31.6755Z" fill="#72BBFF"/>
|
||||
<path d="M10.6115 29.458H8.7285C8.43727 29.458 8.20117 29.6941 8.20117 29.9853V31.2785C8.20117 32.0885 8.86012 32.7473 9.67002 32.7473C10.4799 32.7473 11.1389 32.0884 11.1389 31.2785V29.9853C11.1389 29.6941 10.9028 29.458 10.6115 29.458Z" fill="#6B717D"/>
|
||||
<path d="M14.4785 31.6755C14.7697 31.6755 15.0058 31.9116 15.0058 32.2029V33.5343C15.0058 34.3122 15.6388 34.9452 16.4167 34.9452H17.7387C18.0299 34.9452 18.266 35.1813 18.266 35.4725C18.266 35.7637 18.0299 35.9998 17.7387 35.9998H16.4167C15.0572 35.9998 13.9512 34.8938 13.9512 33.5343V32.2029C13.9512 31.9116 14.1873 31.6755 14.4785 31.6755Z" fill="#FF6C6C"/>
|
||||
<path d="M13.5371 29.4583H15.4201C15.7114 29.4583 15.9475 29.6944 15.9475 29.9856V31.2787C15.9475 32.0887 15.2885 32.7476 14.4786 32.7476C13.6687 32.7476 13.0098 32.0886 13.0098 31.2787V29.9856C13.0098 29.6944 13.2459 29.4583 13.5371 29.4583Z" fill="#6B717D"/>
|
||||
<path d="M2.14594 3.29248H0.527327C0.236102 3.29248 0 3.52858 0 3.81981C0 4.11103 0.236102 4.34714 0.527327 4.34714H2.14594C2.43717 4.34714 2.67327 4.11103 2.67327 3.81981C2.67327 3.52858 2.43717 3.29248 2.14594 3.29248Z" fill="#6B717D"/>
|
||||
<path d="M2.14594 5.75342H0.527327C0.236102 5.75342 0 5.98952 0 6.28075C0 6.57197 0.236102 6.80807 0.527327 6.80807H2.14594C2.43717 6.80807 2.67327 6.57197 2.67327 6.28075C2.67327 5.98952 2.43717 5.75342 2.14594 5.75342Z" fill="#6B717D"/>
|
||||
<path d="M23.6186 17.7485H22C21.7088 17.7485 21.4727 17.9846 21.4727 18.2759C21.4727 18.5671 21.7088 18.8032 22 18.8032H23.6186C23.9098 18.8032 24.1459 18.5671 24.1459 18.2759C24.1459 17.9846 23.9098 17.7485 23.6186 17.7485Z" fill="#6B717D"/>
|
||||
<path d="M19.5902 27.5019V29.9663C19.5902 30.2574 19.354 30.4936 19.0629 30.4936H5.08592C4.79484 30.4936 4.55859 30.2574 4.55859 29.9663V27.5019C4.55859 27.2109 4.79484 26.9746 5.08592 26.9746H19.0629C19.354 26.9746 19.5902 27.2109 19.5902 27.5019Z" fill="#D6EAEC"/>
|
||||
<path d="M19.5898 27.5019V29.9663C19.5898 30.2574 19.3535 30.4936 19.0624 30.4936H16.9531C17.2442 30.4936 17.4805 30.2574 17.4805 29.9663V27.5019C17.4805 27.2109 17.2442 26.9746 16.9531 26.9746H19.0624C19.3535 26.9746 19.5898 27.2109 19.5898 27.5019Z" fill="#B5D9DD"/>
|
||||
<path d="M22.5287 1.66214V22.3242L20.4194 22.8994L12.0736 25.1767L1.61914 22.3242V1.66214C1.61914 0.745289 2.36443 0 3.28128 0H20.8666C21.7827 0 22.5287 0.745289 22.5287 1.66214Z" fill="#D6EAEC"/>
|
||||
<path d="M22.5273 1.66214V22.3242L20.418 22.8994V1.66214C20.418 0.745289 19.672 0 18.7559 0H20.8652C21.7813 0 22.5273 0.745289 22.5273 1.66214Z" fill="#B5D9DD"/>
|
||||
<path d="M22.5287 22.3242V26.3671C22.5287 27.2839 21.7827 28.0292 20.8666 28.0292H3.28128C2.36443 28.0292 1.61914 27.2839 1.61914 26.3671V22.3242H22.5287Z" fill="#6B717D"/>
|
||||
<path d="M22.5273 22.3242V26.3671C22.5273 27.2839 21.7813 28.0292 20.8652 28.0292H18.7559C19.672 28.0292 20.418 27.2839 20.418 26.3671V22.3242H22.5273Z" fill="#47505E"/>
|
||||
<path d="M12.43 13.9283C12.2285 13.744 11.9196 13.744 11.7181 13.9283C11.6376 14.0019 9.74805 15.7519 9.74805 17.6083C9.74805 18.8909 10.7915 19.9344 12.0741 19.9344C13.3567 19.9344 14.4001 18.8909 14.4001 17.6083C14.4 15.7519 12.5104 14.0019 12.43 13.9283Z" fill="#72BBFF"/>
|
||||
<path d="M16.1576 8.85955C17.0908 6.60807 16.0222 4.02635 13.7707 3.0931C11.5192 2.15986 8.93748 3.2285 8.00424 5.47998C7.071 7.73146 8.13964 10.3132 10.3911 11.2464C12.6426 12.1797 15.2243 11.111 16.1576 8.85955Z" fill="#B5D9DD"/>
|
||||
<path d="M14.7283 7.16982C14.7283 9.02109 13.9254 10.5271 12.0741 10.5271C11.7957 10.5271 11.525 10.4927 11.2655 10.428C9.80379 10.0659 8.7168 8.74266 8.7168 7.16982C8.7168 5.59698 9.80379 4.27374 11.2655 3.91164C11.525 3.84695 11.7957 3.8125 12.0741 3.8125C13.9254 3.8125 14.7283 5.31855 14.7283 7.16982Z" fill="white"/>
|
||||
<path d="M15.4315 7.16982C15.4315 9.02109 13.9255 10.5271 12.0742 10.5271C11.7958 10.5271 11.5251 10.4927 11.2656 10.428C12.7274 10.0659 13.8144 8.74266 13.8144 7.16982C13.8144 5.59698 12.7274 4.27374 11.2656 3.91164C11.5251 3.84695 11.7958 3.8125 12.0742 3.8125C13.9255 3.8125 15.4315 5.31855 15.4315 7.16982Z" fill="#D6EAEC"/>
|
||||
<path d="M12.4478 6.79706C12.2418 6.59105 11.908 6.59112 11.7021 6.79706L10.5783 7.92094C10.3723 8.12688 10.3723 8.4608 10.5783 8.66667C10.6812 8.76961 10.8162 8.82115 10.9512 8.82115C11.0861 8.82115 11.2211 8.76968 11.324 8.66667L12.4478 7.5428C12.6538 7.33685 12.6538 7.00294 12.4478 6.79706Z" fill="#FF6C6C"/>
|
||||
<path d="M5.55469 25.7041C5.84593 25.7041 6.08203 25.468 6.08203 25.1768C6.08203 24.8855 5.84593 24.6494 5.55469 24.6494C5.26344 24.6494 5.02734 24.8855 5.02734 25.1768C5.02734 25.468 5.26344 25.7041 5.55469 25.7041Z" fill="#47505E"/>
|
||||
<path d="M18.5918 25.7041C18.883 25.7041 19.1191 25.468 19.1191 25.1768C19.1191 24.8855 18.883 24.6494 18.5918 24.6494C18.3006 24.6494 18.0645 24.8855 18.0645 25.1768C18.0645 25.468 18.3006 25.7041 18.5918 25.7041Z" fill="#47505E"/>
|
||||
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
@ -17,6 +17,7 @@ class DefaultButton extends StatelessWidget {
|
||||
this.borderRadius,
|
||||
this.height,
|
||||
this.padding,
|
||||
this.borderColor,
|
||||
});
|
||||
final void Function()? onPressed;
|
||||
final Widget child;
|
||||
@ -31,6 +32,8 @@ class DefaultButton extends StatelessWidget {
|
||||
final ButtonStyle? customButtonStyle;
|
||||
final Color? backgroundColor;
|
||||
final Color? foregroundColor;
|
||||
final Color? borderColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
@ -61,6 +64,7 @@ class DefaultButton extends StatelessWidget {
|
||||
}),
|
||||
shape: MaterialStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
side: BorderSide(color: borderColor ?? Colors.transparent),
|
||||
borderRadius: BorderRadius.circular(borderRadius ?? 20),
|
||||
),
|
||||
),
|
||||
|
@ -18,6 +18,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
on<AcFetchBatchStatusEvent>(_onFetchAcBatchStatus);
|
||||
on<AcControlEvent>(_onAcControl);
|
||||
on<AcBatchControlEvent>(_onAcBatchControl);
|
||||
on<AcFactoryResetEvent>(_onFactoryReset);
|
||||
}
|
||||
|
||||
FutureOr<void> _onFetchAcStatus(
|
||||
@ -184,4 +185,22 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
emit: emit,
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> _onFactoryReset(
|
||||
AcFactoryResetEvent event, Emitter<AcsState> emit) async {
|
||||
emit(AcsLoadingState());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryResetModel,
|
||||
event.deviceId,
|
||||
);
|
||||
if (!response) {
|
||||
emit(const AcsFailedState(error: 'Failed'));
|
||||
} else {
|
||||
add(AcFetchDeviceStatusEvent(event.deviceId));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AcsFailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
|
||||
sealed class AcsEvent extends Equatable {
|
||||
const AcsEvent();
|
||||
@ -54,3 +55,16 @@ class AcBatchControlEvent extends AcsEvent {
|
||||
@override
|
||||
List<Object> get props => [devicesIds, code, value];
|
||||
}
|
||||
|
||||
class AcFactoryResetEvent extends AcsEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryResetModel;
|
||||
|
||||
const AcFactoryResetEvent({
|
||||
required this.deviceId,
|
||||
required this.factoryResetModel,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceId, factoryResetModel];
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_ac_mode.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_current_temp.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_fan_speed.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||
@ -89,7 +90,15 @@ class AcDeviceBatchControlView extends StatelessWidget
|
||||
},
|
||||
),
|
||||
FirmwareUpdateWidget(deviceId: devicesIds.first, version: 5),
|
||||
FactoryResetWidget(deviceId: devicesIds.first),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<AcBloc>().add(AcFactoryResetEvent(
|
||||
deviceId: state.status.uuid,
|
||||
factoryResetModel:
|
||||
FactoryResetModel(devicesUuid: devicesIds),
|
||||
));
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
} else if (state is AcsLoadingState) {
|
||||
|
@ -118,7 +118,7 @@ class _CurrentTempState extends State<BatchCurrentTemp> {
|
||||
onIncrement: () {
|
||||
if (_adjustedValue < 30) {
|
||||
setState(() {
|
||||
_adjustedValue++;
|
||||
_adjustedValue = _adjustedValue + 0.5;
|
||||
});
|
||||
_onValueChanged(_adjustedValue);
|
||||
}
|
||||
@ -126,7 +126,7 @@ class _CurrentTempState extends State<BatchCurrentTemp> {
|
||||
onDecrement: () {
|
||||
if (_adjustedValue > 20) {
|
||||
setState(() {
|
||||
_adjustedValue--;
|
||||
_adjustedValue = _adjustedValue - 0.5;
|
||||
});
|
||||
_onValueChanged(_adjustedValue);
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ class _CurrentTempState extends State<CurrentTemp> {
|
||||
onIncrement: () {
|
||||
if (_adjustedValue < 30) {
|
||||
setState(() {
|
||||
_adjustedValue++;
|
||||
_adjustedValue = _adjustedValue + 0.5;
|
||||
});
|
||||
_onValueChanged(_adjustedValue);
|
||||
}
|
||||
@ -126,7 +126,7 @@ class _CurrentTempState extends State<CurrentTemp> {
|
||||
onDecrement: () {
|
||||
if (_adjustedValue > 20) {
|
||||
setState(() {
|
||||
_adjustedValue--;
|
||||
_adjustedValue = _adjustedValue - 0.5;
|
||||
});
|
||||
_onValueChanged(_adjustedValue);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import 'package:syncrow_web/pages/device_managment/two_gang_switch/view/wall_lig
|
||||
import 'package:syncrow_web/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_batch_control.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heater_batch_control.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heater_device_control.dart';
|
||||
|
||||
mixin RouteControlsBasedCode {
|
||||
@ -56,7 +57,7 @@ mixin RouteControlsBasedCode {
|
||||
case 'AC':
|
||||
return AcDeviceControlsView(device: device);
|
||||
case 'WH':
|
||||
return WaterHeaterDeviceControl(
|
||||
return WaterHeaterDeviceControlView(
|
||||
device: device,
|
||||
);
|
||||
case 'DS':
|
||||
@ -140,6 +141,14 @@ mixin RouteControlsBasedCode {
|
||||
.where((e) => (e.productType == 'AC'))
|
||||
.map((e) => e.uuid!)
|
||||
.toList());
|
||||
case 'WH':
|
||||
return WaterHEaterBatchControlView(
|
||||
deviceIds: devices
|
||||
.where((e) => (e.productType == 'WH'))
|
||||
.map((e) => e.uuid!)
|
||||
.toList(),
|
||||
);
|
||||
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class FactoryResetModel {
|
||||
final List<String> devicesUuid;
|
||||
|
||||
FactoryResetModel({
|
||||
required this.devicesUuid,
|
||||
});
|
||||
|
||||
factory FactoryResetModel.fromJson(Map<String, dynamic> json) {
|
||||
return FactoryResetModel(
|
||||
devicesUuid: List<String>.from(json['devicesUuid']),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'devicesUuid': devicesUuid,
|
||||
};
|
||||
}
|
||||
|
||||
FactoryResetModel copyWith({
|
||||
List<String>? devicesUuid,
|
||||
}) {
|
||||
return FactoryResetModel(
|
||||
devicesUuid: devicesUuid ?? this.devicesUuid,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'devicesUuid': devicesUuid,
|
||||
};
|
||||
}
|
||||
|
||||
factory FactoryResetModel.fromMap(Map<String, dynamic> map) {
|
||||
return FactoryResetModel(
|
||||
devicesUuid: List<String>.from(map['devicesUuid']),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'FactoryReset(devicesUuid: $devicesUuid)';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is FactoryResetModel &&
|
||||
listEquals(other.devicesUuid, devicesUuid);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => devicesUuid.hashCode;
|
||||
}
|
@ -20,14 +20,15 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
on<GetCeilingDeviceReportsEvent>(_getDeviceReports);
|
||||
on<ShowCeilingDescriptionEvent>(_showDescription);
|
||||
on<BackToCeilingGridViewEvent>(_backToGridView);
|
||||
on<CeilingFactoryResetEvent>(_onFactoryReset);
|
||||
}
|
||||
|
||||
void _fetchCeilingSensorStatus(
|
||||
CeilingInitialEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
emit(CeilingLoadingInitialState());
|
||||
try {
|
||||
var response = await DevicesManagementApi()
|
||||
.getDeviceStatus(event.deviceId);
|
||||
var response =
|
||||
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus = CeilingSensorModel.fromJson(response.status);
|
||||
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||
// _listenToChanges();
|
||||
@ -188,4 +189,22 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onFactoryReset(
|
||||
CeilingFactoryResetEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus));
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryResetModel,
|
||||
event.devicesId,
|
||||
);
|
||||
if (!response) {
|
||||
emit(const CeilingFailedState(error: 'Failed'));
|
||||
} else {
|
||||
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(CeilingFailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
|
||||
abstract class CeilingSensorEvent extends Equatable {
|
||||
const CeilingSensorEvent();
|
||||
@ -69,3 +70,16 @@ class ShowCeilingDescriptionEvent extends CeilingSensorEvent {
|
||||
}
|
||||
|
||||
class BackToCeilingGridViewEvent extends CeilingSensorEvent {}
|
||||
|
||||
class CeilingFactoryResetEvent extends CeilingSensorEvent {
|
||||
final String devicesId;
|
||||
final FactoryResetModel factoryResetModel;
|
||||
|
||||
const CeilingFactoryResetEvent({
|
||||
required this.devicesId,
|
||||
required this.factoryResetModel,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [devicesId, factoryResetModel];
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
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/ceiling_sensor/bloc/bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/state.dart';
|
||||
@ -112,7 +113,17 @@ class CeilingSensorBatchControlView extends StatelessWidget
|
||||
),
|
||||
),
|
||||
FirmwareUpdateWidget(deviceId: devicesIds.first, version: 4),
|
||||
FactoryResetWidget(deviceId: devicesIds.first),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<CeilingSensorBloc>().add(
|
||||
CeilingFactoryResetEvent(
|
||||
devicesId: devicesIds.first,
|
||||
factoryResetModel:
|
||||
FactoryResetModel(devicesUuid: devicesIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
|
||||
on<CurtainFetchBatchStatus>(_onFetchBatchStatus);
|
||||
on<CurtainControl>(_onCurtainControl);
|
||||
on<CurtainBatchControl>(_onCurtainBatchControl);
|
||||
on<CurtainFactoryReset>(_onFactoryReset);
|
||||
}
|
||||
|
||||
FutureOr<void> _onFetchDeviceStatus(
|
||||
@ -139,4 +140,22 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
|
||||
isBatch: true,
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> _onFactoryReset(
|
||||
CurtainFactoryReset event, Emitter<CurtainState> emit) async {
|
||||
emit(CurtainStatusLoading());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryReset,
|
||||
event.deviceId,
|
||||
);
|
||||
if (!response) {
|
||||
emit(const CurtainControlError('Failed'));
|
||||
} else {
|
||||
add(CurtainFetchDeviceStatus(event.deviceId));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(CurtainControlError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
|
||||
sealed class CurtainEvent extends Equatable {
|
||||
const CurtainEvent();
|
||||
@ -48,3 +49,14 @@ class CurtainBatchControl extends CurtainEvent {
|
||||
@override
|
||||
List<Object> get props => [devicesIds, code, value];
|
||||
}
|
||||
|
||||
class CurtainFactoryReset extends CurtainEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryReset;
|
||||
|
||||
const CurtainFactoryReset(
|
||||
{required this.deviceId, required this.factoryReset});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceId, factoryReset];
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/curtain_toggle.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart';
|
||||
@ -68,7 +69,16 @@ class CurtainBatchStatusView extends StatelessWidget
|
||||
},
|
||||
),
|
||||
FirmwareUpdateWidget(deviceId: devicesIds.first, version: 5),
|
||||
FactoryResetWidget(deviceId: devicesIds.first),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<CurtainBloc>().add(
|
||||
CurtainFactoryReset(
|
||||
deviceId: devicesIds.first,
|
||||
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ class DoorLockBloc extends Bloc<DoorLockEvent, DoorLockState> {
|
||||
on<DoorLockFetchStatus>(_onFetchDeviceStatus);
|
||||
//on<DoorLockControl>(_onDoorLockControl);
|
||||
on<UpdateLockEvent>(_updateLock);
|
||||
on<DoorLockFactoryReset>(_onFactoryReset);
|
||||
}
|
||||
|
||||
FutureOr<void> _onFetchDeviceStatus(
|
||||
@ -113,4 +114,22 @@ class DoorLockBloc extends Bloc<DoorLockEvent, DoorLockState> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onFactoryReset(
|
||||
DoorLockFactoryReset event, Emitter<DoorLockState> emit) async {
|
||||
emit(DoorLockStatusLoading());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryReset,
|
||||
event.deviceId,
|
||||
);
|
||||
if (!response) {
|
||||
emit(const DoorLockControlError('Failed'));
|
||||
} else {
|
||||
add(DoorLockFetchStatus(event.deviceId));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(DoorLockControlError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
|
||||
sealed class DoorLockEvent extends Equatable {
|
||||
const DoorLockEvent();
|
||||
@ -38,4 +39,15 @@ class UpdateLockEvent extends DoorLockEvent {
|
||||
List<Object> get props => [value];
|
||||
}
|
||||
|
||||
class DoorLockFactoryReset extends DoorLockEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryReset;
|
||||
|
||||
const DoorLockFactoryReset({
|
||||
required this.deviceId,
|
||||
required this.factoryReset,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceId, factoryReset];
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
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/door_lock/bloc/door_lock_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
@ -30,7 +34,16 @@ class DoorLockBatchControlView extends StatelessWidget
|
||||
deviceId: devicesIds.first,
|
||||
version: 12,
|
||||
),
|
||||
FactoryResetWidget(deviceId: devicesIds.first),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
BlocProvider.of<DoorLockBloc>(context).add(
|
||||
DoorLockFactoryReset(
|
||||
deviceId: devicesIds.first,
|
||||
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
|
||||
@ -12,6 +13,7 @@ class GateWayBloc extends Bloc<GateWayEvent, GateWayState> {
|
||||
GateWayBloc() : super(GateWayInitial()) {
|
||||
on<GateWayFetch>((event, emit) {});
|
||||
on<GatWayById>(_getGatWayById);
|
||||
on<GateWayFactoryReset>(_onFactoryReset);
|
||||
}
|
||||
|
||||
FutureOr<void> _getGatWayById(
|
||||
@ -27,4 +29,22 @@ class GateWayBloc extends Bloc<GateWayEvent, GateWayState> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onFactoryReset(
|
||||
GateWayFactoryReset event, Emitter<GateWayState> emit) async {
|
||||
emit(GatewayLoadingState());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryReset,
|
||||
event.deviceId,
|
||||
);
|
||||
if (!response) {
|
||||
emit(const ErrorState(message: 'Failed'));
|
||||
} else {
|
||||
add(GatWayById(event.deviceId));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(ErrorState(message: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,3 +18,15 @@ class GatWayById extends GateWayEvent {
|
||||
final String getWayId;
|
||||
const GatWayById(this.getWayId);
|
||||
}
|
||||
|
||||
class GateWayFactoryReset extends GateWayEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryReset;
|
||||
const GateWayFactoryReset({
|
||||
required this.deviceId,
|
||||
required this.factoryReset,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceId, factoryReset];
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
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/gateway/bloc/gate_way_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||
@ -36,7 +37,17 @@ class GatewayBatchControlView extends StatelessWidget
|
||||
),
|
||||
children: [
|
||||
FirmwareUpdateWidget(deviceId: gatewayIds.first, version: 2),
|
||||
FactoryResetWidget(deviceId: gatewayIds.first),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<GateWayBloc>().add(
|
||||
GateWayFactoryReset(
|
||||
deviceId: gatewayIds.first,
|
||||
factoryReset:
|
||||
FactoryResetModel(devicesUuid: gatewayIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
|
@ -75,16 +75,18 @@ class MainDoorSensorControlView extends StatelessWidget
|
||||
),
|
||||
children: [
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: true,
|
||||
name: status.doorContactState ? 'Open' : 'Close',
|
||||
icon: Assets.mainDoor,
|
||||
icon: Assets.openCloseDoor,
|
||||
onTap: () {},
|
||||
status: status.doorContactState,
|
||||
textColor: ColorsManager.red,
|
||||
paddingAmount: 8,
|
||||
),
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: true,
|
||||
name: 'Open/Close\nRecord',
|
||||
icon: Assets.mainDoorReports,
|
||||
icon: Assets.openCloseRecords,
|
||||
onTap: () {
|
||||
final from = DateTime.now()
|
||||
.subtract(const Duration(days: 30))
|
||||
@ -103,6 +105,7 @@ class MainDoorSensorControlView extends StatelessWidget
|
||||
textColor: ColorsManager.blackColor,
|
||||
),
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: false,
|
||||
name: 'Notifications\nSettings',
|
||||
icon: Assets.mainDoorNotifi,
|
||||
onTap: () {
|
||||
@ -113,6 +116,7 @@ class MainDoorSensorControlView extends StatelessWidget
|
||||
},
|
||||
status: false,
|
||||
textColor: ColorsManager.blackColor,
|
||||
paddingAmount: 14,
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -128,6 +132,7 @@ class IconNameStatusContainer extends StatelessWidget {
|
||||
required this.status,
|
||||
required this.textColor,
|
||||
this.paddingAmount = 12,
|
||||
required this.isFullIcon,
|
||||
});
|
||||
|
||||
final String name;
|
||||
@ -136,6 +141,7 @@ class IconNameStatusContainer extends StatelessWidget {
|
||||
final bool status;
|
||||
final Color textColor;
|
||||
final double? paddingAmount;
|
||||
final bool isFullIcon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -145,6 +151,14 @@ class IconNameStatusContainer extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (isFullIcon)
|
||||
ClipOval(
|
||||
child: SvgPicture.asset(
|
||||
icon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
)
|
||||
else
|
||||
Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
@ -152,12 +166,12 @@ class IconNameStatusContainer extends StatelessWidget {
|
||||
shape: BoxShape.circle,
|
||||
color: ColorsManager.whiteColors,
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
//margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
padding: EdgeInsets.all(paddingAmount ?? 12),
|
||||
child: ClipOval(
|
||||
child: SvgPicture.asset(
|
||||
icon,
|
||||
fit: BoxFit.fill,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -15,6 +15,7 @@ class WallLightSwitchBloc
|
||||
on<WallLightSwitchControl>(_onControl);
|
||||
on<WallLightSwitchFetchBatchEvent>(_onFetchBatchStatus);
|
||||
on<WallLightSwitchBatchControl>(_onBatchControl);
|
||||
on<WallLightFactoryReset>(_onFactoryReset);
|
||||
}
|
||||
|
||||
late WallLightStatusModel deviceStatus;
|
||||
@ -153,4 +154,22 @@ class WallLightSwitchBloc
|
||||
isBatch: true,
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> _onFactoryReset(
|
||||
WallLightFactoryReset event, Emitter<WallLightSwitchState> emit) async {
|
||||
emit(WallLightSwitchLoading());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryReset,
|
||||
event.deviceId,
|
||||
);
|
||||
if (!response) {
|
||||
emit(WallLightSwitchError('Failed'));
|
||||
} else {
|
||||
emit(WallLightSwitchStatusLoaded(deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(WallLightSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
|
||||
class WallLightSwitchEvent extends Equatable {
|
||||
@override
|
||||
@ -46,3 +47,13 @@ class WallLightSwitchBatchControl extends WallLightSwitchEvent {
|
||||
@override
|
||||
List<Object> get props => [devicesIds, code, value];
|
||||
}
|
||||
|
||||
class WallLightFactoryReset extends WallLightSwitchEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryReset;
|
||||
|
||||
WallLightFactoryReset({required this.deviceId, required this.factoryReset});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceId, factoryReset];
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
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/one_gang_switch/bloc/wall_light_switch_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart';
|
||||
@ -77,7 +78,13 @@ class WallLightBatchControlView extends StatelessWidget
|
||||
deviceId: deviceIds.first,
|
||||
version: 12,
|
||||
),
|
||||
FactoryResetWidget(deviceId: deviceIds.first),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<WallLightSwitchBloc>().add(WallLightFactoryReset(
|
||||
deviceId: status.uuid,
|
||||
factoryReset: FactoryResetModel(devicesUuid: deviceIds)));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -6,11 +6,15 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class FactoryResetWidget extends StatelessWidget {
|
||||
const FactoryResetWidget({super.key, required String deviceId});
|
||||
const FactoryResetWidget({super.key, required this.callFactoryReset});
|
||||
|
||||
final Null Function() callFactoryReset;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DeviceControlsContainer(
|
||||
child: GestureDetector(
|
||||
onTap: callFactoryReset,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@ -37,6 +41,7 @@ class FactoryResetWidget extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import 'dart:async';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.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/three_gang_switch/models/living_room_model.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
|
||||
@ -20,6 +21,7 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
|
||||
on<LivingRoomControl>(_livingRoomControl);
|
||||
on<LivingRoomBatchControl>(_livingRoomBatchControl);
|
||||
on<LivingRoomFetchBatchEvent>(_livingRoomFetchBatchControl);
|
||||
on<LivingRoomFactoryResetEvent>(_livingRoomFactoryReset);
|
||||
}
|
||||
|
||||
FutureOr<void> _onFetchDeviceStatus(LivingRoomFetchDeviceStatusEvent event,
|
||||
@ -165,4 +167,22 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
|
||||
isBatch: true,
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> _livingRoomFactoryReset(
|
||||
LivingRoomFactoryResetEvent event, Emitter<LivingRoomState> emit) async {
|
||||
emit(LivingRoomDeviceStatusLoading());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryReset,
|
||||
event.uuid,
|
||||
);
|
||||
if (!response) {
|
||||
emit(const LivingRoomDeviceManagementError('Failed'));
|
||||
} else {
|
||||
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(LivingRoomDeviceManagementError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,3 +49,12 @@ class LivingRoomBatchControl extends LivingRoomEvent {
|
||||
@override
|
||||
List<Object> get props => [devicesIds, code, value];
|
||||
}
|
||||
|
||||
class LivingRoomFactoryResetEvent extends LivingRoomEvent {
|
||||
final String uuid;
|
||||
final FactoryResetModel factoryReset;
|
||||
const LivingRoomFactoryResetEvent(this.uuid, this.factoryReset);
|
||||
|
||||
@override
|
||||
List<Object> get props => [uuid, factoryReset];
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
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/shared/batch_control/factory_reset.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart';
|
||||
@ -105,7 +106,14 @@ class LivingRoomBatchControlsView extends StatelessWidget
|
||||
deviceId: deviceIds.first,
|
||||
version: 12,
|
||||
),
|
||||
FactoryResetWidget(deviceId: deviceIds.first),
|
||||
FactoryResetWidget(callFactoryReset: () {
|
||||
context.read<LivingRoomBloc>().add(
|
||||
LivingRoomFactoryResetEvent(
|
||||
status.uuid,
|
||||
FactoryResetModel(devicesUuid: deviceIds),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -13,6 +13,7 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
|
||||
on<TwoGangSwitchControl>(_onControl);
|
||||
on<TwoGangSwitchFetchBatchEvent>(_onFetchBatchStatus);
|
||||
on<TwoGangSwitchBatchControl>(_onBatchControl);
|
||||
on<TwoGangFactoryReset>(_onFactoryReset);
|
||||
}
|
||||
|
||||
late TwoGangStatusModel deviceStatus;
|
||||
@ -155,4 +156,22 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
|
||||
isBatch: true,
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> _onFactoryReset(
|
||||
TwoGangFactoryReset event, Emitter<TwoGangSwitchState> emit) async {
|
||||
emit(TwoGangSwitchLoading());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryReset,
|
||||
event.deviceId,
|
||||
);
|
||||
if (!response) {
|
||||
emit(TwoGangSwitchError('Failed'));
|
||||
} else {
|
||||
emit(TwoGangSwitchStatusLoaded(deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(TwoGangSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
|
||||
class TwoGangSwitchEvent extends Equatable {
|
||||
@override
|
||||
@ -46,3 +47,13 @@ class TwoGangSwitchBatchControl extends TwoGangSwitchEvent {
|
||||
@override
|
||||
List<Object> get props => [deviceId, code, value];
|
||||
}
|
||||
|
||||
class TwoGangFactoryReset extends TwoGangSwitchEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryReset;
|
||||
|
||||
TwoGangFactoryReset({required this.deviceId, required this.factoryReset});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceId, factoryReset];
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
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/shared/batch_control/factory_reset.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||
@ -87,7 +88,14 @@ class TwoGangBatchControlView extends StatelessWidget
|
||||
deviceId: deviceIds.first,
|
||||
version: 12,
|
||||
),
|
||||
FactoryResetWidget(deviceId: deviceIds.first),
|
||||
FactoryResetWidget(callFactoryReset: () {
|
||||
context.read<TwoGangSwitchBloc>().add(
|
||||
TwoGangFactoryReset(
|
||||
deviceId: status.uuid,
|
||||
factoryReset: FactoryResetModel(devicesUuid: deviceIds),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -19,6 +19,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
on<GetDeviceReportsEvent>(_getDeviceReports);
|
||||
on<ShowDescriptionEvent>(_showDescription);
|
||||
on<BackToGridViewEvent>(_backToGridView);
|
||||
on<WallSensorFactoryResetEvent>(_onFactoryReset);
|
||||
}
|
||||
|
||||
void _fetchWallSensorStatus(
|
||||
@ -168,4 +169,22 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
BackToGridViewEvent event, Emitter<WallSensorState> emit) {
|
||||
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||
}
|
||||
|
||||
FutureOr<void> _onFactoryReset(
|
||||
WallSensorFactoryResetEvent event, Emitter<WallSensorState> emit) async {
|
||||
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryReset,
|
||||
event.deviceId,
|
||||
);
|
||||
if (!response) {
|
||||
emit(const WallSensorFailedState(error: 'Failed'));
|
||||
} else {
|
||||
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(WallSensorFailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
|
||||
abstract class WallSensorEvent extends Equatable {
|
||||
const WallSensorEvent();
|
||||
@ -59,3 +60,13 @@ class WallSensorBatchControlEvent extends WallSensorEvent {
|
||||
@override
|
||||
List<Object> get props => [deviceIds, code, value];
|
||||
}
|
||||
|
||||
class WallSensorFactoryResetEvent extends WallSensorEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryReset;
|
||||
|
||||
const WallSensorFactoryResetEvent({
|
||||
required this.deviceId,
|
||||
required this.factoryReset,
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
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/shared/batch_control/factory_reset.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart';
|
||||
@ -118,7 +119,16 @@ class WallSensorBatchControlView extends StatelessWidget
|
||||
),
|
||||
),
|
||||
FirmwareUpdateWidget(deviceId: devicesIds.first, version: 2),
|
||||
FactoryResetWidget(deviceId: devicesIds.first),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<WallSensorBloc>().add(
|
||||
WallSensorFactoryResetEvent(
|
||||
deviceId: devicesIds.first,
|
||||
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -1,9 +1,14 @@
|
||||
// water_heater_bloc.dart
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||
|
||||
part 'water_heater_event.dart';
|
||||
part 'water_heater_state.dart';
|
||||
@ -12,43 +17,129 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
WaterHeaterBloc() : super(WaterHeaterInitial()) {
|
||||
on<WaterHeaterFetchStatusEvent>(_fetchWaterHeaterStatus);
|
||||
on<ToggleWaterHeaterEvent>(_controlWaterHeater);
|
||||
on<FetchWaterHeaterBatchStatusEvent>(_batchFetchWaterHeater);
|
||||
on<ControlWaterHeaterBatchEvent>(_batchControlWaterHeater);
|
||||
on<UpdateScheduleEvent>(_updateScheduleEvent);
|
||||
on<StopScheduleEvent>(_stopScheduleEvent);
|
||||
on<DecrementCountdownEvent>(_onDecrementCountdown);
|
||||
on<InitializeAddScheduleEvent>(_initializeAddSchedule);
|
||||
on<UpdateSelectedTimeEvent>(_updateSelectedTime);
|
||||
on<UpdateSelectedDayEvent>(_updateSelectedDay);
|
||||
on<UpdateFunctionOnEvent>(_updateFunctionOn);
|
||||
|
||||
on<GetSchedulesEvent>(_getSchedule);
|
||||
on<AddScheduleEvent>(_onAddSchedule);
|
||||
on<DeleteScheduleEvent>(_onDeleteSchedule);
|
||||
on<UpdateScheduleEntryEvent>(_onUpdateSchedule);
|
||||
}
|
||||
|
||||
late WaterHeaterStatusModel deviceStatus;
|
||||
Timer? _timer;
|
||||
Timer? _countdownTimer;
|
||||
// Timer? _inchingTimer;
|
||||
|
||||
FutureOr<void> _initializeAddSchedule(
|
||||
InitializeAddScheduleEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
if (event.isEditing) {
|
||||
emit(currentState.copyWith(
|
||||
selectedTime: event.selectedTime,
|
||||
selectedDays: event.selectedDays ?? List.filled(7, false),
|
||||
functionOn: event.functionOn ?? false,
|
||||
isEditing: event.isEditing,
|
||||
));
|
||||
} else {
|
||||
emit(currentState.copyWith(
|
||||
selectedTime: null,
|
||||
selectedDays: List.filled(7, false),
|
||||
functionOn: false,
|
||||
isEditing: false,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _updateSelectedTime(
|
||||
UpdateSelectedTimeEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
emit(currentState.copyWith(selectedTime: event.selectedTime));
|
||||
}
|
||||
|
||||
FutureOr<void> _updateSelectedDay(
|
||||
UpdateSelectedDayEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
final updatedDays = List<bool>.from(currentState.selectedDays);
|
||||
updatedDays[event.index] = event.value;
|
||||
emit(currentState.copyWith(selectedDays: updatedDays));
|
||||
}
|
||||
|
||||
FutureOr<void> _updateFunctionOn(
|
||||
UpdateFunctionOnEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
emit(currentState.copyWith(functionOn: event.isOn));
|
||||
}
|
||||
|
||||
FutureOr<void> _updateScheduleEvent(
|
||||
UpdateScheduleEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) async {
|
||||
final currentState = state as WaterHeaterScheduleViewState;
|
||||
final currentState = state;
|
||||
if (currentState is WaterHeaterDeviceStatusLoaded) {
|
||||
if (event.scheduleMode == ScheduleModes.schedule) {
|
||||
emit(currentState.copyWith(
|
||||
scheduleMode: ScheduleModes.schedule,
|
||||
));
|
||||
}
|
||||
if (event.scheduleMode == ScheduleModes.countdown) {
|
||||
final countdownRemaining =
|
||||
Duration(hours: event.hours, minutes: event.minutes);
|
||||
|
||||
final countdownRemaining = currentState.isActive
|
||||
? currentState.countdownRemaining
|
||||
: Duration(hours: event.hours, minutes: event.minutes);
|
||||
|
||||
emit(WaterHeaterScheduleViewState(
|
||||
scheduleMode: event.scheduleMode,
|
||||
hours: countdownRemaining!.inHours,
|
||||
minutes: countdownRemaining.inMinutes % 60,
|
||||
isActive: currentState.isActive,
|
||||
emit(currentState.copyWith(
|
||||
scheduleMode: ScheduleModes.countdown,
|
||||
countdownHours: countdownRemaining.inHours,
|
||||
countdownMinutes: countdownRemaining.inMinutes % 60,
|
||||
isCountdownActive: currentState.isCountdownActive,
|
||||
countdownRemaining: countdownRemaining,
|
||||
));
|
||||
|
||||
if (currentState.isActive) {
|
||||
_startCountdown(countdownRemaining, emit);
|
||||
if (!currentState.isCountdownActive! &&
|
||||
countdownRemaining > Duration.zero) {
|
||||
_startCountdownTimer(emit, countdownRemaining);
|
||||
}
|
||||
} else if (event.scheduleMode == ScheduleModes.inching) {
|
||||
final inchingDuration =
|
||||
Duration(hours: event.hours, minutes: event.minutes);
|
||||
|
||||
emit(currentState.copyWith(
|
||||
scheduleMode: ScheduleModes.inching,
|
||||
inchingHours: inchingDuration.inHours,
|
||||
inchingMinutes: inchingDuration.inMinutes % 60,
|
||||
isInchingActive: currentState.isInchingActive,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _controlWaterHeater(
|
||||
ToggleWaterHeaterEvent event, Emitter<WaterHeaterState> emit) async {
|
||||
ToggleWaterHeaterEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) async {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
|
||||
final oldValue = _getValueByCode(event.code);
|
||||
|
||||
_updateLocalValue(event.code, event.value, emit);
|
||||
_updateLocalValue(event.code, event.value);
|
||||
|
||||
emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
|
||||
emit(currentState.copyWith(
|
||||
status: deviceStatus,
|
||||
));
|
||||
|
||||
final success = await _runDebounce(
|
||||
deviceId: event.deviceId,
|
||||
@ -56,71 +147,319 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: false,
|
||||
);
|
||||
|
||||
if (success && (event.code == "countdown_1" || event.code == "switch_inching")) {
|
||||
if (success) {
|
||||
if (event.code == "countdown_1") {
|
||||
final countdownDuration = Duration(seconds: event.value);
|
||||
|
||||
emit(WaterHeaterScheduleViewState(
|
||||
scheduleMode: deviceStatus.scheduleMode,
|
||||
hours: countdownDuration.inHours,
|
||||
minutes: (countdownDuration.inMinutes % 60),
|
||||
isActive: true,
|
||||
emit(currentState.copyWith(
|
||||
countdownHours: countdownDuration.inHours,
|
||||
countdownMinutes: countdownDuration.inMinutes % 60,
|
||||
countdownRemaining: countdownDuration,
|
||||
isCountdownActive: true,
|
||||
));
|
||||
|
||||
_startCountdown(countdownDuration, emit);
|
||||
if (countdownDuration.inSeconds > 0) {
|
||||
_startCountdownTimer(emit, countdownDuration);
|
||||
} else {
|
||||
_countdownTimer?.cancel();
|
||||
emit(currentState.copyWith(
|
||||
countdownHours: 0,
|
||||
countdownMinutes: 0,
|
||||
countdownRemaining: Duration.zero,
|
||||
isCountdownActive: false,
|
||||
));
|
||||
}
|
||||
} else if (event.code == "switch_inching") {
|
||||
final inchingDuration = Duration(seconds: event.value);
|
||||
//if (inchingDuration.inSeconds > 0) {
|
||||
// _startInchingTimer(emit, inchingDuration);
|
||||
// } else {
|
||||
emit(currentState.copyWith(
|
||||
inchingHours: inchingDuration.inHours,
|
||||
inchingMinutes: inchingDuration.inMinutes % 60,
|
||||
isInchingActive: true,
|
||||
));
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _stopScheduleEvent(
|
||||
StopScheduleEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) async {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
final isCountDown = currentState.scheduleMode == ScheduleModes.countdown;
|
||||
|
||||
_countdownTimer?.cancel();
|
||||
|
||||
if (isCountDown) {
|
||||
emit(currentState.copyWith(
|
||||
countdownHours: 0,
|
||||
countdownMinutes: 0,
|
||||
countdownRemaining: Duration.zero,
|
||||
isCountdownActive: false,
|
||||
));
|
||||
} else if (currentState.scheduleMode == ScheduleModes.inching) {
|
||||
emit(currentState.copyWith(
|
||||
inchingHours: 0,
|
||||
inchingMinutes: 0,
|
||||
isInchingActive: false,
|
||||
));
|
||||
}
|
||||
|
||||
try {
|
||||
final status = await DevicesManagementApi().deviceControl(
|
||||
event.deviceId,
|
||||
Status(
|
||||
code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0),
|
||||
);
|
||||
if (!status) {
|
||||
emit(const WaterHeaterFailedState(error: 'Failed to stop schedule.'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(WaterHeaterFailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _fetchWaterHeaterStatus(
|
||||
WaterHeaterFetchStatusEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) async {
|
||||
emit(WaterHeaterLoadingState());
|
||||
|
||||
try {
|
||||
final status =
|
||||
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus =
|
||||
WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
|
||||
|
||||
if (deviceStatus.scheduleMode == ScheduleModes.countdown) {
|
||||
final countdownRemaining = Duration(
|
||||
hours: deviceStatus.countdownHours,
|
||||
minutes: deviceStatus.countdownMinutes,
|
||||
);
|
||||
|
||||
if (countdownRemaining > Duration.zero) {
|
||||
emit(WaterHeaterDeviceStatusLoaded(
|
||||
deviceStatus,
|
||||
scheduleMode: ScheduleModes.countdown,
|
||||
countdownHours: deviceStatus.countdownHours,
|
||||
countdownMinutes: deviceStatus.countdownMinutes,
|
||||
isCountdownActive: true,
|
||||
countdownRemaining: countdownRemaining,
|
||||
));
|
||||
_startCountdownTimer(emit, countdownRemaining);
|
||||
} else {
|
||||
emit(WaterHeaterDeviceStatusLoaded(
|
||||
deviceStatus,
|
||||
scheduleMode: ScheduleModes.countdown,
|
||||
countdownHours: 0,
|
||||
countdownMinutes: 0,
|
||||
isCountdownActive: false,
|
||||
countdownRemaining: Duration.zero,
|
||||
));
|
||||
}
|
||||
} else if (deviceStatus.scheduleMode == ScheduleModes.inching) {
|
||||
final inchingDuration = Duration(
|
||||
hours: deviceStatus.inchingHours,
|
||||
minutes: deviceStatus.inchingMinutes,
|
||||
);
|
||||
|
||||
if (inchingDuration > Duration.zero) {
|
||||
emit(WaterHeaterDeviceStatusLoaded(
|
||||
deviceStatus,
|
||||
scheduleMode: ScheduleModes.inching,
|
||||
inchingHours: deviceStatus.inchingHours,
|
||||
inchingMinutes: deviceStatus.inchingMinutes,
|
||||
isInchingActive: true,
|
||||
));
|
||||
//_startInchingTimer(emit, inchingDuration);
|
||||
} else {
|
||||
emit(WaterHeaterDeviceStatusLoaded(
|
||||
deviceStatus,
|
||||
scheduleMode: ScheduleModes.inching,
|
||||
inchingHours: 0,
|
||||
inchingMinutes: 0,
|
||||
isInchingActive: false,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
emit(WaterHeaterDeviceStatusLoaded(
|
||||
deviceStatus,
|
||||
scheduleMode: deviceStatus.scheduleMode,
|
||||
countdownHours: 0,
|
||||
countdownMinutes: 0,
|
||||
inchingHours: 0,
|
||||
inchingMinutes: 0,
|
||||
isCountdownActive: false,
|
||||
isInchingActive: false,
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(WaterHeaterFailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void _startCountdownTimer(
|
||||
Emitter<WaterHeaterState> emit,
|
||||
Duration countdownRemaining,
|
||||
) {
|
||||
_countdownTimer?.cancel();
|
||||
|
||||
_countdownTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
|
||||
add(DecrementCountdownEvent());
|
||||
});
|
||||
}
|
||||
|
||||
// void _startInchingTimer(
|
||||
// Emitter<WaterHeaterState> emit,
|
||||
// Duration inchingDuration,
|
||||
// ) {
|
||||
// _inchingTimer?.cancel();
|
||||
|
||||
// _inchingTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
|
||||
// add(DecrementInchingEvent());
|
||||
// });
|
||||
// }
|
||||
|
||||
_onDecrementCountdown(
|
||||
DecrementCountdownEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
|
||||
if (currentState.countdownRemaining != null &&
|
||||
currentState.countdownRemaining! > Duration.zero) {
|
||||
final newRemaining =
|
||||
currentState.countdownRemaining! - const Duration(minutes: 1);
|
||||
|
||||
if (newRemaining <= Duration.zero) {
|
||||
_countdownTimer?.cancel();
|
||||
emit(currentState.copyWith(
|
||||
countdownHours: 0,
|
||||
countdownMinutes: 0,
|
||||
isCountdownActive: false,
|
||||
countdownRemaining: Duration.zero,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
int totalSeconds = newRemaining.inSeconds;
|
||||
|
||||
int newHours = totalSeconds ~/ 3600;
|
||||
int newMinutes = (totalSeconds % 3600) ~/ 60;
|
||||
|
||||
emit(currentState.copyWith(
|
||||
countdownHours: newHours,
|
||||
countdownMinutes: newMinutes,
|
||||
countdownRemaining: newRemaining,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FutureOr<void> _onDecrementInching(
|
||||
// DecrementInchingEvent event,
|
||||
// Emitter<WaterHeaterState> emit,
|
||||
// ) {
|
||||
// if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
// final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
|
||||
// if (currentState.inchingHours > 0 || currentState.inchingMinutes > 0) {
|
||||
// final newRemaining = Duration(
|
||||
// hours: currentState.inchingHours,
|
||||
// minutes: currentState.inchingMinutes,
|
||||
// ) -
|
||||
// const Duration(minutes: 1);
|
||||
|
||||
// if (newRemaining <= Duration.zero) {
|
||||
// _inchingTimer?.cancel();
|
||||
// emit(currentState.copyWith(
|
||||
// inchingHours: 0,
|
||||
// inchingMinutes: 0,
|
||||
// isInchingActive: false,
|
||||
// ));
|
||||
// } else {
|
||||
// emit(currentState.copyWith(
|
||||
// inchingHours: newRemaining.inHours,
|
||||
// inchingMinutes: newRemaining.inMinutes % 60,
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<bool> _runDebounce({
|
||||
required String deviceId,
|
||||
required dynamic deviceId,
|
||||
required String code,
|
||||
required dynamic value,
|
||||
required dynamic oldValue,
|
||||
required Emitter<WaterHeaterState> emit,
|
||||
required bool isBatch,
|
||||
}) async {
|
||||
final completer = Completer<bool>();
|
||||
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
}
|
||||
|
||||
_timer = Timer(const Duration(milliseconds: 500), () async {
|
||||
try {
|
||||
final status = await DevicesManagementApi().deviceControl(
|
||||
late bool status;
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
if (isBatch) {
|
||||
status = await DevicesManagementApi().deviceBatchControl(
|
||||
deviceId,
|
||||
code,
|
||||
value,
|
||||
);
|
||||
} else {
|
||||
status = await DevicesManagementApi().deviceControl(
|
||||
deviceId,
|
||||
Status(code: code, value: value),
|
||||
);
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
_revertValueAndEmit(deviceId, code, oldValue, emit);
|
||||
completer.complete(false);
|
||||
_revertValue(code, oldValue, emit.call);
|
||||
return false;
|
||||
} else {
|
||||
completer.complete(true);
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
_revertValueAndEmit(deviceId, code, oldValue, emit);
|
||||
completer.complete(false);
|
||||
_revertValue(code, oldValue, emit.call);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
void _revertValueAndEmit(
|
||||
String deviceId, String code, dynamic oldValue, Emitter<WaterHeaterState> emit) {
|
||||
_updateLocalValue(code, oldValue, emit);
|
||||
emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
|
||||
void _revertValue(String code, dynamic oldValue,
|
||||
void Function(WaterHeaterState state) emit) {
|
||||
_updateLocalValue(code, oldValue);
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
emit(currentState.copyWith(
|
||||
status: deviceStatus,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void _updateLocalValue(String code, dynamic value, Emitter<WaterHeaterState> emit) {
|
||||
void _updateLocalValue(String code, dynamic value) {
|
||||
switch (code) {
|
||||
case 'switch_1':
|
||||
if (value is bool) {
|
||||
deviceStatus = deviceStatus.copyWith(heaterSwitch: value);
|
||||
}
|
||||
break;
|
||||
case 'countdown_1':
|
||||
if (value is int) {
|
||||
deviceStatus = deviceStatus.copyWith(
|
||||
countdownHours: value ~/ 60,
|
||||
countdownMinutes: value % 60,
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -130,97 +469,178 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
switch (code) {
|
||||
case 'switch_1':
|
||||
return deviceStatus.heaterSwitch;
|
||||
|
||||
case 'countdown_1':
|
||||
return deviceStatus.countdownHours * 60 + deviceStatus.countdownMinutes;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _fetchWaterHeaterStatus(
|
||||
WaterHeaterFetchStatusEvent event, Emitter<WaterHeaterState> emit) async {
|
||||
@override
|
||||
Future<void> close() {
|
||||
_countdownTimer?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
FutureOr<void> _getSchedule(
|
||||
GetSchedulesEvent event, Emitter<WaterHeaterState> emit) async {
|
||||
emit(ScheduleLoadingState());
|
||||
|
||||
try {
|
||||
// List<ScheduleModel> schedules = await DevicesManagementApi()
|
||||
// .getDeviceSchedules(deviceStatus.uuid, event.category);
|
||||
|
||||
List<ScheduleModel> schedules = const [];
|
||||
|
||||
emit(WaterHeaterDeviceStatusLoaded(
|
||||
deviceStatus,
|
||||
schedules: schedules,
|
||||
scheduleMode: ScheduleModes.schedule,
|
||||
));
|
||||
} catch (e) {
|
||||
//(const WaterHeaterFailedState(error: 'Failed to fetch schedules.'));
|
||||
emit(WaterHeaterDeviceStatusLoaded(
|
||||
deviceStatus,
|
||||
schedules: const [],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onAddSchedule(
|
||||
AddScheduleEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) async {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
|
||||
ScheduleModel newSchedule = ScheduleModel(
|
||||
category: event.category,
|
||||
time: formatTimeOfDayToISO(event.time),
|
||||
function: Status(code: 'switch_1', value: event.functionOn),
|
||||
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
|
||||
);
|
||||
|
||||
// emit(ScheduleLoadingState());
|
||||
|
||||
bool success = await DevicesManagementApi()
|
||||
.addScheduleRecord(newSchedule, currentState.status.uuid);
|
||||
|
||||
if (success) {
|
||||
final updatedSchedules =
|
||||
List<ScheduleModel>.from(currentState.schedules)..add(newSchedule);
|
||||
|
||||
emit(currentState.copyWith(schedules: updatedSchedules));
|
||||
} else {
|
||||
emit(currentState);
|
||||
//emit(const WaterHeaterFailedState(error: 'Failed to add schedule.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onUpdateSchedule(
|
||||
UpdateScheduleEntryEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) async {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
|
||||
ScheduleModel updatedSchedule = currentState.schedules[event.index]
|
||||
.copyWith(
|
||||
function: Status(code: 'switch_1', value: event.functionOn));
|
||||
|
||||
// emit(ScheduleLoadingState());
|
||||
|
||||
bool success = await DevicesManagementApi().updateScheduleRecord(
|
||||
enable: event.functionOn,
|
||||
uuid: currentState.status.uuid,
|
||||
scheduleId: event.scheduleId,
|
||||
);
|
||||
|
||||
if (success) {
|
||||
final updatedSchedules =
|
||||
List<ScheduleModel>.from(currentState.schedules)
|
||||
..[event.index] = updatedSchedule;
|
||||
|
||||
emit(currentState.copyWith(schedules: updatedSchedules));
|
||||
} else {
|
||||
emit(currentState);
|
||||
// emit(const WaterHeaterFailedState(error: 'Failed to update schedule.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onDeleteSchedule(
|
||||
DeleteScheduleEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) async {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
|
||||
// emit(ScheduleLoadingState());
|
||||
|
||||
bool success = await DevicesManagementApi()
|
||||
.deleteScheduleRecord(currentState.status.uuid, event.scheduleId);
|
||||
|
||||
if (success) {
|
||||
final updatedSchedules =
|
||||
List<ScheduleModel>.from(currentState.schedules)
|
||||
..removeAt(event.index);
|
||||
|
||||
emit(currentState.copyWith(schedules: updatedSchedules));
|
||||
} else {
|
||||
emit(currentState);
|
||||
// emit(const WaterHeaterFailedState(error: 'Failed to delete schedule.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _batchFetchWaterHeater(FetchWaterHeaterBatchStatusEvent event,
|
||||
Emitter<WaterHeaterState> emit) async {
|
||||
emit(WaterHeaterLoadingState());
|
||||
|
||||
try {
|
||||
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus = WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
|
||||
final status =
|
||||
await DevicesManagementApi().getBatchStatus(event.devicesUuid);
|
||||
deviceStatus = WaterHeaterStatusModel.fromJson(
|
||||
event.devicesUuid.first, status.status);
|
||||
|
||||
if (deviceStatus.countdownHours > 0 || deviceStatus.countdownMinutes > 0) {
|
||||
final remainingDuration = Duration(
|
||||
hours: deviceStatus.countdownHours,
|
||||
minutes: deviceStatus.countdownMinutes,
|
||||
);
|
||||
|
||||
emit(WaterHeaterScheduleViewState(
|
||||
scheduleMode: deviceStatus.scheduleMode,
|
||||
hours: deviceStatus.countdownHours,
|
||||
minutes: deviceStatus.countdownMinutes,
|
||||
isActive: true,
|
||||
countdownRemaining: remainingDuration,
|
||||
));
|
||||
|
||||
// _startCountdown(remainingDuration, emit);
|
||||
} else {
|
||||
emit(WaterHeaterScheduleViewState(
|
||||
scheduleMode: deviceStatus.scheduleMode,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
isActive: false,
|
||||
));
|
||||
}
|
||||
emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
|
||||
} catch (e) {
|
||||
emit(WaterHeaterFailedState(error: e.toString()));
|
||||
emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
|
||||
}
|
||||
}
|
||||
|
||||
void _startCountdown(Duration duration, Emitter<WaterHeaterState> emit) {
|
||||
_timer?.cancel();
|
||||
FutureOr<void> _batchControlWaterHeater(ControlWaterHeaterBatchEvent event,
|
||||
Emitter<WaterHeaterState> emit) async {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
final state = this.state as WaterHeaterScheduleViewState;
|
||||
final remaining = state.countdownRemaining! - const Duration(seconds: 1);
|
||||
final oldValue = _getValueByCode(event.code);
|
||||
|
||||
if (remaining.isNegative || remaining == Duration.zero) {
|
||||
_timer?.cancel();
|
||||
emit(WaterHeaterScheduleViewState(
|
||||
scheduleMode: state.scheduleMode,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
isActive: false,
|
||||
countdownRemaining: Duration.zero,
|
||||
_updateLocalValue(event.code, event.value);
|
||||
|
||||
emit(currentState.copyWith(
|
||||
status: deviceStatus,
|
||||
));
|
||||
} else {
|
||||
emit(WaterHeaterScheduleViewState(
|
||||
scheduleMode: state.scheduleMode,
|
||||
hours: remaining.inHours,
|
||||
minutes: remaining.inMinutes % 60,
|
||||
isActive: true,
|
||||
countdownRemaining: remaining,
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
FutureOr<void> _stopScheduleEvent(
|
||||
StopScheduleEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
_timer?.cancel();
|
||||
deviceStatus = deviceStatus.copyWith(
|
||||
countdownHours: 0,
|
||||
countdownMinutes: 0,
|
||||
scheduleMode: ScheduleModes.countdown,
|
||||
final success = await _runDebounce(
|
||||
deviceId: event.devicesUuid,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: true,
|
||||
);
|
||||
emit(const WaterHeaterScheduleViewState(
|
||||
scheduleMode: ScheduleModes.countdown,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
isActive: false,
|
||||
|
||||
if (success) {
|
||||
if (event.code == "switch_1") {
|
||||
emit(currentState.copyWith(
|
||||
status: deviceStatus,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_timer?.cancel();
|
||||
return super.close();
|
||||
} else {
|
||||
_updateLocalValue(event.code, oldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,14 @@ final class ToggleWaterHeaterEvent extends WaterHeaterEvent {
|
||||
final String deviceId;
|
||||
final String code;
|
||||
|
||||
const ToggleWaterHeaterEvent(
|
||||
{required this.value, required this.deviceId, required this.code});
|
||||
const ToggleWaterHeaterEvent({
|
||||
required this.value,
|
||||
required this.deviceId,
|
||||
required this.code,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [value];
|
||||
List<Object?> get props => [value, deviceId, code];
|
||||
}
|
||||
|
||||
final class UpdateScheduleEvent extends WaterHeaterEvent {
|
||||
@ -24,16 +27,23 @@ final class UpdateScheduleEvent extends WaterHeaterEvent {
|
||||
final int hours;
|
||||
final int minutes;
|
||||
|
||||
const UpdateScheduleEvent(
|
||||
{required this.scheduleMode, required this.hours, required this.minutes});
|
||||
const UpdateScheduleEvent({
|
||||
required this.scheduleMode,
|
||||
required this.hours,
|
||||
required this.minutes,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [scheduleMode, hours, minutes];
|
||||
}
|
||||
|
||||
final class StopScheduleEvent extends WaterHeaterEvent {}
|
||||
final class StopScheduleEvent extends WaterHeaterEvent {
|
||||
final String deviceId;
|
||||
|
||||
class WaterHeaterFetchStatusEvent extends WaterHeaterEvent {
|
||||
const StopScheduleEvent(this.deviceId);
|
||||
}
|
||||
|
||||
final class WaterHeaterFetchStatusEvent extends WaterHeaterEvent {
|
||||
final String deviceId;
|
||||
|
||||
const WaterHeaterFetchStatusEvent(this.deviceId);
|
||||
@ -42,15 +52,131 @@ class WaterHeaterFetchStatusEvent extends WaterHeaterEvent {
|
||||
List<Object?> get props => [deviceId];
|
||||
}
|
||||
|
||||
class WaterHeaterFetchBatchStatusEvent extends WaterHeaterEvent {
|
||||
final String deviceId;
|
||||
final class DecrementCountdownEvent extends WaterHeaterEvent {}
|
||||
|
||||
const WaterHeaterFetchBatchStatusEvent(this.deviceId);
|
||||
final class AddScheduleEvent extends WaterHeaterEvent {
|
||||
final List<bool> selectedDays;
|
||||
final TimeOfDay time;
|
||||
final bool functionOn;
|
||||
final String category;
|
||||
|
||||
const AddScheduleEvent({
|
||||
required this.selectedDays,
|
||||
required this.time,
|
||||
required this.functionOn,
|
||||
required this.category,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceId];
|
||||
List<Object?> get props => [selectedDays, time, functionOn, category];
|
||||
}
|
||||
|
||||
// class ShowScheduleViewEvent extends WaterHeaterEvent {
|
||||
// const ShowScheduleViewEvent();
|
||||
// }
|
||||
final class DeleteScheduleEvent extends WaterHeaterEvent {
|
||||
final int index;
|
||||
final String scheduleId;
|
||||
|
||||
const DeleteScheduleEvent({required this.index, required this.scheduleId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [index, scheduleId];
|
||||
}
|
||||
|
||||
final class UpdateScheduleEntryEvent extends WaterHeaterEvent {
|
||||
final bool functionOn;
|
||||
final String category;
|
||||
final String deviceId;
|
||||
final int index;
|
||||
final String scheduleId;
|
||||
|
||||
const UpdateScheduleEntryEvent({
|
||||
required this.functionOn,
|
||||
required this.category,
|
||||
required this.deviceId,
|
||||
required this.scheduleId,
|
||||
required this.index,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[category, functionOn, deviceId, scheduleId, index];
|
||||
}
|
||||
|
||||
class GetSchedulesEvent extends WaterHeaterEvent {
|
||||
final String uuid;
|
||||
final String category;
|
||||
|
||||
const GetSchedulesEvent({
|
||||
required this.uuid,
|
||||
required this.category,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [uuid, category];
|
||||
}
|
||||
|
||||
class InitializeAddScheduleEvent extends WaterHeaterEvent {
|
||||
final TimeOfDay? selectedTime;
|
||||
final List<bool>? selectedDays;
|
||||
final bool? functionOn;
|
||||
final bool isEditing;
|
||||
final int? index;
|
||||
|
||||
const InitializeAddScheduleEvent({
|
||||
this.selectedTime,
|
||||
this.selectedDays,
|
||||
this.functionOn,
|
||||
this.isEditing = false,
|
||||
this.index,
|
||||
});
|
||||
}
|
||||
|
||||
class UpdateSelectedTimeEvent extends WaterHeaterEvent {
|
||||
final TimeOfDay selectedTime;
|
||||
|
||||
const UpdateSelectedTimeEvent(this.selectedTime);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [selectedTime];
|
||||
}
|
||||
|
||||
class UpdateSelectedDayEvent extends WaterHeaterEvent {
|
||||
final int index;
|
||||
final bool value;
|
||||
|
||||
const UpdateSelectedDayEvent(this.index, this.value);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [index, value];
|
||||
}
|
||||
|
||||
class UpdateFunctionOnEvent extends WaterHeaterEvent {
|
||||
final bool isOn;
|
||||
|
||||
const UpdateFunctionOnEvent(this.isOn);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [isOn];
|
||||
}
|
||||
|
||||
class FetchWaterHeaterBatchStatusEvent extends WaterHeaterEvent {
|
||||
final List<String> devicesUuid;
|
||||
const FetchWaterHeaterBatchStatusEvent({required this.devicesUuid});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [devicesUuid];
|
||||
}
|
||||
|
||||
class ControlWaterHeaterBatchEvent extends WaterHeaterEvent {
|
||||
final List<String> devicesUuid;
|
||||
final String code;
|
||||
final dynamic value;
|
||||
|
||||
const ControlWaterHeaterBatchEvent({
|
||||
required this.devicesUuid,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [devicesUuid, code, value];
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
part of 'water_heater_bloc.dart';
|
||||
// water_heater_state.dart
|
||||
|
||||
enum ScheduleType { countdown, schedule, circulate, inching }
|
||||
part of 'water_heater_bloc.dart';
|
||||
|
||||
sealed class WaterHeaterState extends Equatable {
|
||||
const WaterHeaterState();
|
||||
@ -13,13 +13,97 @@ final class WaterHeaterInitial extends WaterHeaterState {}
|
||||
|
||||
final class WaterHeaterLoadingState extends WaterHeaterState {}
|
||||
|
||||
final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
|
||||
final WaterHeaterStatusModel status;
|
||||
final class ScheduleLoadingState extends WaterHeaterState {}
|
||||
|
||||
const WaterHeaterDeviceStatusLoaded(this.status);
|
||||
class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
|
||||
final WaterHeaterStatusModel status;
|
||||
final ScheduleModes? scheduleMode;
|
||||
|
||||
// Countdown-specific
|
||||
final int? countdownHours;
|
||||
final int? countdownMinutes;
|
||||
final Duration? countdownRemaining;
|
||||
final bool? isCountdownActive;
|
||||
|
||||
// Inching-specific
|
||||
final int? inchingHours;
|
||||
final int? inchingMinutes;
|
||||
final bool? isInchingActive;
|
||||
|
||||
final List<ScheduleModel> schedules;
|
||||
final List<bool> selectedDays;
|
||||
final TimeOfDay? selectedTime;
|
||||
final bool functionOn;
|
||||
final bool isEditing;
|
||||
|
||||
const WaterHeaterDeviceStatusLoaded(
|
||||
this.status, {
|
||||
this.scheduleMode,
|
||||
this.countdownHours,
|
||||
this.countdownMinutes,
|
||||
this.countdownRemaining,
|
||||
this.isCountdownActive,
|
||||
this.inchingHours,
|
||||
this.inchingMinutes,
|
||||
this.isInchingActive,
|
||||
this.schedules = const [],
|
||||
this.selectedDays = const [false, false, false, false, false, false, false],
|
||||
this.selectedTime,
|
||||
this.functionOn = false,
|
||||
this.isEditing = false,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status];
|
||||
List<Object?> get props => [
|
||||
status,
|
||||
scheduleMode,
|
||||
countdownHours,
|
||||
countdownMinutes,
|
||||
countdownRemaining,
|
||||
isCountdownActive,
|
||||
inchingHours,
|
||||
inchingMinutes,
|
||||
isInchingActive,
|
||||
schedules,
|
||||
selectedDays,
|
||||
selectedTime,
|
||||
functionOn,
|
||||
isEditing
|
||||
];
|
||||
|
||||
WaterHeaterDeviceStatusLoaded copyWith({
|
||||
WaterHeaterStatusModel? status,
|
||||
ScheduleModes? scheduleMode,
|
||||
int? countdownHours,
|
||||
int? countdownMinutes,
|
||||
Duration? countdownRemaining,
|
||||
bool? isCountdownActive,
|
||||
int? inchingHours,
|
||||
int? inchingMinutes,
|
||||
bool? isInchingActive,
|
||||
List<ScheduleModel>? schedules,
|
||||
List<bool>? selectedDays,
|
||||
TimeOfDay? selectedTime,
|
||||
bool? functionOn,
|
||||
bool? isEditing,
|
||||
}) {
|
||||
return WaterHeaterDeviceStatusLoaded(
|
||||
status ?? this.status,
|
||||
scheduleMode: scheduleMode ?? this.scheduleMode,
|
||||
countdownHours: countdownHours ?? this.countdownHours,
|
||||
countdownMinutes: countdownMinutes ?? this.countdownMinutes,
|
||||
countdownRemaining: countdownRemaining ?? this.countdownRemaining,
|
||||
isCountdownActive: isCountdownActive ?? this.isCountdownActive,
|
||||
inchingHours: inchingHours ?? this.inchingHours,
|
||||
inchingMinutes: inchingMinutes ?? this.inchingMinutes,
|
||||
isInchingActive: isInchingActive ?? this.isInchingActive,
|
||||
schedules: schedules ?? this.schedules,
|
||||
selectedDays: selectedDays ?? this.selectedDays,
|
||||
selectedTime: selectedTime ?? this.selectedTime,
|
||||
functionOn: functionOn ?? this.functionOn,
|
||||
isEditing: isEditing ?? this.isEditing,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class WaterHeaterFailedState extends WaterHeaterState {
|
||||
@ -39,23 +123,3 @@ final class WaterHeaterBatchFailedState extends WaterHeaterState {
|
||||
@override
|
||||
List<Object?> get props => [error];
|
||||
}
|
||||
|
||||
class WaterHeaterScheduleViewState extends WaterHeaterState {
|
||||
final ScheduleModes scheduleMode;
|
||||
final int hours;
|
||||
final int minutes;
|
||||
final bool isActive;
|
||||
final Duration? countdownRemaining;
|
||||
|
||||
const WaterHeaterScheduleViewState({
|
||||
required this.scheduleMode,
|
||||
required this.hours,
|
||||
required this.minutes,
|
||||
required this.isActive,
|
||||
this.countdownRemaining,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[scheduleMode, hours, minutes, isActive, countdownRemaining];
|
||||
}
|
||||
|
@ -0,0 +1,256 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
|
||||
class ScheduleDialogHelper {
|
||||
static void showAddScheduleDialog(BuildContext context,
|
||||
{ScheduleModel? schedule, int? index, bool? isEdit}) {
|
||||
final bloc = context.read<WaterHeaterBloc>();
|
||||
|
||||
if (schedule != null) {
|
||||
final time = _convertStringToTimeOfDay(schedule.time);
|
||||
final selectedDays = _convertDaysStringToBooleans(schedule.days);
|
||||
|
||||
bloc.add(InitializeAddScheduleEvent(
|
||||
selectedTime: time,
|
||||
selectedDays: selectedDays,
|
||||
functionOn: schedule.function.value,
|
||||
isEditing: true,
|
||||
index: index,
|
||||
));
|
||||
} else {
|
||||
bloc.add(
|
||||
const InitializeAddScheduleEvent(
|
||||
selectedDays: [false, false, false, false, false, false, false],
|
||||
functionOn: false,
|
||||
isEditing: false,
|
||||
index: null,
|
||||
selectedTime: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
return BlocProvider.value(
|
||||
value: bloc,
|
||||
child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
|
||||
builder: (context, state) {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
Text(
|
||||
'Scheduling',
|
||||
style: context.textTheme.titleLarge!.copyWith(
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
height: 40,
|
||||
child: DefaultButton(
|
||||
padding: 8,
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
borderRadius: 15,
|
||||
onPressed: isEdit == true
|
||||
? null
|
||||
: () async {
|
||||
TimeOfDay? time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime:
|
||||
state.selectedTime ?? TimeOfDay.now(),
|
||||
builder: (context, child) {
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: ColorsManager.primaryColor,
|
||||
),
|
||||
),
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
);
|
||||
if (time != null) {
|
||||
bloc.add(UpdateSelectedTimeEvent(time));
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
state.selectedTime == null
|
||||
? 'Time'
|
||||
: state.selectedTime!.format(context),
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.access_time,
|
||||
color: ColorsManager.grayColor,
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildDayCheckboxes(context, state.selectedDays,
|
||||
isEdit: isEdit),
|
||||
const SizedBox(height: 16),
|
||||
_buildFunctionSwitch(context, state.functionOn),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
if (state.selectedTime != null) {
|
||||
if (state.isEditing && index != null) {
|
||||
bloc.add(UpdateScheduleEntryEvent(
|
||||
index: index,
|
||||
deviceId: state.status.uuid,
|
||||
category: 'kg',
|
||||
functionOn: state.functionOn,
|
||||
scheduleId: state.schedules[index].scheduleId,
|
||||
));
|
||||
} else {
|
||||
bloc.add(AddScheduleEvent(
|
||||
category: 'kg',
|
||||
time: state.selectedTime!,
|
||||
selectedDays: state.selectedDays,
|
||||
functionOn: state.functionOn,
|
||||
));
|
||||
}
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static TimeOfDay _convertStringToTimeOfDay(String timeString) {
|
||||
final DateTime dateTime = DateTime.parse(timeString);
|
||||
return TimeOfDay(hour: dateTime.hour, minute: dateTime.minute);
|
||||
}
|
||||
|
||||
static List<bool> _convertDaysStringToBooleans(List<String> selectedDays) {
|
||||
final daysOfWeek = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
List<bool> daysBoolean = List.filled(7, false);
|
||||
|
||||
for (int i = 0; i < daysOfWeek.length; i++) {
|
||||
if (selectedDays.contains(daysOfWeek[i])) {
|
||||
daysBoolean[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return daysBoolean;
|
||||
}
|
||||
|
||||
static Widget _buildDayCheckboxes(
|
||||
BuildContext context, List<bool> selectedDays,
|
||||
{bool? isEdit}) {
|
||||
final dayLabels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
|
||||
return Row(
|
||||
children: List.generate(7, (index) {
|
||||
return Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: selectedDays[index],
|
||||
onChanged: isEdit == true
|
||||
? null
|
||||
: (bool? value) {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(UpdateSelectedDayEvent(index, value!));
|
||||
},
|
||||
),
|
||||
Text(dayLabels[index]),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildFunctionSwitch(BuildContext context, bool isOn) {
|
||||
return Row(
|
||||
children: [
|
||||
Text(
|
||||
'Function:',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.grayColor),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Radio<bool>(
|
||||
value: true,
|
||||
groupValue: isOn,
|
||||
onChanged: (bool? value) {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(const UpdateFunctionOnEvent(true));
|
||||
},
|
||||
),
|
||||
const Text('On'),
|
||||
const SizedBox(width: 10),
|
||||
Radio<bool>(
|
||||
value: false,
|
||||
groupValue: isOn,
|
||||
onChanged: (bool? value) {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(const UpdateFunctionOnEvent(false));
|
||||
},
|
||||
),
|
||||
const Text('Off'),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
// class ScheduleEntry {
|
||||
// final List<bool> selectedDays;
|
||||
// final TimeOfDay time;
|
||||
// final bool functionOn;
|
||||
// final String category;
|
||||
|
||||
// ScheduleEntry({
|
||||
// required this.selectedDays,
|
||||
// required this.time,
|
||||
// required this.functionOn,
|
||||
// required this.category,
|
||||
// });
|
||||
|
||||
// @override
|
||||
// String toString() =>
|
||||
// 'ScheduleEntry(selectedDays: $selectedDays, time: $time, functionOn: $functionOn)';
|
||||
// }
|
@ -0,0 +1,125 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class ScheduleModel {
|
||||
final String scheduleId;
|
||||
final String category;
|
||||
final String time;
|
||||
final Status function;
|
||||
final List<String> days;
|
||||
final TimeOfDay? timeOfDay;
|
||||
final List<bool>? selectedDays;
|
||||
|
||||
ScheduleModel({
|
||||
required this.category,
|
||||
required this.time,
|
||||
required this.function,
|
||||
required this.days,
|
||||
this.timeOfDay,
|
||||
this.selectedDays,
|
||||
this.scheduleId = '',
|
||||
});
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'category': category,
|
||||
'time': time,
|
||||
'function': function.toMap(),
|
||||
'days': days,
|
||||
};
|
||||
}
|
||||
|
||||
factory ScheduleModel.fromMap(Map<String, dynamic> map) {
|
||||
return ScheduleModel(
|
||||
scheduleId: map['scheduleId'] ?? '',
|
||||
category: map['category'] ?? '',
|
||||
time: map['time'] ?? '',
|
||||
function: Status.fromMap(map['function']),
|
||||
days: List<String>.from(map['days']),
|
||||
timeOfDay:
|
||||
parseTimeOfDay(map['time']),
|
||||
selectedDays:
|
||||
parseSelectedDays(map['days']),
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory ScheduleModel.fromJson(String source) =>
|
||||
ScheduleModel.fromMap(json.decode(source));
|
||||
|
||||
ScheduleModel copyWith({
|
||||
String? category,
|
||||
String? time,
|
||||
Status? function,
|
||||
List<String>? days,
|
||||
TimeOfDay? timeOfDay,
|
||||
List<bool>? selectedDays,
|
||||
String? scheduleId,
|
||||
}) {
|
||||
return ScheduleModel(
|
||||
category: category ?? this.category,
|
||||
time: time ?? this.time,
|
||||
function: function ?? this.function,
|
||||
days: days ?? this.days,
|
||||
timeOfDay: timeOfDay ?? this.timeOfDay,
|
||||
selectedDays: selectedDays ?? this.selectedDays,
|
||||
scheduleId: scheduleId ?? this.scheduleId,
|
||||
);
|
||||
}
|
||||
|
||||
static TimeOfDay? parseTimeOfDay(String isoTime) {
|
||||
try {
|
||||
final dateTime = DateTime.parse(isoTime);
|
||||
return TimeOfDay(hour: dateTime.hour, minute: dateTime.minute);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static List<bool> parseSelectedDays(List<String> days) {
|
||||
const allDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
return allDays.map((day) => days.contains(day)).toList();
|
||||
}
|
||||
|
||||
static List<String> convertSelectedDaysToStrings(List<bool> selectedDays) {
|
||||
const allDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
List<String> result = [];
|
||||
for (int i = 0; i < selectedDays.length; i++) {
|
||||
if (selectedDays[i]) {
|
||||
result.add(allDays[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ScheduleModel(category: $category, time: $time, function: $function, days: $days, timeOfDay: $timeOfDay, selectedDays: $selectedDays)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is ScheduleModel &&
|
||||
other.category == category &&
|
||||
other.time == time &&
|
||||
other.function == function &&
|
||||
listEquals(other.days, days) &&
|
||||
timeOfDay == other.timeOfDay &&
|
||||
listEquals(other.selectedDays, selectedDays);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return category.hashCode ^
|
||||
time.hashCode ^
|
||||
function.hashCode ^
|
||||
days.hashCode ^
|
||||
timeOfDay.hashCode ^
|
||||
selectedDays.hashCode;
|
||||
}
|
||||
}
|
@ -1,17 +1,22 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
|
||||
enum ScheduleModes { countdown, schedule, circulate, inching }
|
||||
|
||||
class WaterHeaterStatusModel {
|
||||
class WaterHeaterStatusModel extends Equatable {
|
||||
final String uuid;
|
||||
final bool heaterSwitch;
|
||||
final int countdownHours;
|
||||
final int countdownMinutes;
|
||||
final int inchingHours;
|
||||
final int inchingMinutes;
|
||||
final ScheduleModes scheduleMode;
|
||||
final String relayStatus;
|
||||
final String cycleTiming;
|
||||
final List<ScheduleModel> schedules;
|
||||
|
||||
WaterHeaterStatusModel({
|
||||
const WaterHeaterStatusModel({
|
||||
required this.uuid,
|
||||
required this.heaterSwitch,
|
||||
required this.countdownHours,
|
||||
@ -19,6 +24,9 @@ class WaterHeaterStatusModel {
|
||||
required this.relayStatus,
|
||||
required this.cycleTiming,
|
||||
required this.scheduleMode,
|
||||
required this.schedules,
|
||||
this.inchingHours = 0,
|
||||
this.inchingMinutes = 0,
|
||||
});
|
||||
|
||||
factory WaterHeaterStatusModel.fromJson(String id, List<Status> jsonList) {
|
||||
@ -49,8 +57,7 @@ class WaterHeaterStatusModel {
|
||||
}
|
||||
|
||||
final countdownHours = countdownInSeconds ~/ 3600;
|
||||
final countdownMinutes =
|
||||
(countdownInSeconds % 3600) ~/ 60;
|
||||
final countdownMinutes = (countdownInSeconds % 3600) ~/ 60;
|
||||
|
||||
return WaterHeaterStatusModel(
|
||||
uuid: id,
|
||||
@ -60,6 +67,7 @@ class WaterHeaterStatusModel {
|
||||
relayStatus: relayStatus,
|
||||
cycleTiming: cycleTiming,
|
||||
scheduleMode: scheduleMode,
|
||||
schedules: const [],
|
||||
);
|
||||
}
|
||||
|
||||
@ -71,6 +79,7 @@ class WaterHeaterStatusModel {
|
||||
String? relayStatus,
|
||||
String? cycleTiming,
|
||||
ScheduleModes? scheduleMode,
|
||||
List<ScheduleModel>? schedules,
|
||||
}) {
|
||||
return WaterHeaterStatusModel(
|
||||
uuid: uuid ?? this.uuid,
|
||||
@ -80,6 +89,7 @@ class WaterHeaterStatusModel {
|
||||
relayStatus: relayStatus ?? this.relayStatus,
|
||||
cycleTiming: cycleTiming ?? this.cycleTiming,
|
||||
scheduleMode: scheduleMode ?? this.scheduleMode,
|
||||
schedules: schedules ?? this.schedules,
|
||||
);
|
||||
}
|
||||
|
||||
@ -97,4 +107,15 @@ class WaterHeaterStatusModel {
|
||||
return ScheduleModes.countdown;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
uuid,
|
||||
heaterSwitch,
|
||||
countdownHours,
|
||||
countdownMinutes,
|
||||
scheduleMode,
|
||||
relayStatus,
|
||||
cycleTiming,
|
||||
];
|
||||
}
|
||||
|
@ -0,0 +1,87 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class WaterHEaterBatchControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
const WaterHEaterBatchControlView({super.key, required this.deviceIds});
|
||||
|
||||
final List<String> deviceIds;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => WaterHeaterBloc()
|
||||
..add(FetchWaterHeaterBatchStatusEvent(devicesUuid: deviceIds)),
|
||||
child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
|
||||
builder: (context, state) {
|
||||
if (state is WaterHeaterLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
} else if (state is WaterHeaterBatchFailedState) {
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(
|
||||
BuildContext context, WaterHeaterStatusModel status) {
|
||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
return SizedBox(
|
||||
child: GridView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: isLarge || isExtraLarge
|
||||
? 3
|
||||
: isMedium
|
||||
? 2
|
||||
: 1,
|
||||
mainAxisExtent: 140,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
children: [
|
||||
ToggleWidget(
|
||||
value: status.heaterSwitch,
|
||||
code: 'switch_1',
|
||||
deviceId: deviceIds.first,
|
||||
label: 'Water Heater',
|
||||
icon: Assets.waterHeater,
|
||||
onChange: (value) {
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
ControlWaterHeaterBatchEvent(
|
||||
devicesUuid: deviceIds,
|
||||
code: 'switch_1',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
FirmwareUpdateWidget(
|
||||
deviceId: deviceIds.first,
|
||||
version: 12,
|
||||
),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -12,9 +12,9 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class WaterHeaterDeviceControl extends StatelessWidget
|
||||
class WaterHeaterDeviceControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
const WaterHeaterDeviceControl({super.key, required this.device});
|
||||
const WaterHeaterDeviceControlView({super.key, required this.device});
|
||||
|
||||
final AllDevicesModel device;
|
||||
|
||||
@ -29,14 +29,12 @@ class WaterHeaterDeviceControl extends StatelessWidget
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
} else if (state is WaterHeaterScheduleViewState) {
|
||||
final status = context.read<WaterHeaterBloc>().deviceStatus;
|
||||
return _buildStatusControls(context, status);
|
||||
} else if (state is WaterHeaterFailedState ||
|
||||
state is WaterHeaterBatchFailedState) {
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
return const SizedBox(
|
||||
height: 200, child: Center(child: SizedBox()));
|
||||
}
|
||||
},
|
||||
));
|
||||
@ -75,7 +73,6 @@ class WaterHeaterDeviceControl extends StatelessWidget
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// context.read<WaterHeaterBloc>().add(const ShowScheduleViewEvent());
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => BlocProvider.value(
|
||||
|
@ -0,0 +1,74 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class CountdownModeButtons extends StatelessWidget {
|
||||
final bool isActive;
|
||||
final String deviceId;
|
||||
final int hours;
|
||||
final int minutes;
|
||||
|
||||
const CountdownModeButtons({
|
||||
super.key,
|
||||
required this.isActive,
|
||||
required this.deviceId,
|
||||
required this.hours,
|
||||
required this.minutes,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () => Navigator.pop(context),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
child: Text('Cancel', style: context.textTheme.bodyMedium),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: isActive
|
||||
? DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(StopScheduleEvent(deviceId));
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
ToggleWaterHeaterEvent(
|
||||
deviceId: deviceId,
|
||||
code: 'countdown_1',
|
||||
value: 0,
|
||||
),
|
||||
);
|
||||
},
|
||||
backgroundColor: Colors.red,
|
||||
child: const Text('Stop'),
|
||||
)
|
||||
: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
ToggleWaterHeaterEvent(
|
||||
deviceId: deviceId,
|
||||
code: 'countdown_1',
|
||||
value: Duration(hours: hours, minutes: minutes)
|
||||
.inSeconds,
|
||||
),
|
||||
);
|
||||
},
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class CountdownInchingView extends StatelessWidget {
|
||||
final WaterHeaterDeviceStatusLoaded state;
|
||||
|
||||
const CountdownInchingView({
|
||||
super.key,
|
||||
required this.state,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isCountDown =
|
||||
state.scheduleMode?.name == ScheduleModes.countdown.name;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
isCountDown ? 'Countdown:' : 'Inching:',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Visibility(
|
||||
visible: !isCountDown,
|
||||
child: const Text(
|
||||
'Once enabled this feature, each time the device is turned on, it will automatically turn off after a preset time.'),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_hourMinutesWheel(context, state),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Row _hourMinutesWheel(
|
||||
BuildContext context, WaterHeaterDeviceStatusLoaded state) {
|
||||
final isCountDown =
|
||||
state.scheduleMode?.name == ScheduleModes.countdown.name;
|
||||
late bool isActive;
|
||||
if (isCountDown &&
|
||||
state.countdownRemaining != null &&
|
||||
state.isCountdownActive == true) {
|
||||
isActive = true;
|
||||
} else if (!isCountDown &&
|
||||
state.countdownRemaining != null &&
|
||||
state.isInchingActive == true) {
|
||||
isActive = true;
|
||||
} else {
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
_buildPickerColumn(
|
||||
context,
|
||||
'h',
|
||||
isCountDown
|
||||
? (state.countdownHours ?? 0)
|
||||
: (state.inchingHours ?? 0),
|
||||
24, (value) {
|
||||
context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
|
||||
scheduleMode: state.scheduleMode ?? ScheduleModes.countdown,
|
||||
hours: value,
|
||||
minutes: isCountDown
|
||||
? (state.countdownMinutes ?? 0)
|
||||
: (state.inchingMinutes ?? 0),
|
||||
));
|
||||
}, isActive: isActive),
|
||||
const SizedBox(width: 10),
|
||||
_buildPickerColumn(
|
||||
context,
|
||||
'm',
|
||||
isCountDown
|
||||
? (state.countdownMinutes ?? 0)
|
||||
: (state.inchingMinutes ?? 0),
|
||||
60, (value) {
|
||||
context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
|
||||
scheduleMode: state.scheduleMode ?? ScheduleModes.countdown,
|
||||
hours: isCountDown
|
||||
? (state.countdownHours ?? 0)
|
||||
: (state.inchingHours ?? 0),
|
||||
minutes: value,
|
||||
));
|
||||
}, isActive: isActive),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPickerColumn(
|
||||
BuildContext context,
|
||||
String label,
|
||||
int initialValue,
|
||||
int itemCount,
|
||||
ValueChanged<int> onSelected, {
|
||||
required bool isActive,
|
||||
}) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
height: 40,
|
||||
width: 80,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ListWheelScrollView.useDelegate(
|
||||
key: ValueKey('$label-$initialValue'),
|
||||
controller: FixedExtentScrollController(
|
||||
initialItem: initialValue,
|
||||
),
|
||||
itemExtent: 40.0,
|
||||
physics: const FixedExtentScrollPhysics(),
|
||||
onSelectedItemChanged: onSelected,
|
||||
childDelegate: ListWheelChildBuilderDelegate(
|
||||
builder: (context, index) {
|
||||
return Center(
|
||||
child: Text(
|
||||
index.toString().padLeft(2, '0'),
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
color: isActive ? ColorsManager.grayColor : Colors.black,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: itemCount,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class InchingModeButtons extends StatelessWidget {
|
||||
final bool isActive;
|
||||
final String deviceId;
|
||||
final int hours;
|
||||
final int minutes;
|
||||
|
||||
const InchingModeButtons({
|
||||
Key? key,
|
||||
required this.isActive,
|
||||
required this.deviceId,
|
||||
required this.hours,
|
||||
required this.minutes,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () => Navigator.pop(context),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
child: Text('Cancel', style: context.textTheme.bodyMedium),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: isActive
|
||||
? DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(StopScheduleEvent(deviceId));
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
ToggleWaterHeaterEvent(
|
||||
deviceId: deviceId,
|
||||
code: 'switch_inching',
|
||||
value: 0,
|
||||
),
|
||||
);
|
||||
},
|
||||
backgroundColor: Colors.red,
|
||||
child: const Text('Stop'),
|
||||
)
|
||||
: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
ToggleWaterHeaterEvent(
|
||||
deviceId: deviceId,
|
||||
code: 'switch_inching',
|
||||
value: Duration(hours: hours, minutes: minutes)
|
||||
.inSeconds,
|
||||
),
|
||||
);
|
||||
},
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,20 +1,32 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.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/water_heater_status_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/widgets/count_down_button.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/widgets/count_down_inching_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/widgets/inching_mode_buttons.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedule_header.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedule_managment_ui.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedule_mode_buttons.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedule_mode_selector.dart';
|
||||
|
||||
class BuildScheduleView extends StatelessWidget {
|
||||
class BuildScheduleView extends StatefulWidget {
|
||||
const BuildScheduleView({super.key, required this.status});
|
||||
|
||||
final WaterHeaterStatusModel status;
|
||||
|
||||
@override
|
||||
State<BuildScheduleView> createState() => _BuildScheduleViewState();
|
||||
}
|
||||
|
||||
class _BuildScheduleViewState extends State<BuildScheduleView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
final bloc = BlocProvider.of<WaterHeaterBloc>(context);
|
||||
return BlocProvider.value(
|
||||
value: bloc,
|
||||
child: Dialog(
|
||||
backgroundColor: Colors.white,
|
||||
insetPadding: const EdgeInsets.all(20),
|
||||
shape: RoundedRectangleBorder(
|
||||
@ -24,389 +36,65 @@ class BuildScheduleView extends StatelessWidget {
|
||||
width: 700,
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20),
|
||||
child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
|
||||
builder: (context, state) {
|
||||
if (state is WaterHeaterScheduleViewState) {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
Text(
|
||||
'Scheduling',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22,
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 25,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Colors.grey,
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.all(1),
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
color: Colors.grey,
|
||||
size: 18,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const ScheduleHeader(),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'Type:',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
SizedBox(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Flexible(
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
'Countdown',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
leading: Radio<ScheduleModes>(
|
||||
value: ScheduleModes.countdown,
|
||||
groupValue: state.scheduleMode,
|
||||
onChanged: (ScheduleModes? value) {
|
||||
if (value != null) {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(UpdateScheduleEvent(
|
||||
scheduleMode: value,
|
||||
hours: state.hours,
|
||||
minutes: state.minutes,
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
'Schedule',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
leading: Radio<ScheduleModes>(
|
||||
value: ScheduleModes.schedule,
|
||||
groupValue: state.scheduleMode,
|
||||
onChanged: (ScheduleModes? value) {
|
||||
if (value != null) {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(UpdateScheduleEvent(
|
||||
scheduleMode: value,
|
||||
hours: state.hours,
|
||||
minutes: state.minutes,
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
'Circulate',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
leading: Radio<ScheduleModes>(
|
||||
value: ScheduleModes.circulate,
|
||||
groupValue: state.scheduleMode,
|
||||
onChanged: (ScheduleModes? value) {
|
||||
if (value != null) {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(UpdateScheduleEvent(
|
||||
scheduleMode: value,
|
||||
hours: state.hours,
|
||||
minutes: state.minutes,
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
'Inching',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
leading: Radio<ScheduleModes>(
|
||||
value: ScheduleModes.inching,
|
||||
groupValue: state.scheduleMode,
|
||||
onChanged: (ScheduleModes? value) {
|
||||
if (value != null) {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(UpdateScheduleEvent(
|
||||
scheduleMode: value,
|
||||
hours: state.hours,
|
||||
minutes: state.minutes,
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ScheduleModeSelector(state: state),
|
||||
const SizedBox(height: 20),
|
||||
if (state.scheduleMode == ScheduleModes.schedule)
|
||||
ScheduleManagementUI(
|
||||
state: state,
|
||||
onAddSchedule: () =>
|
||||
ScheduleDialogHelper.showAddScheduleDialog(
|
||||
context,
|
||||
schedule: null,
|
||||
index: null,
|
||||
isEdit: false
|
||||
),
|
||||
),
|
||||
if (state.scheduleMode == ScheduleModes.countdown ||
|
||||
state.scheduleMode == ScheduleModes.inching) ...[
|
||||
Text(
|
||||
'Countdown:',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
_hourMinutesWheel(state, context)
|
||||
],
|
||||
state.scheduleMode == ScheduleModes.inching)
|
||||
CountdownInchingView(state: state),
|
||||
const SizedBox(height: 20),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 400,
|
||||
height: 50,
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: (state.countdownRemaining != null &&
|
||||
state.isActive)
|
||||
? DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
late String code;
|
||||
if (state.scheduleMode ==
|
||||
ScheduleModes.countdown) {
|
||||
code = 'countdown_1';
|
||||
} else if (state.scheduleMode ==
|
||||
ScheduleModes.inching) {
|
||||
code = 'switch_inching';
|
||||
}
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(StopScheduleEvent());
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
ToggleWaterHeaterEvent(
|
||||
deviceId: status.uuid,
|
||||
code: code,
|
||||
value: 0,
|
||||
),
|
||||
);
|
||||
},
|
||||
backgroundColor: Colors.red,
|
||||
child: const Text('Stop'),
|
||||
)
|
||||
: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
late String code;
|
||||
if (state.scheduleMode ==
|
||||
ScheduleModes.countdown) {
|
||||
code = 'countdown_1';
|
||||
} else if (state.scheduleMode ==
|
||||
ScheduleModes.inching) {
|
||||
code = 'switch_inching';
|
||||
}
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
ToggleWaterHeaterEvent(
|
||||
deviceId: status.uuid,
|
||||
code: code,
|
||||
// value is time in seconds
|
||||
value: Duration(
|
||||
hours: state.hours,
|
||||
minutes:
|
||||
state.minutes)
|
||||
.inSeconds,
|
||||
),
|
||||
);
|
||||
},
|
||||
backgroundColor:
|
||||
ColorsManager.primaryColor,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (state.scheduleMode == ScheduleModes.countdown)
|
||||
CountdownModeButtons(
|
||||
isActive: state.isCountdownActive ?? false,
|
||||
deviceId: widget.status.uuid,
|
||||
hours: state.countdownHours ?? 0,
|
||||
minutes: state.countdownMinutes ?? 0,
|
||||
),
|
||||
if (state.scheduleMode == ScheduleModes.inching)
|
||||
InchingModeButtons(
|
||||
isActive: state.isInchingActive ?? false,
|
||||
deviceId: widget.status.uuid,
|
||||
hours: state.inchingHours ?? 0,
|
||||
minutes: state.inchingMinutes ?? 0,
|
||||
),
|
||||
if (state.scheduleMode != ScheduleModes.countdown &&
|
||||
state.scheduleMode != ScheduleModes.inching)
|
||||
ScheduleModeButtons(
|
||||
onSave: () {},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
if (state is WaterHeaterLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Row _hourMinutesWheel(
|
||||
WaterHeaterScheduleViewState state, BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
// Hours Picker
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
height: 50,
|
||||
width: 80,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ListWheelScrollView.useDelegate(
|
||||
controller:
|
||||
FixedExtentScrollController(initialItem: state.hours),
|
||||
itemExtent: 40.0,
|
||||
physics: FixedExtentScrollPhysics(),
|
||||
onSelectedItemChanged: (int value) {
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
UpdateScheduleEvent(
|
||||
scheduleMode: state.scheduleMode,
|
||||
hours: value,
|
||||
minutes: state.minutes,
|
||||
),
|
||||
);
|
||||
},
|
||||
childDelegate: ListWheelChildBuilderDelegate(
|
||||
builder: (context, index) {
|
||||
return Center(
|
||||
child: Text(
|
||||
index.toString().padLeft(2, '0'),
|
||||
style: const TextStyle(fontSize: 24),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'h',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
// Minutes Picker
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
height: 50,
|
||||
width: 80,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ListWheelScrollView.useDelegate(
|
||||
controller:
|
||||
FixedExtentScrollController(initialItem: state.minutes),
|
||||
itemExtent: 40.0,
|
||||
physics: FixedExtentScrollPhysics(),
|
||||
onSelectedItemChanged: (int value) {
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
UpdateScheduleEvent(
|
||||
scheduleMode: state.scheduleMode,
|
||||
hours: state.hours,
|
||||
minutes: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
childDelegate: ListWheelChildBuilderDelegate(
|
||||
builder: (context, index) {
|
||||
return Center(
|
||||
child: Text(
|
||||
index.toString().padLeft(2, '0'),
|
||||
style: const TextStyle(fontSize: 24),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: 60,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'm',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ScheduleHeader extends StatelessWidget {
|
||||
const ScheduleHeader({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
Text(
|
||||
'Scheduling',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22,
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 25,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Colors.grey,
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
child: IconButton(
|
||||
padding: const EdgeInsets.all(1),
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
color: Colors.grey,
|
||||
size: 18,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedule_table.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class ScheduleManagementUI extends StatelessWidget {
|
||||
final WaterHeaterDeviceStatusLoaded state;
|
||||
final Function onAddSchedule;
|
||||
|
||||
const ScheduleManagementUI({
|
||||
super.key,
|
||||
required this.state,
|
||||
required this.onAddSchedule,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 170,
|
||||
height: 40,
|
||||
child: DefaultButton(
|
||||
borderColor: ColorsManager.boxColor,
|
||||
padding: 2,
|
||||
backgroundColor: ColorsManager.graysColor,
|
||||
borderRadius: 15,
|
||||
onPressed: () => onAddSchedule(),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.add, color: ColorsManager.primaryColor),
|
||||
Text(
|
||||
' Add new schedule',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ScheduleTableWidget(state: state),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class ScheduleModeButtons extends StatelessWidget {
|
||||
final VoidCallback onSave;
|
||||
|
||||
const ScheduleModeButtons({
|
||||
super.key,
|
||||
required this.onSave,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: onSave,
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
||||
|
||||
class ScheduleModeSelector extends StatelessWidget {
|
||||
final WaterHeaterDeviceStatusLoaded state;
|
||||
|
||||
const ScheduleModeSelector({super.key, required this.state});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Type:',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
_buildRadioTile(
|
||||
context, 'Countdown', ScheduleModes.countdown, state),
|
||||
_buildRadioTile(context, 'Schedule', ScheduleModes.schedule, state),
|
||||
_buildRadioTile(
|
||||
context, 'Circulate', ScheduleModes.circulate, state),
|
||||
_buildRadioTile(context, 'Inching', ScheduleModes.inching, state),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRadioTile(BuildContext context, String label, ScheduleModes mode,
|
||||
WaterHeaterDeviceStatusLoaded state) {
|
||||
return Flexible(
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
label,
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
leading: Radio<ScheduleModes>(
|
||||
value: mode,
|
||||
groupValue: state.scheduleMode,
|
||||
onChanged: (ScheduleModes? value) {
|
||||
if (value != null) {
|
||||
if (value == ScheduleModes.countdown) {
|
||||
context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
|
||||
scheduleMode: value,
|
||||
hours: state.countdownHours ?? 0,
|
||||
minutes: state.countdownMinutes ?? 0,
|
||||
));
|
||||
} else if (value == ScheduleModes.inching) {
|
||||
context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
|
||||
scheduleMode: value,
|
||||
hours: state.inchingHours ?? 0,
|
||||
minutes: state.inchingMinutes ?? 0,
|
||||
));
|
||||
}
|
||||
|
||||
if (value == ScheduleModes.schedule) {
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
GetSchedulesEvent(
|
||||
category: 'kg',
|
||||
uuid: state.status.uuid,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ScheduleRowWidget extends StatelessWidget {
|
||||
final ScheduleModel schedule;
|
||||
final int index;
|
||||
final Function onEdit;
|
||||
final Function onDelete;
|
||||
|
||||
const ScheduleRowWidget({
|
||||
super.key,
|
||||
required this.schedule,
|
||||
required this.index,
|
||||
required this.onEdit,
|
||||
required this.onDelete,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Table(
|
||||
border: TableBorder.all(color: ColorsManager.graysColor),
|
||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||
children: [
|
||||
TableRow(
|
||||
children: [
|
||||
Center(
|
||||
child: schedule.function.value
|
||||
? const Icon(Icons.radio_button_checked,
|
||||
color: ColorsManager.blueColor)
|
||||
: const Icon(Icons.radio_button_unchecked),
|
||||
),
|
||||
Center(child: Text(_getSelectedDays(schedule.selectedDays ?? []))),
|
||||
Center(child: Text(formatIsoStringToTime(schedule.time))),
|
||||
Center(child: Text(schedule.function.value ? 'On' : 'Off')),
|
||||
Center(
|
||||
child: Wrap(
|
||||
runAlignment: WrapAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||
onPressed: () => onEdit(),
|
||||
child: const Text(
|
||||
'Edit',
|
||||
style: TextStyle(color: ColorsManager.blueColor),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||
onPressed: () => onDelete(),
|
||||
child: const Text(
|
||||
'Delete',
|
||||
style: TextStyle(color: ColorsManager.blueColor),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _getSelectedDays(List<bool> selectedDays) {
|
||||
final days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
List<String> selectedDaysStr = [];
|
||||
for (int i = 0; i < selectedDays.length; i++) {
|
||||
if (selectedDays[i]) {
|
||||
selectedDaysStr.add(days[i]);
|
||||
}
|
||||
}
|
||||
return selectedDaysStr.join(', ');
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||
|
||||
import '../helper/add_schedule_dialog_helper.dart';
|
||||
|
||||
class ScheduleTableWidget extends StatelessWidget {
|
||||
final WaterHeaterDeviceStatusLoaded state;
|
||||
|
||||
const ScheduleTableWidget({
|
||||
super.key,
|
||||
required this.state,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Table(
|
||||
border: TableBorder.all(
|
||||
color: ColorsManager.graysColor,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(20), topRight: Radius.circular(20)),
|
||||
),
|
||||
children: [
|
||||
TableRow(
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
children: [
|
||||
_buildTableHeader('Active'),
|
||||
_buildTableHeader('Days'),
|
||||
_buildTableHeader('Time'),
|
||||
_buildTableHeader('Function'),
|
||||
_buildTableHeader('Action'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
|
||||
builder: (context, state) {
|
||||
if (state is ScheduleLoadingState) {
|
||||
return const SizedBox(
|
||||
height: 200,
|
||||
child: Center(child: CircularProgressIndicator()));
|
||||
}
|
||||
if (state is WaterHeaterDeviceStatusLoaded &&
|
||||
state.schedules.isEmpty) {
|
||||
return _buildEmptyState(context);
|
||||
} else if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
return Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: ColorsManager.graysColor),
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(20),
|
||||
bottomRight: Radius.circular(20)),
|
||||
),
|
||||
child: _buildTableBody(state, context));
|
||||
}
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState(BuildContext context) {
|
||||
return Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: ColorsManager.graysColor),
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SvgPicture.asset(Assets.emptyRecords, width: 40, height: 40),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'No schedules added yet',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTableBody(
|
||||
WaterHeaterDeviceStatusLoaded state, BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Table(
|
||||
border: TableBorder.all(color: ColorsManager.graysColor),
|
||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||
children: [
|
||||
for (int i = 0; i < state.schedules.length; i++)
|
||||
_buildScheduleRow(state.schedules[i], i, context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTableHeader(String label) {
|
||||
return TableCell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TableRow _buildScheduleRow(
|
||||
ScheduleModel schedule, int index, BuildContext context) {
|
||||
return TableRow(
|
||||
children: [
|
||||
Center(
|
||||
child: schedule.function.value
|
||||
? const Icon(Icons.radio_button_checked,
|
||||
color: ColorsManager.blueColor)
|
||||
: const Icon(Icons.radio_button_unchecked)),
|
||||
Center(
|
||||
child: Text(_getSelectedDays(
|
||||
ScheduleModel.parseSelectedDays(schedule.days)))),
|
||||
Center(child: Text(formatIsoStringToTime(schedule.time))),
|
||||
Center(child: Text(schedule.function.value ? 'On' : 'Off')),
|
||||
Center(
|
||||
child: Wrap(
|
||||
runAlignment: WrapAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||
onPressed: () {
|
||||
ScheduleDialogHelper.showAddScheduleDialog(context,
|
||||
schedule: schedule, index: index, isEdit: true);
|
||||
},
|
||||
child: Text(
|
||||
'Edit',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.blueColor),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||
onPressed: () {
|
||||
context.read<WaterHeaterBloc>().add(DeleteScheduleEvent(
|
||||
index: index,
|
||||
scheduleId: schedule.scheduleId,
|
||||
));
|
||||
},
|
||||
child: Text(
|
||||
'Delete',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.blueColor),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _getSelectedDays(List<bool> selectedDays) {
|
||||
final days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
List<String> selectedDaysStr = [];
|
||||
for (int i = 0; i < selectedDays.length; i++) {
|
||||
if (selectedDays[i]) {
|
||||
selectedDaysStr.add(days[i]);
|
||||
}
|
||||
}
|
||||
return selectedDaysStr.join(', ');
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
@ -175,4 +177,104 @@ class DevicesManagementApi {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> addScheduleRecord(
|
||||
ScheduleModel sendSchedule, String uuid) async {
|
||||
try {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
||||
body: sendSchedule.toMap(),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return json['success'] ?? false;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
debugPrint('Error fetching $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ScheduleModel>> getDeviceSchedules(
|
||||
String uuid, String category) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.scheduleByDeviceId
|
||||
.replaceAll('{deviceUuid}', uuid)
|
||||
.replaceAll('{category}', category),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
List<ScheduleModel> schedules = [];
|
||||
for (var schedule in json['schedules']) {
|
||||
schedules.add(ScheduleModel.fromJson(schedule));
|
||||
}
|
||||
return schedules;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
debugPrint('Error fetching $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> updateScheduleRecord(
|
||||
{required bool enable,
|
||||
required String uuid,
|
||||
required String scheduleId}) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.scheduleByDeviceId
|
||||
.replaceAll('{deviceUuid}', uuid)
|
||||
.replaceAll('{scheduleUuid}', scheduleId),
|
||||
body: {
|
||||
'scheduleId': scheduleId,
|
||||
'enable': enable,
|
||||
},
|
||||
expectedResponseModel: (json) {
|
||||
return json['success'] ?? false;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
debugPrint('Error fetching $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteScheduleRecord(String uuid, String scheduleId) async {
|
||||
try {
|
||||
final response = await HTTPService().delete(
|
||||
path: ApiEndpoints.scheduleByDeviceId
|
||||
.replaceAll('{deviceUuid}', uuid)
|
||||
.replaceAll('{scheduleUuid}', scheduleId),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return json['success'] ?? false;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
debugPrint('Error fetching $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> factoryReset(FactoryResetModel factoryReset, String uuid) async {
|
||||
try {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.factoryReset.replaceAll('{deviceUuid}', uuid),
|
||||
body: factoryReset.toMap(),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return json['success'] ?? false;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
debugPrint('Error fetching $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,4 +38,14 @@ abstract class ApiEndpoints {
|
||||
static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}';
|
||||
static const String getDeviceLogsByDate =
|
||||
'/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}';
|
||||
|
||||
static const String scheduleByDeviceId = '/schedule/{deviceUuid}';
|
||||
static const String getScheduleByDeviceId =
|
||||
'/schedule/{deviceUuid}?category={category}';
|
||||
static const String deleteScheduleByDeviceId =
|
||||
'/schedule/{deviceUuid}/{scheduleUuid}';
|
||||
static const String updateScheduleByDeviceId =
|
||||
'/schedule/enable/{deviceUuid}';
|
||||
|
||||
static const String factoryReset = '/device/factory/reset/{deviceUuid}';
|
||||
}
|
||||
|
@ -154,4 +154,16 @@ class Assets {
|
||||
static const String mainDoorReports = 'assets/icons/main_door_reports.svg';
|
||||
//assets/icons/main_door.svg
|
||||
static const String mainDoor = 'assets/icons/main_door.svg';
|
||||
|
||||
//assets/icons/empty_records.svg
|
||||
static const String emptyRecords = 'assets/icons/empty_records.svg';
|
||||
|
||||
//assets/icons/open_close_door.svg
|
||||
static const String openCloseDoor = 'assets/icons/open_close_door.svg';
|
||||
|
||||
//assets/icons/open_close_records.svg
|
||||
static const String openCloseRecords = 'assets/icons/open_close_records.svg';
|
||||
|
||||
//assets/icons/water_heater.svg
|
||||
static const String waterHeater = 'assets/icons/water_heater.svg';
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
String formatDateTime(DateTime? dateTime) {
|
||||
@ -9,3 +10,22 @@ String formatDateTime(DateTime? dateTime) {
|
||||
|
||||
return '${dateFormatter.format(dateTime)} ${timeFormatter.format(dateTime)}';
|
||||
}
|
||||
|
||||
String formatTimeOfDayToISO(TimeOfDay time, {DateTime? currentDate}) {
|
||||
final now = currentDate ?? DateTime.now();
|
||||
|
||||
final dateTime = DateTime(
|
||||
now.year,
|
||||
now.month,
|
||||
now.day,
|
||||
time.hour,
|
||||
time.minute,
|
||||
);
|
||||
|
||||
return dateTime.toUtc().toIso8601String();
|
||||
}
|
||||
|
||||
String formatIsoStringToTime(String isoString) {
|
||||
final dateTime = DateTime.parse(isoString);
|
||||
return DateFormat('hh:mm a').format(dateTime);
|
||||
}
|
||||
|
@ -7,7 +7,8 @@ final myTheme = ThemeData(
|
||||
bodySmall: TextStyle(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.whiteColors,
|
||||
fontWeight: FontWeight.bold),
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
bodyMedium: TextStyle(color: Colors.black87, fontSize: 14),
|
||||
bodyLarge: TextStyle(fontSize: 16, color: Colors.white),
|
||||
headlineSmall: TextStyle(color: Colors.black87, fontSize: 18),
|
||||
|
Reference in New Issue
Block a user