[gelöst] HTTPMod mehrere Header und put

Begonnen von Wernieman, 21 Februar 2021, 19:02:51

Vorheriges Thema - Nächstes Thema

Wernieman

Hallo Freunde,

bin beim Umzug meines FHEM-Servers und möchte einiges, was bisher in Scripten erledigt wurde, nun pur in FHEM umsetzen. Dabei bin ich auf ein HTTPMod Unverständnis meinerseits gestosen.

a) Man kann für HTTPMod bekanntlich auch Header setzen. Stichwort "requestHeader.*". Nur .. wenn man mehr als einen hat, gibt es dafür eine Umsetzung?
b) kann HTTPMod auch put? Sehe in der Doku nur "get"

Hintergrund, ich will meine IP Adresse, ausgelesen über das FritzBox-Modul, über Cloudflare setzen lassen. Bisher erledigte ich es mit einem ShellScript:
Content_Data='{"type":"A","name":"<Servername>","content":"'<IP>'","ttl":60,"proxied":false}'
curl -X PUT "https://api.cloudflare.com/client/v4/zones/XXXX/dns_records/XXXX" -H "X-Auth-Email: <MeineEMail>" -H "X-Auth-Key: <AuthKey>" -H "Content-Type: application/json" --data $Content_Data


Hinweis: Alles mit <> und XXXX Passend ersetzen.

Hat jemand einen Fingerzweig für mich?
habe mich jetzt durch die Doku + Wiki gewühlt und "stehe auf den Schlauch".

Hinweis:
Die Doku für HTTPMod ist eigentlich "nicht schlecht", aber für die Filter wäre es gut, wenn auch der Quelltext mitgeliefert würde. Nicht jeder hat das im Beispiel angegebene Pool-Device ....

Ergänzung:
Natürlich habe ich mich in HTTPMod mit einem einfachen Beispiel eingearbeitet. Wenn jemand Seine IP-Adresse über eine Webside rausfinden möchte ... kann gerne meinen Code veröffentlichen ;o)
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

StefanStrobel

Hallo,

zu a):
.* in requestHeader.* ist als Regex zu lesen.
Man definiert die Header als requestHeader01, requestHeader02 o.ä.
Oder bei einem set oder get als get01Header1, get01Header2 o.ä.

zu b)
Zitat
set[0-9]*Method
HTTP Method (GET, POST or PUT) which shall be used for the set.

Für Dein Vorhaben würdest Du einen set-Befehl ungefähr so definieren:

attr MyDevice set01Name RegisterIP
attr MyDevice set01URL https://api.cloudflare.com/client/v4/zones/XXXX/dns_records/XXXX
attr MyDevice set01Header1 X-Auth-Email: <MeineEMail>
attr MyDevice set01Header2 X-Auth-Key: <AuthKey>
attr MyDevice set01Header3 Content-Type: application/json
attr MyDevice set01Method PUT
attr MyDevice set01Data '{"type":"A","name":"<Servername>","content":"'$val'","ttl":60,"proxied":false}'
...


Die IP-Adresse kannst Du dabei als Parameter übergeben und dann per set MyDevice RegisterIP 1.2.3.4 setzen. In set01Data wird sie per $val referenziert.

Zitat
Hinweis:
Die Doku für HTTPMod ist eigentlich "nicht schlecht", aber für die Filter wäre es gut, wenn auch der Quelltext mitgeliefert würde. Nicht jeder hat das im Beispiel angegebene Pool-Device ....
Welchen Quelltext meinst Du?

Gruss
   Stefan

Wernieman

#2
Es sind viele "Filterangaben" auf der Seite. Man würde sie besser verstehen, wenn vorher ein Example-Output der Webside stehen würde. Bei JSON-parse steht eines.

Habe ich mich jetzt besser ausgedrückt?

Und danke für Deine Komplette Umsetzung.

Ich dachte
requestHeader01 würde für set/get01 stehen ...

Edit:
Ich habe jetzt nur ein Problem, was ich mir nicht erklären kann.
Bei einem set Set_IP_1 RegisterIP 1.2.3.4 bekomme ich
Zitatset value 1.2.3.4 is not numeric

