Offset Einstellung Fritz Dect 301

Begonnen von Pocki90, 22 Januar 2019, 15:59:44

Vorheriges Thema - Nächstes Thema

tobi1049

Zitat von: tobi1049 am 01 Januar 2024, 20:51:59Ich vermute, dass es irgendeine Kleinigkeit ist.
Das Problem ist offenbar das ';' in den timer_item_x Parameter. Ersetze ich es durch ein anderes z.B. ','  oder '%3B' wird die Anfrage komplett übertragen, nur quittiert die Fritzbox dann nicht das apply, sondern liefert das HTML für die Einstellungsseite.

Ich habe das Problem gefunden. Das ';' wird während der Ausführung der Funktion fhem() dazu genutzt, den Commandstring in SubCommands zu splitten. Um das zu vermeiden, muss ein ';' mit ';;' escaped werden.

JoWiemann

Zitat von: tobi1049 am 02 Januar 2024, 12:11:00
Zitat von: tobi1049 am 01 Januar 2024, 20:51:59Ich vermute, dass es irgendeine Kleinigkeit ist.
Das Problem ist offenbar das ';' in den timer_item_x Parameter. Ersetze ich es durch ein anderes z.B. ','  oder '%3B' wird die Anfrage komplett übertragen, nur quittiert die Fritzbox dann nicht das apply, sondern liefert das HTML für die Einstellungsseite.

Ich habe das Problem gefunden. Das ';' wird während der Ausführung der Funktion fhem() dazu genutzt, den Commandstring in SubCommands zu splitten. Um das zu vermeiden, muss ein ';' mit ';;' escaped werden.


Hm, dass mit dem escapen habe ich auch schon versucht. Führte bei mir nicht zum Erfolg. Funktioniert das wirklich bei Dir?

Ansonsten anbei eine Version, wo Du das ; durch #x003B ersetzen musst. Ich wandele das dann wieder in ein ; um.

Grüße Jörg
Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

tobi1049

Zitat von: JoWiemann am 02 Januar 2024, 12:41:01Hm, dass mit dem escapen habe ich auch schon versucht. Führte bei mir nicht zum Erfolg. Funktioniert das wirklich bei Dir?

Ansonsten anbei eine Version, wo Du das ; durch #x003B ersetzen musst. Ich wandele das dann wieder in ein ; um.

Das Ersetzen mit #x003B funktioniert auch in Verbindung mit dem Patch von dir.

tobi1049

Aktuell benötige ich zwei get luaData Aufrufe, um den Parameterstring für die Offset-Änderung zusammenzustellen:
Der erste fordert die Einstellungseite der Fritzbox als HTML ab, ich parse dann aus dem Formular die Presets, auf diese Weise bekomme ich fast alle Infos, außer dem wöchentlichen Zeitplan. Dieser wird nämlich offenbar durch ein javascript erstellt und deshalb wird ein zweiter Aufruf benötigt: get <FritzBox> luaData xhr 1 lang de page sh_dev xhrId all.
In der Antwort sind aber im Grunde alle Infos vorhanden, der erste Aufruf wäre überflüssig.

Nur leider lässt sich die Antwort nur sehr bedingt auswerten, da der Hash mit den Json dekodierten Daten bereits in einen String verwandelt mit vorangestelltem Request zurück kommt.

Daher meine Frage: Ließe sich das Fritzbox-Modul dahingehend erweitern, dass für get LuaData Anfragen der Hash zurückgegeben wird, z.B. durch einen zusätzlichen Parameter oder ein spezielles get luaData Kommando?

So etwas in der Art wurde ja schon für das fritzlog umgesetzt: https://forum.fhem.de/index.php?topic=132790.0


JoWiemann

Zitat von: tobi1049 am 03 Januar 2024, 17:36:33Nur leider lässt sich die Antwort nur sehr bedingt auswerten, da der Hash mit den Json dekodierten Daten bereits in einen String verwandelt mit vorangestelltem Request zurück kommt.

Daher meine Frage: Ließe sich das Fritzbox-Modul dahingehend erweitern, dass für get LuaData Anfragen der Hash zurückgegeben wird, z.B. durch einen zusätzlichen Parameter oder ein spezielles get luaData Kommando?

So etwas in der Art wurde ja schon für das fritzlog umgesetzt: https://forum.fhem.de/index.php?topic=132790.0

Hallo Tobi,

möchtest Du nur die HASH Referenz oder wie in FritzLog die Möglichkeit das über eine im Attribut definierte Sub direkt weiter verarbeiten zu können?

Grüße Jörg
Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

JoWiemann

Hallo Tobi,

ich habe Dir das jetzt mit [hash] als optionalem Parameter eingebaut.

Bitte schau Dir doch die Sub: sub FRITZBOX_Helper_analyse_Lua_Result($$;@) einmal an. Hier wird die Rückgabe analysiert und auf Fehler reagiert.

Grüße Jörg
Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

tobi1049

Zitat von: JoWiemann am 05 Januar 2024, 10:56:38Hallo Tobi,

ich habe Dir das jetzt mit [hash] als optionalem Parameter eingebaut.

Bitte schau Dir doch die Sub: sub FRITZBOX_Helper_analyse_Lua_Result($$;@) einmal an. Hier wird die Rückgabe analysiert und auf Fehler reagiert.

Grüße Jörg
Hallo Jörg,

