From 0fdfa505f00588009c9667a3c87d7d605219dd04 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Thu, 27 Jul 2023 20:47:25 +0200 Subject: add thermal sensor readings from /sys/class/thermal --- html/index.html | 46 +++++++++++++++++++++++++++++-------------- 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 @@

-

refreshed every - seconds

-
- - +
+
+ + +
+
-
@@ -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 = $(''); + + data.forEach(function(info) { + let type = info['type']; + let temp = info['temp'].toFixed(2) + '°C'; + let row = $('') + .append($('', {'class': 'py-0'}) + .append($('', {'class': 'text-reset'}).text(type))) + .append($('', {'class': 'py-0 text-right'}) + .append($('', {'class': 'text-reset'}).html(temp))); + body.append(row); + }); + + $('#thermal').empty(); + $('#thermal').append($('
', {'class': 'table-responsive'}) + .append($('', {'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'); diff --git a/src/app.py b/src/app.py index 7533df7..3964a38 100755 --- a/src/app.py +++ b/src/app.py @@ -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(), } -- cgit v1.2.3