Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .devcontainer/Containerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
FROM mcr.microsoft.com/vscode/devcontainers/python:3.12
FROM mcr.microsoft.com/vscode/devcontainers/python:3.14

USER root

RUN rm -f /etc/apt/sources.list.d/yarn.list \
&& rm -f /usr/share/keyrings/yarn.gpg \
&& apt-get update
20 changes: 12 additions & 8 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"containerEnv": {
"POETRY_VIRTUALENVS_IN_PROJECT": "true"
"UV_PROJECT_ENVIRONMENT": "${workspaceFolder}/.venv"
},
"customizations": {
"codespaces": {
Expand All @@ -11,6 +11,7 @@
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.mypy-type-checker",
"redhat.vscode-yaml",
"esbenp.prettier-vscode",
"GitHub.vscode-pull-request-github",
Expand All @@ -21,9 +22,11 @@
"settings": {
"[python]": {
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true
}
"source.fixAll": "always",
"source.organizeImports": "always"
},
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true
},
"coverage-gutters.customizable.context-menu": true,
"coverage-gutters.customizable.status-bar-toggler-watchCoverageAndVisibleEditors-enabled": true,
Expand All @@ -33,8 +36,7 @@
"python.analysis.extraPaths": [
"${workspaceFolder}/src"
],
"python.defaultInterpreterPath": ".venv/bin/python",
"python.formatting.provider": "black",
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
"python.linting.enabled": true,
"python.linting.mypyEnabled": true,
"python.linting.pylintEnabled": true,
Expand All @@ -43,16 +45,19 @@
"--cov-report=xml"
],
"python.testing.pytestEnabled": true,
"python.languageServer": "Pylance",
"ruff.enable": true,
"ruff.importStrategy": "fromEnvironment",
"ruff.interpreter": [
".venv/bin/python"
"${workspaceFolder}/.venv/bin/python"
],
"terminal.integrated.defaultProfile.linux": "zsh"
}
}
},
"features": {
"ghcr.io/devcontainers-extra/features/uv:1": {},
"ghcr.io/devcontainers-extra/features/ruff:2": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/devcontainers/features/python:1": {
Expand All @@ -66,6 +71,5 @@
"dockerfile": "Containerfile"
},
"name": "Asynchronous Python client Tado",
"updateContentCommand": ". ${NVM_DIR}/nvm.sh && nvm install && nvm use && npm install && uv sync && uv run prek install",
"postStartCommand": ". ${NVM_DIR}/nvm.sh && nvm install && nvm use && npm install && uv sync && uv run prek install"
}
2 changes: 1 addition & 1 deletion .github/workflows/linting.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
workflow_dispatch:

env:
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.14"

jobs:
ruff:
Expand Down
18 changes: 5 additions & 13 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ on:
required: true

env:
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.14"

jobs:
release:
Expand All @@ -42,20 +42,12 @@ jobs:
steps:
- name: ⤵️ Check out code from GitHub
uses: actions/checkout@v4.1.1
- name: 🏗 Set up Poetry
run: pipx install poetry
- name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.0.0
- name: 🏗 Install uv and set the Python version ${{ env.DEFAULT_PYTHON }}
uses: astral-sh/setup-uv@v7
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache: "poetry"
- name: 🏗 Install workflow dependencies
run: |
poetry config virtualenvs.create true
poetry config virtualenvs.in-project true
- name: 🏗 Install dependencies
run: poetry install --no-interaction
- name: 🏗 Install Python dependencies
run: uv sync
- name: 🏗 Set package version
run: |
version="${{ github.event.release.tag_name }}"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ on:
workflow_dispatch:

env:
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.14"

jobs:
pytest:
name: Python ${{ matrix.python }}
runs-on: ubuntu-latest
strategy:
matrix:
python: ["3.12"]
python: ["3.14"]
steps:
- name: ⤵️ Check out code from GitHub
uses: actions/checkout@v4.1.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/typing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
workflow_dispatch:

env:
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.14"

jobs:
mypy:
Expand Down
18 changes: 8 additions & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ repos:
args: [--branch=main]

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.8
rev: v0.15.5
hooks:
- id: ruff
name: 🐶 Ruff Linter
Expand All @@ -60,22 +60,15 @@ repos:
stages: [commit, push, manual]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.14.1
rev: v1.19.1
hooks:
- id: mypy
name: 🆎 Static type checking using mypy
types: [python]
additional_dependencies: []

- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0
hooks:
- id: prettier
name: 💄 Ensuring files are prettier
types: [yaml, json, markdown]

- repo: https://github.com/adrienverge/yamllint
rev: v1.35.1
rev: v1.38.0
hooks:
- id: yamllint
name: 🎗 Check YAML files with yamllint
Expand All @@ -100,3 +93,8 @@ repos:
types: [python]
entry: uv run pytest --cov=tadoasync --cov-report=term-missing --cov-report=xml --cov-fail-under=95
pass_filenames: false
- id: prettier
name: 💄 Ensuring files are prettier
language: system
entry: npx prettier --write --ignore-unknown
types: [yaml, json, markdown]
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ based on the following:
- `MINOR`: Backwards-compatible new features and enhancements.
- `PATCH`: Backwards-compatible bugfixes and package updates.

## Usage
## Authentication

As of the 15th of March 2025, Tado has updated their OAuth2 authentication flow. It will now use the device flow, instead of a username/password flow. This means that the user will have to authenticate the device using a browser, and then enter the code that is displayed on the browser into the terminal.

Expand All @@ -77,7 +77,7 @@ PyTado handles this as following:
2. Once the URL is obtained, the user will have to enter the code that is displayed on the browser into the terminal. By default, the URL has the `user_code` attached, for the ease of going trough the flow. At this point, run the method `device_activation()`. It will poll every five seconds to see if the flow has been completed. If the flow has been completed, the method will return a token that will be used for all further requests. It will timeout after five minutes.

3. Once the token has been obtained, the user can use the PyTado object to interact with the Tado API. The token will be stored in the `Tado` object, and will be used for all further requests. The token will be refreshed automatically when it expires.
The `device_verification_url()` will be reset to `None` and the `device_activation_status()` will return `COMPLETED`.
The `device_verification_url()` will be reset to `None` and the `device_activation_status()` will return `COMPLETED`.

### Screenshots of the device flow

Expand Down Expand Up @@ -114,7 +114,7 @@ To install all packages, including all development requirements:

```bash
npm install
uv install
uv sync
```

