mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
Merge pull request #251 from waveform80/source-tools
The source/values toolkit
This commit is contained in:
@@ -41,6 +41,8 @@ Errors
|
|||||||
|
|
||||||
.. autoexception:: DeviceClosed
|
.. autoexception:: DeviceClosed
|
||||||
|
|
||||||
|
.. autoexception:: BadEventHandler
|
||||||
|
|
||||||
.. autoexception:: CompositeDeviceError
|
.. autoexception:: CompositeDeviceError
|
||||||
|
|
||||||
.. autoexception:: CompositeDeviceBadName
|
.. autoexception:: CompositeDeviceBadName
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ classes:
|
|||||||
* :class:`SPIDevice` represents devices that communicate over an SPI interface
|
* :class:`SPIDevice` represents devices that communicate over an SPI interface
|
||||||
(implemented as four GPIO pins)
|
(implemented as four GPIO pins)
|
||||||
|
|
||||||
|
* :class:`InternalDevice` represents devices that are entirely internal to
|
||||||
|
the Pi (usually operating system related services)
|
||||||
|
|
||||||
* :class:`CompositeDevice` represents devices composed of multiple other
|
* :class:`CompositeDevice` represents devices composed of multiple other
|
||||||
devices like HATs
|
devices like HATs
|
||||||
|
|
||||||
@@ -31,6 +34,9 @@ There are also several `mixin classes`_:
|
|||||||
* :class:`SharedMixin` which causes classes to track their construction and
|
* :class:`SharedMixin` which causes classes to track their construction and
|
||||||
return existing instances when equivalent constructor arguments are passed
|
return existing instances when equivalent constructor arguments are passed
|
||||||
|
|
||||||
|
* :class:`EventsMixin` which adds activated/deactivated events to devices
|
||||||
|
along with the machinery to trigger those events
|
||||||
|
|
||||||
.. _mixin classes: https://en.wikipedia.org/wiki/Mixin
|
.. _mixin classes: https://en.wikipedia.org/wiki/Mixin
|
||||||
|
|
||||||
The current class hierarchies are displayed below. For brevity, the mixin
|
The current class hierarchies are displayed below. For brevity, the mixin
|
||||||
@@ -47,6 +53,10 @@ Next, the classes below :class:`SPIDevice`:
|
|||||||
|
|
||||||
.. image:: images/spi_device_hierarchy.*
|
.. image:: images/spi_device_hierarchy.*
|
||||||
|
|
||||||
|
Next, the classes below :class:`InternalDevice`:
|
||||||
|
|
||||||
|
.. image:: images/other_device_hierarchy.*
|
||||||
|
|
||||||
Next, the classes below :class:`CompositeDevice`:
|
Next, the classes below :class:`CompositeDevice`:
|
||||||
|
|
||||||
.. image:: images/composite_device_hierarchy.*
|
.. image:: images/composite_device_hierarchy.*
|
||||||
@@ -60,15 +70,18 @@ Base Classes
|
|||||||
============
|
============
|
||||||
|
|
||||||
.. autoclass:: Device
|
.. autoclass:: Device
|
||||||
:members: close, closed
|
:members: close, closed, value, is_active
|
||||||
|
|
||||||
.. autoclass:: GPIODevice(pin)
|
.. autoclass:: GPIODevice(pin)
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. autoclass:: CompositeDevice
|
.. autoclass:: SPIDevice
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. autoclass:: SPIDevice
|
.. autoclass:: InternalDevice
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: CompositeDevice
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
Input Devices
|
Input Devices
|
||||||
@@ -77,9 +90,6 @@ Input Devices
|
|||||||
.. autoclass:: InputDevice(pin, pull_up=False)
|
.. autoclass:: InputDevice(pin, pull_up=False)
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. autoclass:: WaitableInputDevice
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: DigitalInputDevice(pin, pull_up=False, bounce_time=None)
|
.. autoclass:: DigitalInputDevice(pin, pull_up=False, bounce_time=None)
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
@@ -128,3 +138,6 @@ Mixin Classes
|
|||||||
.. autoclass:: SharedMixin(...)
|
.. autoclass:: SharedMixin(...)
|
||||||
:members: _shared_key
|
:members: _shared_key
|
||||||
|
|
||||||
|
.. autoclass:: EventsMixin(...)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|||||||
14
docs/api_other.rst
Normal file
14
docs/api_other.rst
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
================
|
||||||
|
Internal Devices
|
||||||
|
================
|
||||||
|
|
||||||
|
.. currentmodule:: gpiozero
|
||||||
|
|
||||||
|
GPIO Zero also provides several "internal" devices which represent facilities
|
||||||
|
provided by the operating system itself. These can be used to react to things
|
||||||
|
like the time of day, or whether a server is available on the network.
|
||||||
|
|
||||||
|
|
||||||
|
.. autoclass:: TimeOfDay
|
||||||
|
|
||||||
|
.. autoclass:: PingServer
|
||||||
49
docs/api_source_tools.rst
Normal file
49
docs/api_source_tools.rst
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
============
|
||||||
|
Source Tools
|
||||||
|
============
|
||||||
|
|
||||||
|
.. currentmodule:: gpiozero
|
||||||
|
|
||||||
|
GPIO Zero includes several utility routines which are intended to be used with
|
||||||
|
the :attr:`~SourceMixin.source` and :attr:`~ValuesMixin.values` attributes
|
||||||
|
common to most devices in the library. Given that ``source`` and ``values``
|
||||||
|
deal with infinite iterators, another excellent source of utilities is the
|
||||||
|
:mod:`itertools` module in the standard library.
|
||||||
|
|
||||||
|
Single source conversions
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. autofunction:: negated
|
||||||
|
|
||||||
|
.. autofunction:: inverted
|
||||||
|
|
||||||
|
.. autofunction:: scaled
|
||||||
|
|
||||||
|
.. autofunction:: clamped
|
||||||
|
|
||||||
|
.. autofunction:: post_delayed
|
||||||
|
|
||||||
|
.. autofunction:: pre_delayed
|
||||||
|
|
||||||
|
.. autofunction:: quantized
|
||||||
|
|
||||||
|
.. autofunction:: queued
|
||||||
|
|
||||||
|
Combining sources
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. autofunction:: conjunction
|
||||||
|
|
||||||
|
.. autofunction:: disjunction
|
||||||
|
|
||||||
|
.. autofunction:: averaged
|
||||||
|
|
||||||
|
Artifical sources
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. autofunction:: random_values
|
||||||
|
|
||||||
|
.. autofunction:: sin_values
|
||||||
|
|
||||||
|
.. autofunction:: cos_values
|
||||||
|
|
||||||
@@ -9,7 +9,6 @@ digraph classes {
|
|||||||
node [color="#9ec6e0", fontcolor="#000000"]
|
node [color="#9ec6e0", fontcolor="#000000"]
|
||||||
Device;
|
Device;
|
||||||
GPIODevice;
|
GPIODevice;
|
||||||
WaitableInputDevice;
|
|
||||||
SmoothedInputDevice;
|
SmoothedInputDevice;
|
||||||
|
|
||||||
/* Concrete classes */
|
/* Concrete classes */
|
||||||
@@ -17,9 +16,8 @@ digraph classes {
|
|||||||
|
|
||||||
GPIODevice->Device;
|
GPIODevice->Device;
|
||||||
InputDevice->GPIODevice;
|
InputDevice->GPIODevice;
|
||||||
WaitableInputDevice->InputDevice;
|
DigitalInputDevice->InputDevice;
|
||||||
DigitalInputDevice->WaitableInputDevice;
|
SmoothedInputDevice->InputDevice;
|
||||||
SmoothedInputDevice->WaitableInputDevice;
|
|
||||||
Button->DigitalInputDevice;
|
Button->DigitalInputDevice;
|
||||||
MotionSensor->SmoothedInputDevice;
|
MotionSensor->SmoothedInputDevice;
|
||||||
LightSensor->SmoothedInputDevice;
|
LightSensor->SmoothedInputDevice;
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 42 KiB |
@@ -4,175 +4,165 @@
|
|||||||
<!-- Generated by graphviz version 2.36.0 (20140111.2315)
|
<!-- Generated by graphviz version 2.36.0 (20140111.2315)
|
||||||
-->
|
-->
|
||||||
<!-- Title: classes Pages: 1 -->
|
<!-- Title: classes Pages: 1 -->
|
||||||
<svg width="701pt" height="404pt"
|
<svg width="721pt" height="332pt"
|
||||||
viewBox="0.00 0.00 701.00 404.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
viewBox="0.00 0.00 721.00 332.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 400)">
|
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 328)">
|
||||||
<title>classes</title>
|
<title>classes</title>
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-400 697,-400 697,4 -4,4"/>
|
<polygon fill="white" stroke="none" points="-4,4 -4,-328 717,-328 717,4 -4,4"/>
|
||||||
<!-- Device -->
|
<!-- Device -->
|
||||||
<g id="node1" class="node"><title>Device</title>
|
<g id="node1" class="node"><title>Device</title>
|
||||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="485,-396 431,-396 431,-360 485,-360 485,-396"/>
|
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="499,-324 445,-324 445,-288 499,-288 499,-324"/>
|
||||||
<text text-anchor="middle" x="458" y="-375.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text>
|
<text text-anchor="middle" x="472" y="-303.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- GPIODevice -->
|
<!-- GPIODevice -->
|
||||||
<g id="node2" class="node"><title>GPIODevice</title>
|
<g id="node2" class="node"><title>GPIODevice</title>
|
||||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="494,-324 422,-324 422,-288 494,-288 494,-324"/>
|
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="508,-252 436,-252 436,-216 508,-216 508,-252"/>
|
||||||
<text text-anchor="middle" x="458" y="-303.5" font-family="Sans" font-size="10.00" fill="#000000">GPIODevice</text>
|
<text text-anchor="middle" x="472" y="-231.5" font-family="Sans" font-size="10.00" fill="#000000">GPIODevice</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- GPIODevice->Device -->
|
<!-- GPIODevice->Device -->
|
||||||
<g id="edge1" class="edge"><title>GPIODevice->Device</title>
|
<g id="edge1" class="edge"><title>GPIODevice->Device</title>
|
||||||
<path fill="none" stroke="black" d="M458,-324.303C458,-332.017 458,-341.288 458,-349.888"/>
|
<path fill="none" stroke="black" d="M472,-252.303C472,-260.017 472,-269.288 472,-277.888"/>
|
||||||
<polygon fill="black" stroke="black" points="454.5,-349.896 458,-359.896 461.5,-349.896 454.5,-349.896"/>
|
<polygon fill="black" stroke="black" points="468.5,-277.896 472,-287.896 475.5,-277.896 468.5,-277.896"/>
|
||||||
</g>
|
|
||||||
<!-- WaitableInputDevice -->
|
|
||||||
<g id="node3" class="node"><title>WaitableInputDevice</title>
|
|
||||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="440.5,-180 327.5,-180 327.5,-144 440.5,-144 440.5,-180"/>
|
|
||||||
<text text-anchor="middle" x="384" y="-159.5" font-family="Sans" font-size="10.00" fill="#000000">WaitableInputDevice</text>
|
|
||||||
</g>
|
|
||||||
<!-- InputDevice -->
|
|
||||||
<g id="node5" class="node"><title>InputDevice</title>
|
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="438.5,-252 365.5,-252 365.5,-216 438.5,-216 438.5,-252"/>
|
|
||||||
<text text-anchor="middle" x="402" y="-231.5" font-family="Sans" font-size="10.00" fill="#ffffff">InputDevice</text>
|
|
||||||
</g>
|
|
||||||
<!-- WaitableInputDevice->InputDevice -->
|
|
||||||
<g id="edge3" class="edge"><title>WaitableInputDevice->InputDevice</title>
|
|
||||||
<path fill="none" stroke="black" d="M388.449,-180.303C390.455,-188.102 392.869,-197.491 395.101,-206.171"/>
|
|
||||||
<polygon fill="black" stroke="black" points="391.722,-207.082 397.602,-215.896 398.501,-205.339 391.722,-207.082"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- SmoothedInputDevice -->
|
<!-- SmoothedInputDevice -->
|
||||||
<g id="node4" class="node"><title>SmoothedInputDevice</title>
|
<g id="node3" class="node"><title>SmoothedInputDevice</title>
|
||||||
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="289.5,-108 166.5,-108 166.5,-72 289.5,-72 289.5,-108"/>
|
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="289.5,-108 166.5,-108 166.5,-72 289.5,-72 289.5,-108"/>
|
||||||
<text text-anchor="middle" x="228" y="-87.5" font-family="Sans" font-size="10.00" fill="#000000">SmoothedInputDevice</text>
|
<text text-anchor="middle" x="228" y="-87.5" font-family="Sans" font-size="10.00" fill="#000000">SmoothedInputDevice</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- SmoothedInputDevice->WaitableInputDevice -->
|
<!-- InputDevice -->
|
||||||
<g id="edge5" class="edge"><title>SmoothedInputDevice->WaitableInputDevice</title>
|
<g id="node4" class="node"><title>InputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M266.162,-108.124C287.393,-117.651 314.008,-129.593 336.569,-139.717"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="445.5,-180 372.5,-180 372.5,-144 445.5,-144 445.5,-180"/>
|
||||||
<polygon fill="black" stroke="black" points="335.406,-143.031 345.962,-143.932 338.271,-136.644 335.406,-143.031"/>
|
<text text-anchor="middle" x="409" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">InputDevice</text>
|
||||||
|
</g>
|
||||||
|
<!-- SmoothedInputDevice->InputDevice -->
|
||||||
|
<g id="edge4" class="edge"><title>SmoothedInputDevice->InputDevice</title>
|
||||||
|
<path fill="none" stroke="black" d="M272.278,-108.124C299.802,-118.769 335.123,-132.429 362.97,-143.198"/>
|
||||||
|
<polygon fill="black" stroke="black" points="361.893,-146.535 372.483,-146.877 364.418,-140.006 361.893,-146.535"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- InputDevice->GPIODevice -->
|
<!-- InputDevice->GPIODevice -->
|
||||||
<g id="edge2" class="edge"><title>InputDevice->GPIODevice</title>
|
<g id="edge2" class="edge"><title>InputDevice->GPIODevice</title>
|
||||||
<path fill="none" stroke="black" d="M415.843,-252.303C422.489,-260.611 430.578,-270.723 437.887,-279.859"/>
|
<path fill="none" stroke="black" d="M424.573,-180.303C432.202,-188.78 441.523,-199.136 449.876,-208.417"/>
|
||||||
<polygon fill="black" stroke="black" points="435.336,-282.273 444.317,-287.896 440.803,-277.901 435.336,-282.273"/>
|
<polygon fill="black" stroke="black" points="447.315,-210.804 456.606,-215.896 452.518,-206.121 447.315,-210.804"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- DigitalInputDevice -->
|
<!-- DigitalInputDevice -->
|
||||||
<g id="node6" class="node"><title>DigitalInputDevice</title>
|
<g id="node5" class="node"><title>DigitalInputDevice</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="439.5,-108 336.5,-108 336.5,-72 439.5,-72 439.5,-108"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="460.5,-108 357.5,-108 357.5,-72 460.5,-72 460.5,-108"/>
|
||||||
<text text-anchor="middle" x="388" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">DigitalInputDevice</text>
|
<text text-anchor="middle" x="409" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">DigitalInputDevice</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- DigitalInputDevice->WaitableInputDevice -->
|
<!-- DigitalInputDevice->InputDevice -->
|
||||||
<g id="edge4" class="edge"><title>DigitalInputDevice->WaitableInputDevice</title>
|
<g id="edge3" class="edge"><title>DigitalInputDevice->InputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M387.011,-108.303C386.57,-116.017 386.041,-125.288 385.549,-133.888"/>
|
<path fill="none" stroke="black" d="M409,-108.303C409,-116.017 409,-125.288 409,-133.888"/>
|
||||||
<polygon fill="black" stroke="black" points="382.054,-133.712 384.977,-143.896 389.042,-134.112 382.054,-133.712"/>
|
<polygon fill="black" stroke="black" points="405.5,-133.896 409,-143.896 412.5,-133.896 405.5,-133.896"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- Button -->
|
<!-- Button -->
|
||||||
<g id="node7" class="node"><title>Button</title>
|
<g id="node6" class="node"><title>Button</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="445,-36 391,-36 391,-0 445,-0 445,-36"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="445,-36 391,-36 391,-0 445,-0 445,-36"/>
|
||||||
<text text-anchor="middle" x="418" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Button</text>
|
<text text-anchor="middle" x="418" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Button</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- Button->DigitalInputDevice -->
|
<!-- Button->DigitalInputDevice -->
|
||||||
<g id="edge6" class="edge"><title>Button->DigitalInputDevice</title>
|
<g id="edge5" class="edge"><title>Button->DigitalInputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M410.584,-36.3034C407.206,-44.1868 403.13,-53.6958 399.377,-62.4536"/>
|
<path fill="none" stroke="black" d="M415.775,-36.3034C414.783,-44.0173 413.592,-53.2875 412.486,-61.8876"/>
|
||||||
<polygon fill="black" stroke="black" points="396.053,-61.3255 395.33,-71.8957 402.487,-64.0829 396.053,-61.3255"/>
|
<polygon fill="black" stroke="black" points="409.003,-61.531 411.199,-71.8957 415.946,-62.4237 409.003,-61.531"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- MotionSensor -->
|
<!-- MotionSensor -->
|
||||||
<g id="node8" class="node"><title>MotionSensor</title>
|
<g id="node7" class="node"><title>MotionSensor</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="82.5,-36 -0.5,-36 -0.5,-0 82.5,-0 82.5,-36"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="82.5,-36 -0.5,-36 -0.5,-0 82.5,-0 82.5,-36"/>
|
||||||
<text text-anchor="middle" x="41" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MotionSensor</text>
|
<text text-anchor="middle" x="41" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">MotionSensor</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- MotionSensor->SmoothedInputDevice -->
|
<!-- MotionSensor->SmoothedInputDevice -->
|
||||||
<g id="edge7" class="edge"><title>MotionSensor->SmoothedInputDevice</title>
|
<g id="edge6" class="edge"><title>MotionSensor->SmoothedInputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M82.5014,-34.5353C109.086,-44.4869 143.895,-57.5168 172.8,-68.3368"/>
|
<path fill="none" stroke="black" d="M82.5014,-34.5353C109.086,-44.4869 143.895,-57.5168 172.8,-68.3368"/>
|
||||||
<polygon fill="black" stroke="black" points="171.763,-71.6859 182.356,-71.9139 174.217,-65.1302 171.763,-71.6859"/>
|
<polygon fill="black" stroke="black" points="171.763,-71.6859 182.356,-71.9139 174.217,-65.1302 171.763,-71.6859"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- LightSensor -->
|
<!-- LightSensor -->
|
||||||
<g id="node9" class="node"><title>LightSensor</title>
|
<g id="node8" class="node"><title>LightSensor</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="175,-36 101,-36 101,-0 175,-0 175,-36"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="175,-36 101,-36 101,-0 175,-0 175,-36"/>
|
||||||
<text text-anchor="middle" x="138" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">LightSensor</text>
|
<text text-anchor="middle" x="138" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">LightSensor</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- LightSensor->SmoothedInputDevice -->
|
<!-- LightSensor->SmoothedInputDevice -->
|
||||||
<g id="edge8" class="edge"><title>LightSensor->SmoothedInputDevice</title>
|
<g id="edge7" class="edge"><title>LightSensor->SmoothedInputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M160.247,-36.3034C171.582,-45.1193 185.53,-55.9679 197.819,-65.5258"/>
|
<path fill="none" stroke="black" d="M160.247,-36.3034C171.582,-45.1193 185.53,-55.9679 197.819,-65.5258"/>
|
||||||
<polygon fill="black" stroke="black" points="195.966,-68.519 206.009,-71.8957 200.264,-62.9935 195.966,-68.519"/>
|
<polygon fill="black" stroke="black" points="195.966,-68.519 206.009,-71.8957 200.264,-62.9935 195.966,-68.519"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- LineSensor -->
|
<!-- LineSensor -->
|
||||||
<g id="node10" class="node"><title>LineSensor</title>
|
<g id="node9" class="node"><title>LineSensor</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="263,-36 193,-36 193,-0 263,-0 263,-36"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="263,-36 193,-36 193,-0 263,-0 263,-36"/>
|
||||||
<text text-anchor="middle" x="228" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">LineSensor</text>
|
<text text-anchor="middle" x="228" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">LineSensor</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- LineSensor->SmoothedInputDevice -->
|
<!-- LineSensor->SmoothedInputDevice -->
|
||||||
<g id="edge9" class="edge"><title>LineSensor->SmoothedInputDevice</title>
|
<g id="edge8" class="edge"><title>LineSensor->SmoothedInputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M228,-36.3034C228,-44.0173 228,-53.2875 228,-61.8876"/>
|
<path fill="none" stroke="black" d="M228,-36.3034C228,-44.0173 228,-53.2875 228,-61.8876"/>
|
||||||
<polygon fill="black" stroke="black" points="224.5,-61.8956 228,-71.8957 231.5,-61.8957 224.5,-61.8956"/>
|
<polygon fill="black" stroke="black" points="224.5,-61.8956 228,-71.8957 231.5,-61.8957 224.5,-61.8956"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- DistanceSensor -->
|
<!-- DistanceSensor -->
|
||||||
<g id="node11" class="node"><title>DistanceSensor</title>
|
<g id="node10" class="node"><title>DistanceSensor</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="373,-36 281,-36 281,-0 373,-0 373,-36"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="373,-36 281,-36 281,-0 373,-0 373,-36"/>
|
||||||
<text text-anchor="middle" x="327" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">DistanceSensor</text>
|
<text text-anchor="middle" x="327" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">DistanceSensor</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- DistanceSensor->SmoothedInputDevice -->
|
<!-- DistanceSensor->SmoothedInputDevice -->
|
||||||
<g id="edge10" class="edge"><title>DistanceSensor->SmoothedInputDevice</title>
|
<g id="edge9" class="edge"><title>DistanceSensor->SmoothedInputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M302.782,-36.1239C290.077,-45.1069 274.336,-56.2375 260.577,-65.9659"/>
|
<path fill="none" stroke="black" d="M302.782,-36.1239C290.077,-45.1069 274.336,-56.2375 260.577,-65.9659"/>
|
||||||
<polygon fill="black" stroke="black" points="258.456,-63.1791 252.311,-71.8102 262.497,-68.8947 258.456,-63.1791"/>
|
<polygon fill="black" stroke="black" points="258.456,-63.1791 252.311,-71.8102 262.497,-68.8947 258.456,-63.1791"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- OutputDevice -->
|
<!-- OutputDevice -->
|
||||||
<g id="node12" class="node"><title>OutputDevice</title>
|
<g id="node11" class="node"><title>OutputDevice</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="556,-252 474,-252 474,-216 556,-216 556,-252"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="576,-180 494,-180 494,-144 576,-144 576,-180"/>
|
||||||
<text text-anchor="middle" x="515" y="-231.5" font-family="Sans" font-size="10.00" fill="#ffffff">OutputDevice</text>
|
<text text-anchor="middle" x="535" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">OutputDevice</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- OutputDevice->GPIODevice -->
|
<!-- OutputDevice->GPIODevice -->
|
||||||
<g id="edge11" class="edge"><title>OutputDevice->GPIODevice</title>
|
<g id="edge10" class="edge"><title>OutputDevice->GPIODevice</title>
|
||||||
<path fill="none" stroke="black" d="M500.91,-252.303C494.077,-260.695 485.743,-270.93 478.244,-280.139"/>
|
<path fill="none" stroke="black" d="M519.427,-180.303C511.798,-188.78 502.477,-199.136 494.124,-208.417"/>
|
||||||
<polygon fill="black" stroke="black" points="475.528,-277.931 471.928,-287.896 480.956,-282.351 475.528,-277.931"/>
|
<polygon fill="black" stroke="black" points="491.482,-206.121 487.394,-215.896 496.685,-210.804 491.482,-206.121"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- DigitalOutputDevice -->
|
<!-- DigitalOutputDevice -->
|
||||||
<g id="node13" class="node"><title>DigitalOutputDevice</title>
|
<g id="node12" class="node"><title>DigitalOutputDevice</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="571,-180 459,-180 459,-144 571,-144 571,-180"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="591,-108 479,-108 479,-72 591,-72 591,-108"/>
|
||||||
<text text-anchor="middle" x="515" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">DigitalOutputDevice</text>
|
<text text-anchor="middle" x="535" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">DigitalOutputDevice</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- DigitalOutputDevice->OutputDevice -->
|
<!-- DigitalOutputDevice->OutputDevice -->
|
||||||
<g id="edge12" class="edge"><title>DigitalOutputDevice->OutputDevice</title>
|
<g id="edge11" class="edge"><title>DigitalOutputDevice->OutputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M515,-180.303C515,-188.017 515,-197.288 515,-205.888"/>
|
<path fill="none" stroke="black" d="M535,-108.303C535,-116.017 535,-125.288 535,-133.888"/>
|
||||||
<polygon fill="black" stroke="black" points="511.5,-205.896 515,-215.896 518.5,-205.896 511.5,-205.896"/>
|
<polygon fill="black" stroke="black" points="531.5,-133.896 535,-143.896 538.5,-133.896 531.5,-133.896"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- LED -->
|
<!-- LED -->
|
||||||
<g id="node14" class="node"><title>LED</title>
|
<g id="node13" class="node"><title>LED</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="519,-108 465,-108 465,-72 519,-72 519,-108"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="534,-36 480,-36 480,-0 534,-0 534,-36"/>
|
||||||
<text text-anchor="middle" x="492" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">LED</text>
|
<text text-anchor="middle" x="507" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">LED</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- LED->DigitalOutputDevice -->
|
<!-- LED->DigitalOutputDevice -->
|
||||||
<g id="edge13" class="edge"><title>LED->DigitalOutputDevice</title>
|
<g id="edge12" class="edge"><title>LED->DigitalOutputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M497.685,-108.303C500.248,-116.102 503.333,-125.491 506.185,-134.171"/>
|
<path fill="none" stroke="black" d="M513.921,-36.3034C517.075,-44.1868 520.878,-53.6958 524.381,-62.4536"/>
|
||||||
<polygon fill="black" stroke="black" points="502.933,-135.488 509.38,-143.896 509.584,-133.303 502.933,-135.488"/>
|
<polygon fill="black" stroke="black" points="521.195,-63.9108 528.158,-71.8957 527.694,-61.311 521.195,-63.9108"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- Buzzer -->
|
<!-- Buzzer -->
|
||||||
<g id="node15" class="node"><title>Buzzer</title>
|
<g id="node14" class="node"><title>Buzzer</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="591,-108 537,-108 537,-72 591,-72 591,-108"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="606,-36 552,-36 552,-0 606,-0 606,-36"/>
|
||||||
<text text-anchor="middle" x="564" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Buzzer</text>
|
<text text-anchor="middle" x="579" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Buzzer</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- Buzzer->DigitalOutputDevice -->
|
<!-- Buzzer->DigitalOutputDevice -->
|
||||||
<g id="edge14" class="edge"><title>Buzzer->DigitalOutputDevice</title>
|
<g id="edge13" class="edge"><title>Buzzer->DigitalOutputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M551.888,-108.303C546.132,-116.526 539.138,-126.517 532.794,-135.579"/>
|
<path fill="none" stroke="black" d="M568.124,-36.3034C563.008,-44.4411 556.805,-54.311 551.155,-63.2987"/>
|
||||||
<polygon fill="black" stroke="black" points="529.84,-133.696 526.973,-143.896 535.575,-137.71 529.84,-133.696"/>
|
<polygon fill="black" stroke="black" points="548.11,-61.5667 545.751,-71.8957 554.036,-65.2919 548.11,-61.5667"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- PWMOutputDevice -->
|
<!-- PWMOutputDevice -->
|
||||||
<g id="node16" class="node"><title>PWMOutputDevice</title>
|
<g id="node15" class="node"><title>PWMOutputDevice</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="693,-180 589,-180 589,-144 693,-144 693,-180"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="713,-108 609,-108 609,-72 713,-72 713,-108"/>
|
||||||
<text text-anchor="middle" x="641" y="-159.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMOutputDevice</text>
|
<text text-anchor="middle" x="661" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMOutputDevice</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- PWMOutputDevice->OutputDevice -->
|
<!-- PWMOutputDevice->OutputDevice -->
|
||||||
<g id="edge15" class="edge"><title>PWMOutputDevice->OutputDevice</title>
|
<g id="edge14" class="edge"><title>PWMOutputDevice->OutputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M610.177,-180.124C593.55,-189.361 572.835,-200.869 554.981,-210.788"/>
|
<path fill="none" stroke="black" d="M630.177,-108.124C613.55,-117.361 592.835,-128.869 574.981,-138.788"/>
|
||||||
<polygon fill="black" stroke="black" points="552.983,-207.894 545.942,-215.81 556.383,-214.013 552.983,-207.894"/>
|
<polygon fill="black" stroke="black" points="572.983,-135.894 565.942,-143.81 576.383,-142.013 572.983,-135.894"/>
|
||||||
</g>
|
</g>
|
||||||
<!-- PWMLED -->
|
<!-- PWMLED -->
|
||||||
<g id="node17" class="node"><title>PWMLED</title>
|
<g id="node16" class="node"><title>PWMLED</title>
|
||||||
<polygon fill="#2980b9" stroke="#2980b9" points="670,-108 612,-108 612,-72 670,-72 670,-108"/>
|
<polygon fill="#2980b9" stroke="#2980b9" points="690,-36 632,-36 632,-0 690,-0 690,-36"/>
|
||||||
<text text-anchor="middle" x="641" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMLED</text>
|
<text text-anchor="middle" x="661" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMLED</text>
|
||||||
</g>
|
</g>
|
||||||
<!-- PWMLED->PWMOutputDevice -->
|
<!-- PWMLED->PWMOutputDevice -->
|
||||||
<g id="edge16" class="edge"><title>PWMLED->PWMOutputDevice</title>
|
<g id="edge15" class="edge"><title>PWMLED->PWMOutputDevice</title>
|
||||||
<path fill="none" stroke="black" d="M641,-108.303C641,-116.017 641,-125.288 641,-133.888"/>
|
<path fill="none" stroke="black" d="M661,-36.3034C661,-44.0173 661,-53.2875 661,-61.8876"/>
|
||||||
<polygon fill="black" stroke="black" points="637.5,-133.896 641,-143.896 644.5,-133.896 637.5,-133.896"/>
|
<polygon fill="black" stroke="black" points="657.5,-61.8956 661,-71.8957 664.5,-61.8957 657.5,-61.8956"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
19
docs/images/other_device_hierarchy.dot
Normal file
19
docs/images/other_device_hierarchy.dot
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/* vim: set et sw=4 sts=4: */
|
||||||
|
|
||||||
|
digraph classes {
|
||||||
|
graph [rankdir=BT];
|
||||||
|
node [shape=rect, style=filled, fontname=Sans, fontsize=10];
|
||||||
|
edge [];
|
||||||
|
|
||||||
|
/* Abstract classes */
|
||||||
|
node [color="#9ec6e0", fontcolor="#000000"]
|
||||||
|
Device;
|
||||||
|
InternalDevice;
|
||||||
|
|
||||||
|
/* Concrete classes */
|
||||||
|
node [color="#2980b9", fontcolor="#ffffff"];
|
||||||
|
|
||||||
|
InternalDevice->Device;
|
||||||
|
TimeOfDay->InternalDevice;
|
||||||
|
PingServer->InternalDevice;
|
||||||
|
}
|
||||||
BIN
docs/images/other_device_hierarchy.pdf
Normal file
BIN
docs/images/other_device_hierarchy.pdf
Normal file
Binary file not shown.
BIN
docs/images/other_device_hierarchy.png
Normal file
BIN
docs/images/other_device_hierarchy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
48
docs/images/other_device_hierarchy.svg
Normal file
48
docs/images/other_device_hierarchy.svg
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?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.36.0 (20140111.2315)
|
||||||
|
-->
|
||||||
|
<!-- Title: classes Pages: 1 -->
|
||||||
|
<svg width="163pt" height="188pt"
|
||||||
|
viewBox="0.00 0.00 163.00 188.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 184)">
|
||||||
|
<title>classes</title>
|
||||||
|
<polygon fill="white" stroke="none" points="-4,4 -4,-184 159,-184 159,4 -4,4"/>
|
||||||
|
<!-- Device -->
|
||||||
|
<g id="node1" class="node"><title>Device</title>
|
||||||
|
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="104,-180 50,-180 50,-144 104,-144 104,-180"/>
|
||||||
|
<text text-anchor="middle" x="77" y="-159.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text>
|
||||||
|
</g>
|
||||||
|
<!-- InternalDevice -->
|
||||||
|
<g id="node2" class="node"><title>InternalDevice</title>
|
||||||
|
<polygon fill="#9ec6e0" stroke="#9ec6e0" points="119.5,-108 34.5,-108 34.5,-72 119.5,-72 119.5,-108"/>
|
||||||
|
<text text-anchor="middle" x="77" y="-87.5" font-family="Sans" font-size="10.00" fill="#000000">InternalDevice</text>
|
||||||
|
</g>
|
||||||
|
<!-- InternalDevice->Device -->
|
||||||
|
<g id="edge1" class="edge"><title>InternalDevice->Device</title>
|
||||||
|
<path fill="none" stroke="black" d="M77,-108.303C77,-116.017 77,-125.288 77,-133.888"/>
|
||||||
|
<polygon fill="black" stroke="black" points="73.5001,-133.896 77,-143.896 80.5001,-133.896 73.5001,-133.896"/>
|
||||||
|
</g>
|
||||||
|
<!-- TimeOfDay -->
|
||||||
|
<g id="node3" class="node"><title>TimeOfDay</title>
|
||||||
|
<polygon fill="#2980b9" stroke="#2980b9" points="68.5,-36 -0.5,-36 -0.5,-0 68.5,-0 68.5,-36"/>
|
||||||
|
<text text-anchor="middle" x="34" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">TimeOfDay</text>
|
||||||
|
</g>
|
||||||
|
<!-- TimeOfDay->InternalDevice -->
|
||||||
|
<g id="edge2" class="edge"><title>TimeOfDay->InternalDevice</title>
|
||||||
|
<path fill="none" stroke="black" d="M44.6292,-36.3034C49.6281,-44.4411 55.691,-54.311 61.2121,-63.2987"/>
|
||||||
|
<polygon fill="black" stroke="black" points="58.2766,-65.2069 66.4931,-71.8957 64.2411,-61.5429 58.2766,-65.2069"/>
|
||||||
|
</g>
|
||||||
|
<!-- PingServer -->
|
||||||
|
<g id="node4" class="node"><title>PingServer</title>
|
||||||
|
<polygon fill="#2980b9" stroke="#2980b9" points="155,-36 87,-36 87,-0 155,-0 155,-36"/>
|
||||||
|
<text text-anchor="middle" x="121" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">PingServer</text>
|
||||||
|
</g>
|
||||||
|
<!-- PingServer->InternalDevice -->
|
||||||
|
<g id="edge3" class="edge"><title>PingServer->InternalDevice</title>
|
||||||
|
<path fill="none" stroke="black" d="M110.124,-36.3034C105.008,-44.4411 98.8045,-54.311 93.1551,-63.2987"/>
|
||||||
|
<polygon fill="black" stroke="black" points="90.1098,-61.5667 87.7513,-71.8957 96.0363,-65.2919 90.1098,-61.5667"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -12,7 +12,9 @@ Table of Contents
|
|||||||
api_output
|
api_output
|
||||||
api_spi
|
api_spi
|
||||||
api_boards
|
api_boards
|
||||||
|
api_other
|
||||||
api_generic
|
api_generic
|
||||||
|
api_source_tools
|
||||||
api_pins
|
api_pins
|
||||||
api_exc
|
api_exc
|
||||||
changelog
|
changelog
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from .pins import (
|
|||||||
from .exc import (
|
from .exc import (
|
||||||
GPIOZeroError,
|
GPIOZeroError,
|
||||||
DeviceClosed,
|
DeviceClosed,
|
||||||
|
BadEventHandler,
|
||||||
CompositeDeviceError,
|
CompositeDeviceError,
|
||||||
CompositeDeviceBadName,
|
CompositeDeviceBadName,
|
||||||
SPIError,
|
SPIError,
|
||||||
@@ -45,13 +46,15 @@ from .devices import (
|
|||||||
Device,
|
Device,
|
||||||
GPIODevice,
|
GPIODevice,
|
||||||
CompositeDevice,
|
CompositeDevice,
|
||||||
|
)
|
||||||
|
from .mixins import (
|
||||||
SharedMixin,
|
SharedMixin,
|
||||||
SourceMixin,
|
SourceMixin,
|
||||||
ValuesMixin,
|
ValuesMixin,
|
||||||
|
EventsMixin,
|
||||||
)
|
)
|
||||||
from .input_devices import (
|
from .input_devices import (
|
||||||
InputDevice,
|
InputDevice,
|
||||||
WaitableInputDevice,
|
|
||||||
DigitalInputDevice,
|
DigitalInputDevice,
|
||||||
SmoothedInputDevice,
|
SmoothedInputDevice,
|
||||||
Button,
|
Button,
|
||||||
@@ -103,3 +106,24 @@ from .boards import (
|
|||||||
CamJamKitRobot,
|
CamJamKitRobot,
|
||||||
Energenie,
|
Energenie,
|
||||||
)
|
)
|
||||||
|
from .other_devices import (
|
||||||
|
InternalDevice,
|
||||||
|
PingServer,
|
||||||
|
TimeOfDay,
|
||||||
|
)
|
||||||
|
from .source_tools import (
|
||||||
|
averaged,
|
||||||
|
clamped,
|
||||||
|
conjunction,
|
||||||
|
cos_values,
|
||||||
|
disjunction,
|
||||||
|
inverted,
|
||||||
|
negated,
|
||||||
|
post_delayed,
|
||||||
|
pre_delayed,
|
||||||
|
quantized,
|
||||||
|
queued,
|
||||||
|
random_values,
|
||||||
|
scaled,
|
||||||
|
sin_values,
|
||||||
|
)
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ from .exc import (
|
|||||||
from .input_devices import Button
|
from .input_devices import Button
|
||||||
from .output_devices import OutputDevice, LED, PWMLED, Buzzer, Motor
|
from .output_devices import OutputDevice, LED, PWMLED, Buzzer, Motor
|
||||||
from .threads import GPIOThread
|
from .threads import GPIOThread
|
||||||
from .devices import Device, CompositeDevice, SharedMixin, SourceMixin
|
from .devices import Device, CompositeDevice
|
||||||
|
from .mixins import SharedMixin, SourceMixin
|
||||||
|
|
||||||
|
|
||||||
class CompositeOutputDevice(SourceMixin, CompositeDevice):
|
class CompositeOutputDevice(SourceMixin, CompositeDevice):
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ from types import FunctionType
|
|||||||
from threading import RLock
|
from threading import RLock
|
||||||
|
|
||||||
from .threads import GPIOThread, _threads_shutdown
|
from .threads import GPIOThread, _threads_shutdown
|
||||||
|
from .mixins import (
|
||||||
|
ValuesMixin,
|
||||||
|
SharedMixin,
|
||||||
|
)
|
||||||
from .exc import (
|
from .exc import (
|
||||||
DeviceClosed,
|
DeviceClosed,
|
||||||
GPIOPinMissing,
|
GPIOPinMissing,
|
||||||
@@ -201,127 +205,35 @@ class GPIOBase(GPIOMeta(nstr('GPIOBase'), (), {})):
|
|||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
class ValuesMixin(object):
|
|
||||||
"""
|
|
||||||
Adds a :attr:`values` property to the class which returns an infinite
|
|
||||||
generator of readings from the :attr:`value` property.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Use this mixin *first* in the parent class list.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def values(self):
|
|
||||||
"""
|
|
||||||
An infinite iterator of values read from `value`.
|
|
||||||
"""
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
yield self.value
|
|
||||||
except GPIODeviceClosed:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
class SourceMixin(object):
|
|
||||||
"""
|
|
||||||
Adds a :attr:`source` property to the class which, given an iterable,
|
|
||||||
sets :attr:`value` to each member of that iterable until it is exhausted.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Use this mixin *first* in the parent class list.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self._source = None
|
|
||||||
self._source_thread = None
|
|
||||||
self._source_delay = 0.01
|
|
||||||
super(SourceMixin, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
try:
|
|
||||||
super(SourceMixin, self).close()
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
self.source = None
|
|
||||||
|
|
||||||
def _copy_values(self, source):
|
|
||||||
for v in source:
|
|
||||||
self.value = v
|
|
||||||
if self._source_thread.stopping.wait(self._source_delay):
|
|
||||||
break
|
|
||||||
|
|
||||||
@property
|
|
||||||
def source_delay(self):
|
|
||||||
"""
|
|
||||||
The delay (measured in seconds) in the loop used to read values from
|
|
||||||
:attr:`source`. Defaults to 0.01 seconds which is generally sufficient
|
|
||||||
to keep CPU usage to a minimum while providing adequate responsiveness.
|
|
||||||
"""
|
|
||||||
return self._source_delay
|
|
||||||
|
|
||||||
@source_delay.setter
|
|
||||||
def source_delay(self, value):
|
|
||||||
if value < 0:
|
|
||||||
raise GPIOBadSourceDelay('source_delay must be 0 or greater')
|
|
||||||
self._source_delay = float(value)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def source(self):
|
|
||||||
"""
|
|
||||||
The iterable to use as a source of values for :attr:`value`.
|
|
||||||
"""
|
|
||||||
return self._source
|
|
||||||
|
|
||||||
@source.setter
|
|
||||||
def source(self, value):
|
|
||||||
if self._source_thread is not None:
|
|
||||||
self._source_thread.stop()
|
|
||||||
self._source_thread = None
|
|
||||||
self._source = value
|
|
||||||
if value is not None:
|
|
||||||
self._source_thread = GPIOThread(target=self._copy_values, args=(value,))
|
|
||||||
self._source_thread.start()
|
|
||||||
|
|
||||||
|
|
||||||
class SharedMixin(object):
|
|
||||||
"""
|
|
||||||
This mixin marks a class as "shared". In this case, the meta-class
|
|
||||||
(GPIOMeta) will use :meth:`_shared_key` to convert the constructor
|
|
||||||
arguments to an immutable key, and will check whether any existing
|
|
||||||
instances match that key. If they do, they will be returned by the
|
|
||||||
constructor instead of a new instance. An internal reference counter is
|
|
||||||
used to determine how many times an instance has been "constructed" in this
|
|
||||||
way.
|
|
||||||
|
|
||||||
When :meth:`close` is called, an internal reference counter will be
|
|
||||||
decremented and the instance will only close when it reaches zero.
|
|
||||||
"""
|
|
||||||
_INSTANCES = {}
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self._refs = 0
|
|
||||||
super(SharedMixin, self).__del__()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _shared_key(cls, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Given the constructor arguments, returns an immutable key representing
|
|
||||||
the instance. The default simply assumes all positional arguments are
|
|
||||||
immutable.
|
|
||||||
"""
|
|
||||||
return args
|
|
||||||
|
|
||||||
|
|
||||||
class Device(ValuesMixin, GPIOBase):
|
class Device(ValuesMixin, GPIOBase):
|
||||||
"""
|
"""
|
||||||
Represents a single device of any type; GPIO-based, SPI-based, I2C-based,
|
Represents a single device of any type; GPIO-based, SPI-based, I2C-based,
|
||||||
etc. This is the base class of the device hierarchy.
|
etc. This is the base class of the device hierarchy. It defines the
|
||||||
|
basic services applicable to all devices (specifically thhe :attr:`is_active`
|
||||||
|
property, the :attr:`value` property, and the :meth:`close` method).
|
||||||
"""
|
"""
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<gpiozero.%s object>" % (self.__class__.__name__)
|
return "<gpiozero.%s object>" % (self.__class__.__name__)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
"""
|
||||||
|
Returns a value representing the device's state. Frequently, this is a
|
||||||
|
boolean value, or a number between 0 and 1 but some devices use larger
|
||||||
|
ranges (e.g. -1 to +1) and composite devices usually use tuples to
|
||||||
|
return the states of all their subordinate components.
|
||||||
|
"""
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_active(self):
|
||||||
|
"""
|
||||||
|
Returns ``True`` if the device is currently active and ``False``
|
||||||
|
otherwise. This property is usually derived from :attr:`value`. Unlike
|
||||||
|
:attr:`value`, this is *always* a boolean.
|
||||||
|
"""
|
||||||
|
return bool(self.value)
|
||||||
|
|
||||||
|
|
||||||
class CompositeDevice(Device):
|
class CompositeDevice(Device):
|
||||||
"""
|
"""
|
||||||
@@ -417,15 +329,16 @@ class CompositeDevice(Device):
|
|||||||
def value(self):
|
def value(self):
|
||||||
return self.tuple(*(device.value for device in self))
|
return self.tuple(*(device.value for device in self))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_active(self):
|
||||||
|
return any(self.value)
|
||||||
|
|
||||||
|
|
||||||
class GPIODevice(Device):
|
class GPIODevice(Device):
|
||||||
"""
|
"""
|
||||||
Extends :class:`Device`. Represents a generic GPIO device.
|
Extends :class:`Device`. Represents a generic GPIO device and provides
|
||||||
|
the services common to all single-pin GPIO devices (like ensuring two
|
||||||
This is the class at the root of the gpiozero class hierarchy. It handles
|
GPIO devices do no share a :attr:`pin`).
|
||||||
ensuring that two GPIO devices do not share the same pin, and provides
|
|
||||||
basic services applicable to all devices (specifically the :attr:`pin`
|
|
||||||
property, :attr:`is_active` property, and the :attr:`close` method).
|
|
||||||
|
|
||||||
:param int pin:
|
:param int pin:
|
||||||
The GPIO pin (in BCM numbering) that the device is connected to. If
|
The GPIO pin (in BCM numbering) that the device is connected to. If
|
||||||
@@ -494,14 +407,8 @@ class GPIODevice(Device):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
"""
|
|
||||||
Returns ``True`` if the device is currently active and ``False``
|
|
||||||
otherwise.
|
|
||||||
"""
|
|
||||||
return self._read()
|
return self._read()
|
||||||
|
|
||||||
is_active = value
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
try:
|
try:
|
||||||
return "<gpiozero.%s object on pin %r, is_active=%s>" % (
|
return "<gpiozero.%s object on pin %r, is_active=%s>" % (
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ class GPIOZeroError(Exception):
|
|||||||
class DeviceClosed(GPIOZeroError):
|
class DeviceClosed(GPIOZeroError):
|
||||||
"Error raised when an operation is attempted on a closed device"
|
"Error raised when an operation is attempted on a closed device"
|
||||||
|
|
||||||
|
class BadEventHandler(GPIOZeroError, ValueError):
|
||||||
|
"Error raised when an event handler with an incompatible prototype is specified"
|
||||||
|
|
||||||
class CompositeDeviceError(GPIOZeroError):
|
class CompositeDeviceError(GPIOZeroError):
|
||||||
"Base class for errors specific to the CompositeDevice hierarchy"
|
"Base class for errors specific to the CompositeDevice hierarchy"
|
||||||
|
|
||||||
|
|||||||
@@ -7,15 +7,12 @@ from __future__ import (
|
|||||||
division,
|
division,
|
||||||
)
|
)
|
||||||
|
|
||||||
import inspect
|
|
||||||
import warnings
|
import warnings
|
||||||
from functools import wraps
|
|
||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
from threading import Event
|
|
||||||
|
|
||||||
from .exc import InputDeviceError, GPIODeviceError, GPIODeviceClosed
|
from .exc import InputDeviceError, GPIODeviceError, GPIODeviceClosed
|
||||||
from .devices import GPIODevice, CompositeDevice
|
from .devices import GPIODevice, CompositeDevice
|
||||||
from .threads import GPIOQueue
|
from .mixins import GPIOQueue, EventsMixin
|
||||||
|
|
||||||
|
|
||||||
class InputDevice(GPIODevice):
|
class InputDevice(GPIODevice):
|
||||||
@@ -65,148 +62,7 @@ class InputDevice(GPIODevice):
|
|||||||
return super(InputDevice, self).__repr__()
|
return super(InputDevice, self).__repr__()
|
||||||
|
|
||||||
|
|
||||||
class WaitableInputDevice(InputDevice):
|
class DigitalInputDevice(EventsMixin, InputDevice):
|
||||||
"""
|
|
||||||
Represents a generic input device with distinct waitable states.
|
|
||||||
|
|
||||||
This class extends :class:`InputDevice` with methods for waiting on the
|
|
||||||
device's status (:meth:`wait_for_active` and :meth:`wait_for_inactive`),
|
|
||||||
and properties that hold functions to be called when the device changes
|
|
||||||
state (:meth:`when_activated` and :meth:`when_deactivated`). These are
|
|
||||||
aliased appropriately in various subclasses.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Note that this class provides no means of actually firing its events;
|
|
||||||
it's effectively an abstract base class.
|
|
||||||
"""
|
|
||||||
def __init__(self, pin=None, pull_up=False):
|
|
||||||
super(WaitableInputDevice, self).__init__(pin, pull_up)
|
|
||||||
self._active_event = Event()
|
|
||||||
self._inactive_event = Event()
|
|
||||||
self._when_activated = None
|
|
||||||
self._when_deactivated = None
|
|
||||||
self._last_state = None
|
|
||||||
|
|
||||||
def wait_for_active(self, timeout=None):
|
|
||||||
"""
|
|
||||||
Pause the script until the device is activated, or the timeout is
|
|
||||||
reached.
|
|
||||||
|
|
||||||
:param float timeout:
|
|
||||||
Number of seconds to wait before proceeding. If this is ``None``
|
|
||||||
(the default), then wait indefinitely until the device is active.
|
|
||||||
"""
|
|
||||||
return self._active_event.wait(timeout)
|
|
||||||
|
|
||||||
def wait_for_inactive(self, timeout=None):
|
|
||||||
"""
|
|
||||||
Pause the script until the device is deactivated, or the timeout is
|
|
||||||
reached.
|
|
||||||
|
|
||||||
:param float timeout:
|
|
||||||
Number of seconds to wait before proceeding. If this is ``None``
|
|
||||||
(the default), then wait indefinitely until the device is inactive.
|
|
||||||
"""
|
|
||||||
return self._inactive_event.wait(timeout)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def when_activated(self):
|
|
||||||
"""
|
|
||||||
The function to run when the device changes state from inactive to
|
|
||||||
active.
|
|
||||||
|
|
||||||
This can be set to a function which accepts no (mandatory) parameters,
|
|
||||||
or a Python function which accepts a single mandatory parameter (with
|
|
||||||
as many optional parameters as you like). If the function accepts a
|
|
||||||
single mandatory parameter, the device that activated will be passed
|
|
||||||
as that parameter.
|
|
||||||
|
|
||||||
Set this property to ``None`` (the default) to disable the event.
|
|
||||||
"""
|
|
||||||
return self._when_activated
|
|
||||||
|
|
||||||
@when_activated.setter
|
|
||||||
def when_activated(self, value):
|
|
||||||
self._when_activated = self._wrap_callback(value)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def when_deactivated(self):
|
|
||||||
"""
|
|
||||||
The function to run when the device changes state from active to
|
|
||||||
inactive.
|
|
||||||
|
|
||||||
This can be set to a function which accepts no (mandatory) parameters,
|
|
||||||
or a Python function which accepts a single mandatory parameter (with
|
|
||||||
as many optional parameters as you like). If the function accepts a
|
|
||||||
single mandatory parameter, the device that deactivated will be
|
|
||||||
passed as that parameter.
|
|
||||||
|
|
||||||
Set this property to ``None`` (the default) to disable the event.
|
|
||||||
"""
|
|
||||||
return self._when_deactivated
|
|
||||||
|
|
||||||
@when_deactivated.setter
|
|
||||||
def when_deactivated(self, value):
|
|
||||||
self._when_deactivated = self._wrap_callback(value)
|
|
||||||
|
|
||||||
def _wrap_callback(self, fn):
|
|
||||||
if fn is None:
|
|
||||||
return None
|
|
||||||
elif not callable(fn):
|
|
||||||
raise InputDeviceError('value must be None or a callable')
|
|
||||||
elif inspect.isbuiltin(fn):
|
|
||||||
# We can't introspect the prototype of builtins. In this case we
|
|
||||||
# assume that the builtin has no (mandatory) parameters; this is
|
|
||||||
# the most reasonable assumption on the basis that pre-existing
|
|
||||||
# builtins have no knowledge of gpiozero, and the sole parameter
|
|
||||||
# we would pass is a gpiozero object
|
|
||||||
return fn
|
|
||||||
else:
|
|
||||||
# Try binding ourselves to the argspec of the provided callable.
|
|
||||||
# If this works, assume the function is capable of accepting no
|
|
||||||
# parameters
|
|
||||||
try:
|
|
||||||
inspect.getcallargs(fn)
|
|
||||||
return fn
|
|
||||||
except TypeError:
|
|
||||||
try:
|
|
||||||
# If the above fails, try binding with a single parameter
|
|
||||||
# (ourselves). If this works, wrap the specified callback
|
|
||||||
inspect.getcallargs(fn, self)
|
|
||||||
@wraps(fn)
|
|
||||||
def wrapper():
|
|
||||||
return fn(self)
|
|
||||||
return wrapper
|
|
||||||
except TypeError:
|
|
||||||
raise InputDeviceError(
|
|
||||||
'value must be a callable which accepts up to one '
|
|
||||||
'mandatory parameter')
|
|
||||||
|
|
||||||
def _fire_events(self):
|
|
||||||
old_state = self._last_state
|
|
||||||
new_state = self._last_state = self.is_active
|
|
||||||
if old_state is None:
|
|
||||||
# Initial "indeterminate" state; set events but don't fire
|
|
||||||
# callbacks as there's not necessarily an edge
|
|
||||||
if new_state:
|
|
||||||
self._active_event.set()
|
|
||||||
else:
|
|
||||||
self._inactive_event.set()
|
|
||||||
else:
|
|
||||||
if not old_state and new_state:
|
|
||||||
self._inactive_event.clear()
|
|
||||||
self._active_event.set()
|
|
||||||
if self.when_activated:
|
|
||||||
self.when_activated()
|
|
||||||
elif old_state and not new_state:
|
|
||||||
self._active_event.clear()
|
|
||||||
self._inactive_event.set()
|
|
||||||
if self.when_deactivated:
|
|
||||||
self.when_deactivated()
|
|
||||||
|
|
||||||
|
|
||||||
class DigitalInputDevice(WaitableInputDevice):
|
|
||||||
"""
|
"""
|
||||||
Represents a generic input device with typical on/off behaviour.
|
Represents a generic input device with typical on/off behaviour.
|
||||||
|
|
||||||
@@ -233,7 +89,7 @@ class DigitalInputDevice(WaitableInputDevice):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
class SmoothedInputDevice(WaitableInputDevice):
|
class SmoothedInputDevice(EventsMixin, InputDevice):
|
||||||
"""
|
"""
|
||||||
Represents a generic input device which takes its value from the mean of a
|
Represents a generic input device which takes its value from the mean of a
|
||||||
queue of historical values.
|
queue of historical values.
|
||||||
|
|||||||
326
gpiozero/mixins.py
Normal file
326
gpiozero/mixins.py
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
print_function,
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
nstr = str
|
||||||
|
str = type('')
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import weakref
|
||||||
|
from functools import wraps
|
||||||
|
from threading import Event
|
||||||
|
from collections import deque
|
||||||
|
try:
|
||||||
|
from statistics import median, mean
|
||||||
|
except ImportError:
|
||||||
|
from .compat import median, mean
|
||||||
|
|
||||||
|
from .threads import GPIOThread
|
||||||
|
from .exc import BadEventHandler, DeviceClosed
|
||||||
|
|
||||||
|
|
||||||
|
class ValuesMixin(object):
|
||||||
|
"""
|
||||||
|
Adds a :attr:`values` property to the class which returns an infinite
|
||||||
|
generator of readings from the :attr:`value` property.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Use this mixin *first* in the parent class list.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self):
|
||||||
|
"""
|
||||||
|
An infinite iterator of values read from `value`.
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
yield self.value
|
||||||
|
except DeviceClosed:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
class SourceMixin(object):
|
||||||
|
"""
|
||||||
|
Adds a :attr:`source` property to the class which, given an iterable,
|
||||||
|
sets :attr:`value` to each member of that iterable until it is exhausted.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Use this mixin *first* in the parent class list.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._source = None
|
||||||
|
self._source_thread = None
|
||||||
|
self._source_delay = 0.01
|
||||||
|
super(SourceMixin, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
try:
|
||||||
|
super(SourceMixin, self).close()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
self.source = None
|
||||||
|
|
||||||
|
def _copy_values(self, source):
|
||||||
|
for v in source:
|
||||||
|
self.value = v
|
||||||
|
if self._source_thread.stopping.wait(self._source_delay):
|
||||||
|
break
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source_delay(self):
|
||||||
|
"""
|
||||||
|
The delay (measured in seconds) in the loop used to read values from
|
||||||
|
:attr:`source`. Defaults to 0.01 seconds which is generally sufficient
|
||||||
|
to keep CPU usage to a minimum while providing adequate responsiveness.
|
||||||
|
"""
|
||||||
|
return self._source_delay
|
||||||
|
|
||||||
|
@source_delay.setter
|
||||||
|
def source_delay(self, value):
|
||||||
|
if value < 0:
|
||||||
|
raise GPIOBadSourceDelay('source_delay must be 0 or greater')
|
||||||
|
self._source_delay = float(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source(self):
|
||||||
|
"""
|
||||||
|
The iterable to use as a source of values for :attr:`value`.
|
||||||
|
"""
|
||||||
|
return self._source
|
||||||
|
|
||||||
|
@source.setter
|
||||||
|
def source(self, value):
|
||||||
|
if self._source_thread is not None:
|
||||||
|
self._source_thread.stop()
|
||||||
|
self._source_thread = None
|
||||||
|
self._source = value
|
||||||
|
if value is not None:
|
||||||
|
self._source_thread = GPIOThread(target=self._copy_values, args=(value,))
|
||||||
|
self._source_thread.start()
|
||||||
|
|
||||||
|
|
||||||
|
class SharedMixin(object):
|
||||||
|
"""
|
||||||
|
This mixin marks a class as "shared". In this case, the meta-class
|
||||||
|
(GPIOMeta) will use :meth:`_shared_key` to convert the constructor
|
||||||
|
arguments to an immutable key, and will check whether any existing
|
||||||
|
instances match that key. If they do, they will be returned by the
|
||||||
|
constructor instead of a new instance. An internal reference counter is
|
||||||
|
used to determine how many times an instance has been "constructed" in this
|
||||||
|
way.
|
||||||
|
|
||||||
|
When :meth:`close` is called, an internal reference counter will be
|
||||||
|
decremented and the instance will only close when it reaches zero.
|
||||||
|
"""
|
||||||
|
_INSTANCES = {}
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self._refs = 0
|
||||||
|
super(SharedMixin, self).__del__()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _shared_key(cls, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Given the constructor arguments, returns an immutable key representing
|
||||||
|
the instance. The default simply assumes all positional arguments are
|
||||||
|
immutable.
|
||||||
|
"""
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
class EventsMixin(object):
|
||||||
|
"""
|
||||||
|
Adds edge-detected :meth:`when_activated` and :meth:`when_deactivated`
|
||||||
|
events to a device based on changes to the :attr:`~Device.is_active`
|
||||||
|
property common to all devices. Also adds :meth:`wait_for_active` and
|
||||||
|
:meth:`wait_for_inactive` methods for level-waiting.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Note that this mixin provides no means of actually firing its events;
|
||||||
|
call :meth:`_fire_events` in sub-classes when device state changes to
|
||||||
|
trigger the events. This should also be called once at the end of
|
||||||
|
initialization to set initial states.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(EventsMixin, self).__init__(*args, **kwargs)
|
||||||
|
self._active_event = Event()
|
||||||
|
self._inactive_event = Event()
|
||||||
|
self._when_activated = None
|
||||||
|
self._when_deactivated = None
|
||||||
|
self._last_state = None
|
||||||
|
|
||||||
|
def wait_for_active(self, timeout=None):
|
||||||
|
"""
|
||||||
|
Pause the script until the device is activated, or the timeout is
|
||||||
|
reached.
|
||||||
|
|
||||||
|
:param float timeout:
|
||||||
|
Number of seconds to wait before proceeding. If this is ``None``
|
||||||
|
(the default), then wait indefinitely until the device is active.
|
||||||
|
"""
|
||||||
|
return self._active_event.wait(timeout)
|
||||||
|
|
||||||
|
def wait_for_inactive(self, timeout=None):
|
||||||
|
"""
|
||||||
|
Pause the script until the device is deactivated, or the timeout is
|
||||||
|
reached.
|
||||||
|
|
||||||
|
:param float timeout:
|
||||||
|
Number of seconds to wait before proceeding. If this is ``None``
|
||||||
|
(the default), then wait indefinitely until the device is inactive.
|
||||||
|
"""
|
||||||
|
return self._inactive_event.wait(timeout)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def when_activated(self):
|
||||||
|
"""
|
||||||
|
The function to run when the device changes state from inactive to
|
||||||
|
active.
|
||||||
|
|
||||||
|
This can be set to a function which accepts no (mandatory) parameters,
|
||||||
|
or a Python function which accepts a single mandatory parameter (with
|
||||||
|
as many optional parameters as you like). If the function accepts a
|
||||||
|
single mandatory parameter, the device that activated will be passed
|
||||||
|
as that parameter.
|
||||||
|
|
||||||
|
Set this property to ``None`` (the default) to disable the event.
|
||||||
|
"""
|
||||||
|
return self._when_activated
|
||||||
|
|
||||||
|
@when_activated.setter
|
||||||
|
def when_activated(self, value):
|
||||||
|
self._when_activated = self._wrap_callback(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def when_deactivated(self):
|
||||||
|
"""
|
||||||
|
The function to run when the device changes state from active to
|
||||||
|
inactive.
|
||||||
|
|
||||||
|
This can be set to a function which accepts no (mandatory) parameters,
|
||||||
|
or a Python function which accepts a single mandatory parameter (with
|
||||||
|
as many optional parameters as you like). If the function accepts a
|
||||||
|
single mandatory parameter, the device that deactivated will be
|
||||||
|
passed as that parameter.
|
||||||
|
|
||||||
|
Set this property to ``None`` (the default) to disable the event.
|
||||||
|
"""
|
||||||
|
return self._when_deactivated
|
||||||
|
|
||||||
|
@when_deactivated.setter
|
||||||
|
def when_deactivated(self, value):
|
||||||
|
self._when_deactivated = self._wrap_callback(value)
|
||||||
|
|
||||||
|
def _wrap_callback(self, fn):
|
||||||
|
if fn is None:
|
||||||
|
return None
|
||||||
|
elif not callable(fn):
|
||||||
|
raise BadEventHandler('value must be None or a callable')
|
||||||
|
elif inspect.isbuiltin(fn):
|
||||||
|
# We can't introspect the prototype of builtins. In this case we
|
||||||
|
# assume that the builtin has no (mandatory) parameters; this is
|
||||||
|
# the most reasonable assumption on the basis that pre-existing
|
||||||
|
# builtins have no knowledge of gpiozero, and the sole parameter
|
||||||
|
# we would pass is a gpiozero object
|
||||||
|
return fn
|
||||||
|
else:
|
||||||
|
# Try binding ourselves to the argspec of the provided callable.
|
||||||
|
# If this works, assume the function is capable of accepting no
|
||||||
|
# parameters
|
||||||
|
try:
|
||||||
|
inspect.getcallargs(fn)
|
||||||
|
return fn
|
||||||
|
except TypeError:
|
||||||
|
try:
|
||||||
|
# If the above fails, try binding with a single parameter
|
||||||
|
# (ourselves). If this works, wrap the specified callback
|
||||||
|
inspect.getcallargs(fn, self)
|
||||||
|
@wraps(fn)
|
||||||
|
def wrapper():
|
||||||
|
return fn(self)
|
||||||
|
return wrapper
|
||||||
|
except TypeError:
|
||||||
|
raise BadEventHandler(
|
||||||
|
'value must be a callable which accepts up to one '
|
||||||
|
'mandatory parameter')
|
||||||
|
|
||||||
|
def _fire_events(self):
|
||||||
|
old_state = self._last_state
|
||||||
|
new_state = self._last_state = self.is_active
|
||||||
|
if old_state is None:
|
||||||
|
# Initial "indeterminate" state; set events but don't fire
|
||||||
|
# callbacks as there's not necessarily an edge
|
||||||
|
if new_state:
|
||||||
|
self._active_event.set()
|
||||||
|
else:
|
||||||
|
self._inactive_event.set()
|
||||||
|
else:
|
||||||
|
if not old_state and new_state:
|
||||||
|
self._inactive_event.clear()
|
||||||
|
self._active_event.set()
|
||||||
|
if self.when_activated:
|
||||||
|
self.when_activated()
|
||||||
|
elif old_state and not new_state:
|
||||||
|
self._active_event.clear()
|
||||||
|
self._inactive_event.set()
|
||||||
|
if self.when_deactivated:
|
||||||
|
self.when_deactivated()
|
||||||
|
|
||||||
|
|
||||||
|
class GPIOQueue(GPIOThread):
|
||||||
|
"""
|
||||||
|
Extends :class:`GPIOThread`. Provides a background thread that monitors a
|
||||||
|
device's values and provides a running *average* (defaults to median) of
|
||||||
|
those values. If the *parent* device includes the :class:`EventsMixin` in
|
||||||
|
its ancestry, the thread automatically calls
|
||||||
|
:meth:`~EventsMixin._fire_events`.
|
||||||
|
"""
|
||||||
|
def __init__(
|
||||||
|
self, parent, queue_len=5, sample_wait=0.0, partial=False,
|
||||||
|
average=median):
|
||||||
|
assert callable(average)
|
||||||
|
super(GPIOQueue, self).__init__(target=self.fill)
|
||||||
|
if queue_len < 1:
|
||||||
|
raise GPIOBadQueueLen('queue_len must be at least one')
|
||||||
|
if sample_wait < 0:
|
||||||
|
raise GPIOBadSampleWait('sample_wait must be 0 or greater')
|
||||||
|
self.queue = deque(maxlen=queue_len)
|
||||||
|
self.partial = partial
|
||||||
|
self.sample_wait = sample_wait
|
||||||
|
self.full = Event()
|
||||||
|
self.parent = weakref.proxy(parent)
|
||||||
|
self.average = average
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
if not self.partial:
|
||||||
|
self.full.wait()
|
||||||
|
try:
|
||||||
|
return self.average(self.queue)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
# No data == inactive value
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
def fill(self):
|
||||||
|
try:
|
||||||
|
while (not self.stopping.wait(self.sample_wait) and
|
||||||
|
len(self.queue) < self.queue.maxlen):
|
||||||
|
self.queue.append(self.parent._read())
|
||||||
|
if self.partial and isinstance(self.parent, EventsMixin):
|
||||||
|
self.parent._fire_events()
|
||||||
|
self.full.set()
|
||||||
|
while not self.stopping.wait(self.sample_wait):
|
||||||
|
self.queue.append(self.parent._read())
|
||||||
|
if isinstance(self.parent, EventsMixin):
|
||||||
|
self.parent._fire_events()
|
||||||
|
except ReferenceError:
|
||||||
|
# Parent is dead; time to die!
|
||||||
|
pass
|
||||||
|
|
||||||
168
gpiozero/other_devices.py
Normal file
168
gpiozero/other_devices.py
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
# vim: set fileencoding=utf-8:
|
||||||
|
|
||||||
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
print_function,
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
str = type('')
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import io
|
||||||
|
import subprocess
|
||||||
|
from datetime import datetime, time
|
||||||
|
|
||||||
|
from .devices import Device
|
||||||
|
from .mixins import EventsMixin
|
||||||
|
|
||||||
|
|
||||||
|
class InternalDevice(EventsMixin, Device):
|
||||||
|
"""
|
||||||
|
Extends :class:`Device` to provide a basis for devices which have no
|
||||||
|
specific hardware representation. This are effectively pseudo-devices and
|
||||||
|
usually represent operating system services like the internal clock, file
|
||||||
|
systems or network facilities.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class PingServer(InternalDevice):
|
||||||
|
"""
|
||||||
|
Extends :class:`InternalDevice` to provide a device which is active when a
|
||||||
|
*host* on the network can be pinged.
|
||||||
|
|
||||||
|
The following example lights an LED while a server is reachable (note the
|
||||||
|
use of :attr:`~SourceMixin.source_delay` to ensure the server is not
|
||||||
|
flooded with pings)::
|
||||||
|
|
||||||
|
from gpiozero import PingServer, LED
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
server = PingServer('my-server')
|
||||||
|
led = LED(4)
|
||||||
|
led.source_delay = 1
|
||||||
|
led.source = server.values
|
||||||
|
pause()
|
||||||
|
|
||||||
|
:param str host:
|
||||||
|
The hostname or IP address to attempt to ping.
|
||||||
|
"""
|
||||||
|
def __init__(self, host):
|
||||||
|
self.host = host
|
||||||
|
super(PingServer, self).__init__()
|
||||||
|
self._fire_events()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<gpiozero.PingDevice host="%s">' % self.host
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
# XXX This is doing a DNS lookup every time it's queried; should we
|
||||||
|
# call gethostbyname in the constructor and ping that instead (good
|
||||||
|
# for consistency, but what if the user *expects* the host to change
|
||||||
|
# address?)
|
||||||
|
with io.open(os.devnull, 'wb') as devnull:
|
||||||
|
try:
|
||||||
|
subprocess.check_call(
|
||||||
|
['ping', '-c1', self.host],
|
||||||
|
stdout=devnull, stderr=devnull)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class TimeOfDay(InternalDevice):
|
||||||
|
"""
|
||||||
|
Extends :class:`InternalDevice` to provide a device which is active when
|
||||||
|
the computer's clock indicates that the current time is between
|
||||||
|
*start_time* and *end_time* (inclusive) which are :class:`~datetime.time`
|
||||||
|
instances.
|
||||||
|
|
||||||
|
The following example turns on a lamp attached to an :class:`Energenie`
|
||||||
|
plug between 7 and 8 AM::
|
||||||
|
|
||||||
|
from datetime import time
|
||||||
|
from gpiozero import TimeOfDay, Energenie
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
lamp = Energenie(0)
|
||||||
|
morning = TimeOfDay(time(7), time(8))
|
||||||
|
morning.when_activated = lamp.on
|
||||||
|
morning.when_deactivated = lamp.off
|
||||||
|
pause()
|
||||||
|
|
||||||
|
:param ~datetime.time start_time:
|
||||||
|
The time from which the device will be considered active.
|
||||||
|
|
||||||
|
:param ~datetime.time end_time:
|
||||||
|
The time after which the device will be considered inactive.
|
||||||
|
|
||||||
|
:param bool utc:
|
||||||
|
If ``True`` (the default), a naive UTC time will be used for the
|
||||||
|
comparison rather than a local time-zone reading.
|
||||||
|
"""
|
||||||
|
def __init__(self, start_time, end_time, utc=True):
|
||||||
|
self._start_time = None
|
||||||
|
self._end_time = None
|
||||||
|
self._utc = True
|
||||||
|
super(TimeOfDay, self).__init__()
|
||||||
|
self.start_time = start_time
|
||||||
|
self.end_time = end_time
|
||||||
|
self.utc = utc
|
||||||
|
self._fire_events()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<gpiozero.TimeOfDay active between %s and %s %s>' % (
|
||||||
|
self.start_time, self.end_time, ('local', 'UTC')[self.utc])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start_time(self):
|
||||||
|
"""
|
||||||
|
The time of day after which the device will be considered active.
|
||||||
|
"""
|
||||||
|
return self._start_time
|
||||||
|
|
||||||
|
@start_time.setter
|
||||||
|
def start_time(self, value):
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
value = value.time()
|
||||||
|
if not isinstance(value, time):
|
||||||
|
raise ValueError('start_time must be a datetime, or time instance')
|
||||||
|
self._start_time = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end_time(self):
|
||||||
|
"""
|
||||||
|
The time of day after which the device will be considered inactive.
|
||||||
|
"""
|
||||||
|
return self._end_time
|
||||||
|
|
||||||
|
@end_time.setter
|
||||||
|
def end_time(self, value):
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
value = value.time()
|
||||||
|
if not isinstance(value, time):
|
||||||
|
raise ValueError('end_time must be a datetime, or time instance')
|
||||||
|
self._end_time = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def utc(self):
|
||||||
|
"""
|
||||||
|
If ``True``, use a naive UTC time reading for comparison instead of a
|
||||||
|
local timezone reading.
|
||||||
|
"""
|
||||||
|
return self._utc
|
||||||
|
|
||||||
|
@utc.setter
|
||||||
|
def utc(self, value):
|
||||||
|
self._utc = bool(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
if self.utc:
|
||||||
|
return self.start_time <= datetime.utcnow().time() <= self.end_time
|
||||||
|
else:
|
||||||
|
return self.start_time <= datetime.now().time() <= self.end_time
|
||||||
|
|
||||||
@@ -11,7 +11,8 @@ from threading import Lock
|
|||||||
from itertools import repeat, cycle, chain
|
from itertools import repeat, cycle, chain
|
||||||
|
|
||||||
from .exc import OutputDeviceBadValue, GPIOPinMissing
|
from .exc import OutputDeviceBadValue, GPIOPinMissing
|
||||||
from .devices import GPIODevice, Device, CompositeDevice, SourceMixin
|
from .devices import GPIODevice, Device, CompositeDevice
|
||||||
|
from .mixins import SourceMixin
|
||||||
from .threads import GPIOThread
|
from .threads import GPIOThread
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
296
gpiozero/source_tools.py
Normal file
296
gpiozero/source_tools.py
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
# vim: set fileencoding=utf-8:
|
||||||
|
|
||||||
|
from __future__ import (
|
||||||
|
unicode_literals,
|
||||||
|
print_function,
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
)
|
||||||
|
str = type('')
|
||||||
|
|
||||||
|
|
||||||
|
from random import random
|
||||||
|
from time import sleep
|
||||||
|
try:
|
||||||
|
from itertools import izip as zip
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
from itertools import count, cycle
|
||||||
|
from math import sin, cos, floor
|
||||||
|
try:
|
||||||
|
from statistics import mean
|
||||||
|
except ImportError:
|
||||||
|
from .compat import mean
|
||||||
|
|
||||||
|
|
||||||
|
def negated(values):
|
||||||
|
"""
|
||||||
|
Returns the negation of the supplied values (``True`` becomes ``False``,
|
||||||
|
and ``False`` becomes ``True``). For example::
|
||||||
|
|
||||||
|
from gpiozero import Button, LED, negated
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
led = LED(4)
|
||||||
|
btn = Button(17)
|
||||||
|
led.source = negated(btn.values)
|
||||||
|
pause()
|
||||||
|
"""
|
||||||
|
for v in values:
|
||||||
|
yield not v
|
||||||
|
|
||||||
|
|
||||||
|
def inverted(values):
|
||||||
|
"""
|
||||||
|
Returns the inversion of the supplied values (1 becomes 0, 0 becomes 1,
|
||||||
|
0.1 becomes 0.9, etc.). For example::
|
||||||
|
|
||||||
|
from gpiozero import MCP3008, PWMLED, inverted
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
led = PWMLED(4)
|
||||||
|
pot = MCP3008(channel=0)
|
||||||
|
led.source = inverted(pot.values)
|
||||||
|
pause()
|
||||||
|
"""
|
||||||
|
for v in values:
|
||||||
|
yield 1 - v
|
||||||
|
|
||||||
|
|
||||||
|
def scaled(values, range_min, range_max, domain_min=0, domain_max=1):
|
||||||
|
"""
|
||||||
|
Returns *values* scaled from *range_min* to *range_max*, assuming that all
|
||||||
|
items in *values* lie between *domain_min* and *domain_max* (which default
|
||||||
|
to 0 and 1 respectively). For example, to control the direction of a motor
|
||||||
|
(which is represented as a value between -1 and 1) using a potentiometer
|
||||||
|
(which typically provides values between 0 and 1)::
|
||||||
|
|
||||||
|
from gpiozero import Motor, MCP3008, scaled
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
motor = Motor(20, 21)
|
||||||
|
pot = MCP3008(channel=0)
|
||||||
|
motor.source = scaled(pot.values, -1, 1)
|
||||||
|
pause()
|
||||||
|
"""
|
||||||
|
domain_size = domain_max - domain_min
|
||||||
|
range_size = range_max - range_min
|
||||||
|
for v in values:
|
||||||
|
yield (((v - domain_min) / domain_size) * range_size) + range_min
|
||||||
|
|
||||||
|
|
||||||
|
def clamped(values, range_min=0, range_max=1):
|
||||||
|
"""
|
||||||
|
Returns *values* clamped from *range_min* to *range_max*, i.e. any items
|
||||||
|
less than *range_min* will be returned as *range_min* and any items
|
||||||
|
larger than *range_max* will be returned as *range_max* (these default to
|
||||||
|
0 and 1 respectively). For example::
|
||||||
|
|
||||||
|
from gpiozero import PWMLED, MCP3008, clamped
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
led = PWMLED(4)
|
||||||
|
pot = MCP3008(channel=0)
|
||||||
|
led.source = clamped(pot.values, 0.5, 1.0)
|
||||||
|
pause()
|
||||||
|
"""
|
||||||
|
for v in values:
|
||||||
|
yield min(max(v, range_min), range_max)
|
||||||
|
|
||||||
|
|
||||||
|
def quantized(values, steps, range_min=0, range_max=1):
|
||||||
|
"""
|
||||||
|
Returns *values* quantized to *steps* increments. All items in *values* are
|
||||||
|
assumed to be between *range_min* and *range_max* (use :func:`scaled` to
|
||||||
|
ensure this if necessary).
|
||||||
|
|
||||||
|
For example, to quantize values between 0 and 1 to 5 "steps" (0.0, 0.25,
|
||||||
|
0.5, 0.75, 1.0)::
|
||||||
|
|
||||||
|
from gpiozero import PWMLED, MCP3008, quantized
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
led = PWMLED(4)
|
||||||
|
pot = MCP3008(channel=0)
|
||||||
|
led.source = quantized(pot.values, 4)
|
||||||
|
pause()
|
||||||
|
"""
|
||||||
|
range_size = range_max - range_min
|
||||||
|
for v in scaled(values, 0, 1, range_min, range_max):
|
||||||
|
yield ((int(v * steps) / steps) * range_size) + range_min
|
||||||
|
|
||||||
|
|
||||||
|
def conjunction(*values):
|
||||||
|
"""
|
||||||
|
Returns the `logical conjunction`_ of all supplied values (the result is
|
||||||
|
only ``True`` if and only if all input values are simultaneously ``True``).
|
||||||
|
One or more *values* can be specified. For example, to light an
|
||||||
|
:class:`LED` only when *both* buttons are pressed::
|
||||||
|
|
||||||
|
from gpiozero import LED, Button, conjunction
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
led = LED(4)
|
||||||
|
btn1 = Button(20)
|
||||||
|
btn2 = Button(21)
|
||||||
|
led.source = conjunction(btn1.values, btn2.values)
|
||||||
|
pause()
|
||||||
|
|
||||||
|
.. _logical conjunction: https://en.wikipedia.org/wiki/Logical_conjunction
|
||||||
|
"""
|
||||||
|
for v in zip(*values):
|
||||||
|
yield all(v)
|
||||||
|
|
||||||
|
|
||||||
|
def disjunction(*values):
|
||||||
|
"""
|
||||||
|
Returns the `logical disjunction`_ of all supplied values (the result is
|
||||||
|
``True`` if any of the input values are currently ``True``). One or more
|
||||||
|
*values* can be specified. For example, the light an :class:`LED` when
|
||||||
|
*any* button is pressed::
|
||||||
|
|
||||||
|
from gpiozero import LED, Button, conjunction
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
led = LED(4)
|
||||||
|
btn1 = Button(20)
|
||||||
|
btn2 = Button(21)
|
||||||
|
led.source = disjunction(btn1.values, btn2.values)
|
||||||
|
pause()
|
||||||
|
|
||||||
|
.. _logical disjunction: https://en.wikipedia.org/wiki/Logical_disjunction
|
||||||
|
"""
|
||||||
|
for v in zip(*values):
|
||||||
|
yield any(v)
|
||||||
|
|
||||||
|
|
||||||
|
def averaged(*values):
|
||||||
|
"""
|
||||||
|
Returns the mean of all supplied values. One or more *values* can be
|
||||||
|
specified. For example, to light a :class:`PWMLED` as the average of
|
||||||
|
several potentiometers connected to an :class:`MCP3008` ADC::
|
||||||
|
|
||||||
|
from gpiozero import MCP3008, PWMLED, averaged
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
pot1 = MCP3008(channel=0)
|
||||||
|
pot2 = MCP3008(channel=1)
|
||||||
|
pot3 = MCP3008(channel=2)
|
||||||
|
led = PWMLED(4)
|
||||||
|
led.source = averaged(pot1.values, pot2.values, pot3.values)
|
||||||
|
pause()
|
||||||
|
"""
|
||||||
|
for v in zip(*values):
|
||||||
|
yield mean(v)
|
||||||
|
|
||||||
|
|
||||||
|
def queued(values, qsize):
|
||||||
|
"""
|
||||||
|
Queues up readings from *values* (the number of readings queued is
|
||||||
|
determined by *qsize*) and begins yielding values only when the queue is
|
||||||
|
full. For example, to "cascade" values along a sequence of LEDs::
|
||||||
|
|
||||||
|
from gpiozero import LEDBoard, Button, queued
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
leds = LEDBoard(5, 6, 13, 19, 26)
|
||||||
|
btn = Button(17)
|
||||||
|
for i in range(4):
|
||||||
|
leds[i].source = queued(leds[i + 1].values, 5)
|
||||||
|
leds[i].source_delay = 0.01
|
||||||
|
leds[4].source = btn.values
|
||||||
|
pause()
|
||||||
|
"""
|
||||||
|
q = []
|
||||||
|
it = iter(values)
|
||||||
|
for i in range(qsize):
|
||||||
|
q.append(next(it))
|
||||||
|
for i in cycle(range(qsize)):
|
||||||
|
yield q[i]
|
||||||
|
try:
|
||||||
|
q[i] = next(it)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def pre_delayed(values, delay):
|
||||||
|
"""
|
||||||
|
Waits for *delay* seconds before returning each item from *values*.
|
||||||
|
"""
|
||||||
|
for v in values:
|
||||||
|
sleep(delay)
|
||||||
|
yield v
|
||||||
|
|
||||||
|
|
||||||
|
def post_delayed(values, delay):
|
||||||
|
"""
|
||||||
|
Waits for *delay* seconds after returning each item from *values*.
|
||||||
|
"""
|
||||||
|
for v in values:
|
||||||
|
yield v
|
||||||
|
sleep(delay)
|
||||||
|
|
||||||
|
|
||||||
|
def random_values():
|
||||||
|
"""
|
||||||
|
Provides an infinite source of random values between 0 and 1. For example,
|
||||||
|
to produce a "flickering candle" effect with an LED::
|
||||||
|
|
||||||
|
from gpiozero import PWMLED, random_values
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
led = PWMLED(4)
|
||||||
|
led.source = random_values()
|
||||||
|
pause()
|
||||||
|
|
||||||
|
If you require a wider range than 0 to 1, see :func:`scaled`.
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
yield random()
|
||||||
|
|
||||||
|
|
||||||
|
def sin_values():
|
||||||
|
"""
|
||||||
|
Provides an infinite source of values representing a sine wave (from -1 to
|
||||||
|
+1), calculated as the result of applying sign to a simple degrees counter
|
||||||
|
that increments by one for each requested value. For example, to produce a
|
||||||
|
"siren" effect with a couple of LEDs::
|
||||||
|
|
||||||
|
from gpiozero import PWMLED, sin_values, scaled, inverted
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
red = PWMLED(2)
|
||||||
|
blue = PWMLED(3)
|
||||||
|
red.source_delay = 0.1
|
||||||
|
blue.source_delay = 0.1
|
||||||
|
red.source = scaled(sin_values(), 0, 1, -1, 1)
|
||||||
|
blue.source = inverted(red.values)
|
||||||
|
pause()
|
||||||
|
|
||||||
|
If you require a wider range than 0 to 1, see :func:`scaled`.
|
||||||
|
"""
|
||||||
|
for d in cycle(range(360)):
|
||||||
|
yield sin(d)
|
||||||
|
|
||||||
|
|
||||||
|
def cos_values():
|
||||||
|
"""
|
||||||
|
Provides an infinite source of values representing a cosine wave (from -1
|
||||||
|
to +1), calculated as the result of applying sign to a simple degrees
|
||||||
|
counter that increments by one for each requested value. For example, to
|
||||||
|
produce a "siren" effect with a couple of LEDs::
|
||||||
|
|
||||||
|
from gpiozero import PWMLED, cos_values, scaled, inverted
|
||||||
|
from signal import pause
|
||||||
|
|
||||||
|
red = PWMLED(2)
|
||||||
|
blue = PWMLED(3)
|
||||||
|
red.source = scaled(cos_values(), 0, 1, -1, 1)
|
||||||
|
blue.source = inverted(red.values)
|
||||||
|
pause()
|
||||||
|
|
||||||
|
If you require a wider range than 0 to 1, see :func:`scaled`.
|
||||||
|
"""
|
||||||
|
for d in cycle(range(360)):
|
||||||
|
yield cos(d)
|
||||||
|
|
||||||
@@ -6,13 +6,7 @@ from __future__ import (
|
|||||||
)
|
)
|
||||||
str = type('')
|
str = type('')
|
||||||
|
|
||||||
import weakref
|
from threading import Thread, Event
|
||||||
from collections import deque
|
|
||||||
from threading import Thread, Event, RLock
|
|
||||||
try:
|
|
||||||
from statistics import median, mean
|
|
||||||
except ImportError:
|
|
||||||
from .compat import median, mean
|
|
||||||
|
|
||||||
from .exc import (
|
from .exc import (
|
||||||
GPIOBadQueueLen,
|
GPIOBadQueueLen,
|
||||||
@@ -46,46 +40,3 @@ class GPIOThread(Thread):
|
|||||||
super(GPIOThread, self).join()
|
super(GPIOThread, self).join()
|
||||||
_THREADS.discard(self)
|
_THREADS.discard(self)
|
||||||
|
|
||||||
|
|
||||||
class GPIOQueue(GPIOThread):
|
|
||||||
def __init__(
|
|
||||||
self, parent, queue_len=5, sample_wait=0.0, partial=False,
|
|
||||||
average=median):
|
|
||||||
assert callable(average)
|
|
||||||
super(GPIOQueue, self).__init__(target=self.fill)
|
|
||||||
if queue_len < 1:
|
|
||||||
raise GPIOBadQueueLen('queue_len must be at least one')
|
|
||||||
if sample_wait < 0:
|
|
||||||
raise GPIOBadSampleWait('sample_wait must be 0 or greater')
|
|
||||||
self.queue = deque(maxlen=queue_len)
|
|
||||||
self.partial = partial
|
|
||||||
self.sample_wait = sample_wait
|
|
||||||
self.full = Event()
|
|
||||||
self.parent = weakref.proxy(parent)
|
|
||||||
self.average = average
|
|
||||||
|
|
||||||
@property
|
|
||||||
def value(self):
|
|
||||||
if not self.partial:
|
|
||||||
self.full.wait()
|
|
||||||
try:
|
|
||||||
return self.average(self.queue)
|
|
||||||
except ZeroDivisionError:
|
|
||||||
# No data == inactive value
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
def fill(self):
|
|
||||||
try:
|
|
||||||
while (not self.stopping.wait(self.sample_wait) and
|
|
||||||
len(self.queue) < self.queue.maxlen):
|
|
||||||
self.queue.append(self.parent._read())
|
|
||||||
if self.partial:
|
|
||||||
self.parent._fire_events()
|
|
||||||
self.full.set()
|
|
||||||
while not self.stopping.wait(self.sample_wait):
|
|
||||||
self.queue.append(self.parent._read())
|
|
||||||
self.parent._fire_events()
|
|
||||||
except ReferenceError:
|
|
||||||
# Parent is dead; time to die!
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user