IO Bitmaske aus Dezimalwert nutzen für postUpdate von Contacts

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Antworten
maxico
Beiträge: 4
Registriert: 3. Jan 2024 11:20
Answers: 0

IO Bitmaske aus Dezimalwert nutzen für postUpdate von Contacts

Beitrag von maxico »

Hallo zusammen.
Lese hier schon viele Jahre still mit und bedanke mich für Eure Ratschläge und Tipps, welche oft zum Erfolg geführt haben. Jetzt hängt es bei einer Rule.

Code: Alles auswählen

import java.math.BigInteger
rule "lightstatusviamqtt"
when Item tinkerforge_37_callback_io16_bricklet_ESS changed
then
  
  val port = transform("JSONPATH", "$.port", tinkerforge_37_callback_io16_bricklet_ESS.state.toString) 
  val interrupt_mask =transform("JSONPATH", "$.interrupt_mask", tinkerforge_37_callback_io16_bricklet_ESS.state.toString)
  val value_mask =transform("JSONPATH", "$.value_mask", tinkerforge_37_callback_io16_bricklet_ESS.state.toString)

logInfo("ESSlog", "Port ist " + port)
logInfo("ESSlog", "Interrupt Maske ist " + interrupt_mask)
logInfo("ESSlog", "Value Maske ist " + value_mask)

if (port == "a")
logInfo("ESSlog", "if Schleife a")

  var interrupt_binary = interrupt_mask.toString().toBinaryString()
logInfo("ESSlog", "Interrupt Binary ist " + interrupt_binary)  

end
Es kommt die Fehlermeldung:

Code: Alles auswählen

'toBinaryString' is not a member of 'java.lang.String"
Das Item wird mit einem dreiteiligen Payload gefüttert der den Status eines IOs mit 16 Kanälen widerspiegelt.
Der payload ist:
port – Type: char, Range: [“a” to “b”]
interrupt_mask – Type: int, Range: [0 to 255]
value_mask – Type: int, Range: [0 to 255]
Z.B. meint a 1 255 (a 00000001, 11111111) dass an Port A ein interrupt an Pin 0 aufgetreten ist und der Pin 0 hat sich von low auf high geändert und die anderen pins 1-7 sind high.
Die Regel soll eigentlich die 16 Kanläe in den Status von contact items überführen. Aber hänge schon beim Umrechnen in den Binärwert :roll:

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

Re: IO Bitmaske aus Dezimalwert nutzen für postUpdate von Contacts

Beitrag von udo1toni »

Mein Posting im englischen Forum dazu hast Du ja schon gefunden :) https://community.openhab.org/t/i-o-mod ... o_hartmann. Wir können das gerne auch hier auf deutsch noch mal durchgehen (allerdings erst morgen...)
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

maxico
Beiträge: 4
Registriert: 3. Jan 2024 11:20
Answers: 0

Re: IO Bitmaske aus Dezimalwert nutzen für postUpdate von Contacts

Beitrag von maxico »

Das wäre super. :D
Genau, den Post hatte ich gesehen (:oops: und geherzt) und daraufhin die Contact Items entsprechend nach den Eingängen benannt angelegt:

Code: Alles auswählen

Contact ESS_a0 "WP_ticker"                  
Contact ESS_a1 "Sensor_Licht_DG_Bad"        
Contact ESS_a2 "Sensor_Licht_DG_Kammer"
Contact ESS_a3 "Sensor_Licht_DG_Gast_Spitze"
Contact ESS_a4 "Sensor_Licht_DG_Gast_Decke
...
Contact ESS_a7 "xxx"
Contact ESS_b0 "yyy"
...
Soweit ist es ja leider noch nicht, dass die zum Einsatz (postUpdate) kommen könnten. :(
Und in dem EN Post startet es bereits mit dem Binärwert, oder?
Den zu Erhalten ist glaube ich das erste Problem...

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

Re: IO Bitmaske aus Dezimalwert nutzen für postUpdate von Contacts

Beitrag von udo1toni »

ok, Zunächst mal, nur zur Sicherheit, der Status des Items tinkerforge_37_callback_io16_bricklet_ESS ist beispielsweise exakt "a 1 255"? Das bedeutet, es handelt sich NICHT um ein JSON Objekt.

Du musst stattdessen den String in seine Einzelteile zerlegen, und anschließend auswerten, z.B. so:

Code: Alles auswählen

rule "zwei byte Register in contact überführen"
when
    Item tinkerforge_37_callback_io16_bricklet_ESS changed
then
    val parts     = newState.toString.split(" ")                                             // trenne bei Leerzeichen
    val register  = parts.get(0).toLowerCase                                                 // 1. Teil Register
 
    val bits      = DecimalType.valueOf(parts.get(2)).toBigDecimal.toBigInteger              // 3. Teil die Bits des Registers 

    if(register != "a" && register != "b") {                                                // falls Register weder a noch b
        logWarn("tfio16","Da ist was schief gelaufen! ungültiges Register {}", register)     // Warnung
        return;                                                                              // Abbruch
    }
    if(bits < 0 || bits > 255) {                                                             // falls gelieferte Zahl nicht plausibel
        logWarn("tfio16","Da ist was schief gelaufen! ungültiges Bitmuster {}", bits)        // Warnung
        return;                                                                              // Abbruch
    }
    gContacts.members.filter[i|i.name.split("_").get(1) == register].forEach[c|              // Nimm alle Items, die zum Register gehören
        val n = Integer.parseInt(c.name.split("_").get(2))                                   // Bit im Namen des Items
        val soll = if(bits.testBit(n)) CLOSED else OPEN                                      // Sollwert bestimmen
        if(c.state != soll)                                                                  // Falls Wert abweicht
            c.postUpdate(soll)                                                               // Wert setzen
        logDebug("tfio16","Register {} Bit {}  -> Item {} auf {}",register,n,c.name,soll)
    ]
end
Die Itemnamen sehen so aus:

Code: Alles auswählen

String tinkerforge_37_callback_io16_bricklet_ESS "TF IO 16 Input" {channel="mqtt:topic:broker:tfio16_37:ess"}
Group  gContacts "TF 37 IO 16 Kontakte"
Contact ESS_a_0 "bit a 0" (gContacts)
Contact ESS_a_1 "bit a 1" (gContacts)
Contact ESS_a_2 "bit a 2" (gContacts)
Contact ESS_a_3 "bit a 3" (gContacts)
Contact ESS_a_4 "bit a 4" (gContacts)
Contact ESS_a_5 "bit a 5" (gContacts)
Contact ESS_a_6 "bit a 6" (gContacts)
Contact ESS_a_7 "bit a 7" (gContacts)
Contact ESS_b_0 "bit b 0" (gContacts)
Contact ESS_b_1 "bit b 1" (gContacts)
Contact ESS_b_2 "bit b 2" (gContacts)
Contact ESS_b_3 "bit b 3" (gContacts)
Contact ESS_b_4 "bit b 4" (gContacts)
Contact ESS_b_5 "bit b 5" (gContacts)
Contact ESS_b_6 "bit b 6" (gContacts)
Contact ESS_b_7 "bit b 7" (gContacts)
Der zweite Unterstrich ist wichtig, damit die Auswertung in der Rule funktioniert. Natürlich könnte man den Code in der Rule auch an die andere Schreibweise anpassen, dadurch wird der Code aber viel aufwändiger :)

Zunächst wird der gelieferte String an den Leerzeichen aufgetrennt.
Der erste Teil ist das Register, der dritte Teil ist der Zustand der acht Bits des Registers. Der Interrupt spielt für openHAB keine Rolle, weshalb ich ihn in der Rule auch nicht weiter beachte.
Der dritte Teil landet, nach BigInteger umgeformt, in der lokalen Konstanten bits. BigInteger hat die Funktion .testBit(), welche wir hier nutzen.
Die nächsten Zeilen kümmern sich um die rudimentäre Absicherung gegen falsche Daten, da ginge sicherlich wesentlich mehr (und damit wesentlich sicherer...) aber ich bin faul und deute es hier nur an... :) Man könnte z.B. darauf testen (vor der Zuordnung zu den einzelnen Konstanten), ob tatsächlich exakt drei Teile entstehen, und vor der Wandlung des dritten Teils nach BigInteger, ob der dritte Teilstring tatsächlich eine gültige Zahl liefert.

Wurden alle Tests erfolgreich "überwunden", geht es an die Zuordnung der Bits zu den Items, was nun "easy" ist ;)
Zunächst wird die Gruppe, in der alle Contacts zusammengefasst sind, auf die Elemente gefiltert, die zum empfangenen Register gehören (also "a" oder "b" im zweiten Teil des Itemnamens stehen haben). die entstehende Liste wird Item für Item komplett durchlaufen. Dabei wird jeweils aus dem letzten Namensteil die Bitposition ausgelesen (0 - 7).
Anschließend wird der lokalen Konstanten soll abhängig vom zugehörigen Bit aus dem empfangenen String entweder OPEN oder CLOSED zugewiesen.
Zum Schluss wird, falls der Status des aktuellen Contact Items vom Soll abweicht, der Status passend gesetzt.
Es werden also stets nur die Items angesteuert, welche sich auch tatsächlich ändern müssen. Das ist auch der Grund, warum der Interrupt keine Rolle spielt :)

Da ein paar Befehle und Funktionen nicht so ganz trivial erscheinen, habe ich die Rule bei mir getestet (und kann deshalb behaupten, dass der Code funktioniert) :)

Falls Du noch mehr Tinkforge Module haben solltest, könnte man die Rule auch noch anpassen, so dass sie sich um mehr als nur dieses eine Modul kümmert; man müsste dazu nur einen weiteren eindeutigen Namensteil bei den Contacts ergänzen, der sich aus dem triggernden Item ergeben muss, z.B. die 37 :) .

Die logDebug() Zeile führt nur dann zu einer Ausgabe, wenn das Loglevel für org.openhab.core.model.script.tfio16 auf DEBUG steht, die Warnmeldungen werden hingegen ausgegeben, solange das Loglevel nicht auf OFF oder ERROR steht. Default ist INFO vererbt. :)
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

maxico
Beiträge: 4
Registriert: 3. Jan 2024 11:20
Answers: 0

Re: IO Bitmaske aus Dezimalwert nutzen für postUpdate von Contacts

Beitrag von maxico »

Wow. Danke schonmal. Werde asap testen und versuchen zu verstehen.
Zur Frage ob JSON Objekt:
Das Item wird per MQTT befüllt und sieht so aus bei einer Änderung:

Code: Alles auswählen

2024-01-06 00:49:52.753 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'tinkerforge_37_callback_io16_bricklet_wjW' changed from {"port": "a", "interrupt_mask": 4, "value_mask": 27} to {"port": "a", "interrupt_mask": 4, "value_mask": 31}
Deshalb war der Gedanke in der ersten Rule oben das per JSON Path zu zerlegen, was auch funktioniert hat. Ist das jetzt ein JSON Objekt? Ist eigentlich nur ein String mit geschweiften Klammern?
Sorry, hätte den Item Inhalt auch gleich einbauen können.
Würde am liebsten gleich testen (ist nur leider schon spät, wird der nächste Abend)
Danke nochmal!

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

Re: IO Bitmaske aus Dezimalwert nutzen für postUpdate von Contacts

Beitrag von udo1toni »

Ah, ja, das ist ein JSON Objekt. Dann ändert sich halt der erste Teil:

Code: Alles auswählen

    val register  = transform("JSONPATH","$.port",newState.toString)                         // 1. Teil Register
 
    val mask      = transform("JSONPATH","$.value_mask",newState.toString)                   // Bitmaske als String
    val bits      = DecimalType.valueOf(mask).toBigDecimal.toBigInteger                      // 3. Teil die Bits des Registers 
Man könnte den Ausdruck für bits auch in einer Zeile zusammenfassen, aber so erscheint es mit übersichtlicher :)

Ach so... JSON... im vorliegenden Beispiel könnte man tatsächlich von einem String mit geschwungenen Klammern ausgehen, aber es gibt auch wesentlich komplexere JSON Objekte, openHAB speichert seine Konfiguration intern als JSON Objekte ab, da ist es dann nciht mehr mit ein paar geschwungenen Klammern getan :)
Und JSONPath kann jedes JSON Objekt zerlegen, egal wie komplex dieses ist. Typisches Beispiel: Du hast ein JSON Objekt mit einer variablen Anzahl Objekte, beispielsweise eine Liste von Büchern. Nun möchte man diese Liste eventuell nach verschiedenen Kriterien betrachten, nach Autor, anch Titel, nach Genre sortiert, oder man möchte z.B. den Autor eines Buches erfahren, ein Andermal aber den Titel eines Buches anhand des Autorennamens, auch das geht ohne Probleme. Im openHAB Umfeld wären z.B. die Wetterinformationen des DWD interessant. Die Messwerte sind nach Messstationen gebündelt, und die einzelnen Knoten (= Stationen) liegen unsortiert vor, mal taucht die Station 0815 ganz oben auf, mal als vorletztes... Das entsprechende JSONPath Statement fragt dann z.B. so:

Code: Alles auswählen

$.stations[?(@.stationID=='0815')].pressure
und man erhält den Luftdruck des Datensatzes, bei dem die stationID den Wert "0815" hat.
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

maxico
Beiträge: 4
Registriert: 3. Jan 2024 11:20
Answers: 0

Re: IO Bitmaske aus Dezimalwert nutzen für postUpdate von Contacts

Beitrag von maxico »

Alles funktioniert. 1000 Dank!!! Hoffe das hilft auch anderen die ein Tinkerforge IO16 Bricklet o.ä. in Openhab > 2.5 einbinden wollen. Da Tinkerforge das OH Binding nicht mehr aktualisiert für OH3 oder 4, bleibt nur der Weg über MQTT (oder das OH Remote Binding mit dem alten Tinkerforge Binding).
Noch zwei Infos:
- "ESS" ist in dem Beispiel die UID des IO 16 Bricklets
- "37" im Item Name ist eigentlich nur das MQTT Topic "tinkerforge/37/callback/io16_bricklet/ESS/interrupt". -> globales Präfix um mehrere TF-Stapel mit verschiedenen IPs zu unterscheiden.

Antworten