]> arthur.barton.de Git - netdata.git/blob - python.d/python_modules/lm_sensors.py
sensors
[netdata.git] / python.d / python_modules / lm_sensors.py
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     10: 'vid',
36     11: 'intrusion',
37     12: 'max_other',
38     18: '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         )
120
121     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
205
206     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')
227
228     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