Mal mein Device in RAW (anonymisiert)
defmod Set_IP_1 HTTPMOD CloudCflare 0
attr Set_IP_1 userattr set01Method:GET,POST,PUT
attr Set_IP_1 extractAllJSON 1
attr Set_IP_1 room System
attr Set_IP_1 set01Data '{"type":"A","name":"XXXX","content":"'$val'","ttl":60,"proxied":false}'
attr Set_IP_1 set01Header1 X-Auth-Email: XXXX
attr Set_IP_1 set01Header2 X-Auth-Key: XXXX
attr Set_IP_1 set01Header3 Content-Type: application/json
attr Set_IP_1 set01Method PUT
attr Set_IP_1 set01Name RegisterIP
attr Set_IP_1 set01URL https://api.cloudflare.com/client/v4/zones/XXXX/dns_records/XXXX
attr Set_IP_1 verbose 5


Edit2:
Das Attribut set01TextArg hat gefehlt
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

Wernieman

#3
Ich verstehe es nicht, es funzt immer noch nicht. Ich bekomme ein:
{"result":null,"success":false,"errors":[{"code":9207,"message":"Content-type must be application/json."}],"messages":[]}

Aktuell sieht es so aus:

defmod Set_IP_1 HTTPMOD CloudCflare 0
attr Set_IP_1 extractAllJSON 1
attr Set_IP_1 room System
attr Set_IP_1 set01Data '{"type":"A","name":"XXXX","content":"'$val'","ttl":60,"proxied":false}'
attr Set_IP_1 set01Header1 X-Auth-Email: XXXX
attr Set_IP_1 set01Header2 X-Auth-Key: XXXX
attr Set_IP_1 set01Header3 Content-Type: application/json
attr Set_IP_1 set01Method PUT
attr Set_IP_1 set01Name RegisterIP
attr Set_IP_1 set01TextArg 1
attr Set_IP_1 set01URL https://api.cloudflare.com/client/v4/zones/XXXX/dns_records/XXXX
attr Set_IP_1 verbose 5


Im Logfile (verbose 5) steht:


2021.02.21 21:47:07 5: Set_IP_1: set called with RegisterIP 1.2.3.4
2021.02.21 21:47:07 5: Set_IP_1: set found option RegisterIP in attribute set01Name
2021.02.21 21:47:07 4: Set_IP_1: set will now set RegisterIP -> 1.2.3.4
2021.02.21 21:47:07 5: Set_IP_1: AddToQueue adds type set01 to URL https://api.cloudflare.com/client/v4/zones/XXXX/dns_records/XXXX, data '{"type":"A","name":"XXXX","content":"'$val'","ttl":60,"proxied":false}', header X-Auth-Email: XXXX
X-Auth-Key: XXXX
Content-Type: application/json, retry 0, initial queue len: 0
2021.02.21 21:47:07 5: Set_IP_1: HandleSendQueue called from AddToSendQueue, qlen = 1
2021.02.21 21:47:07 5: Set_IP_1: HandleSendQueue - call with HTTP METHOD: PUT
2021.02.21 21:47:07 5: Set_IP_1: HandleSendQueue is using Cookie __cfduid with path / and Value d2bbf2f7126df1491f571d6c93df3ae5c1613939125 (key __cfduid;/, destination path is /client/v4/zones/XXXX/dns_records/XXXX)
2021.02.21 21:47:07 5: Set_IP_1: HandleSendQueue is using Cookie __cflb with path / and Value 0H28vgHxwvgAQtjUGU56Rb8iNWZVUvXhq7SVLKjCeb7 (key __cflb;/, destination path is /client/v4/zones/XXXX/dns_records/XXXX)
2021.02.21 21:47:07 5: Set_IP_1: HandleSendQueue is using Cookie __cfruid with path / and Value c86b3bf5046fd04152ae8a9727bfdaafdddf1059-1613939126 (key __cfruid;/, destination path is /client/v4/zones/XXXX/dns_records/XXXX)
2021.02.21 21:47:07 5: Set_IP_1: DoCookies is adding Cookie header: __cfduid=d2bbf2f7126df1491f571d6c93df3ae5c1613939125; __cflb=0H28vgHxwvgAQtjUGU56Rb8iNWZVUvXhq7SVLKjCeb7; __cfruid=c86b3bf5046fd04152ae8a9727bfdaafdddf1059-1613939126
2021.02.21 21:47:07 4: Set_IP_1: HandleSendQueue sends set01 with timeout 2 to https://api.cloudflare.com/client/v4/zones/XXXX/dns_records/XXXX,
data: '{"type":"A","name":"XXXX","content":"'1.2.3.4'","ttl":60,"proxied":false}',
header: X-Auth-Email: XXXX
X-Auth-Key: XXXX
Content-Type: application/json
Cookie: __cfduid=d2bbf2f7126df1491f571d6c93df3ae5c1613939125; __cflb=0H28vgHxwvgAQtjUGU56Rb8iNWZVUvXhq7SVLKjCeb7; __cfruid=c86b3bf5046fd04152ae8a9727bfdaafdddf1059-1613939126
2021.02.21 21:47:08 5: Set_IP_1: ReadCallback called from __ANON__
2021.02.21 21:47:08 4: Set_IP_1: Read callback: request type was set01 retry 0,
header: HTTP/1.1 400 Bad Request
Date: Sun, 21 Feb 2021 20:47:08 GMT
Content-Type: application/json
Connection: close
CF-Ray: 625352169bd8084f-CDG
Content-Encoding: gzip
Vary: Accept-Encoding
CF-Cache-Status: DYNAMIC
cf-request-id: 0867f1a21d0000084f838d2000000001
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
x-envoy-upstream-service-time: 1
Server: cloudflare, body length 121
2021.02.21 21:47:08 5: Set_IP_1: Read callback: body
{"result":null,"success":false,"errors":[{"code":9207,"message":"Content-type must be application/json."}],"messages":[]}
2021.02.21 21:47:08 4: Set_IP_1: BodyDecode found no charset header (bodyDecode was set to auto)
2021.02.21 21:47:08 4: Set_IP_1: extracted JSON values to internal
2021.02.21 21:47:08 5: Set_IP_1: GetCookies is looking for Cookies
2021.02.21 21:47:08 5: Set_IP_1: ExtractSid called, context set, num 01
2021.02.21 21:47:08 4: Set_IP_1: checking for redirects, code=400, ignore=0
2021.02.21 21:47:08 4: Set_IP_1: no redirects to handle
2021.02.21 21:47:08 5: Set_IP_1: Read callback sets LAST_REQUEST to set01
2021.02.21 21:47:08 5: Set_IP_1: CheckAuth decided no authentication required
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

Wernieman

#4
Und bevor ich gefragt werden:
- der obengenannte curl Aufruf Funktioniert (getestet)
- Wenn ich Key oder Mail verfälsche, geht ja relativ einfach, bekomme ich als Fehler {"success":false,"errors":[{"code":10000,"message":"Authentication error"}]},
-> Angabe dürfte so also Stimmen
-> Url Sollte Stimmen
- Wenn ich dem im Log angegebene Data per jq prüfe, ist es O.K. (Sorry fürs Anonymisieren)

echo '{"type":"A","name":"XXXX","content":"'1.2.3.4'","ttl":60,"proxied":false}' | jq .
{
  "type": "A",
  "name": "XXXX",
  "content": "1.2.3.4",
  "ttl": 60,
  "proxied": false
}


Hat noch irgendjemand eine Idee?
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

Wernieman

Mist ... es waren die ' im Data Teil.
Sieht jetzt so aus:
defmod Set_IP_1 HTTPMOD CloudCflare 0
attr Set_IP_1 userattr set01Method:PUT
attr Set_IP_1 extractAllJSON 1
attr Set_IP_1 room System
attr Set_IP_1 set01Data {"type":"A","name":"XXXX","content":"$val","ttl":60,"proxied":false}
attr Set_IP_1 set01Header1 Content-Type: application/json
attr Set_IP_1 set01Header2 X-Auth-Email: XXXX
attr Set_IP_1 set01Header3 X-Auth-Key: XXXX
attr Set_IP_1 set01Method PUT
attr Set_IP_1 set01Name RegisterIP
attr Set_IP_1 set01TextArg 1
attr Set_IP_1 set01URL https://api.cloudflare.com/client/v4/zones/XXXX/dns_records/XXXX


Kann man eigentlich den Output von diesem SET Auswerten lassen?
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

StefanStrobel

Zitat von: Wernieman am 21 Februar 2021, 22:47:14
Kann man eigentlich den Output von diesem SET Auswerten lassen?

meinst Du sowas?
Zitat
set[0-9]*ParseResponse
defines that the HTTP response to the set will be parsed as if it was the response to a get command.

Gruss
   Stefan

Wernieman

Danke .. wollte es auch gerade reinschreiben. Hatte es heute Mittag gefunden.

Die Anleitung hat so viel Info, das man sehr viel Zeit braucht ... aber langsam komme ich dahinter.

- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

Ralli

#8
Hallo Stefan,

ich habe vermutlich ein ähnliches Verständnisproblem, daher nutze ich diesen Thread.

Ich möchte inspiriert durch den Thread https://forum.fhem.de/index.php/topic,84031.msg1266692.html#msg1266692 das gleiche für AdGuard Home nach-implementieren. Das ist mir auch grundsätzlich gelungen, allerdings kann man das bestimmt hübscher machen. Wenn ich die API von AdGuard ( https://github.com/AdguardTeam/AdGuardHome/tree/master/openapi ) richtig verstehe, muss ich bei jedem Request eine Base64-kodierte Benutzername:Passwort-Kombination mitgeben. So sieht jetzt mein Device aus:


defmod adguard HTTPMOD none 60
attr adguard alias AdGuard
attr adguard devStateIcon {adguardDevStateIcon($name)}
attr adguard enforceGoodReadingNames 1
attr adguard event-on-change-reading .*
attr adguard extractAllJSONFilter num_.*|avg_processing_time|running|protection_enabled|version|check_new_version
attr adguard get01ExtractAllJSON 1
attr adguard get01Header Authorization: Basic %%base64_auth%%
attr adguard get01Name summary
attr adguard get01Poll 1
attr adguard get01URL %%base_url%%/stats
attr adguard get02ExtractAllJSON 1
attr adguard get02Header Authorization: Basic %%base64_auth%%
attr adguard get02Name status
attr adguard get02Poll 1
attr adguard get02URL %%base_url%%/status
attr adguard get03Header Authorization: Basic %%base64_auth%%
attr adguard get03JSON enabled
attr adguard get03Name safebrowsing_enabled
attr adguard get03Poll 1
attr adguard get03URL %%base_url%%/safebrowsing/status
attr adguard get04Header Authorization: Basic %%base64_auth%%
attr adguard get04JSON enabled
attr adguard get04Name safesearch_enabled
attr adguard get04Poll 1
attr adguard get04URL %%base_url%%/safesearch/status
attr adguard get05Header Authorization: Basic %%base64_auth%%
attr adguard get05JSON enabled
attr adguard get05Name parental_enabled
attr adguard get05Poll 1
attr adguard get05URL %%base_url%%/parental/status
attr adguard get06Header Authorization: Basic %%base64_auth%%
attr adguard get06JSON enabled
attr adguard get06Name filtering_enabled
attr adguard get06Poll 1
attr adguard get06URL %%base_url%%/filtering/status
attr adguard icon security
attr adguard replacement01Mode text
attr adguard replacement01Regex %%base_url%%
attr adguard replacement01Value http://%%ip%%/control
attr adguard replacement02Mode text
attr adguard replacement02Regex %%ip%%
attr adguard replacement02Value 10.0.0.7
attr adguard replacement03Mode text
attr adguard replacement03Regex %%base64_auth%%
attr adguard replacement03Value MEINE_BASE64_KODIERUNG_BASIC_AUTH
attr adguard set01Header Authorization: Basic %%base64_auth%%
attr adguard set01JSON new_version
attr adguard set01Name check_new_version
attr adguard set01NoArg 1
attr adguard set01ParseResponse 1
attr adguard set01URL %%base_url%%/version.json
attr adguard set02Header Authorization: Basic %%base64_auth%%
attr adguard set02Method POST
attr adguard set02Name systemupdate
attr adguard set02NoArg 1
attr adguard set02URL %%base_url%%/update
attr adguard set03Header Authorization: Basic %%base64_auth%%
attr adguard set03Hint enable,disable
attr adguard set03Method POST
attr adguard set03Name safebrowsing
attr adguard set03TextArg 1
attr adguard set03URL %%base_url%%/safebrowsing/$val
attr adguard set04Header Authorization: Basic %%base64_auth%%
attr adguard set04Hint enable,disable
attr adguard set04Method POST
attr adguard set04Name safesearch
attr adguard set04TextArg 1
attr adguard set04URL %%base_url%%/safesearch/$val
attr adguard set05Header Authorization: Basic %%base64_auth%%
attr adguard set05Hint enable,disable
attr adguard set05Method POST
attr adguard set05Name parental
attr adguard set05TextArg 1
attr adguard set05URL %%base_url%%/parental/$val
attr adguard set06Data {"enabled":$val , "interval":1}
attr adguard set06Header Authorization: Basic %%base64_auth%%\
Content-Type: application/json
attr adguard set06Hint false,true
attr adguard set06Method POST
attr adguard set06Name filtering
attr adguard set06TextArg 1
attr adguard set06URL %%base_url%%/filtering/config
attr adguard set07Data {"protection_enabled":true}
attr adguard set07Header Authorization: Basic %%base64_auth%%\
Content-Type: application/json
attr adguard set07Method POST
attr adguard set07Name enable
attr adguard set07NoArg 1
attr adguard set07URL %%base_url%%/dns_config
attr adguard set08Data {"protection_enabled":false}
attr adguard set08Header Authorization: Basic %%base64_auth%%\
Content-Type: application/json
attr adguard set08Method POST
attr adguard set08Name disable
attr adguard set08NoArg 1
attr adguard set08URL %%base_url%%/dns_config
attr adguard stateFormat {ReadingsVal($name,"protection_enabled",0)==1?"enabled":"disabled"}
attr adguard userReadings ads_percentage_today:num_.* {my $p = ReadingsNum($name,"num_dns_queries",0)/100;; (ReadingsNum($name,"num_blocked_filtering",0)+ReadingsNum($name,"num_replaced_parental",0)+ReadingsNum($name,"num_replaced_safebrowsing",0))/$p},\
update_required:.*version.* {(ReadingsVal($name,"version","") eq ReadingsVal($name,"check_new_version",""))?"0":"1"}


