Merge pull request #96 from SyncrowIOT/SP-1520-FE-Build-Motion-Status-Illuminance-Row-and-Presence-Record-Navigation

add new flush sensor device to app
This commit is contained in:
mohammadnemer1
2025-05-07 10:16:12 +03:00
committed by GitHub
21 changed files with 1155 additions and 7 deletions

View File

@ -0,0 +1,17 @@
<svg width="30" height="19" viewBox="0 0 30 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M29.9999 14.9414L9.72644 11.4258L14.9999 18.457H29.9999V14.9414Z" fill="#FFB54C"/>
<path d="M0 14.9414V18.457H15V11.4258L0 14.9414Z" fill="#FFCC4A"/>
<path d="M30.0002 14.9404V7.03027H27.7883L26.9094 7.90918L26.0305 7.03027H23.8186L22.9397 7.90918L22.0608 7.03027H19.8489L18.97 7.90918L18.0911 7.03027H15.8792L11.4846 10.9854L15.0002 14.9404H30.0002Z" fill="#FFCC4A"/>
<path d="M15 7.03027H14.1211H11.9092L11.0303 7.90918L10.1514 7.03027H7.93945L7.06055 7.90918L6.18164 7.03027H3.96973L3.09082 7.90918L2.21191 7.03027H0V14.9404H15V7.03027Z" fill="#FFE278"/>
<path d="M29.1212 1.75781H15.0001L14.1212 2.63672L15.0001 3.51562H29.1212V1.75781Z" fill="#ABD5ED"/>
<path d="M15 1.75781H1.75781V0H0V5.27344H1.75781V3.51562H15V1.75781Z" fill="#BFEBFF"/>
<path d="M28.2423 0H30.0001V5.27344H28.2423V0Z" fill="#ABD5ED"/>
<path d="M2.21191 7.03076H3.96973V11.4253H2.21191V7.03076Z" fill="#587AA1"/>
<path d="M6.18152 7.03076H7.93933V9.66748H6.18152V7.03076Z" fill="#587AA1"/>
<path d="M10.1516 7.03076H11.9094V11.4253H10.1516V7.03076Z" fill="#587AA1"/>
<path d="M15.8787 9.66699H14.9998L14.5603 9.22754V7.46973L14.9998 7.03027H15.8787V9.66699Z" fill="#455F80"/>
<path d="M14.1212 7.03027H15.0001V9.66699H14.1212V7.03027Z" fill="#587AA1"/>
<path d="M18.0907 7.03027H19.8485V11.4248H18.0907V7.03027Z" fill="#455F80"/>
<path d="M22.0603 7.03027H23.8181V9.66699H22.0603V7.03027Z" fill="#455F80"/>
<path d="M26.0304 7.03027H27.7882V11.4248H26.0304V7.03027Z" fill="#455F80"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,17 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_8194_10048)">
<path d="M11.4732 16.7316C8.31096 16.7316 5.73828 14.1589 5.73828 10.9967C5.73828 7.8344 8.31096 5.26172 11.4732 5.26172C14.6355 5.26172 17.2081 7.8344 17.2081 10.9967C17.2081 14.1589 14.6355 16.7316 11.4732 16.7316Z" fill="#EBF6FF"/>
<path d="M17.2076 10.9967C17.2076 7.8344 14.6349 5.26172 11.4727 5.26172V16.7316C14.6349 16.7316 17.2076 14.1589 17.2076 10.9967Z" fill="#D7E7F8"/>
<path d="M11.4721 17.3699C7.95827 17.3699 5.09961 14.5112 5.09961 10.9974C5.09961 7.48362 7.95827 4.625 11.472 4.625C14.9858 4.625 17.8445 7.48366 17.8445 10.9974C17.8445 14.5112 14.9858 17.3699 11.4721 17.3699ZM11.4721 5.89473C8.6584 5.89473 6.36934 8.18379 6.36934 10.9974C6.36934 13.811 8.6584 16.1001 11.472 16.1001C14.2857 16.1001 16.5748 13.8111 16.5748 10.9974C16.5748 8.18375 14.2857 5.89473 11.4721 5.89473Z" fill="#9FE066"/>
<path d="M11.4728 11.6318H8.28525C7.93464 11.6318 7.65039 11.3475 7.65039 10.9969C7.65039 10.6463 7.93464 10.3621 8.28525 10.3621H10.8379V8.95127C10.8379 8.60066 11.1222 8.31641 11.4728 8.31641C11.8234 8.31641 12.1076 8.60066 12.1076 8.95127V10.9969C12.1077 11.3475 11.8234 11.6318 11.4728 11.6318Z" fill="#394949"/>
<path d="M12.1075 10.9969V8.95127C12.1075 8.60066 11.8233 8.31641 11.4727 8.31641V11.6318C11.8233 11.6318 12.1075 11.3475 12.1075 10.9969Z" fill="#151F1F"/>
<path d="M16.5753 10.9974C16.5753 13.8111 14.2863 16.1001 11.4727 16.1001V17.3698C14.9865 17.3698 17.8451 14.5112 17.8451 10.9974C17.8451 7.48366 14.9864 4.625 11.4727 4.625V5.89473C14.2863 5.89473 16.5753 8.18379 16.5753 10.9974Z" fill="#4ACA7B"/>
<path d="M18.6832 3.78752C16.7572 1.86147 14.1963 0.800781 11.4725 0.800781C8.74869 0.800781 6.18782 1.86147 4.26177 3.78752C2.67775 5.37158 1.67966 7.38516 1.37492 9.56545L1.08378 9.2743C0.835882 9.02641 0.433887 9.02641 0.185951 9.2743C-0.0619838 9.52219 -0.0619838 9.92419 0.185951 10.1721L1.46097 11.4471C1.58494 11.5711 1.74742 11.6331 1.90986 11.6331C2.0723 11.6331 2.23482 11.5711 2.35875 11.4471L3.63377 10.1721C3.8817 9.92423 3.8817 9.52224 3.63377 9.2743C3.38587 9.02641 2.98388 9.02641 2.73594 9.2743L2.70678 9.30346C3.50083 5.18845 7.12907 2.07051 11.4725 2.07051C16.3953 2.07051 20.4003 6.07548 20.4003 10.9983C20.4003 15.921 16.3953 19.926 11.4725 19.926C7.46868 19.926 3.9294 17.234 2.86558 13.3794C2.77234 13.0413 2.42266 12.8429 2.08474 12.9363C1.74674 13.0295 1.54837 13.3792 1.64165 13.7171C2.22378 15.8264 3.50315 17.7286 5.24408 19.0732C7.04184 20.4618 9.19555 21.1957 11.4725 21.1957C14.1963 21.1957 16.7571 20.135 18.6832 18.209C20.6093 16.2829 21.67 13.7221 21.67 10.9983C21.67 8.27439 20.6093 5.71361 18.6832 3.78752Z" fill="#FF4D5B"/>
<path d="M20.4004 10.9983C20.4004 15.9211 16.3955 19.926 11.4727 19.926V21.1958C14.1965 21.1958 16.7573 20.135 18.6834 18.209C20.6095 16.2829 21.6702 13.7221 21.6702 10.9983C21.6702 8.27439 20.6094 5.71361 18.6834 3.78752C16.7573 1.86147 14.1965 0.800781 11.4727 0.800781V2.07051C16.3955 2.07051 20.4004 6.07548 20.4004 10.9983Z" fill="#DE0062"/>
</g>
<defs>
<clipPath id="clip0_8194_10048">
<rect width="21.67" height="21.67" fill="white" transform="translate(0 0.164062)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,22 @@
<svg width="38" height="40" viewBox="0 0 38 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 0C18.3528 0 17.8281 0.524531 17.8281 1.17188V5.85938C17.8281 6.50656 18.3528 7.03125 19 7.03125C19.6472 7.03125 20.1719 6.50656 20.1719 5.85938V1.17188C20.1719 0.524531 19.6472 0 19 0Z" fill="#596C76"/>
<path d="M20.1719 5.85938V1.17188C20.1719 0.524531 19.6472 0 19 0V7.03125C19.6472 7.03125 20.1719 6.50656 20.1719 5.85938Z" fill="#3B4A51"/>
<path d="M30.7188 4.6875H19H7.28125C5.34266 4.6875 3.76562 6.26453 3.76562 8.20312V13.0469C3.76562 14.9855 5.34266 16.5625 7.28125 16.5625H19H30.7188C32.6573 16.5625 34.2344 14.9855 34.2344 13.0469V8.20312C34.2344 6.26453 32.6573 4.6875 30.7188 4.6875Z" fill="#596C76"/>
<path d="M34.2344 13.0469V8.20312C34.2344 6.26453 32.6573 4.6875 30.7188 4.6875H19V16.5625H30.7188C32.6573 16.5625 34.2344 14.9855 34.2344 13.0469Z" fill="#3B4A51"/>
<path d="M28.375 0H19H9.625C8.97727 0 8.45312 0.524141 8.45312 1.17188C8.45312 1.81961 8.97727 2.34375 9.625 2.34375H19H28.375C29.0227 2.34375 29.5469 1.81961 29.5469 1.17188C29.5469 0.524141 29.0227 0 28.375 0Z" fill="#596C76"/>
<path d="M29.5469 1.17188C29.5469 0.524141 29.0227 0 28.375 0H19V2.34375H28.375C29.0227 2.34375 29.5469 1.81961 29.5469 1.17188Z" fill="#3B4A51"/>
<path d="M19 35.3125C24.5046 35.3125 29.6315 33.1096 33.4344 29.1087C33.8808 28.6395 33.8613 27.8979 33.3933 27.4516C32.9229 27.0053 32.1825 27.0247 31.7361 27.4928C28.3796 31.0244 23.8557 32.9688 19 32.9688C14.1443 32.9688 9.62037 31.0244 6.26388 27.4928C5.81873 27.0247 5.07716 27.0053 4.60677 27.4516C4.13873 27.8979 4.11927 28.6395 4.5656 29.1087C8.36841 33.1096 13.4954 35.3125 19 35.3125Z" fill="#D5E8FE"/>
<path d="M19 30.6253C23.0203 30.6253 26.8152 29.0677 29.6853 26.2399C30.1465 25.7867 30.1511 25.044 29.6967 24.5828C29.2447 24.1227 28.5031 24.1182 28.0396 24.5714C25.6112 26.9632 22.4001 28.2816 19 28.2816C15.5999 28.2816 12.3887 26.9632 9.96031 24.5714C9.49679 24.1148 8.75523 24.1217 8.3032 24.5828C7.8489 25.044 7.85343 25.7867 8.31461 26.2399C11.1848 29.0677 14.9797 30.6253 19 30.6253Z" fill="#D5E8FE"/>
<path d="M19 25.9377C21.5223 25.9377 23.9667 25.0291 25.8813 23.3788C26.3712 22.9554 26.4261 22.2161 26.0037 21.7252C25.578 21.233 24.8399 21.1816 24.3501 21.6027C22.8612 22.8868 20.9305 23.5287 19 23.5287C17.0695 23.5287 15.1387 22.8867 13.6499 21.6027C13.1578 21.1816 12.4185 21.2331 11.9962 21.7252C11.574 22.2161 11.6289 22.9554 12.1187 23.3788C14.0333 25.0291 16.4777 25.9377 19 25.9377Z" fill="#D5E8FE"/>
<path d="M16.1687 20.52C17.0316 20.9973 18.0112 21.2502 19 21.2502C19.9887 21.2502 20.9683 20.9973 21.8312 20.52C22.3977 20.2064 22.6026 19.4934 22.289 18.927C21.9754 18.3605 21.259 18.1545 20.6959 18.4692C20.1798 18.7553 19.5898 18.8984 18.9999 18.8984C18.41 18.8984 17.82 18.7553 17.3039 18.4692C16.7386 18.1545 16.0233 18.3605 15.7109 18.927C15.3974 19.4935 15.6023 20.2065 16.1687 20.52Z" fill="#D5E8FE"/>
<path d="M37.0897 30.324C36.6113 29.8903 35.8709 29.92 35.4337 30.3984C31.2177 35.0104 25.2279 37.6563 19 37.6563C12.7721 37.6563 6.78226 35.0104 2.56624 30.3985C2.12906 29.9201 1.38867 29.8904 0.910306 30.3241C0.433119 30.7613 0.399916 31.5017 0.835931 31.98C5.49593 37.0772 12.1164 40 19 40C25.8836 40 32.5041 37.0772 37.1641 31.98C37.6001 31.5017 37.5669 30.7612 37.0897 30.324Z" fill="#D5E8FE"/>
<path d="M37.0897 30.324C36.6113 29.8903 35.8709 29.92 35.4338 30.3984C31.2177 35.0104 25.2279 37.6563 19 37.6563V40C25.8836 40 32.5041 37.0772 37.1641 31.98C37.6001 31.5017 37.5669 30.7612 37.0897 30.324Z" fill="#B5DBFF"/>
<path d="M33.3932 27.4516C32.9228 27.0053 32.1824 27.0247 31.7361 27.4928C28.3796 31.0244 23.8557 32.9688 19 32.9688V35.3125C24.5046 35.3125 29.6316 33.1096 33.4345 29.1087C33.8808 28.6395 33.8613 27.8979 33.3932 27.4516Z" fill="#B5DBFF"/>
<path d="M29.6968 24.5826C29.2448 24.1225 28.5032 24.118 28.0397 24.5712C25.6113 26.963 22.4001 28.2813 19 28.2813V30.6251C23.0203 30.6251 26.8152 29.0675 29.6853 26.2397C30.1466 25.7865 30.1511 25.0438 29.6968 24.5826Z" fill="#B5DBFF"/>
<path d="M26.0038 21.7252C25.578 21.233 24.8399 21.1816 24.3501 21.6027C22.8612 22.8868 20.9305 23.5287 19 23.5287V25.9377C21.5223 25.9377 23.9667 25.0291 25.8813 23.3788C26.3712 22.9554 26.4261 22.2161 26.0038 21.7252Z" fill="#B5DBFF"/>
<path d="M22.2891 18.927C21.9755 18.3605 21.2591 18.1545 20.696 18.4692C20.1799 18.7553 19.5899 18.8984 19 18.8984V21.2501C19.9887 21.2501 20.9684 20.9972 21.8312 20.5199C22.3977 20.2064 22.6026 19.4934 22.2891 18.927Z" fill="#B5DBFF"/>
<path d="M14.3125 11.7969C14.9597 11.7969 15.4844 11.2722 15.4844 10.625C15.4844 9.97779 14.9597 9.45312 14.3125 9.45312C13.6653 9.45312 13.1406 9.97779 13.1406 10.625C13.1406 11.2722 13.6653 11.7969 14.3125 11.7969Z" fill="#EDF5FF"/>
<path d="M23.6875 11.7969C24.3347 11.7969 24.8594 11.2722 24.8594 10.625C24.8594 9.97779 24.3347 9.45312 23.6875 9.45312C23.0403 9.45312 22.5156 9.97779 22.5156 10.625C22.5156 11.2722 23.0403 11.7969 23.6875 11.7969Z" fill="#51FAFF"/>
<path d="M19 11.7969C19.6472 11.7969 20.1719 11.2722 20.1719 10.625C20.1719 9.97779 19.6472 9.45312 19 9.45312C18.3528 9.45312 17.8281 9.97779 17.8281 10.625C17.8281 11.2722 18.3528 11.7969 19 11.7969Z" fill="#EDF5FF"/>
<path d="M20.1719 10.625C20.1719 9.97766 19.6472 9.45312 19 9.45312V11.7969C19.6472 11.7969 20.1719 11.2722 20.1719 10.625Z" fill="#D5E8FE"/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -0,0 +1,13 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_8194_10065)">
<path d="M17.6069 21.8341H4.06313C1.81893 21.8341 0 20.0151 0 17.7709V4.22719C0 1.98299 1.81893 0.164062 4.06313 0.164062H17.6069C19.8511 0.164062 21.67 1.98299 21.67 4.22719V17.7709C21.67 20.0151 19.8511 21.8341 17.6069 21.8341Z" fill="#E3F8FA"/>
<path d="M15.1312 7.18375L10.955 5.60387C10.8771 5.57475 10.7925 5.57475 10.7153 5.60387L6.53904 7.18375C6.40767 7.23386 6.32031 7.35982 6.32031 7.50067V9.30605C6.32031 9.41711 6.37516 9.5214 6.46591 9.58438C6.55868 9.64736 6.67516 9.6609 6.77877 9.6223L10.8351 8.08779L14.8915 9.62298C14.9301 9.6372 14.9707 9.64465 15.0113 9.64465C15.0791 9.64465 15.1461 9.62433 15.2043 9.58438C15.2958 9.5214 15.3499 9.41711 15.3499 9.30605V7.5C15.3499 7.35914 15.2626 7.23318 15.1312 7.18375Z" fill="#8CE1EB"/>
<path d="M15.1312 13.9572L10.955 12.3773C10.8771 12.3482 10.7925 12.3482 10.7153 12.3773L6.53904 13.9572C6.40767 14.0073 6.32031 14.1333 6.32031 14.2741V16.0795C6.32031 16.1906 6.37516 16.2948 6.46591 16.3578C6.55868 16.4215 6.67516 16.435 6.77877 16.3957L10.8351 14.8612L14.8915 16.3964C14.9301 16.4106 14.9707 16.4181 15.0113 16.4181C15.0791 16.4181 15.1461 16.3978 15.2043 16.3578C15.2958 16.2948 15.3499 16.1906 15.3499 16.0795V14.2734C15.3499 14.1326 15.2626 14.0066 15.1312 13.9572Z" fill="#26C6DA"/>
<path d="M15.1312 10.5705L10.955 8.99059C10.8771 8.96147 10.7925 8.96147 10.7153 8.99059L6.53904 10.5705C6.40767 10.6206 6.32031 10.7465 6.32031 10.8874V12.6928C6.32031 12.8038 6.37516 12.9081 6.46591 12.9711C6.55868 13.0348 6.67516 13.0483 6.77877 13.009L10.8351 11.4745L14.8915 13.0097C14.9301 13.0239 14.9707 13.0314 15.0113 13.0314C15.0791 13.0314 15.1461 13.0111 15.2043 12.9711C15.2958 12.9081 15.3499 12.8038 15.3499 12.6928V10.8867C15.3499 10.7459 15.2626 10.6199 15.1312 10.5705Z" fill="#26C6DA"/>
</g>
<defs>
<clipPath id="clip0_8194_10065">
<rect width="21.67" height="21.67" fill="white" transform="translate(0 0.164062)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,21 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.7802 9.0399L20.0667 9.18046C19.7057 9.25157 19.4663 9.5894 19.5103 9.95466C19.6254 10.9106 19.5871 11.9137 19.3549 12.9433C18.5334 16.5849 15.4143 19.3394 11.6982 19.6957C6.22309 20.2206 1.65927 15.6884 2.13085 10.223C2.48684 6.09727 5.78343 2.73681 9.90242 2.3098C12.864 2.00274 15.5631 3.17762 17.3531 5.18175L18.396 4.25096C16.4588 2.08197 13.6036 0.750365 10.4397 0.87082C5.29437 1.06665 1.04625 5.19944 0.720226 10.3382C0.34608 16.2355 5.01846 21.1352 10.8352 21.1352C16.4331 21.1352 20.9712 16.5971 20.9712 10.9992C20.9712 10.3288 20.9044 9.674 20.7802 9.0399Z" fill="#FFF0D2"/>
<path d="M10.8355 12.7451C11.8006 12.7451 12.583 11.9627 12.583 10.9976C12.583 10.0324 11.8006 9.25 10.8355 9.25C9.8703 9.25 9.08789 10.0324 9.08789 10.9976C9.08789 11.9627 9.8703 12.7451 10.8355 12.7451Z" fill="#FFF0D2"/>
<path d="M10.8358 16.5911C10.6426 16.5911 10.4863 16.4346 10.4863 16.2416V10.9989C10.4863 10.9062 10.5232 10.8173 10.5887 10.7517L14.4334 6.90709C14.5699 6.77055 14.7911 6.77055 14.9276 6.90709C15.0642 7.04363 15.0642 7.26482 14.9276 7.40131L11.1854 11.1436V16.2416C11.1854 16.4347 11.029 16.5911 10.8358 16.5911Z" fill="#7A4646"/>
<path d="M10.8356 12.0463C11.4147 12.0463 11.8842 11.5769 11.8842 10.9978C11.8842 10.4187 11.4147 9.94922 10.8356 9.94922C10.2566 9.94922 9.78711 10.4187 9.78711 10.9978C9.78711 11.5769 10.2566 12.0463 10.8356 12.0463Z" fill="#AA6E4D"/>
<path d="M10.8358 4.35899C10.6426 4.35899 10.4863 4.20248 10.4863 4.00948V3.31045C10.4863 3.11745 10.6427 2.96094 10.8358 2.96094C11.0291 2.96094 11.1854 3.11745 11.1854 3.31045V4.00948C11.1854 4.20248 11.029 4.35899 10.8358 4.35899Z" fill="#DE966C"/>
<path d="M10.8358 19.0387C10.6426 19.0387 10.4863 18.8822 10.4863 18.6892V17.9901C10.4863 17.7971 10.6427 17.6406 10.8358 17.6406C11.0291 17.6406 11.1854 17.7971 11.1854 17.9901V18.6892C11.1854 18.8822 11.029 19.0387 10.8358 19.0387Z" fill="#DE966C"/>
<path d="M18.5251 11.3475H17.8261C17.6329 11.3475 17.4766 11.1909 17.4766 10.998C17.4766 10.805 17.6329 10.6484 17.8261 10.6484H18.5251C18.7183 10.6484 18.8746 10.805 18.8746 10.998C18.8746 11.1909 18.7183 11.3475 18.5251 11.3475Z" fill="#DE966C"/>
<path d="M3.84546 11.3475H3.14639C2.95318 11.3475 2.79688 11.1909 2.79688 10.998C2.79688 10.805 2.95322 10.6484 3.14639 10.6484H3.84541C4.03863 10.6484 4.19493 10.805 4.19493 10.998C4.19493 11.1909 4.03863 11.3475 3.84546 11.3475Z" fill="#DE966C"/>
<path d="M4.78099 7.85338C4.72161 7.85338 4.66151 7.83836 4.60657 7.80661L4.00108 7.4571C3.83382 7.36051 3.77651 7.14682 3.87309 6.9796C3.96968 6.81233 4.18371 6.75502 4.35059 6.85161L4.95608 7.20112C5.12335 7.29771 5.18066 7.5114 5.08407 7.67862C5.01923 7.79074 4.90178 7.85338 4.78099 7.85338Z" fill="#F7B97E"/>
<path d="M17.4939 15.1933C17.4345 15.1933 17.3744 15.1782 17.3195 15.1465L16.714 14.797C16.5467 14.7004 16.4894 14.4867 16.586 14.3195C16.6826 14.152 16.8966 14.0949 17.0635 14.1915L17.669 14.541C17.8362 14.6376 17.8935 14.8513 17.797 15.0185C17.7321 15.1306 17.6147 15.1933 17.4939 15.1933Z" fill="#F7B97E"/>
<path d="M14.3298 5.29299C14.2704 5.29299 14.2103 5.27796 14.1554 5.24622C13.9881 5.14964 13.9308 4.93594 14.0274 4.76872L14.3769 4.16323C14.4735 3.99579 14.6875 3.93866 14.8544 4.03524C15.0217 4.13182 15.079 4.34552 14.9824 4.51274L14.6329 5.11823C14.568 5.23035 14.4506 5.29299 14.3298 5.29299Z" fill="#F7B97E"/>
<path d="M6.98995 18.0077C6.93057 18.0077 6.87047 17.9927 6.81553 17.961C6.64827 17.8644 6.59096 17.6507 6.68755 17.4834L7.03706 16.878C7.13331 16.7107 7.34768 16.6536 7.51456 16.75C7.68183 16.8466 7.73913 17.0602 7.64255 17.2275L7.29304 17.833C7.2282 17.9451 7.11079 18.0077 6.98995 18.0077Z" fill="#F7B97E"/>
<path d="M16.8891 7.85337C16.7682 7.85337 16.6508 7.79073 16.586 7.67861C16.4894 7.51135 16.5467 7.2977 16.714 7.20111L17.3195 6.8516C17.486 6.75502 17.7004 6.81236 17.797 6.97959C17.8935 7.14685 17.8362 7.36051 17.669 7.45709L17.0635 7.8066C17.0085 7.83835 16.9485 7.85337 16.8891 7.85337Z" fill="#F7B97E"/>
<path d="M4.17617 15.1933C4.05533 15.1933 3.93792 15.1306 3.87308 15.0185C3.7765 14.8512 3.83385 14.6376 4.00107 14.541L4.60656 14.1915C4.77277 14.0949 4.98714 14.1521 5.08406 14.3195C5.18065 14.4867 5.1233 14.7004 4.95608 14.797L4.35059 15.1465C4.29565 15.1782 4.23555 15.1933 4.17617 15.1933Z" fill="#F7B97E"/>
<path d="M7.34013 5.29298C7.2193 5.29298 7.10189 5.23034 7.03705 5.11822L6.68754 4.51273C6.59095 4.34547 6.6483 4.13181 6.81553 4.03523C6.98207 3.93865 7.1961 3.99583 7.29303 4.16322L7.64254 4.76871C7.73912 4.93597 7.68178 5.14963 7.51455 5.24621C7.45962 5.27795 7.39956 5.29298 7.34013 5.29298Z" fill="#F7B97E"/>
<path d="M14.68 18.0077C14.5591 18.0077 14.4417 17.9451 14.3769 17.833L14.0274 17.2275C13.9308 17.0602 13.9881 16.8465 14.1554 16.75C14.3219 16.6535 14.5363 16.7107 14.6329 16.8779L14.9824 17.4834C15.079 17.6507 15.0216 17.8644 14.8544 17.9609C14.7994 17.9927 14.7394 18.0077 14.68 18.0077Z" fill="#F7B97E"/>
<path d="M10.8358 11.3475C11.0289 11.3475 11.1854 11.191 11.1854 10.998C11.1854 10.8049 11.0289 10.6484 10.8358 10.6484C10.6428 10.6484 10.4863 10.8049 10.4863 10.998C10.4863 11.191 10.6428 11.3475 10.8358 11.3475Z" fill="#7A4646"/>
<path d="M10.8356 1.56212C5.42443 1.56212 1.07697 6.11655 1.41743 11.6012C1.70913 16.3007 5.53397 20.1256 10.2335 20.4173C15.7181 20.7578 20.2726 16.4103 20.2726 10.9991C20.2726 10.2541 20.1854 9.5297 20.0217 8.83474C19.9829 8.66988 20.0623 8.5019 20.2172 8.43346C20.4105 8.34805 20.6773 8.24402 20.8922 8.16267C21.0866 8.08907 21.3059 8.19949 21.3557 8.40125C21.6326 9.52411 21.7356 10.7154 21.6299 11.9463C21.4062 14.551 20.2433 16.8986 18.4888 18.652C18.4888 18.652 14.9619 21.4157 11.4687 21.4157C4.94621 21.4157 0.410156 16.7271 0.410156 10.1916C0.410156 7.40625 3.18409 3.34816 3.18409 3.34816C5.04237 1.49153 7.56422 0.300058 10.3375 0.175287C13.4409 0.035659 16.2657 1.20796 18.3212 3.17781L18.9202 2.71389C19.1388 2.5446 19.4582 2.68418 19.4824 2.95967L19.7199 5.66482C19.7422 5.91826 19.4949 6.10982 19.2551 6.02483L16.6954 5.11791C16.4348 5.02556 16.3795 4.68142 16.5982 4.51212L17.2071 4.04055C15.5283 2.50215 13.2923 1.56212 10.8356 1.56212Z" fill="#FB5F7A"/>
<path d="M18.485 18.6485C18.2212 18.3848 17.7996 18.3723 17.5193 18.6183C15.5456 20.3511 12.893 21.3292 10.0123 21.102C5.09917 20.7145 1.11879 16.7339 0.731479 11.8208C0.50441 8.94025 1.48256 6.28788 3.21519 4.3143C3.46126 4.03403 3.44709 3.61138 3.18336 3.34766C1.37642 5.15274 0.197009 7.58634 0.0232257 10.2799C-0.397562 16.8019 4.95607 22.1858 11.468 21.8155C14.1882 21.6609 16.6595 20.4788 18.4881 18.6515L18.485 18.6485Z" fill="#F74455"/>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -0,0 +1,23 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_8194_10077)">
<path d="M17.6 18.673C17.3521 18.4251 17.3521 18.0231 17.6 17.7752C19.4054 15.9698 20.3996 13.5678 20.3996 11.0115C20.3996 8.45525 19.4054 6.05318 17.6 4.24784C17.3521 3.99994 17.3521 3.59795 17.6 3.35001C17.8479 3.10208 18.2499 3.10208 18.4978 3.35001C20.543 5.39516 21.6694 8.11606 21.6694 11.0115C21.6694 13.9069 20.543 16.6278 18.4978 18.673C18.2503 18.9205 17.8484 18.9213 17.6 18.673Z" fill="#7AFFE4"/>
<path d="M15.6781 16.7502C15.4302 16.5023 15.4302 16.1003 15.6781 15.8524C16.9704 14.5602 17.6821 12.8408 17.6821 11.011C17.6821 9.18128 16.9705 7.46194 15.6781 6.1697C15.4302 5.92176 15.4302 5.51981 15.6781 5.27187C15.926 5.02398 16.328 5.02394 16.576 5.27187C18.1081 6.80393 18.9519 8.84213 18.9519 11.0111C18.9519 13.18 18.1081 15.2182 16.576 16.7503C16.3279 16.9982 15.926 16.9981 15.6781 16.7502Z" fill="#00DDC1"/>
<path d="M13.7543 14.8285C13.5064 14.5805 13.5064 14.1785 13.7543 13.9306C14.5331 13.152 14.9619 12.1158 14.9619 11.0131C14.9619 9.91034 14.5331 8.87416 13.7543 8.09548C13.5064 7.84758 13.5064 7.44559 13.7543 7.19765C14.0022 6.94976 14.4042 6.94972 14.6521 7.19765C15.6707 8.21619 16.2317 9.5712 16.2317 11.0131C16.2317 12.455 15.6707 13.81 14.6521 14.8285C14.4042 15.0764 14.0022 15.0763 13.7543 14.8285Z" fill="#7AFFE4"/>
<path d="M17.6 18.6732C17.8484 18.9216 18.2503 18.9207 18.4978 18.6732C20.543 16.6281 21.6694 13.9072 21.6694 11.0117H20.3996C20.3996 13.568 19.4054 15.97 17.6 17.7754C17.3521 18.0233 17.3521 18.4253 17.6 18.6732Z" fill="#00DDC1"/>
<path d="M15.6781 16.7509C15.926 16.9988 16.3279 16.9989 16.5759 16.7509C18.1081 15.2189 18.9518 13.1807 18.9518 11.0117H17.6821C17.6821 12.8415 16.9704 14.5608 15.6781 15.8531C15.4302 16.101 15.4302 16.503 15.6781 16.7509Z" fill="#00B4BC"/>
<path d="M13.7543 14.8272C14.0022 15.0751 14.4041 15.0751 14.6521 14.8272C15.6707 13.8086 16.2316 12.4536 16.2316 11.0117H14.9619C14.9619 12.1144 14.533 13.1506 13.7543 13.9293C13.5064 14.1772 13.5064 14.5792 13.7543 14.8272Z" fill="#00DDC1"/>
<path d="M3.17152 18.673C1.12633 16.6278 0 13.9069 0 11.0115C0 8.11604 1.12633 5.39515 3.17152 3.35C3.41946 3.10206 3.82141 3.10211 4.06935 3.35C4.31728 3.59793 4.31728 3.99989 4.06935 4.24782C2.26396 6.05316 1.26973 8.45523 1.26973 11.0115C1.26973 13.5677 2.26396 15.9698 4.06935 17.7751C4.31728 18.023 4.31728 18.425 4.06935 18.673C3.82141 18.921 3.41937 18.9209 3.17152 18.673Z" fill="#7AFFE4"/>
<path d="M5.09462 16.7503C3.56253 15.2182 2.71875 13.18 2.71875 11.0111C2.71875 8.84212 3.56253 6.80391 5.09466 5.27186C5.3426 5.02396 5.74459 5.02396 5.99249 5.27186C6.24038 5.51979 6.24038 5.92175 5.99249 6.16968C4.70016 7.46197 3.98848 9.1813 3.98848 11.0111C3.98848 12.8408 4.70016 14.5602 5.99249 15.8524C6.24042 16.1003 6.24042 16.5023 5.99249 16.7502C5.74489 16.9978 5.34294 16.9985 5.09462 16.7503Z" fill="#00DDC1"/>
<path d="M7.01704 14.8285C5.99847 13.81 5.4375 12.455 5.4375 11.0131C5.4375 9.5712 5.99847 8.21619 7.01704 7.19765C7.26498 6.94972 7.66697 6.94976 7.91486 7.19765C8.1628 7.44559 8.16276 7.84758 7.91486 8.09548C7.1361 8.87416 6.70723 9.91034 6.70723 11.0131C6.70723 12.1158 7.1361 13.152 7.91486 13.9306C8.1628 14.1785 8.1628 14.5805 7.91486 14.8285C7.66727 15.0761 7.26531 15.0768 7.01704 14.8285Z" fill="#7AFFE4"/>
<path d="M4.06935 18.6732C4.31728 18.4253 4.31728 18.0233 4.06935 17.7754C2.26396 15.97 1.26973 13.568 1.26973 11.0117H0C0 13.9072 1.12633 16.6281 3.17152 18.6732C3.41937 18.9211 3.82141 18.9212 4.06935 18.6732Z" fill="#00DDC1"/>
<path d="M5.99249 16.7509C6.24038 16.5029 6.24038 16.101 5.99249 15.8531C4.70016 14.5609 3.98848 12.8415 3.98848 11.0117H2.71875C2.71875 13.1807 3.56253 15.2189 5.09466 16.7509C5.34294 16.9992 5.74489 16.9985 5.99249 16.7509Z" fill="#00B4BC"/>
<path d="M7.91486 14.8271C8.1628 14.5792 8.16276 14.1772 7.91486 13.9293C7.1361 13.1506 6.70723 12.1144 6.70723 11.0117H5.4375C5.4375 12.4536 5.99846 13.8086 7.01704 14.8272C7.26527 15.0754 7.66722 15.0747 7.91486 14.8271Z" fill="#00DDC1"/>
<path d="M10.835 13.6857C12.3123 13.6857 13.5099 12.4881 13.5099 11.0108C13.5099 9.53353 12.3123 8.33594 10.835 8.33594C9.35775 8.33594 8.16016 9.53353 8.16016 11.0108C8.16016 12.4881 9.35775 13.6857 10.835 13.6857Z" fill="#00DDC1"/>
<path d="M10.835 13.6866C12.31 13.6866 13.5099 12.4866 13.5099 11.0117H8.16016C8.16016 12.4867 9.36009 13.6866 10.835 13.6866Z" fill="#00B4BC"/>
</g>
<defs>
<clipPath id="clip0_8194_10077">
<rect width="21.67" height="21.67" fill="white" transform="translate(0 0.164062)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,132 @@
import 'dart:async';
import 'dart:developer';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
class FlushSensorBloc extends Bloc<FlushSensorEvent, FlushSensorState> {
final String deviceId;
late DeviceModel deviceModel;
late FlushSensorModel deviceStatus;
FlushSensorBloc({required this.deviceId}) : super(FlushSensorInitialState()) {
on<FlushSensorInitialEvent>(_fetchFlushSensorStatus);
on<FlushSensorChangeIndicatorEvent>(_changeIndicator);
on<FlushSensorChangeValueEvent>(_changeValue);
on<FlushSensorUpdatedEvent>(_flushSensorUpdated);
on<FlushSensorGetDeviceReportsEvent>(_getDeviceReports);
}
void _fetchFlushSensorStatus(
FlushSensorInitialEvent event, Emitter<FlushSensorState> emit) async {
emit(FlushSensorLoadingInitialState());
try {
var response = await DevicesAPI.getDeviceStatus(deviceId);
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
}
deviceStatus = FlushSensorModel.fromJson(statusModelList);
emit(FlushSensorUpdateState(flushSensorModel: deviceStatus));
_listenToChanges();
} catch (e) {
emit(FlushSensorFailedState(error: e.toString()));
return;
}
}
StreamSubscription<DatabaseEvent>? _streamSubscription;
void _listenToChanges() {
try {
_streamSubscription?.cancel();
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
_streamSubscription = stream.listen((DatabaseEvent event) async {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(StatusModel(code: element['code'], value: element['value']));
});
deviceStatus = FlushSensorModel.fromJson(statusList);
if (!isClosed) {
add(FlushSensorUpdatedEvent());
}
});
} catch (_) {
log(
'Error listening to changes',
name: 'FlushMountedPresenceSensorBloc._listenToChanges',
);
}
}
@override
Future<void> close() async {
_streamSubscription?.cancel();
_streamSubscription = null;
return super.close();
}
_flushSensorUpdated(
FlushSensorUpdatedEvent event, Emitter<FlushSensorState> emit) {
emit(FlushSensorUpdateState(flushSensorModel: deviceStatus));
}
void _changeIndicator(FlushSensorChangeIndicatorEvent event,
Emitter<FlushSensorState> emit) async {
emit(FlushSensorLoadingNewSate(flushSensorModel: deviceStatus));
try {
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: deviceId, code: 'indicator', value: !event.value),
deviceId);
} catch (e) {
emit(FlushSensorFailedState(error: e.toString()));
return;
}
emit(FlushSensorUpdateState(flushSensorModel: deviceStatus));
}
void _changeValue(
FlushSensorChangeValueEvent event, Emitter<FlushSensorState> emit) async {
emit(FlushSensorLoadingNewSate(flushSensorModel: deviceStatus));
try {
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: deviceId, code: event.code, value: event.value),
deviceId);
} catch (e) {
emit(FlushSensorFailedState(error: e.toString()));
return;
}
emit(FlushSensorUpdateState(flushSensorModel: deviceStatus));
}
void _getDeviceReports(FlushSensorGetDeviceReportsEvent event,
Emitter<FlushSensorState> emit) async {
emit(FlushSensorLoadingInitialState());
try {
await DevicesAPI.getDeviceReports(deviceId, event.code).then((value) {
emit(FlushSensorDeviceReportsState(
deviceReport: value, code: event.code));
});
} catch (e) {
emit(FlushSensorFailedState(error: e.toString()));
return;
}
}
}

