diff --git a/.devcontainer.json b/.devcontainer.json new file mode 100644 index 0000000..af3a9de --- /dev/null +++ b/.devcontainer.json @@ -0,0 +1,58 @@ +{ + "name": "cyberjunky/home-assistant-garmin_connect", + "image": "mcr.microsoft.com/vscode/devcontainers/python:0-3.11-bullseye", + "postCreateCommand": "scripts/setup", + "forwardPorts": [ + 8123 + ], + "portsAttributes": { + "8123": { + "label": "Home Assistant" + }, + "0-8122": { + "label": "Auto-Forwarded - Other", + "onAutoForward": "ignore" + }, + "8124-999999": { + "label": "Auto-Forwarded - Other", + "onAutoForward": "ignore" + } + }, + "customizations": { + "extensions": [ + "ms-python.python", + "github.vscode-pull-request-github", + "ryanluker.vscode-coverage-gutters", + "ms-python.vscode-pylance" + ], + "vscode": { + "settings": { + "python.defaultInterpreterPath": "/usr/local/bin/python", + "files.eol": "\n", + "editor.tabSize": 4, + "python.pythonPath": "/usr/local/python/bin/python", + "python.analysis.autoSearchPaths": false, + "python.linting.pylintArgs": [ + "--disable", + "import-error" + ], + "python.formatting.provider": "black", + "editor.formatOnPaste": false, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "files.trimTrailingWhitespace": true + }, + "extensions": [ + "github.vscode-pull-request-github", + "ms-python.python", + "ms-python.vscode-pylance", + "ms-vscode.makefile-tools", + "ryanluker.vscode-coverage-gutters" + ] + } + }, + "remoteUser": "vscode", + "features": { + "rust": "latest" + } +} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..94f480d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..04f2d40 --- /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" \ No newline at end of file diff --git a/.github/pre-commit-config.yaml b/.github/pre-commit-config.yaml new file mode 100644 index 0000000..ea3b73b --- /dev/null +++ b/.github/pre-commit-config.yaml @@ -0,0 +1,50 @@ +repos: + - repo: https://github.com/asottile/pyupgrade + rev: v2.34.0 + hooks: + - id: pyupgrade + stages: [manual] + args: + - "--py39-plus" + + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + name: isort (python) + + - repo: https://github.com/psf/black + rev: 23.1.0 + hooks: + - id: black + stages: [manual] + args: + - --safe + - --quiet + files: ^((custom_components|script|tests)/.+)?[^/]+\.py$ + + - repo: https://github.com/codespell-project/codespell + rev: v2.1.0 + hooks: + - id: codespell + stages: [manual] + args: + - --quiet-level=2 + - --ignore-words-list=hass,ba,fo + - --exclude-file=custom_components/hacs/utils/default.repositories + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-executables-have-shebangs + stages: [manual] + - id: check-json + stages: [manual] + - id: requirements-txt-fixer + stages: [manual] + - id: check-ast + stages: [manual] + - id: mixed-line-ending + stages: [manual] + args: + - --fix=lf \ No newline at end of file diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..05601ee --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,25 @@ +changelog: + categories: + - title: '💥 Breaking changes' + labels: + - 'Breaking Change' + + - title: '🛎️ Experimental' + labels: + - 'Experimental' + + - title: '✨ New features' + labels: + - 'pr: new-feature' + + - title: '⚡ Enhancements' + labels: + - 'pr: enhancement' + + - title: '♻️ Refactor' + labels: + - 'pr: refactor' + + - title: '🐛 Bug Fixes' + labels: + - 'pr: bugfix' \ No newline at end of file diff --git a/.gitignore b/.gitignore index b6e4761..9fe9da4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,15 @@ +# misc +.vscode +outputdata +settings.json + +# Translation files +custom_components/garmin_connect/translations +!custom_components/garmin_connect/translations/en.json + +# Home Assistant configuration +config + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..260b188 --- /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 diff --git a/custom_components/garmin_connect/__init__.py b/custom_components/garmin_connect/__init__.py index 860be75..26183e0 100644 --- a/custom_components/garmin_connect/__init__.py +++ b/custom_components/garmin_connect/__init__.py @@ -112,13 +112,13 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator): activity_types = {} sleep_data = {} sleep_score = None - + try: summary = await self.hass.async_add_executor_job( self._api.get_user_summary, date.today().isoformat() ) _LOGGER.debug(f"Summary data: {summary}") - + body = await self.hass.async_add_executor_job( self._api.get_body_composition, date.today().isoformat() ) diff --git a/custom_components/garmin_connect/alarm_util.py b/custom_components/garmin_connect/alarm_util.py index 07dff48..eaca3dd 100644 --- a/custom_components/garmin_connect/alarm_util.py +++ b/custom_components/garmin_connect/alarm_util.py @@ -19,8 +19,7 @@ DAY_TO_NUMBER = { def calculate_next_active_alarms(alarms): - """ - Calculate garmin next active alarms from settings. + """Calculate garmin next active alarms from settings. Alarms are sorted by time """ diff --git a/custom_components/garmin_connect/const.py b/custom_components/garmin_connect/const.py index 6e832d3..762b7b9 100644 --- a/custom_components/garmin_connect/const.py +++ b/custom_components/garmin_connect/const.py @@ -1,6 +1,5 @@ """Constants for the Garmin Connect integration.""" from datetime import timedelta -from enum import Enum from typing import NamedTuple from homeassistant.const import ( diff --git a/custom_components/garmin_connect/sensor.py b/custom_components/garmin_connect/sensor.py index cf4b59d..2d820ad 100644 --- a/custom_components/garmin_connect/sensor.py +++ b/custom_components/garmin_connect/sensor.py @@ -19,7 +19,7 @@ from homeassistant.const import ( ATTR_ENTITY_ID, CONF_ID, ) -from homeassistant.const import ATTR_ATTRIBUTION, CONF_ID +from homeassistant.const import CONF_ID from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_platform from homeassistant.helpers.entity import DeviceInfo diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..018f271 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +colorlog==6.8.0 +homeassistant==2024.1.0 +pip>=21.0,<23.4 +ruff==0.1.11 \ No newline at end of file diff --git a/requirements_lint.txt b/requirements_lint.txt new file mode 100644 index 0000000..7c8c47d --- /dev/null +++ b/requirements_lint.txt @@ -0,0 +1,3 @@ +-r requirements.txt +pre-commit==3.5.0 +vulture==2.10 \ No newline at end of file diff --git a/scripts/develop b/scripts/develop new file mode 100644 index 0000000..20366e8 --- /dev/null +++ b/scripts/develop @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +# Create config dir if not present +if [[ ! -d "${PWD}/config" ]]; then + mkdir -p "${PWD}/config" + hass --config "${PWD}/config" --script ensure_config +fi + +# Set the path to custom_components +## This let's us have the structure we want /custom_components/integration_blueprint +## while at the same time have Home Assistant configuration inside /config +## without resulting to symlinks. +export PYTHONPATH="${PYTHONPATH}:${PWD}/custom_components" + +# Start Home Assistant +hass --config "${PWD}/config" --debug \ No newline at end of file diff --git a/scripts/lint b/scripts/lint new file mode 100644 index 0000000..c51ced8 --- /dev/null +++ b/scripts/lint @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +python3 -m pip install --requirement requirements_lint.txt + +ruff check . --fix; +pre-commit install-hooks --config .github/pre-commit-config.yaml; +pre-commit run --hook-stage manual --all-files --config .github/pre-commit-config.yaml; + +vulture . --min-confidence 55 --ignore-names policy diff --git a/scripts/setup b/scripts/setup new file mode 100644 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