Merge pull request #574 from waveform80/pin-factory-param

Fix #279
This commit is contained in:
Dave Jones
2017-07-14 13:38:35 +01:00
committed by GitHub
33 changed files with 1037 additions and 617 deletions

View File

@@ -18,21 +18,21 @@ individually.
LEDBoard LEDBoard
======== ========
.. autoclass:: LEDBoard(\*pins, pwm=False, active_high=True, initial_value=False, \*\*named_pins) .. autoclass:: LEDBoard(\*pins, pwm=False, active_high=True, initial_value=False, pin_factory=None, \*\*named_pins)
:inherited-members: :inherited-members:
:members: :members:
LEDBarGraph LEDBarGraph
=========== ===========
.. autoclass:: LEDBarGraph(\*pins, pwm=False, active_high=True, initial_value=0) .. autoclass:: LEDBarGraph(\*pins, pwm=False, active_high=True, initial_value=0, pin_factory=None)
:inherited-members: :inherited-members:
:members: :members:
ButtonBoard ButtonBoard
=========== ===========
.. autoclass:: ButtonBoard(\*pins, pull_up=True, bounce_time=None, hold_time=1, hold_repeat=False, \*\*named_pins) .. autoclass:: ButtonBoard(\*pins, pull_up=True, bounce_time=None, hold_time=1, hold_repeat=False, pin_factory=None, \*\*named_pins)
:inherited-members: :inherited-members:
:members: :members:
@@ -130,14 +130,14 @@ Energenie
StatusZero StatusZero
========== ==========
.. autoclass:: StatusZero .. autoclass:: StatusZero(\*labels, pwm=False, active_high=True, initial_value=False, pin_factory=None)
:inherited-members: :inherited-members:
:members: :members:
StatusBoard StatusBoard
=========== ===========
.. autoclass:: StatusBoard .. autoclass:: StatusBoard(\*labels, pwm=False, active_high=True, initial_value=False, pin_factory=None)
:inherited-members: :inherited-members:
:members: :members:
@@ -168,17 +168,17 @@ to construct classes for their own devices.
LEDCollection LEDCollection
============= =============
.. autoclass:: LEDCollection .. autoclass:: LEDCollection(\*pins, pwm=False, active_high=True, initial_value=False, pin_factory=None, \*\*named_pins)
:members: :members:
CompositeOutputDevice CompositeOutputDevice
===================== =====================
.. autoclass:: CompositeOutputDevice(\*args, _order=None, \*\*kwargs) .. autoclass:: CompositeOutputDevice(\*args, _order=None, pin_factory=None, \*\*kwargs)
:members: :members:
CompositeDevice CompositeDevice
=============== ===============
.. autoclass:: CompositeDevice(\*args, _order=None, \*\*kwargs) .. autoclass:: CompositeDevice(\*args, _order=None, pin_factory=None, \*\*kwargs)
:members: :members:

View File

@@ -33,7 +33,7 @@ are represented in purple, while abstract classes are shaded lighter):
Device Device
====== ======
.. autoclass:: Device .. autoclass:: Device(\*, pin_factory=None)
:members: close, closed, value, is_active :members: close, closed, value, is_active
ValuesMixin ValuesMixin

View File

@@ -16,35 +16,35 @@ everyday components. Components must be wired up correctly before use in code.
Button Button
====== ======
.. autoclass:: Button(pin, pull_up=True, bounce_time=None, hold_time=1, hold_repeat=False) .. autoclass:: Button(pin, \*, pull_up=True, bounce_time=None, hold_time=1, hold_repeat=False, pin_factory=None)
:members: wait_for_press, wait_for_release, pin, is_pressed, is_held, hold_time, held_time, hold_repeat, pull_up, when_pressed, when_released, when_held :members: wait_for_press, wait_for_release, pin, is_pressed, is_held, hold_time, held_time, hold_repeat, pull_up, when_pressed, when_released, when_held
Line Sensor (TRCT5000) Line Sensor (TRCT5000)
====================== ======================
.. autoclass:: LineSensor(pin, queue_len=5, sample_rate=100, threshold=0.5, partial=False) .. autoclass:: LineSensor(pin, \*, queue_len=5, sample_rate=100, threshold=0.5, partial=False, pin_factory=None)
:members: wait_for_line, wait_for_no_line, pin, line_detected, when_line, when_no_line :members: wait_for_line, wait_for_no_line, pin, line_detected, when_line, when_no_line
Motion Sensor (D-SUN PIR) Motion Sensor (D-SUN PIR)
========================= =========================
.. autoclass:: MotionSensor(pin, queue_len=1, sample_rate=10, threshold=0.5, partial=False) .. autoclass:: MotionSensor(pin, \*, queue_len=1, sample_rate=10, threshold=0.5, partial=False, pin_factory=None)
:members: wait_for_motion, wait_for_no_motion, pin, motion_detected, when_motion, when_no_motion :members: wait_for_motion, wait_for_no_motion, pin, motion_detected, when_motion, when_no_motion
Light Sensor (LDR) Light Sensor (LDR)
================== ==================
.. autoclass:: LightSensor(pin, queue_len=5, charge_time_limit=0.01, threshold=0.1, partial=False) .. autoclass:: LightSensor(pin, \*, queue_len=5, charge_time_limit=0.01, threshold=0.1, partial=False, pin_factory=None)
:members: wait_for_light, wait_for_dark, pin, light_detected, when_light, when_dark :members: wait_for_light, wait_for_dark, pin, light_detected, when_light, when_dark
Distance Sensor (HC-SR04) Distance Sensor (HC-SR04)
========================= =========================
.. autoclass:: DistanceSensor(echo, trigger, queue_len=30, max_distance=1, threshold_distance=0.3, partial=False) .. autoclass:: DistanceSensor(echo, trigger, \*, queue_len=30, max_distance=1, threshold_distance=0.3, partial=False, pin_factory=None)
:members: wait_for_in_range, wait_for_out_of_range, trigger, echo, when_in_range, when_out_of_range, max_distance, distance, threshold_distance :members: wait_for_in_range, wait_for_out_of_range, trigger, echo, when_in_range, when_out_of_range, max_distance, distance, threshold_distance
Base Classes Base Classes
@@ -63,24 +63,24 @@ to construct classes for their own devices.
DigitalInputDevice DigitalInputDevice
================== ==================
.. autoclass:: DigitalInputDevice(pin, pull_up=False, bounce_time=None) .. autoclass:: DigitalInputDevice(pin, \*, pull_up=False, bounce_time=None, pin_factory=None)
:members: :members:
SmoothedInputDevice SmoothedInputDevice
=================== ===================
.. autoclass:: SmoothedInputDevice .. autoclass:: SmoothedInputDevice(pin, \*, pull_up=False, threshold=0.5, queue_len=5, sample_wait=0.0, partial=False, pin_factory=None)
:members: :members:
InputDevice InputDevice
=========== ===========
.. autoclass:: InputDevice(pin, pull_up=False) .. autoclass:: InputDevice(pin, \*, pull_up=False, pin_factory=None)
:members: :members:
GPIODevice GPIODevice
========== ==========
.. autoclass:: GPIODevice(pin) .. autoclass:: GPIODevice(pin, pin_factory=None)
:members: :members:

View File

@@ -16,44 +16,44 @@ everyday components. Components must be wired up correctly before use in code.
LED LED
=== ===
.. autoclass:: LED(pin, active_high=True, initial_value=False) .. autoclass:: LED(pin, \*, active_high=True, initial_value=False, pin_factory=None)
:members: on, off, toggle, blink, pin, is_lit :members: on, off, toggle, blink, pin, is_lit
PWMLED PWMLED
====== ======
.. autoclass:: PWMLED(pin, active_high=True, initial_value=0, frequency=100) .. autoclass:: PWMLED(pin, \*, active_high=True, initial_value=0, frequency=100, pin_factory=None)
:members: on, off, toggle, blink, pulse, pin, is_lit, value :members: on, off, toggle, blink, pulse, pin, is_lit, value
RGBLED RGBLED
====== ======
.. autoclass:: RGBLED(red, green, blue, active_high=True, initial_value=(0, 0, 0), pwm=True) .. autoclass:: RGBLED(red, green, blue, \*, active_high=True, initial_value=(0, 0, 0), pwm=True, pin_factory=None)
:members: on, off, toggle, blink, pulse, red, green, blue, is_lit, color :members: on, off, toggle, blink, pulse, red, green, blue, is_lit, color
Buzzer Buzzer
====== ======
.. autoclass:: Buzzer(pin, active_high=True, initial_value=False) .. autoclass:: Buzzer(pin, \*, active_high=True, initial_value=False, pin_factory=None)
:members: on, off, toggle, beep, pin, is_active :members: on, off, toggle, beep, pin, is_active
Motor Motor
===== =====
.. autoclass:: Motor(forward, backward, pwm=True) .. autoclass:: Motor(forward, backward, \*, pwm=True, pin_factory=None)
:members: forward, backward, stop :members: forward, backward, stop
Servo Servo
===== =====
.. autoclass:: Servo(pin, initial_value=0, min_pulse_width=1/1000, max_pulse_width=2/1000, frame_width=20/1000) .. autoclass:: Servo(pin, \*, initial_value=0, min_pulse_width=1/1000, max_pulse_width=2/1000, frame_width=20/1000, pin_factory=None)
:inherited-members: :inherited-members:
:members: :members:
AngularServo AngularServo
============ ============
.. autoclass:: AngularServo(pin, initial_angle=0, min_angle=-90, max_angle=90, min_pulse_width=1/1000, max_pulse_width=2/1000, frame_width=20/1000) .. autoclass:: AngularServo(pin, \*, initial_angle=0, min_angle=-90, max_angle=90, min_pulse_width=1/1000, max_pulse_width=2/1000, frame_width=20/1000, pin_factory=None)
:inherited-members: :inherited-members:
:members: :members:
@@ -73,25 +73,25 @@ to construct classes for their own devices.
DigitalOutputDevice DigitalOutputDevice
=================== ===================
.. autoclass:: DigitalOutputDevice(pin, active_high=True, initial_value=False) .. autoclass:: DigitalOutputDevice(pin, \*, active_high=True, initial_value=False, pin_factory=None)
:members: :members:
PWMOutputDevice PWMOutputDevice
=============== ===============
.. autoclass:: PWMOutputDevice(pin, active_high=True, initial_value=0, frequency=100) .. autoclass:: PWMOutputDevice(pin, \*, active_high=True, initial_value=0, frequency=100, pin_factory=None)
:members: :members:
OutputDevice OutputDevice
============ ============
.. autoclass:: OutputDevice(pin, active_high=True, initial_value=False) .. autoclass:: OutputDevice(pin, \*, active_high=True, initial_value=False, pin_factory=None)
:members: :members:
GPIODevice GPIODevice
========== ==========
.. autoclass:: GPIODevice(pin) .. autoclass:: GPIODevice(pin, \*, pin_factory=None)
:members: :members:
:noindex: :noindex:

View File

@@ -11,10 +11,23 @@ are concerned with. However, some users may wish to take advantage of the
capabilities of alternative GPIO implementations or (in future) use GPIO capabilities of alternative GPIO implementations or (in future) use GPIO
extender chips. This is the purpose of the pins portion of the library. extender chips. This is the purpose of the pins portion of the library.
When you construct a device, you pass in a pin specification. However, what the When you construct a device, you pass in a pin specification. This is passed to
library actually expects is a :class:`Pin` implementation. If it finds anything a pin :class:`Factory` which turns it into a :class:`Pin` implementation. The
else, it uses the existing ``Device.pin_factory`` to construct a :class:`Pin` default factory can be queried (and changed) with ``Device.pin_factory``, i.e.
implementation based on the specification. the ``pin_factory`` attribute of the :class:`Device` class. However, all
classes accept a ``pin_factory`` keyword argument to their constructors
permitting the factory to be overridden on a per-device basis (the reason for
allowing per-device factories is made apparent later in the :doc:`remote_gpio`
chapter).
This is illustrated in the following flow-chart:
.. image:: images/device_pin_flowchart.*
The default factory is constructed when GPIO Zero is first imported; if no
default factory can be constructed (e.g. because no GPIO implementations are
installed, or all of them fail to load for whatever reason), an
:exc:`ImportError` will be raised.
Changing the pin factory Changing the pin factory
======================== ========================
@@ -24,7 +37,7 @@ The default pin factory can be replaced by specifying a value for the
.. code-block:: console .. code-block:: console
pi@raspberrypi $ GPIOZERO_PIN_FACTORY=native python $ GPIOZERO_PIN_FACTORY=native python
Python 3.4.2 (default, Oct 19 2014, 13:31:11) Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux [GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information. Type "help", "copyright", "credits" or "license" for more information.
@@ -37,8 +50,8 @@ export this value:
.. code-block:: console .. code-block:: console
pi@raspberrypi $ export GPIOZERO_PIN_FACTORY=native $ export GPIOZERO_PIN_FACTORY=native
pi@raspberrypi $ python $ python
Python 3.4.2 (default, Oct 19 2014, 13:31:11) Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux [GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information. Type "help", "copyright", "credits" or "license" for more information.
@@ -46,7 +59,7 @@ export this value:
>>> gpiozero.Device.pin_factory >>> gpiozero.Device.pin_factory
<gpiozero.pins.native.NativeFactory object at 0x762c26b0> <gpiozero.pins.native.NativeFactory object at 0x762c26b0>
>>> quit() >>> quit()
pi@raspberrypi $ python $ python
Python 3.4.2 (default, Oct 19 2014, 13:31:11) Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux [GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information. Type "help", "copyright", "credits" or "license" for more information.
@@ -73,17 +86,30 @@ they are tried by default.
| native | :class:`gpiozero.pins.native.NativeFactory` | :class:`gpiozero.pins.native.NativePin` | | native | :class:`gpiozero.pins.native.NativeFactory` | :class:`gpiozero.pins.native.NativePin` |
+---------+-----------------------------------------------+-------------------------------------------+ +---------+-----------------------------------------------+-------------------------------------------+
If you need to change the default pin factory from within a script, set If you need to change the default pin factory from within a script, either set
``Device.pin_factory`` to the new factory instance to use:: ``Device.pin_factory`` to the new factory instance to use::
from gpiozero.pins.native import NativeFactory from gpiozero.pins.native import NativeFactory
from gpiozero import * from gpiozero import Device, LED
Device.pin_factory = NativeFactory() Device.pin_factory = NativeFactory()
# These will now implicitly use NativePin instead of
# RPiGPIOPin
led1 = LED(16)
led2 = LED(17)
Or use the ``pin_factory`` keyword parameter mentioned above::
from gpiozero.pins.native import NativeFactory
from gpiozero import LED from gpiozero import LED
# This will now use NativePin instead of RPiGPIOPin my_factory = NativeFactory()
led = LED(16)
# This will use NativePin instead of RPiGPIOPin for led1
# but led2 will continue to use RPiGPIOPin
led1 = LED(16, pin_factory=my_factory)
led2 = LED(17)
Certain factories may take default information from additional sources. Certain factories may take default information from additional sources.
For example, to default to creating pins with For example, to default to creating pins with
@@ -100,11 +126,13 @@ Like the ``GPIOZERO_PIN_FACTORY`` value, these can be exported from your
.. warning:: .. warning::
The astute and mischievous reader may note that it is possible to mix pin The astute and mischievous reader may note that it is possible to mix
implementations, e.g. using ``RPiGPIOPin`` for one pin, and ``NativePin`` strictly local pin implementations, e.g. using ``RPiGPIOPin`` for one pin,
for another. This is unsupported, and if it results in your script and ``NativePin`` for another. This is unsupported, and if it results in
crashing, your components failing, or your Raspberry Pi turning into an your script crashing, your components failing, or your Raspberry Pi turning
actual raspberry pie, you have only yourself to blame. into an actual raspberry pie, you have only yourself to blame.
Sensible uses of multiple pin factories are given in :doc:`remote_gpio`.
RPi.GPIO RPi.GPIO

View File

@@ -64,6 +64,10 @@ omit any arguments from either scheme. The defaults are:
* *clock_pin* defaults to 11, *mosi_pin* defaults to 10, *miso_pin* defaults * *clock_pin* defaults to 11, *mosi_pin* defaults to 10, *miso_pin* defaults
to 9, and *select_pin* defaults to 8. to 9, and *select_pin* defaults to 8.
* As with other GPIO based devices you can optionally specify a *pin_factory*
argument overriding the default pin factory (see :doc:`api_pins` for more
information).
Hence the following constructors are all equivalent:: Hence the following constructors are all equivalent::
from gpiozero import MCP3008 from gpiozero import MCP3008

View File

@@ -0,0 +1,19 @@
/* vim: set et sw=4 sts=4: */
digraph device_pins {
graph [rankdir=TB];
node [shape=rect, shape=filled, fontname=Sans, fontsize=10];
edge [fontname=Sans, fontsize=10];
constructor [label="LED(pin_spec, ...,\npin_factory=None)"];
pin_factory_kwarg [shape=diamond,label="pin_factory == None?"];
default_factory [label="self.pin_factory = Device.pin_factory"];
override_factory [label="self.pin_factory = pin_factory"];
factory_pin [label="self.pin = self.pin_factory.pin(pin_spec)"];
constructor->pin_factory_kwarg;
pin_factory_kwarg->default_factory [label="yes"];
pin_factory_kwarg->override_factory [label="no"];
default_factory->factory_pin;
override_factory->factory_pin;
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
-->
<!-- Title: device_pins Pages: 1 -->
<svg width="372pt" height="273pt"
viewBox="0.00 0.00 371.50 273.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 269)">
<title>device_pins</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-269 367.5,-269 367.5,4 -4,4"/>
<!-- constructor -->
<g id="node1" class="node"><title>constructor</title>
<polygon fill="none" stroke="black" points="243.5,-265 136.5,-265 136.5,-229 243.5,-229 243.5,-265"/>
<text text-anchor="middle" x="190" y="-250" font-family="Sans" font-size="10.00">LED(pin_spec, ...,</text>
<text text-anchor="middle" x="190" y="-239" font-family="Sans" font-size="10.00">pin_factory=None)</text>
</g>
<!-- pin_factory_kwarg -->
<g id="node2" class="node"><title>pin_factory_kwarg</title>
<polygon fill="none" stroke="black" points="190,-192 88.3711,-174 190,-156 291.629,-174 190,-192"/>
<text text-anchor="middle" x="190" y="-171.5" font-family="Sans" font-size="10.00">pin_factory == None?</text>
</g>
<!-- constructor&#45;&gt;pin_factory_kwarg -->
<g id="edge1" class="edge"><title>constructor&#45;&gt;pin_factory_kwarg</title>
<path fill="none" stroke="black" d="M190,-228.813C190,-220.789 190,-211.047 190,-202.069"/>
<polygon fill="black" stroke="black" points="193.5,-202.029 190,-192.029 186.5,-202.029 193.5,-202.029"/>
</g>
<!-- default_factory -->
<g id="node3" class="node"><title>default_factory</title>
<polygon fill="none" stroke="black" points="190,-109 0,-109 0,-73 190,-73 190,-109"/>
<text text-anchor="middle" x="95" y="-88.5" font-family="Sans" font-size="10.00">self.pin_factory = Device.pin_factory</text>
</g>
<!-- pin_factory_kwarg&#45;&gt;default_factory -->
<g id="edge2" class="edge"><title>pin_factory_kwarg&#45;&gt;default_factory</title>
<path fill="none" stroke="black" d="M173.452,-158.891C159.473,-146.971 139.158,-129.65 122.703,-115.621"/>
<polygon fill="black" stroke="black" points="124.861,-112.861 114.98,-109.036 120.319,-118.187 124.861,-112.861"/>
<text text-anchor="middle" x="157.5" y="-130" font-family="Sans" font-size="10.00">yes</text>
</g>
<!-- override_factory -->
<g id="node4" class="node"><title>override_factory</title>
<polygon fill="none" stroke="black" points="363.5,-109 208.5,-109 208.5,-73 363.5,-73 363.5,-109"/>
<text text-anchor="middle" x="286" y="-88.5" font-family="Sans" font-size="10.00">self.pin_factory = pin_factory</text>
</g>
<!-- pin_factory_kwarg&#45;&gt;override_factory -->
<g id="edge3" class="edge"><title>pin_factory_kwarg&#45;&gt;override_factory</title>
<path fill="none" stroke="black" d="M206.722,-158.891C220.849,-146.971 241.377,-129.65 258.005,-115.621"/>
<polygon fill="black" stroke="black" points="260.424,-118.16 265.809,-109.036 255.909,-112.81 260.424,-118.16"/>
<text text-anchor="middle" x="250.5" y="-130" font-family="Sans" font-size="10.00">no</text>
</g>
<!-- factory_pin -->
<g id="node5" class="node"><title>factory_pin</title>
<polygon fill="none" stroke="black" points="311,-36 111,-36 111,-0 311,-0 311,-36"/>
<text text-anchor="middle" x="211" y="-15.5" font-family="Sans" font-size="10.00">self.pin = self.pin_factory.pin(pin_spec)</text>
</g>
<!-- default_factory&#45;&gt;factory_pin -->
<g id="edge4" class="edge"><title>default_factory&#45;&gt;factory_pin</title>
<path fill="none" stroke="black" d="M122.785,-72.9937C138.167,-63.5785 157.484,-51.7554 174.117,-41.5748"/>
<polygon fill="black" stroke="black" points="176.338,-44.3193 183.04,-36.1136 172.683,-38.3489 176.338,-44.3193"/>
</g>
<!-- override_factory&#45;&gt;factory_pin -->
<g id="edge5" class="edge"><title>override_factory&#45;&gt;factory_pin</title>
<path fill="none" stroke="black" d="M267.845,-72.8129C258.448,-63.9174 246.82,-52.9094 236.533,-43.1717"/>
<polygon fill="black" stroke="black" points="238.656,-40.3619 228.988,-36.0288 233.844,-45.4454 238.656,-40.3619"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -9,42 +9,58 @@ operating systems, including for PCs using the :doc:`remote_gpio` feature.
Raspberry Pi Raspberry Pi
============ ============
First, update your repositories list:: First, update your repositories list:
sudo apt update .. code-block:: console
Then install the package for Python 3:: pi@raspberrypi:~$ sudo apt update
sudo apt install python3-gpiozero Then install the package for Python 3:
or Python 2:: .. code-block:: console
sudo apt install python-gpiozero pi@raspberrypi:~$ sudo apt install python3-gpiozero
or Python 2:
.. code-block:: console
pi@raspberrypi:~$ sudo apt install python-gpiozero
Linux Linux
===== =====
First, update your distribution's repositories list. For example:: First, update your distribution's repositories list. For example:
sudo apt update .. code-block:: console
Then install pip for Python 3:: $ sudo apt update
sudo apt install python3-pip Then install pip for Python 3:
or Python 3:: .. code-block:: console
sudo apt install python-pip $ sudo apt install python3-pip
or Python 3:
.. code-block:: console
$ sudo apt install python-pip
(Alternatively, install pip with `get-pip`_.) (Alternatively, install pip with `get-pip`_.)
Next, install gpiozero for Python 3:: Next, install gpiozero for Python 3:
sudo pip3 install gpiozero .. code-block:: console
or Python 2:: $ sudo pip3 install gpiozero
sudo pip install gpiozero or Python 2:
.. code-block:: console
$ sudo pip install gpiozero
.. note:: .. note::
@@ -55,24 +71,32 @@ or Python 2::
Mac OS Mac OS
====== ======
First, install pip:: First, install pip:
??? .. code-block:: console
Next, install gpiozero with pip:: $ ???
pip install gpiozero Next, install gpiozero with pip:
.. code-block:: console
$ pip install gpiozero
Windows Windows
======= =======
First, install pip:: First, install pip:
??? .. code-block:: doscon
Next, install gpiozero with pip:: C:\Users\user1> ???
pip install gpiozero Next, install gpiozero with pip:
.. code-block:: doscon
C:\Users\user1> pip install gpiozero
.. _Raspbian Jessie: https://www.raspberrypi.org/downloads/raspbian/ .. _Raspbian Jessie: https://www.raspberrypi.org/downloads/raspbian/

View File

@@ -102,4 +102,4 @@ the ``pip`` utility. This can be done with the following command in Raspbian:
Alternatively, install pip with `get-pip`_. Alternatively, install pip with `get-pip`_.
.. _get_pip: https://pip.pypa.io/en/stable/installing/ .. _get-pip: https://pip.pypa.io/en/stable/installing/

View File

@@ -36,7 +36,9 @@ functionality without the need to wire up your own LEDs (also useful because
the power and activity LEDs are "known good"). the power and activity LEDs are "known good").
Firstly you need to disable the usual triggers for the built-in LEDs. This can Firstly you need to disable the usual triggers for the built-in LEDs. This can
be done from the terminal with the following commands:: be done from the terminal with the following commands:
.. code-block:: console
$ echo none | sudo tee /sys/class/leds/led0/trigger $ echo none | sudo tee /sys/class/leds/led0/trigger
$ echo gpio | sudo tee /sys/class/leds/led1/trigger $ echo gpio | sudo tee /sys/class/leds/led1/trigger
@@ -46,7 +48,9 @@ Now you can control the LEDs with gpiozero like so:
.. literalinclude:: examples/led_builtin.py .. literalinclude:: examples/led_builtin.py
To revert the LEDs to their usual purpose you can either reboot your Pi or To revert the LEDs to their usual purpose you can either reboot your Pi or
run the following commands:: run the following commands:
.. code-block:: console
$ echo mmc0 | sudo tee /sys/class/leds/led0/trigger $ echo mmc0 | sudo tee /sys/class/leds/led0/trigger
$ echo input | sudo tee /sys/class/leds/led1/trigger $ echo input | sudo tee /sys/class/leds/led1/trigger

View File

@@ -7,7 +7,7 @@ Remote GPIO
GPIO Zero supports a number of different pin implementations (low-level pin GPIO Zero supports a number of different pin implementations (low-level pin
libraries which deal with the GPIO pins directly). By default, the `RPi.GPIO`_ libraries which deal with the GPIO pins directly). By default, the `RPi.GPIO`_
library is used (assuming it is installed on your system), but you can library is used (assuming it is installed on your system), but you can
optionally specify one to use. For more information, see the :doc:`pins` optionally specify one to use. For more information, see the :doc:`api_pins`
documentation page. documentation page.
One of the pin libraries supported, `pigpio`_, provides the ability to control One of the pin libraries supported, `pigpio`_, provides the ability to control
@@ -23,9 +23,11 @@ Preparing the Raspberry Pi
If you're using Raspbian Jessie (desktop - not Jessie Lite) then you have If you're using Raspbian Jessie (desktop - not Jessie Lite) then you have
everything you need to use the remote GPIO feature. If you're using Jessie Lite, everything you need to use the remote GPIO feature. If you're using Jessie Lite,
or another distribution, you'll need to install pigpio:: or another distribution, you'll need to install pigpio:
sudo apt install pigpio .. code-block:: console
$ sudo apt install pigpio
Then you just need to enable **Remote GPIO** in the Raspberry Pi configuration Then you just need to enable **Remote GPIO** in the Raspberry Pi configuration
tool: tool:
@@ -34,21 +36,25 @@ tool:
(Alternatively, use ``sudo raspi-config`` on the command line) (Alternatively, use ``sudo raspi-config`` on the command line)
Then launch the pigpio daemon:: Then launch the pigpio daemon:
sudo pigpiod .. code-block:: console
$ sudo pigpiod
To only allow connections from a specific IP address, use the ``-n`` flag. For To only allow connections from a specific IP address, use the ``-n`` flag. For
example:: example:
sudo pigpiod -n localhost # allow localhost only .. code-block:: console
sudo pigpiod -n 192.168.1.65 # allow 192.168.1.65 only
sudo pigpiod -n localhost -n 192.168.1.65 # allow localhost and 192.168.1.65 only $ sudo pigpiod -n localhost # allow localhost only
$ sudo pigpiod -n 192.168.1.65 # allow 192.168.1.65 only
$ sudo pigpiod -n localhost -n 192.168.1.65 # allow localhost and 192.168.1.65 only
You will need to launch the pigpio daemon every time you wish to use this You will need to launch the pigpio daemon every time you wish to use this
feature. To automate running the daemon at boot time:: feature. To automate running the daemon at boot time::
sudo systemctl enable pigpiod $ sudo systemctl enable pigpiod
Preparing the host computer Preparing the host computer
=========================== ===========================
@@ -61,72 +67,100 @@ Python library on the PC.
Raspberry Pi Raspberry Pi
------------ ------------
First, update your repositories list:: First, update your repositories list:
sudo apt update .. code-block:: console
Then install the pigpio library for Python 3:: $ sudo apt update
sudo apt install python3-pigpio Then install the pigpio library for Python 3:
or Python 2:: .. code-block:: console
sudo apt install python-pigpio $ sudo apt install python3-pigpio
Alternatively, install with pip:: or Python 2:
sudo pip3 install pigpio .. code-block:: console
or:: $ sudo apt install python-pigpio
sudo pip install pigpio Alternatively, install with pip:
.. code-block:: console
$ sudo pip3 install pigpio
or:
.. code-block:: console
$ sudo pip install pigpio
Linux Linux
----- -----
First, update your distribution's repositories list. For example:: First, update your distribution's repositories list. For example:
sudo apt update .. code-block:: console
Then install pip for Python 3:: $ sudo apt update
sudo apt install python3-pip Then install pip for Python 3:
or Python 2:: .. code-block:: console
sudo apt install python-pip $ sudo apt install python3-pip
or Python 2:
.. code-block:: console
$ sudo apt install python-pip
(Alternatively, install pip with `get-pip`_.) (Alternatively, install pip with `get-pip`_.)
Next, install pigpio for Python 3:: Next, install pigpio for Python 3:
sudo pip3 install pigpio .. code-block:: console
or Python 2:: $ sudo pip3 install pigpio
sudo pip install pigpio or Python 2:
.. code-block:: console
$ sudo pip install pigpio
Mac OS Mac OS
------ ------
First, install pip:: First, install pip:
??? .. code-block:: console
Next, install pigpio with pip:: $ ???
pip install pigpio Next, install pigpio with pip:
.. code-block:: console
$ pip install pigpio
Windows Windows
------- -------
First install pip:: First install pip:
??? .. code-block:: doscon
Next, install pigpio with pip:: C:\Users\user1> ???
pip install pigpio Next, install pigpio with pip:
.. code-block:: doscon
C:\Users\user1> pip install pigpio
Environment variables Environment variables
===================== =====================
@@ -135,7 +169,9 @@ The simplest way to use devices with remote pins is to set the ``PIGPIO_ADDR``
environment variable to the IP address of the desired Raspberry Pi. You must environment variable to the IP address of the desired Raspberry Pi. You must
run your Python script or launch your development environment with the run your Python script or launch your development environment with the
environment variable set using the command line. For example, one of the environment variable set using the command line. For example, one of the
following:: following:
.. code-block:: console
$ PIGPIO_ADDR=192.168.1.3 python3 hello.py $ PIGPIO_ADDR=192.168.1.3 python3 hello.py
$ PIGPIO_ADDR=192.168.1.3 python3 $ PIGPIO_ADDR=192.168.1.3 python3
@@ -147,7 +183,9 @@ pigpio Python library installed, this will work with no further configuration.
However, if you are running this from a Raspberry Pi, you will also need to However, if you are running this from a Raspberry Pi, you will also need to
ensure the default pin factory is set to ``PiGPIOPin``. If ``RPi.GPIO`` is ensure the default pin factory is set to ``PiGPIOPin``. If ``RPi.GPIO`` is
installed, this will be selected as the default pin factory, so either uninstall installed, this will be selected as the default pin factory, so either uninstall
it, or use another environment variable to set it to ``PiGPIOPin``:: it, or use another environment variable to set it to ``PiGPIOPin``:
.. code-block:: console
$ GPIOZERO_PIN_FACTORY=pigpio PIGPIO_ADDR=192.168.1.3 python3 hello.py $ GPIOZERO_PIN_FACTORY=pigpio PIGPIO_ADDR=192.168.1.3 python3 hello.py
@@ -160,12 +198,16 @@ with no modifications needed. For example:
.. literalinclude:: examples/led_1.py .. literalinclude:: examples/led_1.py
When run with:: When run with:
.. code-block:: console
$ PIGPIO_ADDR=192.168.1.3 python3 led.py $ PIGPIO_ADDR=192.168.1.3 python3 led.py
will flash the LED connected to pin 17 of the Raspberry Pi with the IP address will flash the LED connected to pin 17 of the Raspberry Pi with the IP address
``192.168.1.3``. And:: ``192.168.1.3``. And:
.. code-block:: console
$ PIGPIO_ADDR=192.168.1.4 python3 led.py $ PIGPIO_ADDR=192.168.1.4 python3 led.py
@@ -236,11 +278,13 @@ computer using remote pins.
First, configure the boot partition of the SD card: First, configure the boot partition of the SD card:
1. Edit ``config.txt`` and add ``dtoverlay=dwc2`` on a new line, then save the 1. Edit ``config.txt`` and add ``dtoverlay=dwc2`` on a new line, then save the
file. file.
2. Create an empty file called ``ssh`` (no file extension) and save it in the 2. Create an empty file called ``ssh`` (no file extension) and save it in the
boot partition. boot partition.
3. Edit ``cmdline.txt`` and insert ``modules-load=dwc2,g_ether`` after 3. Edit ``cmdline.txt`` and insert ``modules-load=dwc2,g_ether`` after
``rootwait``. ``rootwait``.
(See `blog.gbaman.info`_ for more information) (See `blog.gbaman.info`_ for more information)

View File

@@ -47,6 +47,10 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
specific order). All keyword arguments *must* be included in the specific order). All keyword arguments *must* be included in the
collection. If omitted, an alphabetically sorted order will be selected collection. If omitted, an alphabetically sorted order will be selected
for keyword arguments. for keyword arguments.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def on(self): def on(self):
@@ -124,6 +128,10 @@ class ButtonBoard(HoldMixin, CompositeDevice):
executed once per hold. This parameter can only be specified as a executed once per hold. This parameter can only be specified as a
keyword parameter. keyword parameter.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
:param \*\*named_pins: :param \*\*named_pins:
Specify GPIO pins that buttons of the board are attached to, Specify GPIO pins that buttons of the board are attached to,
associating each button with a property name. You can designate as associating each button with a property name. You can designate as
@@ -135,6 +143,7 @@ class ButtonBoard(HoldMixin, CompositeDevice):
bounce_time = kwargs.pop('bounce_time', None) bounce_time = kwargs.pop('bounce_time', None)
hold_time = kwargs.pop('hold_time', 1) hold_time = kwargs.pop('hold_time', 1)
hold_repeat = kwargs.pop('hold_repeat', False) hold_repeat = kwargs.pop('hold_repeat', False)
pin_factory = kwargs.pop('pin_factory', None)
order = kwargs.pop('_order', None) order = kwargs.pop('_order', None)
super(ButtonBoard, self).__init__( super(ButtonBoard, self).__init__(
*( *(
@@ -142,6 +151,7 @@ class ButtonBoard(HoldMixin, CompositeDevice):
for pin in args for pin in args
), ),
_order=order, _order=order,
pin_factory=pin_factory,
**{ **{
name: Button(pin, pull_up, bounce_time, hold_time, hold_repeat) name: Button(pin, pull_up, bounce_time, hold_time, hold_repeat)
for name, pin in kwargs.items() for name, pin in kwargs.items()
@@ -209,20 +219,28 @@ class LEDCollection(CompositeOutputDevice):
pwm = kwargs.pop('pwm', False) pwm = kwargs.pop('pwm', False)
active_high = kwargs.pop('active_high', True) active_high = kwargs.pop('active_high', True)
initial_value = kwargs.pop('initial_value', False) initial_value = kwargs.pop('initial_value', False)
pin_factory = kwargs.pop('pin_factory', None)
order = kwargs.pop('_order', None) order = kwargs.pop('_order', None)
LEDClass = PWMLED if pwm else LED LEDClass = PWMLED if pwm else LED
super(LEDCollection, self).__init__( super(LEDCollection, self).__init__(
*( *(
pin_or_collection pin_or_collection
if isinstance(pin_or_collection, LEDCollection) else if isinstance(pin_or_collection, LEDCollection) else
LEDClass(pin_or_collection, active_high, initial_value) LEDClass(
pin_or_collection, active_high, initial_value,
pin_factory=pin_factory
)
for pin_or_collection in args for pin_or_collection in args
), ),
_order=order, _order=order,
pin_factory=pin_factory,
**{ **{
name: pin_or_collection name: pin_or_collection
if isinstance(pin_or_collection, LEDCollection) else if isinstance(pin_or_collection, LEDCollection) else
LEDClass(pin_or_collection, active_high, initial_value) LEDClass(
pin_or_collection, active_high, initial_value,
pin_factory=pin_factory
)
for name, pin_or_collection in kwargs.items() for name, pin_or_collection in kwargs.items()
}) })
leds = [] leds = []
@@ -283,6 +301,10 @@ class LEDBoard(LEDCollection):
the device will be switched on initially. This parameter can only be the device will be switched on initially. This parameter can only be
specified as a keyword parameter. specified as a keyword parameter.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
:param \*\*named_pins: :param \*\*named_pins:
Specify GPIO pins that LEDs of the board are attached to, associating Specify GPIO pins that LEDs of the board are attached to, associating
each LED with a property name. You can designate as many pins as each LED with a property name. You can designate as many pins as
@@ -486,6 +508,10 @@ class LEDBarGraph(LEDCollection):
The initial :attr:`value` of the graph given as a float between -1 and The initial :attr:`value` of the graph given as a float between -1 and
+1. Defaults to ``0.0``. This parameter can only be specified as a +1. Defaults to ``0.0``. This parameter can only be specified as a
keyword parameter. keyword parameter.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__(self, *pins, **kwargs): def __init__(self, *pins, **kwargs):
@@ -495,9 +521,12 @@ class LEDBarGraph(LEDCollection):
pwm = kwargs.pop('pwm', False) pwm = kwargs.pop('pwm', False)
active_high = kwargs.pop('active_high', True) active_high = kwargs.pop('active_high', True)
initial_value = kwargs.pop('initial_value', 0.0) initial_value = kwargs.pop('initial_value', 0.0)
pin_factory = kwargs.pop('pin_factory', None)
if kwargs: if kwargs:
raise TypeError('unexpected keyword argument: %s' % kwargs.popitem()[0]) raise TypeError('unexpected keyword argument: %s' % kwargs.popitem()[0])
super(LEDBarGraph, self).__init__(*pins, pwm=pwm, active_high=active_high) super(LEDBarGraph, self).__init__(
*pins, pwm=pwm, active_high=active_high, pin_factory=pin_factory
)
try: try:
self.value = initial_value self.value = initial_value
except: except:
@@ -572,12 +601,17 @@ class LedBorg(RGBLED):
each component of the LedBorg. If ``False``, construct regular each component of the LedBorg. If ``False``, construct regular
:class:`LED` instances, which prevents smooth color graduations. :class:`LED` instances, which prevents smooth color graduations.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _PiBorg LedBorg: https://www.piborg.org/ledborg .. _PiBorg LedBorg: https://www.piborg.org/ledborg
""" """
def __init__(self, initial_value=(0, 0, 0), pwm=True): def __init__(self, initial_value=(0, 0, 0), pwm=True, pin_factory=None):
super(LedBorg, self).__init__(red=17, green=27, blue=22, super(LedBorg, self).__init__(red=17, green=27, blue=22,
pwm=pwm, initial_value=initial_value) pwm=pwm, initial_value=initial_value,
pin_factory=pin_factory)
class PiLiter(LEDBoard): class PiLiter(LEDBoard):
@@ -604,12 +638,17 @@ class PiLiter(LEDBoard):
in when configured for output (warning: this can be on). If ``True``, in when configured for output (warning: this can be on). If ``True``,
the device will be switched on initially. the device will be switched on initially.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _Ciseco Pi-LITEr: http://shop.ciseco.co.uk/pi-liter-8-led-strip-for-the-raspberry-pi/ .. _Ciseco Pi-LITEr: http://shop.ciseco.co.uk/pi-liter-8-led-strip-for-the-raspberry-pi/
""" """
def __init__(self, pwm=False, initial_value=False): def __init__(self, pwm=False, initial_value=False, pin_factory=None):
super(PiLiter, self).__init__(4, 17, 27, 18, 22, 23, 24, 25, super(PiLiter, self).__init__(4, 17, 27, 18, 22, 23, 24, 25,
pwm=pwm, initial_value=initial_value) pwm=pwm, initial_value=initial_value,
pin_factory=pin_factory)
class PiLiterBarGraph(LEDBarGraph): class PiLiterBarGraph(LEDBarGraph):
@@ -634,13 +673,18 @@ class PiLiterBarGraph(LEDBarGraph):
The initial :attr:`value` of the graph given as a float between -1 and The initial :attr:`value` of the graph given as a float between -1 and
+1. Defaults to ``0.0``. +1. Defaults to ``0.0``.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _Ciseco Pi-LITEr: http://shop.ciseco.co.uk/pi-liter-8-led-strip-for-the-raspberry-pi/ .. _Ciseco Pi-LITEr: http://shop.ciseco.co.uk/pi-liter-8-led-strip-for-the-raspberry-pi/
""" """
def __init__(self, pwm=False, initial_value=0.0): def __init__(self, pwm=False, initial_value=0.0, pin_factory=None):
pins = (4, 17, 27, 18, 22, 23, 24, 25) pins = (4, 17, 27, 18, 22, 23, 24, 25)
super(PiLiterBarGraph, self).__init__(*pins, super(PiLiterBarGraph, self).__init__(
pwm=pwm, initial_value=initial_value) *pins, pwm=pwm, initial_value=initial_value, pin_factory=pin_factory
)
class TrafficLights(LEDBoard): class TrafficLights(LEDBoard):
@@ -680,9 +724,14 @@ class TrafficLights(LEDBoard):
The GPIO pin that the yellow LED is attached to. This is merely an The GPIO pin that the yellow LED is attached to. This is merely an
alias for the ``amber`` parameter - you can't specify both ``amber`` alias for the ``amber`` parameter - you can't specify both ``amber``
and ``yellow``. and ``yellow``.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__(self, red=None, amber=None, green=None, def __init__(self, red=None, amber=None, green=None,
pwm=False, initial_value=False, yellow=None): pwm=False, initial_value=False, yellow=None,
pin_factory=None):
if amber is not None and yellow is not None: if amber is not None and yellow is not None:
raise OutputDeviceBadValue( raise OutputDeviceBadValue(
'Only one of amber or yellow can be specified' 'Only one of amber or yellow can be specified'
@@ -700,7 +749,7 @@ class TrafficLights(LEDBoard):
) )
super(TrafficLights, self).__init__( super(TrafficLights, self).__init__(
pwm=pwm, initial_value=initial_value, pwm=pwm, initial_value=initial_value,
_order=devices.keys(), _order=devices.keys(), pin_factory=pin_factory,
**devices) **devices)
def __getattr__(self, name): def __getattr__(self, name):
@@ -739,11 +788,16 @@ class PiTraffic(TrafficLights):
in when configured for output (warning: this can be on). If ``True``, in when configured for output (warning: this can be on). If ``True``,
the device will be switched on initially. the device will be switched on initially.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _Low Voltage Labs PI-TRAFFIC: http://lowvoltagelabs.com/products/pi-traffic/ .. _Low Voltage Labs PI-TRAFFIC: http://lowvoltagelabs.com/products/pi-traffic/
""" """
def __init__(self, pwm=False, initial_value=False): def __init__(self, pwm=False, initial_value=False, pin_factory=None):
super(PiTraffic, self).__init__(9, 10, 11, super(PiTraffic, self).__init__(9, 10, 11,
pwm=pwm, initial_value=initial_value) pwm=pwm, initial_value=initial_value,
pin_factory=pin_factory)
class PiStop(TrafficLights): class PiStop(TrafficLights):
@@ -774,6 +828,10 @@ class PiStop(TrafficLights):
in when configured for output (warning: this can be on). If ``True``, in when configured for output (warning: this can be on). If ``True``,
the device will be switched on initially. the device will be switched on initially.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _PiHardware Pi-Stop: https://pihw.wordpress.com/meltwaters-pi-hardware-kits/pi-stop/ .. _PiHardware Pi-Stop: https://pihw.wordpress.com/meltwaters-pi-hardware-kits/pi-stop/
.. _location: https://github.com/PiHw/Pi-Stop/blob/master/markdown_source/markdown/Discover-PiStop.md .. _location: https://github.com/PiHw/Pi-Stop/blob/master/markdown_source/markdown/Discover-PiStop.md
""" """
@@ -786,13 +844,17 @@ class PiStop(TrafficLights):
'D': (2, 3, 4), 'D': (2, 3, 4),
} }
def __init__(self, location=None, pwm=False, initial_value=False): def __init__(
self, location=None, pwm=False, initial_value=False,
pin_factory=None):
gpios = self.LOCATIONS.get(location, None) gpios = self.LOCATIONS.get(location, None)
if gpios is None: if gpios is None:
raise ValueError('location must be one of: %s' % raise ValueError('location must be one of: %s' %
', '.join(sorted(self.LOCATIONS.keys()))) ', '.join(sorted(self.LOCATIONS.keys())))
super(PiStop, self).__init__(*gpios, super(PiStop, self).__init__(
pwm=pwm, initial_value=initial_value) *gpios, pwm=pwm, initial_value=initial_value,
pin_factory=pin_factory
)
class StatusZero(LEDBoard): class StatusZero(LEDBoard):
@@ -817,6 +879,10 @@ class StatusZero(LEDBoard):
not all strips are given labels, any remaining strips will not be not all strips are given labels, any remaining strips will not be
initialised. initialised.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _STATUS Zero: https://thepihut.com/statuszero .. _STATUS Zero: https://thepihut.com/statuszero
""" """
default_labels = ('one', 'two', 'three') default_labels = ('one', 'two', 'three')
@@ -827,6 +893,7 @@ class StatusZero(LEDBoard):
(22, 27), (22, 27),
(9, 10), (9, 10),
) )
pin_factory = kwargs.pop('pin_factory', None)
if len(labels) == 0: if len(labels) == 0:
labels = self.default_labels labels = self.default_labels
elif len(labels) > len(pins): elif len(labels) > len(pins):
@@ -834,10 +901,15 @@ class StatusZero(LEDBoard):
dup, count = Counter(labels).most_common(1)[0] dup, count = Counter(labels).most_common(1)[0]
if count > 1: if count > 1:
raise ValueError("Duplicate label %s" % dup) raise ValueError("Duplicate label %s" % dup)
super(StatusZero, self).__init__(_order=labels, **{ super(StatusZero, self).__init__(
label: LEDBoard(red=red, green=green, _order=('red', 'green'), **kwargs) _order=labels, pin_factory=pin_factory, **{
label: LEDBoard(
red=red, green=green, _order=('red', 'green'),
pin_factory=pin_factory, **kwargs
)
for (green, red), label in zip(pins, labels) for (green, red), label in zip(pins, labels)
}) }
)
class StatusBoard(CompositeOutputDevice): class StatusBoard(CompositeOutputDevice):
@@ -862,6 +934,10 @@ class StatusBoard(CompositeOutputDevice):
will be initialised with names 'one' to 'five'. If some, but not all will be initialised with names 'one' to 'five'. If some, but not all
strips are given labels, any remaining strips will not be initialised. strips are given labels, any remaining strips will not be initialised.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _STATUS: https://thepihut.com/status .. _STATUS: https://thepihut.com/status
""" """
default_labels = ('one', 'two', 'three', 'four', 'five') default_labels = ('one', 'two', 'three', 'four', 'five')
@@ -874,6 +950,7 @@ class StatusBoard(CompositeOutputDevice):
(5, 11, 26), (5, 11, 26),
(13, 6, 18), (13, 6, 18),
) )
pin_factory = kwargs.pop('pin_factory', None)
if len(labels) == 0: if len(labels) == 0:
labels = self.default_labels labels = self.default_labels
elif len(labels) > len(pins): elif len(labels) > len(pins):
@@ -881,14 +958,18 @@ class StatusBoard(CompositeOutputDevice):
dup, count = Counter(labels).most_common(1)[0] dup, count = Counter(labels).most_common(1)[0]
if count > 1: if count > 1:
raise ValueError("Duplicate label %s" % dup) raise ValueError("Duplicate label %s" % dup)
super(StatusBoard, self).__init__(_order=labels, **{ super(StatusBoard, self).__init__(
_order=labels, pin_factory=pin_factory, **{
label: CompositeOutputDevice( label: CompositeOutputDevice(
button=Button(button), button=Button(button, pin_factory=pin_factory),
lights=LEDBoard( lights=LEDBoard(
red=red, green=green, _order=('red', 'green'), **kwargs red=red, green=green, _order=('red', 'green'),
), _order=('button', 'lights')) pin_factory=pin_factory, **kwargs
), _order=('button', 'lights'), pin_factory=pin_factory
)
for (green, red, button), label in zip(pins, labels) for (green, red, button), label in zip(pins, labels)
}) }
)
class SnowPi(LEDBoard): class SnowPi(LEDBoard):
@@ -917,29 +998,38 @@ class SnowPi(LEDBoard):
in when configured for output (warning: this can be on). If ``True``, in when configured for output (warning: this can be on). If ``True``,
the device will be switched on initially. the device will be switched on initially.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _Ryanteck SnowPi: https://ryanteck.uk/raspberry-pi/114-snowpi-the-gpio-snowman-for-raspberry-pi-0635648608303.html .. _Ryanteck SnowPi: https://ryanteck.uk/raspberry-pi/114-snowpi-the-gpio-snowman-for-raspberry-pi-0635648608303.html
""" """
def __init__(self, pwm=False, initial_value=False): def __init__(self, pwm=False, initial_value=False, pin_factory=None):
super(SnowPi, self).__init__( super(SnowPi, self).__init__(
arms=LEDBoard( arms=LEDBoard(
left=LEDBoard( left=LEDBoard(
top=17, middle=18, bottom=22, top=17, middle=18, bottom=22,
pwm=pwm, initial_value=initial_value, pwm=pwm, initial_value=initial_value,
_order=('top', 'middle', 'bottom')), _order=('top', 'middle', 'bottom'),
pin_factory=pin_factory),
right=LEDBoard( right=LEDBoard(
top=7, middle=8, bottom=9, top=7, middle=8, bottom=9,
pwm=pwm, initial_value=initial_value, pwm=pwm, initial_value=initial_value,
_order=('top', 'middle', 'bottom')), _order=('top', 'middle', 'bottom'),
_order=('left', 'right') pin_factory=pin_factory),
_order=('left', 'right'),
pin_factory=pin_factory
), ),
eyes=LEDBoard( eyes=LEDBoard(
left=23, right=24, left=23, right=24,
pwm=pwm, initial_value=initial_value, pwm=pwm, initial_value=initial_value,
_order=('left', 'right') _order=('left', 'right'),
pin_factory=pin_factory
), ),
nose=25, nose=25,
pwm=pwm, initial_value=initial_value, pwm=pwm, initial_value=initial_value,
_order=('eyes', 'nose', 'arms') _order=('eyes', 'nose', 'arms'),
pin_factory=pin_factory
) )
@@ -957,12 +1047,18 @@ class TrafficLightsBuzzer(CompositeOutputDevice):
:param Button button: :param Button button:
An instance of :class:`Button` representing the button on the HAT. An instance of :class:`Button` representing the button on the HAT.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__(self, lights, buzzer, button): def __init__(self, lights, buzzer, button, pin_factory=None):
super(TrafficLightsBuzzer, self).__init__( super(TrafficLightsBuzzer, self).__init__(
lights=lights, buzzer=buzzer, button=button, lights=lights, buzzer=buzzer, button=button,
_order=('lights', 'buzzer', 'button')) _order=('lights', 'buzzer', 'button'),
pin_factory=pin_factory
)
class FishDish(TrafficLightsBuzzer): class FishDish(TrafficLightsBuzzer):
@@ -985,14 +1081,19 @@ class FishDish(TrafficLightsBuzzer):
LED. If ``False`` (the default), construct regular :class:`LED` LED. If ``False`` (the default), construct regular :class:`LED`
instances. instances.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _Pi Supply FishDish: https://www.pi-supply.com/product/fish-dish-raspberry-pi-led-buzzer-board/ .. _Pi Supply FishDish: https://www.pi-supply.com/product/fish-dish-raspberry-pi-led-buzzer-board/
""" """
def __init__(self, pwm=False): def __init__(self, pwm=False, pin_factory=None):
super(FishDish, self).__init__( super(FishDish, self).__init__(
TrafficLights(9, 22, 4, pwm=pwm), TrafficLights(9, 22, 4, pwm=pwm, pin_factory=pin_factory),
Buzzer(8), Buzzer(8, pin_factory=pin_factory),
Button(7, pull_up=False), Button(7, pull_up=False, pin_factory=pin_factory),
pin_factory=pin_factory
) )
@@ -1016,14 +1117,19 @@ class TrafficHat(TrafficLightsBuzzer):
LED. If ``False`` (the default), construct regular :class:`LED` LED. If ``False`` (the default), construct regular :class:`LED`
instances. instances.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _Ryanteck Traffic HAT: https://ryanteck.uk/hats/1-traffichat-0635648607122.html .. _Ryanteck Traffic HAT: https://ryanteck.uk/hats/1-traffichat-0635648607122.html
""" """
def __init__(self, pwm=False): def __init__(self, pwm=False, pin_factory=None):
super(TrafficHat, self).__init__( super(TrafficHat, self).__init__(
TrafficLights(24, 23, 22, pwm=pwm), TrafficLights(24, 23, 22, pwm=pwm, pin_factory=pin_factory),
Buzzer(5), Buzzer(5, pin_factory=pin_factory),
Button(25), Button(25, pin_factory=pin_factory),
pin_factory=pin_factory
) )
@@ -1049,13 +1155,19 @@ class Robot(SourceMixin, CompositeDevice):
:param tuple right: :param tuple right:
A tuple of two GPIO pins representing the forward and backward inputs A tuple of two GPIO pins representing the forward and backward inputs
of the right motor's controller. of the right motor's controller.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__(self, left=None, right=None): def __init__(self, left=None, right=None, pin_factory=None):
super(Robot, self).__init__( super(Robot, self).__init__(
left_motor=Motor(*left), left_motor=Motor(*left, pin_factory=pin_factory),
right_motor=Motor(*right), right_motor=Motor(*right, pin_factory=pin_factory),
_order=('left_motor', 'right_motor')) _order=('left_motor', 'right_motor'),
pin_factory=pin_factory
)
@property @property
def value(self): def value(self):
@@ -1148,11 +1260,17 @@ class RyanteckRobot(Robot):
robot = RyanteckRobot() robot = RyanteckRobot()
robot.forward() robot.forward()
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _Ryanteck MCB: https://ryanteck.uk/add-ons/6-ryanteck-rpi-motor-controller-board-0635648607160.html .. _Ryanteck MCB: https://ryanteck.uk/add-ons/6-ryanteck-rpi-motor-controller-board-0635648607160.html
""" """
def __init__(self): def __init__(self, pin_factory=None):
super(RyanteckRobot, self).__init__((17, 18), (22, 23)) super(RyanteckRobot, self).__init__(
(17, 18), (22, 23), pin_factory=pin_factory
)
class CamJamKitRobot(Robot): class CamJamKitRobot(Robot):
@@ -1168,20 +1286,31 @@ class CamJamKitRobot(Robot):
robot = CamJamKitRobot() robot = CamJamKitRobot()
robot.forward() robot.forward()
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _CamJam #3 EduKit: http://camjam.me/?page_id=1035 .. _CamJam #3 EduKit: http://camjam.me/?page_id=1035
""" """
def __init__(self): def __init__(self, pin_factory=None):
super(CamJamKitRobot, self).__init__((9, 10), (7, 8)) super(CamJamKitRobot, self).__init__(
(9, 10), (7, 8), pin_factory=pin_factory
)
class _EnergenieMaster(SharedMixin, CompositeOutputDevice): class _EnergenieMaster(SharedMixin, CompositeOutputDevice):
def __init__(self): def __init__(self, pin_factory=None):
self._lock = Lock() self._lock = Lock()
super(_EnergenieMaster, self).__init__( super(_EnergenieMaster, self).__init__(
*(OutputDevice(pin) for pin in (17, 22, 23, 27)), *(
mode=OutputDevice(24), enable=OutputDevice(25), OutputDevice(pin, pin_factory=pin_factory)
_order=('mode', 'enable')) for pin in (17, 22, 23, 27)
),
mode=OutputDevice(24, pin_factory=pin_factory),
enable=OutputDevice(25, pin_factory=pin_factory),
_order=('mode', 'enable'), pin_factory=pin_factory
)
def close(self): def close(self):
if self._lock: if self._lock:
@@ -1190,7 +1319,7 @@ class _EnergenieMaster(SharedMixin, CompositeOutputDevice):
self._lock = None self._lock = None
@classmethod @classmethod
def _shared_key(cls): def _shared_key(cls, pin_factory):
# There's only one Energenie master # There's only one Energenie master
return None return None
@@ -1231,18 +1360,22 @@ class Energenie(SourceMixin, Device):
the socket, which will be set upon construction. This defaults to the socket, which will be set upon construction. This defaults to
``False`` which will switch the socket off. ``False`` which will switch the socket off.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _Energenie socket: https://energenie4u.co.uk/index.php/catalogue/product/ENER002-2PI .. _Energenie socket: https://energenie4u.co.uk/index.php/catalogue/product/ENER002-2PI
""" """
def __init__(self, socket=None, initial_value=False): def __init__(self, socket=None, initial_value=False, pin_factory=None):
if socket is None: if socket is None:
raise EnergenieSocketMissing('socket number must be provided') raise EnergenieSocketMissing('socket number must be provided')
if not (1 <= socket <= 4): if not (1 <= socket <= 4):
raise EnergenieBadSocket('socket number must be between 1 and 4') raise EnergenieBadSocket('socket number must be between 1 and 4')
self._value = None self._value = None
super(Energenie, self).__init__() super(Energenie, self).__init__(pin_factory=pin_factory)
self._socket = socket self._socket = socket
self._master = _EnergenieMaster() self._master = _EnergenieMaster(pin_factory=pin_factory)
if initial_value: if initial_value:
self.on() self.on()
else: else:

