1r"""
2Display hard drive temperatures.
3
4hddtemp is a small utility with daemon that gives the hard drive temperature
5via S.M.A.R.T. (Self-Monitoring, Analysis, and Reporting Technology). This
6module requires the user-defined hddtemp daemon to be running at all times.
7
8Configuration parameters:
9    cache_timeout: refresh interval for this module (default 10)
10    format: display format for this module (default '{format_hdd}')
11    format_hdd: display format for hard drives
12        (default '{name} [\?color=temperature {temperature}°{unit}]')
13    format_separator: show separator if more than one (default ' ')
14    thresholds: specify color thresholds to use
15        *(default [(19, 'skyblue'), (24, 'deepskyblue'), (25, 'lime'),
16        (41, 'yellow'), (46, 'orange'), (51, 'red'), (56, 'tomato')])*
17
18Format placeholders:
19    {format_hdd} format for hard drives
20
21format_hdd placeholders:
22    {name}        name, eg ADATA SP550
23    {path}        path, eg /dev/sda
24    {temperature} temperature, eg 32
25    {unit}        temperature unit, eg C
26
27Temperatures:
28    Less than 25°C: Too cold     (color deepskyblue)
29    25°C to 40°C: Ideal          (color good)
30    41°C to 50°C: Acceptable     (color degraded)
31    46°C to 50°C: Almost too hot (color orange)
32    More than 50°C: Too hot      (color bad)
33
34Color thresholds:
35    xxx: print a color based on the value of `xxx` placeholder
36
37Requires:
38    hddtemp: utility to monitor hard drive temperatures
39
40Bible of HDD failures:
41    Hard disk temperatures higher than 45°C led to higher failure rates.
42    Temperatures lower than 25°C led to higher failure rates as well.
43    Aging hard disk drives (3 years and older) were much more prone to
44    failure when their average temperatures were 40°C and higher.
45
46    Hard disk manufacturers often state the operating temperatures of
47    their hard disk drives to be between 0°C to 60°C. This can be misleading
48    because what they mean is that your hard disk will function at these
49    temperatures, but it doesn't tell you anything about how long they are
50    going to survive at this range.
51    http://www.buildcomputers.net/hdd-temperature.html
52
53Backblaze:
54    Overall, there is not a correlation between operating temperature and
55    failure rates The one exception is the Seagate Barracuda 1.5TB drives,
56    which fail slightly more when they run warmer. As long as you run drives
57    well within their allowed range of operating temperatures, keeping them
58    cooler doesn’t matter.
59    https://www.backblaze.com/blog/hard-drive-temperature-does-it-matter/
60
61Examples:
62```
63# compact the format
64hddtemp {
65    format = 'HDD {format_hdd}'
66    format_hdd = '\?color=temperature {temperature}°C'
67}
68
69# show paths instead of names
70hddtemp {
71    format_hdd = '{path} [\?color=temperature {temperature}°{unit}]'
72}
73
74# show more colors
75hddtemp {
76    gradients = True
77}
78```
79
80@author lasers
81
82SAMPLE OUTPUT
83[
84    {'full_text': u'ADATA SP550 '},
85    {'full_text': u'25°C ', 'color': '#00FF00'},
86    {'full_text': u'SanDisk SDSSDA240 '},
87    {'full_text': u'41°C', 'color': '#FFFF00'},
88]
89
90path
91[
92    {'full_text': '/dev/sda '}, {'color': '#00BFFF', u'full_text': '24°C '},
93    {'full_text': '/dev/sdb '}, {'color': '#00FF00', u'full_text': '25°C '},
94    {'full_text': '/dev/sdc '}, {'color': '#FF0000', u'full_text': '51°C'},
95]
96
97compact
98[
99    {'full_text': 'HDD '},
100    {'color': '#87CEEB', u'full_text': '19°C '},
101    {'color': '#00BFFF', u'full_text': '24°C '},
102    {'color': '#00FF00', u'full_text': '25°C '},
103    {'color': '#FFFF00', u'full_text': '41°C '},
104    {'color': '#FFA500', u'full_text': '46°C '},
105    {'color': '#FF0000', u'full_text': '51°C '},
106    {'color': '#FF6347', u'full_text': '56°C'},
107]
108"""
109
110from telnetlib import Telnet
111from string import printable
112
113
114class Py3status:
115    """
116    """
117
118    # available configuration parameters
119    cache_timeout = 10
120    format = "{format_hdd}"
121    format_hdd = r"{name} [\?color=temperature {temperature}°{unit}]"
122    format_separator = " "
123    thresholds = [
124        (19, "skyblue"),
125        (24, "deepskyblue"),
126        (25, "lime"),
127        (41, "yellow"),
128        (46, "orange"),
129        (51, "red"),
130        (56, "tomato"),
131    ]
132
133    def post_config_hook(self):
134        self.keys = ["path", "name", "temperature", "unit"]
135        self.cache_names = {}
136        self.thresholds_init = self.py3.get_color_names_list(self.format_hdd)
137
138    def hddtemp(self):
139        line = Telnet("localhost", 7634).read_all().decode("utf-8", "ignore")
140        new_data = []
141
142        for chunk in line[1:-1].split("||"):
143            hdd = dict(zip(self.keys, chunk.split("|")))
144            # workaround for hddtemp byte bug
145            try:
146                hdd["name"] = self.cache_names[hdd["name"]]
147            except KeyError:
148                key = "".join(x for x in hdd["name"] if x in printable).strip()
149                if key.endswith("G B") and key[-4].isdigit():
150                    key = "GB".join(key.rsplit("G B", 1))
151                hdd["name"] = self.cache_names[hdd["name"]] = key
152
153            for x in self.thresholds_init:
154                if x in hdd:
155                    self.py3.threshold_get_color(hdd[x], x)
156
157            new_data.append(self.py3.safe_format(self.format_hdd, hdd))
158
159        format_separator = self.py3.safe_format(self.format_separator)
160        format_hdd = self.py3.composite_join(format_separator, new_data)
161
162        return {
163            "cached_until": self.py3.time_in(self.cache_timeout),
164            "full_text": self.py3.safe_format(self.format, {"format_hdd": format_hdd}),
165        }
166
167
168if __name__ == "__main__":
169    """
170    Run module in test mode.
171    """
172    from py3status.module_test import module_test
173
174    module_test(Py3status)
175