From 29a7db028e4104bad44c29301b96fa6723923678 Mon Sep 17 00:00:00 2001 From: Jules Date: Tue, 23 Jun 2026 08:35:03 +0200 Subject: [PATCH] USB rebind Canon : unbind/rebind sysfs pour recovery hard DSLR PTP freeze Escalade progressive : ioctl reset (tentative 2), unbind/rebind USB (tentative 3+) Co-Authored-By: Claude Opus 4.6 --- backend/main.py | 46 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/backend/main.py b/backend/main.py index 6569c18..a6d0dc3 100644 --- a/backend/main.py +++ b/backend/main.py @@ -123,6 +123,30 @@ def _usb_reset_canon(): log.debug(f"_usb_reset_canon: {e}") +def _usb_rebind_canon(): + """Unbind/rebind USB Canon — simule un debranchement/rebranchement physique.""" + import glob + try: + for f in glob.glob("/sys/bus/usb/devices/*/idVendor"): + if open(f).read().strip() == "04a9": + dev_id = __import__("os.path", fromlist=["basename"]).basename( + __import__("os.path", fromlist=["dirname"]).dirname(f) + ) + log.info(f"USB rebind Canon: unbind {dev_id}") + with open("/sys/bus/usb/drivers/usb/unbind", "w") as fh: + fh.write(dev_id) + import time + time.sleep(2) + with open("/sys/bus/usb/drivers/usb/bind", "w") as fh: + fh.write(dev_id) + log.info(f"USB rebind Canon: bind {dev_id}") + time.sleep(3) + return True + except Exception as e: + log.warning(f"_usb_rebind_canon: {e}") + return False + + async def surveiller_dslr(): """Surveille le DSLR en continu : detecte les deconnexions et reconnecte automatiquement. Apres plusieurs echecs, fait un reset USB ioctl pour debloquer le port PTP.""" @@ -158,11 +182,20 @@ async def surveiller_dslr(): async def _reconnecter_dslr_avec_reset(echecs: int): - """Reconnexion DSLR avec reset USB progressif.""" + """Reconnexion DSLR avec escalade progressive : + 1-2 : ioctl reset + 3+ : unbind/rebind USB (simule debranchement physique) + """ camera.deconnecter() await diffuser_ws({"type": "camera_erreur", "message": "Appareil photo deconnecte"}) - if echecs >= 2: + if echecs >= 3: + log.info(f"USB rebind Canon (echec #{echecs})...") + ok = await asyncio.get_event_loop().run_in_executor(None, _usb_rebind_canon) + if not ok: + _usb_reset_canon() + await asyncio.sleep(4) + elif echecs >= 2: log.info(f"Reset USB Canon (echec #{echecs})...") _usb_reset_canon() await asyncio.sleep(4) @@ -176,15 +209,6 @@ async def _reconnecter_dslr_avec_reset(echecs: int): await diffuser_ws({"type": "camera_ok"}) else: log.warning(f"Echec reconnexion DSLR (tentative {echecs})") - if echecs >= 3: - log.info("Reset USB force supplementaire...") - _usb_reset_canon() - await asyncio.sleep(5) - camera.connecter(source="gphoto2") - if camera.connectee: - log.info("DSLR reconnecte apres double reset") - _appliquer_config_camera() - await diffuser_ws({"type": "camera_ok"}) def _appliquer_config_camera():