mirror of
				https://github.com/KevinMidboe/python-gpiozero.git
				synced 2025-10-29 17:50:37 +00:00 
			
		
		
		
	This PR implements SnowPi, adds the ability for LEDBoard's to own other LEDBoards as well as LEDs, and enhances blink so that manually controlling a LED automatically stops it from blinking (no matter whether it's blinking itself or a LEDBoard is blinking it). It also fixes up RGBLED and Motor which I managed to break with the last PR ...
This commit is contained in:
		@@ -95,6 +95,7 @@ from .boards import (
 | 
			
		||||
    PiLiterBarGraph,
 | 
			
		||||
    TrafficLights,
 | 
			
		||||
    PiTraffic,
 | 
			
		||||
    SnowPi,
 | 
			
		||||
    TrafficLightsBuzzer,
 | 
			
		||||
    FishDish,
 | 
			
		||||
    TrafficHat,
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
 | 
			
		||||
        Turn all the output devices on.
 | 
			
		||||
        """
 | 
			
		||||
        for device in self.all:
 | 
			
		||||
            if isinstance(device, OutputDevice):
 | 
			
		||||
            if isinstance(device, (OutputDevice, CompositeOutputDevice)):
 | 
			
		||||
                device.on()
 | 
			
		||||
 | 
			
		||||
    def off(self):
 | 
			
		||||
@@ -45,7 +45,7 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
 | 
			
		||||
        Turn all the output devices off.
 | 
			
		||||
        """
 | 
			
		||||
        for device in self.all:
 | 
			
		||||
            if isinstance(device, OutputDevice):
 | 
			
		||||
            if isinstance(device, (OutputDevice, CompositeOutputDevice)):
 | 
			
		||||
                device.off()
 | 
			
		||||
 | 
			
		||||
    def toggle(self):
 | 
			
		||||
@@ -54,21 +54,21 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
 | 
			
		||||
        off; if it's off, turn it on.
 | 
			
		||||
        """
 | 
			
		||||
        for device in self.all:
 | 
			
		||||
            if isinstance(device, OutputDevice):
 | 
			
		||||
            if isinstance(device, (OutputDevice, CompositeOutputDevice)):
 | 
			
		||||
                device.toggle()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def value(self):
 | 
			
		||||
        """
 | 
			
		||||
        A tuple containing a value for each subordinate device. This property
 | 
			
		||||
        can also be set to update the state of all output subordinate devices.
 | 
			
		||||
        can also be set to update the state of all subordinate output devices.
 | 
			
		||||
        """
 | 
			
		||||
        return super(CompositeOutputDevice, self).value
 | 
			
		||||
 | 
			
		||||
    @value.setter
 | 
			
		||||
    def value(self, value):
 | 
			
		||||
        for device, v in zip(self.all, value):
 | 
			
		||||
            if isinstance(device, OutputDevice):
 | 
			
		||||
            if isinstance(device, (OutputDevice, CompositeOutputDevice)):
 | 
			
		||||
                device.value = v
 | 
			
		||||
            # Simply ignore values for non-output devices
 | 
			
		||||
 | 
			
		||||
@@ -87,17 +87,32 @@ class LEDCollection(CompositeOutputDevice):
 | 
			
		||||
        order = kwargs.pop('_order', None)
 | 
			
		||||
        LEDClass = PWMLED if pwm else LED
 | 
			
		||||
        super(LEDCollection, self).__init__(
 | 
			
		||||
            *(LEDClass(pin, active_high, initial_value) for pin in args),
 | 
			
		||||
            *(
 | 
			
		||||
                pin_or_collection
 | 
			
		||||
                if isinstance(pin_or_collection, LEDCollection) else
 | 
			
		||||
                LEDClass(pin_or_collection, active_high, initial_value)
 | 
			
		||||
                for pin_or_collection in args
 | 
			
		||||
                ),
 | 
			
		||||
            _order=order,
 | 
			
		||||
            **{name: LEDClass(pin, active_high, initial_value) for name, pin in kwargs.items()})
 | 
			
		||||
            **{
 | 
			
		||||
                name: pin_or_collection
 | 
			
		||||
                if isinstance(pin_or_collection, LEDCollection) else
 | 
			
		||||
                LEDClass(pin_or_collection, active_high, initial_value)
 | 
			
		||||
                for name, pin_or_collection in kwargs.items()
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def leds(self):
 | 
			
		||||
        """
 | 
			
		||||
        A tuple of all the :class:`LED` or :class:`PWMLED` objects contained by
 | 
			
		||||
        the instance.
 | 
			
		||||
        A flat iterator over all LEDs contained in this collection (and all
 | 
			
		||||
        sub-collections).
 | 
			
		||||
        """
 | 
			
		||||
        return self.all
 | 
			
		||||
        for item in self:
 | 
			
		||||
            if isinstance(item, LEDCollection):
 | 
			
		||||
                for subitem in item.leds:
 | 
			
		||||
                    yield subitem
 | 
			
		||||
            else:
 | 
			
		||||
                yield item
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LEDBoard(LEDCollection):
 | 
			
		||||
@@ -115,7 +130,8 @@ class LEDBoard(LEDCollection):
 | 
			
		||||
 | 
			
		||||
    :param int \*pins:
 | 
			
		||||
        Specify the GPIO pins that the LEDs of the board are attached to. You
 | 
			
		||||
        can designate as many pins as necessary.
 | 
			
		||||
        can designate as many pins as necessary. You can also specify
 | 
			
		||||
        :class:`LEDBoard` instances to create trees of LEDs.
 | 
			
		||||
 | 
			
		||||
    :param bool pwm:
 | 
			
		||||
        If ``True``, construct :class:`PWMLED` instances for each pin. If
 | 
			
		||||
@@ -132,7 +148,18 @@ class LEDBoard(LEDCollection):
 | 
			
		||||
        ``None``, each device will be left in whatever state the pin is found
 | 
			
		||||
        in when configured for output (warning: this can be on). The ``True``,
 | 
			
		||||
        the device will be switched on initially.
 | 
			
		||||
 | 
			
		||||
    :param \*\*named_pins:
 | 
			
		||||
        Sepcify GPIO pins that LEDs of the board are attached to, associated
 | 
			
		||||
        each LED with a property name. You can designate as many pins as
 | 
			
		||||
        necessary and any name provided it's not already in use by something
 | 
			
		||||
        else. You can also specify :class:`LEDBoard` instances to create
 | 
			
		||||
        trees of LEDs.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        self._blink_leds = []
 | 
			
		||||
        self._blink_lock = Lock()
 | 
			
		||||
        super(LEDBoard, self).__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        self._stop_blink()
 | 
			
		||||
@@ -181,11 +208,12 @@ class LEDBoard(LEDCollection):
 | 
			
		||||
            finished (warning: the default value of *n* will result in this
 | 
			
		||||
            method never returning).
 | 
			
		||||
        """
 | 
			
		||||
        if isinstance(self.leds[0], LED):
 | 
			
		||||
            if fade_in_time:
 | 
			
		||||
                raise ValueError('fade_in_time must be 0 with non-PWM LEDs')
 | 
			
		||||
            if fade_out_time:
 | 
			
		||||
                raise ValueError('fade_out_time must be 0 with non-PWM LEDs')
 | 
			
		||||
        for led in self.leds:
 | 
			
		||||
            if isinstance(led, LED):
 | 
			
		||||
                if fade_in_time:
 | 
			
		||||
                    raise ValueError('fade_in_time must be 0 with non-PWM LEDs')
 | 
			
		||||
                if fade_out_time:
 | 
			
		||||
                    raise ValueError('fade_out_time must be 0 with non-PWM LEDs')
 | 
			
		||||
        self._stop_blink()
 | 
			
		||||
        self._blink_thread = GPIOThread(
 | 
			
		||||
            target=self._blink_device,
 | 
			
		||||
@@ -196,10 +224,14 @@ class LEDBoard(LEDCollection):
 | 
			
		||||
            self._blink_thread.join()
 | 
			
		||||
            self._blink_thread = None
 | 
			
		||||
 | 
			
		||||
    def _stop_blink(self):
 | 
			
		||||
        if self._blink_thread:
 | 
			
		||||
            self._blink_thread.stop()
 | 
			
		||||
            self._blink_thread = None
 | 
			
		||||
    def _stop_blink(self, led=None):
 | 
			
		||||
        if led is None:
 | 
			
		||||
            if self._blink_thread:
 | 
			
		||||
                self._blink_thread.stop()
 | 
			
		||||
                self._blink_thread = None
 | 
			
		||||
        else:
 | 
			
		||||
            with self._blink_lock:
 | 
			
		||||
                self._blink_leds.remove(led)
 | 
			
		||||
 | 
			
		||||
    def pulse(self, fade_in_time=1, fade_out_time=1, n=None, background=True):
 | 
			
		||||
        """
 | 
			
		||||
@@ -243,9 +275,18 @@ class LEDBoard(LEDCollection):
 | 
			
		||||
                cycle(sequence) if n is None else
 | 
			
		||||
                chain.from_iterable(repeat(sequence, n))
 | 
			
		||||
                )
 | 
			
		||||
        with self._blink_lock:
 | 
			
		||||
            self._blink_leds = list(self.leds)
 | 
			
		||||
            for led in self._blink_leds:
 | 
			
		||||
                if led._controller not in (None, self):
 | 
			
		||||
                    led._controller._stop_blink(led)
 | 
			
		||||
                led._controller = self
 | 
			
		||||
        for value, delay in sequence:
 | 
			
		||||
            for led in self.leds:
 | 
			
		||||
                led.value = value
 | 
			
		||||
            with self._blink_lock:
 | 
			
		||||
                if not self._blink_leds:
 | 
			
		||||
                    break
 | 
			
		||||
                for led in self._blink_leds:
 | 
			
		||||
                    led._write(value)
 | 
			
		||||
            if self._blink_thread.stopping.wait(delay):
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
@@ -432,7 +473,7 @@ class TrafficLights(LEDBoard):
 | 
			
		||||
 | 
			
		||||
class PiTraffic(TrafficLights):
 | 
			
		||||
    """
 | 
			
		||||
    Extends :class:`TrafficLights` for the Low Voltage Labs PI-TRAFFIC:
 | 
			
		||||
    Extends :class:`TrafficLights` for the `Low Voltage Labs PI-TRAFFIC`_:
 | 
			
		||||
    vertical traffic lights board when attached to GPIO pins 9, 10, and 11.
 | 
			
		||||
 | 
			
		||||
    There's no need to specify the pins if the PI-TRAFFIC is connected to the
 | 
			
		||||
@@ -446,12 +487,54 @@ class PiTraffic(TrafficLights):
 | 
			
		||||
 | 
			
		||||
    To use the PI-TRAFFIC board when attached to a non-standard set of pins,
 | 
			
		||||
    simply use the parent class, :class:`TrafficLights`.
 | 
			
		||||
 | 
			
		||||
    .. _Low Voltage Labs PI-TRAFFIC: http://lowvoltagelabs.com/products/pi-traffic/
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super(PiTraffic, self).__init__(9, 10, 11)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SnowPi(LEDBoard):
 | 
			
		||||
    """
 | 
			
		||||
    Extends :class:`LEDBoard` for the `Ryanteck SnowPi`_ board.
 | 
			
		||||
 | 
			
		||||
    The SnowPi pins are fixed and therefore there's no need to specify them
 | 
			
		||||
    when constructing this class. The following example turns on the eyes, sets
 | 
			
		||||
    the nose pulsing, and the arms blinking::
 | 
			
		||||
 | 
			
		||||
        from gpiozero import SnowPi
 | 
			
		||||
 | 
			
		||||
        snowman = SnowPi(pwm=True)
 | 
			
		||||
        snowman.eyes.on()
 | 
			
		||||
        snowman.nose.pulse()
 | 
			
		||||
        snowman.arms.blink()
 | 
			
		||||
 | 
			
		||||
    :param bool pwm:
 | 
			
		||||
        If ``True``, construct :class:`PWMLED` instances to represent each
 | 
			
		||||
        LED. If ``False`` (the default), construct regular :class:`LED`
 | 
			
		||||
        instances.
 | 
			
		||||
 | 
			
		||||
    .. _Ryanteck SnowPi: https://ryanteck.uk/raspberry-pi/114-snowpi-the-gpio-snowman-for-raspberry-pi-0635648608303.html
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, pwm=False):
 | 
			
		||||
        super(SnowPi, self).__init__(
 | 
			
		||||
            arms=LEDBoard(
 | 
			
		||||
                left=LEDBoard(
 | 
			
		||||
                    top=17, middle=18, bottom=22, pwm=pwm,
 | 
			
		||||
                    _order=('top', 'middle', 'bottom')),
 | 
			
		||||
                right=LEDBoard(
 | 
			
		||||
                    top=7, middle=8, bottom=9, pwm=pwm,
 | 
			
		||||
                    _order=('top', 'middle', 'bottom'))
 | 
			
		||||
                ),
 | 
			
		||||
            eyes=LEDBoard(
 | 
			
		||||
                left=23, right=24, pwm=pwm,
 | 
			
		||||
                _order=('left', 'right')
 | 
			
		||||
                ),
 | 
			
		||||
            nose=25, pwm=pwm
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TrafficLightsBuzzer(CompositeOutputDevice):
 | 
			
		||||
    """
 | 
			
		||||
    Extends :class:`CompositeDevice` and is a generic class for HATs with
 | 
			
		||||
 
 | 
			
		||||
@@ -356,7 +356,7 @@ class CompositeDevice(Device):
 | 
			
		||||
            raise CompositeDeviceBadName('%s is a reserved name' % name)
 | 
			
		||||
        self._all = args + tuple(kwargs[v] for v in self._order)
 | 
			
		||||
        self._named = kwargs
 | 
			
		||||
        self._tuple = namedtuple('CompositeDeviceValue', chain(
 | 
			
		||||
        self._tuple = namedtuple('%sValue' % self.__class__.__name__, chain(
 | 
			
		||||
            (str(i) for i in range(len(args))), self._order),
 | 
			
		||||
            rename=True)
 | 
			
		||||
 | 
			
		||||
@@ -375,18 +375,39 @@ class CompositeDevice(Device):
 | 
			
		||||
            raise AttributeError("can't set attribute %s" % name)
 | 
			
		||||
        return super(CompositeDevice, self).__setattr__(name, value)
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self._check_open()
 | 
			
		||||
            return "<gpiozero.%s object containing %d devices: %s and %d unnamed>" % (
 | 
			
		||||
                    self.__class__.__name__,
 | 
			
		||||
                    len(self), ','.join(self._named),
 | 
			
		||||
                    len(self) - len(self._named)
 | 
			
		||||
                    )
 | 
			
		||||
        except DeviceClosed:
 | 
			
		||||
            return "<gpiozero.%s object closed>"
 | 
			
		||||
 | 
			
		||||
    def __len__(self):
 | 
			
		||||
        return len(self._all)
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, index):
 | 
			
		||||
        return self._all[index]
 | 
			
		||||
 | 
			
		||||
    def __iter__(self):
 | 
			
		||||
        return iter(self._all)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def all(self):
 | 
			
		||||
        # XXX Deprecate this in favour of using the instance as a container
 | 
			
		||||
        return self._all
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        for device in self._all:
 | 
			
		||||
            device.close()
 | 
			
		||||
        self._all = ()
 | 
			
		||||
        if self._all:
 | 
			
		||||
            for device in self._all:
 | 
			
		||||
                device.close()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def closed(self):
 | 
			
		||||
        return bool(self._all)
 | 
			
		||||
        return all(device.closed for device in self)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def tuple(self):
 | 
			
		||||
@@ -394,7 +415,7 @@ class CompositeDevice(Device):
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def value(self):
 | 
			
		||||
        return self.tuple(*(device.value for device in self._all))
 | 
			
		||||
        return self.tuple(*(device.value for device in self))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GPIODevice(Device):
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ class SPIBadArgs(SPIError, ValueError):
 | 
			
		||||
class GPIODeviceError(GPIOZeroError):
 | 
			
		||||
    "Base class for errors specific to the GPIODevice hierarchy"
 | 
			
		||||
 | 
			
		||||
class GPIODeviceClosed(GPIODeviceError):
 | 
			
		||||
class GPIODeviceClosed(GPIODeviceError, DeviceClosed):
 | 
			
		||||
    "Deprecated descendent of :exc:`DeviceClosed`"
 | 
			
		||||
 | 
			
		||||
class GPIOPinInUse(GPIODeviceError):
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ from threading import Lock
 | 
			
		||||
from itertools import repeat, cycle, chain
 | 
			
		||||
 | 
			
		||||
from .exc import OutputDeviceBadValue, GPIOPinMissing
 | 
			
		||||
from .devices import GPIODevice, CompositeDevice, SourceMixin
 | 
			
		||||
from .devices import GPIODevice, Device, CompositeDevice, SourceMixin
 | 
			
		||||
from .threads import GPIOThread
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -121,6 +121,16 @@ class DigitalOutputDevice(OutputDevice):
 | 
			
		||||
        self._blink_thread = None
 | 
			
		||||
        super(DigitalOutputDevice, self).__init__(pin, active_high, initial_value)
 | 
			
		||||
        self._lock = Lock()
 | 
			
		||||
        self._controller = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def value(self):
 | 
			
		||||
        return self._read()
 | 
			
		||||
 | 
			
		||||
    @value.setter
 | 
			
		||||
    def value(self, value):
 | 
			
		||||
        self._stop_blink()
 | 
			
		||||
        self._write(value)
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        self._stop_blink()
 | 
			
		||||
@@ -174,6 +184,9 @@ class DigitalOutputDevice(OutputDevice):
 | 
			
		||||
            self._blink_thread = None
 | 
			
		||||
 | 
			
		||||
    def _stop_blink(self):
 | 
			
		||||
        if self._controller:
 | 
			
		||||
            self._controller._stop_blink(self)
 | 
			
		||||
            self._controller = None
 | 
			
		||||
        if self._blink_thread:
 | 
			
		||||
            self._blink_thread.stop()
 | 
			
		||||
            self._blink_thread = None
 | 
			
		||||
@@ -286,6 +299,7 @@ class PWMOutputDevice(OutputDevice):
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, pin=None, active_high=True, initial_value=0, frequency=100):
 | 
			
		||||
        self._blink_thread = None
 | 
			
		||||
        self._controller = None
 | 
			
		||||
        if not 0 <= initial_value <= 1:
 | 
			
		||||
            raise OutputDeviceBadValue("initial_value must be between 0 and 1")
 | 
			
		||||
        super(PWMOutputDevice, self).__init__(pin, active_high)
 | 
			
		||||
@@ -437,6 +451,9 @@ class PWMOutputDevice(OutputDevice):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _stop_blink(self):
 | 
			
		||||
        if self._controller:
 | 
			
		||||
            self._controller._stop_blink(self)
 | 
			
		||||
            self._controller = None
 | 
			
		||||
        if self._blink_thread:
 | 
			
		||||
            self._blink_thread.stop()
 | 
			
		||||
            self._blink_thread = None
 | 
			
		||||
@@ -508,10 +525,10 @@ def _led_property(index, doc=None):
 | 
			
		||||
    return property(getter, setter, doc=doc)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RGBLED(SourceMixin, CompositeDevice):
 | 
			
		||||
class RGBLED(SourceMixin, Device):
 | 
			
		||||
    """
 | 
			
		||||
    Extends :class:`CompositeDevice` and represents a full color LED component
 | 
			
		||||
    (composed of red, green, and blue LEDs).
 | 
			
		||||
    Extends :class:`Device` and represents a full color LED component (composed
 | 
			
		||||
    of red, green, and blue LEDs).
 | 
			
		||||
 | 
			
		||||
    Connect the common cathode (longest leg) to a ground pin; connect each of
 | 
			
		||||
    the other legs (representing the red, green, and blue anodes) to any GPIO
 | 
			
		||||
@@ -556,6 +573,18 @@ class RGBLED(SourceMixin, CompositeDevice):
 | 
			
		||||
    green = _led_property(1)
 | 
			
		||||
    blue = _led_property(2)
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        if self._leds:
 | 
			
		||||
            self._stop_blink()
 | 
			
		||||
            for led in self._leds:
 | 
			
		||||
                led.close()
 | 
			
		||||
            self._leds = ()
 | 
			
		||||
        super(RGBLED, self).close()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def closed(self):
 | 
			
		||||
        return bool(self._leds)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def value(self):
 | 
			
		||||
        """
 | 
			
		||||
@@ -569,6 +598,7 @@ class RGBLED(SourceMixin, CompositeDevice):
 | 
			
		||||
 | 
			
		||||
    @value.setter
 | 
			
		||||
    def value(self, value):
 | 
			
		||||
        self._stop_blink()
 | 
			
		||||
        self.red, self.green, self.blue = value
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
@@ -606,11 +636,6 @@ class RGBLED(SourceMixin, CompositeDevice):
 | 
			
		||||
        r, g, b = self.value
 | 
			
		||||
        self.value = (1 - r, 1 - g, 1 - b)
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        self._stop_blink()
 | 
			
		||||
        for led in self._leds:
 | 
			
		||||
            led.close()
 | 
			
		||||
 | 
			
		||||
    def blink(
 | 
			
		||||
            self, on_time=1, off_time=1, fade_in_time=0, fade_out_time=0,
 | 
			
		||||
            on_color=(1, 1, 1), off_color=(0, 0, 0), n=None, background=True):
 | 
			
		||||
@@ -654,7 +679,8 @@ class RGBLED(SourceMixin, CompositeDevice):
 | 
			
		||||
            self._blink_thread.join()
 | 
			
		||||
            self._blink_thread = None
 | 
			
		||||
 | 
			
		||||
    def _stop_blink(self):
 | 
			
		||||
    def _stop_blink(self, led=None):
 | 
			
		||||
        # If this is called with a single led, we stop all blinking anyway
 | 
			
		||||
        if self._blink_thread:
 | 
			
		||||
            self._blink_thread.stop()
 | 
			
		||||
            self._blink_thread = None
 | 
			
		||||
@@ -687,16 +713,19 @@ class RGBLED(SourceMixin, CompositeDevice):
 | 
			
		||||
                cycle(sequence) if n is None else
 | 
			
		||||
                chain.from_iterable(repeat(sequence, n))
 | 
			
		||||
                )
 | 
			
		||||
        for l in self._leds:
 | 
			
		||||
            l._controller = self
 | 
			
		||||
        for value, delay in sequence:
 | 
			
		||||
            self._leds[0].value, self._leds[1].value, self._leds[2].value = value
 | 
			
		||||
            for l, v in zip(self._leds, value):
 | 
			
		||||
                l._write(v)
 | 
			
		||||
            if self._blink_thread.stopping.wait(delay):
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Motor(SourceMixin, CompositeDevice):
 | 
			
		||||
    """
 | 
			
		||||
    Extends :class:`CompositeDevice` and represents a generic motor connected
 | 
			
		||||
    to a bi-directional motor driver circuit (i.e.  an `H-bridge`_).
 | 
			
		||||
    Extends :class:`CompositeDevice` and represents a generic motor
 | 
			
		||||
    connected to a bi-directional motor driver circuit (i.e.  an `H-bridge`_).
 | 
			
		||||
 | 
			
		||||
    Attach an `H-bridge`_ motor controller to your Pi; connect a power source
 | 
			
		||||
    (e.g. a battery pack or the 5V pin) to the controller; connect the outputs
 | 
			
		||||
@@ -725,33 +754,9 @@ class Motor(SourceMixin, CompositeDevice):
 | 
			
		||||
            raise GPIOPinMissing(
 | 
			
		||||
                'forward and backward pins must be provided'
 | 
			
		||||
            )
 | 
			
		||||
        super(Motor, self).__init__()
 | 
			
		||||
        self._forward = PWMOutputDevice(forward)
 | 
			
		||||
        self._backward = PWMOutputDevice(backward)
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        self._forward.close()
 | 
			
		||||
        self._backward.close()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def closed(self):
 | 
			
		||||
        return self._forward.closed and self._backward.closed
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def forward_device(self):
 | 
			
		||||
        """
 | 
			
		||||
        Returns the `PWMOutputDevice` representing the forward pin of the motor
 | 
			
		||||
        controller.
 | 
			
		||||
        """
 | 
			
		||||
        return self._forward
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def backward_device(self):
 | 
			
		||||
        """
 | 
			
		||||
        Returns the `PWMOutputDevice` representing the backward pin of the
 | 
			
		||||
        motor controller.
 | 
			
		||||
        """
 | 
			
		||||
        return self._backward
 | 
			
		||||
        super(Motor, self).__init__(
 | 
			
		||||
                forward_device=PWMOutputDevice(forward),
 | 
			
		||||
                backward_device=PWMOutputDevice(backward))
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def value(self):
 | 
			
		||||
@@ -759,7 +764,7 @@ class Motor(SourceMixin, CompositeDevice):
 | 
			
		||||
        Represents the speed of the motor as a floating point value between -1
 | 
			
		||||
        (full speed backward) and 1 (full speed forward).
 | 
			
		||||
        """
 | 
			
		||||
        return self._forward.value - self._backward.value
 | 
			
		||||
        return self.forward_device.value - self.backward_device.value
 | 
			
		||||
 | 
			
		||||
    @value.setter
 | 
			
		||||
    def value(self, value):
 | 
			
		||||
@@ -788,8 +793,8 @@ class Motor(SourceMixin, CompositeDevice):
 | 
			
		||||
            The speed at which the motor should turn. Can be any value between
 | 
			
		||||
            0 (stopped) and the default 1 (maximum speed).
 | 
			
		||||
        """
 | 
			
		||||
        self._backward.off()
 | 
			
		||||
        self._forward.value = speed
 | 
			
		||||
        self.backward_device.off()
 | 
			
		||||
        self.forward_device.value = speed
 | 
			
		||||
 | 
			
		||||
    def backward(self, speed=1):
 | 
			
		||||
        """
 | 
			
		||||
@@ -799,8 +804,8 @@ class Motor(SourceMixin, CompositeDevice):
 | 
			
		||||
            The speed at which the motor should turn. Can be any value between
 | 
			
		||||
            0 (stopped) and the default 1 (maximum speed).
 | 
			
		||||
        """
 | 
			
		||||
        self._forward.off()
 | 
			
		||||
        self._backward.value = speed
 | 
			
		||||
        self.forward_device.off()
 | 
			
		||||
        self.backward_device.value = speed
 | 
			
		||||
 | 
			
		||||
    def reverse(self):
 | 
			
		||||
        """
 | 
			
		||||
@@ -814,5 +819,5 @@ class Motor(SourceMixin, CompositeDevice):
 | 
			
		||||
        """
 | 
			
		||||
        Stop the motor.
 | 
			
		||||
        """
 | 
			
		||||
        self._forward.off()
 | 
			
		||||
        self._backward.off()
 | 
			
		||||
        self.forward_device.off()
 | 
			
		||||
        self.backward_device.off()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user