[Sugar-devel] [PATCH] battery frame device: replace HAL with UPower

Sascha Silbe sascha-pgp at silbe.org
Sat May 1 08:55:49 EDT 2010


HAL is deprecated, UPower is the designated replacement w.r.t. power
supplies. As a bonus the time displayed is now correct (calculated at
run-time by UPower, self-adjusting to changes in power consumption).

Tested on XO-1 running Debian Squeeze with stock UPower (0.9.2-1) and kernel
patches that are in the process of getting included upstream (i.e. at OLPC).
---
 extensions/deviceicon/battery.py |  219 +++++++++++++++++++------------------
 1 files changed, 112 insertions(+), 107 deletions(-)

diff --git a/extensions/deviceicon/battery.py b/extensions/deviceicon/battery.py
index edfcce4..ee4ad55 100644
--- a/extensions/deviceicon/battery.py
+++ b/extensions/deviceicon/battery.py
@@ -16,8 +16,9 @@
 
 import logging
 from gettext import gettext as _
-import gconf
+import sys
 
+import gconf
 import gobject
 import gtk
 import dbus
@@ -30,6 +31,7 @@ from sugar.graphics.xocolor import XoColor
 
 from jarabe.frame.frameinvoker import FrameWidgetInvoker
 
+
 _ICON_NAME = 'battery'
 
 _STATUS_CHARGING = 0
@@ -37,34 +39,35 @@ _STATUS_DISCHARGING = 1
 _STATUS_FULLY_CHARGED = 2
 _STATUS_NOT_PRESENT = 3
 
-_LEVEL_PROP = 'battery.charge_level.percentage'
-_CHARGING_PROP = 'battery.rechargeable.is_charging'
-_DISCHARGING_PROP = 'battery.rechargeable.is_discharging'
-_PRESENT_PROP = 'battery.present'
+_UP_TYPE_BATTERY = 2
+
+_UP_STATE_UNKNOWN = 0
+_UP_STATE_CHARGING = 1
+_UP_STATE_DISCHARGING = 2
+_UP_STATE_EMPTY = 3
+_UP_STATE_FULL = 4
+_UP_STATE_CHARGE_PENDING = 5
+_UP_STATE_DISCHARGE_PENDING = 6
+
+_WARN_MIN_PERCENTAGE = 15
+
 
 class DeviceView(TrayIcon):
 
     FRAME_POSITION_RELATIVE = 102
 
-    def __init__(self, udi):
-        client = gconf.client_get_default()        
+    def __init__(self, battery):
+        client = gconf.client_get_default()
         self._color = XoColor(client.get_string('/desktop/sugar/user/color'))
 
         TrayIcon.__init__(self, icon_name=_ICON_NAME, xo_color=self._color)
 
         self.set_palette_invoker(FrameWidgetInvoker(self))
 
-        self._model = DeviceModel(udi)
+        self._model = DeviceModel(battery)
         self.palette = BatteryPalette(_('My Battery'))
         self.palette.set_group_id('frame')
-
-        self._model.connect('notify::level',
-                            self._battery_status_changed_cb)
-        self._model.connect('notify::charging',
-                            self._battery_status_changed_cb)
-        self._model.connect('notify::discharging',
-                            self._battery_status_changed_cb)
-        self._model.connect('notify::present',
+        self._model.connect('updated',
                             self._battery_status_changed_cb)
         self._update_info()
 
@@ -86,27 +89,30 @@ class DeviceView(TrayIcon):
                                           style.COLOR_WHITE.get_svg()))
         elif self._model.props.discharging:
             status = _STATUS_DISCHARGING
-            if current_level <= 15:
+            if current_level <= _WARN_MIN_PERCENTAGE:
                 badge_name = 'emblem-warning'
         else:
             status = _STATUS_FULLY_CHARGED
 
-        self.icon.props.icon_name = get_icon_state(name, current_level, step=-5)
+        self.icon.props.icon_name = get_icon_state(name, current_level,
+            step=-5)
         self.icon.props.xo_color = xo_color
         self.icon.props.badge_name = badge_name
 
-        self.palette.set_level(current_level)
-        self.palette.set_status(status)
+        self.palette.set_info(current_level, self._model.props.time_remaining,
+            status)
 
-    def _battery_status_changed_cb(self, pspec, param):
+    def _battery_status_changed_cb(self, *args):
         self._update_info()
 
