Hi,
vielleicht kennt jemand ja schon Matrix (https://matrix.org) als Chat-System. Dies arbeitet dezentral mittels "Federation". D.h. jeder kann einen Server betreiben oder sich auf einem beliebigen Server anmelden und dann nicht nur mit Usern dieses Servers chatten, sondern auch mit Usern, die ihre Accounts auf anderen Servern haben.
Als kleiner Tipp: CoolTux hat hier (https://forum.fhem.de/index.php/topic,118870.0.html) schon mal einen Server aufgesetzt und hier tummeln sich bereits einige "FHEMler".
Ich betreibe nun seit einiger Zeit meinen eigenen Server und wollte nun mal eine Verbindung mit FHEM herstellen. Bisher habe ich dazu immer Telegram (mit dem dazugehörigen FHEM-Modul) genutzt, aber eigentlich ist Telegram ja aus Sicht von Privatsphäre und Datenschutz (meiner Meinung nach) eher bedenklich. Dazu wollte ich mal sehen, was mit Matrix so geht.
Ziel war es zunächst einmal nur, eine Nachricht aus FHEM heraus an einen Matrix-Raum zu schicken. Das ganze funktioniert mit der API von Matrix, die durch ein Stückchen Perl-Code angesprochen wird.
Voraussetzungen:
Zunächst braucht ihr natürlich einen Matrix-Account (entweder bei Matrix.org selbst, oder auf einem beliebigen anderen Server). Als Domain habe ich hier beispielhaft "matrix.meinedomain.de" verwendet.
Zusätzlich einen zweiten Account exklusiv für FHEM (optional, aber sicher empfehlenswert).
Die Authentifizierung geschieht hier mittels Token. Dieses Token (vom Matrix-FHEM-Account) bekommt ihr direkt im Client (Browser, Desktop, etc.) unter "Alle Einstellungen" > "Hilfe und Über" > ganz runter scrollen und hier auf "Zugangstoken" klicken. Diesen langen String schon mal kopieren. Siehe erstes Bild im Anhang.
Anschließend erstellt ihr euch einen Raum mit eurem persönlichen User und ladet den Matrix-FHEM-User in diesen Raum ein. Von diesem Raum braucht ihr noch die Raum-ID (Raumeinstellungen > "Erweitert" > "Interne Raum-ID". In diesem Beispiel "!abcxyz:matrix.meinedomain.de". Siehe zweites Bild im Anhang.
Bitte denkt daran, diesen Raum möglichst privat zu halten, also z.B. nicht öffentlich verfügbar zu machen (besser ist das).
Dann kommt ein bisschen Code (ich habe das in meiner 99_myUtils.pm als Funktion eingefügt). Bitte "token-hier-einfügen" und Domain bzw. Raum-ID anpassen (das "!" ist hier URL-encoded):
######################################################
# Sendet eine Matrix Message
# Aufruf: "{SendMatrixMessage($txt)}"
# Parameter: $txt: Die zu sendende Meldung
######################################################
sub SendMatrixMessage($)
{
my ($txt) = @_;
my $token = "token-hier-einfügen";
my $url = "https://matrix.meinedomain.de/_matrix/client/r0/rooms/%21abcxyz:matrix.meinedomain.de/send/m.room.message?access_token=" . $token;
my $msg = '{"msgtype":"m.text", "body":"' . $txt . '"}';
my $header = "Content-Type: application/json; charset=UTF-8";
my $param = {
url => $url,
method => "POST",
header => $header,
data => $msg,
noshutdown => 0,
callback => sub($$$)
{
my ($param, $err, $data) = @_;
Log(1, 'SendMatrixMessage ' . ($err ne '' ? 'Error: Could not send message: ' . $err : (index($data, "event_id") == -1 ? 'Error: Could not send message: ' . $data : 'successfully')));
},
};
HttpUtils_NonblockingGet($param);
return 0;
}
# End SendMatrixMessage
Aufgerufen wird das ganze nun z.B. über die FHEM-Eingabemaske (kann dann aber auch in Notifies, o.ä. genutzt werden):
{SendMatrixMessage("test")}
Nun sollte im entsprechenden Raum eine Meldung ankommen.
Einschränkung ist hier, dass die Meldungen unverschlüsselt versendet werden (auch wenn der Raum eigentlich mit Verschlüsselung arbeitet). Mit der Verschlüsselung sind hier wohl noch ein paar mehr Schritte notwendig, wo ich noch nicht ganz durchgestiegen bin.
Ja, ich weiß: Die Sache ist äußerst "hemdsärmelig". Trotzdem könnte das Code-Schnipsel vielleicht auch für andere interessant sein.
Fragen oder Anmerkungen? Immer her damit! ;)
Edit: Umgebaut auf "HttpUtils_NonblockingGet", ansonsten war der HTTP-Request blocking. Danke an CoolTux für den Hinweis.
ZitatDie Authentifizierung geschieht hier mittels Token. Dieses Token (vom Matrix-FHEM-Account) bekommt ihr direkt im Client (Browser, Desktop, etc.) unter "Alle Einstellungen" > "Hilfe und Über" > ganz runter scrollen und hier auf "Zugangstoken" klicken.
Ich finde das leider unter iOS nicht. ,,Hilfe und über" sehe ich nicht.
Nutze Element als client
Danke für info
P.s. Tolle idee
Zitat von: BOFH am 03 Mai 2021, 13:58:08
Ich finde das leider unter iOS nicht. ,,Hilfe und über" sehe ich nicht.
Stimmt, da scheint es das nicht zu geben. Hier einfach einmal per Element/Web einloggen (entweder über den offiziellen Client (https://app.element.io/#/welcome), oder eine beliebige anderen Element/Web-Instanz).
Ansonsten tut es auch so etwas:
curl -XPOST -d '{"type":"m.login.password", "user":"USER", "password":"PASSWORT"}' "https://matrix.meinedomain.de/_matrix/client/r0/login"
Das spuckt dir dann auch das Token aus.
Schau mal bitte ob der Code geht.
Dann sparst die "HandleMatrixResponse" Funktion
my $param = {
url => $url,
method => "POST",
header => $header,
data => $msg,
noshutdown => 0,
callback => \&sub() { my (undef, $err, $data) = @_; Log(1, 'SendMatrixMessage ' . ($err ne '' ? 'Error: Could not send message: ' . $err : (index($data, "event_id") == -1 ? 'Error: Could not send message: ' . $data : 'successfully') ); },
};
Ja, das ginge sicher auch, aber wie sieht das denn aus? :P
Da hab ich dann lieber eine Callback-Funktion.
Sorry, bin kein Fan von solchen Inline-Definitionen.
Edit: Wenn man es ein wenig formatiert, dann isses kein One-Liner mehr und etwas übersichtlicher. Änderungsvorschlag akzeptiert. ;)
Gibt es auch einen Weg, um matrix-Nachrichten in FHEM zu empfangen/verarbeiten?
Zitat von: Carsten K. am 08 Oktober 2023, 17:57:17Gibt es auch einen Weg, um matrix-Nachrichten in FHEM zu empfangen/verarbeiten?
Das wäre technisch sicherlich auch möglich, aber dazu müsste man wohl ein komplettes Modul basteln (wie es dies z.B. für Telegram gibt), dann könnte man sicherlich die Kommunikation in "beide Richtungen" abbilden.
Das Code-Snippet von oben ist aber wirklich nur dazu da, um schnell eine Nachricht FHEM -> Matrix zu senden.
Es gibt da ein inoffiziell Modul von jemandem aus dem Forum.
Ich hatte daran auch noch etwas gearbeitet und die Änderungen dem jenigen zukommen lassen. Das Modul mit meinen Änderungen verrichtet hier seine Arbeit. Und ich glaube es kann sogar Nachrichten empfangen. Nutze ich nur nicht.
Vielen Dank für Eure Antworten...
Ich hatte gehofft, hier kurzfristig eine Alternative zu Signal zu finden, da die Signal-Installation (zumindest bei mir) nicht immer reibungslos läuft.
Ich kann mir die erforderlichen Schritte für eine Matrix-Integration grob vorstellen und halte den benötigten Zeitaufwand (Bauchgefühl) für recht hoch.
Schönen Tag noch :)
Hi,
Ich hatte da auch mal reingeschnuppert und wollte als Snippet folgendes BASH-Script fallen lassen. Es sendet eine Textnachricht und eine Bildnachricht und ich weiß noch nicht ob und ggf. wann ich daran weiterbastel. NTFY erfüllt meine Anforderungen bisher sehr gut, aber Matrix hatte mich einfach mal interessiert. Ich nutze es täglich als Brücke zu dem in DE leider immer noch recht verbreitetem WhatsApp. Um nicht die WA-App in meinem Hauptprofil zu haben kommen mir nur Opensource-Apps in Sinn. WA-App läuft deswegen nur auf einem Burnerphone und wird alles 14 Tage zum Refresh einmal aktiviert. Egal, hier der Snippet der sich den Token selbst besorgt:
#!/bin/bash
SERVER="https://nope.chat"
USER="DeinUsername"
PASS="Password123"
ROOM_ID="!1234567890123456:nope.chat" # find it at Room Settings --> Advanced --> Internal room ID
ATTACHMENT="./output.gif"
# Get Matrix versions
#VERSIONS=$(curl -s "${SERVER}/_matrix/client/versions" | jq '.')
# Try v3 login first
RESPONSE=$(curl -s -X POST "${SERVER}/_matrix/client/v3/login" \
-H "Content-Type: application/json" \
-d "{
\"type\": \"m.login.password\",
\"identifier\": {
\"type\": \"m.id.user\",
\"user\": \"${USER}\"
},
\"password\": \"${PASS}\"
}")
# Extract and save access token
TOKEN=$(echo "$RESPONSE" | jq -r '.access_token')
if [[ "$TOKEN" != "null" ]]; then
echo "Access Token: $TOKEN"
else
echo "Login failed!"
exit 1
fi
if [ "$ROOM_ID" == "" ]; then
echo "You are in the following rooms:"
echo "---------------------------------------------------------------"
for ID in $(curl -s -X GET "${SERVER}/_matrix/client/v3/joined_rooms" \
-H "Authorization: Bearer ${TOKEN}" | jq -r '.joined_rooms[]'); do
NAME=$(curl -s -X GET "${SERVER}/_matrix/client/v3/rooms/${ID}/state/m.room.name" \
-H "Authorization: Bearer ${TOKEN}" | jq -r '.name // "<no name>"')
echo "$NAME => $ID"
echo "check if your ROOM_ID is there ------------------------------"
exit 0
done
fi
MESSAGE="Hello from Bash script!"
TXN_ID=$(date +%s) # Unique transaction ID
curl -s -X PUT "${SERVER}/_matrix/client/v3/rooms/${ROOM_ID}/send/m.room.message/${TXN_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"msgtype\": \"m.text\",
\"body\": \"${MESSAGE}\"
}"
# Get the MIME type of the attachment
MIME_TYPE=$(file --mime-type -b "$ATTACHMENT")
# Upload the image
UPLOAD_RESPONSE=$(curl -s -X POST "${SERVER}/_matrix/media/v3/upload" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: $MIME_TYPE" \
--data-binary @"$ATTACHMENT")
# Extract the Matrix content URL
CONTENT_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.content_uri')
if [[ "$CONTENT_URL" == "null" || -z "$CONTENT_URL" ]]; then
echo "Image upload failed: $UPLOAD_RESPONSE"
exit 1
fi
TXN_ID=$(date +%s) # Unique transaction ID
RESPONSE=$(curl -s -X PUT "${SERVER}/_matrix/client/v3/rooms/${ROOM_ID}/send/m.room.message/${TXN_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"msgtype\": \"m.image\",
\"body\": \"$(basename "$ATTACHMENT")\",
\"url\": \"$CONTENT_URL\",
\"info\": {
\"mimetype\": \"$MIME_TYPE\",
\"size\": $(stat -c%s "$ATTACHMENT")
}
}")
# Check for success
if [[ $(echo "$RESPONSE" | jq -r '.event_id') != "null" ]]; then
echo "Image sent successfully!"
else
echo "Failed to send image: $RESPONSE"
exit 1
fi
Als HTTPMOD kann man so bereits eine Nachricht versenden:
defmod MatrixBot HTTPMOD none 0
attr MatrixBot userattr MatrixPassword MatrixRoomID MatrixServer MatrixUser
attr MatrixBot MatrixRoomID !abcddefghijklmn:nope.chat
attr MatrixBot MatrixServer https://nope.chat
attr MatrixBot MatrixUser MeinNutzername
attr MatrixBot comment "\
The room-id can be found in Element-Web at:\
Room Settings --> Advanced --> Internal room ID\
\
To set the password:\
set MatrixBot storeKeyValue MatrixPassword yourPassword123 \
"
attr MatrixBot get01Data {\
"type": "m.login.password",\
"identifier": {\
"type": "m.id.user",\
"user": "[$name:MatrixUser]"\
},\
"password": "%%MatrixPassword%%"\
}
attr MatrixBot get01HeaderContent-Type application/json
attr MatrixBot get01Name token
attr MatrixBot get01Regex "access_token"\s*:\s*"([^"]+)"
attr MatrixBot get01TextArg 0
attr MatrixBot get01URL [$name:MatrixServer]/_matrix/client/v3/login
attr MatrixBot replacement01Mode expression
attr MatrixBot replacement01Regex \[([^:]+):([^\]]+)\]
attr MatrixBot replacement01Value my $device = $name if ($1 eq "\$name") // $1;;\
ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");;
attr MatrixBot replacement02Mode expression
attr MatrixBot replacement02Regex %%uuid%%
attr MatrixBot replacement02Value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15))))
attr MatrixBot replacement03Mode key
attr MatrixBot replacement03Regex %%MatrixPassword%%
attr MatrixBot replacement03Value MatrixPassword
attr MatrixBot set01Data {\
"msgtype": "m.text",\
"body": "$val"\
}
attr MatrixBot set01HeaderAuthorization Authorization: Bearer [$name:token]
attr MatrixBot set01HeaderContent-Type application/json
attr MatrixBot set01Method POST
attr MatrixBot set01Name sendText
attr MatrixBot set01TextArg 1
attr MatrixBot set01URL [$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%
attr MatrixBot verbose 1
Das Passwort wird verschleiert gespeichert, damit man es setzt muss man:
set MatrixBot storeKeyValue MatrixPassword yourPassword123
Um dann einen Access-Token anzufordern (muss man nur ganz selten machen sobald man einen Token hat):
get MatrixBot token
Es wird dann das Reading token gesetzt
Zum Senden einer Textnachricht:
set MatrixBot sendText Mein Test Text kommt hier hin
Du kannst gerne Änderungen im Modul als PR einreichen.
https://git.cooltux.net/FHEM/mod-matrix
@CoolTux: Danke für das Modul.
Ich dachte ich mach' es mir "einfach" und nur das Nötigste "mal kurz" in HTTPMOD und es war wie so oft viel aufwendiger als ich Anfangs dachte - hätte ich mal einfach das Modul genommen :-)
Wie auch immer, hier ein HTTPMOD Device mit dem man Nachrichten senden kann und auch longpoll auf Nachrichten in einem Raum ausführen kann (Matrix kann keine Websockets). Gibt noch viel zu tun, aber ich wollte den aktuellen Stand teilen:
defmod MatrixBot HTTPMOD none 0
attr MatrixBot userattr MatrixPassword MatrixRoomID MatrixServer MatrixUser
attr MatrixBot MatrixRoomID !123456789EHIbAYFYh:nope.chat
attr MatrixBot MatrixServer nope.chat
attr MatrixBot MatrixUser meinNutzerName
attr MatrixBot bodyDecode utf-8
attr MatrixBot comment "\
Create a room for FHEM.\
\
The room must not use encryption, a room that has encryption\
enabled, cannot be converted to a non-encrypted room anymore \
\
The room-id can be found in Element-Web at:\
Room Settings --> Advanced --> Internal room ID\
\
To store the password in FHEM in obfuscated way:\
set MatrixBot storeKeyValue MatrixPassword yourPassword123\
\
###\
To send a text:\
# 1. obtain token (if not already there):\
get MatrixBot token\
\
# 2. send Text\
set MatrixBot sendText Bla Bla Bla\
\
\
###\
To longpoll for messages:\
# 1. obtain token (if not already there):\
get MatrixBot token\
\
# 2. send special filter to Matrix:\
set MatrixBot sendFilter\
\
# 3. enable longPoll (waits up to 60 seconds\
# or until data is available)\
get MatrixBot longpoll\
\
#############################################################\
https://spec.matrix.org/v1.14/client-server-api/#syncing\
"
attr MatrixBot get01Data {\
"type": "m.login.password",\
"identifier": {\
"type": "m.id.user",\
"user": "[$name:MatrixUser]"\
},\
"password": "%%MatrixPassword%%"\
}
attr MatrixBot get01HeaderContent-Type application/json
attr MatrixBot get01Name token
attr MatrixBot get01Regex "access_token"\s*:\s*"([^"]+)"
attr MatrixBot get01TextArg 0
attr MatrixBot get01URL https://[$name:MatrixServer]/_matrix/client/v3/login
attr MatrixBot get02AlwaysNum 0
attr MatrixBot get02DeleteIfUnmatched 1
attr MatrixBot get02HeaderAuthorization Authorization: Bearer [$name:token]
attr MatrixBot get02Name longpoll
attr MatrixBot get02Regex \"next_batch\":\s*\"(?<next_batch>[^\"]+)\"(?:.*?\"timeline\":\s*{\s*\"events\":\s*\[\s*(?<events_json>.*?)(?=\s*\]\s*)\s*)?
attr MatrixBot get02TextArg 0
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&filter=[$name:filter_id]%%next_batch_param%%
attr MatrixBot replacement01Mode expression
attr MatrixBot replacement01Regex \[([^:\s\[\"\']+):([^\]\s]+)\]
attr MatrixBot replacement01Value my $device = $name if ($1 eq "\$name") // $1;;\
ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");;
attr MatrixBot replacement02Mode expression
attr MatrixBot replacement02Regex %%uuid%%
attr MatrixBot replacement02Value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15))))
attr MatrixBot replacement03Mode key
attr MatrixBot replacement03Regex %%MatrixPassword%%
attr MatrixBot replacement03Value MatrixPassword
attr MatrixBot replacement04Mode expression
attr MatrixBot replacement04Regex %%next_batch_param%%
attr MatrixBot replacement04Value #is there a reading 'next_batch'?\
my $val = ReadingsVal($name, 'next_batch', '???');;\
\
#return the GET parameter 'sync=value' for /sync Endpoint\
return "&since=$val" if ($val ne '???');;\
\
#return neither since-key nor value for it:\
return "";;
attr MatrixBot set01Data {\
"msgtype": "m.text",\
"body": "$val"\
}
attr MatrixBot set01HeaderAuthorization Authorization: Bearer [$name:token]
attr MatrixBot set01HeaderContent-Type application/json
attr MatrixBot set01Method POST
attr MatrixBot set01Name sendText
attr MatrixBot set01TextArg 1
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%
attr MatrixBot set02Data {\
"room": {\
"rooms": ["[$name:MatrixRoomID]"],\
"timeline": {\
"limit": 10,\
"types": ["m.room.message"]\
},\
"include_leave": false,\
"include_join": false,\
"include_account_data": false,\
"include_state": false,\
"state": {\
"types": []\
},\
"ephemeral": {\
"types": []\
},\
"account_data": {\
"types": []\
}\
},\
"event_fields": [\
"content.body",\
"sender",\
"origin_server_ts"\
],\
"event_format": "client",\
"presence": {\
"types": []\
},\
"account_data": {\
"types": []\
}\
}
attr MatrixBot set02HeaderAuthorization Authorization: Bearer [$name:token]
attr MatrixBot set02HeaderContent-Type application/json
attr MatrixBot set02Method POST
attr MatrixBot set02Name sendFilter
attr MatrixBot set02NoArg 1
attr MatrixBot set02ParseResponse 1
attr MatrixBot set02Regex \"filter_id\":\s*\"(?<filter_id>\d+)\"
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter
attr MatrixBot timeout 61
attr MatrixBot verbose 1
Hallo,
Dieses HTTPMOD kann Nachrichten an Matrix senden und auch verzögerungsarm empfangen (longpoll, websockets sind in Matrix nicht vorhanden).
Das Passwort wird verschleiert gespeichert, damit man es setzt muss man:
set MatrixBot storeKeyValue MatrixPassword yourPassword123
Zum Senden einer Textnachricht:
set MatrixBot sendText Mein Test Text kommt hier hin
Nachrichten kann man an verschlüsselte als auch unverschlüsselte Räume senden. Lediglich, wenn verschlüsselte Räume es verbieten, kann man hiermit keine Nachricht senden. Es wird immer HTTPS als Transportverschlüsselung genutzt, das Megolm-E2EE-Protokoll allerdings nicht.
Zum Empfangen von Nachrichten legt man einen unverschlüsselten Raum an und kann dann so auf Nachrichten lauschen:
set MatrixBot longpollCmd startTimer
Eintreffende Nachrichten können "im Schwung" eintreffen. Damit FHEM diese Nachrichten einzeln sieht, erzeugt das Device Einzel-Events, die im Eventmonitor so aussehen:
2025-04-11 20:59:13 HTTPMOD MatrixBot msg: 2025-04-11 20:58:56: @MeinNutzerName:nope.chat: Nachricht 1
2025-04-11 20:59:13 HTTPMOD MatrixBot msg: 2025-04-11 20:59:04: @MeinNutzerName:nope.chat: und 2
2025-04-11 20:59:13 HTTPMOD MatrixBot msg: 2025-04-11 20:59:04: @MeinNutzerName:nope.chat: 3 und
2025-04-11 20:59:13 HTTPMOD MatrixBot msg: 2025-04-11 20:59:05: @MeinNutzerName:nope.chat: 4
So kann man dann mit DOIF, Notify etc auf bestimmte Nachrichten lauschen.
Unicode 🚀🤩 und Umlaute äöü funktionieren natürlich auch.
defmod MatrixBot HTTPMOD none 0
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser
attr MatrixBot MatrixRoomID !1234567890:nope.chat
attr MatrixBot MatrixServer nope.chat
attr MatrixBot MatrixUser DeinNutzername
attr MatrixBot bodyDecode utf-8
attr MatrixBot comment "\
Create a room for FHEM.\
\
The room must not use encryption, a room that has encryption\
enabled, cannot be converted to a non-encrypted room anymore \
\
The room-id can be found in Element-Web at:\
Room Settings --> Advanced --> Internal room ID\
\
To store the password in FHEM in obfuscated way:\
set MatrixBot storeKeyValue MatrixPassword yourPassword123\
\
###\
To send a text:\
set MatrixBot sendText Bla Bla Bla\
\
\
###\
To longpoll for messages once:\
# 1. send special filter to Matrix:\
set MatrixBot sendFilter\
\
# 2. start one longPoll (waits up to 60 seconds\
# or until data is available)\
get MatrixBot longpoll\
\
#alternatively, to keep on longPolling (this also sets filter):\
set MatrixBot longpollCmd startTimer\
#to stop the timers:\
set MatrixBot longpollCmd stopTimer\
\
#############################################################\
https://spec.matrix.org/v1.14/client-server-api/#syncing\
"
attr MatrixBot get02AlwaysNum 0
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot get02Name longpoll
attr MatrixBot get02Regex \"next_batch\":\s*\"(?<next_batch>[^\"]+)\"(?:.*?\"timeline\":\s*{\s*\"events\":\s*(?<messages>\[.*?\])\s*)?
attr MatrixBot get02TextArg 0
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&filter=[$name:filter_id]%%next_batch_param%%
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN
attr MatrixBot replacement01Mode expression
attr MatrixBot replacement01Regex \[([^:\s\[\"\']+):([^\]\s]+)\]
attr MatrixBot replacement01Value my $device = $name if ($1 eq "\$name") // $1;;\
ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");;
attr MatrixBot replacement02Mode expression
attr MatrixBot replacement02Regex %%uuid%%
attr MatrixBot replacement02Value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15))))
attr MatrixBot replacement03Mode key
attr MatrixBot replacement03Regex %%MatrixPassword%%
attr MatrixBot replacement03Value MatrixPassword
attr MatrixBot replacement04Mode expression
attr MatrixBot replacement04Regex %%next_batch_param%%
attr MatrixBot replacement04Value #is there a reading 'next_batch'?\
my $val = ReadingsVal($name, 'next_batch', '???');;\
\
#return the GET parameter 'sync=value' for /sync Endpoint\
return "&since=$val" if ($val ne '???');;\
\
#return neither since-key nor value for it:\
return "";;
attr MatrixBot set01Data {\
"msgtype": "m.text",\
"body": "$val"\
}
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot set01HeaderContent-Type application/json
attr MatrixBot set01Method POST
attr MatrixBot set01Name sendText
attr MatrixBot set01TextArg 1
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%
attr MatrixBot set02Data {\
"room": {\
"rooms": ["[$name:MatrixRoomID]"],\
"timeline": {\
"limit": 10,\
"types": ["m.room.message"]\
},\
"include_leave": false,\
"include_join": false,\
"include_account_data": false,\
"include_state": false,\
"state": {\
"types": []\
},\
"ephemeral": {\
"types": []\
},\
"account_data": {\
"types": []\
}\
},\
"event_fields": [\
"content.body",\
"sender",\
"origin_server_ts"\
],\
"event_format": "client",\
"presence": {\
"types": [],\
"not_types": ["*"]\
},\
"account_data": {\
"types": [],\
"not_types": ["*"]\
}\
}
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot set02HeaderContent-Type application/json
attr MatrixBot set02Method POST
attr MatrixBot set02Name sendFilter
attr MatrixBot set02NoArg 1
attr MatrixBot set02ParseResponse 1
attr MatrixBot set02Regex \"filter_id\":\s*\"(?<filter_id>\d+)\"
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter
attr MatrixBot set03Local 1
attr MatrixBot set03Name longpollCmd
attr MatrixBot set03TextArg 1
attr MatrixBot showBody 0
attr MatrixBot showError 1
attr MatrixBot sid01Data {\
"type": "m.login.password",\
"identifier": {\
"type": "m.id.user",\
"user": "[$name:MatrixUser]"\
},\
"password": "%%MatrixPassword%%"\
}
attr MatrixBot sid01HeaderContent-Type application/json
attr MatrixBot sid01IdRegex "access_token"\s*:\s*"([^"]+)"
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login
attr MatrixBot timeout 62
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR):.* {\
my $longpollCmd = ReadingsVal($name, 'longpollCmd', '???');;\
my $delay = 1;;\
my $timeout = AttrVal($name, 'timeout', 61) + $delay + 5;;\
\
# stop our timers:\
if ($longpollCmd eq "stopTimer") {\
fhem("cancel ${name}_longpollTimer quiet");;\
fhem("cancel ${name}_longpollTimer2 quiet");;\
return "stopped";;\
}\
\
if ($longpollCmd ne "startTimer") {\
return "no timer set, longpollCmd is not set to 'startTimer'";;\
}\
\
#if startTimer cmd was given now, set filter as well:\
if (ReadingsAge($name, 'longpollCmd', 0) <= 1) {\
$delay = 5;; #delay to allow for sendFilter to be answered\
#Log(1, "🪲 $name: >>". InternalVal($name, 'httpbody', '???') ."<<");;\
fhem("sleep 0.1 quiet;; set $name sendFilter");;\
}\
\
#we handle an error reported by http-utils:\
if (ReadingsAge($name, 'LAST_ERROR', 0) <= 1) {\
my $last_error = ReadingsVal($name, 'LAST_ERROR', '???');;\
$delay = 10;; #delay to allow for error reasons to improve\
#Log(1, "🪲 $name: Dealing with error: >>$last_error<<");;\
}\
\
#set timers, one regular and one fallback:\
fhem("sleep $delay ${name}_longpollTimer quiet;; get $name longpoll");;\
fhem("sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer");;\
\
return strftime("next longpoll at %H:%M:%S", localtime( time()+$delay ));;\
},\
messages_list:messages:.* {\
my $this = ReadingsVal($name, $reading, '');;\
my @timestampArray = split("\n", $this);;\
my $messages_ref = decode_json(ReadingsVal($name, 'messages', ''));;\
my $length = 20;;\
\
my %seen_messages = map { $_ => 1 } @timestampArray;;\
\
foreach my $msg (@$messages_ref) {\
my $val = $msg->{content}{body};;\
$val = Encode::decode('utf-8', $val) unless Encode::is_utf8($val);;\
\
my $sender = $msg->{sender};;\
my $ts = strftime("%Y-%m-%d %H:%M:%S", localtime($msg->{origin_server_ts} / 1000));;\
my $new_entry = "$ts: $sender: $val";;\
\
next if $seen_messages{$new_entry};;\
\
my $inserted = 0;;\
for (my $i = 0;; $i < @timestampArray;; $i++) {\
my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\
if ($ts lt $existing_ts) {\
splice(@timestampArray, $i, 0, $new_entry);;\
$inserted = 1;;\
last;;\
}\
}\
push(@timestampArray, encode('utf-8', $new_entry)) unless $inserted;;\
shift(@timestampArray) while @timestampArray > $length;;\
}\
\
return join("\n", @timestampArray);;\
},\
process_messages:messages:.* {\
my $val = ReadingsVal($name, 'messages_list', '');;\
my $this = ReadingsVal($name, $reading, '');;\
my $latest_ts = $this;;\
\
foreach my $line (split(/\n/, $val)) {\
if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\
my ($ts, $msg) = ($1, $2);;\
\
if ($ts gt $this) {\
fhem("trigger $name msg: $line");;\
$latest_ts = $ts if ($ts gt $latest_ts);;\
}\
}\
}\
return $latest_ts;;\
}
attr MatrixBot verbose 3
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer
Ein einfaches DOIF kann zum Beispiel auf die Nachricht "Ping" oder auch "ping" mit einem "Pong!" reagieren:
defmod MatrixPing.doif DOIF (["MatrixBot:msg: .*: @.*: [Pp]ing$"])\
(set MatrixBot sendText Pong!)
attr MatrixPing.doif do always
attr MatrixPing.doif icon homeConnect
attr MatrixPing.doif alias Matrix Ping/Pong
Falls es gefällt, freue ich mich über einen Daumen hoch.
Bekannte Probleme: Wenn HTTPMOD bereits auf Nachrichten lauscht und man sendet dann Nachrichten, wird der Empfang bis zum Timeout unterbrochen und synchronisiert sich erst später. Wenn das stört, kann man einfach ein Device zum Senden anlegen und eins für den Empfang.
So kann es aussehen:
Screenshot_2025-04-12_09-00-38.png
Hi,
Noch eine Detailverbesserung: Ich habe noch eine parseFunction1 ergänzt, falls der Server einen HTTP-Code von 401, 403 oder 500 sendet wird der Access-Token erneuert:
if (!defined &HTTPMOD::handleAuthErrors) {
*HTTPMOD::handleAuthErrors = sub {
my ($hash, $header, $body, $request) = @_;
my $name = $hash->{NAME};
my $status;
if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {
$status = $1;
}
Log3($name, 4, "$name: HTTP status code is $status");
if ( $status == 401 || $status == 403 || $status == 500 ) {
Log3($name, 3, "$name: auth-error or servererror ($status), calling doAuth()");
HTTPMOD::DoAuth($hash);
}
};
}
als komplettes Listing dann:
defmod MatrixBot HTTPMOD none 0
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser
attr MatrixBot MatrixRoomID !12345678901234:nope.chat
attr MatrixBot MatrixServer nope.chat
attr MatrixBot MatrixUser DeinNutzername
attr MatrixBot bodyDecode utf-8
attr MatrixBot comment "\
Create a room for FHEM.\
\
The room must not use encryption, a room that has encryption\
enabled, cannot be converted to a non-encrypted room anymore \
\
The room-id can be found in Element-Web at:\
Room Settings --> Advanced --> Internal room ID\
\
To store the password in FHEM in obfuscated way:\
set MatrixBot storeKeyValue MatrixPassword yourPassword123\
\
###\
To send a text:\
set MatrixBot sendText Bla Bla Bla\
\
\
###\
To longpoll for messages once:\
# 1. send special filter to Matrix:\
set MatrixBot sendFilter\
\
# 2. start one longPoll (waits up to 60 seconds\
# or until data is available)\
get MatrixBot longpoll\
\
#alternatively, to keep on longPolling (this also sets filter):\
set MatrixBot longpollCmd startTimer\
#to stop the timers:\
set MatrixBot longpollCmd stopTimer\
\
#############################################################\
https://spec.matrix.org/v1.14/client-server-api/#syncing\
"
attr MatrixBot get02AlwaysNum 0
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot get02Name longpoll
attr MatrixBot get02Regex \"next_batch\":\s*\"(?<next_batch>[^\"]+)\"(?:.*?\"timeline\":\s*{\s*\"events\":\s*(?<messages>\[.*?\])\s*)?
attr MatrixBot get02TextArg 0
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&filter=[$name:filter_id]%%next_batch_param%%
attr MatrixBot icon message_info
attr MatrixBot parseFunction1 handleAuthErrors
attr MatrixBot reAuthAlways 0
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN
attr MatrixBot replacement01Mode expression
attr MatrixBot replacement01Regex \[([^:\s\[\"\']+):([^\]\s]+)\]
attr MatrixBot replacement01Value my $device = $name if ($1 eq "\$name") // $1;;\
ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");;
attr MatrixBot replacement02Mode expression
attr MatrixBot replacement02Regex %%uuid%%
attr MatrixBot replacement02Value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15))))
attr MatrixBot replacement03Mode key
attr MatrixBot replacement03Regex %%MatrixPassword%%
attr MatrixBot replacement03Value MatrixPassword
attr MatrixBot replacement04Mode expression
attr MatrixBot replacement04Regex %%next_batch_param%%
attr MatrixBot replacement04Value #is there a reading 'next_batch'?\
my $val = ReadingsVal($name, 'next_batch', '???');;\
\
#return the GET parameter 'sync=value' for /sync Endpoint\
return "&since=$val" if ($val ne '???');;\
\
#return neither since-key nor value for it:\
return "";;
attr MatrixBot set01Data {\
"msgtype": "m.text",\
"body": "$val"\
}
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot set01HeaderContent-Type application/json
attr MatrixBot set01Method POST
attr MatrixBot set01Name sendText
attr MatrixBot set01TextArg 1
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%
attr MatrixBot set02Data {\
"room": {\
"rooms": ["[$name:MatrixRoomID]"],\
"timeline": {\
"limit": 10,\
"types": ["m.room.message"]\
},\
"include_leave": false,\
"include_join": false,\
"include_account_data": false,\
"include_state": false,\
"state": {\
"types": []\
},\
"ephemeral": {\
"types": []\
},\
"account_data": {\
"types": []\
}\
},\
"event_fields": [\
"content.body",\
"sender",\
"origin_server_ts"\
],\
"event_format": "client",\
"presence": {\
"types": [],\
"not_types": ["*"]\
},\
"account_data": {\
"types": [],\
"not_types": ["*"]\
}\
}
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot set02HeaderContent-Type application/json
attr MatrixBot set02Method POST
attr MatrixBot set02Name sendFilter
attr MatrixBot set02NoArg 1
attr MatrixBot set02ParseResponse 1
attr MatrixBot set02Regex \"filter_id\":\s*\"(?<filter_id>\d+)\"
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter
attr MatrixBot set03Local 1
attr MatrixBot set03Name longpollCmd
attr MatrixBot set03TextArg 1
attr MatrixBot showBody 0
attr MatrixBot showError 1
attr MatrixBot sid01Data {\
"type": "m.login.password",\
"identifier": {\
"type": "m.id.user",\
"user": "[$name:MatrixUser]"\
},\
"password": "%%MatrixPassword%%"\
}
attr MatrixBot sid01HeaderContent-Type application/json
attr MatrixBot sid01IdRegex "access_token"\s*:\s*"([^"]+)"
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login
attr MatrixBot timeout 65
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR):.* {\
my $longpollCmd = ReadingsVal($name, 'longpollCmd', '???');;\
my $delay = 1;;\
my $timeout = AttrVal($name, 'timeout', 61) + $delay + 5;;\
\
# stop our timers:\
if ($longpollCmd eq "stopTimer") {\
fhem("cancel ${name}_longpollTimer quiet");;\
fhem("cancel ${name}_longpollTimer2 quiet");;\
return "stopped";;\
}\
\
if ($longpollCmd ne "startTimer") {\
return "no timer set, longpollCmd is not set to 'startTimer'";;\
}\
\
#if startTimer cmd was given now, set filter as well:\
if (ReadingsAge($name, 'longpollCmd', 0) <= 1) {\
$delay = 5;; #delay to allow for sendFilter to be answered\
#Log(1, "🪲 $name: >>". InternalVal($name, 'httpbody', '???') ."<<");;\
fhem("sleep 0.1 quiet;; set $name sendFilter");;\
}\
\
#we handle an error reported by http-utils:\
if (ReadingsAge($name, 'LAST_ERROR', 0) <= 1) {\
my $last_error = ReadingsVal($name, 'LAST_ERROR', '???');;\
$delay = 10;; #delay to allow for error reasons to improve\
#Log(1, "🪲 $name: Dealing with error: >>$last_error<<");;\
}\
\
# for testing this: { $defs{MatrixBot}{sid} = 'bla' }\
if (!defined &HTTPMOD::handleAuthErrors) {\
*HTTPMOD::handleAuthErrors = sub {\
my ($hash, $header, $body, $request) = @_;;\
my $name = $hash->{NAME};;\
my $status;;\
\
if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {\
$status = $1;;\
}\
\
Log3($name, 4, "$name: HTTP status code is $status");;\
\
if ( $status == 401 || $status == 403 || $status == 500 ) {\
Log3($name, 3, "$name: auth-error or servererror ($status), calling doAuth()");;\
HTTPMOD::DoAuth($hash);;\
}\
};;\
}\
\
#set timers, one regular and one fallback:\
fhem("sleep $delay ${name}_longpollTimer quiet;; get $name longpoll");;\
fhem("sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer");;\
\
return strftime("next longpoll at %H:%M:%S", localtime( time()+$delay ));;\
},\
messages_list:messages:.* {\
my $this = ReadingsVal($name, $reading, '');;\
my @timestampArray = split("\n", $this);;\
my $messages_ref = decode_json(ReadingsVal($name, 'messages', ''));;\
my $length = 20;;\
\
my %seen_messages = map { $_ => 1 } @timestampArray;;\
\
foreach my $msg (@$messages_ref) {\
my $val = $msg->{content}{body};;\
$val = Encode::decode('utf-8', $val) unless Encode::is_utf8($val);;\
\
my $sender = $msg->{sender};;\
my $ts = strftime("%Y-%m-%d %H:%M:%S", localtime($msg->{origin_server_ts} / 1000));;\
my $new_entry = "$ts: $sender: $val";;\
\
next if $seen_messages{$new_entry};;\
\
my $inserted = 0;;\
for (my $i = 0;; $i < @timestampArray;; $i++) {\
my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\
if ($ts lt $existing_ts) {\
splice(@timestampArray, $i, 0, $new_entry);;\
$inserted = 1;;\
last;;\
}\
}\
push(@timestampArray, encode('utf-8', $new_entry)) unless $inserted;;\
shift(@timestampArray) while @timestampArray > $length;;\
}\
\
return join("\n", @timestampArray);;\
},\
process_messages:messages:.* {\
my $val = ReadingsVal($name, 'messages_list', '');;\
my $this = ReadingsVal($name, $reading, '');;\
my $latest_ts = $this;;\
\
foreach my $line (split(/\n/, $val)) {\
if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\
my ($ts, $msg) = ($1, $2);;\
\
if ($ts gt $this) {\
fhem("trigger $name msg: $line");;\
$latest_ts = $ts if ($ts gt $latest_ts);;\
}\
}\
}\
return $latest_ts;;\
}
attr MatrixBot verbose 3
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer
Hallo,
Das bekannte Problem, wenn der Longpoll noch aktiv ist und man dann eine Nachricht senden möchte, ist nun gelöst - man braucht also nicht zwei Devices einrichten.
Es wird das Attribut set01IExpr ein wenig ungewöhnlich genutzt um vor dem Senden ein wenig Perlcode auszuführen. So wird dann der longpoll-Aufruf vorzeitig beendet und das set01 kann seine Arbeit verrichten ohne extra warten zu müssen:
attr MatrixBot set01IExpr #cancel an active longpoll\
if ($hash->{BUSY}\
&& $hash->{HttpUtils}\
&& $hash->{HttpUtils}->{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&filter=| ) {\
Log(3, "$name: longpoll active, cutting it off now");;\
HttpUtils_Close($hash->{HttpUtils});;\
}\
\
#just return $val unteraltered\
$val
Warum das Probleme machte kann ich nur eingrenzen auf den Abschnitt aus der 98_HTTPMOD.pm:
if ($hash->{BUSY}) { # still waiting for reply to last request
if ($now > $last + max(15, AttrVal($name, "timeout", 2) *2)) {
Log3 $name, 5, "$name: HandleSendQueue - still waiting for reply, timeout is over twice - this should never happen. Stop waiting";
$hash->{BUSY} = 0; # waited long enough, clear busy flag and continue
}
Obwohl das Timeout auf 65 Sekunden steht und auch .LASTSEND auf sinnvollen Werten steht, springt HTTPMOD in das Fehlerhandling, das einfach nur busy auf 0 setzt, ohne den ggf. noch aktiven longpoll darüber zu informieren. Als Reaktion darauf kann es passieren, dass die ReadCallback-Funktion dann zweimal aufgerufen wird, die Ergebnisse aber bei dem falschem Reading landen. Wie auch immer, so wie oben umschiffe ich das Problem.
Hier das ganze Device zum einfachen Einrichten:
defmod MatrixBot HTTPMOD none 0
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser
attr MatrixBot MatrixRoomID !12345678901234:nope.chat
attr MatrixBot MatrixServer nope.chat
attr MatrixBot MatrixUser DeinUsername
attr MatrixBot bodyDecode utf-8
attr MatrixBot bodyEncode utf-8
attr MatrixBot comment "\
Create a room for FHEM.\
\
The room must not use encryption, a room that has encryption\
enabled, cannot be converted to a non-encrypted room anymore \
\
The room-id can be found in Element-Web at:\
Room Settings --> Advanced --> Internal room ID\
\
To store the password in FHEM in obfuscated way:\
set MatrixBot storeKeyValue MatrixPassword yourPassword123\
\
###\
To send a text:\
set MatrixBot sendText Bla Bla Bla\
\
\
###\
To longpoll for messages once:\
# 1. send special filter to Matrix:\
set MatrixBot sendFilter\
\
# 2. start one longPoll (waits up to 60 seconds\
# or until data is available)\
get MatrixBot longpoll\
\
#alternatively, to keep on longPolling (this also sets filter):\
set MatrixBot longpollCmd startTimer\
#to stop the timers:\
set MatrixBot longpollCmd stopTimer\
\
#############################################################\
https://spec.matrix.org/v1.14/client-server-api/#syncing\
"
attr MatrixBot get02AlwaysNum 0
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot get02Name longpoll
attr MatrixBot get02Regex \"next_batch\":\s*\"(?<next_batch>[^\"]+)\"(?:.*?\"timeline\":\s*{\s*\"events\":\s*(?<messages>\[.*?\])\s*)?
attr MatrixBot get02TextArg 0
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&filter=[$name:filter_id]%%next_batch_param%%
attr MatrixBot icon message_info
attr MatrixBot parseFunction1 handleAuthErrors
attr MatrixBot reAuthAlways 0
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN
attr MatrixBot replacement01Mode expression
attr MatrixBot replacement01Regex \[([^:\s\[\"\']+):([^\]\s]+)\]
attr MatrixBot replacement01Value my $device = $name if ($1 eq "\$name") // $1;;\
ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");;
attr MatrixBot replacement02Mode expression
attr MatrixBot replacement02Regex %%uuid%%
attr MatrixBot replacement02Value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15))))
attr MatrixBot replacement03Mode key
attr MatrixBot replacement03Regex %%MatrixPassword%%
attr MatrixBot replacement03Value MatrixPassword
attr MatrixBot replacement04Mode expression
attr MatrixBot replacement04Regex %%next_batch_param%%
attr MatrixBot replacement04Value #is there a reading 'next_batch'?\
my $val = ReadingsVal($name, 'next_batch', '???');;\
\
#return the GET parameter 'sync=value' for /sync Endpoint\
return "&since=$val" if ($val ne '???');;\
\
#return neither since-key nor value for it:\
return "";;
attr MatrixBot set01Data {\
"msgtype": "m.text",\
"body": "$val"\
}
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot set01HeaderContent-Type application/json
attr MatrixBot set01IExpr #cancel an active longpoll\
if ($hash->{BUSY}\
&& $hash->{HttpUtils}\
&& $hash->{HttpUtils}->{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&filter=| ) {\
Log(3, "$name: longpoll active, cutting it off now");;\
HttpUtils_Close($hash->{HttpUtils});;\
}\
\
#just return $val unteraltered\
$val
attr MatrixBot set01Method POST
attr MatrixBot set01Name sendText
attr MatrixBot set01ParseResponse 1
attr MatrixBot set01Regex {\"event_id\":\"(.*)\"}
attr MatrixBot set01TextArg 1
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%
attr MatrixBot set02Data {\
"room": {\
"rooms": ["[$name:MatrixRoomID]"],\
"timeline": {\
"limit": 10,\
"types": ["m.room.message"]\
},\
"include_leave": false,\
"include_join": false,\
"include_account_data": false,\
"include_state": false,\
"state": {\
"types": []\
},\
"ephemeral": {\
"types": []\
},\
"account_data": {\
"types": []\
}\
},\
"event_fields": [\
"content.body",\
"sender",\
"origin_server_ts"\
],\
"event_format": "client",\
"presence": {\
"types": [],\
"not_types": ["*"]\
},\
"account_data": {\
"types": [],\
"not_types": ["*"]\
}\
}
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot set02HeaderContent-Type application/json
attr MatrixBot set02Method POST
attr MatrixBot set02Name sendFilter
attr MatrixBot set02NoArg 1
attr MatrixBot set02ParseResponse 1
attr MatrixBot set02Regex \"filter_id\":\s*\"(?<filter_id>\d+)\"
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter
attr MatrixBot set03Local 1
attr MatrixBot set03Name longpollCmd
attr MatrixBot set03TextArg 1
attr MatrixBot showError 1
attr MatrixBot sid01Data {\
"type": "m.login.password",\
"identifier": {\
"type": "m.id.user",\
"user": "[$name:MatrixUser]"\
},\
"password": "%%MatrixPassword%%"\
}
attr MatrixBot sid01HeaderContent-Type application/json
attr MatrixBot sid01IdRegex "access_token"\s*:\s*"([^"]+)"
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login
attr MatrixBot timeout 65
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR|sendText):.* {\
my $longpollCmd = ReadingsVal($name, 'longpollCmd', '???');;\
my $delay = 1;;\
my $timeout = AttrVal($name, 'timeout', 61) + $delay + 5;;\
\
# stop our timers:\
if ($longpollCmd eq "stopTimer") {\
fhem("cancel ${name}_longpollTimer quiet");;\
fhem("cancel ${name}_longpollTimer2 quiet");;\
return "stopped";;\
}\
\
if ($longpollCmd ne "startTimer") {\
return "no timer set, longpollCmd is not set to 'startTimer'";;\
}\
\
#if startTimer cmd was given now, set filter as well:\
if (ReadingsAge($name, 'longpollCmd', 0) <= 1) {\
$delay = 5;; #delay to allow for sendFilter to be answered\
#Log(1, "🪲 $name: >>". InternalVal($name, 'httpbody', '???') ."<<");;\
fhem("sleep 0.1 quiet;; set $name sendFilter");;\
}\
\
#we handle an error reported by http-utils:\
if (ReadingsAge($name, 'LAST_ERROR', 0) <= 1) {\
my $last_error = ReadingsVal($name, 'LAST_ERROR', '???');;\
$delay = 10;; #delay to allow for error reasons to improve\
#Log(1, "🪲 $name: Dealing with error: >>$last_error<<");;\
}\
\
# for testing this: { $defs{MatrixBot}{sid} = 'bla' }\
if (!defined &HTTPMOD::handleAuthErrors) {\
*HTTPMOD::handleAuthErrors = sub {\
my ($hash, $header, $body, $request) = @_;;\
my $name = $hash->{NAME};;\
my $status;;\
\
if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {\
$status = $1;;\
}\
\
Log3($name, 4, "$name: HTTP status code is $status");;\
\
if ( $status == 401 || $status == 403 || $status == 500 ) {\
Log3($name, 3, "$name: auth-error or servererror ($status), calling doAuth()");;\
HTTPMOD::DoAuth($hash);;\
}\
};;\
}\
\
#set timers, one regular and one fallback:\
fhem("sleep $delay ${name}_longpollTimer quiet;; get $name longpoll");;\
fhem("sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer");;\
\
return strftime("next longpoll at %H:%M:%S", localtime( time()+$delay ));;\
},\
messages_list:messages:.* {\
my $this = ReadingsVal($name, $reading, '');;\
my @timestampArray = split("\n", $this);;\
my $messages_ref = decode_json(ReadingsVal($name, 'messages', ''));;\
my $length = 20;;\
\
my %seen_messages = map { $_ => 1 } @timestampArray;;\
\
foreach my $msg (@$messages_ref) {\
my $val = $msg->{content}{body};;\
$val = Encode::decode('utf-8', $val) unless Encode::is_utf8($val);;\
$val =~ s/\n/ /g;; #replace newlines with spaces\
\
my $sender = $msg->{sender};;\
my $ts = strftime("%Y-%m-%d %H:%M:%S", localtime($msg->{origin_server_ts} / 1000));;\
my $new_entry = "$ts: $sender: $val";;\
\
next if $seen_messages{$new_entry};;\
\
my $inserted = 0;;\
for (my $i = 0;; $i < @timestampArray;; $i++) {\
my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\
if ($ts lt $existing_ts) {\
splice(@timestampArray, $i, 0, $new_entry);;\
$inserted = 1;;\
last;;\
}\
}\
push(@timestampArray, encode('utf-8', $new_entry)) unless $inserted;;\
shift(@timestampArray) while @timestampArray > $length;;\
}\
\
return join("\n", @timestampArray);;\
},\
process_messages:messages:.* {\
my $val = ReadingsVal($name, 'messages_list', '');;\
my $this = ReadingsVal($name, $reading, '');;\
my $latest_ts = $this;;\
\
foreach my $line (split(/\n/, $val)) {\
if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\
my ($ts, $msg) = ($1, $2);;\
\
if ($ts gt $this) {\
fhem("trigger $name msg: $line");;\
$latest_ts = $ts if ($ts gt $latest_ts);;\
}\
}\
}\
return $latest_ts;;\
}
attr MatrixBot verbose 3
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer
Im Wiki habe ich mal hierzu eine Seite angelegt: https://wiki.fhem.de/wiki/Matrix
Hi
irgendwie bin ich zu blöd.
Matrix kenne ich auch wenig, mir scheint als ob ich laufend neue Access Token erhalte.
Wo muss ich denn im Device den Token angeben, ?
muss hier attr MatrixTRBot sid01IdRegex "access_token"\s*:\s*"([^"]+)"
der token rein?
laufend die meldung falscher Token
der user muss doch mit @user:server eingegeben werden? richtig?
muss der Server als matrix.tchncs.de oder nur tchncs.de eingegeben werden?
Danke für die Klarstellung
Hi,
Den Token musst du garnicht selbst erstellen, das macht das Device für dich.
Nutzername und Server werden getrennt angegeben:
- attr MatrixUser FHEM_Nutzername_WieBeimServerOhneDoppelpunktUndOhneAt
- attr MatrixServer nope.chat (oder wo auch immer dein Matrix-Synapse-Server läuft)
Bezüglich des servers (ob mit Matrix. oder ohne) musste bitte einfach probieren. Ich meine es muss mit, da es auf eine andere IP verweist:
;; ANSWER SECTION:
matrix.tchncs.de. 110 IN CNAME h2.tchncs.de.
h2.tchncs.de. 423 IN A 135.181.232.169
;; ANSWER SECTION:
tchncs.de. 600 IN A 89.58.62.32
Zitat von: Torxgewinde am 23 Juli 2025, 11:22:47Hi,
Den Token musst du garnicht selbst erstellen, das macht das Device für dich.
Nutzername und Server werden getrennt angegeben:
- attr MatrixUser FHEM_Nutzername_WieBeimServerOhneDoppelpunktUndOhneAt
- attr MatrixServer nope.chat (oder wo auch immer dein Matrix-Synapse-Server läuft)
Bezüglich des servers (ob mit Matrix. oder ohne) musste bitte einfach probieren. Ich meine es muss mit, da es auf eine andere IP verweist:
;; ANSWER SECTION:
matrix.tchncs.de. 110 IN CNAME h2.tchncs.de.
h2.tchncs.de. 423 IN A 135.181.232.169
;; ANSWER SECTION:
tchncs.de. 600 IN A 89.58.62.32
alles klar danke, aber irgendwie schaffe ich es nicht.
habe mal parallel die andere sendefunktoin mit myutils eingebunden, da klappt das senden.
Habe hier aber noch andere Fehler, eventuell ist das das Problem:
als Server habe ich : https://matrix.tchncs.de
errors:
2025.07.24 10:35:50.386 5: MatrixTRBot: calling parseFunction1 as handleAuthErrors via attr for HTTP Response to set02
2025.07.24 10:35:50.386 1: PERL ERROR: Undefined subroutine &HTTPMOD::handleAuthErrors called at ./FHEM/98_HTTPMOD.pm line 1469.
modulversion: 98_HTTPMOD.pm:0.291590/2024-09-23 ein update ergab alles ok
Wo kann ich noch schauen? was mache ich falsch?
Danke
log
2025.07.24 10:35:37.402 4: MatrixTRBot: Read callback: request type was set02 retry 0, header: HTTP/1.1 401 Unauthorized Server: openresty Date: Thu, 24 Jul 2025 08:35:33 GMT Content-Type: application/json Connection: close Content-Encoding: gzip Cache-Control: no-cache, no-store, must-revalidate Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization, Date Access-Control-Expose-Headers: Synapse-Trace-Id, Server Strict-Transport-Security: max-age=31536000; includeSubdomains; preload, body length 88
2025.07.24 10:35:37.403 5: MatrixTRBot: Read callback: body {"errcode":"M_UNKNOWN_TOKEN","error":"Invalid access token passed.","soft_logout":false}
2025.07.24 10:35:37.403 4: MatrixTRBot: BodyDecode is decoding the response body as utf-8 but charset header is not found
2025.07.24 10:35:37.403 5: MatrixTRBot: GetCookies is looking for Cookies
2025.07.24 10:35:37.404 5: MatrixTRBot: ExtractSid called, context set, num 02
2025.07.24 10:35:37.404 4: MatrixTRBot: checking for redirects, code=401, ignore=0
2025.07.24 10:35:37.404 4: MatrixTRBot: no redirects to handle
2025.07.24 10:35:37.404 5: MatrixTRBot: Read callback sets LAST_REQUEST to set02
2025.07.24 10:35:37.405 5: MatrixTRBot: CheckAuth is checking buffer with ReAuthRegex (?^:M_UNKNOWN_TOKEN)
2025.07.24 10:35:37.405 4: MatrixTRBot: CheckAuth decided new authentication required
2025.07.24 10:35:37.405 5: MatrixTRBot: AddToQueue prepends type set02 to URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter, data { "room": { "rooms": ["[$name:MatrixRoomID]"], "timeline": { "limit": 10, "types": ["m.room.message"] }, "include_leave": false, "include_join": false, "include_account_data": false, "include_state": false, "state": { "types": [] }, "ephemeral": { "types": [] }, "account_data": { "types": [] } }, "event_fields": [ "content.body", "sender", "origin_server_ts" ], "event_format": "client", "presence": { "types": [], "not_types": ["*"] }, "account_data": { "types": [], "not_types": ["*"] } }, header Authorization: Bearer $sid application/json, retry 1, initial queue len: 0
2025.07.24 10:35:37.405 4: MatrixTRBot: CheckAuth prepended request set02 again before auth, retryCount 0 ...
2025.07.24 10:35:37.406 4: MatrixTRBot: DoAuth called with Steps: 01
2025.07.24 10:35:37.406 5: MatrixTRBot: AddToQueue prepends type auth01 to URL https://[$name:MatrixServer]/_matrix/client/v3/login, data { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "[$name:MatrixUser]" }, "password": "%%MatrixPassword%%" }, header application/json, retry 0, initial queue len: 1
2025.07.24 10:35:37.407 5: MatrixTRBot: HandleSendQueue called from DoAuth, qlen = 2
2025.07.24 10:35:37.407 5: MatrixTRBot: Replace called for type auth01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: application/json
2025.07.24 10:35:37.407 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: application/json
2025.07.24 10:35:37.407 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: application/json
2025.07.24 10:35:37.408 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 10:35:37.409 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: application/json
2025.07.24 10:35:37.409 5: MatrixTRBot: Replace called for type auth01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "[$name:MatrixUser]" }, "password": "%%MatrixPassword%%" }
2025.07.24 10:35:37.409 5: MatrixTRBot: Replace: match for type auth01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value package main; my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");, input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "[$name:MatrixUser]" }, "password": "%%MatrixPassword%%" }, result is { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "%%MatrixPassword%%" }
2025.07.24 10:35:37.410 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "%%MatrixPassword%%" }
2025.07.24 10:35:37.410 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "%%MatrixPassword%%" }
2025.07.24 10:35:37.410 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 10:35:37.411 5: MatrixTRBot: Replace: key MatrixPassword value is xxxxx
2025.07.24 10:35:37.411 5: MatrixTRBot: Replace: match for type auth01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword, input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "[$name:MatrixUser]" }, "password": "%%MatrixPassword%%" }, result is { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "myPassWDMatrix" }
2025.07.24 10:35:37.411 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "myPassWDMatrix" }
2025.07.24 10:35:37.411 5: MatrixTRBot: Replace called for type auth01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: https://[$name:MatrixServer]/_matrix/client/v3/login
2025.07.24 10:35:37.412 5: MatrixTRBot: Replace: match for type auth01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value package main; my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");, input: https://[$name:MatrixServer]/_matrix/client/v3/login, result is https://matrix.tchncs.de/_matrix/client/v3/login
2025.07.24 10:35:37.412 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: https://matrix.tchncs.de/_matrix/client/v3/login
2025.07.24 10:35:37.412 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: https://matrix.tchncs.de/_matrix/client/v3/login
2025.07.24 10:35:37.412 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 10:35:37.413 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: https://matrix.tchncs.de/_matrix/client/v3/login
2025.07.24 10:35:37.413 5: MatrixTRBot: no separator for multiple values (Context auth01, 01)
2025.07.24 10:35:37.413 4: MatrixTRBot: HandleSendQueue sends auth01 with timeout 65 to https://matrix.tchncs.de/_matrix/client/v3/login, data: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "myPassWDMatrix" }, header: application/json
2025.07.24 10:35:37.415 5: MatrixTRBot: StartQueueTimer called from HandleSendQueue sets internal timer to process queue in 1.000 seconds
2025.07.24 10:35:50.383 4: MatrixTRBot: Read callback: request type was set02 retry 1, header: HTTP/1.1 401 Unauthorized Server: openresty Date: Thu, 24 Jul 2025 08:35:48 GMT Content-Type: application/json Connection: close Content-Encoding: gzip Cache-Control: no-cache, no-store, must-revalidate Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization, Date Access-Control-Expose-Headers: Synapse-Trace-Id, Server Strict-Transport-Security: max-age=31536000; includeSubdomains; preload, body length 88
2025.07.24 10:35:50.383 5: MatrixTRBot: Read callback: body {"errcode":"M_UNKNOWN_TOKEN","error":"Invalid access token passed.","soft_logout":false}
2025.07.24 10:35:50.383 4: MatrixTRBot: BodyDecode is decoding the response body as utf-8 but charset header is not found
2025.07.24 10:35:50.383 5: MatrixTRBot: GetCookies is looking for Cookies
2025.07.24 10:35:50.384 5: MatrixTRBot: ExtractSid called, context set, num 02
2025.07.24 10:35:50.384 4: MatrixTRBot: checking for redirects, code=401, ignore=0
2025.07.24 10:35:50.384 4: MatrixTRBot: no redirects to handle
2025.07.24 10:35:50.384 5: MatrixTRBot: Read callback sets LAST_REQUEST to set02
2025.07.24 10:35:50.384 5: MatrixTRBot: CheckAuth is checking buffer with ReAuthRegex (?^:M_UNKNOWN_TOKEN)
2025.07.24 10:35:50.385 4: MatrixTRBot: CheckAuth decided new authentication required
2025.07.24 10:35:50.385 4: MatrixTRBot: Authentication still required but no retries left - did last authentication fail?
2025.07.24 10:35:50.385 5: MatrixTRBot: ExtractReading sendFilter with regex /(?^:\"filter_id\":\s*\"(?<filter_id>\d+)\")/...
2025.07.24 10:35:50.385 5: MatrixTRBot: ExtractReading sendFilter did not match
2025.07.24 10:35:50.385 4: MatrixTRBot: Read response to set02 didn't match any Reading
2025.07.24 10:35:50.386 5: MatrixTRBot: calling parseFunction1 as handleAuthErrors via attr for HTTP Response to set02
2025.07.24 10:35:50.386 1: PERL ERROR: Undefined subroutine &HTTPMOD::handleAuthErrors called at ./FHEM/98_HTTPMOD.pm line 1469.
2025.07.24 10:35:50.386 1: stacktrace:
2025.07.24 10:35:50.386 1: main::__ANON__ called by ./FHEM/98_HTTPMOD.pm (1469)
2025.07.24 10:35:50.386 1: (eval) called by ./FHEM/98_HTTPMOD.pm (1469)
2025.07.24 10:35:50.386 1: HTTPMOD::HandleParseFunctions called by ./FHEM/98_HTTPMOD.pm (2529)
2025.07.24 10:35:50.386 1: HTTPMOD::ReadCallback called by FHEM/HttpUtils.pm (756)
2025.07.24 10:35:50.387 1: main::__ANON__ called by fhem.pl (786)
2025.07.24 10:35:50.387 3: MatrixTRBot: error calling handleAuthErrors: Undefined subroutine &HTTPMOD::handleAuthErrors called at ./FHEM/98_HTTPMOD.pm line 1469.
2025.07.24 10:35:50.387 5: MatrixTRBot: HandleSendQueue called from ReadCallback, qlen = 0
2025.07.24 10:35:50.387 5: MatrixTRBot: HandleSendQueue found no usable entry in queue
Danke für das ausführliche Log, ich jubel HTTPMOD eine eigene Parsefunction unter, aber das scheint nicht in der Reihenfolge zu passieren, wie ich es beabsichtigte.
Kannst du testweise bitte in myUtils folgende Funktion packen:
sub handleAuthErrorsAusMyUtils {
my ($hash, $header, $body, $request) = @_;
my $name = $hash->{NAME};
my $status;
if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {
$status = $1;
}
Log3($name, 4, "$name: HTTP status code is $status");
if ($status == 401 || $status == 403 || $status == 500) {
Log3($name, 3, "$name: auth-error or servererror ($status), calling doAuth()");
HTTPMOD::DoAuth($hash);
}
}
Und im HTTPMOD device dann:
attr MatrixBot parseFunction1 handleAuthErrorsAusMyUtils
Wenn das so nichts wird, dann muss ich mal auf einem "frischem" FHEM debuggen.
Danke für die Hilfe
hier mal das log mit der erweiterten myUtil
hoffe habe alles gefunden
2025.07.24 21:06:40.790 5: MatrixTRBot: set called with sendText testmatrix
2025.07.24 21:06:40.790 5: MatrixTRBot: set found option sendText in attribute set01Name
2025.07.24 21:06:40.791 5: MatrixTRBot: set01IExpr evaluated package main; my @setValArr = @{$oRef->{'@setValArr'}};#cancel an active longpoll if ($hash->{BUSY} && $hash->{HttpUtils} && $hash->{HttpUtils}->{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&filter=| ) { Log(3, "$name: longpoll active, cutting it off now"); HttpUtils_Close($hash->{HttpUtils}); } #just return $val unteraltered $val to testmatrix
2025.07.24 21:06:40.791 4: MatrixTRBot: set will now set sendText -> testmatrix
2025.07.24 21:06:40.792 5: MatrixTRBot: AddToQueue adds type set01 to URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%, data { "msgtype": "m.text", "body": "$val" }, header Authorization: Bearer $sid application/json, retry 0, initial queue len: 0
2025.07.24 21:06:40.792 5: MatrixTRBot: HandleSendQueue called from AddToSendQueue, qlen = 1
2025.07.24 21:06:40.793 5: MatrixTRBot: HandleSendQueue - call with HTTP METHOD: POST
2025.07.24 21:06:40.793 5: MatrixTRBot: Replace called for type set01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: Authorization: Bearer $sid application/json
2025.07.24 21:06:40.794 5: MatrixTRBot: Replace called for type set01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: Authorization: Bearer $sid application/json
2025.07.24 21:06:40.794 5: MatrixTRBot: Replace called for type set01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: Authorization: Bearer $sid application/json
2025.07.24 21:06:40.794 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 21:06:40.795 5: MatrixTRBot: Replace called for type set01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: Authorization: Bearer $sid application/json
2025.07.24 21:06:40.795 5: MatrixTRBot: Replace called for type set01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: { "msgtype": "m.text", "body": "$val" }
2025.07.24 21:06:40.795 5: MatrixTRBot: Replace called for type set01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: { "msgtype": "m.text", "body": "$val" }
2025.07.24 21:06:40.796 5: MatrixTRBot: Replace called for type set01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: { "msgtype": "m.text", "body": "$val" }
2025.07.24 21:06:40.796 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 21:06:40.796 5: MatrixTRBot: Replace called for type set01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: { "msgtype": "m.text", "body": "$val" }
2025.07.24 21:06:40.797 5: MatrixTRBot: Replace called for type set01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%
2025.07.24 21:06:40.797 5: MatrixTRBot: Replace: match for type set01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value package main; my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");, input: https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%, result is https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=%%uuid%%
2025.07.24 21:06:40.797 5: MatrixTRBot: Replace called for type set01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=%%uuid%%
2025.07.24 21:06:40.798 5: MatrixTRBot: Replace: match for type set01, regex (?^:%%uuid%%), mode expression, value package main; join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))), input: https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%, result is https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=db50df5c-6d2b-b7b3-1da1-859c4803461f
2025.07.24 21:06:40.798 5: MatrixTRBot: Replace called for type set01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=db50df5c-6d2b-b7b3-1da1-859c4803461f
2025.07.24 21:06:40.798 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 21:06:40.798 5: MatrixTRBot: Replace called for type set01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=db50df5c-6d2b-b7b3-1da1-859c4803461f
2025.07.24 21:06:40.799 5: MatrixTRBot: no separator for multiple values (Context set01, 01)
2025.07.24 21:06:40.799 4: MatrixTRBot: HandleSendQueue sends set01 with timeout 65 to https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=db50df5c-6d2b-b7b3-1da1-859c4803461f, data: { "msgtype": "m.text", "body": "testmatrix" }, header: Authorization: Bearer $sid application/json
2025-07-24 21:06:40.808 HTTPMOD MatrixTRBot sendText testmatrix
2025.07.24 21:06:53.943 5: MatrixTRBot: ReadCallback called from __ANON__
2025.07.24 21:06:53.943 4: MatrixTRBot: Read callback: request type was set01 retry 0, header: HTTP/1.1 401 Unauthorized Server: openresty Date: Thu, 24 Jul 2025 19:06:47 GMT Content-Type: application/json Connection: close Cache-Control: no-cache, no-store, must-revalidate Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization, Date Access-Control-Expose-Headers: Synapse-Trace-Id, Server Strict-Transport-Security: max-age=31536000; includeSubdomains; preload, body length 88
2025.07.24 21:06:53.943 5: MatrixTRBot: Read callback: body {"errcode":"M_UNKNOWN_TOKEN","error":"Invalid access token passed.","soft_logout":false}
2025.07.24 21:06:53.943 4: MatrixTRBot: BodyDecode is decoding the response body as utf-8 but charset header is not found
2025.07.24 21:06:53.944 5: MatrixTRBot: GetCookies is looking for Cookies
2025.07.24 21:06:53.944 5: MatrixTRBot: ExtractSid called, context set, num 01
2025.07.24 21:06:53.944 4: MatrixTRBot: checking for redirects, code=401, ignore=0
2025.07.24 21:06:53.944 4: MatrixTRBot: no redirects to handle
2025.07.24 21:06:53.944 5: MatrixTRBot: Read callback sets LAST_REQUEST to set01
2025.07.24 21:06:53.945 5: MatrixTRBot: CheckAuth is checking buffer with ReAuthRegex (?^:M_UNKNOWN_TOKEN)
2025.07.24 21:06:53.945 4: MatrixTRBot: CheckAuth decided new authentication required
2025.07.24 21:06:53.945 5: MatrixTRBot: AddToQueue prepends type set01 to URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%, data { "msgtype": "m.text", "body": "$val" }, header Authorization: Bearer $sid application/json, retry 1, initial queue len: 0
2025.07.24 21:06:53.945 4: MatrixTRBot: CheckAuth prepended request set01 again before auth, retryCount 0 ...
2025.07.24 21:06:53.945 4: MatrixTRBot: DoAuth called with Steps: 01
2025.07.24 21:06:53.946 5: MatrixTRBot: AddToQueue prepends type auth01 to URL https://[$name:MatrixServer]/_matrix/client/v3/login, data { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "[$name:MatrixUser]" }, "password": "%%MatrixPassword%%" }, header application/json, retry 0, initial queue len: 1
2025.07.24 21:06:53.946 5: MatrixTRBot: HandleSendQueue called from DoAuth, qlen = 2
2025.07.24 21:06:53.946 5: MatrixTRBot: Replace called for type auth01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: application/json
2025.07.24 21:06:53.947 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: application/json
2025.07.24 21:06:53.947 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: application/json
2025.07.24 21:06:53.947 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 21:06:53.947 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: application/json
2025.07.24 21:06:53.948 5: MatrixTRBot: Replace called for type auth01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "[$name:MatrixUser]" }, "password": "%%MatrixPassword%%" }
2025.07.24 21:06:53.948 5: MatrixTRBot: Replace: match for type auth01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value package main; my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");, input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "[$name:MatrixUser]" }, "password": "%%MatrixPassword%%" }, result is { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "%%MatrixPassword%%" }
2025.07.24 21:06:53.948 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "%%MatrixPassword%%" }
2025.07.24 21:06:53.948 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "%%MatrixPassword%%" }
2025.07.24 21:06:53.948 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 21:06:53.949 5: MatrixTRBot: Replace: key MatrixPassword value is myPassWDMatrix
2025.07.24 21:06:53.949 5: MatrixTRBot: Replace: match for type auth01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword, input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "[$name:MatrixUser]" }, "password": "%%MatrixPassword%%" }, result is { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "myPassWDMatrix" }
2025.07.24 21:06:53.949 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "myPassWDMatrix" }
2025.07.24 21:06:53.949 5: MatrixTRBot: Replace called for type auth01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: https://[$name:MatrixServer]/_matrix/client/v3/login
2025.07.24 21:06:53.950 5: MatrixTRBot: Replace: match for type auth01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value package main; my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");, input: https://[$name:MatrixServer]/_matrix/client/v3/login, result is https://matrix.tchncs.de/_matrix/client/v3/login
2025.07.24 21:06:53.950 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: https://matrix.tchncs.de/_matrix/client/v3/login
2025.07.24 21:06:53.950 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: https://matrix.tchncs.de/_matrix/client/v3/login
2025.07.24 21:06:53.950 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 21:06:53.950 5: MatrixTRBot: Replace called for type auth01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: https://matrix.tchncs.de/_matrix/client/v3/login
2025.07.24 21:06:53.951 5: MatrixTRBot: no separator for multiple values (Context auth01, 01)
2025.07.24 21:06:53.951 4: MatrixTRBot: HandleSendQueue sends auth01 with timeout 65 to https://matrix.tchncs.de/_matrix/client/v3/login, data: { "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "fhemtrbotmatrix" }, "password": "myPassWDMatrix" }, header: application/json
2025.07.24 21:06:53.952 5: MatrixTRBot: StartQueueTimer called from HandleSendQueue sets internal timer to process queue in 1.000 seconds
2025.07.24 21:06:56.212 5: MatrixTRBot: HandleSendQueue called from Fhem internal timer, qlen = 1
2025.07.24 21:06:56.213 5: MatrixTRBot: StartQueueTimer called from ReadyForSending sets internal timer to process queue in 2.000 seconds, still waiting for reply to last request
2025.07.24 21:07:05.507 5: MatrixTRBot: ReadCallback called from __ANON__
2025.07.24 21:07:05.508 4: MatrixTRBot: Read callback: request type was auth01 retry 0, header: HTTP/1.1 200 OK Server: openresty Date: Thu, 24 Jul 2025 19:06:59 GMT Content-Type: application/json Connection: close Content-Encoding: gzip Cache-Control: no-cache, no-store, must-revalidate Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization, Date Access-Control-Expose-Headers: Synapse-Trace-Id, Server Permissions-Policy: interest-cohort=() X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block Referrer-Policy: no-referrer Content-Security-Policy: default-src 'self' *.tchncs.de tchncs.de Strict-Transport-Security: max-age=31536000; includeSubdomains; preload, body length 232
2025.07.24 21:07:05.514 5: MatrixTRBot: Read callback: body {"user_id":"@fhemtrbotmatrix:tchncs.de","access_token":"syt_ZmhlbXRyYm90bWF0cml4_GVMmwnokCWwJjAeuXkQa_1x4dDx","home_server":"tchncs.de","device_id":"MHZTCPFVYG","well_known":{"m.homeserver":{"base_url":"https://matrix.tchncs.de/"}}}
2025.07.24 21:07:05.515 4: MatrixTRBot: BodyDecode is decoding the response body as utf-8 but charset header is not found
2025.07.24 21:07:05.515 5: MatrixTRBot: GetCookies is looking for Cookies
2025.07.24 21:07:05.515 5: MatrixTRBot: ExtractSid called, context sid, num 01
2025.07.24 21:07:05.516 5: MatrixTRBot: ExtractSid could not match buffer to IdRegex (?^:"access_toke"\s*:\s*"([^"]+)")
2025.07.24 21:07:05.516 4: MatrixTRBot: checking for redirects, code=200, ignore=0
2025.07.24 21:07:05.516 4: MatrixTRBot: no redirects to handle
2025.07.24 21:07:05.517 5: MatrixTRBot: Read callback sets LAST_REQUEST to auth01
2025.07.24 21:07:06.555 5: MatrixTRBot: HandleSendQueue called from Fhem internal timer, qlen = 1
2025.07.24 21:07:06.556 5: MatrixTRBot: HandleSendQueue - call with HTTP METHOD: POST
2025.07.24 21:07:06.557 5: MatrixTRBot: Replace called for type set01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: Authorization: Bearer $sid application/json
2025.07.24 21:07:06.557 5: MatrixTRBot: Replace called for type set01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: Authorization: Bearer $sid application/json
2025.07.24 21:07:06.557 5: MatrixTRBot: Replace called for type set01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: Authorization: Bearer $sid application/json
2025.07.24 21:07:06.557 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 21:07:06.558 5: MatrixTRBot: Replace called for type set01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: Authorization: Bearer $sid application/json
2025.07.24 21:07:06.558 5: MatrixTRBot: Replace called for type set01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: { "msgtype": "m.text", "body": "$val" }
2025.07.24 21:07:06.559 5: MatrixTRBot: Replace called for type set01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: { "msgtype": "m.text", "body": "$val" }
2025.07.24 21:07:06.559 5: MatrixTRBot: Replace called for type set01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: { "msgtype": "m.text", "body": "$val" }
2025.07.24 21:07:06.559 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 21:07:06.559 5: MatrixTRBot: Replace called for type set01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: { "msgtype": "m.text", "body": "$val" }
2025.07.24 21:07:06.559 5: MatrixTRBot: Replace called for type set01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???"); input: https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%
2025.07.24 21:07:06.560 5: MatrixTRBot: Replace: match for type set01, regex (?^:\[([^:\s\[\"\']+):([^\]\s]+)\]), mode expression, value package main; my $device = $name if ($1 eq "\$name") // $1; ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");, input: https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%, result is https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=%%uuid%%
2025.07.24 21:07:06.560 5: MatrixTRBot: Replace called for type set01, regex (?^:%%uuid%%), mode expression, value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))) input: https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=%%uuid%%
2025.07.24 21:07:06.560 5: MatrixTRBot: Replace: match for type set01, regex (?^:%%uuid%%), mode expression, value package main; join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15)))), input: https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%, result is https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=ece21d38-071a-34e5-ba75-847bb625e773
2025.07.24 21:07:06.560 5: MatrixTRBot: Replace called for type set01, regex (?^:%%MatrixPassword%%), mode key, value MatrixPassword input: https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=ece21d38-071a-34e5-ba75-847bb625e773
2025.07.24 21:07:06.561 5: MatrixTRBot: ReadKeyValue tries to read value for MatrixPassword from file
2025.07.24 21:07:06.561 5: MatrixTRBot: Replace called for type set01, regex (?^:%%next_batch_param%%), mode expression, value #is there a reading 'next_batch'? my $val = ReadingsVal($name, 'next_batch', '???'); #return the GET parameter 'sync=value' for /sync Endpoint return "&since=$val" if ($val ne '???'); #return neither since-key nor value for it: return ""; input: https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=ece21d38-071a-34e5-ba75-847bb625e773
2025.07.24 21:07:06.561 5: MatrixTRBot: no separator for multiple values (Context set01, 01)
2025.07.24 21:07:06.561 4: MatrixTRBot: HandleSendQueue sends set01 with timeout 65 to https://matrix.tchncs.de/_matrix/client/v3/rooms/!XhbARBYHuWpihSbkCF:tchncs.de/send/m.room.message?txnId=ece21d38-071a-34e5-ba75-847bb625e773, data: { "msgtype": "m.text", "body": "testmatrix" }, header: Authorization: Bearer $sid application/json
2025.07.24 21:07:18.822 5: MatrixTRBot: ReadCallback called from __ANON__
2025.07.24 21:07:18.822 4: MatrixTRBot: Read callback: request type was set01 retry 1, header: HTTP/1.1 401 Unauthorized Server: openresty Date: Thu, 24 Jul 2025 19:07:11 GMT Content-Type: application/json Connection: close Cache-Control: no-cache, no-store, must-revalidate Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization, Date Access-Control-Expose-Headers: Synapse-Trace-Id, Server Strict-Transport-Security: max-age=31536000; includeSubdomains; preload, body length 88
2025.07.24 21:07:18.823 5: MatrixTRBot: Read callback: body {"errcode":"M_UNKNOWN_TOKEN","error":"Invalid access token passed.","soft_logout":false}
2025.07.24 21:07:18.823 4: MatrixTRBot: BodyDecode is decoding the response body as utf-8 but charset header is not found
2025.07.24 21:07:18.823 5: MatrixTRBot: GetCookies is looking for Cookies
2025.07.24 21:07:18.823 5: MatrixTRBot: ExtractSid called, context set, num 01
2025.07.24 21:07:18.824 4: MatrixTRBot: checking for redirects, code=401, ignore=0
2025.07.24 21:07:18.824 4: MatrixTRBot: no redirects to handle
2025.07.24 21:07:18.824 5: MatrixTRBot: Read callback sets LAST_REQUEST to set01
2025.07.24 21:07:18.824 5: MatrixTRBot: CheckAuth is checking buffer with ReAuthRegex (?^:M_UNKNOWN_TOKEN)
2025.07.24 21:07:18.825 4: MatrixTRBot: CheckAuth decided new authentication required
2025.07.24 21:07:18.825 4: MatrixTRBot: Authentication still required but no retries left - did last authentication fail?
2025.07.24 21:07:18.825 5: MatrixTRBot: ExtractReading sendText with regex /(?^:{\"event_id\":\"(.*)\"})/...
2025.07.24 21:07:18.826 5: MatrixTRBot: ExtractReading sendText did not match
2025.07.24 21:07:18.826 5: MatrixTRBot: UpdateReadingList created list of reading.* nums to parse during getUpdate as
2025.07.24 21:07:18.826 4: MatrixTRBot: Read response to set01 didn't match any Reading
2025.07.24 21:07:18.826 5: MatrixTRBot: calling parseFunction1 as handleAuthErrorsAusMyUtils via attr for HTTP Response to set01
2025.07.24 21:07:18.827 1: PERL ERROR: Undefined subroutine &HTTPMOD::handleAuthErrorsAusMyUtils called at ./FHEM/98_HTTPMOD.pm line 1469.
2025.07.24 21:07:18.827 1: stacktrace:
2025.07.24 21:07:18.827 1: main::__ANON__ called by ./FHEM/98_HTTPMOD.pm (1469)
2025.07.24 21:07:18.827 1: (eval) called by ./FHEM/98_HTTPMOD.pm (1469)
2025.07.24 21:07:18.827 1: HTTPMOD::HandleParseFunctions called by ./FHEM/98_HTTPMOD.pm (2529)
2025.07.24 21:07:18.828 1: HTTPMOD::ReadCallback called by FHEM/HttpUtils.pm (756)
2025.07.24 21:07:18.828 1: main::__ANON__ called by fhem.pl (786)
2025.07.24 21:07:18.828 3: MatrixTRBot: error calling handleAuthErrorsAusMyUtils: Undefined subroutine &HTTPMOD::handleAuthErrorsAusMyUtils called at ./FHEM/98_HTTPMOD.pm line 1469.
2025.07.24 21:07:18.828 5: MatrixTRBot: HandleSendQueue called from ReadCallback, qlen = 0
2025.07.24 21:07:18.828 5: MatrixTRBot: HandleSendQueue found no usable entry in queue
Ok, ich habe mal einen Testaccount bei Tchncs.de gemacht und das Device bei Cooltux angelegt:
Dort (https://demo-fhem.cooltux.net/fhem?detail=MatrixBot) klappt es mit dieser Defintion:
define MatrixBot HTTPMOD none 0
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser
attr MatrixBot MatrixRoomID !dhGzyuDtJxMUAguhhc:tchncs.de
attr MatrixBot MatrixServer tchncs.de
attr MatrixBot MatrixUser fhemtest
attr MatrixBot bodyDecode utf-8
attr MatrixBot bodyEncode utf-8
attr MatrixBot comment "\
Create a room for FHEM.\
\
The room must not use encryption, a room that has encryption\
enabled, cannot be converted to a non-encrypted room anymore \
\
The room-id can be found in Element-Web at:\
Room Settings --> Advanced --> Internal room ID\
\
To store the password in FHEM in obfuscated way:\
set MatrixBot storeKeyValue MatrixPassword yourPassword123\
\
###\
To send a text:\
set MatrixBot sendText Bla Bla Bla\
\
\
###\
To longpoll for messages once:\
# 1. send special filter to Matrix:\
set MatrixBot sendFilter\
\
# 2. start one longPoll (waits up to 60 seconds\
# or until data is available)\
get MatrixBot longpoll\
\
#alternatively, to keep on longPolling (this also sets filter):\
set MatrixBot longpollCmd startTimer\
#to stop the timers:\
set MatrixBot longpollCmd stopTimer\
\
#############################################################\
https://spec.matrix.org/v1.14/client-server-api/#syncing\
"
attr MatrixBot get02AlwaysNum 0
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot get02Name longpoll
attr MatrixBot get02Regex \"next_batch\":\s*\"(?<next_batch>[^\"]+)\"(?:.*?\"timeline\":\s*{\s*\"events\":\s*(?<messages>\[.*?\])\s*)?
attr MatrixBot get02TextArg 0
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&filter=[$name:filter_id]%%next_batch_param%%
attr MatrixBot icon message_info
attr MatrixBot parseFunction1 handleAuthErrors
attr MatrixBot reAuthAlways 0
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN
attr MatrixBot replacement01Mode expression
attr MatrixBot replacement01Regex \[([^:\s\[\"\']+):([^\]\s]+)\]
attr MatrixBot replacement01Value my $device = $name if ($1 eq "\$name") // $1;;\
ReadingsVal($device, $2, undef) or AttrVal($device, $2, "???");;
attr MatrixBot replacement02Mode expression
attr MatrixBot replacement02Regex %%uuid%%
attr MatrixBot replacement02Value join("-", unpack("A8 A4 A4 A4 A12", unpack("H*", join("", map { chr(int rand 256) } 0..15))))
attr MatrixBot replacement03Mode key
attr MatrixBot replacement03Regex %%MatrixPassword%%
attr MatrixBot replacement03Value MatrixPassword
attr MatrixBot replacement04Mode expression
attr MatrixBot replacement04Regex %%next_batch_param%%
attr MatrixBot replacement04Value #is there a reading 'next_batch'?\
my $val = ReadingsVal($name, 'next_batch', '???');;\
\
#return the GET parameter 'sync=value' for /sync Endpoint\
return "&since=$val" if ($val ne '???');;\
\
#return neither since-key nor value for it:\
return "";;
attr MatrixBot set01Data {\
"msgtype": "m.text",\
"body": "$val"\
}
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot set01HeaderContent-Type application/json
attr MatrixBot set01IExpr #cancel an active longpoll\
if ($hash->{BUSY}\
&& $hash->{HttpUtils}\
&& $hash->{HttpUtils}->{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&filter=| ) {\
Log(3, "$name: longpoll active, cutting it off now");;\
HttpUtils_Close($hash->{HttpUtils});;\
}\
\
#just return $val unteraltered\
$val
attr MatrixBot set01Method POST
attr MatrixBot set01Name sendText
attr MatrixBot set01ParseResponse 1
attr MatrixBot set01Regex {\"event_id\":\"(.*)\"}
attr MatrixBot set01TextArg 1
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%
attr MatrixBot set02Data {\
"room": {\
"rooms": ["[$name:MatrixRoomID]"],\
"timeline": {\
"limit": 10,\
"types": ["m.room.message"]\
},\
"include_leave": false,\
"include_join": false,\
"include_account_data": false,\
"include_state": false,\
"state": {\
"types": []\
},\
"ephemeral": {\
"types": []\
},\
"account_data": {\
"types": []\
}\
},\
"event_fields": [\
"content.body",\
"sender",\
"origin_server_ts"\
],\
"event_format": "client",\
"presence": {\
"types": [],\
"not_types": ["*"]\
},\
"account_data": {\
"types": [],\
"not_types": ["*"]\
}\
}
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid
attr MatrixBot set02HeaderContent-Type application/json
attr MatrixBot set02Method POST
attr MatrixBot set02Name sendFilter
attr MatrixBot set02NoArg 1
attr MatrixBot set02ParseResponse 1
attr MatrixBot set02Regex \"filter_id\":\s*\"(?<filter_id>\d+)\"
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter
attr MatrixBot set03Local 1
attr MatrixBot set03Name longpollCmd
attr MatrixBot set03TextArg 1
attr MatrixBot showError 1
attr MatrixBot sid01Data {\
"type": "m.login.password",\
"identifier": {\
"type": "m.id.user",\
"user": "[$name:MatrixUser]"\
},\
"password": "%%MatrixPassword%%"\
}
attr MatrixBot sid01HeaderContent-Type application/json
attr MatrixBot sid01IdRegex "access_token"\s*:\s*"([^"]+)"
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login
attr MatrixBot timeout 65
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR|sendText):.* {\
my $longpollCmd = ReadingsVal($name, 'longpollCmd', '???');;\
my $delay = 1;;\
my $timeout = AttrVal($name, 'timeout', 61) + $delay + 5;;\
\
# stop our timers:\
if ($longpollCmd eq "stopTimer") {\
fhem("cancel ${name}_longpollTimer quiet");;\
fhem("cancel ${name}_longpollTimer2 quiet");;\
return "stopped";;\
}\
\
if ($longpollCmd ne "startTimer") {\
return "no timer set, longpollCmd is not set to 'startTimer'";;\
}\
\
#if startTimer cmd was given now, set filter as well:\
if (ReadingsAge($name, 'longpollCmd', 0) <= 1) {\
$delay = 5;; #delay to allow for sendFilter to be answered\
#Log(1, "🪲 $name: >>". InternalVal($name, 'httpbody', '???') ."<<");;\
fhem("sleep 0.1 quiet;; set $name sendFilter");;\
}\
\
#we handle an error reported by http-utils:\
if (ReadingsAge($name, 'LAST_ERROR', 0) <= 1) {\
my $last_error = ReadingsVal($name, 'LAST_ERROR', '???');;\
$delay = 10;; #delay to allow for error reasons to improve\
#Log(1, "🪲 $name: Dealing with error: >>$last_error<<");;\
}\
\
# for testing this: { $defs{MatrixBot}{sid} = 'bla' }\
if (!defined &HTTPMOD::handleAuthErrors) {\
*HTTPMOD::handleAuthErrors = sub {\
my ($hash, $header, $body, $request) = @_;;\
my $name = $hash->{NAME};;\
my $status;;\
\
if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {\
$status = $1;;\
}\
\
Log3($name, 4, "$name: HTTP status code is $status");;\
\
if ( $status == 401 || $status == 403 || $status == 500 ) {\
Log3($name, 3, "$name: auth-error or servererror ($status), calling doAuth()");;\
HTTPMOD::DoAuth($hash);;\
}\
};;\
}\
\
#set timers, one regular and one fallback:\
fhem("sleep $delay ${name}_longpollTimer quiet;; get $name longpoll");;\
fhem("sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer");;\
\
return strftime("next longpoll at %H:%M:%S", localtime( time()+$delay ));;\
},\
messages_list:messages:.* {\
my $this = ReadingsVal($name, $reading, '');;\
my @timestampArray = split("\n", $this);;\
my $messages_ref = decode_json(ReadingsVal($name, 'messages', ''));;\
my $length = 20;;\
\
my %seen_messages = map { $_ => 1 } @timestampArray;;\
\
foreach my $msg (@$messages_ref) {\
my $val = $msg->{content}{body};;\
$val = Encode::decode('utf-8', $val) unless Encode::is_utf8($val);;\
$val =~ s/\n/ /g;; #replace newlines with spaces\
\
my $sender = $msg->{sender};;\
my $ts = strftime("%Y-%m-%d %H:%M:%S", localtime($msg->{origin_server_ts} / 1000));;\
my $new_entry = "$ts: $sender: $val";;\
\
next if $seen_messages{$new_entry};;\
\
my $inserted = 0;;\
for (my $i = 0;; $i < @timestampArray;; $i++) {\
my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\
if ($ts lt $existing_ts) {\
splice(@timestampArray, $i, 0, $new_entry);;\
$inserted = 1;;\
last;;\
}\
}\
push(@timestampArray, encode('utf-8', $new_entry)) unless $inserted;;\
shift(@timestampArray) while @timestampArray > $length;;\
}\
\
return join("\n", @timestampArray);;\
},\
process_messages:messages:.* {\
my $val = ReadingsVal($name, 'messages_list', '');;\
my $this = ReadingsVal($name, $reading, '');;\
my $latest_ts = $this;;\
\
foreach my $line (split(/\n/, $val)) {\
if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\
my ($ts, $msg) = ($1, $2);;\
\
if ($ts gt $this) {\
fhem("trigger $name msg: $line");;\
$latest_ts = $ts if ($ts gt $latest_ts);;\
}\
}\
}\
return $latest_ts;;\
}
attr MatrixBot verbose 5
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer
# BUSY 1
# CFGFN
# DEF none 0
# FUUID 68828de5-f33f-124a-2018-1a84a2796f2ab96a
# Interval 0
# LastAuthTry 2025-07-24 22:03:12
# MainURL
# ModuleVersion 4.2.0 - 11.8.2023
# NAME MatrixBot
# NOTIFYDEV global
# NR 96
# NTFY_ORDER 50-MatrixBot
# STATE ???
# TYPE HTTPMOD
# eventCount 56
# sid syt_ZmhlbXRlc3Q_NulTSMihfMALhjZObAkG_4EJI8o
# value
# CompiledRegexes:
# HttpUtils:
# NAME
# addr https://tchncs.de:443
# auth 0
# code 200
# compress 1
# conn
# data
# displayurl https://tchncs.de/_matrix/client/v3/sync?timeout=60000&filter=1&since=s689280504_2010597770_83218_517727981_91772009_15429834_24008571_289469634_0_40109
# header Authorization: Bearer syt_ZmhlbXRlc3Q_NulTSMihfMALhjZObAkG_4EJI8o
# host tchncs.de
# httpheader HTTP/1.1 200 OK
#Server: nginx
#Date: Thu, 24 Jul 2025 20:06:08 GMT
#Content-Type: application/json
#Connection: close
#Vary: Accept-Encoding
#Vary: Accept-Encoding
#Cache-Control: no-cache, no-store, must-revalidate
#Access-Control-Allow-Origin: *
#Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, OPTIONS
#Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization, Date
#Access-Control-Expose-Headers: Synapse-Trace-Id, Server
#Permissions-Policy: interest-cohort=()
#X-Content-Type-Options: nosniff
#X-Frame-Options: SAMEORIGIN
#X-XSS-Protection: 1; mode=block
#Referrer-Policy: no-referrer
#Content-Security-Policy: default-src 'self' *.tchncs.de tchncs.de
#Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
#Permissions-Policy: interest-cohort=()
#Content-Security-Policy: default-src https: 'unsafe-inline' 'unsafe-eval' 'self' data: *.tchncs.de
#Referrer-Policy: strict-origin
# httpversion 1.0
# hu_blocking 0
# hu_filecount 1
# hu_port 443
# hu_portSfx
# ignoreredirects 1
# loglevel 4
# path /_matrix/client/v3/sync?timeout=60000&filter=1&since=s689280504_2010597770_83218_517727981_91772009_15429834_24008571_289469634_0_40109
# protocol https
# redirects 0
# timeout 65
# url https://tchncs.de/_matrix/client/v3/sync?timeout=60000&filter=1&since=s689280504_2010597770_83218_517727981_91772009_15429834_24008571_289469634_0_40109
# sslargs:
# QUEUE:
# READINGS:
# 2025-07-24 22:04:13 filter_id 1
# 2025-07-24 22:04:13 longpollCmd startTimer
# 2025-07-24 22:06:08 longpollTimer next longpoll at 22:06:18
# 2025-07-24 22:06:08 messages [{"content":{"body":"Test um 22:06"},"sender":"@fhemtest:tchncs.de","origin_server_ts":1753387568313}]
# 2025-07-24 22:06:08 messages_list 2025-07-24 21:49:19: @fhemtest:tchncs.de: dsfsdf
#2025-07-24 21:49:44: @fhemtest:tchncs.de: und so?
#2025-07-24 21:50:24: @fhemtest:tchncs.de: mein test
#2025-07-24 21:51:10: @fhemtest:tchncs.de: únd noch einer
#2025-07-24 21:54:24: @fhemtest:tchncs.de: sfsdf
#2025-07-24 21:54:33: @fhemtest:tchncs.de: vvbh
#2025-07-24 21:58:13: @fhemtest:tchncs.de: $daeq93aPMPPCwaPwExtfGI6mzacqafwcl1JeIzTwhfo
#2025-07-24 22:01:52: @fhemtest:tchncs.de: cxvxcv
#2025-07-24 22:04:25: @fhemtest:tchncs.de: $L8PfwFSk7Zq3egXhWQglGUVlVyGC_OvMXHIarqf_GQY
#2025-07-24 22:04:37: @fhemtest:tchncs.de: fghfgh
#2025-07-24 22:06:08: @fhemtest:tchncs.de: Test um 22:06
# 2025-07-24 22:06:08 next_batch s689280916_2010597770_83422_517728357_91772135_15429836_24008575_289469676_0_40109
# 2025-07-24 22:06:08 process_messages 2025-07-24 22:06:08
# 2025-07-24 22:04:25 sendText $7htR9P3PCZJqZ4-P0Vkzrz7h5qrrwkKWDDYa-t8uFxo
# REQUEST:
# context get
# data
# header Authorization: Bearer $sid
# ignoreredirects 0
# num 02
# retryCount 0
# type get02
# url https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&filter=[$name:filter_id]%%next_batch_param%%
# value
# defptr:
# readingBase:
# filter_id set
# messages get
# next_batch get
# sendText set
# readingNum:
# filter_id 02
# messages 02
# next_batch 02
# sendText 01
# readingOutdated:
# requestReadings:
# get02:
# messages get 02
# next_batch get 02
# set01:
# sendText set 01
# set02:
# filter_id set 02
#
setstate MatrixBot 2025-07-24 22:04:13 filter_id 1
setstate MatrixBot 2025-07-24 22:04:13 longpollCmd startTimer
setstate MatrixBot 2025-07-24 22:06:08 longpollTimer next longpoll at 22:06:18
setstate MatrixBot 2025-07-24 22:06:08 messages [{"content":{"body":"Test um 22:06"},"sender":"@fhemtest:tchncs.de","origin_server_ts":1753387568313}]
setstate MatrixBot 2025-07-24 22:06:08 messages_list 2025-07-24 21:49:19: @fhemtest:tchncs.de: dsfsdf\
2025-07-24 21:49:44: @fhemtest:tchncs.de: und so?\
2025-07-24 21:50:24: @fhemtest:tchncs.de: mein test\
2025-07-24 21:51:10: @fhemtest:tchncs.de: únd noch einer\
2025-07-24 21:54:24: @fhemtest:tchncs.de: sfsdf\
2025-07-24 21:54:33: @fhemtest:tchncs.de: vvbh\
2025-07-24 21:58:13: @fhemtest:tchncs.de: $daeq93aPMPPCwaPwExtfGI6mzacqafwcl1JeIzTwhfo\
2025-07-24 22:01:52: @fhemtest:tchncs.de: cxvxcv\
2025-07-24 22:04:25: @fhemtest:tchncs.de: $L8PfwFSk7Zq3egXhWQglGUVlVyGC_OvMXHIarqf_GQY\
2025-07-24 22:04:37: @fhemtest:tchncs.de: fghfgh\
2025-07-24 22:06:08: @fhemtest:tchncs.de: Test um 22:06
setstate MatrixBot 2025-07-24 22:06:08 next_batch s689280916_2010597770_83422_517728357_91772135_15429836_24008575_289469676_0_40109
setstate MatrixBot 2025-07-24 22:06:08 process_messages 2025-07-24 22:06:08
setstate MatrixBot 2025-07-24 22:04:25 sendText $7htR9P3PCZJqZ4-P0Vkzrz7h5qrrwkKWDDYa-t8uFxo
So sieht es dann aus:
Screenshot_2025-07-24_22-07-21.pngScreenshot_2025-07-24_22-08-08.png
Es ist als Server also doch "tchncs.de" und nicht "matrix.tchncs.de". Kannst du das Device nochmal ganz löschen und mit der Definition neu erzeugen und auf deinen Nutzer anpassen bitte.
Dein Perl,Betriebsystem und FHEM ist aktuell?
P.S.: Das Testkonto habe ich gerade wieder gelöscht, also nicht wundern wenn es jetzt nicht mehr bei Cooltux klappt.
ok, danke, muss ich mal probieren. war paar tage weg.
VG
bezüglich fhem: Fhem installer sagt alles ok
Readings
cpanminusVersion
1.7046
2025-07-22 02:03:30
outdatedPerl
check completed
2025-07-29 02:01:55
perlVersion
5.034000
2025-07-22 02:03:30
state
up to date
2025-07-29 02:01:55
updatesAvailablePerl
0
2025-07-22 02:03:50
ich bekomme es nicht hin.
eventuell an meinem Environemnt was schief.
2025.07.29 16:17:53.539 4: MatrixBot: Read callback: request type was set01 retry 1, header: HTTP/1.1 401 Unauthorized Server: nginx Date: Tue, 29 Jul 2025 14:17:51 GMT Content-Type: application/json Connection: close Cache-Control: no-cache, no-store, must-revalidate Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization, Date Access-Control-Expose-Headers: Synapse-Trace-Id, Server Strict-Transport-Security: max-age=31536000; includeSubdomains; preload, body length 88
2025.07.29 16:17:53.539 5: MatrixBot: Read callback: body {"errcode":"M_UNKNOWN_TOKEN","error":"Invalid access token passed.","soft_logout":false}
hatte das deive gelöscht und neu angelegt.
Merkwürdigerweise geht die Funktion:
{SendMatrixMessage("testtmatrix99fkt")}
Fhem scheint komplett aktuell zu sein.
VG