From 7c1b65a25761b90eb2877115908a0359d73344e2 Mon Sep 17 00:00:00 2001 From: Kattni Rembor Date: Wed, 26 Jun 2019 18:14:02 -0400 Subject: [PATCH 1/5] Initial working code. --- .gitignore | 12 + .pylintrc | 433 ++++++++++++++++++++++++++++++++ .readthedocs.yml | 3 + .travis.yml | 48 ++++ CODE_OF_CONDUCT.md | 127 ++++++++++ LICENSE | 21 ++ README.md | 1 - README.rst | 119 +++++++++ adafruit_pybadger.py | 342 +++++++++++++++++++++++++ docs/_static/favicon.ico | Bin 0 -> 4414 bytes docs/api.rst | 8 + docs/conf.py | 160 ++++++++++++ docs/examples.rst | 8 + docs/index.rst | 51 ++++ examples/pybadger_simpletest.py | 0 requirements.txt | 1 + setup.py | 63 +++++ 17 files changed, 1396 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .pylintrc create mode 100644 .readthedocs.yml create mode 100644 .travis.yml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE delete mode 100644 README.md create mode 100644 README.rst create mode 100644 adafruit_pybadger.py create mode 100644 docs/_static/favicon.ico create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 100644 docs/examples.rst create mode 100644 docs/index.rst create mode 100644 examples/pybadger_simpletest.py create mode 100644 requirements.txt create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..55f127b --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.mpy +.idea +__pycache__ +_build +*.pyc +.env +build* +bundles +*.DS_Store +.eggs +dist +**/*.egg-info \ No newline at end of file diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..039eaec --- /dev/null +++ b/.pylintrc @@ -0,0 +1,433 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. +# jobs=1 +jobs=2 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# 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 + +# 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 +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable= + + +[REPORTS] + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio).You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +# notes=FIXME,XXX,TODO +notes=FIXME,XXX + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules=board + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,future.builtins + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +# expected-line-ending-format= +expected-line-ending-format=LF + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module +max-module-lines=1000 + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[BASIC] + +# Naming hint for argument names +argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct argument names +argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for attribute names +attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct attribute names +attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class names +# class-name-hint=[A-Z_][a-zA-Z0-9]+$ +class-name-hint=[A-Z_][a-zA-Z0-9_]+$ + +# Regular expression matching correct class names +# class-rgx=[A-Z_][a-zA-Z0-9]+$ +class-rgx=[A-Z_][a-zA-Z0-9_]+$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming hint for function names +function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct function names +function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Good variable names which should always be accepted, separated by a comma +# good-names=i,j,k,ex,Run,_ +good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for method names +method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct method names +method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Naming hint for variable names +variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct variable names +variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + + +[IMPORTS] + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of attributes for a class (see R0902). +# max-attributes=7 +max-attributes=11 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of statements in function / method body +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=1 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..f4243ad --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,3 @@ +python: + version: 3 +requirements_file: requirements.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2d7e312 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,48 @@ +# This is a common .travis.yml for generating library release zip files for +# CircuitPython library releases using circuitpython-build-tools. +# See https://github.com/adafruit/circuitpython-build-tools for detailed setup +# instructions. + +dist: xenial +language: python +python: + - "3.6" + +cache: + pip: true + +# TODO: if deployment to PyPi is desired, change 'DEPLOY_PYPI' to "true", +# or remove the env block entirely and remove the condition in the +# deploy block. +env: + - DEPLOY_PYPI="false" + +deploy: + - provider: releases + api_key: "$GITHUB_TOKEN" + file_glob: true + file: "$TRAVIS_BUILD_DIR/bundles/*" + skip_cleanup: true + overwrite: true + on: + tags: true + # TODO: Use 'travis encrypt --com -r adafruit/' to generate + # the encrypted password for adafruit-travis. Paste result below. + - provider: pypi + user: adafruit-travis + password: + secure: #-- PASTE ENCRYPTED PASSWORD HERE --# + on: + tags: true + condition: $DEPLOY_PYPI = "true" + +install: + - pip install -r requirements.txt + - pip install circuitpython-build-tools Sphinx sphinx-rtd-theme + - pip install --force-reinstall pylint==1.9.2 + +script: + - pylint adafruit_pybadger.py + - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) + - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-pybadger --library_location . + - cd docs && sphinx-build -E -W -b html . _build/html && cd .. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7ca3a1d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,127 @@ +# Adafruit Community Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and leaders pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level or type of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +We are committed to providing a friendly, safe and welcoming environment for +all. + +Examples of behavior that contributes to creating a positive environment +include: + +* Be kind and courteous to others +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Collaborating with other community members +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and sexual attention or advances +* The use of inappropriate images, including in a community member's avatar +* The use of inappropriate language, including in a community member's nickname +* Any spamming, flaming, baiting or other attention-stealing behavior +* Excessive or unwelcome helping; answering outside the scope of the question + asked +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate + +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. + +We value many things beyond technical expertise, including collaboration and +supporting others within our community. Providing a positive experience for +other community members can have a much more significant impact than simply +providing the correct answer. + +## Our Responsibilities + +Project leaders are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project leaders have the right and responsibility to remove, edit, or +reject messages, comments, commits, code, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any community member for other behaviors that they deem +inappropriate, threatening, offensive, or harmful. + +## Moderation + +Instances of behaviors that violate the Adafruit Community Code of Conduct +may be reported by any member of the community. Community members are +encouraged to report these situations, including situations they witness +involving other community members. + +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 Helpers 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, or +@Andon#8175. + +Email and direct message reports will be kept confidential. + +In situations on Discord where the issue is particularly egregious, possibly +illegal, requires immediate action, or violates the Discord terms of service, +you should also report the message directly to Discord. + +These are the steps for upholding our community’s standards of conduct. + +1. Any member of the community may report any situation that violates the +Adafruit Community Code of Conduct. All reports will be reviewed and +investigated. +2. If the behavior is an egregious violation, the community member who +committed the violation may be banned immediately, without warning. +3. Otherwise, moderators will first respond to such behavior with a warning. +4. Moderators follow a soft "three strikes" policy - the community member may +be given another chance, if they are receptive to the warning and change their +behavior. +5. If the community member is unreceptive or unreasonable when warned by a +moderator, or the warning goes unheeded, they may be banned for a first or +second offense. Repeated offenses will result in the community member being +banned. + +## Scope + +This Code of Conduct and the enforcement policies listed above apply to all +Adafruit Community venues. This includes but is not limited to any community +spaces (both public and private), the entire Adafruit Discord server, and +Adafruit GitHub repositories. Examples of Adafruit Community spaces include +but are not limited to meet-ups, audio chats on the Adafruit Discord, or +interaction at a conference. + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. As a community +member, you are representing our community, and are expected to behave +accordingly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +, +and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). + +For other projects adopting the Adafruit Community Code of +Conduct, please contact the maintainers of those projects for enforcement. +If you wish to use this code of conduct for your own project, consider +explicitly mentioning your moderation policy or making a copy with your +own moderation policy so as to avoid confusion. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e79e919 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Kattni Rembor for Adafruit Industries + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 6cd2f97..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# Adafruit_CircuitPython_PyBadger \ No newline at end of file diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..3f92de7 --- /dev/null +++ b/README.rst @@ -0,0 +1,119 @@ +Introduction +============ + +.. image:: https://readthedocs.org/projects/adafruit-circuitpython-pybadger/badge/?version=latest + :target: https://circuitpython.readthedocs.io/projects/pybadger/en/latest/ + :alt: Documentation Status + +.. image:: https://img.shields.io/discord/327254708534116352.svg + :target: https://discord.gg/nBQh6qu + :alt: Discord + +.. image:: https://travis-ci.com/adafruit/Adafruit_CircuitPython_PyBadger.svg?branch=master + :target: https://travis-ci.com/adafruit/Adafruit_CircuitPython_PyBadger + :alt: Build Status + +Badge-focused CircuitPython helper library for PyBadge and PyGamer. + + +Dependencies +============= +This driver depends on: + +* `Adafruit CircuitPython `_ + +Please ensure all dependencies are available on the CircuitPython filesystem. +This is easily achieved by downloading +`the Adafruit library and driver bundle `_. + +Installing from PyPI +===================== +.. note:: This library is not available on PyPI yet. Install documentation is included + as a standard element. Stay tuned for PyPI availability! + +.. todo:: Remove the above note if PyPI version is/will be available at time of release. + If the library is not planned for PyPI, remove the entire 'Installing from PyPI' section. + +On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from +PyPI `_. To install for current user: + +.. code-block:: shell + + pip3 install adafruit-circuitpython-pybadger + +To install system-wide (this may be required in some cases): + +.. code-block:: shell + + sudo pip3 install adafruit-circuitpython-pybadger + +To install in a virtual environment in your current project: + +.. code-block:: shell + + mkdir project-name && cd project-name + python3 -m venv .env + source .env/bin/activate + pip3 install adafruit-circuitpython-pybadger + +Usage Example +============= + +.. todo:: Add a quick, simple example. It and other examples should live in the examples folder and be included in docs/examples.rst. + +Contributing +============ + +Contributions are welcome! Please read our `Code of Conduct +`_ +before contributing to help this project stay welcoming. + +Building locally +================ + +Zip release files +----------------- + +To build this library locally you'll need to install the +`circuitpython-build-tools `_ package. + +.. code-block:: shell + + python3 -m venv .env + source .env/bin/activate + pip install circuitpython-build-tools + +Once installed, make sure you are in the virtual environment: + +.. code-block:: shell + + source .env/bin/activate + +Then run the build: + +.. code-block:: shell + + circuitpython-build-bundles --filename_prefix adafruit-circuitpython-pybadger --library_location . + +Sphinx documentation +----------------------- + +Sphinx is used to build the documentation based on rST files and comments in the code. First, +install dependencies (feel free to reuse the virtual environment from above): + +.. code-block:: shell + + python3 -m venv .env + source .env/bin/activate + pip install Sphinx sphinx-rtd-theme + +Now, once you have the virtual environment activated: + +.. code-block:: shell + + cd docs + sphinx-build -E -W -b html . _build/html + +This will output the documentation to ``docs/_build/html``. Open the index.html in your browser to +view them. It will also (due to -W) error out on any warning like Travis will. This is a good way to +locally verify it will pass. diff --git a/adafruit_pybadger.py b/adafruit_pybadger.py new file mode 100644 index 0000000..07ee1fb --- /dev/null +++ b/adafruit_pybadger.py @@ -0,0 +1,342 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_pybadger` +================================================================================ + +Badge-focused CircuitPython helper library for PyBadge and PyGamer. + + +* Author(s): Kattni Rembor + +Implementation Notes +-------------------- + +**Hardware:** + +.. todo:: Add links to any specific hardware product page(s), or category page(s). Use unordered list & hyperlink rST + inline format: "* `Link Text `_" + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +.. todo:: Uncomment or remove the Bus Device and/or the Register library dependencies based on the library's use of either. + +# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice +# * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register +""" + +import board +import time +import adafruit_lis3dh +import array +import audioio +import displayio +import digitalio +from gamepadshift import GamePadShift +from micropython import const +import math +import neopixel +import analogio +from adafruit_display_shapes.rect import Rect +from adafruit_display_text.label import Label +import terminalio +from collections import namedtuple +import adafruit_miniqr + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PyBadger.git" + +Buttons = namedtuple("Buttons", "b a start select right down up left") + + +class PyBadger: + # Button Constants + BUTTON_LEFT = const(128) + BUTTON_UP = const(64) + BUTTON_DOWN = const(32) + BUTTON_RIGHT = const(16) + BUTTON_SELECT = const(8) + BUTTON_START = const(4) + BUTTON_A = const(2) + BUTTON_B = const(1) + + def __init__(self, i2c=None): + # Accelerometer + if i2c is None: + i2c = board.I2C() + int1 = digitalio.DigitalInOut(board.ACCELEROMETER_INTERRUPT) + self._accelerometer = adafruit_lis3dh.LIS3DH_I2C(i2c, address=0x19, int1=int1) + + # Buttons + self._buttons = GamePadShift(digitalio.DigitalInOut(board.BUTTON_CLOCK), + digitalio.DigitalInOut(board.BUTTON_OUT), + digitalio.DigitalInOut(board.BUTTON_LATCH)) + + # Display + self.display = board.DISPLAY + + # Light sensor + self._light_sensor = analogio.AnalogIn(board.A7) + + # PyGamer joystick + if hasattr(board, "JOYSTICK_X"): + self._pygamer_joystick_x = analogio.AnalogIn(board.JOYSTICK_X) + self._pygamer_joystick_y = analogio.AnalogIn(board.JOYSTICK_Y) + + # NeoPixels + # Todo: Tie pixelcount to automatically figuring out which board is being used + neopixel_count = 5 + self._neopixels = neopixel.NeoPixel(board.NEOPIXEL, neopixel_count, + pixel_order=neopixel.GRB) + + # Auto dim display based on movement + self._last_accelerometer = None + self._start_time = time.monotonic() + + # Define audio: + self._speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) + self._speaker_enable.switch_to_output(value=False) + self._sample = None + self._sine_wave = None + self._sine_wave_sample = None + + def check_for_movement(self, movement_threshold=1.5): + current_accelerometer = self.acceleration + if self._last_accelerometer is None: + self._last_accelerometer = current_accelerometer + return False + acceleration_delta = sum([abs(self._last_accelerometer[n] - current_accelerometer[n]) for n + in range(3)]) + self._last_accelerometer = current_accelerometer + return acceleration_delta > movement_threshold + + def auto_dim_display(self, delay=5.0): + if not self.check_for_movement(): + current_time = time.monotonic() + if current_time - self._start_time > delay: + self.display.brightness = 0.1 + self._start_time = current_time + else: + self.display.brightness = 1 + + @property + def pixels(self): + return self._neopixels + + @property + def joystick(self): + if hasattr(board, "JOYSTICK_X"): + x = self._pygamer_joystick_x.value + y = self._pygamer_joystick_y.value + return x, y + else: + raise RuntimeError("This board does not have a built in joystick.") + + @property + def button(self): + button_values = self._buttons.get_pressed() + return Buttons(*[button_values & button for button in + (BUTTON_B, BUTTON_A, BUTTON_START, BUTTON_SELECT, BUTTON_RIGHT, + BUTTON_DOWN, BUTTON_UP, BUTTON_LEFT)]) + + @property + def light(self): + return self._light_sensor.value + + @property + def acceleration(self): + return self._accelerometer.acceleration + + @property + def brightness(self): + return self.display.brightness + + @brightness.setter + def brightness(self, value): + self.display.brightness = value + + def badge(self, *, background_color=0xFF0000, foreground_color=0xFFFFFF, + background_text_color=0xFFFFFF, foreground_text_color=0x000000, hello_scale=1, + hello_string="HELLO", my_name_is_scale=1, my_name_is_string="MY NAME IS", + name_scale=1, name_string="Blinka"): + # Make the Display Background + splash = displayio.Group(max_size=20) + + color_bitmap = displayio.Bitmap(self.display.width, self.display.height, 1) + color_palette = displayio.Palette(1) + color_palette[0] = background_color + + bg_sprite = displayio.TileGrid(color_bitmap, + pixel_shader=color_palette, + x=0, y=0) + splash.append(bg_sprite) + + # Draw a Foreground Rectangle where the name goes + # x, y, width, height + rect = Rect(0, (int(self.display.height * 0.4)), self.display.width, + (int(self.display.height * 0.5)), fill=foreground_color) + splash.append(rect) + + hello_scale = hello_scale + hello_group = displayio.Group(scale=hello_scale) + # Setup and Center the Hello Label + hello_label = Label(terminalio.FONT, text=hello_string) + (x, y, w, h) = hello_label.bounding_box + hello_label.x = ((self.display.width // (2 * hello_scale)) - w // 2) + hello_label.y = int(h // (1.2 * hello_scale)) + hello_label.color = background_text_color + hello_group.append(hello_label) + + my_name_is_scale = my_name_is_scale + my_name_is_group = displayio.Group(scale=my_name_is_scale) + # Setup and Center the "My Name Is" Label + my_name_is_label = Label(terminalio.FONT, text=my_name_is_string) + (x, y, w, h) = my_name_is_label.bounding_box + my_name_is_label.x = ((self.display.width // (2 * my_name_is_scale)) - w // 2) + my_name_is_label.y = int(h // (0.42 * my_name_is_scale)) + my_name_is_label.color = background_text_color + my_name_is_group.append(my_name_is_label) + + name_scale = name_scale + name_group = displayio.Group(scale=name_scale) + # Setup and Center the Name Label + name_label = Label(terminalio.FONT, text=name_string) + (x, y, w, h) = name_label.bounding_box + name_label.x = ((self.display.width // (2 * name_scale)) - w // 2) + name_label.y = int(h // (0.17 * name_scale)) + name_label.color = foreground_text_color + name_group.append(name_label) + + group = displayio.Group() + group.append(splash) + group.append(hello_group) + group.append(my_name_is_group) + group.append(name_group) + self.display.show(group) + + @staticmethod + def bitmap_qr(matrix): + # monochome (2 color) palette + border_pixels = 2 + + # bitmap the size of the screen, monochrome (2 colors) + bitmap = displayio.Bitmap(matrix.width + 2 * border_pixels, + matrix.height + 2 * border_pixels, 2) + # raster the QR code + for y in range(matrix.height): # each scanline in the height + for x in range(matrix.width): + if matrix[x, y]: + bitmap[x + border_pixels, y + border_pixels] = 1 + else: + bitmap[x + border_pixels, y + border_pixels] = 0 + return bitmap + + def qr_code(self, data=b'https://circuitpython.org', dwell=20): + qr = adafruit_miniqr.QRCode(qr_type=3, error_correct=adafruit_miniqr.L) + qr.add_data(data) + qr.make() + qr_bitmap = self.bitmap_qr(qr.matrix) + palette = displayio.Palette(2) + palette[0] = 0xFFFFFF + palette[1] = 0x000000 + qr_code_scale = min(self.display.width // qr_bitmap.width, self.display.height // qr_bitmap.height) + qr_position_x = int(((self.display.width / qr_code_scale) - qr_bitmap.width) / 2) + qr_position_y = int(((self.display.height / qr_code_scale) - qr_bitmap.height) / 2) + qr_img = displayio.TileGrid(qr_bitmap, pixel_shader=palette, x=qr_position_x, y=qr_position_y) + qr_code = displayio.Group(scale=qr_code_scale) + qr_code.append(qr_img) + self.display.show(qr_code) + time.sleep(dwell) + + @staticmethod + def _sine_sample(length): + tone_volume = (2 ** 15) - 1 + shift = 2 ** 15 + for i in range(length): + yield int(tone_volume * math.sin(2*math.pi*(i / length)) + shift) + + def _generate_sample(self, length=100): + if self._sample is not None: + return + self._sine_wave = array.array("H", PyBadger._sine_sample(length)) + self._sample = audioio.AudioOut(board.SPEAKER) + self._sine_wave_sample = audioio.RawSample(self._sine_wave) + + def play_tone(self, frequency, duration): + """ Produce a tone using the speaker. Try changing frequency to change + the pitch of the tone. + + :param int frequency: The frequency of the tone in Hz + :param float duration: The duration of the tone in seconds + + """ + # Play a tone of the specified frequency (hz). + self.start_tone(frequency) + time.sleep(duration) + self.stop_tone() + + def start_tone(self, frequency): + """ Produce a tone using the speaker. Try changing frequency to change + the pitch of the tone. + + :param int frequency: The frequency of the tone in Hz + + """ + self._speaker_enable.value = True + length = 100 + if length * frequency > 350000: + length = 350000 // frequency + self._generate_sample(length) + # Start playing a tone of the specified frequency (hz). + self._sine_wave_sample.sample_rate = int(len(self._sine_wave) * frequency) + if not self._sample.playing: + self._sample.play(self._sine_wave_sample, loop=True) + + def stop_tone(self): + """ Use with start_tone to stop the tone produced. + + """ + # Stop playing any tones. + if self._sample is not None and self._sample.playing: + self._sample.stop() + self._sample.deinit() + self._sample = None + self._speaker_enable.value = False + + def play_file(self, file_name): + """ Play a .wav file using the onboard speaker. + + :param file_name: The name of your .wav file in quotation marks including .wav + + """ + # Play a specified file. + self.stop_tone() + self._speaker_enable.value = True + with audioio.AudioOut(board.SPEAKER) as audio: + wavefile = audioio.WaveFile(open(file_name, "rb")) + audio.play(wavefile) + while audio.playing: + pass + self._speaker_enable.value = False diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5aca98376a1f7e593ebd9cf41a808512c2135635 GIT binary patch literal 4414 zcmd^BX;4#F6n=SG-XmlONeGrD5E6J{RVh+e928U#MG!$jWvO+UsvWh`x&VqGNx*en zx=qox7Dqv{kPwo%fZC$dDwVpRtz{HzTkSs8QhG0)%Y=-3@Kt!4ag|JcIo?$-F|?bXVS9UDUyev>MVZQ(H8K4#;BQW-t2CPorj8^KJrMX}QK zp+e<;4ldpXz~=)2GxNy811&)gt-}Q*yVQpsxr@VMoA##{)$1~=bZ1MmjeFw?uT(`8 z^g=09<=zW%r%buwN%iHtuKSg|+r7HkT0PYN*_u9k1;^Ss-Z!RBfJ?Un4w(awqp2b3 z%+myoFis_lTlCrGx2z$0BQdh+7?!JK#9K9@Z!VrG zNj6gK5r(b4?YDOLw|DPRoN7bdP{(>GEG41YcN~4r_SUHU2hgVtUwZG@s%edC;k7Sn zC)RvEnlq~raE2mY2ko64^m1KQL}3riixh?#J{o)IT+K-RdHae2eRX91-+g!y`8^># z-zI0ir>P%Xon)!@xp-BK2bDYUB9k613NRrY6%lVjbFcQc*pRqiK~8xtkNPLxt}e?&QsTB}^!39t_%Qb)~Ukn0O%iC;zt z<&A-y;3h++)>c1br`5VFM~5(83!HKx$L+my8sW_c#@x*|*vB1yU)_dt3vH;2hqPWx zAl^6@?ipx&U7pf`a*>Yq6C85nb+B=Fnn+(id$W#WB^uHAcZVG`qg;rWB}ubvi(Y>D z$ei>REw$#xp0SHAd^|1hq&9HJ=jKK8^zTH~nk)G?yUcmTh9vUM6Y0LMw4(gYVY$D$ zGl&WY&H<)BbJ&3sYbKjx1j^=3-0Q#f^}(aP1?8^`&FUWMp|rmtpK)bLQ1Zo?^s4jqK=Lfg*9&geMGVQ z#^-*!V`fG@;H&{M9S8%+;|h&Qrxym0Ar>WT4BCVLR8cGXF=JmEYN(sNT(9vl+S|%g z8r7nXQ(95i^`=+XHo|){$vf2$?=`F$^&wFlYXyXg$B{a>$-Fp+V}+D;9k=~Xl~?C4 zAB-;RKXdUzBJE{V&d&%R>aEfFe;vxqI$0@hwVM}gFeQR@j}a>DDxR+n+-*6|_)k%% z*mSpDV|=5I9!&VC&9tD%fcVygWZV!iIo2qFtm#!*(s|@ZT33*Ad;+<|3^+yrp*;oH zBSYLV(H1zTU?2WjrCQoQW)Z>J2a=dTriuvezBmu16`tM2fm7Q@d4^iqII-xFpwHGI zn9CL}QE*1vdj2PX{PIuqOe5dracsciH6OlAZATvE8rj6ykqdIjal2 z0S0S~PwHb-5?OQ-tU-^KTG@XNrEVSvo|HIP?H;7ZhYeZkhSqh-{reE!5di;1zk$#Y zCe7rOnlzFYJ6Z#Hm$GoidKB=2HBCwm`BbZVeZY4ukmG%1uz7p2URs6c9j-Gjj^oQV zsdDb3@k2e`C$1I5ML5U0Qs0C1GAp^?!*`=|Nm(vWz3j*j*8ucum2;r0^-6Aca=Gv) zc%}&;!+_*S2tlnnJnz0EKeRmw-Y!@9ob!XQBwiv}^u9MkaXHvM=!<3YX;+2#5Cj5pp?FEK750S3BgeSDtaE^ zXUM@xoV6yBFKfzvY20V&Lr0yC + CircuitPython Reference Documentation + CircuitPython Support Forum + Discord Chat + Adafruit Learning System + Adafruit Blog + Adafruit Store + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/examples/pybadger_simpletest.py b/examples/pybadger_simpletest.py new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..edf9394 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Adafruit-Blinka diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..5b7dd13 --- /dev/null +++ b/setup.py @@ -0,0 +1,63 @@ +"""A setuptools based setup module. + +See: +https://packaging.python.org/en/latest/distributing.html +https://github.com/pypa/sampleproject +""" + +from setuptools import setup, find_packages +# To use a consistent encoding +from codecs import open +from os import path + +here = path.abspath(path.dirname(__file__)) + +# Get the long description from the README file +with open(path.join(here, 'README.rst'), encoding='utf-8') as f: + long_description = f.read() + +setup( + name='adafruit-circuitpython-pybadger', + + use_scm_version=True, + setup_requires=['setuptools_scm'], + + description='Badge-focused CircuitPython helper library for PyBadge and PyGamer.', + long_description=long_description, + long_description_content_type='text/x-rst', + + # The project's main homepage. + url='https://github.com/adafruit/Adafruit_CircuitPython_PyBadger', + + # Author details + author='Adafruit Industries', + author_email='circuitpython@adafruit.com', + + install_requires=[ + 'Adafruit-Blinka' + ], + + # Choose your license + license='MIT', + + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Libraries', + 'Topic :: System :: Hardware', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + ], + + # What does your project relate to? + keywords='adafruit blinka circuitpython micropython pybadger pybadge pygamer badge', + + # You can just specify the packages manually here if your project is + # simple. Or you can use find_packages(). + # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, + # CHANGE `py_modules=['...']` TO `packages=['...']` + py_modules=['adafruit_pybadger'], +) From b3501cecfe3e69549120cbc5f494443c2ef685e1 Mon Sep 17 00:00:00 2001 From: Kattni Rembor Date: Wed, 26 Jun 2019 22:31:34 -0400 Subject: [PATCH 2/5] WIP - cleaned up for PR. --- README.rst | 20 ++++- adafruit_pybadger.py | 149 ++++++++++++++++++++++++-------- docs/conf.py | 4 +- docs/index.rst | 4 - examples/Blinka.bmp | Bin 0 -> 61496 bytes examples/pybadger_simpletest.py | 16 ++++ 6 files changed, 146 insertions(+), 47 deletions(-) create mode 100755 examples/Blinka.bmp diff --git a/README.rst b/README.rst index 3f92de7..6e31995 100644 --- a/README.rst +++ b/README.rst @@ -31,9 +31,6 @@ Installing from PyPI .. note:: This library is not available on PyPI yet. Install documentation is included as a standard element. Stay tuned for PyPI availability! -.. todo:: Remove the above note if PyPI version is/will be available at time of release. - If the library is not planned for PyPI, remove the entire 'Installing from PyPI' section. - On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from PyPI `_. To install for current user: @@ -59,7 +56,22 @@ To install in a virtual environment in your current project: Usage Example ============= -.. todo:: Add a quick, simple example. It and other examples should live in the examples folder and be included in docs/examples.rst. +from adafruit_pybadger import PyBadger + +pybadger = PyBadger() + +while True: + pybadger.badge(hello_scale=2, my_name_is_scale=2, name_scale=3) + pybadger.auto_dim_display() + + if pybadger.button.a: + pybadger.business_card(image_name="Blinka.bmp") + elif pybadger.button.b: + print("b B") + elif pybadger.button.start: + print("b start") + elif pybadger.button.select: + pybadger.qr_code() Contributing ============ diff --git a/adafruit_pybadger.py b/adafruit_pybadger.py index 07ee1fb..f972b18 100644 --- a/adafruit_pybadger.py +++ b/adafruit_pybadger.py @@ -33,36 +33,33 @@ **Hardware:** -.. todo:: Add links to any specific hardware product page(s), or category page(s). Use unordered list & hyperlink rST - inline format: "* `Link Text `_" +* `Adafruit PyBadge `_ +* `Adafruit PyBadge LC `_ +* `Adafruit PyGamer `_ **Software and Dependencies:** * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases -.. todo:: Uncomment or remove the Bus Device and/or the Register library dependencies based on the library's use of either. - -# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice -# * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register """ -import board import time -import adafruit_lis3dh import array +import math +from collections import namedtuple +import board +import adafruit_lis3dh import audioio import displayio import digitalio from gamepadshift import GamePadShift from micropython import const -import math import neopixel import analogio from adafruit_display_shapes.rect import Rect from adafruit_display_text.label import Label import terminalio -from collections import namedtuple import adafruit_miniqr __version__ = "0.0.0-auto.0" @@ -71,7 +68,9 @@ Buttons = namedtuple("Buttons", "b a start select right down up left") +# pylint: disable=too-many-instance-attributes class PyBadger: + """PyBadger class.""" # Button Constants BUTTON_LEFT = const(128) BUTTON_UP = const(64) @@ -106,7 +105,7 @@ def __init__(self, i2c=None): self._pygamer_joystick_y = analogio.AnalogIn(board.JOYSTICK_Y) # NeoPixels - # Todo: Tie pixelcount to automatically figuring out which board is being used + # Count is hardcoded - should be based on board ID, currently no board info for PyBadge LC neopixel_count = 5 self._neopixels = neopixel.NeoPixel(board.NEOPIXEL, neopixel_count, pixel_order=neopixel.GRB) @@ -122,7 +121,8 @@ def __init__(self, i2c=None): self._sine_wave = None self._sine_wave_sample = None - def check_for_movement(self, movement_threshold=1.5): + def _check_for_movement(self, movement_threshold=10): + """Checks to see if board is moving. Used to auto-dim display when not moving.""" current_accelerometer = self.acceleration if self._last_accelerometer is None: self._last_accelerometer = current_accelerometer @@ -132,8 +132,15 @@ def check_for_movement(self, movement_threshold=1.5): self._last_accelerometer = current_accelerometer return acceleration_delta > movement_threshold - def auto_dim_display(self, delay=5.0): - if not self.check_for_movement(): + def auto_dim_display(self, delay=5.0, movement_threshold=10): + """Auto-dim the display when board is not moving. + + :param int delay: Time in seconds before display auto-dims after movement has ceased. + :param int movement_threshold: Threshold required for movement to be considered stopped. + Change to increase or decrease sensitivity. + + """ + if not self._check_for_movement(movement_threshold=movement_threshold): current_time = time.monotonic() if current_time - self._start_time > delay: self.display.brightness = 0.1 @@ -143,44 +150,105 @@ def auto_dim_display(self, delay=5.0): @property def pixels(self): + """Sequence like object representing the NeoPixels on the board.""" return self._neopixels @property def joystick(self): + """The joystick on the PyGamer.""" if hasattr(board, "JOYSTICK_X"): x = self._pygamer_joystick_x.value y = self._pygamer_joystick_y.value return x, y - else: - raise RuntimeError("This board does not have a built in joystick.") + raise RuntimeError("This board does not have a built in joystick.") @property def button(self): + """The buttons on the board. + + Example use: + + .. code-block:: python + + from adafruit_pybadger import PyBadger + + pybadger = PyBadger() + + while True: + if pybadger.button.a: + print("Button A") + elif pybadger.button.b: + print("Button B") + elif pybadger.button.start: + print("Button start") + elif pybadger.button.select: + print("Button select") + + """ button_values = self._buttons.get_pressed() return Buttons(*[button_values & button for button in - (BUTTON_B, BUTTON_A, BUTTON_START, BUTTON_SELECT, BUTTON_RIGHT, - BUTTON_DOWN, BUTTON_UP, BUTTON_LEFT)]) + (PyBadger.BUTTON_B, PyBadger.BUTTON_A, PyBadger.BUTTON_START, + PyBadger.BUTTON_SELECT, PyBadger.BUTTON_RIGHT, + PyBadger.BUTTON_DOWN, PyBadger.BUTTON_UP, PyBadger.BUTTON_LEFT)]) @property def light(self): + """Light sensor data.""" return self._light_sensor.value @property def acceleration(self): + """Accelerometer data.""" return self._accelerometer.acceleration @property def brightness(self): + """Display brightness.""" return self.display.brightness @brightness.setter def brightness(self, value): self.display.brightness = value + def business_card(self, image_name=None, dwell=20): + """Display a bitmap image and a text string, such as a personal image and email address. + CURRENTLY ONLY DISPLAYS BITMAP IMAGE. Text string to be added. + + :param str image_name: The name of the bitmap image including .bmp, e.g. ``"Blinka.bmp"``. + :param int dwell: The amount of time in seconds to display the business card. + + """ + business_card_splash = displayio.Group(max_size=30) + self.display.show(business_card_splash) + with open(image_name, "rb") as file_name: + on_disk_bitmap = displayio.OnDiskBitmap(file_name) + face_image = displayio.TileGrid(on_disk_bitmap, pixel_shader=displayio.ColorConverter()) + business_card_splash.append(face_image) + # Wait for the image to load. + self.display.wait_for_frame() + time.sleep(dwell) + + # pylint: disable=too-many-locals def badge(self, *, background_color=0xFF0000, foreground_color=0xFFFFFF, background_text_color=0xFFFFFF, foreground_text_color=0x000000, hello_scale=1, hello_string="HELLO", my_name_is_scale=1, my_name_is_string="MY NAME IS", name_scale=1, name_string="Blinka"): + """Create a "Hello My Name is"-style badge. + + :param background_color: The color of the background. Defaults to 0xFF0000. + :param foreground_color: The color of the foreground rectangle. Defaults to 0xFFFFFF. + :param background_text_color: The color of the "HELLO MY NAME IS" text. Defaults to + 0xFFFFFF. + :param foreground_text_color: The color of the name text. Defaults to 0x000000. + :param hello_scale: The size scale of the "HELLO" string. Defaults to 1. + :param hello_string: The first string of the badge. Defaults to "HELLO". + :param my_name_is_scale: The size scale of the "MY NAME IS" string. Defaults to 1. + :param my_name_is_string: The second string of the badge. Defaults to "MY NAME IS". + :param name_scale: The size scale of the name string. Defaults to 1. + :param name_string: The third string of the badge - change to be your name. Defaults to + "Blinka". + + """ # Make the Display Background splash = displayio.Group(max_size=20) @@ -203,9 +271,9 @@ def badge(self, *, background_color=0xFF0000, foreground_color=0xFFFFFF, hello_group = displayio.Group(scale=hello_scale) # Setup and Center the Hello Label hello_label = Label(terminalio.FONT, text=hello_string) - (x, y, w, h) = hello_label.bounding_box - hello_label.x = ((self.display.width // (2 * hello_scale)) - w // 2) - hello_label.y = int(h // (1.2 * hello_scale)) + (_, _, width, height) = hello_label.bounding_box + hello_label.x = ((self.display.width // (2 * hello_scale)) - width // 2) + hello_label.y = int(height // (1.2 * hello_scale)) hello_label.color = background_text_color hello_group.append(hello_label) @@ -213,9 +281,9 @@ def badge(self, *, background_color=0xFF0000, foreground_color=0xFFFFFF, my_name_is_group = displayio.Group(scale=my_name_is_scale) # Setup and Center the "My Name Is" Label my_name_is_label = Label(terminalio.FONT, text=my_name_is_string) - (x, y, w, h) = my_name_is_label.bounding_box - my_name_is_label.x = ((self.display.width // (2 * my_name_is_scale)) - w // 2) - my_name_is_label.y = int(h // (0.42 * my_name_is_scale)) + (_, _, width, height) = my_name_is_label.bounding_box + my_name_is_label.x = ((self.display.width // (2 * my_name_is_scale)) - width // 2) + my_name_is_label.y = int(height // (0.42 * my_name_is_scale)) my_name_is_label.color = background_text_color my_name_is_group.append(my_name_is_label) @@ -223,9 +291,9 @@ def badge(self, *, background_color=0xFF0000, foreground_color=0xFFFFFF, name_group = displayio.Group(scale=name_scale) # Setup and Center the Name Label name_label = Label(terminalio.FONT, text=name_string) - (x, y, w, h) = name_label.bounding_box - name_label.x = ((self.display.width // (2 * name_scale)) - w // 2) - name_label.y = int(h // (0.17 * name_scale)) + (_, _, width, height) = name_label.bounding_box + name_label.x = ((self.display.width // (2 * name_scale)) - width // 2) + name_label.y = int(height // (0.17 * name_scale)) name_label.color = foreground_text_color name_group.append(name_label) @@ -238,14 +306,11 @@ def badge(self, *, background_color=0xFF0000, foreground_color=0xFFFFFF, @staticmethod def bitmap_qr(matrix): - # monochome (2 color) palette + """The QR code bitmap.""" border_pixels = 2 - - # bitmap the size of the screen, monochrome (2 colors) bitmap = displayio.Bitmap(matrix.width + 2 * border_pixels, matrix.height + 2 * border_pixels, 2) - # raster the QR code - for y in range(matrix.height): # each scanline in the height + for y in range(matrix.height): for x in range(matrix.width): if matrix[x, y]: bitmap[x + border_pixels, y + border_pixels] = 1 @@ -254,17 +319,25 @@ def bitmap_qr(matrix): return bitmap def qr_code(self, data=b'https://circuitpython.org', dwell=20): - qr = adafruit_miniqr.QRCode(qr_type=3, error_correct=adafruit_miniqr.L) - qr.add_data(data) - qr.make() - qr_bitmap = self.bitmap_qr(qr.matrix) + """Generate a QR code and display it for ``dwell`` seconds. + + :param bytearray data: A bytearray of data for the QR code + :param int dwell: The amount of time in seconds to display the QR code + + """ + qr_code = adafruit_miniqr.QRCode(qr_type=3, error_correct=adafruit_miniqr.L) + qr_code.add_data(data) + qr_code.make() + qr_bitmap = self.bitmap_qr(qr_code.matrix) palette = displayio.Palette(2) palette[0] = 0xFFFFFF palette[1] = 0x000000 - qr_code_scale = min(self.display.width // qr_bitmap.width, self.display.height // qr_bitmap.height) + qr_code_scale = min(self.display.width // qr_bitmap.width, + self.display.height // qr_bitmap.height) qr_position_x = int(((self.display.width / qr_code_scale) - qr_bitmap.width) / 2) qr_position_y = int(((self.display.height / qr_code_scale) - qr_bitmap.height) / 2) - qr_img = displayio.TileGrid(qr_bitmap, pixel_shader=palette, x=qr_position_x, y=qr_position_y) + qr_img = displayio.TileGrid(qr_bitmap, pixel_shader=palette, x=qr_position_x, + y=qr_position_y) qr_code = displayio.Group(scale=qr_code_scale) qr_code.append(qr_img) self.display.show(qr_code) diff --git a/docs/conf.py b/docs/conf.py index 889ae15..6adb416 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,9 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -# autodoc_mock_imports = ["digitalio", "busio"] +autodoc_mock_imports = ["audioio", "displayio", "gamepadshift", "neopixel", "analogio", + "adafruit_display_shapes", "adafruit_display_text", "terminalio", + "adafruit_miniqr"] intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)} diff --git a/docs/index.rst b/docs/index.rst index bcf7159..98b65bb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,14 +23,10 @@ Table of Contents .. toctree:: :caption: Tutorials -.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave - the toctree above for use later. .. toctree:: :caption: Related Products -.. todo:: Add any product links here. If there are none, then simply delete this todo and leave - the toctree above for use later. .. toctree:: :caption: Other Links diff --git a/examples/Blinka.bmp b/examples/Blinka.bmp new file mode 100755 index 0000000000000000000000000000000000000000..d1b93c6554c1c9347f7bca2f546306f8020a2c3a GIT binary patch literal 61496 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 Date: Wed, 26 Jun 2019 22:41:46 -0400 Subject: [PATCH 3/5] Import order. --- adafruit_pybadger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_pybadger.py b/adafruit_pybadger.py index f972b18..4fe78f7 100644 --- a/adafruit_pybadger.py +++ b/adafruit_pybadger.py @@ -49,7 +49,6 @@ import math from collections import namedtuple import board -import adafruit_lis3dh import audioio import displayio import digitalio @@ -61,6 +60,7 @@ from adafruit_display_text.label import Label import terminalio import adafruit_miniqr +import adafruit_lis3dh __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PyBadger.git" From 19a52802be6bf2ce77276d7e2f8eb6c46ec75c6d Mon Sep 17 00:00:00 2001 From: Kattni Rembor Date: Wed, 26 Jun 2019 22:43:24 -0400 Subject: [PATCH 4/5] Import order. --- adafruit_pybadger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_pybadger.py b/adafruit_pybadger.py index 4fe78f7..2ae70d1 100644 --- a/adafruit_pybadger.py +++ b/adafruit_pybadger.py @@ -49,11 +49,11 @@ import math from collections import namedtuple import board +from micropython import const +import digitalio import audioio import displayio -import digitalio from gamepadshift import GamePadShift -from micropython import const import neopixel import analogio from adafruit_display_shapes.rect import Rect From 434a1214efa0284b672fd8722ab894460061529a Mon Sep 17 00:00:00 2001 From: Kattni Rembor Date: Wed, 26 Jun 2019 22:46:47 -0400 Subject: [PATCH 5/5] Sphinx. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 6adb416..58a8916 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ # autodoc module docs will fail to generate with a warning. autodoc_mock_imports = ["audioio", "displayio", "gamepadshift", "neopixel", "analogio", "adafruit_display_shapes", "adafruit_display_text", "terminalio", - "adafruit_miniqr"] + "adafruit_miniqr", "adafruit_lis3dh", "adafruit_miniqr"] intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)}