Anfängerfragen

Hier bitte alles rein was Off-topic ist.

Moderatoren: Cyrelian, seppy

harasch
Beiträge: 17
Registriert: 24. Dez 2023 20:09
Answers: 0

Re: Anfängerfragen

Beitrag von harasch »

Hallo udo1toni,
bin wieder zurück. Habe jetzt lange daran herumgebastelt bis es letztendlich lief. Ich habe einen Code hier vom Forum etwas abändern müssen aber es scheint richtig zu zählen.
Ich hatte und habe immer noch Verständnisprobleme mit der DSL Syntax.
das ist der Code welcher bei mir funktioniert.

Code: Alles auswählen

var Number Letzter_abgelesener_Zaehlerstand = 28177.429
var Number Einheit = 0.01
//var Number Gaspreis_Monat = 200.1 // € pro Jahr
var Number Gaspreis_Tag = 54.82191780821918 // ct pro Tag
var Number Gaspreis_kWh = 12.80 // ct pro kWh
var Number Gaspreis_zZahl = 0.9244 // z Zahl
var Number Gaspreis_Brennwert = 11.495 //Brennwert
var Faktor = Gaspreis_zZahl * Gaspreis_Brennwert * Gaspreis_kWh 
var Umrechnen = Gaspreis_zZahl * Gaspreis_Brennwert



if (Gaszaehler_Stand.state != "NULL" && Gaszaehler_Stand.state < Letzter_abgelesener_Zaehlerstand) {
postUpdate(Gaszaehler_Stand, (Letzter_abgelesener_Zaehlerstand))  }
postUpdate(Gaszaehler_Heute, (Gaszaehler_Heute.state as DecimalType + Einheit))
postUpdate(Gaszaehler_Stand, (Gaszaehler_Stand.state as DecimalType + Einheit))
postUpdate(Gaskosten_Heute, Gaszaehler_Heute.state as DecimalType * Faktor / 100)
postUpdate(Gas_Heute, Gaszaehler_Heute.state as DecimalType * Umrechnen)
Jetzt wollte ich noch eine Regel schreiben bei der das item Gaskosten_Heute einem Item Gaskosten_bis_dato übergeben wird und anschließen das item Gaskosten_bis_dato das item Gaskosten_Heute.state aufaddiert wird. Funktioniert erwartungsgemäß nicht.
Alleine die Zeile var Number dummy = 0 erzeugt bei mir schon ERROR warum.
2024-04-07 17:38:01.249 [WARN ] [.internal.OpenhabGraalJSScriptEngine] - Failed to retrieve script script dependency listener from engine bindings. Script dependency tracking will be disabled.
2024-04-07 17:38:08.462 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: SyntaxError: <eval>:1:11 Expected ; but found dummy
var Number dummy = 0
ich verstehe das nicht, denn im dem Code für den Gaszaehler sind doch etliche Variablen angelegt worden.
Wiso funktioniert die Zeile nicht.
Ich hatte mir so etwas ausgedacht.

Code: Alles auswählen

var Number dummy = 0
dummy = Gaskosten_Heute.state
postUpdate(Gaskosten_bis_dato, Gaskosten_Heute.state + (dummy)) 
funktioniert aber nicht.

Ich hatte noch folgendes im Sinn
postUpdate(Gaskosten-bis-dato, (Gaskosten_bis_dato.state) + (Gaskosten-Heute.state))
kommen aber Seiteneffekte und ist wahrscheinlich Blödsinn. In Bascom funktioniert so etwas.

Hast Du einen Rat?

Gruß, Harald

Benutzeravatar
udo1toni
Beiträge: 13989
Registriert: 11. Apr 2018 18:05
Answers: 222
Wohnort: Darmstadt

Re: Anfängerfragen

Beitrag von udo1toni »

Die Rule ist (mit Verlaub) schlecht.

Erst mal der Vergleich

Code: Alles auswählen

if (Gaszaehler_Stand.state != "NULL") 
Dieser Teil ist immer wahr, denn "NULL" ist ein String, der Status nimmt niemals als Wert den String NULL an.
Was Du eigentlich prüfen willst, ist, ob der Status eine gültige Zahl enthält.

Code: Alles auswählen

