some updates
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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.")
|
||||
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.")
|
||||
|
||||
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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: {}
|
||||
action_args: {}
|
||||
|
||||
|
||||
1
static/frontend/public/icons/next_arrow.svg
Normal file
1
static/frontend/public/icons/next_arrow.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M0 5v14a5.006 5.006 0 0 0 5 5h14a5.006 5.006 0 0 0 5-5V5a5.006 5.006 0 0 0-5-5H5a5.006 5.006 0 0 0-5 5Zm16 7a2.993 2.993 0 0 1-.752 1.987c-.291.327-.574.637-.777.84L11.647 17.7a1 1 0 1 1-1.426-1.4l2.829-2.88c.187-.188.441-.468.7-.759a1 1 0 0 0 0-1.323 20.85 20.85 0 0 0-.693-.752L10.221 7.7a1 1 0 1 1 1.426-1.4l2.829 2.879c.2.2.48.507.769.833A2.99 2.99 0 0 1 16 12Z" fill="#970ff8" opacity="1" data-original="#000000" class=""></path></g></svg>
|
||||
1
static/frontend/public/icons/play_pause.svg
Normal file
1
static/frontend/public/icons/play_pause.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M22 21a1 1 0 0 1-1-1V4a1 1 0 1 1 2 0v16a1 1 0 0 1-1 1Zm-4 0a1 1 0 0 1-1-1V4a1 1 0 1 1 2 0v16a1 1 0 0 1-1 1Zm-13.673-.271a3.346 3.346 0 0 1-1.509-.367A3.26 3.26 0 0 1 1 17.404V6.597a3.26 3.26 0 0 1 1.817-2.958 3.305 3.305 0 0 1 3.46.285l7.368 5.402c.86.631 1.354 1.606 1.354 2.674s-.494 2.043-1.355 2.674l-7.368 5.403a3.28 3.28 0 0 1-1.949.651Z" fill="#970ff8" opacity="1" data-original="#000000" class=""></path></g></svg>
|
||||
1
static/frontend/public/icons/prev_arrow.svg
Normal file
1
static/frontend/public/icons/prev_arrow.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M24 19V5a5.006 5.006 0 0 0-5-5H5a5.006 5.006 0 0 0-5 5v14a5.006 5.006 0 0 0 5 5h14a5.006 5.006 0 0 0 5-5ZM8 12a2.993 2.993 0 0 1 .752-1.987c.291-.327.574-.637.777-.84L12.353 6.3a1 1 0 0 1 1.426 1.4l-2.829 2.88c-.187.188-.441.468-.7.759a1 1 0 0 0 0 1.323c.258.29.512.57.693.752l2.836 2.886a1 1 0 0 1-1.426 1.4l-2.829-2.878c-.2-.2-.48-.507-.769-.833A2.99 2.99 0 0 1 8 12Z" fill="#970ff8" opacity="1" data-original="#000000" class=""></path></g></svg>
|
||||
1
static/frontend/public/icons/sound.svg
Normal file
1
static/frontend/public/icons/sound.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M20.807 4.29a1 1 0 0 0-1.415 1.415 8.913 8.913 0 0 1 0 12.59 1 1 0 0 0 1.415 1.415 10.916 10.916 0 0 0 0-15.42Z" fill="#970ff8" opacity="1" data-original="#000000" class=""></path><path d="M18.1 7.291a1 1 0 0 0-1.42 1.415 4.662 4.662 0 0 1 0 6.588 1 1 0 0 0 1.42 1.415 6.666 6.666 0 0 0 0-9.418ZM13.82.2A12.054 12.054 0 0 0 6.266 5H5a5.008 5.008 0 0 0-5 5v4a5.008 5.008 0 0 0 5 5h1.266a12.059 12.059 0 0 0 7.554 4.8.917.917 0 0 0 .181.017 1 1 0 0 0 1-1V1.186A1 1 0 0 0 13.82.2ZM13 21.535a10.083 10.083 0 0 1-5.371-4.08A1 1 0 0 0 6.792 17H5a3 3 0 0 1-3-3v-4a3 3 0 0 1 3-3h1.8a1 1 0 0 0 .837-.453A10.079 10.079 0 0 1 13 2.465Z" fill="#970ff8" opacity="1" data-original="#000000" class=""></path></g></svg>
|
||||
41
static/frontend/public/icons/vscode.svg
Normal file
41
static/frontend/public/icons/vscode.svg
Normal file
@@ -0,0 +1,41 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="100" height="100">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M70.9119 99.3171C72.4869 99.9307 74.2828 99.8914 75.8725 99.1264L96.4608 89.2197C98.6242 88.1787 100 85.9892 100 83.5872V16.4133C100 14.0113 98.6243 11.8218 96.4609 10.7808L75.8725 0.873756C73.7862 -0.130129 71.3446 0.11576 69.5135 1.44695C69.252 1.63711 69.0028 1.84943 68.769 2.08341L29.3551 38.0415L12.1872 25.0096C10.589 23.7965 8.35363 23.8959 6.86933 25.2461L1.36303 30.2549C-0.452552 31.9064 -0.454633 34.7627 1.35853 36.417L16.2471 50.0001L1.35853 63.5832C-0.454633 65.2374 -0.452552 68.0938 1.36303 69.7453L6.86933 74.7541C8.35363 76.1043 10.589 76.2037 12.1872 74.9905L29.3551 61.9587L68.769 97.9167C69.3925 98.5406 70.1246 99.0104 70.9119 99.3171ZM75.0152 27.2989L45.1091 50.0001L75.0152 72.7012V27.2989Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0)">
|
||||
<path d="M96.4614 10.7962L75.8569 0.875542C73.4719 -0.272773 70.6217 0.211611 68.75 2.08333L1.29858 63.5832C-0.515693 65.2373 -0.513607 68.0937 1.30308 69.7452L6.81272 74.754C8.29793 76.1042 10.5347 76.2036 12.1338 74.9905L93.3609 13.3699C96.086 11.3026 100 13.2462 100 16.6667V16.4275C100 14.0265 98.6246 11.8378 96.4614 10.7962Z" fill="#0065A9"/>
|
||||
<g filter="url(#filter0_d)">
|
||||
<path d="M96.4614 89.2038L75.8569 99.1245C73.4719 100.273 70.6217 99.7884 68.75 97.9167L1.29858 36.4169C-0.515693 34.7627 -0.513607 31.9063 1.30308 30.2548L6.81272 25.246C8.29793 23.8958 10.5347 23.7964 12.1338 25.0095L93.3609 86.6301C96.086 88.6974 100 86.7538 100 83.3334V83.5726C100 85.9735 98.6246 88.1622 96.4614 89.2038Z" fill="#007ACC"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_d)">
|
||||
<path d="M75.8578 99.1263C73.4721 100.274 70.6219 99.7885 68.75 97.9166C71.0564 100.223 75 98.5895 75 95.3278V4.67213C75 1.41039 71.0564 -0.223106 68.75 2.08329C70.6219 0.211402 73.4721 -0.273666 75.8578 0.873633L96.4587 10.7807C98.6234 11.8217 100 14.0112 100 16.4132V83.5871C100 85.9891 98.6234 88.1786 96.4586 89.2196L75.8578 99.1263Z" fill="#1F9CF0"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.25">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M70.8511 99.3171C72.4261 99.9306 74.2221 99.8913 75.8117 99.1264L96.4 89.2197C98.5634 88.1787 99.9392 85.9892 99.9392 83.5871V16.4133C99.9392 14.0112 98.5635 11.8217 96.4001 10.7807L75.8117 0.873695C73.7255 -0.13019 71.2838 0.115699 69.4527 1.44688C69.1912 1.63705 68.942 1.84937 68.7082 2.08335L29.2943 38.0414L12.1264 25.0096C10.5283 23.7964 8.29285 23.8959 6.80855 25.246L1.30225 30.2548C-0.513334 31.9064 -0.515415 34.7627 1.29775 36.4169L16.1863 50L1.29775 63.5832C-0.515415 65.2374 -0.513334 68.0937 1.30225 69.7452L6.80855 74.754C8.29285 76.1042 10.5283 76.2036 12.1264 74.9905L29.2943 61.9586L68.7082 97.9167C69.3317 98.5405 70.0638 99.0104 70.8511 99.3171ZM74.9544 27.2989L45.0483 50L74.9544 72.7012V27.2989Z" fill="url(#paint0_linear)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d" x="-8.39411" y="15.8291" width="116.727" height="92.2456" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<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"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="4.16667"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="overlay" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter1_d" x="60.4167" y="-8.07558" width="47.9167" height="116.151" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<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"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="4.16667"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="overlay" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear" x1="49.9392" y1="0.257812" x2="49.9392" y2="99.7423" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
1
static/frontend/public/icons/yandex.svg
Normal file
1
static/frontend/public/icons/yandex.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" fill="none" viewBox="0 0 26 26"><path fill="#F8604A" d="M26 13c0-7.18-5.82-13-13-13S0 5.82 0 13s5.82 13 13 13 13-5.82 13-13Z"></path><path fill="#fff" d="M17.55 20.822h-2.622V7.28h-1.321c-2.254 0-3.38 1.127-3.38 2.817 0 1.885.758 2.816 2.448 3.943l1.322.932-3.749 5.828H7.237l3.575-5.265c-2.059-1.495-3.185-2.817-3.185-5.265 0-3.012 2.058-5.07 6.023-5.07h3.9v15.622Z"></path></svg>
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -13,7 +13,7 @@ const TextTile = ({ label }) => (
|
||||
|
||||
const ImageTile = ({ imageUrl, label, enabled, toggleEnabled }) => (
|
||||
<div className="tile-content cursor-pointer">
|
||||
<img src={imageUrl} className="rounded-lg w-[6.3rem] h-[6.3rem] object-cover" alt={label} />
|
||||
<img src={imageUrl} className="rounded-lg w-[5rem] h-[5rem] object-cover" alt={label} />
|
||||
<span className="text-purple-400 text-lg font-semibold mt-2">{label}</span>
|
||||
</div>
|
||||
);
|
||||
@@ -62,7 +62,7 @@ const GaugeTile = ({ gauges }) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const SliderTile = ({ sliders, tileId, updateSliderValue, label }) => {
|
||||
const SliderTile = ({ sliders, tile, updateSliderValue, label }) => {
|
||||
return (
|
||||
<div className="tile-content">
|
||||
<span className="text-purple-400 text-lg font-semibold">{label}</span>
|
||||
@@ -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) }}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@@ -88,12 +90,38 @@ const SliderTile = ({ sliders, tileId, updateSliderValue, label }) => {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const MultiButtonTile = ({ tile, onTileClick }) => {
|
||||
return (
|
||||
<div className="tile-content">
|
||||
<span className="text-purple-400 text-lg font-semibold">{tile.label}</span>
|
||||
<div className="flex justify-center w-full gap-6 mt-12">
|
||||
{tile.buttons.map((s, index) => (
|
||||
<button key={index} className="cursor-pointer click-button" onClick={() => { onTileClick(s) }}>
|
||||
<img src={s.imageUrl} className='h-12 w-12' />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const NumberTile = ({ value, label }) => (
|
||||
<div className="tile-content">
|
||||
<span className="text-5xl font-bold text-purple-200">{value}</span>
|
||||
<span className="text-purple-400 text-lg font-semibold mt-2">{label}</span>
|
||||
</div>
|
||||
);
|
||||
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 = () => {
|
||||
<div className="flex w-full flex-col gap-4 anim-pop">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-6 flex-wrap gap-4">
|
||||
{tiles.map(tile => (
|
||||
<div key={tile.id} className={tile.type === "break" ? "col-span-full" : (tile.type === "gauge" || tile.type === "slider" || tile.type === "clock" || tile.type === "number" ? "w-full sm:w-auto col-span-2" : "col-span-1")}>
|
||||
<div key={tile.id} className={tile.type === "break" ? "col-span-full" : (tile.type === "gauge" || tile.type === "slider" || tile.type === "clock" || tile.type === "multi" || tile.type === "number" ? "w-full sm:w-auto col-span-2" : "col-span-1")}>
|
||||
{tile.type === "break" ? (
|
||||
<div className="w-full mt-2 text-purple-400 text-2xl font-bold">{tile.label}</div>
|
||||
) : (
|
||||
@@ -337,8 +402,9 @@ const App = () => {
|
||||
/>
|
||||
)}
|
||||
{tile.type === "clock" && <ClockTile id={tile.id} />}
|
||||
{tile.type === "multi" && <MultiButtonTile id={tile.id} tile={tile} onTileClick={onTileClick} />}
|
||||
{tile.type === "gauge" && <GaugeTile gauges={tile.gauges} />}
|
||||
{tile.type === "slider" && <SliderTile label={tile.label} sliders={tile.sliders} tileId={tile.id} updateSliderValue={updateSliderValue} />}
|
||||
{tile.type === "slider" && <SliderTile label={tile.label} sliders={tile.sliders} tile={tile} updateSliderValue={updateSliderValue} />}
|
||||
{tile.type === "number" && <NumberTile value={tile.value} label={tile.label} />}
|
||||
{tile.mini_icon && (
|
||||
<div className="absolute top-3 right-3 flex gap-2">
|
||||
|
||||
@@ -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, передавая имя действия и аргументы
|
||||
|
||||
Reference in New Issue
Block a user