+
 class BatteryPalette(Palette):
 
     def __init__(self, primary_text):
         Palette.__init__(self, primary_text)
-            
         self._level = 0
+        self._time = 0
+        self._status = _STATUS_NOT_PRESENT
         self._progress_bar = gtk.ProgressBar()
         self._progress_bar.set_size_request(
             style.zoom(style.GRID_CELL_SIZE * 4), -1)
@@ -122,29 +128,29 @@ class BatteryPalette(Palette):
         self._progress_widget = vbox
         self.set_content(self._progress_widget)
 
-    def set_level(self, percent):
+    def set_info(self, percent, seconds, status):
         self._level = percent
-        fraction = percent / 100.0
-        self._progress_bar.set_fraction(fraction)
+        self._time = seconds
+        self._status = status
+        self._progress_bar.set_fraction(percent / 100.0)
+        self._update_secondary()
 
-    def set_status(self, status):
-        current_level = self._level
+    def _update_secondary(self):
         secondary_text = ''
-        status_text = '%s%%' % current_level
+        status_text = '%s%%' % self._level
 
         progress_widget = self._progress_widget
-        if status == _STATUS_NOT_PRESENT:
+        if self._status == _STATUS_NOT_PRESENT:
             secondary_text = _('Removed')
             progress_widget = None
-        elif status == _STATUS_CHARGING:
+        elif self._status == _STATUS_CHARGING:
             secondary_text = _('Charging')
-        elif status == _STATUS_DISCHARGING:
-            if current_level <= 15:
+        elif self._status == _STATUS_DISCHARGING:
+            if self._level <= _WARN_MIN_PERCENTAGE:
                 secondary_text = _('Very little power remaining')
             else:
-                #TODO: make this less of an wild/educated guess
-                minutes_remaining = int(current_level / 0.59)
-                remaining_hourpart = minutes_remaining / 60
+                minutes_remaining = self._time // 60
+                remaining_hourpart = minutes_remaining // 60
                 remaining_minpart = minutes_remaining % 60
                 secondary_text = _('%(hour)d:%(min).2d remaining') % \
                         {'hour': remaining_hourpart, 'min': remaining_minpart}
@@ -155,97 +161,96 @@ class BatteryPalette(Palette):
         self.props.secondary_text = secondary_text
         self._status_label.set_text(status_text)
 
+
 class DeviceModel(gobject.GObject):
     __gproperties__ = {
-        'level'       : (int, None, None, 0, 100, 0,
-                         gobject.PARAM_READABLE),
-        'charging'    : (bool, None, None, False,
-                         gobject.PARAM_READABLE),
-        'discharging' : (bool, None, None, False,
-                         gobject.PARAM_READABLE),
-        'present'     : (bool, None, None, False,
-                         gobject.PARAM_READABLE)
+        'level': (int, None, None, 0, 100, 0, gobject.PARAM_READABLE),
+        'time-remaining': (int, None, None, 0, sys.maxint, 0,
+            gobject.PARAM_READABLE),  # unit: seconds
+        'charging': (bool, None, None, False, gobject.PARAM_READABLE),
+        'discharging': (bool, None, None, False, gobject.PARAM_READABLE),
+        'present': (bool, None, None, False, gobject.PARAM_READABLE),
     }
 
