Make tests work reliably on the Pi
While the tests work well on a PC or Travis, the Pi (where I ought to be running them!) has some issues with the timing tests. Need to relax the tolerance of the "assert_states_and_times" method to 0.05 seconds otherwise it periodically fails even on something reasonably quick like a Pi 2 (less failures on a Pi 3 but still occasionally). Also reduced default fps to 25; if the default timing occasionally fails on a Pi 2 it's evidently too fast for a Pi 1 and shouldn't be the default; 25 also doesn't look any different to me on a pulsing LED. There's also a bunch of miscellaneous fixes in here; last minute typos and chart re-gens for the 1.2 release.
| @@ -18,7 +18,7 @@ individually. | ||||
| LED Board | ||||
| ========= | ||||
|  | ||||
| .. autoclass:: LEDBoard(\*pins, pwm=False, active_high=True, initial_value=False) | ||||
| .. autoclass:: LEDBoard(\*pins, pwm=False, active_high=True, initial_value=False, \*\*named_pins) | ||||
|     :inherited-members: | ||||
|     :members: | ||||
|  | ||||
|   | ||||
| @@ -81,10 +81,10 @@ Base Classes | ||||
| .. autoclass:: SPIDevice | ||||
|     :members: | ||||
|  | ||||
| .. autoclass:: InternalDevice | ||||
| .. autoclass:: InternalDevice() | ||||
|     :members: | ||||
|  | ||||
| .. autoclass:: CompositeDevice | ||||
| .. autoclass:: CompositeDevice(\*args, _order=None, \*\*kwargs) | ||||
|     :members: | ||||
|  | ||||
| Input Devices | ||||
|   | ||||
| @@ -2,13 +2,20 @@ | ||||
| Source Tools | ||||
| ============ | ||||
| 
 | ||||
| .. currentmodule:: gpiozero | ||||
| .. currentmodule:: gpiozero.tools | ||||
| 
 | ||||
| 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. | ||||
| the :attr:`~gpiozero.SourceMixin.source` and | ||||
| :attr:`~gpiozero.ValuesMixin.values` attributes common to most devices in the | ||||
| library. These utility routines are in the ``tools`` module of GPIO Zero and | ||||
| are typically imported as follows:: | ||||
| 
 | ||||
|     from gpiozero.tools import scaled, negated, conjunction | ||||
| 
 | ||||
| Given that :attr:`~gpiozero.SourceMixin.source` and | ||||
| :attr:`~gpiozero.ValuesMixin.values` deal with infinite iterators, another | ||||
| excellent source of utilities is the :mod:`itertools` module in the standard | ||||
| library. | ||||
| 
 | ||||
| Single source conversions | ||||
| ========================= | ||||
| @@ -12,6 +12,8 @@ Release 1.2.0 (2016-04-??) | ||||
| * Added :class:`LineSensor` class for single line-sensors (`#109`_) | ||||
| * Added :class:`DistanceSensor` class for HC-SR04 ultra-sonic sensors (`#114`_) | ||||
| * Added :class:`SnowPi` class for the Ryanteck Snow-pi board (`#130`_) | ||||
| * Added :attr:`~Button.when_held` (and related properties) to :class:`Button` | ||||
|   (`#115`_) | ||||
| * Fixed issues with installing GPIO Zero for python 3 on Raspbian Wheezy | ||||
|   releases (`#140`_) | ||||
| * Added support for lots of ADC chips (MCP3xxx family) (`#162`_) - many thanks | ||||
| @@ -35,6 +37,7 @@ reports in this version - keep 'em coming! | ||||
| .. _#69: https://github.com/RPi-Distro/python-gpiozero/issues/69 | ||||
| .. _#109: https://github.com/RPi-Distro/python-gpiozero/issues/109 | ||||
| .. _#114: https://github.com/RPi-Distro/python-gpiozero/issues/114 | ||||
| .. _#115: https://github.com/RPi-Distro/python-gpiozero/issues/115 | ||||
| .. _#130: https://github.com/RPi-Distro/python-gpiozero/issues/130 | ||||
| .. _#140: https://github.com/RPi-Distro/python-gpiozero/issues/140 | ||||
| .. _#162: https://github.com/RPi-Distro/python-gpiozero/issues/162 | ||||
|   | ||||
| Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 20 KiB | 
| @@ -4,11 +4,11 @@ | ||||
| <!-- Generated by graphviz version 2.36.0 (20140111.2315) | ||||
|  --> | ||||
| <!-- Title: classes Pages: 1 --> | ||||
| <svg width="447pt" height="116pt" | ||||
|  viewBox="0.00 0.00 447.00 116.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | ||||
| <svg width="538pt" height="116pt" | ||||
|  viewBox="0.00 0.00 538.00 116.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 112)"> | ||||
| <title>classes</title> | ||||
| <polygon fill="white" stroke="none" points="-4,4 -4,-112 443,-112 443,4 -4,4"/> | ||||
| <polygon fill="white" stroke="none" points="-4,4 -4,-112 534,-112 534,4 -4,4"/> | ||||
| <!-- RGBLED --> | ||||
| <g id="node1" class="node"><title>RGBLED</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="55.5,-108 0.5,-108 0.5,-72 55.5,-72 55.5,-108"/> | ||||
| @@ -16,83 +16,98 @@ | ||||
| </g> | ||||
| <!-- PWMLED --> | ||||
| <g id="node2" class="node"><title>PWMLED</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="58.5,-36 1.5,-36 1.5,-0 58.5,-0 58.5,-36"/> | ||||
| <text text-anchor="middle" x="30" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMLED</text> | ||||
| <polygon fill="#298029" stroke="#298029" points="143.5,-36 86.5,-36 86.5,-0 143.5,-0 143.5,-36"/> | ||||
| <text text-anchor="middle" x="115" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">PWMLED</text> | ||||
| </g> | ||||
| <!-- RGBLED->PWMLED --> | ||||
| <g id="edge1" class="edge"><title>RGBLED->PWMLED</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M28.4944,-71.6966C28.7148,-63.9827 28.9796,-54.7125 29.2254,-46.1124"/> | ||||
| <polygon fill="none" stroke="black" points="32.7242,-46.2002 29.5113,-36.1043 25.727,-46.0003 32.7242,-46.2002"/> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M49.5056,-71.6966C60.4625,-62.8807 73.9459,-52.0321 85.8249,-42.4742"/> | ||||
| <polygon fill="none" stroke="black" points="88.1446,-45.1001 93.7418,-36.1043 83.7565,-39.6462 88.1446,-45.1001"/> | ||||
| </g> | ||||
| <!-- LEDBoard --> | ||||
| <g id="node3" class="node"><title>LEDBoard</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="138,-108 74,-108 74,-72 138,-72 138,-108"/> | ||||
| <text text-anchor="middle" x="106" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBoard</text> | ||||
| <polygon fill="#298029" stroke="#298029" points="238,-108 174,-108 174,-72 238,-72 238,-108"/> | ||||
| <text text-anchor="middle" x="206" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBoard</text> | ||||
| </g> | ||||
| <!-- LEDBoard->PWMLED --> | ||||
| <g id="edge3" class="edge"><title>LEDBoard->PWMLED</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M87.2135,-71.6966C77.826,-63.0503 66.3156,-52.4486 56.0865,-43.027"/> | ||||
| <polygon fill="none" stroke="black" points="58.2971,-40.3047 48.5704,-36.1043 53.5547,-45.4535 58.2971,-40.3047"/> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M183.506,-71.6966C172.045,-62.8807 157.942,-52.0321 145.516,-42.4742"/> | ||||
| <polygon fill="none" stroke="black" points="147.296,-39.4273 137.236,-36.1043 143.028,-44.9757 147.296,-39.4273"/> | ||||
| </g> | ||||
| <!-- LED --> | ||||
| <g id="node4" class="node"><title>LED</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="133,-36 79,-36 79,-0 133,-0 133,-36"/> | ||||
| <text text-anchor="middle" x="106" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">LED</text> | ||||
| <polygon fill="#298029" stroke="#298029" points="224,-36 170,-36 170,-0 224,-0 224,-36"/> | ||||
| <text text-anchor="middle" x="197" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">LED</text> | ||||
| </g> | ||||
| <!-- LEDBoard->LED --> | ||||
| <g id="edge2" class="edge"><title>LEDBoard->LED</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M106,-71.6966C106,-63.9827 106,-54.7125 106,-46.1124"/> | ||||
| <polygon fill="none" stroke="black" points="109.5,-46.1043 106,-36.1043 102.5,-46.1044 109.5,-46.1043"/> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M203.775,-71.6966C202.783,-63.9827 201.592,-54.7125 200.486,-46.1124"/> | ||||
| <polygon fill="none" stroke="black" points="203.946,-45.5763 199.199,-36.1043 197.003,-46.469 203.946,-45.5763"/> | ||||
| </g> | ||||
| <!-- LEDBarGraph --> | ||||
| <g id="node5" class="node"><title>LEDBarGraph</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="155.25,-108 74.75,-108 74.75,-72 155.25,-72 155.25,-108"/> | ||||
| <text text-anchor="middle" x="115" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">LEDBarGraph</text> | ||||
| </g> | ||||
| <!-- LEDBarGraph->PWMLED --> | ||||
| <g id="edge5" class="edge"><title>LEDBarGraph->PWMLED</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M115,-71.6966C115,-63.9827 115,-54.7125 115,-46.1124"/> | ||||
| <polygon fill="none" stroke="black" points="118.5,-46.1043 115,-36.1043 111.5,-46.1044 118.5,-46.1043"/> | ||||
| </g> | ||||
| <!-- LEDBarGraph->LED --> | ||||
| <g id="edge4" class="edge"><title>LEDBarGraph->LED</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M135.27,-71.6966C145.498,-62.9655 158.061,-52.2405 169.178,-42.7503"/> | ||||
| <polygon fill="none" stroke="black" points="171.63,-45.259 176.963,-36.1043 167.085,-39.935 171.63,-45.259"/> | ||||
| </g> | ||||
| <!-- TrafficLightsBuzzer --> | ||||
| <g id="node5" class="node"><title>TrafficLightsBuzzer</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="320.25,-108 215.75,-108 215.75,-72 320.25,-72 320.25,-108"/> | ||||
| <text text-anchor="middle" x="268" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLightsBuzzer</text> | ||||
| <g id="node6" class="node"><title>TrafficLightsBuzzer</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="411.25,-108 306.75,-108 306.75,-72 411.25,-72 411.25,-108"/> | ||||
| <text text-anchor="middle" x="359" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLightsBuzzer</text> | ||||
| </g> | ||||
| <!-- TrafficLights --> | ||||
| <g id="node6" class="node"><title>TrafficLights</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="223,-36 151,-36 151,-0 223,-0 223,-36"/> | ||||
| <text text-anchor="middle" x="187" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLights</text> | ||||
| <g id="node7" class="node"><title>TrafficLights</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="314,-36 242,-36 242,-0 314,-0 314,-36"/> | ||||
| <text text-anchor="middle" x="278" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">TrafficLights</text> | ||||
| </g> | ||||
| <!-- TrafficLightsBuzzer->TrafficLights --> | ||||
| <g id="edge4" class="edge"><title>TrafficLightsBuzzer->TrafficLights</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M247.978,-71.6966C237.874,-62.9655 225.464,-52.2405 214.482,-42.7503"/> | ||||
| <polygon fill="none" stroke="black" points="216.647,-39.9948 206.792,-36.1043 212.07,-45.2912 216.647,-39.9948"/> | ||||
| <g id="edge6" class="edge"><title>TrafficLightsBuzzer->TrafficLights</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M338.978,-71.6966C328.874,-62.9655 316.464,-52.2405 305.482,-42.7503"/> | ||||
| <polygon fill="none" stroke="black" points="307.647,-39.9948 297.792,-36.1043 303.07,-45.2912 307.647,-39.9948"/> | ||||
| </g> | ||||
| <!-- Buzzer --> | ||||
| <g id="node7" class="node"><title>Buzzer</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="295,-36 241,-36 241,-0 295,-0 295,-36"/> | ||||
| <text text-anchor="middle" x="268" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Buzzer</text> | ||||
| <g id="node8" class="node"><title>Buzzer</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="386,-36 332,-36 332,-0 386,-0 386,-36"/> | ||||
| <text text-anchor="middle" x="359" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Buzzer</text> | ||||
| </g> | ||||
| <!-- TrafficLightsBuzzer->Buzzer --> | ||||
| <g id="edge5" class="edge"><title>TrafficLightsBuzzer->Buzzer</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M268,-71.6966C268,-63.9827 268,-54.7125 268,-46.1124"/> | ||||
| <polygon fill="none" stroke="black" points="271.5,-46.1043 268,-36.1043 264.5,-46.1044 271.5,-46.1043"/> | ||||
| <g id="edge7" class="edge"><title>TrafficLightsBuzzer->Buzzer</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M359,-71.6966C359,-63.9827 359,-54.7125 359,-46.1124"/> | ||||
| <polygon fill="none" stroke="black" points="362.5,-46.1043 359,-36.1043 355.5,-46.1044 362.5,-46.1043"/> | ||||
| </g> | ||||
| <!-- Button --> | ||||
| <g id="node8" class="node"><title>Button</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="367,-36 313,-36 313,-0 367,-0 367,-36"/> | ||||
| <text text-anchor="middle" x="340" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Button</text> | ||||
| <g id="node9" class="node"><title>Button</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="458,-36 404,-36 404,-0 458,-0 458,-36"/> | ||||
| <text text-anchor="middle" x="431" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Button</text> | ||||
| </g> | ||||
| <!-- TrafficLightsBuzzer->Button --> | ||||
| <g id="edge6" class="edge"><title>TrafficLightsBuzzer->Button</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M285.798,-71.6966C294.604,-63.135 305.382,-52.6562 315.001,-43.3045"/> | ||||
| <polygon fill="none" stroke="black" points="317.677,-45.5846 322.407,-36.1043 312.797,-40.5657 317.677,-45.5846"/> | ||||
| <g id="edge8" class="edge"><title>TrafficLightsBuzzer->Button</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M376.798,-71.6966C385.604,-63.135 396.382,-52.6562 406.001,-43.3045"/> | ||||
| <polygon fill="none" stroke="black" points="408.677,-45.5846 413.407,-36.1043 403.797,-40.5657 408.677,-45.5846"/> | ||||
| </g> | ||||
| <!-- Robot --> | ||||
| <g id="node9" class="node"><title>Robot</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="439,-108 385,-108 385,-72 439,-72 439,-108"/> | ||||
| <text text-anchor="middle" x="412" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Robot</text> | ||||
| <g id="node10" class="node"><title>Robot</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="530,-108 476,-108 476,-72 530,-72 530,-108"/> | ||||
| <text text-anchor="middle" x="503" y="-87.5" font-family="Sans" font-size="10.00" fill="#ffffff">Robot</text> | ||||
| </g> | ||||
| <!-- Motor --> | ||||
| <g id="node10" class="node"><title>Motor</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="439,-36 385,-36 385,-0 439,-0 439,-36"/> | ||||
| <text text-anchor="middle" x="412" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Motor</text> | ||||
| <g id="node11" class="node"><title>Motor</title> | ||||
| <polygon fill="#298029" stroke="#298029" points="530,-36 476,-36 476,-0 530,-0 530,-36"/> | ||||
| <text text-anchor="middle" x="503" y="-15.5" font-family="Sans" font-size="10.00" fill="#ffffff">Motor</text> | ||||
| </g> | ||||
| <!-- Robot->Motor --> | ||||
| <g id="edge7" class="edge"><title>Robot->Motor</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M412,-71.6966C412,-63.9827 412,-54.7125 412,-46.1124"/> | ||||
| <polygon fill="none" stroke="black" points="415.5,-46.1043 412,-36.1043 408.5,-46.1044 415.5,-46.1043"/> | ||||
| <g id="edge9" class="edge"><title>Robot->Motor</title> | ||||
| <path fill="none" stroke="black" stroke-dasharray="5,2" d="M503,-71.6966C503,-63.9827 503,-54.7125 503,-46.1124"/> | ||||
| <polygon fill="none" stroke="black" points="506.5,-46.1043 503,-36.1043 499.5,-46.1044 506.5,-46.1043"/> | ||||
| </g> | ||||
| </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.7 KiB | 
| @@ -32,6 +32,5 @@ digraph classes { | ||||
|     RyanteckRobot->Robot; | ||||
|     CamJamKitRobot->Robot; | ||||
|  | ||||
|     RGBLED->CompositeDevice; | ||||
|     Motor->CompositeDevice; | ||||
| } | ||||
|   | ||||
| Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 44 KiB | 
| @@ -4,25 +4,25 @@ | ||||
| <!-- Generated by graphviz version 2.36.0 (20140111.2315) | ||||
|  --> | ||||
| <!-- Title: classes Pages: 1 --> | ||||
| <svg width="603pt" height="476pt" | ||||
|  viewBox="0.00 0.00 603.00 476.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | ||||
| <svg width="587pt" height="476pt" | ||||
|  viewBox="0.00 0.00 587.00 476.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 472)"> | ||||
| <title>classes</title> | ||||
| <polygon fill="white" stroke="none" points="-4,4 -4,-472 599,-472 599,4 -4,4"/> | ||||
| <polygon fill="white" stroke="none" points="-4,4 -4,-472 583,-472 583,4 -4,4"/> | ||||
| <!-- Device --> | ||||
| <g id="node1" class="node"><title>Device</title> | ||||
| <polygon fill="#9ec6e0" stroke="#9ec6e0" points="485,-468 431,-468 431,-432 485,-432 485,-468"/> | ||||
| <text text-anchor="middle" x="458" y="-447.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text> | ||||
| <polygon fill="#9ec6e0" stroke="#9ec6e0" points="449,-468 395,-468 395,-432 449,-432 449,-468"/> | ||||
| <text text-anchor="middle" x="422" y="-447.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text> | ||||
| </g> | ||||
| <!-- CompositeDevice --> | ||||
| <g id="node2" class="node"><title>CompositeDevice</title> | ||||
| <polygon fill="#9ec6e0" stroke="#9ec6e0" points="508.5,-396 407.5,-396 407.5,-360 508.5,-360 508.5,-396"/> | ||||
| <text text-anchor="middle" x="458" y="-375.5" font-family="Sans" font-size="10.00" fill="#000000">CompositeDevice</text> | ||||
| <polygon fill="#9ec6e0" stroke="#9ec6e0" points="472.5,-396 371.5,-396 371.5,-360 472.5,-360 472.5,-396"/> | ||||
| <text text-anchor="middle" x="422" y="-375.5" font-family="Sans" font-size="10.00" fill="#000000">CompositeDevice</text> | ||||
| </g> | ||||
| <!-- CompositeDevice->Device --> | ||||
| <g id="edge1" class="edge"><title>CompositeDevice->Device</title> | ||||
| <path fill="none" stroke="black" d="M458,-396.303C458,-404.017 458,-413.288 458,-421.888"/> | ||||
| <polygon fill="black" stroke="black" points="454.5,-421.896 458,-431.896 461.5,-421.896 454.5,-421.896"/> | ||||
| <path fill="none" stroke="black" d="M422,-396.303C422,-404.017 422,-413.288 422,-421.888"/> | ||||
| <polygon fill="black" stroke="black" points="418.5,-421.896 422,-431.896 425.5,-421.896 418.5,-421.896"/> | ||||
| </g> | ||||
| <!-- CompositeOutputDevice --> | ||||
| <g id="node3" class="node"><title>CompositeOutputDevice</title> | ||||
| @@ -31,8 +31,8 @@ | ||||
| </g> | ||||
| <!-- CompositeOutputDevice->CompositeDevice --> | ||||
| <g id="edge2" class="edge"><title>CompositeOutputDevice->CompositeDevice</title> | ||||
| <path fill="none" stroke="black" d="M344.694,-324.124C365.018,-333.608 390.471,-345.487 412.102,-355.581"/> | ||||
| <polygon fill="black" stroke="black" points="410.883,-358.874 421.425,-359.932 413.843,-352.531 410.883,-358.874"/> | ||||
| <path fill="none" stroke="black" d="M335.888,-324.124C350.793,-333.276 369.329,-344.658 385.382,-354.515"/> | ||||
| <polygon fill="black" stroke="black" points="383.652,-357.56 394.005,-359.81 387.315,-351.595 383.652,-357.56"/> | ||||
| </g> | ||||
| <!-- LEDCollection --> | ||||
| <g id="node4" class="node"><title>LEDCollection</title> | ||||
| @@ -141,8 +141,8 @@ | ||||
| </g> | ||||
| <!-- Robot->CompositeDevice --> | ||||
| <g id="edge13" class="edge"><title>Robot->CompositeDevice</title> | ||||
| <path fill="none" stroke="black" d="M430.899,-324.303C434.997,-332.272 439.949,-341.9 444.493,-350.736"/> | ||||
| <polygon fill="black" stroke="black" points="441.517,-352.604 449.203,-359.896 447.742,-349.402 441.517,-352.604"/> | ||||
| <path fill="none" stroke="black" d="M422,-324.303C422,-332.017 422,-341.288 422,-349.888"/> | ||||
| <polygon fill="black" stroke="black" points="418.5,-349.896 422,-359.896 425.5,-349.896 418.5,-349.896"/> | ||||
| </g> | ||||
| <!-- RyanteckRobot --> | ||||
| <g id="node15" class="node"><title>RyanteckRobot</title> | ||||
| @@ -164,25 +164,15 @@ | ||||
| <path fill="none" stroke="black" d="M504.336,-252.124C490.216,-261.192 472.689,-272.448 457.439,-282.241"/> | ||||
| <polygon fill="black" stroke="black" points="455.29,-279.462 448.767,-287.81 459.073,-285.352 455.29,-279.462"/> | ||||
| </g> | ||||
| <!-- RGBLED --> | ||||
| <g id="node17" class="node"><title>RGBLED</title> | ||||
| <polygon fill="#2980b9" stroke="#2980b9" points="523,-324 467,-324 467,-288 523,-288 523,-324"/> | ||||
| <text text-anchor="middle" x="495" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">RGBLED</text> | ||||
| </g> | ||||
| <!-- RGBLED->CompositeDevice --> | ||||
| <g id="edge16" class="edge"><title>RGBLED->CompositeDevice</title> | ||||
| <path fill="none" stroke="black" d="M485.854,-324.303C481.597,-332.356 476.444,-342.106 471.734,-351.018"/> | ||||
| <polygon fill="black" stroke="black" points="468.62,-349.419 467.041,-359.896 474.808,-352.69 468.62,-349.419"/> | ||||
| </g> | ||||
| <!-- Motor --> | ||||
| <g id="node18" class="node"><title>Motor</title> | ||||
| <polygon fill="#2980b9" stroke="#2980b9" points="595,-324 541,-324 541,-288 595,-288 595,-324"/> | ||||
| <text text-anchor="middle" x="568" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">Motor</text> | ||||
| <g id="node17" class="node"><title>Motor</title> | ||||
| <polygon fill="#2980b9" stroke="#2980b9" points="521,-324 467,-324 467,-288 521,-288 521,-324"/> | ||||
| <text text-anchor="middle" x="494" y="-303.5" font-family="Sans" font-size="10.00" fill="#ffffff">Motor</text> | ||||
| </g> | ||||
| <!-- Motor->CompositeDevice --> | ||||
| <g id="edge17" class="edge"><title>Motor->CompositeDevice</title> | ||||
| <path fill="none" stroke="black" d="M541.091,-324.124C526.842,-333.192 509.154,-344.448 493.764,-354.241"/> | ||||
| <polygon fill="black" stroke="black" points="491.57,-351.489 485.012,-359.81 495.328,-357.394 491.57,-351.489"/> | ||||
| <g id="edge16" class="edge"><title>Motor->CompositeDevice</title> | ||||
| <path fill="none" stroke="black" d="M476.202,-324.303C467.396,-332.865 456.618,-343.344 446.999,-352.696"/> | ||||
| <polygon fill="black" stroke="black" points="444.323,-350.415 439.593,-359.896 449.203,-355.434 444.323,-350.415"/> | ||||
| </g> | ||||
| </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB | 
| @@ -30,5 +30,6 @@ digraph classes { | ||||
|     Buzzer->DigitalOutputDevice; | ||||
|     PWMOutputDevice->OutputDevice; | ||||
|     PWMLED->PWMOutputDevice; | ||||
|     RGBLED->Device; | ||||
| } | ||||
|  | ||||
|   | ||||
| Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 45 KiB | 
| @@ -11,8 +11,8 @@ | ||||
| <polygon fill="white" stroke="none" points="-4,4 -4,-328 717,-328 717,4 -4,4"/> | ||||
| <!-- Device --> | ||||
| <g id="node1" class="node"><title>Device</title> | ||||
| <polygon fill="#9ec6e0" stroke="#9ec6e0" points="499,-324 445,-324 445,-288 499,-288 499,-324"/> | ||||
| <text text-anchor="middle" x="472" y="-303.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text> | ||||
| <polygon fill="#9ec6e0" stroke="#9ec6e0" points="540,-324 486,-324 486,-288 540,-288 540,-324"/> | ||||
| <text text-anchor="middle" x="513" y="-303.5" font-family="Sans" font-size="10.00" fill="#000000">Device</text> | ||||
| </g> | ||||
| <!-- GPIODevice --> | ||||
| <g id="node2" class="node"><title>GPIODevice</title> | ||||
| @@ -21,8 +21,8 @@ | ||||
| </g> | ||||
| <!-- GPIODevice->Device --> | ||||
| <g id="edge1" class="edge"><title>GPIODevice->Device</title> | ||||
| <path fill="none" stroke="black" d="M472,-252.303C472,-260.017 472,-269.288 472,-277.888"/> | ||||
| <polygon fill="black" stroke="black" points="468.5,-277.896 472,-287.896 475.5,-277.896 468.5,-277.896"/> | ||||
| <path fill="none" stroke="black" d="M482.135,-252.303C486.852,-260.356 492.562,-270.106 497.782,-279.018"/> | ||||
| <polygon fill="black" stroke="black" points="494.908,-281.036 502.982,-287.896 500.948,-277.498 494.908,-281.036"/> | ||||
| </g> | ||||
| <!-- SmoothedInputDevice --> | ||||
| <g id="node3" class="node"><title>SmoothedInputDevice</title> | ||||
| @@ -164,5 +164,15 @@ | ||||
| <path fill="none" stroke="black" d="M661,-36.3034C661,-44.0173 661,-53.2875 661,-61.8876"/> | ||||
| <polygon fill="black" stroke="black" points="657.5,-61.8956 661,-71.8957 664.5,-61.8957 657.5,-61.8956"/> | ||||
| </g> | ||||
| <!-- RGBLED --> | ||||
| <g id="node17" class="node"><title>RGBLED</title> | ||||
| <polygon fill="#2980b9" stroke="#2980b9" points="582,-252 526,-252 526,-216 582,-216 582,-252"/> | ||||
| <text text-anchor="middle" x="554" y="-231.5" font-family="Sans" font-size="10.00" fill="#ffffff">RGBLED</text> | ||||
| </g> | ||||
| <!-- RGBLED->Device --> | ||||
| <g id="edge16" class="edge"><title>RGBLED->Device</title> | ||||
| <path fill="none" stroke="black" d="M543.865,-252.303C539.148,-260.356 533.438,-270.106 528.218,-279.018"/> | ||||
| <polygon fill="black" stroke="black" points="525.052,-277.498 523.018,-287.896 531.092,-281.036 525.052,-277.498"/> | ||||
| </g> | ||||
| </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB | 
| @@ -14,7 +14,7 @@ Table of Contents | ||||
|     api_boards | ||||
|     api_other | ||||
|     api_generic | ||||
|     api_source_tools | ||||
|     api_tools | ||||
|     api_pins | ||||
|     api_exc | ||||
|     changelog | ||||
|   | ||||
| @@ -121,19 +121,3 @@ from .other_devices import ( | ||||
|     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, | ||||
| ) | ||||
|   | ||||
| @@ -258,7 +258,7 @@ class LEDBoard(LEDCollection): | ||||
|             on_time, off_time, fade_in_time, fade_out_time, n, background | ||||
|         ) | ||||
|  | ||||
|     def _blink_device(self, on_time, off_time, fade_in_time, fade_out_time, n, fps=50): | ||||
|     def _blink_device(self, on_time, off_time, fade_in_time, fade_out_time, n, fps=25): | ||||
|         sequence = [] | ||||
|         if fade_in_time > 0: | ||||
|             sequence += [ | ||||
|   | ||||
| @@ -107,7 +107,13 @@ class GPIOMeta(type): | ||||
|                         try: | ||||
|                             old_close() | ||||
|                         finally: | ||||
|                             del cls._INSTANCES[key] | ||||
|                             try: | ||||
|                                 del cls._INSTANCES[key] | ||||
|                             except KeyError: | ||||
|                                 # If the _refs go negative (too many closes) | ||||
|                                 # just ignore the resulting KeyError here - | ||||
|                                 # it's already gone | ||||
|                                 pass | ||||
|                 self.close = close | ||||
|                 cls._INSTANCES[key] = weakref.proxy(self) | ||||
|         else: | ||||
|   | ||||
| @@ -374,7 +374,7 @@ class HoldMixin(EventsMixin): | ||||
|     @hold_time.setter | ||||
|     def hold_time(self, value): | ||||
|         if value < 0: | ||||
|             raise BadWaitTime('source_delay must be 0 or greater') | ||||
|             raise BadWaitTime('hold_time must be 0 or greater') | ||||
|         self._hold_time = float(value) | ||||
|  | ||||
|     @property | ||||
| @@ -403,8 +403,8 @@ class HoldMixin(EventsMixin): | ||||
|         The length of time (in seconds) that the device has been held for. | ||||
|         This is counted from the first execution of the :attr:`when_held` event | ||||
|         rather than when the device activated, in contrast to | ||||
|         :attr:`active_time`. If the device is not currently held, this is | ||||
|         ``None``. | ||||
|         :attr:`~EventsMixin.active_time`. If the device is not currently held, | ||||
|         this is ``None``. | ||||
|         """ | ||||
|         if self._held_from is not None: | ||||
|             return time() - self._held_from | ||||
|   | ||||
| @@ -19,8 +19,8 @@ class OutputDevice(SourceMixin, GPIODevice): | ||||
|     Represents a generic GPIO output device. | ||||
|  | ||||
|     This class extends :class:`GPIODevice` to add facilities common to GPIO | ||||
|     output devices: an :meth:`on` method to switch the device on, and a | ||||
|     corresponding :meth:`off` method. | ||||
|     output devices: an :meth:`on` method to switch the device on, a | ||||
|     corresponding :meth:`off` method, and a :meth:`toggle` method. | ||||
|  | ||||
|     :param int pin: | ||||
|         The GPIO pin (in BCM numbering) that the device is connected to. If | ||||
| @@ -123,10 +123,9 @@ class DigitalOutputDevice(OutputDevice): | ||||
|     """ | ||||
|     Represents a generic output device with typical on/off behaviour. | ||||
|  | ||||
|     This class extends :class:`OutputDevice` with a :meth:`toggle` method to | ||||
|     switch the device between its on and off states, and a :meth:`blink` method | ||||
|     which uses an optional background thread to handle toggling the device | ||||
|     state without further interaction. | ||||
|     This class extends :class:`OutputDevice` with a :meth:`blink` method which | ||||
|     uses an optional background thread to handle toggling the device state | ||||
|     without further interaction. | ||||
|     """ | ||||
|     def __init__(self, pin=None, active_high=True, initial_value=False): | ||||
|         self._blink_thread = None | ||||
| @@ -458,7 +457,7 @@ class PWMOutputDevice(OutputDevice): | ||||
|             self._blink_thread = None | ||||
|  | ||||
|     def _blink_device( | ||||
|             self, on_time, off_time, fade_in_time, fade_out_time, n, fps=50): | ||||
|             self, on_time, off_time, fade_in_time, fade_out_time, n, fps=25): | ||||
|         sequence = [] | ||||
|         if fade_in_time > 0: | ||||
|             sequence += [ | ||||
| @@ -686,7 +685,7 @@ class RGBLED(SourceMixin, Device): | ||||
|  | ||||
|     def _blink_device( | ||||
|             self, on_time, off_time, fade_in_time, fade_out_time, on_color, | ||||
|             off_color, n, fps=50): | ||||
|             off_color, n, fps=25): | ||||
|         # Define some simple lambdas to perform linear interpolation between | ||||
|         # off_color and on_color | ||||
|         lerp = lambda t, fade_in: tuple( | ||||
|   | ||||
| @@ -159,7 +159,7 @@ class MockPin(Pin): | ||||
|         # that's about all we can reasonably expect in a non-realtime | ||||
|         # environment on a Pi 1) | ||||
|         for actual, expected in zip(self.states, expected_states): | ||||
|             assert isclose(actual.timestamp, expected[0], rel_tol=0.01, abs_tol=0.01) | ||||
|             assert isclose(actual.timestamp, expected[0], rel_tol=0.05, abs_tol=0.05) | ||||
|             assert isclose(actual.state, expected[1]) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -31,84 +31,84 @@ def test_composite_output_on_off(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     device = CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) | ||||
|     device.on() | ||||
|     assert all((pin1.state, pin2.state, pin3.state)) | ||||
|     device.off() | ||||
|     assert not any((pin1.state, pin2.state, pin3.state)) | ||||
|     with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device: | ||||
|         device.on() | ||||
|         assert all((pin1.state, pin2.state, pin3.state)) | ||||
|         device.off() | ||||
|         assert not any((pin1.state, pin2.state, pin3.state)) | ||||
|  | ||||
| def test_composite_output_toggle(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     device = CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) | ||||
|     device.toggle() | ||||
|     assert all((pin1.state, pin2.state, pin3.state)) | ||||
|     device[0].off() | ||||
|     device.toggle() | ||||
|     assert pin1.state | ||||
|     assert not pin2.state | ||||
|     assert not pin3.state | ||||
|     with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device: | ||||
|         device.toggle() | ||||
|         assert all((pin1.state, pin2.state, pin3.state)) | ||||
|         device[0].off() | ||||
|         device.toggle() | ||||
|         assert pin1.state | ||||
|         assert not pin2.state | ||||
|         assert not pin3.state | ||||
|  | ||||
| def test_composite_output_value(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     device = CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) | ||||
|     assert device.value == (0, 0, 0) | ||||
|     device.toggle() | ||||
|     assert device.value == (1, 1, 1) | ||||
|     device.value = (1, 0, 1) | ||||
|     assert device[0].is_active | ||||
|     assert not device[1].is_active | ||||
|     assert device[2].is_active | ||||
|     with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device: | ||||
|         assert device.value == (0, 0, 0) | ||||
|         device.toggle() | ||||
|         assert device.value == (1, 1, 1) | ||||
|         device.value = (1, 0, 1) | ||||
|         assert device[0].is_active | ||||
|         assert not device[1].is_active | ||||
|         assert device[2].is_active | ||||
|  | ||||
| def test_led_board_on_off(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     board = LEDBoard(pin1, pin2, foo=pin3) | ||||
|     assert isinstance(board[0], LED) | ||||
|     assert isinstance(board[1], LED) | ||||
|     assert isinstance(board[2], LED) | ||||
|     board.on() | ||||
|     assert all((pin1.state, pin2.state, pin3.state)) | ||||
|     board.off() | ||||
|     assert not any((pin1.state, pin2.state, pin3.state)) | ||||
|     board[0].on() | ||||
|     assert board.value == (1, 0, 0) | ||||
|     assert pin1.state | ||||
|     assert not pin2.state | ||||
|     assert not pin3.state | ||||
|     board.toggle() | ||||
|     assert board.value == (0, 1, 1) | ||||
|     assert not pin1.state | ||||
|     assert pin2.state | ||||
|     assert pin3.state | ||||
|     with LEDBoard(pin1, pin2, foo=pin3) as board: | ||||
|         assert isinstance(board[0], LED) | ||||
|         assert isinstance(board[1], LED) | ||||
|         assert isinstance(board[2], LED) | ||||
|         board.on() | ||||
|         assert all((pin1.state, pin2.state, pin3.state)) | ||||
|         board.off() | ||||
|         assert not any((pin1.state, pin2.state, pin3.state)) | ||||
|         board[0].on() | ||||
|         assert board.value == (1, 0, 0) | ||||
|         assert pin1.state | ||||
|         assert not pin2.state | ||||
|         assert not pin3.state | ||||
|         board.toggle() | ||||
|         assert board.value == (0, 1, 1) | ||||
|         assert not pin1.state | ||||
|         assert pin2.state | ||||
|         assert pin3.state | ||||
|  | ||||
| def test_led_board_nested(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     board = LEDBoard(pin1, LEDBoard(pin2, pin3)) | ||||
|     assert list(led.pin for led in board.leds) == [pin1, pin2, pin3] | ||||
|     assert board.value == (0, (0, 0)) | ||||
|     board.value = (1, (0, 1)) | ||||
|     assert pin1.state | ||||
|     assert not pin2.state | ||||
|     assert pin3.state | ||||
|     with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: | ||||
|         assert list(led.pin for led in board.leds) == [pin1, pin2, pin3] | ||||
|         assert board.value == (0, (0, 0)) | ||||
|         board.value = (1, (0, 1)) | ||||
|         assert pin1.state | ||||
|         assert not pin2.state | ||||
|         assert pin3.state | ||||
|  | ||||
| def test_led_board_bad_blink(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     board = LEDBoard(pin1, LEDBoard(pin2, pin3)) | ||||
|     with pytest.raises(ValueError): | ||||
|         board.blink(fade_in_time=1, fade_out_time=1) | ||||
|     with pytest.raises(ValueError): | ||||
|         board.blink(fade_out_time=1) | ||||
|     with pytest.raises(ValueError): | ||||
|         board.pulse() | ||||
|     with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: | ||||
|         with pytest.raises(ValueError): | ||||
|             board.blink(fade_in_time=1, fade_out_time=1) | ||||
|         with pytest.raises(ValueError): | ||||
|             board.blink(fade_out_time=1) | ||||
|         with pytest.raises(ValueError): | ||||
|             board.pulse() | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| @@ -116,19 +116,19 @@ def test_led_board_blink_background(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     board = LEDBoard(pin1, LEDBoard(pin2, pin3)) | ||||
|     board.blink(0.1, 0.1, n=2) | ||||
|     board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test | ||||
|     test = [ | ||||
|         (0.0, False), | ||||
|         (0.0, True), | ||||
|         (0.1, False), | ||||
|         (0.1, True), | ||||
|         (0.1, False) | ||||
|         ] | ||||
|     pin1.assert_states_and_times(test) | ||||
|     pin2.assert_states_and_times(test) | ||||
|     pin3.assert_states_and_times(test) | ||||
|     with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: | ||||
|         board.blink(0.1, 0.1, n=2) | ||||
|         board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test | ||||
|         test = [ | ||||
|             (0.0, False), | ||||
|             (0.0, True), | ||||
|             (0.1, False), | ||||
|             (0.1, True), | ||||
|             (0.1, False) | ||||
|             ] | ||||
|         pin1.assert_states_and_times(test) | ||||
|         pin2.assert_states_and_times(test) | ||||
|         pin3.assert_states_and_times(test) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| @@ -136,18 +136,18 @@ def test_led_board_blink_foreground(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     board = LEDBoard(pin1, LEDBoard(pin2, pin3)) | ||||
|     board.blink(0.1, 0.1, n=2, background=False) | ||||
|     test = [ | ||||
|         (0.0, False), | ||||
|         (0.0, True), | ||||
|         (0.1, False), | ||||
|         (0.1, True), | ||||
|         (0.1, False) | ||||
|         ] | ||||
|     pin1.assert_states_and_times(test) | ||||
|     pin2.assert_states_and_times(test) | ||||
|     pin3.assert_states_and_times(test) | ||||
|     with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: | ||||
|         board.blink(0.1, 0.1, n=2, background=False) | ||||
|         test = [ | ||||
|             (0.0, False), | ||||
|             (0.0, True), | ||||
|             (0.1, False), | ||||
|             (0.1, True), | ||||
|             (0.1, False) | ||||
|             ] | ||||
|         pin1.assert_states_and_times(test) | ||||
|         pin2.assert_states_and_times(test) | ||||
|         pin3.assert_states_and_times(test) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| @@ -155,24 +155,24 @@ def test_led_board_blink_control(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     board = LEDBoard(pin1, LEDBoard(pin2, pin3)) | ||||
|     board.blink(0.1, 0.1, n=2) | ||||
|     # make sure the blink thread's started | ||||
|     while not board._blink_leds: | ||||
|         sleep(0.00001) | ||||
|     board[1][0].off() # immediately take over the second LED | ||||
|     board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test | ||||
|     test = [ | ||||
|         (0.0, False), | ||||
|         (0.0, True), | ||||
|         (0.1, False), | ||||
|         (0.1, True), | ||||
|         (0.1, False) | ||||
|         ] | ||||
|     pin1.assert_states_and_times(test) | ||||
|     pin3.assert_states_and_times(test) | ||||
|     print(pin2.states) | ||||
|     pin2.assert_states_and_times([(0.0, False), (0.0, True), (0.0, False)]) | ||||
|     with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: | ||||
|         board.blink(0.1, 0.1, n=2) | ||||
|         # make sure the blink thread's started | ||||
|         while not board._blink_leds: | ||||
|             sleep(0.00001) | ||||
|         board[1][0].off() # immediately take over the second LED | ||||
|         board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test | ||||
|         test = [ | ||||
|             (0.0, False), | ||||
|             (0.0, True), | ||||
|             (0.1, False), | ||||
|             (0.1, True), | ||||
|             (0.1, False) | ||||
|             ] | ||||
|         pin1.assert_states_and_times(test) | ||||
|         pin3.assert_states_and_times(test) | ||||
|         print(pin2.states) | ||||
|         pin2.assert_states_and_times([(0.0, False), (0.0, True), (0.0, False)]) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| @@ -180,21 +180,21 @@ def test_led_board_blink_take_over(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     board = LEDBoard(pin1, LEDBoard(pin2, pin3)) | ||||
|     board[1].blink(0.1, 0.1, n=2) | ||||
|     board.blink(0.1, 0.1, n=2) # immediately take over blinking | ||||
|     board[1]._blink_thread.join() | ||||
|     board._blink_thread.join() | ||||
|     test = [ | ||||
|         (0.0, False), | ||||
|         (0.0, True), | ||||
|         (0.1, False), | ||||
|         (0.1, True), | ||||
|         (0.1, False) | ||||
|         ] | ||||
|     pin1.assert_states_and_times(test) | ||||
|     pin2.assert_states_and_times(test) | ||||
|     pin3.assert_states_and_times(test) | ||||
|     with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: | ||||
|         board[1].blink(0.1, 0.1, n=2) | ||||
|         board.blink(0.1, 0.1, n=2) # immediately take over blinking | ||||
|         board[1]._blink_thread.join() | ||||
|         board._blink_thread.join() | ||||
|         test = [ | ||||
|             (0.0, False), | ||||
|             (0.0, True), | ||||
|             (0.1, False), | ||||
|             (0.1, True), | ||||
|             (0.1, False) | ||||
|             ] | ||||
|         pin1.assert_states_and_times(test) | ||||
|         pin2.assert_states_and_times(test) | ||||
|         pin3.assert_states_and_times(test) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| @@ -202,47 +202,47 @@ def test_led_board_blink_control_all(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     board = LEDBoard(pin1, LEDBoard(pin2, pin3)) | ||||
|     board.blink(0.1, 0.1, n=2) | ||||
|     # make sure the blink thread's started | ||||
|     while not board._blink_leds: | ||||
|         sleep(0.00001) | ||||
|     board[0].off() # immediately take over all LEDs | ||||
|     board[1][0].off() | ||||
|     board[1][1].off() | ||||
|     board._blink_thread.join() # blink should terminate here anyway | ||||
|     test = [ | ||||
|         (0.0, False), | ||||
|         (0.0, True), | ||||
|         (0.0, False), | ||||
|         ] | ||||
|     pin1.assert_states_and_times(test) | ||||
|     pin2.assert_states_and_times(test) | ||||
|     pin3.assert_states_and_times(test) | ||||
|     with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: | ||||
|         board.blink(0.1, 0.1, n=2) | ||||
|         # make sure the blink thread's started | ||||
|         while not board._blink_leds: | ||||
|             sleep(0.00001) | ||||
|         board[0].off() # immediately take over all LEDs | ||||
|         board[1][0].off() | ||||
|         board[1][1].off() | ||||
|         board._blink_thread.join() # blink should terminate here anyway | ||||
|         test = [ | ||||
|             (0.0, False), | ||||
|             (0.0, True), | ||||
|             (0.0, False), | ||||
|             ] | ||||
|         pin1.assert_states_and_times(test) | ||||
|         pin2.assert_states_and_times(test) | ||||
|         pin3.assert_states_and_times(test) | ||||
|  | ||||
| def test_led_board_blink_interrupt_on(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     board = LEDBoard(pin1, LEDBoard(pin2, pin3)) | ||||
|     board.blink(1, 0.1) | ||||
|     sleep(0.2) | ||||
|     board.off() # should interrupt while on | ||||
|     pin1.assert_states([False, True, False]) | ||||
|     pin2.assert_states([False, True, False]) | ||||
|     pin3.assert_states([False, True, False]) | ||||
|     with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: | ||||
|         board.blink(1, 0.1) | ||||
|         sleep(0.2) | ||||
|         board.off() # should interrupt while on | ||||
|         pin1.assert_states([False, True, False]) | ||||
|         pin2.assert_states([False, True, False]) | ||||
|         pin3.assert_states([False, True, False]) | ||||
|  | ||||
| def test_led_board_blink_interrupt_off(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     board = LEDBoard(pin1, LEDBoard(pin2, pin3)) | ||||
|     board.blink(0.1, 1) | ||||
|     sleep(0.2) | ||||
|     board.off() # should interrupt while off | ||||
|     pin1.assert_states([False, True, False]) | ||||
|     pin2.assert_states([False, True, False]) | ||||
|     pin3.assert_states([False, True, False]) | ||||
|     with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: | ||||
|         board.blink(0.1, 1) | ||||
|         sleep(0.2) | ||||
|         board.off() # should interrupt while off | ||||
|         pin1.assert_states([False, True, False]) | ||||
|         pin2.assert_states([False, True, False]) | ||||
|         pin3.assert_states([False, True, False]) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| @@ -250,76 +250,76 @@ def test_led_board_fade_background(): | ||||
|     pin1 = MockPWMPin(2) | ||||
|     pin2 = MockPWMPin(3) | ||||
|     pin3 = MockPWMPin(4) | ||||
|     board = LEDBoard(pin1, LEDBoard(pin2, pin3, pwm=True), pwm=True) | ||||
|     board.blink(0, 0, 0.1, 0.1, n=2) | ||||
|     board._blink_thread.join() | ||||
|     test = [ | ||||
|         (0.0, 0), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 1), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 1), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0), | ||||
|         ] | ||||
|     pin1.assert_states_and_times(test) | ||||
|     pin2.assert_states_and_times(test) | ||||
|     pin3.assert_states_and_times(test) | ||||
|     with LEDBoard(pin1, LEDBoard(pin2, pin3, pwm=True), pwm=True) as board: | ||||
|         board.blink(0, 0, 0.2, 0.2, n=2) | ||||
|         board._blink_thread.join() | ||||
|         test = [ | ||||
|             (0.0, 0), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 1), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 1), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0), | ||||
|             ] | ||||
|         pin1.assert_states_and_times(test) | ||||
|         pin2.assert_states_and_times(test) | ||||
|         pin3.assert_states_and_times(test) | ||||
|  | ||||
| def test_led_bar_graph_value(): | ||||
|     pin1 = MockPin(2) | ||||
|     pin2 = MockPin(3) | ||||
|     pin3 = MockPin(4) | ||||
|     graph = LEDBarGraph(pin1, pin2, pin3) | ||||
|     graph.value = 0 | ||||
|     assert not any((pin1.state, pin2.state, pin3.state)) | ||||
|     graph.value = 1 | ||||
|     assert all((pin1.state, pin2.state, pin3.state)) | ||||
|     graph.value = 1/3 | ||||
|     assert pin1.state and not (pin2.state or pin3.state) | ||||
|     graph.value = -1/3 | ||||
|     assert pin3.state and not (pin1.state or pin2.state) | ||||
|     pin1.state = True | ||||
|     pin2.state = True | ||||
|     assert graph.value == 1 | ||||
|     pin3.state = False | ||||
|     assert graph.value == 2/3 | ||||
|     pin3.state = True | ||||
|     pin1.state = False | ||||
|     assert graph.value == -2/3 | ||||
|     with LEDBarGraph(pin1, pin2, pin3) as graph: | ||||
|         graph.value = 0 | ||||
|         assert not any((pin1.state, pin2.state, pin3.state)) | ||||
|         graph.value = 1 | ||||
|         assert all((pin1.state, pin2.state, pin3.state)) | ||||
|         graph.value = 1/3 | ||||
|         assert pin1.state and not (pin2.state or pin3.state) | ||||
|         graph.value = -1/3 | ||||
|         assert pin3.state and not (pin1.state or pin2.state) | ||||
|         pin1.state = True | ||||
|         pin2.state = True | ||||
|         assert graph.value == 1 | ||||
|         pin3.state = False | ||||
|         assert graph.value == 2/3 | ||||
|         pin3.state = True | ||||
|         pin1.state = False | ||||
|         assert graph.value == -2/3 | ||||
|  | ||||
| def test_led_bar_graph_pwm_value(): | ||||
|     pin1 = MockPWMPin(2) | ||||
|     pin2 = MockPWMPin(3) | ||||
|     pin3 = MockPWMPin(4) | ||||
|     graph = LEDBarGraph(pin1, pin2, pin3, pwm=True) | ||||
|     graph.value = 0 | ||||
|     assert not any((pin1.state, pin2.state, pin3.state)) | ||||
|     graph.value = 1 | ||||
|     assert all((pin1.state, pin2.state, pin3.state)) | ||||
|     graph.value = 1/3 | ||||
|     assert pin1.state and not (pin2.state or pin3.state) | ||||
|     graph.value = -1/3 | ||||
|     assert pin3.state and not (pin1.state or pin2.state) | ||||
|     graph.value = 1/2 | ||||
|     assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0) | ||||
|     pin1.state = 0 | ||||
|     pin3.state = 1 | ||||
|     assert graph.value == -1/2 | ||||
|     with LEDBarGraph(pin1, pin2, pin3, pwm=True) as graph: | ||||
|         graph.value = 0 | ||||
|         assert not any((pin1.state, pin2.state, pin3.state)) | ||||
|         graph.value = 1 | ||||
|         assert all((pin1.state, pin2.state, pin3.state)) | ||||
|         graph.value = 1/3 | ||||
|         assert pin1.state and not (pin2.state or pin3.state) | ||||
|         graph.value = -1/3 | ||||
|         assert pin3.state and not (pin1.state or pin2.state) | ||||
|         graph.value = 1/2 | ||||
|         assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0) | ||||
|         pin1.state = 0 | ||||
|         pin3.state = 1 | ||||
|         assert graph.value == -1/2 | ||||
|  | ||||
| def test_led_bar_graph_bad_init(): | ||||
|     pin1 = MockPin(2) | ||||
| @@ -330,26 +330,26 @@ def test_led_bar_graph_bad_init(): | ||||
|  | ||||
| def test_pi_liter(): | ||||
|     pins = [MockPin(n) for n in (4, 17, 27, 18, 22, 23, 24, 25)] | ||||
|     board = PiLiter() | ||||
|     assert [device.pin for device in board] == pins | ||||
|     with PiLiter() as board: | ||||
|         assert [device.pin for device in board] == pins | ||||
|  | ||||
| def test_pi_liter_graph(): | ||||
|     pins = [MockPin(n) for n in (4, 17, 27, 18, 22, 23, 24, 25)] | ||||
|     board = PiLiterBarGraph() | ||||
|     board.value = 0.5 | ||||
|     assert [pin.state for pin in pins] == [1, 1, 1, 1, 0, 0, 0, 0] | ||||
|     pins[4].state = 1 | ||||
|     assert board.value == 5/8 | ||||
|     with PiLiterBarGraph() as board: | ||||
|         board.value = 0.5 | ||||
|         assert [pin.state for pin in pins] == [1, 1, 1, 1, 0, 0, 0, 0] | ||||
|         pins[4].state = 1 | ||||
|         assert board.value == 5/8 | ||||
|  | ||||
| def test_traffic_lights(): | ||||
|     red_pin = MockPin(2) | ||||
|     amber_pin = MockPin(3) | ||||
|     green_pin = MockPin(4) | ||||
|     board = TrafficLights(red_pin, amber_pin, green_pin) | ||||
|     board.red.on() | ||||
|     assert red_pin.state | ||||
|     assert not amber_pin.state | ||||
|     assert not green_pin.state | ||||
|     with TrafficLights(red_pin, amber_pin, green_pin) as board: | ||||
|         board.red.on() | ||||
|         assert red_pin.state | ||||
|         assert not amber_pin.state | ||||
|         assert not green_pin.state | ||||
|  | ||||
| def test_traffic_lights_bad_init(): | ||||
|     with pytest.raises(ValueError): | ||||
| @@ -357,13 +357,13 @@ def test_traffic_lights_bad_init(): | ||||
|  | ||||
| def test_pi_traffic(): | ||||
|     pins = [MockPin(n) for n in (9, 10, 11)] | ||||
|     board = PiTraffic() | ||||
|     assert [device.pin for device in board] == pins | ||||
|     with PiTraffic() as board: | ||||
|         assert [device.pin for device in board] == pins | ||||
|  | ||||
| def test_snow_pi(): | ||||
|     pins = [MockPin(n) for n in (23, 24, 25, 17, 18, 22, 7, 8, 9)] | ||||
|     board = SnowPi() | ||||
|     assert [device.pin for device in board.leds] == pins | ||||
|     with SnowPi() as board: | ||||
|         assert [device.pin for device in board.leds] == pins | ||||
|  | ||||
| def test_traffic_lights_buzzer(): | ||||
|     red_pin = MockPin(2) | ||||
| @@ -371,59 +371,59 @@ def test_traffic_lights_buzzer(): | ||||
|     green_pin = MockPin(4) | ||||
|     buzzer_pin = MockPin(5) | ||||
|     button_pin = MockPin(6) | ||||
|     board = TrafficLightsBuzzer( | ||||
|     with TrafficLightsBuzzer( | ||||
|             TrafficLights(red_pin, amber_pin, green_pin), | ||||
|             Buzzer(buzzer_pin), | ||||
|             Button(button_pin)) | ||||
|     board.lights.red.on() | ||||
|     board.buzzer.on() | ||||
|     assert red_pin.state | ||||
|     assert not amber_pin.state | ||||
|     assert not green_pin.state | ||||
|     assert buzzer_pin.state | ||||
|     button_pin.drive_low() | ||||
|     assert board.button.is_active | ||||
|             Button(button_pin)) as board: | ||||
|         board.lights.red.on() | ||||
|         board.buzzer.on() | ||||
|         assert red_pin.state | ||||
|         assert not amber_pin.state | ||||
|         assert not green_pin.state | ||||
|         assert buzzer_pin.state | ||||
|         button_pin.drive_low() | ||||
|         assert board.button.is_active | ||||
|  | ||||
| def test_fish_dish(): | ||||
|     pins = [MockPin(n) for n in (9, 22, 4, 8, 7)] | ||||
|     board = FishDish() | ||||
|     assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins | ||||
|     with FishDish() as board: | ||||
|         assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins | ||||
|  | ||||
| def test_traffic_hat(): | ||||
|     pins = [MockPin(n) for n in (24, 23, 22, 5, 25)] | ||||
|     board = TrafficHat() | ||||
|     assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins | ||||
|     with TrafficHat() as board: | ||||
|         assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins | ||||
|  | ||||
| def test_robot(): | ||||
|     pins = [MockPWMPin(n) for n in (2, 3, 4, 5)] | ||||
|     robot = Robot((2, 3), (4, 5)) | ||||
|     assert ( | ||||
|         [device.pin for device in robot.left_motor] + | ||||
|         [device.pin for device in robot.right_motor]) == pins | ||||
|     robot.forward() | ||||
|     assert [pin.state for pin in pins] == [1, 0, 1, 0] | ||||
|     robot.backward() | ||||
|     assert [pin.state for pin in pins] == [0, 1, 0, 1] | ||||
|     robot.forward(0.5) | ||||
|     assert [pin.state for pin in pins] == [0.5, 0, 0.5, 0] | ||||
|     robot.left() | ||||
|     assert [pin.state for pin in pins] == [0, 1, 1, 0] | ||||
|     robot.right() | ||||
|     assert [pin.state for pin in pins] == [1, 0, 0, 1] | ||||
|     robot.reverse() | ||||
|     assert [pin.state for pin in pins] == [0, 1, 1, 0] | ||||
|     robot.stop() | ||||
|     assert [pin.state for pin in pins] == [0, 0, 0, 0] | ||||
|     with Robot((2, 3), (4, 5)) as robot: | ||||
|         assert ( | ||||
|             [device.pin for device in robot.left_motor] + | ||||
|             [device.pin for device in robot.right_motor]) == pins | ||||
|         robot.forward() | ||||
|         assert [pin.state for pin in pins] == [1, 0, 1, 0] | ||||
|         robot.backward() | ||||
|         assert [pin.state for pin in pins] == [0, 1, 0, 1] | ||||
|         robot.forward(0.5) | ||||
|         assert [pin.state for pin in pins] == [0.5, 0, 0.5, 0] | ||||
|         robot.left() | ||||
|         assert [pin.state for pin in pins] == [0, 1, 1, 0] | ||||
|         robot.right() | ||||
|         assert [pin.state for pin in pins] == [1, 0, 0, 1] | ||||
|         robot.reverse() | ||||
|         assert [pin.state for pin in pins] == [0, 1, 1, 0] | ||||
|         robot.stop() | ||||
|         assert [pin.state for pin in pins] == [0, 0, 0, 0] | ||||
|  | ||||
| def test_ryanteck_robot(): | ||||
|     pins = [MockPWMPin(n) for n in (17, 18, 22, 23)] | ||||
|     board = RyanteckRobot() | ||||
|     assert [device.pin for motor in board for device in motor] == pins | ||||
|     with RyanteckRobot() as board: | ||||
|         assert [device.pin for motor in board for device in motor] == pins | ||||
|  | ||||
| def test_camjam_kit_robot(): | ||||
|     pins = [MockPWMPin(n) for n in (9, 10, 7, 8)] | ||||
|     board = CamJamKitRobot() | ||||
|     assert [device.pin for motor in board for device in motor] == pins | ||||
|     with CamJamKitRobot() as board: | ||||
|         assert [device.pin for motor in board for device in motor] == pins | ||||
|  | ||||
| def test_energenie_bad_init(): | ||||
|     with pytest.raises(ValueError): | ||||
| @@ -433,26 +433,26 @@ def test_energenie_bad_init(): | ||||
|  | ||||
| def test_energenie(): | ||||
|     pins = [MockPin(n) for n in (17, 22, 23, 27, 24, 25)] | ||||
|     device1 = Energenie(1, initial_value=True) | ||||
|     device2 = Energenie(2, initial_value=False) | ||||
|     assert device1.value | ||||
|     assert not device2.value | ||||
|     [pin.clear_states() for pin in pins] | ||||
|     device1.on() | ||||
|     assert device1.value | ||||
|     pins[0].assert_states_and_times([(0.0, False), (0.0, True)]) | ||||
|     pins[1].assert_states_and_times([(0.0, True), (0.0, True)]) | ||||
|     pins[2].assert_states_and_times([(0.0, True), (0.0, True)]) | ||||
|     pins[3].assert_states_and_times([(0.0, False), (0.0, True)]) | ||||
|     pins[4].assert_states_and_times([(0.0, False)]) | ||||
|     pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)]) | ||||
|     [pin.clear_states() for pin in pins] | ||||
|     device2.on() | ||||
|     assert device2.value | ||||
|     pins[0].assert_states_and_times([(0.0, True), (0.0, False)]) | ||||
|     pins[1].assert_states_and_times([(0.0, True), (0.0, True)]) | ||||
|     pins[2].assert_states_and_times([(0.0, True), (0.0, True)]) | ||||
|     pins[3].assert_states_and_times([(0.0, True), (0.0, True)]) | ||||
|     pins[4].assert_states_and_times([(0.0, False)]) | ||||
|     pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)]) | ||||
|     with Energenie(1, initial_value=True) as device1, \ | ||||
|             Energenie(2, initial_value=False) as device2: | ||||
|         assert device1.value | ||||
|         assert not device2.value | ||||
|         [pin.clear_states() for pin in pins] | ||||
|         device1.on() | ||||
|         assert device1.value | ||||
|         pins[0].assert_states_and_times([(0.0, False), (0.0, True)]) | ||||
|         pins[1].assert_states_and_times([(0.0, True), (0.0, True)]) | ||||
|         pins[2].assert_states_and_times([(0.0, True), (0.0, True)]) | ||||
|         pins[3].assert_states_and_times([(0.0, False), (0.0, True)]) | ||||
|         pins[4].assert_states_and_times([(0.0, False)]) | ||||
|         pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)]) | ||||
|         [pin.clear_states() for pin in pins] | ||||
|         device2.on() | ||||
|         assert device2.value | ||||
|         pins[0].assert_states_and_times([(0.0, True), (0.0, False)]) | ||||
|         pins[1].assert_states_and_times([(0.0, True), (0.0, True)]) | ||||
|         pins[2].assert_states_and_times([(0.0, True), (0.0, True)]) | ||||
|         pins[3].assert_states_and_times([(0.0, True), (0.0, True)]) | ||||
|         pins[4].assert_states_and_times([(0.0, False)]) | ||||
|         pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)]) | ||||
|  | ||||
|   | ||||
| @@ -24,21 +24,22 @@ def test_device_no_pin(): | ||||
|  | ||||
| def test_device_init(): | ||||
|     pin = MockPin(2) | ||||
|     device = GPIODevice(pin) | ||||
|     assert not device.closed | ||||
|     assert device.pin == pin | ||||
|     with GPIODevice(pin) as device: | ||||
|         assert not device.closed | ||||
|         assert device.pin == pin | ||||
|  | ||||
| def test_device_init_twice_same_pin(): | ||||
|     pin = MockPin(2) | ||||
|     device = GPIODevice(pin) | ||||
|     with pytest.raises(GPIOPinInUse): | ||||
|         device2 = GPIODevice(pin) | ||||
|     with GPIODevice(pin) as device: | ||||
|         with pytest.raises(GPIOPinInUse): | ||||
|             device2 = GPIODevice(pin) | ||||
|  | ||||
| def test_device_init_twice_different_pin(): | ||||
|     pin = MockPin(2) | ||||
|     device = GPIODevice(pin) | ||||
|     pin2 = MockPin(3) | ||||
|     device2 = GPIODevice(pin2) | ||||
|     with GPIODevice(pin) as device: | ||||
|         with GPIODevice(pin2) as device2: | ||||
|             pass | ||||
|  | ||||
| def test_device_close(): | ||||
|     pin = MockPin(2) | ||||
| @@ -56,11 +57,12 @@ def test_device_reopen_same_pin(): | ||||
|     assert device2.pin == pin | ||||
|     assert device.closed | ||||
|     assert device.pin is None | ||||
|     device2.close() | ||||
|  | ||||
| def test_device_repr(): | ||||
|     pin = MockPin(2) | ||||
|     device = GPIODevice(pin) | ||||
|     assert repr(device) == '<gpiozero.GPIODevice object on pin %s, is_active=False>' % pin | ||||
|     with GPIODevice(pin) as device: | ||||
|         assert repr(device) == '<gpiozero.GPIODevice object on pin %s, is_active=False>' % pin | ||||
|  | ||||
| def test_device_repr_after_close(): | ||||
|     pin = MockPin(2) | ||||
| @@ -70,9 +72,9 @@ def test_device_repr_after_close(): | ||||
|  | ||||
| def test_device_unknown_attr(): | ||||
|     pin = MockPin(2) | ||||
|     device = GPIODevice(pin) | ||||
|     with pytest.raises(AttributeError): | ||||
|         device.foo = 1 | ||||
|     with GPIODevice(pin) as device: | ||||
|         with pytest.raises(AttributeError): | ||||
|             device.foo = 1 | ||||
|  | ||||
| def test_device_context_manager(): | ||||
|     pin = MockPin(2) | ||||
| @@ -81,35 +83,35 @@ def test_device_context_manager(): | ||||
|     assert device.closed | ||||
|  | ||||
| def test_composite_device_sequence(): | ||||
|     device = CompositeDevice( | ||||
|         InputDevice(MockPin(2)), | ||||
|         InputDevice(MockPin(3)) | ||||
|         ) | ||||
|     assert len(device) == 2 | ||||
|     assert device[0].pin.number == 2 | ||||
|     assert device[1].pin.number == 3 | ||||
|     assert device.tuple._fields == ('_0', '_1') | ||||
|     with CompositeDevice( | ||||
|             InputDevice(MockPin(2)), | ||||
|             InputDevice(MockPin(3)) | ||||
|             ) as device: | ||||
|         assert len(device) == 2 | ||||
|         assert device[0].pin.number == 2 | ||||
|         assert device[1].pin.number == 3 | ||||
|         assert device.tuple._fields == ('_0', '_1') | ||||
|  | ||||
| def test_composite_device_values(): | ||||
|     device = CompositeDevice( | ||||
|         InputDevice(MockPin(2)), | ||||
|         InputDevice(MockPin(3)) | ||||
|         ) | ||||
|     assert device.value == (0, 0) | ||||
|     assert not device.is_active | ||||
|     device[0].pin.drive_high() | ||||
|     assert device.value == (1, 0) | ||||
|     assert device.is_active | ||||
|     with CompositeDevice( | ||||
|             InputDevice(MockPin(2)), | ||||
|             InputDevice(MockPin(3)) | ||||
|             ) as device: | ||||
|         assert device.value == (0, 0) | ||||
|         assert not device.is_active | ||||
|         device[0].pin.drive_high() | ||||
|         assert device.value == (1, 0) | ||||
|         assert device.is_active | ||||
|  | ||||
| def test_composite_device_named(): | ||||
|     device = CompositeDevice( | ||||
|         foo=InputDevice(MockPin(2)), | ||||
|         bar=InputDevice(MockPin(3)), | ||||
|         _order=('foo', 'bar') | ||||
|         ) | ||||
|     assert device.tuple._fields == ('foo', 'bar') | ||||
|     assert device.value == (0, 0) | ||||
|     assert not device.is_active | ||||
|     with CompositeDevice( | ||||
|             foo=InputDevice(MockPin(2)), | ||||
|             bar=InputDevice(MockPin(3)), | ||||
|             _order=('foo', 'bar') | ||||
|             ) as device: | ||||
|         assert device.tuple._fields == ('foo', 'bar') | ||||
|         assert device.value == (0, 0) | ||||
|         assert not device.is_active | ||||
|  | ||||
| def test_composite_device_bad_init(): | ||||
|     with pytest.raises(ValueError): | ||||
|   | ||||
| @@ -25,131 +25,121 @@ def teardown_function(function): | ||||
|  | ||||
| def test_input_initial_values(): | ||||
|     pin = MockPin(2) | ||||
|     device = InputDevice(pin, pull_up=True) | ||||
|     assert pin.function == 'input' | ||||
|     assert pin.pull == 'up' | ||||
|     assert device.pull_up | ||||
|     device.close() | ||||
|     device = InputDevice(pin, pull_up=False) | ||||
|     assert pin.pull == 'down' | ||||
|     assert not device.pull_up | ||||
|     device.close() | ||||
|     with InputDevice(pin, pull_up=True) as device: | ||||
|         assert pin.function == 'input' | ||||
|         assert pin.pull == 'up' | ||||
|         assert device.pull_up | ||||
|         device.close() | ||||
|         device = InputDevice(pin, pull_up=False) | ||||
|         assert pin.pull == 'down' | ||||
|         assert not device.pull_up | ||||
|  | ||||
| def test_input_is_active(): | ||||
|     pin = MockPin(2) | ||||
|     device = InputDevice(pin, pull_up=True) | ||||
|     pin.drive_high() | ||||
|     assert not device.is_active | ||||
|     pin.drive_low() | ||||
|     assert device.is_active | ||||
|     with InputDevice(pin, pull_up=True) as device: | ||||
|         pin.drive_high() | ||||
|         assert not device.is_active | ||||
|         pin.drive_low() | ||||
|         assert device.is_active | ||||
|  | ||||
| def test_input_pulled_up(): | ||||
|     pin = MockPulledUpPin(2) | ||||
|     with pytest.raises(PinFixedPull): | ||||
|         device = InputDevice(pin, pull_up=False) | ||||
|         InputDevice(pin, pull_up=False) | ||||
|  | ||||
| def test_input_event_activated(): | ||||
|     event = Event() | ||||
|     pin = MockPin(2) | ||||
|     device = DigitalInputDevice(pin) | ||||
|     device.when_activated = lambda: event.set() | ||||
|     assert not event.wait(0) | ||||
|     pin.drive_high() | ||||
|     assert event.wait(0) | ||||
|     with DigitalInputDevice(pin) as device: | ||||
|         device.when_activated = lambda: event.set() | ||||
|         assert not event.wait(0) | ||||
|         pin.drive_high() | ||||
|         assert event.wait(0) | ||||
|  | ||||
| def test_input_event_deactivated(): | ||||
|     event = Event() | ||||
|     pin = MockPin(2) | ||||
|     device = DigitalInputDevice(pin) | ||||
|     device.when_deactivated = lambda: event.set() | ||||
|     assert not event.wait(0) | ||||
|     pin.drive_high() | ||||
|     assert not event.wait(0) | ||||
|     pin.drive_low() | ||||
|     assert event.wait(0) | ||||
|     with DigitalInputDevice(pin) as device: | ||||
|         device.when_deactivated = lambda: event.set() | ||||
|         assert not event.wait(0) | ||||
|         pin.drive_high() | ||||
|         assert not event.wait(0) | ||||
|         pin.drive_low() | ||||
|         assert event.wait(0) | ||||
|  | ||||
| def test_input_wait_active(): | ||||
|     pin = MockPin(2) | ||||
|     device = DigitalInputDevice(pin) | ||||
|     pin.drive_high() | ||||
|     assert device.wait_for_active(1) | ||||
|     assert not device.wait_for_inactive(0) | ||||
|     with DigitalInputDevice(pin) as device: | ||||
|         pin.drive_high() | ||||
|         assert device.wait_for_active(1) | ||||
|         assert not device.wait_for_inactive(0) | ||||
|  | ||||
| def test_input_wait_inactive(): | ||||
|     pin = MockPin(2) | ||||
|     device = DigitalInputDevice(pin) | ||||
|     assert device.wait_for_inactive(1) | ||||
|     assert not device.wait_for_active(0) | ||||
|     with DigitalInputDevice(pin) as device: | ||||
|         assert device.wait_for_inactive(1) | ||||
|         assert not device.wait_for_active(0) | ||||
|  | ||||
| def test_input_smoothed_attrib(): | ||||
|     pin = MockPin(2) | ||||
|     device = SmoothedInputDevice(pin, threshold=0.5, queue_len=5, partial=False) | ||||
|     assert device.threshold == 0.5 | ||||
|     assert device.queue_len == 5 | ||||
|     assert not device.partial | ||||
|     device._queue.start() | ||||
|     assert not device.is_active | ||||
|  | ||||
| def test_input_smoothed_silly(): | ||||
|     pin = MockPin(2) | ||||
|     with pytest.raises(InputDeviceError): | ||||
|         device = SmoothedInputDevice(pin, threshold=-1) | ||||
|     device = SmoothedInputDevice(pin) | ||||
|     del device._queue.stopping | ||||
|     with pytest.raises(AttributeError): | ||||
|         device.close() | ||||
|     with SmoothedInputDevice(pin, threshold=0.5, queue_len=5, partial=False) as device: | ||||
|         assert device.threshold == 0.5 | ||||
|         assert device.queue_len == 5 | ||||
|         assert not device.partial | ||||
|         device._queue.start() | ||||
|         assert not device.is_active | ||||
|  | ||||
| def test_input_smoothed_values(): | ||||
|     pin = MockPin(2) | ||||
|     device = SmoothedInputDevice(pin) | ||||
|     device._queue.start() | ||||
|     assert not device.is_active | ||||
|     pin.drive_high() | ||||
|     assert device.wait_for_active(1) | ||||
|     pin.drive_low() | ||||
|     assert device.wait_for_inactive(1) | ||||
|     with SmoothedInputDevice(pin) as device: | ||||
|         device._queue.start() | ||||
|         assert not device.is_active | ||||
|         pin.drive_high() | ||||
|         assert device.wait_for_active(1) | ||||
|         pin.drive_low() | ||||
|         assert device.wait_for_inactive(1) | ||||
|  | ||||
| def test_input_button(): | ||||
|     pin = MockPin(2) | ||||
|     button = Button(pin) | ||||
|     assert pin.pull == 'up' | ||||
|     assert not button.is_pressed | ||||
|     pin.drive_low() | ||||
|     assert button.is_pressed | ||||
|     assert button.wait_for_press(1) | ||||
|     pin.drive_high() | ||||
|     assert not button.is_pressed | ||||
|     assert button.wait_for_release(1) | ||||
|     with Button(pin) as button: | ||||
|         assert pin.pull == 'up' | ||||
|         assert not button.is_pressed | ||||
|         pin.drive_low() | ||||
|         assert button.is_pressed | ||||
|         assert button.wait_for_press(1) | ||||
|         pin.drive_high() | ||||
|         assert not button.is_pressed | ||||
|         assert button.wait_for_release(1) | ||||
|  | ||||
| def test_input_line_sensor(): | ||||
|     pin = MockPin(2) | ||||
|     sensor = LineSensor(pin) | ||||
|     pin.drive_low() # logic is inverted for line sensor | ||||
|     assert sensor.wait_for_line(1) | ||||
|     assert sensor.line_detected | ||||
|     pin.drive_high() | ||||
|     assert sensor.wait_for_no_line(1) | ||||
|     assert not sensor.line_detected | ||||
|     with LineSensor(pin) as sensor: | ||||
|         pin.drive_low() # logic is inverted for line sensor | ||||
|         assert sensor.wait_for_line(1) | ||||
|         assert sensor.line_detected | ||||
|         pin.drive_high() | ||||
|         assert sensor.wait_for_no_line(1) | ||||
|         assert not sensor.line_detected | ||||
|  | ||||
| def test_input_motion_sensor(): | ||||
|     pin = MockPin(2) | ||||
|     sensor = MotionSensor(pin) | ||||
|     pin.drive_high() | ||||
|     assert sensor.wait_for_motion(1) | ||||
|     assert sensor.motion_detected | ||||
|     pin.drive_low() | ||||
|     assert sensor.wait_for_no_motion(1) | ||||
|     assert not sensor.motion_detected | ||||
|     with MotionSensor(pin) as sensor: | ||||
|         pin.drive_high() | ||||
|         assert sensor.wait_for_motion(1) | ||||
|         assert sensor.motion_detected | ||||
|         pin.drive_low() | ||||
|         assert sensor.wait_for_no_motion(1) | ||||
|         assert not sensor.motion_detected | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| def test_input_light_sensor(): | ||||
|     pin = MockChargingPin(2) | ||||
|     sensor = LightSensor(pin) | ||||
|     pin.charge_time = 0.1 | ||||
|     assert sensor.wait_for_dark(1) | ||||
|     pin.charge_time = 0.0 | ||||
|     assert sensor.wait_for_light(1) | ||||
|     with LightSensor(pin) as sensor: | ||||
|         pin.charge_time = 0.1 | ||||
|         assert sensor.wait_for_dark(1) | ||||
|         pin.charge_time = 0.0 | ||||
|         assert sensor.wait_for_light(1) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| @@ -162,22 +152,22 @@ def test_input_distance_sensor(): | ||||
|         DistanceSensor(echo_pin, trig_pin, max_distance=-1) | ||||
|     # normal queue len is large (because the sensor is *really* jittery) but | ||||
|     # we want quick tests and we've got precisely controlled pins :) | ||||
|     sensor = DistanceSensor(echo_pin, trig_pin, queue_len=5, max_distance=1) | ||||
|     assert sensor.max_distance == 1 | ||||
|     assert sensor.trigger is trig_pin | ||||
|     assert sensor.echo is echo_pin | ||||
|     assert sensor.wait_for_out_of_range(1) | ||||
|     assert not sensor.in_range | ||||
|     assert sensor.distance == 1.0 # should be waay before max-distance so this should work | ||||
|     trig_pin.echo_time = 0.0 | ||||
|     assert sensor.wait_for_in_range(1) | ||||
|     assert sensor.in_range | ||||
|     assert sensor.distance < sensor.threshold_distance # depending on speed of machine, may not reach 0 here | ||||
|     sensor.threshold_distance = 0.1 | ||||
|     assert sensor.threshold_distance == 0.1 | ||||
|     with pytest.raises(ValueError): | ||||
|         sensor.max_distance = -1 | ||||
|     sensor.max_distance = 20 | ||||
|     assert sensor.max_distance == 20 | ||||
|     assert sensor.threshold_distance == 0.1 | ||||
|     with DistanceSensor(echo_pin, trig_pin, queue_len=5, max_distance=1) as sensor: | ||||
|         assert sensor.max_distance == 1 | ||||
|         assert sensor.trigger is trig_pin | ||||
|         assert sensor.echo is echo_pin | ||||
|         assert sensor.wait_for_out_of_range(1) | ||||
|         assert not sensor.in_range | ||||
|         assert sensor.distance == 1.0 # should be waay before max-distance so this should work | ||||
|         trig_pin.echo_time = 0.0 | ||||
|         assert sensor.wait_for_in_range(1) | ||||
|         assert sensor.in_range | ||||
|         assert sensor.distance < sensor.threshold_distance # depending on speed of machine, may not reach 0 here | ||||
|         sensor.threshold_distance = 0.1 | ||||
|         assert sensor.threshold_distance == 0.1 | ||||
|         with pytest.raises(ValueError): | ||||
|             sensor.max_distance = -1 | ||||
|         sensor.max_distance = 20 | ||||
|         assert sensor.max_distance == 20 | ||||
|         assert sensor.threshold_distance == 0.1 | ||||
|  | ||||
|   | ||||
| @@ -25,114 +25,112 @@ def teardown_function(function): | ||||
|  | ||||
| def test_output_initial_values(): | ||||
|     pin = MockPin(2) | ||||
|     device = OutputDevice(pin, initial_value=False) | ||||
|     assert pin.function == 'output' | ||||
|     assert not pin.state | ||||
|     device.close() | ||||
|     device = OutputDevice(pin, initial_value=True) | ||||
|     assert pin.state | ||||
|     device.close() | ||||
|     state = pin.state | ||||
|     device = OutputDevice(pin, initial_value=None) | ||||
|     assert state == pin.state | ||||
|     with OutputDevice(pin, initial_value=False) as device: | ||||
|         assert pin.function == 'output' | ||||
|         assert not pin.state | ||||
|     with OutputDevice(pin, initial_value=True) as device: | ||||
|         assert pin.state | ||||
|         state = pin.state | ||||
|     with OutputDevice(pin, initial_value=None) as device: | ||||
|         assert state == pin.state | ||||
|  | ||||
| def test_output_write_active_high(): | ||||
|     pin = MockPin(2) | ||||
|     device = OutputDevice(pin) | ||||
|     device.on() | ||||
|     assert pin.state | ||||
|     device.off() | ||||
|     assert not pin.state | ||||
|     with OutputDevice(pin) as device: | ||||
|         device.on() | ||||
|         assert pin.state | ||||
|         device.off() | ||||
|         assert not pin.state | ||||
|  | ||||
| def test_output_write_active_low(): | ||||
|     pin = MockPin(2) | ||||
|     device = OutputDevice(pin, active_high=False) | ||||
|     device.on() | ||||
|     assert not pin.state | ||||
|     device.off() | ||||
|     assert pin.state | ||||
|     with OutputDevice(pin, active_high=False) as device: | ||||
|         device.on() | ||||
|         assert not pin.state | ||||
|         device.off() | ||||
|         assert pin.state | ||||
|  | ||||
| def test_output_write_closed(): | ||||
|     device = OutputDevice(MockPin(2)) | ||||
|     device.close() | ||||
|     with pytest.raises(GPIODeviceClosed): | ||||
|         device.on() | ||||
|     with OutputDevice(MockPin(2)) as device: | ||||
|         device.close() | ||||
|         with pytest.raises(GPIODeviceClosed): | ||||
|             device.on() | ||||
|  | ||||
| def test_output_write_silly(): | ||||
|     pin = MockPin(2) | ||||
|     device = OutputDevice(pin) | ||||
|     pin.function = 'input' | ||||
|     with pytest.raises(AttributeError): | ||||
|         device.on() | ||||
|     with OutputDevice(pin) as device: | ||||
|         pin.function = 'input' | ||||
|         with pytest.raises(AttributeError): | ||||
|             device.on() | ||||
|  | ||||
| def test_output_value(): | ||||
|     pin = MockPin(2) | ||||
|     device = OutputDevice(pin) | ||||
|     assert not device.value | ||||
|     assert not pin.state | ||||
|     device.on() | ||||
|     assert device.value | ||||
|     assert pin.state | ||||
|     device.value = False | ||||
|     assert not device.value | ||||
|     assert not pin.state | ||||
|     with OutputDevice(pin) as device: | ||||
|         assert not device.value | ||||
|         assert not pin.state | ||||
|         device.on() | ||||
|         assert device.value | ||||
|         assert pin.state | ||||
|         device.value = False | ||||
|         assert not device.value | ||||
|         assert not pin.state | ||||
|  | ||||
| def test_output_digital_toggle(): | ||||
|     pin = MockPin(2) | ||||
|     device = DigitalOutputDevice(pin) | ||||
|     assert not device.value | ||||
|     assert not pin.state | ||||
|     device.toggle() | ||||
|     assert device.value | ||||
|     assert pin.state | ||||
|     device.toggle() | ||||
|     assert not device.value | ||||
|     assert not pin.state | ||||
|     with DigitalOutputDevice(pin) as device: | ||||
|         assert not device.value | ||||
|         assert not pin.state | ||||
|         device.toggle() | ||||
|         assert device.value | ||||
|         assert pin.state | ||||
|         device.toggle() | ||||
|         assert not device.value | ||||
|         assert not pin.state | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| def test_output_blink_background(): | ||||
|     pin = MockPin(2) | ||||
|     device = DigitalOutputDevice(pin) | ||||
|     device.blink(0.1, 0.1, n=2) | ||||
|     device._blink_thread.join() # naughty, but ensures no arbitrary waits in the test | ||||
|     pin.assert_states_and_times([ | ||||
|         (0.0, False), | ||||
|         (0.0, True), | ||||
|         (0.1, False), | ||||
|         (0.1, True), | ||||
|         (0.1, False) | ||||
|         ]) | ||||
|     with DigitalOutputDevice(pin) as device: | ||||
|         device.blink(0.1, 0.1, n=2) | ||||
|         device._blink_thread.join() # naughty, but ensures no arbitrary waits in the test | ||||
|         pin.assert_states_and_times([ | ||||
|             (0.0, False), | ||||
|             (0.0, True), | ||||
|             (0.1, False), | ||||
|             (0.1, True), | ||||
|             (0.1, False) | ||||
|             ]) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| def test_output_blink_foreground(): | ||||
|     pin = MockPin(2) | ||||
|     device = DigitalOutputDevice(pin) | ||||
|     device.blink(0.1, 0.1, n=2, background=False) | ||||
|     pin.assert_states_and_times([ | ||||
|         (0.0, False), | ||||
|         (0.0, True), | ||||
|         (0.1, False), | ||||
|         (0.1, True), | ||||
|         (0.1, False) | ||||
|         ]) | ||||
|     with DigitalOutputDevice(pin) as device: | ||||
|         device.blink(0.1, 0.1, n=2, background=False) | ||||
|         pin.assert_states_and_times([ | ||||
|             (0.0, False), | ||||
|             (0.0, True), | ||||
|             (0.1, False), | ||||
|             (0.1, True), | ||||
|             (0.1, False) | ||||
|             ]) | ||||
|  | ||||
| def test_output_blink_interrupt_on(): | ||||
|     pin = MockPin(2) | ||||
|     device = DigitalOutputDevice(pin) | ||||
|     device.blink(1, 0.1) | ||||
|     sleep(0.2) | ||||
|     device.off() # should interrupt while on | ||||
|     pin.assert_states([False, True, False]) | ||||
|     with DigitalOutputDevice(pin) as device: | ||||
|         device.blink(1, 0.1) | ||||
|         sleep(0.2) | ||||
|         device.off() # should interrupt while on | ||||
|         pin.assert_states([False, True, False]) | ||||
|  | ||||
| def test_output_blink_interrupt_off(): | ||||
|     pin = MockPin(2) | ||||
|     device = DigitalOutputDevice(pin) | ||||
|     device.blink(0.1, 1) | ||||
|     sleep(0.2) | ||||
|     device.off() # should interrupt while off | ||||
|     pin.assert_states([False, True, False]) | ||||
|     with DigitalOutputDevice(pin) as device: | ||||
|         device.blink(0.1, 1) | ||||
|         sleep(0.2) | ||||
|         device.off() # should interrupt while off | ||||
|         pin.assert_states([False, True, False]) | ||||
|  | ||||
| def test_output_pwm_bad_initial_value(): | ||||
|     with pytest.raises(ValueError): | ||||
| @@ -144,50 +142,50 @@ def test_output_pwm_not_supported(): | ||||
|  | ||||
| def test_output_pwm_states(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     device.value = 0.1 | ||||
|     device.value = 0.2 | ||||
|     device.value = 0.0 | ||||
|     pin.assert_states([0.0, 0.1, 0.2, 0.0]) | ||||
|     with PWMOutputDevice(pin) as device: | ||||
|         device.value = 0.1 | ||||
|         device.value = 0.2 | ||||
|         device.value = 0.0 | ||||
|         pin.assert_states([0.0, 0.1, 0.2, 0.0]) | ||||
|  | ||||
| def test_output_pwm_read(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin, frequency=100) | ||||
|     assert device.frequency == 100 | ||||
|     device.value = 0.1 | ||||
|     assert isclose(device.value, 0.1) | ||||
|     assert isclose(pin.state, 0.1) | ||||
|     assert device.is_active | ||||
|     device.frequency = None | ||||
|     assert not device.value | ||||
|     assert not device.is_active | ||||
|     assert device.frequency is None | ||||
|     with PWMOutputDevice(pin, frequency=100) as device: | ||||
|         assert device.frequency == 100 | ||||
|         device.value = 0.1 | ||||
|         assert isclose(device.value, 0.1) | ||||
|         assert isclose(pin.state, 0.1) | ||||
|         assert device.is_active | ||||
|         device.frequency = None | ||||
|         assert not device.value | ||||
|         assert not device.is_active | ||||
|         assert device.frequency is None | ||||
|  | ||||
| def test_output_pwm_write(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     device.on() | ||||
|     device.off() | ||||
|     pin.assert_states([False, True, False]) | ||||
|     with PWMOutputDevice(pin) as device: | ||||
|         device.on() | ||||
|         device.off() | ||||
|         pin.assert_states([False, True, False]) | ||||
|  | ||||
| def test_output_pwm_toggle(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     device.toggle() | ||||
|     device.value = 0.5 | ||||
|     device.value = 0.1 | ||||
|     device.toggle() | ||||
|     device.off() | ||||
|     pin.assert_states([False, True, 0.5, 0.1, 0.9, False]) | ||||
|     with PWMOutputDevice(pin) as device: | ||||
|         device.toggle() | ||||
|         device.value = 0.5 | ||||
|         device.value = 0.1 | ||||
|         device.toggle() | ||||
|         device.off() | ||||
|         pin.assert_states([False, True, 0.5, 0.1, 0.9, False]) | ||||
|  | ||||
| def test_output_pwm_active_high_read(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin, active_high=False) | ||||
|     device.value = 0.1 | ||||
|     assert isclose(device.value, 0.1) | ||||
|     assert isclose(pin.state, 0.9) | ||||
|     device.frequency = None | ||||
|     assert device.value | ||||
|     with PWMOutputDevice(pin, active_high=False) as device: | ||||
|         device.value = 0.1 | ||||
|         assert isclose(device.value, 0.1) | ||||
|         assert isclose(pin.state, 0.9) | ||||
|         device.frequency = None | ||||
|         assert device.value | ||||
|  | ||||
| def test_output_pwm_bad_value(): | ||||
|     with pytest.raises(ValueError): | ||||
| @@ -201,108 +199,108 @@ def test_output_pwm_write_closed(): | ||||
|  | ||||
| def test_output_pwm_write_silly(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     pin.function = 'input' | ||||
|     with pytest.raises(AttributeError): | ||||
|         device.off() | ||||
|     with PWMOutputDevice(pin) as device: | ||||
|         pin.function = 'input' | ||||
|         with pytest.raises(AttributeError): | ||||
|             device.off() | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| def test_output_pwm_blink_background(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     device.blink(0.1, 0.1, n=2) | ||||
|     device._blink_thread.join() | ||||
|     pin.assert_states_and_times([ | ||||
|         (0.0, 0), | ||||
|         (0.0, 1), | ||||
|         (0.1, 0), | ||||
|         (0.1, 1), | ||||
|         (0.1, 0) | ||||
|         ]) | ||||
|     with PWMOutputDevice(pin) as device: | ||||
|         device.blink(0.1, 0.1, n=2) | ||||
|         device._blink_thread.join() | ||||
|         pin.assert_states_and_times([ | ||||
|             (0.0, 0), | ||||
|             (0.0, 1), | ||||
|             (0.1, 0), | ||||
|             (0.1, 1), | ||||
|             (0.1, 0) | ||||
|             ]) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| def test_output_pwm_blink_foreground(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     device.blink(0.1, 0.1, n=2, background=False) | ||||
|     pin.assert_states_and_times([ | ||||
|         (0.0, 0), | ||||
|         (0.0, 1), | ||||
|         (0.1, 0), | ||||
|         (0.1, 1), | ||||
|         (0.1, 0) | ||||
|         ]) | ||||
|     with PWMOutputDevice(pin) as device: | ||||
|         device.blink(0.1, 0.1, n=2, background=False) | ||||
|         pin.assert_states_and_times([ | ||||
|             (0.0, 0), | ||||
|             (0.0, 1), | ||||
|             (0.1, 0), | ||||
|             (0.1, 1), | ||||
|             (0.1, 0) | ||||
|             ]) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| def test_output_pwm_fade_background(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     device.blink(0, 0, 0.1, 0.1, n=2) | ||||
|     device._blink_thread.join() | ||||
|     pin.assert_states_and_times([ | ||||
|         (0.0, 0), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 1), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 1), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0), | ||||
|         ]) | ||||
|     with PWMOutputDevice(pin) as device: | ||||
|         device.blink(0, 0, 0.2, 0.2, n=2) | ||||
|         device._blink_thread.join() | ||||
|         pin.assert_states_and_times([ | ||||
|             (0.0, 0), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 1), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 1), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0), | ||||
|             ]) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| def test_output_pwm_fade_foreground(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     device.blink(0, 0, 0.1, 0.1, n=2, background=False) | ||||
|     pin.assert_states_and_times([ | ||||
|         (0.0, 0), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 1), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 1), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0), | ||||
|         ]) | ||||
|     with PWMOutputDevice(pin) as device: | ||||
|         device.blink(0, 0, 0.2, 0.2, n=2, background=False) | ||||
|         pin.assert_states_and_times([ | ||||
|             (0.0, 0), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 1), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 1), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0), | ||||
|             ]) | ||||
|  | ||||
| def test_output_pwm_blink_interrupt(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     device.blink(1, 0.1) | ||||
|     sleep(0.2) | ||||
|     device.off() # should interrupt while on | ||||
|     pin.assert_states([0, 1, 0]) | ||||
|     with PWMOutputDevice(pin) as device: | ||||
|         device.blink(1, 0.1) | ||||
|         sleep(0.2) | ||||
|         device.off() # should interrupt while on | ||||
|         pin.assert_states([0, 1, 0]) | ||||
|  | ||||
| def test_rgbled_missing_pins(): | ||||
|     with pytest.raises(ValueError): | ||||
| @@ -310,116 +308,116 @@ def test_rgbled_missing_pins(): | ||||
|  | ||||
| def test_rgbled_initial_value(): | ||||
|     r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) | ||||
|     device = RGBLED(r, g, b, initial_value=(0.1, 0.2, 0)) | ||||
|     assert r.frequency | ||||
|     assert g.frequency | ||||
|     assert b.frequency | ||||
|     assert isclose(r.state, 0.1) | ||||
|     assert isclose(g.state, 0.2) | ||||
|     assert isclose(b.state, 0.0) | ||||
|     with RGBLED(r, g, b, initial_value=(0.1, 0.2, 0)) as device: | ||||
|         assert r.frequency | ||||
|         assert g.frequency | ||||
|         assert b.frequency | ||||
|         assert isclose(r.state, 0.1) | ||||
|         assert isclose(g.state, 0.2) | ||||
|         assert isclose(b.state, 0.0) | ||||
|  | ||||
| def test_rgbled_value(): | ||||
|     r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) | ||||
|     device = RGBLED(r, g, b) | ||||
|     assert not device.is_active | ||||
|     assert device.value == (0, 0, 0) | ||||
|     device.on() | ||||
|     assert device.is_active | ||||
|     assert device.value == (1, 1, 1) | ||||
|     device.off() | ||||
|     assert not device.is_active | ||||
|     assert device.value == (0, 0, 0) | ||||
|     with RGBLED(r, g, b) as device: | ||||
|         assert not device.is_active | ||||
|         assert device.value == (0, 0, 0) | ||||
|         device.on() | ||||
|         assert device.is_active | ||||
|         assert device.value == (1, 1, 1) | ||||
|         device.off() | ||||
|         assert not device.is_active | ||||
|         assert device.value == (0, 0, 0) | ||||
|  | ||||
| def test_rgbled_toggle(): | ||||
|     r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) | ||||
|     device = RGBLED(r, g, b) | ||||
|     assert not device.is_active | ||||
|     assert device.value == (0, 0, 0) | ||||
|     device.toggle() | ||||
|     assert device.is_active | ||||
|     assert device.value == (1, 1, 1) | ||||
|     device.toggle() | ||||
|     assert not device.is_active | ||||
|     assert device.value == (0, 0, 0) | ||||
|     with RGBLED(r, g, b) as device: | ||||
|         assert not device.is_active | ||||
|         assert device.value == (0, 0, 0) | ||||
|         device.toggle() | ||||
|         assert device.is_active | ||||
|         assert device.value == (1, 1, 1) | ||||
|         device.toggle() | ||||
|         assert not device.is_active | ||||
|         assert device.value == (0, 0, 0) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| def test_rgbled_blink_background(): | ||||
|     r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) | ||||
|     device = RGBLED(r, g, b) | ||||
|     device.blink(0.1, 0.1, n=2) | ||||
|     device._blink_thread.join() | ||||
|     expected = [ | ||||
|         (0.0, 0), | ||||
|         (0.0, 1), | ||||
|         (0.1, 0), | ||||
|         (0.1, 1), | ||||
|         (0.1, 0) | ||||
|         ] | ||||
|     r.assert_states_and_times(expected) | ||||
|     g.assert_states_and_times(expected) | ||||
|     b.assert_states_and_times(expected) | ||||
|     with RGBLED(r, g, b) as device: | ||||
|         device.blink(0.1, 0.1, n=2) | ||||
|         device._blink_thread.join() | ||||
|         expected = [ | ||||
|             (0.0, 0), | ||||
|             (0.0, 1), | ||||
|             (0.1, 0), | ||||
|             (0.1, 1), | ||||
|             (0.1, 0) | ||||
|             ] | ||||
|         r.assert_states_and_times(expected) | ||||
|         g.assert_states_and_times(expected) | ||||
|         b.assert_states_and_times(expected) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| def test_rgbled_blink_foreground(): | ||||
|     r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) | ||||
|     device = RGBLED(r, g, b) | ||||
|     device.blink(0.1, 0.1, n=2, background=False) | ||||
|     expected = [ | ||||
|         (0.0, 0), | ||||
|         (0.0, 1), | ||||
|         (0.1, 0), | ||||
|         (0.1, 1), | ||||
|         (0.1, 0) | ||||
|         ] | ||||
|     r.assert_states_and_times(expected) | ||||
|     g.assert_states_and_times(expected) | ||||
|     b.assert_states_and_times(expected) | ||||
|     with RGBLED(r, g, b) as device: | ||||
|         device.blink(0.1, 0.1, n=2, background=False) | ||||
|         expected = [ | ||||
|             (0.0, 0), | ||||
|             (0.0, 1), | ||||
|             (0.1, 0), | ||||
|             (0.1, 1), | ||||
|             (0.1, 0) | ||||
|             ] | ||||
|         r.assert_states_and_times(expected) | ||||
|         g.assert_states_and_times(expected) | ||||
|         b.assert_states_and_times(expected) | ||||
|  | ||||
| @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), | ||||
|                     reason='timing is too random on pypy') | ||||
| def test_rgbled_fade_background(): | ||||
|     r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) | ||||
|     device = RGBLED(r, g, b) | ||||
|     device.blink(0, 0, 0.1, 0.1, n=2) | ||||
|     device._blink_thread.join() | ||||
|     expected = [ | ||||
|         (0.0, 0), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 1), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 1), | ||||
|         (0.02, 0.8), | ||||
|         (0.02, 0.6), | ||||
|         (0.02, 0.4), | ||||
|         (0.02, 0.2), | ||||
|         (0.02, 0), | ||||
|         ] | ||||
|     r.assert_states_and_times(expected) | ||||
|     g.assert_states_and_times(expected) | ||||
|     b.assert_states_and_times(expected) | ||||
|     with RGBLED(r, g, b) as device: | ||||
|         device.blink(0, 0, 0.2, 0.2, n=2) | ||||
|         device._blink_thread.join() | ||||
|         expected = [ | ||||
|             (0.0, 0), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 1), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 1), | ||||
|             (0.04, 0.8), | ||||
|             (0.04, 0.6), | ||||
|             (0.04, 0.4), | ||||
|             (0.04, 0.2), | ||||
|             (0.04, 0), | ||||
|             ] | ||||
|         r.assert_states_and_times(expected) | ||||
|         g.assert_states_and_times(expected) | ||||
|         b.assert_states_and_times(expected) | ||||
|  | ||||
| def test_output_rgbled_blink_interrupt(): | ||||
|     r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) | ||||
|     device = RGBLED(r, g, b) | ||||
|     device.blink(1, 0.1) | ||||
|     sleep(0.2) | ||||
|     device.off() # should interrupt while on | ||||
|     r.assert_states([0, 1, 0]) | ||||
|     g.assert_states([0, 1, 0]) | ||||
|     b.assert_states([0, 1, 0]) | ||||
|     with RGBLED(r, g, b) as device: | ||||
|         device.blink(1, 0.1) | ||||
|         sleep(0.2) | ||||
|         device.off() # should interrupt while on | ||||
|         r.assert_states([0, 1, 0]) | ||||
|         g.assert_states([0, 1, 0]) | ||||
|         b.assert_states([0, 1, 0]) | ||||
|  | ||||
| def test_motor_missing_pins(): | ||||
|     with pytest.raises(ValueError): | ||||
| @@ -428,55 +426,55 @@ def test_motor_missing_pins(): | ||||
| def test_motor_pins(): | ||||
|     f = MockPWMPin(1) | ||||
|     b = MockPWMPin(2) | ||||
|     device = Motor(f, b) | ||||
|     assert device.forward_device.pin is f | ||||
|     assert device.backward_device.pin is b | ||||
|     with Motor(f, b) as device: | ||||
|         assert device.forward_device.pin is f | ||||
|         assert device.backward_device.pin is b | ||||
|  | ||||
| def test_motor_close(): | ||||
|     f = MockPWMPin(1) | ||||
|     b = MockPWMPin(2) | ||||
|     device = Motor(f, b) | ||||
|     device.close() | ||||
|     assert device.closed | ||||
|     assert device.forward_device.pin is None | ||||
|     assert device.backward_device.pin is None | ||||
|     with Motor(f, b) as device: | ||||
|         device.close() | ||||
|         assert device.closed | ||||
|         assert device.forward_device.pin is None | ||||
|         assert device.backward_device.pin is None | ||||
|  | ||||
| def test_motor_value(): | ||||
|     f = MockPWMPin(1) | ||||
|     b = MockPWMPin(2) | ||||
|     device = Motor(f, b) | ||||
|     device.value = -1 | ||||
|     assert device.is_active | ||||
|     assert device.value == -1 | ||||
|     assert b.state == 1 and f.state == 0 | ||||
|     device.value = 1 | ||||
|     assert device.is_active | ||||
|     assert device.value == 1 | ||||
|     assert b.state == 0 and f.state == 1 | ||||
|     device.value = 0.5 | ||||
|     assert device.is_active | ||||
|     assert device.value == 0.5 | ||||
|     assert b.state == 0 and f.state == 0.5 | ||||
|     device.value = 0 | ||||
|     assert not device.is_active | ||||
|     assert not device.value | ||||
|     assert b.state == 0 and f.state == 0 | ||||
|     with Motor(f, b) as device: | ||||
|         device.value = -1 | ||||
|         assert device.is_active | ||||
|         assert device.value == -1 | ||||
|         assert b.state == 1 and f.state == 0 | ||||
|         device.value = 1 | ||||
|         assert device.is_active | ||||
|         assert device.value == 1 | ||||
|         assert b.state == 0 and f.state == 1 | ||||
|         device.value = 0.5 | ||||
|         assert device.is_active | ||||
|         assert device.value == 0.5 | ||||
|         assert b.state == 0 and f.state == 0.5 | ||||
|         device.value = 0 | ||||
|         assert not device.is_active | ||||
|         assert not device.value | ||||
|         assert b.state == 0 and f.state == 0 | ||||
|  | ||||
| def test_motor_bad_value(): | ||||
|     f = MockPWMPin(1) | ||||
|     b = MockPWMPin(2) | ||||
|     device = Motor(f, b) | ||||
|     with pytest.raises(ValueError): | ||||
|         device.value = 2 | ||||
|     with Motor(f, b) as device: | ||||
|         with pytest.raises(ValueError): | ||||
|             device.value = 2 | ||||
|  | ||||
| def test_motor_reverse(): | ||||
|     f = MockPWMPin(1) | ||||
|     b = MockPWMPin(2) | ||||
|     device = Motor(f, b) | ||||
|     device.forward() | ||||
|     assert device.value == 1 | ||||
|     assert b.state == 0 and f.state == 1 | ||||
|     device.reverse() | ||||
|     assert device.value == -1 | ||||
|     assert b.state == 1 and f.state == 0 | ||||
|     with Motor(f, b) as device: | ||||
|         device.forward() | ||||
|         assert device.value == 1 | ||||
|         assert b.state == 0 and f.state == 1 | ||||
|         device.reverse() | ||||
|         assert device.value == -1 | ||||
|         assert b.state == 1 and f.state == 0 | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import pytest | ||||
| from math import sin, cos, radians | ||||
| from time import time | ||||
| 
 | ||||
| from gpiozero import * | ||||
| from gpiozero.tools import * | ||||
| try: | ||||
|     from math import isclose | ||||
| except ImportError: | ||||