Completed
Push — master ( 793552...8b5b19 )
by Nicolas
05:48 queued 01:54
created

glances.plugins.glances_sensors.to_fahrenheit()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2019 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
"""Sensors plugin."""
21
22
import psutil
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
introduced by
Unable to import 'psutil'
Loading history...
23
import warnings
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
introduced by
standard import "import warnings" should be placed before "import psutil"
Loading history...
24
25
from glances.logger import logger
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
26
from glances.compat import iteritems, to_fahrenheit
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
27
from glances.timer import Counter
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
28
from glances.plugins.sensors.glances_batpercent import Plugin as BatPercentPlugin
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
import missing from __future__ import absolute_import
Loading history...
29
from glances.plugins.sensors.glances_hddtemp import Plugin as HddTempPlugin
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
30
from glances.plugins.glances_plugin import GlancesPlugin
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
31
32
SENSOR_TEMP_UNIT = 'C'
33
SENSOR_FAN_UNIT = 'R'
34
35
36
class Plugin(GlancesPlugin):
37
    """Glances sensors plugin.
38
39
    The stats list includes both sensors and hard disks stats, if any.
40
    The sensors are already grouped by chip type and then sorted by name.
41
    The hard disks are already sorted by name.
42
    """
43
44
    def __init__(self, args=None, config=None):
45
        """Init the plugin."""
46
        super(Plugin, self).__init__(args=args,
47
                                     config=config,
48
                                     stats_init_value=[])
49
50
        start_duration = Counter()
51
52
        # Init the sensor class
53
        start_duration.reset()
54
        self.glancesgrabsensors = GlancesGrabSensors()
