mirror of
				https://github.com/KevinMidboe/python-gpiozero.git
				synced 2025-10-29 17:50:37 +00:00 
			
		
		
		
	Fix #74
Add more property aliases and do them properly (no more lambdas and string lookups) which means we can remove `_alias`. This commit also defines `__slots__` for all classes which should prevent assignation of invalid attributes with an AttributeError (more friendly than silently doing the wrong thing). Finally, it cleans up all the property defs to use Ben's preferred decorator style.
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user