View File

@ -0,0 +1,45 @@
import 'package:equatable/equatable.dart';
abstract class FlushSensorEvent extends Equatable {
const FlushSensorEvent();
@override
List<Object> get props => [];
}
class FlushSensorLoadingEvent extends FlushSensorEvent {}
class FlushSensorInitialEvent extends FlushSensorEvent {}
class FlushSensorInitialDeviseInfo extends FlushSensorEvent {}
class FlushSensorUpdatedEvent extends FlushSensorEvent {}
class FlushSensorChangeIndicatorEvent extends FlushSensorEvent {
final bool value;
const FlushSensorChangeIndicatorEvent({required this.value});
@override
List<Object> get props => [value];
}
class FlushSensorChangeValueEvent extends FlushSensorEvent {
final dynamic value;
final String code;
const FlushSensorChangeValueEvent({required this.value, required this.code});
@override
List<Object> get props => [value, code];
}
class FlushSensorGetDeviceReportsEvent extends FlushSensorEvent {
final String deviceUuid;
final String code;
const FlushSensorGetDeviceReportsEvent({
required this.deviceUuid,
required this.code,
});
@override
List<Object> get props => [deviceUuid, code];
}

View File

@ -0,0 +1,52 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart';
class FlushSensorState extends Equatable {
const FlushSensorState();
@override
List<Object> get props => [];
}
class FlushSensorInitialState extends FlushSensorState {}
class FlushSensorLoadingInitialState extends FlushSensorState {}
class FlushSensorUpdateState extends FlushSensorState {
final FlushSensorModel flushSensorModel;
const FlushSensorUpdateState({required this.flushSensorModel});
@override
List<Object> get props => [flushSensorModel];
}
class FlushSensorLoadingNewSate extends FlushSensorState {
final FlushSensorModel flushSensorModel;
const FlushSensorLoadingNewSate({required this.flushSensorModel});
@override
List<Object> get props => [flushSensorModel];
}
class FlushSensorFailedState extends FlushSensorState {
final String error;
const FlushSensorFailedState({required this.error});
@override
List<Object> get props => [error];
}
class FlushSensorDeviceReportsState extends FlushSensorState {
final DeviceReport deviceReport;
final String code;
const FlushSensorDeviceReportsState(
{required this.deviceReport, required this.code});
}
class FlushSensorLoadingDeviceInfo extends FlushSensorState {
final DeviceInfoModel? deviceInfo;
const FlushSensorLoadingDeviceInfo({this.deviceInfo});
}

