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
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
}
},
"features": {
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
"ghcr.io/devcontainers-extra/features/poetry:2": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/devcontainers/features/python:1": {
Expand Down
17 changes: 17 additions & 0 deletions .devcontainer/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Run example.py",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/examples/example.py",
"console": "integratedTerminal",
"justMyCode": true,
"env": {
"PYTHONPATH": "${workspaceFolder}/src"
},
"envFile": "${workspaceFolder}/.env"
}
]
}
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,6 @@ ENV/
# ruff
.ruff_cache

# Visual Studio Code
.vscode

# IntelliJ Idea family of suites
.idea
*.iml
Expand Down
17 changes: 17 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Run tadoasync example.py",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/examples/example.py",
"console": "integratedTerminal",
"justMyCode": true,
"env": {
"PYTHONPATH": "${workspaceFolder}/src"
},
"envFile": "${workspaceFolder}/.env"
}
]
}
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@ based on the following:
- `MINOR`: Backwards-compatible new features and enhancements.
- `PATCH`: Backwards-compatible bugfixes and package updates.

## Usage

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.

PyTado handles this as following:

1. The `_login_device_flow()` will be invoked at the initialization of a PyTado object. This will start the device flow and will return a URL and a code that the user will have to enter in the browser. The URL can be obtained via the method `device_verification_url()`. Or, when in debug mode, the URL will be printed. Alternatively, you can use the `device_activation_status()` method to check if the device has been activated. It returns three statuses: `NOT_STARTED`, `PENDING`, and `COMPLETED`. Wait to invoke the `device_activation()` method until the status is `PENDING`.

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`.

### Screenshots of the device flow

![Tado device flow: invoking](/screenshots/tado-device-flow-0.png)
![Tado device flow: browser](/screenshots/tado-device-flow-1.png)
![Tado device flow: complete](/screenshots/tado-device-flow-2.png)

## Contributing

This is an active open-source project. We are always open to people who want to
Expand Down
32 changes: 30 additions & 2 deletions examples/example.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,42 @@
"""Asynchronous Python client for the Tado API. This is an example file."""

from __future__ import annotations

import asyncio
import logging

from tadoasync import Tado

logging.basicConfig(level=logging.DEBUG)


async def main() -> None:
"""Show example on how to use aiohttp.ClientSession."""
async with Tado("username", "password") as tado:
await tado.get_devices()
refresh_token: str | None = None
async with Tado(debug=True) as tado:
print("Device activation status: ", tado.device_activation_status) # noqa: T201
print("Device verification URL: ", tado.device_verification_url) # noqa: T201

print("Starting device activation") # noqa: T201
await tado.device_activation()
refresh_token = tado.refresh_token

print("Device activation status: ", tado.device_activation_status) # noqa: T201

devices = await tado.get_devices()

print("Devices: ", devices) # noqa: T201

print("Trying to use the stored refresh token for another run...") # noqa: T201
await asyncio.sleep(1)

async with Tado(debug=True, refresh_token=refresh_token) as tado:
print("Refresh token: ", tado.refresh_token) # noqa: T201
print("Device activation status: ", tado.device_activation_status) # noqa: T201

devices = await tado.get_devices()

print("Devices: ", devices) # noqa: T201


if __name__ == "__main__":
Expand Down
15 changes: 8 additions & 7 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mashumaro = ">=3.10"
orjson = ">=3.9.8"
python = "^3.12"
yarl = ">=1.6.0"
aioresponses = "^0.7.6"
aioresponses = "^0.7.7"

[tool.poetry.urls]
"Bug Tracker" = "https://github.com/erwindouna/python-tado/issues"
Expand Down
Binary file added screenshots/tado-device-flow-0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/tado-device-flow-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/tado-device-flow-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading