XML Befehl per http post an URL senden

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Beasty
Beiträge: 10
Registriert: 15. Mai 2022 23:20

XML Befehl per http post an URL senden

Beitrag von Beasty »

Hallo, liebe Gemeinde!

Ich habe ein Vorhaben, an dem ich leider vollkommen scheitere. Mir ist bekannt, dass es dazu hier schon einen Thread gibt, ich steige da aber nicht durch und brauche jemanden, der mich etwas an die Hand nimmt ;)

Ich habe hier ein Alpha 2 von Möhlenhoff. Das Gerät steuert die verschiedenen Heizkreise unserer Fußbodenheizung. Über ein Web-Interface lassen sich die Einstellungen ändern, es soll aber auch mit OH gehen. Es geht um die Sollwerte der Raumtemperaturen. Es gibt eine XML unter http://<IP>/data/static.xml, bei der alle Werte auslesbar sind. LESEN kann ich die IST-Temperaturen schon in OH, ich möchte aber auch die Sollwerte der Raumtemperaturen setzen können. Dazu steht in der Anleitung: Befehle werden immer als XML Datei an die Basis gesendet. Per http post wird der XML-Befehl an die URL: http://<IP>/data/changes.xml gesendet. Der Inhalt der changes.xml ist prinzipiell immer der entsprechende Ausschnitt aus der static.xml.
Hier ein Abschnitt aus der Static.xml für den Heizkreis im WC:

Code: Alles auswählen

<HEATAREA nr="8">
<HEATAREA_NAME>WC</HEATAREA_NAME>
<HEATAREA_MODE>1</HEATAREA_MODE>
<T_ACTUAL>20.5</T_ACTUAL>
<T_ACTUAL_EXT>20.5</T_ACTUAL_EXT>
<T_TARGET>21.0</T_TARGET>
<T_TARGET_BASE>21.0</T_TARGET_BASE>
<HEATAREA_STATE>0</HEATAREA_STATE>
<PROGRAM_SOURCE>0</PROGRAM_SOURCE>
<PROGRAM_WEEK>0</PROGRAM_WEEK>
<PROGRAM_WEEKEND>0</PROGRAM_WEEKEND>
<PARTY>0</PARTY>
<PARTY_REMAININGTIME>0</PARTY_REMAININGTIME>
<PRESENCE>0</PRESENCE>
<T_TARGET_MIN>5.0</T_TARGET_MIN>
<T_TARGET_MAX>30.0</T_TARGET_MAX>
<RPM_MOTOR>0</RPM_MOTOR>
<OFFSET>0.5</OFFSET>
<T_HEAT_DAY>22.0</T_HEAT_DAY>
<T_HEAT_NIGHT>22.0</T_HEAT_NIGHT>
<T_COOL_DAY>22.0</T_COOL_DAY>
<T_COOL_NIGHT>22.0</T_COOL_NIGHT>
<T_FLOOR_DAY>3.0</T_FLOOR_DAY>
<HEATINGSYSTEM>1</HEATINGSYSTEM>
<BLOCK_HC>0</BLOCK_HC>
<ISLOCKED>1</ISLOCKED>
<LOCK_CODE>455A52DCA721AF87</LOCK_CODE>
<LOCK_AVAILABLE>1</LOCK_AVAILABLE>
<LIGHT>15</LIGHT>
<SENSOR_EXT>0</SENSOR_EXT>
<T_TARGET_ADJUSTABLE>1</T_TARGET_ADJUSTABLE>
</HEATAREA>

Weiter unten in der Anleitung steht auch, wie der entsprechende Befehl aussehen muss:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?> <Devices> <Device> <ID>EZR010A49</ID> <HEATAREA nr="1"> <T_TARGET>20.6</T_TARGET> </HEATAREA> </Device> </Devices>
Ich habe schon versucht, manche Lösungen aus anderen Threads nachzubilden, leider immer ohne Erfolg. Das liegt sicherlich an meiner Unerfahrenheit ;). Oder die Lösungen sind nicht mehr aktuell, was ich unmöglich selber prüfen kann. Ich weiß schon, dass eine Rule gebraucht wird, bin mir aber nicht sicher, wie ich diese Rule richtig anlege, sodass es auch funktioniert.
Das HTTP-Binding habe ich schon installiert.

Ich hoffe, dass jemand von euch die Geduld und Muße hat, mir hier durch den Wald zu helfen ;)
LG Basti
von udo1toni » 2. Okt 2022 19:07
Oh. Ja, da fehlt tatsächlich noch das Wort name...

Code: Alles auswählen

rule "send command"
when
    Member of gHeizung received command
then
    var String strPart1 = ""
    var String strPart2 = ""
//                        \/\/
    switch(triggeringItem.name.split("_").get(2)){
        case "Solltemperatur" : {
            strPart1 = '"><T_TARGET>'
            strPart2 = '</T_TARGET>'
        }
        default : { return; }
    }
    val strHA = triggeringItem.name.split("_").get(1)
    val strURL = "http://192.168.178.22/data/changes.xml"
    val sbCommand = new StringBuilder()

    sbCommand.append('<?xml version="1.0" encoding="UTF-8"?><Devices><Device><ID>HKV147</ID><HEATAREA nr="')
    sbCommand.append(strHA)
    sbCommand.append(strPart1)
    sbCommand.append(receivedCommand.toString)
    sbCommand.append(strPart2)
    sbCommand.append('</HEATAREA></Device></Devices>')

    sendHttpPostRequest(strURL, "application/xml", sbCommand.toString)
end
Gehe zur vollständigen Antwort
Zuletzt geändert von Beasty am 28. Sep 2022 11:06, insgesamt 1-mal geändert.

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

Re: XML Befehl per http post an URL senden

Beitrag von udo1toni »

Was habt ihr immer mit Screenshots? Es ist viel effizienter und auch einfacher, Text zu kopieren und als Code einzufügen.
Und nebenbei wäre es auch wesentlich einfacher, Teile des Codes zu zitieren.

Es kommt etwas darauf an, wieviele Werte Du setzen möchtest. Geht es nur um einen einzelnen Wert, so sollte das recht einfach über einen Channel machbar sein. Das wäre dann format before publish. Das heißt, Du gibst den gesamten statischen Teil an und ersetzt den konkreten Wert innerhalb dieses Textes mit %2s.

Wenn Du allerdings mehrere Werte setzen willst, wirst Du viel fast identischen Code anlegen müssen. Da wäre es dann wesentlich sinnvoller, eine Rule zu bauen, die direkt die Werte sendet.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

Beasty
Beiträge: 10
Registriert: 15. Mai 2022 23:20

Re: XML Befehl per http post an URL senden

Beitrag von Beasty »

Hallo Udo, danke für die schnelle Antwort!
Habe die Screenshots durch den Code ersetzt. Der Grund für die Screenshots war, dass ich nicht riskieren wollte, dass was bei der Formatierung schief geht. Vielleicht unbegründet ;)

Mein Ziel ist, dass ich z.B. einen Slider oder einen Setpoint in der Sitemap habe, mit dem ich die Solltemperaturen der Heizkreise (zum Testen hier mal das WC) setzen kann. z.B. mit einer Skala von 0.2°C.
Es muss also eine Rule her, wenn ich dich oben richtig verstanden habe, damit der oben gezeigte Code (aus der Anleitung) dynamisch verändert wird und die gesetzten Werte an die Adresse http://<IP>/data/changes.xml gesendet werden. Die IP lautet 192.168.178.22. Ich habe aber keine Ahnung, wie diese Rule erstellt werden muss.

Hier der Code von meinem Thing und dem Channel. Weiter komme ich selber (noch) nicht ;(

Code: Alles auswählen

UID: http:url:HKV_UG_SET
label: HKV UG Set
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: false
  baseURL: http://192.168.178.22/data/changes.xml
  delay: 0
  stateMethod: GET
  refresh: 5
  commandMethod: POST
  contentType: application/xml
  timeout: 3000
  bufferSize: 2048
channels:
  - id: Solltemp_WC
    channelTypeUID: http:string
    label: Solltemp. WC
    description: ""
    configuration:
      mode: WRITEONLY
Danke für deine erneute Hilfe!

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

Re: XML Befehl per http post an URL senden

Beitrag von udo1toni »

Grundsätzlich brauchst Du nur ein http Thing, um mit Deiner Heizung zu sprechen.
Also z.B.

Code: Alles auswählen

UID: http:url:Heizung
label: Heizung
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: false
  baseURL: http://192.168.178.22/data/
  delay: 0
  stateMethod: GET
  refresh: 5
  commandMethod: POST
  contentType: application/xml
  timeout: 3000
  bufferSize: 2048
channels:
  - id: Solltemp_WC
    channelTypeUID: http:number
    label: Solltemp. WC
    description: null
    configuration:
      mode: READWRITE
      commandTransformation: <?xml version="1.0" encoding="UTF-8"?> <Devices> <Device>
        <ID>EZR010A49</ID> <HEATAREA nr="8"> <T_TARGET>%2$s</T_TARGET>
        </HEATAREA> </Device> </Devices>
      stateExtension: static.xml
      commandExtension: changes.xml
      stateTransformation: XPATH:/HEATAREA/T_TARGET_BASE/Text()
Der XPATH stimmt natürlich nicht, da muss man den korrekten anhand des kompletten Inhalts der static.xml heraussuchen. Dabei wird es darauf hinauslaufen, dass bestimmte Werte als Filter angegeben werden, z.B. die HEAT_AREA als Suchparameter auf 8 gesetzt wird, um das Bad anzusprechen.
Mit XPATH habe ich mich noch nicht intensiv beschäftigt, es funktioniert aber ähnlich wie JSONPATH, sollte also kein Hexenwerk sein.
Du kannst am Beispiel oben sehen, dass es einen Channel gibt, der sowohl das gelesene als auch das geschriebene Datum (die Solltemperatur) repräsentiert. Der Wert muss als Number übergeben werden, alles andere ist Unfug.
Ein Teil der Parameter des Beispiels ist nur über show advanced sichtbar.
Du kannst sogar die Unit setzen und dann ein UoM Item nutzen, allerdings hat UoM so seine Tücken, wenn, dann möchte ich empfehlen, das erst zu aktivieren, wenn der Rest schon korrekt funktioniert, einfach um Fehlerketten möglichst kurz zu halten.

In der commandTransformation wird einfach der Wert vom Text eingeschlossen, der drum herum stehen soll. %2$s ist dabei der Platzhalter für den Wert, der vom Item kommt. %2 gibt an, dass es sich um den Status handelt (%1 wäre der Zeitstempel des Items), $s, dass dieser als String formatiert werden soll. In der offiziellen Doku steht das so nicht drin, aber da es bei anderen Bindings so ist, gehe ich davon aus, dass es auch bei http so funktioniert.

Die Alternative ist, wie gesagt, das mit einer Rule zu erledigen und dafür einen Channel zum Lesen und einen Channel zum Schreiben zu definieren.
So oder so brauchst Du aber nur ein Thing, keine zwei.
Insbesondere benötigst Du nur eine Base URL, die dann im jeweiligen Channel von der URL Extension ergänzt wird.
Beachte auch, dass die Base URL nicht mit der IP endet, sondern mit /data/, dem letzten Teil der URL, der für Befehl und Status identisch ist.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

Beasty
Beiträge: 10
Registriert: 15. Mai 2022 23:20

Re: XML Befehl per http post an URL senden

Beitrag von Beasty »

udo1toni hat geschrieben: 28. Sep 2022 13:18 Grundsätzlich brauchst Du nur ein http Thing
Hallo nochmal,

das war schonmal teilweise erfolgreich! Das ist, was ich habe:

Code: Alles auswählen

UID: http:url:HKV_UG_Neu
label: HKV UG NEU
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: true
  baseURL: http://192.168.178.22/data/
  delay: 0
  stateMethod: GET
  refresh: 5
  commandMethod: POST
  contentType: application/xml
  timeout: 3000
  bufferSize: 2048
channels:
  - id: Solltemp_WC
    channelTypeUID: http:number
    label: Solltemp. WC
    description: ""
    configuration:
      commandTransformation: <?xml version="1.0" encoding="UTF-8"?> <Devices> <Device>
        <ID>HKV147</ID> <HEATAREA nr="8"> <T_TARGET>%2$s</T_TARGET> </HEATAREA>
        </Device> </Devices>
      stateExtension: static.xml
      commandExtension: changes.xml
      stateTransformation: XPATH://HEATAREA[@nr="8"]/T_TARGET/text()
Daran habe ich ein Number Item gelinkt. Das Item zeigt mir auch ganz brav den eingestellten Sollwert an, hier 21°C (über das Webiterface eingestellter Sollwert!).
Über einen Slider per Sitemap schicke ich dem Item einen neuen Wert. Das Item wechselt den Status dann auch kurz auf diesen Wert (26°C), springt dann aber zurück auf die 21. Das liegt wohl daran, dass der neue Wert nicht angekommen ist, denn auch in der static.xml wird er nicht verändert.
Ich habe im Log eine Fehlermeldung, wenn ich das Thing pausiere und neu starte:

Code: Alles auswählen

2022-09-28 17:07:39.271 [WARN ] [form.CascadedValueTransformationImpl] - Transformation ignore, failed to parse <?xml version="1.0" encoding="UTF-8"?> <Devices> <Device> <ID>HKV147</ID> <HEATAREA nr="8"> <T_TARGET>%2$s</T_TARGET> </HEATAREA> </Device> </Devices>: The transformation pattern must consist of the type and the pattern separated by a colon

Und gelegentlich diese:

Code: Alles auswählen

2022-09-28 17:11:57.024 [WARN ] [p.internal.http.HttpResponseListener] - Requesting 'http://192.168.178.22/data/static.xml' (method='GET', content='null') failed: HttpConnectionOverHTTP@1b284ef::SocketChannelEndPoint@cfa776{l=/192.168.178.180:40460,r=/192.168.178.22:80,ISHUT,fill=-,flush=-,to=219/0}{io=0/0,kio=0,kro=1}->HttpConnectionOverHTTP@1b284ef(l:/192.168.178.180:40460 <-> r:/192.168.178.22:80,closed=false)=>HttpChannelOverHTTP@2bb710(exchange=HttpExchange@cdb800{req=HttpRequest[GET /data/static.xml HTTP/1.1]@155c78a[TERMINATED/null] res=HttpResponse[null 0 null]@8a33c1[PENDING/null]})[send=HttpSenderOverHTTP@11885b7(req=QUEUED,snd=COMPLETED,failure=null)[HttpGenerator@cc226c{s=START}],recv=HttpReceiverOverHTTP@1b2d528(rsp=IDLE,failure=null)[HttpParser{s=CLOSED,0 of -1}]]

Die erste Fehlermeldung verstehe ich ansatzweise, weiß aber nicht was zu tun ist ;)
Bei den anderen beiden bin ich leider lost.

Beasty
Beiträge: 10
Registriert: 15. Mai 2022 23:20

Re: XML Befehl per http post an URL senden

Beitrag von Beasty »

Die obere Fehlermeldung konnte ich beseitigen, indem ich XPath: vor die Statetransformation gesetzt habe.
Nun kommt eine neue Fehlermeldung:

Code: Alles auswählen

2022-09-28 21:53:36.767 [WARN ] [.transform.SingleValueTransformation] - Executing transformation ChannelStateTransformation{pattern='<?xml version="1.0" encoding="UTF-8"?> <Devices> <Device> <ID>HKV147</ID> <HEATAREA nr="8"> <T_TARGET>%2$s</T_TARGET> </HEATAREA> </Device> </Devices>', serviceName='XPATH'} failed: transformation throws exceptions
Kann jemand erkennen, was ich falsch gemacht habe?

Code: Alles auswählen

UID: http:url:HKV_UG_Neu
label: HKV UG NEU
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: false
  baseURL: http://192.168.178.22/data/
  delay: 0
  stateMethod: GET
  refresh: 5
  commandMethod: POST
  contentType: application/xml
  timeout: 3000
  bufferSize: 2048
channels:
  - id: Solltemp_WC
    channelTypeUID: http:number
    label: Solltemp. WC
    description: ""
    configuration:
      commandTransformation: XPATH:<?xml version="1.0" encoding="UTF-8"?> <Devices>
        <Device> <ID>HKV147</ID> <HEATAREA nr="8"> <T_TARGET>%2$s</T_TARGET>
        </HEATAREA> </Device> </Devices>
      stateExtension: static.xml
      commandExtension: changes.xml
      stateTransformation: XPATH://HEATAREA[@nr="8"]/T_TARGET/text()

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

Re: XML Befehl per http post an URL senden

Beitrag von udo1toni »

Ich hab noch mal etwas intensiver die Doku studiert, und offensichtlich funktioniert das gar nicht...

Muss also über eine Rule laufen.

Code: Alles auswählen

rule "send command"
when
    Member of gHeizung received command
then
    var String strPart1 = ""
    var String strPart2 = ""

    switch(triggeringItem.name.split("_").get(2)){
        case "Solltemperatur" : {
            strPart1 = '"><T_TARGET>'
            strPart2 = '</T_TARGET>'
        }
        default : return;
    }
    val strHA = triggeringItem.name.split("_").get(1)
    val strURL = "http://192.168.178.22/data/changes.xml"
    val sbCommand = new StringBuilder()

    sbCommand.append('<?xml version="1.0" encoding="UTF-8"?><Devices><Device><ID>EZR010A49</ID><HEATAREA nr="')
    sbCommand.append(strHA)
    sbCommand.append(strPart1)
    sbCommand.append(receivedCommand.toString)
    sbCommand.append(strPart2)
    sbCommand.append('</HEATAREA></Device></Devices>')

    sendHttpPostRequest(strURL, "application/xml", sbCommand.toString)
end
Diese Rule kann für jedes Item der Gruppe gHeizung ein Command umsetzen.
Dabei gilt, dass der Name des Items den Heizkreis als Nummer enthalten muss, in der Form Bad_8_Solltemperatur.
Die 8 ist die Nummer des Heizkreises, Solltemperatur ist das Schlüsselwort, um den passenden Rahmen für den Wert zu setzen (über die beiden Variablen strPart1 und strPart2)
Natürlich kann die Form des Itemnamens auch anders aussehen, die Rule muss dann halt entsprechend angepasst werden.
Wenn es nur die Solltemperatur als zu setzenden Wert gibt, kann man natürlich das switch-case- Konstrukt weg lassen, ansonsten kann man hier über weitere case-Teile auch andere Rahmen definieren.

Falls Teile der Befehle keine Heatarea beinhalten, kann dieser Teil auch mit in die Variablen integriert werden, wobei strHA dann natürlich in diesem Teil mit eingebaut werden muss.

Die Status der Items lassen sich dann über das http Binding mit einem Channel und Links auf die einzelnen Items mit XPATH als Profile setzen.

EDIT: Code korrigiert (2.10.22)
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

Beasty
Beiträge: 10
Registriert: 15. Mai 2022 23:20

Re: XML Befehl per http post an URL senden

Beitrag von Beasty »

udo1toni hat geschrieben: 28. Sep 2022 22:19 Muss also über eine Rule laufen.
Hi Udo, tausend Dank für deine Mühe, die du dir hier gibst!

Ich habe die Rule an sich, glaube ich, verstanden, auch wenn ich sowas nicht schreiben kann.
Leider bin ich mir nicht ganz sicher, was ich mit dieser Rule machen muss. Sorry, ist etwas Basiswissen, ich steige aber leider nicht durch ;). Bisher habe ich immer Rules mit Blockly geschrieben.

Ich habe ein Gruppenitem angelegt: gHeizung
Dann noch ein Number Item mit dem Namen (nicht Label): WC_8_Solltemperatur. Dieses Label ist Gruppenmitglied in gHeizung.

Ich habe deine Rule als Rule DSL eingefügt, aber leider wird sie (glaube ich) nicht getriggert, wenn ich beim Item WC_8_Solltemperatur den Wert verändere. Sicherlich habe ich hier was falsch gemacht. Kannst du mir sagen, was?

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

Re: XML Befehl per http post an URL senden

Beitrag von udo1toni »

Die Rule ist in der von mir geposteten Rule nur für die Textkonfiguration geeignet, das heißt, sie muss in eine Datei im Ordner
/etc/openhab/rules/
gespeichert werden. Der Dateiname muss auf .rules enden, z.B. meine.rules wäre ein Möglichkeit.

Alternativ kannst Du den Teil zwischen then und end (ohne diese beiden Schlüsselworte) als DSL Rule über die UI einfügen.
Der Teil zwischen when und then (ohne die Schlüsselworte...) beschreibt die Trigger der Rule. In diesem Fall soll jeder Befehl an ein Item, welches zur Gruppe gHeizung gehört die Rule starten.
Wenn man über die UI Items setzt, resultiert das jedes Mal in einem Befehl.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

Beasty
Beiträge: 10
Registriert: 15. Mai 2022 23:20

Re: XML Befehl per http post an URL senden

Beitrag von Beasty »

udo1toni hat geschrieben: 28. Sep 2022 23:57 Die Rule ist in der von mir geposteten Rule nur für die Textkonfiguration geeignet
Hallo Udo,

ich habe die Rule wie beschrieben im Ordner Rules eingefügt. Nur zur Sicherheit, dass ich nichts falsch gemacht habe: In Windows 10 -> Im Ordner Rules "neue Textdatei" -> Name.rules -> dein Script eingefügt und speichern. Direkt nach dem Speichern der Rule kommt folgende Fehlermeldung:

Code: Alles auswählen

2022-09-29 09:51:26.714 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'Heizung Sollw.rules' has errors, therefore ignoring it: [13,25]: extraneous input ';' expecting '}'


Ich habe auch die andere Möglichkeit mit der DSL Rule probiert:

Code: Alles auswählen

2022-09-29 09:43:42.740 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID '28b86ab374' failed: rule "send command"
    var String strPart1 = ""
    var String strPart2 = ""
    switch(triggeringItem.split("_").get(2)){
        case "Solltemperatur" : {
            strPart1 = '"><T_TARGET>'
            strPart2 = '</T_TARGET>'
        }
        default : return;
    }
    val strHA = triggeringItem.name.split("_").get(1)
    val strURL = "http://192.168.178.22/data/changes.xml"
    val sbCommand = new StringBuilder()
    sbCommand.append('<?xml version="1.0" encoding="UTF-8"?><Devices><Device><ID>HKV147</ID><HEATAREA nr="')
    sbCommand.append(strHA)
    sbCommand.append(strPart1)
    sbCommand.append(receivedCommand.toString)
    sbCommand.append(strPart2)
    sbCommand.append('</HEATAREA></Device></Devices>')
    sendHttpPostRequest(strURL, "application/xml", sbCommand.toString)
end
Ich sehe den Fehler natürlich nicht ;) Du vielleicht?

LG

Antworten