Hallo zusammen,
ich möchte in FHEM einzelne Aktionen über einfache HTTP-Aufrufe (Webhooks) auslösen können.
Will man die Hooks aufrufen und damit den den dahinterliegenden Befehl anstoßen, reicht das Ansurfen von Links mit einem Browser, Apple-Kurzbefehlen, Shelly-URL-Aufrufen etc. Es wird kein CSRF-Token verlangt, dafür sind die Befehle aber auch genau definiert und beschränkt.
Die Befehle und die Schlüsselwörter kann man einfach in dem Hash-Code einfügen. Hinweis: Wenn man mit
qq() escaped, gibt es weniger Verwirrung mit den Anführungszeichen wie " oder '.
- https://192.168.1.123/fhem/webhook/index.html?cm=bla
- https://192.168.1.123/fhem/webhook/index.html?cm=blubb
- https://192.168.1.123/fhem/webhook/index.html?cm=parat_P&pm=555
Mit HTTPSRV kann man dies wie folgt realisieren:
defmod webhook.device HTTPSRV webhook /opt/fhem/www/webhook webhook
attr webhook.device userattr readings
attr webhook.device readings cm,pm
attr webhook.device userReadings state:(cm|pm):.* {\
my $hash = $defs{$name};;\
my $cm = ReadingsVal($name, 'cm', '???');; #command\
my $pm = ReadingsVal($name, 'pm', '???');; #parameter\
$cm = ($cm =~ s/[^a-zA-Z0-9\?_-]/_/rg);; #sanitize $cm\
$pm = ($pm =~ s/[^a-zA-Z0-9_-]/_/rg);; #sanitize $pm\
\
#List of commands this webhook understands:\
my %commands = (\
'???' => qq({ Log(1, "$name: could not get reading cm") }),\
'bla' => qq(setreading $name irgendwas 123),\
'blubb' => qq(setreading $name irgendwas 456),\
'parat_P' => qq(setreading $name irgendwas $pm)\
);;\
return 'cmd not found in hash %commands' if not exists $commands{$cm};;\
\
#special handling for command with parameter ending with "_P"\
if($cm =~ /_P$/ and "$pm" eq "---") {\
#wait for second parameter to be set\
return;;\
}\
readingsBulkUpdate($hash, 'pm', '---') if "$pm" ne "---";;\
\
#commands cannot simply be executed from userreading,\
#so add a 100ms sleep and then execute cmd\
fhem('sleep 0.1;; '.$commands{$cm});;\
return "OK: $cm";;\
}
Diese Datei muss an den Ort "/opt/fhem/www/webhook/index.html" und kann angepasst werden:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webhook Commands</title>
<style>
/* Basic styling for the page */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f4f7fc;
}
h1 {
text-align: center;
color: #333;
}
.command-list {
list-style-type: none;
padding: 0;
margin: 20px 0;
}
.command-list li {
background-color: #ffffff;
border: 1px solid #ddd;
margin-bottom: 10px;
padding: 15px;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.command-list strong {
font-size: 1.1em;
color: #333;
}
.command-button {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
}
.command-button:hover {
background-color: #45a049;
}
.param-input-container {
display: flex;
align-items: center;
}
.param-input-container label {
margin-right: 10px;
font-size: 1em;
}
.param-input-container input {
padding: 8px;
font-size: 1em;
margin-right: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.param-input-container button {
background-color: #008CBA;
color: white;
padding: 8px 15px;
border: none;
border-radius: 5px;
cursor: pointer;
}
.param-input-container button:hover {
background-color: #007B8A;
}
.alert {
margin-top: 20px;
padding: 10px;
background-color: #f44336;
color: white;
border-radius: 5px;
display: none;
}
.alert.success {
background-color: #4CAF50;
}
</style>
<script>
// Function to call webhook for commands ending in '_P' with the parameter from input
async function callWebhook(command) {
let paramValue = document.getElementById("paramInput").value;
let alertBox = document.getElementById("alertBox");
// If the command ends with '_P', include the parameter
if (command.endsWith('_P')) {
if (!paramValue) {
alertBox.textContent = "Please enter a parameter value.";
alertBox.classList.remove('success');
alertBox.classList.add('error');
alertBox.style.display = "block";
return;
}
command += "&pm=" + paramValue; // Add the pm parameter
}
// Construct the full URL
const url = "/fhem/webhook/index.html?cm=" + command;
try {
// Using fetch to make the request without reloading the page
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
// Check if the request was successful
if (response.ok) {
const result = await response.text(); // You can handle the response data here
alertBox.textContent = "Webhook called successfully!";
alertBox.classList.add('success');
alertBox.classList.remove('error');
alertBox.style.display = "block";
} else {
alertBox.textContent = "Failed to call webhook.";
alertBox.classList.remove('success');
alertBox.classList.add('error');
alertBox.style.display = "block";
}
} catch (error) {
console.error("Error in fetch request:", error);
alertBox.textContent = "An error occurred.";
alertBox.classList.remove('success');
alertBox.classList.add('error');
alertBox.style.display = "block";
}
}
</script>
</head>
<body>
<h1>Webhook Command List</h1>
<div id="alertBox" class="alert"></div>
<ul class="command-list">
<li>
<div>
<strong>???</strong> - Command: Log(1, "$name: could not get reading cm")
</div>
<button class="command-button" onclick="callWebhook('???')">Call Webhook</button>
</li>
<li>
<div>
<strong>bla</strong> - Command: setreading $name irgendwas 123
</div>
<button class="command-button" onclick="callWebhook('bla')">Call Webhook</button>
</li>
<li>
<div>
<strong>blubb</strong> - Command: setreading $name irgendwas 456
</div>
<button class="command-button" onclick="callWebhook('blubb')">Call Webhook</button>
</li>
<li>
<div>
<strong>parat_P</strong> - Command: setreading $name irgendwas $pm
</div>
<div class="param-input-container">
<label for="paramInput">Parameter:</label>
<input type="text" id="paramInput" name="paramInput" placeholder="Enter parameter value">
<button class="command-button" onclick="callWebhook('parat_P')">Call Webhook</button>
</div>
</li>
</ul>
</body>
</html>
So sieht es dann aus:
Screenshot_2024-12-23_22-06-12.png