View File

@ -92,6 +92,8 @@ class DeviceModel {
tempIcon = Assets.sixSceneHomeIcon;
} else if (type == DeviceType.SOS) {
tempIcon = Assets.sosHomeIcon;
} else if (type == DeviceType.FlushMountedSensor) {
tempIcon = Assets.flushIcon;
} else {
tempIcon = Assets.assetsIconsLogo;
}

View File

@ -0,0 +1,100 @@
import 'package:syncrow_app/features/devices/model/status_model.dart';
class FlushSensorModel {
FlushSensorModel({
required this.presenceState,
required this.farDetection,
required this.illuminance,
required this.sensitivity,
required this.occurDistReduce,
required this.noneDelay,
required this.presenceDelay,
required this.nearDetection,
required this.sensiReduce,
required this.checkingResult,
});
static const String codePresenceState = 'presence_state';
static const String codeSensitivity = 'sensitivity';
static const String codeNearDetection = 'near_detection';
static const String codeFarDetection = 'far_detection';
static const String codeCheckingResult = 'checking_result';
static const String codePresenceDelay = 'presence_delay';
static const String codeNoneDelay = 'none_delay';
static const String codeOccurDistReduce = 'occur_dist_reduce';
static const String codeIlluminance = 'illum_value';
static const String codeSensiReduce = 'sensi_reduce';
String presenceState;
int sensitivity;
int nearDetection;
int farDetection;
String checkingResult;
int presenceDelay;
int noneDelay;
int occurDistReduce;
int illuminance;
int sensiReduce;
factory FlushSensorModel.fromJson(List<StatusModel> jsonList) {
String presenceState = 'none';
int sensitivity = 0;
int nearDetection = 0;
int farDetection = 0;
String checkingResult = 'none';
int presenceDelay = 0;
int noneDelay = 0;
int occurDistReduce = 0;
int illuminance = 0;
int sensiReduce = 0;
for (var status in jsonList) {
switch (status.code) {
case codePresenceState:
presenceState = status.value ?? 'presence';
break;
case codeSensitivity:
sensitivity = status.value ?? 0;
break;
case codeNearDetection:
nearDetection = status.value ?? 0;
break;
case codeFarDetection:
farDetection = status.value ?? 0;
break;
case codeCheckingResult:
checkingResult = status.value ?? 'check_success';
break;
case codePresenceDelay:
presenceDelay = status.value ?? 0;
break;
case codeNoneDelay:
noneDelay = status.value ?? 0;
break;
case codeOccurDistReduce:
occurDistReduce = status.value ?? 0;
break;
case codeIlluminance:
illuminance = status.value ?? 0;
break;
case codeSensiReduce:
sensiReduce = status.value ?? 0;
break;
}
}
return FlushSensorModel(
presenceState: presenceState,
sensitivity: sensitivity,
nearDetection: nearDetection,
farDetection: farDetection,
checkingResult: checkingResult,
presenceDelay: presenceDelay,
noneDelay: noneDelay,
occurDistReduce: occurDistReduce,
illuminance: illuminance,
sensiReduce: sensiReduce,
);
}
}

