small updates to the app

This commit is contained in:
Daniel Pavel 2012-10-09 15:13:31 +03:00
parent 9b2c1bdef6
commit 9111afcd6b
6 changed files with 74 additions and 53 deletions

View File

@ -4,6 +4,7 @@
from gi.repository import Gtk from gi.repository import Gtk
from logitech.devices import constants as C
_ICON_OK = 'Solaar' _ICON_OK = 'Solaar'
_ICON_FAIL = _ICON_OK + '-fail' _ICON_FAIL = _ICON_OK + '-fail'
@ -36,9 +37,9 @@ def create(title, click_action=None):
return icon return icon
def update(icon, rstatus, tooltip): def update(icon, receiver, tooltip):
icon.set_tooltip_markup(tooltip) icon.set_tooltip_markup(tooltip)
if rstatus.code < 0: if receiver.code < C.STATUS.CONNECTED:
icon.set_from_icon_name(_ICON_FAIL) icon.set_from_icon_name(_ICON_FAIL)
else: else:
icon.set_from_icon_name(_ICON_OK) icon.set_from_icon_name(_ICON_OK)

View File

@ -5,6 +5,8 @@
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import Gdk from gi.repository import Gdk
from logitech.devices import constants as C
_DEVICE_ICON_SIZE = Gtk.IconSize.DIALOG _DEVICE_ICON_SIZE = Gtk.IconSize.DIALOG
_STATUS_ICON_SIZE = Gtk.IconSize.DIALOG _STATUS_ICON_SIZE = Gtk.IconSize.DIALOG
@ -16,7 +18,7 @@ def _update_receiver_box(box, receiver):
icon, vbox = box.get_children() icon, vbox = box.get_children()
label, buttons_box = vbox.get_children() label, buttons_box = vbox.get_children()
label.set_text(receiver.text or '') label.set_text(receiver.text or '')
buttons_box.set_visible(receiver.code >= 0) buttons_box.set_visible(receiver.code >= C.STATUS.CONNECTED)
def _update_device_box(frame, devstatus): def _update_device_box(frame, devstatus):
@ -30,7 +32,7 @@ def _update_device_box(frame, devstatus):
icon.set_name(devstatus.name) icon.set_name(devstatus.name)
icon.set_from_icon_name(devstatus.name, _DEVICE_ICON_SIZE) icon.set_from_icon_name(devstatus.name, _DEVICE_ICON_SIZE)
if devstatus.code < 0: if devstatus.code < C.STATUS.CONNECTED:
expander.set_sensitive(False) expander.set_sensitive(False)
expander.set_expanded(False) expander.set_expanded(False)
expander.set_label('<big><b>%s</b></big>\n%s' % (devstatus.name, devstatus.text)) expander.set_label('<big><b>%s</b></big>\n%s' % (devstatus.name, devstatus.text))
@ -41,7 +43,7 @@ def _update_device_box(frame, devstatus):
texts = [] texts = []
light_icon = ebox.get_children()[-2] light_icon = ebox.get_children()[-2]
light_level = getattr(devstatus, 'light_level', None) light_level = getattr(devstatus, C.PROPS.LIGHT_LEVEL, None)
light_icon.set_visible(light_level is not None) light_icon.set_visible(light_level is not None)
if light_level is not None: if light_level is not None:
texts.append('Light: %d lux' % light_level) texts.append('Light: %d lux' % light_level)
@ -50,7 +52,7 @@ def _update_device_box(frame, devstatus):
light_icon.set_tooltip_text(texts[-1]) light_icon.set_tooltip_text(texts[-1])
battery_icon = ebox.get_children()[-1] battery_icon = ebox.get_children()[-1]
battery_level = getattr(devstatus, 'battery_level', None) battery_level = getattr(devstatus, C.PROPS.BATTERY_LEVEL, None)
battery_icon.set_sensitive(battery_level is not None) battery_icon.set_sensitive(battery_level is not None)
if battery_level is None: if battery_level is None:
battery_icon.set_from_icon_name('battery_unknown', _STATUS_ICON_SIZE) battery_icon.set_from_icon_name('battery_unknown', _STATUS_ICON_SIZE)
@ -61,7 +63,7 @@ def _update_device_box(frame, devstatus):
battery_icon.set_from_icon_name(icon_name, _STATUS_ICON_SIZE) battery_icon.set_from_icon_name(icon_name, _STATUS_ICON_SIZE)
battery_icon.set_tooltip_text(texts[-1]) battery_icon.set_tooltip_text(texts[-1])
battery_status = getattr(devstatus, 'battery_status', None) battery_status = getattr(devstatus, C.PROPS.BATTERY_STATUS, None)
if battery_status is not None: if battery_status is not None:
texts.append(battery_status) texts.append(battery_status)
battery_icon.set_tooltip_text(battery_icon.get_tooltip_text() + '\n' + battery_status) battery_icon.set_tooltip_text(battery_icon.get_tooltip_text() + '\n' + battery_status)
@ -163,16 +165,7 @@ def _device_box():
def create(title, rstatus, show=True, close_to_tray=False): def create(title, rstatus, show=True, close_to_tray=False):
vbox = Gtk.VBox(homogeneous=False, spacing=4)
vbox.set_border_width(4)
vbox.add(_receiver_box(rstatus))
for i in range(1, 1 + _MAX_DEVICES):
vbox.add(_device_box())
vbox.set_visible(True)
window = Gtk.Window() window = Gtk.Window()
window.add(vbox)
Gtk.Window.set_default_icon_name('mouse') Gtk.Window.set_default_icon_name('mouse')
window.set_icon_name(title) window.set_icon_name(title)
@ -181,6 +174,8 @@ def create(title, rstatus, show=True, close_to_tray=False):
window.set_keep_above(True) window.set_keep_above(True)
window.set_deletable(False) window.set_deletable(False)
window.set_resizable(False) window.set_resizable(False)
window.set_size_request(200, 50)
window.set_default_size(200, 50)
window.set_position(Gtk.WindowPosition.MOUSE) window.set_position(Gtk.WindowPosition.MOUSE)
window.set_type_hint(Gdk.WindowTypeHint.UTILITY) window.set_type_hint(Gdk.WindowTypeHint.UTILITY)
@ -190,6 +185,15 @@ def create(title, rstatus, show=True, close_to_tray=False):
# window.set_wmclass(title, 'status-window') # window.set_wmclass(title, 'status-window')
# window.set_role('status-window') # window.set_role('status-window')
vbox = Gtk.VBox(homogeneous=False, spacing=4)
vbox.set_border_width(4)
vbox.add(_receiver_box(rstatus))
for i in range(1, 1 + _MAX_DEVICES):
vbox.add(_device_box())
vbox.set_visible(True)
window.add(vbox)
if close_to_tray: if close_to_tray:
def _state_event(window, event): def _state_event(window, event):
if event.new_window_state & Gdk.WindowState.ICONIFIED: if event.new_window_state & Gdk.WindowState.ICONIFIED:

