diff --git a/docs/api_pins.rst b/docs/api_pins.rst index fda62cf..467de4d 100644 --- a/docs/api_pins.rst +++ b/docs/api_pins.rst @@ -154,6 +154,22 @@ Base classes .. autoclass:: SPI :members: +.. currentmodule:: gpiozero.pins.pi + +.. autoclass:: PiFactory + :members: + +.. autoclass:: PiPin + :members: + +.. currentmodule:: gpiozero.pins.local + +.. autoclass:: LocalPiFactory + :members: + +.. autoclass:: LocalPiPin + :members: + Utilities ========= @@ -164,6 +180,8 @@ non-physical pins are used, or to raise exceptions when pull-downs are requested on pins with physical pull-up resistors attached. The following functions and classes can be used to query this database: +.. currentmodule:: gpiozero + .. autofunction:: pi_info .. autoclass:: PiBoardInfo diff --git a/docs/images/composite_device_hierarchy.pdf b/docs/images/composite_device_hierarchy.pdf index fe1482d..6d032bd 100644 Binary files a/docs/images/composite_device_hierarchy.pdf and b/docs/images/composite_device_hierarchy.pdf differ diff --git a/docs/images/input_device_hierarchy.pdf b/docs/images/input_device_hierarchy.pdf index 994f7db..7b2e60b 100644 Binary files a/docs/images/input_device_hierarchy.pdf and b/docs/images/input_device_hierarchy.pdf differ diff --git a/gpiozero/pins/local.py b/gpiozero/pins/local.py index 13b961b..b9ec330 100644 --- a/gpiozero/pins/local.py +++ b/gpiozero/pins/local.py @@ -25,8 +25,10 @@ from ..exc import DeviceClosed, PinUnknownPi, SPIInvalidClockMode class LocalPiFactory(PiFactory): """ Abstract base class representing pins attached locally to a Pi. This forms - the base class for local-only pin interfaces (:class:`RPiGPIOPin`, - :class:`RPIOPin`, and :class:`NativePin`). + the base class for local-only pin interfaces + (:class:`~gpiozero.pins.rpigpio.RPiGPIOPin`, + :class:`~gpiozero.pins.rpio.RPIOPin`, and + :class:`~gpiozero.pins.native.NativePin`). """ pins = {} diff --git a/gpiozero/pins/mock.py b/gpiozero/pins/mock.py index a874e4d..7af2658 100644 --- a/gpiozero/pins/mock.py +++ b/gpiozero/pins/mock.py @@ -161,9 +161,9 @@ class MockConnectedPin(MockPin): mock pin. This is used in the "real pins" portion of the test suite to check that one pin can influence another. """ - def __init__(self, factory, number): + def __init__(self, factory, number, input_pin=None): super(MockConnectedPin, self).__init__(factory, number) - self.input_pin = None + self.input_pin = input_pin def _change_state(self, value): if self.input_pin: @@ -181,9 +181,9 @@ class MockChargingPin(MockPin): (as if attached to, e.g. a typical circuit using an LDR and a capacitor to time the charging rate). """ - def __init__(self, factory, number): + def __init__(self, factory, number, charge_time=0.01): super(MockChargingPin, self).__init__(factory, number) - self.charge_time = 0.01 # dark charging time + self.charge_time = charge_time # dark charging time self._charge_stop = Event() self._charge_thread = None @@ -220,10 +220,10 @@ class MockTriggerPin(MockPin): corresponding pin instance. When this pin is driven high it will trigger the echo pin to drive high for the echo time. """ - def __init__(self, factory, number): + def __init__(self, factory, number, echo_pin=None, echo_time=0.04): super(MockTriggerPin, self).__init__(factory, number) - self.echo_pin = None - self.echo_time = 0.04 # longest echo time + self.echo_pin = echo_pin + self.echo_time = echo_time # longest echo time self._echo_thread = None def _set_state(self, value): @@ -432,14 +432,14 @@ class MockFactory(LocalPiFactory): def reset(self): self.pins.clear() - def pin(self, spec, pin_class=None): + def pin(self, spec, pin_class=None, **kwargs): if pin_class is None: pin_class = self.pin_class n = self._to_gpio(spec) try: pin = self.pins[n] except KeyError: - pin = pin_class(self, n) + pin = pin_class(self, n, **kwargs) self.pins[n] = pin else: # Ensure the pin class expected supports PWM (or not) diff --git a/gpiozero/pins/pi.py b/gpiozero/pins/pi.py index b5f11ef..6769607 100644 --- a/gpiozero/pins/pi.py +++ b/gpiozero/pins/pi.py @@ -34,7 +34,7 @@ from ..exc import ( class PiFactory(Factory): """ Abstract base class representing hardware attached to a Raspberry Pi. This - forms the base of :class:`LocalPiFactory`. + forms the base of :class:`~gpiozero.pins.local.LocalPiFactory`. """ def __init__(self): self._info = None @@ -187,7 +187,31 @@ class PiFactory(Factory): class PiPin(Pin): """ Abstract base class representing a multi-function GPIO pin attached to a - Raspberry Pi. + Raspberry Pi. This overrides several methods in the abstract base + :class:`~gpiozero.Pin`. Descendents must override the following methods: + + * :meth:`_get_function` + * :meth:`_set_function` + * :meth:`_get_state` + * :meth:`_call_when_changed` + * :meth:`_enable_event_detect` + * :meth:`_disable_event_detect` + + Descendents *may* additionally override the following methods, if + applicable: + + * :meth:`close` + * :meth:`output_with_state` + * :meth:`input_with_pull` + * :meth:`_set_state` + * :meth:`_get_frequency` + * :meth:`_set_frequency` + * :meth:`_get_pull` + * :meth:`_set_pull` + * :meth:`_get_bounce` + * :meth:`_set_bounce` + * :meth:`_get_edges` + * :meth:`_set_edges` """ def __init__(self, factory, number): super(PiPin, self).__init__() @@ -214,6 +238,11 @@ class PiPin(Pin): return self.factory.address + ('GPIO%d' % self.number,) def _call_when_changed(self): + """ + Called to fire the :attr:`when_changed` event handler; override this + in descendents if additional (currently redundant) parameters need + to be passed. + """ method = self.when_changed() if method is None: self.when_changed = None @@ -242,8 +271,17 @@ class PiPin(Pin): self._enable_event_detect() def _enable_event_detect(self): + """ + Enables event detection. This is called to activate event detection on + pin :attr:`number`, watching for the specified :attr:`edges`. In + response, :meth:`_call_when_changed` should be executed. + """ raise NotImplementedError def _disable_event_detect(self): + """ + Disables event detection. This is called to deactivate event detection + on pin :attr:`number`. + """ raise NotImplementedError diff --git a/tests/test_inputs.py b/tests/test_inputs.py index 0b61ddd..d2bf447 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -171,9 +171,7 @@ def test_input_light_sensor(): reason='timing is too random on pypy') def test_input_distance_sensor(): echo_pin = Device._pin_factory.pin(4) - trig_pin = Device._pin_factory.pin(5, pin_class=MockTriggerPin) - trig_pin.echo_pin = echo_pin - trig_pin.echo_time = 0.02 + trig_pin = Device._pin_factory.pin(5, pin_class=MockTriggerPin, echo_pin=echo_pin, echo_time=0.02) with pytest.raises(ValueError): DistanceSensor(echo_pin, trig_pin, max_distance=-1) # normal queue len is large (because the sensor is *really* jittery) but diff --git a/tests/test_real_pins.py b/tests/test_real_pins.py index 2fc0fa7..7aee8ea 100644 --- a/tests/test_real_pins.py +++ b/tests/test_real_pins.py @@ -85,15 +85,13 @@ def pins(request, pin_factory): # Why return both pins in a single fixture? If we defined one fixture for # each pin then pytest will (correctly) test RPiGPIOPin(22) against # NativePin(27) and so on. This isn't supported, so we don't test it - if pin_factory.__class__.__name__ == 'MockFactory': - test_pin = pin_factory.pin(TEST_PIN, pin_class=MockConnectedPin) - else: - test_pin = pin_factory.pin(TEST_PIN) input_pin = pin_factory.pin(INPUT_PIN) input_pin.function = 'input' input_pin.pull = 'down' if pin_factory.__class__.__name__ == 'MockFactory': - test_pin.input_pin = input_pin + test_pin = pin_factory.pin(TEST_PIN, pin_class=MockConnectedPin, input_pin=input_pin) + else: + test_pin = pin_factory.pin(TEST_PIN) def fin(): test_pin.close() input_pin.close()