View File

@ -0,0 +1,135 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart';
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class FlushPresenceRecords extends StatelessWidget {
final String deviceId;
final String code;
final String title;
const FlushPresenceRecords(
{super.key,
required this.deviceId,
required this.code,
required this.title});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: title,
child: BlocProvider(
create: (context) => FlushSensorBloc(deviceId: deviceId)
..add(FlushSensorGetDeviceReportsEvent(
deviceUuid: deviceId, code: code)),
child: BlocBuilder<FlushSensorBloc, FlushSensorState>(
builder: (context, state) {
final Map<String, List<DeviceEvent>> groupedRecords = {};
if (state is FlushSensorLoadingInitialState) {
return const Center(
child: DefaultContainer(
width: 50, height: 50, child: CircularProgressIndicator()),
);
} else if (state is FlushSensorDeviceReportsState) {
for (var record in state.deviceReport.data ?? []) {
final DateTime eventDateTime =
DateTime.fromMillisecondsSinceEpoch(record.eventTime!);
final String formattedDate =
DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime);
// Group by formatted date
if (groupedRecords.containsKey(formattedDate)) {
groupedRecords[formattedDate]!.add(record);
} else {
groupedRecords[formattedDate] = [record];
}
}
}
return groupedRecords.isEmpty
? const Center(
child: Text('No records found'),
)
: ListView.builder(
itemCount: groupedRecords.length,
itemBuilder: (context, index) {
final String date = groupedRecords.keys.elementAt(index);
final List<DeviceEvent> recordsForDate =
groupedRecords[date]!;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 5, top: 10),
child: Text(
date,
style: const TextStyle(
color: ColorsManager.grayColor,
fontSize: 13,
fontWeight: FontWeight.w700,
),
),
),
DefaultContainer(
child: Column(
children: [
...recordsForDate.asMap().entries.map((entry) {
final int idx = entry.key;
final DeviceEvent record = entry.value;
final DateTime eventDateTime =
DateTime.fromMillisecondsSinceEpoch(
record.eventTime!);
final String formattedTime =
DateFormat('HH:mm:ss')
.format(eventDateTime);
return Column(
children: [
SizedBox(
child: ListTile(
leading: Icon(
record.value == 'true'
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
color: record.value == 'true'
? Colors.blue
: Colors.grey,
),
title: Text(
record.value == 'true'
? "Opened"
: "Closed",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
subtitle: Text('$formattedTime'),
),
),
if (idx != recordsForDate.length - 1)
const Divider(
color: ColorsManager.graysColor,
),
],
);
}).toList(),
],
),
),
],
);
},
);
}),
),
);
}
}

View File

@ -0,0 +1,110 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart';
import 'package:syncrow_app/features/devices/view/device_settings/settings_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart';
import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_persence_records.dart';
import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_sensor_option.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/helpers/misc_string_helpers.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
part "presence_indicator.dart";
part "flush_sensor_options_list.dart";
part 'flush_sensor_parameter_control_dialog.dart';
class FlushMountedInterface extends StatelessWidget {
const FlushMountedInterface({super.key, required this.deviceModel});
final DeviceModel deviceModel;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => FlushSensorBloc(deviceId: deviceModel.uuid ?? '')
..add(FlushSensorInitialEvent()),
child: BlocBuilder<FlushSensorBloc, FlushSensorState>(
builder: (context, state) {
final bloc = BlocProvider.of<FlushSensorBloc>(context);
FlushSensorModel flushSensorModel = FlushSensorModel(
presenceState: 'none',
farDetection: 0,
illuminance: 0,
checkingResult: '',
nearDetection: 0,
noneDelay: 0,
occurDistReduce: 0,
presenceDelay: 0,
sensiReduce: 0,
sensitivity: 0);
if (state is FlushSensorUpdateState) {
flushSensorModel = state.flushSensorModel;
} else if (state is FlushSensorLoadingNewSate) {
flushSensorModel = state.flushSensorModel;
}
return AnnotatedRegion(
value: SystemUiOverlayStyle(
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
statusBarIconBrightness: Brightness.light,
),
child: DefaultScaffold(
title: deviceModel.name!,
child: Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
Assets.assetsImagesBackground,
),
fit: BoxFit.cover,
opacity: 0.4,
),
),
child: state is FlushSensorLoadingInitialState
? const Center(
child: RefreshProgressIndicator(),
)
: SafeArea(
child: RefreshIndicator(
onRefresh: () async {
bloc.add(FlushSensorInitialEvent());
},
child: ListView(children: [
SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(children: [
PresenceIndicator(
state: flushSensorModel.presenceState,
),
Expanded(
flex: 2,
child: FlushSensorOptionsList(
bloc: bloc,
flushSensorModel: flushSensorModel,
deviceModel: deviceModel,
),
)
]),
),
]),
),
))));
}),
);
}
}

View File

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class FlushSensorOption extends StatelessWidget {
final String icon;
final String label;
final VoidCallback onTap;
final String? sliderValue;
final bool withArrow;
const FlushSensorOption({
super.key,
required this.icon,
required this.label,
required this.onTap,
required this.withArrow,
this.sliderValue = '',
});
@override
Widget build(BuildContext context) {
return DefaultContainer(
margin: const EdgeInsets.only(bottom: 5),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20),
onTap: onTap,
child: Row(
children: [
SvgPicture.asset(icon),
const SizedBox(width: 25),
Expanded(
child:
Text(label, style: Theme.of(context).textTheme.bodyMedium)),
Text(sliderValue!,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayTextColor,
fontWeight: FontWeight.bold)),
withArrow == true
? const Icon(Icons.arrow_forward_ios,
color: ColorsManager.grayTextColor, size: 15)
: SizedBox(),
],
),
);
}
}

View File

@ -0,0 +1,193 @@
part of "flush_sensor_interface.dart";
class FlushSensorOptionsList extends StatelessWidget {
final FlushSensorModel flushSensorModel;
final DeviceModel deviceModel;
final FlushSensorBloc bloc;
const FlushSensorOptionsList(
{super.key,
required this.flushSensorModel,
required this.bloc,
required this.deviceModel});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FlushSensorOption(
withArrow: false,
sliderValue: '${flushSensorModel.illuminance.toString()} Lux',
icon: Assets.assetsIconsPresenceSensorAssetsIlluminanceValue,
label: 'Illuminance Value',
onTap: () {},
),
FlushSensorOption(
withArrow: false,
icon: Assets.assetsIconsPresenceSensorAssetsIlluminanceRecord,
label: 'Presence Record',
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => FlushPresenceRecords(
deviceId: deviceModel.uuid!,
code: FlushSensorModel.codePresenceState,
title: 'Illuminance Record'),
));
}),
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Text(
'Configuration',
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(fontWeight: FontWeight.bold),
),
),
FlushSensorOption(
withArrow: true,
sliderValue: flushSensorModel.sensitivity.toString(),
icon: Assets.assetsSensitivityFunction,
label: 'Sensitivity',
onTap: () => _showParameterDialog(
min: 0,
max: 9,
value: flushSensorModel.sensitivity.toDouble(),
context: context,
title: 'Sensitivity',
controlCode: FlushSensorModel.codeSensitivity,
step: 1.0,
),
),
FlushSensorOption(
withArrow: true,
sliderValue: "${flushSensorModel.nearDetection / 100} m",
icon: Assets.detectionDistanceIcon,
label: 'Min Detection Distance',
onTap: () => _showParameterDialog(
min: 0.0,
max: 9.5,
value: flushSensorModel.nearDetection.toDouble() / 100,
context: context,
title: 'Min Detection Distance',
controlCode: FlushSensorModel.codeNearDetection,
step: 0.1,
description: 'm',
),
),
FlushSensorOption(
withArrow: true,
sliderValue: '${flushSensorModel.farDetection / 100} m',
icon: Assets.detectionDistanceIcon,
label: 'Max Detection Distance',
onTap: () => _showParameterDialog(
description: 'm',
min: 0.0,
max: 9.5,
value: flushSensorModel.farDetection / 100,
context: context,
title: 'Max Detection Distance',
controlCode: FlushSensorModel.codeFarDetection,
step: 0.1),
),
FlushSensorOption(
sliderValue: flushSensorModel.sensiReduce.toString(),
withArrow: true,
icon: Assets.triggerLevelIcon,
label: 'Trigger Level',
onTap: () => _showParameterDialog(
min: 0,
max: 3,
value: flushSensorModel.sensiReduce.toDouble(),
context: context,
title: 'Trigger Level',
controlCode: FlushSensorModel.codeSensiReduce,
step: 1),
),
FlushSensorOption(
sliderValue: flushSensorModel.occurDistReduce.toString(),
withArrow: true,
icon: Assets.indentLevelIcon,
label: 'Indent Level',
onTap: () => _showParameterDialog(
min: 0,
max: 3,
value: flushSensorModel.occurDistReduce.toDouble(),
context: context,
title: 'Indent Level',
controlCode: FlushSensorModel.codeOccurDistReduce,
step: 1),
),
FlushSensorOption(
sliderValue: '${flushSensorModel.presenceDelay / 10}',
withArrow: true,
icon: Assets.targetConfirmTimeIcon,
label: 'Target Confirm Time',
onTap: () => _showParameterDialog(
min: 0.0,
max: 0.5,
value: flushSensorModel.presenceDelay.toDouble() / 10,
context: context,
title: 'Target Confirm Time',
controlCode: FlushSensorModel.codePresenceDelay,
step: 0.1),
),
FlushSensorOption(
sliderValue: '${flushSensorModel.noneDelay.toDouble() / 10}',
withArrow: true,
icon: Assets.disappeDelayIcon,
label: 'Disappe Delay',
onTap: () => _showParameterDialog(
min: 20,
max: 300,
value: flushSensorModel.noneDelay.toDouble() / 10,
context: context,
title: 'Disappe Delay',
controlCode: FlushSensorModel.codeNoneDelay,
step: 1),
),
],
);
}
void _showParameterDialog({
required double step,
required BuildContext context,
required String title,
required String controlCode,
required double min,
required double max,
required double value,
String? description = '',
}) async {
var result = await showDialog(
context: context,
builder: (context) {
return FlushParameterControlDialog(
title: title,
sensor: deviceModel,
value: value,
min: min,
max: max,
step: step,
description: description);
},
);
if (result != null) {
if (controlCode == FlushSensorModel.codeNearDetection ||
controlCode == FlushSensorModel.codeFarDetection) {
result = (result * 100).toInt();
}
if (controlCode == FlushSensorModel.codeNoneDelay) {
result = (result * 10).toInt();
}
if (controlCode == FlushSensorModel.codePresenceDelay) {
result = (result * 10).toInt();
}
bloc.add(
FlushSensorChangeValueEvent(value: result, code: controlCode),
);
}
}
}

View File

@ -0,0 +1,177 @@
part of 'flush_sensor_interface.dart';
class FlushParameterControlDialog extends StatefulWidget {
final String title;
final String? description;
final DeviceModel sensor;
final double value;
final double min;
final double max;
final double step;
const FlushParameterControlDialog({
super.key,
required this.title,
required this.sensor,
required this.value,
required this.min,
required this.max,
required this.step,
this.description = '',
});
@override
FlushParameterControlDialogState createState() =>
FlushParameterControlDialogState();
}
class FlushParameterControlDialogState
extends State<FlushParameterControlDialog> {
late double _value;
@override
void initState() {
super.initState();
_value = widget.value;
}
@override
Widget build(BuildContext context) {
final divisions = ((widget.max - widget.min) / widget.step).toInt();
final decimalPlaces = widget.step % 1 == 0 ? 0 : 1;
return Dialog(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
BodyMedium(
text: widget.title,
style: context.bodyMedium.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontsManager.extraBold,
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 15,
horizontal: 50,
),
child: Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
),
child: TitleMedium(
text:
"${_value.toStringAsFixed(decimalPlaces)} ${widget.description}",
style: context.titleMedium.copyWith(
color: Colors.black,
fontWeight: FontsManager.bold,
),
),
),
SizedBox(
width: MediaQuery.sizeOf(context).width,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () {
setState(() {
_value = (_value - widget.step)
.clamp(widget.min, widget.max);
_value =
double.parse(_value.toStringAsFixed(decimalPlaces));
});
},
icon: const Icon(Icons.remove, color: Colors.grey),
),
Flexible(
child: Slider(
value: _value,
onChanged: (value) {
final newValue =
(value / widget.step).round() * widget.step;
setState(() {
_value = newValue.clamp(widget.min, widget.max);
});
},
min: widget.min,
max: widget.max,
divisions: divisions,
label: _value.toStringAsFixed(decimalPlaces),
),
),
IconButton(
onPressed: () {
setState(() {
_value = (_value + widget.step)
.clamp(widget.min, widget.max);
_value =
double.parse(_value.toStringAsFixed(decimalPlaces));
});
},
icon: const Icon(Icons.add, color: Colors.grey),
),
],
),
),
Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
InkWell(
onTap: () {
Navigator.pop(context);
},
child: Center(
child: BodyMedium(
text: 'Cancel',
style: context.bodyMedium
.copyWith(color: ColorsManager.greyColor),
),
),
),
Container(
height: 50,
width: 1,
color: ColorsManager.greyColor,
),
InkWell(
onTap: () {
if (widget.sensor.isOnline == null || !widget.sensor.isOnline!) {
CustomSnackBar.displaySnackBar('The device is offline');
return;
}
Navigator.pop(context, _value);
},
child: Center(
child: BodyMedium(
text: 'Confirm',
style: context.bodyMedium.copyWith(
color: ColorsManager.primaryColorWithOpacity),
),
),
),
],
)
],
),
),
);
}
}

