diff --git a/gpiozero/boards.py b/gpiozero/boards.py index fcce164..635690a 100644 --- a/gpiozero/boards.py +++ b/gpiozero/boards.py @@ -11,7 +11,7 @@ except ImportError: from time import sleep from collections import namedtuple -from itertools import repeat +from itertools import repeat, cycle, chain from .exc import InputDeviceError, OutputDeviceError from .input_devices import Button @@ -102,18 +102,30 @@ class LEDBoard(SourceMixin, CompositeDevice): for led in self.leds: led.toggle() - def blink(self, on_time=1, off_time=1, n=None, background=True): + def blink( + self, on_time=1, off_time=1, fade_in_time=0, fade_out_time=0, + n=None, background=True): """ Make all the LEDs turn on and off repeatedly. :param float on_time: - Number of seconds on + Number of seconds on. Defaults to 1 second. :param float off_time: - Number of seconds off + Number of seconds off. Defaults to 1 second. + + :param float fade_in_time: + Number of seconds to spend fading in. Defaults to 0. Must be 0 if + ``pwm`` was ``False`` when the class was constructed + (:exc:`ValueError` will be raised if not). + + :param float fade_out_time: + Number of seconds to spend fading out. Defaults to 0. Must be 0 if + ``pwm`` was ``False`` when the class was constructed + (:exc:`ValueError` will be raised if not). :param int n: - Number of times to blink; ``None`` means forever + Number of times to blink; ``None`` (the default) means forever. :param bool background: If ``True``, start a background thread to continue blinking and @@ -121,9 +133,15 @@ class LEDBoard(SourceMixin, CompositeDevice): 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') self._stop_blink() self._blink_thread = GPIOThread( - target=self._blink_leds, args=(on_time, off_time, n) + target=self._blink_leds, + args=(on_time, off_time, fade_in_time, fade_out_time, n) ) self._blink_thread.start() if not background: @@ -135,17 +153,29 @@ class LEDBoard(SourceMixin, CompositeDevice): self._blink_thread.stop() self._blink_thread = None - def _blink_leds(self, on_time, off_time, n): - iterable = repeat(0) if n is None else repeat(0, n) - for i in iterable: + def _blink_leds(self, on_time, off_time, fade_in_time, fade_out_time, n, fps=50): + sequence = [] + if fade_in_time > 0: + sequence += [ + (i * (1 / fps) / fade_in_time, 1 / fps) + for i in range(int(fps * fade_in_time)) + ] + sequence.append((1, on_time)) + if fade_out_time > 0: + sequence += [ + (1 - (i * (1 / fps) / fade_out_time), 1 / fps) + for i in range(int(fps * fade_out_time)) + ] + sequence.append((0, off_time)) + sequence = ( + cycle(sequence) if n is None else + chain.from_iterable(repeat(sequence, n)) + ) + for value, delay in sequence: for led in self.leds: - led.on() - if self._blink_thread.stopping.wait(on_time): - break - for led in self.leds: - led.off() - if self._blink_thread.stopping.wait(off_time): - break + led.value = value + if self._blink_thread.stopping.wait(delay): + break class PiLiter(LEDBoard):