Skip to content

About

Python client for Zephyr Connect

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

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

About

Python client for Zephyr Connect

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages