pyinfra-tasmota 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyinfra_tasmota-0.1.0/.envrc +2 -0
- pyinfra_tasmota-0.1.0/.github/workflows/0-testing.yml +43 -0
- pyinfra_tasmota-0.1.0/.github/workflows/1-build-and-publish.yml +73 -0
- pyinfra_tasmota-0.1.0/.gitignore +9 -0
- pyinfra_tasmota-0.1.0/AGENTS.md +146 -0
- pyinfra_tasmota-0.1.0/PKG-INFO +93 -0
- pyinfra_tasmota-0.1.0/README.md +82 -0
- pyinfra_tasmota-0.1.0/pyproject.toml +79 -0
- pyinfra_tasmota-0.1.0/src/pyinfra_tasmota/__init__.py +5 -0
- pyinfra_tasmota-0.1.0/src/pyinfra_tasmota/connector.py +474 -0
- pyinfra_tasmota-0.1.0/src/pyinfra_tasmota/operations.py +170 -0
- pyinfra_tasmota-0.1.0/tests/__init__.py +0 -0
- pyinfra_tasmota-0.1.0/tests/conftest.py +116 -0
- pyinfra_tasmota-0.1.0/tests/test_connector.py +443 -0
- pyinfra_tasmota-0.1.0/tests/test_operations.py +243 -0
- pyinfra_tasmota-0.1.0/uv.lock +2209 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Style and unit tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["main"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
run-tests:
|
|
11
|
+
name: Setup and run tests
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.13", "3.14"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Check out the source
|
|
20
|
+
uses: actions/checkout@v6
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v7
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
enable-cache: true
|
|
29
|
+
|
|
30
|
+
- name: Install dependencies
|
|
31
|
+
run: uv sync --locked
|
|
32
|
+
|
|
33
|
+
- name: Style and formatting (ruff)
|
|
34
|
+
run: uv run ruff check .
|
|
35
|
+
|
|
36
|
+
- name: Unit and integration tests (pytest)
|
|
37
|
+
run: uv run pytest
|
|
38
|
+
|
|
39
|
+
- name: Mypy Check
|
|
40
|
+
run: uv run mypy .
|
|
41
|
+
|
|
42
|
+
- name: Bandit check
|
|
43
|
+
run: uv run bandit -c pyproject.toml -r .
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: Build package, publish to PyPi, and make a release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
workflow_dispatch: # allows manual triggering
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
name: "Build package"
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: Check out the source
|
|
15
|
+
uses: actions/checkout@v6
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
persist-credentials: false
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v7
|
|
22
|
+
|
|
23
|
+
- name: Build package
|
|
24
|
+
run: uv build
|
|
25
|
+
|
|
26
|
+
- name: Upload distributions
|
|
27
|
+
uses: actions/upload-artifact@v6
|
|
28
|
+
with:
|
|
29
|
+
name: python-package-distributions
|
|
30
|
+
path: dist/
|
|
31
|
+
|
|
32
|
+
publish-to-pypi:
|
|
33
|
+
name: Upload to PyPi
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
36
|
+
needs: build
|
|
37
|
+
|
|
38
|
+
environment: pypi
|
|
39
|
+
permissions:
|
|
40
|
+
id-token: write
|
|
41
|
+
contents: read
|
|
42
|
+
|
|
43
|
+
steps:
|
|
44
|
+
- name: Download distributions
|
|
45
|
+
uses: actions/download-artifact@v7
|
|
46
|
+
with:
|
|
47
|
+
name: python-package-distributions
|
|
48
|
+
path: dist/
|
|
49
|
+
|
|
50
|
+
- name: Push to PyPI
|
|
51
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
52
|
+
|
|
53
|
+
github-release:
|
|
54
|
+
name: Create GitHub Release
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
57
|
+
needs: build
|
|
58
|
+
|
|
59
|
+
permissions:
|
|
60
|
+
contents: write
|
|
61
|
+
|
|
62
|
+
steps:
|
|
63
|
+
- name: Download all the dists
|
|
64
|
+
uses: actions/download-artifact@v7
|
|
65
|
+
with:
|
|
66
|
+
name: python-package-distributions
|
|
67
|
+
path: dist/
|
|
68
|
+
|
|
69
|
+
- name: Create GitHub Release
|
|
70
|
+
uses: softprops/action-gh-release@v2
|
|
71
|
+
with:
|
|
72
|
+
files: dist/*.whl dist/*.tar.gz
|
|
73
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# `pyinfra-tasmota`
|
|
2
|
+
|
|
3
|
+
Guidance for coding agents working in this repository. Read this together with
|
|
4
|
+
the user-level `AGENTS.md`, which takes precedence where the two overlap.
|
|
5
|
+
|
|
6
|
+
## Product
|
|
7
|
+
|
|
8
|
+
A Pyinfra connector to handle Tasmota devices via their HTTP API.
|
|
9
|
+
|
|
10
|
+
## Stack
|
|
11
|
+
|
|
12
|
+
- Python, [`decode-config`](https://github.com/tasmota/decode-config)
|
|
13
|
+
- `uv` for dependency management
|
|
14
|
+
|
|
15
|
+
## Project Structure
|
|
16
|
+
|
|
17
|
+
```raw
|
|
18
|
+
./
|
|
19
|
+
├── src/pyinfra_tasmota/ # package
|
|
20
|
+
│ ├── connector.py # Implementation of the connector
|
|
21
|
+
│ └── __init__.py # Export of public API
|
|
22
|
+
├── tests/ # pytest test suite
|
|
23
|
+
├── pyproject.toml # project metadata & tool config
|
|
24
|
+
├── .editorconfig # common editor configuration
|
|
25
|
+
├── .gitignore # Git ignore file
|
|
26
|
+
└── .pre-commit-config.yaml # `pre-commit` configuration
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Setup & Build
|
|
30
|
+
|
|
31
|
+
- `uv add <dependency>` to add a dependency (never `uv pip`).
|
|
32
|
+
- `uv add --dev <dependency>` to add a development dependency.
|
|
33
|
+
- `uv sync` to create the environment and install the package.
|
|
34
|
+
- `uv build` for production wheels.
|
|
35
|
+
- `uv run <tool>` to run any tool (`pytest`, `mypy`, `ruff`, `bandit`).
|
|
36
|
+
|
|
37
|
+
## Quality Gates
|
|
38
|
+
|
|
39
|
+
| Gate | Tool |
|
|
40
|
+
| -------- | ------------------------------- |
|
|
41
|
+
| Format | `ruff format --diff` |
|
|
42
|
+
| Lint | `ruff check --show-fixes` |
|
|
43
|
+
| Types | `mypy .` |
|
|
44
|
+
| Security | `bandit -c pyproject.toml -r .` |
|
|
45
|
+
| Tests | `pytest` |
|
|
46
|
+
|
|
47
|
+
All gates run via pre-commit hooks; only `pytest` needs a manual run (with
|
|
48
|
+
`uv run`).
|
|
49
|
+
|
|
50
|
+
The pre-commit config lives in `.pre-commit-config.yaml`.
|
|
51
|
+
|
|
52
|
+
A task is done only when docstrings, other comments, README, and manpage are
|
|
53
|
+
updated, and `uv run pytest` passes with 100% coverage.
|
|
54
|
+
|
|
55
|
+
### Mypy configuration
|
|
56
|
+
|
|
57
|
+
Set `warn_unreachable = true`.
|
|
58
|
+
|
|
59
|
+
Set `ignore_missing_imports = true` for module `ipdb`.
|
|
60
|
+
|
|
61
|
+
The Mypy pre-commit hook runs `uv run mypy .`, so it sees the project
|
|
62
|
+
environment and its dependencies; there is no separate dependency list to
|
|
63
|
+
maintain.
|
|
64
|
+
|
|
65
|
+
### Coverage
|
|
66
|
+
|
|
67
|
+
```toml
|
|
68
|
+
exclude_also = [
|
|
69
|
+
'def __repr__',
|
|
70
|
+
'if TYPE_CHECKING:',
|
|
71
|
+
'class .*\bProtocol\):',
|
|
72
|
+
'@overload',
|
|
73
|
+
'@(abc\.)?abstractmethod',
|
|
74
|
+
'raise NotImplementedError',
|
|
75
|
+
'raise AssertionError',
|
|
76
|
+
]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Bandit
|
|
80
|
+
|
|
81
|
+
Skip `assert_used` for test files.
|
|
82
|
+
|
|
83
|
+
## Git Workflow
|
|
84
|
+
|
|
85
|
+
Each task goes on a **separate feature branch** created from `main`:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
git checkout main
|
|
89
|
+
git checkout -b feature/<short-name>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Never commit to `main`.
|
|
93
|
+
|
|
94
|
+
Always bundle commits which update the tests, change the code, and reflect the
|
|
95
|
+
new behaviour in all documentation, docstrings, and comments. Do not split a
|
|
96
|
+
change's tests, code, or documentation across separate commits.
|
|
97
|
+
|
|
98
|
+
Scope-prefixed messages (e.g. `docs:`, `ci:`, `collector:`) commit messages, not
|
|
99
|
+
Conventional Commits.
|
|
100
|
+
|
|
101
|
+
While a feature branch has not been merged into `main`, related follow-up work
|
|
102
|
+
(bugfixes, tweaks) should be appended to the appropriate feature branch. If it
|
|
103
|
+
is not clear to you what branch to use, let me choose from a list of
|
|
104
|
+
possibilities.
|
|
105
|
+
|
|
106
|
+
## Coding Conventions
|
|
107
|
+
|
|
108
|
+
- Google-style docstrings on every public function.
|
|
109
|
+
- Strict formatting according to Ruff;
|
|
110
|
+
- Prefer test D211 over D203.
|
|
111
|
+
- Prefer test D213 over D212.
|
|
112
|
+
- Type hints everywhere (use `| None` / `list[X]` / `dict[K, V]`).
|
|
113
|
+
- Relative imports within the package (`from .foo import bar`).
|
|
114
|
+
- Module-level imports preferred; inline imports only for tests, otherwise with
|
|
115
|
+
approval; Select Ruff test "PLC0415" except for tests.
|
|
116
|
+
- EAFP: catch exceptions instead of pre-checking.
|
|
117
|
+
- `pathlib` over `os.path`; f-strings over `%` / `.format()`.
|
|
118
|
+
- Use comprehensions, generators, decorators where they fit.
|
|
119
|
+
|
|
120
|
+
## Test Conventions
|
|
121
|
+
|
|
122
|
+
- 100% line + branch coverage goal. `# pragma: no cover` only on defensive
|
|
123
|
+
branches that cannot be triggered (document why).
|
|
124
|
+
- Red / green: write the test, make it pass, keep it green.
|
|
125
|
+
- Prefer behaviour-based assertions over introspection of private attributes.
|
|
126
|
+
- Deterministic fixtures (factories / `tmp_path` / `CliRunner`).
|
|
127
|
+
- `pytest.mark.xfail` instead of commenting out a failing test.
|
|
128
|
+
- Run affected tests before the full suite.
|
|
129
|
+
|
|
130
|
+
## Text encoding
|
|
131
|
+
|
|
132
|
+
- Write typographic characters directly as UTF-8 (for example ’ “ ” – — é © …),
|
|
133
|
+
not as HTML/XML entities or numeric references such as `’`, `’`,
|
|
134
|
+
or `’`.
|
|
135
|
+
- Use escaping only where the target syntax requires it, such as `&`, `<`, and
|
|
136
|
+
`>` inside HTML or XML markup, or where a format explicitly needs ASCII.
|
|
137
|
+
- Save all text files as UTF-8.
|
|
138
|
+
|
|
139
|
+
## Ignore
|
|
140
|
+
|
|
141
|
+
Treat everything in `.gitignore` as off-limits to read or edit. On top of that,
|
|
142
|
+
never open:
|
|
143
|
+
|
|
144
|
+
- Secrets and `.env` files
|
|
145
|
+
- Large data files unrelated to the current task
|
|
146
|
+
- Vendored or generated code
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pyinfra-tasmota
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Pyinfra connector to manage Tasmota devices via their HTTP API
|
|
5
|
+
Author: Martin F. Krafft
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: pyinfra>=3.10
|
|
9
|
+
Requires-Dist: requests>=2.31
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# pyinfra-tasmota
|
|
13
|
+
|
|
14
|
+
A [Pyinfra](https://pyinfra.com) connector for Tasmota devices.
|
|
15
|
+
|
|
16
|
+
The connector talks to a Tasmota device over its HTTP API. It reads the
|
|
17
|
+
device settings once at connect time, compares operation intent against
|
|
18
|
+
that cache, and sends only the commands that are needed as batched
|
|
19
|
+
`Backlog0` requests.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
uv add pyinfra-tasmota
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
Inventory:
|
|
30
|
+
|
|
31
|
+
```py
|
|
32
|
+
hosts = ["@tasmota/tasmota.example.org"]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Deploy:
|
|
36
|
+
|
|
37
|
+
```py
|
|
38
|
+
from pyinfra_tasmota.operations import apply, cmnd
|
|
39
|
+
|
|
40
|
+
cmnd(SetOption114=1, SetOption120=0, Power="toggle")
|
|
41
|
+
apply()
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`cmnd` takes any number of commands as keyword arguments. It compares each
|
|
45
|
+
desired value with the cache. It reads the current value from the device if
|
|
46
|
+
the setting is not cached, and records a pending change only if the value
|
|
47
|
+
differs. Every command is sent by `apply`, or on disconnect as a safety net.
|
|
48
|
+
|
|
49
|
+
Command names are matched case-insensitively and resolve the `SO` alias of
|
|
50
|
+
`SetOption`, so `SetOption114`, `setoption114`, and `so114` are the same
|
|
51
|
+
command. Boolean values are folded, so a desired `0` matches a device value
|
|
52
|
+
of `OFF`, and `1` matches `ON`.
|
|
53
|
+
|
|
54
|
+
Non-rebooting commands are flushed as `Backlog0` batches, split to stay
|
|
55
|
+
within Tasmota's limit of 30 commands and decode-config's limit of 320
|
|
56
|
+
characters per command line. Reboot-triggering commands are sent on their own.
|
|
57
|
+
|
|
58
|
+
## Reboots
|
|
59
|
+
|
|
60
|
+
Some commands reboot the device, for example `Restart`, `Module`, and
|
|
61
|
+
`Template`. The connector sends these commands on their own, after all other
|
|
62
|
+
commands, and waits for the device to answer again before continuing. A
|
|
63
|
+
device is considered back once it was seen to drop and is answering, or once
|
|
64
|
+
its `BootCount` has advanced.
|
|
65
|
+
|
|
66
|
+
If the device does not return within the timeout, `apply` reports an error.
|
|
67
|
+
|
|
68
|
+
## Verbosity
|
|
69
|
+
|
|
70
|
+
The connector follows pyinfra's `-v` flags:
|
|
71
|
+
|
|
72
|
+
- `-v` reports every command sent to the device.
|
|
73
|
+
- `-vv` also reports the observed state of each command, and any command
|
|
74
|
+
skipped because the device already matches the desired value.
|
|
75
|
+
- `-vvv` also reports internal detail, such as the size of the status cache
|
|
76
|
+
and how commands are grouped into `Backlog0` batches.
|
|
77
|
+
|
|
78
|
+
## Versioning
|
|
79
|
+
|
|
80
|
+
The package version comes from Git via `uv-dynamic-versioning`. Tag a release
|
|
81
|
+
as `vX.Y.Z`; commits after a tag get a `.postN.dev0+<hash>` suffix. A checkout
|
|
82
|
+
with no tags reports `0.0.0`.
|
|
83
|
+
|
|
84
|
+
## Connector data
|
|
85
|
+
|
|
86
|
+
Set these on a host in the inventory:
|
|
87
|
+
|
|
88
|
+
- `tasmota_friendly_name`: an optional human-readable name, used in logs.
|
|
89
|
+
- `tasmota_reboot_timeout`: seconds to wait for a rebooted device to answer
|
|
90
|
+
again (default `60`).
|
|
91
|
+
|
|
92
|
+
The connector registers itself with Pyinfra under the name `tasmota`, so use
|
|
93
|
+
`@tasmota/<host>` anywhere a connector is expected.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# pyinfra-tasmota
|
|
2
|
+
|
|
3
|
+
A [Pyinfra](https://pyinfra.com) connector for Tasmota devices.
|
|
4
|
+
|
|
5
|
+
The connector talks to a Tasmota device over its HTTP API. It reads the
|
|
6
|
+
device settings once at connect time, compares operation intent against
|
|
7
|
+
that cache, and sends only the commands that are needed as batched
|
|
8
|
+
`Backlog0` requests.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
uv add pyinfra-tasmota
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
Inventory:
|
|
19
|
+
|
|
20
|
+
```py
|
|
21
|
+
hosts = ["@tasmota/tasmota.example.org"]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Deploy:
|
|
25
|
+
|
|
26
|
+
```py
|
|
27
|
+
from pyinfra_tasmota.operations import apply, cmnd
|
|
28
|
+
|
|
29
|
+
cmnd(SetOption114=1, SetOption120=0, Power="toggle")
|
|
30
|
+
apply()
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`cmnd` takes any number of commands as keyword arguments. It compares each
|
|
34
|
+
desired value with the cache. It reads the current value from the device if
|
|
35
|
+
the setting is not cached, and records a pending change only if the value
|
|
36
|
+
differs. Every command is sent by `apply`, or on disconnect as a safety net.
|
|
37
|
+
|
|
38
|
+
Command names are matched case-insensitively and resolve the `SO` alias of
|
|
39
|
+
`SetOption`, so `SetOption114`, `setoption114`, and `so114` are the same
|
|
40
|
+
command. Boolean values are folded, so a desired `0` matches a device value
|
|
41
|
+
of `OFF`, and `1` matches `ON`.
|
|
42
|
+
|
|
43
|
+
Non-rebooting commands are flushed as `Backlog0` batches, split to stay
|
|
44
|
+
within Tasmota's limit of 30 commands and decode-config's limit of 320
|
|
45
|
+
characters per command line. Reboot-triggering commands are sent on their own.
|
|
46
|
+
|
|
47
|
+
## Reboots
|
|
48
|
+
|
|
49
|
+
Some commands reboot the device, for example `Restart`, `Module`, and
|
|
50
|
+
`Template`. The connector sends these commands on their own, after all other
|
|
51
|
+
commands, and waits for the device to answer again before continuing. A
|
|
52
|
+
device is considered back once it was seen to drop and is answering, or once
|
|
53
|
+
its `BootCount` has advanced.
|
|
54
|
+
|
|
55
|
+
If the device does not return within the timeout, `apply` reports an error.
|
|
56
|
+
|
|
57
|
+
## Verbosity
|
|
58
|
+
|
|
59
|
+
The connector follows pyinfra's `-v` flags:
|
|
60
|
+
|
|
61
|
+
- `-v` reports every command sent to the device.
|
|
62
|
+
- `-vv` also reports the observed state of each command, and any command
|
|
63
|
+
skipped because the device already matches the desired value.
|
|
64
|
+
- `-vvv` also reports internal detail, such as the size of the status cache
|
|
65
|
+
and how commands are grouped into `Backlog0` batches.
|
|
66
|
+
|
|
67
|
+
## Versioning
|
|
68
|
+
|
|
69
|
+
The package version comes from Git via `uv-dynamic-versioning`. Tag a release
|
|
70
|
+
as `vX.Y.Z`; commits after a tag get a `.postN.dev0+<hash>` suffix. A checkout
|
|
71
|
+
with no tags reports `0.0.0`.
|
|
72
|
+
|
|
73
|
+
## Connector data
|
|
74
|
+
|
|
75
|
+
Set these on a host in the inventory:
|
|
76
|
+
|
|
77
|
+
- `tasmota_friendly_name`: an optional human-readable name, used in logs.
|
|
78
|
+
- `tasmota_reboot_timeout`: seconds to wait for a rebooted device to answer
|
|
79
|
+
again (default `60`).
|
|
80
|
+
|
|
81
|
+
The connector registers itself with Pyinfra under the name `tasmota`, so use
|
|
82
|
+
`@tasmota/<host>` anywhere a connector is expected.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pyinfra-tasmota"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "A Pyinfra connector to manage Tasmota devices via their HTTP API"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [{ name = "Martin F. Krafft" }]
|
|
9
|
+
dependencies = ["pyinfra>=3.10", "requests>=2.31"]
|
|
10
|
+
|
|
11
|
+
[project.entry-points."pyinfra.connectors"]
|
|
12
|
+
tasmota = "pyinfra_tasmota.connector:TasmotaConnector"
|
|
13
|
+
|
|
14
|
+
[dependency-groups]
|
|
15
|
+
dev = [
|
|
16
|
+
"bandit>=1.7",
|
|
17
|
+
"mypy>=1.11",
|
|
18
|
+
"pytest>=8.0",
|
|
19
|
+
"pytest-cov>=5.0",
|
|
20
|
+
"ruff>=0.6",
|
|
21
|
+
"types-requests>=2.32",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["hatchling", "uv-dynamic-versioning"]
|
|
26
|
+
build-backend = "hatchling.build"
|
|
27
|
+
|
|
28
|
+
[tool.hatch.version]
|
|
29
|
+
source = "uv-dynamic-versioning"
|
|
30
|
+
|
|
31
|
+
[tool.hatch.build.targets.wheel]
|
|
32
|
+
packages = ["src/pyinfra_tasmota"]
|
|
33
|
+
|
|
34
|
+
[tool.ruff]
|
|
35
|
+
line-length = 100
|
|
36
|
+
target-version = "py310"
|
|
37
|
+
|
|
38
|
+
[tool.ruff.lint]
|
|
39
|
+
select = ["E", "F", "I", "N", "UP", "B", "A", "C4", "PT", "RET", "SIM", "PL", "RUF", "D"]
|
|
40
|
+
ignore = ["D203", "D212", "PLR0913"]
|
|
41
|
+
|
|
42
|
+
[tool.ruff.lint.pydocstyle]
|
|
43
|
+
convention = "google"
|
|
44
|
+
|
|
45
|
+
[tool.ruff.lint.per-file-ignores]
|
|
46
|
+
"tests/*" = ["D101", "D102", "D103", "D104", "D107", "PLC0415", "PLR2004"]
|
|
47
|
+
|
|
48
|
+
[tool.mypy]
|
|
49
|
+
python_version = "3.10"
|
|
50
|
+
strict = true
|
|
51
|
+
warn_unreachable = true
|
|
52
|
+
ignore_missing_imports = true
|
|
53
|
+
|
|
54
|
+
[[tool.mypy.overrides]]
|
|
55
|
+
module = "tests.*"
|
|
56
|
+
disallow_untyped_defs = false
|
|
57
|
+
disallow_untyped_calls = false
|
|
58
|
+
|
|
59
|
+
[tool.pytest.ini_options]
|
|
60
|
+
testpaths = ["tests"]
|
|
61
|
+
addopts = "--cov=pyinfra_tasmota --cov-report=term-missing --cov-fail-under=100"
|
|
62
|
+
|
|
63
|
+
[tool.coverage.run]
|
|
64
|
+
branch = true
|
|
65
|
+
|
|
66
|
+
[tool.coverage.report]
|
|
67
|
+
exclude_also = [
|
|
68
|
+
'def __repr__',
|
|
69
|
+
'if TYPE_CHECKING:',
|
|
70
|
+
'class .*\bProtocol\):',
|
|
71
|
+
'@overload',
|
|
72
|
+
'@(abc\.)?abstractmethod',
|
|
73
|
+
'raise NotImplementedError',
|
|
74
|
+
'raise AssertionError',
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
[tool.bandit]
|
|
78
|
+
skips = ["B101"]
|
|
79
|
+
exclude_dirs = [".venv", "tests"]
|