diff options
-rw-r--r-- | kvmd/apps/__init__.py | 26 | ||||
-rw-r--r-- | kvmd/apps/kvmd/__init__.py | 2 | ||||
-rw-r--r-- | kvmd/apps/kvmd/api/wol.py | 46 | ||||
-rw-r--r-- | kvmd/apps/kvmd/server.py | 5 | ||||
-rw-r--r-- | kvmd/plugins/ugpio/wol.py (renamed from kvmd/apps/kvmd/wol.py) | 87 | ||||
-rw-r--r-- | web/kvm/index.html | 2 | ||||
-rw-r--r-- | web/kvm/navbar-system.pug | 4 | ||||
-rw-r--r-- | web/share/js/kvm/gpio.js | 1 | ||||
-rw-r--r-- | web/share/js/kvm/session.js | 3 | ||||
-rw-r--r-- | web/share/js/kvm/wol.js | 70 |
10 files changed, 80 insertions, 166 deletions
diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index d930ed8f..5b07aae5 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -79,7 +79,6 @@ from ..validators.os import valid_options from ..validators.os import valid_command from ..validators.net import valid_ip_or_host -from ..validators.net import valid_ip from ..validators.net import valid_net from ..validators.net import valid_port from ..validators.net import valid_ports_list @@ -183,7 +182,7 @@ def _init_config(config_path: str, override_options: List[str], **load_flags: bo raise SystemExit(f"ConfigError: {err}") -def _patch_raw(raw_config: Dict) -> None: +def _patch_raw(raw_config: Dict) -> None: # pylint: disable=too-many-branches if isinstance(raw_config.get("otg"), dict): for (old, new) in [ ("msd", "msd"), @@ -195,6 +194,23 @@ def _patch_raw(raw_config: Dict) -> None: raw_config["otg"]["devices"] = {} raw_config["otg"]["devices"][new] = raw_config["otg"].pop(old) + if isinstance(raw_config.get("kvmd"), dict) and isinstance(raw_config["kvmd"].get("wol"), dict): + if not isinstance(raw_config["kvmd"].get("gpio"), dict): + raw_config["kvmd"]["gpio"] = {} + for section in ["drivers", "scheme"]: + if not isinstance(raw_config["kvmd"]["gpio"].get(section), dict): + raw_config["kvmd"]["gpio"][section] = {} + raw_config["kvmd"]["gpio"]["drivers"]["__wol__"] = { + "type": "wol", + **raw_config["kvmd"].pop("wol"), + } + raw_config["kvmd"]["gpio"]["scheme"]["__wol__"] = { + "driver": "__wol__", + "pin": 0, + "mode": "output", + "switch": False, + } + if isinstance(raw_config.get("kvmd"), dict) and isinstance(raw_config["kvmd"].get("streamer"), dict): streamer_config = raw_config["kvmd"]["streamer"] @@ -359,12 +375,6 @@ def _get_config_scheme() -> Dict: }, }, - "wol": { - "ip": Option("255.255.255.255", type=functools.partial(valid_ip, v6=False)), - "port": Option(9, type=valid_port), - "mac": Option("", type=_make_ifarg(valid_mac, "")), - }, - "hid": { "type": Option("", type=valid_stripped_string_not_empty), diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py index 678b23e8..b49c0190 100644 --- a/kvmd/apps/kvmd/__init__.py +++ b/kvmd/apps/kvmd/__init__.py @@ -34,7 +34,6 @@ from .. import init from .auth import AuthManager from .info import InfoManager from .logreader import LogReader -from .wol import WakeOnLan from .ugpio import UserGpio from .streamer import Streamer from .snapshoter import Snapshoter @@ -86,7 +85,6 @@ def main(argv: Optional[List[str]]=None) -> None: ), info_manager=InfoManager(global_config), log_reader=LogReader(), - wol=WakeOnLan(**config.wol._unpack()), user_gpio=UserGpio(config.gpio, global_config.otg.udc), hid=hid, diff --git a/kvmd/apps/kvmd/api/wol.py b/kvmd/apps/kvmd/api/wol.py deleted file mode 100644 index ded6f6bf..00000000 --- a/kvmd/apps/kvmd/api/wol.py +++ /dev/null @@ -1,46 +0,0 @@ -# ========================================================================== # -# # -# KVMD - The main Pi-KVM daemon. # -# # -# Copyright (C) 2018-2021 Maxim Devaev <[email protected]> # -# # -# 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 aiohttp.web import Request -from aiohttp.web import Response - -from ..wol import WakeOnLan - -from ..http import exposed_http -from ..http import make_json_response - - -# ===== -class WolApi: - def __init__(self, wol: WakeOnLan) -> None: - self.__wol = wol - - # ===== - - @exposed_http("GET", "/wol") - async def __state_handler(self, _: Request) -> Response: - return make_json_response(await self.__wol.get_state()) - - @exposed_http("POST", "/wol/wakeup") - async def __wakeup_handler(self, _: Request) -> Response: - await self.__wol.wakeup() - return make_json_response() diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py index a5681580..08b310d0 100644 --- a/kvmd/apps/kvmd/server.py +++ b/kvmd/apps/kvmd/server.py @@ -65,7 +65,6 @@ from ... import aioproc from .auth import AuthManager from .info import InfoManager from .logreader import LogReader -from .wol import WakeOnLan from .ugpio import UserGpio from .streamer import Streamer from .snapshoter import Snapshoter @@ -85,7 +84,6 @@ from .api.auth import check_request_auth from .api.info import InfoApi from .api.log import LogApi -from .api.wol import WolApi from .api.ugpio import UserGpioApi from .api.hid import HidApi from .api.atx import AtxApi @@ -148,7 +146,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins auth_manager: AuthManager, info_manager: InfoManager, log_reader: LogReader, - wol: WakeOnLan, user_gpio: UserGpio, hid: BaseHid, @@ -186,7 +183,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins for sub in sorted(info_manager.get_subs()) ], *[ - _Component("Wake-on-LAN", "wol_state", wol), _Component("User-GPIO", "gpio_state", user_gpio), _Component("HID", "hid_state", hid), _Component("ATX", "atx_state", atx), @@ -201,7 +197,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins AuthApi(auth_manager), InfoApi(info_manager), LogApi(log_reader), - WolApi(wol), UserGpioApi(user_gpio), self.__hid_api, AtxApi(atx), diff --git a/kvmd/apps/kvmd/wol.py b/kvmd/plugins/ugpio/wol.py index 70c60a89..deeb06c6 100644 --- a/kvmd/apps/kvmd/wol.py +++ b/kvmd/plugins/ugpio/wol.py @@ -27,46 +27,69 @@ from typing import Optional from ...logging import get_logger -from ...errors import OperationError - from ... import aiotools +from ...yamlconf import Option -# ===== -class WolDisabledError(OperationError): - def __init__(self) -> None: - super().__init__("WoL is disabled") +from ...validators.net import valid_ip +from ...validators.net import valid_port +from ...validators.net import valid_mac + +from . import GpioDriverOfflineError +from . import BaseUserGpioDriver # ===== -class WakeOnLan: - def __init__(self, ip: str, port: int, mac: str) -> None: +class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attributes + def __init__( # pylint: disable=super-init-not-called + self, + instance_name: str, + notifier: aiotools.AioNotifier, + + ip: str, + port: int, + mac: str, + ) -> None: + + super().__init__(instance_name, notifier) + self.__ip = ip self.__port = port self.__mac = mac - self.__magic = b"" - - if mac: - assert len(mac) == 17, mac - self.__magic = bytes.fromhex("FF" * 6 + mac.replace(":", "") * 16) - async def get_state(self) -> Dict: + @classmethod + def get_plugin_options(cls) -> Dict: return { - "enabled": bool(self.__magic), - "target": { - "ip": self.__ip, - "port": self.__port, - "mac": self.__mac, - }, + "ip": Option("255.255.255.255", type=(lambda arg: valid_ip(arg, v6=False))), + "port": Option(9, type=valid_port), + "mac": Option("", type=(lambda arg: (valid_mac(arg) if arg else ""))), } - @aiotools.atomic - async def wakeup(self) -> None: - if not self.__magic: - raise WolDisabledError() + def register_input(self, pin: int, debounce: float) -> None: + _ = pin + _ = debounce - logger = get_logger(0) - logger.info("Waking up %s (%s:%s) using Wake-on-LAN ...", self.__mac, self.__ip, self.__port) + def register_output(self, pin: int, initial: Optional[bool]) -> None: + _ = pin + _ = initial + + def prepare(self) -> None: + get_logger(0).info("Probing driver %s on MAC %s and %s:%d ...", self, self.__mac, self.__ip, self.__port) + + async def run(self) -> None: + await aiotools.wait_infinite() + + async def cleanup(self) -> None: + pass + + async def read(self, pin: int) -> bool: + _ = pin + return False + + async def write(self, pin: int, state: bool) -> None: + _ = pin + if not state: + return sock: Optional[socket.socket] = None try: @@ -74,14 +97,18 @@ class WakeOnLan: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) sock.connect((self.__ip, self.__port)) - sock.send(self.__magic) + sock.send(bytes.fromhex("FF" * 6 + self.__mac.replace(":", "") * 16)) except Exception: - logger.exception("Can't send Wake-on-LAN packet") - else: - logger.info("Wake-on-LAN packet sent") + get_logger(0).exception("Can't send Wake-on-LAN packet via %s to %s", self, self.__mac) + raise GpioDriverOfflineError(self) finally: if sock: try: sock.close() except Exception: pass + + def __str__(self) -> str: + return f"WakeOnLan({self._instance_name})" + + __repr__ = __str__ diff --git a/web/kvm/index.html b/web/kvm/index.html index 94c0863c..59870d50 100644 --- a/web/kvm/index.html +++ b/web/kvm/index.html @@ -121,7 +121,7 @@ <button class="small" data-force-hide-menu id="open-log-button">• Log</button> </td> <td class="feature-disabled" id="wol"> - <button class="small" disabled data-force-hide-menu id="wol-wakeup-button">• WoL</button> + <button class="gpio-button small" data-force-hide-menu id="gpio-button-__wol__" data-channel="__wol__" data-confirm="Are you sure to send Wake-on-LAN packet to the server?">• WoL</button> </td> </tr> </table> diff --git a/web/kvm/navbar-system.pug b/web/kvm/navbar-system.pug index 8602e673..03910cb2 100644 --- a/web/kvm/navbar-system.pug +++ b/web/kvm/navbar-system.pug @@ -12,7 +12,9 @@ li(class="right") td(id="webterm" class="feature-disabled") #[button(data-force-hide-menu data-show-window="webterm-window" class="small") • Term] td #[button(data-force-hide-menu data-show-window="about-window" class="small") • About] td #[button(data-force-hide-menu id="open-log-button" class="small") • Log] - td(id="wol" class="feature-disabled") #[button(disabled data-force-hide-menu id="wol-wakeup-button" class="small") • WoL] + td(id="wol" class="feature-disabled") + button(data-force-hide-menu id="gpio-button-__wol__" class="gpio-button small" data-channel="__wol__" + data-confirm="Are you sure to send Wake-on-LAN packet to the server?") • WoL hr table(class="kv" style="width: calc(100% - 20px)") tr(id="stream-resolution" class="feature-disabled") diff --git a/web/share/js/kvm/gpio.js b/web/share/js/kvm/gpio.js index f31dbf05..d1969f39 100644 --- a/web/share/js/kvm/gpio.js +++ b/web/share/js/kvm/gpio.js @@ -105,6 +105,7 @@ export function Gpio() { } tools.featureSetEnabled($("v3-usb-breaker"), ("__v3_usb_breaker__" in model.scheme.outputs)); + tools.featureSetEnabled($("wol"), ("__wol__" in model.scheme.outputs)); self.setState(__state); }; diff --git a/web/share/js/kvm/session.js b/web/share/js/kvm/session.js index 3a0b501c..12eb46fd 100644 --- a/web/share/js/kvm/session.js +++ b/web/share/js/kvm/session.js @@ -30,7 +30,6 @@ import {Hid} from "./hid.js"; import {Atx} from "./atx.js"; import {Msd} from "./msd.js"; import {Streamer} from "./stream.js"; -import {WakeOnLan} from "./wol.js"; import {Gpio} from "./gpio.js"; @@ -48,7 +47,6 @@ export function Session() { var __hid = new Hid(__streamer.getResolution); var __atx = new Atx(); var __msd = new Msd(); - var __wol = new WakeOnLan(); var __gpio = new Gpio(); var __init__ = function() { @@ -243,7 +241,6 @@ export function Session() { case "info_hw_state": __setAboutInfoHw(data.event); break; case "info_system_state": __setAboutInfoSystem(data.event); break; case "info_extras_state": __setExtras(data.event); break; - case "wol_state": __wol.setState(data.event); break; case "gpio_model_state": __gpio.setModel(data.event); break; case "gpio_state": __gpio.setState(data.event); break; case "hid_keymaps_state": __hid.setKeymaps(data.event); break; diff --git a/web/share/js/kvm/wol.js b/web/share/js/kvm/wol.js deleted file mode 100644 index c0f68883..00000000 --- a/web/share/js/kvm/wol.js +++ /dev/null @@ -1,70 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main Pi-KVM daemon. # -# # -# Copyright (C) 2018-2021 Maxim Devaev <[email protected]> # -# # -# 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/>. # -# # -*****************************************************************************/ - - -"use strict"; - - -import {tools, $} from "../tools.js"; -import {wm} from "../wm.js"; - - -export function WakeOnLan() { - var self = this; - - /************************************************************************/ - - var __target = {}; - - var __init__ = function() { - tools.setOnClick($("wol-wakeup-button"), __clickWakeupButton); - }; - - /************************************************************************/ - - self.setState = function(state) { - if (state) { - tools.featureSetEnabled($("wol"), state.enabled); - __target = state.target; - } - wm.setElementEnabled($("wol-wakeup-button"), (state && state.enabled)); - }; - - var __clickWakeupButton = function() { - let msg = ` - Are you sure to send Wake-on-LAN packet to the server?<br> - Target: <b>${__target.mac}</b> (${__target.ip}:${__target.port})? - `; - wm.confirm(msg).then(function(ok) { - if (ok) { - let http = tools.makeRequest("POST", "/api/wol/wakeup", function() { - if (http.readyState === 4) { - if (http.status !== 200) { - wm.error("Wakeup error:<br>", http.responseText); - } - } - }); - } - }); - }; - - __init__(); -} |