# Skripts

# Skript - Kontrolle der Sonne

Um den Sonnen- auf und untergang zu bestimmten, hatte ich zuerst ein Bash Skript benutzt, was ich von einer Seite gefunden hatte.  
Nun wurde es in Python ungeschrieben und sieht so aus:

##### Python:

```python
import math
from datetime import datetime, timezone

# Unsere Position
posLaenge = 10.8
posBreite = 53.8

# Notwendige Vorberechnungen
zoneinfo = int(datetime.now().astimezone().utcoffset().total_seconds() / 3600)  # Zeitzone in Stunden
T = datetime.now().timetuple().tm_yday  # Tag im Jahr
pi = 3.14159265358979323844
rad = pi / 180
h = -(5/6) * rad  # Höhe des Sonnenmittelpunkts bei Aufgang: Radius+Refraktion
BreiteRAD = posBreite * rad

# Berechnungen
sonnendekl = 0.409526325277017 * math.sin(0.0169060504029192 * (T - 80.0856919827619))
arccosint = (math.sin(h) - math.sin(BreiteRAD) * math.sin(sonnendekl)) / (math.cos(BreiteRAD) * math.cos(sonnendekl))
arc2cosint = max(min(arccosint ** 2, 1.0), -1.0)
acoszeit = math.acos(math.sqrt(arc2cosint))

zeitdiff = 12 * acoszeit / pi  # KORREKT!

zeitgleich = -0.170869921174742 * math.sin(0.0336997028793971 * T + 0.465419984181394) - 0.129890681040717 * math.sin(0.0178674832556871 * T - 0.167936777524864)
aufgang = 12 - zeitdiff - zeitgleich - (posLaenge / 15) + zoneinfo
untergang = 12 + zeitdiff - zeitgleich - (posLaenge / 15) + zoneinfo

# Ausgabe der Zwischenergebnisse
print(f"Sonnendeklination: {sonnendekl}")
print(f"Arccosint: {arccosint}")
print(f"Arc2cosint: {arc2cosint}")
print(f"Acoszeit: {acoszeit}")
print(f"Zeitdiff: {zeitdiff}")
print(f"Zeitgleich: {zeitgleich}")
print(f"Aufgang (vor Formatierung): {aufgang}")
print(f"Untergang (vor Formatierung): {untergang}")

# Formatierung
AufgangStunde, AufgangMinute = divmod(int(aufgang * 60), 60)
UntergangStunde, UntergangMinute = divmod(int(untergang * 60), 60)

# Aktualisierte Korrektur
if AufgangStunde == 24:
    AufgangStunde = 0

if UntergangStunde == 24:
    UntergangStunde = 0

# Ausgabe der finalen Ergebnisse
print(f"Aufgang (hh:mm): {AufgangStunde:02d}:{AufgangMinute:02d}")
print(f"Untergang (hh:mm): {UntergangStunde:02d}:{UntergangMinute:02d}")

```

##### Bash:

```powershell
# Unsere Position
posLaenge="10.8"
posBreite="53.8"

# Notwendige Vorberechnungen
zoneinfo=$(date +%z) # Zeitzone
T=`date +%j` # Tag im Jahr
pi="3.14159265358979323844" # pi=`echo "4*a(1)" | bc -l`
rad=$(echo "${pi}/180" | bc -l)
h=$(echo "-(5/6)*(${rad})" | bc -l) # Höhe des Sonnenmittelpunkts bei Aufgang: Radius+Refraktion
BreiteRAD=$(echo "${posBreite}*${rad}" | bc -l)

# Welcher Tag ist heute?
#echo "Heute ist $(date +%d.%m.%y), der $(date +%j). Tag im Jahr"

#echo -n "Wir nutzen die Zeitzone $(date +%Z), dies entspricht $(date +%z) und damit "
#echo "${zoneinfo:0:3}"

sonnendekl=`echo "0.409526325277017*s(0.0169060504029192*(${T}-80.0856919827619))" | bc -l`
sonnendeklDEG=$(echo "${sonnendekl} / ${rad}" | bc -l)

arccosint=$(echo "(s(${h})-s(${BreiteRAD})*s(${sonnendekl}))/(c(${BreiteRAD})*c(${sonnendekl}))" | bc -l)
arccosintsign=${arccosint:0:1}
if [ ${arccosintsign} == "-" ]; then
  usesign="+"
else
  usesign="-"
fi
arc2cosint=$(echo "(${arccosint}) * (${arccosint})" | bc -l)
acoszeit=$(echo "${pi}/2 ${usesign} a(sqrt(${arc2cosint} / (1 - (${arc2cosint}) ) ) ) " | bc -l)

zeitdiff=$(echo "12*${acoszeit}/${pi}" | bc -l) # KORREKT!

zeitgleich=$(echo "-0.170869921174742*s(0.0336997028793971 * ${T} + 0.465419984181394) - 0.129890681040717*s(0.0178674832556871*${T} - 0.167936777524864)" >
aufgang=$(echo "12-(${zeitdiff})-(${zeitgleich})-(${posLaenge}/15)${zoneinfo:0:3}" | bc -l)
untergang=$(echo "12+(${zeitdiff})-(${zeitgleich})-(${posLaenge}/15)${zoneinfo:0:3}" | bc -l)

if [ ${aufgang:1:1} == "." ]; then
  # Ist ein einstelliges Ergebnis der Form x.xxxx, wir brauchen noch eine 0 vorne
  aufgang=$(echo 0${aufgang})
fi
# Fuer unsere Breitengrade ueberfluessig, nur der Vollstaendigkeit halber:
#if [ ${untergang:1:1} == "." ]; then
# Ist ein einstelliges Ergebnis der Form x.xxxx, wir brauchen noch eine 0 vorne
#  untergang=$(echo 0${untergang})
#fi

# Umrechnung in Stunden (trivial) und Minuten (runden!)
AufgangMinute=$(echo "(${aufgang} - ${aufgang:0:2}) * 60" | bc | xargs printf "%02.0f\n")
if [ ${AufgangMinute} == "60" ]; then
  AufgangMinute="00"
  AufgangStunde=$(echo "${aufgang:0:2} + 1" | bc | xargs printf "%02.0f")
else
  AufgangStunde=${aufgang:0:2}
fi
echo "Aufgang (hh:mm): ${AufgangStunde}:${AufgangMinute}" # Immer ein zweistelliges Ergebnis


UntergangMinute=$(echo "(${untergang} - ${untergang:0:2}) * 60" | bc | xargs printf "%02.0f\n")
if [ ${UntergangMinute} == "60" ]; then
  UntergangMinute="00"
  UntergangStunde=$(echo "${untergang:0:2} + 1" | bc | xargs printf "%02.0f")
else
  UntergangStunde=${untergang:0:2}
fi
echo "Untergang (hh:mm): ${UntergangStunde}:${UntergangMinute}" # Immer ein zweistelliges Ergebnis
```

# vCard von iCloud auf Radicale (CalDAV/CardDAV) hochladen

<article id="bkmrk-dieses-skript-liest-">Dieses Skript liest Kontakte aus einer `.vcf`-Datei (z. B. iCloud-Export) ein, entfernt alle nicht standardkonformen Felder und lädt jeden Kontakt als einzelne `.vcf`-Datei in einen bestehenden Radicale CardDAV-Server hoch.

Radicale speichert Adressbücher im CardDAV-Standard über WebDAV. Apple-exportierte vCards enthalten oft proprietäre Felder wie `item1.X-ADDRESSING-GRAMMAR` oder `X-APPLE-*`, die von Radicale nicht akzeptiert werden. Das Skript sorgt dafür, dass nur die Felder übrigbleiben, die auch RFC-konform sind.

Es installiert automatisch die benötigten Python-Pakete (`vobject` und `requests`), bereinigt die Einträge und führt den Upload per `PUT`-Anfrage aus. Für die Ausführung empfiehlt sich ein virtuelles Environment, damit Abhängigkeiten voneinander getrennt bleiben.

---

### Vorbereitung

Lege die exportierte vCard-Datei (z. B. `icloud_contacts.vcf`) in dasselbe Verzeichnis wie das Skript. Stelle sicher, dass die Datei lesbar ist und gültige `BEGIN:VCARD ... END:VCARD`-Blöcke enthält.

