diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66ce4db..b6977a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,15 +34,22 @@ jobs: with: repository: adafruit/actions-ci-circuitpython-libs path: actions-ci - - name: Install deps + - name: Install dependencies + # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) run: | source actions-ci/install.sh + - name: Pip install pylint, black, & Sphinx + run: | + pip install --force-reinstall pylint black==19.10b0 Sphinx sphinx-rtd-theme - name: Library version run: git describe --dirty --always --tags + - name: Check formatting + run: | + black --check --target-version=py35 . - name: PyLint run: | pylint $( find . -path './adafruit*.py' ) - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) + ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace $( find . -path "./examples/*.py" )) - name: Build assets run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - name: Build docs diff --git a/.pylintrc b/.pylintrc index 039eaec..d8f0ee8 100644 --- a/.pylintrc +++ b/.pylintrc @@ -52,7 +52,7 @@ confidence= # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" # disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error +disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a62e132..134d510 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -43,7 +43,7 @@ Examples of unacceptable behavior by participants include: The goal of the standards and moderation guidelines outlined here is to build and maintain a respectful community. We ask that you don’t just aim to be -"technically unimpeachable", but rather try to be your best self. +"technically unimpeachable", but rather try to be your best self. We value many things beyond technical expertise, including collaboration and supporting others within our community. Providing a positive experience for @@ -74,9 +74,9 @@ You may report in the following ways: In any situation, you may send an email to . On the Adafruit Discord, you may send an open message from any channel -to all Community Moderators by tagging @community moderators. You may -also send an open message from any channel, or a direct message to -@kattni#1507, @tannewt#4653, @Dan Halbert#1614, @cater#2442, +to all Community Moderators by tagging @community moderators. You may +also send an open message from any channel, or a direct message to +@kattni#1507, @tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, @Mr. Certainly#0472 or @Andon#8175. Email and direct message reports will be kept confidential. diff --git a/adafruit_led_animation/animation.py b/adafruit_led_animation/animation.py index 15ed244..c54e1a2 100644 --- a/adafruit_led_animation/animation.py +++ b/adafruit_led_animation/animation.py @@ -45,7 +45,6 @@ Implementation Notes import random from math import ceil - from . import NANOS_PER_SECOND, monotonic_ns from .color import BLACK, RAINBOW, wheel @@ -470,6 +469,67 @@ class ColorWheel(Animation): self._generator = self._wheel_generator() +class SparklePulse(Animation): + """ + Combination of the Spark and Pulse animations. + + :param pixel_object: The initialised LED object. + :param int speed: Animation refresh rate in seconds, e.g. ``0.1``. + :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. + :param period: Period to pulse the LEDs over. Default 5. + :param max_intensity: The maximum intensity to pulse, between 0 and 1.0. Default 1. + :param min_intensity: The minimum intensity to pulse, between 0 and 1.0. Default 0. + """ + + # pylint: disable=too-many-arguments + def __init__( + self, pixel_object, speed, color, period=5, max_intensity=1, min_intensity=0 + ): + if len(pixel_object) < 2: + raise ValueError("Sparkle needs at least 2 pixels") + self.max_intensity = max_intensity + self.min_intensity = min_intensity + self._period = period + self._intensity_delta = max_intensity - min_intensity + self._half_period = period / 2 + self._position_factor = 1 / self._half_period + self._bpp = len(pixel_object[0]) + self._last_update = monotonic_ns() + self._cycle_position = 0 + self._half_color = None + self._dim_color = None + super(SparklePulse, self).__init__(pixel_object, speed, color) + + def _recompute_color(self, color): + half_color = tuple(color[rgb] // 4 for rgb in range(len(color))) + dim_color = tuple(color[rgb] // 10 for rgb in range(len(color))) + for pixel in range(len(self.pixel_object)): + if self.pixel_object[pixel] == self._half_color: + self.pixel_object[pixel] = half_color + elif self.pixel_object[pixel] == self._dim_color: + self.pixel_object[pixel] = dim_color + self._half_color = half_color + self._dim_color = dim_color + + def draw(self): + pixel = random.randint(0, (len(self.pixel_object) - 2)) + + now = monotonic_ns() + time_since_last_draw = (now - self._last_update) / NANOS_PER_SECOND + self._last_update = now + pos = self._cycle_position = ( + self._cycle_position + time_since_last_draw + ) % self._period + if pos > self._half_period: + pos = self._period - pos + intensity = self.min_intensity + ( + pos * self._intensity_delta * self._position_factor + ) + color = [int(self.color[n] * intensity) for n in range(self._bpp)] + self.pixel_object[pixel] = color + self.show() + + class Chase(Animation): """ Chase pixels in one direction in a single color, like a theater marquee sign. @@ -807,3 +867,4 @@ class AnimationGroup: """ for item in self._members: item.reset() +