From 96ef259b4ce28de15f8d145ec2521b0a0ee678a7 Mon Sep 17 00:00:00 2001 From: 2Shirt <1923621+2Shirt@users.noreply.github.com> Date: Thu, 21 Dec 2017 17:11:35 -0700 Subject: [PATCH] New HW sensors script * Rewritten in python * Report CoreTemps first then others * Wrap into two columns as necessary (if the window is big enough) --- .bin/Scripts/borrowed/sensors-README.md | 35 +++ .bin/Scripts/borrowed/sensors.py | 236 ++++++++++++++++++ .bin/Scripts/hw-sensors | 147 +++++++++++ .bin/Scripts/linux-old/hw-diags-sensors | 42 ---- .../live/airootfs/etc/skel/.config/i3/config | 2 +- .../airootfs/etc/skel/.config/openbox/rc.xml | 2 +- 6 files changed, 420 insertions(+), 44 deletions(-) create mode 100644 .bin/Scripts/borrowed/sensors-README.md create mode 100644 .bin/Scripts/borrowed/sensors.py create mode 100755 .bin/Scripts/hw-sensors delete mode 100755 .bin/Scripts/linux-old/hw-diags-sensors diff --git a/.bin/Scripts/borrowed/sensors-README.md b/.bin/Scripts/borrowed/sensors-README.md new file mode 100644 index 00000000..11858382 --- /dev/null +++ b/.bin/Scripts/borrowed/sensors-README.md @@ -0,0 +1,35 @@ +sensors.py +========== +python bindings using ctypes for libsensors3 of the [lm-sensors project](https://github.com/groeck/lm-sensors). The code was written against libsensors 3.3.4. + +For documentation of the low level API see [sensors.h](https://github.com/groeck/lm-sensors/blob/master/lib/sensors.h). For an example of the high level API see [example.py](example.py). + +For a GUI application that displays the sensor readings and is based on this library, take a look at [sensors-unity](https://launchpad.net/sensors-unity). + +Features +-------- +* Full access to low level libsensors3 API +* High level iterator API +* unicode handling +* Python2 and Python3 compatible + +Licensing +--------- +LGPLv2 (same as libsensors3) + +Usage Notes +----------- +As Python does not support call by reference for primitive types some of the libsensors API had to be adapted: + +```python +# nr is changed by refrence in the C API +chip_name, nr = sensors.get_detected_chips(None, nr) + +# returns the value. throws on error +val = sensors.get_value(chip, subfeature_nr) +``` + +Missing Features (pull requests are welcome): +* `sensors_subfeature_type` enum +* `sensors_get_subfeature` +* Error handlers diff --git a/.bin/Scripts/borrowed/sensors.py b/.bin/Scripts/borrowed/sensors.py new file mode 100644 index 00000000..847f2619 --- /dev/null +++ b/.bin/Scripts/borrowed/sensors.py @@ -0,0 +1,236 @@ +""" +@package sensors.py +Python Bindings for libsensors3 + +use the documentation of libsensors for the low level API. +see example.py for high level API usage. + +@author: Pavel Rojtberg (http://www.rojtberg.net) +@see: https://github.com/paroj/sensors.py +@copyright: LGPLv2 (same as libsensors) +""" + +from ctypes import * +import ctypes.util + +_libc = cdll.LoadLibrary(ctypes.util.find_library("c")) +# see https://github.com/paroj/sensors.py/issues/1 +_libc.free.argtypes = [c_void_p] + +_hdl = cdll.LoadLibrary(ctypes.util.find_library("sensors")) + +version = c_char_p.in_dll(_hdl, "libsensors_version").value.decode("ascii") + +class bus_id(Structure): + _fields_ = [("type", c_short), + ("nr", c_short)] + +class chip_name(Structure): + _fields_ = [("prefix", c_char_p), + ("bus", bus_id), + ("addr", c_int), + ("path", c_char_p)] + +class feature(Structure): + _fields_ = [("name", c_char_p), + ("number", c_int), + ("type", c_int)] + + # sensors_feature_type + IN = 0x00 + FAN = 0x01 + TEMP = 0x02 + POWER = 0x03 + ENERGY = 0x04 + CURR = 0x05 + HUMIDITY = 0x06 + MAX_MAIN = 0x7 + VID = 0x10 + INTRUSION = 0x11 + MAX_OTHER = 0x12 + BEEP_ENABLE = 0x18 + +class subfeature(Structure): + _fields_ = [("name", c_char_p), + ("number", c_int), + ("type", c_int), + ("mapping", c_int), + ("flags", c_uint)] + +_hdl.sensors_get_detected_chips.restype = POINTER(chip_name) +_hdl.sensors_get_features.restype = POINTER(feature) +_hdl.sensors_get_all_subfeatures.restype = POINTER(subfeature) +_hdl.sensors_get_label.restype = c_void_p # return pointer instead of str so we can free it +_hdl.sensors_get_adapter_name.restype = c_char_p # docs do not say whether to free this or not +_hdl.sensors_strerror.restype = c_char_p + +### RAW API ### +MODE_R = 1 +MODE_W = 2 +COMPUTE_MAPPING = 4 + +def init(cfg_file = None): + file = _libc.fopen(cfg_file.encode("utf-8"), "r") if cfg_file is not None else None + + if _hdl.sensors_init(file) != 0: + raise Exception("sensors_init failed") + + if file is not None: + _libc.fclose(file) + +def cleanup(): + _hdl.sensors_cleanup() + +def parse_chip_name(orig_name): + ret = chip_name() + err= _hdl.sensors_parse_chip_name(orig_name.encode("utf-8"), byref(ret)) + + if err < 0: + raise Exception(strerror(err)) + + return ret + +def strerror(errnum): + return _hdl.sensors_strerror(errnum).decode("utf-8") + +def free_chip_name(chip): + _hdl.sensors_free_chip_name(byref(chip)) + +def get_detected_chips(match, nr): + """ + @return: (chip, next nr to query) + """ + _nr = c_int(nr) + + if match is not None: + match = byref(match) + + chip = _hdl.sensors_get_detected_chips(match, byref(_nr)) + chip = chip.contents if bool(chip) else None + return chip, _nr.value + +def chip_snprintf_name(chip, buffer_size=200): + """ + @param buffer_size defaults to the size used in the sensors utility + """ + ret = create_string_buffer(buffer_size) + err = _hdl.sensors_snprintf_chip_name(ret, buffer_size, byref(chip)) + + if err < 0: + raise Exception(strerror(err)) + + return ret.value.decode("utf-8") + +def do_chip_sets(chip): + """ + @attention this function was not tested + """ + err = _hdl.sensors_do_chip_sets(byref(chip)) + if err < 0: + raise Exception(strerror(err)) + +def get_adapter_name(bus): + return _hdl.sensors_get_adapter_name(byref(bus)).decode("utf-8") + +def get_features(chip, nr): + """ + @return: (feature, next nr to query) + """ + _nr = c_int(nr) + feature = _hdl.sensors_get_features(byref(chip), byref(_nr)) + feature = feature.contents if bool(feature) else None + return feature, _nr.value + +def get_label(chip, feature): + ptr = _hdl.sensors_get_label(byref(chip), byref(feature)) + val = cast(ptr, c_char_p).value.decode("utf-8") + _libc.free(ptr) + return val + +def get_all_subfeatures(chip, feature, nr): + """ + @return: (subfeature, next nr to query) + """ + _nr = c_int(nr) + subfeature = _hdl.sensors_get_all_subfeatures(byref(chip), byref(feature), byref(_nr)) + subfeature = subfeature.contents if bool(subfeature) else None + return subfeature, _nr.value + +def get_value(chip, subfeature_nr): + val = c_double() + err = _hdl.sensors_get_value(byref(chip), subfeature_nr, byref(val)) + if err < 0: + raise Exception(strerror(err)) + return val.value + +def set_value(chip, subfeature_nr, value): + """ + @attention this function was not tested + """ + val = c_double(value) + err = _hdl.sensors_set_value(byref(chip), subfeature_nr, byref(val)) + if err < 0: + raise Exception(strerror(err)) + +### Convenience API ### +class ChipIterator: + def __init__(self, match = None): + self.match = parse_chip_name(match) if match is not None else None + self.nr = 0 + + def __iter__(self): + return self + + def __next__(self): + chip, self.nr = get_detected_chips(self.match, self.nr) + + if chip is None: + raise StopIteration + + return chip + + def __del__(self): + if self.match is not None: + free_chip_name(self.match) + + def next(self): # python2 compability + return self.__next__() + +class FeatureIterator: + def __init__(self, chip): + self.chip = chip + self.nr = 0 + + def __iter__(self): + return self + + def __next__(self): + feature, self.nr = get_features(self.chip, self.nr) + + if feature is None: + raise StopIteration + + return feature + + def next(self): # python2 compability + return self.__next__() + +class SubFeatureIterator: + def __init__(self, chip, feature): + self.chip = chip + self.feature = feature + self.nr = 0 + + def __iter__(self): + return self + + def __next__(self): + subfeature, self.nr = get_all_subfeatures(self.chip, self.feature, self.nr) + + if subfeature is None: + raise StopIteration + + return subfeature + + def next(self): # python2 compability + return self.__next__() diff --git a/.bin/Scripts/hw-sensors b/.bin/Scripts/hw-sensors new file mode 100755 index 00000000..07c98460 --- /dev/null +++ b/.bin/Scripts/hw-sensors @@ -0,0 +1,147 @@ +#!/bin/python3 +# +## Wizard Kit: Sensor monitoring tool + +import itertools +import os +import shutil +import sys + +# Init +os.chdir(os.path.dirname(os.path.realpath(__file__))) +sys.path.append(os.getcwd()) +from functions.common import * +from borrowed import sensors + +# STATIC VARIABLES +COLORS = { + 'CLEAR': '\033[0m', + 'RED': '\033[31m', + 'GREEN': '\033[32m', + 'YELLOW': '\033[33m', + 'ORANGE': '\033[31;1m', + 'BLUE': '\033[34m' + } +TEMP_LIMITS = { + 'GREEN': 60, + 'YELLOW': 70, + 'ORANGE': 80, + 'RED': 90, + } + +# REGEX +REGEX_COLORS = re.compile(r'\033\[\d+;?1?m') + +def color_temp(temp): + try: + temp = float(temp) + except ValueError: + return '{YELLOW}{temp}{CLEAR}'.format(temp=temp, **COLORS) + if temp > TEMP_LIMITS['RED']: + color = COLORS['RED'] + elif temp > TEMP_LIMITS['ORANGE']: + color = COLORS['ORANGE'] + elif temp > TEMP_LIMITS['YELLOW']: + color = COLORS['YELLOW'] + elif temp > TEMP_LIMITS['GREEN']: + color = COLORS['GREEN'] + elif temp > 0: + color = COLORS['BLUE'] + else: + color = COLORS['CLEAR'] + return '{color}{prefix}{temp:2}°C{CLEAR}'.format( + color = color, + prefix = '+' if temp>0 else '-', + temp = int(temp), + **COLORS) + +def get_feature_string(chip, feature): + sfs = list(sensors.SubFeatureIterator(chip, feature)) # get a list of all subfeatures + label = sensors.get_label(chip, feature) + skipname = len(feature.name)+1 # skip common prefix + data = {} + + if feature.type == sensors.feature.INTRUSION: + vals = [sensors.get_value(chip, sf.number) for sf in sfs] + # short path for INTRUSION to demonstrate type usage + status = "alarm" if int(vals[0]) == 1 else "normal" + print_standard(' {:18} {}'.format(label, status)) + return + + for sf in sfs: + name = sf.name[skipname:].decode("utf-8").strip() + val = sensors.get_value(chip, sf.number) + if 'alarm' in name: + # Skip + continue + data[name] = color_temp(val) + + main_temp = data.pop('input', None) + if main_temp: + list_data = [] + for item in ['max', 'crit']: + if item in data: + list_data.append('{}: {}'.format(item, data.pop(item))) + list_data.extend( + ['{}: {}'.format(k, v) for k, v in sorted(data.items())]) + data_str = '{:18} {} ({})'.format( + label, main_temp, ', '.join(list_data)) + else: + list_data.extend(sorted(data.items())) + list_data = ['{}: {}'.format(item[0], item[1]) for item in list_data] + data_str = '{:18} {}'.format(label, ', '.join(list_data)) + return data_str + +def join_columns(column1, column2, width=55): + return '{:<{}}{}'.format( + column1, + 55+len(column1)-len(REGEX_COLORS.sub('', column1)), + column2) + +if __name__ == '__main__': + try: + # Prep + clear_screen() + sensors.init() + + # Get sensor data + chip_temps = {} + for chip in sensors.ChipIterator(): + chip_name = '{} ({})'.format( + sensors.chip_snprintf_name(chip), + sensors.get_adapter_name(chip.bus)) + chip_temps[chip_name] = [chip_name] + for feature in sensors.FeatureIterator(chip): + chip_temps[chip_name].append(get_feature_string(chip, feature)) + chip_temps[chip_name].append('') + + # Sort chips + sensor_temps = [] + for chip in [k for k in sorted(chip_temps.keys()) if 'coretemp' in k]: + sensor_temps.extend(chip_temps[chip]) + for chip in sorted(chip_temps.keys()): + if 'coretemp' not in chip: + sensor_temps.extend(chip_temps[chip]) + + # Wrap columns as needed + screen_size = shutil.get_terminal_size() + rows = screen_size.lines - 1 + if len(sensor_temps) > rows and screen_size.columns > 55*2: + sensor_temps = list(itertools.zip_longest( + sensor_temps[:rows], sensor_temps[rows:], fillvalue='')) + sensor_temps = [join_columns(a, b) for a, b in sensor_temps] + + # Print data + for line in sensor_temps: + print_standard(line) + + # Done + sensors.cleanup() + exit_script() + except SystemExit: + sensors.cleanup() + pass + except: + sensors.cleanup() + major_exception() + diff --git a/.bin/Scripts/linux-old/hw-diags-sensors b/.bin/Scripts/linux-old/hw-diags-sensors deleted file mode 100755 index 5f4f494d..00000000 --- a/.bin/Scripts/linux-old/hw-diags-sensors +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# -## Wizard Kit: HW Diagnostics - Sensors - -LOG_DIR="$1" - -function usage { - echo "Usage: $0 log-dir" - echo " e.g. $0 /tmp/tmp.7Mh5f1RhSL9001" -} - -# Create directory if necessary -if [ ! -d "$LOG_DIR" ]; then - LOG_DIR="$(mktemp -d)" -fi - -# Run Sensor loop -if sensors >/dev/null 2>&1; then - while :; do - sensors -A | grep -E -i -v '(N/A|RPM|\d+\s+V\s+|^\s*$)' > "$LOG_DIR/sensors.out" 2>/dev/null - - # Colorize - # Blue: All temps (superseeded by other colors below) - sed -i -r 's#(\+[0-9]+\.[0-9].C)#\\e[34m\1\\e[0m#g' "$LOG_DIR/sensors.out" >/dev/null 2>&1 - # Green >= 60* C - sed -i -r 's#(\+6[0-9]\.[0-9].C)#\\e[32m\1\\e[0m#g' "$LOG_DIR/sensors.out" >/dev/null 2>&1 - # Yellow >= 70* C - sed -i -r 's#(\+7[0-9]\.[0-9].C)#\\e[33m\1\\e[0m#g' "$LOG_DIR/sensors.out" >/dev/null 2>&1 - # Orange >= 80* C - sed -i -r 's#(\+(8[0-9]|9[0-4])\.[0-9].C)#\\e[31\;1m\1\\e[0m#g' "$LOG_DIR/sensors.out" >/dev/null 2>&1 - # Red >= 95* C - sed -i -r 's#(\+(9[5-9]|1[0-9][0-9])\.[0-9].C)#\\e[31m\1\\e[0m#g' "$LOG_DIR/sensors.out" >/dev/null 2>&1 - - # Output data - clear - echo -e "$(cat "$LOG_DIR/sensors.out")" - sleep 1s - done -else - echo -e "\e[33mNo sensors found!\nPlease monitor temperatures manually\e[0m" - sleep 1h -fi diff --git a/.linux_items/include/live/airootfs/etc/skel/.config/i3/config b/.linux_items/include/live/airootfs/etc/skel/.config/i3/config index 67423053..59b798cf 100644 --- a/.linux_items/include/live/airootfs/etc/skel/.config/i3/config +++ b/.linux_items/include/live/airootfs/etc/skel/.config/i3/config @@ -74,7 +74,7 @@ bindsym $mod+i exec "hardinfo" bindsym $mod+m exec "urxvt -title 'Mount All Volumes' -e mount-all-volumes foh" bindsym $mod+s exec "urxvt -title 'Hardware Diagnostics' -e hw-diags foh" bindsym $mod+t exec "urxvt" -bindsym $mod+v exec "urxvt -title 'Hardware Sensors' -e hw-diags-sensors" +bindsym $mod+v exec "urxvt -title 'Hardware Sensors' -e watch -c -n1 -t hw-sensors" bindsym $mod+w exec "firefox" focus_follows_mouse no diff --git a/.linux_items/include/live/airootfs/etc/skel/.config/openbox/rc.xml b/.linux_items/include/live/airootfs/etc/skel/.config/openbox/rc.xml index c48340dd..f1056533 100644 --- a/.linux_items/include/live/airootfs/etc/skel/.config/openbox/rc.xml +++ b/.linux_items/include/live/airootfs/etc/skel/.config/openbox/rc.xml @@ -334,7 +334,7 @@ - urxvt -title "Hardware Sensors" -e hw-diags-sensors + urxvt -title "Hardware Sensors" -e watch -c -n1 -t hw-sensors