pyzephyrconnect
Python client for Zephyr / Gemtek range hoods.
These hoods expose no local API. All communication is a cloud round-trip through AWS IoT Core device shadows. See PROTOCOL.md for how the protocol was reverse-engineered.
Install
pip install pyzephyrconnect
Read state
import aiohttp
from pyzephyrconnect import ZephyrClient
async with aiohttp.ClientSession() as session:
client = ZephyrClient.from_credentials("you@example.com", "password", session)
try:
for hood in await client.async_setup():
print(hood.capabilities.model, hood.capabilities.max_fan_speed)
await hood.async_start()
print(hood.state)
finally:
await client.async_stop()
Persisting tokens
The library never persists credentials - storage is yours. Supply tokens from a previous session and a callback to save new ones, and a restart skips the SRP login entirely:
from pyzephyrconnect import ZephyrClient, ZephyrTokens, ZephyrDataError
try:
tokens = ZephyrTokens.from_dict(saved) if saved else None
except ZephyrDataError:
# from_dict validates rather than coercing, so a corrupted or partial
# record raises here instead of failing much later as a SECRET_HASH
# Cognito rejects. Discard it - a full SRP login rebuilds it.
tokens = None
client = ZephyrClient.from_credentials(
username, password, session,
tokens=tokens,
token_updater=lambda t: save(t.as_dict()),
)
To keep the password out of the library completely, subclass AbstractAuth
and implement async_get_tokens().
MQTT client ID
AWS IoT treats two live connections sharing a client ID as one session and
evicts one for the other, so every connection is identified by the account's
Cognito identity plus a suffix identifying you. It defaults to -py; pass
your own if anything else may talk to the same account - the vendor phone app
already does:
client = ZephyrClient.from_credentials(
username, password, session, client_id_suffix="-ha",
)
The same keyword works on CredentialsAuth, and on AbstractAuth for a
custom subclass. It must be a non-empty string, and short: AWS IoT caps the
whole client ID at 128 characters, and the library appends -<thingName> per
hood on top of identity plus suffix.
Probe CLI
The write path actuates a physical fan and light. The CLI writes one field
at a time, refuses anything outside an allowlist, and requires --confirm:
export ZEPHYR_USER=you@example.com
python -m pyzephyrconnect --watch
python -m pyzephyrconnect --set light=1 --confirm
Destructive writes need --force as well. resetgreasefilter zeroes a usage
counter that cannot be reconstructed.
Status
Read and write paths both verified against a Zephyr AK7400AS. power,
light, fan, setdelaytimer (seconds) and setcleanairfunction have
established semantics; resetgreasefilter ships untested by design.
Filter counters are in minutes, run-time counters in hours - see PROTOCOL.md §5 before deriving anything from them.
Still unestablished: the act mode strings and the delay-timer ceiling.
See PROTOCOL.md §7.
License
GPL-3.0-or-later
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyzephyrconnect-0.2.0.tar.gz.
File metadata
- Download URL: pyzephyrconnect-0.2.0.tar.gz
- Upload date:
- Size: 155.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2afe47e95570351596390735e053e06b2838153e99340ec6cebf72586fbeddec
|
|
| MD5 |
d2f550b09368be45ffcc43369d752ee8
|
|
| BLAKE2b-256 |
79e50a259ed8c983c3daaf78b3fdc8e3a2f63f8d8d75e2dcb9aeca982c5d2f01
|
Provenance
The following attestation bundles were made for pyzephyrconnect-0.2.0.tar.gz:
Publisher:
release.yml on RyanMorash/pyzephyrconnect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyzephyrconnect-0.2.0.tar.gz -
Subject digest:
2afe47e95570351596390735e053e06b2838153e99340ec6cebf72586fbeddec - Sigstore transparency entry: 2596820505
- Sigstore integration time:
-
Permalink:
RyanMorash/pyzephyrconnect@a1cee77c2282b2ffb22d30d8c46f04d566fb843d -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/RyanMorash
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a1cee77c2282b2ffb22d30d8c46f04d566fb843d -
Trigger Event:
release
-
Statement type:
File details
Details for the file pyzephyrconnect-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pyzephyrconnect-0.2.0-py3-none-any.whl
- Upload date:
- Size: 76.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f4d4ba10e6d29fc6160d9ddcae545e2368ff6a6034e162a2acd385fb7d99f85
|
|
| MD5 |
0e49f7c495f2a0bf3f263022f0717308
|
|
| BLAKE2b-256 |
2fad97eddc40cf02914a14741306044cd09a9724737765cb4e9115eb484ea9ce
|
Provenance
The following attestation bundles were made for pyzephyrconnect-0.2.0-py3-none-any.whl:
Publisher:
release.yml on RyanMorash/pyzephyrconnect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyzephyrconnect-0.2.0-py3-none-any.whl -
Subject digest:
6f4d4ba10e6d29fc6160d9ddcae545e2368ff6a6034e162a2acd385fb7d99f85 - Sigstore transparency entry: 2596820794
- Sigstore integration time:
-
Permalink:
RyanMorash/pyzephyrconnect@a1cee77c2282b2ffb22d30d8c46f04d566fb843d -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/RyanMorash
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a1cee77c2282b2ffb22d30d8c46f04d566fb843d -
Trigger Event:
release
-
Statement type: