From f914a13a6615db034d7c276ee9ed45f26046b2c2 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Wed, 29 Mar 2023 18:59:21 +0000 Subject: [PATCH] Initial structure --- .devcontainer.json | 36 ++++++++++ .gitattributes | 1 + .github/ISSUE_TEMPLATE/bug.yml | 55 +++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 1 + .github/ISSUE_TEMPLATE/feature_request.yml | 47 +++++++++++++ .github/dependabot.yml | 15 ++++ .github/workflows/lint.yml | 29 ++++++++ .github/workflows/release.yml | 40 +++++++++++ .github/workflows/validate.yml | 35 +++++++++ .gitignore | 30 ++++++++ .ruff.toml | 48 +++++++++++++ .vscode/launch.json | 34 +++++++++ .vscode/settings.json | 14 ++++ .vscode/tasks.json | 11 +++ CONTRIBUTING.md | 61 ++++++++++++++++ LICENSE | 21 ++++++ README.md | 74 ++++++++++++++++++++ configuration.yaml | 8 +++ custom_components/__init__.py | 1 + custom_components/chore_helper/manifest.json | 13 ++++ hacs.json | 8 +++ requirements.txt | 3 + requirements_test.txt | 2 + scripts/develop | 8 +++ scripts/lint | 7 ++ scripts/setup | 7 ++ setup.cfg | 63 +++++++++++++++++ tests/README.md | 17 +++++ tests/__init__.py | 1 + tests/conftest.py | 40 +++++++++++ tests/const.py | 5 ++ tests/fixtures/.gitkeep | 0 32 files changed, 735 insertions(+) create mode 100755 .devcontainer.json create mode 100755 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/validate.yml create mode 100755 .gitignore create mode 100644 .ruff.toml create mode 100755 .vscode/launch.json create mode 100755 .vscode/settings.json create mode 100755 .vscode/tasks.json create mode 100755 CONTRIBUTING.md create mode 100755 LICENSE create mode 100755 README.md create mode 100755 configuration.yaml create mode 100644 custom_components/__init__.py create mode 100644 custom_components/chore_helper/manifest.json create mode 100755 hacs.json create mode 100644 requirements.txt create mode 100644 requirements_test.txt create mode 100755 scripts/develop create mode 100755 scripts/lint create mode 100755 scripts/setup create mode 100755 setup.cfg create mode 100755 tests/README.md create mode 100755 tests/__init__.py create mode 100755 tests/conftest.py create mode 100755 tests/const.py create mode 100644 tests/fixtures/.gitkeep diff --git a/.devcontainer.json b/.devcontainer.json new file mode 100755 index 0000000..a3dc911 --- /dev/null +++ b/.devcontainer.json @@ -0,0 +1,36 @@ +{ + "name": "ludeeus/integration_blueprint", + "image": "mcr.microsoft.com/vscode/devcontainers/python:0-3.10-bullseye", + "postCreateCommand": "scripts/setup", + "forwardPorts": [ + 8123 + ], + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "github.vscode-pull-request-github", + "ryanluker.vscode-coverage-gutters", + "ms-python.vscode-pylance" + ], + "settings": { + "files.eol": "\n", + "editor.tabSize": 4, + "python.pythonPath": "/usr/bin/python3", + "python.analysis.autoSearchPaths": false, + "python.linting.pylintEnabled": true, + "python.linting.enabled": true, + "python.formatting.provider": "black", + "python.formatting.blackPath": "/usr/local/py-utils/bin/black", + "editor.formatOnPaste": false, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "files.trimTrailingWhitespace": true + } + } + }, + "remoteUser": "vscode", + "features": { + "rust": "latest" + } +} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100755 index 0000000..94f480d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..c51a320 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,55 @@ +--- +name: "Bug report" +description: "Report a bug with the integration" +labels: ["Bug"] +body: + - type: markdown + attributes: + value: Before you open a new issue, search through the existing issues to see if others have had the same problem. + - type: textarea + attributes: + label: "System Health details" + description: "Paste the data from the System Health card in Home Assistant (https://www.home-assistant.io//more-info/system-health#github-issues)" + validations: + required: true + - type: checkboxes + attributes: + label: Checklist + options: + - label: I have enabled debug logging for my installation. + required: true + - label: I have filled out the issue template to the best of my ability. + required: true + - label: This issue only contains 1 issue (if you have multiple issues, open one issue for each issue). + required: true + - label: This issue is not a duplicate issue of currently [previous issues](https://github.com/bmcclure/ha-chore-helper/issues?q=is%3Aissue+label%3A%22Bug%22+).. + required: true + - type: textarea + attributes: + label: "Describe the issue" + description: "A clear and concise description of what the issue is." + validations: + required: true + - type: textarea + attributes: + label: Reproduction steps + description: "Without steps to reproduce, it will be hard to fix, it is very important that you fill out this part, issues without it will be closed" + value: | + 1. + 2. + 3. + ... + validations: + required: true + - type: textarea + attributes: + label: "Debug logs" + description: "To enable debug logs check this https://www.home-assistant.io/integrations/logger/, this **needs** to include _everything_ from startup of Home Assistant to the point where you encounter the issue." + render: Text + validations: + required: true + + - type: textarea + attributes: + label: "Diagnostics dump" + description: "Drag the diagnostics dump file here. (see https://www.home-assistant.io/integrations/diagnostics/ for info)" diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..df4416f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,47 @@ +--- +name: "Feature request" +description: "Suggest an idea for this project" +labels: ["Feature+Request"] +body: + - type: markdown + attributes: + value: Before you open a new feature request, search through the existing feature requests to see if others have had the same idea. + - type: checkboxes + attributes: + label: Checklist + options: + - label: I have filled out the template to the best of my ability. + required: true + - label: This only contains 1 feature request (if you have multiple feature requests, open one feature request for each feature request). + required: true + - label: This issue is not a duplicate feature request of [previous feature requests](https://github.com/bmcclure/ha-chore-helper/issues?q=is%3Aissue+label%3A%22Feature+Request%22+). + required: true + + - type: textarea + attributes: + label: "Is your feature request related to a problem? Please describe." + description: "A clear and concise description of what the problem is." + placeholder: "I'm always frustrated when [...]" + validations: + required: true + + - type: textarea + attributes: + label: "Describe the solution you'd like" + description: "A clear and concise description of what you want to happen." + validations: + required: true + + - type: textarea + attributes: + label: "Describe alternatives you've considered" + description: "A clear and concise description of any alternative solutions or features you've considered." + validations: + required: true + + - type: textarea + attributes: + label: "Additional context" + description: "Add any other context or screenshots about the feature request here." + validations: + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..eee9634 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + ignore: + # Dependabot should not update Home Assistant as that should match the homeassistant key in hacs.json + - dependency-name: "homeassistant" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..232c5b3 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,29 @@ +name: "Lint" + +on: + push: + branches: + - "main" + pull_request: + branches: + - "main" + +jobs: + ruff: + name: "Ruff" + runs-on: "ubuntu-latest" + steps: + - name: "Checkout the repository" + uses: "actions/checkout@v3.5.0" + + - name: "Set up Python" + uses: actions/setup-python@v4.5.0 + with: + python-version: "3.10" + cache: "pip" + + - name: "Install requirements" + run: python3 -m pip install -r requirements.txt + + - name: "Run" + run: python3 -m ruff check . diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2deed1c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,40 @@ +name: "Release" + +on: + release: + types: + - "published" + +env: + RELEASE_VERSION: "" + +jobs: + release: + name: "Release" + runs-on: "ubuntu-latest" + steps: + - name: "Checkout the repository" + uses: "actions/checkout@v3.5.0" + + - name: "Get version number from release tag" + shell: "bash" + run: | + echo "RELEASE_VERSION=$(echo '${{ github.event.release.tag_name }}' | sed s/v//)" >> $GITHUB_ENV + + - name: "Adjust version number" + if: env.release_version != '' + shell: "bash" + run: | + yq -i -o json '.version="${{ env.RELEASE_VERSION }}"' \ + "${{ github.workspace }}/custom_components/chore_helper/manifest.json" + + - name: "ZIP the integration directory" + shell: "bash" + run: | + cd "${{ github.workspace }}/custom_components/chore_helper" + zip chore-helper.zip -r ./ + + - name: "Upload the ZIP file to the release" + uses: softprops/action-gh-release@v0.1.15 + with: + files: ${{ github.workspace }}/custom_components/chore_helper/chore-helper.zip diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..475a030 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,35 @@ +name: "Validate" + +on: + workflow_dispatch: + schedule: + - cron: "0 0 * * *" + push: + branches: + - "main" + pull_request: + branches: + - "main" + +jobs: + hassfest: # https://developers.home-assistant.io/blog/2020/04/16/hassfest + name: "Hassfest Validation" + runs-on: "ubuntu-latest" + steps: + - name: "Checkout the repository" + uses: "actions/checkout@v3.5.0" + + - name: "Run hassfest validation" + uses: "home-assistant/actions/hassfest@master" + + hacs: # https://github.com/hacs/action + name: "HACS Validation" + runs-on: "ubuntu-latest" + steps: + - name: "Checkout the repository" + uses: "actions/checkout@v3.5.0" + + - name: "Run HACS validation" + uses: "hacs/action@main" + with: + category: "integration" diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..796b3d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# artifacts +__pycache__ +.pytest* +*.egg-info +*/build/* +*/dist/* + + +# misc +.coverage +coverage.xml + + +# Home Assistant configuration +.cloud +.HA_VERSION +.storage +automations.yaml +blueprints +configuration.yaml +!/configuration.yaml +deps +home-assistant_v2* +home-assistant.log* +tts +scenes.yaml +scripts.yaml +secrets.yaml + +/venv \ No newline at end of file diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..7a8331a --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,48 @@ +# The contents of this file is based on https://github.com/home-assistant/core/blob/dev/pyproject.toml + +target-version = "py310" + +select = [ + "B007", # Loop control variable {name} not used within loop body + "B014", # Exception handler with duplicate exception + "C", # complexity + "D", # docstrings + "E", # pycodestyle + "F", # pyflakes/autoflake + "ICN001", # import concentions; {name} should be imported as {asname} + "PGH004", # Use specific rule codes when using noqa + "PLC0414", # Useless import alias. Import alias does not rename original package. + "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass + "SIM117", # Merge with-statements that use the same scope + "SIM118", # Use {key} in {dict} instead of {key} in {dict}.keys() + "SIM201", # Use {left} != {right} instead of not {left} == {right} + "SIM212", # Use {a} if {a} else {b} instead of {b} if not {a} else {a} + "SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'. + "SIM401", # Use get from dict with default instead of an if block + "T20", # flake8-print + "TRY004", # Prefer TypeError exception for invalid type + "RUF006", # Store a reference to the return value of asyncio.create_task + "UP", # pyupgrade + "W", # pycodestyle +] + +ignore = [ + "D202", # No blank lines allowed after function docstring + "D203", # 1 blank line required before class docstring + "D213", # Multi-line docstring summary should start at the second line + "D404", # First word of the docstring should not be This + "D406", # Section name should end with a newline + "D407", # Section name underlining + "D411", # Missing blank line before section + "E501", # line too long + "E731", # do not assign a lambda expression, use a def +] + +[flake8-pytest-style] +fixture-parentheses = false + +[pyupgrade] +keep-runtime-typing = true + +[mccabe] +max-complexity = 25 \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100755 index 0000000..27adb82 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,34 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + // Example of attaching to local debug server + "name": "Python: Attach Local", + "type": "python", + "request": "attach", + "port": 5678, + "host": "localhost", + "pathMappings": [ + { + "localRoot": "${workspaceFolder}", + "remoteRoot": "." + } + ] + }, + { + // Example of attaching to my production server + "name": "Python: Attach Remote", + "type": "python", + "request": "attach", + "port": 5678, + "host": "homeassistant.local", + "pathMappings": [ + { + "localRoot": "${workspaceFolder}", + "remoteRoot": "/usr/src/homeassistant" + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100755 index 0000000..8f91aa8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "python.linting.pylintEnabled": true, + "python.linting.enabled": true, + "python.defaultInterpreterPath": "/usr/local/bin/python", + "files.associations": { + "*.yaml": "home-assistant" + }, + "cSpell.words": [ + "HACS", + "hass", + "hassfest", + "homeassistant", + ], +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100755 index 0000000..3aa1c50 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,11 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Run Home Assistant on port 8123", + "type": "shell", + "command": "scripts/develop", + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100755 index 0000000..d85a589 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,61 @@ +# Contribution guidelines + +Contributing to this project should be as easy and transparent as possible, whether it's: + +- Reporting a bug +- Discussing the current state of the code +- Submitting a fix +- Proposing new features + +## Github is used for everything + +Github is used to host code, to track issues and feature requests, as well as accept pull requests. + +Pull requests are the best way to propose changes to the codebase. + +1. Fork the repo and create your branch from `master`. +2. If you've changed something, update the documentation. +3. Make sure your code lints (using black). +4. Test you contribution. +5. Issue that pull request! + +## Any contributions you make will be under the MIT Software License + +In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. + +## Report bugs using Github's [issues](../../issues) + +GitHub issues are used to track public bugs. +Report a bug by [opening a new issue](../../issues/new/choose); it's that easy! + +## Write bug reports with detail, background, and sample code + +**Great Bug Reports** tend to have: + +- A quick summary and/or background +- Steps to reproduce + - Be specific! + - Give sample code if you can. +- What you expected would happen +- What actually happens +- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) + +People *love* thorough bug reports. I'm not even kidding. + +## Use a Consistent Coding Style + +Use [black](https://github.com/ambv/black) to make sure the code follows the style. + +## Test your code modification + +This custom component is based on [integration_blueprint template](https://github.com/custom-components/integration_blueprint). + +It comes with development environment in a container, easy to launch +if you use Visual Studio Code. With this container you will have a stand alone +Home Assistant instance running and already configured with the included +[`.devcontainer/configuration.yaml`](./.devcontainer/configuration.yaml) +file. + +## License + +By contributing, you agree that your contributions will be licensed under its MIT License. diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..2891ccf --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Ben McClure @bmcclure + +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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100755 index 0000000..e1171bd --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# Chore Helper for Home Assistant + +[![GitHub Release][releases-shield]][releases] +[![GitHub Activity][commits-shield]][commits] +[![License][license-shield]](LICENSE) + +[![hacs][hacsbadge]][hacs] +![Project Maintenance][maintenance-shield] +[![BuyMeCoffee][buymecoffeebadge]][buymecoffee] + +[![Discord][discord-shield]][discord] +[![Community Forum][forum-shield]][forum] + +This component allows you to set up and manage all of your recurring household chores in a flexible way using Home Assistant helpers. + +Chore Helper is in its infancy and might not work well (or at all) for your use case. If you run into issues, let me know! + +**This component will set up the following platforms.** + +| Platform | Description | +| --------------- | ------------------------------------------ | +| `helper` | Create configuration for each chore | +| `sensor` | Statistics for each chore | +| `binary_sensor` | Binary statistics for each chore | +| `calendar` | A Chores calendar for easy tracking | + +This helper is very loosely inspired by the way that the Tody app for Android works, except it is entirely managed within Home Assistant and benefits from the power of calendars, automations, and sensors. + +## Installation + +### Option 1: HACS (Recommended) + +1. Add this repository to HACS. +2. Search for "Chore Helper" under "Integrations". +3. Install the integration. +4. Restart Home Assistant. + +### Option 2: Manual + +1. Using the tool of choice open the directory (folder) for your HA configuration (where you find `configuration.yaml`). +2. If you do not have a `custom_components` directory (folder) there, you need to create it. +3. In the `custom_components` directory (folder) create a new folder called `chore_helper`. +4. Download _all_ the files from the `custom_components/chore_helper/` directory (folder) in this repository. +5. Place the files you downloaded in the new directory (folder) you created. +6. Restart Home Assistant. + + +## Configuration + +All configuration is done in the UI. + +1. In the HA UI go to "Settings" -> "Devices & Services" -> "Helpers", click the "Create Helper" button, and search for Chore +2. Enter your chore details and submit to add the helper. + +## Contributions are welcome! + +If you want to contribute to this please read the [Contribution guidelines](CONTRIBUTING.md) + +*** + +[buymecoffee]: https://www.buymeacoffee.com/benmcclure +[buymecoffeebadge]: https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg?style=for-the-badge +[commits-shield]: https://img.shields.io/github/commit-activity/y/bmcclure/ha-chore-helper.svg?style=for-the-badge +[commits]: https://github.com/bmcclure/ha-chore-helper/commits/master +[hacs]: https://github.com/custom-components/hacs +[hacsbadge]: https://img.shields.io/badge/HACS-Custom-orange.svg?style=for-the-badge +[discord]: https://discord.gg/Qa5fW2R +[discord-shield]: https://img.shields.io/discord/330944238910963714.svg?style=for-the-badge +[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg?style=for-the-badge +[forum]: https://community.home-assistant.io/ +[license-shield]: https://img.shields.io/github/license/custom-components/blueprint.svg?style=for-the-badge +[maintenance-shield]: https://img.shields.io/badge/maintainer-Ben%20McClure%20%40bmcclure-blue.svg?style=for-the-badge +[releases-shield]: https://img.shields.io/github/release/bmcclure/ha-chore-helper.svg?style=for-the-badge +[releases]: https://github.com/bmcclure/ha-chore-helper/releases diff --git a/configuration.yaml b/configuration.yaml new file mode 100755 index 0000000..090aec7 --- /dev/null +++ b/configuration.yaml @@ -0,0 +1,8 @@ +# https://www.home-assistant.io/integrations/default_config/ +default_config: + +# https://www.home-assistant.io/integrations/logger/ +logger: + default: info + logs: + custom_components.chore_helper: debug diff --git a/custom_components/__init__.py b/custom_components/__init__.py new file mode 100644 index 0000000..9e5dc14 --- /dev/null +++ b/custom_components/__init__.py @@ -0,0 +1 @@ +"""Dummy init so that pytest works.""" diff --git a/custom_components/chore_helper/manifest.json b/custom_components/chore_helper/manifest.json new file mode 100644 index 0000000..0058f1b --- /dev/null +++ b/custom_components/chore_helper/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "chore_helper", + "name": "Chore Helper", + "codeowners": [ + "@bmcclure" + ], + "config_flow": true, + "dependencies": [], + "documentation": "https://github.com/bmcclure/ha-chore-helper", + "iot_class": "cloud_polling", + "issue_tracker": "https://github.com/bmcclure/ha-chore-helper/issues", + "version": "1.0.0" +} diff --git a/hacs.json b/hacs.json new file mode 100755 index 0000000..788cd5f --- /dev/null +++ b/hacs.json @@ -0,0 +1,8 @@ +{ + "name": "Chore Helper", + "filename": "chore-helper.zip", + "hacs": "1.6.0", + "homeassistant": "2023.3.0", + "render_readme": true, + "zip_release": true +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b1436ab --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +colorlog==6.7.0 +homeassistant>=2023.3.1 +ruff==0.0.259 diff --git a/requirements_test.txt b/requirements_test.txt new file mode 100644 index 0000000..4a0c02c --- /dev/null +++ b/requirements_test.txt @@ -0,0 +1,2 @@ +-r requirements.txt +pytest-homeassistant-custom-component==0.13.10 diff --git a/scripts/develop b/scripts/develop new file mode 100755 index 0000000..5247604 --- /dev/null +++ b/scripts/develop @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +# Start Home Assistant +hass -c . --debug \ No newline at end of file diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 0000000..752d23a --- /dev/null +++ b/scripts/lint @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +ruff check . --fix \ No newline at end of file diff --git a/scripts/setup b/scripts/setup new file mode 100755 index 0000000..abe537a --- /dev/null +++ b/scripts/setup @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +python3 -m pip install --requirement requirements.txt \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100755 index 0000000..fd3c722 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,63 @@ +[coverage:run] +source = + custom_components + +[coverage:report] +exclude_lines = + pragma: no cover + raise NotImplemented() + if __name__ == '__main__': + main() +; todo: restore threshold +;fail_under = 93 +show_missing = true + +[tool:pytest] +testpaths = tests +norecursedirs = + .git +addopts = + --strict-markers + --cov=custom_components +filterwarnings = + ignore::DeprecationWarning:asynctest.*: + +[flake8] +exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build,__pycache__ +doctests = True +# To work with Black +# https://github.com/ambv/black#line-length +max-line-length = 88 +# E501: Line too long +# W503: Line break occurred before a binary operator +# E203: Whitespace before ':' +# D202: No blank lines allowed after function docstring +# W504: Line break after binary operator +ignore = + E501, + W503, + E203, + D202, + W504 + +[isort] +# https://github.com/timothycrosley/isort +# https://github.com/timothycrosley/isort/wiki/isort-Settings +# splits long import on multiple lines indented by 4 spaces +multi_line_output = 3 +include_trailing_comma=True +force_grid_wrap=0 +use_parentheses=True +line_length=88 +indent = " " +sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER +known_first_party = custom_components,tests +forced_separate = tests + +[mypy] +ignore_errors = true +follow_imports = silent +ignore_missing_imports = true +warn_incomplete_stub = true +warn_redundant_casts = true +warn_unused_configs = true \ No newline at end of file diff --git a/tests/README.md b/tests/README.md new file mode 100755 index 0000000..72bb8ba --- /dev/null +++ b/tests/README.md @@ -0,0 +1,17 @@ +# Why? + +While tests aren't required to publish a custom component for Home Assistant, they will generally make development easier because good tests will expose when changes you want to make to the component logic will break expected functionality. Home Assistant uses [`pytest`](https://docs.pytest.org/en/latest/) for its tests, and the tests that have been included are modeled after tests that are written for core Home Assistant integrations. These tests pass with 100% coverage (unless something has changed ;) ) and have comments to help you understand the purpose of different parts of the test. + +# Getting Started + +To begin, it is recommended to create a virtual environment and install all necessary dependencies: +```bash +./bin/setup +``` + +# Useful commands + +| Command | Description | +| ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `pytest` | This will run all tests and tell you how many passed/failed. It also show you a [code coverage](https://en.wikipedia.org/wiki/Code_coverage) summary of component, including % of code that was executed and the line numbers of missed executions. | +| `pytest tests/test_init.py -k test_setup_unload_and_reload_entry` | Runs the `test_setup_unload_and_reload_entry` test function located in `tests/test_init.py` | diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100755 index 0000000..db4446c --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for Chore Helper integration.""" diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100755 index 0000000..49edd72 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,40 @@ +"""Global fixtures for Chore Helper integration.""" +# Fixtures allow you to replace functions with a Mock object. You can perform +# many options via the Mock to reflect a particular behavior from the original +# function that you want to see without going through the function's actual logic. +# Fixtures can either be passed into tests as parameters, or if autouse=True, they +# will automatically be used across all tests. +# +# Fixtures that are defined in conftest.py are available across all tests. You can also +# define fixtures within a particular test file to scope them locally. +# +# pytest_homeassistant_custom_component provides some fixtures that are provided by +# Home Assistant core. You can find those fixture definitions here: +# https://github.com/MatthewFlamm/pytest-homeassistant-custom-component/blob/master/pytest_homeassistant_custom_component/common.py +# +# See here for more info: https://docs.pytest.org/en/latest/fixture.html (note that +# pytest includes fixtures OOB which you can use as defined on this page) +from unittest.mock import patch + +import pytest + +pytest_plugins = "pytest_homeassistant_custom_component" + + +# This fixture enables loading custom integrations in all tests. +# Remove to enable selective use of this fixture +@pytest.fixture(autouse=True) +def auto_enable_custom_integrations(enable_custom_integrations): + """Enable custom integrations.""" + yield + + +# This fixture is used to prevent HomeAssistant from attempting to create and dismiss persistent +# notifications. These calls would fail without this fixture since the persistent_notification +# integration is never loaded during a test. +@pytest.fixture(name="skip_notifications", autouse=True) +def skip_notifications_fixture(): + """Skip notification calls.""" + ha_mod = "homeassistant.components.persistent_notification" + with patch(f"{ha_mod}.async_create"), patch(f"{ha_mod}.async_dismiss"): + yield diff --git a/tests/const.py b/tests/const.py new file mode 100755 index 0000000..c7ec19f --- /dev/null +++ b/tests/const.py @@ -0,0 +1,5 @@ +"""Constants for integration_blueprint tests.""" +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME + +# Mock config data to be used across multiple tests +MOCK_CONFIG = {CONF_USERNAME: "test_username", CONF_PASSWORD: "test_password"} diff --git a/tests/fixtures/.gitkeep b/tests/fixtures/.gitkeep new file mode 100644 index 0000000..e69de29