mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 00:51:53 +08:00
feat: merge upstream master - version 4.94
Merge upstream PiKVM master branch updates: - Bump version from 4.93 to 4.94 - HID: improved jiggler pattern for better compatibility - Streamer: major refactoring for improved performance and maintainability - Prometheus: tidying GPIO channel name formatting - Web: added __gpio-label class for custom styling - HID: customizable /api/hid/print delay configuration - ATX: independent power/reset regions for better control - OLED: added --fill option for display testing - Web: improved keyboard handling in modal dialogs - Web: enhanced login error messages - Switch: added heartbeat functionality - Web: mouse touch code simplification and refactoring - Configs: use systemd-networkd-wait-online --any by default - PKGBUILD: use cp -r to install systemd units properly - Various bug fixes and performance improvements
This commit is contained in:
@@ -54,7 +54,8 @@ class BaseAtx(BasePlugin):
|
||||
async def poll_state(self) -> AsyncGenerator[dict, None]:
|
||||
# ==== Granularity table ====
|
||||
# - enabled -- Full
|
||||
# - busy -- Partial
|
||||
# - busy -- Partial, follows with acts
|
||||
# - acts -- Partial, follows with busy
|
||||
# - leds -- Partial
|
||||
# ===========================
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ class Plugin(BaseAtx):
|
||||
return {
|
||||
"enabled": False,
|
||||
"busy": False,
|
||||
"acts": {
|
||||
"power": False,
|
||||
"reset": False,
|
||||
},
|
||||
"leds": {
|
||||
"power": False,
|
||||
"hdd": False,
|
||||
|
||||
@@ -75,7 +75,8 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
|
||||
self.__long_click_delay = long_click_delay
|
||||
|
||||
self.__notifier = aiotools.AioNotifier()
|
||||
self.__region = aiotools.AioExclusiveRegion(AtxIsBusyError, self.__notifier)
|
||||
self.__power_region = aiotools.AioExclusiveRegion(AtxIsBusyError, self.__notifier)
|
||||
self.__reset_region = aiotools.AioExclusiveRegion(AtxIsBusyError, self.__notifier)
|
||||
|
||||
self.__line_req: (gpiod.LineRequest | None) = None
|
||||
|
||||
@@ -122,9 +123,15 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
|
||||
)
|
||||
|
||||
async def get_state(self) -> dict:
|
||||
power_busy = self.__power_region.is_busy()
|
||||
reset_busy = self.__reset_region.is_busy()
|
||||
return {
|
||||
"enabled": True,
|
||||
"busy": self.__region.is_busy(),
|
||||
"busy": (power_busy or reset_busy),
|
||||
"acts": {
|
||||
"power": power_busy,
|
||||
"reset": reset_busy,
|
||||
},
|
||||
"leds": {
|
||||
"power": self.__reader.get(self.__power_led_pin),
|
||||
"hdd": self.__reader.get(self.__hdd_led_pin),
|
||||
@@ -175,13 +182,13 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
|
||||
# =====
|
||||
|
||||
async def click_power(self, wait: bool) -> None:
|
||||
await self.__click("power", self.__power_switch_pin, self.__click_delay, wait)
|
||||
await self.__click("power", self.__power_region, self.__power_switch_pin, self.__click_delay, wait)
|
||||
|
||||
async def click_power_long(self, wait: bool) -> None:
|
||||
await self.__click("power_long", self.__power_switch_pin, self.__long_click_delay, wait)
|
||||
await self.__click("power_long", self.__power_region, self.__power_switch_pin, self.__long_click_delay, wait)
|
||||
|
||||
async def click_reset(self, wait: bool) -> None:
|
||||
await self.__click("reset", self.__reset_switch_pin, self.__click_delay, wait)
|
||||
await self.__click("reset", self.__reset_region, self.__reset_switch_pin, self.__click_delay, wait)
|
||||
|
||||
# =====
|
||||
|
||||
@@ -189,14 +196,14 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
|
||||
return (await self.get_state())["leds"]["power"]
|
||||
|
||||
@aiotools.atomic_fg
|
||||
async def __click(self, name: str, pin: int, delay: float, wait: bool) -> None:
|
||||
async def __click(self, name: str, region: aiotools.AioExclusiveRegion, pin: int, delay: float, wait: bool) -> None:
|
||||
if wait:
|
||||
with self.__region:
|
||||
with region:
|
||||
await self.__inner_click(name, pin, delay)
|
||||
else:
|
||||
await aiotools.run_region_task(
|
||||
f"Can't perform ATX {name} click or operation was not completed",
|
||||
self.__region, self.__inner_click, name, pin, delay,
|
||||
region, self.__inner_click, name, pin, delay,
|
||||
)
|
||||
|
||||
@aiotools.atomic_fg
|
||||
|
||||
31
kvmd/plugins/auth/forbidden.py
Normal file
31
kvmd/plugins/auth/forbidden.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# ========================================================================== #
|
||||
# #
|
||||
# KVMD - The main PiKVM daemon. #
|
||||
# #
|
||||
# Copyright (C) 2018-2024 Maxim Devaev <mdevaev@gmail.com> #
|
||||
# #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# ========================================================================== #
|
||||
|
||||
|
||||
from . import BaseAuthService
|
||||
|
||||
|
||||
# =====
|
||||
class Plugin(BaseAuthService):
|
||||
async def authorize(self, user: str, passwd: str) -> bool:
|
||||
_ = user
|
||||
_ = passwd
|
||||
return False
|
||||
@@ -20,12 +20,12 @@
|
||||
# ========================================================================== #
|
||||
|
||||
|
||||
import passlib.apache
|
||||
|
||||
from ...yamlconf import Option
|
||||
|
||||
from ...validators.os import valid_abs_file
|
||||
|
||||
from ...crypto import KvmdHtpasswdFile
|
||||
|
||||
from . import BaseAuthService
|
||||
|
||||
|
||||
@@ -43,5 +43,5 @@ class Plugin(BaseAuthService):
|
||||
async def authorize(self, user: str, passwd: str) -> bool:
|
||||
assert user == user.strip()
|
||||
assert user
|
||||
htpasswd = passlib.apache.HtpasswdFile(self.__path)
|
||||
htpasswd = KvmdHtpasswdFile(self.__path)
|
||||
return htpasswd.check_password(user, passwd)
|
||||
|
||||
@@ -29,6 +29,8 @@ from typing import Callable
|
||||
from typing import AsyncGenerator
|
||||
from typing import Any
|
||||
|
||||
from evdev import ecodes
|
||||
|
||||
from ...yamlconf import Option
|
||||
|
||||
from ...validators.basic import valid_bool
|
||||
@@ -37,7 +39,8 @@ 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 ...keyboard.mappings import WEB_TO_EVDEV
|
||||
from ...keyboard.mappings import EvdevModifiers
|
||||
from ...mouse import MouseRange
|
||||
|
||||
from .. import BasePlugin
|
||||
@@ -60,7 +63,7 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
|
||||
jiggler_interval: int,
|
||||
) -> None:
|
||||
|
||||
self.__ignore_keys = ignore_keys
|
||||
self.__ignore_keys = [WEB_TO_EVDEV[key] for key in ignore_keys]
|
||||
|
||||
self.__mouse_x_range = (mouse_x_min, mouse_x_max)
|
||||
self.__mouse_y_range = (mouse_y_min, mouse_y_max)
|
||||
@@ -69,7 +72,7 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
|
||||
self.__j_active = jiggler_active
|
||||
self.__j_interval = jiggler_interval
|
||||
self.__j_absolute = True
|
||||
self.__j_activity_ts = 0
|
||||
self.__j_activity_ts = self.__get_monotonic_seconds()
|
||||
self.__j_last_x = 0
|
||||
self.__j_last_y = 0
|
||||
|
||||
@@ -140,37 +143,42 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
|
||||
|
||||
# =====
|
||||
|
||||
def get_inactivity_seconds(self) -> int:
|
||||
return (self.__get_monotonic_seconds() - self.__j_activity_ts)
|
||||
|
||||
# =====
|
||||
|
||||
async def send_key_events(
|
||||
self,
|
||||
keys: Iterable[tuple[str, bool]],
|
||||
keys: Iterable[tuple[int, bool]],
|
||||
no_ignore_keys: bool=False,
|
||||
slow: bool=False,
|
||||
delay: float=0.0,
|
||||
) -> None:
|
||||
|
||||
for (key, state) in keys:
|
||||
if no_ignore_keys or key not in self.__ignore_keys:
|
||||
if slow:
|
||||
await asyncio.sleep(0.02)
|
||||
if delay > 0:
|
||||
await asyncio.sleep(delay)
|
||||
self.send_key_event(key, state, False)
|
||||
|
||||
def send_key_event(self, key: str, state: bool, finish: bool) -> None:
|
||||
def send_key_event(self, key: int, state: bool, finish: bool) -> None:
|
||||
self._send_key_event(key, state)
|
||||
if state and finish and (key not in WebModifiers.ALL and key != "PrintScreen"):
|
||||
if state and finish and (key not in EvdevModifiers.ALL and key != ecodes.KEY_SYSRQ):
|
||||
# Считаем что PrintScreen это модификатор для Alt+SysRq+...
|
||||
# По-хорошему надо учитывать факт нажатия на Alt, но можно и забить.
|
||||
self._send_key_event(key, False)
|
||||
self.__bump_activity()
|
||||
|
||||
def _send_key_event(self, key: str, state: bool) -> None:
|
||||
def _send_key_event(self, key: int, state: bool) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
# =====
|
||||
|
||||
def send_mouse_button_event(self, button: str, state: bool) -> None:
|
||||
def send_mouse_button_event(self, button: int, state: bool) -> None:
|
||||
self._send_mouse_button_event(button, state)
|
||||
self.__bump_activity()
|
||||
|
||||
def _send_mouse_button_event(self, button: str, state: bool) -> None:
|
||||
def _send_mouse_button_event(self, button: int, state: bool) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
# =====
|
||||
@@ -246,7 +254,10 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
|
||||
handler(*xy)
|
||||
|
||||
def __bump_activity(self) -> None:
|
||||
self.__j_activity_ts = int(time.monotonic())
|
||||
self.__j_activity_ts = self.__get_monotonic_seconds()
|
||||
|
||||
def __get_monotonic_seconds(self) -> int:
|
||||
return int(time.monotonic())
|
||||
|
||||
def _set_jiggler_absolute(self, absolute: bool) -> None:
|
||||
self.__j_absolute = absolute
|
||||
@@ -268,14 +279,14 @@ class BaseHid(BasePlugin): # pylint: disable=too-many-instance-attributes
|
||||
|
||||
async def systask(self) -> None:
|
||||
while True:
|
||||
if self.__j_active and (self.__j_activity_ts + self.__j_interval < int(time.monotonic())):
|
||||
if self.__j_active and (self.__j_activity_ts + self.__j_interval < self.__get_monotonic_seconds()):
|
||||
if self.__j_absolute:
|
||||
(x, y) = (self.__j_last_x, self.__j_last_y)
|
||||
for move in [100, -100, 100, -100, 0]:
|
||||
for move in (([100, -100] * 5) + [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]:
|
||||
for move in ([10, -10] * 5):
|
||||
self.send_mouse_relative_event(move, move)
|
||||
await asyncio.sleep(0.1)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
@@ -285,10 +285,10 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
|
||||
def set_connected(self, connected: bool) -> None:
|
||||
self.__queue_event(SetConnectedEvent(connected), clear=True)
|
||||
|
||||
def _send_key_event(self, key: str, state: bool) -> None:
|
||||
def _send_key_event(self, key: int, state: bool) -> None:
|
||||
self.__queue_event(KeyEvent(key, state))
|
||||
|
||||
def _send_mouse_button_event(self, button: str, state: bool) -> None:
|
||||
def _send_mouse_button_event(self, button: int, state: bool) -> None:
|
||||
self.__queue_event(MouseButtonEvent(button, state))
|
||||
|
||||
def _send_mouse_move_event(self, to_x: int, to_y: int) -> None:
|
||||
|
||||
@@ -68,7 +68,7 @@ class Gpio: # pylint: disable=too-many-instance-attributes
|
||||
self.__line_req = gpiod.request_lines(
|
||||
self.__device_path,
|
||||
consumer="kvmd::hid",
|
||||
config=config,
|
||||
config=config, # type: ignore
|
||||
)
|
||||
|
||||
def __exit__(
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
import dataclasses
|
||||
import struct
|
||||
|
||||
from evdev import ecodes
|
||||
|
||||
from ....keyboard.mappings import KEYMAP
|
||||
|
||||
from ....mouse import MouseRange
|
||||
@@ -106,33 +108,36 @@ class ClearEvent(BaseEvent):
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class KeyEvent(BaseEvent):
|
||||
name: str
|
||||
code: int
|
||||
state: bool
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert self.name in KEYMAP
|
||||
assert self.code in KEYMAP
|
||||
|
||||
def make_request(self) -> bytes:
|
||||
code = KEYMAP[self.name].mcu.code
|
||||
code = KEYMAP[self.code].mcu.code
|
||||
return _make_request(struct.pack(">BBBxx", 0x11, code, int(self.state)))
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class MouseButtonEvent(BaseEvent):
|
||||
name: str
|
||||
code: int
|
||||
state: bool
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert self.name in ["left", "right", "middle", "up", "down"]
|
||||
assert self.code in [
|
||||
ecodes.BTN_LEFT, ecodes.BTN_RIGHT, ecodes.BTN_MIDDLE,
|
||||
ecodes.BTN_BACK, ecodes.BTN_FORWARD,
|
||||
]
|
||||
|
||||
def make_request(self) -> bytes:
|
||||
(code, state_pressed, is_main) = {
|
||||
"left": (0b10000000, 0b00001000, True),
|
||||
"right": (0b01000000, 0b00000100, True),
|
||||
"middle": (0b00100000, 0b00000010, True),
|
||||
"up": (0b10000000, 0b00001000, False), # Back
|
||||
"down": (0b01000000, 0b00000100, False), # Forward
|
||||
}[self.name]
|
||||
ecodes.BTN_LEFT: (0b10000000, 0b00001000, True),
|
||||
ecodes.BTN_RIGHT: (0b01000000, 0b00000100, True),
|
||||
ecodes.BTN_MIDDLE: (0b00100000, 0b00000010, True),
|
||||
ecodes.BTN_BACK: (0b10000000, 0b00001000, False), # Up
|
||||
ecodes.BTN_FORWARD: (0b01000000, 0b00000100, False), # Down
|
||||
}[self.code]
|
||||
if self.state:
|
||||
code |= state_pressed
|
||||
if is_main:
|
||||
|
||||
@@ -203,10 +203,10 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
|
||||
self._set_jiggler_active(jiggler)
|
||||
self.__notifier.notify()
|
||||
|
||||
def _send_key_event(self, key: str, state: bool) -> None:
|
||||
def _send_key_event(self, key: int, state: bool) -> None:
|
||||
self.__server.queue_event(make_keyboard_event(key, state))
|
||||
|
||||
def _send_mouse_button_event(self, button: str, state: bool) -> None:
|
||||
def _send_mouse_button_event(self, button: int, state: bool) -> None:
|
||||
self.__server.queue_event(MouseButtonEvent(button, state))
|
||||
|
||||
def _send_mouse_relative_event(self, delta_x: int, delta_y: int) -> None:
|
||||
|
||||
@@ -168,10 +168,10 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst
|
||||
self._set_jiggler_active(jiggler)
|
||||
self.__notifier.notify()
|
||||
|
||||
def _send_key_event(self, key: str, state: bool) -> None:
|
||||
def _send_key_event(self, key: int, state: bool) -> None:
|
||||
self.__queue_cmd(self.__keyboard.process_key(key, state))
|
||||
|
||||
def _send_mouse_button_event(self, button: str, state: bool) -> None:
|
||||
def _send_mouse_button_event(self, button: int, state: bool) -> None:
|
||||
self.__queue_cmd(self.__mouse.process_button(button, state))
|
||||
|
||||
def _send_mouse_move_event(self, to_x: int, to_y: int) -> None:
|
||||
|
||||
@@ -46,7 +46,7 @@ class Keyboard:
|
||||
async def get_leds(self) -> dict[str, bool]:
|
||||
return (await self.__leds.get())
|
||||
|
||||
def process_key(self, key: str, state: bool) -> bytes:
|
||||
def process_key(self, key: int, state: bool) -> bytes:
|
||||
code = KEYMAP[key].usb.code
|
||||
is_modifier = KEYMAP[key].usb.is_modifier
|
||||
if state:
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
import math
|
||||
|
||||
from evdev import ecodes
|
||||
|
||||
from ....mouse import MouseRange
|
||||
from ....mouse import MouseDelta
|
||||
|
||||
@@ -43,18 +45,18 @@ class Mouse: # pylint: disable=too-many-instance-attributes
|
||||
def is_absolute(self) -> bool:
|
||||
return self.__absolute
|
||||
|
||||
def process_button(self, button: str, state: bool) -> bytes:
|
||||
def process_button(self, button: int, state: bool) -> bytes:
|
||||
code = 0x00
|
||||
match button:
|
||||
case "left":
|
||||
case ecodes.BTN_LEFT:
|
||||
code = 0x01
|
||||
case "right":
|
||||
case ecodes.BTN_RIGHT:
|
||||
code = 0x02
|
||||
case "middle":
|
||||
case ecodes.BTN_MIDDLE:
|
||||
code = 0x04
|
||||
case "up":
|
||||
case ecodes.BTN_BACK:
|
||||
code = 0x08
|
||||
case "down":
|
||||
case ecodes.BTN_FORWARD:
|
||||
code = 0x10
|
||||
if code:
|
||||
if state:
|
||||
|
||||
@@ -110,7 +110,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
|
||||
"horizontal_wheel": Option(True, type=valid_bool),
|
||||
},
|
||||
"mouse_alt": {
|
||||
"device": Option("", type=valid_abs_path, if_empty="", unpack_as="device_path"),
|
||||
"device": Option("/dev/kvmd-hid-mouse-alt", type=valid_abs_path, if_empty="", unpack_as="device_path"),
|
||||
"select_timeout": Option(0.1, type=valid_float_f01),
|
||||
"queue_timeout": Option(0.1, type=valid_float_f01),
|
||||
"write_retries": Option(150, type=valid_int_f1),
|
||||
@@ -206,10 +206,10 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
|
||||
self._set_jiggler_active(jiggler)
|
||||
self.__notifier.notify()
|
||||
|
||||
def _send_key_event(self, key: str, state: bool) -> None:
|
||||
def _send_key_event(self, key: int, state: bool) -> None:
|
||||
self.__keyboard_proc.send_key_event(key, state)
|
||||
|
||||
def _send_mouse_button_event(self, button: str, state: bool) -> None:
|
||||
def _send_mouse_button_event(self, button: int, state: bool) -> None:
|
||||
self.__mouse_current.send_button_event(button, state)
|
||||
|
||||
def _send_mouse_move_event(self, to_x: int, to_y: int) -> None:
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
import struct
|
||||
import dataclasses
|
||||
|
||||
from evdev import ecodes
|
||||
|
||||
from ....keyboard.mappings import UsbKey
|
||||
from ....keyboard.mappings import KEYMAP
|
||||
|
||||
@@ -46,7 +48,7 @@ class ResetEvent(BaseEvent):
|
||||
# =====
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class KeyEvent(BaseEvent):
|
||||
key: UsbKey
|
||||
key: UsbKey
|
||||
state: bool
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
@@ -56,13 +58,13 @@ class KeyEvent(BaseEvent):
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class ModifierEvent(BaseEvent):
|
||||
modifier: UsbKey
|
||||
state: bool
|
||||
state: bool
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert self.modifier.is_modifier
|
||||
|
||||
|
||||
def make_keyboard_event(key: str, state: bool) -> (KeyEvent | ModifierEvent):
|
||||
def make_keyboard_event(key: int, state: bool) -> (KeyEvent | ModifierEvent):
|
||||
usb_key = KEYMAP[key].usb
|
||||
if usb_key.is_modifier:
|
||||
return ModifierEvent(usb_key, state)
|
||||
@@ -102,17 +104,17 @@ def make_keyboard_report(
|
||||
# =====
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class MouseButtonEvent(BaseEvent):
|
||||
button: str
|
||||
state: bool
|
||||
code: int = 0
|
||||
button: int
|
||||
state: bool
|
||||
code: int = 0
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
object.__setattr__(self, "code", {
|
||||
"left": 0x1,
|
||||
"right": 0x2,
|
||||
"middle": 0x4,
|
||||
"up": 0x8, # Back
|
||||
"down": 0x10, # Forward
|
||||
ecodes.BTN_LEFT: 0x1,
|
||||
ecodes.BTN_RIGHT: 0x2,
|
||||
ecodes.BTN_MIDDLE: 0x4,
|
||||
ecodes.BTN_BACK: 0x8, # Back/Up
|
||||
ecodes.BTN_FORWARD: 0x10, # Forward/Down
|
||||
}[self.button])
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class KeyboardProcess(BaseDeviceProcess):
|
||||
self._clear_queue()
|
||||
self._queue_event(ResetEvent())
|
||||
|
||||
def send_key_event(self, key: str, state: bool) -> None:
|
||||
def send_key_event(self, key: int, state: bool) -> None:
|
||||
self._queue_event(make_keyboard_event(key, state))
|
||||
|
||||
# =====
|
||||
|
||||
@@ -85,7 +85,7 @@ class MouseProcess(BaseDeviceProcess):
|
||||
self._clear_queue()
|
||||
self._queue_event(ResetEvent())
|
||||
|
||||
def send_button_event(self, button: str, state: bool) -> None:
|
||||
def send_button_event(self, button: int, state: bool) -> None:
|
||||
self._queue_event(MouseButtonEvent(button, state))
|
||||
|
||||
def send_move_event(self, to_x: int, to_y: int) -> None:
|
||||
|
||||
@@ -153,7 +153,7 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
|
||||
)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def __sw_cs_connected(self) -> Generator[(Callable[[bool], bool] | None), None, None]:
|
||||
def __sw_cs_connected(self) -> Generator[(Callable[[bool], None] | None), None, None]:
|
||||
if self.__sw_cs_pin > 0:
|
||||
with gpiod.request_lines(
|
||||
self.__gpio_device_path,
|
||||
|
||||
@@ -108,7 +108,7 @@ class Plugin(BaseUserGpioDriver):
|
||||
|
||||
async def write(self, pin: str, state: bool) -> None:
|
||||
async with self.__lock:
|
||||
if self.read(pin) == state:
|
||||
if (await self.read(pin)) == state:
|
||||
return
|
||||
if pin == "udc":
|
||||
if state:
|
||||
|
||||
Reference in New Issue
Block a user