DevelopmentIntroduction

Aus FHEMWiki

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. 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
  • 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 readingsEndUpdateaktualisiert $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 DoTriggerwerden

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