Skip to content

Commit e66103e

Browse files
authored
feat: add pedestal fan, auto release, device fixes (#442)
Number of bug fixes and device features, including: - Fix greensun outlet - Fix 6000s display state - Remove 131s child lock - Add Pedestal Fan - Add doc github actions and auto release
2 parents 1496a5e + 70bd0da commit e66103e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1278
-182
lines changed

.github/workflows/Releases.yml

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
name: Release and Publish
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
workflow_dispatch:
8+
9+
jobs:
10+
release:
11+
name: Create Release and Publish to PyPI
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Check out repository
16+
uses: actions/checkout@v5
17+
with:
18+
fetch-depth: 0 # ensure full history & tags
19+
20+
- name: Fetch tags
21+
run: |
22+
git fetch --tags --force
23+
24+
- name: Set up Python
25+
uses: actions/setup-python@v6
26+
with:
27+
python-version: "3.12"
28+
29+
- name: Install tooling (packaging)
30+
run: |
31+
python -m pip install --upgrade pip
32+
pip install packaging
33+
34+
# 1) Extract the new version from pyproject.toml
35+
- name: Extract version from pyproject.toml
36+
id: get_version
37+
shell: bash
38+
run: |
39+
python - << 'PY' > version.txt
40+
import tomllib
41+
from pathlib import Path
42+
43+
data = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
44+
version = data["project"]["version"]
45+
print(version)
46+
PY
47+
version="$(cat version.txt)"
48+
echo "New version from pyproject.toml: $version"
49+
echo "version=$version" >> "$GITHUB_OUTPUT"
50+
51+
# 2) Get the previous tag in format "x.x.x"
52+
- name: Get previous semantic tag
53+
id: get_prev_tag
54+
shell: bash
55+
run: |
56+
# Look for tags that look like x.x.x (no leading "v")
57+
prev_tag="$(git tag --list '[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n 1 || true)"
58+
59+
if [ -z "$prev_tag" ]; then
60+
echo "No previous semantic tag found (x.x.x). Exiting workflow."
61+
exit 1
62+
else
63+
echo "Found previous tag: $prev_tag"
64+
echo "previous_tag=$prev_tag" >> "$GITHUB_OUTPUT"
65+
echo "previous_version=$prev_tag" >> "$GITHUB_OUTPUT"
66+
fi
67+
68+
# 3) Ensure the new version is greater than the previous tag's version
69+
- name: Ensure new version is greater than previous
70+
if: steps.get_prev_tag.outputs.previous_version != ''
71+
env:
72+
NEW_VERSION: ${{ steps.get_version.outputs.version }}
73+
OLD_VERSION: ${{ steps.get_prev_tag.outputs.previous_version }}
74+
shell: python
75+
run: |
76+
import os
77+
import sys
78+
from packaging.version import Version
79+
80+
new = Version(os.environ["NEW_VERSION"])
81+
old = Version(os.environ["OLD_VERSION"])
82+
83+
print(f"Previous version: {old}")
84+
print(f"New version: {new}")
85+
86+
if new <= old:
87+
print(f"Error: new version {new} is not greater than previous {old}.")
88+
sys.exit(1)
89+
else:
90+
print(f"OK: new version {new} is greater than previous {old}.")
91+
92+
- name: Install build dependencies
93+
run: |
94+
python -m pip install --upgrade pip
95+
pip install build
96+
97+
- name: Build distribution
98+
run: |
99+
python -m build
100+
101+
- name: Create GitHub Release
102+
uses: softprops/action-gh-release@v2
103+
env:
104+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
105+
with:
106+
tag_name: "${{ steps.get_version.outputs.version }}" # e.g. "1.2.3"
107+
name: "${{ steps.get_version.outputs.version }}"
108+
generate_release_notes: true
109+
draft: false
110+
prerelease: false
111+
files: |
112+
dist/*
113+
114+
- name: Publish to PyPI
115+
uses: pypa/gh-action-pypi-publish@release/v1
116+
with:
117+
password: ${{ secrets.PYPI_API_TOKEN }}
118+
# skip-existing: true
119+
120+
- name: Configure Git for mike
121+
run: |
122+
git config user.name github-actions
123+
git config user.email [email protected]
124+
125+
- name: Fetch gh-pages branch for mike
126+
run: |
127+
git fetch origin gh-pages --depth=1 || true
128+
129+
- name: Install documentation dependencies
130+
run: |
131+
pip install .[docs]
132+
133+
- name: Deploy documentation with mike
134+
env:
135+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
136+
run: |
137+
mike deploy --push --update-aliases ${{ steps.get_version.outputs.version }} latest
138+
mike set-default --push latest

.github/workflows/RunTest.yaml

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
- master
77
- dev
88
push:
9+
branches-ignore:
10+
- master
11+
- dev
912
workflow_dispatch:
1013

1114

@@ -17,14 +20,15 @@ jobs:
1720
python-version: ["3.11", "3.12", "3.13"]
1821

1922
steps:
20-
- uses: actions/checkout@v3
23+
- uses: actions/checkout@v5
2124
- name: Set up Python ${{ matrix.python-version }}
22-
uses: actions/setup-python@v4
25+
uses: actions/setup-python@v6
2326
with:
2427
python-version: ${{ matrix.python-version }}
2528
- name: Install dependencies
2629
run: |
2730
python -m pip install --upgrade pip
31+
pip install .
2832
pip install .[dev]
2933
- name: Lint with Ruff
3034
id: ruff
@@ -43,6 +47,35 @@ jobs:
4347
run: |
4448
pip install pytest
4549
pytest -v
50+
- name: Build docs (mkdocs)
51+
id: docs
52+
if: github.event_name == 'pull_request' && github.base_ref == 'master' && matrix.python-version == '3.12'
53+
run: |
54+
pip install .[docs]
55+
mkdocs build
56+
4657
- name: Fail if any steps failed
4758
run: |
48-
[ "${{ steps.ruff.outcome }}" == "failure" ] || [ "${{ steps.pylint.outcome }}" == "failure" ] || [ "${{ steps.pytest.outcome }}" == "failure" ] && exit 1 || exit 0
59+
failed=false
60+
61+
if [ "${{ steps.ruff.outcome }}" = "failure" ]; then
62+
echo "::error::Ruff linting failed"
63+
failed=true
64+
fi
65+
66+
if [ "${{ steps.pylint.outcome }}" = "failure" ]; then
67+
echo "::error::Pylint failed"
68+
failed=true
69+
fi
70+
71+
if [ "${{ steps.pytest.outcome }}" = "failure" ]; then
72+
echo "::error::Pytest failed"
73+
failed=true
74+
fi
75+
76+
if [ "$failed" = "true" ]; then
77+
echo "One or more checks failed. See errors above."
78+
exit 1
79+
else
80+
echo "All checks passed."
81+
fi

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ testing_scripts/api_test_editor.py
3939
testing_scripts/yamltest.yaml
4040
testing_scripts/yamltest copy.yaml
4141
creds.json
42+
docstest/*

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ repos:
1111
- id: check-ast
1212
- id: check-toml
1313
- repo: https://github.com/pre-commit/mirrors-mypy
14-
rev: v1.17.1
14+
rev: v1.18.2
1515
hooks:
1616
- id: mypy
1717
- repo: https://github.com/astral-sh/ruff-pre-commit
18-
rev: v0.12.12
18+
rev: v0.14.3
1919
hooks:
2020
- id: ruff-check
2121
args: [ --fix, --config=ruff.toml ]

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ if __name__ == "__main__":
299299
asyncio.run(main())
300300
```
301301

302-
If you want to reuse your token and account_id between runs. The `VeSync.auth` object holds the credentials and helper methods to save and load credentials. See the [Authentication Documentation](https://webdjoe.github.io/pyvesync/latest/authentication.md) for more details.
302+
If you want to reuse your token and account_id between runs. The `VeSync.auth` object holds the credentials and helper methods to save and load credentials. See the [Authentication Documentation](https://webdjoe.github.io/pyvesync/latest/authentication) for more details.
303303

304304
```python
305305
import asyncio
@@ -555,11 +555,11 @@ if __name__ == "__main__":
555555

556556
## Device Requests
557557

558-
If you would like to request a new device to be added to the library, please open an issue on GitHub. Be sure to include the device model number and a link to the product page. If you are able to provide packet captures or are willing to share the device temporarily, please indicate that in the issue. See the [Packet Capturing for New Device Support](https://webdjoe.github.io/pyvesync/latest/development/capturing.md) document for more details.
558+
If you would like to request a new device to be added to the library, please open an issue on GitHub. Be sure to include the device model number and a link to the product page. If you are able to provide packet captures or are willing to share the device temporarily, please indicate that in the issue. See the [Packet Capturing for New Device Support](https://webdjoe.github.io/pyvesync/latest/development/capturing) document for more details.
559559

560560
## Contributing
561561

562-
All [contributions](https://webdjoe.github.io/pyvesync/latest/development/CONTRIBUTING.md) are welcome.
562+
All [contributions](https://webdjoe.github.io/pyvesync/latest/development/contributing) are welcome.
563563

564564
This project is licensed under [MIT](LICENSE).
565565

docs/development/vesync_device_base.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ All methods and attributes are available on all devices.
3636
show_if_no_docstring: true
3737
show_signature_annotations: true
3838
docstring_options:
39-
ignore_init_summary: false
39+
ignore_init_summary: false
4040
filters:
41-
- "!.*pid"
4241
- "!^__*"
4342
- "!displayJSON"
4443

@@ -54,7 +53,7 @@ All methods and attributes are available on all devices.
5453
show_if_no_docstring: true
5554
show_signature_annotations: true
5655
docstring_options:
57-
ignore_init_summary: false
56+
ignore_init_summary: false
5857
filters:
5958
- "!.*pid"
6059
- "!^__*"

docs/devices/fans.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,25 @@
3838
ignore_init_summary: false
3939
merge_init_into_class: true
4040

41+
::: pyvesync.devices.vesyncfan.VeSyncPedestalFan
42+
options:
43+
filters:
44+
- "!^_.*"
45+
- "!__init__"
46+
summary:
47+
functions: false
48+
group_by_category: true
49+
show_root_heading: true
50+
show_root_toc_entry: true
51+
show_category_heading: true
52+
show_source: false
53+
show_signature_annotations: true
54+
signature_crossrefs: true
55+
inherited_members: true
56+
docstring_options:
57+
ignore_init_summary: false
58+
merge_init_into_class: true
59+
4160
::: pyvesync.base_devices.fan_base.VeSyncFanBase
4261
options:
4362
filters:

docs/devices/thermostats.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@
1919
show_root_heading: true
2020
members_order: source
2121
filters:
22-
- "!^_.*"
22+
- "!^_.*"

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Some of the major structural changes include:
3737
- Device Classes have been refactored to be more consistent and easier to manage. No more random property and method names for different types of the same device.
3838
- [`const`][pyvesync.const] module to hold all library constants.
3939
- [`device_map`][pyvesync.device_map] module holds all device type mappings and configuration.
40-
- Authentication logic has been moved to the [`auth`][pyvesync.auth] module and is now handled by the `VeSync.auth` attribute. This can store and load credentials, so login is not required every time.
40+
- Authentication logic has been moved to the [`auth`][pyvesync.auth.VeSyncAuth] module and is now handled by the `VeSync.auth` attribute. This can store and load credentials, so login is not required every time.
4141

4242
See [pyvesync V3](./pyvesync3.md) for more details.
4343

docs/supported_devices.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ The VeSync API supports a variety of devices. The following is a list of devices
1313
- [Etekcity 10A Rount Outlet USA][pyvesync.devices.vesyncoutlet.VeSyncESW10USA]
1414
- [Etekcity 15A Rectangle Outlet][pyvesync.devices.vesyncoutlet.VeSyncOutlet15A]
1515
- [Etekcity 15A Outdoor Dual Outlet][pyvesync.devices.vesyncoutlet.VeSyncOutdoorPlug]
16-
- [Round Smart Outlet Series][pyvesync.devices.vesyncoutlet.VeSyncOutletBSDGO1] - WHOPLUG / GREENSUN
16+
- [BSDOG / Greensun Smart Outlet Series][pyvesync.devices.vesyncoutlet.VeSyncBSDOGPlug] - WHOPLUG / GREENSUN
1717
3. Switches
1818
- [ESWL01][pyvesync.devices.vesyncswitch.VeSyncWallSwitch] - Etekcity Wall Switch
1919
- [ESWL03][pyvesync.devices.vesyncswitch.VeSyncWallSwitch] - Etekcity 3-Way Switch
@@ -37,6 +37,7 @@ The VeSync API supports a variety of devices. The following is a list of devices
3737
- [Superior 6000S][pyvesync.devices.vesynchumidifier.VeSyncSuperior6000S] - 6L Smart Humidifier
3838
6. Fans
3939
- [42" Tower Fan][pyvesync.devices.vesyncfan.VeSyncTowerFan]
40+
- [Pedestal Fan][pyvesync.devices.vesyncfan.VeSyncPedestalFan]
4041
7. Air Fryers
4142
- [CS137][pyvesync.devices.vesynckitchen.VeSyncAirFryer158] - 3.7qt Air Fryer
4243
- [CS158][pyvesync.devices.vesynckitchen.VeSyncAirFryer158] - 5.8qt Air Fryer

0 commit comments

Comments
 (0)