capy entre appareils du même wifi, sans connaître les IP. Le serveur s'annonce sous un nom ("maison", "salon"…), le client l'appelle par ce nom. Pratique pour un Raspberry Pi, un 2e ordi, une manette faite maison, un jeu en réseau local…
Sur l'appareil qui sert : crée un capy.wifi.Server("un-nom") et ajoute des actions, comme avec capy.
Lance-le : il affiche son nom et son IP sur le wifi.
Sur n'importe quel appareil du même wifi (ou le même) : capy.wifi.call("un-nom", "action").
serveur.py
import capy.wifi
api = capy.wifi.Server("maison") # le port est choisi tout seul@api.action("temperature")
deftemperature():
return {"salon": 20.5}
api.run()
résultat
Visible sur le wifi sous le nom 'maison' (192.168.1.42)
API en écoute sur le port 51234, chiffrée (TLS) · empreinte : 9dfa7b98c1e0…
Pas de magie : au 1er appel, le client crie sur tout le wifi « il y a des serveurs capy ? » (un petit message UDP sur le port 38790). Chaque serveur répond avec son nom, son port et s'il est chiffré. Le client retient la réponse : les appels suivants vont directement au bon endroit, sans redemander.
échanges
client → tout le wifi : capy?
maison → client : {"name": "maison", "port": 51234, "tls": true}
client → maison : {"action": "temperature"} (chiffré, en TCP, comme capy)
💡Le serveur a redémarré ?
Il a sûrement changé de port. Pas grave : si l'appel échoue, call refait un scan et retrouve le serveur tout seul.
💡Avec un fichier capy.config.json
{"name": "maison", "mode": "wifi"} dans un capy.config.json, puis capy.project().server() et capy.project().call("temperature") : le nom n'est écrit qu'à un seul endroit.
Comme capy : tout est chiffré, et l'empreinte d'un serveur est retenue sous le nom wifi:<nom> dans ~/.capy/known_hosts.json. Un serveur en clair se crée avec tls=False, et s'appelle aussi avec tls=False :
python
api = capy.wifi.Server("maison", tls=False)
capy.wifi.call("maison", "temperature", tls=False)
N'importe quel appareil connecté au même wifi peut trouver ton serveur et appeler ses actions. Rien de dangereux sans mot de passe !
🪤« aucun serveur sur le wifi » alors qu'il tourne
Vérifie que les deux appareils sont sur le même wifi (pas l'un en 4G, ou sur le wifi invité).
Beaucoup de wifi publics (école, hôtel, gare) isolent les appareils entre eux : capy.wifi ne peut rien y faire.
Sur Mac, au 1er lancement, macOS demande d'accepter les connexions entrantes pour Python : réponds oui.
Un pare-feu peut bloquer le port UDP 38790 : il doit laisser passer les scans.
💡Toujours le même port
capy.wifi.Server("maison", port=4000) : pratique si tu veux aussi l'appeler avec capy.call(ip, 4000, ...).
💡Plusieurs serveurs sur le même appareil
Aucun problème : chacun a son nom et son port, et ils répondent tous aux scans.
Et pour joindre ma maison depuis l'extérieur ?
capy.wifi ne sort pas du wifi. Pour passer par internet, utilise capy.network avec un relais.
Exemples
capy.wifi : se trouver par son nom sur le même wifi
Pas besoin de connaître l'IP ni le port : le serveur s'annonce sous un nom. Lance serveur.py sur un appareil, client.py sur un autre du même wifi (ou le même).
serveur.py
import capy.wifi
api = capy.wifi.Server("salon") # Port choisi tout seul@api.action("lumiere")
deflumiere(allumer=True):
etat = "allumée"if allumer else"éteinte"return {"lumiere": etat}
api.run()
Le client cherche "salon" sur le wifi puis l'appelle
scan() demande à tout le wifi « il y a des serveurs capy ? » et attend les réponses pendant 1 seconde. Lance quelques capy.wifi.Server ailleurs (ou l'exemple 5) pour voir quelque chose.
scanner_le_wifi.py
import capy.wifi
serveurs = capy.wifi.scan(timeout=1)
ifnot serveurs:
print("Aucun serveur capy.wifi trouvé (même wifi ? wifi public qui isole les appareils ?)")
for nom, (ip, port) insorted(serveurs.items()):
ici = " (cet appareil)"if ip == "127.0.0.1"else""print(f"{nom:<15} {ip}:{port}{ici}")
# maison 192.168.1.42:51234# salon 127.0.0.1:50110 (cet appareil)
Un projet complet : capy.config.json
On décrit le projet une fois (nom, version, port, adresse, chiffrement, fichiers…) ; serveur et client le lisent avec capy.project(), et cy lance ses scripts et ses fichiers (comme npm).
1. Créer le projet (modes : local, wifi ou network), puis tout lancer d'un coup
creer_le_projet.sh
capy new mon-jeu
cd mon-jeu
cy # Voir ce que capy a compris
# mon-jeu v1.0
# mode local (localhost:4000)
# tls oui (chiffré)
# server server.py ✓
# client client.py ✓
cy run # Serveur en fond + client, puis arrêt du serveur
# server │ API en écoute sur le port 4000, chiffrée (TLS) · empreinte : …
# Bonjour toi !
cy server # Ou chacun dans son terminal
cy client
2. Le serveur : pas de port écrit dans le code, il vient du fichier
serveur.py
import capy
app = capy.project() # Lit capy.config.json (à côté du script, ou dans un dossier au-dessus)
server = app.server() # capy.Server, capy.wifi.Server ou capy.network.Server selon "mode"@server.action("hello")
defhello(nom="monde"):
return {"message": f"Bonjour {nom} !"}
server.run()
3. Le client : pas d'IP, pas de port, pas de relais
Lancer le serveur, et répondre aux scans du wifi pour être trouvé
Voir le code
python
defrun(self, port=None):
threading.Thread(target=self._announce, daemon=True).start()
print(f"Visible sur le wifi sous le nom {self.name!r} ({_local_ip()})")
super().run(port)
defscan(timeout=1, _wanted=None):
found, me = {}, _local_ip()
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
end = time.monotonic() + timeout
next_ask = 0while time.monotonic() < end:
# On redemande 3 fois : en wifi, un message UDP peut se perdreif time.monotonic() >= next_ask:
for target in ("255.255.255.255", "127.0.0.1"): # Tout le wifi + cet appareil (même sans wifi)try:
s.sendto(b"capy?", (target, DISCOVERY_PORT))
exceptOSError:
pass
next_ask = time.monotonic() + timeout / 3
s.settimeout(max(min(end, next_ask) - time.monotonic(), 0.001))
try:
data, (ip, _) = s.recvfrom(1024)
info = jsonlib.loads(data)
name, port = info["name"], int(info["port"])
except socket.timeout:
continueexcept (OSError, ValueError, KeyError, TypeError):
continue# Serveur sur cet appareil -> 127.0.0.1 (20x plus rapide que passer par l'IP du wifi)
found.setdefault(name, ("127.0.0.1"if ip == me else ip, port))
_secure.setdefault(name, info.get("tls") isTrue)
if name == _wanted: # find() : on a ce qu'on cherche, inutile d'attendrebreak
_found.update(found)
return found
deffind(name, timeout=1):
if name notin _found:
found = scan(timeout, _wanted=name)
if name notin found:
raiseConnectionError(f"capy.wifi : aucun serveur {name!r} sur le wifi (trouvés : {', '.join(found) or 'aucun'})")
return _found[name]