From 69dd8a439ae5a2f41d123175f09ed5c637e21d59 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Mon, 4 Apr 2016 01:34:53 +0100 Subject: [PATCH] The source/values toolkit Me and my big mouth. No sooner do I declare the base classes "relatively stable" than I go and mess around with it all again. Anyway, this is the long promised set of utilities to make source/values more interesting. It includes a few interesting little utility functions, a whole bunch of examples and introduces the notion of "pseudo" devices with no (obvious) hardware representation like a time-of-day device. This necessitated making the event system a little more generic (it's not exclusive the GPIO devices after all; no reason we can't use it on composite devices in future) and by this point the mixins have gotten large enough to justify their own module. The pseudo-devices are a bit spartan and basic at the moment but I'm sure there'll be plenty of future ideas... --- docs/api_exc.rst | 2 + docs/api_generic.rst | 25 +- docs/api_other.rst | 14 ++ docs/api_source_tools.rst | 49 ++++ docs/images/gpio_device_hierarchy.dot | 6 +- docs/images/gpio_device_hierarchy.pdf | Bin 9890 -> 9634 bytes docs/images/gpio_device_hierarchy.png | Bin 48299 -> 43392 bytes docs/images/gpio_device_hierarchy.svg | 164 ++++++------- docs/images/other_device_hierarchy.dot | 19 ++ docs/images/other_device_hierarchy.pdf | Bin 0 -> 7383 bytes docs/images/other_device_hierarchy.png | Bin 0 -> 9007 bytes docs/images/other_device_hierarchy.svg | 48 ++++ docs/index.rst | 2 + gpiozero/__init__.py | 26 +- gpiozero/boards.py | 3 +- gpiozero/devices.py | 159 +++--------- gpiozero/exc.py | 3 + gpiozero/input_devices.py | 150 +----------- gpiozero/mixins.py | 326 +++++++++++++++++++++++++ gpiozero/other_devices.py | 168 +++++++++++++ gpiozero/output_devices.py | 3 +- gpiozero/source_tools.py | 296 ++++++++++++++++++++++ gpiozero/threads.py | 51 +--- 23 files changed, 1091 insertions(+), 423 deletions(-) create mode 100644 docs/api_other.rst create mode 100644 docs/api_source_tools.rst create mode 100644 docs/images/other_device_hierarchy.dot create mode 100644 docs/images/other_device_hierarchy.pdf create mode 100644 docs/images/other_device_hierarchy.png create mode 100644 docs/images/other_device_hierarchy.svg create mode 100644 gpiozero/mixins.py create mode 100644 gpiozero/other_devices.py create mode 100644 gpiozero/source_tools.py diff --git a/docs/api_exc.rst b/docs/api_exc.rst index fe6e461..0514dce 100644 --- a/docs/api_exc.rst +++ b/docs/api_exc.rst @@ -41,6 +41,8 @@ Errors .. autoexception:: DeviceClosed +.. autoexception:: BadEventHandler + .. autoexception:: CompositeDeviceError .. autoexception:: CompositeDeviceBadName diff --git a/docs/api_generic.rst b/docs/api_generic.rst index 5c6b51f..66ee153 100644 --- a/docs/api_generic.rst +++ b/docs/api_generic.rst @@ -16,6 +16,9 @@ classes: * :class:`SPIDevice` represents devices that communicate over an SPI interface (implemented as four GPIO pins) +* :class:`InternalDevice` represents devices that are entirely internal to + the Pi (usually operating system related services) + * :class:`CompositeDevice` represents devices composed of multiple other devices like HATs @@ -31,6 +34,9 @@ There are also several `mixin classes`_: * :class:`SharedMixin` which causes classes to track their construction and return existing instances when equivalent constructor arguments are passed +* :class:`EventsMixin` which adds activated/deactivated events to devices + along with the machinery to trigger those events + .. _mixin classes: https://en.wikipedia.org/wiki/Mixin The current class hierarchies are displayed below. For brevity, the mixin @@ -47,6 +53,10 @@ Next, the classes below :class:`SPIDevice`: .. image:: images/spi_device_hierarchy.* +Next, the classes below :class:`InternalDevice`: + +.. image:: images/other_device_hierarchy.* + Next, the classes below :class:`CompositeDevice`: .. image:: images/composite_device_hierarchy.* @@ -60,15 +70,18 @@ Base Classes ============ .. autoclass:: Device - :members: close, closed + :members: close, closed, value, is_active .. autoclass:: GPIODevice(pin) :members: -.. autoclass:: CompositeDevice +.. autoclass:: SPIDevice :members: -.. autoclass:: SPIDevice +.. autoclass:: InternalDevice + :members: + +.. autoclass:: CompositeDevice :members: Input Devices @@ -77,9 +90,6 @@ Input Devices .. autoclass:: InputDevice(pin, pull_up=False) :members: -.. autoclass:: WaitableInputDevice - :members: - .. autoclass:: DigitalInputDevice(pin, pull_up=False, bounce_time=None) :members: @@ -128,3 +138,6 @@ Mixin Classes .. autoclass:: SharedMixin(...) :members: _shared_key +.. autoclass:: EventsMixin(...) + :members: + diff --git a/docs/api_other.rst b/docs/api_other.rst new file mode 100644 index 0000000..a5d9d31 --- /dev/null +++ b/docs/api_other.rst @@ -0,0 +1,14 @@ +================ +Internal Devices +================ + +.. currentmodule:: gpiozero + +GPIO Zero also provides several "internal" devices which represent facilities +provided by the operating system itself. These can be used to react to things +like the time of day, or whether a server is available on the network. + + +.. autoclass:: TimeOfDay + +.. autoclass:: PingServer diff --git a/docs/api_source_tools.rst b/docs/api_source_tools.rst new file mode 100644 index 0000000..f0cb189 --- /dev/null +++ b/docs/api_source_tools.rst @@ -0,0 +1,49 @@ +============ +Source Tools +============ + +.. currentmodule:: gpiozero + +GPIO Zero includes several utility routines which are intended to be used with +the :attr:`~SourceMixin.source` and :attr:`~ValuesMixin.values` attributes +common to most devices in the library. Given that ``source`` and ``values`` +deal with infinite iterators, another excellent source of utilities is the +:mod:`itertools` module in the standard library. + +Single source conversions +========================= + +.. autofunction:: negated + +.. autofunction:: inverted + +.. autofunction:: scaled + +.. autofunction:: clamped + +.. autofunction:: post_delayed + +.. autofunction:: pre_delayed + +.. autofunction:: quantized + +.. autofunction:: queued + +Combining sources +================= + +.. autofunction:: conjunction + +.. autofunction:: disjunction + +.. autofunction:: averaged + +Artifical sources +================= + +.. autofunction:: random_values + +.. autofunction:: sin_values + +.. autofunction:: cos_values + diff --git a/docs/images/gpio_device_hierarchy.dot b/docs/images/gpio_device_hierarchy.dot index 3eaf266..7ff0bed 100644 --- a/docs/images/gpio_device_hierarchy.dot +++ b/docs/images/gpio_device_hierarchy.dot @@ -9,7 +9,6 @@ digraph classes { node [color="#9ec6e0", fontcolor="#000000"] Device; GPIODevice; - WaitableInputDevice; SmoothedInputDevice; /* Concrete classes */ @@ -17,9 +16,8 @@ digraph classes { GPIODevice->Device; InputDevice->GPIODevice; - WaitableInputDevice->InputDevice; - DigitalInputDevice->WaitableInputDevice; - SmoothedInputDevice->WaitableInputDevice; + DigitalInputDevice->InputDevice; + SmoothedInputDevice->InputDevice; Button->DigitalInputDevice; MotionSensor->SmoothedInputDevice; LightSensor->SmoothedInputDevice; diff --git a/docs/images/gpio_device_hierarchy.pdf b/docs/images/gpio_device_hierarchy.pdf index 8c981cc6179fba652bd76ff2266f362fceb04c64..9dc2a3e821d25c66407ee63f72e538fdf0905ee7 100644 GIT binary patch delta 8019 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 Mory_aLjnE&04SQlQ2+n{ diff --git a/docs/images/gpio_device_hierarchy.png b/docs/images/gpio_device_hierarchy.png index 1df730150b4b07855d59f1c9984879f78b40b7fd..ff0de16a2b989f6c3544c32f19a38446d5309742 100644 GIT binary patch literal 43392 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 Wg0qEHxipHp!mk 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 IE0_oW4_;VbWB>pF literal 48299 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

