Metadata-Version: 2.4
Name: pyzeptrion
Version: 2.0.1
Summary: Async Python library to control Zeptrion lights and blinds (Feller / Schneider Electric)
Author-email: Thierry Perroud <astronaut@footprintsonthemoon.ch>
License-Expression: MIT
Project-URL: Homepage, https://github.com/footprintsonthemoon/zeptrion_python
Project-URL: Documentation, https://github.com/footprintsonthemoon/zeptrion_python
Project-URL: Repository, https://github.com/footprintsonthemoon/zeptrion_python
Project-URL: Bug Tracker, https://github.com/footprintsonthemoon/zeptrion_python/issues
Keywords: zeptrion,feller,home assistant,smart home,light,blind,dimmer
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Home Automation
Classifier: Framework :: AsyncIO
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp>=3.8
Requires-Dist: zeroconf>=0.38
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: aioresponses>=0.7; extra == "dev"
Dynamic: license-file

# pyzeptrion

[![PyPI version](https://img.shields.io/pypi/v/pyzeptrion)](https://pypi.org/project/pyzeptrion/)
[![Python](https://img.shields.io/pypi/pyversions/pyzeptrion)](https://pypi.org/project/pyzeptrion/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

An async Python library for controlling **Zeptrion** devices made by [Feller AG](https://www.feller.ch) (Schneider Electric). Supports lights (on/off and dimmable bulbs) and covers (blinds/shutters).

> For the implementation, I used the API documentation provided by Feller AG. Unfortunately, this documentation has been discontinued since March 2025.

---

## Supported devices

Tested with:
- [2-channel module](https://online-katalog.feller.ch/kat_details.php?fnr=3340-2-B.ZEP)
- [4-channel module](https://online-katalog.feller.ch/kat_details.php?fnr=3340-4-B.FMI.61)

The library implements the Zeptrion REST API (`/zrap/`) which is available on all Zeptrion Wi-Fi devices. Other Zeptrion products should work as well.

Device types detected automatically:

| API cat | Type |
|---------|------|
| 1 | `Bulb on/off` |
| 3 | `Bulb dimmable` |
| 5 / 6 | `Blind` |

---

## Requirements

- Python 3.10+
- One or more Zeptrion devices on your local network
- Consumers (bulbs, blinds) wired to the switch channels

---

## Installation

```bash
pip install pyzeptrion
```

---

## Quick start

All classes use `async`/`await`. Use `asyncio.run()` or an existing event loop.

### Discover devices on the network

```python
import asyncio
from pyzeptrion.discover import ZeptrionRegistry

async def main():
    registry = await ZeptrionRegistry.create_registry()
    for device in registry.devices:
        print(device)

asyncio.run(main())
```

Output example:
```
Host: 192.168.0.181  Channel: 1  dev_type: Bulb dimmable
Host: 192.168.0.185  Channel: 1  dev_type: Blind
...
```

### Control a dimmable bulb

```python
import asyncio
from pyzeptrion.bulb import ZeptrionBulb

async def main():
    bulb = await ZeptrionBulb.create("192.168.0.181", "1")

    await bulb.on()            # turn on
    await bulb.off()           # turn off
    await bulb.set_level(30)   # dim to 30%  (0–100)
    await bulb.set_level(100)  # full brightness
    await bulb.set_level(0)    # turn off via set_level

    print(bulb)
    await bulb.close()

asyncio.run(main())
```

> **Note on dimming:** The Zeptrion API does not expose the current brightness level — the state
> endpoint only returns on/off. `set_level()` uses a calibrated press-and-hold strategy: it ramps
> fully up to 100 %, then holds `dim_down` for a proportional duration based on the target level.
> The full travel time constant (`DIMMER_FULL_TRAVEL_TIME = 3.4 s`) was measured on real hardware
> and can be adjusted in `pyzeptrion/const.py` if your device travels faster or slower.

### Control a blind

```python
import asyncio
from pyzeptrion.blind import ZeptrionBlind

async def main():
    blind = await ZeptrionBlind.create("192.168.0.185", "1")

    await blind.move_open()    # open
    await blind.move_close()   # close
    await blind.stop()         # stop mid-movement

    await blind.close()

asyncio.run(main())
```

### Using an existing aiohttp session

All classes accept an optional `session` parameter so you can share a single `aiohttp.ClientSession` across multiple devices (useful for Home Assistant integrations):

```python
import asyncio
import aiohttp
from pyzeptrion.bulb import ZeptrionBulb

async def main():
    async with aiohttp.ClientSession() as session:
        bulb = await ZeptrionBulb.create("192.168.0.181", "1", session=session)
        await bulb.on()

asyncio.run(main())
```

---

## API reference

### `ZeptrionBulb`

| Method | Description |
|--------|-------------|
| `await ZeptrionBulb.create(host, chn, session=None)` | Factory — creates and initialises the object |
| `await bulb.on()` | Turn on |
| `await bulb.off()` | Turn off |
| `await bulb.dim_up()` | Start dimming up (press-and-hold) |
| `await bulb.dim_down()` | Start dimming down (press-and-hold) |
| `await bulb.set_level(target)` | Dim to `target` % (0–100); 0 turns the light off |
| `await bulb.get_state()` | Returns `True` (on) or `False` (off) |
| `await bulb.post_cmd(cmd)` | Send a raw command string to the device |
| `await bulb.close()` | Close the aiohttp session (only if owned by this object) |

Properties: `name`, `group`, `host`, `chn`, `dev_type`, `dev_id`, `state`

### `ZeptrionBlind`

| Method | Description |
|--------|-------------|
| `await ZeptrionBlind.create(host, chn, session=None)` | Factory — creates and initialises the object |
| `await blind.move_open()` | Open the blind |
| `await blind.move_close()` | Close the blind |
| `await blind.stop()` | Stop movement |
| `await blind.get_state()` | Returns `True` (open) or `False` (closed) |
| `await blind.close()` | Close the aiohttp session |

Properties: `name`, `group`, `host`, `chn`, `dev_type`, `dev_id`, `state`

### `ZeptrionRegistry`

| Method | Description |
|--------|-------------|
| `await ZeptrionRegistry.create_registry()` | Discovers all Zeptrion devices via Zeroconf (waits ~5 s) |
| `registry.devices` | List of `ZeptrionRegistryDevice` objects, sorted by type |

`ZeptrionRegistryDevice` has properties: `host`, `chn`, `dev_type`

---

## Command line interface

```bash
# Discover all devices on the network
python3 -m pyzeptrion.cli discover

# Turn a bulb on/off
python3 -m pyzeptrion.cli set -a 192.168.0.181 -c 1 -p on
python3 -m pyzeptrion.cli set -a 192.168.0.181 -c 1 -p off

# Dim a dimmable bulb to a specific level (0–100)
python3 -m pyzeptrion.cli set -a 192.168.0.181 -c 1 -p dim_to --level 30

# Control a blind
python3 -m pyzeptrion.cli set -a 192.168.0.185 -c 1 -p move_close
python3 -m pyzeptrion.cli set -a 192.168.0.185 -c 1 -p move_open
python3 -m pyzeptrion.cli set -a 192.168.0.185 -c 1 -p stop
```

Valid commands for bulbs: `on`, `off`, `toggle`, `dim_up`, `dim_down`, `dim_to` (requires `--level`)

Valid commands for blinds: `move_open`, `move_close`, `stop`

---

## Exceptions

| Exception | When raised |
|-----------|-------------|
| `ZeptrionConnectionError` | Network timeout or HTTP error communicating with the device |
| `ZeptrionError` | Base exception class |

---

## Home Assistant

This library is designed to be used as the backend for a Home Assistant custom integration. See the companion repository for details.

---

## Development

### Set up

```bash
git clone https://github.com/footprintsonthemoon/zeptrion_python.git
cd zeptrion_python
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

### Run the tests

```bash
pytest tests/
```

The test suite uses `pytest-asyncio` and `aioresponses` to mock all HTTP and Zeroconf calls — no real hardware is needed.

### Build and publish

```bash
# Build source distribution and wheel
python -m build

# Upload to PyPI (requires a PyPI account and API token)
twine upload dist/pyzeptrion-*
```

---

## License

MIT — see [LICENSE](LICENSE).