-    def __init__(self, udi):
-        gobject.GObject.__init__(self)
-        
-        bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
-        proxy = bus.get_object('org.freedesktop.Hal', udi,
-                               follow_name_owner_changes=True)
-        self._battery = dbus.Interface(proxy, 'org.freedesktop.Hal.Device')
-        bus.add_signal_receiver(self._battery_changed,
-                                'PropertyModified',
-                                'org.freedesktop.Hal.Device',
-                                'org.freedesktop.Hal',
-                                udi)
-
-        self._level = self._get_level()
-        self._charging = self._get_charging()
-        self._discharging = self._get_discharging()
-        self._present = self._get_present()
-
-    def _get_level(self):
-        try:
-            return self._battery.GetProperty(_LEVEL_PROP)
-        except dbus.DBusException:
-            logging.error('Cannot access %s', _LEVEL_PROP)
-            return 0
-
-    def _get_charging(self):
-        try:
-            return self._battery.GetProperty(_CHARGING_PROP)
-        except dbus.DBusException:
-            logging.error('Cannot access %s', _CHARGING_PROP)
-            return False
+    __gsignals__ = {
+        'updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+    }
 
-    def _get_discharging(self):
+    def __init__(self, battery):
+        gobject.GObject.__init__(self)
+        self._battery = battery
+        self._battery_props_iface = dbus.Interface(self._battery,
+            'org.freedesktop.DBus.Properties')
+        self._battery.connect_to_signal('Changed', self._battery_changed,
+            dbus_interface='org.freedesktop.UPower.Device')
+        self._fetch()
+
+    def _fetch(self):
+        """Get current values from UPower."""
+        # pylint: disable-msg=W0201
         try:
-            return self._battery.GetProperty(_DISCHARGING_PROP)
+            dbus_props = self._battery_props_iface.GetAll(
+                'org.freedesktop.UPower.Device')
         except dbus.DBusException:
-            logging.error('Cannot access %s', _DISCHARGING_PROP)
-            return False
+            logging.error('Cannot access battery properties')
+            dbus_props = {}
 
-    def _get_present(self):
-        try:
-            return self._battery.GetProperty(_PRESENT_PROP)
-        except dbus.DBusException:
-            logging.error('Cannot access %s', _PRESENT_PROP)
-            return False
+        self._level = dbus_props.get('Percentage', 0)
+        self._state = dbus_props.get('State', _UP_STATE_UNKNOWN)
+        self._present = dbus_props.get('IsPresent', False)
+        self._time_to_empty = dbus_props.get('TimeToEmpty', 0)
+        self._time_to_full = dbus_props.get('TimeToFull', 0)
 
     def do_get_property(self, pspec):
+        """Return current value of given GObject property."""
         if pspec.name == 'level':
-            return self._level 
+            return self._level
         if pspec.name == 'charging':
-            return self._charging
+            return self._state == _UP_STATE_CHARGING
         if pspec.name == 'discharging':
-            return self._discharging
+            return self._state == _UP_STATE_DISCHARGING
         if pspec.name == 'present':
             return self._present
+        if pspec.name == 'time-remaining':
+            if self._state == _UP_STATE_CHARGING:
+                return self._time_to_full
+            if self._state == _UP_STATE_DISCHARGING:
+                return self._time_to_empty
+            return 0
 
     def get_type(self):
         return 'battery'
 
-    def _battery_changed(self, num_changes, changes_list):
-        for change in changes_list:
-            if change[0] == _LEVEL_PROP:
-                self._level = self._get_level()
-                self.notify('level')
-            elif change[0] == _CHARGING_PROP:
-                self._charging = self._get_charging()
-                self.notify('charging')
-            elif change[0] == _DISCHARGING_PROP:
-                self._discharging = self._get_discharging()
-                self.notify('discharging')
-            elif change[0] == _PRESENT_PROP:
-                self._present = self._get_present()
-                self.notify('present')
+    def _battery_changed(self, *args):
+        old_level = self._level
+        old_state = self._state
+        old_present = self._present
+        old_time = self.props.time_remaining
+        self._fetch()
+        if self._level != old_level:
+            self.notify('level')
+        if self._state != old_state:
+            self.notify('charging')
+            self.notify('discharging')
+        if self._present != old_present:
+            self.notify('present')
+        if self.props.time_remaining != old_time:
+            self.notify('time-remaining')
+
+        self.emit('updated')
+
 
 def setup(tray):
     bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
-    proxy = bus.get_object('org.freedesktop.Hal',
-                            '/org/freedesktop/Hal/Manager')
-    hal_manager = dbus.Interface(proxy, 'org.freedesktop.Hal.Manager')
-
-    for udi in hal_manager.FindDeviceByCapability('battery'):
-        tray.add_device(DeviceView(udi))
+    up_proxy = bus.get_object('org.freedesktop.UPower',
+                            '/org/freedesktop/UPower')
+    upower = dbus.Interface(up_proxy, 'org.freedesktop.UPower')
+
+    for battery_path in upower.EnumerateDevices():
+        battery = bus.get_object('org.freedesktop.UPower', battery_path)
+        batt_prop_iface = dbus.Interface(battery,
+            'org.freedesktop.DBus.Properties')
+        device_type = batt_prop_iface.Get('org.freedesktop.UPower.Device',
+            'Type')
+        if device_type == _UP_TYPE_BATTERY:
+            tray.add_device(DeviceView(battery))
-- 
1.7.0



More information about the Sugar-devel mailing list