mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 00:51:53 +08:00
Add support for PiKVM Switch and related features
This commit introduces several new components and improvements: - Added Switch module with firmware update and configuration support - Implemented new media streaming capabilities - Updated various UI elements and CSS styles - Enhanced keyboard and mouse event handling - Added new validators and configuration options - Updated Python version support to 3.13 - Improved error handling and logging
This commit is contained in:
@@ -37,6 +37,7 @@ from ...validators.basic import valid_string_list
|
||||
from ...validators.hid import valid_hid_key
|
||||
from ...validators.hid import valid_hid_mouse_move
|
||||
|
||||
from ...keyboard.mappings import WebModifiers
|
||||
from ...mouse import MouseRange
|
||||
|
||||
from .. import BasePlugin
|
||||
@@ -64,11 +65,13 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
|
||||
self.__mouse_x_range = (mouse_x_min, mouse_x_max)
|
||||
self.__mouse_y_range = (mouse_y_min, mouse_y_max)
|
||||
|
||||
self.__jiggler_enabled = jiggler_enabled
|
||||
self.__jiggler_active = jiggler_active
|
||||
self.__jiggler_interval = jiggler_interval
|
||||
self.__jiggler_absolute = True
|
||||
self.__activity_ts = 0
|
||||
self.__j_enabled = jiggler_enabled
|
||||
self.__j_active = jiggler_active
|
||||
self.__j_interval = jiggler_interval
|
||||
self.__j_absolute = True
|
||||
self.__j_activity_ts = 0
|
||||
self.__j_last_x = 0
|
||||
self.__j_last_y = 0
|
||||
|
||||
@classmethod
|
||||
def _get_base_options(cls) -> dict[str, Any]:
|
||||
@@ -83,7 +86,7 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
|
||||
"max": Option(MouseRange.MAX, type=valid_hid_mouse_move, unpack_as="mouse_y_max"),
|
||||
},
|
||||
"jiggler": {
|
||||
"enabled": Option(False, type=valid_bool, unpack_as="jiggler_enabled"),
|
||||
"enabled": Option(True, type=valid_bool, unpack_as="jiggler_enabled"),
|
||||
"active": Option(False, type=valid_bool, unpack_as="jiggler_active"),
|
||||
"interval": Option(60, type=valid_int_f1, unpack_as="jiggler_interval"),
|
||||
},
|
||||
@@ -137,13 +140,25 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
|
||||
|
||||
# =====
|
||||
|
||||
def send_key_events(self, keys: Iterable[tuple[str, bool]], no_ignore_keys: bool=False) -> None:
|
||||
async def send_key_events(
|
||||
self,
|
||||
keys: Iterable[tuple[str, bool]],
|
||||
no_ignore_keys: bool=False,
|
||||
slow: bool=False,
|
||||
) -> None:
|
||||
|
||||
for (key, state) in keys:
|
||||
if no_ignore_keys or key not in self.__ignore_keys:
|
||||
self.send_key_event(key, state)
|
||||
if slow:
|
||||
await asyncio.sleep(0.02)
|
||||
self.send_key_event(key, state, False)
|
||||
|
||||
def send_key_event(self, key: str, state: bool) -> None:
|
||||
def send_key_event(self, key: str, state: bool, finish: bool) -> None:
|
||||
self._send_key_event(key, state)
|
||||
if state and finish and (key not in WebModifiers.ALL and key != "PrintScreen"):
|
||||
# Считаем что PrintScreen это модификатор для Alt+SysRq+...
|
||||
# По-хорошему надо учитывать факт нажатия на Alt, но можно и забить.
|
||||
self._send_key_event(key, False)
|
||||
self.__bump_activity()
|
||||
|
||||
def _send_key_event(self, key: str, state: bool) -> None:
|
||||
@@ -161,6 +176,8 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
|
||||
# =====
|
||||
|
||||
def send_mouse_move_event(self, to_x: int, to_y: int) -> None:
|
||||
self.__j_last_x = to_x
|
||||
self.__j_last_y = to_y
|
||||
if self.__mouse_x_range != MouseRange.RANGE:
|
||||
to_x = MouseRange.remap(to_x, *self.__mouse_x_range)
|
||||
if self.__mouse_y_range != MouseRange.RANGE:
|
||||
@@ -229,37 +246,38 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
|
||||
handler(*xy)
|
||||
|
||||
def __bump_activity(self) -> None:
|
||||
self.__activity_ts = int(time.monotonic())
|
||||
self.__j_activity_ts = int(time.monotonic())
|
||||
|
||||
def _set_jiggler_absolute(self, absolute: bool) -> None:
|
||||
self.__jiggler_absolute = absolute
|
||||
self.__j_absolute = absolute
|
||||
|
||||
def _set_jiggler_active(self, active: bool) -> None:
|
||||
if self.__jiggler_enabled:
|
||||
self.__jiggler_active = active
|
||||
if self.__j_enabled:
|
||||
self.__j_active = active
|
||||
|
||||
def _get_jiggler_state(self) -> dict:
|
||||
return {
|
||||
"jiggler": {
|
||||
"enabled": self.__jiggler_enabled,
|
||||
"active": self.__jiggler_active,
|
||||
"interval": self.__jiggler_interval,
|
||||
"enabled": self.__j_enabled,
|
||||
"active": self.__j_active,
|
||||
"interval": self.__j_interval,
|
||||
},
|
||||
}
|
||||
|
||||
# =====
|
||||
|
||||
async def systask(self) -> None:
|
||||
factor = 1
|
||||
while True:
|
||||
if self.__jiggler_active and (self.__activity_ts + self.__jiggler_interval < int(time.monotonic())):
|
||||
for _ in range(5):
|
||||
if self.__jiggler_absolute:
|
||||
self.send_mouse_move_event(100 * factor, 100 * factor)
|
||||
else:
|
||||
self.send_mouse_relative_event(10 * factor, 10 * factor)
|
||||
factor *= -1
|
||||
await asyncio.sleep(0.1)
|
||||
if self.__j_active and (self.__j_activity_ts + self.__j_interval < int(time.monotonic())):
|
||||
if self.__j_absolute:
|
||||
(x, y) = (self.__j_last_x, self.__j_last_y)
|
||||
for move in [100, -100, 100, -100, 0]:
|
||||
self.send_mouse_move_event(MouseRange.normalize(x + move), MouseRange.normalize(y + move))
|
||||
await asyncio.sleep(0.1)
|
||||
else:
|
||||
for move in [10, -10, 10, -10]:
|
||||
self.send_mouse_relative_event(move, move)
|
||||
await asyncio.sleep(0.1)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import struct
|
||||
from ....keyboard.mappings import KEYMAP
|
||||
|
||||
from ....mouse import MouseRange
|
||||
from ....mouse import MouseDelta
|
||||
|
||||
from .... import tools
|
||||
from .... import bitbang
|
||||
@@ -162,8 +163,8 @@ class MouseRelativeEvent(BaseEvent):
|
||||
delta_y: int
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert -127 <= self.delta_x <= 127
|
||||
assert -127 <= self.delta_y <= 127
|
||||
assert MouseDelta.MIN <= self.delta_x <= MouseDelta.MAX
|
||||
assert MouseDelta.MIN <= self.delta_y <= MouseDelta.MAX
|
||||
|
||||
def make_request(self) -> bytes:
|
||||
return _make_request(struct.pack(">Bbbxx", 0x15, self.delta_x, self.delta_y))
|
||||
@@ -175,8 +176,8 @@ class MouseWheelEvent(BaseEvent):
|
||||
delta_y: int
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert -127 <= self.delta_x <= 127
|
||||
assert -127 <= self.delta_y <= 127
|
||||
assert MouseDelta.MIN <= self.delta_x <= MouseDelta.MAX
|
||||
assert MouseDelta.MIN <= self.delta_y <= MouseDelta.MAX
|
||||
|
||||
def make_request(self) -> bytes:
|
||||
# Горизонтальная прокрутка пока не поддерживается
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
import math
|
||||
|
||||
from ....mouse import MouseRange
|
||||
from ....mouse import MouseDelta
|
||||
|
||||
|
||||
# =====
|
||||
@@ -79,7 +80,7 @@ class Mouse: # pylint: disable=too-many-instance-attributes
|
||||
|
||||
def process_wheel(self, delta_x: int, delta_y: int) -> bytes:
|
||||
_ = delta_x
|
||||
assert -127 <= delta_y <= 127
|
||||
assert MouseDelta.MIN <= delta_y <= MouseDelta.MAX
|
||||
self.__wheel_y = (1 if delta_y > 0 else 255)
|
||||
if not self.__absolute:
|
||||
return self.__make_relative_cmd()
|
||||
@@ -110,6 +111,6 @@ class Mouse: # pylint: disable=too-many-instance-attributes
|
||||
])
|
||||
|
||||
def __fix_relative(self, value: int) -> int:
|
||||
assert -127 <= value <= 127
|
||||
assert MouseDelta.MIN <= value <= MouseDelta.MAX
|
||||
value = math.ceil(value / 3)
|
||||
return (value if value >= 0 else (255 + value))
|
||||
|
||||
@@ -27,6 +27,7 @@ from ....keyboard.mappings import UsbKey
|
||||
from ....keyboard.mappings import KEYMAP
|
||||
|
||||
from ....mouse import MouseRange
|
||||
from ....mouse import MouseDelta
|
||||
|
||||
|
||||
# =====
|
||||
@@ -144,8 +145,8 @@ class MouseRelativeEvent(BaseEvent):
|
||||
delta_y: int
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert -127 <= self.delta_x <= 127
|
||||
assert -127 <= self.delta_y <= 127
|
||||
assert MouseDelta.MIN <= self.delta_x <= MouseDelta.MAX
|
||||
assert MouseDelta.MIN <= self.delta_y <= MouseDelta.MAX
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
@@ -154,8 +155,8 @@ class MouseWheelEvent(BaseEvent):
|
||||
delta_y: int
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert -127 <= self.delta_x <= 127
|
||||
assert -127 <= self.delta_y <= 127
|
||||
assert MouseDelta.MIN <= self.delta_x <= MouseDelta.MAX
|
||||
assert MouseDelta.MIN <= self.delta_y <= MouseDelta.MAX
|
||||
|
||||
|
||||
def make_mouse_report(
|
||||
|
||||
@@ -153,7 +153,6 @@ class MouseProcess(BaseDeviceProcess):
|
||||
move_x = self.__x
|
||||
move_y = self.__y
|
||||
else:
|
||||
assert self.__x == self.__y == 0
|
||||
if relative_event is not None:
|
||||
move_x = relative_event.delta_x
|
||||
move_y = relative_event.delta_y
|
||||
@@ -177,5 +176,3 @@ class MouseProcess(BaseDeviceProcess):
|
||||
|
||||
def __clear_state(self) -> None:
|
||||
self.__pressed_buttons = 0
|
||||
self.__x = 0
|
||||
self.__y = 0
|
||||
|
||||
Reference in New Issue
Block a user