diff options
author | Egor Tensin <Egor.Tensin@gmail.com> | 2023-07-27 20:47:25 +0200 |
---|---|---|
committer | Egor Tensin <Egor.Tensin@gmail.com> | 2023-07-27 20:47:25 +0200 |
commit | 0fdfa505f00588009c9667a3c87d7d605219dd04 (patch) | |
tree | bea04ee5c3b3eb1223fb73b4c43c89c664e99bb7 | |
parent | workflows/ci: fix Python 3.6 tests (diff) | |
download | linux-status-0fdfa505f00588009c9667a3c87d7d605219dd04.tar.gz linux-status-0fdfa505f00588009c9667a3c87d7d605219dd04.zip |
add thermal sensor readings from /sys/class/thermalv2.4
Diffstat (limited to '')
-rw-r--r-- | html/index.html | 46 | ||||
-rwxr-xr-x | src/app.py | 61 |
2 files changed, 92 insertions, 15 deletions
diff --git a/html/index.html b/html/index.html index c70d862..8c4ebf1 100644 --- a/html/index.html +++ b/html/index.html @@ -15,9 +15,12 @@ <h1 class="h2" id="hostname">-</h1> <p class="mb-0 small">refreshed every <span id="status_refresh_interval">-</span> seconds</p> </div> - <div class="btn-group" role="group" id="power_buttons"> - <button type="button" class="btn btn-sm btn-warning" onclick="reboot();">Reboot</button> - <button type="button" class="btn btn-sm btn-danger" onclick="poweroff();">Shutdown</button> + <div> + <div class="btn-group" role="group" id="power_buttons"> + <button type="button" class="btn btn-sm btn-warning" onclick="reboot();">Reboot</button> + <button type="button" class="btn btn-sm btn-danger" onclick="poweroff();">Shutdown</button> + </div> + <div title="Thermal sensor readings from /sys/class/thermal. Only the first few are shown." class="small mt-1" id="thermal">-</div> </div> </div> </div> @@ -196,6 +199,30 @@ function set_hostname(data) { $('title').text(data); } +var thermal_info_max_rows = 2; + +function set_thermal(data) { + data = data.slice(0, thermal_info_max_rows); + + let body = $('<tbody/>'); + + data.forEach(function(info) { + let type = info['type']; + let temp = info['temp'].toFixed(2) + '°C'; + let row = $('<tr/>') + .append($('<td/>', {'class': 'py-0'}) + .append($('<code/>', {'class': 'text-reset'}).text(type))) + .append($('<td/>', {'class': 'py-0 text-right'}) + .append($('<code/>', {'class': 'text-reset'}).html(temp))); + body.append(row); + }); + + $('#thermal').empty(); + $('#thermal').append($('<div/>', {'class': 'table-responsive'}) + .append($('<table/>', {'class': 'table table-borderless table-sm text-nowrap mb-0'}) + .append(body))); +} + function set_top(data) { $('#top').text(data); } @@ -401,6 +428,7 @@ function refresh_status() { get('status', function(data) { data = JSON.parse(data); set_hostname(data['hostname']); + set_thermal(data['thermal']); set_system(data['system']); set_users(data['user']); }); @@ -425,18 +453,6 @@ function main() { $(function() { main(); }); - -function on_resize(width) { - if (width < 576) { // xs, in Bootstrap's terms - $('#power_buttons').attr('class', 'btn-group-vertical'); - } else { - $('#power_buttons').attr('class', 'btn-group'); - } -} - -$(window).bind('resize', function() { - on_resize($(this).width()); -}).trigger('resize'); </script> </body> </html> @@ -47,6 +47,7 @@ from http import HTTPStatus import json import os import pwd +import re import shlex import socket import subprocess @@ -314,6 +315,7 @@ class DockerStatus(DockerInspect): 'status': info['State']['Status'], } + class Hostname(Task): def run(self): pass @@ -322,6 +324,64 @@ class Hostname(Task): return socket.gethostname() +class ThermalInfo(Task): + ROOT = '/sys/class/thermal' + + @staticmethod + def _collect_dirs(): + root = ThermalInfo.ROOT + dirs = [ + dir for dir in os.listdir(root) + if re.match(r'^thermal_zone\d+$', dir) + ] + dirs = sorted(dirs) + dirs = [os.path.join(root, dir) for dir in dirs] + return dirs + + @staticmethod + def _read_temp(dir): + with open(os.path.join(dir, 'temp')) as fd: + temp = fd.read() + return ThermalInfo._parse_temp(temp) + + @staticmethod + def _parse_temp(temp): + if not temp.endswith('\n'): + raise RuntimeError('invalid temp file contents: ' + temp) + temp = temp.strip('\n') + try: + temp = int(temp) + except ValueError: + raise RuntimeError('invalid temp file contents: ' + temp) + return round(temp / 1000, 2) + + @staticmethod + def _read_type(dir): + with open(os.path.join(dir, 'type')) as fd: + type = fd.read() + return ThermalInfo._parse_type(type) + + @staticmethod + def _parse_type(type): + if not type.endswith('\n'): + raise RuntimeError('invalid type file contents: ' + type) + type = type.strip('\n') + return type + + @staticmethod + def _read_dir(dir): + return { + 'temp': ThermalInfo._read_temp(dir), + 'type': ThermalInfo._read_type(dir), + } + + def run(self): + pass + + def result(self): + return [self._read_dir(dir) for dir in ThermalInfo._collect_dirs()] + + class Top(Command): COMMAND = None @@ -401,6 +461,7 @@ class Status(TaskList): def __init__(self): tasks = { 'hostname': Hostname(), + 'thermal': ThermalInfo(), 'system': SystemStatus(), 'user': UserStatusList(), } |