mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-12-08 20:39:01 +00:00
Add events to all input devices
Fairly major tidy up of the hierarchy as well. There's now a trivial base class: InputDevice which simply permits reading of state. WaitableInputDevice descends from this and introduces waitable events and callbacks, and provides a hook for calling them but needs further machinery to activate that hook. DigitalInputDevice (crap name?) descends from WaitableInputDevice and uses the standard RPi.GPIO callback mechanisms to handle events. This is intended for use with trivial on/off devices with predictably small bounce times. Next is SmoothedInputDevice (crap name?) which also descends from WaitableInputDevice. This includes a background threaded queue which constantly monitors the state of the device and provides a running mean of its state. This is compared to a threshold for determining active / inactive state. This is intended for use with on/off devices that "jitter" a lot and for which a running average is therefore appropriate or for devices which provide an effectively analog readout (like charging capacitor timings). MonitorSensor and LightSensor now descend from SmoothedInputDevice, and Button descends from DigitalInputDevice. All "concrete" classes provide event aliases appropriate to their function (e.g. when_dark, when_pressed, etc.)
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import weakref
|
||||
from threading import Thread, Event
|
||||
from collections import deque
|
||||
|
||||
from RPi import GPIO
|
||||
|
||||
@@ -12,8 +14,14 @@ class GPIODevice(object):
|
||||
if pin is None:
|
||||
raise GPIODeviceError('No GPIO pin number given')
|
||||
self._pin = pin
|
||||
self._active_state = 1
|
||||
self._inactive_state = 0
|
||||
self._active_state = GPIO.HIGH
|
||||
self._inactive_state = GPIO.LOW
|
||||
|
||||
def _read(self):
|
||||
return GPIO.input(self.pin) == self._active_state
|
||||
|
||||
def _fire_events(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def pin(self):
|
||||
@@ -21,7 +29,7 @@ class GPIODevice(object):
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
return GPIO.input(self.pin) == self._active_state
|
||||
return self._read()
|
||||
|
||||
|
||||
_GPIO_THREADS = set()
|
||||
@@ -47,3 +55,44 @@ class GPIOThread(Thread):
|
||||
self.join()
|
||||
_GPIO_THREADS.discard(self)
|
||||
|
||||
|
||||
class GPIOQueue(GPIOThread):
|
||||
def __init__(self, parent, queue_len=5, sample_wait=0.0, partial=False):
|
||||
assert isinstance(parent, GPIODevice)
|
||||
super(GPIOQueue, self).__init__(target=self.fill)
|
||||
if queue_len < 1:
|
||||
raise InputDeviceError('queue_len must be at least one')
|
||||
self.queue = deque(maxlen=queue_len)
|
||||
self.partial = partial
|
||||
self.sample_wait = sample_wait
|
||||
self.full = Event()
|
||||
self.parent = weakref.proxy(parent)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
if not self.partial:
|
||||
self.full.wait()
|
||||
try:
|
||||
return sum(self.queue) / len(self.queue)
|
||||
except ZeroDivisionError:
|
||||
# No data == inactive value
|
||||
return 0.0
|
||||
|
||||
def fill(self):
|
||||
try:
|
||||
while (
|
||||
not self.stopping.wait(self.sample_wait) and
|
||||
len(self.queue) < self.queue.maxlen
|
||||
):
|
||||
self.queue.append(self.parent._read())
|
||||
if self.partial:
|
||||
self.parent._fire_events()
|
||||
self.full.set()
|
||||
while not self.stopping.wait(self.sample_wait):
|
||||
self.queue.append(self.parent._read())
|
||||
self.parent._fire_events()
|
||||
except ReferenceError:
|
||||
# Parent is dead; time to die!
|
||||
pass
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user