!^ zv(g4QNLW&4ecLazq>l&tcgkwgqnz^QePGS?7yTQn19iQ$s18X@ETO-y@#>tP)_c8t z^R|e4dsffb8dF9;8S)SX{90e2J1EIZp%=fJ@^SjxMBJ-V#S;d^W4v`my31mXfVf>3 z4KJyZN;y`9pl7}6yYx|3nbxklgVn|vZHoM#k1Dy_Cq5g0Xm7*?q;q3}>1kq6MHNrh z`b29--4!sFviSJTb7tfpzL=Py0ftCc70>Fb07Us2Q?B^}IiSR`v zuWc5r_-OO8bpEcY4Q^nT=V-6C7oXKmJ|tRePCR@L3o(}=e`DeeVW>bnNPcIRPD5a1 z+8Lz*M0?^BZ_-Pdazl~&@YGE4v*oQ*M7Q0%!|ahD?9S!&Oiy877tu&QoiUXhFYuPq zzAJLFGpBAYq9)hd)p8EWQZs&LX@@z_Qrm@sNQWk0rKR$5}dPyb?Pq2oG#UlD$#_-wV0MJEvpR4Xl~XBncmp%PzQ z`MGkW@4Ja;C==_LOs&Q+A$a{cm$(}?+df2ol2nwTbWRNk3Q2q-=X@)^^ZfdFhxcWU zv)kOM;a7sMZnGRRGCyuVet5z*#46FvcETW0x^-OpqH?(cVWC)&tHMz#ZPR66{^vut z8{XJseY6`B4$(Sa&n|BS{WMaX`^>*PmaFt>#j3AmVug40{E|QOX@qPONAap<_Kq{h zpPrd~t^@_yUC-A1jFjJ>_05-iN+;6O@WK~2ZP)*KI4M;FzXr#-_|f~%0G+rpwz??8}dUD4IQnqGFiPrlDuF)J+g zToG@AlO#a;+5>rpLL&1;!3N4wE_GtGm0^L+nGjgLCUxw&x$U4CTK z&txcEw{+#Nbe#p$3#46ViDn2=ztNE~yLM-jx%HJz8FT-Wn{a^P&_=Mz<&O9v0TYP` zo>p~Z3w(Mmkf;8|LmKVrH1DbPa%khFd-b${TcFe?Xi}?-$PAd^_l2^{ zUT2rhRTR{p>MZ*roMt}+?OsNd*)+yIVy7okyE!@AUO4M2=@@)fHENvxfs;Vo!hrkE zL`*z?NtDo(yWZqbVGNxlRuXqqIl5NU#I(O;y`_knOy8h}8Ud?4_Y*o?F?8H%-++&2 zUkqf>uWPqVbDY7aQ(wMN+rEt)W&>I^O*5N1HazdN0~L8264S>VJNT zmwgda&avZJ!2TAkW==1as&yz)bo%D$j?vd_vy0VpIRa3>I^Fs*?)W^6Il;5>d8l=AVdoYo%Y8eg zj+t1e(5!l;q@Fl`%}u3yYG<h8$<>~s{MVrgx(D87tZ7nttUZbShn*+CB8Tuc z0*Nf{!P&|Jswv)Jga*-Au~V;Xj;$)f?N;>BpL5$C8#ELP{ww9F$(WT&>1Q7je*sLZmnPYpA_p)Gpm;GD$D7mR-`OMb4jJj<|17O1vDts4*gZ~1G>$1 zs}r?nJfklX`+r)W;NT-djuSSb(i~3iz0S<9-Yd1US~yFe-8&LNKb?!2Ksl}4{55^j zf9vOL*0=B{xJCAZx#K+5jQgG*KUGVIhntsImPMRurH4wqno~C-Ix1~!sTe8pX8wuv zyMKSN$xqJ17ZWsn&ZINbD3KJHaE=kD_-gqwlY}B=>~%ZeeqMGK+OH*Q&G}!En0Xqf zT3@B&{5@)=zwkKdo}c8(WWRuo+V<{7%)-Jc&D|pua5BOC!J=jV{!>`!z>ez!L^aQM z?n~o;7^EU%2+@DRvC6v&D19WFrxo{5UmGVcKx_Q7PhFb~F3a~&1XGawnyMvU zo)R@&tw~&+Ea=J;9iEnB&KG23lNa>Ipi_D~^Zd1HWM3L<;5>(mSgaYB|L{c9eOxmvX3t&fT~`uI-9@R&cl~dN!ol|Mua2(RjxplJ zgUjrm?b%4&6NhDWd)mSa$5=FH)L3_c9l?8C<@!0|<<3~61hpa2a_v5$;!@dx!sKXj z_p@>uY+D*^l^&649)H*-dHvCX3p=0NY~y9so+DKsyUCLPEOM^O_&afn_{2`@Z(3u$ zj01MIy2)>S;S7)O@2CysbZ_v(n!&p5G7+Yhv*@aG2Ro0mwJSheqoNN_k#RokvWMtF z>z^)-0fHQ^2$`N{tykr;I})Ab@Klr$&T@|&tj|dbO5JA1pLXQ&n>!Xcm?l1b+B2~w zdv$p!ijsJ~L&0)hwla9*P1U$+sHSui2WEgzEpGUxNl+l|wAx7A)ltYns|L-yw%849 z%~9uC9{woEQIErwtXBl@*_>CV3mQHHuTS!5{MBOmF*~n@ZKeq|s;^MRZ8F+@#`2-Ld!+O=&n>~~iZt}gf@N6>~0){+8h`(k#W*kN=z3Nd}MV_fjKy>UuO^sY{c z09O)=N;*fH%C#a>spW%fjqC`{ibn!MJw8>Uejil3Ph{N%;j5b5pQ<4pDjMw;dkLv-)5LN10@!gkSda8 zN2r>#=beT{tBIYA7QkMXft&j{LgzoBMkr{Z2lINJ_xOfRdB_M`zT(8Ru7)qHYqu$h z^c}buSeLFhl$kl~E1(^t0}RE66j>OJ*39rD5M+LF0m+k{T>%Tux>d3ME!lTGc*Ec@ zS?zlrry86jBPANK{}C?#Nin04mk+pR-utRUl{xN4OX z;i@txw-0)WU^|+p(wf~NZD?+EqnSnxG}^b*kWU}l@4cRBZPcJ4u03jLnmGEJU&|!V zVHNOAO=+jEoD(sMR%<`Fg0-BXmlb)iEzH?-mV)nwd@P~|) zI*fGLZ-U8hq3)KZGr2019*&zeVoez`kx^Ctd+KjF(C8u`*6z0A$S~#pF9 zD=1`dQ{$L)QK9{>VCK-}d>viHeN$6YB+v=ciE)a(r~Izb&}DZIV5qQ=5KrOMH@W%M zR)t!Pi4VzR8`?~vnK62*ByA!A1O>3ZZxj?pNc#+pjYVI- zMiBu`$D!T}cJ@faL?=`M0RbSD9RYfS7T_qab4NZJ8+QS|YVp1VwFodD;EKi4L!oK% zzpr_v1(h?-{&2%*vTXK;E)yAg&VR zhIfD)dPYmzg&+g}Gk~MB(fM7nYVuA47`6gsB$E}EsTV+rOS3LwE0^i zM+tgH1Dx#{TWscXva@_~Mx?dAva9BWQRaA!!dJ>Hsx{rM8~n1rr_~9tNt{x?zH(2w<;(XQ!8x{0Rs_&KAZW zN?AYQw6V>JGGiPheF{CF9UUDR^?yGGWKr14iV+3aGkur?c_k%KdK48G&!6u$X2Mmx z5>LipR@>o1R7pEKJFg@)6_tm0cp|S~zYc4Yj#A78Xwe5yc;_J=`>oDzIMf#JF`{a% z=5GRH{1_KkgGmwe7Ypg)Mvud-27emD)Y=svhI(qX10yL30 zOrX(iYN^B}V7Pwu_Xk@qw(Ec)f5jwvFDj}7oN$I7ruIrt42<1R#axNDcd^VWx7KDG z1psVx^qRh6V1Q20+D(|^qbTkm(?ZSFPvmt91>$hJh;w8A@KA60cLb)p0W0CRfPnBg z%|Qk*Zw^k4%%NhqJ7EX9@zX|=dp~#)4eGs%cTP@DNQ4hi;^yY&@mhyhg?0P)V`5_R zRLj4EjEiF&(;D;`StBO!1|nu=3~;}#`M$CMTr!Hhu@-)3!|q%zTOPP&W``XFRd8|v z+`+)=cq@C*NV(qm2s1Ff0I0=-0U_*>DPfcHSdc+j|2s5>=I%X!f%iU1dVpE2+KkGk zI|*(4Tfn59ZjnC%1zI$c7xnm}S-1)kmFHAcU*S54h4u9=;QpgQ76Ei?E!Z#gg$C3u ztxL_UO!Y-D@BUrTAtBpj1C0;Bxumc#DkyUxN&yp2v?Vzr56!Qw?d<9|&ez}y;a=6q zx|EbJMMaE2#>&t|+(tvQfm^?!SH06xhzLAA+pD_2H$fc*jW)g+wEX_Pk#ooAr88W4 z>;wjFVz@y%e-M3;WVgbfAAZjQ34Qn0vo zZ9 zf}GF{0Cr|(;J+#k{Y)Pz-h%${RwF0=%S%ghJ3H<|i`~7wZBtX{ zv9GdG|3a*UphxrgJJI8bhydY5wlOUYjb2ch^#WzupCyxulo%CoMZWjT7>369$Gfw^ zV(HC5#q;b`c>(-ub9LeL6l!teL>RWlH?Z;d8KfgPK;FWwByd;xGRRx>L8bul>*qv8 ziblcDpPvDZEd^Z<^4eX1cujYH6#Pg^>IZ|{lyCtvxqJWqN3iUpj1!Ppfw+*oeWQunL4KxEGg)#ISY_| zmHzU|N{Pt`9mpj)oeyasiyFWK1rTvyxwyF4_?~38wzo4o9~$N5<(-1i&+@dYtZ}iA zXEzNgkORaXDxO!l02agL2ul#7kuhiv?gpt|5wG$wGJ{JgfRG)IJ8njQRb;=dy0Ea& zGpYIllmffP1QKY1tf4@_He6r9)2@=Zy1F`o_K<+-<82_sw-D7~(Bl1P8>MNI+c5!L z0YL#oH3(kHvh=nMbU+UXIS6?3TM(KvA^Cu=q#dF2O|5gei}2UhM5(BxpnjtTJoq8S zw}5CAp)>slqG>x!!rsvlB{OpyV~yjuvQi^X%5yjGW|t zTX~nScmQTuYPYEfV#D`vg}h&2AR9cz6J`hudHMP9bxaO45LvHoijKeaPZ?T2g%3xH zxEdN77*xyrkdET*eH*Tc=E#srav#E-LHYp<2xw44z}#57;d=y-J^bNz7hw>h04=`? zn$xrzXiNk4_E|V}9(id43}0uQE7YFLppGiL3xQS#Jni)Klo3=5z+NMDz7W&u4*!Uk zSxiqN$k@L{N8^LuQxqOLP48L)sit8w9YrdUkk{le8x1@IL31y0WYryYhP_7PAu1c+ z=ioU~u&7{F!+WVdm*6l%2I3FMwa9z{VM#(!@d?sdF3(RIjca%{C38oRFgS4NUAct~ zbd=|o%wg10E91qwpyh%4IQnYrHhVy1-K7Cv05JIfeZd>X6crGkn2i^C!ZWg)PrilP z85~|8N}$`QsIfeCM9NYE}>W1XFoyK70`}B|LL0AtXp(-G)Q9laf5+zi zL(8viKznY1@gw>g=r=CUBUZ=}?y0M*L;L<}BzZ!ye)Dy>mPOFeFd6*Cu8XVdCh#Jg zAUU=`eZbS)+1lPl3{o6{w*dwJ``K^dv*p2Y)|_lfu2-xuAmajY7vT@!Y7NVYiU`o= zdIkmt>r7oRd{0ff?;^L>(2g?ZN&r=CGs%m4kM}#IIahDoFaejlfGauv4@2aEE?hZN zE{unuD~4Z!Oc7w`J_xsBi{Y=sD+s59P#9oX?9QfL5*ivL1}#ApY;4LBdx0y+E8^be ztb+@eIzCVcyTGMKh(9L@ZW&MXCj{Q#b!UAd79?(8t~QUoAIR4kl4M`$6C?1Ah~De% zm4kt(hi5`ybQmFztv2egZ0!XgP7uR{${*Ena{|y^E&yU62?7{f3c$c%aP43`gi?QJ z6!3uqMUaw6t_X?^D6VY?luTwn9)d(11&;vBv1Y!KSEt*Ql^Hmt^{Q=|R4LR0_;6iz zZ~(Bb1p%d#mzV#MB7H)StRz_9sPF?Kq4&l>wApHd=I8;x4!&0(B>utCQ30@p07-TL zlH&(=mX!-zMg{}~APz6DdM^g%HMjw?4+LBT#`-sqX3XHhVb#ETGJOra)~kq!i2r`R zGBGhRLiix?5iGi}4#m`vpNU%Yq`0p3u|M%Up;0_0EyV14mm zDcSptuI6Z!hXL^<+2+-k3=DJ!Fq)7U;UaMt_&`8BV|kwRy+OEIFiCcsNy~LPh?82r z4=pdg0u}odJRo31C_Z=g3l}b|j(qbeE93m^xu*`|x|2*l@Js)a3tSn8dTQ_vMu+3A zL<&+;Fm4SYvm7n(1@8jR%5pvb8P-pb4S@Ejm%U8QFs>g^s5X$p0^r`w)^BQt1I+}!rvuXB4hWP8fz}0X*B3G&A9#%))$Y!Sxgos2 zqhoP-<1P9+a6ThoR=}aN{0Uzq_phPpQ(?p)L@*hCzX>A-&glgdAd-`l^If-%k*lYc zwhoAp^#)L=o|zdf)v@pB2pat#z3H;u#etl90R%X#FjjkTrOB`Y6lQe)R`#s%kQ5$5 zSwlxhAea3D!V)8CIE=N+0?H)0V^-IYl>|9%1Htvca3jM`zp5kN4`hy5iUJZ(0KSHJ zV!8kbyO@+z&|f=xeyb!_&CQK_d$EU_lQSCedtqS>V&tCBz>LT}ckm#hayY-I*MOo5 z+z_S@8LL8pE-R0hmls@U#>jhqvW>uAAdD52lnh}eSlC`P{KHCu1Imx%&)_gnUPDW# zLhdz~rOM*+TMq%U+au^;O0U4%T8v7 zNKT$TdzNo8%wX?F6+;m$if~_mS$_Qc2BPo=O<;m3IORb{M+bpbVHyiedhW?2@OOca z3jQ`70e`qDjzz!eDjaXo16f0;P9BG|+E)~eYRG?v=SB85 z4gJ&7AXz6W6wAF$%rVz7Tkp_+zob6?89+d%R^!11B4*sSl?)k$kCb9(ODaXDZHFG- ztV!1%)RZfcVrL%=KNLE~raW5D@OF3oRC)R4@>=iJkJ<%16rtoYhV0ACu|Li>qGo@s zOF!?a2gzO`y+QokJ+#GByK&E+%aY$QQ9f$)!&}PAgBN|<&nDPDel{>>J3O*h&Y7@8 zjaE7)F(~R<&H3)8kvO8F(1hEBXNlUTM0{Vwhx^r*y{G{Y?aI_?aJtWxu+(wm&r3Y; zw7g^fNk~cx8xm7WPR3i72j@k~-jOjBv9b zaw8@1KF)_PECg>8KEA@gM>xgHffp?LIS5MW@GaZIPhTnNgWS^d(cUcIbBDmp>quDbjr=`B@*P)G zvKO^1X@-}$J}EbR9B80h!1wM%_XRxnruO1QIX0VkqRacLi|MeuLM*}^2envuH*E{P zmVFISL^a=)WNhfIt05X@-(hFW9+1Eu{Ggagm6ZP`{A9~*+X0#@v_W(iZSGqzbO zoA{;PzW1&rz4CZp{Uu#$3yC5(c%=nXjTeRfc_~}YB@~$v_!xttoS&41#(l#Cim@fJ zsFdHCFRxzbzSG}9q(aoiM6Qn)p( z-+1zo?Al(DP5Atj`DaqXhUn)4N5#&wiTpY0yZ^H4@{R<{c*&%?W@3xB=Ng<(kp1kU zR;*w(DjV*CDUsfGV|hA8PFH;Stb3@6_d$3Ff5-xEPV>sG>84sg$1lzwNH2|57QXbL zb~-5}*TEg$=flb0_CNc_U7Op4o<6*ZpW`YAkF?_3C$I*Gbgf@h{xWsL6Bgn-+%vs` z;Umn)6{+)l<9;dK!mdMpLz{-QzBRFOaSnwO3Blu(iocs=V&^!67R*rBmFc`iyZb8uMGT9ZQ)ItSo1 z&Sh!mB9%|Nsg0we!IkmvU_aEmtU9*RQ~dC*41GFBSD!|3ln=osSBjbj4LFN%(_Ph0 zgFJbtNrntXLi1FA*T1$R1WDY7UlktTIo_;slO1QAN`M!G&J5Ud4AV_zkuDuh?^Px3 zTh20@HI}r~86}WiSC+U8yU5`y*bf&YhAj?@OBL{@Errbmxu_zd&aP;7#3*KHrKJ_e z(l6Gnf zPINl$Ac>ru{aRp8`PNv963uVtl9ovcunDv3wOobArZ7k;H8DjecKxJ{ z!2@pfPjRX`jH5a3aj+FkmG(^Xg-8dd%yK(Ccu!tPte4KyYC6S=%fA;Q3+qzsPiENr z)5Fy==xH+38|o#3h2#r^$5&ayS9V4$%yZot*v&B{=(5rWbdRiU=Jka&1W)ZpTYg+j z5faQ$k%c*64tNZ^<~4pE5GAsX(AUt;Vm&PU?Nj0T!OL@vV?JiPx7bNLVpK|h{c8d{ z*|sI$=5(h%>$R3^J#yNmu4{e=tzO1b$>cn<_J`A@{|rGNE)mB`cTKWKHaH@_M{(;` zT5V4<>tG3()bO;~nAl?+SZdsM$KNKtx1el~pZLn#)BTOXl8BPBYSN%;Qq?LJ+&d!n zHi2>@$p2rH^cD##L7j|9t}KH#+QA=Gb^faBHNltK4xbg4x#VtgY-;xXt#O9Um4yyL`GSvBnfLtMW8o0FC>|T zw=*_r$(Ofa@7L4>nK1`+;oQs7Mpd#CzS@*O;}v-F_Fz>3=H>2lhJTF`)1CX73S})? zM2}u1D&0&_Y^SscQAq9I&g1cs#|Wf$xTsg(V;esB$c<1!q}o$$f9l4*MMpM&4#X3o z_fdRwdnv!W85c2JywotELG$C8jOp$mbUtj2rLvr;p{ahK-Fqz+v`3xu<)S+s!O~>{ z-n03q7z;aAjg6=1TM60{A1cYt(Wvh;Q@6~AMr(0uWQK|y3oq+6*C^HShE3j%J$P46 z*BM*cP=)$7a}&;xDMEC|B}73 zsfZE!M}k^n)}0+~zA%=2r6aihxtt~XoA!y+@mjoY_cwjr>lhoMq0kE!gcjzd>0kb) zKIZn2yeO=eNrULUf@zUpKmmCQ)KA@S8apXbJ&c~@D= z@v9i?g`YJc02WWL79ZWtA{;_g}3^?DCc$JJU5Gt9n2N*tZ7EcQG{ z`mO3DdDmVc*ErcvVX*b$zVH20anpuYZ>IWMKbDI6{WQ&4uymreny?~1Ig9?QOz&YL#Jd<<2ZzFv%zUd(8qF|1-@Da0XMLf` zBnt5Yr7L&6d$4f?GhThiFljGx;~)Dtq8If$QWOM&(u^K^ahnt4tz@C`E+pT(5U;W->v8Y zcms-!jfbU59ncsU4L|5yRocIEo0Yp4g(tZurzGFZ;{1;3af2T$_i&|Ub$6QJ8|y2b z^>Y`U;yQlDj$rrW3qU~M2np7MwwoGmyL(tEZ!Yh@dupS1^IXUKItJ(@5J^7|H)LPf zq!_QvdLki~tu`)WLUXe};|bxZ28*#|lEPPxJMxcjcapo3#zJ2ZqSg(}>KiuWT~7Ay ze?G1;E+?uC!}V}(mF7BYA8v~=*}yi2^W#?~zEi>*R?VGt8)}9g?FTqjpuAMLATp*yJHFK#EHFiC*0#-cjr_G zEWKWLw$=a$(B$N6&EWT-Q}$|Iou2dhsVOxl%?JiE2 zZnjie7W&}iE(%>szfaFo2mMJ%A6c`{#7I7ng$K7%PaVn0#QDqDRW8UA3J{$eE+IDd-!ON*MQRV za4q3<#O+aRf{gJu0j{Z{aairY28V+AZB9kSQ$ClhJ9%u&IhQ1KJuQyhwO(nDK3}8C zlWJ`|9b*P3$>;A{Hfk1qbH3-5b4jd*HRF0I$ZTS~^{xr>RSMd?AbvsXC#VsWr~l!@ z!4iI)<>~q+Bh2TfUn0_}K*w{TAp=A{0a2yf)NjJ1c=KtworZ(QQ?t%uq`Qs#Fsa2A?y|G!t z?F_8csyUbh@D8SP_ z4@%G2d|ze!Y#mo|Wu$pFS(nGT#d*_Uz|UR$J8cSBO{DZvLroK}VvipT59)W@8Q}ze z0|c(9cYa@$Xs+&-P?svTE)Jt;s<&p%-ubz1=h?`DJZqjO9O{H@pYk5+@bY=g84{K~MXpe@`4J?n`Y86;0H$8#?D z_ApOLY~wx$_)w~+X+gj0Bx`l$jiy&9m+ritX@89Sh^V*P=`iDP9(g6Dw>KaQD>{my zj^qzf0hKe`LAeO|9c{+IcB8-4k=Bs1LsxxKEVH^#3~9v)bNrGdH>~n%N9!5}4SOaW z&tkmz#F+Ta@>DDI2WYKL#B$y|IZ}k?+bS;ZI2?QTzSI&Ui@mb&Uwj`ib`d@dt2u&~`~G|D*9C#1`kw4mh+r0{fi`>Nq>bmCrU)e zYJUjEd1yYu8IY?|)~Nr)rJcc#f5S6IfEPD`DY}%Kj?hHblH##=~FQ zOl?6Fv4TBfmg|8VrL5$iR2)-&Iu_G{qoS2Ws$pwH@EKQS96cJ9=J(I6rGa>Qsc^Mx z4Uf}O{cuT{WyRpk1wC$DkO?4kW%9U(SE~IF>ABS}r86tpb7#Fp za%lnGw+V%g)wZF*iO?f`jms!CchjU$k}FK(-vp=iR99RoqUf+1@C_px8olk!sq(Y1 zi5=TEW&PsP!QL-d{8j6jSSMvAsJiV~IbUCUeCqil2*0FjI!wK%G0Y5cjcXSaFvW`@T zpEbE>RY(2AT5GJ3hO7{L^Y3BEDUDwV`9zm7V2~)Mm!qPfZlu=|&?psxJ1yWm_0IR! zB$}kB1s_#5HYD&a(b&6UM2q{1tjTG4fCat$gr~CBBCmaMann`R+c(Cd9B2jen3W38 zT&}EynoR2(w2h%A%1R3vC7r2$nw$9hD#&l_-$=RrLEB^LpxkbPEr;^RoDx5B-9%zv zrAiJyJC&PokYZ%XzIFwWmo(6no|TGf*NeUR9xHd>WghMKqA^ z9c+ZAap@dm%-CJ>;d5~^Wtt-Enw~p+AVIh5u$6WGihiwNd#J?8yKF>FK}lZ3sV%~Q zbHb87gI&ipZdJT&U@S9_yI+(0?Z8Cizbr0nlF7^5KNjo1Eo+_riqB1N`)*$BRpPMim$ORg?`fD%=coH3TmMOmKLn*2T$XBSzdIf}PA1ZM+@#m!vU_>zAE+N(+jeAitiJfGuVuGFljU(%CiK^&Gmz$~ zsAj12Y>q_|znU9ow)DJvmLGmN2&&)J2F*2zcFIjX?)jkS#f=hy=lk^abfrskKl&dg z5}Wl?S9+Li44ct@Fo1+{?LW#?GXELcz12H%{tXF=`K6ijfXen#?d;1_x;jH@XHX@U$*G+rk zysV-Nxg#MX7$DrutA)H4ROy4Mrq7tuzoQ$k6#w3;4iPeY_?DjgUhAz6~6r% zc2TrgD!#3vWXoOHG+-AKu4zmXwWn%Gs9Z3;$T@k!72@A_f!9@xskUYQLyWFY-k`Ez zmrJqRM5#^KY;vw$`~`9`Iz_Xy?aT9UG<|H?aQ9g^EbIZ&Wiq-eNg$)~iknkxPIune ziN1ADn*Q-9(0sl#&vw^O0x6w(1@FzQZYHcQPM)t>{Vpu_`$2qbxve_h^V(*?VtFD( z9H*|~5wk#aSkDJJ*#=KCQV)VUNTi|A=hFv2v{$rvm`KJMq2Ae+#Y>jk(b^XS*+_q3 zFdbCTc1&m61u%=LA>6@~$(Tgs(@}&HtzS=Z)>MTo)M3kB-z9qA@pNL}X)mSPk4(iJ z3ydjWLGweLs;HnVfxx*}{eUzQ`(~3))ZLlZLW#yMLZQzMfS2Q*Vwphdy3cQUX9^_| zFFt;6rrsind&bbyGbdAS+bd(>#O{$O=FJ(S(0I}2rrfj6b@V$q8%)Rt5DD@9DtXCp z$@|>#1R+dD_j^lIk?htbF5GHQq2M{@K63i=NXv?z7=J0SGX4r?mgL|Jq_``gMv!u~ zo#U8`eaXTJ#lddq5NvdIyj2n@VU$>=ud6e8{YcD-YA)=9VvD!v+aJXdEPy3Ief-WH zt^QkEFKbJrRwe`VWZj`Idxv~p_FT_dQF5eQ9`#XvY$RL|NHzK-qrWG z4ljOL>G0Z$x$CEj0E)3ipsaDB!GnzW=4wb%F$r)3ysK=fRt_{NuK=>yu~?MJPve8U z03wB3sbh1$PUHKRHt!}E94Bn4=E4i;_@hkx-u33i^MFUh#@&6Eq);G0n*d?$#H7i} z0n4F&R8(4t&Sd@9^tHO|$(-_9llxGOJ>WqzvHu9=;dY4*`6Rpl5HScye}iiaaVA4l zf&zZmPmLbkn1#x)@v}0HTzJJ%r0~Ve$9u%R$GgV6>XI%?s!=(%Woh;CV}gwKp;tqs zR8qlx5Z6F~r8?R)!&(Dct98K9@ffMz;Cuo5N#Z&FL7jfKq5ul*r7Sf$uuRj&Y4vhe zbI;8$%vK!WEw8=$R6NFd?;wMszR0O~{->7}08wmI&@Vx8R#iLRprWqaRUiH)tHk53 z6fIMAqZ#Bu;5f?ivIJy?%yH8Qf1m89Ny$zP-IJP~?5^QuzW-jB_Ryn>#UHZ3+ryF4 zQ-o1nq_N6ULAZ6JqFq13h0dy%e{3Xl(xj%J{7^gv5-iD#&A|UUoq4-z59yt^rAY*U zmH=S3+3-#=^}=qbr){|3aq+x=eLbf}mUtW8Y@je3!C8RCVjdkuX?XTnt`|Ir4%r}{ zGY?K0b0DgADJUaleRM;1YmRJ9{iO}(JU1Tzacs(Dbi>3_B1{@F09`E>NFtj{)k}fi z!PJ#%9iyu;4C;fTl>2#{O{+ClcVInCHt|BKy#G$?W8to+N~OH+#_~qhgrSSI0?hz5 zVq-fko?30jeWx+DbHHe9tOC#VQCX`^jOUhmO{sBZDuL2pe&$XSU%W>j@G~g+&bFW! zPqo4g2FF2W1^et}9MLD?y@(y`WFA(6V2b@bw-~VmiP8OYj&wdss4leAnT%}-Yo)IR zL`3SoZl7V?A!japV@E~}7nhAhLGdR{F_SHr<#miLwZ>_Rt*mPbclSVz%dni4VM~B8 z0`CMi3V(yZ1BIRW;#PNH=4LAop7OVoLq#77P*q!vW;MCh{yerd?=e;H>dU}_T$>DF z&iQPx0% z3`F~+>WhG)GbB7cjC8XPl%{_OCYp|6Vkc1yWmvBD6{qtjH2tW10mPZX<3#g4+rm6@ zYVytCwj$SLE}Gv9s)9^u{rYx8e|^AcR!Ba=PVHMXwiEZowLShhA6yIN%bucD{KdfK z;^av46^sXD2}&9==$>xjJ+Bf&x_8!-YuHb(y=; zjQSqo6b=7|I9u3flEp z$*7gDPy($r_wh^+Xq(a4XcISD4=9MSsNOT4th@i{*`Y0aWjJjD8EbU~C2eU?+?5KK zl85Cf8n^5_qG5Odo0yT{v$&CpDxt44aSubh0#>W@jG7x>o>f5Buu!$3yY%arRvVjE zkaiyKtUi|o5{z7;Ibv~GMFq&?ftIj{*@soBGJRCAjx{Q6~b>DBS+%17rRDV1Sr z1$=^gXz2&z!yk)pI9UuBnePrdc$_)v z{>RnLMOKZ7=YRG(;jiGoC(I+CW}5onLne*?$JvwrM_+#A=F>M$F|L5!l8T-@G9QhS zZXO9gmhKir9$iaRc^{L52Pf%)M<+pG8SWbIT8v@&YwMDrtNYerMuAz|PDSQBMkN4< zJ&RuLNS*uHdvKgnofaSG!+~s$1lLGmr3lp|G4}swoN0oj56jg4ju1tY?j!97zJE4< fZ~lJ?sUvUYB2iI(- - - + + classes - + Device - -Device + +Device GPIODevice - -GPIODevice + +GPIODevice GPIODevice->Device - - - - -WaitableInputDevice - -WaitableInputDevice - - -InputDevice - -InputDevice - - -WaitableInputDevice->InputDevice - - + + -SmoothedInputDevice +SmoothedInputDevice SmoothedInputDevice - -SmoothedInputDevice->WaitableInputDevice - - + +InputDevice + +InputDevice + + +SmoothedInputDevice->InputDevice + + InputDevice->GPIODevice - - + + -DigitalInputDevice - -DigitalInputDevice +DigitalInputDevice + +DigitalInputDevice - -DigitalInputDevice->WaitableInputDevice - - + +DigitalInputDevice->InputDevice + + -Button +Button Button -Button->DigitalInputDevice - - +Button->DigitalInputDevice + + -MotionSensor +MotionSensor MotionSensor -MotionSensor->SmoothedInputDevice +MotionSensor->SmoothedInputDevice -LightSensor +LightSensor LightSensor -LightSensor->SmoothedInputDevice +LightSensor->SmoothedInputDevice -LineSensor +LineSensor LineSensor -LineSensor->SmoothedInputDevice +LineSensor->SmoothedInputDevice -DistanceSensor +DistanceSensor DistanceSensor -DistanceSensor->SmoothedInputDevice +DistanceSensor->SmoothedInputDevice -OutputDevice - -OutputDevice +OutputDevice + +OutputDevice -OutputDevice->GPIODevice - - +OutputDevice->GPIODevice + + -DigitalOutputDevice - -DigitalOutputDevice +DigitalOutputDevice + +DigitalOutputDevice -DigitalOutputDevice->OutputDevice - - +DigitalOutputDevice->OutputDevice + + -LED - -LED +LED + +LED -LED->DigitalOutputDevice - - +LED->DigitalOutputDevice + + -Buzzer - -Buzzer +Buzzer + +Buzzer -Buzzer->DigitalOutputDevice - - +Buzzer->DigitalOutputDevice + + -PWMOutputDevice - -PWMOutputDevice +PWMOutputDevice + +PWMOutputDevice -PWMOutputDevice->OutputDevice - - +PWMOutputDevice->OutputDevice + + -PWMLED - -PWMLED +PWMLED + +PWMLED -PWMLED->PWMOutputDevice - - +PWMLED->PWMOutputDevice + + diff --git a/docs/images/other_device_hierarchy.dot b/docs/images/other_device_hierarchy.dot new file mode 100644 index 0000000..c8654d7 --- /dev/null +++ b/docs/images/other_device_hierarchy.dot @@ -0,0 +1,19 @@ +/* vim: set et sw=4 sts=4: */ + +digraph classes { + graph [rankdir=BT]; + node [shape=rect, style=filled, fontname=Sans, fontsize=10]; + edge []; + + /* Abstract classes */ + node [color="#9ec6e0", fontcolor="#000000"] + Device; + InternalDevice; + + /* Concrete classes */ + node [color="#2980b9", fontcolor="#ffffff"]; + + InternalDevice->Device; + TimeOfDay->InternalDevice; + PingServer->InternalDevice; +} diff --git a/docs/images/other_device_hierarchy.pdf b/docs/images/other_device_hierarchy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dcfa7dede2edcbe48d4978ba67d470d65ffaa937 GIT binary patch literal 7383 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 an(TJ?=~)ZiJ>YN)NI_QhZLze;m;V8NWT;&L literal 0 HcmV?d00001 diff --git a/docs/images/other_device_hierarchy.svg b/docs/images/other_device_hierarchy.svg new file mode 100644 index 0000000..275484c --- /dev/null +++ b/docs/images/other_device_hierarchy.svg @@ -0,0 +1,48 @@ + + + + + + +classes + + +Device + +Device + + +InternalDevice + +InternalDevice + + +InternalDevice->Device + + + + +TimeOfDay + +TimeOfDay + + +TimeOfDay->InternalDevice + + + + +PingServer + +PingServer + + +PingServer->InternalDevice + + + + + diff --git a/docs/index.rst b/docs/index.rst index 2f5498f..14357b4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,7 +12,9 @@ Table of Contents api_output api_spi api_boards + api_other api_generic + api_source_tools api_pins api_exc changelog diff --git a/gpiozero/__init__.py b/gpiozero/__init__.py index 6ac96ea..b5200d1 100644 --- a/gpiozero/__init__.py +++ b/gpiozero/__init__.py @@ -11,6 +11,7 @@ from .pins import ( from .exc import ( GPIOZeroError, DeviceClosed, + BadEventHandler, CompositeDeviceError, CompositeDeviceBadName, SPIError, @@ -45,13 +46,15 @@ from .devices import ( Device, GPIODevice, CompositeDevice, +) +from .mixins import ( SharedMixin, SourceMixin, ValuesMixin, + EventsMixin, ) from .input_devices import ( InputDevice, - WaitableInputDevice, DigitalInputDevice, SmoothedInputDevice, Button, @@ -103,3 +106,24 @@ from .boards import ( CamJamKitRobot, Energenie, ) +from .other_devices import ( + InternalDevice, + PingServer, + TimeOfDay, +) +from .source_tools import ( + averaged, + clamped, + conjunction, + cos_values, + disjunction, + inverted, + negated, + post_delayed, + pre_delayed, + quantized, + queued, + random_values, + scaled, + sin_values, +) diff --git a/gpiozero/boards.py b/gpiozero/boards.py index d799dbd..24ec9d5 100644 --- a/gpiozero/boards.py +++ b/gpiozero/boards.py @@ -22,7 +22,8 @@ from .exc import ( from .input_devices import Button from .output_devices import OutputDevice, LED, PWMLED, Buzzer, Motor from .threads import GPIOThread -from .devices import Device, CompositeDevice, SharedMixin, SourceMixin +from .devices import Device, CompositeDevice +from .mixins import SharedMixin, SourceMixin class CompositeOutputDevice(SourceMixin, CompositeDevice): diff --git a/gpiozero/devices.py b/gpiozero/devices.py index 527ac28..5493664 100644 --- a/gpiozero/devices.py +++ b/gpiozero/devices.py @@ -15,6 +15,10 @@ from types import FunctionType from threading import RLock from .threads import GPIOThread, _threads_shutdown +from .mixins import ( + ValuesMixin, + SharedMixin, + ) from .exc import ( DeviceClosed, GPIOPinMissing, @@ -201,127 +205,35 @@ class GPIOBase(GPIOMeta(nstr('GPIOBase'), (), {})): self.close() -class ValuesMixin(object): - """ - Adds a :attr:`values` property to the class which returns an infinite - generator of readings from the :attr:`value` property. - - .. note:: - - Use this mixin *first* in the parent class list. - """ - - @property - def values(self): - """ - An infinite iterator of values read from `value`. - """ - while True: - try: - yield self.value - except GPIODeviceClosed: - break - - -class SourceMixin(object): - """ - Adds a :attr:`source` property to the class which, given an iterable, - sets :attr:`value` to each member of that iterable until it is exhausted. - - .. note:: - - Use this mixin *first* in the parent class list. - """ - - def __init__(self, *args, **kwargs): - self._source = None - self._source_thread = None - self._source_delay = 0.01 - super(SourceMixin, self).__init__(*args, **kwargs) - - def close(self): - try: - super(SourceMixin, self).close() - except AttributeError: - pass - self.source = None - - def _copy_values(self, source): - for v in source: - self.value = v - if self._source_thread.stopping.wait(self._source_delay): - break - - @property - def source_delay(self): - """ - The delay (measured in seconds) in the loop used to read values from - :attr:`source`. Defaults to 0.01 seconds which is generally sufficient - to keep CPU usage to a minimum while providing adequate responsiveness. - """ - return self._source_delay - - @source_delay.setter - def source_delay(self, value): - if value < 0: - raise GPIOBadSourceDelay('source_delay must be 0 or greater') - self._source_delay = float(value) - - @property - def source(self): - """ - The iterable to use as a source of values for :attr:`value`. - """ - return self._source - - @source.setter - def source(self, value): - if self._source_thread is not None: - self._source_thread.stop() - self._source_thread = None - self._source = value - if value is not None: - self._source_thread = GPIOThread(target=self._copy_values, args=(value,)) - self._source_thread.start() - - -class SharedMixin(object): - """ - This mixin marks a class as "shared". In this case, the meta-class - (GPIOMeta) will use :meth:`_shared_key` to convert the constructor - arguments to an immutable key, and will check whether any existing - instances match that key. If they do, they will be returned by the - constructor instead of a new instance. An internal reference counter is - used to determine how many times an instance has been "constructed" in this - way. - - When :meth:`close` is called, an internal reference counter will be - decremented and the instance will only close when it reaches zero. - """ - _INSTANCES = {} - - def __del__(self): - self._refs = 0 - super(SharedMixin, self).__del__() - - @classmethod - def _shared_key(cls, *args, **kwargs): - """ - Given the constructor arguments, returns an immutable key representing - the instance. The default simply assumes all positional arguments are - immutable. - """ - return args - - class Device(ValuesMixin, GPIOBase): """ Represents a single device of any type; GPIO-based, SPI-based, I2C-based, - etc. This is the base class of the device hierarchy. + etc. This is the base class of the device hierarchy. It defines the + basic services applicable to all devices (specifically thhe :attr:`is_active` + property, the :attr:`value` property, and the :meth:`close` method). """ def __repr__(self): return "" % (self.__class__.__name__) + @property + def value(self): + """ + Returns a value representing the device's state. Frequently, this is a + boolean value, or a number between 0 and 1 but some devices use larger + ranges (e.g. -1 to +1) and composite devices usually use tuples to + return the states of all their subordinate components. + """ + return 0 + + @property + def is_active(self): + """ + Returns ``True`` if the device is currently active and ``False`` + otherwise. This property is usually derived from :attr:`value`. Unlike + :attr:`value`, this is *always* a boolean. + """ + return bool(self.value) + class CompositeDevice(Device): """ @@ -417,15 +329,16 @@ class CompositeDevice(Device): def value(self): return self.tuple(*(device.value for device in self)) + @property + def is_active(self): + return any(self.value) + class GPIODevice(Device): """ - Extends :class:`Device`. Represents a generic GPIO device. - - This is the class at the root of the gpiozero class hierarchy. It handles - ensuring that two GPIO devices do not share the same pin, and provides - basic services applicable to all devices (specifically the :attr:`pin` - property, :attr:`is_active` property, and the :attr:`close` method). + Extends :class:`Device`. Represents a generic GPIO device and provides + the services common to all single-pin GPIO devices (like ensuring two + GPIO devices do no share a :attr:`pin`). :param int pin: The GPIO pin (in BCM numbering) that the device is connected to. If @@ -494,14 +407,8 @@ class GPIODevice(Device): @property def value(self): - """ - Returns ``True`` if the device is currently active and ``False`` - otherwise. - """ return self._read() - is_active = value - def __repr__(self): try: return "" % ( diff --git a/gpiozero/exc.py b/gpiozero/exc.py index de2a530..e3b262e 100644 --- a/gpiozero/exc.py +++ b/gpiozero/exc.py @@ -13,6 +13,9 @@ class GPIOZeroError(Exception): class DeviceClosed(GPIOZeroError): "Error raised when an operation is attempted on a closed device" +class BadEventHandler(GPIOZeroError, ValueError): + "Error raised when an event handler with an incompatible prototype is specified" + class CompositeDeviceError(GPIOZeroError): "Base class for errors specific to the CompositeDevice hierarchy" diff --git a/gpiozero/input_devices.py b/gpiozero/input_devices.py index bafc67a..b19f802 100644 --- a/gpiozero/input_devices.py +++ b/gpiozero/input_devices.py @@ -7,15 +7,12 @@ from __future__ import ( division, ) -import inspect import warnings -from functools import wraps from time import sleep, time -from threading import Event from .exc import InputDeviceError, GPIODeviceError, GPIODeviceClosed from .devices import GPIODevice, CompositeDevice -from .threads import GPIOQueue +from .mixins import GPIOQueue, EventsMixin class InputDevice(GPIODevice): @@ -65,148 +62,7 @@ class InputDevice(GPIODevice): return super(InputDevice, self).__repr__() -class WaitableInputDevice(InputDevice): - """ - Represents a generic input device with distinct waitable states. - - This class extends :class:`InputDevice` with methods for waiting on the - device's status (:meth:`wait_for_active` and :meth:`wait_for_inactive`), - and properties that hold functions to be called when the device changes - state (:meth:`when_activated` and :meth:`when_deactivated`). These are - aliased appropriately in various subclasses. - - .. note:: - - Note that this class provides no means of actually firing its events; - it's effectively an abstract base class. - """ - def __init__(self, pin=None, pull_up=False): - super(WaitableInputDevice, self).__init__(pin, pull_up) - self._active_event = Event() - self._inactive_event = Event() - self._when_activated = None - self._when_deactivated = None - self._last_state = None - - def wait_for_active(self, timeout=None): - """ - Pause the script until the device is activated, or the timeout is - reached. - - :param float timeout: - Number of seconds to wait before proceeding. If this is ``None`` - (the default), then wait indefinitely until the device is active. - """ - return self._active_event.wait(timeout) - - def wait_for_inactive(self, timeout=None): - """ - Pause the script until the device is deactivated, or the timeout is - reached. - - :param float timeout: - Number of seconds to wait before proceeding. If this is ``None`` - (the default), then wait indefinitely until the device is inactive. - """ - return self._inactive_event.wait(timeout) - - @property - def when_activated(self): - """ - The function to run when the device changes state from inactive to - active. - - This can be set to a function which accepts no (mandatory) parameters, - or a Python function which accepts a single mandatory parameter (with - as many optional parameters as you like). If the function accepts a - single mandatory parameter, the device that activated will be passed - as that parameter. - - Set this property to ``None`` (the default) to disable the event. - """ - return self._when_activated - - @when_activated.setter - def when_activated(self, value): - self._when_activated = self._wrap_callback(value) - - @property - def when_deactivated(self): - """ - The function to run when the device changes state from active to - inactive. - - This can be set to a function which accepts no (mandatory) parameters, - or a Python function which accepts a single mandatory parameter (with - as many optional parameters as you like). If the function accepts a - single mandatory parameter, the device that deactivated will be - passed as that parameter. - - Set this property to ``None`` (the default) to disable the event. - """ - return self._when_deactivated - - @when_deactivated.setter - def when_deactivated(self, value): - self._when_deactivated = self._wrap_callback(value) - - def _wrap_callback(self, fn): - if fn is None: - return None - elif not callable(fn): - raise InputDeviceError('value must be None or a callable') - elif inspect.isbuiltin(fn): - # We can't introspect the prototype of builtins. In this case we - # assume that the builtin has no (mandatory) parameters; this is - # the most reasonable assumption on the basis that pre-existing - # builtins have no knowledge of gpiozero, and the sole parameter - # we would pass is a gpiozero object - return fn - else: - # Try binding ourselves to the argspec of the provided callable. - # If this works, assume the function is capable of accepting no - # parameters - try: - inspect.getcallargs(fn) - return fn - except TypeError: - try: - # If the above fails, try binding with a single parameter - # (ourselves). If this works, wrap the specified callback - inspect.getcallargs(fn, self) - @wraps(fn) - def wrapper(): - return fn(self) - return wrapper - except TypeError: - raise InputDeviceError( - 'value must be a callable which accepts up to one ' - 'mandatory parameter') - - def _fire_events(self): - old_state = self._last_state - new_state = self._last_state = self.is_active - if old_state is None: - # Initial "indeterminate" state; set events but don't fire - # callbacks as there's not necessarily an edge - if new_state: - self._active_event.set() - else: - self._inactive_event.set() - else: - if not old_state and new_state: - self._inactive_event.clear() - self._active_event.set() - if self.when_activated: - self.when_activated() - elif old_state and not new_state: - self._active_event.clear() - self._inactive_event.set() - if self.when_deactivated: - self.when_deactivated() - - -class DigitalInputDevice(WaitableInputDevice): +class DigitalInputDevice(EventsMixin, InputDevice): """ Represents a generic input device with typical on/off behaviour. @@ -233,7 +89,7 @@ class DigitalInputDevice(WaitableInputDevice): raise -class SmoothedInputDevice(WaitableInputDevice): +class SmoothedInputDevice(EventsMixin, InputDevice): """ Represents a generic input device which takes its value from the mean of a queue of historical values. diff --git a/gpiozero/mixins.py b/gpiozero/mixins.py new file mode 100644 index 0000000..979b917 --- /dev/null +++ b/gpiozero/mixins.py @@ -0,0 +1,326 @@ +from __future__ import ( + unicode_literals, + print_function, + absolute_import, + division, + ) +nstr = str +str = type('') + +import inspect +import weakref +from functools import wraps +from threading import Event +from collections import deque +try: + from statistics import median, mean +except ImportError: + from .compat import median, mean + +from .threads import GPIOThread +from .exc import BadEventHandler, DeviceClosed + + +class ValuesMixin(object): + """ + Adds a :attr:`values` property to the class which returns an infinite + generator of readings from the :attr:`value` property. + + .. note:: + + Use this mixin *first* in the parent class list. + """ + + @property + def values(self): + """ + An infinite iterator of values read from `value`. + """ + while True: + try: + yield self.value + except DeviceClosed: + break + + +class SourceMixin(object): + """ + Adds a :attr:`source` property to the class which, given an iterable, + sets :attr:`value` to each member of that iterable until it is exhausted. + + .. note:: + + Use this mixin *first* in the parent class list. + """ + + def __init__(self, *args, **kwargs): + self._source = None + self._source_thread = None + self._source_delay = 0.01 + super(SourceMixin, self).__init__(*args, **kwargs) + + def close(self): + try: + super(SourceMixin, self).close() + except AttributeError: + pass + self.source = None + + def _copy_values(self, source): + for v in source: + self.value = v + if self._source_thread.stopping.wait(self._source_delay): + break + + @property + def source_delay(self): + """ + The delay (measured in seconds) in the loop used to read values from + :attr:`source`. Defaults to 0.01 seconds which is generally sufficient + to keep CPU usage to a minimum while providing adequate responsiveness. + """ + return self._source_delay + + @source_delay.setter + def source_delay(self, value): + if value < 0: + raise GPIOBadSourceDelay('source_delay must be 0 or greater') + self._source_delay = float(value) + + @property + def source(self): + """ + The iterable to use as a source of values for :attr:`value`. + """ + return self._source + + @source.setter + def source(self, value): + if self._source_thread is not None: + self._source_thread.stop() + self._source_thread = None + self._source = value + if value is not None: + self._source_thread = GPIOThread(target=self._copy_values, args=(value,)) + self._source_thread.start() + + +class SharedMixin(object): + """ + This mixin marks a class as "shared". In this case, the meta-class + (GPIOMeta) will use :meth:`_shared_key` to convert the constructor + arguments to an immutable key, and will check whether any existing + instances match that key. If they do, they will be returned by the + constructor instead of a new instance. An internal reference counter is + used to determine how many times an instance has been "constructed" in this + way. + + When :meth:`close` is called, an internal reference counter will be + decremented and the instance will only close when it reaches zero. + """ + _INSTANCES = {} + + def __del__(self): + self._refs = 0 + super(SharedMixin, self).__del__() + + @classmethod + def _shared_key(cls, *args, **kwargs): + """ + Given the constructor arguments, returns an immutable key representing + the instance. The default simply assumes all positional arguments are + immutable. + """ + return args + + +class EventsMixin(object): + """ + Adds edge-detected :meth:`when_activated` and :meth:`when_deactivated` + events to a device based on changes to the :attr:`~Device.is_active` + property common to all devices. Also adds :meth:`wait_for_active` and + :meth:`wait_for_inactive` methods for level-waiting. + + .. note:: + + Note that this mixin provides no means of actually firing its events; + call :meth:`_fire_events` in sub-classes when device state changes to + trigger the events. This should also be called once at the end of + initialization to set initial states. + """ + def __init__(self, *args, **kwargs): + super(EventsMixin, self).__init__(*args, **kwargs) + self._active_event = Event() + self._inactive_event = Event() + self._when_activated = None + self._when_deactivated = None + self._last_state = None + + def wait_for_active(self, timeout=None): + """ + Pause the script until the device is activated, or the timeout is + reached. + + :param float timeout: + Number of seconds to wait before proceeding. If this is ``None`` + (the default), then wait indefinitely until the device is active. + """ + return self._active_event.wait(timeout) + + def wait_for_inactive(self, timeout=None): + """ + Pause the script until the device is deactivated, or the timeout is + reached. + + :param float timeout: + Number of seconds to wait before proceeding. If this is ``None`` + (the default), then wait indefinitely until the device is inactive. + """ + return self._inactive_event.wait(timeout) + + @property + def when_activated(self): + """ + The function to run when the device changes state from inactive to + active. + + This can be set to a function which accepts no (mandatory) parameters, + or a Python function which accepts a single mandatory parameter (with + as many optional parameters as you like). If the function accepts a + single mandatory parameter, the device that activated will be passed + as that parameter. + + Set this property to ``None`` (the default) to disable the event. + """ + return self._when_activated + + @when_activated.setter + def when_activated(self, value): + self._when_activated = self._wrap_callback(value) + + @property + def when_deactivated(self): + """ + The function to run when the device changes state from active to + inactive. + + This can be set to a function which accepts no (mandatory) parameters, + or a Python function which accepts a single mandatory parameter (with + as many optional parameters as you like). If the function accepts a + single mandatory parameter, the device that deactivated will be + passed as that parameter. + + Set this property to ``None`` (the default) to disable the event. + """ + return self._when_deactivated + + @when_deactivated.setter + def when_deactivated(self, value): + self._when_deactivated = self._wrap_callback(value) + + def _wrap_callback(self, fn): + if fn is None: + return None + elif not callable(fn): + raise BadEventHandler('value must be None or a callable') + elif inspect.isbuiltin(fn): + # We can't introspect the prototype of builtins. In this case we + # assume that the builtin has no (mandatory) parameters; this is + # the most reasonable assumption on the basis that pre-existing + # builtins have no knowledge of gpiozero, and the sole parameter + # we would pass is a gpiozero object + return fn + else: + # Try binding ourselves to the argspec of the provided callable. + # If this works, assume the function is capable of accepting no + # parameters + try: + inspect.getcallargs(fn) + return fn + except TypeError: + try: + # If the above fails, try binding with a single parameter + # (ourselves). If this works, wrap the specified callback + inspect.getcallargs(fn, self) + @wraps(fn) + def wrapper(): + return fn(self) + return wrapper + except TypeError: + raise BadEventHandler( + 'value must be a callable which accepts up to one ' + 'mandatory parameter') + + def _fire_events(self): + old_state = self._last_state + new_state = self._last_state = self.is_active + if old_state is None: + # Initial "indeterminate" state; set events but don't fire + # callbacks as there's not necessarily an edge + if new_state: + self._active_event.set() + else: + self._inactive_event.set() + else: + if not old_state and new_state: + self._inactive_event.clear() + self._active_event.set() + if self.when_activated: + self.when_activated() + elif old_state and not new_state: + self._active_event.clear() + self._inactive_event.set() + if self.when_deactivated: + self.when_deactivated() + + +class GPIOQueue(GPIOThread): + """ + Extends :class:`GPIOThread`. Provides a background thread that monitors a + device's values and provides a running *average* (defaults to median) of + those values. If the *parent* device includes the :class:`EventsMixin` in + its ancestry, the thread automatically calls + :meth:`~EventsMixin._fire_events`. + """ + def __init__( + self, parent, queue_len=5, sample_wait=0.0, partial=False, + average=median): + assert callable(average) + super(GPIOQueue, self).__init__(target=self.fill) + if queue_len < 1: + raise GPIOBadQueueLen('queue_len must be at least one') + if sample_wait < 0: + raise GPIOBadSampleWait('sample_wait must be 0 or greater') + self.queue = deque(maxlen=queue_len) + self.partial = partial + self.sample_wait = sample_wait + self.full = Event() + self.parent = weakref.proxy(parent) + self.average = average + + @property + def value(self): + if not self.partial: + self.full.wait() + try: + return self.average(self.queue) + except ZeroDivisionError: + # No data == inactive value + return 0.0 + + def fill(self): + try: + while (not self.stopping.wait(self.sample_wait) and + len(self.queue) < self.queue.maxlen): + self.queue.append(self.parent._read()) + if self.partial and isinstance(self.parent, EventsMixin): + self.parent._fire_events() + self.full.set() + while not self.stopping.wait(self.sample_wait): + self.queue.append(self.parent._read()) + if isinstance(self.parent, EventsMixin): + self.parent._fire_events() + except ReferenceError: + # Parent is dead; time to die! + pass + diff --git a/gpiozero/other_devices.py b/gpiozero/other_devices.py new file mode 100644 index 0000000..1d0f3da --- /dev/null +++ b/gpiozero/other_devices.py @@ -0,0 +1,168 @@ +# vim: set fileencoding=utf-8: + +from __future__ import ( + unicode_literals, + print_function, + absolute_import, + division, +) +str = type('') + + +import os +import io +import subprocess +from datetime import datetime, time + +from .devices import Device +from .mixins import EventsMixin + + +class InternalDevice(EventsMixin, Device): + """ + Extends :class:`Device` to provide a basis for devices which have no + specific hardware representation. This are effectively pseudo-devices and + usually represent operating system services like the internal clock, file + systems or network facilities. + """ + + +class PingServer(InternalDevice): + """ + Extends :class:`InternalDevice` to provide a device which is active when a + *host* on the network can be pinged. + + The following example lights an LED while a server is reachable (note the + use of :attr:`~SourceMixin.source_delay` to ensure the server is not + flooded with pings):: + + from gpiozero import PingServer, LED + from signal import pause + + server = PingServer('my-server') + led = LED(4) + led.source_delay = 1 + led.source = server.values + pause() + + :param str host: + The hostname or IP address to attempt to ping. + """ + def __init__(self, host): + self.host = host + super(PingServer, self).__init__() + self._fire_events() + + def __repr__(self): + return '' % self.host + + @property + def value(self): + # XXX This is doing a DNS lookup every time it's queried; should we + # call gethostbyname in the constructor and ping that instead (good + # for consistency, but what if the user *expects* the host to change + # address?) + with io.open(os.devnull, 'wb') as devnull: + try: + subprocess.check_call( + ['ping', '-c1', self.host], + stdout=devnull, stderr=devnull) + except subprocess.CalledProcessError: + return False + else: + return True + + +class TimeOfDay(InternalDevice): + """ + Extends :class:`InternalDevice` to provide a device which is active when + the computer's clock indicates that the current time is between + *start_time* and *end_time* (inclusive) which are :class:`~datetime.time` + instances. + + The following example turns on a lamp attached to an :class:`Energenie` + plug between 7 and 8 AM:: + + from datetime import time + from gpiozero import TimeOfDay, Energenie + from signal import pause + + lamp = Energenie(0) + morning = TimeOfDay(time(7), time(8)) + morning.when_activated = lamp.on + morning.when_deactivated = lamp.off + pause() + + :param ~datetime.time start_time: + The time from which the device will be considered active. + + :param ~datetime.time end_time: + The time after which the device will be considered inactive. + + :param bool utc: + If ``True`` (the default), a naive UTC time will be used for the + comparison rather than a local time-zone reading. + """ + def __init__(self, start_time, end_time, utc=True): + self._start_time = None + self._end_time = None + self._utc = True + super(TimeOfDay, self).__init__() + self.start_time = start_time + self.end_time = end_time + self.utc = utc + self._fire_events() + + def __repr__(self): + return '' % ( + self.start_time, self.end_time, ('local', 'UTC')[self.utc]) + + @property + def start_time(self): + """ + The time of day after which the device will be considered active. + """ + return self._start_time + + @start_time.setter + def start_time(self, value): + if isinstance(value, datetime): + value = value.time() + if not isinstance(value, time): + raise ValueError('start_time must be a datetime, or time instance') + self._start_time = value + + @property + def end_time(self): + """ + The time of day after which the device will be considered inactive. + """ + return self._end_time + + @end_time.setter + def end_time(self, value): + if isinstance(value, datetime): + value = value.time() + if not isinstance(value, time): + raise ValueError('end_time must be a datetime, or time instance') + self._end_time = value + + @property + def utc(self): + """ + If ``True``, use a naive UTC time reading for comparison instead of a + local timezone reading. + """ + return self._utc + + @utc.setter + def utc(self, value): + self._utc = bool(value) + + @property + def value(self): + if self.utc: + return self.start_time <= datetime.utcnow().time() <= self.end_time + else: + return self.start_time <= datetime.now().time() <= self.end_time + diff --git a/gpiozero/output_devices.py b/gpiozero/output_devices.py index c65dc73..e5f806d 100644 --- a/gpiozero/output_devices.py +++ b/gpiozero/output_devices.py @@ -11,7 +11,8 @@ from threading import Lock from itertools import repeat, cycle, chain from .exc import OutputDeviceBadValue, GPIOPinMissing -from .devices import GPIODevice, Device, CompositeDevice, SourceMixin +from .devices import GPIODevice, Device, CompositeDevice +from .mixins import SourceMixin from .threads import GPIOThread diff --git a/gpiozero/source_tools.py b/gpiozero/source_tools.py new file mode 100644 index 0000000..4aa1b51 --- /dev/null +++ b/gpiozero/source_tools.py @@ -0,0 +1,296 @@ +# vim: set fileencoding=utf-8: + +from __future__ import ( + unicode_literals, + print_function, + absolute_import, + division, +) +str = type('') + + +from random import random +from time import sleep +try: + from itertools import izip as zip +except ImportError: + pass +from itertools import count, cycle +from math import sin, cos, floor +try: + from statistics import mean +except ImportError: + from .compat import mean + + +def negated(values): + """ + Returns the negation of the supplied values (``True`` becomes ``False``, + and ``False`` becomes ``True``). For example:: + + from gpiozero import Button, LED, negated + from signal import pause + + led = LED(4) + btn = Button(17) + led.source = negated(btn.values) + pause() + """ + for v in values: + yield not v + + +def inverted(values): + """ + Returns the inversion of the supplied values (1 becomes 0, 0 becomes 1, + 0.1 becomes 0.9, etc.). For example:: + + from gpiozero import MCP3008, PWMLED, inverted + from signal import pause + + led = PWMLED(4) + pot = MCP3008(channel=0) + led.source = inverted(pot.values) + pause() + """ + for v in values: + yield 1 - v + + +def scaled(values, range_min, range_max, domain_min=0, domain_max=1): + """ + Returns *values* scaled from *range_min* to *range_max*, assuming that all + items in *values* lie between *domain_min* and *domain_max* (which default + to 0 and 1 respectively). For example, to control the direction of a motor + (which is represented as a value between -1 and 1) using a potentiometer + (which typically provides values between 0 and 1):: + + from gpiozero import Motor, MCP3008, scaled + from signal import pause + + motor = Motor(20, 21) + pot = MCP3008(channel=0) + motor.source = scaled(pot.values, -1, 1) + pause() + """ + domain_size = domain_max - domain_min + range_size = range_max - range_min + for v in values: + yield (((v - domain_min) / domain_size) * range_size) + range_min + + +def clamped(values, range_min=0, range_max=1): + """ + Returns *values* clamped from *range_min* to *range_max*, i.e. any items + less than *range_min* will be returned as *range_min* and any items + larger than *range_max* will be returned as *range_max* (these default to + 0 and 1 respectively). For example:: + + from gpiozero import PWMLED, MCP3008, clamped + from signal import pause + + led = PWMLED(4) + pot = MCP3008(channel=0) + led.source = clamped(pot.values, 0.5, 1.0) + pause() + """ + for v in values: + yield min(max(v, range_min), range_max) + + +def quantized(values, steps, range_min=0, range_max=1): + """ + Returns *values* quantized to *steps* increments. All items in *values* are + assumed to be between *range_min* and *range_max* (use :func:`scaled` to + ensure this if necessary). + + For example, to quantize values between 0 and 1 to 5 "steps" (0.0, 0.25, + 0.5, 0.75, 1.0):: + + from gpiozero import PWMLED, MCP3008, quantized + from signal import pause + + led = PWMLED(4) + pot = MCP3008(channel=0) + led.source = quantized(pot.values, 4) + pause() + """ + range_size = range_max - range_min + for v in scaled(values, 0, 1, range_min, range_max): + yield ((int(v * steps) / steps) * range_size) + range_min + + +def conjunction(*values): + """ + Returns the `logical conjunction`_ of all supplied values (the result is + only ``True`` if and only if all input values are simultaneously ``True``). + One or more *values* can be specified. For example, to light an + :class:`LED` only when *both* buttons are pressed:: + + from gpiozero import LED, Button, conjunction + from signal import pause + + led = LED(4) + btn1 = Button(20) + btn2 = Button(21) + led.source = conjunction(btn1.values, btn2.values) + pause() + + .. _logical conjunction: https://en.wikipedia.org/wiki/Logical_conjunction + """ + for v in zip(*values): + yield all(v) + + +def disjunction(*values): + """ + Returns the `logical disjunction`_ of all supplied values (the result is + ``True`` if any of the input values are currently ``True``). One or more + *values* can be specified. For example, the light an :class:`LED` when + *any* button is pressed:: + + from gpiozero import LED, Button, conjunction + from signal import pause + + led = LED(4) + btn1 = Button(20) + btn2 = Button(21) + led.source = disjunction(btn1.values, btn2.values) + pause() + + .. _logical disjunction: https://en.wikipedia.org/wiki/Logical_disjunction + """ + for v in zip(*values): + yield any(v) + + +def averaged(*values): + """ + Returns the mean of all supplied values. One or more *values* can be + specified. For example, to light a :class:`PWMLED` as the average of + several potentiometers connected to an :class:`MCP3008` ADC:: + + from gpiozero import MCP3008, PWMLED, averaged + from signal import pause + + pot1 = MCP3008(channel=0) + pot2 = MCP3008(channel=1) + pot3 = MCP3008(channel=2) + led = PWMLED(4) + led.source = averaged(pot1.values, pot2.values, pot3.values) + pause() + """ + for v in zip(*values): + yield mean(v) + + +def queued(values, qsize): + """ + Queues up readings from *values* (the number of readings queued is + determined by *qsize*) and begins yielding values only when the queue is + full. For example, to "cascade" values along a sequence of LEDs:: + + from gpiozero import LEDBoard, Button, queued + from signal import pause + + leds = LEDBoard(5, 6, 13, 19, 26) + btn = Button(17) + for i in range(4): + leds[i].source = queued(leds[i + 1].values, 5) + leds[i].source_delay = 0.01 + leds[4].source = btn.values + pause() + """ + q = [] + it = iter(values) + for i in range(qsize): + q.append(next(it)) + for i in cycle(range(qsize)): + yield q[i] + try: + q[i] = next(it) + except StopIteration: + break + + +def pre_delayed(values, delay): + """ + Waits for *delay* seconds before returning each item from *values*. + """ + for v in values: + sleep(delay) + yield v + + +def post_delayed(values, delay): + """ + Waits for *delay* seconds after returning each item from *values*. + """ + for v in values: + yield v + sleep(delay) + + +def random_values(): + """ + Provides an infinite source of random values between 0 and 1. For example, + to produce a "flickering candle" effect with an LED:: + + from gpiozero import PWMLED, random_values + from signal import pause + + led = PWMLED(4) + led.source = random_values() + pause() + + If you require a wider range than 0 to 1, see :func:`scaled`. + """ + while True: + yield random() + + +def sin_values(): + """ + Provides an infinite source of values representing a sine wave (from -1 to + +1), calculated as the result of applying sign to a simple degrees counter + that increments by one for each requested value. For example, to produce a + "siren" effect with a couple of LEDs:: + + from gpiozero import PWMLED, sin_values, scaled, inverted + from signal import pause + + red = PWMLED(2) + blue = PWMLED(3) + red.source_delay = 0.1 + blue.source_delay = 0.1 + red.source = scaled(sin_values(), 0, 1, -1, 1) + blue.source = inverted(red.values) + pause() + + If you require a wider range than 0 to 1, see :func:`scaled`. + """ + for d in cycle(range(360)): + yield sin(d) + + +def cos_values(): + """ + Provides an infinite source of values representing a cosine wave (from -1 + to +1), calculated as the result of applying sign to a simple degrees + counter that increments by one for each requested value. For example, to + produce a "siren" effect with a couple of LEDs:: + + from gpiozero import PWMLED, cos_values, scaled, inverted + from signal import pause + + red = PWMLED(2) + blue = PWMLED(3) + red.source = scaled(cos_values(), 0, 1, -1, 1) + blue.source = inverted(red.values) + pause() + + If you require a wider range than 0 to 1, see :func:`scaled`. + """ + for d in cycle(range(360)): + yield cos(d) + diff --git a/gpiozero/threads.py b/gpiozero/threads.py index 55c4beb..805212d 100644 --- a/gpiozero/threads.py +++ b/gpiozero/threads.py @@ -6,13 +6,7 @@ from __future__ import ( ) str = type('') -import weakref -from collections import deque -from threading import Thread, Event, RLock -try: - from statistics import median, mean -except ImportError: - from .compat import median, mean +from threading import Thread, Event from .exc import ( GPIOBadQueueLen, @@ -46,46 +40,3 @@ class GPIOThread(Thread): super(GPIOThread, self).join() _THREADS.discard(self) - -class GPIOQueue(GPIOThread): - def __init__( - self, parent, queue_len=5, sample_wait=0.0, partial=False, - average=median): - assert callable(average) - super(GPIOQueue, self).__init__(target=self.fill) - if queue_len < 1: - raise GPIOBadQueueLen('queue_len must be at least one') - if sample_wait < 0: - raise GPIOBadSampleWait('sample_wait must be 0 or greater') - self.queue = deque(maxlen=queue_len) - self.partial = partial - self.sample_wait = sample_wait - self.full = Event() - self.parent = weakref.proxy(parent) - self.average = average - - @property - def value(self): - if not self.partial: - self.full.wait() - try: - return self.average(self.queue) - except ZeroDivisionError: - # No data == inactive value - return 0.0 - - def fill(self): - try: - while (not self.stopping.wait(self.sample_wait) and - len(self.queue) < self.queue.maxlen): - self.queue.append(self.parent._read()) - if self.partial: - self.parent._fire_events() - self.full.set() - while not self.stopping.wait(self.sample_wait): - self.queue.append(self.parent._read()) - self.parent._fire_events() - except ReferenceError: - # Parent is dead; time to die! - pass -