Skip to content

Commit 4fbc8ab

Browse files
authored
Merge pull request #109 from erwindouna/device-auth
Add device auth
2 parents 3f0d7dd + d936b10 commit 4fbc8ab

File tree

15 files changed

+454
-74
lines changed

15 files changed

+454
-74
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
}
5353
},
5454
"features": {
55-
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
55+
"ghcr.io/devcontainers-extra/features/poetry:2": {},
5656
"ghcr.io/devcontainers/features/github-cli:1": {},
5757
"ghcr.io/devcontainers/features/node:1": {},
5858
"ghcr.io/devcontainers/features/python:1": {

.devcontainer/launch.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Run example.py",
6+
"type": "python",
7+
"request": "launch",
8+
"program": "${workspaceFolder}/examples/example.py",
9+
"console": "integratedTerminal",
10+
"justMyCode": true,
11+
"env": {
12+
"PYTHONPATH": "${workspaceFolder}/src"
13+
},
14+
"envFile": "${workspaceFolder}/.env"
15+
}
16+
]
17+
}

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,6 @@ ENV/
9494
# ruff
9595
.ruff_cache
9696

97-
# Visual Studio Code
98-
.vscode
99-
10097
# IntelliJ Idea family of suites
10198
.idea
10299
*.iml

.vscode/launch.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Run tadoasync example.py",
6+
"type": "debugpy",
7+
"request": "launch",
8+
"program": "${workspaceFolder}/examples/example.py",
9+
"console": "integratedTerminal",
10+
"justMyCode": true,
11+
"env": {
12+
"PYTHONPATH": "${workspaceFolder}/src"
13+
},
14+
"envFile": "${workspaceFolder}/.env"
15+
}
16+
]
17+
}

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,25 @@ based on the following:
6464
- `MINOR`: Backwards-compatible new features and enhancements.
6565
- `PATCH`: Backwards-compatible bugfixes and package updates.
6666

67+
## Usage
68+
69+
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.
70+
71+
PyTado handles this as following:
72+
73+
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`.
74+
75+
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.
76+
77+
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.
78+
The `device_verification_url()` will be reset to `None` and the `device_activation_status()` will return `COMPLETED`.
79+
80+
### Screenshots of the device flow
81+
82+
![Tado device flow: invoking](/screenshots/tado-device-flow-0.png)
83+
![Tado device flow: browser](/screenshots/tado-device-flow-1.png)
84+
![Tado device flow: complete](/screenshots/tado-device-flow-2.png)
85+
6786
## Contributing
6887

6988
This is an active open-source project. We are always open to people who want to

examples/example.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,42 @@
11
"""Asynchronous Python client for the Tado API. This is an example file."""
22

3+
from __future__ import annotations
4+
35
import asyncio
6+
import logging
47

58
from tadoasync import Tado
69

10+
logging.basicConfig(level=logging.DEBUG)
11+
712

813
async def main() -> None:
914
"""Show example on how to use aiohttp.ClientSession."""
10-
async with Tado("username", "password") as tado:
11-
await tado.get_devices()
15+
refresh_token: str | None = None
16+
async with Tado(debug=True) as tado:
17+
print("Device activation status: ", tado.device_activation_status) # noqa: T201
18+
print("Device verification URL: ", tado.device_verification_url) # noqa: T201
19+
20+
print("Starting device activation") # noqa: T201
21+
await tado.device_activation()
22+
refresh_token = tado.refresh_token
23+
24+
print("Device activation status: ", tado.device_activation_status) # noqa: T201
25+
26+
devices = await tado.get_devices()
27+
28+
print("Devices: ", devices) # noqa: T201
29+
30+
print("Trying to use the stored refresh token for another run...") # noqa: T201
31+
await asyncio.sleep(1)
32+
33+
async with Tado(debug=True, refresh_token=refresh_token) as tado:
34+
print("Refresh token: ", tado.refresh_token) # noqa: T201
35+
print("Device activation status: ", tado.device_activation_status) # noqa: T201
36+
37+
devices = await tado.get_devices()
38+
39+
print("Devices: ", devices) # noqa: T201
1240

1341

1442
if __name__ == "__main__":

poetry.lock

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ mashumaro = ">=3.10"
2626
orjson = ">=3.9.8"
2727
python = "^3.12"
2828
yarl = ">=1.6.0"
29-
aioresponses = "^0.7.6"
29+
aioresponses = "^0.7.7"
3030

3131
[tool.poetry.urls]
3232
"Bug Tracker" = "https://github.com/erwindouna/python-tado/issues"

screenshots/tado-device-flow-0.png

12.3 KB
Loading

screenshots/tado-device-flow-1.png

23.4 KB
Loading

0 commit comments

Comments
 (0)