Suche Möglichkeit, Daten über Socket/TCP in FHEM zu bekommen

Begonnen von Cruiser79, 10 Januar 2016, 15:18:49

Vorheriges Thema - Nächstes Thema

Cruiser79

Moin moin,

ich suche eine Möglichkeit von Devices Daten in FHEM zu pumpen. Das ganze soll z.B. per HTTP passieren. Momentan kenne ich nur Möglichkeiten von FHEM Daten von Geräten z.B. über HTTPMOD abzufragen, oder DevIO für dauerhafte Verbindungen zu einem Device. Ich will aber gerne, dass das Device seine Daten sendet, wenn es gerade aktiv ist, da es somit zeitweise schlafen könnte. Ist das überhaupt so in FHEM möglich, oder kann ich mit FHEM nur Abfragen VON FHEM aus machen und keine Werte IN ein lauschendes Modul pushen?

Gruß,
Tim
FHEM auf Raspberry Pi
HM-CFG-LAN mit HM-TC-IT-WM-W-EU, HM-CC-RT-DN, HM-WDS10-TH-O, HM-LC-SW1-FM, HM-LC-Bl1-FM
Signalduino mit Elro AB440, LOGILINK WS0002, IT CMR-1000

Markus Bloch

Hallo Tim,

doch, indem du ein Modul schreibst, was mit Hilfe von TcpServerUtils.pm einen TCP Port öffnest und dann lauschst auf eingehende Verbindungen und deren Daten. Alternativ geht es auch über einen telnet-Zugang, sofern du deinem Gerät sagen kannst, dass es ein "setreading <name> <reading> <value>" schicken soll.

Viele Grüße
Markus

Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

Loredo

einige meiner Module arbeiten so und können da inspirieren:

HP1000
GEOFANCY
Pushover


Gruß
Julian
Hat meine Arbeit dir geholfen? ⟹ https://paypal.me/pools/c/8gDLrIWrG9

Maintainer:
FHEM-Docker Image, https://github.com/fhem, Astro(Co-Maintainer), ENIGMA2, GEOFANCY, GUEST, HP1000, Installer, LaMetric2, MSG, msgConfig, npmjs, PET, PHTV, Pushover, RESIDENTS, ROOMMATE, search, THINKINGCLEANER

smurfix

Eine weitere Möglichkeit wäre, die Daten über MQTT zu schicken. FHEM kann das bereits, d.h. du müsstest kein eigenes Modul schreiben.

Cruiser79

Zitat von: Markus Bloch am 10 Januar 2016, 15:34:09
Hallo Tim,

doch, indem du ein Modul schreibst, was mit Hilfe von TcpServerUtils.pm einen TCP Port öffnest und dann lauschst auf eingehende Verbindungen und deren Daten. Alternativ geht es auch über einen telnet-Zugang, sofern du deinem Gerät sagen kannst, dass es ein "setreading <name> <reading> <value>" schicken soll.

Viele Grüße
Markus

Ah, super, das hört sich passend an. Es geht um einen ESP8266, dessen Daten ich an FHEM senden will, insofern kann ich da auch ein setreading reinprogrammieren.

Zitat von: Loredo am 10 Januar 2016, 15:45:42
einige meiner Module arbeiten so und können da inspirieren:

HP1000
GEOFANCY
Pushover


Gruß
Julian

Danke, werde ich mir dann mal ansehen.

Zitat von: smurfix am 10 Januar 2016, 15:46:52
Eine weitere Möglichkeit wäre, die Daten über MQTT zu schicken. FHEM kann das bereits, d.h. du müsstest kein eigenes Modul schreiben.

MQTT kenne ich noch nicht. Wenn ich das richtig verstehe, brauche ich da ja aber einen MQTT-Server, an dem das Device die Daten schickt und die dann FHEM abholt, oder kann FHEM dieser MQTT-Broker sein?
FHEM auf Raspberry Pi
HM-CFG-LAN mit HM-TC-IT-WM-W-EU, HM-CC-RT-DN, HM-WDS10-TH-O, HM-LC-SW1-FM, HM-LC-Bl1-FM
Signalduino mit Elro AB440, LOGILINK WS0002, IT CMR-1000

Ed66

Mit so eine Lösung könnte vielleicht auch diese billige CO2 sensor in FHEM integriert werden.

http://vair-monitor.com

Grusse,
Ed

Cruiser79

Zitat von: Loredo am 10 Januar 2016, 15:45:42
einige meiner Module arbeiten so und können da inspirieren:

HP1000
GEOFANCY
Pushover