if(Gaszaehler_Stand.state instanceof Number)
Die Prüfung muss separat stattfinden, nicht ver-und-et mit anderen Tests. Diese Prüfung verhindert nullPointerExceptions bei folgenden Schritten, nämlich, wenn man versucht, den Status des Items in eine Zahl zu wandeln. Das wäre der Teil, wo Du in der Rule Gaszaehler_Stand.state as DecimalType verwendest, allerdings ohne Klammern, das kann funktionieren. Oder auch nicht.
Du greifst in der Rule auf Gaszaehler_Stand zu und schreibst für den Fall, dass der Wert in diesem Item kleiner ist als der abgelesene Zählerstand eben diesen letzten Zählerstand in das Item, mutmaßlich als eine Art Rückfall, was allerdings eher so semi ist.
Schwerwiegender ist aber, dass Du anschließend dieses Item lesend verwendest, offenbar darauf vertrauend, dass nach dem postUpdate der neue Wert in dem Item steht. Das kann funktionieren. Oder auch nicht. Tatsächlich ist nicht vorhersehbar, wie die Rule hier reagiert. Glücklicherweise wird das nur zum Problem, wenn das Item einen Wert kleiner dem Zählerstand hat...
Also besser so:

Code: Alles auswählen

val nLastCount  = 28177.429                                           // m³ initial
val nQuantum    = 0.01                                                // m³ pro Impuls
val nEURkWh     = 0.128                                               // €/kWh 
val nZahl       = 0.9244                                              // whatever...
val nBrennwert  = 11.495
val nEnergie    = nBrennwert * nZahl
val nFaktor     = nEnergie * nEURkWh

var nCurcCount  = nLastCount                                          // Initialwert

if(Gaszaehler_Stand.state instanceof Number)                          // falls gültige Zahl
    if((Gaszaehler_Stand.state as Number).floatValue > nCurrCount)    // falls Status größer als Initialwert
        nCurrCount = (Gaszaehler_Stand.state as Number).floatValue    // Status nutzen

val nNewCount = nCurrentCount + nQuantum                              // Zählerstand aufaddieren

Gaszaehler_Stand.postUpdate(nNewCount)                                // neuen Stand in Items schreiben
Gas_Heute.postUpdate(nNewCount * nEnergie)
Gaskosten_Heute.postUpdate(nNewCount * nFaktor)
Allgemein sollte es nicht notwendig sein, die Objekte als Number zu definieren.
Ich habe weniger ausladende Namen für die Konstanten und die Variable gewählt, das ist aber nur meine Präferenz.

Was Deine nicht funktionierende Ergänzung betrifft: Was möchtest Du erreichen? dummy definierst Du mit dem Wert 0, dann weist Du Gaskosten_Heute.state zu (wohlgemerkt: nicht den Number Wert... wird mit hoher Wahrscheinlichkeit nicht funktionieren) und anschließend fügst Du den Wert nochmals zum Status hinzu. Du verwendest dazu die Action. Nun hat die Action die unangenehme Eigenschaft, nur mit zwei Strings zu arbeiten.
openHAB versucht, wenn möglich passende Typen zu verwenden, manchmal geht das aber auch schief. Die Berechnung ist aber ohnehin nicht stimmig, denn Du könntest genauso gut auch dummy * 2 schreiben :)
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

harasch
Beiträge: 17
Registriert: 24. Dez 2023 20:09
Answers: 0

Re: Anfängerfragen

Beitrag von harasch »

Hallo udo1toni,
Gaskosten_Heute.state zu (wohlgemerkt: nicht den Number Wert
ich dachte '.state' währe der Number wert.
Wie komme ich an den Number wert?
Klar dass der code von der Rule nicht schoen aussieht, so ist es wenn man keine Ahnung hat und den code von Anderen nutzt, aber komischerweise funktioniert die Rule und die Berechnungen stimmen sogar überein mit meinem Gaszähler. Also Gasverbrauch sowie auch die Kosten. Ich habe das mal Hochgerechnet und mit meinem Verbrauchswerten von den etzten Jahren verglichen. Kommt sehr gut nahe.
Wie gesagt muß ich noch einiges lernen bevor ich die Syntax verstehe.
Danke, für Dein Codebeispiel ich werde es mal ausprobieren.
Was ich aber auch nicht verstehe ist folgendes.
val ist quasi eine Konstante, wobei var eine Variable ist. So mein Verständnis.Wiso dann das?

Code: Alles auswählen

val nNewCount = nCurrentCount + nQuantum 
nNewCount dürfte dann keinen anderen Wert annehmen.
Wieder mal Verständnis Problem. Im Hintekopf vergleiche ich immer die Syntax mit Bascom, dort könnte ich eine so geschriebene Konstante nicht überschreiben.

Gruß, Harald

Benutzeravatar
udo1toni
Beiträge: 13989
Registriert: 11. Apr 2018 18:05
Answers: 222
Wohnort: Darmstadt

Re: Anfängerfragen

Beitrag von udo1toni »

harasch hat geschrieben: 8. Apr 2024 09:19 ich dachte '.state' währe der Number wert.
Nein, .state ist (wie der Name schon sagt) der Status. openHAB kann .state als Zahl betrachten, sicher ist das aber nicht. Will man zuverlässige Ergebnisse haben, muss man den Status eines Number Items auf eine gültige Zahl prüfen und gegebenenfalls nach Number casten, das heißt, man erzwingt, dass openHAB den Status als Number betrachtet. Weil ein Number Item auch andere Datentypen enthalten kann (z.B. NULL oder UNDEF, beide nicht Teil des Number Datentyps), muss man vorher auf Gültigkeit prüfen.

Wenn es sich beim Item um ein String Item handelt, funktioniert das casten übrigens nicht, stattdessen muss man den String dann parsen. Und gemeinerweise ist .state auch beim String Item kein String, auch wenn es nur so betrachtet werden kann. Deshalb sähe der Aufruf dann so aus:

Code: Alles auswählen

val nMeinWert = Float.parseFloat(MeinStringItem.state.toString)
Auch das Parsen liefert nur dann eine gültige Zahl, wenn der komplette String nur Ziffern, maximal einen Dezimalpunkt und evtl. noch hängende Leerzeichen enthält. Schon bei führenden Leerzeichen bin ich mir ziemlich sicher, dass Float.parseFloat() - oder die vergleichbaren parse-Funktionen Integer.parseInt() und Double.parseDouble() - scheitern werden.
harasch hat geschrieben: 8. Apr 2024 09:19 val ist quasi eine Konstante, wobei var eine Variable ist. So mein Verständnis.Wiso dann das?

Code: Alles auswählen

val nNewCount = nCurrentCount + nQuantum 
nNewCount dürfte dann keinen anderen Wert annehmen.
Tut es ja auch nicht :) das ist der Punkt. Innerhalb der Rule gibt es nur eine lokale Variable, alle anderen sind lokale Konstanten, es wird einmalig ein Wert zugewiesen, der zur Laufzeit der Rule nicht mehr verändert wird. Der Unterschied ist marginal und vermutlich nicht mal zuverlässig messbar, aber die Konstante benötigt nun mal weniger Platz im Speicher und Rechenzeit.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

harasch
Beiträge: 17
Registriert: 24. Dez 2023 20:09
Answers: 0

Re: Anfängerfragen

Beitrag von harasch »

Hallo udo1toni,
ich steh auf'm Schlauch. Verstehe ich das richtig.
val Konstante = 1+2 das heißt die Konstante entsteht durch eine Berechnung. Ich kenne Konstanten nur so byval Konstante = 3. Der Konstanten wird ein Wert zu gewiesen und kann auch nicht verändert werden. Aber das die Konstante durch eine Berechnung gebildet wird kenne ich nicht.
Was ich eignetlich noch machen möchte ist den Wert Gasverbrauch_Heute einfach einem neuem Item z.B. Gasverbrauch_bis_zum_jetzigen_Zeitpunkt aufaddieren. Das heißt jeden Tag um 23:55 wird dem Item Gasverbrauch_bis_zum_jetzigen_Zeitpunkt der Wert vom Item Gasverbrauch_Heute dazu addiert. (Die namen der Items sind natürlich nicht so ausscheifend, nur zum besseren Verständnis).

Gruß, Harald

Benutzeravatar
udo1toni
Beiträge: 13989
Registriert: 11. Apr 2018 18:05
Answers: 222
Wohnort: Darmstadt

Re: Anfängerfragen

Beitrag von udo1toni »

harasch hat geschrieben: 8. Apr 2024 19:06 val Konstante = 1+2 das heißt die Konstante entsteht durch eine Berechnung.
Ja, selbstverständlich. Es handelt sich um eine Wertzuweisung, die bei lokalen Konstanten einmal pro Durchlauf möglich ist, bei globalen Konstanten einmal pro Reload der Datei (globale Konstanten/Variablen gehen nur über *.rules Dateien). Was auf der rechten Seite steht, also der Wert, der der Konstanten zugewiesen wird, ist vollkommen egal, solange der komplette Ausdruck einen Wert ergibt. Du kannst auch sowas machen:

Code: Alles auswählen

