@cryptotaxi247 / netdata-1 / commits / 1779a2b46

use sensors.py library from Pavel Rojtberg instead of PySensors. fix #781

paulfantom committed Aug 14, 2016 at 19:51 UTC 1779a2b46a9f6115201f6dd8fdad57a9d21d015d
3 files changed +303 -351
LICENSE.md
+3 -3
@@ -138,10 +138,10 @@ connectivity is not available.
138 Copyright 2015, Joseph Huckaby
139 [MIT License](https://github.com/jhuckaby/pixl-xml)
140
141 -- [PySensors](https://bitbucket.org/blackjack/pysensors)
141 +- [sensors](https://github.com/paroj/sensors.py)
142
143 - Copyright 2014, Marc 'BlackJack' Rintsch
144 - [LGPL 2.1 License](https://bitbucket.org/blackjack/pysensors)
143 + Copyright 2014, Pavel Rojtberg
144 + [LGPL 2.1 License](http://opensource.org/licenses/LGPL-2.1)
145
146 - [PyYAML](https://bitbucket.org/blackjack/pysensors)
147
python.d/python_modules/lm_sensors.py
+251 -303
@@ -1,309 +1,257 @@
1 -#!/usr/bin/env python
2 -# -*- coding: utf-8 -*-
3 -import os
4 -
5 -from ctypes import CDLL, c_char_p, c_int, c_void_p, c_uint, c_double, byref, Structure, get_errno,\
6 - POINTER, c_short, c_size_t, create_string_buffer
7 -from ctypes.util import find_library
8 -
9 -version_info = (0, 0, 3)
10 -
11 -__version__ = '.'.join(map(str, version_info))
12 -__date__ = '2014-08-17'
13 -__author__ = "Marc 'BlackJack' Rintsch"
14 -__contact__ = 'marc@rintsch.de'
15 -__license__ = 'LGPL v2.1'
16 -
17 -API_VERSION = 4
18 -DEFAULT_CONFIG_FILENAME = '/etc/sensors3.conf'
19 -
20 -LIB_FILENAME = os.environ.get('SENSORS_LIB') or find_library('sensors')
21 -SENSORS_LIB = CDLL(LIB_FILENAME)
22 -VERSION = c_char_p.in_dll(SENSORS_LIB, 'libsensors_version').value
23 -MAJOR_VERSION = version_info[0]
24 -STDC_LIB = CDLL(find_library('c'), use_errno=True)
25 -
26 -TYPE_DICT = {
27 - 0: 'voltage',
28 - 1: 'fan',
29 - 2: 'temperature',
30 - 3: 'power',
31 - 4: 'energy',
32 - 5: 'current',
33 - 6: 'humidity',
34 - 7: 'max_main',
35 - 16: 'vid',
36 - 17: 'intrusion',
37 - 18: 'max_other',
38 - 24: 'beep_enable'
39 -}
40 -
41 -class SensorsError(Exception):
42 - def __init__(self, message, error_number=None):
43 - Exception.__init__(self, message)
44 - self.error_number = error_number
45 -
46 -
47 -def _error_check(result, _func, _arguments):
48 - if result < 0:
49 - raise SensorsError(_strerror(result), result)
50 - return result
51 -
52 -_strerror = SENSORS_LIB.sensors_strerror
53 -_strerror.argtypes = [c_int]
54 -_strerror.restype = c_char_p
55 -
56 -_init = SENSORS_LIB.sensors_init
57 -_init.argtypes = [c_void_p]
58 -_init.restype = c_int
59 -_init.errcheck = _error_check
60 -
61 -cleanup = SENSORS_LIB.sensors_cleanup
62 -cleanup.argtypes = None
63 -cleanup.restype = None
64 -
65 -
66 -def init(config_filename=DEFAULT_CONFIG_FILENAME):
67 - file_p = STDC_LIB.fopen(config_filename.encode('utf-8'), b'r')
68 - if file_p is None:
69 - error_number = get_errno()
70 - raise OSError(error_number, os.strerror(error_number), config_filename)
71 - try:
72 - _init(file_p)
73 - finally:
74 - STDC_LIB.fclose(file_p)
75 -
76 -
77 -class Subfeature(Structure):
78 - _fields_ = [
79 - ('name', c_char_p),
80 - ('number', c_int),
81 - ('type', c_int),
82 - ('mapping', c_int),
83 - ('flags', c_uint),
84 - ]
85 -
86 - def __repr__(self):
87 - return '<%s name=%r number=%d type=%d mapping=%d flags=%08x>' % (
88 - self.__class__.__name__,
89 - self.name,
90 - self.number,
91 - self.type,
92 - self.mapping,
93 - self.flags
94 - )
95 -
96 - def get_value(self):
97 - result = c_double()
98 - _get_value(byref(self.parent.chip), self.number, byref(result))
99 - return result.value
100 -
101 -SUBFEATURE_P = POINTER(Subfeature)
102 -
103 -
104 -class Feature(Structure):
105 - _fields_ = [
106 - ('name', c_char_p),
107 - ('number', c_int),
108 - ('type', c_int),
109 - ('_first_subfeature', c_int),
110 - ('_padding1', c_int),
111 - ]
112 -
113 - def __repr__(self):
114 - return '<%s name=%r number=%r type=%r>' % (
115 - self.__class__.__name__,
116 - self.name,
117 - self.number,
118 - self.type
119 - )
1 +"""
2 +@package sensors.py
3 +Python Bindings for libsensors3
4 +
5 +use the documentation of libsensors for the low level API.
6 +see example.py for high level API usage.
7 +
8 +@author: Pavel Rojtberg (http://www.rojtberg.net)
9 +@see: https://github.com/paroj/sensors.py
10 +@copyright: LGPLv2 (same as libsensors) <http://opensource.org/licenses/LGPL-2.1>
11 +"""
12 +
13 +from ctypes import *
14 +import ctypes.util
15 +
16 +_libc = cdll.LoadLibrary(ctypes.util.find_library("c"))
17 +# see https://github.com/paroj/sensors.py/issues/1
18 +_libc.free.argtypes = [c_void_p]
19 +_hdl = cdll.LoadLibrary(ctypes.util.find_library("sensors"))
20 +
21 +version = c_char_p.in_dll(_hdl, "libsensors_version").value.decode("ascii")
22 +
23 +
24 +class bus_id(Structure):
25 + _fields_ = [("type", c_short),
26 + ("nr", c_short)]
27 +
28 +
29 +class chip_name(Structure):
30 + _fields_ = [("prefix", c_char_p),
31 + ("bus", bus_id),
32 + ("addr", c_int),
33 + ("path", c_char_p)]
34 +
35 +
36 +class feature(Structure):
37 + _fields_ = [("name", c_char_p),
38 + ("number", c_int),
39 + ("type", c_int)]
40 +
41 + # sensors_feature_type
42 + IN = 0x00
43 + FAN = 0x01
44 + TEMP = 0x02
45 + POWER = 0x03
46 + ENERGY = 0x04
47 + CURR = 0x05
48 + HUMIDITY = 0x06
49 + MAX_MAIN = 0x7
50 + VID = 0x10
51 + INTRUSION = 0x11
52 + MAX_OTHER = 0x12
53 + BEEP_ENABLE = 0x18
54 +
55 +
56 +class subfeature(Structure):
57 + _fields_ = [("name", c_char_p),
58 + ("number", c_int),
59 + ("type", c_int),
60 + ("mapping", c_int),
61 + ("flags", c_uint)]
62 +
63 +
64 +_hdl.sensors_get_detected_chips.restype = POINTER(chip_name)
65 +_hdl.sensors_get_features.restype = POINTER(feature)
66 +_hdl.sensors_get_all_subfeatures.restype = POINTER(subfeature)
67 +_hdl.sensors_get_label.restype = c_void_p # return pointer instead of str so we can free it
68 +_hdl.sensors_get_adapter_name.restype = c_char_p # docs do not say whether to free this or not
69 +_hdl.sensors_strerror.restype = c_char_p
70 +
71 +### RAW API ###
72 +MODE_R = 1
73 +MODE_W = 2
74 +COMPUTE_MAPPING = 4
75 +
76 +
77 +def init(cfg_file=None):
78 + file = _libc.fopen(cfg_file.encode("utf-8"), "r") if cfg_file is not None else None
79 +
80 + if _hdl.sensors_init(file) != 0:
81 + raise Exception("sensors_init failed")
82 +
83 + if file is not None:
84 + _libc.fclose(file)
85 +
86 +
87 +def cleanup():
88 + _hdl.sensors_cleanup()
89 +
90 +
91 +def parse_chip_name(orig_name):
92 + ret = chip_name()
93 + err = _hdl.sensors_parse_chip_name(orig_name.encode("utf-8"), byref(ret))
94 +
95 + if err < 0:
96 + raise Exception(strerror(err))
97 +
98 + return ret
99 +
100 +
101 +def strerror(errnum):
102 + return _hdl.sensors_strerror(errnum).decode("utf-8")
103 +
104 +
105 +def free_chip_name(chip):
106 + _hdl.sensors_free_chip_name(byref(chip))
107 +
108 +
109 +def get_detected_chips(match, nr):
110 + """
111 + @return: (chip, next nr to query)
112 + """
113 + _nr = c_int(nr)
114 +
115 + if match is not None:
116 + match = byref(match)
117 +
118 + chip = _hdl.sensors_get_detected_chips(match, byref(_nr))
119 + chip = chip.contents if bool(chip) else None
120 + return chip, _nr.value
121 +
122 +
123 +def chip_snprintf_name(chip, buffer_size=200):
124 + """
125 + @param buffer_size defaults to the size used in the sensors utility
126 + """
127 + ret = create_string_buffer(buffer_size)
128 + err = _hdl.sensors_snprintf_chip_name(ret, buffer_size, byref(chip))
129 +
130 + if err < 0:
131 + raise Exception(strerror(err))
132 +
133 + return ret.value.decode("utf-8")
134 +
135 +
136 +def do_chip_sets(chip):
137 + """
138 + @attention this function was not tested
139 + """
140 + err = _hdl.sensors_do_chip_sets(byref(chip))
141 + if err < 0:
142 + raise Exception(strerror(err))
143 +
144 +
145 +def get_adapter_name(bus):
146 + return _hdl.sensors_get_adapter_name(byref(bus)).decode("utf-8")
147 +
148 +
149 +def get_features(chip, nr):
150 + """
151 + @return: (feature, next nr to query)
152 + """
153 + _nr = c_int(nr)
154 + feature = _hdl.sensors_get_features(byref(chip), byref(_nr))
155 + feature = feature.contents if bool(feature) else None
156 + return feature, _nr.value
157 +
158 +
159 +def get_label(chip, feature):
160 + ptr = _hdl.sensors_get_label(byref(chip), byref(feature))
161 + val = cast(ptr, c_char_p).value.decode("utf-8")
162 + _libc.free(ptr)
163 + return val
164 +
165 +
166 +def get_all_subfeatures(chip, feature, nr):
167 + """
168 + @return: (subfeature, next nr to query)
169 + """
170 + _nr = c_int(nr)
171 + subfeature = _hdl.sensors_get_all_subfeatures(byref(chip), byref(feature), byref(_nr))
172 + subfeature = subfeature.contents if bool(subfeature) else None
173 + return subfeature, _nr.value
174 +
175 +
176 +def get_value(chip, subfeature_nr):
177 + val = c_double()
178 + err = _hdl.sensors_get_value(byref(chip), subfeature_nr, byref(val))
179 + if err < 0:
180 + raise Exception(strerror(err))
181 + return val.value
182 +
183 +
184 +def set_value(chip, subfeature_nr, value):
185 + """
186 + @attention this function was not tested
187 + """
188 + val = c_double(value)
189 + err = _hdl.sensors_set_value(byref(chip), subfeature_nr, byref(val))
190 + if err < 0:
191 + raise Exception(strerror(err))
192 +
193 +
194 +### Convenience API ###
195 +class ChipIterator:
196 + def __init__(self, match=None):
197 + self.match = parse_chip_name(match) if match is not None else None
198 + self.nr = 0
199
200 def __iter__(self):
122 - number = c_int(0)
123 - while True:
124 - result_p = _get_all_subfeatures(
125 - byref(self.chip),
126 - byref(self),
127 - byref(number)
128 - )
129 - if not result_p:
130 - break
131 - result = result_p.contents
132 - result.chip = self.chip
133 - result.parent = self
134 - yield result
135 -
136 - @property
137 - def label(self):
138 - #
139 - # TODO Maybe this is a memory leak!
140 - #
141 - return _get_label(byref(self.chip), byref(self)).decode('utf-8')
142 -
143 - def get_value(self):
144 - #
145 - # TODO Is the first always the correct one for all feature types?
146 - #
147 - return next(iter(self)).get_value()
148 -
149 -FEATURE_P = POINTER(Feature)
150 -
151 -
152 -class Bus(Structure):
153 - TYPE_ANY = -1
154 - NR_ANY = -1
155 -
156 - _fields_ = [
157 - ('type', c_short),
158 - ('nr', c_short),
159 - ]
160 -
161 - def __str__(self):
162 - return (
163 - '*' if self.type == self.TYPE_ANY
164 - else _get_adapter_name(byref(self)).decode('utf-8')
165 - )
166 -
167 - def __repr__(self):
168 - return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.nr)
169 -
170 - @property
171 - def has_wildcards(self):
172 - return self.type == self.TYPE_ANY or self.nr == self.NR_ANY
173 -
174 -BUS_P = POINTER(Bus)
175 -
176 -
177 -class Chip(Structure):
178 - #
179 - # TODO Move common stuff into `AbstractChip` class.
180 - #
181 - _fields_ = [
182 - ('prefix', c_char_p),
183 - ('bus', Bus),
184 - ('addr', c_int),
185 - ('path', c_char_p),
186 - ]
187 -
188 - PREFIX_ANY = None
189 - ADDR_ANY = -1
190 -
191 - def __new__(cls, *args):
192 - result = super(Chip, cls).__new__(cls)
193 - if args:
194 - _parse_chip_name(args[0].encode('utf-8'), byref(result))
195 - return result
196 -
197 - def __init__(self, *_args):
198 - Structure.__init__(self)
199 - #
200 - # Need to bind the following to the instance so it is available in
201 - # `__del__()` when the interpreter shuts down.
202 - #
203 - self._free_chip_name = _free_chip_name
204 - self.byref = byref
201 + return self
202 +
203 + def __next__(self):
204 + chip, self.nr = get_detected_chips(self.match, self.nr)
205 +
206 + if chip is None:
207 + raise StopIteration
208 +
209 + return chip
210
211 def __del__(self):
207 - if self._b_needsfree_:
208 - self._free_chip_name(self.byref(self))
209 -
210 - def __repr__(self):
211 - return '<%s prefix=%r bus=%r addr=%r path=%r>' % (
212 - (
213 - self.__class__.__name__,
214 - self.prefix,
215 - self.bus,
216 - self.addr,
217 - self.path
218 - )
219 - )
220 -
221 - def __str__(self):
222 - buffer_size = 200
223 - result = create_string_buffer(buffer_size)
224 - used = _snprintf_chip_name(result, len(result), byref(self))
225 - assert used < buffer_size
226 - return result.value.decode('utf-8')
212 + if self.match is not None:
213 + free_chip_name(self.match)
214 +
215 + def next(self): # python2 compability
216 + return self.__next__()
217 +
218 +
219 +class FeatureIterator:
220 + def __init__(self, chip):
221 + self.chip = chip
222 + self.nr = 0
223 +
224 + def __iter__(self):
225 + return self
226 +
227 + def __next__(self):
228 + feature, self.nr = get_features(self.chip, self.nr)
229 +
230 + if feature is None:
231 + raise StopIteration
232 +
233 + return feature
234 +
235 + def next(self): # python2 compability
236 + return self.__next__()
237 +
238 +
239 +class SubFeatureIterator:
240 + def __init__(self, chip, feature):
241 + self.chip = chip
242 + self.feature = feature
243 + self.nr = 0
244
245 def __iter__(self):
229 - number = c_int(0)
230 - while True:
231 - result_p = _get_features(byref(self), byref(number))
232 - if not result_p:
233 - break
234 - result = result_p.contents
235 - result.chip = self
236 - yield result
237 -
238 - @property
239 - def adapter_name(self):
240 - return str(self.bus)
241 -
242 - @property
243 - def has_wildcards(self):
244 - return (
245 - self.prefix == self.PREFIX_ANY
246 - or self.addr == self.ADDR_ANY
247 - or self.bus.has_wildcards
248 - )
249 -
250 -CHIP_P = POINTER(Chip)
251 -
252 -
253 -_parse_chip_name = SENSORS_LIB.sensors_parse_chip_name
254 -_parse_chip_name.argtypes = [c_char_p, CHIP_P]
255 -_parse_chip_name.restype = c_int
256 -_parse_chip_name.errcheck = _error_check
257 -
258 -_free_chip_name = SENSORS_LIB.sensors_free_chip_name
259 -_free_chip_name.argtypes = [CHIP_P]
260 -_free_chip_name.restype = None
261 -
262 -_snprintf_chip_name = SENSORS_LIB.sensors_snprintf_chip_name
263 -_snprintf_chip_name.argtypes = [c_char_p, c_size_t, CHIP_P]
264 -_snprintf_chip_name.restype = c_int
265 -_snprintf_chip_name.errcheck = _error_check
266 -
267 -_get_adapter_name = SENSORS_LIB.sensors_get_adapter_name
268 -_get_adapter_name.argtypes = [BUS_P]
269 -_get_adapter_name.restype = c_char_p
270 -
271 -_get_label = SENSORS_LIB.sensors_get_label
272 -_get_label.argtypes = [CHIP_P, FEATURE_P]
273 -_get_label.restype = c_char_p
274 -
275 -_get_value = SENSORS_LIB.sensors_get_value
276 -_get_value.argtypes = [CHIP_P, c_int, POINTER(c_double)]
277 -_get_value.restype = c_int
278 -_get_value.errcheck = _error_check
279 -
280 -#
281 -# TODO sensors_set_value()
282 -# TODO sensors_do_chip_sets()
283 -#
284 -
285 -_get_detected_chips = SENSORS_LIB.sensors_get_detected_chips
286 -_get_detected_chips.argtypes = [CHIP_P, POINTER(c_int)]
287 -_get_detected_chips.restype = CHIP_P
288 -
289 -_get_features = SENSORS_LIB.sensors_get_features
290 -_get_features.argtypes = [CHIP_P, POINTER(c_int)]
291 -_get_features.restype = FEATURE_P
292 -
293 -_get_all_subfeatures = SENSORS_LIB.sensors_get_all_subfeatures
294 -_get_all_subfeatures.argtypes = [CHIP_P, FEATURE_P, POINTER(c_int)]
295 -_get_all_subfeatures.restype = SUBFEATURE_P
296 -
297 -#
298 -# TODO sensors_get_subfeature() ?
299 -#
300 -
301 -
302 -def iter_detected_chips(chip_name='*-*'):
303 - chip = Chip(chip_name)
304 - number = c_int(0)
305 - while True:
306 - result = _get_detected_chips(byref(chip), byref(number))
307 - if not result:
308 - break
309 - yield result.contents
246 + return self
247 +
248 + def __next__(self):
249 + subfeature, self.nr = get_all_subfeatures(self.chip, self.feature, self.nr)
250 +
251 + if subfeature is None:
252 + raise StopIteration
253 +
254 + return subfeature
255 +
256 + def next(self): # python2 compability
257 + return self.__next__()
\ No newline at end of file
python.d/sensors.chart.py
+49 -45
@@ -3,7 +3,7 @@
3 # Author: Pawel Krupa (paulfantom)
4
5 from base import SimpleService
6 -import lm_sensors as sensors
6 +import new_sensors as sensors
7
8 # default module values (can be overridden per job in `config`)
9 # update_every = 2
@@ -49,6 +49,21 @@ CHARTS = {
49 ]}
50 }
51
52 +TYPE_MAP = {
53 + 0: 'voltage',
54 + 1: 'fan',
55 + 2: 'temperature',
56 + 3: 'power',
57 + 4: 'energy',
58 + 5: 'current',
59 + 6: 'humidity',
60 + 7: 'max_main',
61 + 16: 'vid',
62 + 17: 'intrusion',
63 + 18: 'max_other',
64 + 24: 'beep_enable'
65 +}
66 +
67
68 class Service(SimpleService):
69 def __init__(self, configuration=None, name=None):
@@ -60,11 +75,14 @@ class Service(SimpleService):
75 def _get_data(self):
76 data = {}
77 try:
63 - for chip in sensors.iter_detected_chips():
64 - prefix = '_'.join(str(chip.path.decode()).split('/')[3:])
65 - lines = {}
66 - for feature in chip:
67 - data[prefix + "_" + str(feature.name.decode())] = feature.get_value() * 1000
78 + for chip in sensors.ChipIterator():
79 + prefix = sensors.chip_snprintf_name(chip)
80 + for feature in sensors.FeatureIterator(chip):
81 + sfi = sensors.SubFeatureIterator(chip, feature)
82 + for sf in sfi:
83 + val = sensors.get_value(chip, sf.number)
84 + break
85 + data[prefix + "_" + str(feature.name.decode())] = int(val * 1000)
86 except Exception as e:
87 self.error(e)
88 return None
@@ -74,57 +92,43 @@ class Service(SimpleService):
92 return data
93
94 def _create_definitions(self):
95 + prev_chip = ""
96 for type in ORDER:
78 - for chip in sensors.iter_detected_chips():
79 - prefix = '_'.join(str(chip.path.decode()).split('/')[3:])
80 - name = ""
81 - lines = []
82 - pref = str(chip.prefix.decode())
83 - if len(self.chips) != 0 and not any([ex.startswith(pref) for ex in self.chips]):
97 + for chip in sensors.ChipIterator():
98 + chip_name = sensors.chip_snprintf_name(chip)
99 + if len(self.chips) != 0 and not any([chip_name.startswith(ex) for ex in self.chips]):
100 continue
85 - pref = pref + '_' + str(chip.addr)
86 - for feature in chip:
87 - try:
88 - float(feature.get_value())
89 - except ValueError:
90 - continue
91 - if feature.get_value() < 0:
101 + for feature in sensors.FeatureIterator(chip):
102 + sfi = sensors.SubFeatureIterator(chip, feature)
103 + vals = [sensors.get_value(chip, sf.number) for sf in sfi]
104 + if vals[0] == 0:
105 continue
93 - if sensors.TYPE_DICT[feature.type] == type:
94 - name = pref + "_" + sensors.TYPE_DICT[feature.type]
95 - if name not in self.order:
96 - options = list(CHARTS[type]['options'])
97 - options[1] = pref + options[1]
98 - self.definitions[name] = {'options': options}
99 - self.definitions[name]['lines'] = []
100 - self.order.append(name)
106 + if TYPE_MAP[feature.type] == type:
107 + # create chart
108 + if chip_name != prev_chip:
109 + name = chip_name + "_" + TYPE_MAP[feature.type]
110 + if name not in self.order:
111 + self.order.append(name)
112 + chart_def = list(CHARTS[type]['options'])
113 + chart_def[1] = chip_name + chart_def[1]
114 + self.definitions[name] = {'options': chart_def}
115 + self.definitions[name]['lines'] = []
116 line = list(CHARTS[type]['lines'][0])
102 - line[0] = prefix + "_" + str(feature.name.decode())
103 - line[1] = str(feature.label)
117 + line[0] = chip_name + "_" + str(feature.name.decode())
118 + line[1] = sensors.get_label(chip, feature)
119 self.definitions[name]['lines'].append(line)
120 + prev_chip = chip_name
121
122 def check(self):
107 - try:
108 - self.chips = list(self.configuration['chips'])
109 - except (KeyError, TypeError):
110 - self.error("No path to log specified. Using all chips.")
111 - try:
112 - global ORDER
113 - ORDER = list(self.configuration['types'])
114 - except (KeyError, TypeError):
115 - self.error("No path to log specified. Using all sensor types.")
123 try:
124 sensors.init()
125 except Exception as e:
126 self.error(e)
127 return False
128 +
129 try:
130 self._create_definitions()
123 - except:
124 - return False
125 -
126 - if len(self.definitions) == 0:
127 - self.error("No sensors found")
131 + except Exception as e:
132 + self.error(e)
133 return False
129 -
130 - return True
134 + return True
\ No newline at end of file