- 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
184 lines
7.4 KiB
Markdown
184 lines
7.4 KiB
Markdown
# 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`)
|
|
|
|
```udev
|
|
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`)
|
|
|
|
```ini
|
|
[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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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`.
|