---

### Ablauf

```

┌─────────────────────┐
│   CardDAV-Server    │
│   REPORT vCards     │
└─────────────────────┘
           │
           ▼
┌─────────────────────┐
│   Python-Skript     │
│  vCards parsen      │
│  X-Felder entfernen │
│  UID vergeben       │
└─────────────────────┘
           │
           ▼
┌─────────────────────┐
│   CardDAV-Server    │
│     PUT vCards      │
└─────────────────────┘
  
```

---

### Das Python-Skript

```python

import subprocess
import sys

def install(package):
    subprocess.check_call([sys.executable, "-m", "pip", "install", package])

try:
    import vobject
except ImportError:
    install("vobject")
    import vobject

try:
    import requests
except ImportError:
    install("requests")
    import requests

import uuid
from requests.auth import HTTPBasicAuth

# Konfiguration
vcf_datei = "icloud_contacts.vcf"
carddav_url = "https://deinserver.de/benutzer/contacts/"
username = "DEIN_USERNAME"
password = "DEIN_PASSWORT"

def ist_standard_property(name):
    name = name.upper()
    return name in ["FN", "N", "TEL", "EMAIL", "ORG", "UID"]

def lade_und_bereinige_vcards(pfad):
    with open(pfad, "r", encoding="utf-8") as f:
        content = f.read()
    rohkarten = content.strip().split("END:VCARD")
    rohkarten = [c + "END:VCARD" for c in rohkarten if "BEGIN:VCARD" in c]

    bereinigt = []
    for raw in rohkarten:
        try:
            card = vobject.readOne(raw)
            new_card = vobject.vCard()
            new_card.add("uid")
            new_card.uid.value = str(uuid.uuid4())
            for prop in card.getChildren():
                name = prop.name.upper()
                if name.startswith("ITEM") or name.startswith("X-") or name in ["PRODID", "REV", "PHOTO"]:
                    continue
                if ist_standard_property(name):
                    new_card.add(prop)
            bereinigt.append(new_card.serialize())
        except Exception as e:
            print("Fehler beim Parsen eines Kontakts:", e)
    return bereinigt

def upload_to_carddav(cards):
    hochgeladen = 0
    for card_data in cards:
        uid = str(uuid.uuid4())
        ziel_url = carddav_url + uid + ".vcf"
        response = requests.put(
            ziel_url,
            data=card_data,
            headers={"Content-Type": "text/vcard"},
            auth=HTTPBasicAuth(username, password)
        )
        if response.status_code in [200, 201, 204]:
            print("Hochgeladen:", ziel_url)
            hochgeladen += 1
        else:
            print("Fehler (", response.status_code, ") bei", ziel_url)
            print(response.text)
    print(hochgeladen, "Kontakte erfolgreich hochgeladen.")

def main():
    bereinigt = lade_und_bereinige_vcards(vcf_datei)
    if not bereinigt:
        print("Keine gültigen vCards gefunden.")
        return
    upload_to_carddav(bereinigt)

if __name__ == "__main__":
    main()
  
```

---

### Verwendung

Führe das Skript in einem Terminal oder einer Konsole aus:

```
python carddav_upload.py
```

Es bereinigt alle Kontakte und lädt sie anschließend einzeln hoch. Nicht standardkonforme Felder werden automatisch entfernt.

---

### Hintergrund

vCard (RFC 6350) ist der Standard für den Austausch elektronischer Visitenkarten. CardDAV erweitert WebDAV um dieses Format und erlaubt das Speichern von `.vcf`-Dateien in Adressbüchern.

Radicale implementiert CardDAV und akzeptiert nur RFC-konforme vCard-Felder. Das Skript stellt sicher, dass alle importierten Kontakte diesen Vorgaben entsprechen und nicht durch Apple-spezifische Erweiterungen fehlschlagen.

---

Wenn die CardDAV-URL nicht bekannt ist, hilft ein Client wie DAVx⁵ oder das Radicale-Debug-Log, den korrekten Pfad zu ermitteln. Die URL endet meist auf `/username/contacts/`.

</article>