View File

@ -10,23 +10,23 @@ from binascii import hexlify as _hexlify
from logitech.unifying_receiver import api from logitech.unifying_receiver import api
from logitech.unifying_receiver.listener import EventsListener from logitech.unifying_receiver.listener import EventsListener
from logitech import devices from logitech import devices
from logitech.devices import constants as C
_STATUS_TIMEOUT = 31 # seconds _STATUS_TIMEOUT = 31 # seconds
_THREAD_SLEEP = 2 # seconds _THREAD_SLEEP = 2 # seconds
_UNIFYING_RECEIVER = 'Unifying Receiver' _UNIFYING_RECEIVER = 'Unifying Receiver'
_NO_RECEIVER = 'Receiver not found.' _NO_RECEIVER = 'Receiver not found.'
_INITIALIZING = 'Initializing...' _INITIALIZING = 'Initializing...'
_SCANNING = 'Scanning...' _SCANNING = 'Scanning...'
_NO_DEVICES = 'No devices found.' _NO_DEVICES = 'No devices found.'
_OKAY = 'Status okay.' _OKAY = 'Status ok.'
class _DevStatus(api.AttachedDeviceInfo): class _DevStatus(api.AttachedDeviceInfo):
timestamp = time.time() timestamp = time.time()
code = devices.STATUS.UNKNOWN code = C.STATUS.UNKNOWN
text = _INITIALIZING text = _INITIALIZING
refresh = None refresh = None
@ -55,28 +55,30 @@ class WatcherThread(threading.Thread):
while self.active: while self.active:
if self.listener is None: if self.listener is None:
self._device_status_changed(self.rstatus, (devices.STATUS.UNKNOWN, _INITIALIZING)) self._device_status_changed(self.rstatus, (C.STATUS.UNKNOWN, _INITIALIZING))
self._update_status_text() self._update_status_text()
receiver = api.open() receiver = api.open()
if receiver: if receiver:
self._device_status_changed(self.rstatus, (-10, _SCANNING)) self._device_status_changed(self.rstatus, (C.STATUS.BOOTING, _SCANNING))
self._update_status_text() self._update_status_text()
for devinfo in api.list_devices(receiver): for devinfo in api.list_devices(receiver):
self._new_device(devinfo) self._new_device(devinfo)
logging.debug("initial scan finished: %s", self.devices)
if self.devices: if self.devices:
self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, _OKAY)) self._device_status_changed(self.rstatus, (C.STATUS.CONNECTED, _OKAY))
else: else:
self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, _NO_DEVICES)) self._device_status_changed(self.rstatus, (C.STATUS.CONNECTED, _NO_DEVICES))
self._update_status_text()
self.listener = EventsListener(receiver, self._events_callback) self.listener = EventsListener(receiver, self._events_callback)
self.listener.start() self.listener.start()
else: else:
self._device_status_changed(self.rstatus, (devices.STATUS.UNAVAILABLE, _NO_RECEIVER)) self._device_status_changed(self.rstatus, (C.STATUS.UNAVAILABLE, _NO_RECEIVER))
elif not self.listener.active: elif not self.listener.active:
self.listener = None self.listener = None
self._device_status_changed(self.rstatus, (devices.STATUS.UNAVAILABLE, _NO_RECEIVER)) self._device_status_changed(self.rstatus, (C.STATUS.UNAVAILABLE, _NO_RECEIVER))
self.devices.clear() self.devices.clear()
if self.active: if self.active:
@ -134,15 +136,14 @@ class WatcherThread(threading.Thread):
if not self.active: if not self.active:
return None return None
logging.debug("new devstatus from %s", dev)
if type(dev) == int: if type(dev) == int:
dev = self.listener.request(api.get_device_info, dev) dev = self.listener.request(api.get_device_info, dev)
logging.debug("new devstatus from %s", dev)
if dev: if dev:
devstatus = _DevStatus(*dev) devstatus = _DevStatus(*dev)
devstatus.refresh = self._request_status devstatus.refresh = self._request_status
self.devices[dev.number] = devstatus self.devices[dev.number] = devstatus
self._device_status_changed(devstatus, devices.STATUS.CONNECTED) self._device_status_changed(devstatus, C.STATUS.CONNECTED)
logging.debug("new devstatus %s", devstatus)
return devstatus return devstatus
def _events_callback(self, code, devnumber, data): def _events_callback(self, code, devnumber, data):
@ -154,7 +155,7 @@ class WatcherThread(threading.Thread):
devstatus = self.devices[devnumber] devstatus = self.devices[devnumber]
if code == 0x10 and data[:1] == b'\x8F': if code == 0x10 and data[:1] == b'\x8F':
updated = True updated = True
self._device_status_changed(devstatus, devices.STATUS.UNAVAILABLE) self._device_status_changed(devstatus, C.STATUS.UNAVAILABLE)
elif code == 0x11: elif code == 0x11:
status = devices.process_event(devstatus, self.listener, data) status = devices.process_event(devstatus, self.listener, data)
updated |= self._device_status_changed(devstatus, status) updated |= self._device_status_changed(devstatus, status)
@ -178,8 +179,8 @@ class WatcherThread(threading.Thread):
if type(status) == int: if type(status) == int:
status_code = status status_code = status
if status_code in devices.STATUS_NAME: if status_code in C.STATUS_NAME:
status_text = devices.STATUS_NAME[status_code] status_text = C.STATUS_NAME[status_code]
else: else:
status_code = status[0] status_code = status[0]
if isinstance(status[1], str): if isinstance(status[1], str):
@ -192,15 +193,16 @@ class WatcherThread(threading.Thread):
else: else:
setattr(devstatus, key, value) setattr(devstatus, key, value)
else: else:
status_code = devices.STATUS.UNKNOWN status_code = C.STATUS.UNKNOWN
status_text = '' status_text = ''
if not (status_code == 0 and old_status_code > 0): if not (status_code == C.STATUS.CONNECTED and old_status_code > C.STATUS.CONNECTED):
# if this is not just a ping for a device with an already known status
devstatus.code = status_code devstatus.code = status_code
devstatus.text = status_text devstatus.text = status_text
logging.debug("%s: device '%s' status update %s => %s: %s", time.asctime(), devstatus.name, old_status_code, status_code, status_text) logging.debug("%s: device '%s' status update %s => %s: %s", time.asctime(), devstatus.name, old_status_code, status_code, status_text)
if status_code <= 0 or old_status_code <= 0 or status_code < old_status_code: if status_code < C.STATUS.CONNECTED or old_status_code < C.STATUS.CONNECTED or status_code < old_status_code:
self._notify(devstatus.code, devstatus.name, devstatus.text) self._notify(devstatus.code, devstatus.name, devstatus.text)
return True return True
@ -214,7 +216,7 @@ class WatcherThread(threading.Thread):
if self.devices: if self.devices:
lines = [] lines = []
if self.rstatus.code < 0: if self.rstatus.code < C.STATUS.CONNECTED:
lines += (self.rstatus.text, '') lines += (self.rstatus.text, '')
devstatuses = [self.devices[d] for d in range(1, 1 + api.C.MAX_ATTACHED_DEVICES) if d in self.devices] devstatuses = [self.devices[d] for d in range(1, 1 + api.C.MAX_ATTACHED_DEVICES) if d in self.devices]
@ -224,7 +226,7 @@ class WatcherThread(threading.Thread):
lines.append('<b>' + devstatus.name + '</b>') lines.append('<b>' + devstatus.name + '</b>')
lines.append(' ' + devstatus.text) lines.append(' ' + devstatus.text)
else: else:
lines.append('<b>' + devstatus.name + '</b>: ' + devstatus.text) lines.append('<b>' + devstatus.name + '</b> ' + devstatus.text)
else: else:
lines.append('<b>' + devstatus.name + '</b>') lines.append('<b>' + devstatus.name + '</b>')
lines.append('') lines.append('')

