~onox/awn-extras/sensors-monitor

« back to all changes in this revision

Viewing changes to src/sensors.py

  • Committer: onox
  • Date: 2008-08-11 15:43:56 UTC
  • Revision ID: denkpadje@gmail.com-20080811154356-yydjvxcxjb5a1clt
Initial import of Sensors Monitor applet

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2008  onox <denkpadje@gmail.com>
 
2
 
3
# This program is free software: you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation, version 2 of the License.
 
6
 
7
# This program is distributed in the hope that it will be useful,
 
8
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
# GNU General Public License for more details.
 
11
 
12
# You should have received a copy of the GNU General Public License
 
13
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
14
 
 
15
import os
 
16
import time
 
17
 
 
18
import gobject
 
19
import pygtk
 
20
pygtk.require('2.0')
 
21
import gtk
 
22
from gtk import gdk
 
23
from awn.extras import AWNLib
 
24
import cairo
 
25
 
 
26
from interfaces.sensor import Sensor
 
27
from interfaces.acpi import AcpiInterface
 
28
from interfaces.ibmacpi import IbmAcpiInterface
 
29
 
 
30
applet_name = "Sensors Monitor"
 
31
applet_version = "0.2.8"
 
32
applet_description = "An applet to monitor the various sensors in your system"
 
33
 
 
34
images_dir = os.path.join(os.path.dirname(__file__), "images")
 
35
 
 
36
# Logo of the applet, used as the applet's icon and shown in the GTK About dialog
 
37
applet_logo = os.path.join(images_dir, "sensors-applet.png")
 
38
 
 
39
# Interval in seconds between two successive updates of the sensors
 
40
update_sensors_interval = 2.0
 
41
 
 
42
"""
 
43
Range in percents of difference between very high and very low temperature.
 
44
 
 
45
If, for example, the very high temperature is 60 C and the very low temperature
 
46
is 20 C, then the high temperature is 85 C minus 25%. Low temperature is 20 C
 
47
plus 15%
 
48
"""
 
49
high_to_very_high_range = 25.0
 
50
very_low_to_low_range = 15.0
 
51
 
 
52
temperature_units = ["Celcius", "Fahrenheit", "Kelvin"]
 
53
 
 
54
interfaces = [AcpiInterface, IbmAcpiInterface]
 
55
 
 
56
"""
 
57
TODO:
 
58
FIXED 1) temperature unit kunnen uitlezen en opslaan
 
59
FIXED 2) weten welke unit een sensor heeft, en dat omrekenen naar de gewenste unit
 
60
3) ander plaatje kunnen kiezen (maar bij sommige sensoren moet het plaatje niet veranderbaar zijn)
 
61
4) alarm kunnen instellen, checken voor libnotify en notifyen als alarm afgaat
 
62
5) via dbus de cpufreq monitor met een algoritme downscalen als de temperature van de cpu high wordt
 
63
6) iets voor de titel en icon bedenken
 
64
7) meer backends toevoegen
 
65
"""
 
66
 
 
67
 
 
68
class SensorsApplet:
 
69
    """ An applet to monitor the various sensors in your system """
 
70
    
 
71
    def __init__(self, applet):
 
72
        self.applet = applet
 
73
        
 
74
        applet.title.set("Sensors")
 
75
        height = self.applet.get_height()
 
76
        applet.icon.set(gdk.pixbuf_new_from_file_at_size(applet_logo, height, height), True)
 
77
        
 
78
        self.active_interface = {}
 
79
        for interface in interfaces:
 
80
            print interface.name()
 
81
            if interface.interface_useable():
 
82
                self.active_interface[interface.name()] = interface(self.applet.settings)
 
83
        
 
84
        self.setup_context_menu()
 
85
        
 
86
        self.setup_main_dialog()
 
87
    
 
88
    def setup_context_menu(self):
 
89
        """ Creates a context menu to activate "Preferences" ("About" window
 
90
        is created automatically by AWNLib) """
 
91
        
 
92
        prefs = self.applet.dialog.new("preferences").vbox
 
93
        
 
94
        notebook = gtk.Notebook()
 
95
        notebook.set_border_width(6)
 
96
        prefs.add(notebook)
 
