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
5 changes: 5 additions & 0 deletions .devcontainer/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM mcr.microsoft.com/vscode/devcontainers/python:3.12
USER root
RUN rm -f /etc/apt/sources.list.d/yarn.list \
&& rm -f /usr/share/keyrings/yarn.gpg \
&& apt-get update
9 changes: 5 additions & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@
"ghcr.io/devcontainers/features/python:1": {
"installTools": false
},
"ghcr.io/devcontainers-extra/features/pre-commit:2": {},
"ghcr.io/devcontainers/features/common-utils:2": {
"installOhMyZsh": true
}
},
"image": "mcr.microsoft.com/vscode/devcontainers/python:3.12",
"build": {
"dockerfile": "Containerfile"
},
"name": "Asynchronous Python client Tado",
"updateContentCommand": ". ${NVM_DIR}/nvm.sh && nvm install && nvm use && npm install && poetry install && poetry run pre-commit install",
"postStartCommand": ". ${NVM_DIR}/nvm.sh && nvm install && nvm use && npm install && poetry install && poetry run pre-commit install"
"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"
}
32 changes: 16 additions & 16 deletions .github/workflows/linting.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,33 +57,33 @@ jobs:
- name: 🏗 Install Python dependencies
run: poetry install --no-interaction
- name: 🚀 Check Python AST
run: poetry run pre-commit run check-ast --all-files
run: uv run prek run check-ast --all-files
- name: 🚀 Check for case conflicts
run: poetry run pre-commit run check-case-conflict --all-files
run: uv run prek run check-case-conflict --all-files
- name: 🚀 Check docstring is first
run: poetry run pre-commit run check-docstring-first --all-files
run: uv run prek run check-docstring-first --all-files
- name: 🚀 Check that executables have shebangs
run: poetry run pre-commit run check-executables-have-shebangs --all-files
run: uv run prek run check-executables-have-shebangs --all-files
- name: 🚀 Check JSON files
run: poetry run pre-commit run check-json --all-files
run: uv run prek run check-json --all-files
- name: 🚀 Check for merge conflicts
run: poetry run pre-commit run check-merge-conflict --all-files
run: uv run prek run check-merge-conflict --all-files
- name: 🚀 Check for broken symlinks
run: poetry run pre-commit run check-symlinks --all-files
run: uv run prek run check-symlinks --all-files
- name: 🚀 Check TOML files
run: poetry run pre-commit run check-toml --all-files
run: uv run prek run check-toml --all-files
- name: 🚀 Check XML files
run: poetry run pre-commit run check-xml --all-files
run: uv run prek run check-xml --all-files
- name: 🚀 Check YAML files
run: poetry run pre-commit run check-yaml --all-files
run: uv run prek run check-yaml --all-files
- name: 🚀 Check YAML files
run: poetry run pre-commit run check-yaml --all-files
run: uv run prek run check-yaml --all-files
Comment on lines 79 to +80
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate step: "Check YAML files" is executed twice (lines 77-78 and 79-80). One of these steps should be removed.

Copilot uses AI. Check for mistakes.
- name: 🚀 Detect Private Keys
run: poetry run pre-commit run detect-private-key --all-files
run: uv run prek run detect-private-key --all-files
- name: 🚀 Check End of Files
run: poetry run pre-commit run end-of-file-fixer --all-files
run: uv run prek run end-of-file-fixer --all-files
- name: 🚀 Trim Trailing Whitespace
run: poetry run pre-commit run trailing-whitespace --all-files
run: uv run prek run trailing-whitespace --all-files

pylint:
name: Pylint
Expand All @@ -106,7 +106,7 @@ jobs:
- name: 🏗 Install Python dependencies
run: poetry install --no-interaction
- name: 🚀 Run pylint
run: poetry run pre-commit run pylint --all-files
run: uv run prek run pylint --all-files

yamllint:
name: Yamllint
Expand Down Expand Up @@ -159,4 +159,4 @@ jobs:
- name: 🏗 Install NPM dependencies
run: npm install
- name: 🚀 Run prettier
run: poetry run pre-commit run prettier --all-files
run: uv run prek run prettier --all-files
88 changes: 39 additions & 49 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,96 +1,92 @@
---
repos:
- repo: local
- repo: builtin
hooks:
- id: ruff-check
name: 🐶 Ruff Linter
language: system
types: [python]
entry: uv run ruff check --fix
require_serial: true
stages: [commit, push, manual]
- id: ruff-format
name: 🐶 Ruff Formatter
language: system
types: [python]
entry: uv run ruff format
require_serial: true
stages: [commit, push, manual]
- id: check-ast
name: 🐍 Check Python AST
language: system
types: [python]
entry: uv run check-ast
- id: check-case-conflict
name: 🔠 Check for case conflicts
language: system
entry: uv run check-case-conflict
- id: check-docstring-first
name: ℹ️ Check docstring is first
language: system
types: [python]
entry: uv run check-docstring-first
- id: check-executables-have-shebangs
name: 🧐 Check that executables have shebangs
language: system
types: [text, executable]
entry: uv run check-executables-have-shebangs
stages: [commit, push, manual]
- id: check-json
name: { Check JSON files
language: system
types: [json]
entry: uv run check-json
- id: check-merge-conflict
name: 💥 Check for merge conflicts
language: system
types: [text]
entry: uv run check-merge-conflict
- id: check-symlinks
name: 🔗 Check for broken symlinks
language: system
types: [symlink]
entry: uv run check-symlinks
- id: check-toml
name: ✅ Check TOML files
language: system
types: [toml]
entry: uv run check-toml
- id: check-xml
name: ✅ Check XML files
entry: check-xml
language: system
types: [xml]
- id: check-yaml
name: ✅ Check YAML files
language: system
types: [yaml]
entry: uv run check-yaml
- id: detect-private-key
name: 🕵️ Detect Private Keys
language: system
types: [text]
entry: uv run detect-private-key
- id: end-of-file-fixer
name: ⮐ Fix End of Files
language: system
types: [text]
entry: uv run end-of-file-fixer
stages: [commit, push, manual]
- id: mypy
name: 🆎 Static type checking using mypy
language: system
types: [python]
entry: uv run mypy
require_serial: true
- id: no-commit-to-branch
name: 🛑 Don't commit to main branch
language: system
entry: uv run no-commit-to-branch
pass_filenames: false
always_run: true
args:
- --branch=main
- id: trailing-whitespace
name: ✄ Trim Trailing Whitespace
language: system
types: [text]
stages: [commit, push, manual]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-ast
name: 🐍 Check Python AST
types: [python]
- id: check-docstring-first
name: ℹ️ Check docstring is first
types: [python]

- repo: local
hooks:
- id: ruff-check
name: 🐶 Ruff Linter
language: system
types: [python]
entry: uv run ruff check --fix
require_serial: true
stages: [commit, push, manual]
- id: ruff-format
name: 🐶 Ruff Formatter
language: system
types: [python]
entry: uv run ruff format
require_serial: true
stages: [commit, push, manual]
- id: mypy
name: 🆎 Static type checking using mypy
language: system
types: [python]
entry: uv run mypy
require_serial: true
- id: uv
name: 📜 Check pyproject with uv
language: system
Expand All @@ -114,12 +110,6 @@ repos:
types: [python]
entry: uv run pytest --cov=tadoasync --cov-report=term-missing --cov-report=xml --cov-fail-under=95
pass_filenames: false
- id: trailing-whitespace
name: ✄ Trim Trailing Whitespace
language: system
types: [text]
entry: uv run trailing-whitespace-fixer
stages: [commit, push, manual]
- id: yamllint
name: 🎗 Check YAML files with yamllint
language: system
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,33 +101,33 @@ the [Dev Container][devcontainer] feature of Visual Studio Code.

[![Open in Dev Containers][devcontainer-shield]][devcontainer]

This Python project is fully managed using the [Poetry][poetry] dependency manager. But also relies on the use of NodeJS for certain checks during development.
This Python project is fully managed using the [uv][uv] dependency manager. But also relies on the use of NodeJS for certain checks during development.

You need at least:

- Python 3.12+
- [Poetry][poetry-install]
- [uv][uv-install]
- NodeJS 18+ (including NPM)

To install all packages, including all development requirements:

```bash
npm install
poetry install
uv install
```

As this repository uses the [pre-commit][pre-commit] framework, all changes
are linted and tested with each commit. You can run all checks and tests
manually, using the following command:

```bash
poetry run pre-commit run --all-files
uv run prek run --all-files
```

To run just the Python tests:

```bash
poetry run pytest
uv run pytest
```

## Authors & contributors
Expand Down Expand Up @@ -174,8 +174,8 @@ SOFTWARE.
[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
[poetry-install]: https://python-poetry.org/docs/#installation
[poetry]: https://python-poetry.org
[uv-install]: https://docs.astral.sh/uv/getting-started/installation/
[uv]: https://docs.astral.sh/uv/
[pre-commit]: https://pre-commit.com/
[project-stage-shield]: https://img.shields.io/badge/project%20stage-production%20ready-brightgreen.svg
[pypi]: https://pypi.org/project/tadoasync/
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ dev = [
"covdefaults==2.3.0",
"coverage[toml]==7.4.3",
"mypy==1.9.0",
"pre-commit==3.6.0",
"pre-commit-hooks==4.5.0",
"prek",
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prek dependency is not pinned to a specific version, which is inconsistent with most other dev dependencies in this file (e.g., mypy==1.9.0, pylint==3.0.3, pytest==8.1.1). Consider pinning prek to a specific version for reproducibility. However, if the intent is to always use the latest version, this approach is acceptable but should be documented or made consistent across all dev dependencies.

Suggested change
"prek",
"prek==0.6.5",

Copilot uses AI. Check for mistakes.
"pylint==3.0.3",
"pytest==8.1.1",
"pytest-asyncio>=0.23.5.post1,<0.24",
Expand Down
10 changes: 4 additions & 6 deletions src/tadoasync/tadoasync.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import logging
import time
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from datetime import UTC, datetime, timedelta
from importlib import metadata
from typing import Self
from urllib.parse import urlencode
Expand Down Expand Up @@ -185,9 +185,7 @@ async def login_device_flow(self) -> DeviceActivationStatus:
_LOGGER.info("Please visit the following URL: %s", visit_url)

expires_in_seconds = float(self._device_flow_data["expires_in"])
self._expires_at = datetime.now(timezone.utc) + timedelta(
seconds=expires_in_seconds
)
self._expires_at = datetime.now(UTC) + timedelta(seconds=expires_in_seconds)

_LOGGER.info(
"Waiting for user to authorize the device. Expires at %s",
Expand All @@ -198,7 +196,7 @@ async def login_device_flow(self) -> DeviceActivationStatus:

async def _check_device_activation(self) -> bool:
if self._expires_at is not None and datetime.timestamp(
datetime.now(timezone.utc)
datetime.now(UTC)
) > datetime.timestamp(self._expires_at):
raise TadoError("User took too long to enter key")

Expand Down Expand Up @@ -544,7 +542,7 @@ async def set_meter_readings(
) -> None:
"""Set the meter readings."""
if date is None:
date = datetime.now(timezone.utc)
date = datetime.now(UTC)

payload = {"date": date.strftime("%Y-%m-%d"), "reading": reading}
response = await self._request(
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Asynchronous Python client for Tado."""

from typing import AsyncGenerator, Generator
from collections.abc import AsyncGenerator, Generator

import aiohttp
import pytest
Expand Down
6 changes: 3 additions & 3 deletions tests/test_tado.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import asyncio
import os
import time
from datetime import datetime, timedelta, timezone
from datetime import UTC, datetime, timedelta
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch

Expand Down Expand Up @@ -111,7 +111,7 @@ async def test_activation_timeout(responses: aioresponses) -> None:
async with aiohttp.ClientSession() as session:
tado = Tado(session=session)
await tado.async_init()
tado._expires_at = datetime.now(timezone.utc) - timedelta(seconds=1)
tado._expires_at = datetime.now(UTC) - timedelta(seconds=1)
with pytest.raises(TadoError, match="User took too long"):
await tado.device_activation()

Expand Down Expand Up @@ -608,7 +608,7 @@ async def test_add_meter_readings_duplicated(
python_tado: Tado, responses: aioresponses
) -> None:
"""Test adding meter readings with duplicate."""
date = datetime(2023, 10, 1, 12, 0, 0, tzinfo=timezone.utc)
date = datetime(2023, 10, 1, 12, 0, 0, tzinfo=UTC)
reading = 5
responses.post(
TADO_EIQ_URL,
Expand Down
Loading
Loading