View File

@ -2,16 +2,17 @@
# #
# #
import logging
from . import k750 from . import k750
from .constants import * from . import constants as C
from ..unifying_receiver import api as _api from ..unifying_receiver import api as _api
from ..unifying_receiver.common import FallbackDict as _FDict
def ping(devinfo, listener): def ping(devinfo, listener):
reply = listener.request(_api.ping, devinfo.number) reply = listener.request(_api.ping, devinfo.number)
return STATUS.CONNECTED if reply else STATUS.UNAVAILABLE return C.STATUS.CONNECTED if reply else C.STATUS.UNAVAILABLE
def default_request_status(devinfo, listener): def default_request_status(devinfo, listener):
@ -19,43 +20,54 @@ def default_request_status(devinfo, listener):
reply = listener.request(_api.get_device_battery_level, devinfo.number, features_array=devinfo.features) reply = listener.request(_api.get_device_battery_level, devinfo.number, features_array=devinfo.features)
if reply: if reply:
discharge, dischargeNext, status = reply discharge, dischargeNext, status = reply
return STATUS.CONNECTED, {PROPS.BATTERY_LEVEL: discharge} return C.STATUS.CONNECTED, {C.PROPS.BATTERY_LEVEL: discharge}
def default_process_event(devinfo, listener, data): def default_process_event(devinfo, listener, data):
feature_index = ord(data[0:1]) feature_index = ord(data[0:1])
feature = devinfo.features[feature_index] feature = devinfo.features[feature_index]
feature_function = ord(data[1:2]) & 0xF0
if feature == _api.C.FEATURE.BATTERY: if feature == _api.C.FEATURE.BATTERY:
if ord(data[1:2]) & 0xF0 == 0: if feature_function == 0:
discharge = ord(data[2:3]) discharge = ord(data[2:3])
status = _api.C.BATTERY_STATUS[ord(data[3:4])] status = _api.C.BATTERY_STATUS[ord(data[3:4])]
return STATUS.CONNECTED, {BATTERY_LEVEL: discharge, BATTERY_STATUS: status} return C.STATUS.CONNECTED, {C.PROPS.BATTERY_LEVEL: discharge, C.PROPS.BATTERY_STATUS: status}
# ? # ?
elif feature == _api.C.FEATURE.REPROGRAMMABLE_KEYS: elif feature == _api.C.FEATURE.REPROGRAMMABLE_KEYS:
if ord(data[1:2]) & 0xF0 == 0: if feature_function == 0:
print 'reprogrammable key', repr(data) logging.debug('reprogrammable key: %s', repr(data))
# TODO # TODO
pass pass
# ? # ?
elif feature == _api.C.FEATURE.WIRELESS: elif feature == _api.C.FEATURE.WIRELESS:
if ord(data[1:2]) & 0xF0 == 0: if feature_function == 0:
logging.debug("wireless status: %s", repr(data))
# TODO # TODO
pass pass
# ? # ?
_REQUEST_STATUS_FUNCTIONS = _FDict() _REQUEST_STATUS_FUNCTIONS = {
_REQUEST_STATUS_FUNCTIONS[k750.NAME] = k750.request_status k750.NAME: k750.request_status
}
def request_status(devinfo, listener): def request_status(devinfo, listener):
if listener: if listener:
return _REQUEST_STATUS_FUNCTIONS[devinfo.name](devinfo, listener) or default_request_status(devinfo, listener) or ping(devinfo, listener) if devinfo.name in _REQUEST_STATUS_FUNCTIONS:
return _REQUEST_STATUS_FUNCTIONS[devinfo.name](devinfo, listener)
return default_request_status(devinfo, listener) or ping(devinfo, listener)
_PROCESS_EVENT_FUNCTIONS = _FDict() _PROCESS_EVENT_FUNCTIONS = {
_PROCESS_EVENT_FUNCTIONS[k750.NAME] = k750.process_event k750.NAME: k750.process_event
}
def process_event(devinfo, listener, data): def process_event(devinfo, listener, data):
if listener: if listener:
return default_process_event(devinfo, listener, data) or _PROCESS_EVENT_FUNCTIONS[devinfo.name](devinfo, listener, data) default_result = default_process_event(devinfo, listener, data)
if default_result is not None:
return default_result
if devinfo.name in _PROCESS_EVENT_FUNCTIONS:
return _PROCESS_EVENT_FUNCTIONS[devinfo.name](devinfo, listener, data)

View File

@ -6,11 +6,13 @@ STATUS = type('STATUS', (),
dict( dict(
UNKNOWN=-9999, UNKNOWN=-9999,
UNAVAILABLE=-1, UNAVAILABLE=-1,
CONNECTED=0, BOOTING=0,
CONNECTED=1,
)) ))
STATUS_NAME = { STATUS_NAME = {
STATUS.UNAVAILABLE: 'inactive', STATUS.UNAVAILABLE: 'inactive',
STATUS.BOOTING: 'initializing',
STATUS.CONNECTED: 'connected', STATUS.CONNECTED: 'connected',
} }

View File

@ -189,7 +189,7 @@ def read(handle, timeout=DEFAULT_TIMEOUT):
devnumber = ord(data[1:2]) devnumber = ord(data[1:2])
return code, devnumber, data[2:] return code, devnumber, data[2:]
_l.log(_LOG_LEVEL, "(%d,*) => r[]", handle) # _l.log(_LOG_LEVEL, "(%d,*) => r[]", handle)
def request(handle, devnumber, feature_index_function, params=b'', features_array=None): def request(handle, devnumber, feature_index_function, params=b'', features_array=None):