DevelopmentIntroduction
Developer's Introduction to FHEM
Introduction
This page is intended to give an introduction to the FHEM application from a developer's perspective. If you're interested in extending, modifying or reusing the FHEM code, then hopefully you will find this a useful document. Note that this document is a work in progress! I myself at the time of writing this have only a few hours of experience under my belt, but I'm documenting things I learn as I learn them.
Application Lifecycle
When you first launch the FHEM server, the following activities take place:
- Application variables are initialised
- The valid application commands are loaded into a structure (%cmds) with the corresponding function name to call
- Do the job, either act as client or server. One of the following applies:
- If the application has been called as a FHEM client (eg perl fhem.pl 127.0.0.1:7072 commandToExecute)
- Connect to the FHEM server
- Execute the command
- Exit the program
- Otherwise (the application has been started as a server)
- Execute each of the commands listed in the configuration file to initialise the server (see the CommandInclude and then the AnalyzeCommandChain methods)
- Execute each of the commands in the state file if configured to restore the devices to the last state recorded when the server last ran
- Start the main application loop
- If the application has been called as a FHEM client (eg perl fhem.pl 127.0.0.1:7072 commandToExecute)
Main Application Loop
The following code is repeated until the application ends:
- For each input device (eg FHZ1000 / FHZ1300 / CUL):
- Use that device module's Ready and Read functions to read data from the device
- For CUL devices, the data is read and parsed at this point
- For FHZ devices, the FHZ_CheckCRC method is used to validate the received data
- Call the Dispatch method in FHEM.pl to pass the message onto the correct device and corresponding module (Parse function)
$server->fileno() is used to check for and accept new connections to the server For each connected client:
- Read data from the client using sysread
- Pass the received input into the AnalyzeInput method to execute the command
Configuration and State Files
Note that these are just lists of commands to execute in a text file! So anything in these files will be interpreted by FHEM in the exact same way if a user keys the commands manually into a client application, eg a telnet connection.
Readings
The following mechanism is recommended for updating readings. It saves lines of codes and automatically makes your module honor the event-on-update-reading and event-on-change-reading attributes.
Before you start updating readings, write (in case $hash = $defs{<device>})
readingsBeginUpdate($hash);
For every reading you update, write
readingsBulkUpdate($hash,$reading,$value);
Terminate with
readingsEndUpdate($hash, $dotrigger);
The $dotrigger parameter should be 0 for updates initiated by fhem message dispatcher as the dispatcher already calls DoTrigger on its behalf. $dotrigger should be 1 for updates initiated by internal timers, e.g. for polling devices.
As a shorthand notation for updating just one reading write
readingsSingleUpdate($hash,$reading,$value,$dotrigger);
which is the same as
readingsBeginUpdate($hash); readingsBulkUpdate($hash,$reading,$value); readingsEndUpdate($hash, $dotrigger);
STATE
Vorschlag zur Umsetzung, diskutiert hier.
In den DevelopmentGuidelines, Kapselung, Standardisierung der Vorgehensweise.
Die Funktion readingsEndUpdate aktualisiert $hash->{STATE} grundsätzlich immer bei jedem Aufruf nach folgendem Algorithmus:
Falls $sr= $attr{$name}{stateReading} gesetzt ist:
Fall 1: wenn $sr =~ "^{.*}$" dann eval "\$hash->{STATE} = $sr"; Fall 2: sonst $hash->{STATE}= $hash->{READINGS}{$sr}{VAL}
sonst
Falls es $hash->{READINGS}{state} gibt, $hash->{STATE}= $hash->{READINGS}{state}{VAL}
sonst
Tue nichts (das Modul hat sich gekuemmert).
Ausserdem wird
ReplaceEventMap()
losgelassen, um die EventMaps des Anwenders auch im STATE zur Anwendung zu bringen.
Aus DoTrigger werden
$defs{$dev}{STATE} = ReplaceEventMap($dev, $defs{$dev}{STATE}, 1);
und
# STATE && {READINGS}{state} should be the same my $r = $defs{$dev}{READINGS}; $r->{state}{VAL} = $defs{$dev}{STATE} if($r && $r->{state});
entfernt.
Global Attributes
In fhem.pl existiert ein
my $AttrList = "room comment alias ...";
Weiterhin kann jedes Modul im Initialize mit addToAttrList eigene Attribute zu global userattr hinzufuegen.