Compare commits

...

66 Commits

Author SHA1 Message Date
Ron Klinkien
5a5b42a560 Bumped version of garminconnect 2024-11-27 20:20:57 +01:00
Ron Klinkien
e2deaed42b Dev version, added garmin badges 2024-11-27 10:14:55 +01:00
Ron Klinkien
78b6641506 Updated sk.json 2024-11-27 10:14:17 +01:00
Ron Klinkien
29f0832e8b Updated LICENSE 2024-11-27 10:14:17 +01:00
Ron
07473ef701 Merge pull request #139 from kozerskil/badges
Added badges
2024-11-27 10:13:26 +01:00
Ron
b8156a6a7d Merge pull request #186 from cyberjunky/dependabot/pip/pylint-3.2.5
Bump pylint from 3.0.3 to 3.2.5
2024-11-27 09:41:25 +01:00
Ron
7546a40012 Merge branch 'main' into dependabot/pip/pylint-3.2.5 2024-11-27 09:40:48 +01:00
Ron
e6f7947e2c Merge pull request #187 from cyberjunky/dependabot/pip/ruff-0.5.0
Bump ruff from 0.3.5 to 0.5.0
2024-11-27 09:39:53 +01:00
Ron
dda115539f Merge branch 'main' into dependabot/pip/ruff-0.5.0 2024-11-27 09:39:41 +01:00
Ron
304cd1f227 Merge pull request #188 from cyberjunky/dependabot/pip/mypy-1.10.1
Bump mypy from 1.8.0 to 1.10.1
2024-11-27 09:38:59 +01:00
Ron
6dc64df4d9 Merge branch 'main' into dependabot/pip/mypy-1.10.1 2024-11-27 09:38:23 +01:00
Ron
c3af1cc392 Merge pull request #185 from cyberjunky/dependabot/pip/pip-gte-24.1.1-and-lt-24.2
Update pip requirement from <24.1,>=21.0 to >=24.1.1,<24.2
2024-11-27 09:37:25 +01:00
Ron
4fa9f446de Merge pull request #173 from cyberjunky/dependabot/pip/pre-commit-3.7.1
Bump pre-commit from 3.6.0 to 3.7.1
2024-11-27 09:37:11 +01:00
Ron
9431374a71 Delete .github/workflows/ci.yml 2024-11-27 09:20:29 +01:00
Ron Klinkien
684acf436c Bumped python-garminconnect version to temp fix Garmin's API change 2024-11-27 09:18:47 +01:00
dependabot[bot]
3b54e25db5 Bump mypy from 1.8.0 to 1.10.1
Bumps [mypy](https://github.com/python/mypy) from 1.8.0 to 1.10.1.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.8.0...v1.10.1)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 19:10:08 +00:00
dependabot[bot]
73eb0cea73 Bump ruff from 0.3.5 to 0.5.0
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.3.5 to 0.5.0.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.3.5...0.5.0)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 19:10:03 +00:00
dependabot[bot]
8099a99077 Bump pylint from 3.0.3 to 3.2.5
Bumps [pylint](https://github.com/pylint-dev/pylint) from 3.0.3 to 3.2.5.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v3.0.3...v3.2.5)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 19:09:53 +00:00
dependabot[bot]
8647e95e30 Update pip requirement from <24.1,>=21.0 to >=24.1.1,<24.2
Updates the requirements on [pip](https://github.com/pypa/pip) to permit the latest version.
- [Changelog](https://github.com/pypa/pip/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/pip/compare/21.0...24.1.1)

---
updated-dependencies:
- dependency-name: pip
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 19:09:49 +00:00
dependabot[bot]
270327d7d8 Bump pre-commit from 3.6.0 to 3.7.1
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.6.0 to 3.7.1.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.6.0...v3.7.1)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-13 19:18:19 +00:00
Ron Klinkien
d42edcabc6 Fixed hacs validation 2024-04-06 12:29:33 +00:00
Ron Klinkien
b827597dab Revert unique id for now, added dev files 2024-04-06 14:13:10 +02:00
Ron
cb16c0198c Merge pull request #126 from cyberjunky/dependabot/pip/vulture-2.11
Bump vulture from 2.10 to 2.11
2024-04-06 10:54:49 +02:00
Ron
d8478aea37 Merge branch 'main' into dependabot/pip/vulture-2.11 2024-04-06 10:54:39 +02:00
Ron
3392a1458a Merge pull request #128 from cyberjunky/dependabot/pip/colorlog-6.8.2
Bump colorlog from 6.8.0 to 6.8.2
2024-04-06 10:54:00 +02:00
Ron
09e10f0da2 Merge pull request #130 from cyberjunky/dependabot/pip/pip-gte-21.0-and-lt-24.1
Update pip requirement from <23.4,>=21.0 to >=21.0,<24.1
2024-04-06 10:53:49 +02:00
Ron
265c0fbb21 Merge branch 'main' into dependabot/pip/pip-gte-21.0-and-lt-24.1 2024-04-06 10:53:41 +02:00
Ron
9c9cf6609c Merge pull request #155 from AnotherGroupChat/main
Bump garminconnect version
2024-04-06 10:52:33 +02:00
Ron
91c35eb486 Merge pull request #152 from cyberjunky/dependabot/pip/pre-commit-3.7.0
Bump pre-commit from 3.6.0 to 3.7.0
2024-04-06 10:51:09 +02:00
Ron
5334378493 Merge pull request #156 from cyberjunky/dependabot/pip/ruff-0.3.5
Bump ruff from 0.1.13 to 0.3.5
2024-04-06 10:50:57 +02:00
dependabot[bot]
e99ec5c770 Bump ruff from 0.1.13 to 0.3.5
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.13 to 0.3.5.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.13...v0.3.5)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-01 19:21:08 +00:00
dylan madisetti
38fd1fb631 Bump connect version 2024-03-31 13:05:56 -04:00
dependabot[bot]
9b8f536601 Bump pre-commit from 3.6.0 to 3.7.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.6.0...v3.7.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 19:19:34 +00:00
luk
261f41940f Add badges 2024-02-13 13:47:51 +01:00
dependabot[bot]
27551067ba Update pip requirement from <23.4,>=21.0 to >=21.0,<24.1
Updates the requirements on [pip](https://github.com/pypa/pip) to permit the latest version.
- [Changelog](https://github.com/pypa/pip/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/pip/compare/21.0...24.0)

---
updated-dependencies:
- dependency-name: pip
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 19:50:05 +00:00
dependabot[bot]
d5dddd5f73 Bump colorlog from 6.8.0 to 6.8.2
Bumps [colorlog](https://github.com/borntyping/python-colorlog) from 6.8.0 to 6.8.2.
- [Release notes](https://github.com/borntyping/python-colorlog/releases)
- [Commits](https://github.com/borntyping/python-colorlog/compare/v6.8.0...v6.8.2)

---
updated-dependencies:
- dependency-name: colorlog
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-29 19:37:29 +00:00
dependabot[bot]
4e033b91a5 Bump vulture from 2.10 to 2.11
Bumps [vulture](https://github.com/jendrikseipp/vulture) from 2.10 to 2.11.
- [Release notes](https://github.com/jendrikseipp/vulture/releases)
- [Changelog](https://github.com/jendrikseipp/vulture/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jendrikseipp/vulture/compare/v2.10...v2.11)

---
updated-dependencies:
- dependency-name: vulture
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 19:28:07 +00:00
Ron
a12306d79d Merge pull request #120 from ronlut/main
Add user- and integration name to sensor names and ids
2024-01-16 13:32:04 +01:00
Ron
6abe6ace3c Merge branch 'main' into main 2024-01-16 13:26:02 +01:00
Ron
5f31a7492d Merge pull request #121 from cyberjunky/dependabot/github_actions/actions/checkout-4
Bump actions/checkout from 3 to 4
2024-01-16 13:20:50 +01:00
Ron
b8abef2954 Merge pull request #122 from cyberjunky/dependabot/pip/ruff-0.1.13
Bump ruff from 0.1.11 to 0.1.13
2024-01-16 13:20:42 +01:00
Ron
a3096b4c80 Merge pull request #123 from cyberjunky/dependabot/pip/pre-commit-3.6.0
Bump pre-commit from 3.5.0 to 3.6.0
2024-01-16 13:20:33 +01:00
dependabot[bot]
23e7ab94c0 Bump pre-commit from 3.5.0 to 3.6.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.5.0 to 3.6.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.5.0...v3.6.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-16 12:18:50 +00:00
dependabot[bot]
7fb469121c Bump ruff from 0.1.11 to 0.1.13
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.11 to 0.1.13.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.11...v0.1.13)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-16 12:18:44 +00:00
dependabot[bot]
d5c88528b1 Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-16 12:18:26 +00:00
cyberjunky
7ab5141a6c Added development enviroment 2024-01-16 12:17:17 +00:00
Rony Lutsky
7ad8d099e1 unique_id with domain 2024-01-11 00:02:42 +02:00
Rony Lutsky
9198f469c2 use username for name and entity_id 2024-01-11 00:01:39 +02:00
Ron
7b227663dc Update README.md 2024-01-08 17:42:53 +01:00
Ron
afc9baa4a2 Update services.yaml 2024-01-03 12:01:19 +01:00
Ron
f15658548d Update services.yaml 2024-01-02 19:07:29 +01:00
Ron
e64573c6aa Update manifest.json 2024-01-02 18:51:21 +01:00
Ron
71f24148a2 Update sk.json 2024-01-02 18:47:22 +01:00
Ron
891230ce7a Update sk.json 2024-01-02 18:47:12 +01:00
Ron
c72e7a1879 Update README.md 2024-01-02 18:28:02 +01:00
Ron
65720791c2 Create hassfest.yaml 2024-01-02 13:57:02 +01:00
Ron
1029e21e23 Merge pull request #116 from alexives/add_body_composition_service
Add service for body composition
2024-01-02 12:33:56 +01:00
Ron
ac923b7517 Update services.yaml 2024-01-02 12:31:50 +01:00
Ron
cf5d39fb56 Update sensor.py 2024-01-02 12:31:16 +01:00
Ron
77dbbc9f47 Update manifest.json 2024-01-02 12:30:43 +01:00
Ron
2326be7455 Update const.py 2024-01-02 12:30:21 +01:00
Ron
bfb720ac5d Update __init__.py 2024-01-02 12:29:46 +01:00
Alex Ives
d20c9bedb2 Add service for body composition
Relates to https://github.com/cyberjunky/home-assistant-garmin_connect/issues/74
2023-12-30 15:42:19 -06:00
Ron
5e6f7ff6e1 Create FUNDING.yml 2023-12-26 20:26:50 +01:00
Ron
9d90c366d9 Merge pull request #113 from misa1515/patch-3
Update sk.json
2023-12-23 17:54:49 +01:00
misa1515
dc345c4d53 Update sk.json 2023-11-21 13:55:51 +01:00
32 changed files with 846 additions and 68 deletions

58
.devcontainer.json Normal file
View File

@@ -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"
}
}

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* text=auto eol=lf

13
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [cyberjunky] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

15
.github/dependabot.yml vendored Normal file
View File

@@ -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"

50
.github/pre-commit-config.yaml vendored Normal file
View File

@@ -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

25
.github/release.yml vendored Normal file
View File

@@ -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'

14
.github/workflows/hassfest.yaml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: Validate with hassfest
on:
push:
pull_request:
schedule:
- cron: "0 0 * * *"
jobs:
validate:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v4"
- uses: home-assistant/actions/hassfest@master

12
.gitignore vendored
View File

@@ -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]

89
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,89 @@
ci:
skip:
- mypy
- pylint
default_language_version:
python: python3.11
repos:
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.0
hooks:
- id: pyupgrade
args: [--py310-plus]
- repo: https://github.com/psf/black
rev: 23.12.1
hooks:
- id: black
args:
- --safe
- --quiet
<<: &python-files-with-tests
files: ^((custom_components|tests)/.+)?[^/]+\.py$
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
hooks:
- id: flake8
additional_dependencies:
- flake8-docstrings==1.6.0
- pydocstyle==6.1.1
<<: &python-files
files: ^(custom_components/.+)?[^/]+\.py$
- repo: https://github.com/PyCQA/bandit
rev: 1.7.6
hooks:
- id: bandit
args:
- --quiet
- --format=custom
- --configfile=bandit.yaml
<<: *python-files-with-tests
- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: detect-private-key
- id: no-commit-to-branch
- id: requirements-txt-fixer
- id: mixed-line-ending
args:
- --fix=lf
stages: [manual]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
additional_dependencies:
- prettier@2.7.1
- prettier-plugin-sort-json@0.0.3
exclude_types:
- python
exclude: manifest\.json$
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.33.0
hooks:
- id: yamllint
- repo: local
hooks:
# Run mypy through our wrapper script in order to get the possible
# pyenv and/or virtualenv activated; it may not have been e.g. if
# committing from a GUI tool that was not launched from an activated
# shell.
- id: mypy
name: Check with mypy
entry: scripts/run-in-env.sh mypy
language: script
types: [python]
<<: *python-files
- id: pylint
name: Check with pylint
entry: scripts/run-in-env.sh pylint
language: script
types: [python]
<<: *python-files

3
.prettierrc Normal file
View File

@@ -0,0 +1,3 @@
{
"jsonRecursiveSort": true
}

48
.ruff.toml Normal file
View File

@@ -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

59
.yamlllint Normal file
View File

@@ -0,0 +1,59 @@
rules:
braces:
level: error
min-spaces-inside: 0
max-spaces-inside: 1
min-spaces-inside-empty: -1
max-spaces-inside-empty: -1
brackets:
level: error
min-spaces-inside: 0
max-spaces-inside: 0
min-spaces-inside-empty: -1
max-spaces-inside-empty: -1
colons:
level: error
max-spaces-before: 0
max-spaces-after: 1
commas:
level: error
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
comments:
level: error
require-starting-space: true
min-spaces-from-content: 2
comments-indentation:
level: error
document-end:
level: error
present: false
document-start:
level: error
present: false
empty-lines:
level: error
max: 1
max-start: 0
max-end: 1
hyphens:
level: error
max-spaces-after: 1
indentation:
level: error
spaces: 2
indent-sequences: true
check-multi-line-strings: false
key-duplicates:
level: error
line-length: disable
new-line-at-end-of-file:
level: error
new-lines:
level: error
type: unix
trailing-spaces:
level: error
truthy:
disable

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2023 Ron Klinkien
Copyright (c) 2021-2024 Ron Klinkien
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -135,6 +135,39 @@ Metabolic Age
![screenshot](https://github.com/cyberjunky/home-assistant-garmin_connect/blob/main/screenshots/garmin_connect.png?raw=true "Screenshot Garmin Connect")
## Tips and Tricks
### Set up an automation using the garmin_connect.add_body_composition service
Useful if you want to pass your weight from another (incompatible) device to Garmin Connect. Garmin Connect does not calculate your BMI when you enter your weight manually so it needs to be passed along for now.
```
alias: uiSendWeightToGarminConnect
description: ""
trigger:
- platform: state
entity_id:
- sensor.my_weight
condition:
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.my_weight
above: 75
- condition: numeric_state
entity_id: sensor.my_weight
below: 88
action:
- service: garmin_connect.add_body_composition
data:
entity_id: sensor.garmin_connect_weight
weight: "{{trigger.to_state.state}}"
timestamp: "{{ as_timestamp(now()) | timestamp_local}}"
bmi: >-
{{ (trigger.to_state.state | float(0) / 1.86**2 )| round(1, default=0)
}}
mode: single
```
## Debugging
Add the relevant lines below to the `configuration.yaml`:

20
bandit.yaml Normal file
View File

@@ -0,0 +1,20 @@
# https://bandit.readthedocs.io/en/latest/config.html
tests:
- B103
- B108
- B306
- B307
- B313
- B314
- B315
- B316
- B317
- B318
- B319
- B320
- B601
- B602
- B604
- B608
- B609

View File

@@ -101,41 +101,52 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
async def _async_update_data(self) -> dict:
"""Fetch data from Garmin Connect."""
summary = {}
body = {}
activites = {}
alarms = {}
gear = {}
gear_stats = {}
gear_defaults = {}
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(summary)
_LOGGER.debug(f"Summary data: {summary}")
body = await self.hass.async_add_executor_job(
self._api.get_body_composition, date.today().isoformat()
)
_LOGGER.debug(f"Body data: {body}")
activities = await self.hass.async_add_executor_job(
self._api.get_activities_by_date, (date.today()-timedelta(days=7)).isoformat(), (date.today()+timedelta(days=1)).isoformat()
)
_LOGGER.debug(f"Activities data: {activities}")
summary['lastActivities'] = activities
_LOGGER.debug(body)
alarms = await self.hass.async_add_executor_job(self._api.get_device_alarms)
_LOGGER.debug(alarms)
gear = await self.hass.async_add_executor_job(
self._api.get_gear, summary[GEAR.USERPROFILE_ID]
badges = await self.hass.async_add_executor_job(
self._api.get_earned_badges
)
tasks: list[Awaitable] = [
self.hass.async_add_executor_job(
self._api.get_gear_stats, gear_item[GEAR.UUID]
)
for gear_item in gear
]
gear_stats = await asyncio.gather(*tasks)
_LOGGER.debug(f"Badges data: {badges}")
summary['badges'] = badges
alarms = await self.hass.async_add_executor_job(self._api.get_device_alarms)
_LOGGER.debug(f"Alarms data: {alarms}")
activity_types = await self.hass.async_add_executor_job(
self._api.get_activity_types
)
gear_defaults = await self.hass.async_add_executor_job(
self._api.get_gear_defaults, summary[GEAR.USERPROFILE_ID]
)
_LOGGER.debug(f"Activity types data: {activity_types}")
sleep_data = await self.hass.async_add_executor_job(
self._api.get_sleep_data, date.today().isoformat())
_LOGGER.debug(f"Sleep data: {sleep_data}")
except (
GarminConnectAuthenticationError,
GarminConnectTooManyRequestsError,
@@ -146,12 +157,33 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
raise UpdateFailed(error) from error
return {}
sleep_score = None
try:
gear = await self.hass.async_add_executor_job(
self._api.get_gear, summary[GEAR.USERPROFILE_ID]
)
_LOGGER.debug(f"Gear data: {gear}")
tasks: list[Awaitable] = [
self.hass.async_add_executor_job(
self._api.get_gear_stats, gear_item[GEAR.UUID]
)
for gear_item in gear
]
gear_stats = await asyncio.gather(*tasks)
_LOGGER.debug(f"Gear stats data: {gear_stats}")
gear_defaults = await self.hass.async_add_executor_job(
self._api.get_gear_defaults, summary[GEAR.USERPROFILE_ID]
)
_LOGGER.debug(f"Gear defaults data: {gear_defaults}")
except:
_LOGGER.debug("Gear data is not available")
try:
sleep_score = sleep_data["dailySleepDTO"]["sleepScores"]["overall"]["value"]
_LOGGER.debug(f"Sleep score data: {sleep_score}")
except KeyError:
_LOGGER.debug("sleepScore was absent")
summary['lastActivities'] = activities
_LOGGER.debug("Sleep score data is not available")
return {
**summary,
@@ -207,3 +239,27 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
await self.hass.async_add_executor_job(
self._api.set_gear_default, activity_type_id, entity.uuid, True
)
async def add_body_composition(self, entity, service_data):
"""Record a weigh in/body composition"""
if not await self.async_login():
raise IntegrationError(
"Failed to login to Garmin Connect, unable to update"
)
await self.hass.async_add_executor_job(
self._api.add_body_composition,
service_data.data.get("timestamp", None),
service_data.data.get("weight"),
service_data.data.get("percent_fat", None),
service_data.data.get("percent_hydration", None),
service_data.data.get("visceral_fat_mass", None),
service_data.data.get("bone_mass", None),
service_data.data.get("muscle_mass", None),
service_data.data.get("basal_met", None),
service_data.data.get("active_met", None),
service_data.data.get("physique_rating", None),
service_data.data.get("metabolic_age", None),
service_data.data.get("visceral_fat_rating", None),
service_data.data.get("bmi", None)
)

View File

@@ -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
"""

View File

@@ -1,14 +1,12 @@
"""Constants for the Garmin Connect integration."""
from datetime import timedelta
from enum import Enum
from typing import NamedTuple
from homeassistant.const import (
LENGTH_METERS,
MASS_KILOGRAMS,
UnitOfMass,
UnitOfTime,
UnitOfLength,
PERCENTAGE,
TIME_MINUTES,
TIME_YEARS,
)
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -46,7 +44,7 @@ GARMIN_ENTITY_LIST = {
"netCalorieGoal": ["Net Calorie Goal", "kcal", "mdi:food", None, SensorStateClass.TOTAL, False],
"totalDistanceMeters": [
"Total Distance Mtr",
LENGTH_METERS,
UnitOfLength.METERS,
"mdi:walk",
SensorDeviceClass.DISTANCE,
SensorStateClass.TOTAL,
@@ -71,7 +69,7 @@ GARMIN_ENTITY_LIST = {
"wellnessDescription": ["Wellness Description", "", "mdi:clock", None, SensorStateClass.TOTAL, False],
"wellnessDistanceMeters": [
"Wellness Distance Mtr",
LENGTH_METERS,
UnitOfLength.METERS,
"mdi:walk",
SensorDeviceClass.DISTANCE,
SensorStateClass.TOTAL,
@@ -88,18 +86,18 @@ GARMIN_ENTITY_LIST = {
"wellnessKilocalories": ["Wellness KiloCalories", "kcal", "mdi:food", None, SensorStateClass.TOTAL, False],
"highlyActiveSeconds": [
"Highly Active Time",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:fire",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
False,
],
"activeSeconds": ["Active Time", TIME_MINUTES, "mdi:fire", None, SensorStateClass.TOTAL, True],
"sedentarySeconds": ["Sedentary Time", TIME_MINUTES, "mdi:seat", None, SensorStateClass.TOTAL, True],
"sleepingSeconds": ["Sleeping Time", TIME_MINUTES, "mdi:sleep", None, SensorStateClass.TOTAL, True],
"activeSeconds": ["Active Time", UnitOfTime.MINUTES, "mdi:fire", None, SensorStateClass.TOTAL, True],
"sedentarySeconds": ["Sedentary Time", UnitOfTime.MINUTES, "mdi:seat", None, SensorStateClass.TOTAL, True],
"sleepingSeconds": ["Sleeping Time", UnitOfTime.MINUTES, "mdi:sleep", None, SensorStateClass.TOTAL, True],
"measurableAwakeDuration": [
"Awake Duration",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:sleep",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
@@ -107,7 +105,7 @@ GARMIN_ENTITY_LIST = {
],
"measurableAsleepDuration": [
"Sleep Duration",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:sleep",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
@@ -115,7 +113,7 @@ GARMIN_ENTITY_LIST = {
],
"floorsAscendedInMeters": [
"Floors Ascended Mtr",
LENGTH_METERS,
UnitOfLength.METERS,
"mdi:stairs",
SensorDeviceClass.DISTANCE,
SensorStateClass.TOTAL,
@@ -123,7 +121,7 @@ GARMIN_ENTITY_LIST = {
],
"floorsDescendedInMeters": [
"Floors Descended Mtr",
LENGTH_METERS,
UnitOfLength.METERS,
"mdi:stairs",
SensorDeviceClass.DISTANCE,
SensorStateClass.TOTAL,
@@ -163,10 +161,10 @@ GARMIN_ENTITY_LIST = {
"averageStressLevel": ["Avg Stress Level", "lvl", "mdi:flash-alert", None, SensorStateClass.TOTAL, True],
"maxStressLevel": ["Max Stress Level", "lvl", "mdi:flash-alert", None, SensorStateClass.TOTAL, True],
"stressQualifier": ["Stress Qualifier", None, "mdi:flash-alert", None, None, False],
"stressDuration": ["Stress Duration", TIME_MINUTES, "mdi:flash-alert", None, SensorStateClass.TOTAL, False],
"stressDuration": ["Stress Duration", UnitOfTime.MINUTES, "mdi:flash-alert", None, SensorStateClass.TOTAL, False],
"restStressDuration": [
"Rest Stress Duration",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:flash-alert",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
@@ -174,7 +172,7 @@ GARMIN_ENTITY_LIST = {
],
"activityStressDuration": [
"Activity Stress Duration",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:flash-alert",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
@@ -182,7 +180,7 @@ GARMIN_ENTITY_LIST = {
],
"uncategorizedStressDuration": [
"Uncat. Stress Duration",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:flash-alert",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
@@ -190,7 +188,7 @@ GARMIN_ENTITY_LIST = {
],
"totalStressDuration": [
"Total Stress Duration",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:flash-alert",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
@@ -198,7 +196,7 @@ GARMIN_ENTITY_LIST = {
],
"lowStressDuration": [
"Low Stress Duration",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:flash-alert",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
@@ -206,7 +204,7 @@ GARMIN_ENTITY_LIST = {
],
"mediumStressDuration": [
"Medium Stress Duration",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:flash-alert",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
@@ -214,7 +212,7 @@ GARMIN_ENTITY_LIST = {
],
"highStressDuration": [
"High Stress Duration",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:flash-alert",
None,
SensorStateClass.TOTAL,
@@ -278,7 +276,7 @@ GARMIN_ENTITY_LIST = {
],
"moderateIntensityMinutes": [
"Moderate Intensity",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:flash-alert",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
@@ -286,7 +284,7 @@ GARMIN_ENTITY_LIST = {
],
"vigorousIntensityMinutes": [
"Vigorous Intensity",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:run-fast",
SensorDeviceClass.DURATION,
SensorStateClass.TOTAL,
@@ -294,7 +292,7 @@ GARMIN_ENTITY_LIST = {
],
"intensityMinutesGoal": [
"Intensity Goal",
TIME_MINUTES,
UnitOfTime.MINUTES,
"mdi:run-fast",
None,
SensorStateClass.TOTAL,
@@ -391,17 +389,18 @@ GARMIN_ENTITY_LIST = {
None,
False,
],
"weight": ["Weight", MASS_KILOGRAMS, "mdi:weight-kilogram", SensorDeviceClass.WEIGHT, SensorStateClass.MEASUREMENT, False],
"weight": ["Weight", UnitOfMass.KILOGRAMS, "mdi:weight-kilogram", SensorDeviceClass.WEIGHT, SensorStateClass.MEASUREMENT, False],
"bmi": ["BMI", "bmi", "mdi:food", None, SensorStateClass.TOTAL, False],
"bodyFat": ["Body Fat", PERCENTAGE, "mdi:food", None, SensorStateClass.TOTAL, False],
"bodyWater": ["Body Water", PERCENTAGE, "mdi:water-percent", None, SensorStateClass.TOTAL, False],
"boneMass": ["Bone Mass", MASS_KILOGRAMS, "mdi:bone", SensorDeviceClass.WEIGHT, SensorStateClass.MEASUREMENT, False],
"muscleMass": ["Muscle Mass", MASS_KILOGRAMS, "mdi:dumbbell", SensorDeviceClass.WEIGHT, SensorStateClass.MEASUREMENT, False],
"boneMass": ["Bone Mass", UnitOfMass.KILOGRAMS, "mdi:bone", SensorDeviceClass.WEIGHT, SensorStateClass.MEASUREMENT, False],
"muscleMass": ["Muscle Mass", UnitOfMass.KILOGRAMS, "mdi:dumbbell", SensorDeviceClass.WEIGHT, SensorStateClass.MEASUREMENT, False],
"physiqueRating": ["Physique Rating", None, "mdi:numeric", None, SensorStateClass.TOTAL, False],
"visceralFat": ["Visceral Fat", PERCENTAGE, "mdi:food", None, SensorStateClass.TOTAL, False],
"metabolicAge": ["Metabolic Age", TIME_YEARS, "mdi:calendar-heart", None, SensorStateClass.TOTAL, False],
"metabolicAge": ["Metabolic Age", UnitOfTime.YEARS, "mdi:calendar-heart", None, SensorStateClass.TOTAL, False],
"nextAlarm": ["Next Alarm Time", None, "mdi:alarm", SensorDeviceClass.TIMESTAMP, None, True],
"lastActivities": ["Last Activities", None, "mdi:numeric", SensorStateClass.TOTAL, None, False],
"badges": ["Badges", None, "mdi:numeric", SensorStateClass.TOTAL, None, False],
"sleepScore": [
"Sleep Score",
None,

View File

@@ -3,9 +3,10 @@
"name": "Garmin Connect",
"codeowners": ["@cyberjunky"],
"config_flow": true,
"dependencies": [],
"documentation": "https://github.com/cyberjunky/home-assistant-garmin_connect",
"issue_tracker": "https://github.com/cyberjunky/home-assistant-garmin_connect/issues",
"iot_class": "cloud_polling",
"requirements": ["garminconnect==0.2.12", "tzlocal"],
"version": "0.2.18"
"issue_tracker": "https://github.com/cyberjunky/home-assistant-garmin_connect/issues",
"requirements": ["garminconnect>=0.2.23", "tzlocal"],
"version": "0.2.22"
}

View File

@@ -15,10 +15,9 @@ from homeassistant.components.sensor import (
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
UnitOfLength,
ATTR_ENTITY_ID,
CONF_ID,
DEVICE_CLASS_TIMESTAMP,
LENGTH_KILOMETERS,
)
from homeassistant.const import ATTR_ATTRIBUTION, CONF_ID
from homeassistant.core import HomeAssistant
@@ -101,6 +100,9 @@ async def async_setup_entry(
"set_active_gear", ENTITY_SERVICE_SCHEMA, coordinator.set_active_gear
)
platform.async_register_entity_service(
"add_body_composition", BODY_COMPOSITION_SERVICE_SCHEMA, coordinator.add_body_composition
)
ENTITY_SERVICE_SCHEMA = vol.Schema(
{
@@ -110,6 +112,24 @@ ENTITY_SERVICE_SCHEMA = vol.Schema(
}
)
BODY_COMPOSITION_SERVICE_SCHEMA = vol.Schema(
{
vol.Required(ATTR_ENTITY_ID): str,
vol.Optional("timestamp"): str,
vol.Required("weight"): float,
vol.Optional("percent_fat"): float,
vol.Optional("percent_hydration"): float,
vol.Optional("visceral_fat_mass"): float,
vol.Optional("bone_mass"): float,
vol.Optional("muscle_mass"): float,
vol.Optional("basal_met"): float,
vol.Optional("active_met"): float,
vol.Optional("physique_rating"): float,
vol.Optional("metabolic_age"): float,
vol.Optional("visceral_fat_rating"): float,
vol.Optional("bmi"): float
}
)
class GarminConnectSensor(CoordinatorEntity, SensorEntity):
"""Representation of a Garmin Connect Sensor."""
@@ -149,6 +169,9 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
if self._type == "lastActivities":
return len(self.coordinator.data[self._type])
if self._type == "badges":
return len(self.coordinator.data[self._type])
if not self.coordinator.data or not self.coordinator.data[self._type]:
return None
@@ -192,6 +215,9 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
if self._type == "lastActivities":
attributes["last_Activities"] = self.coordinator.data[self._type]
if self._type == "badges":
attributes["badges"] = self.coordinator.data[self._type]
if self._type == "nextAlarm":
attributes["next_alarms"] = calculate_next_active_alarms(
self.coordinator.data[self._type]
@@ -248,7 +274,7 @@ class GarminConnectGearSensor(CoordinatorEntity, SensorEntity):
self._attr_name = name
self._attr_device_class = self._device_class
self._attr_icon = GEAR_ICONS[sensor_type]
self._attr_native_unit_of_measurement = LENGTH_KILOMETERS
self._attr_native_unit_of_measurement = UnitOfLength.KILOMETERS
self._attr_unique_id = f"{self._unique_id}_{self._uuid}"
self._attr_state_class = SensorStateClass.TOTAL
self._attr_device_class = "garmin_gear"
@@ -351,4 +377,4 @@ class GarminConnectGearSensor(CoordinatorEntity, SensorEntity):
lambda d: d[GEAR.UUID] == self.uuid and d["defaultGear"] is True,
self.coordinator.data["gear_defaults"],
)
)
)

View File

@@ -1,8 +1,6 @@
set_active_gear:
name: Set active gear for activity
# target:
# entity:
# integration: "garmin_connect"
description: Set active gear for activity.
fields:
activity_type:
required: true
@@ -31,9 +29,88 @@ set_active_gear:
- set as default
- unset default
entity_id:
name: entity
description: entity
required: true
selector:
entity:
integration: garmin_connect
device_class: garmin_gear
add_body_composition:
name: Adds updated body composition metrics
description: Adds updated body composition metrics.
fields:
weight:
required: true
name: Weight
description: Weight in KG
example: 82.3
timestamp:
required: false
name: Timestamp
description: Datetime string of when the measurements were recorded. Defaults to now.
example: 2023-12-30T07:34:00
bmi:
required: false
name: BMI (Body Mass Index)
description: Body mass index is based on weight and height.
example: 24.7
percent_fat:
required: false
name: Percent Fat
description: Percent body fat
example: 23.6
percent_hydration:
required: false
name: Percent Hydration
description: Percent body hydration
example: 51.2
visceral_fat_mass:
required: false
name: Visceral Fat Mass
description: Estimated mass of visceral fat in KG
example: 45.3
bone_mass:
required: false
name: Bone Mass
description: Estimated mass of bones in KG
example: 10.1
muscle_mass:
required: false
name: Muscle Mass
description: Estimated mass of muscle in KG
example: 15.2
basal_met:
required: false
name: Basel Metabolism
description: Basel metabolism
example: 1900
active_met:
required: false
name: Active Metabolism
description: Active metabolism
example: 840
physique_rating:
required: false
name: Physique Rating
description: Physique Rating
example: 28
metabolic_age:
required: false
name: Metabolic Age
description: Metabolic Age
example: 37
visceral_fat_rating:
required: false
name: Visceral Fat Rating
description: Visceral Fat Rating
example: 10
entity_id:
name: entity
description: entity
required: true
selector:
entity:
integration: garmin_connect
device_class: weight

View File

@@ -15,7 +15,7 @@
"password": "Heslo",
"username": "Užívateľské meno"
},
"description": "Zadajte svoje poverenia.",
"description": "Zadajte svoje poverovacie údaje.",
"title": "Garmin Connect"
}
}

View File

@@ -1,5 +1,4 @@
{
"name": "Garmin Connect",
"render_readme": true,
"domains": ["sensor"]
}
"render_readme": true
}

19
mypy.ini Normal file
View File

@@ -0,0 +1,19 @@
[mypy]
python_version = 3.11
show_error_codes = true
follow_imports = silent
ignore_missing_imports = true
strict_equality = true
warn_incomplete_stub = true
warn_redundant_casts = true
warn_unused_configs = true
warn_unused_ignores = true
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true

81
pylintrc Normal file
View File

@@ -0,0 +1,81 @@
[MASTER]
ignore=tests
# Use a conservative default here; 2 should speed up most setups and not hurt
# any too bad. Override on command line as appropriate.
jobs=2
# Return non-zero exit code if any of these messages/categories are detected,
# even if score is above --fail-under value. Syntax same as enable. Messages
# specified are enabled, while categories only check already-enabled messages.
fail-on=
useless-suppression,
# Specify a score threshold to be exceeded before program exits with error.
fail-under=10.0
# List of plugins (as comma separated values of python module names) to load,
# usually to register additional checkers.
# load-plugins=
# Pickle collected data for later comparisons.
persistent=no
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
# for backward compatibility.)
extension-pkg-whitelist=ciso8601,
cv2
[BASIC]
good-names=i,j,k,ex,_,T,x,y,id
[MESSAGES CONTROL]
# Reasons disabled:
# format - handled by black
# duplicate-code - unavoidable
# cyclic-import - doesn't test if both import on load
# too-many-* - are not enforced for the sake of readability
# abstract-method - with intro of async there are always methods missing
# inconsistent-return-statements - doesn't handle raise
# wrong-import-order - isort guards this
disable=
format,
abstract-class-little-used,
abstract-method,
cyclic-import,
duplicate-code,
inconsistent-return-statements,
too-many-instance-attributes,
wrong-import-order,
too-few-public-methods
# enable useless-suppression temporarily every now and then to clean them up
enable=
useless-suppression,
use-symbolic-message-instead,
[REPORTS]
score=no
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=sys.exit,argparse.parse_error
[FORMAT]
expected-line-ending-format=LF
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "BaseException, Exception".
overgeneral-exceptions=BaseException,
Exception

8
requirements.txt Normal file
View File

@@ -0,0 +1,8 @@
colorlog==6.8.2
homeassistant==2024.1.0
pip>=24.1.1,<24.2
ruff==0.5.0
mypy==1.10.1
pre-commit==3.7.1
pylint==3.2.5
types-cachetools

3
requirements_lint.txt Normal file
View File

@@ -0,0 +1,3 @@
-r requirements.txt
pre-commit==3.7.1
vulture==2.11

20
scripts/develop Normal file
View File

@@ -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 <root>/custom_components/integration_blueprint
## while at the same time have Home Assistant configuration inside <root>/config
## without resulting to symlinks.
export PYTHONPATH="${PYTHONPATH}:${PWD}/custom_components"
# Start Home Assistant
hass --config "${PWD}/config" --debug

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -e
python3 -m pip \
install \
--upgrade \
--disable-pip-version-check \
"${@}"

13
scripts/lint Normal file
View File

@@ -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

7
scripts/setup Normal file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")/.."
python3 -m pip install --requirement requirements.txt

21
setup.cfg Normal file
View File

@@ -0,0 +1,21 @@
[flake8]
exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build
doctests = True
# To work with Black
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
# D107 Missing docstring in __init__
ignore =
E501,
W503,
E203,
D202,
D107
[isort]
# https://github.com/timothycrosley/isort
# https://github.com/timothycrosley/isort/wiki/isort-Settings
profile = black