moved wol to gpio

This commit is contained in:
Devaev Maxim 2021-07-17 01:57:01 +03:00
parent 688ddca549
commit 0c500aa0c9
10 changed files with 80 additions and 166 deletions

View File

@ -79,7 +79,6 @@ from ..validators.os import valid_options
from ..validators.os import valid_command from ..validators.os import valid_command
from ..validators.net import valid_ip_or_host 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_net
from ..validators.net import valid_port from ..validators.net import valid_port
from ..validators.net import valid_ports_list 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}") 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): if isinstance(raw_config.get("otg"), dict):
for (old, new) in [ for (old, new) in [
("msd", "msd"), ("msd", "msd"),
@ -195,6 +194,23 @@ def _patch_raw(raw_config: Dict) -> None:
raw_config["otg"]["devices"] = {} raw_config["otg"]["devices"] = {}
raw_config["otg"]["devices"][new] = raw_config["otg"].pop(old) 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): if isinstance(raw_config.get("kvmd"), dict) and isinstance(raw_config["kvmd"].get("streamer"), dict):
streamer_config = raw_config["kvmd"]["streamer"] 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": { "hid": {
"type": Option("", type=valid_stripped_string_not_empty), "type": Option("", type=valid_stripped_string_not_empty),

View File

@ -34,7 +34,6 @@ from .. import init
from .auth import AuthManager from .auth import AuthManager
from .info import InfoManager from .info import InfoManager
from .logreader import LogReader from .logreader import LogReader
from .wol import WakeOnLan
from .ugpio import UserGpio from .ugpio import UserGpio
from .streamer import Streamer from .streamer import Streamer
from .snapshoter import Snapshoter from .snapshoter import Snapshoter
@ -86,7 +85,6 @@ def main(argv: Optional[List[str]]=None) -> None:
), ),
info_manager=InfoManager(global_config), info_manager=InfoManager(global_config),
log_reader=LogReader(), log_reader=LogReader(),
wol=WakeOnLan(**config.wol._unpack()),
user_gpio=UserGpio(config.gpio, global_config.otg.udc), user_gpio=UserGpio(config.gpio, global_config.otg.udc),
hid=hid, hid=hid,

View File

@ -1,46 +0,0 @@
# ========================================================================== #
# #
# KVMD - The main Pi-KVM daemon. #
# #
# Copyright (C) 2018-2021 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 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()

View File

@ -65,7 +65,6 @@ from ... import aioproc
from .auth import AuthManager from .auth import AuthManager
from .info import InfoManager from .info import InfoManager
from .logreader import LogReader from .logreader import LogReader
from .wol import WakeOnLan
from .ugpio import UserGpio from .ugpio import UserGpio
from .streamer import Streamer from .streamer import Streamer
from .snapshoter import Snapshoter from .snapshoter import Snapshoter
@ -85,7 +84,6 @@ from .api.auth import check_request_auth
from .api.info import InfoApi from .api.info import InfoApi
from .api.log import LogApi from .api.log import LogApi
from .api.wol import WolApi
from .api.ugpio import UserGpioApi from .api.ugpio import UserGpioApi
from .api.hid import HidApi from .api.hid import HidApi
from .api.atx import AtxApi from .api.atx import AtxApi
@ -148,7 +146,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
auth_manager: AuthManager, auth_manager: AuthManager,
info_manager: InfoManager, info_manager: InfoManager,
log_reader: LogReader, log_reader: LogReader,
wol: WakeOnLan,
user_gpio: UserGpio, user_gpio: UserGpio,
hid: BaseHid, 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()) for sub in sorted(info_manager.get_subs())
], ],
*[ *[
_Component("Wake-on-LAN", "wol_state", wol),
_Component("User-GPIO", "gpio_state", user_gpio), _Component("User-GPIO", "gpio_state", user_gpio),
_Component("HID", "hid_state", hid), _Component("HID", "hid_state", hid),
_Component("ATX", "atx_state", atx), _Component("ATX", "atx_state", atx),
@ -201,7 +197,6 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins
AuthApi(auth_manager), AuthApi(auth_manager),
InfoApi(info_manager), InfoApi(info_manager),
LogApi(log_reader), LogApi(log_reader),
WolApi(wol),
UserGpioApi(user_gpio), UserGpioApi(user_gpio),
self.__hid_api, self.__hid_api,
AtxApi(atx), AtxApi(atx),

View File

@ -27,46 +27,69 @@ from typing import Optional
from ...logging import get_logger from ...logging import get_logger
from ...errors import OperationError
from ... import aiotools from ... import aiotools
from ...yamlconf import Option
# ===== from ...validators.net import valid_ip
class WolDisabledError(OperationError): from ...validators.net import valid_port
def __init__(self) -> None: from ...validators.net import valid_mac
super().__init__("WoL is disabled")
from . import GpioDriverOfflineError
from . import BaseUserGpioDriver
# ===== # =====
class WakeOnLan: class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attributes
def __init__(self, ip: str, port: int, mac: str) -> None: 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.__ip = ip
self.__port = port self.__port = port
self.__mac = mac self.__mac = mac
self.__magic = b""
if mac: @classmethod
assert len(mac) == 17, mac def get_plugin_options(cls) -> Dict:
self.__magic = bytes.fromhex("FF" * 6 + mac.replace(":", "") * 16)
async def get_state(self) -> Dict:
return { return {
"enabled": bool(self.__magic), "ip": Option("255.255.255.255", type=(lambda arg: valid_ip(arg, v6=False))),
"target": { "port": Option(9, type=valid_port),
"ip": self.__ip, "mac": Option("", type=(lambda arg: (valid_mac(arg) if arg else ""))),
"port": self.__port,
"mac": self.__mac,
},
} }
@aiotools.atomic def register_input(self, pin: int, debounce: float) -> None:
async def wakeup(self) -> None: _ = pin
if not self.__magic: _ = debounce
raise WolDisabledError()
logger = get_logger(0) def register_output(self, pin: int, initial: Optional[bool]) -> None:
logger.info("Waking up %s (%s:%s) using Wake-on-LAN ...", self.__mac, self.__ip, self.__port) _ = 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 sock: Optional[socket.socket] = None
try: try:
@ -74,14 +97,18 @@ class WakeOnLan:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.connect((self.__ip, self.__port)) sock.connect((self.__ip, self.__port))
sock.send(self.__magic) sock.send(bytes.fromhex("FF" * 6 + self.__mac.replace(":", "") * 16))
except Exception: except Exception:
logger.exception("Can't send Wake-on-LAN packet") get_logger(0).exception("Can't send Wake-on-LAN packet via %s to %s", self, self.__mac)
else: raise GpioDriverOfflineError(self)
logger.info("Wake-on-LAN packet sent")
finally: finally:
if sock: if sock:
try: try:
sock.close() sock.close()
except Exception: except Exception:
pass pass
def __str__(self) -> str:
return f"WakeOnLan({self._instance_name})"
__repr__ = __str__

View File

@ -121,7 +121,7 @@
<button class="small" data-force-hide-menu id="open-log-button">&bull; Log</button> <button class="small" data-force-hide-menu id="open-log-button">&bull; Log</button>
</td> </td>
<td class="feature-disabled" id="wol"> <td class="feature-disabled" id="wol">
<button class="small" disabled data-force-hide-menu id="wol-wakeup-button">&bull; 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?">&bull; WoL</button>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -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") &bull; Term] td(id="webterm" class="feature-disabled") #[button(data-force-hide-menu data-show-window="webterm-window" class="small") &bull; Term]
td #[button(data-force-hide-menu data-show-window="about-window" class="small") &bull; About] td #[button(data-force-hide-menu data-show-window="about-window" class="small") &bull; About]
td #[button(data-force-hide-menu id="open-log-button" class="small") &bull; Log] td #[button(data-force-hide-menu id="open-log-button" class="small") &bull; Log]
td(id="wol" class="feature-disabled") #[button(disabled data-force-hide-menu id="wol-wakeup-button" class="small") &bull; 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?") &bull; WoL
hr hr
table(class="kv" style="width: calc(100% - 20px)") table(class="kv" style="width: calc(100% - 20px)")
tr(id="stream-resolution" class="feature-disabled") tr(id="stream-resolution" class="feature-disabled")

View File

@ -105,6 +105,7 @@ export function Gpio() {
} }
tools.featureSetEnabled($("v3-usb-breaker"), ("__v3_usb_breaker__" in model.scheme.outputs)); tools.featureSetEnabled($("v3-usb-breaker"), ("__v3_usb_breaker__" in model.scheme.outputs));
tools.featureSetEnabled($("wol"), ("__wol__" in model.scheme.outputs));
self.setState(__state); self.setState(__state);
}; };

View File

@ -30,7 +30,6 @@ import {Hid} from "./hid.js";
import {Atx} from "./atx.js"; import {Atx} from "./atx.js";
import {Msd} from "./msd.js"; import {Msd} from "./msd.js";
import {Streamer} from "./stream.js"; import {Streamer} from "./stream.js";
import {WakeOnLan} from "./wol.js";
import {Gpio} from "./gpio.js"; import {Gpio} from "./gpio.js";
@ -48,7 +47,6 @@ export function Session() {
var __hid = new Hid(__streamer.getResolution); var __hid = new Hid(__streamer.getResolution);
var __atx = new Atx(); var __atx = new Atx();
var __msd = new Msd(); var __msd = new Msd();
var __wol = new WakeOnLan();
var __gpio = new Gpio(); var __gpio = new Gpio();
var __init__ = function() { var __init__ = function() {
@ -243,7 +241,6 @@ export function Session() {
case "info_hw_state": __setAboutInfoHw(data.event); break; case "info_hw_state": __setAboutInfoHw(data.event); break;
case "info_system_state": __setAboutInfoSystem(data.event); break; case "info_system_state": __setAboutInfoSystem(data.event); break;
case "info_extras_state": __setExtras(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_model_state": __gpio.setModel(data.event); break;
case "gpio_state": __gpio.setState(data.event); break; case "gpio_state": __gpio.setState(data.event); break;
case "hid_keymaps_state": __hid.setKeymaps(data.event); break; case "hid_keymaps_state": __hid.setKeymaps(data.event); break;

View File

@ -1,70 +0,0 @@
/*****************************************************************************
# #
# KVMD - The main Pi-KVM daemon. #
# #
# Copyright (C) 2018-2021 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/>. #
# #
*****************************************************************************/
"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__();
}