Solaar/app/watcher.py

109 lines
2.7 KiB
Python

#
#
#
from threading import Thread
import time
from logging import getLogger as _Logger
from collections import namedtuple
from logitech.devices.constants import STATUS
from receiver import Receiver
_DUMMY_RECEIVER = namedtuple('_DUMMY_RECEIVER', ['NAME', 'kind', 'status', 'status_text', 'max_devices', 'devices'])
_DUMMY_RECEIVER.__nonzero__ = lambda _: False
_DUMMY_RECEIVER.device_name = Receiver.NAME
DUMMY = _DUMMY_RECEIVER(Receiver.NAME, Receiver.NAME, STATUS.UNAVAILABLE, 'Receiver not found.', Receiver.max_devices, {})
_l = _Logger('watcher')
def _sleep(seconds, granularity, breakout=lambda: False):
slept = 0
while slept < seconds and not breakout():
time.sleep(granularity)
slept += granularity
class Watcher(Thread):
"""Keeps an active receiver object if possible, and updates the UI when
necessary.
"""
def __init__(self, apptitle, update_ui, notify=None):
super(Watcher, self).__init__(group=apptitle, name='Watcher')
self._active = False
self._receiver = DUMMY
self.update_ui = update_ui
self.notify = notify or (lambda d: None)
@property
def receiver(self):
return self._receiver
def run(self):
self._active = True
notify_missing = True
while self._active:
if self._receiver == DUMMY:
r = Receiver.open()
if r is None:
if notify_missing:
_sleep(0.8, 0.4, lambda: not self._active)
notify_missing = False
if self._active:
self.update_ui(DUMMY)
self.notify(DUMMY)
_sleep(4, 0.4, lambda: not self._active)
continue
_l.info("receiver %s ", r)
self.update_ui(r)
self.notify(r)
if r.count_devices() > 0:
# give it some time to read all devices
r.status_changed.clear()
_sleep(8, 0.4, r.status_changed.is_set)
if r.devices:
_l.info("%d device(s) found", len(r.devices))
for d in r.devices.values():
self.notify(d)
else:
# if no devices found so far, assume none at all
_l.info("no devices found")
r.status = STATUS.CONNECTED
self._receiver = r
notify_missing = True
if self._active:
if self._receiver:
_l.debug("waiting for status_changed")
sc = self._receiver.status_changed
sc.wait()
sc.clear()
_l.debug("status_changed %s %d", sc.reason, sc.urgent)
self.update_ui(self._receiver)
if sc.reason and sc.urgent:
self.notify(sc.reason)
else:
self._receiver = DUMMY
self.update_ui(DUMMY)
self.notify(DUMMY)
if self._receiver:
self._receiver.close()
def stop(self):
if self._active:
_l.info("stopping %s", self)
self._active = False
if self._receiver:
# break out of an eventual wait()
self._receiver.status_changed.reason = None
self._receiver.status_changed.set()