diff options
Diffstat (limited to 'extensions/deviceicon/network.py')
-rw-r--r-- | extensions/deviceicon/network.py | 1054 |
1 files changed, 1054 insertions, 0 deletions
diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py new file mode 100644 index 0000000..96713fb --- /dev/null +++ b/extensions/deviceicon/network.py @@ -0,0 +1,1054 @@ +# +# Copyright (C) 2008 One Laptop Per Child +# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer +# Copyright (C) 2009 Paraguay Educa, Martin Abente +# Copyright (C) 2010 Plan Ceibal, Daniel Castelo +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from gettext import gettext as _ +import logging +import hashlib +import socket +import struct +import datetime +import time +import gtk +import glib +import gobject +import gconf +import dbus + +from sugar.graphics.icon import get_icon_state +from sugar.graphics import style +from sugar.graphics.palette import Palette +from sugar.graphics.toolbutton import ToolButton +from sugar.graphics.tray import TrayIcon +from sugar.graphics.menuitem import MenuItem +from sugar.graphics.icon import Icon +from sugar.graphics import xocolor +from sugar import profile + +from jarabe.model import network +from jarabe.frame.frameinvoker import FrameWidgetInvoker +from jarabe.view.pulsingicon import PulsingIcon + + +IP_ADDRESS_TEXT_TEMPLATE = _('IP address: %s') + +_GSM_STATE_NOT_READY = 0 +_GSM_STATE_DISCONNECTED = 1 +_GSM_STATE_CONNECTING = 2 +_GSM_STATE_CONNECTED = 3 +_GSM_STATE_FAILED = 4 + + +class WirelessPalette(Palette): + __gtype_name__ = 'SugarWirelessPalette' + + __gsignals__ = { + 'deactivate-connection': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([])), + } + + def __init__(self, primary_text): + Palette.__init__(self, label=primary_text) + + self._disconnect_item = None + + self._channel_label = gtk.Label() + self._channel_label.props.xalign = 0.0 + self._channel_label.show() + + self._ip_address_label = gtk.Label() + + self._info = gtk.VBox() + + def _padded(child, xalign=0, yalign=0.5): + padder = gtk.Alignment(xalign=xalign, yalign=yalign, + xscale=1, yscale=0.33) + padder.set_padding(style.DEFAULT_SPACING, + style.DEFAULT_SPACING, + style.DEFAULT_SPACING, + style.DEFAULT_SPACING) + padder.add(child) + return padder + + self._info.pack_start(_padded(self._channel_label)) + self._info.pack_start(_padded(self._ip_address_label)) + self._info.show_all() + + self._disconnect_item = MenuItem(_('Disconnect')) + icon = Icon(icon_size=gtk.ICON_SIZE_MENU, icon_name='media-eject') + self._disconnect_item.set_image(icon) + self._disconnect_item.connect('activate', + self.__disconnect_activate_cb) + self.menu.append(self._disconnect_item) + + def set_connecting(self): + label = glib.markup_escape_text(_('Connecting...')) + self.props.secondary_text = label + + def _set_connected(self, iaddress): + self.set_content(self._info) + self.props.secondary_text = glib.markup_escape_text(_('Connected')) + self._set_ip_address(iaddress) + self._disconnect_item.show() + + def set_connected_with_frequency(self, frequency, iaddress): + self._set_connected(iaddress) + self._set_frequency(frequency) + + def set_connected_with_channel(self, channel, iaddress): + self._set_connected(iaddress) + self._set_channel(channel) + + def set_disconnected(self): + label = glib.markup_escape_text(_('No wireless connection')) + self.props.primary_text = label + self.props.secondary_text = '' + self._disconnect_item.hide() + self.set_content(None) + + def __disconnect_activate_cb(self, menuitem): + self.emit('deactivate-connection') + + def _set_frequency(self, frequency): + channel = network.frequency_to_channel(frequency) + self._set_channel(channel) + + def _set_channel(self, channel): + self._channel_label.set_text('%s: %d' % (_('Channel'), channel)) + + def _set_ip_address(self, ip_address): + if ip_address is not None: + ip_address_text = IP_ADDRESS_TEXT_TEMPLATE % \ + socket.inet_ntoa(struct.pack('I', ip_address)) + else: + ip_address_text = "" + self._ip_address_label.set_text(ip_address_text) + + +class WiredPalette(Palette): + __gtype_name__ = 'SugarWiredPalette' + + def __init__(self): + label = glib.markup_escape_text(_('Wired Network')) + Palette.__init__(self, primary_text=label) + + self._speed_label = gtk.Label() + self._speed_label.props.xalign = 0.0 + self._speed_label.show() + + self._ip_address_label = gtk.Label() + + self._info = gtk.VBox() + + def _padded(child, xalign=0, yalign=0.5): + padder = gtk.Alignment(xalign=xalign, yalign=yalign, + xscale=1, yscale=0.33) + padder.set_padding(style.DEFAULT_SPACING, + style.DEFAULT_SPACING, + style.DEFAULT_SPACING, + style.DEFAULT_SPACING) + padder.add(child) + return padder + + self._info.pack_start(_padded(self._speed_label)) + self._info.pack_start(_padded(self._ip_address_label)) + self._info.show_all() + + self.set_content(self._info) + self.props.secondary_text = glib.markup_escape_text(_('Connected')) + + def set_connected(self, speed, iaddress): + self._speed_label.set_text('%s: %d Mb/s' % (_('Speed'), speed)) + self._set_ip_address(iaddress) + + def _inet_ntoa(self, iaddress): + address = ['%s' % ((iaddress >> i) % 256) for i in [0, 8, 16, 24]] + return '.'.join(address) + + def _set_ip_address(self, ip_address): + if ip_address is not None: + ip_address_text = IP_ADDRESS_TEXT_TEMPLATE % \ + socket.inet_ntoa(struct.pack('I', ip_address)) + else: + ip_address_text = "" + self._ip_address_label.set_text(ip_address_text) + + +class GsmPalette(Palette): + __gtype_name__ = 'SugarGsmPalette' + + __gsignals__ = { + 'gsm-connect': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'gsm-disconnect': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + } + + def __init__(self): + label = glib.markup_escape_text(_('Wireless modem')) + Palette.__init__(self, primary_text=label) + + self._current_state = None + self._failed_connection = False + + self._toggle_state_item = MenuItem('') + self._toggle_state_item.connect('activate', self.__toggle_state_cb) + self.menu.append(self._toggle_state_item) + self._toggle_state_item.show() + + self.info_box = gtk.VBox() + + self.error_title_label = gtk.Label("") + self.error_title_label.set_alignment(0, 0.5) + self.error_title_label.set_line_wrap(True) + self.info_box.pack_start(self.error_title_label) + self.error_description_label = gtk.Label("") + self.error_description_label.set_alignment(0, 0.5) + self.error_description_label.set_line_wrap(True) + self.info_box.pack_start(self.error_description_label) + + self.connection_info_box = gtk.HBox() + icon = Icon(icon_name='data-upload', icon_size=gtk.ICON_SIZE_MENU) + self.connection_info_box.pack_start(icon) + icon.show() + + self._data_label_up = gtk.Label() + self._data_label_up.props.xalign = 0.0 + label_alignment = self._add_widget_with_padding(self._data_label_up) + self.connection_info_box.pack_start(label_alignment) + self._data_label_up.show() + label_alignment.show() + + icon = Icon(icon_name='data-download', icon_size=gtk.ICON_SIZE_MENU) + self.connection_info_box.pack_start(icon) + icon.show() + self._data_label_down = gtk.Label() + self._data_label_down.props.xalign = 0.0 + label_alignment = self._add_widget_with_padding(self._data_label_down) + self.connection_info_box.pack_start(label_alignment) + self._data_label_down.show() + label_alignment.show() + + self.info_box.pack_start(self.connection_info_box) + + self.info_box.show() + self.set_content(self.info_box) + + self.set_state(_GSM_STATE_NOT_READY) + + def _add_widget_with_padding(self, child, xalign=0, yalign=0.5): + alignment = gtk.Alignment(xalign=xalign, yalign=yalign, + xscale=1, yscale=0.33) + alignment.set_padding(style.DEFAULT_SPACING, + style.DEFAULT_SPACING, + style.DEFAULT_SPACING, + style.DEFAULT_SPACING) + alignment.add(child) + return alignment + + def update_state(self, state, reason=0): + self._current_state = state + self._update_label_and_text(reason) + + def _update_label_and_text(self, reason=0): + if self._current_state == _GSM_STATE_NOT_READY: + self._toggle_state_item.get_child().set_label('...') + label = glib.markup_escape_text(_('Please wait...')) + self.props.secondary_text = label + + elif self._current_state == _GSM_STATE_DISCONNECTED: + if not self._failed_connection: + self._toggle_state_item.get_child().set_label(_('Connect')) + label = glib.markup_escape_text(_('Disconnected')) + self.props.secondary_text = label + icon = Icon(icon_name='dialog-ok', \ + icon_size=gtk.ICON_SIZE_MENU) + self._toggle_state_item.set_image(icon) + + elif self._current_state == _GSM_STATE_CONNECTING: + self._toggle_state_item.get_child().set_label(_('Cancel')) + label = glib.markup_escape_text(_('Connecting...')) + self.props.secondary_text = label + icon = Icon(icon_name='dialog-cancel', \ + icon_size=gtk.ICON_SIZE_MENU) + self._toggle_state_item.set_image(icon) + + elif self._current_state == _GSM_STATE_CONNECTED: + self._failed_connection = False + self._toggle_state_item.get_child().set_label(_('Disconnect')) + self.update_connection_time() + icon = Icon(icon_name='media-eject', \ + icon_size=gtk.ICON_SIZE_MENU) + self._toggle_state_item.set_image(icon) + + elif self._current_state == _GSM_STATE_FAILED: + message_error = self._get_error_by_nm_reason(reason) + self.add_alert(message_error[0], message_error[1]) + else: + raise ValueError('Invalid GSM state while updating label and ' \ + 'text, %s' % str(self._current_state)) + + def __toggle_state_cb(self, menuitem): + if self._current_state == _GSM_STATE_NOT_READY: + pass + elif self._current_state == _GSM_STATE_DISCONNECTED: + self.error_title_label.hide() + self.error_description_label.hide() + self.emit('gsm-connect') + elif self._current_state == _GSM_STATE_CONNECTING: + self.emit('gsm-disconnect') + elif self._current_state == _GSM_STATE_CONNECTED: + self.emit('gsm-disconnect') + else: + raise ValueError('Invalid GSM state while emitting signal, %s' % \ + str(self._current_state)) + + def add_alert(self, error, suggestion): + self._failed_connection = True + action = _('Try connection again') + self._toggle_state_item.get_child().set_label(action) + + title = _('Error: %s') % error + self.error_title_label.set_markup('<b>%s</b>' % title) + self.error_title_label.show() + + message = _('Suggestion: %s') % suggestion + self.error_description_label.set_text(message) + self.error_description_label.show() + + def update_connection_time(self, connection_time=None): + if connection_time is not None: + formatted_time = connection_time.strftime('%H:%M:%S') + else: + formatted_time = '00:00:00' + text = _('Connected for %s') % (formatted_time, ) + self.props.secondary_text = glib.markup_escape_text(text) + + def update_stats(self, in_bytes, out_bytes): + in_KBytes = in_bytes / 1024 + out_KBytes = out_bytes / 1024 + self._data_label_up.set_text(_('%d KB') % (out_KBytes)) + self._data_label_down.set_text(_('%d KB') % (in_KBytes)) + + def _get_error_by_nm_reason(self, reason): + if reason in [network.NM_DEVICE_STATE_REASON_NO_SECRETS, + network.NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED]: + message = _('Check your PIN/PUK configuration.') + elif reason in [network.NM_DEVICE_STATE_REASON_PPP_DISCONNECT, + network.NM_DEVICE_STATE_REASON_PPP_FAILED]: + message = _('Check your Access Point Name ' \ + '(APN) configuration') + elif reason in [network.NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER, + network.NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT]: + message = _('Check the Number configuration.') + elif reason == network.NM_DEVICE_STATE_REASON_CONFIG_FAILED: + message = _('Check your configuration.') + else: + message = '' + message_tuple = (network.get_error_by_reason(reason), message) + return message_tuple + + +class WirelessDeviceView(ToolButton): + + FRAME_POSITION_RELATIVE = 302 + + def __init__(self, device): + ToolButton.__init__(self) + + self._bus = dbus.SystemBus() + self._device = device + self._device_props = None + self._flags = 0 + self._ssid = '' + self._display_name = '' + self._mode = network.NM_802_11_MODE_UNKNOWN + self._strength = 0 + self._frequency = 0 + self._device_state = None + self._color = None + self._active_ap_op = None + + self._icon = PulsingIcon() + self._icon.props.icon_name = get_icon_state('network-wireless', 0) + self._inactive_color = xocolor.XoColor( + '%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(), + style.COLOR_TRANSPARENT.get_svg())) + self._icon.props.pulse_color = self._inactive_color + self._icon.props.base_color = self._inactive_color + + self.set_icon_widget(self._icon) + self._icon.show() + + self.set_palette_invoker(FrameWidgetInvoker(self)) + self._palette = WirelessPalette(self._display_name) + self._palette.connect('deactivate-connection', + self.__deactivate_connection_cb) + self.set_palette(self._palette) + self._palette.set_group_id('frame') + + self._device_props = dbus.Interface(self._device, + dbus.PROPERTIES_IFACE) + self._device_props.GetAll(network.NM_DEVICE_IFACE, byte_arrays=True, + reply_handler=self.__get_device_props_reply_cb, + error_handler=self.__get_device_props_error_cb) + + self._device_props.Get(network.NM_WIRELESS_IFACE, 'ActiveAccessPoint', + reply_handler=self.__get_active_ap_reply_cb, + error_handler=self.__get_active_ap_error_cb) + + self._bus.add_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=self._device.object_path, + dbus_interface=network.NM_DEVICE_IFACE) + + def disconnect(self): + self._bus.remove_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=self._device.object_path, + dbus_interface=network.NM_DEVICE_IFACE) + + def __get_device_props_reply_cb(self, properties): + if 'State' in properties: + self._device_state = properties['State'] + self._update_state() + + def __get_device_props_error_cb(self, err): + logging.error('Error getting the device properties: %s', err) + + def __get_active_ap_reply_cb(self, active_ap_op): + if self._active_ap_op != active_ap_op: + if self._active_ap_op is not None: + self._bus.remove_signal_receiver( + self.__ap_properties_changed_cb, + signal_name='PropertiesChanged', + path=self._active_ap_op, + dbus_interface=network.NM_ACCESSPOINT_IFACE) + if active_ap_op == '/': + self._active_ap_op = None + return + self._active_ap_op = active_ap_op + active_ap = self._bus.get_object(network.NM_SERVICE, active_ap_op) + props = dbus.Interface(active_ap, dbus.PROPERTIES_IFACE) + + props.GetAll(network.NM_ACCESSPOINT_IFACE, byte_arrays=True, + reply_handler=self.__get_all_ap_props_reply_cb, + error_handler=self.__get_all_ap_props_error_cb) + + self._bus.add_signal_receiver(self.__ap_properties_changed_cb, + signal_name='PropertiesChanged', + path=self._active_ap_op, + dbus_interface=network.NM_ACCESSPOINT_IFACE) + + def __get_active_ap_error_cb(self, err): + logging.error('Error getting the active access point: %s', err) + + def __state_changed_cb(self, new_state, old_state, reason): + self._device_state = new_state + self._update_state() + self._device_props.Get(network.NM_WIRELESS_IFACE, 'ActiveAccessPoint', + reply_handler=self.__get_active_ap_reply_cb, + error_handler=self.__get_active_ap_error_cb) + + def __ap_properties_changed_cb(self, properties): + self._update_properties(properties) + + def _update_properties(self, properties): + if 'Mode' in properties: + self._mode = properties['Mode'] + self._color = None + if 'Ssid' in properties: + self._ssid = properties['Ssid'] + self._display_name = network.ssid_to_display_name(self._ssid) + self._color = None + if 'Strength' in properties: + self._strength = properties['Strength'] + if 'Flags' in properties: + self._flags = properties['Flags'] + if 'Frequency' in properties: + self._frequency = properties['Frequency'] + + if self._color == None: + if self._mode == network.NM_802_11_MODE_ADHOC and \ + network.is_sugar_adhoc_network(self._ssid): + self._color = profile.get_color() + else: + sha_hash = hashlib.sha1() + data = self._ssid + hex(self._flags) + sha_hash.update(data) + digest = hash(sha_hash.digest()) + index = digest % len(xocolor.colors) + + self._color = xocolor.XoColor('%s,%s' % + (xocolor.colors[index][0], + xocolor.colors[index][1])) + self._update() + + def __get_all_ap_props_reply_cb(self, properties): + self._update_properties(properties) + + def __get_all_ap_props_error_cb(self, err): + logging.error('Error getting the access point properties: %s', err) + + def _update(self): + if self._flags == network.NM_802_11_AP_FLAGS_PRIVACY: + self._icon.props.badge_name = 'emblem-locked' + else: + self._icon.props.badge_name = None + + label = glib.markup_escape_text(self._display_name) + self._palette.props.primary_text = label + + self._update_state() + self._update_color() + + def _update_state(self): + if self._active_ap_op is not None: + state = self._device_state + else: + state = network.NM_DEVICE_STATE_UNKNOWN + + if self._mode != network.NM_802_11_MODE_ADHOC and \ + network.is_sugar_adhoc_network(self._ssid) == False: + if state == network.NM_DEVICE_STATE_ACTIVATED: + icon_name = '%s-connected' % 'network-wireless' + else: + icon_name = 'network-wireless' + + icon_name = get_icon_state(icon_name, self._strength) + if icon_name: + self._icon.props.icon_name = icon_name + else: + channel = network.frequency_to_channel(self._frequency) + if state == network.NM_DEVICE_STATE_ACTIVATED: + self._icon.props.icon_name = 'network-adhoc-%s-connected' \ + % channel + else: + self._icon.props.icon_name = 'network-adhoc-%s' % channel + self._icon.props.base_color = profile.get_color() + + if (state >= network.NM_DEVICE_STATE_PREPARE) and \ + (state <= network.NM_DEVICE_STATE_IP_CONFIG): + self._palette.set_connecting() + self._icon.props.pulsing = True + elif state == network.NM_DEVICE_STATE_ACTIVATED: + address = self._device_props.Get(network.NM_DEVICE_IFACE, 'Ip4Address') + self._palette.set_connected_with_frequency(self._frequency, + address) + self._icon.props.pulsing = False + else: + self._icon.props.badge_name = None + self._icon.props.pulsing = False + self._icon.props.pulse_color = self._inactive_color + self._icon.props.base_color = self._inactive_color + self._palette.set_disconnected() + + def _update_color(self): + self._icon.props.base_color = self._color + + def __deactivate_connection_cb(self, palette, data=None): + network.disconnect_access_points([self._active_ap_op]) + + def __activate_reply_cb(self, connection): + logging.debug('Network created: %s', connection) + + def __activate_error_cb(self, err): + logging.debug('Failed to create network: %s', err) + + +class OlpcMeshDeviceView(ToolButton): + _ICON_NAME = 'network-mesh' + FRAME_POSITION_RELATIVE = 302 + + def __init__(self, device, state): + ToolButton.__init__(self) + + self._bus = dbus.SystemBus() + self._device = device + self._device_props = None + self._device_state = None + self._channel = 0 + + self._icon = PulsingIcon(icon_name=self._ICON_NAME) + self._inactive_color = xocolor.XoColor( + '%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(), + style.COLOR_TRANSPARENT.get_svg())) + self._icon.props.pulse_color = profile.get_color() + self._icon.props.base_color = self._inactive_color + + self.set_icon_widget(self._icon) + self._icon.show() + + self.set_palette_invoker(FrameWidgetInvoker(self)) + title = _('Mesh Network') + self._palette = WirelessPalette(glib.markup_escape_text(title)) + self._palette.connect('deactivate-connection', + self.__deactivate_connection) + self.set_palette(self._palette) + self._palette.set_group_id('frame') + + self.update_state(state) + + self._device_props = dbus.Interface(self._device, + dbus.PROPERTIES_IFACE) + self._device_props.Get(network.NM_OLPC_MESH_IFACE, 'ActiveChannel', + reply_handler=self.__get_active_channel_reply_cb, + error_handler=self.__get_active_channel_error_cb) + + self._bus.add_signal_receiver(self.__wireless_properties_changed_cb, + signal_name='PropertiesChanged', + path=device.object_path, + dbus_interface=network.NM_OLPC_MESH_IFACE) + + def disconnect(self): + self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb, + signal_name='PropertiesChanged', + path=self._device.object_path, + dbus_interface=network.NM_OLPC_MESH_IFACE) + + def __get_active_channel_reply_cb(self, channel): + self._channel = channel + self._update_text() + + def __get_active_channel_error_cb(self, err): + logging.error('Error getting the active channel: %s', err) + + def __state_changed_cb(self, new_state, old_state, reason): + self._device_state = new_state + self._update() + + def __wireless_properties_changed_cb(self, properties): + if 'ActiveChannel' in properties: + self._channel = properties['ActiveChannel'] + self._update_text() + + def _update_text(self): + channel = str(self._channel) + text = glib.markup_escape_text(_('Mesh Network %s') % (channel, )) + self._palette.props.primary_text = text + + def _update(self): + state = self._device_state + + if (state >= network.NM_DEVICE_STATE_PREPARE) and \ + (state <= network.NM_DEVICE_STATE_IP_CONFIG): + self._icon.props.base_color = self._inactive_color + self._icon.props.pulse_color = profile.get_color() + self._palette.set_connecting() + self._icon.props.pulsing = True + elif state == network.NM_DEVICE_STATE_ACTIVATED: + address = self._device_props.Get(network.NM_DEVICE_IFACE, 'Ip4Address') + self._palette.set_connected_with_channel(self._channel, address) + self._icon.props.base_color = profile.get_color() + self._icon.props.pulsing = False + self._update_text() + + def update_state(self, state): + self._device_state = state + self._update() + + def __deactivate_connection(self, palette, data=None): + obj = self._bus.get_object(network.NM_SERVICE, network.NM_PATH) + netmgr = dbus.Interface(obj, network.NM_IFACE) + netmgr_props = dbus.Interface(netmgr, dbus.PROPERTIES_IFACE) + active_connections_o = netmgr_props.Get(network.NM_IFACE, + 'ActiveConnections') + + for conn_o in active_connections_o: + # The connection path for a mesh connection is the device itself. + obj = self._bus.get_object(network.NM_IFACE, conn_o) + props = dbus.Interface(obj, dbus.PROPERTIES_IFACE) + ap_op = props.Get(network.NM_ACTIVE_CONN_IFACE, 'SpecificObject') + + try: + obj = self._bus.get_object(network.NM_IFACE, ap_op) + props = dbus.Interface(obj, dbus.PROPERTIES_IFACE) + device_type = props.Get(network.NM_DEVICE_IFACE, 'DeviceType') + if device_type == network.NM_DEVICE_TYPE_OLPC_MESH: + netmgr.DeactivateConnection(conn_o) + break + except dbus.exceptions.DBusException: + pass + + +class WiredDeviceView(TrayIcon): + + _ICON_NAME = 'network-wired' + FRAME_POSITION_RELATIVE = 301 + + def __init__(self, speed, address): + client = gconf.client_get_default() + color = xocolor.XoColor(client.get_string('/desktop/sugar/user/color')) + + TrayIcon.__init__(self, icon_name=self._ICON_NAME, xo_color=color) + + self.set_palette_invoker(FrameWidgetInvoker(self)) + self._palette = WiredPalette() + self.set_palette(self._palette) + self._palette.set_group_id('frame') + self._palette.set_connected(speed, address) + + +class GsmDeviceView(TrayIcon): + + _ICON_NAME = 'network-gsm' + FRAME_POSITION_RELATIVE = 303 + + def __init__(self, device): + self._connection_time_handler = None + self._connection_timestamp = 0 + + client = gconf.client_get_default() + color = xocolor.XoColor(client.get_string('/desktop/sugar/user/color')) + + TrayIcon.__init__(self, icon_name=self._ICON_NAME, xo_color=color) + + self._bus = dbus.SystemBus() + self._device = device + self._palette = None + self.set_palette_invoker(FrameWidgetInvoker(self)) + + self._bus.add_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=self._device.object_path, + dbus_interface=network.NM_DEVICE_IFACE) + self._bus.add_signal_receiver(self.__ppp_stats_changed_cb, + signal_name='PppStats', + path=self._device.object_path, + dbus_interface=network.NM_MODEM_IFACE) + + def create_palette(self): + palette = GsmPalette() + + palette.set_group_id('frame') + palette.connect('gsm-connect', self.__gsm_connect_cb) + palette.connect('gsm-disconnect', self.__gsm_disconnect_cb) + + self._palette = palette + + props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE) + props.GetAll(network.NM_DEVICE_IFACE, byte_arrays=True, + reply_handler=self.__current_state_check_cb, + error_handler=self.__current_state_check_error_cb) + + return palette + + def __gsm_connect_cb(self, palette, data=None): + connection = network.find_gsm_connection() + if connection is not None: + connection.activate(self._device.object_path, + reply_handler=self.__connect_cb, + error_handler=self.__connect_error_cb) + else: + self._palette.add_alert(_('No GSM connection available.'), \ + _('Create a connection in the ' \ + 'control panel.')) + + def __connect_cb(self, active_connection): + logging.debug('Connected successfully to gsm device, %s', + active_connection) + + def __connect_error_cb(self, error): + raise RuntimeError('Error when connecting to gsm device, %s' % error) + + def __gsm_disconnect_cb(self, palette, data=None): + obj = self._bus.get_object(network.NM_SERVICE, network.NM_PATH) + netmgr = dbus.Interface(obj, network.NM_IFACE) + netmgr_props = dbus.Interface(netmgr, dbus.PROPERTIES_IFACE) + active_connections_o = netmgr_props.Get(network.NM_IFACE, 'ActiveConnections') + + for conn_o in active_connections_o: + obj = self._bus.get_object(network.NM_IFACE, conn_o) + props = dbus.Interface(obj, dbus.PROPERTIES_IFACE) + devices = props.Get(network.NM_ACTIVE_CONN_IFACE, 'Devices') + if self._device.object_path in devices: + netmgr.DeactivateConnection( + conn_o, + reply_handler=self.__disconnect_cb, + error_handler=self.__disconnect_error_cb) + break + + def __disconnect_cb(self): + logging.debug('Disconnected successfully gsm device') + + def __disconnect_error_cb(self, error): + raise RuntimeError('Error when disconnecting gsm device, %s' % error) + + def __state_changed_cb(self, new_state, old_state, reason): + logging.debug('State: %s to %s, reason %s', old_state, + new_state, reason) + self._update_state(int(new_state), int(old_state), int(reason)) + + def __current_state_check_cb(self, properties): + self._update_state(int(properties['State']), 0, 0) + + def __current_state_check_error_cb(self, error): + raise RuntimeError('Error when checking gsm device state, %s' % error) + + def _update_state(self, state, old_state, reason): + gsm_state = None + + if state is network.NM_DEVICE_STATE_ACTIVATED: + gsm_state = _GSM_STATE_CONNECTED + connection = network.find_gsm_connection() + if connection is not None: + self._connection_timestamp = time.time() - \ + connection.get_settings('connection')['timestamp'] + self._connection_time_handler = gobject.timeout_add_seconds( \ + 1, self.__connection_timecount_cb) + self._palette.update_connection_time() + self._palette.update_stats(0, 0) + if self._palette is not None: + self._palette.connection_info_box.show() + + elif state is network.NM_DEVICE_STATE_DISCONNECTED: + gsm_state = _GSM_STATE_DISCONNECTED + self._connection_timestamp = 0 + if self._connection_time_handler is not None: + gobject.source_remove(self._connection_time_handler) + if self._palette is not None: + self._palette.connection_info_box.hide() + + elif state in [network.NM_DEVICE_STATE_UNMANAGED, + network.NM_DEVICE_STATE_UNAVAILABLE, + network.NM_DEVICE_STATE_UNKNOWN]: + gsm_state = _GSM_STATE_NOT_READY + + elif (state >= network.NM_DEVICE_STATE_PREPARE) and \ + (state <= network.NM_DEVICE_STATE_IP_CONFIG): + gsm_state = _GSM_STATE_CONNECTING + + elif state == network.NM_DEVICE_STATE_FAILED: + gsm_state = _GSM_STATE_FAILED + + if self._palette is not None: + self._palette.update_state(gsm_state, reason) + + def disconnect(self): + self._bus.remove_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=self._device.object_path, + dbus_interface=network.NM_DEVICE_IFACE) + + def __ppp_stats_changed_cb(self, in_bytes, out_bytes): + self._palette.update_stats(in_bytes, out_bytes) + + def __connection_timecount_cb(self): + self._connection_timestamp = self._connection_timestamp + 1 + connection_time = \ + datetime.datetime.fromtimestamp(self._connection_timestamp) + self._palette.update_connection_time(connection_time) + return True + + +class WirelessDeviceObserver(object): + def __init__(self, device, tray): + self._device = device + self._device_view = None + self._tray = tray + self._device_view = WirelessDeviceView(self._device) + self._tray.add_device(self._device_view) + + def disconnect(self): + self._device_view.disconnect() + self._tray.remove_device(self._device_view) + del self._device_view + self._device_view = None + + +class MeshDeviceObserver(object): + def __init__(self, device, tray): + self._bus = dbus.SystemBus() + self._device = device + self._device_view = None + self._tray = tray + + props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE) + props.GetAll(network.NM_DEVICE_IFACE, byte_arrays=True, + reply_handler=self.__get_device_props_reply_cb, + error_handler=self.__get_device_props_error_cb) + + self._bus.add_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=self._device.object_path, + dbus_interface=network.NM_DEVICE_IFACE) + + def _remove_device_view(self): + self._device_view.disconnect() + self._tray.remove_device(self._device_view) + self._device_view = None + + def disconnect(self): + if self._device_view is not None: + self._remove_device_view() + + self._bus.remove_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=self._device.object_path, + dbus_interface=network.NM_DEVICE_IFACE) + + def __get_device_props_reply_cb(self, properties): + if 'State' in properties: + self._update_state(properties['State']) + + def __get_device_props_error_cb(self, err): + logging.error('Error getting the device properties: %s', err) + + def __state_changed_cb(self, new_state, old_state, reason): + self._update_state(new_state) + + def _update_state(self, state): + if (state >= network.NM_DEVICE_STATE_PREPARE) and \ + (state <= network.NM_DEVICE_STATE_ACTIVATED): + if self._device_view is not None: + self._device_view.update_state(state) + return + + self._device_view = OlpcMeshDeviceView(self._device, state) + self._tray.add_device(self._device_view) + else: + if self._device_view is not None: + self._remove_device_view() + + +class WiredDeviceObserver(object): + def __init__(self, device, tray): + self._bus = dbus.SystemBus() + self._device = device + self._device_state = None + self._device_view = None + self._tray = tray + + props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE) + props.GetAll(network.NM_DEVICE_IFACE, byte_arrays=True, + reply_handler=self.__get_device_props_reply_cb, + error_handler=self.__get_device_props_error_cb) + + self._bus.add_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=self._device.object_path, + dbus_interface=network.NM_DEVICE_IFACE) + + def disconnect(self): + self._bus.remove_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=self._device.object_path, + dbus_interface=network.NM_DEVICE_IFACE) + + def __get_device_props_reply_cb(self, properties): + if 'State' in properties: + self._update_state(properties['State']) + + def __get_device_props_error_cb(self, err): + logging.error('Error getting the device properties: %s', err) + + def __state_changed_cb(self, new_state, old_state, reason): + self._update_state(new_state) + + def _update_state(self, state): + if state == network.NM_DEVICE_STATE_ACTIVATED: + props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE) + address = props.Get(network.NM_DEVICE_IFACE, 'Ip4Address') + speed = props.Get(network.NM_WIRED_IFACE, 'Speed') + self._device_view = WiredDeviceView(speed, address) + self._tray.add_device(self._device_view) + else: + if self._device_view is not None: + self._tray.remove_device(self._device_view) + del self._device_view + self._device_view = None + + +class GsmDeviceObserver(object): + def __init__(self, device, tray): + self._device = device + self._device_view = None + self._tray = tray + + self._device_view = GsmDeviceView(device) + self._tray.add_device(self._device_view) + + def disconnect(self): + self._device_view.disconnect() + self._tray.remove_device(self._device_view) + self._device_view = None + + +class NetworkManagerObserver(object): + def __init__(self, tray): + self._bus = dbus.SystemBus() + self._devices = {} + self._netmgr = None + self._tray = tray + + try: + obj = self._bus.get_object(network.NM_SERVICE, network.NM_PATH) + self._netmgr = dbus.Interface(obj, network.NM_IFACE) + except dbus.DBusException: + logging.error('%s service not available', network.NM_SERVICE) + return + + self._netmgr.GetDevices(reply_handler=self.__get_devices_reply_cb, + error_handler=self.__get_devices_error_cb) + + self._bus.add_signal_receiver(self.__device_added_cb, + signal_name='DeviceAdded', + dbus_interface=network.NM_IFACE) + self._bus.add_signal_receiver(self.__device_removed_cb, + signal_name='DeviceRemoved', + dbus_interface=network.NM_IFACE) + + def __get_devices_reply_cb(self, devices): + for device_op in devices: + self._check_device(device_op) + + def __get_devices_error_cb(self, err): + logging.error('Failed to get devices: %s', err) + + def _check_device(self, device_op): + if device_op in self._devices: + return + + nm_device = self._bus.get_object(network.NM_SERVICE, device_op) + props = dbus.Interface(nm_device, dbus.PROPERTIES_IFACE) + + device_type = props.Get(network.NM_DEVICE_IFACE, 'DeviceType') + if device_type == network.NM_DEVICE_TYPE_ETHERNET: + device = WiredDeviceObserver(nm_device, self._tray) + self._devices[device_op] = device + elif device_type == network.NM_DEVICE_TYPE_WIFI: + device = WirelessDeviceObserver(nm_device, self._tray) + self._devices[device_op] = device + elif device_type == network.NM_DEVICE_TYPE_OLPC_MESH: + device = MeshDeviceObserver(nm_device, self._tray) + self._devices[device_op] = device + elif device_type == network.NM_DEVICE_TYPE_MODEM: + device = GsmDeviceObserver(nm_device, self._tray) + self._devices[device_op] = device + + def __device_added_cb(self, device_op): + self._check_device(device_op) + + def __device_removed_cb(self, device_op): + if device_op in self._devices: + device = self._devices[device_op] + device.disconnect() + del self._devices[device_op] + + +def setup(tray): + device_observer = NetworkManagerObserver(tray) |