Metadata-Version: 2.5
Name: pyneosol
Version: 0.4.2
Summary: Python library for 868 MHz roller shutter USB dongles speaking the PFX AT serial protocol. Compatible with Profalux Neosol roller shutters and the MAI-DONGLE868-1A.
Project-URL: Homepage, https://github.com/bbayszczak/pyneosol
Project-URL: Repository, https://github.com/bbayszczak/pyneosol
Project-URL: Specification, https://github.com/bbayszczak/pyneosol/blob/main/docs/SPEC-PROTOCOLE-AT.md
Author: Benoit Bayszczak
License-Expression: MIT
License-File: LICENSE
Keywords: 868mhz,calypshome,home-automation,keeloq,neosol,profalux,rolling-shutter
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Home Automation
Classifier: Typing :: Typed
Requires-Python: >=3.13
Requires-Dist: pyserial>=3.5
Description-Content-Type: text/markdown

# pyneosol

[![CI](https://img.shields.io/github/actions/workflow/status/bbayszczak/pyneosol/ci.yml?branch=main&label=CI)](https://github.com/bbayszczak/pyneosol/actions/workflows/ci.yml)
[![Version](https://img.shields.io/github/v/release/bbayszczak/pyneosol?label=version)](https://github.com/bbayszczak/pyneosol/releases/latest)
[![Python](https://img.shields.io/badge/python-3.13%2B-blue)](https://www.python.org/downloads/)
[![Licence](https://img.shields.io/badge/licence-MIT-green)](LICENSE)

Bibliothèque Python pour les dongles USB 868 MHz utilisant le protocole série AT « PFX ».
**Compatible avec** les volets roulants Profalux Neosol et le `MAI-DONGLE868-1A`.
Entièrement locale : ni box Calyps'HOME, ni cloud.

> ⚠️ **PROJET INDÉPENDANT, SANS AUCUNE AFFILIATION**
>
> `pyneosol` n'est **en aucun cas** affilié, soutenu, approuvé ou validé par Profalux, Stella
> Advanced Technology, ou l'une quelconque de leurs filiales, marques, sociétés apparentées,
> sous-traitants ou partenaires. *Profalux*, *Stella Advanced Technology*, *Neosol* et
> *Calyps'HOME* sont des marques de leurs titulaires respectifs, citées uniquement pour décrire
> le matériel avec lequel cette bibliothèque communique, à des fins d'interopérabilité.
>
> 👉 **Lisez impérativement l'avertissement complet ci-dessous avant toute utilisation.** Il
> couvre le **risque matériel** — certaines commandes du dongle sont destructives et peuvent
> effacer tous vos appairages —, l'**absence totale de garantie** et les conditions d'usage.
> En utilisant cette bibliothèque, vous reconnaissez les avoir lues et acceptées.

<details>
<summary><strong>⚠️ Avertissement complet — à lire avant toute utilisation</strong></summary>

**Ce projet est totalement indépendant et n'est en aucun cas affilié, soutenu, approuvé
ou validé par Profalux, Stella Advanced Technology, ou l'une quelconque de leurs
filiales, marques, sociétés apparentées, sous-traitants ou partenaires.**

*Profalux*, *Stella Advanced Technology*, *Neosol*, *NeosoL* et *Calyps'HOME* sont des
marques de leurs titulaires respectifs. Elles ne sont citées ici que pour **décrire le
matériel avec lequel cette bibliothèque est susceptible de communiquer**, à des fins
d'interopérabilité. Aucun code, aucun binaire, aucun micrologiciel, aucune clé
cryptographique et aucune documentation du fabricant n'est reproduit ni redistribué
dans ce dépôt.

**Nom du projet** — `pyneosol` est un nom d'usage choisi pour sa lisibilité. Il
n'emporte aucune affiliation, ne constitue ni une marque, ni une revendication
d'origine, ni une autorisation du titulaire de la marque *Neosol*. Cette bibliothèque
est un composant tiers **compatible avec** ce matériel, et rien d'autre.

**Méthode** — le protocole documenté ici a été reconstitué par la seule observation du
dialogue série avec un dongle acquis légalement, sur une installation appartenant à
l'auteur. Aucune décompilation de micrologiciel, aucune extraction ni publication de
clé cryptographique constructeur n'a été réalisée. Ce travail relève de l'exception
d'interopérabilité (art. L122-6-1 III et IV du Code de la propriété intellectuelle,
directive 2009/24/CE art. 5 et 6).

**Usage** — cette bibliothèque est destinée au pilotage d'équipements dont vous êtes
propriétaire ou légitime utilisateur, et à eux seuls.

**Absence de garantie** — ce logiciel est fourni « tel quel », sans aucune garantie
d'aucune sorte, expresse ou implicite, y compris, sans s'y limiter, les garanties de
qualité marchande, d'adéquation à un usage particulier et d'absence de contrefaçon.
Dans les limites permises par le droit applicable, l'auteur ne saurait être tenu
responsable d'un quelconque dommage : dysfonctionnement, détérioration de matériel,
perte de configuration ou d'appairage, perte de données, ou tout dommage direct ou
indirect résultant de l'utilisation de cette bibliothèque.

**Risque matériel** — certaines commandes du dongle sont **destructives** et peuvent
effacer ses appairages (voir la section *Commandes dangereuses* des
[spécifications](docs/SPEC-PROTOCOLE-AT.md)). Sauvegardez la configuration de votre
dongle avant toute expérimentation.

**Garantie constructeur** — l'usage de ce logiciel avec votre matériel est susceptible
d'en affecter la garantie. Vérifiez-le avant de l'utiliser.

**Support** — assuré bénévolement, sans engagement de délai ni de résultat.

**Licence** — MIT, voir [LICENSE](LICENSE).

En utilisant cette bibliothèque, vous reconnaissez avoir lu et accepté l'ensemble de
ces conditions.

</details>

---

## Sommaire

- [Démarrage rapide](#démarrage-rapide)
- [Matériel](#matériel)
- [État du projet](#état-du-projet)
- [Installation](#installation)
- [Utilisation](#utilisation)
- [Logging](#logging)
- [Développement](#développement)
- [Contribuer](#contribuer)
- [Sécurité](#sécurité)

---

## Démarrage rapide

Avec [`uv`](https://docs.astral.sh/uv/) et le dongle branché :

```bash
git clone https://github.com/bbayszczak/pyneosol
cd pyneosol
uv run demo.py
```

`uv` crée l'environnement et installe les dépendances tout seul. Sans argument, `demo.py`
**n'émet rien** : il identifie le dongle et liste les canaux utilisés — de quoi vérifier en
quelques secondes que votre matériel est reconnu.

---

## Matériel

Cette bibliothèque est **compatible avec** le dongle USB 868 MHz distribué pour les volets
roulants Profalux Neosol. Elle n'est ni fournie, ni distribuée, ni approuvée par le fabricant.

Elle **fonctionne avec le dongle photographié ci-dessous**, le `MAI-DONGLE868-1A`, sur lequel
tout le protocole a été validé.

<!-- Photos du dongle de l'auteur : métadonnées supprimées et numéro de série masqué à la
     source. Toute image ajoutée ici doit conserver ces deux propriétés. -->
<p align="center">
  <img src="docs/images/dongle-mai-dongle868-1a-face.jpg"
       alt="Dongle USB Neosol vu de face" width="190">
  &nbsp;&nbsp;&nbsp;
  <img src="docs/images/dongle-mai-dongle868-1a-etiquette.jpg"
       alt="Étiquette du dongle : Dongle Neosol, MAI-DONGLE868-1A, Lot 25/22, 5V 0.15 W"
       width="190">
  <br>
  <em>Le dongle validé, de face et côté étiquette. Le numéro de série est masqué.</em>
</p>

| | |
|---|---|
| **Modèle validé** | `MAI-DONGLE868-1A` |
| **Identification interne** | `PFX KEELOQ` |
| **Hardware Version** | `0` |
| **Software Version** | `Rev10` |
| **Interface** | USB CDC-ACM (port série virtuel) |
| **Débit** | 115200 bauds |

> ℹ️ **`MAI-DONGLE868-1A` est la seule référence sur laquelle le protocole a été validé**, et
> la seule dont dispose l'auteur. L'existence et le comportement d'éventuelles autres
> références ne sont pas connus : rien n'est vérifié ni garanti sur une autre référence, une
> autre version matérielle ou une autre révision logicielle. Les retours sur d'autres modèles
> sont les bienvenus.

### Identifier votre dongle

Avant d'ouvrir une issue, indiquez toujours **la référence de votre dongle ainsi que ses
versions matérielle et logicielle**. Elles s'obtiennent avec la commande AT `AT&V`.

**1. Repérer le port série**

```bash
# Linux
ls -l /dev/ttyACM*

# macOS
ls -1 /dev/cu.usbmodem*
```

**2. Interroger le dongle**

```bash
python3 -c "
import serial, time
ser = serial.Serial('/dev/ttyACM0', 115200, timeout=1)   # adapter le port
time.sleep(0.4)
ser.write(b'AT&V\r\n'); ser.flush()
time.sleep(1.5)
print(ser.read(ser.in_waiting).decode('utf-8', 'replace'))
ser.close()
"
```

**3. Sortie attendue**

```
PFX KEELOQ

Hardware Version:  0

Software Version: Rev10

S/N: XXXXXXXX

ACTIVE CONFIG :

Return Code Active : 1

Frame Repeat Nb : T0=25,T1=15,T2=70,T3=70

Read Protection Active : 0

AT&V:OK
```

La ligne `PFX KEELOQ` confirme qu'il s'agit bien d'un dongle de cette famille. `Hardware
Version` et `Software Version` sont les deux valeurs à communiquer en cas de problème.

> 🔐 Le numéro de série (`S/N`) identifie votre exemplaire : inutile de le publier.

---

## État du projet

🚧 **Alpha.** Le pilotage fonctionne et le protocole est documenté dans
[`docs/SPEC-PROTOCOLE-AT.md`](docs/SPEC-PROTOCOLE-AT.md). L'API peut encore changer.

Les changements de chaque version sont consignés dans le [CHANGELOG](CHANGELOG.md), tenu à jour
automatiquement à partir des messages de commit.

---

## Installation

Le paquet est publié sur [PyPI](https://pypi.org/project/pyneosol/) :

```bash
pip install pyneosol
```

Avec [`uv`](https://docs.astral.sh/uv/) :

```bash
uv add pyneosol
```

## Utilisation

```python
from pyneosol import Dongle

with Dongle.open() as dongle:  # détection automatique du port
    print(dongle.info().software_version)

    for channel in dongle.used_channels():
        print(channel)  # la clé n'est jamais affichée

    dongle.close_shutter(0)  # descente
    dongle.stop(0)  # arrêt en cours de course
    dongle.favourite(0)  # position favorite
```

Le port peut aussi être imposé : `Dongle.open("/dev/ttyACM0")`.

> ⚠️ **Aucun retour d'état.** Le dongle ne fait qu'émettre. Une commande acceptée signifie
> qu'une trame est partie, jamais qu'un volet a bougé, et aucune position n'est lisible.
> Toute notion d'état ou de position relève de la couche appelante.

### Essayer sans écrire de code

Le dépôt fournit un script de démonstration à sa racine :

```bash
uv run demo.py                           # identification et table des canaux
uv run demo.py --port /dev/ttyACM0       # forcer le port série
uv run demo.py --channel 2 --close       # descente, avec confirmation
uv run demo.py --channel 2 --stop --yes  # stop, sans confirmation
uv run demo.py --debug                   # afficher le dialogue AT (voir Logging)
```

Sans argument, il **n'émet rien** : il se contente d'identifier le dongle et de lister les
canaux utilisés. C'est le moyen le plus rapide de vérifier que votre dongle est reconnu.

Les actions (`--open`, `--close`, `--stop`, `--favourite`) doivent être demandées explicitement
et déclenchent une confirmation, puisqu'elles déplacent un volet réel. Les clés ne sont jamais
affichées.

Exemple de sortie (valeurs factices) :

```
Looking for a dongle...
  /dev/ttyACM0  PROFALUX / KEELOQ USB Device

Dongle
  hardware version : 0
  software version : Rev10
  frame repeat     : T0=25,T1=15,T2=70,T3=70
  read protection  : no
  transmit power   : 14

Channels: 50 total, 2 used
  channel  0  serial 000AAAA1  sync 43
  channel  1  serial 000AAAA2  sync 14

Read-only run: nothing was transmitted.
```

## Logging

La bibliothèque utilise le module `logging` standard et **ne configure rien** : ni handler, ni
niveau, ni format. C'est l'application hôte qui décide. Chaque module a son logger, nommé
d'après le paquet — `pyneosol.dongle`, `pyneosol.discovery` — ce qui permet de filtrer au
paquet entier comme au module.

Tout est en `DEBUG` : le dialogue AT (commande émise, réponse, durée) et la détection du port.
Les erreurs ne sont pas loguées, elles sont **levées** ; c'est à l'appelant de décider ce qu'il
en fait.

Dans Home Assistant, via `configuration.yaml` :

```yaml
logger:
  logs:
    pyneosol: debug          # tout
    pyneosol.dongle: debug   # seulement le dialogue AT
```

En script autonome :

```python
import logging

logging.basicConfig(level=logging.DEBUG)
```

Exemple de trace (valeurs factices) :

```
DEBUG pyneosol.discovery: 1 of 4 serial ports match 10C4:0003: ['/dev/ttyACM0']
DEBUG pyneosol.dongle: opening /dev/ttyACM0
DEBUG pyneosol.dongle: > AT$C?
DEBUG pyneosol.dongle: < ['0,***,0029, ***', '1,***,0009, ***', 'AT$C:OK'] (0.212s)
DEBUG pyneosol.dongle: > AT$SF=0,1
DEBUG pyneosol.dongle: < ['AT$SF:OK'] (0.004s)
```

> ✅ **Aucun secret n'entre dans les logs.** Le masquage joue **dans les deux sens** : les
> réponses du dongle comme les commandes envoyées. Les clés KeeLoq et les numéros de série
> deviennent `***`, tandis que l'index du canal et son compteur `sync` restent lisibles. Une
> trace `DEBUG` peut donc être jointe telle quelle à un rapport de bug.
>
> Le sens commande compte parce que `Dongle.execute()` accepte des commandes brutes : une
> `AT$C=` ou une `AT$SN=` formée à la main porte son secret dans la commande elle-même. Elle
> est loguée `AT$C=0,***,0029,***`. Les messages d'exception suivent la même règle.

## Développement

```bash
uv run ruff check .      # lint
uv run ruff format .     # formatage
uv run pytest            # tests — aucun matériel requis, le dongle est simulé
```

---

## Contribuer

Les retours sont bienvenus, en particulier sur **d'autres références de dongle** que le
`MAI-DONGLE868-1A`, seule référence validée à ce jour.

- **Signaler un bug ou proposer une évolution** —
  [ouvrir une issue](https://github.com/bbayszczak/pyneosol/issues/new/choose), en précisant la
  référence de votre dongle et ses versions matérielle et logicielle
  (voir [Identifier votre dongle](#identifier-votre-dongle)).
- **Proposer du code** — périmètre, conventions et interdits sont décrits dans
  [CONTRIBUTING.md](CONTRIBUTING.md), à lire avant d'ouvrir une pull request.

---

## Sécurité

Le dongle stocke les **clés KeeLoq** de vos volets, lisibles en clair via la commande
`AT$C?`. Quiconque les possède peut commander vos volets.

Pour signaler une faille, utilisez le [signalement privé](SECURITY.md) — jamais une issue
publique.

**Ne publiez jamais le contenu réel de votre table de canaux** — ni dans une issue, ni dans un
rapport de bug, ni dans un export de configuration. Masquez systématiquement les clés et les
numéros de série avant tout partage.