View File

@ -0,0 +1,31 @@
part of "flush_sensor_interface.dart";
class PresenceIndicator extends StatelessWidget {
const PresenceIndicator({super.key, required this.state});
final String state;
@override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
state.toLowerCase() == 'motion'
? Assets.assetsIconsPresenceSensorAssetsPresenceSensorMotion
: Assets.assetsIconsPresenceSensorAssetsPresence,
width: 100,
height: 100,
),
const SizedBox(
height: 10,
),
BodyMedium(
text: StringHelpers.toTitleCase(state),
style: context.bodyMedium.copyWith(fontWeight: FontsManager.bold),
),
],
),
);
}
}

View File

@ -13,6 +13,7 @@ import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/door_sensor/door_sensor_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/four_scene_switch/four_scene_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_door_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.dart';
@ -300,6 +301,13 @@ Future<void> showDeviceInterface(
pageBuilder: (context, animation1, animation2) =>
SosScreen(device: device)));
case DeviceType.FlushMountedSensor:
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) =>
FlushMountedInterface(deviceModel: device)));
break;
default:
}

View File

@ -1148,4 +1148,12 @@ class Assets {
static const String refreshStatusIcon =
'assets/icons/refresh_status_icon.svg';
static const String detectionDistanceIcon =
'assets/icons/detection_distance_icon.svg';
static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg';
static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg';
static const String targetConfirmTimeIcon =
'assets/icons/target_confirm_time_icon.svg';
static const String disappeDelayIcon = 'assets/icons/disappe_delay_icon.svg';
static const String flushIcon = 'assets/icons/flush_icon.svg';
}

View File

@ -595,10 +595,7 @@ class DevicesAPI {
final result = <DeviceModel>[];
for (final device in data) {
final mappedDevice = DeviceModel.fromJson(device);
if (mappedDevice.productType?.name !=
DeviceType.FlushMountedSensor.name) {
result.add(mappedDevice);
}
result.add(mappedDevice);
}
return result;
},

View File

@ -36,7 +36,5 @@ abstract class ColorsManager {
static const Color grayButtonColors = Color(0xffCCCCCC);
static const Color blueButton = Color(0xff85BDFF);
static const Color backgroundGrey = Color(0xFFF3F3F3);
static const Color grayTextColor = Color(0xFFC4C4C4);
}
//background: #F5F5F5;background: #85BDFF;