vielen Dank für die Umsetzung. Im Prinzip ist es genau das, was ich mir vorgestellt habe.
Aber ich bin offenbar zu blöd, das richtig anzuwenden, folgende Funktion soll die Daten mittels luaData Call abholen und auswerten:
sub getFBDECT30x_Settings($$)
{
   my ($devID,$FBdev)= @_;
   my $devData;
   my $response;

   # query device settings
   $response = fhem( "get $FBdev luaData hash xhr 1 lang de page sh_dev xhrId all", 1 );

   $devData = $response->{data}{devices};  # hier entsteht die Referenz auf das Array
 
   ...

Doch die Zeile $devData = $response->{data}{devices}; ergibt folgende Fehlermeldung: Can't use string ("HASH(0x31d2968)") as a HASH ref while "strict refs" in use

Hast du einen Tipp für mich?

Tobias


tobi1049

Hallo Jörg,

ich glaube, das Problem gefunden zu haben. Es liegt vermutlich in dem Callstack von der Funktion fhem() bis zur Funktion FRITZBOX_Get(), irgendwo wird dort die Hash Referenz in einen String umgewandelt. Ich habe bisher nichts gefunden, wie man das wieder rückgängig machen kann, nur einen Hinweis auf serislisieren und deserialisieren der hash-Daten z.B. per JSON.

Darüber hinaus gab es noch einen Fehler beim Parsen des @val-Array bgzl. des Entfernens des optionalen Parameters 'hash'.

Anbei eine Version mit einem Bugfix für das Parse-Problem. Außerdem habe ich dort den optionalen Parameter in 'json' umbenannt und gebe ein json-string zurück.
Damit funktioniert das für mich.

Tobias

JoWiemann

Zitat von: tobi1049 am 05 Januar 2024, 12:54:13sub getFBDECT30x_Settings($$)
{
   my ($devID,$FBdev)= @_;
   my $devData;
   my $response;

   # query device settings
   $response = fhem( "get $FBdev luaData hash xhr 1 lang de page sh_dev xhrId all", 1 );

   $devData = $response->{data}{devices};  # hier entsteht die Referenz auf das Array
 
   ...

Doch die Zeile $devData = $response->{data}{devices}; ergibt folgende Fehlermeldung: Can't use string ("HASH(0x31d2968)") as a HASH ref while "strict refs" in use

Hast du einen Tipp für mich?

Hallo Tobias,

das es sich um eine Referenz handelt musst Du überall -> einsetzen:

$devData = $response->{data}->{devices}

Grüße Jörg
Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

tobi1049

Zitat von: JoWiemann am 05 Januar 2024, 16:48:04
Zitat von: tobi1049 am 05 Januar 2024, 12:54:13sub getFBDECT30x_Settings($$)
{
   my ($devID,$FBdev)= @_;
   my $devData;
   my $response;

   # query device settings
   $response = fhem( "get $FBdev luaData hash xhr 1 lang de page sh_dev xhrId all", 1 );

   $devData = $response->{data}{devices};  # hier entsteht die Referenz auf das Array
 
   ...

Doch die Zeile $devData = $response->{data}{devices}; ergibt folgende Fehlermeldung: Can't use string ("HASH(0x31d2968)") as a HASH ref while "strict refs" in use

Hast du einen Tipp für mich?

Hallo Tobias,

das es sich um eine Referenz handelt musst Du überall -> einsetzen:

$devData = $response->{data}->{devices}

Grüße Jörg

Hallo Jörg,

das hatte ich auch schon probiert, aber es ändert nichts, es führt zu der gleichen Fehlermeldung.

Tobias

JoWiemann

Zitat von: tobi1049 am 05 Januar 2024, 15:17:16Hallo Jörg,

Anbei eine Version mit einem Bugfix für das Parse-Problem. Außerdem habe ich dort den optionalen Parameter in 'json' umbenannt und gebe ein json-string zurück.
Damit funktioniert das für mich.

Tobias


Hallo Tobias,

danke, habe ich so übernommen.

Grüße Jörg
Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

neobiker

Hallo,
gibt es jetzt inzwischen eine Möglichkeit den Temperatur Offset (tempadjust) Wert eines AVM Thermostates mit FHEM zu ändern?
Ich benötige das auch, und vermutlich noch viele andere ;-)

JoWiemann

Zitat von: tobi1049 am 05 Januar 2024, 15:17:16Anbei eine Version mit einem Bugfix für das Parse-Problem. Außerdem habe ich dort den optionalen Parameter in 'json' umbenannt und gebe ein json-string zurück.
Damit funktioniert das für mich.

Hallo Tobias,

es wäre schön, wenn Du einmal Deine Umsetzung posten würdest. Ich würde das dann als neuen set Befehl in das Modul einbauen.

Grüße Jörg
Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

tobi1049

Zitat von: JoWiemann am 15 Januar 2024, 07:40:39
Zitat von: tobi1049 am 05 Januar 2024, 15:17:16Anbei eine Version mit einem Bugfix für das Parse-Problem. Außerdem habe ich dort den optionalen Parameter in 'json' umbenannt und gebe ein json-string zurück.
Damit funktioniert das für mich.

Hallo Tobias,

es wäre schön, wenn Du einmal Deine Umsetzung posten würdest. Ich würde das dann als neuen set Befehl in das Modul einbauen.

Grüße Jörg

Sehr gerne.

Das setzen eines neuen Offset erfolgt durch Aufruf der folgenden Funktion:
sub setFBDECToffset($$$)
{
  my ($devName,$FBdev,$newOffset) = @_;
  my $ret = "Nothing to do.";
  my $actOffset;
  my $targetTemp;

  ($targetTemp) = (ReadingsVal($devName,"desired-temp",0) =~ /([+-]?[0-9.]*)/); # remove any text
  ($actOffset) = (ReadingsVal($devName,"tempadjust",0) =~ /([+-]?[0-9.]*)/);    # remove any text
  ($newOffset) = ($newOffset =~ /([+-]?[0-9.]*)/);                              # remove any text

  if( $actOffset != $newOffset ) {
    my $devID = ReadingsVal($devName,"ID",0);

    if( $devID != 0 ) {
      my %args = %{ getFBDECT30x_Settings($devID, $FBdev) };

      my $arguments = 'xhr 1 view nop back_to_page /smarthome/devices.lua apply nop lang de page home_auto_hkr_edit';
     
      foreach( sort keys %args ) {
        $arguments = $arguments . ' ' . $_ . ' ' . $args{$_};
      }

      # set newOffset in arguments
      $arguments =~ s/(Offset) ([+-]?[0-9.]*)/$1 $newOffset/gm;
      $arguments = encode('UTF-8', $arguments);
     
      # write newOffset to device
      my $response = fhem("get $FBdev luaData json $arguments",1);

      my $resp = decode_json( $response );
     
      if( ( exists $resp->{'data'}{'apply'} )&&( $resp->{'data'}{'apply'} eq "ok" ) ) {
        # restore desired-temp setting
        fhem( "set $devName desired-temp $targetTemp", 1 );
        $ret = "setting offset to $newOffset C successful!";
      } else {
        $ret = "setting offset to $newOffset C failed!";
      }

    } else {
      $ret = "Invalid device ID <$devID>!";
    }
  }

  return $ret;
}

Die Funktion erwartet als Parameter den Namen des FBDECT301-Gerätes, den FHEM-Namen der Fritzbox und den neuen Offset-Wert.
Der Wert wird nur geändert, wenn er ungleich des aktuell gesetzen Wert ist.
Beim lua-Aufruf der Einstellungsseite müssen, soweit ich das herausgefunden habe, alle Parameter mit übergeben werden. Das Fehlen von Werten deaktiviert meist die jeweilige Funktion.
Die Parameter werden durch einen weiteren Get luaData-Aufruf gesammelt, das erledigt die Funktion getFBDECT30x_Settings($$) (s.u.)
Nach erfolgreichem Setzen eines neuen Offsets muss die Soll-Temperatur neu gesetzt werden, weil sie dabei ggfs. überschrieben wurde.

Hier noch der Queltext der zweiten Funktion:

sub getFBDECT30x_Settings($$)
{
  my ($devID,$FBdev)= @_;
  my $devData;
  my $response;

  # query device settings
  $response = fhem( "get $FBdev luaData json xhr 1 lang de page sh_dev xhrId all", 1 );

  $response = decode_json( $response );

  $devData = $response->{'data'}{'devices'};  # hier entsteht die Referenz auf das Array

  my $dayOfWeekMap = { 'SUN' => 64, 'SAT' => 32, 'FRI' => 16, 'THU' => 8, 'WED' => 4, 'TUE' => 2, 'MON' => 1 };
  my $unitData;
  my $skills;
  my $allTimeSchedules;
  my $timeSchedule;
  my $debug = "";
  my %ret;

  # find entry for requested devID
  for my $i (0 .. @{$devData} - 1) {
    if( $devData->[$i]{'id'} eq $devID ) {

      $ret{device}          = $devID;
      $ret{ule_device_name} = $devData->[$i]{'displayName'};
      if( $devData->[$i]{'pushService'}{'isEnabled'} ) {
        $ret{enabled}       = "on";
        $ret{mailto}        = $devData->[$i]{'pushService'}{'mailAddress'};
      }
     
      # find needed unit
      $unitData = $devData->[$i]{'units'};
      for my $j (0 .. scalar @{$unitData} - 1) {
        my $unitData = $unitData->[$j];
 
        if( $unitData->{'type'} eq 'THERMOSTAT' ) {
          $skills = $unitData->{'skills'}[0];
          # parse preset temperatures
          my $presets = $skills->{'presets'};
          for my $i ( 0 .. 1 ) {
            $ret{Absenktemp} = $presets->[$i]{'temperature'} if( $presets->[$i]{'name'} eq 'LOWER_TEMPERATURE' );
            $ret{Heiztemp} = $presets->[$i]{'temperature'} if( $presets->[$i]{'name'} eq 'UPPER_TEMPERATURE' );
          }
          $ret{hkr_adaptheat} = ( ( $skills->{'adaptivHeating'}{'isEnabled'} ) ? 1 : 0 );
          $ret{ExtTempsensorID} = $skills->{'usedTempSensor'}{'id'};
          $ret{WindowOpenTrigger} = $skills->{'temperatureDropDetection'}{'sensitivity'};
          $ret{WindowOpenTimer} = $skills->{'temperatureDropDetection'}{'doNotHeatOffsetInMinutes'};

          # find needed time schedule
          $allTimeSchedules = $skills->{'timeControl'}{'timeSchedules'};
          for my $ts (0 .. scalar @{$allTimeSchedules} - 1) {
            # parse weekly timetable
            if( $allTimeSchedules->[$ts]{'name'} eq 'TEMPERATURE' ) {
              $timeSchedule = $allTimeSchedules->[$ts]{'actions'};
              my $NumEntries = scalar @{$timeSchedule};
              my @timerItems = ();
             
              for my $i (0 .. $NumEntries - 1) {
                my $startTime   = $timeSchedule->[$i]{'timeSetting'}{'startTime'};
                my $dayOfWeek   = $timeSchedule->[$i]{'timeSetting'}{'dayOfWeek'};
                my $temperature = $timeSchedule->[$i]{'description'}{'presetTemperature'}{'temperature'};
                my %timerItem;
                my $newItem = 1;
               
                $debug = "$debug $i $startTime $temperature $dayOfWeek => ";

                $startTime   =~ s/([0-9]{2}):([0-9]{2}):([0-9]{2})/$1$2/;
                $temperature = ( ( $temperature eq $ret{Heiztemp} ) ? 1 : 0 );
                $dayOfWeek   = $dayOfWeekMap->{$dayOfWeek};
               
                $timerItem{'startTime'}   = $startTime;
                $timerItem{'dayOfWeek'}   = $dayOfWeek;
                $timerItem{'temperature'} = $temperature;

                foreach (@timerItems) {
                  if(( $_->{'startTime'} eq $startTime )&&( $_->{'temperature'} eq $temperature ) ) {
                      $_->{'dayOfWeek'} = $_->{'dayOfWeek'} + $dayOfWeek;
                      $newItem = 0;
                      last;
                  }
                }
                if( $newItem ) {
                  push( @timerItems, \%timerItem );
                }

                $debug = "$debug $startTime;$temperature;$dayOfWeek \n";
              }
              my $j = 0;
              foreach (@timerItems) {
                $ret{"timer_item_$j"} = $_->{'startTime'}.';;'.$_->{'temperature'}.';;'.$_->{'dayOfWeek'};
                $debug = "$debug $_->{'startTime'};;$_->{'temperature'};;$_->{'dayOfWeek'} \n";
                $j ++;
              }
            } elsif( $allTimeSchedules->[$ts]{'name'} eq 'HOLIDAYS' ) {
              $timeSchedule = $allTimeSchedules->[$ts]{'actions'};
              my $NumEntries = scalar @{$timeSchedule};
 
              $ret{"HolidayEnabledCount"} = 0;
              for my $i (0 .. $NumEntries - 1) {
                my $holiday     = "Holiday" . eval($i + 1);
                my $startDate   = $timeSchedule->[$i]{'timeSetting'}{'startDate'};
                my $startTime   = $timeSchedule->[$i]{'timeSetting'}{'startTime'};
                my $endDate     = $timeSchedule->[$i]{'timeSetting'}{'endDate'};
                my $endTime     = $timeSchedule->[$i]{'timeSetting'}{'endTime'};
               
                $ret{$holiday . "ID"}        = $i + 1;
                $ret{$holiday . "Enabled"}   = ( ( $timeSchedule->[$i]{'timeSetting'}{'isEnabled'} ) ? 1 : 0 );
                $ret{$holiday ."StartDay"}   = $startDate;
                $ret{$holiday ."StartDay"}   =~ s/([0-9]{4})-([0-9]{2})-([0-9]{2})/$3/;
                $ret{$holiday ."StartMonth"} = $startDate;
                $ret{$holiday ."StartMonth"} =~ s/([0-9]{4})-([0-9]{2})-([0-9]{2})/$2/;
                $ret{$holiday ."StartHour"}  = $startTime;
                $ret{$holiday ."StartHour"}  =~ s/([0-9]{2}):([0-9]{2}):([0-9]{2})/$1/;
               
                $ret{$holiday ."EndDay"}     = $endDate;
                $ret{$holiday ."EndDay"}     =~ s/([0-9]{4})-([0-9]{2})-([0-9]{2})/$3/;
                $ret{$holiday ."EndMonth"}   = $endDate;
                $ret{$holiday ."EndMonth"}   =~ s/([0-9]{4})-([0-9]{2})-([0-9]{2})/$2/;
                $ret{$holiday ."EndHour"}    = $endTime;
                $ret{$holiday ."EndHour"}    =~ s/([0-9]{2}):([0-9]{2}):([0-9]{2})/$1/;
               
                $ret{"HolidayEnabledCount"} ++ if( 1 == $ret{$holiday . "Enabled"} );
               
              }

            } elsif( $allTimeSchedules->[$ts]{'name'} eq 'SUMMER_TIME' ) {
              $timeSchedule = $allTimeSchedules->[$ts]{'actions'};
              my $NumEntries = scalar @{$timeSchedule};
 
              for my $i (0 .. $NumEntries - 1) {
                my $startDate   = $timeSchedule->[$i]{'timeSetting'}{'startDate'};
                my $endDate     = $timeSchedule->[$i]{'timeSetting'}{'endDate'};
               
                $ret{SummerStartDay}   = $startDate;
                $ret{SummerStartDay}   =~ s/([0-9]{4})-([0-9]{2})-([0-9]{2})/$3/;
                $ret{SummerStartMonth} = $startDate;
                $ret{SummerStartMonth} =~ s/([0-9]{4})-([0-9]{2})-([0-9]{2})/$2/;
               
                $ret{SummerEndDay}     = $endDate;
                $ret{SummerEndDay}     =~ s/([0-9]{4})-([0-9]{2})-([0-9]{2})/$3/;
                $ret{SummerEndMonth}   = $endDate;
                $ret{SummerEndMonth}   =~ s/([0-9]{4})-([0-9]{2})-([0-9]{2})/$2/;
               
                $ret{SummerEnabled}    = ( ( $timeSchedule->[$i]{'timeSetting'}{'isEnabled'} ) ? 1 : 0 );
              }

            }
          }
        } elsif ( $unitData->{'type'} eq 'TEMPERATURE_SENSOR' ) {
          $skills = $unitData->{'skills'}[0];
          $ret{Offset}     = $skills->{'offset'};
          $ret{Roomtemp}   = $skills->{'currentInCelsius'};
          $ret{tempsensor} = $ret{Roomtemp} - $ret{Offset};
        }
      }
      last;
    }
  }
   
  #return $debug . "\n" . Dumper(\%ret);
  return \%ret;
}


Das ganze ist aber noch nicht mehr als eine Geradeaus-Implementierung. Es fehlt noch das Abfangen von nicht erwarteten Antworten.
Auch kann nicht ausgeschlossen werden, dass bei Anwendung Einstellungen am Thermostat verloren gehen können.
Vielleicht kann man das alles auch noch eleganter umsetzen, aber das war mein erster Kontakt mit Perl...

Tobias

JoWiemann

Zitat von: tobi1049 am 17 Januar 2024, 20:06:14Das ganze ist aber noch nicht mehr als eine Geradeaus-Implementierung. Es fehlt noch das Abfangen von nicht erwarteten Antworten.
Auch kann nicht ausgeschlossen werden, dass bei Anwendung Einstellungen am Thermostat verloren gehen können.
Vielleicht kann man das alles auch noch eleganter umsetzen, aber das war mein erster Kontakt mit Perl...

Tobias


Hallo Tobias,

danke für die Bereitstellung Deiner Lösung. Wird allerdings etwas dauern, bis ich mir das genauer ansehen kann und ins Modul übernehmen kann.

Grüße Jörg
Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM