]> arthur.barton.de Git - netdata.git/commitdiff
use sensors.py library from Pavel Rojtberg instead of PySensors. fix #781
authorpaulfantom <paulfantom@gmail.com>
Sun, 14 Aug 2016 17:51:06 +0000 (19:51 +0200)
committerpaulfantom <paulfantom@gmail.com>
Sun, 14 Aug 2016 17:51:06 +0000 (19:51 +0200)
LICENSE.md
python.d/python_modules/lm_sensors.py
python.d/sensors.chart.py

index a79fbf80c29bbf65c196b15c0a24a163064b9369..9b6415d1092868ec30c56b0794ea0115e5babc51 100644 (file)
@@ -138,10 +138,10 @@ connectivity is not available.
     Copyright 2015, Joseph Huckaby
     [MIT License](https://github.com/jhuckaby/pixl-xml)
     
-- [PySensors](https://bitbucket.org/blackjack/pysensors)
+- [sensors](https://github.com/paroj/sensors.py)
 
-    Copyright 2014, Marc 'BlackJack' Rintsch
-    [LGPL 2.1 License](https://bitbucket.org/blackjack/pysensors)
+    Copyright 2014, Pavel Rojtberg
+    [LGPL 2.1 License](http://opensource.org/licenses/LGPL-2.1)
 
 - [PyYAML](https://bitbucket.org/blackjack/pysensors)
 
index 9da738879bcb061d8d273be193f704620b2521e6..1d868f0e249ad2c50f2084b113c5161ac4e202b7 100644 (file)
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-import os
-
-from ctypes import CDLL, c_char_p, c_int, c_void_p, c_uint, c_double, byref, Structure, get_errno,\
-    POINTER, c_short, c_size_t, create_string_buffer
-from ctypes.util import find_library
-
-version_info = (0, 0, 3)
-
-__version__ = '.'.join(map(str, version_info))
-__date__ = '2014-08-17'
-__author__ = "Marc 'BlackJack' Rintsch"
-__contact__ = 'marc@rintsch.de'
-__license__ = 'LGPL v2.1'
-
-API_VERSION = 4
-DEFAULT_CONFIG_FILENAME = '/etc/sensors3.conf'
-
-LIB_FILENAME = os.environ.get('SENSORS_LIB') or find_library('sensors')
-SENSORS_LIB = CDLL(LIB_FILENAME)
-VERSION = c_char_p.in_dll(SENSORS_LIB, 'libsensors_version').value
-MAJOR_VERSION = version_info[0]
-STDC_LIB = CDLL(find_library('c'), use_errno=True)
-
-TYPE_DICT = {
-    0: 'voltage',
-    1: 'fan',
-    2: 'temperature',
-    3: 'power',
-    4: 'energy',
-    5: 'current',
-    6: 'humidity',
-    7: 'max_main',
-    16: 'vid',
-    17: 'intrusion',
-    18: 'max_other',
-    24: 'beep_enable'
-}
-
-class SensorsError(Exception):
-    def __init__(self, message, error_number=None):
-        Exception.__init__(self, message)
-        self.error_number = error_number
-
-
-def _error_check(result, _func, _arguments):
-    if result < 0:
-        raise SensorsError(_strerror(result), result)
-    return result
-
-_strerror = SENSORS_LIB.sensors_strerror
-_strerror.argtypes = [c_int]
-_strerror.restype = c_char_p
-
-_init = SENSORS_LIB.sensors_init
-_init.argtypes = [c_void_p]
-_init.restype = c_int
-_init.errcheck = _error_check
-
-cleanup = SENSORS_LIB.sensors_cleanup
-cleanup.argtypes = None
-cleanup.restype = None
-
-
-def init(config_filename=DEFAULT_CONFIG_FILENAME):
-    file_p = STDC_LIB.fopen(config_filename.encode('utf-8'), b'r')
-    if file_p is None:
-        error_number = get_errno()
-        raise OSError(error_number, os.strerror(error_number), config_filename)
-    try:
-        _init(file_p)
-    finally:
-        STDC_LIB.fclose(file_p)
-
-
-class Subfeature(Structure):
-    _fields_ = [
-        ('name', c_char_p),
-        ('number', c_int),
-        ('type', c_int),
-        ('mapping', c_int),
-        ('flags', c_uint),
-    ]
-
-    def __repr__(self):
-        return '<%s name=%r number=%d type=%d mapping=%d flags=%08x>' % (
-            self.__class__.__name__,
-            self.name,
-            self.number,
-            self.type,
-            self.mapping,
-            self.flags
-        )
-
-    def get_value(self):
-        result = c_double()
-        _get_value(byref(self.parent.chip), self.number, byref(result))
-        return result.value
-
-SUBFEATURE_P = POINTER(Subfeature)
-
-
-class Feature(Structure):
-    _fields_ = [
-        ('name', c_char_p),
-        ('number', c_int),
-        ('type', c_int),
-        ('_first_subfeature', c_int),
-        ('_padding1', c_int),
-    ]
-
-    def __repr__(self):
-        return '<%s name=%r number=%r type=%r>' % (
-            self.__class__.__name__,
-            self.name,
-            self.number,
-            self.type
-        )
+"""
+@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) <http://opensource.org/licenses/LGPL-2.1>
+"""
+
+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):
-        number = c_int(0)
-        while True:
-            result_p = _get_all_subfeatures(
-                byref(self.chip),
-                byref(self),
-                byref(number)
-            )
-            if not result_p:
-                break
-            result = result_p.contents
-            result.chip = self.chip
-            result.parent = self
-            yield result
-
-    @property
-    def label(self):
-        #
-        # TODO Maybe this is a memory leak!
-        #
-        return _get_label(byref(self.chip), byref(self)).decode('utf-8')
-
-    def get_value(self):
-        #
-        # TODO Is the first always the correct one for all feature types?
-        #
-        return next(iter(self)).get_value()
-
-FEATURE_P = POINTER(Feature)
-
-
-class Bus(Structure):
-    TYPE_ANY = -1
-    NR_ANY = -1
-
-    _fields_ = [
-        ('type', c_short),
-        ('nr', c_short),
-    ]
-
-    def __str__(self):
-        return (
-            '*' if self.type == self.TYPE_ANY
-            else _get_adapter_name(byref(self)).decode('utf-8')
-        )
-
-    def __repr__(self):
-        return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.nr)
-
-    @property
-    def has_wildcards(self):
-        return self.type == self.TYPE_ANY or self.nr == self.NR_ANY
-
-BUS_P = POINTER(Bus)
-
-
-class Chip(Structure):
-    #
-    # TODO Move common stuff into `AbstractChip` class.
-    #
-    _fields_ = [
-        ('prefix', c_char_p),
-        ('bus', Bus),
-        ('addr', c_int),
-        ('path', c_char_p),
-    ]
-
-    PREFIX_ANY = None
-    ADDR_ANY = -1
-
-    def __new__(cls, *args):
-        result = super(Chip, cls).__new__(cls)
-        if args:
-            _parse_chip_name(args[0].encode('utf-8'), byref(result))
-        return result
-
-    def __init__(self, *_args):
-        Structure.__init__(self)
-        #
-        # Need to bind the following to the instance so it is available in
-        #  `__del__()` when the interpreter shuts down.
-        #
-        self._free_chip_name = _free_chip_name
-        self.byref = byref
+        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._b_needsfree_:
-            self._free_chip_name(self.byref(self))
-
-    def __repr__(self):
-        return '<%s prefix=%r bus=%r addr=%r path=%r>' % (
-            (
-                self.__class__.__name__,
-                self.prefix,
-                self.bus,
-                self.addr,
-                self.path
-            )
-        )
-
-    def __str__(self):
-        buffer_size = 200
-        result = create_string_buffer(buffer_size)
-        used = _snprintf_chip_name(result, len(result), byref(self))
-        assert used < buffer_size
-        return result.value.decode('utf-8')
+        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):
-        number = c_int(0)
-        while True:
-            result_p = _get_features(byref(self), byref(number))
-            if not result_p:
-                break
-            result = result_p.contents
-            result.chip = self
-            yield result
-
-    @property
-    def adapter_name(self):
-        return str(self.bus)
-
-    @property
-    def has_wildcards(self):
-        return (
-            self.prefix == self.PREFIX_ANY
-            or self.addr == self.ADDR_ANY
-            or self.bus.has_wildcards
-        )
-
-CHIP_P = POINTER(Chip)
-
-
-_parse_chip_name = SENSORS_LIB.sensors_parse_chip_name
-_parse_chip_name.argtypes = [c_char_p, CHIP_P]
-_parse_chip_name.restype = c_int
-_parse_chip_name.errcheck = _error_check
-
-_free_chip_name = SENSORS_LIB.sensors_free_chip_name
-_free_chip_name.argtypes = [CHIP_P]
-_free_chip_name.restype = None
-
-_snprintf_chip_name = SENSORS_LIB.sensors_snprintf_chip_name
-_snprintf_chip_name.argtypes = [c_char_p, c_size_t, CHIP_P]
-_snprintf_chip_name.restype = c_int
-_snprintf_chip_name.errcheck = _error_check
-
-_get_adapter_name = SENSORS_LIB.sensors_get_adapter_name
-_get_adapter_name.argtypes = [BUS_P]
-_get_adapter_name.restype = c_char_p
-
-_get_label = SENSORS_LIB.sensors_get_label
-_get_label.argtypes = [CHIP_P, FEATURE_P]
-_get_label.restype = c_char_p
-
-_get_value = SENSORS_LIB.sensors_get_value
-_get_value.argtypes = [CHIP_P, c_int, POINTER(c_double)]
-_get_value.restype = c_int
-_get_value.errcheck = _error_check
-
-#
-# TODO sensors_set_value()
-# TODO sensors_do_chip_sets()
-#
-
-_get_detected_chips = SENSORS_LIB.sensors_get_detected_chips
-_get_detected_chips.argtypes = [CHIP_P, POINTER(c_int)]
-_get_detected_chips.restype = CHIP_P
-
-_get_features = SENSORS_LIB.sensors_get_features
-_get_features.argtypes = [CHIP_P, POINTER(c_int)]
-_get_features.restype = FEATURE_P
-
-_get_all_subfeatures = SENSORS_LIB.sensors_get_all_subfeatures
-_get_all_subfeatures.argtypes = [CHIP_P, FEATURE_P, POINTER(c_int)]
-_get_all_subfeatures.restype = SUBFEATURE_P
-
-#
-# TODO sensors_get_subfeature() ?
-#
-
-
-def iter_detected_chips(chip_name='*-*'):
-    chip = Chip(chip_name)
-    number = c_int(0)
-    while True:
-        result = _get_detected_chips(byref(chip), byref(number))
-        if not result:
-            break
-        yield result.contents
+        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__()
\ No newline at end of file
index 1288931add6a8d3c56e5663526c54d89c00b3944..8078fcf8521399c64ad19cd8ad0006dc0ec3ece7 100644 (file)
@@ -3,7 +3,7 @@
 # Author: Pawel Krupa (paulfantom)
 
 from base import SimpleService
-import lm_sensors as sensors
+import new_sensors as sensors
 
 # default module values (can be overridden per job in `config`)
 # update_every = 2
@@ -49,6 +49,21 @@ CHARTS = {
         ]}
 }
 
+TYPE_MAP = {
+    0: 'voltage',
+    1: 'fan',
+    2: 'temperature',
+    3: 'power',
+    4: 'energy',
+    5: 'current',
+    6: 'humidity',
+    7: 'max_main',
+    16: 'vid',
+    17: 'intrusion',
+    18: 'max_other',
+    24: 'beep_enable'
+}
+
 
 class Service(SimpleService):
     def __init__(self, configuration=None, name=None):
@@ -60,11 +75,14 @@ class Service(SimpleService):
     def _get_data(self):
         data = {}
         try:
-            for chip in sensors.iter_detected_chips():
-                prefix = '_'.join(str(chip.path.decode()).split('/')[3:])
-                lines = {}
-                for feature in chip:
-                    data[prefix + "_" + str(feature.name.decode())] = feature.get_value() * 1000
+            for chip in sensors.ChipIterator():
+                prefix = sensors.chip_snprintf_name(chip)
+                for feature in sensors.FeatureIterator(chip):
+                    sfi = sensors.SubFeatureIterator(chip, feature)
+                    for sf in sfi:
+                        val = sensors.get_value(chip, sf.number)
+                        break
+                    data[prefix + "_" + str(feature.name.decode())] = int(val * 1000)
         except Exception as e:
             self.error(e)
             return None
@@ -74,57 +92,43 @@ class Service(SimpleService):
         return data
 
     def _create_definitions(self):
+        prev_chip = ""
         for type in ORDER:
-            for chip in sensors.iter_detected_chips():
-                prefix = '_'.join(str(chip.path.decode()).split('/')[3:])
-                name = ""
-                lines = []
-                pref = str(chip.prefix.decode())
-                if len(self.chips) != 0 and not any([ex.startswith(pref) for ex in self.chips]):
+            for chip in sensors.ChipIterator():
+                chip_name = sensors.chip_snprintf_name(chip)
+                if len(self.chips) != 0 and not any([chip_name.startswith(ex) for ex in self.chips]):
                     continue
-                pref = pref + '_' + str(chip.addr)
-                for feature in chip:
-                    try:
-                        float(feature.get_value())
-                    except ValueError:
-                        continue
-                    if feature.get_value() < 0:
+                for feature in sensors.FeatureIterator(chip):
+                    sfi = sensors.SubFeatureIterator(chip, feature)
+                    vals = [sensors.get_value(chip, sf.number) for sf in sfi]
+                    if vals[0] == 0:
                         continue
-                    if sensors.TYPE_DICT[feature.type] == type:
-                        name = pref + "_" + sensors.TYPE_DICT[feature.type]
-                        if name not in self.order:
-                            options = list(CHARTS[type]['options'])
-                            options[1] = pref + options[1]
-                            self.definitions[name] = {'options': options}
-                            self.definitions[name]['lines'] = []
-                            self.order.append(name)
+                    if TYPE_MAP[feature.type] == type:
+                        # create chart
+                        if chip_name != prev_chip:
+                            name = chip_name + "_" + TYPE_MAP[feature.type]
+                            if name not in self.order:
+                                self.order.append(name)
+                                chart_def = list(CHARTS[type]['options'])
+                                chart_def[1] = chip_name + chart_def[1]
+                                self.definitions[name] = {'options': chart_def}
+                                self.definitions[name]['lines'] = []
                         line = list(CHARTS[type]['lines'][0])
-                        line[0] = prefix + "_" + str(feature.name.decode())
-                        line[1] = str(feature.label)
+                        line[0] = chip_name + "_" + str(feature.name.decode())
+                        line[1] = sensors.get_label(chip, feature)
                         self.definitions[name]['lines'].append(line)
+                prev_chip = chip_name
 
     def check(self):
-        try:
-            self.chips = list(self.configuration['chips'])
-        except (KeyError, TypeError):
-            self.error("No path to log specified. Using all chips.")
-        try:
-            global ORDER
-            ORDER = list(self.configuration['types'])
-        except (KeyError, TypeError):
-            self.error("No path to log specified. Using all sensor types.")
         try:
             sensors.init()
         except Exception as e:
             self.error(e)
             return False
+
         try:
             self._create_definitions()
-        except:
-            return False
-
-        if len(self.definitions) == 0:
-            self.error("No sensors found")
+        except Exception as e:
+            self.error(e)
             return False
-
       return True
+        return True
No newline at end of file