No description
  • Python 75.8%
  • Shell 24.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Eddy de Vink 8cc04ea409 feat: harden dashcam transfer tegen flakke SD-lezer + stale-mount cleanup
- dashcam-transfer.py: log echte OSError (shutil.copy2 i.p.v. cp), pre-flight
  leesbaarheidscheck voor ruimte-wissen, retry (3x) + early-abort (5x), guards
  op bestandssysteem-calls, WhatsApp GESTOPT bij defecte kaart
- dashcam-transfer-mount.sh: skip als service al draait, read-only fsck.exfat
  health check, %k device-naam doorgeven
- dashcam-transfer-cleanup.sh: nieuw - ruimt stale mount op bij udev REMOVE
- udev/99-dashcam-trigger.rules: ADD + REMOVE regels met %k
- tests: 20 python + 7 mount + 12 cleanup checks
- README: architectuur, robuustheid, deploy-instructies
2026-08-11 20:37:48 +02:00
docs/superpowers fix: corrigeer WHATSAPP_API URL naar localhost:3001 (nextcloud) 2026-06-23 11:04:39 +02:00
tests feat: harden dashcam transfer tegen flakke SD-lezer + stale-mount cleanup 2026-08-11 20:37:48 +02:00
udev feat: harden dashcam transfer tegen flakke SD-lezer + stale-mount cleanup 2026-08-11 20:37:48 +02:00
.gitignore feat: harden dashcam transfer tegen flakke SD-lezer + stale-mount cleanup 2026-08-11 20:37:48 +02:00
dashcam-transfer-cleanup.sh feat: harden dashcam transfer tegen flakke SD-lezer + stale-mount cleanup 2026-08-11 20:37:48 +02:00
dashcam-transfer-mount.sh feat: harden dashcam transfer tegen flakke SD-lezer + stale-mount cleanup 2026-08-11 20:37:48 +02:00
dashcam-transfer.py feat: harden dashcam transfer tegen flakke SD-lezer + stale-mount cleanup 2026-08-11 20:37:48 +02:00
README.md feat: harden dashcam transfer tegen flakke SD-lezer + stale-mount cleanup 2026-08-11 20:37:48 +02:00
restructure.py Uniformeer naar front/back i.p.v. front/rear (B = back, F = front) 2026-05-29 19:34:12 +02:00

Dashcam Transfer

Automatisch dashcam-opnames (MP4) overzetten van een SD-kaart naar een USB-SSD, met organisatie per datum en camera-positie (front/back). Draait op de nextcloud server.

Architectuur

SD-kaart insteken
  → udev detecteert vendor:product (14cd:1212 of 05e3:0751)
    → systemd-run start /usr/local/bin/dashcam-transfer-mount %k
      → skip als de transfer-service al draait (flaky lezer = geen 2e mount)
      → wacht tot /dev/sdX1 verschijnt (max 60s)
      → mount apparaat dynamisch op /mnt/sdcard (uid=1000, exfat)
      → read-only fsck.exfat health check (logt, blokkeert niet)
      → systemctl start dashcam-transfer.service
        → /usr/local/bin/dashcam-transfer
          1. Check mounts (/mnt/sdcard, /mnt/ultratouch)
          2. Scan /mnt/sdcard/Novatek/Movie/ op *.MP4
          3. Leesbaarheidscheck bron (voorkomt ruimte wissen bij defecte kaart)
          4. Check vrije ruimte — verwijder oudste datum-mappen indien nodig
          5. Copy + verify + delete per bestand, met retry
          6. Early-abort bij aanhoudende I/O-fouten (SD-kaart defect)
          7. Stuur WhatsApp notificatie via wwebjs-api

SD-kaart uittrekken
  → udev REMOVE trigger (zelfde vendor:product)
    → systemd-run start /usr/local/bin/dashcam-transfer-cleanup (%k)
      → stale mount? (device-node weg of source = verwijderd device)
        → stop dashcam-transfer.service
        → umount /mnt/sdcard (fallback: umount -l)

Robuustheid

Het script is ontworpen tegen de hierboven geziene productie-fout (defecte/flakke SD-kaartlezer die midden in de transfer offline klapt, I/O error, device offline):

  • Echte fout zichtbaar — kopieerfouten worden als OSError met reden gelogd (i.p.v. alleen "exit status 1"); de eerdere cp-stderr ging verloren.
  • Pre-flight leesbaarheidscheck — voordat oude datum-mappen op de SSD worden verwijderd, wordt eerst een bestand van de kaart gelezen. Een onleesbare kaart resulteert in een duidelijke WhatsApp-"GESTOPT" en niets wordt gewist.
  • Retry per bestand — 3 pogingen met 2s pauze; vangt transiënte USB-glitches.
  • Early-abort — bij 5 opeenvolgende fouten stopt het script en stuurt het een WhatsApp met de laatste fout. Bronbestanden worden nooit verwijderd zonder geverifieerde kopie.
  • Geen crash mid-loop — alle bestandssysteem-calls zijn afgeschermd.
  • fsck.exfat — na mount draait het mount-script een read-only health check; een dirty/defecte kaart wordt daarmee zichtbaar in syslog.
  • Geen dubbele runs — het mount-script skip als de transfer al actief is.
  • Stale-mount opruiming — als de kaartlezer wordt uitgetrokken (of de kaart offline klapt) ruimt een udev REMOVE-trigger de mount op: de service wordt gestopt en /mnt/sdcard netjes ontkoppeld (lazy als dat mislukt). Zo blijft er nooit een dood device in de mount-output hangen.

Hardware

Component Mount Device Doel
USB-SSD (4TB, Seagate Ultra Touch) /mnt/ultratouch /dev/disk/by-uuid/3027-8ECA Opslag
SD-kaart (via USB reader) /mnt/sdcard dynamisch via udev %k Bron

SSD wordt gemount via systemd .automount unit (permanent aangesloten). SD-kaart wordt gemount door dashcam-transfer-mount.sh na udev trigger, met het kernel-device (b.v. sde1) als argument — zo werkt het ongeacht het filesystem UUID.

Bestandsstructuur

SD-kaart (bron)

/mnt/sdcard/Novatek/Movie/
├── 20260622050346_0000001F.MP4    ← front camera
├── 20260622050346_0000002B.MP4    ← back camera
└── ...

Bestandsnaam: YYYYMMDDHHMMSS_<sequentie><kamera>.MP4

  • F = front, B = back
  • ~381 MB per clip (~5 minuten)

SSD (doel)

/mnt/ultratouch/dashcam/
└── YYYYMMDD/
    ├── front/
    │   └── ...F.MP4
    └── back/
        └── ...B.MP4

WhatsApp notificatie

Na elke transfer stuurt het script een WhatsApp bericht naar de beheerder:

✅ Dashcam transfer voltooid: 164 bestanden (56.8 GB) overgezet, geen fouten

De API (wwebjs-api) draait als Docker container op nextcloud zelf:

Config Waarde
API endpoint http://localhost:3001/client/sendMessage/EdGpt
Sessie EdGpt
Ontvanger 31620248011@c.us (privé chat Eddy)

De container: docker-compose.yml in /home/aiaiaiaiagent/wwebjs-api/.

Service & triggers

Udev rule (/etc/udev/rules.d/99-dashcam-trigger.rules)

ACTION=="add", SUBSYSTEM=="block", KERNEL=="sd*[0-9]", ENV{ID_VENDOR_ID}=="14cd", ENV{ID_MODEL_ID}=="1212", RUN+="/usr/bin/systemd-run --no-block --collect /usr/local/bin/dashcam-transfer-mount %k"
ACTION=="add", SUBSYSTEM=="block", KERNEL=="sd*[0-9]", ENV{ID_VENDOR_ID}=="05e3", ENV{ID_MODEL_ID}=="0751", RUN+="/usr/bin/systemd-run --no-block --collect /usr/local/bin/dashcam-transfer-mount %k"
ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd*[0-9]", ENV{ID_VENDOR_ID}=="14cd", ENV{ID_MODEL_ID}=="1212", RUN+="/usr/bin/systemd-run --no-block --collect /usr/local/bin/dashcam-transfer-cleanup %k"
ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd*[0-9]", ENV{ID_VENDOR_ID}=="05e3", ENV{ID_MODEL_ID}=="0751", RUN+="/usr/bin/systemd-run --no-block --collect /usr/local/bin/dashcam-transfer-cleanup %k"

%k geeft het kernel device (b.v. sde1) door aan het mount- en cleanup-script, zodat de kaart dynamisch gemount wordt ongeacht het filesystem UUID. Na wijziging: sudo udevadm control --reload-rules.

Mount script (/usr/local/bin/dashcam-transfer-mount.sh)

Wacht tot het doorgegeven device beschikbaar is, unmount eventuele stale mounts, mount de SD-kaart en start de service.

Cleanup script (/usr/local/bin/dashcam-transfer-cleanup.sh)

Draait op udev REMOVE-trigger. Checkt of /mnt/sdcard een stale mount is (device-node weg of gemount op een verwijderd device) en ruimt hem dan op: service stoppen, umount (fallback umount -l). Bij een gezonde mount (ander device) doet het niets.

Systemd service (/etc/systemd/system/dashcam-transfer.service)

[Unit]
Description=Dashcam SD card transfer
After=mnt-sdcard.mount

[Service]
Type=oneshot
ExecStart=/usr/local/bin/dashcam-transfer
User=root

Logging

Alle activiteit wordt gelogd naar /tmp/dashcam-transfer.log op nextcloud.

Handmatig gebruik

# Alle bestanden overzetten
/usr/local/bin/dashcam-transfer

# Beperkt tot eerste N bestanden (testen)
/usr/local/bin/dashcam-transfer --limit 10

Ontwikkeling

Lokale repo: ~/src/dashcam-transfer/ op Mac Mini. Tests: python3 -m unittest discover -s tests, ./tests/test_mount_script.sh en ./tests/test_cleanup_script.sh. Deployen naar nextcloud:

scp dashcam-transfer.py aiaiaiaiagent@nextcloud:/tmp/
ssh aiaiaiaiagent@nextcloud 'sudo -A cp /tmp/dashcam-transfer.py /usr/local/bin/dashcam-transfer'
scp dashcam-transfer-mount.sh aiaiaiaiagent@nextcloud:/tmp/
ssh aiaiaiaiagent@nextcloud 'sudo -A cp /tmp/dashcam-transfer-mount.sh /usr/local/bin/dashcam-transfer-mount'
scp dashcam-transfer-cleanup.sh aiaiaiaiagent@nextcloud:/tmp/
ssh aiaiaiaiagent@nextcloud 'sudo -A cp /tmp/dashcam-transfer-cleanup.sh /usr/local/bin/dashcam-transfer-cleanup'
scp udev/99-dashcam-trigger.rules aiaiaiaiagent@nextcloud:/tmp/
ssh aiaiaiaiagent@nextcloud 'sudo -A cp /tmp/99-dashcam-trigger.rules /etc/udev/rules.d/ && sudo -A udevadm control --reload-rules'

Controleer na deploy altijd met sudo sha256sum dat de hash op de server gelijk is aan de lokale shasum -a 256.