Nun dachte ich mir, dass es bestimmt eine Möglichkeit gibt, für alle Abfragen auch einen generell gültigen Header-Anteil zu definieren, also habe ich das mit


attr adguard requestHeader1 Authorization: Basic %%base64_auth%%


versucht zu lösen, bin allerdings gescheitert. Was mache ich falsch? Wie kann ich die Definition "hübsch" verschlanken?

Hinweis: Bei dem set0[678]Header müsste ich über den generellen Header noch einen zusätzlichen Teil übergeben.
Gruß,
Ralli

Proxmox 8.2 Cluster mit HP ED800G2i7, Intel NUC11TNHi7+NUC7i5BNH, virtualisiertes fhem 6.3 dev, virtualisierte RaspberryMatic (3.75.7.20240420) mit HB-RF-ETH 1.3.0 / RPI-RF-MOD, HM-LAN-GW (1.1.5) und HMW-GW, FRITZBOX 7490 (07.57), FBDECT, Siri und Alexa

StefanStrobel

Hallo Ralli,

wenn die Header für alle gets gleich sind, kannst Du das Attribut getHeader statt get01Header nennen.
kombinieren lässt sich beides aber nicht, Sobald ein get01Header angegeben ist, wird bei get01 kein getHeader mehr verwendet.
Im Code sieht das so aus:


# hole alle Header bzw. generischen Header ohne Nummer
$header = join ("\r\n", map {$attr{$name}{$_}} sort grep {/${context}${num}Header/} keys %{$attr{$name}});
if (length $header == 0) {
    $header = join ("\r\n", map {$attr{$name}{$_}} sort grep {/${context}Header/} keys %{$attr{$name}});
}

$context enthält dabei "get" und $num ist die Nummer des get.

Wenn ein get09 also abweichende Header benötig, dann musst Du bei ihm alle Header explizit als get09Header01, get09Header02 etc. auflisten.

Gruß
    Stefan

Ralli

Vielen Dank, Stefan, konnte meine Def entsprechend verschlanken. Klappt.
Gruß,
Ralli

Proxmox 8.2 Cluster mit HP ED800G2i7, Intel NUC11TNHi7+NUC7i5BNH, virtualisiertes fhem 6.3 dev, virtualisierte RaspberryMatic (3.75.7.20240420) mit HB-RF-ETH 1.3.0 / RPI-RF-MOD, HM-LAN-GW (1.1.5) und HMW-GW, FRITZBOX 7490 (07.57), FBDECT, Siri und Alexa