val MeinWert = if(Wert1 > Wert2) Wert2 else Wert1
Das nennt sich ternärer Operator, und abhängig vom bool'schen Ausdruck innerhalb des if-Statements wird entweder der erste oder der zweite Wert verwendet.
Das ist allerdings keine Besonderheit der DSL, sondern eher Standard, solange Du in einer der Hochsprachen unterwegs bist (in Assembler hingegen... ;) ).
harasch hat geschrieben: 8. Apr 2024 19:06 Was ich eignetlich noch machen möchte ist den Wert Gasverbrauch_Heute einfach einem neuem Item z.B. Gasverbrauch_bis_zum_jetzigen_Zeitpunkt aufaddieren. Das heißt jeden Tag um 23:55 wird dem Item Gasverbrauch_bis_zum_jetzigen_Zeitpunkt der Wert vom Item Gasverbrauch_Heute dazu addiert.
Grundsätzlich sollte das unnötig sein. Wenn Du ein Item hast, welches den aktuellen Zählerstand enthält (also Gaszaehler_Stand) und dieses Item mit der everyChange Strategy persistierst (am besten in Kombination mit restoreOnStartup, je nach verwendeter Persistence zusätzlich noch mit everyMinute), kannst Du einfach die Persistence befragen, also z.B. Gaszaehler_Stand.deltaSince(Startzeitpunkt) wird die Differenz zwischen "jetzt" und "Startzeitpunkt" ausgeben, also z.B. für Mitternacht bekommst Du dann den bisherigen Gasverbrauch heute, wenn Du Mitternacht am letzten Montag nimmst, bekommst Du den Wochenverbrauch, Mitternacht am Monatsersten liefert Dir den bisherigen Monatsverbrauch usw. und mit .deltaBetween() kannst Du auch zwei "beliebige" Zeitpunkte nutzen, um z.B. on-the-fly den Monatsverbrauch des aktuellen Monats im letzten Jahr anzuzeigen - immer vorausgesetzt natürlich, dass korrekte Daten für den angefragten Zeitraum vorhanden sind. Deine Rule muss sich also nur um das Aufaddieren des Zählerstands kümmern, und je nach gewünschten Daten kannst Du diese innerhalb der Rule errechnen, ohne dafür weitere Items zu nutzen (natürlich brauchst Du die Items irgendwann, um die Werte anzuzeigen...)
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

harasch
Beiträge: 17
Registriert: 24. Dez 2023 20:09
Answers: 0

Re: Anfängerfragen

Beitrag von harasch »

Hallo udo1toni,
erstmal eine Richtigstellung:
was ich das letzte mal erzählt habe bezüglich Bascom war natürlich Quatsch.
Eine Kostante wird so deklariert: const A = 3 und kann auch nicht verändert werden.
Den Ausdruck byval gibt es in einer Funktion oder Prozedur bei der Werte übergeben werden. z.B.
sub berechnen(byval wert1,wert2) diese können in der Funktion bezw. Prozedur nicht verändert werden. Wohl aber bei der Schreibweise
sub berechnen(wert1,wert2). Soviel zur Richtigstellung.

Würde in dem Fall folgendes funktionieren: erstmal ein neues Item erstellen.

