diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..21c125c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +.py text eol=lf +.rst text eol=lf +.txt text eol=lf +.yaml text eol=lf +.toml text eol=lf +.license text eol=lf +.md text eol=lf diff --git a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md new file mode 100644 index 0000000..8de294e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2021 Adafruit Industries +# +# SPDX-License-Identifier: MIT + +Thank you for contributing! Before you submit a pull request, please read the following. + +Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://docs.circuitpython.org/en/latest/docs/design_guide.html + +If your changes are to documentation, please verify that the documentation builds locally by following the steps found here: https://adafru.it/build-docs + +Before submitting the pull request, make sure you've run Pylint and Black locally on your code. You can do this manually or using pre-commit. Instructions are available here: https://adafru.it/check-your-code + +Please remove all of this text before submitting. Include an explanation or list of changes included in your PR, as well as, if applicable, a link to any related issues. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..041a337 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Build CI + +on: [pull_request, push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main diff --git a/.github/workflows/failure-help-text.yml b/.github/workflows/failure-help-text.yml new file mode 100644 index 0000000..0b1194f --- /dev/null +++ b/.github/workflows/failure-help-text.yml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2021 Scott Shawcroft for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Failure help text + +on: + workflow_run: + workflows: ["Build CI"] + types: + - completed + +jobs: + post-help: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.event == 'pull_request' }} + steps: + - name: Post comment to help + uses: adafruit/circuitpython-action-library-ci-failed@v1 diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml new file mode 100644 index 0000000..9acec60 --- /dev/null +++ b/.github/workflows/release_gh.yml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: GitHub Release Actions + +on: + release: + types: [published] + +jobs: + upload-release-assets: + runs-on: ubuntu-latest + steps: + - name: Run GitHub Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-gh@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + upload-url: ${{ github.event.release.upload_url }} diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml new file mode 100644 index 0000000..65775b7 --- /dev/null +++ b/.github/workflows/release_pypi.yml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: PyPI Release Actions + +on: + release: + types: [published] + +jobs: + upload-release-assets: + runs-on: ubuntu-latest + steps: + - name: Run PyPI Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-pypi@main + with: + pypi-username: ${{ secrets.pypi_username }} + pypi-password: ${{ secrets.pypi_password }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..db3d538 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2022 Kattni Rembor, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# Do not include files and directories created by your personal work environment, such as the IDE +# you use, except for those already listed here. Pull requests including changes to this file will +# not be accepted. + +# This .gitignore file contains rules for files generated by working with CircuitPython libraries, +# including building Sphinx, testing with pip, and creating a virual environment, as well as the +# MacOS and IDE-specific files generated by using MacOS in general, or the PyCharm or VSCode IDEs. + +# If you find that there are files being generated on your machine that should not be included in +# your git commit, you should create a .gitignore_global file on your computer to include the +# files created by your personal setup. To do so, follow the two steps below. + +# First, create a file called .gitignore_global somewhere convenient for you, and add rules for +# the files you want to exclude from git commits. + +# Second, configure Git to use the exclude file for all Git repositories by running the +# following via commandline, replacing "path/to/your/" with the actual path to your newly created +# .gitignore_global file: +# git config --global core.excludesfile path/to/your/.gitignore_global + +# CircuitPython-specific files +*.mpy + +# Python-specific files +__pycache__ +*.pyc + +# Sphinx build-specific files +_build + +# This file results from running `pip -e install .` in a local repository +*.egg-info + +# Virtual environment-specific files +.env +.venv + +# MacOS-specific files +*.DS_Store + +# IDE-specific files +.idea +.vscode +*~ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ff19dde --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.4 + hooks: + - id: ruff-format + - id: ruff + args: ["--fix"] + - repo: https://github.com/fsfe/reuse-tool + rev: v3.0.1 + hooks: + - id: reuse diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..255dafd --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +sphinx: + configuration: docs/conf.py + +build: + os: ubuntu-lts-latest + tools: + python: "3" + +python: + install: + - requirements: docs/requirements.txt + - requirements: requirements.txt diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 1617586..8a55c07 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,74 +1,135 @@ -# Contributor Covenant Code of Conduct + + +# Adafruit Community Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and +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 of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. +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 unwelcome sexual attention or -advances +* 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 +* Promoting or spreading disinformation, lies, or conspiracy theories against + a person, group, organisation, project, or community * 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 in a - professional setting +* 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 maintainers are responsible for clarifying the standards of acceptable +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 maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions +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 contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +permanently any community member for other behaviors that they deem +inappropriate, threatening, offensive, or harmful. -## Scope +## Moderation -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +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 . -## Enforcement +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, +@sommersoft#0222, @Mr. Certainly#0472 or @Andon#8175. -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at support@adafruit.com. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. +Email and direct message reports will be kept confidential. -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. +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 [http://contributor-covenant.org/version/1/4][version] +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). -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +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..75d7f43 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017 Mike McWethy 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/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt new file mode 100644 index 0000000..3f92dfc --- /dev/null +++ b/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,324 @@ +Creative Commons Attribution 4.0 International Creative Commons Corporation +("Creative Commons") is not a law firm and does not provide legal services +or legal advice. Distribution of Creative Commons public licenses does not +create a lawyer-client or other relationship. Creative Commons makes its licenses +and related information available on an "as-is" basis. Creative Commons gives +no warranties regarding its licenses, any material licensed under their terms +and conditions, or any related information. Creative Commons disclaims all +liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions +that creators and other rights holders may use to share original works of +authorship and other material subject to copyright and certain other rights +specified in the public license below. The following considerations are for +informational purposes only, are not exhaustive, and do not form part of our +licenses. + +Considerations for licensors: Our public licenses are intended for use by +those authorized to give the public permission to use material in ways otherwise +restricted by copyright and certain other rights. Our licenses are irrevocable. +Licensors should read and understand the terms and conditions of the license +they choose before applying it. Licensors should also secure all rights necessary +before applying our licenses so that the public can reuse the material as +expected. Licensors should clearly mark any material not subject to the license. +This includes other CC-licensed material, or material used under an exception +or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors + +Considerations for the public: By using one of our public licenses, a licensor +grants the public permission to use the licensed material under specified +terms and conditions. If the licensor's permission is not necessary for any +reason–for example, because of any applicable exception or limitation to copyright–then +that use is not regulated by the license. Our licenses grant only permissions +under copyright and certain other rights that a licensor has authority to +grant. Use of the licensed material may still be restricted for other reasons, +including because others have copyright or other rights in the material. A +licensor may make special requests, such as asking that all changes be marked +or described. Although not required by our licenses, you are encouraged to +respect those requests where reasonable. More considerations for the public +: wiki.creativecommons.org/Considerations_for_licensees Creative Commons Attribution +4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to +be bound by the terms and conditions of this Creative Commons Attribution +4.0 International Public License ("Public License"). To the extent this Public +License may be interpreted as a contract, You are granted the Licensed Rights +in consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the Licensor +receives from making the Licensed Material available under these terms and +conditions. + +Section 1 – Definitions. + +a. Adapted Material means material subject to Copyright and Similar Rights +that is derived from or based upon the Licensed Material and in which the +Licensed Material is translated, altered, arranged, transformed, or otherwise +modified in a manner requiring permission under the Copyright and Similar +Rights held by the Licensor. For purposes of this Public License, where the +Licensed Material is a musical work, performance, or sound recording, Adapted +Material is always produced where the Licensed Material is synched in timed +relation with a moving image. + +b. Adapter's License means the license You apply to Your Copyright and Similar +Rights in Your contributions to Adapted Material in accordance with the terms +and conditions of this Public License. + +c. Copyright and Similar Rights means copyright and/or similar rights closely +related to copyright including, without limitation, performance, broadcast, +sound recording, and Sui Generis Database Rights, without regard to how the +rights are labeled or categorized. For purposes of this Public License, the +rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + +d. Effective Technological Measures means those measures that, in the absence +of proper authority, may not be circumvented under laws fulfilling obligations +under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, +and/or similar international agreements. + +e. Exceptions and Limitations means fair use, fair dealing, and/or any other +exception or limitation to Copyright and Similar Rights that applies to Your +use of the Licensed Material. + +f. Licensed Material means the artistic or literary work, database, or other +material to which the Licensor applied this Public License. + +g. Licensed Rights means the rights granted to You subject to the terms and +conditions of this Public License, which are limited to all Copyright and +Similar Rights that apply to Your use of the Licensed Material and that the +Licensor has authority to license. + +h. Licensor means the individual(s) or entity(ies) granting rights under this +Public License. + +i. Share means to provide material to the public by any means or process that +requires permission under the Licensed Rights, such as reproduction, public +display, public performance, distribution, dissemination, communication, or +importation, and to make material available to the public including in ways +that members of the public may access the material from a place and at a time +individually chosen by them. + +j. Sui Generis Database Rights means rights other than copyright resulting +from Directive 96/9/EC of the European Parliament and of the Council of 11 +March 1996 on the legal protection of databases, as amended and/or succeeded, +as well as other essentially equivalent rights anywhere in the world. + +k. You means the individual or entity exercising the Licensed Rights under +this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + +1. Subject to the terms and conditions of this Public License, the Licensor +hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, +irrevocable license to exercise the Licensed Rights in the Licensed Material +to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + +2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions +and Limitations apply to Your use, this Public License does not apply, and +You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + +4. Media and formats; technical modifications allowed. The Licensor authorizes +You to exercise the Licensed Rights in all media and formats whether now known +or hereafter created, and to make technical modifications necessary to do +so. The Licensor waives and/or agrees not to assert any right or authority +to forbid You from making technical modifications necessary to exercise the +Licensed Rights, including technical modifications necessary to circumvent +Effective Technological Measures. For purposes of this Public License, simply +making modifications authorized by this Section 2(a)(4) never produces Adapted +Material. + + 5. Downstream recipients. + +A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed +Material automatically receives an offer from the Licensor to exercise the +Licensed Rights under the terms and conditions of this Public License. + +B. No downstream restrictions. You may not offer or impose any additional +or different terms or conditions on, or apply any Effective Technological +Measures to, the Licensed Material if doing so restricts exercise of the Licensed +Rights by any recipient of the Licensed Material. + +6. No endorsement. Nothing in this Public License constitutes or may be construed +as permission to assert or imply that You are, or that Your use of the Licensed +Material is, connected with, or sponsored, endorsed, or granted official status +by, the Licensor or others designated to receive attribution as provided in +Section 3(a)(1)(A)(i). + + b. Other rights. + +1. Moral rights, such as the right of integrity, are not licensed under this +Public License, nor are publicity, privacy, and/or other similar personality +rights; however, to the extent possible, the Licensor waives and/or agrees +not to assert any such rights held by the Licensor to the limited extent necessary +to allow You to exercise the Licensed Rights, but not otherwise. + +2. Patent and trademark rights are not licensed under this Public License. + +3. To the extent possible, the Licensor waives any right to collect royalties +from You for the exercise of the Licensed Rights, whether directly or through +a collecting society under any voluntary or waivable statutory or compulsory +licensing scheme. In all other cases the Licensor expressly reserves any right +to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following +conditions. + + a. Attribution. + +1. If You Share the Licensed Material (including in modified form), You must: + +A. retain the following if it is supplied by the Licensor with the Licensed +Material: + +i. identification of the creator(s) of the Licensed Material and any others +designated to receive attribution, in any reasonable manner requested by the +Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + +v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + +B. indicate if You modified the Licensed Material and retain an indication +of any previous modifications; and + +C. indicate the Licensed Material is licensed under this Public License, and +include the text of, or the URI or hyperlink to, this Public License. + +2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner +based on the medium, means, and context in which You Share the Licensed Material. +For example, it may be reasonable to satisfy the conditions by providing a +URI or hyperlink to a resource that includes the required information. + +3. If requested by the Licensor, You must remove any of the information required +by Section 3(a)(1)(A) to the extent reasonably practicable. + +4. If You Share Adapted Material You produce, the Adapter's License You apply +must not prevent recipients of the Adapted Material from complying with this +Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to +Your use of the Licensed Material: + +a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, +reuse, reproduce, and Share all or a substantial portion of the contents of +the database; + +b. if You include all or a substantial portion of the database contents in +a database in which You have Sui Generis Database Rights, then the database +in which You have Sui Generis Database Rights (but not its individual contents) +is Adapted Material; and + +c. You must comply with the conditions in Section 3(a) if You Share all or +a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not replace +Your obligations under this Public License where the Licensed Rights include +other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + +a. Unless otherwise separately undertaken by the Licensor, to the extent possible, +the Licensor offers the Licensed Material as-is and as-available, and makes +no representations or warranties of any kind concerning the Licensed Material, +whether express, implied, statutory, or other. This includes, without limitation, +warranties of title, merchantability, fitness for a particular purpose, non-infringement, +absence of latent or other defects, accuracy, or the presence or absence of +errors, whether or not known or discoverable. Where disclaimers of warranties +are not allowed in full or in part, this disclaimer may not apply to You. + +b. To the extent possible, in no event will the Licensor be liable to You +on any legal theory (including, without limitation, negligence) or otherwise +for any direct, special, indirect, incidental, consequential, punitive, exemplary, +or other losses, costs, expenses, or damages arising out of this Public License +or use of the Licensed Material, even if the Licensor has been advised of +the possibility of such losses, costs, expenses, or damages. Where a limitation +of liability is not allowed in full or in part, this limitation may not apply +to You. + +c. The disclaimer of warranties and limitation of liability provided above +shall be interpreted in a manner that, to the extent possible, most closely +approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + +a. This Public License applies for the term of the Copyright and Similar Rights +licensed here. However, if You fail to comply with this Public License, then +Your rights under this Public License terminate automatically. + +b. Where Your right to use the Licensed Material has terminated under Section +6(a), it reinstates: + +1. automatically as of the date the violation is cured, provided it is cured +within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + +c. For the avoidance of doubt, this Section 6(b) does not affect any right +the Licensor may have to seek remedies for Your violations of this Public +License. + +d. For the avoidance of doubt, the Licensor may also offer the Licensed Material +under separate terms or conditions or stop distributing the Licensed Material +at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + +a. The Licensor shall not be bound by any additional or different terms or +conditions communicated by You unless expressly agreed. + +b. Any arrangements, understandings, or agreements regarding the Licensed +Material not stated herein are separate from and independent of the terms +and conditions of this Public License. + +Section 8 – Interpretation. + +a. For the avoidance of doubt, this Public License does not, and shall not +be interpreted to, reduce, limit, restrict, or impose conditions on any use +of the Licensed Material that could lawfully be made without permission under +this Public License. + +b. To the extent possible, if any provision of this Public License is deemed +unenforceable, it shall be automatically reformed to the minimum extent necessary +to make it enforceable. If the provision cannot be reformed, it shall be severed +from this Public License without affecting the enforceability of the remaining +terms and conditions. + +c. No term or condition of this Public License will be waived and no failure +to comply consented to unless expressly agreed to by the Licensor. + +d. Nothing in this Public License constitutes or may be interpreted as a limitation +upon, or waiver of, any privileges and immunities that apply to the Licensor +or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative +Commons may elect to apply one of its public licenses to material it publishes +and in those instances will be considered the "Licensor." The text of the +Creative Commons public licenses is dedicated to the public domain under the +CC0 Public Domain Dedication. Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as otherwise +permitted by the Creative Commons policies published at creativecommons.org/policies, +Creative Commons does not authorize the use of the trademark "Creative Commons" +or any other trademark or logo of Creative Commons without its prior written +consent including, without limitation, in connection with any unauthorized +modifications to any of its public licenses or any other arrangements, understandings, +or agreements concerning use of licensed material. For the avoidance of doubt, +this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 0000000..204b93d --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,19 @@ +MIT License Copyright (c) + +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 (including the next +paragraph) 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/LICENSES/Unlicense.txt b/LICENSES/Unlicense.txt new file mode 100644 index 0000000..24a8f90 --- /dev/null +++ b/LICENSES/Unlicense.txt @@ -0,0 +1,20 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute +this software, either in source code form or as a compiled binary, for any +purpose, commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and +to the detriment of our heirs and successors. We intend this dedication to +be an overt act of relinquishment in perpetuity of all present and future +rights to this software under copyright law. + +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 +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. For more information, +please refer to diff --git a/README.rst b/README.rst index 08f601d..a040868 100644 --- a/README.rst +++ b/README.rst @@ -3,12 +3,21 @@ Introduction ============ .. image:: https://readthedocs.org/projects/adafruit-circuitpython-dht/badge/?version=latest - :target: https://circuitpython.readthedocs.io/projects/dht/en/latest/ + :target: https://docs.circuitpython.org/projects/dht/en/latest/ :alt: Documentation Status -.. image :: https://badges.gitter.im/adafruit/circuitpython.svg - :target: https://gitter.im/adafruit/circuitpython?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge - :alt: Gitter + +.. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg + :target: https://adafru.it/discord + :alt: Discord + +.. image:: https://github.com/adafruit/Adafruit_CircuitPython_DHT/workflows/Build%20CI/badge.svg + :target: https://github.com/adafruit/Adafruit_CircuitPython_DHT/actions + :alt: Build Status + +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Code Style: Ruff CircuitPython support for the DHT11 and DHT22 temperature and humidity devices. @@ -22,18 +31,74 @@ Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading `the Adafruit library and driver bundle `_. +.. note:: + This library uses the `pulseio` module in CircuitPython. As of CircuitPython 7.0.0, `pulseio` is + no longer available on the smallest CircuitPython builds, + such as the Trinket M0, Gemma M0, and Feather M0 Basic boards. + You can substitute a more modern sensor, which will work better as well. + See the guide `Modern Replacements for DHT11 and DHT22 Sensors + `_ + for suggestions. + +Installing from PyPI +==================== + +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-dht + +To install system-wide (this may be required in some cases): + +.. code-block:: shell + + sudo pip3 install adafruit-circuitpython-dht + +To install in a virtual environment in your current project: + +.. code-block:: shell + + mkdir project-name && cd project-name + python3 -m venv .venv + source .venv/bin/activate + pip3 install adafruit-circuitpython-dht + Usage Example -============= +============== Hardware Set-up ---------------- +---------------- + +Designed specifically to work with the Adafruit DHT series sensors: -The DHT11 and DHT22 devices both need a pull-resistor on the data signal wire. -This resistor is in the range of 1k to 5k. Please check your device datasheet for the -appropriate value. +* Adafruit `DHT22 temperature-humidity sensor + extras `_ +* Adafruit `DHT11 temperature-humidity sensor + extras `_ + +.. note:: + DHT11 and DHT22 devices both need a pull-resistor on the data signal wire. This resistor is in the range of 1k to 5k + + +* Please check the device datasheet for the appropriate value. +* Be sure that you are running the Buster Operating System. +* Make sure that your user is part of the ``gpio`` group. + + +Known Issues +------------ + +* The library may or may not work in Linux 64-bit platforms. +* The Raspberry PI Zero does not provide reliable readings. +* Readings in FeatherS2 does not work as expected. + +.. note:: + Using a more modern sensor will avoid these issues. + See the guide `Modern Replacements for DHT11 and DHT22 Sensors + `_. Basics ------- +------- Of course, you must import the library to use it: @@ -60,7 +125,7 @@ OR initialize the DHT22 device: dht_device = adafruit_dht.DHT22() Read temperature and humidity ----------------------------- +------------------------------ Now get the temperature and humidity values @@ -69,20 +134,20 @@ Now get the temperature and humidity values temperature = dht_device.temperature humidity = dht_device.humidity -These properties may raise an exception if a problem occurs. You should use try/raise -logic and catch RuntimeError and then retry getting the values after 1/2 second. +These properties may raise an exception if a problem occurs. You should use try/raise +logic and catch RuntimeError and then retry getting the values after at least 2 seconds. +If you try again to get a result within 2 seconds, cached values are returned. + +Documentation +============= + +API documentation for this library can be found on `Read the Docs `_. + +For information on building library documentation, please check out `this guide `_. Contributing ============ Contributions are welcome! Please read our `Code of Conduct -`_ +`_ before contributing to help this project stay welcoming. - -API Reference -============= - -.. toctree:: - :maxdepth: 2 - - api diff --git a/README.rst.license b/README.rst.license new file mode 100644 index 0000000..11cd75d --- /dev/null +++ b/README.rst.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries + +SPDX-License-Identifier: MIT diff --git a/adafruit_dht.py b/adafruit_dht.py index bc9b1f0..bba1da3 100644 --- a/adafruit_dht.py +++ b/adafruit_dht.py @@ -1,24 +1,7 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2017 Mike McWethy for Adafruit Industries # -# Copyright (c) 2017 Mike McWethy 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. +# SPDX-License-Identifier: MIT + """ :mod:`adafruit_dhtlib` ====================== @@ -26,38 +9,85 @@ CircuitPython support for the DHT11 and DHT22 temperature and humidity devices. * Author(s): Mike McWethy + +**Hardware:** + +* Adafruit `DHT22 temperature-humidity sensor + extras + `_ (Product ID: 385) + +* Adafruit `DHT11 basic temperature-humidity sensor + extras + `_ (Product ID: 386) + + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://circuitpython.org/downloads + """ import array import time +from os import uname + +from digitalio import DigitalInOut, Direction, Pull + +_USE_PULSEIO = False +try: + from pulseio import PulseIn + + _USE_PULSEIO = True +except (ImportError, NotImplementedError): + pass # This is OK, we'll try to bitbang it! + try: - import pulseio -except ImportError as excpt: - print("adafruit_dht requires the pulseio library, but it failed to load."+ - " Note that CircuitPython does not support pulseio on all boards.") - raise excpt + # Used only for typing + from typing import Union + + from microcontroller import Pin +except ImportError: + pass + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DHT.git" + class DHTBase: - """ base support for DHT11 and DHT22 devices + """base support for DHT11 and DHT22 devices + + :param bool dht11: True if device is DHT11, otherwise DHT22. + :param ~board.Pin pin: digital pin used for communication + :param int trig_wait: length of time to hold trigger in LOW state (microseconds) + :param bool use_pulseio: False to force bitbang when pulseio available (only with Blinka) """ __hiLevel = 51 - def __init__(self, dht11, pin, trig_wait): - """ - :param boolean dht11: True if device is DHT11, otherwise DHT22. - :param ~board.Pin pin: digital pin used for communication - :param int trig_wait: length of time to hold trigger in LOW state (microseconds) - """ + def __init__( + self, dht11: bool, pin: Pin, trig_wait: int, use_pulseio: bool, *, max_pulses: int = 81 + ): self._dht11 = dht11 self._pin = pin self._trig_wait = trig_wait + self._max_pulses = max_pulses self._last_called = 0 self._humidity = None self._temperature = None - - - def _pulses_to_binary(self, pulses, start, stop): + self._use_pulseio = use_pulseio + if "Linux" not in uname() and not self._use_pulseio: + raise ValueError("Bitbanging is not supported when using CircuitPython.") + # We don't use a context because linux-based systems are sluggish + # and we're better off having a running process + if self._use_pulseio: + self.pulse_in = PulseIn(self._pin, maxlen=self._max_pulses, idle_state=True) + self.pulse_in.pause() + + def exit(self) -> None: + """Cleans up the PulseIn process. Must be called explicitly""" + if self._use_pulseio: + self.pulse_in.deinit() + + def _pulses_to_binary(self, pulses: array.array, start: int, stop: int) -> int: """Takes pulses, a list of transition times, and converts them to a 1's or 0's. The pulses array contains the transition times. pulses starts with a low transition time followed by a high transistion time. @@ -80,14 +110,14 @@ def _pulses_to_binary(self, pulses, start, stop): bit = 0 if pulses[bit_inx] > self.__hiLevel: bit = 1 - binary = binary<<1 | bit + binary = binary << 1 | bit hi_sig = not hi_sig return binary - def _get_pulses(self): - """ _get_pulses implements the communication protcol for + def _get_pulses_pulseio(self) -> array.array: + """_get_pulses implements the communication protocol for DHT11 and DHT22 type devices. It sends a start signal of a specific length and listens and measures the return signal lengths. @@ -96,119 +126,189 @@ def _get_pulses(self): transition times starting with a low transition time. Normally pulses will have 81 elements for the DHT11/22 type devices. """ - pulses = array.array('H') - tmono = time.monotonic() - - # create the PulseIn object using context manager - with pulseio.PulseIn(self._pin, 81, True) as pulse_in: - + pulses = array.array("H") + if self._use_pulseio: # The DHT type device use a specialize 1-wire protocol # The microprocessor first sends a LOW signal for a # specific length of time. Then the device sends back a # series HIGH and LOW signals. The length the HIGH signals # represents the device values. - pulse_in.pause() - pulse_in.clear() - pulse_in.resume(self._trig_wait) + self.pulse_in.clear() + self.pulse_in.resume(self._trig_wait) # loop until we get the return pulse we need or - # time out after 1/2 seconds - while True: - if len(pulse_in) >= 80: - break - if time.monotonic()-tmono > 0.5: # time out after 1/2 seconds - break - - pulse_in.pause() - while len(pulse_in): - pulses.append(pulse_in.popleft()) - pulse_in.resume() - + # time out after 1/4 second + time.sleep(0.25) + self.pulse_in.pause() + while self.pulse_in: + pulses.append(self.pulse_in.popleft()) return pulses - def measure(self): - """ measure runs the communications to the DHT11/22 type device. - if successful, the class properties temperature and humidity will - return the reading returned from the device. + def _get_pulses_bitbang(self) -> array.array: + """_get_pulses implements the communication protcol for + DHT11 and DHT22 type devices. It sends a start signal + of a specific length and listens and measures the + return signal lengths. - Raises RuntimeError exception for checksum failure and for insuffcient - data returned from the device (try again) + return pulses (array.array uint16) contains alternating high and low + transition times starting with a low transition time. Normally + pulses will have 81 elements for the DHT11/22 type devices. """ - if time.monotonic()-self._last_called > 0.5: - self._last_called = time.monotonic() + pulses = array.array("H") + with DigitalInOut(self._pin) as dhtpin: + # we will bitbang if no pulsein capability + transitions = [] + # Signal by setting pin high, then low, and releasing + dhtpin.direction = Direction.OUTPUT + dhtpin.value = True + time.sleep(0.1) + dhtpin.value = False + # Using the time to pull-down the line according to DHT Model + time.sleep(self._trig_wait / 1000000) + timestamp = time.monotonic() # take timestamp + dhtval = True # start with dht pin true because its pulled up + dhtpin.direction = Direction.INPUT + + try: + dhtpin.pull = Pull.UP + # Catch the NotImplementedError raised because + # blinka.microcontroller.generic_linux.libgpiod_pin does not support + # internal pull resistors. + except NotImplementedError: + dhtpin.pull = None + + while time.monotonic() - timestamp < 0.25: + if dhtval != dhtpin.value: + dhtval = not dhtval # we toggled + transitions.append(time.monotonic()) # save the timestamp + # convert transtions to microsecond delta pulses: + # use last 81 pulses + transition_start = max(1, len(transitions) - self._max_pulses) + for i in range(transition_start, len(transitions)): + pulses_micro_sec = int(1000000 * (transitions[i] - transitions[i - 1])) + pulses.append(min(pulses_micro_sec, 65535)) + return pulses - pulses = self._get_pulses() - ##print(pulses) + def measure(self) -> None: + """measure runs the communications to the DHT11/22 type device. + if successful, the class properties temperature and humidity will + return the reading returned from the device. - if len(pulses) >= 80: - buf = array.array('B') - for byte_start in range(0, 80, 16): - buf.append(self._pulses_to_binary(pulses, byte_start, byte_start+16)) - #print(buf) + Raises RuntimeError exception for checksum failure and for insufficient + data returned from the device (try again) + """ + delay_between_readings = 2 # 2 seconds per read according to datasheet + # Initiate new reading if this is the first call or if sufficient delay + # If delay not sufficient - return previous reading. + # This allows back to back access for temperature and humidity for same reading + if ( + self._last_called == 0 + or (time.monotonic() - self._last_called) > delay_between_readings + ): + self._last_called = time.monotonic() - # humidity is 2 bytes - if self._dht11: - self._humidity = buf[0] - else: - self._humidity = ((buf[0]<<8) | buf[1]) / 10 - - # tempature is 2 bytes - if self._dht11: - self._temperature = buf[2] - else: - self._temperature = ((buf[2]<<8) | buf[3]) / 10 - - # calc checksum - chk_sum = 0 - for b in buf[0:4]: - chk_sum += b - - # checksum is the last byte - if chk_sum & 0xff != buf[4]: - # check sum failed to validate - raise RuntimeError("Checksum did not validate. Try again.") - #print("checksum did not match. Temp: {} Humidity: {} Checksum:{}".format(self._temperature,self._humidity,bites[4])) - - # checksum matches - #print("Temp: {} C Humidity: {}% ".format(self._temperature, self._humidity)) + new_temperature = 0 + new_humidity = 0 + if self._use_pulseio: + pulses = self._get_pulses_pulseio() + else: + pulses = self._get_pulses_bitbang() + # print(len(pulses), "pulses:", [x for x in pulses]) + + if len(pulses) < 10: + # Probably a connection issue! + raise RuntimeError("DHT sensor not found, check wiring") + + if len(pulses) < 80: + # We got *some* data just not 81 bits + raise RuntimeError("A full buffer was not returned. Try again.") + + buf = array.array("B") + for byte_start in range(0, 80, 16): + buf.append(self._pulses_to_binary(pulses, byte_start, byte_start + 16)) + + if self._dht11: + # humidity is 1 byte + new_humidity = buf[0] + # temperature is 1 byte for integral and 1 byte for 1st decimal place + new_temperature = buf[2] + (buf[3] & 0x0F) / 10 else: - raise RuntimeError("A full buffer was not returned. Try again.") - #print("did not get a full return. number returned was: {}".format(len(r))) + # humidity is 2 bytes + new_humidity = ((buf[0] << 8) | buf[1]) / 10 + # temperature is 2 bytes + # MSB is sign, bits 0-14 are magnitude) + new_temperature = (((buf[2] & 0x7F) << 8) | buf[3]) / 10 + # set sign + if buf[2] & 0x80: + new_temperature = -new_temperature + # calc checksum + chk_sum = 0 + for b in buf[0:4]: + chk_sum += b + + # checksum is the last byte + if chk_sum & 0xFF != buf[4]: + # check sum failed to validate + raise RuntimeError("Checksum did not validate. Try again.") + + if new_humidity < 0 or new_humidity > 100: + # We received unplausible data + raise RuntimeError("Received unplausible data. Try again.") + + self._temperature = new_temperature + self._humidity = new_humidity @property - def temperature(self): - """ temperature current reading. It makes sure a reading is available + def temperature(self) -> Union[int, float, None]: + """temperature current reading. It makes sure a reading is available - Raises RuntimeError exception for checksum failure and for insuffcient - data returned from the device (try again) + Raises RuntimeError exception for checksum failure and for insufficient + data returned from the device (try again) """ self.measure() return self._temperature @property - def humidity(self): - """ humidity current reading. It makes sure a reading is available + def humidity(self) -> Union[int, float, None]: + """humidity current reading. It makes sure a reading is available - Raises RuntimeError exception for checksum failure and for insuffcient - data returned from the device (try again) + Raises RuntimeError exception for checksum failure and for insufficient + data returned from the device (try again) """ self.measure() return self._humidity + class DHT11(DHTBase): - """ Support for DHT11 device. + """Support for DHT11 device. - :param ~board.Pin pin: digital pin used for communication + :param ~board.Pin pin: digital pin used for communication """ - def __init__(self, pin): - super().__init__(True, pin, 18000) + + def __init__(self, pin: Pin, use_pulseio: bool = _USE_PULSEIO): + super().__init__(True, pin, 18000, use_pulseio) class DHT22(DHTBase): - """ Support for DHT22 device. + """Support for DHT22 device. - :param ~board.Pin pin: digital pin used for communication + :param ~board.Pin pin: digital pin used for communication """ - def __init__(self, pin): - super().__init__(False, pin, 1000) + + def __init__(self, pin: Pin, use_pulseio: bool = _USE_PULSEIO): + super().__init__(False, pin, 1000, use_pulseio) + + +class DHT21(DHTBase): + """Support for DHT21/AM2301 device. + + :param ~board.Pin pin: digital pin used for communication + """ + + # DHT21/AM2301 is sending three more dummy bytes after the "official" protocol. + # Pulseio will take only the last pulses up to maxPulses. + # If that would be 81, the dummy pulses will be read and the real data would be truncated. + # Hence setting maxPulses to 129, taking both real data and dummy bytes into buffer. + def __init__(self, pin: Pin, use_pulseio: bool = _USE_PULSEIO): + super().__init__(False, pin, 1000, use_pulseio, max_pulses=129) diff --git a/api.rst b/api.rst deleted file mode 100644 index e69910e..0000000 --- a/api.rst +++ /dev/null @@ -1,6 +0,0 @@ - -DHT Libary Documentation -============================ - -.. automodule:: adafruit_dht - :members: diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 0000000..d60cf4b --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2025 Sam Blenny + * SPDX-License-Identifier: MIT + */ + +/* Monkey patch the rtd theme to prevent horizontal stacking of short items + * see https://github.com/readthedocs/sphinx_rtd_theme/issues/1301 + */ +.py.property{display: block !important;} diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 0000000..5aca983 Binary files /dev/null and b/docs/_static/favicon.ico differ diff --git a/docs/_static/favicon.ico.license b/docs/_static/favicon.ico.license new file mode 100644 index 0000000..86a3fbf --- /dev/null +++ b/docs/_static/favicon.ico.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2018 Phillip Torrone for Adafruit Industries + +SPDX-License-Identifier: CC-BY-4.0 diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..d234a8b --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,11 @@ + +.. If you created a package, create one automodule per module in the package. + +.. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py) +.. use this format as the module name: "adafruit_foo.foo" + +API Reference +############# + +.. automodule:: adafruit_dht + :members: diff --git a/docs/api.rst.license b/docs/api.rst.license new file mode 100644 index 0000000..9aae48d --- /dev/null +++ b/docs/api.rst.license @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: MIT diff --git a/conf.py b/docs/conf.py similarity index 50% rename from conf.py rename to docs/conf.py index 01567df..59c0c28 100644 --- a/conf.py +++ b/docs/conf.py @@ -1,8 +1,12 @@ -# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: MIT +import datetime import os import sys -sys.path.insert(0, os.path.abspath('.')) + +sys.path.insert(0, os.path.abspath("..")) # -- General configuration ------------------------------------------------ @@ -10,51 +14,65 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinxcontrib.jquery", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", ] -intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None), - 'BusDevice': ('https://circuitpython.readthedocs.io/projects/bus_device/en/latest/', None), - 'Register': ('https://circuitpython.readthedocs.io/projects/register/en/latest/', None), - 'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)} - autodoc_mock_imports = ["pulseio"] +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "BusDevice": ( + "https://docs.circuitpython.org/projects/busdevice/en/latest/", + None, + ), + "Register": ( + "https://docs.circuitpython.org/projects/register/en/latest/", + None, + ), + "CircuitPython": ("https://docs.circuitpython.org/en/latest/", None), +} + # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'README' +master_doc = "index" # General information about the project. -project = u'Adafruit CircuitPython DHT Library' -copyright = u'2017 Mike McWethy' -author = u'Mike McWethy' +project = "Adafruit CircuitPython DHT Library" +creation_year = "2017" +current_year = str(datetime.datetime.now().year) +year_duration = ( + current_year if current_year == creation_year else creation_year + " - " + current_year +) +copyright = year_duration + " Mike McWethy" +author = "Mike McWethy" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = u'1.0' +version = "1.0" # The full version, including alpha/beta/rc tags. -release = u'1.0' +release = "1.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".env", "CODE_OF_CONDUCT.md"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -66,64 +84,69 @@ add_function_parentheses = True # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False +# If this is True, todo emits a warning for each TODO entries. The default is False. +todo_emit_warnings = True + # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' - -if not on_rtd: # only import and set the theme if we're building docs locally - try: - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), '.'] - except: - html_theme = 'default' - html_theme_path = ['.'] -else: - html_theme_path = ['.'] +import sphinx_rtd_theme + +html_theme = "sphinx_rtd_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] + +# Include extra css to work around rtd theme glitches +html_css_files = ["custom.css"] + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# +html_favicon = "_static/favicon.ico" # Output file base name for HTML help builder. -htmlhelp_basename = 'AdafruitCircuitPythonDHTLibrarydoc' +htmlhelp_basename = "AdafruitCircuitPythonDHTLibrarydoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'AdafruitCircuitPythonDHTLibrary.tex', u'Adafruit CircuitPython DHT Library Documentation', - author, 'manual'), + ( + master_doc, + "AdafruitCircuitPythonDHTLibrary.tex", + "Adafruit CircuitPython DHT Library Documentation", + author, + "manual", + ), ] # -- Options for manual page output --------------------------------------- @@ -131,8 +154,13 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'adafruitCircuitPythonDHTlibrary', u'Adafruit CircuitPython DHT Library Documentation', - [author], 1) + ( + master_doc, + "adafruitCircuitPythonDHTlibrary", + "Adafruit CircuitPython DHT Library Documentation", + [author], + 1, + ) ] # -- Options for Texinfo output ------------------------------------------- @@ -141,7 +169,13 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'AdafruitCircuitPythonDHTLibrary', u'Adafruit CircuitPython DHT Library Documentation', - author, 'AdafruitCircuitPythonDHTLibrary', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "AdafruitCircuitPythonDHTLibrary", + "Adafruit CircuitPython DHT Library Documentation", + author, + "AdafruitCircuitPythonDHTLibrary", + "One line description of project.", + "Miscellaneous", + ), ] diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..9eaf319 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,29 @@ +Simple test +------------ + +Ensure your device works with this simple test. + +.. literalinclude:: ../examples/dht_simpletest.py + :caption: examples/dht_simpletest.py + :linenos: + + +DHT to Led Display +------------------ + +Example of reading temperature and humidity from a DHT device +and displaying results to the serial port and a 8 digit 7-segment display + +.. literalinclude:: ../examples/dht_to_led_display.py + :caption: examples/dht_to_led_display.py + :linenos: + + +Time calibration advance test +------------------------------ + +Example to identify best waiting time for the sensor + +.. literalinclude:: ../examples/dht_time_calibration_advance.py + :caption: examples/dht_time_calibration_advance.py + :linenos: diff --git a/docs/examples.rst.license b/docs/examples.rst.license new file mode 100644 index 0000000..9aae48d --- /dev/null +++ b/docs/examples.rst.license @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: MIT diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..d1f7459 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,52 @@ +.. include:: ../README.rst + +Table of Contents +================= + +.. toctree:: + :maxdepth: 4 + :hidden: + + self + +.. toctree:: + :caption: Examples + + examples + +.. toctree:: + :caption: API Reference + :maxdepth: 3 + + api + +.. toctree:: + :caption: Tutorials + + DHT basic temperature-humidity sensor Learning Guide + +.. toctree:: + :caption: Related Products + + DHT11 basic temperature-humidity sensor + extras + + DHT22 basic temperature-humidity sensor + extras + +.. toctree:: + :caption: Other Links + + Download from GitHub + Download Library Bundle + 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/docs/index.rst.license b/docs/index.rst.license new file mode 100644 index 0000000..9aae48d --- /dev/null +++ b/docs/index.rst.license @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: MIT diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..979f568 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +sphinx +sphinxcontrib-jquery +sphinx-rtd-theme diff --git a/examples/dht_simpletest.py b/examples/dht_simpletest.py new file mode 100644 index 0000000..bea4491 --- /dev/null +++ b/examples/dht_simpletest.py @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import time + +import board + +import adafruit_dht + +# Initial the dht device, with data pin connected to: +dhtDevice = adafruit_dht.DHT22(board.D18) + +# you can pass DHT22 use_pulseio=False if you wouldn't like to use pulseio. +# This may be necessary on a Linux single board computer like the Raspberry Pi, +# but it will not work in CircuitPython. +# dhtDevice = adafruit_dht.DHT22(board.D18, use_pulseio=False) + +while True: + try: + # Print the values to the serial port + temperature_c = dhtDevice.temperature + temperature_f = temperature_c * (9 / 5) + 32 + humidity = dhtDevice.humidity + print(f"Temp: {temperature_f:.1f} F / {temperature_c:.1f} C Humidity: {humidity}% ") + + except RuntimeError as error: + # Errors happen fairly often, DHT's are hard to read, just keep going + print(error.args[0]) + time.sleep(2.0) + continue + except Exception as error: + dhtDevice.exit() + raise error + + time.sleep(2.0) diff --git a/examples/dht_time_calibration_advance.py b/examples/dht_time_calibration_advance.py new file mode 100644 index 0000000..b67a95e --- /dev/null +++ b/examples/dht_time_calibration_advance.py @@ -0,0 +1,100 @@ +# SPDX-FileCopyrightText: 2021 yeyeto2788 for Adafruit Industries +# SPDX-License-Identifier: MIT + +""" +This script let's you check the best timing for you sensor as other people have face timing issues +as seen on issue https://github.com/adafruit/Adafruit_CircuitPython_DHT/issues/66. + +By changing the variables values below you will be able to check the best timing for you sensor, +take into account that by most datasheets the timing for the sensor are 0.001 DHT22 and +0.018 for DHT11 which are the default values of the library. +""" + +import json +import time + +import board + +import adafruit_dht + +# Change the pin used below +pin_to_use = "PG6" + +# Maximum number of tries per timing +max_retries_per_time = 10 +# Minimum wait time from where to start testing +min_time = 1500 +# Maximum wait time on where to stop testing +max_time = 2000 +# Increment on time +time_increment = 100 + +# Variable to store all reads on a try +reads = {} + +initial_msg = f""" +\nInitializing test with the following parameters: + +- Maximum retries per waiting time: {max_retries_per_time} +- Start time (ms): {min_time} +- End time (ms): {max_time} +- Increment time (ms): {time_increment} + +This execution will try to read the sensor {max_retries_per_time} times +for {len(range(min_time, max_time, time_increment))} different wait times values. + +""" +# Print initial message on the console. +print(initial_msg) + +for milliseconds in range(min_time, max_time, time_increment): + # Instantiate the DHT11 object. + dhtDevice = adafruit_dht.DHT11(pin=getattr(board, pin_to_use)) + # Change the default wait time for triggering the read. + dhtDevice._trig_wait = milliseconds + + print(f"Using 'trig_wait' of {dhtDevice._trig_wait}") + # Reset the read count for next loop + reads_count = 0 + + # Create the key on the reads dictionary with the milliseconds used on + # this try. + if milliseconds not in reads: + reads[milliseconds] = {"total_reads": 0} + + for try_number in range(0, max_retries_per_time): + try: + # Read temperature and humidity + temperature = dhtDevice.temperature + humidity = dhtDevice.humidity + read_values = {"temperature": temperature, "humidity": humidity} + + if try_number not in reads[milliseconds]: + reads[milliseconds][try_number] = read_values + + reads_count += 1 + except RuntimeError: + time.sleep(2) + else: + time.sleep(1) + + reads[milliseconds]["total_reads"] = reads_count + + print(f"Total read(s): {reads[milliseconds]['total_reads']}\n") + dhtDevice.exit() + +# Gather the highest read numbers from all reads done. +best_result = max(reads[milliseconds]["total_reads"] for milliseconds in reads) +# Gather best time(s) in milliseconds where we got more reads +best_times = [ + milliseconds for milliseconds in reads if reads[milliseconds]["total_reads"] == best_result +] +print( + f"Maximum reads: {best_result} out of {max_retries_per_time} with the " + f"following times: {', '.join([str(t) for t in best_times])}" +) + +# change the value on the line below to see all reads performed. +print_all = False +if print_all: + print(json.dumps(reads)) diff --git a/examples/dhttoleddisplay.py b/examples/dht_to_led_display.py similarity index 57% rename from examples/dhttoleddisplay.py rename to examples/dht_to_led_display.py index 9b8ff88..e9e0d7b 100644 --- a/examples/dhttoleddisplay.py +++ b/examples/dht_to_led_display.py @@ -1,43 +1,45 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + """ example of reading temperature and humidity from a DHT device and displaying results to the serial port and a 8 digit 7-segment display the DHT device data wire is connected to board.D2 """ -# import for dht devices + +# import for dht devices and 7-segment display devices import time -import adafruit_dht -from board import D2 -#imports for 7-segment display device -from adafruit_max7219 import bcddigits -from board import TX, RX, A2 import busio import digitalio +from adafruit_max7219 import bcddigits +from board import D1, D2, RX, TX + +import adafruit_dht clk = RX din = TX -cs = digitalio.DigitalInOut(A2) +cs = digitalio.DigitalInOut(D1) spi = busio.SPI(clk, MOSI=din) display = bcddigits.BCDDigits(spi, cs, nDigits=8) display.brightness(5) -#initial the dht device +# initial the dht device dhtDevice = adafruit_dht.DHT22(D2) while True: try: # show the values to the serial port - temperature = dhtDevice.temperature*9/5+32 + temperature = dhtDevice.temperature * (9 / 5) + 32 humidity = dhtDevice.humidity - #print("Temp: {:.1f} F Humidity: {}% ".format(temperature, humidity)) + # print("Temp: {:.1f} F Humidity: {}% ".format(temperature, humidity)) # now show the values on the 8 digit 7-segment display display.clear_all() - display.show_str(0,'{:5.1f}{:5.1f}'.format(temperature, humidity)) + display.show_str(0, f"{temperature:5.1f}{humidity:5.1f}") display.show() except RuntimeError as error: - print(error.args) + print(error.args[0]) time.sleep(2.0) - \ No newline at end of file diff --git a/optional_requirements.txt b/optional_requirements.txt new file mode 100644 index 0000000..d4e27c4 --- /dev/null +++ b/optional_requirements.txt @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0707c3d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: 2022 Alec Delaney for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +[build-system] +requires = [ + "setuptools", + "wheel", + "setuptools-scm", +] + +[project] +name = "adafruit-circuitpython-dht" +description = "CircuitPython support for DHT11 and DHT22 type temperature/humidity devices" +version = "0.0.0+auto.0" +readme = "README.rst" +authors = [ + {name = "Adafruit Industries", email = "circuitpython@adafruit.com"} +] +urls = {Homepage = "https://github.com/adafruit/Adafruit_CircuitPython_DHT"} +keywords = [ + "adafruit", + "dht", + "hardware", + "sensors", + "temperature", + "humidity", + "micropython", + "circuitpython", +] +license = {text = "MIT"} +classifiers = [ + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Embedded Systems", + "Topic :: System :: Hardware", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", +] +dynamic = ["dependencies", "optional-dependencies"] + +[tool.setuptools] +py-modules = ["adafruit_dht"] + +[tool.setuptools.dynamic] +dependencies = {file = ["requirements.txt"]} +optional-dependencies = {optional = {file = ["optional_requirements.txt"]}} diff --git a/readthedocs.yml b/readthedocs.yml deleted file mode 100644 index a3a16c1..0000000 --- a/readthedocs.yml +++ /dev/null @@ -1,2 +0,0 @@ -requirements_file: requirements.txt - diff --git a/requirements.txt b/requirements.txt index e69de29..7a984a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +Adafruit-Blinka diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..9811947 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,104 @@ +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +target-version = "py38" +line-length = 100 + +[lint] +preview = true +select = ["I", "PL", "UP"] + +extend-select = [ + "D419", # empty-docstring + "E501", # line-too-long + "W291", # trailing-whitespace + "PLC0414", # useless-import-alias + "PLC2401", # non-ascii-name + "PLC2801", # unnecessary-dunder-call + "PLC3002", # unnecessary-direct-lambda-call + "PLE0101", # return-in-init + "F706", # return-outside-function + "F704", # yield-outside-function + "PLE0116", # continue-in-finally + "PLE0117", # nonlocal-without-binding + "PLE0241", # duplicate-bases + "PLE0302", # unexpected-special-method-signature + "PLE0604", # invalid-all-object + "PLE0605", # invalid-all-format + "PLE0643", # potential-index-error + "PLE0704", # misplaced-bare-raise + "PLE1141", # dict-iter-missing-items + "PLE1142", # await-outside-async + "PLE1205", # logging-too-many-args + "PLE1206", # logging-too-few-args + "PLE1307", # bad-string-format-type + "PLE1310", # bad-str-strip-call + "PLE1507", # invalid-envvar-value + "PLE2502", # bidirectional-unicode + "PLE2510", # invalid-character-backspace + "PLE2512", # invalid-character-sub + "PLE2513", # invalid-character-esc + "PLE2514", # invalid-character-nul + "PLE2515", # invalid-character-zero-width-space + "PLR0124", # comparison-with-itself + "PLR0202", # no-classmethod-decorator + "PLR0203", # no-staticmethod-decorator + "UP004", # useless-object-inheritance + "PLR0206", # property-with-parameters + "PLR0904", # too-many-public-methods + "PLR0911", # too-many-return-statements + "PLR0912", # too-many-branches + "PLR0913", # too-many-arguments + "PLR0914", # too-many-locals + "PLR0915", # too-many-statements + "PLR0916", # too-many-boolean-expressions + "PLR1702", # too-many-nested-blocks + "PLR1704", # redefined-argument-from-local + "PLR1711", # useless-return + "C416", # unnecessary-comprehension + "PLR1733", # unnecessary-dict-index-lookup + "PLR1736", # unnecessary-list-index-lookup + + # ruff reports this rule is unstable + #"PLR6301", # no-self-use + + "PLW0108", # unnecessary-lambda + "PLW0120", # useless-else-on-loop + "PLW0127", # self-assigning-variable + "PLW0129", # assert-on-string-literal + "B033", # duplicate-value + "PLW0131", # named-expr-without-context + "PLW0245", # super-without-brackets + "PLW0406", # import-self + "PLW0602", # global-variable-not-assigned + "PLW0603", # global-statement + "PLW0604", # global-at-module-level + + # fails on the try: import typing used by libraries + #"F401", # unused-import + + "F841", # unused-variable + "E722", # bare-except + "PLW0711", # binary-op-exception + "PLW1501", # bad-open-mode + "PLW1508", # invalid-envvar-default + "PLW1509", # subprocess-popen-preexec-fn + "PLW2101", # useless-with-lock + "PLW3301", # nested-min-max +] + +ignore = [ + "PLR2004", # magic-value-comparison + "UP030", # format literals + "PLW1514", # unspecified-encoding + "PLR0913", # too-many-arguments + "PLR0915", # too-many-statements + "PLR0917", # too-many-positional-arguments + "PLR0904", # too-many-public-methods + "PLR0912", # too-many-branches + "PLR0916", # too-many-boolean-expressions +] + +[format] +line-ending = "lf"