View File

@@ -11,7 +11,7 @@ import os
import atexit import atexit
import weakref import weakref
import warnings import warnings
from collections import namedtuple, defaultdict from collections import namedtuple
from itertools import chain from itertools import chain
from types import FunctionType from types import FunctionType
from threading import Lock from threading import Lock
@@ -194,77 +194,25 @@ class Device(ValuesMixin, GPIOBase):
property, the :attr:`value` property, and the :meth:`close` method). property, the :attr:`value` property, and the :meth:`close` method).
""" """
pin_factory = None # instance of a Factory sub-class pin_factory = None # instance of a Factory sub-class
_reservations = defaultdict(list) # maps pin addresses to lists of devices
_res_lock = Lock() def __init__(self, **kwargs):
# Force pin_factory to be keyword-only, even in Python 2
pin_factory = kwargs.pop('pin_factory', None)
if pin_factory is None:
self.pin_factory = Device.pin_factory
else:
self.pin_factory = pin_factory
if kwargs:
raise TypeError("Device.__init__() got unexpected keyword "
"argument '%s'" % kwargs.popitem()[0])
super(Device, self).__init__()
def __repr__(self): def __repr__(self):
return "<gpiozero.%s object>" % (self.__class__.__name__) return "<gpiozero.%s object>" % (self.__class__.__name__)
def _reserve_pins(self, *pins_or_addresses):
"""
Called to indicate that the device reserves the right to use the
specified *pins_or_addresses*. This should be done during device
construction. If pins are reserved, you must ensure that the
reservation is released by eventually called :meth:`_release_pins`.
The *pins_or_addresses* can be actual :class:`Pin` instances or the
addresses of pin instances (each address is a tuple of strings). The
latter form is permitted to ensure that devices do not have to
construct :class:`Pin` objects to reserve pins. This is important as
constructing a pin often configures it (e.g. as an input) which
conflicts with alternate pin functions like SPI.
"""
addresses = (
p.address if isinstance(p, Pin) else p
for p in pins_or_addresses
)
with Device._res_lock:
for address in addresses:
for device_ref in Device._reservations[address]:
device = device_ref()
if device is not None and self._conflicts_with(device):
raise GPIOPinInUse(
'pin %s is already in use by %r' % (
'/'.join(address), device)
)
Device._reservations[address].append(weakref.ref(self))
def _release_pins(self, *pins_or_addresses):
"""
Releases the reservation of this device against *pins_or_addresses*.
This is typically called during :meth:`close` to clean up reservations
taken during construction. Releasing a reservation that is not
currently held will be silently ignored (to permit clean-up after
failed / partial construction).
"""
addresses = (
p.address if isinstance(p, Pin) else p
for p in pins_or_addresses
)
with Device._res_lock:
for address in addresses:
Device._reservations[address] = [
ref for ref in Device._reservations[address]
if ref() not in (self, None) # may as well clean up dead refs
]
def _release_all(self):
"""
Releases all pin reservations taken out by this device. See
:meth:`_release_pins` for further information).
"""
with Device._res_lock:
Device._reservations = defaultdict(list, {
address: [
ref for ref in conflictors
if ref() not in (self, None)
]
for address, conflictors in Device._reservations.items()
})
def _conflicts_with(self, other): def _conflicts_with(self, other):
""" """
Called by :meth:`_reserve_pin` to test whether the *other* Called by :meth:`Factory.reserve_pins` to test whether the *other*
:class:`Device` using a common pin conflicts with this device's intent :class:`Device` using a common pin conflicts with this device's intent
to use it. The default is ``True`` indicating that all devices conflict to use it. The default is ``True`` indicating that all devices conflict
with common pins. Sub-classes may override this to permit more nuanced with common pins. Sub-classes may override this to permit more nuanced
@@ -315,6 +263,7 @@ class CompositeDevice(Device):
self._named = frozendict({}) self._named = frozendict({})
self._namedtuple = None self._namedtuple = None
self._order = kwargs.pop('_order', None) self._order = kwargs.pop('_order', None)
pin_factory = kwargs.pop('pin_factory', None)
try: try:
if self._order is None: if self._order is None:
self._order = sorted(kwargs.keys()) self._order = sorted(kwargs.keys())
@@ -336,7 +285,7 @@ class CompositeDevice(Device):
dev.close() dev.close()
raise raise
self._all = args + tuple(kwargs[v] for v in self._order) self._all = args + tuple(kwargs[v] for v in self._order)
super(CompositeDevice, self).__init__() super(CompositeDevice, self).__init__(pin_factory=pin_factory)
def __getattr__(self, name): def __getattr__(self, name):
# if _named doesn't exist yet, pretend it's an empty dict # if _named doesn't exist yet, pretend it's an empty dict
@@ -412,20 +361,17 @@ class GPIODevice(Device):
this is ``None``, :exc:`GPIOPinMissing` will be raised. If the pin is this is ``None``, :exc:`GPIOPinMissing` will be raised. If the pin is
already in use by another device, :exc:`GPIOPinInUse` will be raised. already in use by another device, :exc:`GPIOPinInUse` will be raised.
""" """
def __init__(self, pin=None): def __init__(self, pin=None, **kwargs):
super(GPIODevice, self).__init__() super(GPIODevice, self).__init__(**kwargs)
# self._pin must be set before any possible exceptions can be raised # self._pin must be set before any possible exceptions can be raised
# because it's accessed in __del__. However, it mustn't be given the # because it's accessed in __del__. However, it mustn't be given the
# value of pin until we've verified that it isn't already allocated # value of pin until we've verified that it isn't already allocated
self._pin = None self._pin = None
if pin is None: if pin is None:
raise GPIOPinMissing('No pin given') raise GPIOPinMissing('No pin given')
if isinstance(pin, Pin):
self._reserve_pins(pin)
else:
# Check you can reserve *before* constructing the pin # Check you can reserve *before* constructing the pin
self._reserve_pins(Device.pin_factory.pin_address(pin)) self.pin_factory.reserve_pins(self, pin)
pin = Device.pin_factory.pin(pin) pin = self.pin_factory.pin(pin)
self._pin = pin self._pin = pin
self._active_state = True self._active_state = True
self._inactive_state = False self._inactive_state = False
@@ -443,7 +389,7 @@ class GPIODevice(Device):
def close(self): def close(self):
super(GPIODevice, self).close() super(GPIODevice, self).close()
if self._pin is not None: if self._pin is not None:
self._release_pins(self._pin) self.pin_factory.release_pins(self, self._pin.number)
self._pin.close() self._pin.close()
self._pin = None self._pin = None
@@ -512,10 +458,10 @@ def _default_pin_factory(name=os.getenv('GPIOZERO_PIN_FACTORY', None)):
def _devices_shutdown(): def _devices_shutdown():
if Device.pin_factory: if Device.pin_factory:
with Device._res_lock: with Device.pin_factory._res_lock:
reserved_devices = { reserved_devices = {
dev dev
for ref_list in Device._reservations.values() for ref_list in Device.pin_factory._reservations.values()
for ref in ref_list for ref in ref_list
for dev in (ref(),) for dev in (ref(),)
if dev is not None if dev is not None

View File

@@ -32,9 +32,13 @@ class InputDevice(GPIODevice):
:param bool pull_up: :param bool pull_up:
If ``True``, the pin will be pulled high with an internal resistor. If If ``True``, the pin will be pulled high with an internal resistor. If
``False`` (the default), the pin will be pulled low. ``False`` (the default), the pin will be pulled low.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__(self, pin=None, pull_up=False): def __init__(self, pin=None, pull_up=False, pin_factory=None):
super(InputDevice, self).__init__(pin) super(InputDevice, self).__init__(pin, pin_factory=pin_factory)
try: try:
self.pin.function = 'input' self.pin.function = 'input'
pull = 'up' if pull_up else 'down' pull = 'up' if pull_up else 'down'
@@ -75,9 +79,16 @@ class DigitalInputDevice(EventsMixin, InputDevice):
Specifies the length of time (in seconds) that the component will Specifies the length of time (in seconds) that the component will
ignore changes in state after an initial change. This defaults to ignore changes in state after an initial change. This defaults to
``None`` which indicates that no bounce compensation will be performed. ``None`` which indicates that no bounce compensation will be performed.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__(self, pin=None, pull_up=False, bounce_time=None): def __init__(
super(DigitalInputDevice, self).__init__(pin, pull_up) self, pin=None, pull_up=False, bounce_time=None, pin_factory=None):
super(DigitalInputDevice, self).__init__(
pin, pull_up, pin_factory=pin_factory
)
try: try:
self.pin.bounce = bounce_time self.pin.bounce = bounce_time
self.pin.edges = 'both' self.pin.edges = 'both'
@@ -127,12 +138,18 @@ class SmoothedInputDevice(EventsMixin, InputDevice):
(from the :attr:`is_active` property) will block until the queue has (from the :attr:`is_active` property) will block until the queue has
filled. If ``True``, a value will be returned immediately, but be filled. If ``True``, a value will be returned immediately, but be
aware that this value is likely to fluctuate excessively. aware that this value is likely to fluctuate excessively.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__( def __init__(
self, pin=None, pull_up=False, threshold=0.5, self, pin=None, pull_up=False, threshold=0.5,
queue_len=5, sample_wait=0.0, partial=False): queue_len=5, sample_wait=0.0, partial=False, pin_factory=None):
self._queue = None self._queue = None
super(SmoothedInputDevice, self).__init__(pin, pull_up) super(SmoothedInputDevice, self).__init__(
pin, pull_up, pin_factory=pin_factory
)
try: try:
self._queue = GPIOQueue(self, queue_len, sample_wait, partial) self._queue = GPIOQueue(self, queue_len, sample_wait, partial)
self.threshold = float(threshold) self.threshold = float(threshold)
@@ -263,11 +280,17 @@ class Button(HoldMixin, DigitalInputDevice):
as long as the device remains active, every *hold_time* seconds. If as long as the device remains active, every *hold_time* seconds. If
``False`` (the default) the :attr:`when_held` handler will be only be ``False`` (the default) the :attr:`when_held` handler will be only be
executed once per hold. executed once per hold.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__( def __init__(
self, pin=None, pull_up=True, bounce_time=None, self, pin=None, pull_up=True, bounce_time=None,
hold_time=1, hold_repeat=False): hold_time=1, hold_repeat=False, pin_factory=None):
super(Button, self).__init__(pin, pull_up, bounce_time) super(Button, self).__init__(
pin, pull_up, bounce_time, pin_factory=pin_factory
)
self.hold_time = hold_time self.hold_time = hold_time
self.hold_repeat = hold_repeat self.hold_repeat = hold_repeat
@@ -325,14 +348,19 @@ class LineSensor(SmoothedInputDevice):
filled with values. Only set this to ``True`` if you require values filled with values. Only set this to ``True`` if you require values
immediately after object construction. immediately after object construction.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _CamJam #3 EduKit: http://camjam.me/?page_id=1035 .. _CamJam #3 EduKit: http://camjam.me/?page_id=1035
""" """
def __init__( def __init__(
self, pin=None, queue_len=5, sample_rate=100, threshold=0.5, self, pin=None, queue_len=5, sample_rate=100, threshold=0.5,
partial=False): partial=False, pin_factory=None):
super(LineSensor, self).__init__( super(LineSensor, self).__init__(
pin, pull_up=False, threshold=threshold, pin, pull_up=False, threshold=threshold,
queue_len=queue_len, sample_wait=1 / sample_rate, partial=partial queue_len=queue_len, sample_wait=1 / sample_rate, partial=partial,
pin_factory=pin_factory
) )
try: try:
self._queue.start() self._queue.start()
@@ -394,13 +422,18 @@ class MotionSensor(SmoothedInputDevice):
:attr:`~SmoothedInputDevice.is_active` until the internal queue has :attr:`~SmoothedInputDevice.is_active` until the internal queue has
filled with values. Only set this to ``True`` if you require values filled with values. Only set this to ``True`` if you require values
immediately after object construction. immediately after object construction.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__( def __init__(
self, pin=None, queue_len=1, sample_rate=10, threshold=0.5, self, pin=None, queue_len=1, sample_rate=10, threshold=0.5,
partial=False): partial=False, pin_factory=None):
super(MotionSensor, self).__init__( super(MotionSensor, self).__init__(
pin, pull_up=False, threshold=threshold, pin, pull_up=False, threshold=threshold,
queue_len=queue_len, sample_wait=1 / sample_rate, partial=partial queue_len=queue_len, sample_wait=1 / sample_rate, partial=partial,
pin_factory=pin_factory
) )
try: try:
self._queue.start() self._queue.start()
@@ -460,14 +493,19 @@ class LightSensor(SmoothedInputDevice):
filled with values. Only set this to ``True`` if you require values filled with values. Only set this to ``True`` if you require values
immediately after object construction. immediately after object construction.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _CamJam #2 EduKit: http://camjam.me/?page_id=623 .. _CamJam #2 EduKit: http://camjam.me/?page_id=623
""" """
def __init__( def __init__(
self, pin=None, queue_len=5, charge_time_limit=0.01, self, pin=None, queue_len=5, charge_time_limit=0.01,
threshold=0.1, partial=False): threshold=0.1, partial=False, pin_factory=None):
super(LightSensor, self).__init__( super(LightSensor, self).__init__(
pin, pull_up=False, threshold=threshold, pin, pull_up=False, threshold=threshold,
queue_len=queue_len, sample_wait=0.0, partial=partial queue_len=queue_len, sample_wait=0.0, partial=partial,
pin_factory=pin_factory
) )
try: try:
self._charge_time_limit = charge_time_limit self._charge_time_limit = charge_time_limit
@@ -568,17 +606,22 @@ class DistanceSensor(SmoothedInputDevice):
filled with values. Only set this to ``True`` if you require values filled with values. Only set this to ``True`` if you require values
immediately after object construction. immediately after object construction.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _CamJam #3 EduKit: http://camjam.me/?page_id=1035 .. _CamJam #3 EduKit: http://camjam.me/?page_id=1035
""" """
def __init__( def __init__(
self, echo=None, trigger=None, queue_len=30, max_distance=1, self, echo=None, trigger=None, queue_len=30, max_distance=1,
threshold_distance=0.3, partial=False): threshold_distance=0.3, partial=False, pin_factory=None):
if max_distance <= 0: if max_distance <= 0:
raise ValueError('invalid maximum distance (must be positive)') raise ValueError('invalid maximum distance (must be positive)')
self._trigger = None self._trigger = None
super(DistanceSensor, self).__init__( super(DistanceSensor, self).__init__(
echo, pull_up=False, threshold=threshold_distance / max_distance, echo, pull_up=False, threshold=threshold_distance / max_distance,
queue_len=queue_len, sample_wait=0.0, partial=partial queue_len=queue_len, sample_wait=0.0, partial=partial,
pin_factory=pin_factory
) )
try: try:
self.speed_of_sound = 343.26 # m/s self.speed_of_sound = 343.26 # m/s

View File

@@ -36,9 +36,15 @@ class OutputDevice(SourceMixin, GPIODevice):
``None``, the device will be left in whatever state the pin is found in ``None``, the device will be left in whatever state the pin is found in
when configured for output (warning: this can be on). If ``True``, the when configured for output (warning: this can be on). If ``True``, the
device will be switched on initially. device will be switched on initially.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__(self, pin=None, active_high=True, initial_value=False): def __init__(
super(OutputDevice, self).__init__(pin) self, pin=None, active_high=True, initial_value=False,
pin_factory=None):
super(OutputDevice, self).__init__(pin, pin_factory=pin_factory)
self._lock = Lock() self._lock = Lock()
self.active_high = active_high self.active_high = active_high
if initial_value is None: if initial_value is None:
@@ -126,10 +132,14 @@ class DigitalOutputDevice(OutputDevice):
uses an optional background thread to handle toggling the device state uses an optional background thread to handle toggling the device state
without further interaction. without further interaction.
""" """
def __init__(self, pin=None, active_high=True, initial_value=False): def __init__(
self, pin=None, active_high=True, initial_value=False,
pin_factory=None):
self._blink_thread = None self._blink_thread = None
self._controller = None self._controller = None
super(DigitalOutputDevice, self).__init__(pin, active_high, initial_value) super(DigitalOutputDevice, self).__init__(
pin, active_high, initial_value, pin_factory=pin_factory
)
@property @property
def value(self): def value(self):
@@ -230,6 +240,10 @@ class LED(DigitalOutputDevice):
``None``, the LED will be left in whatever state the pin is found in ``None``, the LED will be left in whatever state the pin is found in
when configured for output (warning: this can be on). If ``True``, the when configured for output (warning: this can be on). If ``True``, the
LED will be switched on initially. LED will be switched on initially.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
pass pass
@@ -265,6 +279,10 @@ class Buzzer(DigitalOutputDevice):
``None``, the buzzer will be left in whatever state the pin is found in ``None``, the buzzer will be left in whatever state the pin is found in
when configured for output (warning: this can be on). If ``True``, the when configured for output (warning: this can be on). If ``True``, the
buzzer will be switched on initially. buzzer will be switched on initially.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
pass pass
@@ -293,13 +311,21 @@ class PWMOutputDevice(OutputDevice):
:param int frequency: :param int frequency:
The frequency (in Hz) of pulses emitted to drive the device. Defaults The frequency (in Hz) of pulses emitted to drive the device. Defaults
to 100Hz. to 100Hz.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__(self, pin=None, active_high=True, initial_value=0, frequency=100): def __init__(
self, pin=None, active_high=True, initial_value=0, frequency=100,
pin_factory=None):
self._blink_thread = None self._blink_thread = None
self._controller = None self._controller = None
if not 0 <= initial_value <= 1: if not 0 <= initial_value <= 1:
raise OutputDeviceBadValue("initial_value must be between 0 and 1") raise OutputDeviceBadValue("initial_value must be between 0 and 1")
super(PWMOutputDevice, self).__init__(pin, active_high, initial_value=None) super(PWMOutputDevice, self).__init__(
pin, active_high, initial_value=None, pin_factory=pin_factory
)
try: try:
# XXX need a way of setting these together # XXX need a way of setting these together
self.pin.frequency = frequency self.pin.frequency = frequency
@@ -500,6 +526,10 @@ class PWMLED(PWMOutputDevice):
:param int frequency: :param int frequency:
The frequency (in Hz) of pulses emitted to drive the LED. Defaults The frequency (in Hz) of pulses emitted to drive the LED. Defaults
to 100Hz. to 100Hz.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
pass pass
@@ -552,17 +582,24 @@ class RGBLED(SourceMixin, Device):
If ``True`` (the default), construct :class:`PWMLED` instances for If ``True`` (the default), construct :class:`PWMLED` instances for
each component of the RGBLED. If ``False``, construct regular each component of the RGBLED. If ``False``, construct regular
:class:`LED` instances, which prevents smooth color graduations. :class:`LED` instances, which prevents smooth color graduations.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__( def __init__(
self, red=None, green=None, blue=None, active_high=True, self, red=None, green=None, blue=None, active_high=True,
initial_value=(0, 0, 0), pwm=True): initial_value=(0, 0, 0), pwm=True, pin_factory=None):
self._leds = () self._leds = ()
self._blink_thread = None self._blink_thread = None
if not all(p is not None for p in [red, green, blue]): if not all(p is not None for p in [red, green, blue]):
raise GPIOPinMissing('red, green, and blue pins must be provided') raise GPIOPinMissing('red, green, and blue pins must be provided')
LEDClass = PWMLED if pwm else LED LEDClass = PWMLED if pwm else LED
super(RGBLED, self).__init__() super(RGBLED, self).__init__(pin_factory=pin_factory)
self._leds = tuple(LEDClass(pin, active_high) for pin in (red, green, blue)) self._leds = tuple(
LEDClass(pin, active_high, pin_factory=pin_factory)
for pin in (red, green, blue)
)
self.value = initial_value self.value = initial_value
red = _led_property(0) red = _led_property(0)
@@ -803,17 +840,23 @@ class Motor(SourceMixin, CompositeDevice):
variable speed control. If ``False``, construct variable speed control. If ``False``, construct
:class:`DigitalOutputDevice` instances, allowing only direction :class:`DigitalOutputDevice` instances, allowing only direction
control. control.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__(self, forward=None, backward=None, pwm=True): def __init__(self, forward=None, backward=None, pwm=True, pin_factory=None):
if not all(p is not None for p in [forward, backward]): if not all(p is not None for p in [forward, backward]):
raise GPIOPinMissing( raise GPIOPinMissing(
'forward and backward pins must be provided' 'forward and backward pins must be provided'
) )
PinClass = PWMOutputDevice if pwm else DigitalOutputDevice PinClass = PWMOutputDevice if pwm else DigitalOutputDevice
super(Motor, self).__init__( super(Motor, self).__init__(
forward_device=PinClass(forward), forward_device=PinClass(forward, pin_factory=pin_factory),
backward_device=PinClass(backward), backward_device=PinClass(backward, pin_factory=pin_factory),
_order=('forward_device', 'backward_device')) _order=('forward_device', 'backward_device'),
pin_factory=pin_factory
)
@property @property
def value(self): def value(self):
@@ -946,11 +989,15 @@ class Servo(SourceMixin, CompositeDevice):
:param float frame_width: :param float frame_width:
The length of time between servo control pulses measured in seconds. The length of time between servo control pulses measured in seconds.
This defaults to 20ms which is a common value for servos. This defaults to 20ms which is a common value for servos.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__( def __init__(
self, pin=None, initial_value=0.0, self, pin=None, initial_value=0.0,
min_pulse_width=1/1000, max_pulse_width=2/1000, min_pulse_width=1/1000, max_pulse_width=2/1000,
frame_width=20/1000): frame_width=20/1000, pin_factory=None):
if min_pulse_width >= max_pulse_width: if min_pulse_width >= max_pulse_width:
raise ValueError('min_pulse_width must be less than max_pulse_width') raise ValueError('min_pulse_width must be less than max_pulse_width')
if max_pulse_width >= frame_width: if max_pulse_width >= frame_width:
@@ -961,7 +1008,11 @@ class Servo(SourceMixin, CompositeDevice):
self._min_value = -1 self._min_value = -1
self._value_range = 2 self._value_range = 2
super(Servo, self).__init__( super(Servo, self).__init__(
pwm_device=PWMOutputDevice(pin, frequency=int(1 / frame_width))) pwm_device=PWMOutputDevice(
pin, frequency=int(1 / frame_width), pin_factory=pin_factory
),
pin_factory=pin_factory
)
try: try:
self.value = initial_value self.value = initial_value
except: except:
@@ -1146,17 +1197,23 @@ class AngularServo(Servo):
:param float frame_width: :param float frame_width:
The length of time between servo control pulses measured in seconds. The length of time between servo control pulses measured in seconds.
This defaults to 20ms which is a common value for servos. This defaults to 20ms which is a common value for servos.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
""" """
def __init__( def __init__(
self, pin=None, initial_angle=0.0, self, pin=None, initial_angle=0.0,
min_angle=-90, max_angle=90, min_angle=-90, max_angle=90,
min_pulse_width=1/1000, max_pulse_width=2/1000, min_pulse_width=1/1000, max_pulse_width=2/1000,
frame_width=20/1000): frame_width=20/1000, pin_factory=None):
self._min_angle = min_angle self._min_angle = min_angle
self._angular_range = max_angle - min_angle self._angular_range = max_angle - min_angle
initial_value = 2 * ((initial_angle - min_angle) / self._angular_range) - 1 initial_value = 2 * ((initial_angle - min_angle) / self._angular_range) - 1
super(AngularServo, self).__init__( super(AngularServo, self).__init__(
pin, initial_value, min_pulse_width, max_pulse_width, frame_width) pin, initial_value, min_pulse_width, max_pulse_width, frame_width,
pin_factory=pin_factory
)
@property @property
def min_angle(self): def min_angle(self):

View File

@@ -8,6 +8,10 @@ from __future__ import (
) )
str = type('') str = type('')
from weakref import ref
from collections import defaultdict
from threading import Lock
from ..exc import ( from ..exc import (
PinInvalidFunction, PinInvalidFunction,
PinSetInput, PinSetInput,
@@ -20,6 +24,7 @@ from ..exc import (
SPIFixedBitOrder, SPIFixedBitOrder,
SPIFixedSelect, SPIFixedSelect,
SPIFixedWordSize, SPIFixedWordSize,
GPIOPinInUse,
) )
@@ -36,10 +41,61 @@ class Factory(object):
applicable: applicable:
* :meth:`close` * :meth:`close`
* :meth:`reserve_pins`
* :meth:`release_pins`
* :meth:`release_all`
* :meth:`pin` * :meth:`pin`
* :meth:`spi` * :meth:`spi`
* :meth:`_get_pi_info` * :meth:`_get_pi_info`
""" """
def __init__(self):
self._reservations = defaultdict(list)
self._res_lock = Lock()
def reserve_pins(self, requester, *pins):
"""
Called to indicate that the device reserves the right to use the
specified *pins*. This should be done during device construction. If
pins are reserved, you must ensure that the reservation is released by
eventually called :meth:`release_pins`.
"""
with self._res_lock:
for pin in pins:
for reserver_ref in self._reservations[pin]:
reserver = reserver_ref()
if reserver is not None and requester._conflicts_with(reserver):
raise GPIOPinInUse('pin %s is already in use by %r' %
(pin, reserver))
self._reservations[pin].append(ref(requester))
def release_pins(self, reserver, *pins):
"""
Releases the reservation of *reserver* against *pins*. This is
typically called during :meth:`Device.close` to clean up reservations
taken during construction. Releasing a reservation that is not currently
held will be silently ignored (to permit clean-up after failed / partial
construction).
"""
with self._res_lock:
for pin in pins:
self._reservations[pin] = [
ref for ref in self._reservations[pin]
if ref() not in (reserver, None) # may as well clean up dead refs
]
def release_all(self, reserver):
"""
Releases all pin reservations taken out by *reserver*. See
:meth:`release_pins` for further information).
"""
with self._res_lock:
self._reservations = defaultdict(list, {
pin: [
ref for ref in conflictors
if ref() not in (reserver, None)
]
for pin, conflictors in self._reservations.items()
})
def close(self): def close(self):
""" """
@@ -63,19 +119,6 @@ class Factory(object):
""" """
raise PinUnsupported("Individual pins are not supported by this pin factory") raise PinUnsupported("Individual pins are not supported by this pin factory")
def pin_address(self, spec):
"""
Returns the address that a pin *would* have if constructed from the
given *spec*.
This unusual method is used by the pin reservation system to check
for conflicts *prior* to pin construction; with most implementations,
pin construction implicitly alters the state of the pin (e.g. setting
it to an input). This allows pin reservation to take place without
affecting the state of other components.
"""
raise NotImplementedError
def spi(self, **spi_args): def spi(self, **spi_args):
""" """
Returns an instance of an :class:`SPI` interface, for the specified SPI Returns an instance of an :class:`SPI` interface, for the specified SPI
@@ -89,21 +132,6 @@ class Factory(object):
def _get_address(self): def _get_address(self):
raise NotImplementedError raise NotImplementedError
address = property(
lambda self: self._get_address(),
doc="""\
Returns a tuple of strings representing the address of the factory.
For the Pi itself this is a tuple of one string representing the Pi's
address (e.g. "localhost"). Expander chips can return a tuple appending
whatever string they require to uniquely identify the expander chip
amongst all factories in the system.
.. note::
This property *must* return an immutable object capable of being
used as a dictionary key.
""")
def _get_pi_info(self): def _get_pi_info(self):
return None return None
@@ -128,7 +156,6 @@ class Pin(object):
represent the capabilities of pins. Descendents *must* override the represent the capabilities of pins. Descendents *must* override the
following methods: following methods:
* :meth:`_get_address`
* :meth:`_get_function` * :meth:`_get_function`
* :meth:`_set_function` * :meth:`_set_function`
* :meth:`_get_state` * :meth:`_get_state`
@@ -153,7 +180,7 @@ class Pin(object):
""" """
def __repr__(self): def __repr__(self):
return self.address[-1] return "<Pin>"
def close(self): def close(self):
""" """
@@ -195,18 +222,6 @@ class Pin(object):
self.function = 'input' self.function = 'input'
self.pull = pull self.pull = pull
def _get_address(self):
raise NotImplementedError
address = property(
lambda self: self._get_address(),
doc="""\
The address of the pin. This property is a tuple of strings constructed
from the owning factory's address with the unique address of the pin
appended to it. The tuple as a whole uniquely identifies the pin
amongst all pins attached to the system.
""")
def _get_function(self): def _get_function(self):
return "input" return "input"

View File

@@ -8,6 +8,8 @@ str = type('')
import io import io
import warnings import warnings
from collections import defaultdict
from threading import Lock
try: try:
from spidev import SpiDev from spidev import SpiDev
@@ -31,6 +33,8 @@ class LocalPiFactory(PiFactory):
:class:`~gpiozero.pins.native.NativePin`). :class:`~gpiozero.pins.native.NativePin`).
""" """
pins = {} pins = {}
_reservations = defaultdict(list)
_res_lock = Lock()
def __init__(self): def __init__(self):
super(LocalPiFactory, self).__init__() super(LocalPiFactory, self).__init__()
@@ -40,14 +44,13 @@ class LocalPiFactory(PiFactory):
('software', 'exclusive'): LocalPiSoftwareSPI, ('software', 'exclusive'): LocalPiSoftwareSPI,
('software', 'shared'): LocalPiSoftwareSPIShared, ('software', 'shared'): LocalPiSoftwareSPIShared,
} }
# Override the pins dict to be this class' pins dict. This is a bit of # Override the reservations and pins dict to be this class' attributes.
# a dirty hack, but ensures that anyone evil enough to mix pin # This is a bit of a dirty hack, but ensures that anyone evil enough to
# implementations doesn't try and control the same pin with different # mix pin implementations doesn't try and control the same pin with
# backends # different backends
self.pins = LocalPiFactory.pins self.pins = LocalPiFactory.pins
self._reservations = LocalPiFactory._reservations
def _get_address(self): self._res_lock = LocalPiFactory._res_lock
return ('localhost',)
def _get_revision(self): def _get_revision(self):
# Cache the result as we can reasonably assume it won't change during # Cache the result as we can reasonably assume it won't change during
@@ -74,19 +77,19 @@ class LocalPiPin(PiPin):
class LocalPiHardwareSPI(SPI, Device): class LocalPiHardwareSPI(SPI, Device):
def __init__(self, factory, port, device): def __init__(self, factory, port, device):
if SpiDev is None:
raise ImportError('failed to import spidev')
self._port = port self._port = port
self._device = device self._device = device
self._interface = None self._interface = None
self._address = factory.address + ('SPI(port=%d, device=%d)' % (port, device),) if SpiDev is None:
raise ImportError('failed to import spidev')
super(LocalPiHardwareSPI, self).__init__() super(LocalPiHardwareSPI, self).__init__()
pins = SPI_HARDWARE_PINS[port] pins = SPI_HARDWARE_PINS[port]
self._reserve_pins( self.pin_factory.reserve_pins(
factory.pin_address(pins['clock']), self,
factory.pin_address(pins['mosi']), pins['clock'],
factory.pin_address(pins['miso']), pins['mosi'],
factory.pin_address(pins['select'][device]) pins['miso'],
pins['select'][device]
) )
self._interface = SpiDev() self._interface = SpiDev()
self._interface.open(port, device) self._interface.open(port, device)
@@ -98,7 +101,7 @@ class LocalPiHardwareSPI(SPI, Device):
self._interface.close() self._interface.close()
finally: finally:
self._interface = None self._interface = None
self._release_all() self.pin_factory.release_all(self)
super(LocalPiHardwareSPI, self).close() super(LocalPiHardwareSPI, self).close()
@property @property
@@ -148,10 +151,6 @@ class LocalPiHardwareSPI(SPI, Device):
class LocalPiSoftwareSPI(SPI, OutputDevice): class LocalPiSoftwareSPI(SPI, OutputDevice):
def __init__(self, factory, clock_pin, mosi_pin, miso_pin, select_pin): def __init__(self, factory, clock_pin, mosi_pin, miso_pin, select_pin):
self._bus = None self._bus = None
self._address = factory.address + (
'SPI(clock_pin=%d, mosi_pin=%d, miso_pin=%d, select_pin=%d)' % (
clock_pin, mosi_pin, miso_pin, select_pin),
)
super(LocalPiSoftwareSPI, self).__init__(select_pin, active_high=False) super(LocalPiSoftwareSPI, self).__init__(select_pin, active_high=False)
try: try:
self._clock_phase = False self._clock_phase = False
@@ -163,6 +162,7 @@ class LocalPiSoftwareSPI(SPI, OutputDevice):
raise raise
def _conflicts_with(self, other): def _conflicts_with(self, other):
# XXX Need to refine this
return not ( return not (
isinstance(other, LocalPiSoftwareSPI) and isinstance(other, LocalPiSoftwareSPI) and
(self.pin.number != other.pin.number) (self.pin.number != other.pin.number)

View File

@@ -40,7 +40,7 @@ class MockPin(PiPin):
def __init__(self, factory, number): def __init__(self, factory, number):
super(MockPin, self).__init__(factory, number) super(MockPin, self).__init__(factory, number)
self._function = 'input' self._function = 'input'
self._pull = 'up' if factory.pi_info.pulled_up(self.address[-1]) else 'floating' self._pull = 'up' if self.factory.pi_info.pulled_up(repr(self)) else 'floating'
self._state = self._pull == 'up' self._state = self._pull == 'up'
self._bounce = None self._bounce = None
self._edges = 'both' self._edges = 'both'
@@ -94,7 +94,7 @@ class MockPin(PiPin):
def _set_pull(self, value): def _set_pull(self, value):
if self.function != 'input': if self.function != 'input':
raise PinFixedPull('cannot set pull on non-input pin %r' % self) raise PinFixedPull('cannot set pull on non-input pin %r' % self)
if value != 'up' and self.factory.pi_info.pulled_up(self.address[-1]): if value != 'up' and self.factory.pi_info.pulled_up(repr(self)):
raise PinFixedPull('%r has a physical pull-up resistor' % self) raise PinFixedPull('%r has a physical pull-up resistor' % self)
if value not in ('floating', 'up', 'down'): if value not in ('floating', 'up', 'down'):
raise PinInvalidPull('pull must be floating, up, or down') raise PinInvalidPull('pull must be floating, up, or down')
@@ -423,14 +423,12 @@ class MockFactory(LocalPiFactory):
pin_class = pkg_resources.load_entry_point(dist, group, pin_class.lower()) pin_class = pkg_resources.load_entry_point(dist, group, pin_class.lower())
self.pin_class = pin_class self.pin_class = pin_class
def _get_address(self):
return ('mock',)
def _get_revision(self): def _get_revision(self):
return self._revision return self._revision
def reset(self): def reset(self):
self.pins.clear() self.pins.clear()
self._reservations.clear()
def pin(self, spec, pin_class=None, **kwargs): def pin(self, spec, pin_class=None, **kwargs):
if pin_class is None: if pin_class is None:

View File

@@ -228,7 +228,7 @@ class NativePin(LocalPiPin):
self._change_thread = None self._change_thread = None
self._change_event = Event() self._change_event = Event()
self.function = 'input' self.function = 'input'
self.pull = 'up' if factory.pi_info.pulled_up(self.address[-1]) else 'floating' self.pull = 'up' if self.factory.pi_info.pulled_up(repr(self)) else 'floating'
self.bounce = None self.bounce = None
self.edges = 'both' self.edges = 'both'
@@ -236,7 +236,7 @@ class NativePin(LocalPiPin):
self.frequency = None self.frequency = None
self.when_changed = None self.when_changed = None
self.function = 'input' self.function = 'input'
self.pull = 'up' if self.factory.pi_info.pulled_up(self.address[-1]) else 'floating' self.pull = 'up' if self.factory.pi_info.pulled_up(repr(self)) else 'floating'
def _get_function(self): def _get_function(self):
return self.GPIO_FUNCTION_NAMES[(self.factory.mem[self._func_offset] >> self._func_shift) & 7] return self.GPIO_FUNCTION_NAMES[(self.factory.mem[self._func_offset] >> self._func_shift) & 7]
@@ -269,7 +269,7 @@ class NativePin(LocalPiPin):
def _set_pull(self, value): def _set_pull(self, value):
if self.function != 'input': if self.function != 'input':
raise PinFixedPull('cannot set pull on non-input pin %r' % self) raise PinFixedPull('cannot set pull on non-input pin %r' % self)
if value != 'up' and self.factory.pi_info.pulled_up(self.address[-1]): if value != 'up' and self.factory.pi_info.pulled_up(repr(self)):
raise PinFixedPull('%r has a physical pull-up resistor' % self) raise PinFixedPull('%r has a physical pull-up resistor' % self)
try: try:
value = self.GPIO_PULL_UPS[value] value = self.GPIO_PULL_UPS[value]

View File

@@ -7,8 +7,9 @@ from __future__ import (
str = type('') str = type('')
import io import io
from threading import RLock from threading import RLock, Lock
from types import MethodType from types import MethodType
from collections import defaultdict
try: try:
from weakref import ref, WeakMethod from weakref import ref, WeakMethod
except ImportError: except ImportError:
@@ -48,6 +49,7 @@ class PiFactory(Factory):
forms the base of :class:`~gpiozero.pins.local.LocalPiFactory`. forms the base of :class:`~gpiozero.pins.local.LocalPiFactory`.
""" """
def __init__(self): def __init__(self):
super(PiFactory, self).__init__()
self._info = None self._info = None
self.pins = {} self.pins = {}
self.pin_class = None self.pin_class = None
@@ -72,10 +74,6 @@ class PiFactory(Factory):
self.pins[n] = pin self.pins[n] = pin
return pin return pin
def pin_address(self, spec):
n = self._to_gpio(spec)
return self.address + ('GPIO%d' % n,)
def _to_gpio(self, spec): def _to_gpio(self, spec):
""" """
Converts the pin *spec* to a GPIO port number. Converts the pin *spec* to a GPIO port number.
@@ -240,23 +238,23 @@ class PiPin(Pin):
self._when_changed = None self._when_changed = None
self._number = number self._number = number
try: try:
factory.pi_info.physical_pin(self.address[-1]) factory.pi_info.physical_pin('GPIO%d' % self.number)
except PinNoPins: except PinNoPins:
warnings.warn( warnings.warn(
PinNonPhysical( PinNonPhysical(
'no physical pins exist for %s' % self.address[-1])) 'no physical pins exist for GPIO%d' % self.number))
@property @property
def number(self): def number(self):
return self._number return self._number
def __repr__(self):
return 'GPIO%d' % self._number
@property @property
def factory(self): def factory(self):
return self._factory return self._factory
def _get_address(self):
return self.factory.address + ('GPIO%d' % self.number,)
def _call_when_changed(self): def _call_when_changed(self):
""" """
Called to fire the :attr:`when_changed` event handler; override this Called to fire the :attr:`when_changed` event handler; override this

View File

@@ -7,7 +7,6 @@ from __future__ import (
str = type('') str = type('')
import os import os
from weakref import proxy
import pigpio import pigpio
@@ -126,9 +125,6 @@ class PiGPIOFactory(PiFactory):
def _get_revision(self): def _get_revision(self):
return self.connection.get_hardware_revision() return self.connection.get_hardware_revision()
def _get_address(self):
return ("%s:%d" % (self.host, self.port),)
def spi(self, **spi_args): def spi(self, **spi_args):
intf = super(PiGPIOFactory, self).spi(**spi_args) intf = super(PiGPIOFactory, self).spi(**spi_args)
self._spis.append(intf) self._spis.append(intf)
@@ -166,7 +162,7 @@ class PiGPIOPin(PiPin):
def __init__(self, factory, number): def __init__(self, factory, number):
super(PiGPIOPin, self).__init__(factory, number) super(PiGPIOPin, self).__init__(factory, number)
self._pull = 'up' if factory.pi_info.pulled_up(self.address[-1]) else 'floating' self._pull = 'up' if factory.pi_info.pulled_up(repr(self)) else 'floating'
self._pwm = False self._pwm = False
self._bounce = None self._bounce = None
self._callback = None self._callback = None
@@ -183,7 +179,7 @@ class PiGPIOPin(PiPin):
self.frequency = None self.frequency = None
self.when_changed = None self.when_changed = None
self.function = 'input' self.function = 'input'
self.pull = 'up' if self.factory.pi_info.pulled_up(self.address[-1]) else 'floating' self.pull = 'up' if self.factory.pi_info.pulled_up(repr(self)) else 'floating'
def _get_function(self): def _get_function(self):
return self.GPIO_FUNCTION_NAMES[self.factory.connection.get_mode(self.number)] return self.GPIO_FUNCTION_NAMES[self.factory.connection.get_mode(self.number)]
@@ -225,7 +221,7 @@ class PiGPIOPin(PiPin):
def _set_pull(self, value): def _set_pull(self, value):
if self.function != 'input': if self.function != 'input':
raise PinFixedPull('cannot set pull on non-input pin %r' % self) raise PinFixedPull('cannot set pull on non-input pin %r' % self)
if value != 'up' and self.factory.pi_info.pulled_up(self.address[-1]): if value != 'up' and self.factory.pi_info.pulled_up(repr(self)):
raise PinFixedPull('%r has a physical pull-up resistor' % self) raise PinFixedPull('%r has a physical pull-up resistor' % self)
try: try:
self.factory.connection.set_pull_up_down(self.number, self.GPIO_PULL_UPS[value]) self.factory.connection.set_pull_up_down(self.number, self.GPIO_PULL_UPS[value])
@@ -296,19 +292,17 @@ class PiGPIOHardwareSPI(SPI, Device):
def __init__(self, factory, port, device): def __init__(self, factory, port, device):
self._port = port self._port = port
self._device = device self._device = device
self._factory = proxy(factory) self._factory = factory
self._handle = None self._handle = None
super(PiGPIOHardwareSPI, self).__init__() super(PiGPIOHardwareSPI, self).__init__()
pins = SPI_HARDWARE_PINS[port] pins = SPI_HARDWARE_PINS[port]
self._reserve_pins(*( self._factory.reserve_pins(
factory.address + ('GPIO%d' % pin,) self,
for pin in (
pins['clock'], pins['clock'],
pins['mosi'], pins['mosi'],
pins['miso'], pins['miso'],
pins['select'][device] pins['select'][device]
) )
))
self._spi_flags = 8 << 16 self._spi_flags = 8 << 16
self._baud = 500000 self._baud = 500000
self._handle = self._factory.connection.spi_open( self._handle = self._factory.connection.spi_open(
@@ -330,7 +324,7 @@ class PiGPIOHardwareSPI(SPI, Device):
if not self.closed: if not self.closed:
self._factory.connection.spi_close(self._handle) self._factory.connection.spi_close(self._handle)
self._handle = None self._handle = None
self._release_all() self._factory.release_all(self)
super(PiGPIOHardwareSPI, self).close() super(PiGPIOHardwareSPI, self).close()
@property @property
@@ -397,13 +391,14 @@ class PiGPIOSoftwareSPI(SPI, Device):
self._clock_pin = clock_pin self._clock_pin = clock_pin
self._mosi_pin = mosi_pin self._mosi_pin = mosi_pin
self._miso_pin = miso_pin self._miso_pin = miso_pin
self._factory = proxy(factory) self._factory = factory
super(PiGPIOSoftwareSPI, self).__init__() super(PiGPIOSoftwareSPI, self).__init__()
self._reserve_pins( self._factory.reserve_pins(
factory.pin_address(clock_pin), self,
factory.pin_address(mosi_pin), clock_pin,
factory.pin_address(miso_pin), mosi_pin,
factory.pin_address(select_pin), miso_pin,
select_pin,
) )
self._spi_flags = 0 self._spi_flags = 0
self._baud = 100000 self._baud = 100000
@@ -434,7 +429,7 @@ class PiGPIOSoftwareSPI(SPI, Device):
if not self.closed: if not self.closed:
self._closed = True self._closed = True
self._factory.connection.bb_spi_close(self._select_pin) self._factory.connection.bb_spi_close(self._select_pin)
self._release_all() self.factory.release_all(self)
super(PiGPIOSoftwareSPI, self).close() super(PiGPIOSoftwareSPI, self).close()
@property @property

View File

@@ -85,7 +85,7 @@ class RPiGPIOPin(LocalPiPin):
def __init__(self, factory, number): def __init__(self, factory, number):
super(RPiGPIOPin, self).__init__(factory, number) super(RPiGPIOPin, self).__init__(factory, number)
self._pull = 'up' if factory.pi_info.pulled_up(self.address[-1]) else 'floating' self._pull = 'up' if self.factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self._pwm = None self._pwm = None
self._frequency = None self._frequency = None
self._duty_cycle = None self._duty_cycle = None

View File

@@ -80,7 +80,7 @@ class RPIOPin(LocalPiPin):
def __init__(self, factory, number): def __init__(self, factory, number):
super(RPIOPin, self).__init__(factory, number) super(RPIOPin, self).__init__(factory, number)
self._pull = 'up' if factory.pi_info.pulled_up(self.address[-1]) else 'floating' self._pull = 'up' if self.factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self._pwm = False self._pwm = False
self._duty_cycle = None self._duty_cycle = None
self._bounce = None self._bounce = None

View File

@@ -28,7 +28,9 @@ class SPIDevice(Device):
""" """
def __init__(self, **spi_args): def __init__(self, **spi_args):
self._spi = None self._spi = None
super(SPIDevice, self).__init__() super(SPIDevice, self).__init__(
pin_factory=spi_args.pop('pin_factory', None)
)
self._spi = self.pin_factory.spi(**spi_args) self._spi = self.pin_factory.spi(**spi_args)
def close(self): def close(self):

View File

@@ -36,7 +36,6 @@ def setup_function(function):
def teardown_function(function): def teardown_function(function):
Device.pin_factory.reset() Device.pin_factory.reset()
Device._reservations.clear()
def teardown_module(module): def teardown_module(module):
# make sure we reset the default # make sure we reset the default
@@ -47,7 +46,7 @@ def test_composite_output_on_off():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device: with CompositeOutputDevice(OutputDevice(2), OutputDevice(3), foo=OutputDevice(4)) as device:
device.on() device.on()
assert all((pin1.state, pin2.state, pin3.state)) assert all((pin1.state, pin2.state, pin3.state))
device.off() device.off()
@@ -57,7 +56,7 @@ def test_composite_output_toggle():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device: with CompositeOutputDevice(OutputDevice(2), OutputDevice(3), foo=OutputDevice(4)) as device:
device.toggle() device.toggle()
assert all((pin1.state, pin2.state, pin3.state)) assert all((pin1.state, pin2.state, pin3.state))
device[0].off() device[0].off()
@@ -70,7 +69,7 @@ def test_composite_output_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device: with CompositeOutputDevice(OutputDevice(2), OutputDevice(3), foo=OutputDevice(4)) as device:
assert device.value == (0, 0, 0) assert device.value == (0, 0, 0)
device.toggle() device.toggle()
assert device.value == (1, 1, 1) assert device.value == (1, 1, 1)
@@ -83,7 +82,7 @@ def test_led_board_on_off():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3) as board: with LEDBoard(2, 3, foo=4) as board:
assert isinstance(board[0], LED) assert isinstance(board[0], LED)
assert isinstance(board[1], LED) assert isinstance(board[1], LED)
assert isinstance(board[2], LED) assert isinstance(board[2], LED)
@@ -140,7 +139,7 @@ def test_led_board_active_low():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3, active_high=False) as board: with LEDBoard(2, 3, foo=4, active_high=False) as board:
assert not board.active_high assert not board.active_high
assert not board[0].active_high assert not board[0].active_high
assert not board[1].active_high assert not board[1].active_high
@@ -164,7 +163,7 @@ def test_led_board_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3) as board: with LEDBoard(2, 3, foo=4) as board:
assert board.value == (0, 0, 0) assert board.value == (0, 0, 0)
board.value = (0, 1, 0) board.value = (0, 1, 0)
assert board.value == (0, 1, 0) assert board.value == (0, 1, 0)
@@ -175,7 +174,7 @@ def test_led_board_pwm_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3, pwm=True) as board: with LEDBoard(2, 3, foo=4, pwm=True) as board:
assert board.value == (0, 0, 0) assert board.value == (0, 0, 0)
board.value = (0, 1, 0) board.value = (0, 1, 0)
assert board.value == (0, 1, 0) assert board.value == (0, 1, 0)
@@ -186,7 +185,7 @@ def test_led_board_pwm_bad_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3, pwm=True) as board: with LEDBoard(2, 3, foo=4, pwm=True) as board:
with pytest.raises(ValueError): with pytest.raises(ValueError):
board.value = (-1, 0, 0) board.value = (-1, 0, 0)
with pytest.raises(ValueError): with pytest.raises(ValueError):
@@ -196,20 +195,20 @@ def test_led_board_initial_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3, initial_value=0) as board: with LEDBoard(2, 3, foo=4, initial_value=0) as board:
assert board.value == (0, 0, 0) assert board.value == (0, 0, 0)
with LEDBoard(pin1, pin2, foo=pin3, initial_value=1) as board: with LEDBoard(2, 3, foo=4, initial_value=1) as board:
assert board.value == (1, 1, 1) assert board.value == (1, 1, 1)
def test_led_board_pwm_initial_value(): def test_led_board_pwm_initial_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=0) as board: with LEDBoard(2, 3, foo=4, pwm=True, initial_value=0) as board:
assert board.value == (0, 0, 0) assert board.value == (0, 0, 0)
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=1) as board: with LEDBoard(2, 3, foo=4, pwm=True, initial_value=1) as board:
assert board.value == (1, 1, 1) assert board.value == (1, 1, 1)
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=0.5) as board: with LEDBoard(2, 3, foo=4, pwm=True, initial_value=0.5) as board:
assert board.value == (0.5, 0.5, 0.5) assert board.value == (0.5, 0.5, 0.5)
def test_led_board_pwm_bad_initial_value(): def test_led_board_pwm_bad_initial_value():
@@ -217,15 +216,15 @@ def test_led_board_pwm_bad_initial_value():
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with pytest.raises(ValueError): with pytest.raises(ValueError):
LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=-1) LEDBoard(2, 3, foo=4, pwm=True, initial_value=-1)
with pytest.raises(ValueError): with pytest.raises(ValueError):
LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=2) LEDBoard(2, 3, foo=4, pwm=True, initial_value=2)
def test_led_board_nested(): def test_led_board_nested():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: with LEDBoard(2, LEDBoard(3, 4)) as board:
assert list(led.pin for led in board.leds) == [pin1, pin2, pin3] assert list(led.pin for led in board.leds) == [pin1, pin2, pin3]
assert board.value == (0, (0, 0)) assert board.value == (0, (0, 0))
board.value = (1, (0, 1)) board.value = (1, (0, 1))
@@ -237,7 +236,7 @@ def test_led_board_bad_blink():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: with LEDBoard(2, LEDBoard(3, 4)) as board:
with pytest.raises(ValueError): with pytest.raises(ValueError):
board.blink(fade_in_time=1, fade_out_time=1) board.blink(fade_in_time=1, fade_out_time=1)
with pytest.raises(ValueError): with pytest.raises(ValueError):
@@ -251,7 +250,7 @@ def test_led_board_blink_background():
pin1 = Device.pin_factory.pin(4) pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5) pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6) pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: with LEDBoard(4, LEDBoard(5, 6)) as board:
# Instantiation takes a long enough time that it throws off our timing # Instantiation takes a long enough time that it throws off our timing
# here! # here!
pin1.clear_states() pin1.clear_states()
@@ -276,7 +275,7 @@ def test_led_board_blink_foreground():
pin1 = Device.pin_factory.pin(4) pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5) pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6) pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: with LEDBoard(4, LEDBoard(5, 6)) as board:
pin1.clear_states() pin1.clear_states()
pin2.clear_states() pin2.clear_states()
pin3.clear_states() pin3.clear_states()
@@ -298,7 +297,7 @@ def test_led_board_blink_control():
pin1 = Device.pin_factory.pin(4) pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5) pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6) pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: with LEDBoard(4, LEDBoard(5, 6)) as board:
pin1.clear_states() pin1.clear_states()
pin2.clear_states() pin2.clear_states()
pin3.clear_states() pin3.clear_states()
@@ -326,7 +325,7 @@ def test_led_board_blink_take_over():
pin1 = Device.pin_factory.pin(4) pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5) pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6) pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: with LEDBoard(4, LEDBoard(5, 6)) as board:
pin1.clear_states() pin1.clear_states()
pin2.clear_states() pin2.clear_states()
pin3.clear_states() pin3.clear_states()
@@ -351,7 +350,7 @@ def test_led_board_blink_control_all():
pin1 = Device.pin_factory.pin(4) pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5) pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6) pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: with LEDBoard(4, LEDBoard(5, 6)) as board:
pin1.clear_states() pin1.clear_states()
pin2.clear_states() pin2.clear_states()
pin3.clear_states() pin3.clear_states()
@@ -376,7 +375,7 @@ def test_led_board_blink_interrupt_on():
pin1 = Device.pin_factory.pin(4) pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5) pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6) pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: with LEDBoard(4, LEDBoard(5, 6)) as board:
board.blink(1, 0.1) board.blink(1, 0.1)
sleep(0.2) sleep(0.2)
board.off() # should interrupt while on board.off() # should interrupt while on
@@ -388,7 +387,7 @@ def test_led_board_blink_interrupt_off():
pin1 = Device.pin_factory.pin(4) pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5) pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6) pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: with LEDBoard(4, LEDBoard(5, 6)) as board:
pin1.clear_states() pin1.clear_states()
pin2.clear_states() pin2.clear_states()
pin3.clear_states() pin3.clear_states()
@@ -405,7 +404,7 @@ def test_led_board_fade_background():
pin1 = Device.pin_factory.pin(4) pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5) pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6) pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3, pwm=True), pwm=True) as board: with LEDBoard(4, LEDBoard(5, 6, pwm=True), pwm=True) as board:
pin1.clear_states() pin1.clear_states()
pin2.clear_states() pin2.clear_states()
pin3.clear_states() pin3.clear_states()
@@ -442,7 +441,7 @@ def test_led_bar_graph_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3) as graph: with LEDBarGraph(2, 3, 4) as graph:
assert isinstance(graph[0], LED) assert isinstance(graph[0], LED)
assert isinstance(graph[1], LED) assert isinstance(graph[1], LED)
assert isinstance(graph[2], LED) assert isinstance(graph[2], LED)
@@ -475,7 +474,7 @@ def test_led_bar_graph_active_low():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3, active_high=False) as graph: with LEDBarGraph(2, 3, 4, active_high=False) as graph:
assert not graph.active_high assert not graph.active_high
assert not graph[0].active_high assert not graph[0].active_high
assert not graph[1].active_high assert not graph[1].active_high
@@ -497,7 +496,7 @@ def test_led_bar_graph_pwm_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3, pwm=True) as graph: with LEDBarGraph(2, 3, 4, pwm=True) as graph:
assert isinstance(graph[0], PWMLED) assert isinstance(graph[0], PWMLED)
assert isinstance(graph[1], PWMLED) assert isinstance(graph[1], PWMLED)
assert isinstance(graph[2], PWMLED) assert isinstance(graph[2], PWMLED)
@@ -524,7 +523,7 @@ def test_led_bar_graph_bad_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3) as graph: with LEDBarGraph(2, 3, 4) as graph:
with pytest.raises(ValueError): with pytest.raises(ValueError):
graph.value = -2 graph.value = -2
with pytest.raises(ValueError): with pytest.raises(ValueError):
@@ -535,20 +534,20 @@ def test_led_bar_graph_bad_init():
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with pytest.raises(TypeError): with pytest.raises(TypeError):
LEDBarGraph(pin1, pin2, foo=pin3) LEDBarGraph(2, 3, foo=4)
with pytest.raises(ValueError): with pytest.raises(ValueError):
LEDBarGraph(pin1, pin2, pin3, initial_value=-2) LEDBarGraph(2, 3, 4, initial_value=-2)
with pytest.raises(ValueError): with pytest.raises(ValueError):
LEDBarGraph(pin1, pin2, pin3, initial_value=2) LEDBarGraph(2, 3, 4, initial_value=2)
def test_led_bar_graph_initial_value(): def test_led_bar_graph_initial_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3, initial_value=1/3) as graph: with LEDBarGraph(2, 3, 4, initial_value=1/3) as graph:
assert graph.value == 1/3 assert graph.value == 1/3
assert pin1.state and not (pin2.state or pin3.state) assert pin1.state and not (pin2.state or pin3.state)
with LEDBarGraph(pin1, pin2, pin3, initial_value=-1/3) as graph: with LEDBarGraph(2, 3, 4, initial_value=-1/3) as graph:
assert graph.value == -1/3 assert graph.value == -1/3
assert pin3.state and not (pin1.state or pin2.state) assert pin3.state and not (pin1.state or pin2.state)
@@ -556,10 +555,10 @@ def test_led_bar_graph_pwm_initial_value():
pin1 = Device.pin_factory.pin(2) pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3) pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4) pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3, pwm=True, initial_value=0.5) as graph: with LEDBarGraph(2, 3, 4, pwm=True, initial_value=0.5) as graph:
assert graph.value == 0.5 assert graph.value == 0.5
assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0) assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0)
with LEDBarGraph(pin1, pin2, pin3, pwm=True, initial_value=-0.5) as graph: with LEDBarGraph(2, 3, 4, pwm=True, initial_value=-0.5) as graph:
assert graph.value == -0.5 assert graph.value == -0.5
assert (pin1.state, pin2.state, pin3.state) == (0, 0.5, 1) assert (pin1.state, pin2.state, pin3.state) == (0, 0.5, 1)
@@ -585,7 +584,7 @@ def test_traffic_lights():
red_pin = Device.pin_factory.pin(2) red_pin = Device.pin_factory.pin(2)
amber_pin = Device.pin_factory.pin(3) amber_pin = Device.pin_factory.pin(3)
green_pin = Device.pin_factory.pin(4) green_pin = Device.pin_factory.pin(4)
with TrafficLights(red_pin, amber_pin, green_pin) as board: with TrafficLights(2, 3, 4) as board:
board.red.on() board.red.on()
assert board.red.value assert board.red.value
assert not board.amber.value assert not board.amber.value
@@ -598,7 +597,7 @@ def test_traffic_lights():
assert amber_pin.state assert amber_pin.state
board.yellow.off() board.yellow.off()
assert not amber_pin.state assert not amber_pin.state
with TrafficLights(red=red_pin, yellow=amber_pin, green=green_pin) as board: with TrafficLights(red=2, yellow=3, green=4) as board:
board.yellow.on() board.yellow.on()
assert not board.red.value assert not board.red.value
assert board.amber.value assert board.amber.value
@@ -618,7 +617,7 @@ def test_traffic_lights_bad_init():
green_pin = Device.pin_factory.pin(4) green_pin = Device.pin_factory.pin(4)
yellow_pin = Device.pin_factory.pin(5) yellow_pin = Device.pin_factory.pin(5)
with pytest.raises(ValueError): with pytest.raises(ValueError):
TrafficLights(red=red_pin, amber=amber_pin, yellow=yellow_pin, green=green_pin) TrafficLights(red=2, amber=3, yellow=5, green=4)
def test_pi_traffic(): def test_pi_traffic():
pins = [Device.pin_factory.pin(n) for n in (9, 10, 11)] pins = [Device.pin_factory.pin(n) for n in (9, 10, 11)]
@@ -677,9 +676,9 @@ def test_traffic_lights_buzzer():
buzzer_pin = Device.pin_factory.pin(5) buzzer_pin = Device.pin_factory.pin(5)
button_pin = Device.pin_factory.pin(6) button_pin = Device.pin_factory.pin(6)
with TrafficLightsBuzzer( with TrafficLightsBuzzer(
TrafficLights(red_pin, amber_pin, green_pin), TrafficLights(2, 3, 4),
Buzzer(buzzer_pin), Buzzer(5),
Button(button_pin)) as board: Button(6)) as board:
board.lights.red.on() board.lights.red.on()
board.buzzer.on() board.buzzer.on()
assert red_pin.state assert red_pin.state

View File

@@ -33,7 +33,7 @@ def test_device_non_physical():
def test_device_init(): def test_device_init():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with GPIODevice(pin) as device: with GPIODevice(2) as device:
assert not device.closed assert not device.closed
assert device.pin == pin assert device.pin == pin
@@ -56,9 +56,9 @@ def test_device_close():
def test_device_reopen_same_pin(): def test_device_reopen_same_pin():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with GPIODevice(pin) as device: with GPIODevice(2) as device:
pass pass
with GPIODevice(pin) as device2: with GPIODevice(2) as device2:
assert not device2.closed assert not device2.closed
assert device2.pin is pin assert device2.pin is pin
assert device.closed assert device.closed

View File

@@ -18,22 +18,21 @@ from gpiozero import *
def teardown_function(function): def teardown_function(function):
Device.pin_factory.reset() Device.pin_factory.reset()
Device._reservations.clear()
def test_input_initial_values(): def test_input_initial_values():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with InputDevice(pin, pull_up=True) as device: with InputDevice(4, pull_up=True) as device:
assert pin.function == 'input' assert pin.function == 'input'
assert pin.pull == 'up' assert pin.pull == 'up'
assert device.pull_up assert device.pull_up
with InputDevice(pin, pull_up=False) as device: with InputDevice(4, pull_up=False) as device:
assert pin.pull == 'down' assert pin.pull == 'down'
assert not device.pull_up assert not device.pull_up
def test_input_is_active_low(): def test_input_is_active_low():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with InputDevice(pin, pull_up=True) as device: with InputDevice(2, pull_up=True) as device:
pin.drive_high() pin.drive_high()
assert not device.is_active assert not device.is_active
assert repr(device) == '<gpiozero.InputDevice object on pin GPIO2, pull_up=True, is_active=False>' assert repr(device) == '<gpiozero.InputDevice object on pin GPIO2, pull_up=True, is_active=False>'
@@ -43,7 +42,7 @@ def test_input_is_active_low():
def test_input_is_active_high(): def test_input_is_active_high():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with InputDevice(pin, pull_up=False) as device: with InputDevice(4, pull_up=False) as device:
pin.drive_high() pin.drive_high()
assert device.is_active assert device.is_active
assert repr(device) == '<gpiozero.InputDevice object on pin GPIO4, pull_up=False, is_active=True>' assert repr(device) == '<gpiozero.InputDevice object on pin GPIO4, pull_up=False, is_active=True>'
@@ -54,12 +53,12 @@ def test_input_is_active_high():
def test_input_pulled_up(): def test_input_pulled_up():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with pytest.raises(PinFixedPull): with pytest.raises(PinFixedPull):
InputDevice(pin, pull_up=False) InputDevice(2, pull_up=False)
def test_input_event_activated(): def test_input_event_activated():
event = Event() event = Event()
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with DigitalInputDevice(pin) as device: with DigitalInputDevice(4) as device:
device.when_activated = lambda: event.set() device.when_activated = lambda: event.set()
assert not event.is_set() assert not event.is_set()
pin.drive_high() pin.drive_high()
@@ -68,7 +67,7 @@ def test_input_event_activated():
def test_input_event_deactivated(): def test_input_event_deactivated():
event = Event() event = Event()
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with DigitalInputDevice(pin) as device: with DigitalInputDevice(4) as device:
device.when_deactivated = lambda: event.set() device.when_deactivated = lambda: event.set()
assert not event.is_set() assert not event.is_set()
pin.drive_high() pin.drive_high()
@@ -84,7 +83,7 @@ def test_input_partial_callback():
return a + b return a + b
bar = partial(foo, 1) bar = partial(foo, 1)
baz = partial(bar, 2) baz = partial(bar, 2)
with DigitalInputDevice(pin) as device: with DigitalInputDevice(4) as device:
device.when_activated = baz device.when_activated = baz
assert not event.is_set() assert not event.is_set()
pin.drive_high() pin.drive_high()
@@ -92,20 +91,20 @@ def test_input_partial_callback():
def test_input_wait_active(): def test_input_wait_active():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with DigitalInputDevice(pin) as device: with DigitalInputDevice(4) as device:
pin.drive_high() pin.drive_high()
assert device.wait_for_active(1) assert device.wait_for_active(1)
assert not device.wait_for_inactive(0) assert not device.wait_for_inactive(0)
def test_input_wait_inactive(): def test_input_wait_inactive():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with DigitalInputDevice(pin) as device: with DigitalInputDevice(4) as device:
assert device.wait_for_inactive(1) assert device.wait_for_inactive(1)
assert not device.wait_for_active(0) assert not device.wait_for_active(0)
def test_input_smoothed_attrib(): def test_input_smoothed_attrib():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with SmoothedInputDevice(pin, threshold=0.5, queue_len=5, partial=False) as device: with SmoothedInputDevice(4, threshold=0.5, queue_len=5, partial=False) as device:
assert repr(device) == '<gpiozero.SmoothedInputDevice object on pin GPIO4, pull_up=False>' assert repr(device) == '<gpiozero.SmoothedInputDevice object on pin GPIO4, pull_up=False>'
assert device.threshold == 0.5 assert device.threshold == 0.5
assert device.queue_len == 5 assert device.queue_len == 5
@@ -117,7 +116,7 @@ def test_input_smoothed_attrib():
def test_input_smoothed_values(): def test_input_smoothed_values():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with SmoothedInputDevice(pin) as device: with SmoothedInputDevice(4) as device:
device._queue.start() device._queue.start()
assert not device.is_active assert not device.is_active
pin.drive_high() pin.drive_high()
@@ -127,7 +126,7 @@ def test_input_smoothed_values():
def test_input_button(): def test_input_button():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with Button(pin) as button: with Button(2) as button:
assert pin.pull == 'up' assert pin.pull == 'up'
assert not button.is_pressed assert not button.is_pressed
pin.drive_low() pin.drive_low()
@@ -139,7 +138,7 @@ def test_input_button():
def test_input_line_sensor(): def test_input_line_sensor():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with LineSensor(pin) as sensor: with LineSensor(4) as sensor:
pin.drive_low() # logic is inverted for line sensor pin.drive_low() # logic is inverted for line sensor
assert sensor.wait_for_line(1) assert sensor.wait_for_line(1)
assert sensor.line_detected assert sensor.line_detected
@@ -149,7 +148,7 @@ def test_input_line_sensor():
def test_input_motion_sensor(): def test_input_motion_sensor():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with MotionSensor(pin) as sensor: with MotionSensor(4) as sensor:
pin.drive_high() pin.drive_high()
assert sensor.wait_for_motion(1) assert sensor.wait_for_motion(1)
assert sensor.motion_detected assert sensor.motion_detected
@@ -161,7 +160,7 @@ def test_input_motion_sensor():
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_input_light_sensor(): def test_input_light_sensor():
pin = Device.pin_factory.pin(4, pin_class=MockChargingPin) pin = Device.pin_factory.pin(4, pin_class=MockChargingPin)
with LightSensor(pin) as sensor: with LightSensor(4) as sensor:
pin.charge_time = 0.1 pin.charge_time = 0.1
assert sensor.wait_for_dark(1) assert sensor.wait_for_dark(1)
pin.charge_time = 0.0 pin.charge_time = 0.0
@@ -173,10 +172,10 @@ def test_input_distance_sensor():
echo_pin = Device.pin_factory.pin(4) echo_pin = Device.pin_factory.pin(4)
trig_pin = Device.pin_factory.pin(5, pin_class=MockTriggerPin, echo_pin=echo_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): with pytest.raises(ValueError):
DistanceSensor(echo_pin, trig_pin, max_distance=-1) DistanceSensor(4, 5, max_distance=-1)
# normal queue len is large (because the sensor is *really* jittery) but # normal queue len is large (because the sensor is *really* jittery) but
# we want quick tests and we've got precisely controlled pins :) # we want quick tests and we've got precisely controlled pins :)
with DistanceSensor(echo_pin, trig_pin, queue_len=5, max_distance=1) as sensor: with DistanceSensor(4, 5, queue_len=5, max_distance=1) as sensor:
assert sensor.max_distance == 1 assert sensor.max_distance == 1
assert sensor.trigger is trig_pin assert sensor.trigger is trig_pin
assert sensor.echo is echo_pin assert sensor.echo is echo_pin

View File

@@ -16,29 +16,73 @@ except ImportError:
import pytest import pytest
from gpiozero.pins.mock import MockPWMPin from gpiozero.pins.mock import MockPin, MockPWMPin
from gpiozero import * from gpiozero import *
def setup_function(function):
# dirty, but it does the job
Device.pin_factory.pin_class = MockPWMPin if function.__name__ in (
'test_output_pwm_states',
'test_output_pwm_read',
'test_output_pwm_write',
'test_output_pwm_toggle',
'test_output_pwm_active_high_read',
'test_output_pwm_bad_value',
'test_output_pwm_write_closed',
'test_output_pwm_write_silly',
'test_output_pwm_blink_background',
'test_output_pwm_blink_foreground',
'test_output_pwm_fade_background',
'test_output_pwm_fade_foreground',
'test_output_pwm_pulse_background',
'test_output_pwm_pulse_foreground',
'test_output_pwm_blink_interrupt',
'test_rgbled_initial_value',
'test_rgbled_initial_bad_value',
'test_rgbled_value',
'test_rgbled_bad_value',
'test_rgbled_toggle',
'test_rgbled_blink_background',
'test_rgbled_blink_foreground',
'test_rgbled_fade_background',
'test_rgbled_fade_foreground',
'test_rgbled_pulse_background',
'test_rgbled_pulse_foreground',
'test_rgbled_blink_interrupt',
'test_rgbled_close',
'test_motor_pins',
'test_motor_close',
'test_motor_value',
'test_motor_bad_value',
'test_motor_reverse',
'test_servo_pins',
'test_servo_bad_value',
'test_servo_close',
'test_servo_pulse_width',
'test_servo_values',
'test_angular_servo_range',
'test_angular_servo_angles',
) else MockPin
def teardown_function(function): def teardown_function(function):
Device.pin_factory.reset() Device.pin_factory.reset()
Device._reservations.clear()
def test_output_initial_values(): def test_output_initial_values():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with OutputDevice(pin, initial_value=False) as device: with OutputDevice(2, initial_value=False) as device:
assert pin.function == 'output' assert pin.function == 'output'
assert not pin.state assert not pin.state
with OutputDevice(pin, initial_value=True) as device: with OutputDevice(2, initial_value=True) as device:
assert pin.state assert pin.state
state = pin.state state = pin.state
with OutputDevice(pin, initial_value=None) as device: with OutputDevice(2, initial_value=None) as device:
assert state == pin.state assert state == pin.state
def test_output_write_active_high(): def test_output_write_active_high():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with OutputDevice(pin) as device: with OutputDevice(2) as device:
device.on() device.on()
assert pin.state assert pin.state
device.off() device.off()
@@ -46,14 +90,14 @@ def test_output_write_active_high():
def test_output_write_active_low(): def test_output_write_active_low():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with OutputDevice(pin, active_high=False) as device: with OutputDevice(2, active_high=False) as device:
device.on() device.on()
assert not pin.state assert not pin.state
device.off() device.off()
assert pin.state assert pin.state
def test_output_write_closed(): def test_output_write_closed():
with OutputDevice(Device.pin_factory.pin(2)) as device: with OutputDevice(2) as device:
device.close() device.close()
assert device.closed assert device.closed
device.close() device.close()
@@ -63,14 +107,14 @@ def test_output_write_closed():
def test_output_write_silly(): def test_output_write_silly():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with OutputDevice(pin) as device: with OutputDevice(2) as device:
pin.function = 'input' pin.function = 'input'
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
device.on() device.on()
def test_output_value(): def test_output_value():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with OutputDevice(pin) as device: with OutputDevice(2) as device:
assert not device.value assert not device.value
assert not pin.state assert not pin.state
device.on() device.on()
@@ -82,7 +126,7 @@ def test_output_value():
def test_output_digital_toggle(): def test_output_digital_toggle():
pin = Device.pin_factory.pin(2) pin = Device.pin_factory.pin(2)
with DigitalOutputDevice(pin) as device: with DigitalOutputDevice(2) as device:
assert not device.value assert not device.value
assert not pin.state assert not pin.state
device.toggle() device.toggle()
@@ -96,7 +140,7 @@ def test_output_digital_toggle():
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_output_blink_background(): def test_output_blink_background():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with DigitalOutputDevice(pin) as device: with DigitalOutputDevice(4) as device:
start = time() start = time()
device.blink(0.1, 0.1, n=2) device.blink(0.1, 0.1, n=2)
assert isclose(time() - start, 0, abs_tol=0.05) assert isclose(time() - start, 0, abs_tol=0.05)
@@ -114,7 +158,7 @@ def test_output_blink_background():
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_output_blink_foreground(): def test_output_blink_foreground():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with DigitalOutputDevice(pin) as device: with DigitalOutputDevice(4) as device:
start = time() start = time()
device.blink(0.1, 0.1, n=2, background=False) device.blink(0.1, 0.1, n=2, background=False)
assert isclose(time() - start, 0.4, abs_tol=0.05) assert isclose(time() - start, 0.4, abs_tol=0.05)
@@ -128,7 +172,7 @@ def test_output_blink_foreground():
def test_output_blink_interrupt_on(): def test_output_blink_interrupt_on():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with DigitalOutputDevice(pin) as device: with DigitalOutputDevice(4) as device:
device.blink(1, 0.1) device.blink(1, 0.1)
sleep(0.2) sleep(0.2)
device.off() # should interrupt while on device.off() # should interrupt while on
@@ -136,7 +180,7 @@ def test_output_blink_interrupt_on():
def test_output_blink_interrupt_off(): def test_output_blink_interrupt_off():
pin = Device.pin_factory.pin(4) pin = Device.pin_factory.pin(4)
with DigitalOutputDevice(pin) as device: with DigitalOutputDevice(4) as device:
device.blink(0.1, 1) device.blink(0.1, 1)
sleep(0.2) sleep(0.2)
device.off() # should interrupt while off device.off() # should interrupt while off
@@ -144,23 +188,23 @@ def test_output_blink_interrupt_off():
def test_output_pwm_bad_initial_value(): def test_output_pwm_bad_initial_value():
with pytest.raises(ValueError): with pytest.raises(ValueError):
PWMOutputDevice(Device.pin_factory.pin(2), initial_value=2) PWMOutputDevice(2, initial_value=2)
def test_output_pwm_not_supported(): def test_output_pwm_not_supported():
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
PWMOutputDevice(Device.pin_factory.pin(2)) PWMOutputDevice(2)
def test_output_pwm_states(): def test_output_pwm_states():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin) pin = Device.pin_factory.pin(4)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(4) as device:
device.value = 0.1 device.value = 0.1
device.value = 0.2 device.value = 0.2
device.value = 0.0 device.value = 0.0
pin.assert_states([0.0, 0.1, 0.2, 0.0]) pin.assert_states([0.0, 0.1, 0.2, 0.0])
def test_output_pwm_read(): def test_output_pwm_read():
pin = Device.pin_factory.pin(2, pin_class=MockPWMPin) pin = Device.pin_factory.pin(2)
with PWMOutputDevice(pin, frequency=100) as device: with PWMOutputDevice(2, frequency=100) as device:
assert device.frequency == 100 assert device.frequency == 100
device.value = 0.1 device.value = 0.1
assert isclose(device.value, 0.1) assert isclose(device.value, 0.1)
@@ -172,15 +216,15 @@ def test_output_pwm_read():
assert device.frequency is None assert device.frequency is None
def test_output_pwm_write(): def test_output_pwm_write():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin) pin = Device.pin_factory.pin(4)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(4) as device:
device.on() device.on()
device.off() device.off()
pin.assert_states([False, True, False]) pin.assert_states([False, True, False])
def test_output_pwm_toggle(): def test_output_pwm_toggle():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin) pin = Device.pin_factory.pin(4)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(4) as device:
device.toggle() device.toggle()
device.value = 0.5 device.value = 0.5
device.value = 0.1 device.value = 0.1
@@ -189,8 +233,8 @@ def test_output_pwm_toggle():
pin.assert_states([False, True, 0.5, 0.1, 0.9, False]) pin.assert_states([False, True, 0.5, 0.1, 0.9, False])
def test_output_pwm_active_high_read(): def test_output_pwm_active_high_read():
pin = Device.pin_factory.pin(2, pin_class=MockPWMPin) pin = Device.pin_factory.pin(2)
with PWMOutputDevice(pin, active_high=False) as device: with PWMOutputDevice(2, active_high=False) as device:
device.value = 0.1 device.value = 0.1
assert isclose(device.value, 0.1) assert isclose(device.value, 0.1)
assert isclose(pin.state, 0.9) assert isclose(pin.state, 0.9)
@@ -198,19 +242,21 @@ def test_output_pwm_active_high_read():
assert device.value assert device.value
def test_output_pwm_bad_value(): def test_output_pwm_bad_value():
with PWMOutputDevice(Device.pin_factory.pin(2, pin_class=MockPWMPin)) as device: pin = Device.pin_factory.pin(2)
with PWMOutputDevice(2) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.value = 2 device.value = 2
def test_output_pwm_write_closed(): def test_output_pwm_write_closed():
with PWMOutputDevice(Device.pin_factory.pin(2, pin_class=MockPWMPin)) as device: pin = Device.pin_factory.pin(2)
with PWMOutputDevice(2) as device:
device.close() device.close()
with pytest.raises(GPIODeviceClosed): with pytest.raises(GPIODeviceClosed):
device.on() device.on()
def test_output_pwm_write_silly(): def test_output_pwm_write_silly():
pin = Device.pin_factory.pin(2, pin_class=MockPWMPin) pin = Device.pin_factory.pin(2)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(2) as device:
pin.function = 'input' pin.function = 'input'
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
device.off() device.off()
@@ -218,8 +264,8 @@ def test_output_pwm_write_silly():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_output_pwm_blink_background(): def test_output_pwm_blink_background():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin) pin = Device.pin_factory.pin(4)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(4) as device:
start = time() start = time()
device.blink(0.1, 0.1, n=2) device.blink(0.1, 0.1, n=2)
assert isclose(time() - start, 0, abs_tol=0.05) assert isclose(time() - start, 0, abs_tol=0.05)
@@ -236,8 +282,8 @@ def test_output_pwm_blink_background():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_output_pwm_blink_foreground(): def test_output_pwm_blink_foreground():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin) pin = Device.pin_factory.pin(4)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(4) as device:
start = time() start = time()
device.blink(0.1, 0.1, n=2, background=False) device.blink(0.1, 0.1, n=2, background=False)
assert isclose(time() - start, 0.4, abs_tol=0.05) assert isclose(time() - start, 0.4, abs_tol=0.05)
@@ -252,8 +298,8 @@ def test_output_pwm_blink_foreground():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_output_pwm_fade_background(): def test_output_pwm_fade_background():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin) pin = Device.pin_factory.pin(4)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(4) as device:
start = time() start = time()
device.blink(0, 0, 0.2, 0.2, n=2) device.blink(0, 0, 0.2, 0.2, n=2)
assert isclose(time() - start, 0, abs_tol=0.05) assert isclose(time() - start, 0, abs_tol=0.05)
@@ -286,8 +332,8 @@ def test_output_pwm_fade_background():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_output_pwm_fade_foreground(): def test_output_pwm_fade_foreground():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin) pin = Device.pin_factory.pin(4)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(4) as device:
start = time() start = time()
device.blink(0, 0, 0.2, 0.2, n=2, background=False) device.blink(0, 0, 0.2, 0.2, n=2, background=False)
assert isclose(time() - start, 0.8, abs_tol=0.05) assert isclose(time() - start, 0.8, abs_tol=0.05)
@@ -318,8 +364,8 @@ def test_output_pwm_fade_foreground():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_output_pwm_pulse_background(): def test_output_pwm_pulse_background():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin) pin = Device.pin_factory.pin(4)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(4) as device:
start = time() start = time()
device.pulse(0.2, 0.2, n=2) device.pulse(0.2, 0.2, n=2)
assert isclose(time() - start, 0, abs_tol=0.05) assert isclose(time() - start, 0, abs_tol=0.05)
@@ -352,8 +398,8 @@ def test_output_pwm_pulse_background():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_output_pwm_pulse_foreground(): def test_output_pwm_pulse_foreground():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin) pin = Device.pin_factory.pin(4)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(4) as device:
start = time() start = time()
device.pulse(0.2, 0.2, n=2, background=False) device.pulse(0.2, 0.2, n=2, background=False)
assert isclose(time() - start, 0.8, abs_tol=0.05) assert isclose(time() - start, 0.8, abs_tol=0.05)
@@ -382,8 +428,8 @@ def test_output_pwm_pulse_foreground():
]) ])
def test_output_pwm_blink_interrupt(): def test_output_pwm_blink_interrupt():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin) pin = Device.pin_factory.pin(4)
with PWMOutputDevice(pin) as device: with PWMOutputDevice(4) as device:
device.blink(1, 0.1) device.blink(1, 0.1)
sleep(0.2) sleep(0.2)
device.off() # should interrupt while on device.off() # should interrupt while on
@@ -394,8 +440,8 @@ def test_rgbled_missing_pins():
RGBLED() RGBLED()
def test_rgbled_initial_value(): def test_rgbled_initial_value():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, initial_value=(0.1, 0.2, 0)) as device: with RGBLED(1, 2, 3, initial_value=(0.1, 0.2, 0)) as device:
assert r.frequency assert r.frequency
assert g.frequency assert g.frequency
assert b.frequency assert b.frequency
@@ -405,24 +451,24 @@ def test_rgbled_initial_value():
def test_rgbled_initial_value_nonpwm(): def test_rgbled_initial_value_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False, initial_value=(0, 1, 1)) as device: with RGBLED(1, 2, 3, pwm=False, initial_value=(0, 1, 1)) as device:
assert r.state == 0 assert r.state == 0
assert g.state == 1 assert g.state == 1
assert b.state == 1 assert b.state == 1
def test_rgbled_initial_bad_value(): def test_rgbled_initial_bad_value():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with pytest.raises(ValueError): with pytest.raises(ValueError):
RGBLED(r, g, b, initial_value=(0.1, 0.2, 1.2)) RGBLED(1, 2, 3, initial_value=(0.1, 0.2, 1.2))
def test_rgbled_initial_bad_value_nonpwm(): def test_rgbled_initial_bad_value_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with pytest.raises(ValueError): with pytest.raises(ValueError):
RGBLED(r, g, b, pwm=False, initial_value=(0.1, 0.2, 0)) RGBLED(1, 2, 3, pwm=False, initial_value=(0.1, 0.2, 0))
def test_rgbled_value(): def test_rgbled_value():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
assert isinstance(device._leds[0], PWMLED) assert isinstance(device._leds[0], PWMLED)
assert isinstance(device._leds[1], PWMLED) assert isinstance(device._leds[1], PWMLED)
assert isinstance(device._leds[2], PWMLED) assert isinstance(device._leds[2], PWMLED)
@@ -440,7 +486,7 @@ def test_rgbled_value():
def test_rgbled_value_nonpwm(): def test_rgbled_value_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
assert isinstance(device._leds[0], LED) assert isinstance(device._leds[0], LED)
assert isinstance(device._leds[1], LED) assert isinstance(device._leds[1], LED)
assert isinstance(device._leds[2], LED) assert isinstance(device._leds[2], LED)
@@ -454,35 +500,35 @@ def test_rgbled_value_nonpwm():
assert device.value == (0, 0, 0) assert device.value == (0, 0, 0)
def test_rgbled_bad_value(): def test_rgbled_bad_value():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.value = (2, 0, 0) device.value = (2, 0, 0)
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.value = (0, -1, 0) device.value = (0, -1, 0)
def test_rgbled_bad_value_nonpwm(): def test_rgbled_bad_value_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.value = (2, 0, 0) device.value = (2, 0, 0)
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.value = (0, -1, 0) device.value = (0, -1, 0)
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.value = (0.5, 0, 0) device.value = (0.5, 0, 0)
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.value = (0, 0.5, 0) device.value = (0, 0.5, 0)
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.value = (0, 0, 0.5) device.value = (0, 0, 0.5)
def test_rgbled_toggle(): def test_rgbled_toggle():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
assert not device.is_active assert not device.is_active
assert device.value == (0, 0, 0) assert device.value == (0, 0, 0)
device.toggle() device.toggle()
@@ -494,7 +540,7 @@ def test_rgbled_toggle():
def test_rgbled_toggle_nonpwm(): def test_rgbled_toggle_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
assert not device.is_active assert not device.is_active
assert device.value == (0, 0, 0) assert device.value == (0, 0, 0)
device.toggle() device.toggle()
@@ -506,7 +552,7 @@ def test_rgbled_toggle_nonpwm():
def test_rgbled_blink_nonpwm(): def test_rgbled_blink_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.blink(fade_in_time=1) device.blink(fade_in_time=1)
with pytest.raises(ValueError): with pytest.raises(ValueError):
@@ -515,8 +561,8 @@ def test_rgbled_blink_nonpwm():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_rgbled_blink_background(): def test_rgbled_blink_background():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6)) r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
start = time() start = time()
device.blink(0.1, 0.1, n=2) device.blink(0.1, 0.1, n=2)
assert isclose(time() - start, 0, abs_tol=0.05) assert isclose(time() - start, 0, abs_tol=0.05)
@@ -537,7 +583,7 @@ def test_rgbled_blink_background():
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_rgbled_blink_background_nonpwm(): def test_rgbled_blink_background_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6)) r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
start = time() start = time()
device.blink(0.1, 0.1, n=2) device.blink(0.1, 0.1, n=2)
assert isclose(time() - start, 0, abs_tol=0.05) assert isclose(time() - start, 0, abs_tol=0.05)
@@ -557,8 +603,8 @@ def test_rgbled_blink_background_nonpwm():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_rgbled_blink_foreground(): def test_rgbled_blink_foreground():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6)) r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
start = time() start = time()
device.blink(0.1, 0.1, n=2, background=False) device.blink(0.1, 0.1, n=2, background=False)
assert isclose(time() - start, 0.4, abs_tol=0.05) assert isclose(time() - start, 0.4, abs_tol=0.05)
@@ -577,7 +623,7 @@ def test_rgbled_blink_foreground():
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_rgbled_blink_foreground_nonpwm(): def test_rgbled_blink_foreground_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6)) r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
start = time() start = time()
device.blink(0.1, 0.1, n=2, background=False) device.blink(0.1, 0.1, n=2, background=False)
assert isclose(time() - start, 0.4, abs_tol=0.05) assert isclose(time() - start, 0.4, abs_tol=0.05)
@@ -595,8 +641,8 @@ def test_rgbled_blink_foreground_nonpwm():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_rgbled_fade_background(): def test_rgbled_fade_background():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6)) r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
start = time() start = time()
device.blink(0, 0, 0.2, 0.2, n=2) device.blink(0, 0, 0.2, 0.2, n=2)
assert isclose(time() - start, 0, abs_tol=0.05) assert isclose(time() - start, 0, abs_tol=0.05)
@@ -631,15 +677,15 @@ def test_rgbled_fade_background():
def test_rgbled_fade_background_nonpwm(): def test_rgbled_fade_background_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.blink(0, 0, 0.2, 0.2, n=2) device.blink(0, 0, 0.2, 0.2, n=2)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_rgbled_fade_foreground(): def test_rgbled_fade_foreground():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6)) r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
start = time() start = time()
device.blink(0, 0, 0.2, 0.2, n=2, background=False) device.blink(0, 0, 0.2, 0.2, n=2, background=False)
assert isclose(time() - start, 0.8, abs_tol=0.05) assert isclose(time() - start, 0.8, abs_tol=0.05)
@@ -672,15 +718,15 @@ def test_rgbled_fade_foreground():
def test_rgbled_fade_foreground_nonpwm(): def test_rgbled_fade_foreground_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.blink(0, 0, 0.2, 0.2, n=2, background=False) device.blink(0, 0, 0.2, 0.2, n=2, background=False)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_rgbled_pulse_background(): def test_rgbled_pulse_background():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6)) r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
start = time() start = time()
device.pulse(0.2, 0.2, n=2) device.pulse(0.2, 0.2, n=2)
assert isclose(time() - start, 0, abs_tol=0.05) assert isclose(time() - start, 0, abs_tol=0.05)
@@ -715,15 +761,15 @@ def test_rgbled_pulse_background():
def test_rgbled_pulse_background_nonpwm(): def test_rgbled_pulse_background_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.pulse(0.2, 0.2, n=2) device.pulse(0.2, 0.2, n=2)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy') reason='timing is too random on pypy')
def test_rgbled_pulse_foreground(): def test_rgbled_pulse_foreground():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6)) r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
start = time() start = time()
device.pulse(0.2, 0.2, n=2, background=False) device.pulse(0.2, 0.2, n=2, background=False)
assert isclose(time() - start, 0.8, abs_tol=0.05) assert isclose(time() - start, 0.8, abs_tol=0.05)
@@ -756,13 +802,13 @@ def test_rgbled_pulse_foreground():
def test_rgbled_pulse_foreground_nonpwm(): def test_rgbled_pulse_foreground_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.pulse(0.2, 0.2, n=2, background=False) device.pulse(0.2, 0.2, n=2, background=False)
def test_rgbled_blink_interrupt(): def test_rgbled_blink_interrupt():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6)) r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
device.blink(1, 0.1) device.blink(1, 0.1)
sleep(0.2) sleep(0.2)
device.off() # should interrupt while on device.off() # should interrupt while on
@@ -772,7 +818,7 @@ def test_rgbled_blink_interrupt():
def test_rgbled_blink_interrupt_nonpwm(): def test_rgbled_blink_interrupt_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6)) r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
device.blink(1, 0.1) device.blink(1, 0.1)
sleep(0.2) sleep(0.2)
device.off() # should interrupt while on device.off() # should interrupt while on
@@ -781,8 +827,8 @@ def test_rgbled_blink_interrupt_nonpwm():
b.assert_states([0, 1, 0]) b.assert_states([0, 1, 0])
def test_rgbled_close(): def test_rgbled_close():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b) as device: with RGBLED(1, 2, 3) as device:
assert not device.closed assert not device.closed
device.close() device.close()
assert device.closed assert device.closed
@@ -791,7 +837,7 @@ def test_rgbled_close():
def test_rgbled_close_nonpwm(): def test_rgbled_close_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3)) r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device: with RGBLED(1, 2, 3, pwm=False) as device:
assert not device.closed assert not device.closed
device.close() device.close()
assert device.closed assert device.closed
@@ -803,9 +849,9 @@ def test_motor_missing_pins():
Motor() Motor()
def test_motor_pins(): def test_motor_pins():
f = Device.pin_factory.pin(1, pin_class=MockPWMPin) f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2, pin_class=MockPWMPin) b = Device.pin_factory.pin(2)
with Motor(f, b) as device: with Motor(1, 2) as device:
assert device.forward_device.pin is f assert device.forward_device.pin is f
assert isinstance(device.forward_device, PWMOutputDevice) assert isinstance(device.forward_device, PWMOutputDevice)
assert device.backward_device.pin is b assert device.backward_device.pin is b
@@ -814,16 +860,16 @@ def test_motor_pins():
def test_motor_pins_nonpwm(): def test_motor_pins_nonpwm():
f = Device.pin_factory.pin(1) f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2) b = Device.pin_factory.pin(2)
with Motor(f, b, pwm=False) as device: with Motor(1, 2, pwm=False) as device:
assert device.forward_device.pin is f assert device.forward_device.pin is f
assert isinstance(device.forward_device, DigitalOutputDevice) assert isinstance(device.forward_device, DigitalOutputDevice)
assert device.backward_device.pin is b assert device.backward_device.pin is b
assert isinstance(device.backward_device, DigitalOutputDevice) assert isinstance(device.backward_device, DigitalOutputDevice)
def test_motor_close(): def test_motor_close():
f = Device.pin_factory.pin(1, pin_class=MockPWMPin) f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2, pin_class=MockPWMPin) b = Device.pin_factory.pin(2)
with Motor(f, b) as device: with Motor(1, 2) as device:
device.close() device.close()
assert device.closed assert device.closed
assert device.forward_device.pin is None assert device.forward_device.pin is None
@@ -834,16 +880,16 @@ def test_motor_close():
def test_motor_close_nonpwm(): def test_motor_close_nonpwm():
f = Device.pin_factory.pin(1) f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2) b = Device.pin_factory.pin(2)
with Motor(f, b, pwm=False) as device: with Motor(1, 2, pwm=False) as device:
device.close() device.close()
assert device.closed assert device.closed
assert device.forward_device.pin is None assert device.forward_device.pin is None
assert device.backward_device.pin is None assert device.backward_device.pin is None
def test_motor_value(): def test_motor_value():
f = Device.pin_factory.pin(1, pin_class=MockPWMPin) f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2, pin_class=MockPWMPin) b = Device.pin_factory.pin(2)
with Motor(f, b) as device: with Motor(1, 2) as device:
device.value = -1 device.value = -1
assert device.is_active assert device.is_active
assert device.value == -1 assert device.value == -1
@@ -868,7 +914,7 @@ def test_motor_value():
def test_motor_value_nonpwm(): def test_motor_value_nonpwm():
f = Device.pin_factory.pin(1) f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2) b = Device.pin_factory.pin(2)
with Motor(f, b, pwm=False) as device: with Motor(1, 2, pwm=False) as device:
device.value = -1 device.value = -1
assert device.is_active assert device.is_active
assert device.value == -1 assert device.value == -1
@@ -883,9 +929,9 @@ def test_motor_value_nonpwm():
assert b.state == 0 and f.state == 0 assert b.state == 0 and f.state == 0
def test_motor_bad_value(): def test_motor_bad_value():
f = Device.pin_factory.pin(1, pin_class=MockPWMPin) f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2, pin_class=MockPWMPin) b = Device.pin_factory.pin(2)
with Motor(f, b) as device: with Motor(1, 2) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.value = -2 device.value = -2
with pytest.raises(ValueError): with pytest.raises(ValueError):
@@ -898,7 +944,7 @@ def test_motor_bad_value():
def test_motor_bad_value_nonpwm(): def test_motor_bad_value_nonpwm():
f = Device.pin_factory.pin(1) f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2) b = Device.pin_factory.pin(2)
with Motor(f, b, pwm=False) as device: with Motor(1, 2, pwm=False) as device:
with pytest.raises(ValueError): with pytest.raises(ValueError):
device.value = -2 device.value = -2
with pytest.raises(ValueError): with pytest.raises(ValueError):
@@ -909,9 +955,9 @@ def test_motor_bad_value_nonpwm():
device.value = -0.5 device.value = -0.5
def test_motor_reverse(): def test_motor_reverse():
f = Device.pin_factory.pin(1, pin_class=MockPWMPin) f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2, pin_class=MockPWMPin) b = Device.pin_factory.pin(2)
with Motor(f, b) as device: with Motor(1, 2) as device:
device.forward() device.forward()
assert device.value == 1 assert device.value == 1
assert b.state == 0 and f.state == 1 assert b.state == 0 and f.state == 1
@@ -928,7 +974,7 @@ def test_motor_reverse():
def test_motor_reverse_nonpwm(): def test_motor_reverse_nonpwm():
f = Device.pin_factory.pin(1) f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2) b = Device.pin_factory.pin(2)
with Motor(f, b, pwm=False) as device: with Motor(1, 2, pwm=False) as device:
device.forward() device.forward()
assert device.value == 1 assert device.value == 1
assert b.state == 0 and f.state == 1 assert b.state == 0 and f.state == 1
@@ -937,28 +983,28 @@ def test_motor_reverse_nonpwm():
assert b.state == 1 and f.state == 0 assert b.state == 1 and f.state == 0
def test_servo_pins(): def test_servo_pins():
p = Device.pin_factory.pin(1, pin_class=MockPWMPin) p = Device.pin_factory.pin(1)
with Servo(p) as device: with Servo(1) as device:
assert device.pwm_device.pin is p assert device.pwm_device.pin is p
assert isinstance(device.pwm_device, PWMOutputDevice) assert isinstance(device.pwm_device, PWMOutputDevice)
def test_servo_bad_value(): def test_servo_bad_value():
p = Device.pin_factory.pin(1, pin_class=MockPWMPin) p = Device.pin_factory.pin(1)
with pytest.raises(ValueError): with pytest.raises(ValueError):
Servo(p, initial_value=2) Servo(1, initial_value=2)
with pytest.raises(ValueError): with pytest.raises(ValueError):
Servo(p, min_pulse_width=30/1000) Servo(1, min_pulse_width=30/1000)
with pytest.raises(ValueError): with pytest.raises(ValueError):
Servo(p, max_pulse_width=30/1000) Servo(1, max_pulse_width=30/1000)
def test_servo_pins_nonpwm(): def test_servo_pins_nonpwm():
p = Device.pin_factory.pin(2) p = Device.pin_factory.pin(2)
with pytest.raises(PinPWMUnsupported): with pytest.raises(PinPWMUnsupported):
Servo(p) Servo(1)
def test_servo_close(): def test_servo_close():
p = Device.pin_factory.pin(2, pin_class=MockPWMPin) p = Device.pin_factory.pin(2)
with Servo(p) as device: with Servo(1) as device:
device.close() device.close()
assert device.closed assert device.closed
assert device.pwm_device.pin is None assert device.pwm_device.pin is None
@@ -966,8 +1012,8 @@ def test_servo_close():
assert device.closed assert device.closed
def test_servo_pulse_width(): def test_servo_pulse_width():
p = Device.pin_factory.pin(2, pin_class=MockPWMPin) p = Device.pin_factory.pin(2)
with Servo(p, min_pulse_width=5/10000, max_pulse_width=25/10000) as device: with Servo(1, min_pulse_width=5/10000, max_pulse_width=25/10000) as device:
assert isclose(device.min_pulse_width, 5/10000) assert isclose(device.min_pulse_width, 5/10000)
assert isclose(device.max_pulse_width, 25/10000) assert isclose(device.max_pulse_width, 25/10000)
assert isclose(device.frame_width, 20/1000) assert isclose(device.frame_width, 20/1000)
@@ -980,8 +1026,8 @@ def test_servo_pulse_width():
assert device.pulse_width is None assert device.pulse_width is None
def test_servo_values(): def test_servo_values():
p = Device.pin_factory.pin(1, pin_class=MockPWMPin) p = Device.pin_factory.pin(1)
with Servo(p) as device: with Servo(1) as device:
device.min() device.min()
assert device.is_active assert device.is_active
assert device.value == -1 assert device.value == -1
@@ -1007,14 +1053,14 @@ def test_servo_values():
assert device.value is None assert device.value is None
def test_angular_servo_range(): def test_angular_servo_range():
p = Device.pin_factory.pin(1, pin_class=MockPWMPin) p = Device.pin_factory.pin(1)
with AngularServo(p, initial_angle=15, min_angle=0, max_angle=90) as device: with AngularServo(1, initial_angle=15, min_angle=0, max_angle=90) as device:
assert device.min_angle == 0 assert device.min_angle == 0
assert device.max_angle == 90 assert device.max_angle == 90
def test_angular_servo_angles(): def test_angular_servo_angles():
p = Device.pin_factory.pin(1, pin_class=MockPWMPin) p = Device.pin_factory.pin(1)
with AngularServo(p) as device: with AngularServo(1) as device:
device.angle = 0 device.angle = 0
assert device.angle == 0 assert device.angle == 0
assert isclose(device.value, 0) assert isclose(device.value, 0)
@@ -1026,7 +1072,7 @@ def test_angular_servo_angles():
assert isclose(device.value, -1) assert isclose(device.value, -1)
device.detach() device.detach()
assert device.angle is None assert device.angle is None
with AngularServo(p, initial_angle=15, min_angle=0, max_angle=90) as device: with AngularServo(1, initial_angle=15, min_angle=0, max_angle=90) as device:
assert device.angle == 15 assert device.angle == 15
assert isclose(device.value, -2/3) assert isclose(device.value, -2/3)
device.angle = 0 device.angle = 0
@@ -1037,7 +1083,7 @@ def test_angular_servo_angles():
assert isclose(device.value, 1) assert isclose(device.value, 1)
device.angle = None device.angle = None
assert device.angle is None assert device.angle is None
with AngularServo(p, min_angle=45, max_angle=-45) as device: with AngularServo(1, min_angle=45, max_angle=-45) as device:
assert device.angle == 0 assert device.angle == 0
assert isclose(device.value, 0) assert isclose(device.value, 0)
device.angle = -45 device.angle = -45

