Mehrere Perl / IF-Abfragen in Userreading subtrahieren / verrechnen / Zeitangabe

Begonnen von Floriky, 18 April 2021, 15:12:12

Vorheriges Thema - Nächstes Thema

Floriky

Hallo Zusammen,

bitte entschuldigt den kryptischen Titel – dieser soll lediglich zur späteren Auffindbarkeit dienen.

Mein Problem:
Ich habe zwei funktionierende If-Abfragen, welche jeweils als Userreading brav Ihren Dienst verrichten.

IF-Abfrage 1:
if (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) > 1) {time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime(time-60*60*24*25))."-14 00:00:01")} elsif (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) == 1) {time_str2num(strftime("%Y",localtime(time-60*60*24*360))."-12-14 00:00:01")} else {time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime())."-14 00:00:01")}

IF-Abfrage 2:
if (strftime("%d",localtime()) <= 13 && strftime("%m",localtime()) <= 12) {time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime())."-13 23:59:59")} elsif (strftime("%d",localtime()) > 13 && strftime("%m",localtime()) == 12) {time_str2num(strftime("%Y",localtime(time+60*60*24*360))."-01-13 23:59:59")} else {time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime(time+60*60*24*25))."-13 23:59:59")}


Wenn ich nun versuche, wiederum in einem Userreading beide Zahlen voneinander zu subtrahieren, schmeißt mir FHEM immer die folgende Fehlermeldung aus:

ERROR evaluating {(if (strftime("%d",localtime()) <= 13 && strftime("%m",localtime()) <= 12) {time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime())."-13 23:59:59")} elsif (strftime("%d",localtime()) > 13 && strftime("%m",localtime()) == 12) {time_str2num(strftime("%Y",localtime(time+60*60*24*360))."-01-13 23:59:59")} else {time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime(time+60*60*24*25))."-13 23:59:59")})-(if (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) > 1) {time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime(time-60*60*24*25))."-14 00:00:01")} elsif (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) == 1) {time_str2num(strftime("%Y",localtime(time-60*60*24*360))."-12-14 00:00:01")} else {time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime())."-14 00:00:01")})}: syntax error at (eval 476760) line 1, near "(if"...


Mein Versuch sah in etwa so aus:
{round(((IF 1)-(IF 2)/86400),0)}


Kann mir jemand sagen was ich falsch mache?

Vielen Dank vorab und bleibt gesund!

Otto123

Hi,

also bei dem, was ich Deiner Fehlermeldung entnehme, stimmen eine Reihe Klammern nicht.

Aber Du musst auch die Ergebnisse der Abfragen subtrahieren, einfach die Ausdrücke wird ein schwieriger und kaum beherrschbare Syntax.
Also mach entweder:
{my $zahl1=Ausdruck1;my $zahl2=Ausdruck2; round(($zahl1-$zahl2)/86400),0}
(Wobei Du den Wert im Ergebnis des if Teils zuweisen musst.)
Oder lies die anderen beiden userReadings aus, die werden vor dem dritten gesetzt und sind vorhanden.
{my $zahl1=ReadingsNum($name,'reading1',0);my $zahl2=ReadingsNum($name,'reading2',0); round(($zahl1-$zahl2)/86400),0}

Hintergrund: Ein Perl Block {Perl Code} liefert nach "außen" die letzte Zuweisung / das Ergebnis zurück -> userReading
Eine Klammer - egal ob () oder {} - um den (Perl Code) macht das nicht, da wird eine gesamte Aufgabe interpretiert!

Oder so:
{my ($z1,$z2);;
    if (strftime("%d",localtime()) <= 13 && strftime("%m",localtime()) <= 12)
       {$z1=time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime())."-13 23:59:59")}
    elsif (strftime("%d",localtime()) > 13 && strftime("%m",localtime()) == 12)
          {$z1=time_str2num(strftime("%Y",localtime(time+60*60*24*360))."-01-13 23:59:59")}
    else  {$z1=time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime(time+60*60*24*25))."-13 23:59:59")};;
    if    (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) > 1)
          {$z2=time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime(time-60*60*24*25))."-14 00:00:01")}
    elsif (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) == 1)
          {$z2= time_str2num(strftime("%Y",localtime(time-60*60*24*360))."-12-14 00:00:01")}
    else  {$z2=time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime())."-14 00:00:01")};;
($z1-$z2)/86400}

Hinweis: Wenn Du das so in der DEF einsetzt musst Du die ;; zu einem ; machen. So ist der Code aber in der Kommandozeile ausführbar.
Ich habe das nur gemacht um herauszufinden was Du eigentlich berechnest - ich habe es noch nicht verstanden  ;D ;D ;D

Gruß Otto
Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

Floriky

Hallo Otto, vielen Dank für deine Nachricht.

1. Frage: Wenn die Klammern nicht stimmen, warum funktionieren dann die daraus resultierend Userreadings?

2. Frage: Die Readings kann ich leider nicht direkt nehmen, da ich die Ergebnisse der IF-Abfragen als Datum formatieren lasse (POSIX::strftime("%H:%M:%S...). Die beiden sehen bei mir aktuell so aus:

month_start_date {if (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) > 1) {POSIX::strftime("%H:%M:%S %d.%m.%Y",localtime(time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime(time-60*60*24*25))."-14 00:00:01")))}
elsif (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) == 1) {POSIX::strftime("%H:%M:%S %d.%m.%Y",localtime(time_str2num(strftime("%Y",localtime(time-60*60*24*360))."-12-14 00:00:01")))}
else {POSIX::strftime("%d.%m.%Y",localtime(time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime())."-14 00:00:01")))}},

month_end_date {if (strftime("%d",localtime()) <= 13 && strftime("%m",localtime()) <= 12) {POSIX::strftime("%H:%M:%S %d.%m.%Y",localtime(time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime())."-13 23:59:59")))}
elsif (strftime("%d",localtime()) > 13 && strftime("%m",localtime()) == 12) {POSIX::strftime("%H:%M:%S %d.%m.%Y",localtime(time_str2num(strftime("%Y",localtime(time+60*60*24*360))."-01-13 23:59:59")))}
else {POSIX::strftime("%d.%m.%Y",localtime(time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime(time+60*60*24*25))."-13 23:59:59")))}},


Jedoch kann ich mit den Abfragen auch "Hilfs"-Userreadings erzeugen und wie von dir beschrieben weiter verfahren. Hierbei ergibt sich dann nur die 3. Frage warum die beiden vor dem dritten gesetzt werden. Erkennt FHEM hier eine "sinnvolle" Reihenfolge?


Floriky

Jetzt ist mir doch glatt der zweite Teil deiner Antwort entgangen.  ;)

Ich setze damit den Start- und Entpunkt für mein HP-InstantInk-Abo. Da ich mich automatisch benachrichtigen lassen möchte, wenn der gebuchte Tarif mal nicht ausreicht. Die Anfangs- und Endzeitpunkte möchte ich zum Vergleich von Soll- und Istverbrauch. Sollte ja nach 20% der Dauer keine 80% kontingent aufgebraucht sein. ;-)

Floriky

Zitat von: Otto123 am 18 April 2021, 17:10:27

Oder so:
{my ($z1,$z2);;
    if (strftime("%d",localtime()) <= 13 && strftime("%m",localtime()) <= 12)
       {$z1=time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime())."-13 23:59:59")}
    elsif (strftime("%d",localtime()) > 13 && strftime("%m",localtime()) == 12)
          {$z1=time_str2num(strftime("%Y",localtime(time+60*60*24*360))."-01-13 23:59:59")}
    else  {$z1=time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime(time+60*60*24*25))."-13 23:59:59")};;
    if    (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) > 1)
          {$z2=time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime(time-60*60*24*25))."-14 00:00:01")}
    elsif (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) == 1)
          {$z2= time_str2num(strftime("%Y",localtime(time-60*60*24*360))."-12-14 00:00:01")}
    else  {$z2=time_str2num(strftime("%Y",localtime())."-".strftime("%m",localtime())."-14 00:00:01")};;
round(($z1-$z2)/86400,0)}



Also so hat es jetzt auf jeden Fall funktioniert. Ich danke dir vielmals, Otto!


PS: Hab es noch um das "round(...,0)" ergänzt.  ;)

Otto123

Den Code mehrfach hinzuschreiben pflegt sich halt schlecht. Ich würde es dann wenigsten in zwei drei Bausteine fassen und die 99_myUtils schreiben.
Oder Anstatt 2 in 4 Readings schreiben (quasi die Zwischenergebnisse)

Ich zeig Dir mal noch was.
{my $day=(time_str2num('2021-05-13') - time_str2num('2021-04-14'))/86400}
Dass in deine beiden formatierten Daten (Readings) nur nicht in der typisch deutschen Anordnung und es kommen die 29 Tage raus die bei Deiner Formel auch rauskommen wenn Du nicht round() sondern int() nimmst - was meiner Meinung nach richtiger wäre ;)
Wenn Du aber deine Sekunden einfach zuerst ablegst, werden die anderen drei Readings sehr schön übersichtlich und einfach.

Edit
noch etwas: probier mal in der FHEM Kommandozeile ;)
{strftime("%Y",localtime())."-".strftime("%m",localtime())."-14 00:00:01"}
{strftime("%Y-%m-14 00:00:01",localtime())}

Wobei Du für time_str2num das hier  00:00:01 auch weglassen kannst. Es sei denn die 1 sec ist entscheidend :)
Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

Floriky

Hallo Otto,

erstmals vielen Dank für deine Mühe.

Zum englischen Datumsformat:
Dass es so einfacher ist, kann ich nachvollziehen, allerdings geht ja mein Hauptteil des Codes dafür drauf - über den Jahreswechsel hinweg - den Anfangs- und Endzeitpunkt eines Abrechnungszeitraum (also immer vom 14. bis 14. des Folgemonats) richtig "auszurechnen". Oder verstehe ich dich hier falsch?

zur 99_myUtils:
außer ein paar "zusammengeklauten" Codeschnipseln ist meine Utils noch recht jungfräulich. Wie und warum würdest du den Code dorthin auslagern?

Zur Rundung:
Int verwirft den Bruchteil anstatt ihn zu runden, habe ich gerade nachgelesen. Meinst du das macht sin, da der Tag quasi bei der Anzeige noch nicht beendet ist oder warum hältst du das für sinnvoller?

Zur Vereinfachung:
deinen anderen Tipp habe ich - wo immer ich es nachvollziehen konnte - umgesetzt. Meine gesamten Userreadings sehen jetzt folgendermaßen aus:

month_end_pages {ReadingsVal($name,"month_start_pages","0")+ReadingsVal($name,"Tarif","0")},

month_remaining_pages {ReadingsVal($name,"Tarif","0")-(ReadingsVal($name,"Printer_Pages_Total","0")-ReadingsVal($name,"month_start_pages","0"))},

month_remaining_pages_all {ReadingsVal($name,"Tarif","0")-(ReadingsVal($name,"Printer_Pages_Total","0")-ReadingsVal($name,"month_start_pages","0"))+ReadingsVal($name,"Angesammelt","0")},

month_usage {ReadingsVal($name,"Printer_Pages_Total","0")-ReadingsVal($name,"month_start_pages","0")},

month_start_date {
    if (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) > 1)
        {POSIX::strftime("%d.%m.%Y",localtime(time_str2num(strftime("%Y-%m-14",localtime(time-60*60*24*25)))))}
    elsif (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) == 1)
        {POSIX::strftime("%d.%m.%Y",localtime(time_str2num(strftime("%Y-12-14",localtime(time-60*60*24*360)))))}
    else
        {POSIX::strftime("%d.%m.%Y",localtime(time_str2num(strftime("%Y-%m-14",localtime()))))}},

month_end_date {
    if (strftime("%d",localtime()) <= 13 && strftime("%m",localtime()) <= 12)
        {POSIX::strftime("%d.%m.%Y",localtime(time_str2num(strftime("%Y-%m-13 23:59:59",localtime()))))}
    elsif (strftime("%d",localtime()) > 13 && strftime("%m",localtime()) == 12)
        {POSIX::strftime("%d.%m.%Y",localtime(time_str2num(strftime("%Y-01-13 23:59:59",localtime(time+60*60*24*360)))))}
    else
        {POSIX::strftime("%d.%m.%Y",localtime(time_str2num(strftime("%Y-%m-13 23:59:59",localtime(time+60*60*24*25)))))}},

month_usage_time {my ($z1,$z2);
    if (strftime("%d",localtime()) <= 13 && strftime("%m",localtime()) <= 12)
       {$z1=time_str2num(strftime("%Y-%m-13 23:59:59",localtime()))}
    elsif (strftime("%d",localtime()) > 13 && strftime("%m",localtime()) == 12)
          {$z1=time_str2num(strftime("%Y-01-13 23:59:59",localtime(time+60*60*24*360)))}
    else  {$z1=time_str2num(strftime("%Y-%m-13 23:59:59",localtime(time+60*60*24*25)))};
    if    (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) > 1)
          {$z2=time_str2num(strftime("%Y-%m-14",localtime(time-60*60*24*25)))}
    elsif (strftime("%d",localtime()) < 14 && strftime("%m",localtime()) == 1)
          {$z2= time_str2num(strftime("%Y-12-14",localtime(time-60*60*24*360)))}
    else  {$z2=time_str2num(strftime("%Y-%m-14",localtime()))};
int(((time()-$z2)/86400)/(($z1-$z2)/86400)*100)." %"},
month_usage_pages {round((((ReadingsNum($name,'Tarif',0)-ReadingsNum($name,'month_remaining_pages',0))/ReadingsNum($name,'Tarif',0))*100),0)." %"}


Otto123

Mit dem Format wollte ich Dir sagen: im englischen Format könntest Du die Readings einfach mit ReadingsVal() auslesen und direkt weiter verwenden.

Zur Rundung: Wir hatten diese Diskussion beim Kalender und den Tagen bis zu einem bestimmten Termin:
Wenn jetzt 14:00 Uhr wäre wären es bis morgen 15:00 1 Tag gerundet. Mit int wäre es kein Tag (sind ja nur 23 h)
Bis morgen 1:00 wäre es aber 0 Tage gerundet.
Es ist aber immer ein Kalendertag? Ist halt die Frage welche Tage Du exakt brauchst.

Zu den myUtils
im Userreading schreibst Du einfach in der Art
DatumEins {Datum1},
DatumZwei {Datum2},
DatumDiff {Datum2 - Datum1}

In der Utils
sub Datum1 {Berechnung Datum 1}
sub Datum2 {Berechnung Datum 2}

Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

Floriky

Zitat von: Otto123 am 19 April 2021, 23:40:57

In der Utils
sub Datum1 {Berechnung Datum 1}
sub Datum2 {Berechnung Datum 2}


Hallo Otto, danke für die Unterstützung. Habe nun den folgende funktionierende Funktion in meiner Utils:

sub HPIIMUT {my ($iED,$iSD);

my $fd = 14;
my $ld = 13;

    # Enddatum des Abrechnungszeitraums in Sekunden
    if      (strftime("%d",localtime()) <= $ld && strftime("%m",localtime()) <= 12)
                {$iED=time_str2num(strftime("%Y-%m-$ld 23:59:59",localtime()))}
    elsif   (strftime("%d",localtime()) > $ld && strftime("%m",localtime()) == 12)
                {$iED=time_str2num(strftime("%Y-01-$ld 23:59:59",localtime(time+60*60*24*360)))}
    else 
                {$iED=time_str2num(strftime("%Y-%m-$ld 23:59:59",localtime(time+60*60*24*25)))};

    # Startdatum  des Abrechnungszeitraums in Sekunden
    if      (strftime("%d",localtime()) < $fd && strftime("%m",localtime()) > 1)
                {$iSD=time_str2num(strftime("%Y-%m-$fd",localtime(time-60*60*24*25)))}
    elsif   (strftime("%d",localtime()) < $fd && strftime("%m",localtime()) == 1)
                {$iSD= time_str2num(strftime("%Y-12-$fd",localtime(time-60*60*24*360)))} 
    else
        {$iSD=time_str2num(strftime("%Y-%m-$fd",localtime()))};

    # Erstellung Readings
    fhem("setreading HP_InstantInk month_usage_time_myutils ".int(((time()-$iSD)/86400)/(($iED-$iSD)/86400)*100)." %"."");
    fhem("setreading HP_InstantInk month_start_date_myutils ".POSIX::strftime("%d.%m.%Y",localtime($iSD))."");
    fhem("setreading HP_InstantInk month_end_date_myutils ".POSIX::strftime("%d.%m.%Y",localtime($iED))."");
}


Jetzt kann ich die Funktion ja natürlich über at, notify, etc. aufrufen. Geht das auch irgendwie, dass die Funktion - quasi wie ein Userreading - jedes mal ausgeführt wird sobald sich ein Reading in dem Device ändert?

Otto123

Ok Du hast jetzt alles in eine Routine gepackt und setzt die Readings von dort.
Du kannst auf einen Event von deinem HP_InstantInk mit einem notify reagieren (bitte nicht auf alle!) und dann die Routine aufrufen. Im notify wird eigentlich der set Befehl an das aufrufende Device verhindert um Endlosschleifen zu vermeiden. Das kannst Du mit einem FHEM sleep umgehen.
Aus fhem("Befehl") machst Du einfach fhem("sleep 0.1;Befehl")
Wobei mir beim genauen Hinsehen Deine Schreibweisen der drei FHEM Befehle noch etwas komisch aussehen. Die ."" am Ende sind überflüssig.
Eigentlich kannst Du alles mit einem sleep und drei Befehlen in einem fhem() Aufruf machen.
So mal versuchen?
fhem("sleep 0.1;setreading HP_InstantInk month_usage_time_myutils ".int(((time()-$iSD)/86400)/(($iED-$iSD)/86400)*100)." % ;
                   setreading HP_InstantInk month_start_date_myutils ".POSIX::strftime("%d.%m.%Y",localtime($iSD))." ;
                   setreading HP_InstantInk month_end_date_myutils ".POSIX::strftime("%d.%m.%Y",localtime($iED)) );
Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz