0a893d0684
Die vier abgestuften Helligkeiten des alten MSP430-Programms (5/15/50/100 %)
entfallen. Fuer die Sternwarte genuegt der Wechsel Tag/Nacht: unterhalb der
Schwelle laeuft die Anzeige mit dunkel_prozent, oberhalb mit hell_prozent.
Die Werte stehen nicht mehr im Code, sondern in settings.json auf dem Geraet --
Vorbereitung fuer die spaetere MQTT-Anbindung:
settings.load() # mit Vorgaben aufgefuellt
settings.update({"hell_prozent": 60}) # aendern und speichern
Vorgaben: dunkel 5 %, hell 50 %, Schwelle 1800, Hysterese 150. Die Schwelle liegt
mit Abstand zwischen den gemessenen Lagen (abgedeckt 0, Raumlicht 2453).
Beibehalten, weil die Messung vom 27.07. sie verlangt: der gleitende Mittelwert
und eine Hysterese um die Schwelle. Der ADC rauscht mit +/-32 (nach Mittelung,
1 Sigma) -- ohne Hysterese wuerde die Anzeige am Umschaltpunkt springen.
settings.py prueft jeden Wert beim Laden UND beim Schreiben auf Typ und Bereich.
Das ist der eigentliche Zweck: Mit MQTT kommen die Werte von aussen, und ein
Helligkeitswert von 500 % oder eine Schwelle von -1 wuerde die Anzeige sonst
unbrauchbar machen. Eine fehlende oder beschaedigte Datei faellt auf die Vorgaben
zurueck, ein einzelner kaputter Wert verwirft nur diesen -- ein Geraet in der
Sternwarte soll sich nicht an seiner Konfigurationsdatei aufhaengen.
settings.json wird von deploy.sh bewusst NICHT mitkopiert und steht in
.gitignore: Es ist Laufzeitzustand, kein Quellcode. Sonst waeren die
eingestellten Werte nach jedem Deployen weg.
Am Geraet bestaetigt: schreiben, wiederlesen, Controller uebernimmt die Werte,
beide Helligkeiten auf der Anzeige sichtbar. 119 Tests gruen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
118 lines
4.3 KiB
Python
118 lines
4.3 KiB
Python
"""Einstellungen, die sich im Betrieb aendern lassen -- als JSON auf dem Geraet.
|
|
|
|
config.py ist Programmcode: Pinbelegung, Protokoll, Geometrie. Das aendert sich
|
|
nur beim Deployen. Werte, die im laufenden Betrieb verstellt werden sollen --
|
|
spaeter ueber MQTT -- gehoeren dagegen hierher. Sie liegen als JSON im
|
|
Dateisystem (settings.json) und lassen sich lesen und zurueckschreiben, ohne das
|
|
Programm neu zu spielen.
|
|
|
|
import settings
|
|
w = settings.load() # mit Vorgaben aufgefuellt
|
|
settings.update({"hell_prozent": 60}) # aendern und speichern
|
|
|
|
Fehlt die Datei oder ist sie beschaedigt, gelten die Vorgaben aus DEFAULTS --
|
|
die Anzeige laeuft dann weiter, statt beim Start auszufallen. Das ist Absicht:
|
|
ein Geraet in der Sternwarte soll sich nicht an einer kaputten Konfigurationsdatei
|
|
aufhaengen.
|
|
|
|
Alle Werte werden beim Laden UND beim Schreiben geprueft (siehe _pruefe). Das ist
|
|
wichtig, sobald MQTT dazukommt: Von dort kommen fremde Eingaben, und ein
|
|
Helligkeitswert von 500 % oder eine Schwelle von -1 wuerde die Anzeige sonst in
|
|
einen unbrauchbaren Zustand bringen.
|
|
"""
|
|
|
|
import json
|
|
|
|
PFAD = "settings.json"
|
|
|
|
# Vorgaben. Die Helligkeiten und die Schwelle stammen aus der LDR-Messung vom
|
|
# 2026-07-27 (siehe README, Abschnitt Helligkeitsregelung): abgedeckt ergab 0,
|
|
# Raumlicht 2453, angeleuchtet 3552 -- 1800 liegt mit Abstand zwischen dunkel
|
|
# und Raumlicht. Die Hysterese deckt das gemessene ADC-Rauschen ab (~32 nach
|
|
# der Mittelung, 1 Sigma).
|
|
DEFAULTS = {
|
|
"dunkel_prozent": 5, # Helligkeit unterhalb der Schwelle
|
|
"hell_prozent": 50, # Helligkeit oberhalb der Schwelle
|
|
"schwelle": 1800, # LDR-Wert (nach raw_to_ldr) fuer den Umschaltpunkt
|
|
"hysterese": 150, # Breite um die Schwelle, gegen Flattern
|
|
}
|
|
|
|
# Erlaubte Bereiche: (minimum, maximum). Die Prozente gehen an ESC H, das nur
|
|
# 0..100 kennt; die LDR-Werte kommen aus einem 12-Bit-ADC.
|
|
_GRENZEN = {
|
|
"dunkel_prozent": (0, 100),
|
|
"hell_prozent": (0, 100),
|
|
"schwelle": (0, 4095),
|
|
"hysterese": (0, 2000),
|
|
}
|
|
|
|
|
|
class SettingsError(ValueError):
|
|
"""Ein Wert liegt ausserhalb des erlaubten Bereichs oder ist unbekannt."""
|
|
|
|
|
|
def _pruefe(werte):
|
|
"""Werte auf Typ und Bereich pruefen. Liefert ein geprueftes neues dict."""
|
|
geprueft = {}
|
|
for name, wert in werte.items():
|
|
if name not in DEFAULTS:
|
|
raise SettingsError("unbekannte Einstellung %r" % (name,))
|
|
# bool ist in Python ein int -- hier aber sicher ein Irrtum.
|
|
if isinstance(wert, bool) or not isinstance(wert, int):
|
|
raise SettingsError("%s: %r ist keine ganze Zahl" % (name, wert))
|
|
low, high = _GRENZEN[name]
|
|
if not low <= wert <= high:
|
|
raise SettingsError("%s: %d liegt ausserhalb %d..%d"
|
|
% (name, wert, low, high))
|
|
geprueft[name] = wert
|
|
return geprueft
|
|
|
|
|
|
def load(pfad=None, log=None):
|
|
"""Einstellungen lesen, fehlende Werte aus DEFAULTS ergaenzen.
|
|
|
|
Wirft nicht: Fehlt die Datei, ist sie unlesbar oder steht Unsinn darin, gibt
|
|
es die Vorgaben. Ein einzelner unbrauchbarer Wert verwirft nur diesen, nicht
|
|
die ganze Datei.
|
|
"""
|
|
pfad = PFAD if pfad is None else pfad
|
|
werte = dict(DEFAULTS)
|
|
try:
|
|
with open(pfad) as f:
|
|
gelesen = json.load(f)
|
|
except (OSError, ValueError):
|
|
return werte # keine Datei oder kaputtes JSON
|
|
if not isinstance(gelesen, dict):
|
|
return werte
|
|
|
|
for name, wert in gelesen.items():
|
|
try:
|
|
werte.update(_pruefe({name: wert}))
|
|
except SettingsError as e:
|
|
if log:
|
|
log("settings: %s -- Vorgabe %r bleibt"
|
|
% (e, DEFAULTS.get(name)))
|
|
return werte
|
|
|
|
|
|
def save(werte, pfad=None):
|
|
"""Gepruefte Einstellungen schreiben. Liefert die gespeicherten Werte."""
|
|
pfad = PFAD if pfad is None else pfad
|
|
vollstaendig = dict(DEFAULTS)
|
|
vollstaendig.update(_pruefe(werte))
|
|
with open(pfad, "w") as f:
|
|
json.dump(vollstaendig, f)
|
|
return vollstaendig
|
|
|
|
|
|
def update(aenderungen, pfad=None, log=None):
|
|
"""Einzelne Werte aendern und speichern -- der Weg fuer MQTT.
|
|
|
|
Erst pruefen, dann mit dem Bestand mischen, dann schreiben. Ist etwas
|
|
ungueltig, bleibt die Datei unveraendert.
|
|
"""
|
|
geprueft = _pruefe(aenderungen)
|
|
werte = load(pfad, log=log)
|
|
werte.update(geprueft)
|
|
return save(werte, pfad)
|