class WlanApp: def __init__(self): self.prefix = 'W:' self.suffix = ' ' self.tooltip_heading = 'Wireless LAN Status:\n' self.iface = None # Set to interface name to override interface selection self.build_ui() # Needed by wpa_supplicant.core self.reactor = SelectReactor() thread = threading.Thread(target=self.reactor.run, kwargs={'installSignalHandlers': 0}) thread.daemon = True thread.start() time.sleep(0.1) # let reactor start self.wpasup = None self.wpasup_running = False self.wlan = None self.wlan_signal = None # Monitor the availability of wpa_supplicant via DBus self.dbus = SystemBus() # watch_name() fires an event as soon as GLib.MainLoop() starts, so we don't need to explicitly # call get_wpa_supplicant() here self.dbus.watch_name(BUS_NAME, 0, self.get_wpa_supplicant, self.get_wpa_supplicant) self.start_watch_thread() def build_ui(self): self.tray = tray = Gtkti.TrayIcon() self.eventbox = eventbox = Gtk.EventBox() eventbox.set_tooltip_text(self.tooltip_heading+'WPA Supplicant not running') tray.add(eventbox) self.tray_label = tray_label = Gtk.Label(self.prefix+'_'+self.suffix) eventbox.add(tray_label) tray.show_all() menu = Gtk.Menu() item_quit = Gtk.MenuItem('Quit') def quit(menu_item): if sys.version_info < (3, 0): os.kill(os.getpid(), signal.SIGINT) else: Gtk.main_quit() item_quit.connect('activate', quit) menu.append(item_quit) menu.show_all() def button_pressed(eventbox, event, menu=menu): if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3: menu.popup(None, None, None, None, event.button, event.time) eventbox.connect('button-press-event', button_pressed) # Update the UI (thread-safe) def update_ui(self): GLib.idle_add(self.gtk_update_ui) # Update the UI (within the GTK main thread ; not thread-safe) def gtk_update_ui(self): if not self.wpasup_running: display_str = '_' tooltip_str = 'WLAN Interface is down (WPA Supplicant is not running)' elif not self.wlan: display_str = '_' tooltip_str = 'WLAN Interface not found via WPA Supplicant' else: try: ifname = self.wlan.get_ifname() tooltip_str = ifname state = self.wlan.get_state() tooltip_str += ' '+state.title() if state == 'interface_disabled': display_str = '_' elif state == 'disconnected' or state == 'inactive': display_str = '-' elif state == 'scanning': display_str = '?' elif state == 'authenticating' or state == 'associating' or state == 'associated' or \ state == '4way_handshake' or state == 'group_handshake': display_str = '@' elif state == 'completed': display_str = '' tooltip_str += ' Connected' elif state == 'unknown': display_str = '!' else: display_str = '!' print >> sys.stderr, 'Unknown wpa_supplicant state: '+state+' to '+self.wlan.get_current_bss().get_ssid() bss = self.wlan.get_current_bss() if bss: display_str += bss.get_ssid() tooltip_str += ' to '+bss.get_ssid() except: # This is expected if another thread sets self.wlan to None while the above code is # executing, or if wpa_supplicant shuts down while the above code is executing. In either # case, another UI update should happen momentarily. display_str = '!' tooltip_str = 'Unknown (Exception Thrown)' self.tray_label.set_text(self.prefix+display_str+self.suffix) self.eventbox.set_tooltip_text(self.tooltip_heading+tooltip_str) # Return false to unregister this method as a GLib idle handler return False def select_wlan_interface(self, interfaces): if self.wlan_signal: wlan_signal = self.wlan_signal # To avoid race conditions self.wlan_signal = None wlan_signal.cancel() self.wlan = None if interfaces: if self.iface: for interface in interfaces: if interface.get_ifname() == self.iface: self.wlan = interface break else: self.wlan = interfaces[0] self.wlan_signal = \ self.wlan.register_signal('PropertiesChanged', lambda args: self.update_ui()) self.update_ui() def scan_wpa_interfaces(self, args=None): self.wpa_interfaces = self.wpasup.get_interfaces() self.select_wlan_interface(self.wpa_interfaces) def wlan_interface_removed(self, path): # wpa_supplicant sends InterfaceRemoved just before shutting down, and get_interfaces() may # throw an exception if it is called while wpa_supplicant is shutting down. So, instead of # calling scan_wpa_interfaces on InterfaceRemoved, it is probably better to keep a cache of the # list of interfaces and just delete the relevant interface from the cache. self.wpa_interfaces[:] = [i for i in self.wpa_interfaces if not i.get_path() == path] self.select_wlan_interface(self.wpa_interfaces) def get_wpa_supplicant(self, dbus_name_owner = None): if dbus_name_owner: self.wpasup_running = True if not self.wpasup: # Connect to wpa_supplicant self.wpasup = WpaSupplicantDriver(self.reactor).connect() self.wpasup.register_signal('InterfaceAdded', self.scan_wpa_interfaces) self.wpasup.register_signal('InterfaceRemoved', self.wlan_interface_removed) self.scan_wpa_interfaces() else: # If we don't do anything when wpa_supplicant vanishes, then our signals seem to remain # registered when wpa_supplicant re-appears. However, wpa_supplicant doesn't seem to send # InterfaceAdded signals when it comes up, so we must explicitly re-scan the interfaces. self.scan_wpa_interfaces() else: self.wpasup_running = False self.select_wlan_interface([]) def start_watch_thread(self): thread = threading.Thread(target=GLib.MainLoop().run) thread.daemon = True thread.start()
class PowerMonitor(metaclass=Singleton): """ Watches for changes to the system's suspend state and screensaver, signalling devices to suspend if necessary. """ def __init__(self): self._logger = Log.get('uchroma.power') self._name_watchers = [] self._running = False self._sleeping = False self._user_active = False self._session_bus = SessionBus() self._system_bus = SystemBus() self._dm = UChromaDeviceManager() #singleton def _suspend(self, sleeping, fast): if self._sleeping == sleeping: return self._sleeping = sleeping for name, device in self._dm.devices.items(): if sleeping: self._logger.info("Suspending device: %s", name) device.suspend(fast=fast) else: self._logger.info("Resuming device: %s", name) device.resume() def _prepare_for_sleep(self, sleeping): self._suspend(sleeping, True) def _active_changed(self, active): self._suspend(active, False) def _is_user_active(self): user = self._system_bus.get(LOGIN_SERVICE, "/org/freedesktop/login1/user/self") self._logger.info("is_user_active: %s %s", user, user.Display) return user is not None and user.Display[0] != '' def _user_changed(self, changed): is_active = self._is_user_active() if is_active != self._user_active: self._user_active = is_active self._suspend(not self._user_active, True) def start(self): """ Connects to the PrepareForSleep signal from login1 to monitor system suspend, and sets up watches for known screensaver instances. """ if self._running: return for name, path in SCREENSAVERS: def connect_screensaver(*args, bus_name=name, object_path=path): """ Connects the callback when the service appears. """ try: saver = self._session_bus.get(bus_name=bus_name, object_path=object_path) saver.ActiveChanged.connect(self._active_changed) self._logger.info("Connected screensaver: %s:%s %s", bus_name, object_path, args) except Exception: self._logger.warn("Could not connect to %s:%s service", bus_name, object_path) self._name_watchers.append(self._session_bus.watch_name( \ name, name_appeared=connect_screensaver)) def connect_login1(*args): try: login1 = self._system_bus.get(LOGIN_SERVICE) login1.PrepareForSleep.connect(self._prepare_for_sleep) self._logger.info("Connected to %s %s", LOGIN_SERVICE, args) user = self._system_bus.get(LOGIN_SERVICE, "/org/freedesktop/login1/user/self") user.PropertiesChanged.connect(self._user_changed) except Exception: self._logger.warn("Could not connect to login1 service") self._name_watchers.append(self._system_bus.watch_name( \ LOGIN_SERVICE, name_appeared=connect_login1)) self._running = True def stop(self): """ Disable the monitor """ if not self._running: return for watcher in self._name_watchers: watcher.unwatch() self._name_watchers.clear() self._running = False
class WlanApp: def __init__(self): self.prefix = 'W:' self.suffix = ' ' self.tooltip_heading = 'Wireless LAN Status:\n' self.iface = None # Set to interface name to override interface selection self.build_ui() # Needed by wpa_supplicant.core self.reactor = SelectReactor() thread = threading.Thread(target=self.reactor.run, kwargs={'installSignalHandlers': 0}) thread.daemon = True thread.start() time.sleep(0.1) # let reactor start self.wpasup = None self.wpasup_running = False self.wlan = None self.wlan_signal = None # Monitor the availability of wpa_supplicant via DBus self.dbus = SystemBus() # watch_name() fires an event as soon as GLib.MainLoop() starts, so we don't need to explicitly # call get_wpa_supplicant() here self.dbus.watch_name(BUS_NAME, 0, self.get_wpa_supplicant, self.get_wpa_supplicant) self.start_watch_thread() def build_ui(self): self.tray = tray = Gtkti.TrayIcon() self.eventbox = eventbox = Gtk.EventBox() if background_color: css = Gtk.CssProvider() css.load_from_data( ('* { background-color: ' + background_color + '; }').encode()) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) eventbox.set_tooltip_text(self.tooltip_heading + 'WPA Supplicant not running') tray.add(eventbox) self.tray_label = tray_label = Gtk.Label(label=self.prefix + '_' + self.suffix) eventbox.add(tray_label) tray.show_all() menu = Gtk.Menu() item_quit = Gtk.MenuItem(label='Quit') def quit(menu_item): if sys.version_info < (3, 0): os.kill(os.getpid(), signal.SIGINT) else: Gtk.main_quit() item_quit.connect('activate', quit) menu.append(item_quit) menu.show_all() def button_pressed(eventbox, event, menu=menu): if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3: menu.popup(None, None, None, None, event.button, event.time) eventbox.connect('button-press-event', button_pressed) # Update the UI (thread-safe) def update_ui(self): GLib.idle_add(self.gtk_update_ui) # Update the UI (within the GTK main thread ; not thread-safe) def gtk_update_ui(self): if not self.wpasup_running: display_str = '_' tooltip_str = 'WLAN Interface is down (WPA Supplicant is not running)' elif not self.wlan: display_str = '_' tooltip_str = 'WLAN Interface not found via WPA Supplicant' else: try: ifname = self.wlan.get_ifname() tooltip_str = ifname state = self.wlan.get_state() tooltip_str += ' ' + state.title() if state == 'interface_disabled': display_str = '_' elif state == 'disconnected' or state == 'inactive': display_str = '-' elif state == 'scanning': display_str = '?' elif state == 'authenticating' or state == 'associating' or state == 'associated' or \ state == '4way_handshake' or state == 'group_handshake': display_str = '@' elif state == 'completed': display_str = '' tooltip_str += ' Connected' elif state == 'unknown': display_str = '!' else: display_str = '!' print >> sys.stderr, 'Unknown wpa_supplicant state: ' + state + ' to ' + self.wlan.get_current_bss( ).get_ssid() bss = self.wlan.get_current_bss() if bss: display_str += bss.get_ssid() tooltip_str += ' to ' + bss.get_ssid() except: # This is expected if another thread sets self.wlan to None while the above code is # executing, or if wpa_supplicant shuts down while the above code is executing. In either # case, another UI update should happen momentarily. display_str = '!' tooltip_str = 'Unknown (Exception Thrown)' self.tray_label.set_text(self.prefix + display_str + self.suffix) self.eventbox.set_tooltip_text(self.tooltip_heading + tooltip_str) # Return false to unregister this method as a GLib idle handler return False def select_wlan_interface(self, interfaces): if self.wlan_signal: wlan_signal = self.wlan_signal # To avoid race conditions self.wlan_signal = None wlan_signal.cancel() self.wlan = None if interfaces: if self.iface: for interface in interfaces: if interface.get_ifname() == self.iface: self.wlan = interface break else: self.wlan = interfaces[0] self.wlan_signal = \ self.wlan.register_signal('PropertiesChanged', lambda args: self.update_ui()) self.update_ui() def scan_wpa_interfaces(self, args=None): self.wpa_interfaces = self.wpasup.get_interfaces() self.select_wlan_interface(self.wpa_interfaces) def wlan_interface_removed(self, path): # wpa_supplicant sends InterfaceRemoved just before shutting down, and get_interfaces() may # throw an exception if it is called while wpa_supplicant is shutting down. So, instead of # calling scan_wpa_interfaces on InterfaceRemoved, it is probably better to keep a cache of the # list of interfaces and just delete the relevant interface from the cache. self.wpa_interfaces[:] = [ i for i in self.wpa_interfaces if not i.get_path() == path ] self.select_wlan_interface(self.wpa_interfaces) def get_wpa_supplicant(self, dbus_name_owner=None): if dbus_name_owner: self.wpasup_running = True if not self.wpasup: # Connect to wpa_supplicant self.wpasup = WpaSupplicantDriver(self.reactor).connect() self.wpasup.register_signal('InterfaceAdded', self.scan_wpa_interfaces) self.wpasup.register_signal('InterfaceRemoved', self.wlan_interface_removed) self.scan_wpa_interfaces() else: # If we don't do anything when wpa_supplicant vanishes, then our signals seem to remain # registered when wpa_supplicant re-appears. However, wpa_supplicant doesn't seem to send # InterfaceAdded signals when it comes up, so we must explicitly re-scan the interfaces. self.scan_wpa_interfaces() else: self.wpasup_running = False self.select_wlan_interface([]) def start_watch_thread(self): thread = threading.Thread(target=GLib.MainLoop().run) thread.daemon = True thread.start()