#!/bin/bash # getQrSpcCam - Get QR - Swiss Payments Code - using zbarcam. # Version: 0.31.004 -- Last-Update: 2023 17:16:41 CET # (C) 2019-2022 Felix Hauri - felix@f-hauri.ch # Licensed under terms of LGPL v3. www.gnu.org # - Every time zbarcam send data, this script play a song with `ogg123`. # - store 4 fields from all scans in CSV file, and # - store QR and raw text in two files .png and .txt. # png files are small and could be used in many payment platforms. # - send last QR scanned to clipboard, ready to paste into payment platforms. # # Interactively: # hit `a` to add scanned qr in csv and folder, # hit `c` to review csv content, # hit `q` for quit. # hit `r` to Review scanned list. # Automatically: # run with `-a` flag, all success scan will add result in folder and csv. # # REQUIRE: zbarcam (V >= 0.23.1), wmctrl, xdotool, ncurses-bin, ogg123, qrencode # shellcheck disable=SC2059 # format "$filename" and "$qrfilename" shopt -s extglob autoAdd=false ; [[ $1 = -a ]] && autoAdd=true && shift #scanSnd=/usr/share/sounds/KDE-KStars-Sys-App-Message.ogg #readySnd=/usr/share/sounds/KDE-KStars-Finish-Success.ogg scanSnd=/usr/share/sounds/gnome/default/alerts/drip.ogg readySnd=/usr/share/sounds/gnome/default/alerts/glass.ogg { [ -f "$scanSnd" ] && [ -f "$readySnd" ] ;} || { read -r readySnd;read -r scanSnd }< <( find /usr/share/sounds -type f -size +55 -name '*.ogg' \ -exec ls -1trS {} + ) printf -v filename 'scanQr-%(%F)T-%%05d.txt' -1 printf -v qrfilename 'scanQr-%(%F)T-%%05d.png' -1 printf -v csvfilename 'scanQr-%(%F)T.csv' -1 declare -i filenum=1 total=0 lastAdded='' wholeList=() csv=() res='' while printf -v file "$filename" $filenum && [ -f "$file" ] ;do ((filenum+=1)) ;done die() { echo >&2 "ERROR ${0#*/}: $*" exit 1 } case $LANG in fr* ) fields=( $'\303\211l\303\251ment QR' Version 'Coding Type' IBAN $'Type d\342\200\231adresse' Nom $'Rue ou ligne d\342\200\231adresse 1' $'N\302\260 ou ligne d\342\200\231adresse 2' $'Num\303\251ro postal d\342\200\231acheminement' $'Localit\303\251' Pays $'Type d\342\200\231adresse' Nom $'Rue ou ligne d\342\200\231adresse 1' $'N\302\260 ou ligne d\342\200\231adresse 2' $'Num\303\251ro postal d\342\200\231acheminement' $'Localit\303\251' Pays Montant Devise $'Type d\342\200\231adresse' Nom $'Rue ou ligne d\342\200\231adresse 1' $'N\302\260 ou ligne d\342\200\231adresse 1' $'Num\303\251ro postal d\342\200\231acheminement' $'Localit\303\251' Pays $'Type de r\303\251f\303\251rence' $'R\303\251f\303\251rence' $'Notification non structur\303\251e' Trailer 'Informations de facturation' $'Param\303\250tre de proc\303\251dure alternative' ) ;; de* ) fields=( 'QR Type' Version 'Coding Type' IBAN Adress-Typ Name 'Strasse oder Adresszeile 1' 'Hausnummer oder Adresszeile 2' Postleitzahl Ort Land Adress-Typ Name 'Strasse oder Adresszeile 1' 'Hausnummer oder Adresszeile 2' Postleitzahl Ort Land Betrag $'W\303\244hrung' Adress-Typ Name 'Strasse oder Adresszeile 1' 'Hausnummer oder Adresszeile 2' Postleitzahl Ort Land Referenztyp Referenz 'Unstrukturierte Mitteilung' Trailer Rechnungsinformationen 'Alternatives Verfahren Parameter' ) ;; * ) fields=( QRType Version 'Coding Type' IBAN 'Address type' Name 'Street or address line 1' 'Building number or address line 2' 'Postal code' Town Country 'Address type' Name 'Street or address line 1' 'Building number or address line 2' 'Postal code' Town Country Amount Currency 'Address type' Name 'Street or address line 1' 'Building number or address line 1' 'Postal code' Town Country 'Reference type' Reference 'Unstructured message' Trailer 'Bill information' 'Alternative procedure parameter' ) ;; esac oggPlayer=$(which ogg123) [ "$oggPlayer" ] || die "Can't find 'ogg123' tool." QRScanner=$(which zbarcam) [ "$QRScanner" ] || die "Can't find 'zbarcam' tool." [ -x "$oggPlayer" ] || die "Can't execute '$oggPlayer'." [ -x "$QRScanner" ] || die "Can't execute '$QRScanner'." [ -r "$readySnd" ] || die "Can't access '$readySnd'." [ -r "$scanSnd" ] || die "Can't access '$scanSnd'." check4QRScanner() { local maj min rev IFS=$'.\n' read -r maj min rev < <($QRScanner --version) (( ( maj > 0 ) || ( min > 23 ) || ( min = 23 && rev >= 1) )) && return die "Require zbar-tools V >= 0.23.1 for correct binary scan." } getOneQr() { local -n _gC_res=${1:-QRCode} local _gC_line IFS= read -ru "$scan" _gC_res _gC_res+=$'\n' while IFS= read -u "$scan" -rt .03 _gC_line; do _gC_res+="$_gC_line"$'\n' done _gC_res="${_gC_res%$'\n'}" } strU8DiffLen() { local chLen=${#1} LANG=C LC_ALL=C;return $((${#1}-chLen));} dumpCrtStr() { local cols leftbar rightbar value field colorpair=('38;5;24' '48;5;248;38;5;24' '47;38;5;24;1') read -r cols < <(tput cols) local -i fcnt=0 cfld vl printf -v leftbar %*s 34 '' printf -v rightbar %*s $((cols-37)) '' leftbar=${leftbar// /\\U2500} rightbar=${rightbar// /\\U2500} printf '%b%b%b%b%b' '\U250C' "$leftbar" '\U252c' "$rightbar" \\U2510 fcnt=0 while read -ru "$vl" value ;do field=${fields[fcnt++]} case $fcnt in 5 | 12 | 19 | 21 | 28 ) printf '%b%b%b%b%b' \\U251C "$leftbar" \\U253c "$rightbar" \\U2524;; esac strU8DiffLen "$field" cfld=$? strU8DiffLen "$value" printf "\\U2502%-*s\\U2502 \e[%sm%-*s\\U2502\n" $((34+cfld)) "$field" \ "${colorpair[ fcnt==29 ? 2 : fcnt==19 ? 1 : fcnt==6 ? 1 : 0 ]}" \ $((cols-34+$?)) "${value/$'\r'}"$'\e[0m' done {vl}<<<"$res" printf '%b%b%b%b%b' \\U2570 "$leftbar" \\U2534 "$rightbar" \\U2518 } addThis() { local key values montant ttlstr [ "$res" ] || return if $added;then IFS= read -rsn 1 -p "Already added! Sure? (yN): " key [[ ${key,} = y ]] || return fi mapfile -t values <<<"${res//$'\r'}" printf -v montant %.2f "${values[18]}" total+="10#${montant/.}" ttlstr=0${total} strU8DiffLen "${values[5]}" printf -v lastAdded '%3d - %-*s %9.2f | %11.2f' $filenum \ $((48+$?)) "${values[5]}" "$montant" "${ttlstr::-2}.${ttlstr: -2}" printf -v file "$filename" $filenum [ -f "$file" ] && { echo >&2 "Error: file '$file' already exist.";exit 1;} printf "%s" "$res" >"$file" printf -v file "$qrfilename" $filenum qrencode -8 -o "$file" < <(printf %s "$res") printf -v csv[filenum] '"%s","%s","%s","%s"' "${values[3]}" "${values[5]}" \ "${values[18]}" "${values[28]}" echo >> "$csvfilename" "${csv[filenum]}" added=true filenum+=1 echo "$lastAdded" wholeList+=("$lastAdded") } exec {ogg}> >( exec stdbuf -o0 "$oggPlayer" --audio-buffer 0 -b 8 -p 0 -q - &>/dev/null) oggpid=$! check4QRScanner exec {scan}< <( exec stdbuf -i0 "$QRScanner" -q --raw -Sbinary 2>/dev/null) scanpid=$! trap 'kill $scanpid $oggpid;exit' 0 1 2 3 6 15 while ! wmctrl -l | grep -q zbar;do sleep .1;done cat >&$ogg "$readySnd" xdotool windowactivate "$WINDOWID" while :;do if read -t 0 -u "$scan" && getOneQr res ;then cat >&$ogg "$scanSnd" tst="${res//*([!$'\n'])}" (( ${#tst} > 29 )) && { dumpCrtStr <<<"$res" if ((total));then echo "$lastAdded" fi added=false $autoAdd && addThis xclip <<<"$res" } fi if IFS= read -rsn 1 -t .2 userKey ;then case $userKey in e|x|q ) echo EXIT.; break ;; a ) addThis ;; c ) printf ' - %s\n' "${csv[@]}" ;; r ) dumpCrtStr;printf "%s\n" "${wholeList[@]}" ;; esac fi done exec {ogg}>&- exec {scan}<&- [ -d /proc/$oggpid ] && kill $oggpid [ -d /proc/$scanpid ] && kill $scanpid