FHEM Forum

FHEM => Automatisierung => Thema gestartet von: TomLee am 25 Oktober 2026, 09:44:53

Titel: 32_mailcheck: Reconnect-Patch gegen "Trying command when NOT connected"
Beitrag von: TomLee am 25 Oktober 2026, 09:44:53
Huhu,

die PERL WARNINGs nach der Zwangstrennung, die ich 2022 (https://forum.fhem.de/index.php?msg=1209097) schon mal angesprochen hatte, haben mich nie ganz losgelassen.
Jetzt hab ich mich mit meinem Agenten mal drüber hergemacht, und rausgekommen ist ein Patch, der das direkt in 32_mailcheck.pm löst, ohne an Mail::IMAPClient rumzuschrauben. Nebenbei erledigt er auch das taube Device, das Benni damals beschrieben hat.

Du hattest in #86864 (https://forum.fhem.de/index.php?topic=86864.0) geschrieben, dass die Verbindungsprüfung an einigen Stellen fehlt. Genau daher kommen die Warnungen. Das Log von 2022 zeigt sie in poll Zeile 275/276 (select/idle) und in Read Zeile 378/382/483 (idle_data/done/idle).


Vorschlag (Patch unten):

Bei mir mit vier Devices (Gmail und T-Online) in Betrieb, Read/poll seit 14.10., Timeout/select seit 23.10.: keine PERL WARNING mehr, nach "bad fileno" wieder eingeloggt nach 45–50 s statt Stunden, ein fehlgeschlagener Connect nach einem Intervall nachgeholt. Meine notifies von damals (inactive/active bei mqtt2Cloud:disconnected) habe ich gelöscht, sie sind damit überflüssig.

$Id 16299, Mail::IMAPClient 3.43:
--- a/FHEM/32_mailcheck.pm
+++ b/FHEM/32_mailcheck.pm
@@ -137,10 +137,12 @@
   if( AttrVal($name, "nossl", 0) ) {
     $socket = IO::Socket::INET->new( PeerAddr => $hash->{Host},
                                      PeerPort => 143, #AttrVal($name, "port", 143)
+                                     Timeout  => 10,
                                    );
   } else {
     $socket = IO::Socket::SSL->new( PeerAddr => $hash->{Host},
                                     PeerPort => 993, #AttrVal($name, "port", 993)
+                                    Timeout  => 10,
                                   );
   }
 
@@ -194,6 +196,8 @@
 
     } else {
       mailcheck_Disconnect($hash);
+      $hash->{STATE} = "Login failed";
+      Log3 $name, 2, "$name: login failed for $hash->{helper}{user}";
     }
   } else {
     #$hash->{STATE} = "Connected";
@@ -269,10 +273,33 @@
   RemoveInternalTimer($hash);
   InternalTimer(gettimeofday()+$hash->{INTERVAL}, "mailcheck_poll", $hash, 0);
 
+  # socket may be gone without mailcheck_Read noticing (e.g. fhem.pl dropped
+  # a bad fileno from %selectlist): reconnect instead of polling a dead client
   my $client = $hash->{CLIENT};
-  if( $client && $client->IsConnected && $client->IsAuthenticated ) {
-    $client->done;
+  $client->done if( $client && $client->IsConnected && $client->IsAuthenticated );
+  if( !$client || !$client->IsConnected || !$selectlist{$hash->{NAME}} ) {
+    mailcheck_Disconnect($hash);
+    mailcheck_Connect($hash);
+
+    # keep retrying while the server is unreachable, but not after a
+    # rejected login (wrong password would lock the account)
+    InternalTimer(gettimeofday()+$hash->{INTERVAL}, "mailcheck_poll", $hash, 0)
+      if( !$hash->{CLIENT} && $hash->{STATE} ne "Login failed" );
+
+    return undef;
+  }
+
+  if( $client->IsAuthenticated ) {
     $client->select($hash->{Folder});
+
+    # select may be the first command to notice a dead socket
+    unless( $client->IsConnected ) {
+      mailcheck_Disconnect($hash);
+      mailcheck_Connect($hash);
+
+      return undef;
+    }
+
     $hash->{tag} = $client->idle;
     $hash->{LAST_POLL} = FmtDateTime( gettimeofday() );
   }
@@ -377,6 +404,15 @@
 
   my $ret = $client->idle_data();
 
+  # server closed the connection (e.g. IDLE timeout): reconnect before
+  # sending DONE/IDLE on a dead socket
+  unless( $client->IsConnected ) {
+    mailcheck_Disconnect($hash);
+    mailcheck_Connect($hash);
+
+    return undef;
+  }
+
   if( !defined($ret) || !$ret ) {
     $hash->{tag} = undef;
     $ret = $client->done;

Ich stell das einfach mal zur Diskussion. Vielleicht übersehe ich ja was, oder es geht eleganter.

Gruß Thomas