DevelopmentIntroduction

Aus FHEMWiki
Wechseln zu: Navigation, Suche

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:

  1. Application variables are initialised
  2. The valid application commands are loaded into a structure (%cmds) with the corresponding function name to call
  3. Do the job, either act as client or server. One of the following applies:
    1. If the application has been called as a FHEM client (eg perl fhem.pl 127.0.0.1:7072 commandToExecute)
      1. Connect to the FHEM server
      2. Execute the command
      3. Exit the program
    2. Otherwise (the application has been started as a server)
      1. Execute each of the commands listed in the configuration file to initialise the server (see the CommandInclude and then the AnalyzeCommandChain methods)
      2. 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
      3. Start the main application loop

Main Application Loop

The following code is repeated until the application ends:

  1. For each input device (eg FHZ1000 / FHZ1300 / CUL):
    1. Use that device module's Ready and Read functions to read data from the device
    2. For CUL devices, the data is read and parsed at this point
    3. For FHZ devices, the FHZ_CheckCRC method is used to validate the received data
    4. 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:

  1. Read data from the client using sysread
  2. 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.