aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--html/index.html46
-rwxr-xr-xsrc/app.py61
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) + '&deg;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>
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(),
}