sensors.chart.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. # -*- coding: utf-8 -*-
  2. # Description: sensors netdata python.d plugin
  3. # Author: Pawel Krupa (paulfantom)
  4. # SPDX-License-Identifier: GPL-3.0-or-later
  5. from collections import defaultdict
  6. from bases.FrameworkServices.SimpleService import SimpleService
  7. from third_party import lm_sensors as sensors
  8. ORDER = [
  9. 'temperature',
  10. 'fan',
  11. 'voltage',
  12. 'current',
  13. 'power',
  14. 'energy',
  15. 'humidity',
  16. ]
  17. # This is a prototype of chart definition which is used to dynamically create self.definitions
  18. CHARTS = {
  19. 'temperature': {
  20. 'options': [None, 'Temperature', 'Celsius', 'temperature', 'sensors.temperature', 'line'],
  21. 'lines': [
  22. [None, None, 'absolute', 1, 1000]
  23. ]
  24. },
  25. 'voltage': {
  26. 'options': [None, 'Voltage', 'Volts', 'voltage', 'sensors.voltage', 'line'],
  27. 'lines': [
  28. [None, None, 'absolute', 1, 1000]
  29. ]
  30. },
  31. 'current': {
  32. 'options': [None, 'Current', 'Ampere', 'current', 'sensors.current', 'line'],
  33. 'lines': [
  34. [None, None, 'absolute', 1, 1000]
  35. ]
  36. },
  37. 'power': {
  38. 'options': [None, 'Power', 'Watt', 'power', 'sensors.power', 'line'],
  39. 'lines': [
  40. [None, None, 'absolute', 1, 1000]
  41. ]
  42. },
  43. 'fan': {
  44. 'options': [None, 'Fans speed', 'Rotations/min', 'fans', 'sensors.fan', 'line'],
  45. 'lines': [
  46. [None, None, 'absolute', 1, 1000]
  47. ]
  48. },
  49. 'energy': {
  50. 'options': [None, 'Energy', 'Joule', 'energy', 'sensors.energy', 'line'],
  51. 'lines': [
  52. [None, None, 'incremental', 1, 1000]
  53. ]
  54. },
  55. 'humidity': {
  56. 'options': [None, 'Humidity', 'Percent', 'humidity', 'sensors.humidity', 'line'],
  57. 'lines': [
  58. [None, None, 'absolute', 1, 1000]
  59. ]
  60. }
  61. }
  62. LIMITS = {
  63. 'temperature': [-127, 1000],
  64. 'voltage': [-127, 127],
  65. 'current': [-127, 127],
  66. 'fan': [0, 65535]
  67. }
  68. TYPE_MAP = {
  69. 0: 'voltage',
  70. 1: 'fan',
  71. 2: 'temperature',
  72. 3: 'power',
  73. 4: 'energy',
  74. 5: 'current',
  75. 6: 'humidity',
  76. # 7: 'max_main',
  77. # 16: 'vid',
  78. # 17: 'intrusion',
  79. # 18: 'max_other',
  80. # 24: 'beep_enable'
  81. }
  82. class Service(SimpleService):
  83. def __init__(self, configuration=None, name=None):
  84. SimpleService.__init__(self, configuration=configuration, name=name)
  85. self.order = list()
  86. self.definitions = dict()
  87. self.chips = configuration.get('chips')
  88. self.priority = 60000
  89. def get_data(self):
  90. seen, data = dict(), dict()
  91. try:
  92. for chip in sensors.ChipIterator():
  93. chip_name = sensors.chip_snprintf_name(chip)
  94. seen[chip_name] = defaultdict(list)
  95. for feat in sensors.FeatureIterator(chip):
  96. if feat.type not in TYPE_MAP:
  97. continue
  98. feat_type = TYPE_MAP[feat.type]
  99. feat_name = str(feat.name.decode())
  100. feat_label = sensors.get_label(chip, feat)
  101. feat_limits = LIMITS.get(feat_type)
  102. sub_feat = next(sensors.SubFeatureIterator(chip, feat)) # current value
  103. if not sub_feat:
  104. continue
  105. try:
  106. v = sensors.get_value(chip, sub_feat.number)
  107. except sensors.SensorsError:
  108. continue
  109. if v is None:
  110. continue
  111. seen[chip_name][feat_type].append((feat_name, feat_label))
  112. if feat_limits and (v < feat_limits[0] or v > feat_limits[1]):
  113. continue
  114. data[chip_name + '_' + feat_name] = int(v * 1000)
  115. except sensors.SensorsError as error:
  116. self.error(error)
  117. return None
  118. self.update_sensors_charts(seen)
  119. return data or None
  120. def update_sensors_charts(self, seen):
  121. for chip_name, feat in seen.items():
  122. if self.chips and not any([chip_name.startswith(ex) for ex in self.chips]):
  123. continue
  124. for feat_type, sub_feat in feat.items():
  125. if feat_type not in ORDER or feat_type not in CHARTS:
  126. continue
  127. chart_id = '{}_{}'.format(chip_name, feat_type)
  128. if chart_id in self.charts:
  129. continue
  130. params = [chart_id] + list(CHARTS[feat_type]['options'])
  131. new_chart = self.charts.add_chart(params)
  132. new_chart.params['priority'] = self.get_chart_priority(feat_type)
  133. for name, label in sub_feat:
  134. lines = list(CHARTS[feat_type]['lines'][0])
  135. lines[0] = chip_name + '_' + name
  136. lines[1] = label
  137. new_chart.add_dimension(lines)
  138. def check(self):
  139. try:
  140. sensors.init()
  141. except sensors.SensorsError as error:
  142. self.error(error)
  143. return False
  144. self.priority = self.charts.priority
  145. return bool(self.get_data() and self.charts)
  146. def get_chart_priority(self, feat_type):
  147. for i, v in enumerate(ORDER):
  148. if v == feat_type:
  149. return self.priority + i
  150. return self.priority