DevelopmentGuidelines: Unterschied zwischen den Versionen
Yfhem (Diskussion | Beiträge) K (→Bezeichnungen: Remove dot in source.) |
K (Added batteryReadings) |
||
(5 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt) | |||
Zeile 5: | Zeile 5: | ||
===Allgemeines=== | ===Allgemeines=== | ||
Die Version von fhem, in der diese Guidelines umgesetzt werden sollen, wird mit fhem-NEU bezeichnet. Mit fhem 4.x wird die aktuelle Architektur bezeichnet, die auch | Die Version von fhem, in der diese Guidelines umgesetzt werden sollen, wird mit fhem-NEU bezeichnet. Mit fhem 4.x wird die aktuelle Architektur bezeichnet, die auch für fhem 5.x zutrifft. | ||
===Readings=== | ===Readings=== | ||
Ein Reading (Ablesewert) ist jeglicher Wert, Zustand, etc., der von einem | Ein Reading (Ablesewert) ist jeglicher Wert, Zustand, etc., der von einem Gerät ausgelesen werden kann. Beispiele dafür sind Messtemperatur, Luftfeuchtigkeit, Schaltzustand (an/aus), Firmwareversion, Empfangsstärke. | ||
=== I/O-Devices und Clients=== | === I/O-Devices und Clients=== | ||
Um Readings durch fhem verarbeiten zu | Um Readings durch fhem verarbeiten zu können, müssen die Daten erst über ein I/O-Device in den Rechner kommen. Beispiele: FHZ1300, CUL, CM11, M232. | ||
Ein I/O-Device ist | Ein I/O-Device ist über einen Port (Beispiel für Unix: /dev/ttyS0) oder einen Socket ansprechbar. | ||
Ein I/O-Device kann selbst ein | Ein I/O-Device kann selbst ein Gerät sein, das Readings liefert (Beispiel: SCIVT, sowie die meisten anderen Schnittstellengeräte, wenn man die Geräteversion oder die Uptime mit zu den Readings zählt), oder die Readings von anderen Geräten (Clients) weiterleiten (Beispiel: FHZ1300). | ||
==Requests for Proposal== | ==Requests for Proposal== | ||
Zeile 22: | Zeile 22: | ||
Es sind zwei Arten von Readings zu unterscheiden: | Es sind zwei Arten von Readings zu unterscheiden: | ||
# Readings, die vom | # Readings, die vom Gerät aktiv mitgeteilt werden, entweder ad-hoc oder in regelmäßigen Zeitabständen (aktive Readings). Beispiele: measuredTemp bei FHT80B, wind bei KS300 | ||
# Readings, nach denen fhem die Geräte fragen muss (passive Readings). Beispiele: energyDay bei EM1010PC, counter bei M232, temperature bei OWTEMP | # Readings, nach denen fhem die Geräte fragen muss (passive Readings). Beispiele: energyDay bei EM1010PC, counter bei M232, temperature bei OWTEMP | ||
Dasselbe | Dasselbe Gerät kann sowohl aktive als auch passive Readings beinhalten. Bei Geräten mit aktiven Readings sind passive Readings meist Informationen wie Versionsnummer oder Uptime (CUL, CM11). | ||
Geräte, die mehrere verschiedene Readings haben, senden diese häufig im Batch an fhem (Beispiel: FHT80b, KS300). | |||
====Mechanismus | ====Mechanismus für aktive Readings==== | ||
''TODO: Mechanismus mit ParseFn, GetFn, Match usw. beschreiben (wie in fhem 4.x)!'' | ''TODO: Mechanismus mit ParseFn, GetFn, Match usw. beschreiben (wie in fhem 4.x)!'' | ||
====Mechanismus | ====Mechanismus für passive Readings==== | ||
Für passive Geräte gibt es eine Polling-Infrastruktur. | |||
'''Status Quo in fhem 4.x:''' | '''Status Quo in fhem 4.x:''' | ||
Zeile 60: | Zeile 60: | ||
Fragen: | Fragen: | ||
* Kann/soll dieses Verfahren in einem Modul gekapselt werden? | * Kann/soll dieses Verfahren in einem Modul gekapselt werden? Geräte melden sich dann an der Polling-Infrastruktur an. Wenn ja, wie? | ||
* Wie wird waitIfInitNotDone korrekt eingesetzt? | * Wie wird waitIfInitNotDone korrekt eingesetzt? | ||
====Mechanismus | ====Mechanismus für langsame Readings==== | ||
'''Problemstellung:''' | '''Problemstellung:''' Längere Verarbeitungsprozesse in einzelnen Modulen (z.B. aufgrund von Netzwerklatenzen oder toten Geräten) halten fhem komplett an und verhindern die Verarbeitung von Events. | ||
''' | '''Lösung:''' Multiprocessing | ||
Beim Multiprocessing wird der fhem-Prozess geforkt, wenn ein Reading vom | Beim Multiprocessing wird der fhem-Prozess geforkt, wenn ein Reading vom Gerät eingelesen werden soll. Der Vaterprozess wird sofort fortgesetzt und erledigt weitere Aufgaben. Der Kindprozess holt die Readings vom Gerät, liefert sie an den Vaterprozess und verendet. | ||
Die Lieferung an den Vaterprozess erfolgt durch eine Verbindung zu fhem via | Die Lieferung des Resultats an den Vaterprozess erfolgt durch eine Loopback Verbindung zu fhem via localhost. | ||
Dieser Mechanismus wurde durch Rudolf König in ein entsprechendes Modul verpackt, so dass jedes andere FHEM-Modul diese Möglichkeiten direkt nutzen kann. Eine nähere Erklärung sowie eine Anleitung dazu findet man in dem Artikel → [[Blocking Call]] | |||
'''Fragen''': | '''Fragen''': | ||
* Soll es diesen Mechanismus nur passive Readings geben oder gibt es Anwendungsfälle für aktive Readings? | |||
* Soll es diesen Mechanismus nur passive Readings geben oder gibt es | |||
'''Verworfene Alternative''': Multithreading | '''Verworfene Alternative''': Multithreading | ||
Argumente gegen Multithreading: | Argumente gegen Multithreading: | ||
* Wird praktisch auf keiner kleinen Plattform | * Wird praktisch auf keiner kleinen Plattform unterstützt. | ||
* Negative Erfahrungen (z.B. Probleme bei vielen 3rd-party-Komponenten) | * Negative Erfahrungen (z.B. Probleme bei vielen 3rd-party-Komponenten) | ||
* Kaum Programmiererfahrung mit Multithreading in Perl. | * Kaum Programmiererfahrung mit Multithreading in Perl. | ||
==Verabschiedete Richtlinien== | ==Verabschiedete Richtlinien== | ||
===Zeichensatz=== | ===Zeichensatz=== | ||
Alle nach aussen sichtbaren Werte sind in der Zeichenkodierung ASCII (Codes 32..127). Dies vermeidet Konvertierungsprobleme bei der Bearbeitung des | Alle nach aussen sichtbaren Werte sind in der Zeichenkodierung ASCII (Codes 32..127). Dies vermeidet Konvertierungsprobleme bei der Bearbeitung des Quellcodes, bei der Ausgabe durch Perl, beim Transport in Internet-Protokollen und bei der Darstellung im Frontend/GUI. | ||
===Klassifizierung der Attribute=== | ===Klassifizierung der Attribute=== | ||
Zeile 151: | Zeile 98: | ||
* zu definieren, wie ein Attribut im $hash->{} abgelegt wird (in $hash->{}, $hash->{READINGS}, $hash->{INTERNALS}, ...), | * zu definieren, wie ein Attribut im $hash->{} abgelegt wird (in $hash->{}, $hash->{READINGS}, $hash->{INTERNALS}, ...), | ||
* zu gliedern, welche Namenskonventionen jeweils für Attribute verwendet werden (Kleinbuchstaben, Großbuchstaben, CamelCaps, ...), | * zu gliedern, welche Namenskonventionen jeweils für Attribute verwendet werden (Kleinbuchstaben, Großbuchstaben, CamelCaps, ...), | ||
* abzugrenzen, wo der Entwickler sich an Vorgaben halten | * abzugrenzen, wo der Entwickler sich an Vorgaben halten muss (z.B. bei System Internals) und wo er frei ist, Attribute zu erfinden oder wegzulassen, und | ||
* festzulegen, wo bzgl. der Inhalte Standards notwendig sind und wo nicht (z.B. bei "Logical Readings", wenn diese im GUI angezeigt werden sollten). | * festzulegen, wo bzgl. der Inhalte Standards notwendig sind und wo nicht (z.B. bei "Logical Readings", wenn diese im GUI angezeigt werden sollten). | ||
====Logische Rubriken==== | ====Logische Rubriken==== | ||
Die folgenden Rubriken stellen eine sehr feine Einteilung dar: | Die folgenden Rubriken stellen eine sehr feine Einteilung dar: | ||
* System Internals: werden vom Framework (fhem.pl) | * System Internals: werden vom Framework (fhem.pl) benötigt/verwendet/erkannt, z.B. NAME, NR, CHANGED | ||
* Device Readings: Rohdaten, die vom physischen | * Device Readings: Rohdaten, die vom physischen Gerät ausgelesen werden (nicht interpretiert), z.B. rain_raw (Wippenschläge des Regensensors) in KS300 | ||
* Hilfsvariablen: z.B. rain_raw_adj (rain_raw, jedoch bereinigt um die bei einigen KS300 auftretenden erratischen | * Hilfsvariablen: z.B. rain_raw_adj (rain_raw, jedoch bereinigt um die bei einigen KS300 auftretenden erratischen Sprünge) in KS300, Zwischenergebnisse von Daten/Werten, die zur Mittelwertberechnung benötigt werden | ||
* Logical Readings: ausgewertete Messdaten und | * Logical Readings: ausgewertete Messdaten und Zustände, z.B. Temperatur, Niederschlag (nicht als Wippenschläge sondern in l/qm) | ||
* Derived Readings: aus den Messdaten abgeleitete Werte, z.B. durchschnittliche Temperatur der laufenden Woche, Niederschlagsmenge des Tages | * Derived Readings: aus den Messdaten abgeleitete Werte, z.B. durchschnittliche Temperatur der laufenden Woche, Niederschlagsmenge des Tages | ||
* Physical Device Parameter: z.B. Modell oder Housecode und Unitcode bei X10 oder die Sensornummer beim BS (brightness sensor) | * Physical Device Parameter: z.B. Modell oder Housecode und Unitcode bei X10 oder die Sensornummer beim BS (brightness sensor) | ||
* Logical Device Parameter: statische Werte, die als Grundlage für Berechnungen genutzt werden, z.B. Korrekturfaktoren bei den | * Logical Device Parameter: statische Werte, die als Grundlage für Berechnungen genutzt werden, z.B. Korrekturfaktoren bei den Energiemessgeräten, die Tankgeometrie beim USF1000 | ||
* Messages: alle Nachrichten, die das Gerät betreffen, z.B. "AVG_Month erfolgreich berechnet", "Script XYZ am xx.xx.xx gestartet", LastIODEV, LastRAWMSG | * Messages: alle Nachrichten, die das Gerät betreffen, z.B. "AVG_Month erfolgreich berechnet", "Script XYZ am xx.xx.xx gestartet", LastIODEV, LastRAWMSG | ||
* Trigger: wenn Trigger direkt am | * Trigger: wenn Trigger direkt am Gerät hinterlegt werden, müssen nicht mehr alle Notifies durchsucht werden, sondern es kann das Modul direkt ermitteln, ob ein Notify ausgelöst wird. | ||
====Ablage im Programm==== | ====Ablage im Programm==== | ||
Programmtechnisch werden die Attribute eines Moduls so in | Programmtechnisch werden die Attribute eines Moduls so in Behältern abgelegt, dass folgende Kriterien erfüllt sind: | ||
* Die Aufteilung ist | * Die Aufteilung ist möglichst einfach/minimalistisch. | ||
* Anhand des | * Anhand des Behälters lässt sich entscheiden, | ||
** ob die Werte der darin | ** ob die Werte der darin enthaltenen Attribute über das Programmende hinaus gespeichert oder nicht gespeichert werden, | ||
** welchen Anzeigestandards oder Programmierstandards die darin enthaltenen Attribute entsprechen, und | ** welchen Anzeigestandards oder Programmierstandards die darin enthaltenen Attribute entsprechen, und | ||
** ob es sich um fhem-interne Attribute, Readings oder Hilfsvariablen handelt. | ** ob es sich um fhem-interne Attribute, Readings oder Hilfsvariablen handelt. | ||
Es gibt folgende | Es gibt folgende Behälter: | ||
readings | readings | ||
* Logische Rubrik: Device Reading, Logical Reading, Derived Reading | * Logische Rubrik: Device Reading, Logical Reading, Derived Reading | ||
* Wird gespeichert | * Wird gespeichert | ||
* Alle vom | * Alle vom Gerät gelieferten bzw. berechneten Werte, die den Endanwender interessieren. Keine Rohdaten vom Gerät, die erst interpretiert werden müssen (Wippenschläge beim Regensensor). | ||
* Alle Daten haben einen Wert und ein Zeitstempel | * Alle Daten haben einen Wert und ein Zeitstempel | ||
* Beispiele: | * Beispiele: | ||
Zeile 187: | Zeile 134: | ||
helper | helper | ||
* Logische Rubrik: Hilfsvariable, Device Reading | * Logische Rubrik: Hilfsvariable, Device Reading | ||
* Wird gespeichert | * Wird nicht gespeichert | ||
* Alle Werte, die | * Alle Werte, die für den Endanwender nicht direkt sinnvoll sind aber vom Modul für unterschiedliche Zwecke benötigt werden. Kann auch rohe Readings enthalten. | ||
* Beispiele: | * Beispiele: | ||
Zeile 197: | Zeile 144: | ||
* Logische Rubrik: System Internals, Physical Device Parameter, Logical Device Parameter | * Logische Rubrik: System Internals, Physical Device Parameter, Logical Device Parameter | ||
* Wird nicht gespeichert | * Wird nicht gespeichert | ||
* Alle | * Alle Geräte-Werte, die nicht gespeichert werden müssen, weil entweder berechnet, oder aus der Definition hervorgehen. | ||
* Beispiele | * Beispiele | ||
Zeile 203: | Zeile 150: | ||
$defs{emwz}{fhem}{lastIODev} = "CUL" | $defs{emwz}{fhem}{lastIODev} = "CUL" | ||
Verworfener | Verworfener Behälter STATE | ||
* Logische Rubrik: Device Reading, Logical Reading, Derived Reading | * Logische Rubrik: Device Reading, Logical Reading, Derived Reading | ||
* Wird gespeichert | * Wird gespeichert | ||
* Kurze(!) Zusammenfassung der wichtigsten Information des | * Kurze(!) Zusammenfassung der wichtigsten Information des Geräts. Was man in einer Übersicht über die Geräte sehen möchte, z.B. eine Warnung oder die aktuelle Temperatur beim FHT80 oder der Zeitpunkt der letzten Aktivierung beim PIRI. Falls es das nicht gibt, dann einheitlich "defined". GGf. über Attribute am Modul steuerbar. Eigentlich ist es kein Behälter im üblichen Sinne, da es keine Untereinheiten hat. | ||
Der | Der Behälter wurde verworfen, weil er redundant ist, an sich gar kein Behälter, und einfacher und flexibler über einen Verweis auf das Reading realisiert werden kann, der per Default angezeigt werden soll. | ||
====Status==== | ====Status==== | ||
Kurze Zusammenfassung der wichtigsten Information des | Kurze Zusammenfassung der wichtigsten Information des Geräts. Was man in einer Übersicht über die Geräte sehen möchte, z.B. eine Warnung oder die aktuelle Temperatur beim FHT80 oder der Zeitpunkt der letzten Aktivierung beim PIRI. Falls es sowas nicht gibt, dann einheitlich "defined". Modulseitig gibt es einen Default, der auf das relevante Reading verweist. Dieser ist über das Attribut defaultReading am individuellen Gerät steuerbar. | ||
Beispiel: Definition in der Konfigurationsdatei: attr myDevice defaultReading measuredTemp Verwendung im Code: | Beispiel: Definition in der Konfigurationsdatei: attr myDevice defaultReading measuredTemp Verwendung im Code: | ||
Zeile 220: | Zeile 167: | ||
===Bezeichnungen=== | ===Bezeichnungen=== | ||
Verwendung von lowerCamelCaps | Verwendung von lowerCamelCaps für alle vom Modul nach aussen sichtbaren Bezeichner: a) die Bezeichnungen der Behälter für Readings, Fhem und Helper und der Untereinträge, b) die Bezeichnungen der Readings, c) die Bezeichnungen der Attribute. | ||
Beispiel: | Beispiel: | ||
Zeile 228: | Zeile 175: | ||
===Readings=== | ===Readings=== | ||
====Standardisierung der Readings==== | ====Standardisierung der Readings==== | ||
Die Readings werden standardisiert, indem | Die Readings werden standardisiert, indem Geräteklassen gebildet werden wie in DevelopmentInterfaces beschrieben. Die Definition der Interfaces ist explizit nicht Gegenstand dieser Entscheidungsvorlage und wird weiterentwickelt und angepasst, wie es sich bei der Entwicklung von fhem-NEU ergibt. | ||
Struktur im Code | Struktur im Code | ||
Zu jedem Reading gibt es die folgenden | Zu jedem Reading gibt es die folgenden Unterbehälter: | ||
* value: Wert der | * value: Wert der Messgröße | ||
* time: Zeitpunkt, zu dem der Wert ermittelt wurde | * time: Zeitpunkt, zu dem der Wert ermittelt wurde | ||
Zeile 240: | Zeile 187: | ||
$defs{myFHT}{readings}{measuredTemp}{value}= 23.5; | $defs{myFHT}{readings}{measuredTemp}{value}= 23.5; | ||
Readings enthalten | Readings enthalten grundsätzlich genau einen Wert und diesen ohne Einheit. | ||
====Einheitendarstellung==== | ====Einheitendarstellung==== | ||
Die verwendete Einheit | Die verwendete Einheit für ein Reading ergibt sich aus der Interfacespezifikation. | ||
Beispiel: Gerät ist ein Thermometer => es gibt ein Reading "temperature" und dessen Einheit ist immer °C oder Celsius | Beispiel: Gerät ist ein Thermometer => es gibt ein Reading "temperature" und dessen Einheit ist immer °C oder Celsius | ||
Zeile 250: | Zeile 197: | ||
* Einheit ergibt sich aus der Dokumentation | * Einheit ergibt sich aus der Dokumentation | ||
* Einheit ergibt sich aus einem expliziten Untereintrag $defs{deviceName}{readings}{theReading}{unit}="°C" | * Einheit ergibt sich aus einem expliziten Untereintrag $defs{deviceName}{readings}{theReading}{unit}="°C" | ||
* Einheit ergibt sich aus Festlegung | * Einheit ergibt sich aus Festlegung gemäß "FHEM-Standard", z.B. immer SI-Einheiten, Temperaturen in Celsius, etc. | ||
====BatteryReadings==== | |||
Es gibt '''nur''' diese drei Readings für den Batteriestatus: | |||
* batteryState | |||
* batteryPercent | |||
* batteryVoltage | |||
Wertebereich: | |||
*batteryState: ok|low | |||
*batteryPercent: \d{1,2}|100 | |||
*batteryVoltage: \d+.\d+ | |||
Wichtig: | |||
das jeweilige Modul setzt '''nur''' die Readings, die es aus den aktuellen Daten vom Geraet bestimmen kann. Konkret: niemand kann sich darauf verlassen, welche der drei battery Readings vorhanden sind (es gibt nicht überall ein batteryState). Wenn das Gerät früher ein Percent gemeldet hat, aber in der letzten Nachricht nur state, dann wird das Percent Reading nicht angefasst. | |||
Aktuell wird noch diskutiert, was mit dem Reading für Laden ist. Mehr dazu hier: {{Link2Forum|Topic=87575|Message=62960|LinkText=Beitrag im FHEM Forum}} | |||
===Zeitdarstellung in fhem=== | ===Zeitdarstellung in fhem=== | ||
Zeiten werden im Programm | Zeiten werden im Programm grundsätzlich immer maschinenlesbar abgelegt, also sowohl in $defs{deviceName}{readings}{time} als auch z.B. bei der Speicherung des Zeitpunkts der Ausführung des nächsten at-Kommandos. | ||
Es gibt | Es gibt übergreifende Hilfsfunktionen, die die maschinenlesbare in eine menschenlesbare Darstellung umwandeln. | ||
Argumente | Argumente für maschinenlesbar: | ||
* vereinfacht Datumsarithmetik, z.B. die Berechnung der Zeitdauer zwischen zwei Ereignissen | * vereinfacht Datumsarithmetik, z.B. die Berechnung der Zeitdauer zwischen zwei Ereignissen | ||
* kann vom Frontend in die regionale Darstellung des Anwenders | * kann vom Frontend in die regionale Darstellung des Anwenders übersetzt werden | ||
* hat keine Probleme mit doppelt auftretenden Zeitpunkten bei der Umstellung von Sommer- auf Winterzeit | * hat keine Probleme mit doppelt auftretenden Zeitpunkten bei der Umstellung von Sommer- auf Winterzeit | ||
Folgende Zeitdarstellungen werden ausschliesslich verwendet: | Folgende Zeitdarstellungen werden ausschliesslich verwendet: | ||
A. Zahl der Sekunden seit der Unix-Epoche (was time liefert); auch wenn time eine Ganzzahl liefert, muss der Verwender damit rechnen, eine Gleitkommazahl vorzufinden. Das ist z.B. dann der Fall, wenn | A. Zahl der Sekunden seit der Unix-Epoche (was time liefert); auch wenn time eine Ganzzahl liefert, muss der Verwender damit rechnen, eine Gleitkommazahl vorzufinden. Das ist z.B. dann der Fall, wenn für die Zeitbestimmung höher auflösende Funktionen (z.B. Time::HiRes) zum Einsatz kamen und Sekundenbruchteile mitgespeichert wurden. | ||
Hinweis zu potentiellen Problemen: | Hinweis zu potentiellen Problemen: | ||
* Ab perl 5.12 ist das Jahr-2038-Problem in Perl beseitigt. | * Ab perl 5.12 ist das Jahr-2038-Problem in Perl beseitigt. | ||
* Ab | * Ab Mac OS X liefert time auch auf Macs die Zahl der Sekunden seit der Unix-Epoche (statt seit Anfang 1904) | ||
B. ISO8601 mit optionaler Zeitzonenangabe, wobei bei fehlender Zeitzone die lokale Zeitzone des fhem-Servers gilt | B. ISO8601 mit optionaler Zeitzonenangabe, wobei bei fehlender Zeitzone die lokale Zeitzone des fhem-Servers gilt | ||
Zeile 275: | Zeile 237: | ||
* Zur Programmlaufzeit in Variablen immer A | * Zur Programmlaufzeit in Variablen immer A | ||
* In Konfigurationsdateien (fhem.conf, fhem.save) immer B | * In Konfigurationsdateien (fhem.conf, fhem.save) immer B | ||
* In Logs, die | * In Logs, die für den Menschen bestimmt sind, B | ||
* In Logs, die | * In Logs, die für die Maschine bestimmt sind, also z.B. bei Weiterverarbeitung in einem GUI, A | ||
* In Listen (list, xmllist friendly), die | * In Listen (list, xmllist friendly), die für den Menschen bestimmt sind, B | ||
* In Listen (xmllist), die | * In Listen (xmllist), die für die Maschine bestimmt sind, also z.B. bei Weiterverarbeitung in einem GUI, A | ||
Die Unterscheidung bei den Logs ist noch zu diskutieren. | Die Unterscheidung bei den Logs ist noch zu diskutieren. | ||
Zeile 286: | Zeile 248: | ||
<nowiki>set timefmt '%Y-%m-%dT%H:%M:%S' </nowiki> | <nowiki>set timefmt '%Y-%m-%dT%H:%M:%S' </nowiki> | ||
(ISO8601) funktioniert, und zwar | (ISO8601) funktioniert, und zwar unabhängig davon, ob die optionale Zeitzonenangabe anhängt oder nicht (getestet mit Gnuplot v4.2 pl3). Für die Sekunden seit der Unix-Epoche: | ||
<nowiki>set timefmt '%s' </nowiki> | <nowiki>set timefmt '%s' </nowiki> | ||
Zeile 295: | Zeile 257: | ||
===Events, Filter, Notify=== | ===Events, Filter, Notify=== | ||
====Event==== | ====Event==== | ||
Ein Event tritt ein, wenn ein oder mehrere Readings eines | Ein Event tritt ein, wenn ein oder mehrere Readings eines Geräts aktualisiert werden, weil z.B. ein Datagramm empfangen oder zeitgesteuert Werte eingelesen wurden. Es wird durch drei Angaben | ||
<nowiki>(device, timestamp, { reading1, ..., readingN } )</nowiki> | <nowiki>(device, timestamp, { reading1, ..., readingN } )</nowiki> | ||
beschrieben: | beschrieben: | ||
* das | * das Gerät device | ||
* der Zeitpunkt der Aktualisierung timestamp | * der Zeitpunkt der Aktualisierung timestamp | ||
* die Menge der N>= 1 geaenderten Readings reading1, .. readingN | * die Menge der N>= 1 geaenderten Readings reading1, .. readingN | ||
Zeile 318: | Zeile 280: | ||
====Mechanismus==== | ====Mechanismus==== | ||
0. | 0. Für das Gerät device namens deviceName wird ein Datagramm empfangen oder es werden zeitgesteuert Werte eingelesen. | ||
1. Die Readings werden aktualisiert. | 1. Die Readings werden aktualisiert. Für X= 1..N: | ||
$defs{deviceName}{readings}{readingX}{value}= valueX; | $defs{deviceName}{readings}{readingX}{value}= valueX; | ||
$defs{deviceName}{readings}{readingX}{time}= timestamp; | $defs{deviceName}{readings}{readingX}{time}= timestamp; | ||
2. Das Event wird erstellt und an DoTrigger | 2. Das Event wird erstellt und an DoTrigger übergeben. | ||
3. DoTrigger informiert | 3. DoTrigger informiert zunächst alle Clients mit inform-Wunsch. | ||
4. Das Event wird sodann von DoTrigger nacheinander an alle NotifyFn in der alphabetischen Reihenfolge der | 4. Das Event wird sodann von DoTrigger nacheinander an alle NotifyFn in der alphabetischen Reihenfolge der Gerätenamen gereicht. Darunter auch jene von Logs. | ||
5. Wenn der Filter des Logs auf das Event passt, wird ein Log-Eintrag der Form | 5. Wenn der Filter des Logs auf das Event passt, wird ein Log-Eintrag der Form | ||
Zeile 337: | Zeile 299: | ||
erzeugt. | erzeugt. | ||
== | ==Zurückgestellte Entscheidungen== | ||
===Attribute vs. Internals=== | ===Attribute vs. Internals=== | ||
Die Unterscheidung zwischen Attributen und Internals ist nicht eindeutig. Manche define-Parameter sind optional, und man kann define-Parameter mit "modify" | Die Unterscheidung zwischen Attributen und Internals ist nicht eindeutig. Manche define-Parameter sind optional, und man kann define-Parameter mit "modify" ändern. Insofern könnte man theoretisch einen der beiden Verfahren (modify vs. attribute) ablösen. | ||
1. Idee: define-Parameter | 1. Idee: define-Parameter dürfen nachträglich nicht geändert werden, auch wenn es sich um optionale Parameter handelt => nicht-modifizierbare Internals | ||
Pros: | Pros: | ||
* define-Parameter sind elementar | * define-Parameter sind elementar für den Betrieb des Geräts und können nicht sinnvoll zur Programmlaufzeit geändert werden (z.B. Hauskode bei X10, FHTId bei FHT80b) | ||
* Aus den define-Parametern werden bei der Initialisierung des | * Aus den define-Parametern werden bei der Initialisierung des Geräts weitere Helper und Internals abgeleitet und gespeichert. Eine nachträgliche Änderung zieht Änderungen der Helper und Internals nach sich mit ggf. schwer durchschaubaren Nebeneffekten (z.B. corr1..corr4 bei EM, rainadjustment bei KS300). Auch die Logs ändern sich nicht nachträglich. | ||
Contras: | Contras: | ||
* Man will nicht ein | * Man will nicht ein Gerät löschen und neu anlegen, wenn es ausgetauscht wird. | ||
* modify sollte bleiben, weil es | * modify sollte bleiben, weil es nützlich ist. | ||
2. Idee: Attribute enthalten Werte, die ohne Nebeneffekte zur Laufzeit | 2. Idee: Attribute enthalten Werte, die ohne Nebeneffekte zur Laufzeit geändert werden können, weil sie z.B. ad-hoc ausgewertet werden. | ||
* follow-on-for-timer | * follow-on-for-timer | ||
* retrycount | * retrycount | ||
* lazy | * lazy | ||
3. Idee: Attribute beinhalten | 3. Idee: Attribute beinhalten geräteunabhängige Meta-Informationen: | ||
* room | * room | ||
* defaultReading | * defaultReading | ||
Contras: | Contras: | ||
* Nach 2. und 3. | * Nach 2. und 3. müsste | ||
attr my_at disabled | attr my_at disabled | ||
Zeile 368: | Zeile 330: | ||
Spezialitäten | |||
* beim FS20 bleibt alles, wie es ist, also model als Attribut, was Auswirkung auf die | * beim FS20 bleibt alles, wie es ist, also model als Attribut, was Auswirkung auf die möglichen set Befehle hat. | ||
* 1wire setzt nach define das model Attribut, | * 1wire setzt nach define das model Attribut, lässt aber eine Änderung nicht zu, oder wenn doch, dann muss sich dementsprechend verhalten, es macht ja evtl. Sinn es zu ändern. Was machbar ist, entscheidet der Modul-Author. | ||
Damit ist "model" immer ein Attribut. | Damit ist "model" immer ein Attribut. | ||
Zeile 376: | Zeile 338: | ||
Fazit | Fazit | ||
Da es keine | Da es keine überzeugende Alternative zum Ist-Zustand in fhem 4.x gibt, wird b.a.w. nicht hierüber entschieden. | ||
===notify=== | ===notify=== | ||
Die Frage, ob der notify-Mechanismus, der weiter oben dokumentiert ist, aus | Die Frage, ob der notify-Mechanismus, der weiter oben dokumentiert ist, aus Performancegründen optimiert werden sollte, ist offen. | ||
In fhem 4.x bekommt jedes | In fhem 4.x bekommt jedes Gerät mit NotifyFn jedes Event mit. Alternativ könnte sich ein Gerät als EventListener mit einem Filter bei fhem anmelden. Dann könnten die Events nur an die NotifyFn verteilt werden, für die es ein Match auf den Filter gaebe. | ||
Der Punkt wurde | Der Punkt wurde zurückgestellt, bis nachgewiesen ist, dass sich aus dieser Vorgehensweise relevante Auswirkungen auf die Systemlast ergeben. | ||
==Entscheidungen== | ==Entscheidungen== | ||
Zeile 391: | Zeile 353: | ||
:Es wird ein Attribut defaultReading verwendet. | :Es wird ein Attribut defaultReading verwendet. | ||
;E3 | ;E3 | ||
:Verwendung von lowerCamelCaps | :Verwendung von lowerCamelCaps für a) die Bezeichnungen der Behälter für Readings, Fhem und Helper und der Untereintraege, b) die Bezeichnungen der Readings, c) die Bezeichnungen der Attribute. | ||
;E4 | ;E4 | ||
:Verwendung von value und time. | :Verwendung von value und time. | ||
;E5 | ;E5 | ||
:Zeitdarstellung im Programm | :Zeitdarstellung im Programm grundsätzlich maschinenlesbar | ||
;E6 | ;E6 | ||
: | :Zulässige Zeitdarstellungen | ||
* Sekunden seit der Unix-Epoche | * Sekunden seit der Unix-Epoche | ||
* ISO8601 mit optionaler Zeitzonenangabe | * ISO8601 mit optionaler Zeitzonenangabe | ||
ISO8601 mit _ statt T war nicht | ISO8601 mit _ statt T war nicht mehrheitsfähig. | ||
;E7 | ;E7 | ||
:Verwendung der Zeitdarstellungen | :Verwendung der Zeitdarstellungen | ||
;E8 | ;E8 | ||
:Readings enthalten | :Readings enthalten grundsätzlich genau einen Wert und diesen ohne Einheit. | ||
;E9 | ;E9 | ||
:Einheiten ergeben sich aus der Interfacespezifikation. | :Einheiten ergeben sich aus der Interfacespezifikation. | ||
;E10 | ;E10 | ||
:Entscheidung | :Entscheidung für ASCII | ||
;E11 | ;E11 | ||
:Die Readings werden standardisiert, indem | :Die Readings werden standardisiert, indem Geräteklassen gebildet werden wie in DevelopmentInterfaces beschrieben. Die Definition der Interfaces ist explizit nicht Gegenstand dieser Entscheidungsvorlage und wird weiterentwickelt und angepasst, wie es sich bei der Entwicklung von fhem-NEU ergibt. | ||
[[Kategorie:Development]] | [[Kategorie:Development (Archive)]] |
Aktuelle Version vom 2. Juni 2018, 12:05 Uhr
Wichtiger Hinweis: Diese Seite enthält die Ideen der fhem-Entwickler, wie fhem in einer künftigen Version funktionieren soll. Es beschreibt nicht den Zustand der aktuellen Umsetzung in den Release- oder den SVN-Versionen.
Definitionen
TODO: Rudis kanonische Begriffe verwenden!
Allgemeines
Die Version von fhem, in der diese Guidelines umgesetzt werden sollen, wird mit fhem-NEU bezeichnet. Mit fhem 4.x wird die aktuelle Architektur bezeichnet, die auch für fhem 5.x zutrifft.
Readings
Ein Reading (Ablesewert) ist jeglicher Wert, Zustand, etc., der von einem Gerät ausgelesen werden kann. Beispiele dafür sind Messtemperatur, Luftfeuchtigkeit, Schaltzustand (an/aus), Firmwareversion, Empfangsstärke.
I/O-Devices und Clients
Um Readings durch fhem verarbeiten zu können, müssen die Daten erst über ein I/O-Device in den Rechner kommen. Beispiele: FHZ1300, CUL, CM11, M232.
Ein I/O-Device ist über einen Port (Beispiel für Unix: /dev/ttyS0) oder einen Socket ansprechbar.
Ein I/O-Device kann selbst ein Gerät sein, das Readings liefert (Beispiel: SCIVT, sowie die meisten anderen Schnittstellengeräte, wenn man die Geräteversion oder die Uptime mit zu den Readings zählt), oder die Readings von anderen Geräten (Clients) weiterleiten (Beispiel: FHZ1300).
Requests for Proposal
Aktualisierung der Readings
Aktive und passive Readings
Es sind zwei Arten von Readings zu unterscheiden:
- Readings, die vom Gerät aktiv mitgeteilt werden, entweder ad-hoc oder in regelmäßigen Zeitabständen (aktive Readings). Beispiele: measuredTemp bei FHT80B, wind bei KS300
- Readings, nach denen fhem die Geräte fragen muss (passive Readings). Beispiele: energyDay bei EM1010PC, counter bei M232, temperature bei OWTEMP
Dasselbe Gerät kann sowohl aktive als auch passive Readings beinhalten. Bei Geräten mit aktiven Readings sind passive Readings meist Informationen wie Versionsnummer oder Uptime (CUL, CM11).
Geräte, die mehrere verschiedene Readings haben, senden diese häufig im Batch an fhem (Beispiel: FHT80b, KS300).
Mechanismus für aktive Readings
TODO: Mechanismus mit ParseFn, GetFn, Match usw. beschreiben (wie in fhem 4.x)!
Mechanismus für passive Readings
Für passive Geräte gibt es eine Polling-Infrastruktur.
Status Quo in fhem 4.x:
In der Routine DEVICE_Define wird ein interner Timer gestartet, der die Updatefunktion aufruft. INTERVAL ist die Periode in Sekunden.
sub DEVICE_Define($$) { ... InternalTimer(gettimeofday()+$hash->{INTERVAL}, "DEVICE_GetUpdate", $hash, 0); ... }
In der Routine DEVICE_GetUpdate werden dann die Readings vom Gerät geholt und der Timer wird mit dem gleichen Befehl erneut gestartet.
sub DEVICE_GetUpdate($$) { ... # start internal timer; do it at the beginning to achieve equal intervals no matter how long it takes to gather data InternalTimer(gettimeofday()+$hash->{INTERVAL}, "DEVICE_GetUpdate", $hash, 1); # gather data ... }
Der letzte Paramater 0 oder 1 ist waitIfInitNotDone.
Fragen:
- Kann/soll dieses Verfahren in einem Modul gekapselt werden? Geräte melden sich dann an der Polling-Infrastruktur an. Wenn ja, wie?
- Wie wird waitIfInitNotDone korrekt eingesetzt?
Mechanismus für langsame Readings
Problemstellung: Längere Verarbeitungsprozesse in einzelnen Modulen (z.B. aufgrund von Netzwerklatenzen oder toten Geräten) halten fhem komplett an und verhindern die Verarbeitung von Events.
Lösung: Multiprocessing
Beim Multiprocessing wird der fhem-Prozess geforkt, wenn ein Reading vom Gerät eingelesen werden soll. Der Vaterprozess wird sofort fortgesetzt und erledigt weitere Aufgaben. Der Kindprozess holt die Readings vom Gerät, liefert sie an den Vaterprozess und verendet.
Die Lieferung des Resultats an den Vaterprozess erfolgt durch eine Loopback Verbindung zu fhem via localhost.
Dieser Mechanismus wurde durch Rudolf König in ein entsprechendes Modul verpackt, so dass jedes andere FHEM-Modul diese Möglichkeiten direkt nutzen kann. Eine nähere Erklärung sowie eine Anleitung dazu findet man in dem Artikel → Blocking Call
Fragen:
- Soll es diesen Mechanismus nur passive Readings geben oder gibt es Anwendungsfälle für aktive Readings?
Verworfene Alternative: Multithreading
Argumente gegen Multithreading:
- Wird praktisch auf keiner kleinen Plattform unterstützt.
- Negative Erfahrungen (z.B. Probleme bei vielen 3rd-party-Komponenten)
- Kaum Programmiererfahrung mit Multithreading in Perl.
Verabschiedete Richtlinien
Zeichensatz
Alle nach aussen sichtbaren Werte sind in der Zeichenkodierung ASCII (Codes 32..127). Dies vermeidet Konvertierungsprobleme bei der Bearbeitung des Quellcodes, bei der Ausgabe durch Perl, beim Transport in Internet-Protokollen und bei der Darstellung im Frontend/GUI.
Klassifizierung der Attribute
Die Attribute eines Moduls werden in logische Klassen (Rubriken) eingeteilt.
Sinn und Zweck
Die Rubriken helfen...
- zu sortieren, welche Attribute bei (xml)list gezeigt werden und welche nicht,
- zu definieren, welche Werte per save in die Sicherung kommen und welche nicht,
- zu vereinbaren, welche Änderungen ein notify auslösen und welche nicht,
- zu definieren, wie ein Attribut im $hash->{} abgelegt wird (in $hash->{}, $hash->{READINGS}, $hash->{INTERNALS}, ...),
- zu gliedern, welche Namenskonventionen jeweils für Attribute verwendet werden (Kleinbuchstaben, Großbuchstaben, CamelCaps, ...),
- abzugrenzen, wo der Entwickler sich an Vorgaben halten muss (z.B. bei System Internals) und wo er frei ist, Attribute zu erfinden oder wegzulassen, und
- festzulegen, wo bzgl. der Inhalte Standards notwendig sind und wo nicht (z.B. bei "Logical Readings", wenn diese im GUI angezeigt werden sollten).
Logische Rubriken
Die folgenden Rubriken stellen eine sehr feine Einteilung dar:
- System Internals: werden vom Framework (fhem.pl) benötigt/verwendet/erkannt, z.B. NAME, NR, CHANGED
- Device Readings: Rohdaten, die vom physischen Gerät ausgelesen werden (nicht interpretiert), z.B. rain_raw (Wippenschläge des Regensensors) in KS300
- Hilfsvariablen: z.B. rain_raw_adj (rain_raw, jedoch bereinigt um die bei einigen KS300 auftretenden erratischen Sprünge) in KS300, Zwischenergebnisse von Daten/Werten, die zur Mittelwertberechnung benötigt werden
- Logical Readings: ausgewertete Messdaten und Zustände, z.B. Temperatur, Niederschlag (nicht als Wippenschläge sondern in l/qm)
- Derived Readings: aus den Messdaten abgeleitete Werte, z.B. durchschnittliche Temperatur der laufenden Woche, Niederschlagsmenge des Tages
- Physical Device Parameter: z.B. Modell oder Housecode und Unitcode bei X10 oder die Sensornummer beim BS (brightness sensor)
- Logical Device Parameter: statische Werte, die als Grundlage für Berechnungen genutzt werden, z.B. Korrekturfaktoren bei den Energiemessgeräten, die Tankgeometrie beim USF1000
- Messages: alle Nachrichten, die das Gerät betreffen, z.B. "AVG_Month erfolgreich berechnet", "Script XYZ am xx.xx.xx gestartet", LastIODEV, LastRAWMSG
- Trigger: wenn Trigger direkt am Gerät hinterlegt werden, müssen nicht mehr alle Notifies durchsucht werden, sondern es kann das Modul direkt ermitteln, ob ein Notify ausgelöst wird.
Ablage im Programm
Programmtechnisch werden die Attribute eines Moduls so in Behältern abgelegt, dass folgende Kriterien erfüllt sind:
- Die Aufteilung ist möglichst einfach/minimalistisch.
- Anhand des Behälters lässt sich entscheiden,
- ob die Werte der darin enthaltenen Attribute über das Programmende hinaus gespeichert oder nicht gespeichert werden,
- welchen Anzeigestandards oder Programmierstandards die darin enthaltenen Attribute entsprechen, und
- ob es sich um fhem-interne Attribute, Readings oder Hilfsvariablen handelt.
Es gibt folgende Behälter:
readings
- Logische Rubrik: Device Reading, Logical Reading, Derived Reading
- Wird gespeichert
- Alle vom Gerät gelieferten bzw. berechneten Werte, die den Endanwender interessieren. Keine Rohdaten vom Gerät, die erst interpretiert werden müssen (Wippenschläge beim Regensensor).
- Alle Daten haben einen Wert und ein Zeitstempel
- Beispiele:
$defs{Lampe}{readings}{switchedTo}{value} = "on"; $defs{Lampe}{readings}{switchedTo}{time} = "2010-03-29 23:32:26" $defs{FHToben}{readings}{measuredTemp}{value} = "23"; $defs{FHToben}{readings}{switchedTo}{time} = "2010-03-29 21:58:36"
helper
- Logische Rubrik: Hilfsvariable, Device Reading
- Wird nicht gespeichert
- Alle Werte, die für den Endanwender nicht direkt sinnvoll sind aber vom Modul für unterschiedliche Zwecke benötigt werden. Kann auch rohe Readings enthalten.
- Beispiele:
$defs{ks300}{helper}{cumMonth} = "23 T: 131.4 H: 816 W: 775.1 R: -651.1" $defs{emwz}{helper}{basis} = "4776493"
fhem
- Logische Rubrik: System Internals, Physical Device Parameter, Logical Device Parameter
- Wird nicht gespeichert
- Alle Geräte-Werte, die nicht gespeichert werden müssen, weil entweder berechnet, oder aus der Definition hervorgehen.
- Beispiele
$defs{emwz}{fhem}{def} = "1 75 900" $defs{emwz}{fhem}{lastIODev} = "CUL"
Verworfener Behälter STATE
- Logische Rubrik: Device Reading, Logical Reading, Derived Reading
- Wird gespeichert
- Kurze(!) Zusammenfassung der wichtigsten Information des Geräts. Was man in einer Übersicht über die Geräte sehen möchte, z.B. eine Warnung oder die aktuelle Temperatur beim FHT80 oder der Zeitpunkt der letzten Aktivierung beim PIRI. Falls es das nicht gibt, dann einheitlich "defined". GGf. über Attribute am Modul steuerbar. Eigentlich ist es kein Behälter im üblichen Sinne, da es keine Untereinheiten hat.
Der Behälter wurde verworfen, weil er redundant ist, an sich gar kein Behälter, und einfacher und flexibler über einen Verweis auf das Reading realisiert werden kann, der per Default angezeigt werden soll.
Status
Kurze Zusammenfassung der wichtigsten Information des Geräts. Was man in einer Übersicht über die Geräte sehen möchte, z.B. eine Warnung oder die aktuelle Temperatur beim FHT80 oder der Zeitpunkt der letzten Aktivierung beim PIRI. Falls es sowas nicht gibt, dann einheitlich "defined". Modulseitig gibt es einen Default, der auf das relevante Reading verweist. Dieser ist über das Attribut defaultReading am individuellen Gerät steuerbar.
Beispiel: Definition in der Konfigurationsdatei: attr myDevice defaultReading measuredTemp Verwendung im Code:
$defs{myDevice}{readings}{$attr{defaultReading}}
Die logischen Rubriken Messages und Trigger wurden noch nicht diskutiert.
Bezeichnungen
Verwendung von lowerCamelCaps für alle vom Modul nach aussen sichtbaren Bezeichner: a) die Bezeichnungen der Behälter für Readings, Fhem und Helper und der Untereinträge, b) die Bezeichnungen der Readings, c) die Bezeichnungen der Attribute.
Beispiel:
$defs{myFHT}{readings}{measuredTemp}{time}
Readings
Standardisierung der Readings
Die Readings werden standardisiert, indem Geräteklassen gebildet werden wie in DevelopmentInterfaces beschrieben. Die Definition der Interfaces ist explizit nicht Gegenstand dieser Entscheidungsvorlage und wird weiterentwickelt und angepasst, wie es sich bei der Entwicklung von fhem-NEU ergibt. Struktur im Code
Zu jedem Reading gibt es die folgenden Unterbehälter:
- value: Wert der Messgröße
- time: Zeitpunkt, zu dem der Wert ermittelt wurde
Beispiel:
$defs{myFHT}{readings}{measuredTemp}{time}= timestamp; $defs{myFHT}{readings}{measuredTemp}{value}= 23.5;
Readings enthalten grundsätzlich genau einen Wert und diesen ohne Einheit.
Einheitendarstellung
Die verwendete Einheit für ein Reading ergibt sich aus der Interfacespezifikation.
Beispiel: Gerät ist ein Thermometer => es gibt ein Reading "temperature" und dessen Einheit ist immer °C oder Celsius
Verworfene Alternative:
- Einheit ergibt sich aus der Dokumentation
- Einheit ergibt sich aus einem expliziten Untereintrag $defs{deviceName}{readings}{theReading}{unit}="°C"
- Einheit ergibt sich aus Festlegung gemäß "FHEM-Standard", z.B. immer SI-Einheiten, Temperaturen in Celsius, etc.
BatteryReadings
Es gibt nur diese drei Readings für den Batteriestatus:
- batteryState
- batteryPercent
- batteryVoltage
Wertebereich:
- batteryState: ok|low
- batteryPercent: \d{1,2}|100
- batteryVoltage: \d+.\d+
Wichtig: das jeweilige Modul setzt nur die Readings, die es aus den aktuellen Daten vom Geraet bestimmen kann. Konkret: niemand kann sich darauf verlassen, welche der drei battery Readings vorhanden sind (es gibt nicht überall ein batteryState). Wenn das Gerät früher ein Percent gemeldet hat, aber in der letzten Nachricht nur state, dann wird das Percent Reading nicht angefasst. Aktuell wird noch diskutiert, was mit dem Reading für Laden ist. Mehr dazu hier: Beitrag im FHEM Forum
Zeitdarstellung in fhem
Zeiten werden im Programm grundsätzlich immer maschinenlesbar abgelegt, also sowohl in $defs{deviceName}{readings}{time} als auch z.B. bei der Speicherung des Zeitpunkts der Ausführung des nächsten at-Kommandos.
Es gibt übergreifende Hilfsfunktionen, die die maschinenlesbare in eine menschenlesbare Darstellung umwandeln.
Argumente für maschinenlesbar:
- vereinfacht Datumsarithmetik, z.B. die Berechnung der Zeitdauer zwischen zwei Ereignissen
- kann vom Frontend in die regionale Darstellung des Anwenders übersetzt werden
- hat keine Probleme mit doppelt auftretenden Zeitpunkten bei der Umstellung von Sommer- auf Winterzeit
Folgende Zeitdarstellungen werden ausschliesslich verwendet:
A. Zahl der Sekunden seit der Unix-Epoche (was time liefert); auch wenn time eine Ganzzahl liefert, muss der Verwender damit rechnen, eine Gleitkommazahl vorzufinden. Das ist z.B. dann der Fall, wenn für die Zeitbestimmung höher auflösende Funktionen (z.B. Time::HiRes) zum Einsatz kamen und Sekundenbruchteile mitgespeichert wurden.
Hinweis zu potentiellen Problemen:
- Ab perl 5.12 ist das Jahr-2038-Problem in Perl beseitigt.
- Ab Mac OS X liefert time auch auf Macs die Zahl der Sekunden seit der Unix-Epoche (statt seit Anfang 1904)
B. ISO8601 mit optionaler Zeitzonenangabe, wobei bei fehlender Zeitzone die lokale Zeitzone des fhem-Servers gilt
Die Zeitdarstellungen werden wie folgt verwendet:
- Zur Programmlaufzeit in Variablen immer A
- In Konfigurationsdateien (fhem.conf, fhem.save) immer B
- In Logs, die für den Menschen bestimmt sind, B
- In Logs, die für die Maschine bestimmt sind, also z.B. bei Weiterverarbeitung in einem GUI, A
- In Listen (list, xmllist friendly), die für den Menschen bestimmt sind, B
- In Listen (xmllist), die für die Maschine bestimmt sind, also z.B. bei Weiterverarbeitung in einem GUI, A
Die Unterscheidung bei den Logs ist noch zu diskutieren.
Hinweise zu gnuplot:
set timefmt '%Y-%m-%dT%H:%M:%S'
(ISO8601) funktioniert, und zwar unabhängig davon, ob die optionale Zeitzonenangabe anhängt oder nicht (getestet mit Gnuplot v4.2 pl3). Für die Sekunden seit der Unix-Epoche:
set timefmt '%s'
(nicht getestet).
Allgemeine Dokumentation
Events, Filter, Notify
Event
Ein Event tritt ein, wenn ein oder mehrere Readings eines Geräts aktualisiert werden, weil z.B. ein Datagramm empfangen oder zeitgesteuert Werte eingelesen wurden. Es wird durch drei Angaben
(device, timestamp, { reading1, ..., readingN } )
beschrieben:
- das Gerät device
- der Zeitpunkt der Aktualisierung timestamp
- die Menge der N>= 1 geaenderten Readings reading1, .. readingN
Wenn N> 1, dann handelt es sich um ein Sammelevent, wie es z.B. von KS300 generiert wird.
Filter
Ein Filter filtert Events. Er hat die Form
deviceNamePattern
bzw.
deviceNamePattern:readingNamePattern
Ein Filter passt auf ein Event, wenn deviceNamePattern den Namen des device matcht und, sofern vorhanden, readingNamePattern mindestens irgendeinen Namen von reading1 bis readingN matcht.
Mechanismus
0. Für das Gerät device namens deviceName wird ein Datagramm empfangen oder es werden zeitgesteuert Werte eingelesen.
1. Die Readings werden aktualisiert. Für X= 1..N:
$defs{deviceName}{readings}{readingX}{value}= valueX; $defs{deviceName}{readings}{readingX}{time}= timestamp;
2. Das Event wird erstellt und an DoTrigger übergeben.
3. DoTrigger informiert zunächst alle Clients mit inform-Wunsch.
4. Das Event wird sodann von DoTrigger nacheinander an alle NotifyFn in der alphabetischen Reihenfolge der Gerätenamen gereicht. Darunter auch jene von Logs.
5. Wenn der Filter des Logs auf das Event passt, wird ein Log-Eintrag der Form
timestamp deviceName reading1: value1 reading2: value2 ... readingN: valueN
erzeugt.
Zurückgestellte Entscheidungen
Attribute vs. Internals
Die Unterscheidung zwischen Attributen und Internals ist nicht eindeutig. Manche define-Parameter sind optional, und man kann define-Parameter mit "modify" ändern. Insofern könnte man theoretisch einen der beiden Verfahren (modify vs. attribute) ablösen.
1. Idee: define-Parameter dürfen nachträglich nicht geändert werden, auch wenn es sich um optionale Parameter handelt => nicht-modifizierbare Internals
Pros:
- define-Parameter sind elementar für den Betrieb des Geräts und können nicht sinnvoll zur Programmlaufzeit geändert werden (z.B. Hauskode bei X10, FHTId bei FHT80b)
- Aus den define-Parametern werden bei der Initialisierung des Geräts weitere Helper und Internals abgeleitet und gespeichert. Eine nachträgliche Änderung zieht Änderungen der Helper und Internals nach sich mit ggf. schwer durchschaubaren Nebeneffekten (z.B. corr1..corr4 bei EM, rainadjustment bei KS300). Auch die Logs ändern sich nicht nachträglich.
Contras:
- Man will nicht ein Gerät löschen und neu anlegen, wenn es ausgetauscht wird.
- modify sollte bleiben, weil es nützlich ist.
2. Idee: Attribute enthalten Werte, die ohne Nebeneffekte zur Laufzeit geändert werden können, weil sie z.B. ad-hoc ausgewertet werden.
- follow-on-for-timer
- retrycount
- lazy
3. Idee: Attribute beinhalten geräteunabhängige Meta-Informationen:
- room
- defaultReading
Contras:
- Nach 2. und 3. müsste
attr my_at disabled
doch zu Definition gehoeren. Und "skip_next" auch.
Spezialitäten
- beim FS20 bleibt alles, wie es ist, also model als Attribut, was Auswirkung auf die möglichen set Befehle hat.
- 1wire setzt nach define das model Attribut, lässt aber eine Änderung nicht zu, oder wenn doch, dann muss sich dementsprechend verhalten, es macht ja evtl. Sinn es zu ändern. Was machbar ist, entscheidet der Modul-Author.
Damit ist "model" immer ein Attribut.
Fazit
Da es keine überzeugende Alternative zum Ist-Zustand in fhem 4.x gibt, wird b.a.w. nicht hierüber entschieden.
notify
Die Frage, ob der notify-Mechanismus, der weiter oben dokumentiert ist, aus Performancegründen optimiert werden sollte, ist offen.
In fhem 4.x bekommt jedes Gerät mit NotifyFn jedes Event mit. Alternativ könnte sich ein Gerät als EventListener mit einem Filter bei fhem anmelden. Dann könnten die Events nur an die NotifyFn verteilt werden, für die es ein Match auf den Filter gaebe.
Der Punkt wurde zurückgestellt, bis nachgewiesen ist, dass sich aus dieser Vorgehensweise relevante Auswirkungen auf die Systemlast ergeben.
Entscheidungen
- E1
- Es werden die Container fhem, readings und helper verwendet.
- E2
- Es wird ein Attribut defaultReading verwendet.
- E3
- Verwendung von lowerCamelCaps für a) die Bezeichnungen der Behälter für Readings, Fhem und Helper und der Untereintraege, b) die Bezeichnungen der Readings, c) die Bezeichnungen der Attribute.
- E4
- Verwendung von value und time.
- E5
- Zeitdarstellung im Programm grundsätzlich maschinenlesbar
- E6
- Zulässige Zeitdarstellungen
- Sekunden seit der Unix-Epoche
- ISO8601 mit optionaler Zeitzonenangabe
ISO8601 mit _ statt T war nicht mehrheitsfähig.
- E7
- Verwendung der Zeitdarstellungen
- E8
- Readings enthalten grundsätzlich genau einen Wert und diesen ohne Einheit.
- E9
- Einheiten ergeben sich aus der Interfacespezifikation.
- E10
- Entscheidung für ASCII
- E11
- Die Readings werden standardisiert, indem Geräteklassen gebildet werden wie in DevelopmentInterfaces beschrieben. Die Definition der Interfaces ist explizit nicht Gegenstand dieser Entscheidungsvorlage und wird weiterentwickelt und angepasst, wie es sich bei der Entwicklung von fhem-NEU ergibt.