Gruß
Julian

Moin Julian,

Ich werde aus dem Quellcode noch nicht so ganz schlau. Diese addExtension Funktionen scheinen der Kern der Sache zu sein. Aber was ist dieses FWEXT? Mit welchem Aufruf bekommt man denn dann die Daten in das Modul?
Gibt es dazu irgendwo noch weitere Dokus oder Anleitungen?

Gruß,
Tim
FHEM auf Raspberry Pi
HM-CFG-LAN mit HM-TC-IT-WM-W-EU, HM-CC-RT-DN, HM-WDS10-TH-O, HM-LC-SW1-FM, HM-LC-Bl1-FM
Signalduino mit Elro AB440, LOGILINK WS0002, IT CMR-1000

Loredo

FWEXT steht wohl für FHEMWEB Extension. FHEMWEB ist der Webserver, den FHEM mitbringt.
In den Funktionen GEOFANCY_addExtension() bzw GEOFANCY_removeExtension() wird die eine URI bei FHEMWEB "registriert" (bzw. wieder entfernt) und dabei eine Funktion angegeben, die von FHEMWEB aufgerufen wird, sobald ein GET oder POST Event auf dieser URI eingegangen ist (im Falle von GEOFANCY ist das die Funktion GEOFANCY_CGI()). Dort findet dann die weitere Verarbeitung der Daten statt. Dabei ist es natürlich wichtig ganz penibel die Eingangsdaten zu verifizieren, weil man FHEM sonst mit einer falschen Anfrage von außen leicht zum Absturz bringen oder falsche Daten einschleusen kann. Wichtig ist auch zu wissen, dass man einen return-Wert erzeugen muss und dieser 1zu1 an den Browser zurückgeschickt wird. Dabei befindet man sich auf Layer3, sprich man muss hier nicht nur den HTML Body Text angeben, sondern auch den Header Text. Deshalb erfolgt der return-Wert als ARRAY und dort steht im ersten Teil "text/plain; charset=utf-8", damit der Browser den Body-Text entsprechend interpretieren kann.
Hat meine Arbeit dir geholfen? ⟹ https://paypal.me/pools/c/8gDLrIWrG9

Maintainer:
FHEM-Docker Image, https://github.com/fhem, Astro(Co-Maintainer), ENIGMA2, GEOFANCY, GUEST, HP1000, Installer, LaMetric2, MSG, msgConfig, npmjs, PET, PHTV, Pushover, RESIDENTS, ROOMMATE, search, THINKINGCLEANER

Cruiser79

Zitat von: Loredo am 18 Januar 2016, 11:41:06
FWEXT steht wohl für FHEMWEB Extension. FHEMWEB ist der Webserver, den FHEM mitbringt.
In den Funktionen GEOFANCY_addExtension() bzw GEOFANCY_removeExtension() wird die eine URI bei FHEMWEB "registriert" (bzw. wieder entfernt) und dabei eine Funktion angegeben, die von FHEMWEB aufgerufen wird, sobald ein GET oder POST Event auf dieser URI eingegangen ist (im Falle von GEOFANCY ist das die Funktion GEOFANCY_CGI()). Dort findet dann die weitere Verarbeitung der Daten statt. Dabei ist es natürlich wichtig ganz penibel die Eingangsdaten zu verifizieren, weil man FHEM sonst mit einer falschen Anfrage von außen leicht zum Absturz bringen oder falsche Daten einschleusen kann. Wichtig ist auch zu wissen, dass man einen return-Wert erzeugen muss und dieser 1zu1 an den Browser zurückgeschickt wird. Dabei befindet man sich auf Layer3, sprich man muss hier nicht nur den HTML Body Text angeben, sondern auch den Header Text. Deshalb erfolgt der return-Wert als ARRAY und dort steht im ersten Teil "text/plain; charset=utf-8", damit der Browser den Body-Text entsprechend interpretieren kann.

Danke für die Erklärung, habe ich auch damit hinbekommen eine Extension zu bauen. Jetzt habe ich nur das Problem, das zwar ein Wert, den ich über die Extension an FHEM übergebe, in das Reading eingetragen wird, aber im meinem Logfile, das ich gleichzeitig auf dem Modul sitzen habe, kommen immer 2 mal die gleichen Readings mit der gleichen Uhrzeit und dem gleichen Wert.
Vielleicht hat da noch jemand von euch die Lösung, was das ist.
Mein Code:

