From f1d8283224ea186e61d3b7be1c2c7e6eed2fbcf0 Mon Sep 17 00:00:00 2001 From: MoonDev Date: Fri, 23 May 2025 01:55:25 +0300 Subject: [PATCH] some updates --- __pycache__/web_server.cpython-311.pyc | Bin 6559 -> 6611 bytes callbacks/__pycache__/media.cpython-311.pyc | Bin 4946 -> 5846 bytes callbacks/__pycache__/screen.cpython-311.pyc | Bin 1802 -> 1997 bytes callbacks/__pycache__/v2ray.cpython-311.pyc | Bin 8745 -> 8773 bytes callbacks/media.py | 39 ++++++- callbacks/screen.py | 11 +- callbacks/v2ray.py | 10 +- config/frontend.yaml | 40 +++++-- static/frontend/public/icons/next_arrow.svg | 1 + static/frontend/public/icons/play_pause.svg | 1 + static/frontend/public/icons/prev_arrow.svg | 1 + static/frontend/public/icons/sound.svg | 1 + static/frontend/public/icons/vscode.svg | 41 ++++++++ static/frontend/public/icons/yandex.svg | 1 + static/frontend/src/Api.jsx | 3 +- static/frontend/src/App.css | 15 +++ static/frontend/src/App.jsx | 104 +++++++++++++++---- web_server.py | 39 +++---- 18 files changed, 251 insertions(+), 56 deletions(-) create mode 100644 static/frontend/public/icons/next_arrow.svg create mode 100644 static/frontend/public/icons/play_pause.svg create mode 100644 static/frontend/public/icons/prev_arrow.svg create mode 100644 static/frontend/public/icons/sound.svg create mode 100644 static/frontend/public/icons/vscode.svg create mode 100644 static/frontend/public/icons/yandex.svg diff --git a/__pycache__/web_server.cpython-311.pyc b/__pycache__/web_server.cpython-311.pyc index 7fc74e7ac21eea3e93376161b1b18e5ebd6e9c5e..47f615c00bc834ad1bd879971cb8bbfdaca4b9e9 100644 GIT binary patch delta 1340 zcmZuxU2Gy%6ux($3@|VQ0}KO{@(-Q1N}Ku@x`}PNTQJ?S7!uvEF-EzpYa6o-pfL^W z`p^d+-CScZsj&}QiJ~v72~V5#XB!`9a7>#fyy%NhhK<>zi7(!PAT7IRa?U+D_dDM` z=iHh3eDSwM_A9H^42aA(lQ)d^JFEylxs&)|5?KqA6ez(1vh|I&-f zD47xa7DQa%60c=Dm>^i91A(CA$J@}Iv=bf!;#-abNcM>ighZSNIuao|5G`XrsIgxH z$Tfx*cEMOJAqu1ca_<5pmh|VD3gIEsQvi7o-SQq#BEyVDfF_@i;V(t6 zQ)p^eKURg77t?CK22fzE=3ps`LZ$F^JqkaT7bjEUHWR~&N`4eU(N^qy9}plq52QH0 zN44SF-S>2DGpyqz8xyMfVI}|{!Ah{EtJteFF0e5iHoEY~tOqjqJ2vF7jBrs<<{==c zKUm4bjN(;p2#FSruDffD2C7;3D z{6FR`61z&Y!r*rtp4(l<`49*b8FtDpZfNEiYunNVs&$uVe31Mb%jeP%(Ti(Hy1k0oXpG( z3gl!yqwpD(&&Za{BLfhFUq-)}XBER~kk}skCJz*&Ld*@FZ z-zHXE;78Zw%7X5f)a<1=cn~+Iq3*%_)EcWhWUa(M^o7`q>nB34^2J8T|s;X&d1F4!t4LK3chKpO*SIs`lW8nhCs9|YT zc9$HqlBy*T(8|dAs@Y?a_F=J9_LLy4plS*BNo8+|p((1C)JaG$3=GSy&}q;zXon3P z1lT_fa3E{n%yL4pSmJG%D+fz9+JUMixcwKVsD&zvIV zM_MM|mQ`^_Ryl9S$}cP$Js5Yc)2iUODal<=4m8r_1xzXals@h*s`xmbm z>Y%9(8uF0YD-W&8>57~-4k-=7i;Fycrm9Pm){WF@q$K34N<@R;3 zrGq9S&TlL?MiRj^Mi7IE7!!jgCTbwjgqU_IG8!@X!9>288svk1^4wSU2N#Uq_wIT3 zoO{l>=lssOZ(-99t>OnZn;F5l`d<4hHy!h04KB_%4R&%9=BODX8OesYWx^V@PS~P0 zjpyUyggq)@q~LJ}vt%RZod3GYpo!)e>ujrcs$qv7#15ToqyqK@+=^R4E0$B3cvkbd zb)bJ7Dem(ap^GppgB=~Kkp;!7tXFJ@b?cWlkZf8jJFJ(@C-hOz|IoL9eywahfudg7 z=7o<>7J+V%?Lg~f38-JjLn!E=U*KIfTr&5DhkBoVet35{O#j4(EIdT?8qB*^OkcLQ z@Uu9Bb2><7A5z)g{V}dzTB#cD6;4LyIY<{~Wz6bS+tM1Lj83($a4Ju)aZLs;!{r2q z?Z{GXFj#I(2&s}fqF8F$izMg_DRJ4V%PnLP)_TF%HEx5W0uI)wF_H5*~wd$??r*~dx zDAaCgEx6i>uC~0TJ+E)S3kRc-N2{V8IZRl)y@1?#<-PcIoG!3vaKm8r}~z3p`@Hzx5BAzDCr|u zV&$~xH*7=aA*hQmD+8LJ*FpktW#c<~SvbpOpz+V>)Kw{sJ6L$l7!9EYq;6a?Mrb1< z8)pq_(~3kkt3mp%z8xd_z21j4!$ypbprhPDbOZ-2&?IqI^E;1%5n+K4)`2r^^d!Hj z#ve<_{?3k$8L`qH{*Qs~_G4jegZz z`Br4NoZh@|px}%YosqmXvgmNn@r#n{)X2PP&hU%VPrniVXn4K1dyf-cahm%Ocg0(` zC&XO|8DM=CaeW-R%DMV#KD}w`#{f>i(j;j~=qKZ`Y0?d$57J2~Oy9Tk=twQSZTa!( z4iLfx>x2){3a|xMCA&(dhbLp{lrkJkO(#akR<_>-B*owbd@AS|G##=7WG_+P_9oVx zI@da-$HevZ4{@STC`VQDIQST&pNs$Eqf_!d)W;-IrpOcEBMlk8QHkS)wQ+z#sg@)^Ezy$t8*zQ+5dZ)H delta 1712 zcmaJ>O-v+36t140|9@ZxSztm9e***%1YH6fSOOea5m;DPb`Fjmx&|6&x=D2nECgf| z$woaG*-CU1SDl#bs?h@;O!lS+&IUF_%|sKA>WO4`4|?)d4+H3~)=a&A?{)R}eeYFu z|IqQEIrv#1;6*SVf78C$9NY<3N$>6?W}3WNU(%QLC;bvqk?P0H}M?`oY$y0kE~IG>ck;w`>>r zq%9S30oI!^9}0;tA_;wfJp5Z*6VEt44YJS)6Iya}sjS+T_Xv4O7CCdrSMpiOf$NgL z6hQehln=8wHf2^*@)6pQS?n}o5k4s=9QF-+J|u|vBWLXVar;WEL@PEV)xGXONcDVZ zSG^y}8%Xutv9Xhu1Amw+zDoY8%N4ILJ&8~wqK%sl)^wUt{Vc(6x}L3bZaUs@tV*ls zx_k|-N)6~KXn(kTTLEPXXuKH4EMX>Qiv+)->8i16Vsp)8B#T#!OfE}s&eZe;>;8yA z2_DX=nt_L>$2+ZuEi+6P%zn!gc#4Sds5Z|uLs#e;o;P%+Xu5z&WXKB1B=A;CI%6md zr>T*}U7cN+8MwQ%s}-jWS|YZVo?)3GEJyV-L}w^gHMWRNrm&oev&1x&1%ee_#hSjH zV?q&BqBHXZZ42WWMKj46w5i!KN)6hN$I~SRCSgpdPAgi*#Gp)cX*r`XP?^P=iFJdO zx>kfPI4so*#hv+e=-5H%*f|HFOMfffEF#l0*@fW-!6SE^%=2F8G6Z}v2j8nY*G{B#?W4A<1Cq$TQ_O>`6rHWff z#X*U}G&R`s-{5xx9aYEp$DY`ghJ$ePVYvA%$73f7SKn^B+i*9RuRREL9)>!PkmR{6 z?atqSdH;oR_&>1hck{(-G51S(?S5?PpnUqUe0twM4JZ|HUgLe#xIOaRs2A;dy`z2d zUZifcN8amk2)|Dr9YA{n(Xp`1KMDWDeSRH^T=yr!jUcgLhyJe7E^vqW*Z!NfhlpP; z|M^k^60Hu}0vFmUOdFU&h|#swvXV1NN-@{;dD<>+b$~I&iwcL4TA&VV4ef>81WyIu zmbwM#%+o2i!S9dV_yoHpH)n$BG2yPZIEV%3}5Zq0+ug z$qMZUAQeiV;L-Ad>TZZyV+9%B$%4%=3offp&7fi48fgy|oNR3wjwp4(I_l zId~}E>L67?@a9>(^qN0FNFax>r-E4MEl~8}$v0^N6(75wnR)Z$`+e_C^!tqS#c?cx zaq@Ak_Uph&-B`uNA#sTd4YEUAtqiXIn(SyUMd~g?QrAE-7fK`_I#suer|OY|?W3%L zBUfPfi&;d6{IpX8`)c3L4eVTx=D7K74L-qQ7&n8c@chS5c)eQlo1PGS4wGSdqMfxh zoay+V#S!QTd>p4ySK3-JV9&~pJueS|wYh!o&fN#atx^eBw9KnQ0G`h9bD8SDGD+N0 zQo=!W$Z=%;-Tj`O?$IaA)Ku0S)fv313Z?c89vJr&0o>#o5e!bPq8~l>m zOsTZF+iKSR78e23c}!)%6RNwJah%sSTd9?sRqtXKFXItaqWFsFkP(fr^38hRbgU88AU`5w$iRqS(;%ak6IfAL KACdntmir58F(3GkU{oP1{(^H7!;@&cn&4lo6MO#%-+Tr7+gXD6|I0B^s0B)^x;Q6Uen_b zG|qiDllTlrH28E{glR1<@nXJUl}sVH%<$PCr0#DNsZ)ECjtyF};E!CuebsPKg=`TK zw3uDNJ;d_a?{ow=*%3Ap_D1=R8$!IZ!XRj#O^`RgeZ;0T)lu@M#hLxWy0&Fwr)hdU zXRcbsaw(xYG|TgXhvmDrQZ5y(G8Yam@}PXdrcmMT_k??+wB`zCDR2IrrWZm&pyC>^ zgIuDs)KjSaL_5+>JI@CnL$O*Yw$Gl!efEZWyd6nLQ8nsMPbt-STY6lnjx(?mlv7Ce zw(9MN%d%Xy=ROJ9{ZpG*DI06c<|L1Ti$TQAp!bQK(R6Ob;4+D~I M%{b+v|L|IU0c2!*?f?J) diff --git a/callbacks/__pycache__/v2ray.cpython-311.pyc b/callbacks/__pycache__/v2ray.cpython-311.pyc index a0ec7acfa62be0f66b80129eb040c0102d9aa43a..c206cc796dbbe10fd561f641591d8895133688d5 100644 GIT binary patch delta 1634 zcmcgsTWAwO6rJfdNt5iRNt!gzZZ<(uHef5YwL*QhNv+yawbg(REU_DFAvQXj3W`wm z#|MJQ1yKZFAJ!s*B8WeJ*ed9U5TRm;=(qTxE3{DhAQ1VM=6EwEJR#%4Gni10u-fNLRUOVQ*rHZf!p!Wx<%;F(Q=kG7g2^G`XS*p$fy z=FPq86p!5gyuUZ!wSBxR#fZQ6g-aQCDS4MN5s4b*+U|{j$61>FI^@x#=l8 zi1vuz)tH+3z9M30Q%MyA zkwFcFT`k6TO4V)D;vKv_a3Ps5Uv#omE9D{Y`n}tNTY{zT?zVsAHavHShUWG3|5zHF z*Vv+^9=KfDU=YzGUp-{)9k9rAyQ+~M;o-_;9Z4N(j%NpQB%8^kNlkAitELtB;5lzy z!p^&aK_i^?ZWcWTpN3|kZkFj|b@18Si0{KP-%fl5>{T*8eea=9z_KxAR8Tj{mD7d3 zHpTSw6*!LNvd$U!==VoPIKuuCh?N0PVap6pcekg%PZ}Ih(>b{evZ6bvZ;dG^IFfZ; z&D(mCn>u@1JDXcNS`)A?(By5Uv3~lg<@BPxqt8(^WkjwJTnsFB*$mZJqrC>Ns@@2_64ibucmfq(meS!avHBU?Lw;#=R_x0vgPHUyk_21$&c>}6S8Hf){G^Xh{umu7akBhb Nh|V&7ECxa(_yzNVi;n;R delta 1104 zcmb7@O-vI(6vuZc{n}+V3(Hcav=wSJi1h=%T16tk5R4jQB$c*kti?>Li9{1W z4ss*rp*IydP*6h6}z)DtG2^x{lwst1j6cIUVMefxIi|7K@jh9|=~ zgE7z1_SguV+3*x+?T``U5aRn#8)SIh5#=M6bOp;Ow5SlZN6TP^4#XBKQ z_dgG374M?bv7~eWvaehJ>iDXpjqD!n%Spbh4wkeRsZDyUDQ(EDPqZ96ncI1Kg_88#G{)V=+i8}e&qQW?$3qX6JAud|N ziiSIt%lYzZVW`SUOMF<>h-lTXC3kgzrV=&=sb$a^Ho|vlpQV$^R4;_&IKuEq*1=@2 zhflI@S@kwEo7U={yv_0Ig3Y93Tgc$%)YcF-%~w=XJ@0lhy`AX=%!d@~f?{IYg_zdz z+Tg>YX~AC~7ECV$b6AAWj%T$&I`^RC=w=eRyf-wEOp}2`B1QsziGfrs4)e-gVG}dj z$`FJ;XSeP&t7}{lEk%`&wLs3f70m#zo<#THfvUnaRYXIxw^fYHTo0GVaK6;ldjm~c zy9W77Y}&x($oT{j*aT$^Rtle~umg(G!7^$tezxDZm`ugeJ}W6@sTzZYVI4!>DJuwC zv>lER73XMAS?Oo`NRMmW2T6xpTCAy~w)||W_qRlHkOqfYj+3IW9%~j3yOy;kNE!wTz%YH2tr}rI t^Z8^aRMtD`KkBcaK70(J#x?HG{F0A<{rxE^5m|l_?xM=aJm7QJ`~Z!x6LSCn diff --git a/callbacks/media.py b/callbacks/media.py index dc82a24..cf7055f 100644 --- a/callbacks/media.py +++ b/callbacks/media.py @@ -5,6 +5,7 @@ import comtypes import ctypes from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume from ctypes import cast, POINTER +from comtypes import CLSCTX_ALL from typing import Dict, Any from winrt.windows.media.control import GlobalSystemMediaTransportControlsSessionManager as MediaManager @@ -15,10 +16,25 @@ parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) if parent_dir not in sys.path: sys.path.append(parent_dir) +VOLUME = 0 +async def get_volume(args: Dict[str, Any]) -> Dict[str, Any]: + global VOLUME + + # Get default audio device + devices = AudioUtilities.GetSpeakers() + interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None) + volume = cast(interface, POINTER(IAudioEndpointVolume)) + + # Get current volume level (returns a float between 0.0 and 1.0) + current_volume = volume.GetMasterVolumeLevelScalar() + + return {"value": round(current_volume*100)} + async def set_volume(args: Dict[str, Any]) -> Dict[str, Any]: + global VOLUME """ Sets the Windows system volume using the Core Audio API. @@ -36,12 +52,13 @@ async def set_volume(args: Dict[str, Any]) -> Dict[str, Any]: comtypes.CoInitialize() # Get volume level from args - level = args.get('level', 0.5) # Default to 50% if not specified + level = args.get('level', 50) # Default to 50% if not specified # Validate input level - if not isinstance(level, (int, float)) or not 0.0 <= level <= 1.0: - raise ValueError("Volume level must be a float between 0.0 and 1.0") - + if not isinstance(level, (int, float)) or not 0 <= level <= 100: + raise ValueError("Volume level must be a float between 0 and 100") + VOLUME = level + level = level/100 # Get audio endpoint interface devices = AudioUtilities.GetSpeakers() interface = devices.Activate(IAudioEndpointVolume._iid_, comtypes.CLSCTX_ALL, None) @@ -106,4 +123,16 @@ async def prev(args): # назад await current_session.try_skip_previous_async() else: - print("No media session is active.") \ No newline at end of file + print("No media session is active.") + +async def play_pause(args): + # Получаем менеджер медиасессий + sessions = await MediaManager.request_async() + current_session = sessions.get_current_session() + + if current_session: + await current_session.try_toggle_play_pause_async() + else: + print("No media session is active.") + + diff --git a/callbacks/screen.py b/callbacks/screen.py index 70cf0a8..e6559d2 100644 --- a/callbacks/screen.py +++ b/callbacks/screen.py @@ -1,5 +1,7 @@ import monitorcontrol +BRIGHTNESS = 10 + def get_monitors(): """Retrieve a list of connected monitors.""" return monitorcontrol.get_monitors() @@ -13,9 +15,16 @@ def set_brightness(monitor, brightness): print(f"Set brightness to {brightness}%") -def chenge_brightness(args): +def get_brightness(args): + global BRIGHTNESS + return {"value":BRIGHTNESS} + + +def change_brightness(args): + global BRIGHTNESS try: level = args.get('level', 10) + BRIGHTNESS = level monitors = get_monitors() if not monitors: raise OSError(f"No DDC/CI compatible monitors found.") diff --git a/callbacks/v2ray.py b/callbacks/v2ray.py index 4efee22..1ec2970 100644 --- a/callbacks/v2ray.py +++ b/callbacks/v2ray.py @@ -62,7 +62,6 @@ class V2rayAController: async with self.session.post(url, headers=headers, json=data) as response: if response.status == 200: print("Прокси успешно включен") - V2RAY_VPN_ENABLED = True await show_notification({"title":"✅ V2Ray proxy","message":"Прокси успешно включен"}) return True else: @@ -75,7 +74,7 @@ class V2rayAController: async def disable_proxy(self): """Выключение прокси""" - global V2RAY_VPN_ENABLED + if not self.token: print("Не выполнен вход. Пожалуйста, сначала выполните аутентификацию") return False @@ -88,7 +87,6 @@ class V2rayAController: async with self.session.delete(url, headers=headers, json=data) as response: if response.status == 200: print("Прокси успешно выключен") - V2RAY_VPN_ENABLED = False await show_notification({"title":"🔴 V2Ray proxy","message":"Прокси успешно выключен"}) return True else: @@ -105,6 +103,7 @@ class V2rayAController: await self.session.close() async def enable_vpn(args): + global V2RAY_VPN_ENABLED # Инициализация контроллера config = await read_config("v2ray.yaml") if config.get("username") and config.get("password"): @@ -119,9 +118,11 @@ async def enable_vpn(args): # Закрытие сессии await controller.close() + V2RAY_VPN_ENABLED = True else: raise OSError("Config unset") async def disable_vpn(args): + global V2RAY_VPN_ENABLED # Инициализация контроллера config = await read_config("v2ray.yaml") if config.get("username") and config.get("password"): @@ -136,6 +137,7 @@ async def disable_vpn(args): # Закрытие сессии await controller.close() + V2RAY_VPN_ENABLED = False else: raise OSError("Config unset") @@ -143,5 +145,5 @@ async def disable_vpn(args): async def is_vpn_enabled(args): global V2RAY_VPN_ENABLED return { - "vpn_enabled": V2RAY_VPN_ENABLED + "value": V2RAY_VPN_ENABLED } \ No newline at end of file diff --git a/config/frontend.yaml b/config/frontend.yaml index 76e27ed..95a8f3f 100644 --- a/config/frontend.yaml +++ b/config/frontend.yaml @@ -24,21 +24,37 @@ sliders: - value: 0 label: "Яркость экрана" - action: "screen.chenge_brightness" + action: "screen.change_brightness" + get_state: "screen.get_brightness" - value: 0 label: "Громкость звука" action: "media.set_volume" + get_state: "media.get_volume" mini_icon: "/icons/mini/settings.png" +- type: "multi" + label: "Медиа" + mini_icon: "/icons/sound.svg" + buttons: + - action: "media.prev" + imageUrl: "/icons/prev_arrow.svg" + action_args: {} + - action: "media.play_pause" + imageUrl: "/icons/play_pause.svg" + action_args: {} + - action: "media.next" + imageUrl: "/icons/next_arrow.svg" + action_args: {} + - type: "break" # Разделитель label: "Сайты" - type: "image" # Кнопка с картинкой - imageUrl: "/icons/telegram-logo-svgrepo-com.svg" - label: "Telegram" + imageUrl: "/icons/yandex.svg" + label: "Яндекс" action: "web.open_url" action_args: - url: "https://ya.ru " + url: "https://ya.ru" mini_icon: "/icons/mini/arrow-up-right-from-square.png" - type: "image" # Кнопка с картинкой @@ -46,12 +62,12 @@ label: "Cloud" action: "web.open_url" action_args: - url: "https://cloud.viadev.su " + url: "https://cloud.viadev.su" mini_icon: "/icons/mini/arrow-up-right-from-square.png" - type: "break" # Разделитель label: "Приложения" - + - type: "image" # Кнопка с картинкой imageUrl: "/icons/steam-svgrepo-com.svg" label: "Steam" @@ -60,6 +76,13 @@ path: "C:/Users/Slava/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Steam/Steam.lnk" mini_icon: "/icons/mini/arrow-up-right-from-square.png" +- type: "image" # Кнопка с картинкой + imageUrl: "/icons/vscode.svg" + label: "VS Code" + action: "app.run_app" + action_args: + path: "C:/Users/Slava/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Visual Studio Code/Visual Studio Code.lnk" + mini_icon: "/icons/mini/arrow-up-right-from-square.png" - type: "break" # Разделитель label: "Управление ПК" @@ -67,5 +90,8 @@ - type: "image" # Кнопка с картинкой imageUrl: "/icons/vpn.png" label: "VPN" + is_swtch: True + get_state: "v2ray.is_vpn_enabled" action: "v2ray.enable_vpn" - action_args: {} \ No newline at end of file + action_args: {} + diff --git a/static/frontend/public/icons/next_arrow.svg b/static/frontend/public/icons/next_arrow.svg new file mode 100644 index 0000000..cdb4340 --- /dev/null +++ b/static/frontend/public/icons/next_arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/frontend/public/icons/play_pause.svg b/static/frontend/public/icons/play_pause.svg new file mode 100644 index 0000000..9ac1dd9 --- /dev/null +++ b/static/frontend/public/icons/play_pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/frontend/public/icons/prev_arrow.svg b/static/frontend/public/icons/prev_arrow.svg new file mode 100644 index 0000000..420e169 --- /dev/null +++ b/static/frontend/public/icons/prev_arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/frontend/public/icons/sound.svg b/static/frontend/public/icons/sound.svg new file mode 100644 index 0000000..6ca099d --- /dev/null +++ b/static/frontend/public/icons/sound.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/frontend/public/icons/vscode.svg b/static/frontend/public/icons/vscode.svg new file mode 100644 index 0000000..c453e63 --- /dev/null +++ b/static/frontend/public/icons/vscode.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/frontend/public/icons/yandex.svg b/static/frontend/public/icons/yandex.svg new file mode 100644 index 0000000..d2c8d2e --- /dev/null +++ b/static/frontend/public/icons/yandex.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/frontend/src/Api.jsx b/static/frontend/src/Api.jsx index a83f2ff..0db4942 100644 --- a/static/frontend/src/Api.jsx +++ b/static/frontend/src/Api.jsx @@ -26,7 +26,8 @@ export const createSha256Hash = async (input) => { export const sendRequest = async (action,args) => { try { const TS = Math.round(Date.now()/1000); - const hash = await createSha256Hash(window.localStorage.auth+JSON.stringify(args)+TS); + const AUTH = window.localStorage.auth || window.auth; + const hash = await createSha256Hash(AUTH+JSON.stringify(args)+TS); const response = await axios.post("http://"+window.location.hostname+":"+import.meta.env.VITE_API_PORT+"/action/"+action, {args:args,hash:hash+"."+TS}, { timeout: 4000, headers: { diff --git a/static/frontend/src/App.css b/static/frontend/src/App.css index 938c310..e19e6a6 100644 --- a/static/frontend/src/App.css +++ b/static/frontend/src/App.css @@ -97,4 +97,19 @@ input[type="range"] { .gauge-tile-content { justify-content: flex-start; align-items: flex-start; +} +@keyframes press { + 0% { + transform: scale(1); + } + 50% { + transform: scale(0.8); + } + 100% { + transform: scale(1); + } +} + +.click-button:active { + animation: press 0.5s ease-out; } \ No newline at end of file diff --git a/static/frontend/src/App.jsx b/static/frontend/src/App.jsx index 657362b..7e49086 100644 --- a/static/frontend/src/App.jsx +++ b/static/frontend/src/App.jsx @@ -13,7 +13,7 @@ const TextTile = ({ label }) => ( const ImageTile = ({ imageUrl, label, enabled, toggleEnabled }) => (
- {label} + {label} {label}
); @@ -62,7 +62,7 @@ const GaugeTile = ({ gauges }) => ( ); -const SliderTile = ({ sliders, tileId, updateSliderValue, label }) => { +const SliderTile = ({ sliders, tile, updateSliderValue, label }) => { return (
{label} @@ -78,8 +78,10 @@ const SliderTile = ({ sliders, tileId, updateSliderValue, label }) => { min="0" max="100" value={s.value} - onChange={(e) => updateSliderValue(tileId, index, e.target.value)} + onChange={(e) => updateSliderValue(tile, index, e.target.value)} className="w-full" + onMouseUp={(e) => { sendSlider(tile, index, e.target.value) }} + onTouchEnd={(e) => { sendSlider(tile, index, e.target.value) }} />
))} @@ -88,12 +90,38 @@ const SliderTile = ({ sliders, tileId, updateSliderValue, label }) => { ); }; + +const MultiButtonTile = ({ tile, onTileClick }) => { + return ( +
+ {tile.label} +
+ {tile.buttons.map((s, index) => ( + + ))} +
+
+ ); +}; + + + + const NumberTile = ({ value, label }) => (
{value} {label}
); +const sendSlider = async (tile, sliderIndex, newValue) => { + try { + await sendRequest(tile.sliders[sliderIndex].action, { level: Number(newValue) }); + } catch (e) { + console.error(e); + } +} const App = () => { @@ -109,14 +137,17 @@ const App = () => { - const updateSliderValue = (tileId, sliderIndex, newValue) => { + + + const updateSliderValue = (Tile, sliderIndex, newValue) => { setTiles(prevTiles => prevTiles.map(tile => { - if (tile.id === tileId && tile.type === "slider") { + if (tile.id === Tile.id && tile.type === "slider") { const newSliders = [...tile.sliders]; newSliders[sliderIndex] = { ...newSliders[sliderIndex], value: parseInt(newValue) + }; return { ...tile, sliders: newSliders }; } @@ -177,8 +208,37 @@ const App = () => { 'Content-Type': 'application/json' } }); + window.localStorage.auth = passw; + + for (var tile of response.data) { + if (tile.type == "slider") { + for (var slider_id in tile.sliders) { + if (tile.sliders[slider_id].get_state) { + try { + const data = await sendRequest(tile.sliders[slider_id].get_state, {}); + tile.sliders[slider_id].value = data.result.value; + } catch (e) { + console.error(e); + } + } + } + } + if (tile.is_swtch) { + try { + const data = await sendRequest(tile.get_state, {}); + tile.enabled = data.result.value; + if (tile.action.includes("enable") && tile.enabled) { + tile.enabled = true; + tile.action = tile.action.replace("enable", "disable"); + } + } catch (e) { + console.error(e); + } + } + } + setTimeout(() => { - window.localStorage.auth = passw; + setTiles(response.data); setPhase("dash"); updateSystemStats(); @@ -236,21 +296,26 @@ const App = () => { if (tile.action) { try { - setTileProps(tile.id, { loading: true }) + if (tile.id) { + setTileProps(tile.id, { loading: true }) + } await sendRequest(tile.action, tile.action_args); - if (tile.action.includes("enable")) { - tile.enabled = true; - tile.action = tile.action.replace("enable", "disable"); - } else if (tile.action.includes("disable")) { - tile.enabled = false; - tile.action = tile.action.replace("disable", "enable"); - } - tile.loading = false; + if (tile.id) { + if (tile.action.includes("enable")) { + tile.enabled = true; + tile.action = tile.action.replace("enable", "disable"); + } else if (tile.action.includes("disable")) { + tile.enabled = false; + tile.action = tile.action.replace("disable", "enable"); + } + tile.loading = false; - setTileProps(tile.id, tile) + setTileProps(tile.id, tile) + } } catch (e) { - setTileProps(tile.id, { loading: false }) + if (tile.id) + setTileProps(tile.id, { loading: false }) } } } @@ -313,7 +378,7 @@ const App = () => {
{tiles.map(tile => ( -
+
{tile.type === "break" ? (
{tile.label}
) : ( @@ -337,8 +402,9 @@ const App = () => { /> )} {tile.type === "clock" && } + {tile.type === "multi" && } {tile.type === "gauge" && } - {tile.type === "slider" && } + {tile.type === "slider" && } {tile.type === "number" && } {tile.mini_icon && (
diff --git a/web_server.py b/web_server.py index d6b2d27..9cbbadf 100644 --- a/web_server.py +++ b/web_server.py @@ -15,7 +15,7 @@ from pathlib import Path from utils.get_primary_ip import get_primary_ip PASSWORD = "10010055" - +SKIP_HASH = False app = FastAPI() @@ -90,28 +90,29 @@ async def handle_action(name: str, payload: CommandModel): action_args = payload.args hash_with_ts = payload.hash - if not hash_with_ts: - raise HTTPException(status_code=400, detail="Missing hash") + if(not SKIP_HASH): + if not hash_with_ts: + raise HTTPException(status_code=400, detail="Missing hash") - # Split hash and timestamp - try: - received_hash, received_ts = hash_with_ts.split(".") - received_ts = int(received_ts) - except ValueError: - raise HTTPException(status_code=400, detail="Invalid hash format") + # Split hash and timestamp + try: + received_hash, received_ts = hash_with_ts.split(".") + received_ts = int(received_ts) + except ValueError: + raise HTTPException(status_code=400, detail="Invalid hash format") - # Check if token is within 5-second window - current_ts = int(time.time()) - if abs(current_ts - received_ts) > 5: - raise HTTPException(status_code=401, detail="Token expired") + # Check if token is within 5-second window + current_ts = int(time.time()) + if abs(current_ts - received_ts) > 5: + raise HTTPException(status_code=401, detail="Token expired") - # Reconstruct hash - data_to_hash = PASSWORD + json.dumps(action_args, separators=(",", ":")) + str(received_ts) - computed_hash = hashlib.sha256(data_to_hash.encode("utf-8")).hexdigest() + # Reconstruct hash + data_to_hash = PASSWORD + json.dumps(action_args, separators=(",", ":")) + str(received_ts) + computed_hash = hashlib.sha256(data_to_hash.encode("utf-8")).hexdigest() - # Verify hash - if computed_hash != received_hash: - raise HTTPException(status_code=401, detail="Invalid hash") + # Verify hash + if computed_hash != received_hash: + raise HTTPException(status_code=401, detail="Invalid hash") try: # Вызываем функцию из callback.py, передавая имя действия и аргументы