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.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),

View File

@ -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,

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 .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),

View File

@ -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__

View File

@ -121,7 +121,7 @@
<button class="small" data-force-hide-menu id="open-log-button">&bull; Log</button>
</td>
<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>
</tr>
</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 #[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(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
table(class="kv" style="width: calc(100% - 20px)")
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($("wol"), ("__wol__" in model.scheme.outputs));
self.setState(__state);
};

View File

@ -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;

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__();
}