Python

Python3 Virtual Enviroment

Um das Virtuelle Enviroment zu nutzen, was ab Ubuntu Server 24.04 pflicht wird, musst du folgende Dinge ausführen.
Dabei ist es offensichtlich, das Python3 bennötigt wird und darunter auch das Paket für das Virtuelle Enviroment und da das größe Problem mit pip zusammen hängt (wenn man versucht in Ubuntu 24.04 in pip ein Paket zu installieren ohne ein Virtuelles Enviroment, dann kommt ein Fehler, weil das Virtuelle Enviroment nicht genutzt wird), muss natürlich auch pip installiert sein.

Was ich gerne mache ist, einen extra Ordner für das jeweilige Skript zu nutzen, da das den Sinn und zweck des Virtuellen Enviroments stärkt.

Am Ende muss das Virtuelle Enviroment noch erstellt werden und mit source .venv/bin/active genutzt werden.

apt install python3 python3-venv python3-pip -y

##### ##### ##### ##### ##### Optional ##### ##### ##### ##### #####
mkdir MQTT_system_status
cd MQTT_system_status/
##### ##### ##### ##### ##### ##### ##### ##### ##### #####

python3 -m venv .venv
source .venv/bin/activate

Um das Virtuelle Enviroment zu verlassen musst du folgenden Befehl eingeben:

deactivate

 

mkdir /etc/JJ_SystemMetric; apt install python3 python3-venv python3-pip -y; cd /etc/JJ_SystemMetric; python3 -m venv .venv; source .venv/bin/activate; pip3 install psutil influxdb-client

pip

pip

pip - Ferfügbaren versionen anzeigen lassen

Um alle verfügbaren Pakete von pip anzeigen zu lassen musst du diesen Befehl nutzen:

pip3 index versions paho-mqtt

Also so in etwa:

jj@Sol-Ring ~ % pip3 index versions paho-mqtt
WARNING: pip index is currently an experimental command. It may be removed/changed in a future release without prior warning.
paho-mqtt (2.1.0)
Available versions: 2.1.0, 2.0.0, 1.6.1, 1.6.0, 1.5.1, 1.5.0, 1.4.0, 1.3.1, 1.3.0, 1.2.3, 1.2.2, 1.2.1, 1.2, 1.1, 1.0, 0.9.1, 0.9, 0.4.94, 0.4.92, 0.4.91, 0.4.90
  INSTALLED: 1.6.1
  LATEST:    2.1.0
jj@Sol-Ring ~ % 

Skripts

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:
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:
# 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

Skripts

vCard von iCloud auf Radicale (CalDAV/CardDAV) hochladen

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


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/.