97
        
 
98
        default_values = {
 
99
            "update-interval": update_sensors_interval,
 
100
            "temperature-unit": temperature_units[0]
 
101
        }
 
102
        self.applet.settings.load(default_values)
 
103
        
 
104
        vbox_general = gtk.VBox(spacing=18)
 
105
        vbox_general.set_border_width(12)
 
106
        notebook.append_page(vbox_general, gtk.Label("General"))
 
107
        
 
108
        vbox = AWNLib.create_frame(vbox_general, "Display")
 
109
#        vbox_general.pack_start(frame, expand=False)
 
110
        
 
111
        hbox = gtk.HBox(spacing=13)
 
112
        
 
113
        # TODO fix underline
 
114
        label = gtk.Label("_Temperature unit:")
 
115
        label.set_use_underline(True)
 
116
        hbox.add(label)
 
117
        
 
118
        combobox = gtk.combo_box_new_text()
 
119
        for unit in temperature_units:
 
120
            combobox.append_text(unit)
 
121
        combobox.set_active(temperature_units.index(self.applet.settings["temperature-unit"]))
 
122
        combobox.connect("changed", self.changed_temperature_unit_cb)
 
123
        hbox.add(combobox)
 
124
        
 
125
        vbox.add(hbox)
 
126
        
 
127
        vbox = AWNLib.create_frame(vbox_general, "Update")
 
128
#        vbox_general.pack_start(frame, expand=False)
 
129
        
 
130
        hbox = gtk.HBox(spacing=13)
 
131
        
 
132
        # TODO fix underline
 
133
        label = gtk.Label("Update _interval (secs):")
 
134
        label.set_use_underline(True)
 
135
        hbox.add(label)
 
136
        
 
137
        adjustment = gtk.Adjustment(self.applet.settings["update-interval"], 1.5, 10, 0.5, 1.0) # TODO get value from settings
 
138
        spin_button = gtk.SpinButton(adjustment, 0.5, 1)
 
139
        spin_button.set_update_policy(gtk.UPDATE_IF_VALID)
 
140
        spin_button.connect("value-changed", self.changed_update_interval_cb)
 
141
        hbox.add(spin_button)
 
142
        
 
143
        vbox.add(hbox)
 
144
        
 
145
        vbox = AWNLib.create_frame(vbox_general, "Notifications")
 
146
#        vbox_general.pack_start(frame, expand=False)
 
147
        
 
148
        # TODO connect signal
 
149
        vbox.add(gtk.CheckButton("Display _notifications"))
 
150
        
 
151
        vbox_sensors = gtk.VBox(spacing=18)
 
152
        vbox_sensors.set_border_width(12)
 
153
        notebook.append_page(vbox_sensors, gtk.Label("Sensors"))
 
154
        
 
155
        self.sensors_store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gdk.Pixbuf, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN)
 
156
        
 
157
        for if_name, interface in self.active_interface.iteritems():
 
158
            parent = self.sensors_store.append(None, (None, if_name, None, None, None, False))
 
159
            for sensor in interface.get_sensors():
 
160
                image = gdk.pixbuf_new_from_file(os.path.join(images_dir, "sensors-applet-" + sensor.get_type() + ".png"))
 
161
                self.sensors_store.append(parent, (if_name, sensor.get_name(), image, sensor.get_label(), sensor.is_enabled(), True))
 
162
        
 
163
        tree_view = gtk.TreeView(self.sensors_store)
 
164
        tree_view.set_rules_hint(True)
 
165
        
 
166
        tree_view.append_column(gtk.TreeViewColumn("Sensor", gtk.CellRendererText(), text=1))
 
167
        tree_view.append_column(gtk.TreeViewColumn("Icon", gtk.CellRendererPixbuf(), pixbuf=2, visible=5))
 
168
        cell = gtk.CellRendererText()
 
169
        cell.set_property("editable", True)
 
170
        cell.connect("edited", self.edited_sensor_label_cb)
 
171
        tree_view.append_column(gtk.TreeViewColumn("Label", cell, text=3, visible=5))
 
172
        cell = gtk.CellRendererToggle()
 
173
        cell.connect("toggled", self.toggled_sensor_enabled_cb)
 
174
        tree_view.append_column(gtk.TreeViewColumn("Enabled", cell, active=4, visible=5))
 
175
        
 
176
        scrolled_window = gtk.ScrolledWindow()
 
177
        scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
178
        scrolled_window.add(tree_view)
 
179
        vbox_sensors.add(scrolled_window)
 
180
    
 
181
    def changed_temperature_unit_cb(self, combobox):
 
182
        self.applet.settings["temperature-unit"] = temperature_units[combobox.get_active()]
 
183
        
 
184
        """
 
185
        No forced update is necessary because the dialog will be hidden when the
 
186
        user clicks on the combobox
 
187
        """
 
188
    
 
189
    def changed_update_interval_cb(self, button):
 
190
        self.applet.settings["update-interval"] = interval = button.get_value()
 
191
        
 
192
        # Change the interval of the update callback
 
193
        self.update_callback.change_interval(interval)
 
194
    
 
195
    def edited_sensor_label_cb(self, renderer, path, label):
 
196
        row = self.sensors_store[self.sensors_store.get_iter_from_string(path)]
 
197
        
 
198
        assert row[0] is not None
 
199
        
 
200
        row[3] = label
 
201
        
 
202
        self.active_interface[row[0]].get_sensor(row[1]).set_label(row[3])
 
203
    
 
204
    def toggled_sensor_enabled_cb(self, renderer, path):
 
205
        row = self.sensors_store[self.sensors_store.get_iter_from_string(path)]
 
206
        
 
207
        assert row[0] is not None
 
208
        
 
209
        row[4] = not row[4]
 
210
        
 
211
        self.active_interface[row[0]].get_sensor(row[1]).set_enabled(row[4])
 
212
    
 
213
    def setup_main_dialog(self):
 
214
        self.dialog = self.applet.dialog.new("sensors-dialog")
 
215
        
 
216
        self.sensors_vbox = None
 
217
        self.update_callback = self.applet.timing.register(self.update_sensor_values, self.applet.settings["update-interval"], False)
 
218
        self.update_sensor_values()
 
219
        
 
220
        self.dialog_focus_lost_time = time.time()
 
221
        self.applet.connect("button-press-event", self.button_press_event_cb)
 
222
        self.dialog.connect("focus-out-event", self.dialog_focus_out_cb)
 
223
    
 
224
    def button_press_event_cb(self, widget, event):
 
225
        if event.button == 1 and (time.time() - self.dialog_focus_lost_time) > 0.01:
 
226
            if not self.dialog.is_active():
 
227
                self.update_callback.start()
 
228
                self.update_sensor_values()
 
229
            else:
 
230
                self.update_callback.stop()
 
231
            
 
232
            self.applet.dialog.toggle("sensors-dialog")
 
233
    
 
234
    def dialog_focus_out_cb(self, dialog, event):
 
235
        self.dialog_focus_lost_time = time.time()
 
236
        self.update_callback.stop()
 
237
    
 
238
    def update_sensor_values(self):
 
239
        if self.sensors_vbox is not None:
 
240
            self.sensors_vbox.destroy()
 
241
        self.sensors_vbox = gtk.VBox(spacing=6)
 
242
        self.dialog.add(self.sensors_vbox)
 
243
        
 
244
        # TODO add a message (label) if no sensors have been added
 
245
        
 
246
        for interface in self.active_interface.itervalues():
 
247
            # Only draw sensors that are enabled
 
248
            sensors = [sensor for sensor in interface.get_sensors() if sensor.is_enabled()]
 
249
            
 
250
            # TODO this code smells
 
251
            if len(sensors) == 0:
 
252
                continue
 
253
            
 
254
            vbox = AWNLib.create_frame(self.sensors_vbox, interface.name())
 
255
            
 
256
            i = 1
 
257
            table = gtk.Table(columns=2)
 
258
            table.set_col_spacings(6)
 
259
            vbox.add(table)
 
260
            
 
261
            for sensor in sensors:
 
262
                table.resize(i, 2)
 
263
                i = i + 1
 
264
                
 
265
                image = gdk.pixbuf_new_from_file(os.path.join(images_dir, "sensors-applet-" + sensor.get_type() + ".png"))
 
266
                if sensor.get_sensor_type() is Sensor.UNIT_TEMPERATURE:
 
267
                    overlay_image = gdk.pixbuf_new_from_file(os.path.join(images_dir, self.get_overlay_image_name(sensor) + "-temp-icon.png")) 
 
268
                    overlay_image.composite(image, 0, 0, 22, 22, 0, 0, 1.0, 1.0, gdk.INTERP_NEAREST, 255)
 
269
                image = gtk.image_new_from_pixbuf(image)
 
270
                image.props.xalign = 0.0
 
271
                
 
272
                label = gtk.Label(sensor.get_label())
 
273
                label.props.xalign = 0.0
 
274
                if sensor.get_sensor_type() is Sensor.UNIT_TEMPERATURE:
 
275
                    temp_unit = self.applet.settings["temperature-unit"]
 
276
                    value = int(self.convert_temp_unit(sensor.get_value(), sensor.get_unit(), temp_unit))
 
277
                    
 
278
                    if temp_unit != "Kelvin":
 
279
                        unit_label = str(value) + " \xc2\xb0" + temp_unit[0]
 
280
                    else:
 
281
                        unit_label = str(value) + " " + temp_unit[0]
 
282
                else:
 
283
                    unit_label = str(sensor.get_value()) + " " + sensor.get_unit()
 
284
                unit_label = gtk.Label(unit_label)
 
285
                unit_label.props.xalign = 1.0
 
286
                
 
287
                table.attach(image, 0, 1, i - 1, i, xoptions=gtk.FILL)
 
288
                table.attach(label, 1, 2, i - 1, i, xoptions=gtk.FILL)
 
289
                table.attach(unit_label, 2, 3, i - 1, i, xoptions=gtk.FILL)
 
290
        
 
291
        if self.update_callback.is_started():
 
292
            self.dialog.show_all()
 
293
    
 
294
    def get_overlay_image_name(self, sensor):
 
295
        temperature = sensor.get_value()
 
296
        
 
297
        vh = sensor.get_very_high_temperature()
 
298
        vl = sensor.get_very_low_temperature()
 
299
        
 
300
        diff = vh - vl
 
301
        h = int(vh - (high_to_very_high_range / 100) * diff)
 
302
        l = int(vl + (very_low_to_low_range / 100) * diff)
 
303
        
 
304
        temperatures = {"very-high": (1000, vh), "high": (vh - 1, h), "normal": (h - 1, l), "low": (l - 1, vl), "very-low": (vl - 1, -1000)}
 
305
        return [key for key, value in temperatures.iteritems() if temperature <= value[0] and temperature >= value[1]][0]
 
306
    
 
307
    def convert_temp_unit(self, value, old_unit, new_unit):
 
308
        if old_unit == new_unit:
 
309
            return value
 
310
        
 
311
        if old_unit == "Celcius":
 
312
            if new_unit == "Kelvin":
 
313
                return value + 273.0
 
314
            elif new_unit == "Fahrenheit":
 
315
                return value * 9.0 / 5.0 + 32.0
 
316
        elif old_unit == "Kelvin":
 
317
            if new_unit == "Celcius":
 
318
                return value - 273.0
 
319
            elif new_unit == "Fahrenheit":
 
320
                return (value - 273.0) * 9.0 / 5.0 + 32.0
 
321
        elif old_unit == "Fahrenheit":
 
322
            if new_unit == "Celcius":
 
323
                return 5.0 * (value - 32.0) / 9.0
 
324
            elif new_unit == "Kelvin":
 
325
                return 5.0 * (value - 32.0) / 9.0 + 273.0
 
326
 
 
327
 
 
328
if __name__ == "__main__":
 
329
    applet = AWNLib.initiate({"name": applet_name, "short": "sensors",
 
330
        "version": applet_version,
 
331
        "description": applet_description,
 
332
        "logo": applet_logo,
 
333
        "author": "onox",
 
334
        "copyright-year": 2008,
 
335
        "authors": ["onox <denkpadje@gmail.com>"],
 
336
        "settings-per-instance": True})
 
337
    SensorsApplet(applet)
 
338
    AWNLib.start(applet)
 
 
b'\\ No newline at end of file'