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
|
import ctypes
|
||||||
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
|
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
|
||||||
from ctypes import cast, POINTER
|
from ctypes import cast, POINTER
|
||||||
|
from comtypes import CLSCTX_ALL
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
from winrt.windows.media.control import GlobalSystemMediaTransportControlsSessionManager as MediaManager
|
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:
|
if parent_dir not in sys.path:
|
||||||
sys.path.append(parent_dir)
|
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]:
|
async def set_volume(args: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
global VOLUME
|
||||||
"""
|
"""
|
||||||
Sets the Windows system volume using the Core Audio API.
|
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()
|
comtypes.CoInitialize()
|
||||||
|
|
||||||
# Get volume level from args
|
# 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
|
# Validate input level
|
||||||
if not isinstance(level, (int, float)) or not 0.0 <= level <= 1.0:
|
if not isinstance(level, (int, float)) or not 0 <= level <= 100:
|
||||||
raise ValueError("Volume level must be a float between 0.0 and 1.0")
|
raise ValueError("Volume level must be a float between 0 and 100")
|
||||||
|
VOLUME = level
|
||||||
|
level = level/100
|
||||||
# Get audio endpoint interface
|
# Get audio endpoint interface
|
||||||
devices = AudioUtilities.GetSpeakers()
|
devices = AudioUtilities.GetSpeakers()
|
||||||
interface = devices.Activate(IAudioEndpointVolume._iid_, comtypes.CLSCTX_ALL, None)
|
interface = devices.Activate(IAudioEndpointVolume._iid_, comtypes.CLSCTX_ALL, None)
|
||||||
@@ -106,4 +123,16 @@ async def prev(args):
|
|||||||
# назад
|
# назад
|
||||||
await current_session.try_skip_previous_async()
|
await current_session.try_skip_previous_async()
|
||||||
else:
|
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
|
import monitorcontrol
|
||||||
|
|
||||||
|
BRIGHTNESS = 10
|
||||||
|
|
||||||
def get_monitors():
|
def get_monitors():
|
||||||
"""Retrieve a list of connected monitors."""
|
"""Retrieve a list of connected monitors."""
|
||||||
return monitorcontrol.get_monitors()
|
return monitorcontrol.get_monitors()
|
||||||
@@ -13,9 +15,16 @@ def set_brightness(monitor, brightness):
|
|||||||
print(f"Set brightness to {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:
|
try:
|
||||||
level = args.get('level', 10)
|
level = args.get('level', 10)
|
||||||
|
BRIGHTNESS = level
|
||||||
monitors = get_monitors()
|
monitors = get_monitors()
|
||||||
if not monitors:
|
if not monitors:
|
||||||
raise OSError(f"No DDC/CI compatible monitors found.")
|
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:
|
async with self.session.post(url, headers=headers, json=data) as response:
|
||||||
if response.status == 200:
|
if response.status == 200:
|
||||||
print("Прокси успешно включен")
|
print("Прокси успешно включен")
|
||||||
V2RAY_VPN_ENABLED = True
|
|
||||||
await show_notification({"title":"✅ V2Ray proxy","message":"Прокси успешно включен"})
|
await show_notification({"title":"✅ V2Ray proxy","message":"Прокси успешно включен"})
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -75,7 +74,7 @@ class V2rayAController:
|
|||||||
|
|
||||||
async def disable_proxy(self):
|
async def disable_proxy(self):
|
||||||
"""Выключение прокси"""
|
"""Выключение прокси"""
|
||||||
global V2RAY_VPN_ENABLED
|
|
||||||
if not self.token:
|
if not self.token:
|
||||||
print("Не выполнен вход. Пожалуйста, сначала выполните аутентификацию")
|
print("Не выполнен вход. Пожалуйста, сначала выполните аутентификацию")
|
||||||
return False
|
return False
|
||||||
@@ -88,7 +87,6 @@ class V2rayAController:
|
|||||||
async with self.session.delete(url, headers=headers, json=data) as response:
|
async with self.session.delete(url, headers=headers, json=data) as response:
|
||||||
if response.status == 200:
|
if response.status == 200:
|
||||||
print("Прокси успешно выключен")
|
print("Прокси успешно выключен")
|
||||||
V2RAY_VPN_ENABLED = False
|
|
||||||
await show_notification({"title":"🔴 V2Ray proxy","message":"Прокси успешно выключен"})
|
await show_notification({"title":"🔴 V2Ray proxy","message":"Прокси успешно выключен"})
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -105,6 +103,7 @@ class V2rayAController:
|
|||||||
await self.session.close()
|
await self.session.close()
|
||||||
|
|
||||||
async def enable_vpn(args):
|
async def enable_vpn(args):
|
||||||
|
global V2RAY_VPN_ENABLED
|
||||||
# Инициализация контроллера
|
# Инициализация контроллера
|
||||||
config = await read_config("v2ray.yaml")
|
config = await read_config("v2ray.yaml")
|
||||||
if config.get("username") and config.get("password"):
|
if config.get("username") and config.get("password"):
|
||||||
@@ -119,9 +118,11 @@ async def enable_vpn(args):
|
|||||||
|
|
||||||
# Закрытие сессии
|
# Закрытие сессии
|
||||||
await controller.close()
|
await controller.close()
|
||||||
|
V2RAY_VPN_ENABLED = True
|
||||||
else:
|
else:
|
||||||
raise OSError("Config unset")
|
raise OSError("Config unset")
|
||||||
async def disable_vpn(args):
|
async def disable_vpn(args):
|
||||||
|
global V2RAY_VPN_ENABLED
|
||||||
# Инициализация контроллера
|
# Инициализация контроллера
|
||||||
config = await read_config("v2ray.yaml")
|
config = await read_config("v2ray.yaml")
|
||||||
if config.get("username") and config.get("password"):
|
if config.get("username") and config.get("password"):
|
||||||
@@ -136,6 +137,7 @@ async def disable_vpn(args):
|
|||||||
|
|
||||||
# Закрытие сессии
|
# Закрытие сессии
|
||||||
await controller.close()
|
await controller.close()
|
||||||
|
V2RAY_VPN_ENABLED = False
|
||||||
else:
|
else:
|
||||||
raise OSError("Config unset")
|
raise OSError("Config unset")
|
||||||
|
|
||||||
@@ -143,5 +145,5 @@ async def disable_vpn(args):
|
|||||||
async def is_vpn_enabled(args):
|
async def is_vpn_enabled(args):
|
||||||
global V2RAY_VPN_ENABLED
|
global V2RAY_VPN_ENABLED
|
||||||
return {
|
return {
|
||||||
"vpn_enabled": V2RAY_VPN_ENABLED
|
"value": V2RAY_VPN_ENABLED
|
||||||
}
|
}
|
||||||
@@ -24,21 +24,37 @@
|
|||||||
sliders:
|
sliders:
|
||||||
- value: 0
|
- value: 0
|
||||||
label: "Яркость экрана"
|
label: "Яркость экрана"
|
||||||
action: "screen.chenge_brightness"
|
action: "screen.change_brightness"
|
||||||
|
get_state: "screen.get_brightness"
|
||||||
- value: 0
|
- value: 0
|
||||||
label: "Громкость звука"
|
label: "Громкость звука"
|
||||||
action: "media.set_volume"
|
action: "media.set_volume"
|
||||||
|
get_state: "media.get_volume"
|
||||||
mini_icon: "/icons/mini/settings.png"
|
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" # Разделитель
|
- type: "break" # Разделитель
|
||||||
label: "Сайты"
|
label: "Сайты"
|
||||||
|
|
||||||
- type: "image" # Кнопка с картинкой
|
- type: "image" # Кнопка с картинкой
|
||||||
imageUrl: "/icons/telegram-logo-svgrepo-com.svg"
|
imageUrl: "/icons/yandex.svg"
|
||||||
label: "Telegram"
|
label: "Яндекс"
|
||||||
action: "web.open_url"
|
action: "web.open_url"
|
||||||
action_args:
|
action_args:
|
||||||
url: "https://ya.ru "
|
url: "https://ya.ru"
|
||||||
mini_icon: "/icons/mini/arrow-up-right-from-square.png"
|
mini_icon: "/icons/mini/arrow-up-right-from-square.png"
|
||||||
|
|
||||||
- type: "image" # Кнопка с картинкой
|
- type: "image" # Кнопка с картинкой
|
||||||
@@ -46,12 +62,12 @@
|
|||||||
label: "Cloud"
|
label: "Cloud"
|
||||||
action: "web.open_url"
|
action: "web.open_url"
|
||||||
action_args:
|
action_args:
|
||||||
url: "https://cloud.viadev.su "
|
url: "https://cloud.viadev.su"
|
||||||
mini_icon: "/icons/mini/arrow-up-right-from-square.png"
|
mini_icon: "/icons/mini/arrow-up-right-from-square.png"
|
||||||
|
|
||||||
- type: "break" # Разделитель
|
- type: "break" # Разделитель
|
||||||
label: "Приложения"
|
label: "Приложения"
|
||||||
|
|
||||||
- type: "image" # Кнопка с картинкой
|
- type: "image" # Кнопка с картинкой
|
||||||
imageUrl: "/icons/steam-svgrepo-com.svg"
|
imageUrl: "/icons/steam-svgrepo-com.svg"
|
||||||
label: "Steam"
|
label: "Steam"
|
||||||
@@ -60,6 +76,13 @@
|
|||||||
path: "C:/Users/Slava/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Steam/Steam.lnk"
|
path: "C:/Users/Slava/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Steam/Steam.lnk"
|
||||||
mini_icon: "/icons/mini/arrow-up-right-from-square.png"
|
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" # Разделитель
|
- type: "break" # Разделитель
|
||||||
label: "Управление ПК"
|
label: "Управление ПК"
|
||||||
@@ -67,5 +90,8 @@
|
|||||||
- type: "image" # Кнопка с картинкой
|
- type: "image" # Кнопка с картинкой
|
||||||
imageUrl: "/icons/vpn.png"
|
imageUrl: "/icons/vpn.png"
|
||||||
label: "VPN"
|
label: "VPN"
|
||||||
|
is_swtch: True
|
||||||
|
get_state: "v2ray.is_vpn_enabled"
|
||||||
action: "v2ray.enable_vpn"
|
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) => {
|
export const sendRequest = async (action,args) => {
|
||||||
try {
|
try {
|
||||||
const TS = Math.round(Date.now()/1000);
|
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}, {
|
const response = await axios.post("http://"+window.location.hostname+":"+import.meta.env.VITE_API_PORT+"/action/"+action, {args:args,hash:hash+"."+TS}, {
|
||||||
timeout: 4000,
|
timeout: 4000,
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -97,4 +97,19 @@ input[type="range"] {
|
|||||||
.gauge-tile-content {
|
.gauge-tile-content {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: 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 }) => (
|
const ImageTile = ({ imageUrl, label, enabled, toggleEnabled }) => (
|
||||||
<div className="tile-content cursor-pointer">
|
<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>
|
<span className="text-purple-400 text-lg font-semibold mt-2">{label}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -62,7 +62,7 @@ const GaugeTile = ({ gauges }) => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const SliderTile = ({ sliders, tileId, updateSliderValue, label }) => {
|
const SliderTile = ({ sliders, tile, updateSliderValue, label }) => {
|
||||||
return (
|
return (
|
||||||
<div className="tile-content">
|
<div className="tile-content">
|
||||||
<span className="text-purple-400 text-lg font-semibold">{label}</span>
|
<span className="text-purple-400 text-lg font-semibold">{label}</span>
|
||||||
@@ -78,8 +78,10 @@ const SliderTile = ({ sliders, tileId, updateSliderValue, label }) => {
|
|||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="100"
|
||||||
value={s.value}
|
value={s.value}
|
||||||
onChange={(e) => updateSliderValue(tileId, index, e.target.value)}
|
onChange={(e) => updateSliderValue(tile, index, e.target.value)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
onMouseUp={(e) => { sendSlider(tile, index, e.target.value) }}
|
||||||
|
onTouchEnd={(e) => { sendSlider(tile, index, e.target.value) }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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 }) => (
|
const NumberTile = ({ value, label }) => (
|
||||||
<div className="tile-content">
|
<div className="tile-content">
|
||||||
<span className="text-5xl font-bold text-purple-200">{value}</span>
|
<span className="text-5xl font-bold text-purple-200">{value}</span>
|
||||||
<span className="text-purple-400 text-lg font-semibold mt-2">{label}</span>
|
<span className="text-purple-400 text-lg font-semibold mt-2">{label}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
const sendSlider = async (tile, sliderIndex, newValue) => {
|
||||||
|
try {
|
||||||
|
await sendRequest(tile.sliders[sliderIndex].action, { level: Number(newValue) });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
|
||||||
@@ -109,14 +137,17 @@ const App = () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const updateSliderValue = (tileId, sliderIndex, newValue) => {
|
|
||||||
|
|
||||||
|
const updateSliderValue = (Tile, sliderIndex, newValue) => {
|
||||||
setTiles(prevTiles =>
|
setTiles(prevTiles =>
|
||||||
prevTiles.map(tile => {
|
prevTiles.map(tile => {
|
||||||
if (tile.id === tileId && tile.type === "slider") {
|
if (tile.id === Tile.id && tile.type === "slider") {
|
||||||
const newSliders = [...tile.sliders];
|
const newSliders = [...tile.sliders];
|
||||||
newSliders[sliderIndex] = {
|
newSliders[sliderIndex] = {
|
||||||
...newSliders[sliderIndex],
|
...newSliders[sliderIndex],
|
||||||
value: parseInt(newValue)
|
value: parseInt(newValue)
|
||||||
|
|
||||||
};
|
};
|
||||||
return { ...tile, sliders: newSliders };
|
return { ...tile, sliders: newSliders };
|
||||||
}
|
}
|
||||||
@@ -177,8 +208,37 @@ const App = () => {
|
|||||||
'Content-Type': 'application/json'
|
'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(() => {
|
setTimeout(() => {
|
||||||
window.localStorage.auth = passw;
|
|
||||||
setTiles(response.data);
|
setTiles(response.data);
|
||||||
setPhase("dash");
|
setPhase("dash");
|
||||||
updateSystemStats();
|
updateSystemStats();
|
||||||
@@ -236,21 +296,26 @@ const App = () => {
|
|||||||
|
|
||||||
if (tile.action) {
|
if (tile.action) {
|
||||||
try {
|
try {
|
||||||
setTileProps(tile.id, { loading: true })
|
if (tile.id) {
|
||||||
|
setTileProps(tile.id, { loading: true })
|
||||||
|
}
|
||||||
await sendRequest(tile.action, tile.action_args);
|
await sendRequest(tile.action, tile.action_args);
|
||||||
|
|
||||||
if (tile.action.includes("enable")) {
|
if (tile.id) {
|
||||||
tile.enabled = true;
|
if (tile.action.includes("enable")) {
|
||||||
tile.action = tile.action.replace("enable", "disable");
|
tile.enabled = true;
|
||||||
} else if (tile.action.includes("disable")) {
|
tile.action = tile.action.replace("enable", "disable");
|
||||||
tile.enabled = false;
|
} else if (tile.action.includes("disable")) {
|
||||||
tile.action = tile.action.replace("disable", "enable");
|
tile.enabled = false;
|
||||||
}
|
tile.action = tile.action.replace("disable", "enable");
|
||||||
tile.loading = false;
|
}
|
||||||
|
tile.loading = false;
|
||||||
|
|
||||||
setTileProps(tile.id, tile)
|
setTileProps(tile.id, tile)
|
||||||
|
}
|
||||||
} catch (e) {
|
} 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="flex w-full flex-col gap-4 anim-pop">
|
||||||
<div className="grid grid-cols-2 lg:grid-cols-6 flex-wrap gap-4">
|
<div className="grid grid-cols-2 lg:grid-cols-6 flex-wrap gap-4">
|
||||||
{tiles.map(tile => (
|
{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" ? (
|
{tile.type === "break" ? (
|
||||||
<div className="w-full mt-2 text-purple-400 text-2xl font-bold">{tile.label}</div>
|
<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 === "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 === "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.type === "number" && <NumberTile value={tile.value} label={tile.label} />}
|
||||||
{tile.mini_icon && (
|
{tile.mini_icon && (
|
||||||
<div className="absolute top-3 right-3 flex gap-2">
|
<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
|
from utils.get_primary_ip import get_primary_ip
|
||||||
|
|
||||||
PASSWORD = "10010055"
|
PASSWORD = "10010055"
|
||||||
|
SKIP_HASH = False
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
@@ -90,28 +90,29 @@ async def handle_action(name: str, payload: CommandModel):
|
|||||||
|
|
||||||
action_args = payload.args
|
action_args = payload.args
|
||||||
hash_with_ts = payload.hash
|
hash_with_ts = payload.hash
|
||||||
if not hash_with_ts:
|
if(not SKIP_HASH):
|
||||||
raise HTTPException(status_code=400, detail="Missing hash")
|
if not hash_with_ts:
|
||||||
|
raise HTTPException(status_code=400, detail="Missing hash")
|
||||||
|
|
||||||
# Split hash and timestamp
|
# Split hash and timestamp
|
||||||
try:
|
try:
|
||||||
received_hash, received_ts = hash_with_ts.split(".")
|
received_hash, received_ts = hash_with_ts.split(".")
|
||||||
received_ts = int(received_ts)
|
received_ts = int(received_ts)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise HTTPException(status_code=400, detail="Invalid hash format")
|
raise HTTPException(status_code=400, detail="Invalid hash format")
|
||||||
|
|
||||||
# Check if token is within 5-second window
|
# Check if token is within 5-second window
|
||||||
current_ts = int(time.time())
|
current_ts = int(time.time())
|
||||||
if abs(current_ts - received_ts) > 5:
|
if abs(current_ts - received_ts) > 5:
|
||||||
raise HTTPException(status_code=401, detail="Token expired")
|
raise HTTPException(status_code=401, detail="Token expired")
|
||||||
|
|
||||||
# Reconstruct hash
|
# Reconstruct hash
|
||||||
data_to_hash = PASSWORD + json.dumps(action_args, separators=(",", ":")) + str(received_ts)
|
data_to_hash = PASSWORD + json.dumps(action_args, separators=(",", ":")) + str(received_ts)
|
||||||
computed_hash = hashlib.sha256(data_to_hash.encode("utf-8")).hexdigest()
|
computed_hash = hashlib.sha256(data_to_hash.encode("utf-8")).hexdigest()
|
||||||
|
|
||||||
# Verify hash
|
# Verify hash
|
||||||
if computed_hash != received_hash:
|
if computed_hash != received_hash:
|
||||||
raise HTTPException(status_code=401, detail="Invalid hash")
|
raise HTTPException(status_code=401, detail="Invalid hash")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Вызываем функцию из callback.py, передавая имя действия и аргументы
|
# Вызываем функцию из callback.py, передавая имя действия и аргументы
|
||||||
|
|||||||
Reference in New Issue
Block a user