As this repository uses the [pre-commit][pre-commit] framework, all changes
Expand Down Expand Up @@ -172,8 +172,6 @@ SOFTWARE.
[docs]: https://erwindouna.github.io/python-tado/
[docs-shield]: https://github.com/erwindouna/python-tado/actions/workflows/deploy-docs.yaml/badge.svg
[erwindouna]: https://github.com/erwindouna
[github-sponsors-shield]: https://erwindouna.dev/wp-content/uploads/2019/12/github_sponsor.png
[github-sponsors]: https://github.com/sponsors/erwindouna
[keepchangelog]: http://keepachangelog.com/en/1.0.0/
[license-shield]: https://img.shields.io/github/license/erwindouna/python-tado.svg
[maintenance-shield]: https://img.shields.io/maintenance/yes/2025.svg
Expand Down
42 changes: 20 additions & 22 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ classifiers = [
"Framework :: AsyncIO",
"Intended Audience :: Developers",
"Natural Language :: English",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Libraries :: Python Modules",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
dependencies = [
"aiohttp>=3.0.0",
"mashumaro>=3.10",
"orjson>=3.9.8",
"mashumaro>=3.20",
"orjson>=3.10.12",
"yarl>=1.6.0",
"aioresponses>=0.7.7,<0.8",
"pyjwt>=2.11.0",
Expand All @@ -43,21 +43,21 @@ Changelog = "https://github.com/erwindouna/python-tado/releases"

[dependency-groups]
dev = [
"aresponses==3.0.0",
"codespell==2.2.6",
"covdefaults==2.3.0",
"coverage[toml]==7.4.3",
"mypy==1.9.0",
"prek",
"pylint==3.0.3",
"pytest==8.1.1",
"pytest-asyncio>=0.23.5.post1,<0.24",
"pytest-cov==4.1.0",
"ruff==0.1.13",
"safety==3.0.1",
"yamllint==1.33.0",
"syrupy==4.6.1",
"deptry>=0.19.1,<0.20",
"aresponses>=3.0.0",
"codespell>=2.2.6",
"covdefaults>=2.3.0",
"coverage[toml]>=7.4.3",
"mypy>=1.9.0",
"prek>=0.3.4",
"pylint>=3.0.3",
"pytest>=9.0.2",
"pytest-asyncio>=1.3.0",
"pytest-cov>=4.1.0",
"ruff>=0.15.5",
"safety>=3.0.1",
"yamllint>=1.33.0",
"syrupy>=4.6.1",
"deptry>=0.24.0",
]

[tool.hatch.build.targets.sdist]
Expand Down Expand Up @@ -86,7 +86,7 @@ source = ["tadoasync"]
# free to run mypy on Windows, Linux, or macOS and get consistent
# results.
platform = "linux"
python_version = "3.12"
python_version = "3.14"

# show error messages from unrelated files
follow_imports = "normal"
Expand Down Expand Up @@ -164,9 +164,7 @@ asyncio_mode = "auto"
[tool.ruff]
[tool.ruff.lint]
ignore = [
"ANN101", # Self... explanatory
"ANN102", # cls... just as useless
"ANN401", # Opinioated warning on disallowing dynamically typed expressions
"ANN401", # Opinionated warning on disallowing dynamically typed expressions
"D203", # Conflicts with other rules
"ARG002", # Conflicts with other rules
"D213", # Conflicts with other rules
Expand Down
19 changes: 8 additions & 11 deletions scripts/generate_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,7 @@ def _find_pydoc_markdown() -> str:
"""Find pydoc-markdown binary or raise."""
pydoc_md = shutil.which("pydoc-markdown")
if pydoc_md is None:
msg = (
"pydoc-markdown not found."
" Install it with: uv pip install pydoc-markdown"
)
msg = "pydoc-markdown not found. Install it with: uv pip install pydoc-markdown"
raise RuntimeError(msg)
return pydoc_md

Expand Down Expand Up @@ -103,7 +100,7 @@ def _get_inherited_members(cls: type) -> list[dict[str, str]]:
if callable(attr):
try:
sig = str(inspect.signature(attr))
except (ValueError, TypeError):
except (ValueError, TypeError): # fmt: skip
sig = "(...)"
members.append({"name": name, "sig": sig, "doc": doc})
return members
Expand All @@ -118,13 +115,13 @@ def _format_inherited_section(
anchor = f'<a id="{module_name}.{class_name}.{m["name"]}"></a>\n\n'
if m["sig"]:
lines.append(
f'{anchor}#### {m["name"]}\n\n```python\n'
f'def {m["name"]}{m["sig"]}\n```\n'
f"{anchor}#### {m['name']}\n\n```python\n"
f"def {m['name']}{m['sig']}\n```\n"
)
else:
lines.append(f'{anchor}#### {m["name"]}\n')
lines.append(f"{anchor}#### {m['name']}\n")
if m["doc"]:
lines.append(f'\n{m["doc"]}\n')
lines.append(f"\n{m['doc']}\n")
return "\n".join(lines)


Expand Down Expand Up @@ -176,8 +173,8 @@ def generate(filename: str, config: dict[str, str], pydoc_md: str) -> None:
tmp_path = tmp.name

try:
result = subprocess.run(
[pydoc_md, tmp_path], # noqa: S603
result = subprocess.run( # noqa: S603
[pydoc_md, tmp_path],
capture_output=True,
text=True,
check=True,
Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ sonar.sourceEncoding=UTF-8
sonar.sources=src
sonar.tests=tests

sonar.python.version=3.12
sonar.python.version=3.14
sonar.python.coverage.reportPaths=coverage.xml
6 changes: 3 additions & 3 deletions src/tadoasync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@

__all__ = [
"Tado",
"TadoConnectionError",
"TadoError",
"TadoAuthenticationError",
"TadoServerError",
"TadoBadRequestError",
"TadoConnectionError",
"TadoError",
"TadoReadingError",
"TadoServerError",
]
2 changes: 1 addition & 1 deletion src/tadoasync/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ class ZoneState(DataClassORJSONMixin): # pylint: disable=too-many-instance-attr
geolocation_override: bool = field(
metadata=field_options(alias="geolocationOverride")
)
overlay_type: str = field(metadata=field_options(alias="overlayType"))
overlay_type: str | None = field(metadata=field_options(alias="overlayType"))
next_time_block: dict[str, str] = field(
metadata=field_options(alias="nextTimeBlock")
)
Expand Down
Loading
Loading