55
        logger.debug("Generic sensor plugin init duration: {} seconds".format(start_duration.get()))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (100/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
56
57
        # Instance for the HDDTemp Plugin in order to display the hard disks
58
        # temperatures
59
        start_duration.reset()
60
        self.hddtemp_plugin = HddTempPlugin(args=args, config=config)
61
        logger.debug("HDDTemp sensor plugin init duration: {} seconds".format(start_duration.get()))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (100/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
62
63
        # Instance for the BatPercent in order to display the batteries
64
        # capacities
65
        start_duration.reset()
66
        self.batpercent_plugin = BatPercentPlugin(args=args, config=config)
67
        logger.debug("Battery sensor plugin init duration: {} seconds".format(start_duration.get()))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (100/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
68
69
        # We want to display the stat in the curse interface
70
        self.display_curse = True
71
72
    def get_key(self):
73
        """Return the key of the list."""
74
        return 'label'
75
76
    @GlancesPlugin._check_decorator
77
    @GlancesPlugin._log_result_decorator
78
    def update(self):
79
        """Update sensors stats using the input method."""
80
        # Init new stats
81
        stats = self.get_init_value()
82
83
        if self.input_method == 'local':
84
            # Update stats using the dedicated lib
85
            stats = []
86
            # Get the temperature
87
            try:
88
                temperature = self.__set_type(self.glancesgrabsensors.get('temperature_core'),
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (94/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
89
                                              'temperature_core')
90
            except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
91
                logger.error("Cannot grab sensors temperatures (%s)" % e)
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
92
            else:
93
                # Append temperature
94
                stats.extend(temperature)
95
            # Get the FAN speed
96
            try:
97
                fan_speed = self.__set_type(self.glancesgrabsensors.get('fan_speed'),
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (85/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
98
                                            'fan_speed')
99
            except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
100
                logger.error("Cannot grab FAN speed (%s)" % e)
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
101
            else:
102
                # Append FAN speed
103
                stats.extend(fan_speed)
104
            # Update HDDtemp stats
105
            try:
106
                hddtemp = self.__set_type(self.hddtemp_plugin.update(),
107
                                          'temperature_hdd')
108
            except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
109
                logger.error("Cannot grab HDD temperature (%s)" % e)
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
110
            else:
111
                # Append HDD temperature
112
                stats.extend(hddtemp)
113
            # Update batteries stats
114
            try:
115
                batpercent = self.__set_type(self.batpercent_plugin.update(),
116
                                             'battery')
117
            except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
118
                logger.error("Cannot grab battery percent (%s)" % e)
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
119
            else:
120
                # Append Batteries %
121
                stats.extend(batpercent)
122
123
        elif self.input_method == 'snmp':
124
            # Update stats using SNMP
125
            # No standard:
126
            # http://www.net-snmp.org/wiki/index.php/Net-SNMP_and_lm-sensors_on_Ubuntu_10.04
127
128
            pass
129
130
        # Set the alias for each stat
131
        for stat in stats:
132
            alias = self.has_alias(stat["label"].lower())
133
            if alias:
134
                stat["label"] = alias
135
136
        # Update the stats
137
        self.stats = stats
138
139
        return self.stats
140
141
    def __set_type(self, stats, sensor_type):
142
        """Set the plugin type.
143
144
        4 types of stats is possible in the sensors plugin:
145
        - Core temperature: 'temperature_core'
146
        - Fan speed: 'fan_speed'
147
        - HDD temperature: 'temperature_hdd'
148
        - Battery capacity: 'battery'
149
        """
150
        for i in stats:
151
            # Set the sensors type
152
            i.update({'type': sensor_type})
153
            # also add the key name
154
            i.update({'key': self.get_key()})
155
156
        return stats
157
158
    def update_views(self):
159
        """Update stats views."""
160
        # Call the father's method
161
        super(Plugin, self).update_views()
162
163
        # Add specifics informations
164
        # Alert
165
        for i in self.stats:
166
            if not i['value']:
167
                continue
168
            if i['type'] == 'battery':
169
                self.views[i[self.get_key()]]['value']['decoration'] = self.get_alert(100 - i['value'], header=i['type'])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (121/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
170
            else:
171
                self.views[i[self.get_key()]]['value']['decoration'] = self.get_alert(i['value'], header=i['type'])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (115/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
172
173
    def msg_curse(self, args=None, max_width=None):
174
        """Return the dict to display in the curse interface."""
175
        # Init the return message
176
        ret = []
177
178
        # Only process if stats exist and display plugin enable...
179
        if not self.stats or self.is_disable():
180
            return ret
181
182
        # Max size for the interface name
183
        name_max_width = max_width - 12
184
185
        # Header
186
        msg = '{:{width}}'.format('SENSORS', width=name_max_width)
187
        ret.append(self.curse_add_line(msg, "TITLE"))
188
189
        # Stats
190
        for i in self.stats:
191
            # Do not display anything if no battery are detected
192
            if i['type'] == 'battery' and i['value'] == []:
193
                continue
194
            # New line
195
            ret.append(self.curse_new_line())
196
            msg = '{:{width}}'.format(i["label"][:name_max_width],
197
                                      width=name_max_width)
198
            ret.append(self.curse_add_line(msg))
199
            if i['value'] in (b'ERR', b'SLP', b'UNK', b'NOS'):
200
                msg = '{:>13}'.format(i['value'])
201
                ret.append(self.curse_add_line(
202
                    msg, self.get_views(item=i[self.get_key()],
203
                                        key='value',
204
                                        option='decoration')))
205
            else:
206
                if (args.fahrenheit and i['type'] != 'battery' and
207
                        i['type'] != 'fan_speed'):
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (remove 4 spaces).
Loading history...
208
                    value = to_fahrenheit(i['value'])
209
                    unit = 'F'
210
                else:
211
                    value = i['value']
212
                    unit = i['unit']
213
                try:
214
                    msg = '{:>13.0f}{}'.format(value, unit)
215
                    ret.append(self.curse_add_line(
216
                        msg, self.get_views(item=i[self.get_key()],
217
                                            key='value',
218
                                            option='decoration')))
219
                except (TypeError, ValueError):
220
                    pass
221
222
        return ret
223
224
225
class GlancesGrabSensors(object):
226
    """Get sensors stats."""
227
228
    def __init__(self):
229
        """Init sensors stats."""
230
        # Temperatures
231
        self.init_temp = False
232
        self.stemps = {}
233
        try:
234
            # psutil>=5.1.0, Linux-only
235
            self.stemps = psutil.sensors_temperatures()
236
        except AttributeError:
237
            logger.debug("Cannot grab temperatures. Platform not supported.")
238
        else:
239
            self.init_temp = True
240
            # Solve an issue #1203 concerning a RunTimeError warning message displayed
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (86/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
241
            # in the curses interface.
242
            warnings.filterwarnings("ignore")
243
244
        # Fans
245
        self.init_fan = False
246
        self.sfans = {}
247
        try:
248
            # psutil>=5.2.0, Linux-only
249
            self.sfans = psutil.sensors_fans()
250
        except AttributeError:
251
            logger.debug("Cannot grab fans speed. Platform not supported.")
252
        else:
253
            self.init_fan = True
254
255
        # !!! Disable Fan: High CPU consumption with psutil 5.2.0 or higher
256
        # Delete the two followings lines when corrected (https://github.com/giampaolo/psutil/issues/1199)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
257
        # Correct and tested with PsUtil 5.6.1 (Ubuntu 18.04)
258
        # self.init_fan = False
259
        # logger.debug("Fan speed sensors disable (see https://github.com/giampaolo/psutil/issues/1199)")
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (105/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
260
261
        # Init the stats
262
        self.reset()
263
264
    def reset(self):
265
        """Reset/init the stats."""
266
        self.sensors_list = []
267
268
    def __update__(self):
269
        """Update the stats."""
270
        # Reset the list
271
        self.reset()
272
273
        if not self.init_temp:
274
            return self.sensors_list
275
276
        # Temperatures sensors
277
        self.sensors_list.extend(self.build_sensors_list(SENSOR_TEMP_UNIT))
278
279
        # Fans sensors
280
        self.sensors_list.extend(self.build_sensors_list(SENSOR_FAN_UNIT))
281
282
        return self.sensors_list
283
284
    def build_sensors_list(self, type):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
285
        """Build the sensors list depending of the type.
286
287
        type: SENSOR_TEMP_UNIT or SENSOR_FAN_UNIT
288
289
        output: a list
290
        """
291
        ret = []
292
        if type == SENSOR_TEMP_UNIT and self.init_temp:
293
            input_list = self.stemps
294
            self.stemps = psutil.sensors_temperatures()
295
        elif type == SENSOR_FAN_UNIT and self.init_fan:
296
            input_list = self.sfans
297
            self.sfans = psutil.sensors_fans()
298
        else:
299
            return ret
300
        for chipname, chip in iteritems(input_list):
301
            i = 1
302
            for feature in chip:
303
                sensors_current = {}
304
                # Sensor name
305
                if feature.label == '':
306
                    sensors_current['label'] = chipname + ' ' + str(i)
307
                else:
308
                    sensors_current['label'] = feature.label
309
                # Fan speed and unit
310
                sensors_current['value'] = int(feature.current)
311
                sensors_current['unit'] = type
312
                # Add sensor to the list
313
                ret.append(sensors_current)
314
                i += 1
315
        return ret
316
317
    def get(self, sensor_type='temperature_core'):
318
        """Get sensors list."""
319
        self.__update__()
320
        if sensor_type == 'temperature_core':
321
            ret = [s for s in self.sensors_list if s['unit'] == SENSOR_TEMP_UNIT]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
322
        elif sensor_type == 'fan_speed':
323
            ret = [s for s in self.sensors_list if s['unit'] == SENSOR_FAN_UNIT]
324
        else:
325
            # Unknown type
326
            logger.debug("Unknown sensor type %s" % sensor_type)
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
327
            ret = []
328
        return ret
329