From 9c5ba6445ece4e5018990b38b86fc4a742e63ff0 Mon Sep 17 00:00:00 2001 From: "Peter F. Patel-Schneider" Date: Sun, 10 Mar 2024 09:17:26 -0400 Subject: [PATCH] device: remove status from Device and Receiver --- lib/logitech_receiver/device.py | 7 +- lib/logitech_receiver/diversion.py | 78 ++++++++++----------- lib/logitech_receiver/notifications.py | 38 +++++----- lib/logitech_receiver/receiver.py | 6 +- lib/logitech_receiver/settings_templates.py | 2 +- lib/logitech_receiver/status.py | 70 ------------------ lib/solaar/cli/pair.py | 2 - lib/solaar/listener.py | 39 ++++------- lib/solaar/ui/__init__.py | 4 +- lib/solaar/ui/notify.py | 15 ++-- lib/solaar/ui/tray.py | 2 +- 11 files changed, 91 insertions(+), 172 deletions(-) delete mode 100644 lib/logitech_receiver/status.py diff --git a/lib/logitech_receiver/device.py b/lib/logitech_receiver/device.py index 400bfa93..da65ea91 100644 --- a/lib/logitech_receiver/device.py +++ b/lib/logitech_receiver/device.py @@ -77,7 +77,8 @@ class Device: self.hidpp_short = device_info.hidpp_short if device_info else None self.hidpp_long = device_info.hidpp_long if device_info else None self.bluetooth = device_info.bus_id == 0x0005 if device_info else False # Bluetooth needs long messages - self.setting_callback = setting_callback + self.setting_callback = setting_callback # for changes to settings + self.status_callback = None # for changes to other potentially visible aspects self.wpid = pairing_info["wpid"] if pairing_info else None # the Wireless PID is unique per device model self._kind = pairing_info["kind"] if pairing_info else None # mouse, keyboard, etc (see hidpp10.DEVICE_KIND) self._serial = pairing_info["serial"] if pairing_info else None # serial number (an 8-char hex string) @@ -412,7 +413,6 @@ class Device: def changed(self, active=None, alert=ALERT.NONE, reason=None, push=False): """The status of the device had changed, so invoke the status callback. Also push notifications and settings to the device when necessary.""" - changed_callback = self.status._changed_callback if active is not None: self.online = active was_active, self._active = self._active, active @@ -434,7 +434,8 @@ class Device: settings.apply_all_settings(self) if logger.isEnabledFor(logging.DEBUG): logger.debug("device %d changed: active=%s %s", self.number, self._active, self.battery_info) - changed_callback(self, alert, reason) + if self.status_callback is not None: + self.status_callback(self, alert, reason) def add_notification_handler(self, id: str, fn): """Adds the notification handling callback `fn` to this device under name `id`. diff --git a/lib/logitech_receiver/diversion.py b/lib/logitech_receiver/diversion.py index f9c23d6e..17c90476 100644 --- a/lib/logitech_receiver/diversion.py +++ b/lib/logitech_receiver/diversion.py @@ -495,20 +495,20 @@ class Rule(RuleComponent): source = "(" + self.source + ")" if self.source else "" return "Rule%s[%s]" % (source, ", ".join([c.__str__() for c in self.components])) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate rule: %s", self) result = True for component in self.components: - result = component.evaluate(feature, notification, device, status, result) + result = component.evaluate(feature, notification, device, result) if not isinstance(component, Action) and result is None: return None if isinstance(component, Condition) and not result: return result return result - def once(self, feature, notification, device, status, last_result): - self.evaluate(feature, notification, device, status, last_result) + def once(self, feature, notification, device, last_result): + self.evaluate(feature, notification, device, last_result) return False def data(self): @@ -522,7 +522,7 @@ class Condition(RuleComponent): def __str__(self): return "CONDITION" - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return False @@ -538,10 +538,10 @@ class Not(Condition): def __str__(self): return "Not: " + str(self.component) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) - result = self.component.evaluate(feature, notification, device, status, last_result) + result = self.component.evaluate(feature, notification, device, last_result) return None if result is None else not result def data(self): @@ -555,12 +555,12 @@ class Or(Condition): def __str__(self): return "Or: [" + ", ".join(str(c) for c in self.components) + "]" - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) result = False for component in self.components: - result = component.evaluate(feature, notification, device, status, last_result) + result = component.evaluate(feature, notification, device, last_result) if not isinstance(component, Action) and result is None: return None if isinstance(component, Condition) and result: @@ -578,12 +578,12 @@ class And(Condition): def __str__(self): return "And: [" + ", ".join(str(c) for c in self.components) + "]" - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) result = True for component in self.components: - result = component.evaluate(feature, notification, device, status, last_result) + result = component.evaluate(feature, notification, device, last_result) if not isinstance(component, Action) and result is None: return None if isinstance(component, Condition) and not result: @@ -657,7 +657,7 @@ class Process(Condition): def __str__(self): return "Process: " + str(self.process) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) if not isinstance(self.process, str): @@ -688,7 +688,7 @@ class MouseProcess(Condition): def __str__(self): return "MouseProcess: " + str(self.process) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) if not isinstance(self.process, str): @@ -712,7 +712,7 @@ class Feature(Condition): def __str__(self): return "Feature: " + str(self.feature) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return feature == self.feature @@ -733,7 +733,7 @@ class Report(Condition): def __str__(self): return "Report: " + str(self.report) - def evaluate(self, report, notification, device, status, last_result): + def evaluate(self, report, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return (notification.address >> 4) == self.report @@ -755,7 +755,7 @@ class Setting(Condition): def __str__(self): return "Setting: " + " ".join([str(a) for a in self.args]) - def evaluate(self, report, notification, device, status, last_result): + def evaluate(self, report, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) if len(self.args) < 3: @@ -806,7 +806,7 @@ class Modifiers(Condition): def __str__(self): return "Modifiers: " + str(self.desired) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) if gkeymap: @@ -863,7 +863,7 @@ class Key(Condition): def __str__(self): return "Key: %s (%s)" % ((str(self.key) if self.key else "None"), self.action) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return bool(self.key and self.key == (key_down if self.action == self.DOWN else key_up)) @@ -895,7 +895,7 @@ class KeyIsDown(Condition): def __str__(self): return "KeyIsDown: %s" % (str(self.key) if self.key else "None") - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return key_is_down(self.key) @@ -949,7 +949,7 @@ class Test(Condition): def __str__(self): return "Test: " + str(self.test) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return self.function(feature, notification.address, notification.data, self.parameter) @@ -979,7 +979,7 @@ class TestBytes(Condition): def __str__(self): return "TestBytes: " + str(self.test) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return self.function(feature, notification.address, notification.data) @@ -1012,7 +1012,7 @@ class MouseGesture(Condition): def __str__(self): return "MouseGesture: " + " ".join(self.movements) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) if feature == _F.MOUSE_GESTURE: @@ -1054,7 +1054,7 @@ class Active(Condition): def __str__(self): return "Active: " + str(self.devID) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) dev = device.find(self.devID) @@ -1075,7 +1075,7 @@ class Device(Condition): def __str__(self): return "Device: " + str(self.devID) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) return device.unitId == self.devID or device.serial == self.devID @@ -1095,7 +1095,7 @@ class Host(Condition): def __str__(self): return "Host: " + str(self.host) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluate condition: %s", self) hostname = socket.getfqdn() @@ -1109,7 +1109,7 @@ class Action(RuleComponent): def __init__(self, *args): pass - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): return None @@ -1189,7 +1189,7 @@ class KeyPress(Action): simulate_key(keycode, _KEY_RELEASE) self.mods(level, modifiers, _KEY_RELEASE) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if gkeymap: current = gkeymap.get_modifier_state() if logger.isEnabledFor(logging.INFO): @@ -1209,10 +1209,10 @@ class KeyPress(Action): # KeyDown is dangerous as the key can auto-repeat and make your system unusable # class KeyDown(KeyPress): -# def evaluate(self, feature, notification, device, status, last_result): +# def evaluate(self, feature, notification, device, last_result): # super().keyDown(self.keys, current_key_modifiers) # class KeyUp(KeyPress): -# def evaluate(self, feature, notification, device, status, last_result): +# def evaluate(self, feature, notification, device, last_result): # super().keyUp(self.keys, current_key_modifiers) @@ -1229,7 +1229,7 @@ class MouseScroll(Action): def __str__(self): return "MouseScroll: " + " ".join([str(a) for a in self.amounts]) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): amounts = self.amounts if isinstance(last_result, numbers.Number): amounts = [math.floor(last_result * a) for a in self.amounts] @@ -1268,7 +1268,7 @@ class MouseClick(Action): def __str__(self): return "MouseClick: %s (%d)" % (self.button, self.count) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.INFO): logger.info("MouseClick action: %d %s" % (self.count, self.button)) if self.button and self.count: @@ -1292,7 +1292,7 @@ class Set(Action): def __str__(self): return "Set: " + " ".join([str(a) for a in self.args]) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if len(self.args) < 3: return None if logger.isEnabledFor(logging.INFO): @@ -1335,7 +1335,7 @@ class Execute(Action): def __str__(self): return "Execute: " + " ".join([a for a in self.args]) - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if logger.isEnabledFor(logging.INFO): logger.info("Execute action: %s", self.args) subprocess.Popen(self.args) @@ -1366,9 +1366,9 @@ class Later(Action): def __str__(self): return "Later: [" + str(self.delay) + ", " + ", ".join(str(c) for c in self.components) + "]" - def evaluate(self, feature, notification, device, status, last_result): + def evaluate(self, feature, notification, device, last_result): if self.delay and self.rule: - GLib.timeout_add_seconds(self.delay, Rule.once, self.rule, feature, notification, device, status, last_result) + GLib.timeout_add_seconds(self.delay, Rule.once, self.rule, feature, notification, device, last_result) return None def data(self): @@ -1435,14 +1435,14 @@ def key_is_down(key): return key in keys_down -def evaluate_rules(feature, notification, device, status): +def evaluate_rules(feature, notification, device): if logger.isEnabledFor(logging.DEBUG): logger.debug("evaluating rules on %s", notification) - rules.evaluate(feature, notification, device, status, True) + rules.evaluate(feature, notification, device, True) # process a notification -def process_notification(device, status, notification, feature): +def process_notification(device, notification, feature): global keys_down, g_keys_down, m_keys_down, mr_key_down, key_down, key_up, thumb_wheel_displacement key_down, key_up = None, None # need to keep track of keys that are down to find a new key down @@ -1487,7 +1487,7 @@ def process_notification(device, status, notification, feature): thumb_wheel_displacement = 0 thumb_wheel_displacement += signed(notification.data[0:2]) - GLib.idle_add(evaluate_rules, feature, notification, device, status) + GLib.idle_add(evaluate_rules, feature, notification, device) _XDG_CONFIG_HOME = _os.environ.get("XDG_CONFIG_HOME") or _path.expanduser(_path.join("~", ".config")) diff --git a/lib/logitech_receiver/notifications.py b/lib/logitech_receiver/notifications.py index 006b3597..77c091af 100644 --- a/lib/logitech_receiver/notifications.py +++ b/lib/logitech_receiver/notifications.py @@ -15,8 +15,7 @@ ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# Handles incoming events from the receiver/devices, updating the related -# status object as appropriate. +# Handles incoming events from the receiver/devices, updating the object as appropriate. import logging import threading as _threading @@ -29,10 +28,10 @@ from . import hidpp10_constants as _hidpp10_constants from . import hidpp20_constants as _hidpp20_constants from . import settings_templates as _st from .base import DJ_MESSAGE_ID as _DJ_MESSAGE_ID +from .common import ALERT as _ALERT from .common import Battery as _Battery from .common import strhex as _strhex from .i18n import _ -from .status import ALERT as _ALERT logger = logging.getLogger(__name__) @@ -49,17 +48,12 @@ def process(device, notification): assert device assert notification - assert hasattr(device, "status") - status = device.status - assert status is not None - if not device.isDevice: - return _process_receiver_notification(device, status, notification) - - return _process_device_notification(device, status, notification) + return _process_receiver_notification(device, notification) + return _process_device_notification(device, notification) -def _process_receiver_notification(receiver, status, n): +def _process_receiver_notification(receiver, n): # supposedly only 0x4x notifications arrive for the receiver assert n.sub_id & 0x40 == 0x40 @@ -148,7 +142,7 @@ def _process_receiver_notification(receiver, status, n): logger.warning("%s: unhandled notification %s", receiver, n) -def _process_device_notification(device, status, n): +def _process_device_notification(device, n): # incoming packets with SubId >= 0x80 are supposedly replies from HID++ 1.0 requests, should never get here assert n.sub_id & 0x80 == 0 @@ -163,9 +157,9 @@ def _process_device_notification(device, status, n): # 0x40 to 0x7F appear to be HID++ 1.0 or DJ notifications if n.sub_id >= 0x40: if n.report_id == _DJ_MESSAGE_ID: - return _process_dj_notification(device, status, n) + return _process_dj_notification(device, n) else: - return _process_hidpp10_notification(device, status, n) + return _process_hidpp10_notification(device, n) # These notifications are from the device itself, so it must be active device.online = True @@ -174,7 +168,7 @@ def _process_device_notification(device, status, n): # some custom battery events for HID++ 1.0 devices if device.protocol < 2.0: - return _process_hidpp10_custom_notification(device, status, n) + return _process_hidpp10_custom_notification(device, n) # assuming 0x00 to 0x3F are feature (HID++ 2.0) notifications if not device.features: @@ -186,10 +180,10 @@ def _process_device_notification(device, status, n): logger.warning("%s: notification from invalid feature index %02X: %s", device, n.sub_id, n) return False - return _process_feature_notification(device, status, n, feature) + return _process_feature_notification(device, n, feature) -def _process_dj_notification(device, status, n): +def _process_dj_notification(device, n): if logger.isEnabledFor(logging.DEBUG): logger.debug("%s (%s) DJ %s", device, device.protocol, n) @@ -215,7 +209,7 @@ def _process_dj_notification(device, status, n): logger.warning("%s: unrecognized DJ %s", device, n) -def _process_hidpp10_custom_notification(device, status, n): +def _process_hidpp10_custom_notification(device, n): if logger.isEnabledFor(logging.DEBUG): logger.debug("%s (%s) custom notification %s", device, device.protocol, n) @@ -228,7 +222,7 @@ def _process_hidpp10_custom_notification(device, status, n): logger.warning("%s: unrecognized %s", device, n) -def _process_hidpp10_notification(device, status, n): +def _process_hidpp10_notification(device, n): if n.sub_id == 0x40: # device unpairing if n.address == 0x02: # device un-paired @@ -236,7 +230,7 @@ def _process_hidpp10_notification(device, status, n): if device.number in device.receiver: del device.receiver[device.number] device.changed(active=False, alert=_ALERT.ALL, reason=_("unpaired")) - device.status = None + ## device.status = None else: logger.warning("%s: disconnection with unknown type %02X: %s", device, n.address, n) return True @@ -289,7 +283,7 @@ def _process_hidpp10_notification(device, status, n): logger.warning("%s: unrecognized %s", device, n) -def _process_feature_notification(device, status, n, feature): +def _process_feature_notification(device, n, feature): if logger.isEnabledFor(logging.DEBUG): logger.debug("%s: notification for feature %s, report %s, data %s", device, feature, n.address >> 4, _strhex(n.data)) @@ -440,5 +434,5 @@ def _process_feature_notification(device, status, n, feature): device.setting_callback(device, _st.AdjustableDpi, [profile.resolutions[resolution_index]]) break - _diversion.process_notification(device, status, n, feature) + _diversion.process_notification(device, n, feature) return True diff --git a/lib/logitech_receiver/receiver.py b/lib/logitech_receiver/receiver.py index dfbb6afa..aed45bdf 100644 --- a/lib/logitech_receiver/receiver.py +++ b/lib/logitech_receiver/receiver.py @@ -66,7 +66,8 @@ class Receiver: self.handle = handle self.path = path self.product_id = product_id - self.setting_callback = setting_callback + self.setting_callback = setting_callback # for changes to settings + self.status_callback = None # for changes to other potentially visible aspects self.receiver_kind = receiver_kind self.serial = None self.max_devices = None @@ -105,7 +106,8 @@ class Receiver: def changed(self, alert=ALERT.NOTIFICATION, reason=None): """The status of the device had changed, so invoke the status callback""" - self.status._changed_callback(self, alert=alert, reason=reason) + if self.status_callback is not None: + self.status_callback(self, alert=alert, reason=reason) @property def firmware(self): diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index 4f497f57..c63fecbb 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -841,7 +841,7 @@ class MouseGesturesXY(_RawXYProcessing): logger.info("mouse gesture notification %s", self.data) payload = _pack("!" + (len(self.data) * "h"), *self.data) notification = _HIDPP_Notification(0, 0, 0, 0, payload) - _process_notification(self.device, self.device.status, notification, _F.MOUSE_GESTURE) + _process_notification(self.device, notification, _F.MOUSE_GESTURE) self.fsmState = "idle" def move_action(self, dx, dy): diff --git a/lib/logitech_receiver/status.py b/lib/logitech_receiver/status.py deleted file mode 100644 index adb37703..00000000 --- a/lib/logitech_receiver/status.py +++ /dev/null @@ -1,70 +0,0 @@ -## Copyright (C) 2012-2013 Daniel Pavel -## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ -## -## 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. - -import logging - -from . import hidpp10 -from . import hidpp10_constants as _hidpp10_constants -from .common import NamedInts - -logger = logging.getLogger(__name__) - -_R = _hidpp10_constants.REGISTERS - -_hidpp10 = hidpp10.Hidpp10() - -ALERT = NamedInts(NONE=0x00, NOTIFICATION=0x01, SHOW_WINDOW=0x02, ATTENTION=0x04, ALL=0xFF) - - -def attach_to(device, changed_callback): - assert device - assert changed_callback - - if not hasattr(device, "status") or device.status is None: - if not device.isDevice: - device.status = ReceiverStatus(device, changed_callback) - else: - device.status = DeviceStatus(device, changed_callback) - - -class ReceiverStatus: - """The 'runtime' status of a receiver, currently vestigial.""" - - def __init__(self, receiver, changed_callback): - assert receiver - self._receiver = receiver - assert changed_callback - self._changed_callback = changed_callback - - -class DeviceStatus: - """Holds the 'runtime' status of a peripheral - Currently _active, battery, link_encrypted, notification_flags, error - Updates mostly come from incoming notification events from the device itself. - """ - - def __init__(self, device, changed_callback): - assert device - self._device = device - assert changed_callback - self._changed_callback = changed_callback - self._active = None # is the device active? - - def __bool__(self): - return bool(self._active) - - __nonzero__ = __bool__ diff --git a/lib/solaar/cli/pair.py b/lib/solaar/cli/pair.py index 445c60ff..1a9543cd 100644 --- a/lib/solaar/cli/pair.py +++ b/lib/solaar/cli/pair.py @@ -20,7 +20,6 @@ from logitech_receiver import base as _base from logitech_receiver import hidpp10 from logitech_receiver import hidpp10_constants as _hidpp10_constants from logitech_receiver import notifications as _notifications -from logitech_receiver import status as _status _hidpp10 = hidpp10.Hidpp10() _R = _hidpp10_constants.REGISTERS @@ -38,7 +37,6 @@ def run(receivers, args, find_receiver, _ignore): receiver = receivers[0] assert receiver - receiver.status = _status.ReceiverStatus(receiver, lambda *args, **kwargs: None) # check if it's necessary to set the notification flags old_notification_flags = _hidpp10.get_notification_flags(receiver) or 0 diff --git a/lib/solaar/listener.py b/lib/solaar/listener.py index e03ddecf..34cf8ad8 100644 --- a/lib/solaar/listener.py +++ b/lib/solaar/listener.py @@ -31,7 +31,6 @@ from logitech_receiver import exceptions from logitech_receiver import hidpp10_constants as _hidpp10_constants from logitech_receiver import listener as _listener from logitech_receiver import notifications as _notifications -from logitech_receiver import status as _status from . import configuration @@ -44,15 +43,13 @@ _R = _hidpp10_constants.REGISTERS _IR = _hidpp10_constants.INFO_SUBREGISTERS -_GHOST_DEVICE = namedtuple("_GHOST_DEVICE", ("receiver", "number", "name", "kind", "status", "online")) +_GHOST_DEVICE = namedtuple("_GHOST_DEVICE", ("receiver", "number", "name", "kind", "online")) _GHOST_DEVICE.__bool__ = lambda self: False _GHOST_DEVICE.__nonzero__ = _GHOST_DEVICE.__bool__ def _ghost(device): - return _GHOST_DEVICE( - receiver=device.receiver, number=device.number, name=device.name, kind=device.kind, status=None, online=False - ) + return _GHOST_DEVICE(receiver=device.receiver, number=device.number, name=device.name, kind=device.kind, online=False) class ReceiverListener(_listener.EventsListener): @@ -62,7 +59,7 @@ class ReceiverListener(_listener.EventsListener): assert status_changed_callback super().__init__(receiver, self._notifications_handler) self.status_changed_callback = status_changed_callback - _status.attach_to(receiver, self._status_changed) + receiver.status_callback = self._status_changed def has_started(self): if logger.isEnabledFor(logging.INFO): @@ -95,32 +92,30 @@ class ReceiverListener(_listener.EventsListener): logger.exception("closing receiver %s" % r.path) self.status_changed_callback(r) - def _status_changed(self, device, alert=_status.ALERT.NONE, reason=None): + def _status_changed(self, device, alert=None, reason=None): assert device is not None if logger.isEnabledFor(logging.INFO): try: - device.ping() if device.kind is None: logger.info( - "status_changed %r: %s, %s (%X) %s", + "status_changed %r: %s (%X) %s", device, "present" if bool(device) else "removed", - device.status, - alert, + alert if alert is not None else 0, reason or "", ) else: + device.ping() logger.info( - "status_changed %r: %s %s, %s (%X) %s", + "status_changed %r: %s %s (%X) %s", device, "paired" if bool(device) else "unpaired", "online" if device.online else "offline", - device.status, - alert, + alert if alert is not None else 0, reason or "", ) - except Exception: - logger.info("status_changed for unknown device") + except Exception as e: + logger.info("status_changed for unknown device: %s", e) if device.kind is None: assert device == self.receiver @@ -214,15 +209,11 @@ class ReceiverListener(_listener.EventsListener): # If there are saved configs, bring the device's settings up-to-date. # They will be applied when the device is marked as online. configuration.attach_to(dev) - _status.attach_to(dev, self._status_changed) + dev.status_callback = self._status_changed # the receiver changed status as well self._status_changed(self.receiver) - if not hasattr(dev, "status") or dev.status is None: - # notification before device status set up - don't process it - logger.warning("%s before device %s has status", n, dev) - else: - _notifications.process(dev, n) + _notifications.process(dev, n) if self.receiver.pairing.lock_open and not already_known: # this should be the first notification after a device was paired @@ -297,8 +288,8 @@ def ping_all(resuming=False): count = listener_thread.receiver.count() if count: for dev in listener_thread.receiver: - if resuming and hasattr(dev, "status"): - dev.status._active = None # ensure that settings are pushed + if resuming: + dev._active = None # ensure that settings are pushed if dev.ping(): dev.changed(active=True, push=True) listener_thread._status_changed(dev) diff --git a/lib/solaar/ui/__init__.py b/lib/solaar/ui/__init__.py index 1f852653..21a9a98c 100644 --- a/lib/solaar/ui/__init__.py +++ b/lib/solaar/ui/__init__.py @@ -19,7 +19,7 @@ import logging import gi import yaml as _yaml -from logitech_receiver.status import ALERT +from logitech_receiver.common import ALERT from solaar.i18n import _ from solaar.ui.config_panel import change_setting, record_setting @@ -115,6 +115,8 @@ def _status_changed(device, alert, reason, refresh=False): assert device is not None if logger.isEnabledFor(logging.DEBUG): logger.debug("status changed: %s (%s) %s", device, alert, reason) + if alert is None: + alert = ALERT.NONE tray.update(device) if alert & ALERT.ATTENTION: diff --git a/lib/solaar/ui/notify.py b/lib/solaar/ui/notify.py index d282e49d..e896e78c 100644 --- a/lib/solaar/ui/notify.py +++ b/lib/solaar/ui/notify.py @@ -113,20 +113,21 @@ if available: if reason: message = reason - elif dev.status is None: - message = _("unpaired") - elif bool(dev.status): - message = dev.status_string() or _("connected") else: - message = _("offline") + message = _("unspecified reason") + # elif dev.status is None: + # message = _("unpaired") + # elif bool(dev.status): + # message = dev.status_string() or _("connected") + # else: + # message = _("offline") # we need to use the filename here because the notifications daemon # is an external application that does not know about our icon sets icon_file = _icons.device_icon_file(dev.name, dev.kind) if icon is None else _icons.icon_file(icon) n.update(summary, message, icon_file) - urgency = Notify.Urgency.LOW if dev.status else Notify.Urgency.NORMAL - n.set_urgency(urgency) + n.set_urgency(Notify.Urgency.NORMAL) n.set_hint("desktop-entry", GLib.Variant("s", NAME.lower())) if progress: n.set_hint("value", GLib.Variant("i", progress)) diff --git a/lib/solaar/ui/tray.py b/lib/solaar/ui/tray.py index afad8ee9..929fa88d 100644 --- a/lib/solaar/ui/tray.py +++ b/lib/solaar/ui/tray.py @@ -420,7 +420,7 @@ def _remove_receiver(receiver): def _update_menu_item(index, device): - if device is None or device.status is None: + if device is None: logger.warning("updating an inactive device %s, assuming disconnected", device) return None