Smart Mirror / Infospiegel mit fhem-Daten

Begonnen von sven@allesclip.de, 03 März 2016, 17:55:21

Vorheriges Thema - Nächstes Thema

Wernieman

Habe mir einige Funktionen dafür geschrieben .. ich hoffe, Du versteht sie?
Habe bei mir eine Passwortabfrage drin, eventuell must Du die Passende Zeile rauslöschen

function FHEMConneckt()
{
  global $fhemsocket;
  $service_port = "7072";
  $address = gethostbyname('FHEM-Server-Adresse_oder_IP');
  $PW = "Passwort\n";
  $out = '';

  $fhemsocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  if ($fhemsocket === false) {
    echo "socket_create() fehlgeschlagen: Grund: " . socket_strerror(socket_last_error()) . "\n";
    exit;
  }

  #echo "Versuche, zu '$address' auf Port '$service_port' zu verbinden ...";
  $result = socket_connect($fhemsocket, $address, $service_port);
  if ($result === false) {
    echo "socket_connect() fehlgeschlagen: Grund: " . socket_strerror(socket_last_error($fhemsocket)) . "\n";
    exit;
  }

  # echo "Login Informationen senden ...";
  socket_write($fhemsocket, $PW, strlen($PW));
  # echo "Serverantwort auf Start lesen um Puffer zu leeren:\n\n";
  $out = socket_read($fhemsocket, 2048);
  $out = socket_read($fhemsocket, 2048);
}

function FHEMclose()
{
  global $fhemsocket;
  #echo "Quit senden und Port schließen";
  $in = "quit\n";
  socket_write($fhemsocket, $in, strlen($in));
  socket_close($fhemsocket);
}

function hole_FHEM_daten($device)
{
  global $fhemsocket;
  $in = "getstate $device\n";
  $out = '';

  #echo "Gewünschte Informationen anfragen ...";
  $result = socket_write($fhemsocket, $in, strlen($in));

  # echo "Serverantwort ...";
  $out = socket_read($fhemsocket, 2048);
  preg_match('/state:(?P<zahl>\d+)/', $out, $treffer);
  return( $treffer[1] );
}

function hole_FHEM_status($device, $was)
{
  global $fhemsocket;
  $in = "get $device statusRequest\n";
  $out = '';

  #echo "Gewünschte Informationen anfragen ...";
  $result = socket_write($fhemsocket, $in, strlen($in));

  # echo "Serverantwort ...";
  $out = socket_read($fhemsocket, 2048);
  preg_match('/' . $was . ': (?P<status>\w+)/', $out, $treffer);
  return( $treffer[1] );
}

function schalte_FHEM($device, $status)
{
  global $fhemsocket;
  $in = "set $device $status\n";

  #echo "Gewünschte Informationen enden...";
  socket_write($fhemsocket, $in, strlen($in));
}
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

Grinsekatze

#76
Danke!

Ich verstehe das richtig, dass Du mit FHEMConneckt() initialisierst. Dann z.B. mit hole_FHEM_daten($device), hole_FHEM_status($device, $was) oder schalte_FHEM($device, $status) die FHEM-Devices ausliest bzw. schaltest. Sowie zum Abschluss mit FHEMclose() die Session wieder beendest?

Zwei Sachen erschließen sich mir jedoch noch nicht gleich:
- Warum rufst Du zum Ende zweimal socket_read() in der Funktion FHEMConneckt() auf?
- Warum arbeitest Du in den Funktionen hole_FHEM_daten() und hole_FHEM_status() mit Regular Expressions? Das Unverständnis mag hier aber auch in meiner Unsicherheit diesen gegenüber begründet liegen. Ich hätte (bzw. habe in meiner ersten Version, bevor ich HTTP statt Telnet ausprobiert habe) hier direkt den benötigten FHEM-Befehl genutzt (und keinen StatusRequest), sowie die Rückgabe mit explode() bearbeitet. Ein StatusRequest in Verbindung mit einem RegExp. ist aber sicherlich die elegantere und generischere Methode.

Grinsekatze

Zitat von: Wernieman am 28 September 2016, 20:37:27
Habe mir einige Funktionen dafür geschrieben .. ich hoffe, Du versteht sie?
Habe bei mir eine Passwortabfrage drin, eventuell must Du die Passende Zeile rauslöschen

function FHEMConneckt()
{
  global $fhemsocket;
  $service_port = "7072";
  $address = gethostbyname('FHEM-Server-Adresse_oder_IP');
  $PW = "Passwort\n";
  $out = '';

  $fhemsocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  if ($fhemsocket === false) {
    echo "socket_create() fehlgeschlagen: Grund: " . socket_strerror(socket_last_error()) . "\n";
    exit;
  }

  #echo "Versuche, zu '$address' auf Port '$service_port' zu verbinden ...";
  $result = socket_connect($fhemsocket, $address, $service_port);
  if ($result === false) {
    echo "socket_connect() fehlgeschlagen: Grund: " . socket_strerror(socket_last_error($fhemsocket)) . "\n";
    exit;
  }

  # echo "Login Informationen senden ...";
  socket_write($fhemsocket, $PW, strlen($PW));
  # echo "Serverantwort auf Start lesen um Puffer zu leeren:\n\n";
  $out = socket_read($fhemsocket, 2048);
  $out = socket_read($fhemsocket, 2048);
}

function FHEMclose()
{
  global $fhemsocket;
  #echo "Quit senden und Port schließen";
  $in = "quit\n";
  socket_write($fhemsocket, $in, strlen($in));
  socket_close($fhemsocket);
}

function hole_FHEM_daten($device)
{
  global $fhemsocket;
  $in = "getstate $device\n";
  $out = '';

  #echo "Gewünschte Informationen anfragen ...";
  $result = socket_write($fhemsocket, $in, strlen($in));

  # echo "Serverantwort ...";
  $out = socket_read($fhemsocket, 2048);
  preg_match('/state:(?P<zahl>\d+)/', $out, $treffer);
  return( $treffer[1] );
}

function hole_FHEM_status($device, $was)
{
  global $fhemsocket;
  $in = "get $device statusRequest\n";
  $out = '';

  #echo "Gewünschte Informationen anfragen ...";
  $result = socket_write($fhemsocket, $in, strlen($in));

  # echo "Serverantwort ...";
  $out = socket_read($fhemsocket, 2048);
  preg_match('/' . $was . ': (?P<status>\w+)/', $out, $treffer);
  return( $treffer[1] );
}

function schalte_FHEM($device, $status)
{
  global $fhemsocket;
  $in = "set $device $status\n";

  #echo "Gewünschte Informationen enden...";
  socket_write($fhemsocket, $in, strlen($in));
}


Hm, wenn ich deine Funktionen übernehme (und mein eigenes Passwort & Host angebe), dann bekomme ich sowohl bei der Funktion hole_FHEM_daten als auch bei hole_FHEM_status immer die Meldung: Undefined Offset 1

Grinsekatze

Ok, der Fehler kommt vermutlich davon, dass viele meiner Devices kein getstate beherschen. Ich habe die Funktionen etwas angepasst, sodass sie nun für mich funktionieren.

Wernieman

Sorry, war etwas anderweitig beschäftigt.

Du hast Recht, am Anfang des PHP-Scriptes starte ich mit FHEMConneckt(), am Ende FHEMclose(). So wird bei jedem Aufruf der Seite nur ein Konnekt gestartet. Über diesen Tunnel wird dann alles abgewickelt. Hat nur den Nachteil, wenn man mehr als einen FHEM-Server abfragt, was nur im Normalfall nicht passiert (oder??).

Ich starte beim FHEMConneckt() 2 mal, weil ich es irgendwie brauchte, siehe vorheriges auskommentiertes echo ...

Du kannst eben Daten aus FHEM mit hole_FHEM_daten oder hole_FHEM_status holen. Je nach Device. Analog kann man auch beliebige andere "Befehle" absetzen. Schalten eben über schalte_FHEM().

