Seite 1 von 3

HP Printer Binding - monatliche Seiten berechnen

Verfasst: 3. Sep 2019 23:00
von mcdandrew
Ich habe für unseren Officejet das HP Printer Binding installiert...
Wir nutzen das HP Instant Ink Abo...d.h. wir haben jeden Monat ein gewisses Volumen an Seiten welches ich gerne in Openhab darstellen möchte.

Meine Idee war nun folgende...

Code: Alles auswählen

Number officejet_totalcount			"Anzahl gedruckter Seiten"		{channel="hpprinter:printer:djprinter:usage#totalCount"} 
Number officejet_monthcount				"Seiten aktueller Zeitraum"
Dazu gibt es dann die folgenden Regeln

Summieren der Anzahl der Seiten...rule wird beim Ändern des Items "officejet_totalcount" ausgeführt

Code: Alles auswählen

rule "Druckseite add"
when 
	Item officejet_totalcount changed
	then
		officejet_monthcount.postUpdate(officejet_monthcount + 1)
end
Jeweils am 18. jeden Monats startet ein neuer Abrechnungszeitraum...an diesem wird der Counter auf 0 gesetzt.

Code: Alles auswählen

rule "Druckseiten im Monat reset"
when 
	Time cron "0 1 18 * * ?"
	then
		officejet_monthcount.postUpdate(0)
end
Problem nun...es funktioniert nicht.
Die zweite Regel konnte ich noch nicht testen.

Re: HP Printer Binding - monatliche Seiten berechnen

Verfasst: 4. Sep 2019 11:17
von udo1toni
Ja, Du hast da ein paar Fehler drin.

Zum ersten, wenn Du auf den Status eines Items zugreifen willst, musst Du das Wort .state anhängen.
Zum zweiten, ein Status ist ein Status. Du brauchst aber eine Zahl, also musst Du ein Type Casting verwenden.
Zum dritten, wenn openHAB startet, enthält das Item keine gültige Zahl, das musst Du berücksichtigen.
Zum vierten kann es sein, das die Rule auch triggert, wenn Du nichts gedruckt hast, weil officejet_totalcount erstmalig einen Wert zugewiesen bekommt.

Deine Rule müsste also eher so aussehen:

Code: Alles auswählen

rule "Druckseite add"
when 
    Item officejet_totalcount changed
then
    if(!(previousState instanceof Number) return;                // das Item wurde gerade initialisiert, Rule abbrechen
    if(!(officejet_monthcount.state instanceof Number)) {
        officejet_monthcount.postUpdate(0)                           // einmalig initialisieren
        Thread::sleep(300)                                                   // und openHAB etwas Zeit geben
    }
    officejet_monthcount.postUpdate((officejet_monthcount.state as Number) + 1)
end
Es bietet sich an, das Item zu persistieren, schon um beim Start von openHAB den letzen gültigen Wert zu erhalten.
Allerdings bin ich mir nicht sicher, ob officejet_totalcount wirklich immer nur um 1 anwächst. Es wäre also vermutlich besser, die Differenz zwischen dem alten und dem neuen totalcount zu nutzen:

Code: Alles auswählen

    officejet_monthcount.postUpdate((officejet_monthcount.state as Number) + (officejet_totalcount.state as Number) - (previousState as Number))
Noch besser wäre es vermutlich, totalcount zu persistieren (z.B. mit rrd4j) und in der Rule die Differenz zum letzen 18. zu bestimmen, also in etwa so:

Code: Alles auswählen

rule "Druckseite add"
when 
    System started or                  // Systemstart, Anzeige mit aktuellem Wert initialisieren
    Item officejet_totalcount changed
then
    val nDay = now.minusMonths(if(now.getDayOfMonth < 18) 1 else 0).minusDays(now.minusMonths(if(now.getDayOfMonth < 18) 1 else 0).getDayOfMonth).plusDays(18).timeAtStartOfDay
    officejet_monthcount.postUpdate((officejet_totalcount.state as Number) - (officejet_totalcount.historicState(nDay).state as Number)
end
nDay ist dabei der letzte 18., Mitternacht. der Ausdruck sieht ziemlich krude aus :), weshalb ich ihn noch mal auseinander nehmen möchte:

Code: Alles auswählen

    val nDay = now //Jetzt
    .minusMonths( //Minus Monate
    if(now.getDayOfMonth < 18) 1 else 0 // falls vor dem 18. 1 sonst 0 
    ).minusDays( //Minus Tage
    now.minusMonths(if(now.getDayOfMonth < 18) 1 else 0) // siehe voriger Ausdruck
    .getDayOfMonth) // aktueller Tag (oder der aktuelle Tag des Vormonats, das ergibt also den letzten Tag des Vormonats oder Vor-Vormonats)
    .plusDays(18)  // Zähle 18 Tage hinzu (also der 18. des aktuellen Monats oder des Vormonats) 
    .timeAtStartOfDay  // Nimm Mitternacht als Zeitpunkt
Dieser Ausdruck sollte also immer den Beginn des Abrechnungszeitraums liefern. Falls der Zeitpunkt eher 1 Uhr sein soll, müsste man noch ein .plusHours(1) ran hängen. Der Punkt ist, dass es mit dieser Berechnung keine Rolle spielt, ob wir ein Schaltjahr haben oder eine Jahresgrenze überschreiten, vor oder nach dem 18 des Monats sind, der Monat 28, 29, 30 oder 31 Tage hat.

Damit wäre dann die zweite Rule auch obsolet. Die zweite Rule hat ansonsten auch noch einen Fehler, denn Time cron arbeitet sekundengenau (das ist quartz cron, nicht das gewohnte Tool in GNU/Linux oder anderen unixoiden Umgebungen)
Dein Ausdruck

Code: Alles auswählen

Time cron "0 1 18 * * ?"
bedeutet also: um 18:01:00, täglich. Was Du vermutlich eher wolltest, war das hier:

Code: Alles auswählen

Time cron "0 0 1 18 * ?"
bedeutet: um 01:00:00, am 18. jeden Monats.

Da die Rule Persistence nutzt, wird sie erst nach dem ersten 18. funktionieren, der schon persistiert wurde.

Re: HP Printer Binding - monatliche Seiten berechnen

Verfasst: 6. Sep 2019 20:17
von mcdandrew
Ich danke wie immer für Deine klasse Unterstützung.

Ich habe es nun erst einmal implementiert und warte gespannt auf den 18.09 :lol:
Ich berichte ob es geklappt hat.

Re: HP Printer Binding - monatliche Seiten berechnen

Verfasst: 6. Sep 2019 21:06
von udo1toni
Ich dann auch :)