postUpdate(Gaskosten_insgesamt, (Gaskosten_Heute.deltaSince(Startzeitpunkt)

Problem: woher die Daten nehmen bezw. wie teile ich der Rule mit wo die Daten gespeichert sind?
wie schreibe ich den Startzeitpunkt, wie Tag,Monat,Jahr.

Gruß, Harald

Benutzeravatar
udo1toni
Beiträge: 13989
Registriert: 11. Apr 2018 18:05
Answers: 222
Wohnort: Darmstadt

Re: Anfängerfragen

Beitrag von udo1toni »

Das erste, was ich empfehlen möchte, ist, nicht die Action zu verwenden, wenn das irgendwie vermeidbar ist.
Action, schlecht:

Code: Alles auswählen

postUpdate(Itemname, Wert)
Methode, gut:

Code: Alles auswählen

Itemname.postUpdate(Wert)
Die Wirkung beider Schreibweisen ist gewöhnlich identisch, die erste Schreibweise verlangt aber zwingend zwei Strings, während die zweite Schreibweise ein Item und eine der vom Item unterstützten Datenformate für den Wert verlangt.
Die Action kannst Du verwenden, wenn Du den Namen des Items "errechnest" und es sonst keine elegante Methode gibt, das Item als Item zu verwenden. Das ist so gut wie nie der Fall. Die Action ist ein Notbehelf.
harasch hat geschrieben: 9. Apr 2024 15:52 Problem: woher die Daten nehmen bezw. wie teile ich der Rule mit wo die Daten gespeichert sind?
Voraussetzung für die Verwendung der Persistence Methoden ist, dass das jeweilige Item persistiert wird. Wenn nicht anders angegeben, wird die Default Persistence verwendet. Man kann, wenn es mehrere aktive Persistence Services gibt, auch gezielt angeben, welcher verwendet werden soll.
harasch hat geschrieben: 9. Apr 2024 15:52 wie schreibe ich den Startzeitpunkt, wie Tag,Monat,Jahr.
Die Zeitpunkte werden als Datetime angegeben, also z.B. so:

Code: Alles auswählen

val dtMitternacht = now.with(LocalTime.MIDNIGHT)
val dtWochenstart = dtMitternacht.minusDays(dtMitternacht.getDayOfWeek.getValue - 1) // Montag = 1, Sonntag = 7
val dtMonatsanfang = dtMitternacht.withDayOfMonth(1)

val nVerbrauchHeute = Gaszaehler_Stand.deltaSince(dtMitternacht)
val nVerbrauchWoche = Gaszaehler_Stand.deltaSince(dtWochenstart)
val nVerbrauchMonat = Gaszaehler_Stand.deltaSince(dtMonatsanfang)
Und mit dem absoluten Volumen kannst Du die Kosten leicht ausrechnen, die Abrechung erfolgt ja volumenbezogen.
Falls Du noch tagesbezogene Fixkosten mit einberechnen willst, kannst Du auch leicht mit

Code: Alles auswählen

val nTage = 1 - now.until(dtMonatsanfang,DAYS)
die Anzahl angebrochener Tage bestimmen (wobei ich nicht sicher bin, ob die Funktion so korrekt ist... hab das noch nie genutzt... ist aber auf jeden Fall möglich, entweder so oder auch leicht abgewandelt)

In openHAB4 ist die Persistence nicht automatisch mit konfiguriert (anders als in openHAB3), Du musst also, um in den Genuss der Funktion zu kommen, zunächst einen Persistence Service installieren (rrd4j ist für den Anfang prima), diesen als Default Persistence definieren und außerdem noch bestimmen, welche Items persistiert werden sollen.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

harasch
Beiträge: 17
Registriert: 24. Dez 2023 20:09
Answers: 0

Re: Anfängerfragen

Beitrag von harasch »

Hallo udo1toni,
danke erst mal für Deine Hilfestellung.
Ich habe eine jdbc Persistence, die funktioniert auch. Nur wie teile ich der Rule den Pfad mit.

Ich muß jetzt erst mal alles austesten was Du mir mitgeteilt hast.

Komischerweise funktioniert das bei mir. Ich hatte das auch schon mal mit der guten Methode versucht. Es gelang mir aber nicht die Rule zum laufen zu bringen.

Code: Alles auswählen

postUpdate(Gaszaehler_Gestern, (Gaszaehler_Heute.state))
postUpdate(Gaszaehler_Heute,(0))
 
postUpdate(Gaskosten_Gestern, (Gaskosten_Heute.state))
postUpdate(Gaskosten_Heute,(0))
  
postUpdate(Gas_Gestern, (Gas_Heute.state))
postUpdate(Gas_Heute, (0))
Gruß, Harald

harasch
Beiträge: 17
Registriert: 24. Dez 2023 20:09
Answers: 0

Re: Anfängerfragen

Beitrag von harasch »

Hallo udo1toni,
die Rule läuft nicht. Ich habe folgende Fehlermeldungen.

Code: Alles auswählen

2024-04-10 14:50:10.584 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID '6137111b87' failed: val nLastCount = 28238.55      //m³ initial
val nQuantum = 0.01          //m³ pro Impulse
val nEURkwh = 0.128          //€/Kwh
val nZahl = 0.9244        
val nBrennwert = 11.495
val nEnergie = nBrennwert * nZahl
val nFaktor = nEnergie * nEURKwh


var nCurrCount = nLastCount   // Initialwert

if(Gaszaehler_Stand.state instanceof Number)      //falls gültig
  if((Gaszaehler_Stand.state as Number).floatValue > nCurrCount) //falls Status größer als Initialwert
    nCurrCount = (Gaszaehler_Stand.state as Number).floatValue  //Status nutzen
    
val nNewCount = nCurrCount + nQuantum      //Zaehlerstand aufaddieren

Gaszaehler_Stand.postUpdate(nNewCount)    //neuen Stand in items schreiben
Gas_Heute.postUpdate(nNewCount * nEnergie)
Gaskosten_Heute.postUpdate(nNewCount * nFaktor)


   The method or field nEURKwh is undefined; line 7, column 237, length 7
Hast Du eine Idee?

Gruß, Harald

Antworten