diff options
author | Maxim Devaev <[email protected]> | 2022-03-25 21:19:28 +0300 |
---|---|---|
committer | Maxim Devaev <[email protected]> | 2022-03-25 21:19:28 +0300 |
commit | ed23fef5121f8f71b47b3e6aefe409a7e36fd9a7 (patch) | |
tree | 07fb271740bca9a244b6d1bf87f9f098292cd108 /kvmd | |
parent | 67180e244f4d521816c2f2a82457874da5dcb202 (diff) |
fan monitoring
Diffstat (limited to 'kvmd')
-rw-r--r-- | kvmd/apps/__init__.py | 5 | ||||
-rw-r--r-- | kvmd/apps/kvmd/info/__init__.py | 2 | ||||
-rw-r--r-- | kvmd/apps/kvmd/info/fan.py | 98 |
3 files changed, 105 insertions, 0 deletions
diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index b31ac936..75600390 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -375,6 +375,11 @@ def _get_config_scheme() -> Dict: "vcgencmd_cmd": Option(["/opt/vc/bin/vcgencmd"], type=valid_command), "state_poll": Option(10.0, type=valid_float_f01), }, + "fan": { + "unix": Option("", type=valid_abs_path, if_empty="", unpack_as="unix_path"), + "timeout": Option(5.0, type=valid_float_f01), + "state_poll": Option(5.0, type=valid_float_f01), + }, }, "hid": { diff --git a/kvmd/apps/kvmd/info/__init__.py b/kvmd/apps/kvmd/info/__init__.py index 458eec52..53a02f88 100644 --- a/kvmd/apps/kvmd/info/__init__.py +++ b/kvmd/apps/kvmd/info/__init__.py @@ -29,6 +29,7 @@ from .system import SystemInfoSubmanager from .meta import MetaInfoSubmanager from .extras import ExtrasInfoSubmanager from .hw import HwInfoSubmanager +from .fan import FanInfoSubmanager # ===== @@ -39,6 +40,7 @@ class InfoManager: "meta": MetaInfoSubmanager(config.kvmd.info.meta), "extras": ExtrasInfoSubmanager(config), "hw": HwInfoSubmanager(**config.kvmd.info.hw._unpack()), + "fan": FanInfoSubmanager(**config.kvmd.info.fan._unpack()), } def get_subs(self) -> Set[str]: diff --git a/kvmd/apps/kvmd/info/fan.py b/kvmd/apps/kvmd/info/fan.py new file mode 100644 index 00000000..d4529150 --- /dev/null +++ b/kvmd/apps/kvmd/info/fan.py @@ -0,0 +1,98 @@ +# ========================================================================== # +# # +# KVMD - The main PiKVM daemon. # +# # +# Copyright (C) 2018-2022 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/>. # +# # +# ========================================================================== # + + +import copy +import asyncio + +from typing import Dict +from typing import AsyncGenerator +from typing import Optional + +import aiohttp + +from ....logging import get_logger + +from .... import aiotools +from .... import htclient + +from .base import BaseInfoSubmanager + + +# ===== +class FanInfoSubmanager(BaseInfoSubmanager): + def __init__( + self, + unix_path: str, + timeout: float, + state_poll: float, + ) -> None: + + self.__unix_path = unix_path + self.__timeout = timeout + self.__state_poll = state_poll + + async def get_state(self) -> Dict: + return { + "monitored": bool(self.__unix_path), + "state": ((await self.__get_fan_state() if self.__unix_path else None)), + } + + async def poll_state(self) -> AsyncGenerator[Dict, None]: + prev_state: Dict = {} + while True: + if self.__unix_path: + pure = state = await self.get_state() + if pure["state"] is not None: + try: + pure = copy.deepcopy(state) + pure["state"]["service"]["now_ts"] = 0 + except Exception: + pass + if pure != prev_state: + yield state + prev_state = pure + await asyncio.sleep(self.__state_poll) + else: + yield (await self.get_state()) + await aiotools.wait_infinite() + + # ===== + + async def __get_fan_state(self) -> Optional[Dict]: + try: + async with self.__make_http_session() as session: + async with session.get("http://localhost/state") as response: + htclient.raise_not_200(response) + return (await response.json())["result"] + except Exception as err: + get_logger(0).error("Can't read fan state: %s", err) + return None + + def __make_http_session(self) -> aiohttp.ClientSession: + kwargs: Dict = { + "headers": { + "User-Agent": htclient.make_user_agent("KVMD"), + }, + "timeout": aiohttp.ClientTimeout(total=self.__timeout), + "connector": aiohttp.UnixConnector(path=self.__unix_path) + } + return aiohttp.ClientSession(**kwargs) |