Wie sieht Deine Anpassung aus? Eventuell können wir das Script dann ins WIKI packen ..
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

Grinsekatze

Bei mir sieht es nun so aus:
function FHEMConnect()
{
global $fhemsocket;
$service_port = PORT;
$address = gethostbyname(ADDRESS);
$password = PASSWORD."\n";
$out = '';

$fhemsocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($fhemsocket === false) {
echo "socket_create() fehlgeschlagen: Grund: " . socket_strerror(socket_last_error()) . "\n";
exit;
}

#echo "Versuche, zu '$address' auf Port '$service_port' zu verbinden ...";
$result = socket_connect($fhemsocket, $address, $service_port);
if ($result === false) {
echo "socket_connect() fehlgeschlagen: Grund: " . socket_strerror(socket_last_error($fhemsocket)) . "\n";
exit;
}

# echo "Login Informationen senden ...";
socket_write($fhemsocket, $password, strlen($password));
# echo "Serverantwort auf Start lesen um Puffer zu leeren:\n\n";
$out = socket_read($fhemsocket, 2048);
$out = socket_read($fhemsocket, 2048);
}

function FHEMclose()
{
global $fhemsocket;
#echo "Quit senden und Port schließen";
$in = "quit\n";
socket_write($fhemsocket, $in, strlen($in));
socket_close($fhemsocket);
}

function altgetFHEM($device)
{
global $fhemsocket;
$in = "$device\n";
$out = '';

#echo "Gewünschte Informationen anfragen ...";
$result = socket_write($fhemsocket, $in, strlen($in));

#echo "Serverantwort ...";
$out = socket_read($fhemsocket, 2048);
return $out;
}

function FHEMget($device, $command)
{
global $fhemsocket;
$in = "get $device $command\n";
$out = '';

#echo "Gewünschte Informationen anfragen ...";
$result = socket_write($fhemsocket, $in, strlen($in));

#echo "Serverantwort ...";
$out = socket_read($fhemsocket, 2048);
return $out;
}

function FHEMset($device, $command)
{
global $fhemsocket;
$in = "set $device $scommand\n";

#echo "Gewünschte Informationen enden...";
socket_write($fhemsocket, $in, strlen($in));
}


PORT, ADDRESS und PASSWORD habe ich als Konstanten vergeben (die in einer externen Config.php oder z.B. am Anfang der Seite stehen, damit sie einfacher angepasst werden können):
define ('PORT', '<FHEM-Telnet-Port>');
define ('ADDRESS', '<FHEM-PC-NAME oder -IP');
define ('PASSWORD', 'FHEM-Telnet-Passwort');


Ein typischer Aufruf von getFHEM kann z.B. so lauten (für ein HM-WDS10-TH-O): $temperature = FHEMget("TemperatursensorAussenbereich", "param temperature");

Ich hatte kurz überlegt, ob ich altgetFHEM($device) lösche. Doch ich muss für mein Projekt die States von zwei Dummys abfragen. Daher habe ich es gelassen und rufe sie so auf:
$sunrisetmp = explode(' ', altgetFHEM("{Value('Sonnenaufgang')}"));
$sunrise = trim($sunrisetmp[0]);


Grinsekatze

#81
So, nach etwas Zeit, mal wieder ein Update (s. Screenshot).

Erledigt:
- Datum
- Uhrzeit
- aktuelle Windgeschwindigkeit
- aktuelle Wetterkondition (als Bild)
- aktuelle Temperatur
- dynamische Anzeige von Sonnenauf- oder Untergang (Bild und Uhrzeit, je nachdem was als nächstes kommt)
- Vorhersage der nächsten 6 Tage: Wetterkonditionen (als Bild), min. und max. Temperatur.
- Anzeige der nächsten 6 Termine (wenn sie heute sind: "<Titel> Heute um <Uhrzeit> Uhr", wenn sie binnen 6 Tagen sind: "<Titel> <Wochentag> um <Uhrzeit> Uhr" und wenn sie später als 6 Tage sind: "<Titel> in <verbleibende Tage> Tagen")
- eine Zentrale Konfigurationsdatei, in der alle notwendigen Daten abgelegt werden (Host, Port, Passwort, die Gerätenamen der in FHEM benötigten Geräte, das Aktualisierungsintervall)

Noch offen:
- Gesichtserkennung und passendes Laden von Profilen (z.B. die Ausgabesprache abhängig vom Betrachter).
- Bau des Spiegels
- vereinzelt bekomme ich noch Icons für Wetterkonditionen, die ich noch nicht eingebunden habe. Speziell nachts. Daher werden diese im laufenden Betrieb nachgereicht.
- Eventuell noch die Regenwahrscheinlichkeit anzeigen. Die ist auf Yahoo-Wetter angezeigt (genau so wie Sonnenauf- und Untergang), wird aber vom WEATHER-Modul offenbar nicht erhoben. Daher muss ich mich für die Ermittlung der Regenwahrscheinlichkeit offenbar anderweitig umsehen.

Ich habe nun wieder, wie zu beginn alles von HTTP auf Telnet umgestellt. Die Infos sind somit nicht erst nach 60 Sekunden verfügbar, sondern sofort (max. 2 Sec.).

Hat noch Jemand Wünsche oder Anmerkungen?

KernSani

Zitat von: Grinsekatze link=topic=50187.msg497235#msg497235 date=
Daher muss ich mich für die Ermittlung der Regenwahrscheinlichkeit offenbar anderweitig umsehen.
PROPLANTA kann Regenwahrscheinlichkeit
RasPi: RFXTRX, HM, zigbee2mqtt, mySensors, JeeLink, miLight, squeezbox, Alexa, Siri, ...

ernst1024

#83
re screenshot.


Kann man natürlich so schlecht beurteilen. Es kommt darauf welcher Teil des Spiegels abgebildet ist? Grundsätzlich wäre mir das aber schon zu überladen. Vor allem die Ferientage und auch die Wettervorhersage für 6 Tage. Ich denke 3 Tage Vorhersage max.
Aber das ist natürlich Geschmacksache. Ideal wäre natürlich ein Baukastensystem aus dem sich jeder dann seine Anzeige zusammenstellen kann.

zu den Codebeispielen.

Ich bin als Autodidakt ein wenig verwirrt. Das ist doch in PHP oder? Mir ist der Unterschied im Zugriff noch nicht ganz klar (Telnet oder HTTP).

Wo ich auch noch keinen blassen Schimmer habe ist wie die Daten auf dem Spiegel sich selbst aktualisieren? Ich habe so ein kleines Übungsprojekt, quasi ne Webpage mit ein paar Seiten in HTML und PHP in denen ich teils Daten aus Fhem abrufe, teils Wlan-Fühler direkt abrufe oder auch die Fhem Datenbank auslese usw. Aber das alles passiert immer erst wenn ich die Seite von Hand aktualisiere.

Das mit der config datei habe ich auch. Da habe ich auch einige Funktionen ausgelagert. Und dann in jeder Datei die darauf zugreift ein require_once, das klappt hervorragend.

Nun, ich bin gespannt wie es weiter geht...

EDIT: re noch Wünsche... ja, einen newsfeed fände ich nicht schlecht

EDIT 2: ach ja, ich würde mir dann auch die nostalgische Bahnhofsuhr einbauen, siehe png
Gruß Ernst

Grinsekatze

Zum Screenshot: der ist der Einfachheit in einer Auflösung von ca 400px Breite. Daher ists Überladen. Wenn das Browserfenster in Vollbild ist, dann siehts besser aus. Die Inhalte sind zzt links und rechts in den oberen Ecken.

Newsfeed: bin ich auch am Überlegen. Zb von der Tagesschau. Unten mittig.

Ja es ist in PHP gelöst. Aber irgendwie brauche ich ja die Daten von FHEM.
Da gibt es 3 Möglichkeiten:
- Per SQL-Datenbank direkt auslesen. Vorher muss dies abr in FHEM eingerichtet werden. Daher gefällt mir das nicht, da ich so wenig ändern will wie möglich, um den Spiegel zum Laufen zu bekommen (damit Andere es auch nurzen können).
- Per HTTP-Aufruf. Das dauert aber lange (zumindest bei mir).
- Per Telnet-Aufruf.

Die Daten bleiben aktuell, weil die Seite alle 5 Minuten neu lädt (Stichwort: header refresh).

Wernieman

config.php wollte ich auch umsetzen .. nur habe ich meinen "inneren Schweinehund" nicht dazu bewegen können ;o)

Dein "FHEMget" übergiebt einfach die Werte, ohne irgendwelche Prüfungen .. oder?
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

ernst1024

Zitat von: Grinsekatze am 01 Oktober 2016, 13:33:13

Da gibt es 3 Möglichkeiten:
- Per SQL-Datenbank direkt auslesen. Vorher muss dies abr in FHEM eingerichtet werden. Daher gefällt mir das nicht, da ich so wenig ändern will wie möglich, um den Spiegel zum Laufen zu bekommen (damit Andere es auch nurzen können).
- Per HTTP-Aufruf. Das dauert aber lange (zumindest bei mir).
- Per Telnet-Aufruf.


ok, und wenn das über port 7072 geht ist das Telnet? Wenn ja dann habe ich in meiner Unwissenheit alle drei bei mir eingebaut. Naja, kann ja nicht schaden. Das mit dem header refresh war Gold wert. Klappt.
Gruß Ernst

Grinsekatze

Ja, die Funktion prüft nichts.

Auch ja, der Port 7072 ist der Standard-FHEM-Telnet-Port.

Ich habe jetzt noch zwei weitere Konstanten definiert, um die Anzahl der Terminvorschauen und Wettervorhersagen zu bestimmen.
Ebenfalls sind einige Konstanten dazugekommen um die Module zu aktivieren / deaktivieren. Zzt. habe ich das Modul Datum/Zeit, Kalender, Aktuelle Wetterdaten und Wettervorhersage.

ernst1024

ui, je mehr ich denke durchzublicken desto mehr neue Fragen tuen sich auf. zum einen was ist der Unterschied zwischen den beiden Methoden (siehe unten).

<?php
$fsock 
fsockopen($fhemhost$fhemport$errno$errstr30);     // $fhemsocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$fcmd "{Value('8266_f_Innen')}\r\nQUIT\r\n";                     //  Unterschied??
fwrite($fsock$fcmd);                                             // socket_write($fhemsocket, $in, strlen($in));
$temp=fgets($fsock256);                                          // socket_read($fhemsocket, 2048);
echo "$temp";
// liefert: H: 47.40 % T: 21.10 °C HI: 20.50 °C
echo "<br>";
// $temp zerlegen
$result explode(" "$temp);
echo 
"H: $result[1]";
echo 
"<br>";
echo 
"T: $result[4]";
echo 
"<br>";
echo 
"hI: $result[8]";
echo 
"<br>";
fclose($fsock);
?>



@Grinsekatze mir ist aufgefallen dass dein altGetFHEM davon ausgeht dass das device auch nur einen Wert liefert, richtig? Wenn wie in meinem Fall oben (ein DHT22) mehrere readings liefert muss man die evtl weiter in ihre Einzelteile zerlegen. Siehe oben.

Wovon ich noch keinen blassen Schimmer habe ist die Gestaltung der Seite. Also wie platziere ich wo die Elemente, wie zeige ich die grafischen Wettersymbole usw. Im Moment wird alles immer untereinander angezeigt.  ??? Dann müsste ja wohl auch der Hintergrund schwarz und die Schrift weiss sein...???
Gruß Ernst

Grinsekatze

Zur Platzierung: Da habe ich es mir leicht gemacht und einfach das Gerüst aus dem 1. Post übernommen.

altgetFHEM nutze ich, für Devices die kein get verstehen, z.B. Dummys. Dort übergebe ich dann den gesamten auszuführenden Befehl. getFHEM will nur das Device und den zum get passenden Befehl haben.