Re: HP Printer Binding - monatliche Seiten berechnen

Verfasst: 23. Nov 2019 22:05
von mcdandrew
....ich bin dir/ euch noch ein Antwort schuldig :roll:

Nachdem ich mich nun für meine Charts mit dem Thema Persistenz und rr4dj auseinander gesetzt habe wollte ich das Thema hier auch zu ende bringen. Leider funktioniert es nun doch nicht wie erwartet...

Ich habe als erstes das Item persistiert

Code: Alles auswählen

officejet_monthcount			: strategy = everyChange, restoreOnStartup
Bei deinem Code kam die folgende Fehlermeldung

Code: Alles auswählen

Error during the execution of startup rule 'Druckseite add': 'AtStartOfDay' is not a member of 'org.joda.time.DateTime';
Habe dann ein wenig gesucht und habe in der Doku die folgende Methode gefunden

Code: Alles auswählen

public DateTime withTimeAtStartOfDay()
Der Fehler ist verschwunden nun erscheint allerdings ein neuer

Code: Alles auswählen

Error during the execution of startup rule 'Druckseite add': cannot invoke method public abstract org.eclipse.smarthome.core.types.State org.eclipse.smarthome.core.persistence.HistoricItem.getState() on null
Ich vermute den Fehler da das Item derzeit noch Null ist deshalb habe ich die Zeile

Code: Alles auswählen

//System started or
auskommentiert

Fehlermeldung ist verschwunden nun aber die nächste bei Ausführung es Druckjobs...
Hier weiß ich nun nicht mehr weiter...

Code: Alles auswählen

Rule 'Druckseite_add': An error occurred during the script execution: index=1, size=1

Re: HP Printer Binding - monatliche Seiten berechnen

Verfasst: 24. Nov 2019 00:33
von udo1toni
Zaig doch mal bitte die Rule exakt so, wie Du sie implementiert hast.

Re: HP Printer Binding - monatliche Seiten berechnen

Verfasst: 24. Nov 2019 01:00
von mcdandrew

Code: Alles auswählen

rule "Druckseite_add"
when 
    //System started or                  // Systemstart, Anzeige mit aktuellem Wert initialisieren
    Item officejet_totalcount changed
then
    val nDay = now.minusMonths(if(now.getDayOfMonth < 18) 1 else 0).minusDays(now.minusMonths(if(now.getDayOfMonth < 18) 1 else 0).getDayOfMonth).plusDays(18).withTimeAtStartOfDay
   
    officejet_monthcount.postUpdate((officejet_totalcount.state as Number) - (officejet_totalcount.historicState(nDay).state as Number))
end

Re: HP Printer Binding - monatliche Seiten berechnen

Verfasst: 24. Nov 2019 01:54
von udo1toni
Um es ein wenig einzugrenzen, hab ich die Rule etwas kleinteiliger gemacht:

Code: Alles auswählen

rule "Druckseite_add"
when 
    //System started or                  // Systemstart, Anzeige mit aktuellem Wert initialisieren
    Item officejet_totalcount changed
then
    val Integer nMonth = if(now.getDayOfMonth < 18) 1 else 0
    val Integer nDays =now.minusMonths(nMonth).getDayOfMonth

    val nDay = now.withTimeAtStartOfDay.minusMonths(nMonth).minusDays(nDays-18)

    logInfo("print.count","nMonth={} nDays={} nDay={}",nMonth,nDays,nDay)

    val Number nTcount = officejet_totalcount.state as Number
    val Number nHcount = officejet_totalcount.historicState(nDay).state as Number

    logInfo("print.count","nTcount={} nHcount={}",nTcount,nHcount)

    officejet_monthcount.postUpdate(nTcount - nHcount)
end
Die beiden logInfo Zeilen geben die Zwischenergebnisse aus.

Re: HP Printer Binding - monatliche Seiten berechnen

Verfasst: 24. Nov 2019 02:03
von mcdandrew
Folgende Ausgabe sehe ich im LogViewer

Code: Alles auswählen

2019-11-24 02:02:19.441 [INFO ] [e.smarthome.model.script.print.count] - nMonth=0 nDays=24 nDay=2019-11-18T00:00:00.000+01:00

2019-11-24 02:02:19.462 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Druckseite_add': cannot invoke method public abstract org.eclipse.smarthome.core.types.State org.eclipse.smarthome.core.persistence.HistoricItem.getState() on null

Re: HP Printer Binding - monatliche Seiten berechnen

Verfasst: 24. Nov 2019 18:40
von udo1toni
Welchen Persistence Service hast Du eingerichtet? Ist der Persistence Service auch default Persistence Service?