diff --git a/gpiozero/devices.py b/gpiozero/devices.py index 98178f5..e5465f2 100644 --- a/gpiozero/devices.py +++ b/gpiozero/devices.py @@ -47,7 +47,11 @@ class GPIODevice(object): The GPIO pin (in BCM numbering) that the device is connected to. If this is `None` a `GPIODeviceError` will be raised. """ + + __slots__ = ('_pin', '_active_state', '_inactive_state') + def __init__(self, pin=None): + super(GPIODevice, self).__init__() # self._pin must be set before any possible exceptions can be raised # because it's accessed in __del__. However, it mustn't be given the # value of pin until we've verified that it isn't already allocated diff --git a/gpiozero/input_devices.py b/gpiozero/input_devices.py index b81be04..6b37f02 100644 --- a/gpiozero/input_devices.py +++ b/gpiozero/input_devices.py @@ -13,16 +13,6 @@ from spidev import SpiDev from .devices import GPIODeviceError, GPIODeviceClosed, GPIODevice, GPIOQueue -def _alias(key, doc=None): - if doc is None: - doc = 'Alias for %s' % key - return property( - lambda self: getattr(self, key), - lambda self, val: setattr(self, key, val), - doc=doc - ) - - class InputDeviceError(GPIODeviceError): pass @@ -45,6 +35,13 @@ class InputDevice(GPIODevice): If `True`, the pin will be pulled high with an internal resistor. If `False` (the default), the pin will be pulled low. """ + + __slots__ = ( + '_pull_up', + '_active_edge', + '_inactive_edge', + ) + def __init__(self, pin=None, pull_up=False): if pin in (2, 3) and not pull_up: raise InputDeviceError( @@ -105,6 +102,15 @@ class WaitableInputDevice(InputDevice): Note that this class provides no means of actually firing its events; it's effectively an abstract base class. """ + + __slots__ = ( + '_active_event', + '_inactive_event', + '_when_activated', + '_when_deactivated', + '_last_state', + ) + def __init__(self, pin=None, pull_up=False): super(WaitableInputDevice, self).__init__(pin, pull_up) self._active_event = Event() @@ -135,13 +141,9 @@ class WaitableInputDevice(InputDevice): """ return self._inactive_event.wait(timeout) - def _get_when_activated(self): - return self._when_activated - - def _set_when_activated(self, value): - self._when_activated = self._wrap_callback(value) - - when_activated = property(_get_when_activated, _set_when_activated, doc="""\ + @property + def when_activated(self): + """ The function to run when the device changes state from inactive to active. @@ -154,15 +156,16 @@ class WaitableInputDevice(InputDevice): Set this property to `None` (the default) to disable the event. See also: when_deactivated. - """) + """ + return self._when_activated - def _get_when_deactivated(self): - return self._when_deactivated + @when_activated.setter + def when_activated(self, value): + self._when_activated = self._wrap_callback(value) - def _set_when_deactivated(self, value): - self._when_deactivated = self._wrap_callback(value) - - when_deactivated = property(_get_when_deactivated, _set_when_deactivated, doc="""\ + @property + def when_deactivated(self): + """ The function to run when the device changes state from active to inactive. @@ -175,7 +178,12 @@ class WaitableInputDevice(InputDevice): Set this property to `None` (the default) to disable the event. See also: when_activated. - """) + """ + 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: @@ -240,6 +248,9 @@ class DigitalInputDevice(WaitableInputDevice): ignore changes in state after an initial change. This defaults to `None` which indicates that no bounce compensation will be performed. """ + + __slots__ = () + def __init__(self, pin=None, pull_up=False, bounce_time=None): super(DigitalInputDevice, self).__init__(pin, pull_up) try: @@ -290,6 +301,9 @@ class SmoothedInputDevice(WaitableInputDevice): If `True`, a value will be returned immediately, but be aware that this value is likely to fluctuate excessively. """ + + __slots__ = ('_queue', '_threshold', '__weakref__') + def __init__( self, pin=None, pull_up=False, threshold=0.5, queue_len=5, sample_wait=0.0, partial=False): @@ -357,20 +371,21 @@ class SmoothedInputDevice(WaitableInputDevice): self._check_open() return self._queue.value - def _get_threshold(self): + @property + def threshold(self): + """ + If `value` exceeds this amount, then `is_active` will return `True`. + """ return self._threshold - def _set_threshold(self, value): + @threshold.setter + def threshold(self, value): if not (0.0 < value < 1.0): raise InputDeviceError( 'threshold must be between zero and one exclusive' ) self._threshold = float(value) - threshold = property(_get_threshold, _set_threshold, doc="""\ - If `value` exceeds this amount, then `is_active` will return `True`. - """) - @property def is_active(self): return self.value > self.threshold @@ -384,16 +399,17 @@ class Button(DigitalInputDevice): side of the switch, and ground to the other (the default `pull_up` value is `True`). """ + + __slots__ = () + def __init__(self, pin=None, pull_up=True, bouncetime=None): super(Button, self).__init__(pin, pull_up, bouncetime) - is_pressed = _alias('is_active') - - when_pressed = _alias('when_activated') - when_released = _alias('when_deactivated') - - wait_for_press = _alias('wait_for_active') - wait_for_release = _alias('wait_for_inactive') +Button.is_pressed = Button.is_active +Button.when_pressed = Button.when_activated +Button.when_released = Button.when_deactivated +Button.wait_for_press = Button.wait_for_active +Button.wait_for_release = Button.wait_for_inactive class MotionSensor(SmoothedInputDevice): @@ -410,6 +426,9 @@ class MotionSensor(SmoothedInputDevice): particularly "jittery" you may wish to set this to a higher value (e.g. 5) to mitigate this. """ + + __slots__ = () + def __init__( self, pin=None, queue_len=1, sample_rate=10, threshold=0.5, partial=False): @@ -423,13 +442,11 @@ class MotionSensor(SmoothedInputDevice): self.close() raise - motion_detected = _alias('is_active') - - when_motion = _alias('when_activated') - when_no_motion = _alias('when_deactivated') - - wait_for_motion = _alias('wait_for_active') - wait_for_no_motion = _alias('wait_for_inactive') +MotionSensor.motion_detected = MotionSensor.is_active +MotionSensor.when_motion = MotionSensor.when_activated +MotionSensor.when_no_motion = MotionSensor.when_deactivated +MotionSensor.wait_for_motion = MotionSensor.wait_for_active +MotionSensor.wait_for_no_motion = MotionSensor.wait_for_inactive class LightSensor(SmoothedInputDevice): @@ -441,6 +458,12 @@ class LightSensor(SmoothedInputDevice): class repeatedly discharges the capacitor, then times the duration it takes to charge (which will vary according to the light falling on the LDR). """ + + __slots__ = ( + '_charge_time_limit', + '_charged', + ) + def __init__( self, pin=None, queue_len=5, charge_time_limit=0.01, threshold=0.1, partial=False): @@ -478,13 +501,11 @@ class LightSensor(SmoothedInputDevice): self.charge_time_limit ) - light_detected = _alias('is_active') - - when_light = _alias('when_activated') - when_dark = _alias('when_deactivated') - - wait_for_light = _alias('wait_for_active') - wait_for_dark = _alias('wait_for_inactive') +LightSensor.light_detected = LightSensor.is_active +LightSensor.when_light = LightSensor.when_activated +LightSensor.when_dark = LightSensor.when_deactivated +LightSensor.wait_for_light = LightSensor.wait_for_active +LightSensor.wait_for_dark = LightSensor.wait_for_inactive class TemperatureSensor(W1ThermSensor): @@ -500,6 +521,13 @@ class AnalogInputDevice(object): """ Represents an analog input device connected to SPI (serial interface). """ + + __slots__ = ( + '_device', + '_bits', + '_spi', + ) + def __init__(self, device=0, bits=None): if bits is None: raise InputDeviceError('you must specify the bit resolution of the device') @@ -539,6 +567,9 @@ class AnalogInputDevice(object): class MCP3008(AnalogInputDevice): + + __slots__ = ('_channel') + def __init__(self, device=0, channel=0): if not 0 <= channel < 8: raise InputDeviceError('channel must be between 0 and 7') @@ -571,6 +602,9 @@ class MCP3008(AnalogInputDevice): class MCP3004(MCP3008): + + __slots__ = () + def __init__(self, device=0, channel=0): # MCP3004 protocol is identical to MCP3008 but the top bit of the # channel number must be 0 (effectively restricting it to 4 channels) diff --git a/gpiozero/output_devices.py b/gpiozero/output_devices.py index 02673bc..e8709d9 100644 --- a/gpiozero/output_devices.py +++ b/gpiozero/output_devices.py @@ -25,6 +25,9 @@ class OutputDevice(GPIODevice): `False`, the `on` method will set the GPIO to LOW (the `off` method always does the opposite). """ + + __slots__ = ('_active_high') + def __init__(self, pin=None, active_high=True): self._active_high = active_high super(OutputDevice, self).__init__(pin) @@ -83,6 +86,9 @@ class DigitalOutputDevice(OutputDevice): optional background thread to handle toggling the device state without further interaction. """ + + __slots__ = ('_blink_thread', '_lock') + def __init__(self, pin=None, active_high=True): super(DigitalOutputDevice, self).__init__(pin, active_high) self._blink_thread = None @@ -165,7 +171,9 @@ class LED(DigitalOutputDevice): anode (long leg) of the LED, and the cathode (short leg) to ground, with an optional resistor to prevent the LED from burning out. """ - pass + __slots__ = () + +LED.is_lit = LED.is_active class Buzzer(DigitalOutputDevice): @@ -175,13 +183,16 @@ class Buzzer(DigitalOutputDevice): A typical configuration of such a device is to connect a GPIO pin to the anode (long leg) of the buzzer, and the cathode (short leg) to ground. """ - pass + __slots__ = () class PWMOutputDevice(DigitalOutputDevice): """ Generic Output device configured for PWM (Pulse-Width Modulation). """ + + __slots__ = ('_pwm', '_frequency', '_value') + def __init__(self, pin=None, frequency=100): self._pwm = None super(PWMOutputDevice, self).__init__(pin) @@ -214,35 +225,35 @@ class PWMOutputDevice(DigitalOutputDevice): self._pwm.ChangeDutyCycle(value * 100) self._value = value - def _get_value(self): - return self._read() - - def _set_value(self, value): - self._stop_blink() - self._write(value) - - value = property(_get_value, _set_value, doc="""\ + @property + def value(self): + """ The duty cycle of the PWM device. 0.0 is off, 1.0 is fully on. Values in between may be specified for varying levels of power in the device. """ - ) + return self._read() + + @value.setter + def value(self, value): + self._stop_blink() + self._write(value) @property def is_active(self): return self.value > 0.0 - def _get_frequency(self): - return self._frequency - - def _set_frequency(self, value): - self._pwm.ChangeFrequency(value) - self._frequency = value - - frequency = property(_get_frequency, _set_frequency, doc="""\ + @property + def frequency(self): + """ The frequency of the pulses used with the PWM device, in Hz. The default is 100. """ - ) + return self._frequency + + @frequency.setter + def frequency(self, value): + self._pwm.ChangeFrequency(value) + self._frequency = value def _led_property(index, doc=None): @@ -265,6 +276,9 @@ class RGBLED(object): blue: `None` The GPIO pin that controls the blue component of the RGB LED. """ + + __slots__ = ('_leds') + def __init__(self, red=None, green=None, blue=None): self._leds = tuple(PWMOutputDevice(pin) for pin in (red, green, blue)) @@ -274,6 +288,13 @@ class RGBLED(object): @property def color(self): + """ + Set the color of the LED from an RGB 3-tuple of `(red, green, blue)` + where each value between 0 and 1. + + For example, purple would be `(1, 0, 1)` and yellow would be `(1, 1, + 0)`, while orange would be `(1, 0.5, 0)`. + """ return (self.red, self.green, self.blue) @color.setter @@ -282,13 +303,15 @@ class RGBLED(object): def on(self): """ - Turn the device on + Turn the device on. This equivalent to setting the device color to + white `(1, 1, 1)`. """ self.color = (1, 1, 1) def off(self): """ - Turn the device off + Turn the device off. This is equivalent to setting the device color + to black `(0, 0, 0)`. """ self.color = (0, 0, 0) @@ -307,6 +330,9 @@ class Motor(object): """ Generic bi-directional motor. """ + + __slots__ = ('_forward', '_backward') + def __init__(self, forward=None, back=None): if not all([forward, back]): raise GPIODeviceError('forward and back pins must be provided')