View File

@@ -33,50 +33,50 @@ def test_spi_hardware_params():
with patch('os.open'), patch('mmap.mmap') as mmap_mmap, patch('io.open') as io_open: with patch('os.open'), patch('mmap.mmap') as mmap_mmap, patch('io.open') as io_open:
mmap_mmap.return_value = array(nstr('B'), (0,) * 4096) mmap_mmap.return_value = array(nstr('B'), (0,) * 4096)
io_open.return_value.__enter__.return_value = ['Revision: a21042'] io_open.return_value.__enter__.return_value = ['Revision: a21042']
with patch('gpiozero.devices.Device.pin_factory', NativeFactory()), \ factory = NativeFactory()
patch('gpiozero.pins.local.SpiDev'): with patch('gpiozero.pins.local.SpiDev'):
with Device.pin_factory.spi() as device: with factory.spi() as device:
assert isinstance(device, LocalPiHardwareSPI) assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(port=0, device=0) as device: with factory.spi(port=0, device=0) as device:
assert isinstance(device, LocalPiHardwareSPI) assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(port=0, device=1) as device: with factory.spi(port=0, device=1) as device:
assert isinstance(device, LocalPiHardwareSPI) assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(clock_pin=11) as device: with factory.spi(clock_pin=11) as device:
assert isinstance(device, LocalPiHardwareSPI) assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(clock_pin=11, mosi_pin=10, select_pin=8) as device: with factory.spi(clock_pin=11, mosi_pin=10, select_pin=8) as device:
assert isinstance(device, LocalPiHardwareSPI) assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(clock_pin=11, mosi_pin=10, select_pin=7) as device: with factory.spi(clock_pin=11, mosi_pin=10, select_pin=7) as device:
assert isinstance(device, LocalPiHardwareSPI) assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(shared=True) as device: with factory.spi(shared=True) as device:
assert isinstance(device, LocalPiHardwareSPIShared) assert isinstance(device, LocalPiHardwareSPIShared)
with pytest.raises(ValueError): with pytest.raises(ValueError):
Device.pin_factory.spi(port=1) factory.spi(port=1)
with pytest.raises(ValueError): with pytest.raises(ValueError):
Device.pin_factory.spi(device=2) factory.spi(device=2)
with pytest.raises(ValueError): with pytest.raises(ValueError):
Device.pin_factory.spi(port=0, clock_pin=12) factory.spi(port=0, clock_pin=12)
with pytest.raises(ValueError): with pytest.raises(ValueError):
Device.pin_factory.spi(foo='bar') factory.spi(foo='bar')
def test_spi_software_params(): def test_spi_software_params():
with patch('os.open'), patch('mmap.mmap') as mmap_mmap, patch('io.open') as io_open: with patch('os.open'), patch('mmap.mmap') as mmap_mmap, patch('io.open') as io_open:
mmap_mmap.return_value = array(nstr('B'), (0,) * 4096) mmap_mmap.return_value = array(nstr('B'), (0,) * 4096)
io_open.return_value.__enter__.return_value = ['Revision: a21042'] io_open.return_value.__enter__.return_value = ['Revision: a21042']
with patch('gpiozero.devices.Device.pin_factory', NativeFactory()), \ factory = NativeFactory()
patch('gpiozero.pins.local.SpiDev'): with patch('gpiozero.pins.local.SpiDev'):
with Device.pin_factory.spi(select_pin=6) as device: with factory.spi(select_pin=6) as device:
assert isinstance(device, LocalPiSoftwareSPI) assert isinstance(device, LocalPiSoftwareSPI)
with Device.pin_factory.spi(clock_pin=11, mosi_pin=9, miso_pin=10) as device: with factory.spi(clock_pin=11, mosi_pin=9, miso_pin=10) as device:
assert isinstance(device, LocalPiSoftwareSPI) assert isinstance(device, LocalPiSoftwareSPI)
with Device.pin_factory.spi(select_pin=6, shared=True) as device: with factory.spi(select_pin=6, shared=True) as device:
assert isinstance(device, LocalPiSoftwareSPIShared) assert isinstance(device, LocalPiSoftwareSPIShared)
with patch('gpiozero.devices.Device.pin_factory', NativeFactory()), \ with patch('gpiozero.pins.local.SpiDev', None):
patch('gpiozero.pins.local.SpiDev', None): # Clear out the old factory's caches (this is only necessary because
# Clear out the old factory's pins cache (this is only necessary # we're being naughty switching out patches)
# because we're being very naughty switching out pin factories) factory.pins.clear()
Device.pin_factory.pins.clear() factory._reservations.clear()
# Ensure software fallback works when SpiDev isn't present # Ensure software fallback works when SpiDev isn't present
with Device.pin_factory.spi() as device: with factory.spi() as device:
assert isinstance(device, LocalPiSoftwareSPI) assert isinstance(device, LocalPiSoftwareSPI)
def test_spi_hardware_conflict(): def test_spi_hardware_conflict():