# $Id$
##############################################################################
#
#     22_GasCounter.pm
#     An FHEM Perl module to receive data from NodeMCU.
#
#     Copyright by Me
#
#     This file is part of fhem.
#
#     Fhem is free software: you can redistribute it and/or modify
#     it under the terms of the GNU General Public License as published by
#     the Free Software Foundation, either version 2 of the License, or
#     (at your option) any later version.
#
#     Fhem is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.
#
#     You should have received a copy of the GNU General Public License
#     along with fhem.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

package main;

use strict;
use warnings;
use vars qw(%data);
use HttpUtils;
use Time::Local;
use Data::Dumper;

no if $] >= 5.017011, warnings => 'experimental::smartmatch';

sub GasCounter_Define($$);
sub GasCounter_Undefine($$);
 
#########################
sub GasCounter_addExtension($$$) {
    my ( $name, $func, $link ) = @_;

    my $url = "/$link";
    Log3 $name, 2, "Registering GasCounter $name for URL $url...";
    $data{FWEXT}{$url}{deviceName} = $name;
    $data{FWEXT}{$url}{FUNC}       = $func;
    $data{FWEXT}{$url}{LINK}       = $link;
}

#########################
sub GasCounter_removeExtension($) {
    my ($link) = @_;

    my $url  = "/$link";
    my $name = $data{FWEXT}{$url}{deviceName};
    Log3 $name, 2, "Unregistering GasCounter $name for URL $url...";
    delete $data{FWEXT}{$url};
}

###################################
sub GasCounter_Initialize($) {
    my ($hash) = @_;

    Log3 $hash, 5, "GasCounter_Initialize: Entering";

    $hash->{DefFn}    = "GasCounter_Define";
    $hash->{UndefFn}  = "GasCounter_Undefine";
    $hash->{AttrList} = $readingFnAttributes;
}

###################################
sub GasCounter_Define($$) {

    my ( $hash, $def ) = @_;

    my @a = split( "[ \t]+", $def, 5 );

    return "Usage: define <name> GasCounter"
      if ( int(@a) < 2 );
    my $name  = $a[0];

    $hash->{fhem}{infix} = "GasCounter";

    # create global unique device definition
    $modules{GasCounter}{defptr} = $hash;

    GasCounter_addExtension( $name, "GasCounter_CGI", "GasCounter" );

    return undef;
}

###################################
sub GasCounter_Undefine($$) {

    my ( $hash, $name ) = @_;

    GasCounter_removeExtension( $hash->{fhem}{infix} );

    # release global unique device definition
    delete $modules{GasCounter}{defptr};

    return undef;
}

############################################################################################################
#
#   Begin of helper functions
#
############################################################################################################

###################################
sub GasCounter_CGI() {

    my ($request) = @_;

    my $hash;
    my $name    = "";
    my $link;
    my $URI;
    my $result = "";
    my $webArgs;

    # data received
    if ( $request =~ /^\/GasCounter\?(.+=.+)/ ) {
        $URI  = $1;

        # get device name
        $name = $data{FWEXT}{"/GasCounter"}{deviceName}
          if (defined($data{FWEXT}{"/GasCounter"}));

        # return error if no such device
        return ( "text/plain; charset=utf-8",
            "No GasCounter device for webhook /GasCounter" )
          unless ($name);

        # extract values from URI
        foreach my $pv ( split( "&", $URI ) ) {
            next if ( $pv eq "" );
            $pv =~ s/\+/ /g;
            $pv =~ s/%([\dA-F][\dA-F])/chr(hex($1))/ige;
            my ( $p, $v ) = split( "=", $pv, 2 );

            $webArgs->{$p} = $v;
        }
       
        return ("text/plain; charset=utf-8", "Insufficient data")
          if (!defined($webArgs->{value}))
    }

    # no data received
    else {
        return ( "text/plain; charset=utf-8", "Missing data" );
    }

    $hash = $defs{$name};

    readingsBeginUpdate($hash);

    #
    if (defined($webArgs->{value})) {
      readingsBulkUpdate( $hash, "Value", $webArgs->{value} );
    }

    $result = "Value: ".$webArgs->{value} if (defined($webArgs->{value}));

    readingsBulkUpdate( $hash, "state", $result );
    readingsEndUpdate( $hash, 1 );

    return ( "text/plain; charset=utf-8", "success" );
}

1;



Das definierte Modul
{
  "Arg":"GasCounter",
  "Results": [
  {
    "Name":"GasCounter",
    "PossibleSets":"",
    "PossibleAttrs":"verbose:0,1,2,3,4,5 room group comment:textField-long alias eventMap userReadings:textField-long event-on-change-reading event-on-update-reading event-aggregator event-min-interval stateFormat cmdIcon devStateIcon devStateStyle fp_myFloorplan icon sortby webCmd widgetOverride userattr",
    "Internals": {
      "CFGFN": "",
      "NAME": "GasCounter",
      "NR": "9362",
      "STATE": "Value: 1",
      "TYPE": "GasCounter"
    },
    "Readings": {
      "Value": { "Value":"1", "Time":"2016-02-09 22:19:25" },
      "state": { "Value":"Value: 1", "Time":"2016-02-09 22:19:25" }
    },
    "Attributes": {    }
  }  ],
  "totalResultsReturned":1
}


Das Filelog
{
  "Arg":"FileLog_GasCounter",
  "Results": [
  {
    "Name":"FileLog_GasCounter",
    "PossibleSets":"absorb:.....",
    "PossibleAttrs":"verbose:0,1,2,3,4,5 room group comment:textField-long alias eventMap userReadings:textField-long addStateEvent:0,1 archiveCompress archivecmd archivedir createGluedFile:0,1 disable:0,1 disabledForIntervals logtype nrarchive reformatFn cmdIcon devStateIcon devStateStyle fp_myFloorplan icon sortby webCmd widgetOverride userattr",
    "Internals": {
      "CFGFN": "",
      "DEF": "./log/GasCounter-%Y.log GasCounter",
      "NAME": "FileLog_GasCounter",
      "NOTIFYDEV": "GasCounter",
      "NR": "9394",
      "NTFY_ORDER": "50-FileLog_GasCounter",
      "REGEXP": "GasCounter",
      "STATE": "active",
      "TYPE": "FileLog",
      "currentlogfile": "./log/GasCounter-2016.log",
      "logfile": "./log/GasCounter-%Y.log"
    },
    "Readings": {    },
    "Attributes": {      "logtype": "text"    }
  }  ],
  "totalResultsReturned":1
}


Das gefüllte Logfile

2016-02-09_22:11:54 GasCounter Value: 22
2016-02-09_22:11:54 GasCounter Value: 22
2016-02-09_22:12:21 GasCounter Value: 22
2016-02-09_22:12:21 GasCounter Value: 22
2016-02-09_22:17:25 GasCounter Value: 22
2016-02-09_22:17:25 GasCounter Value: 22
2016-02-09_22:19:25 GasCounter Value: 1
2016-02-09_22:19:25 GasCounter Value: 1
FHEM auf Raspberry Pi
HM-CFG-LAN mit HM-TC-IT-WM-W-EU, HM-CC-RT-DN, HM-WDS10-TH-O, HM-LC-SW1-FM, HM-LC-Bl1-FM
Signalduino mit Elro AB440, LOGILINK WS0002, IT CMR-1000

justme1968

#9
du hast nicht zwei mal das gleiche event sondern ein mal ein event für das reading Value mit dem wert xy und ein mal für das reading state mit derm wert Value: xy. wenn du state so formatierst kannst du es nicht von Value unterscheiden weil das reading state 'speziell' ist und (normalerweise) kein reading name und : im event hat.

lass das state reading einfach weg und verwende stateFormat um in fhemweb etwas zu sehen.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Cruiser79

Zitat von: justme1968 am 09 Februar 2016, 23:11:33
du hast nicht zwei mal das gleiche event sondern ein mal ein event für das reading Value mit dem wert xy und ein mal für das reading state mit derm wert Value: xy. wenn du state so formatierst kannst du es nicht von Value unterscheiden weil das reading state 'speziell' ist und (normalerweise) kein reading name und : im event hat.

lass das state reading einfach weg und verwende stateFormat um in fhemweb etwas zu sehen.

gruss
  andre

Hehe, selbst reingelegt, wusste ich es doch  ;)
Das state kann ich auch ganz weglassen, brauche ich nicht. War nur wegen Copy&Paste des Moduls eh noch da.
Danke für die Aufklärung, aber wieso wird denn das state nicht mit state: Value: 22 im Log gelistet? Ist das das "spezielle" am state?

Gruß,
Tim
FHEM auf Raspberry Pi
HM-CFG-LAN mit HM-TC-IT-WM-W-EU, HM-CC-RT-DN, HM-WDS10-TH-O, HM-LC-SW1-FM, HM-LC-Bl1-FM
Signalduino mit Elro AB440, LOGILINK WS0002, IT CMR-1000

justme1968

wie oben geschrieben: bei state steht  der reading name nicht mit im event.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968