📂 Downloader für Dateien, HttpUtils_NonblockingGet() und FileWrite() ohne NL

Begonnen von Torxgewinde, 19 Dezember 2025, 10:30:02

Vorheriges Thema - Nächstes Thema

Torxgewinde

defmod MyDownloader dummy
attr MyDownloader userattr Filepath
attr MyDownloader devStateIcon {\
#Lade mehrere Dinge herunter, füllt ggf. das Backlog:\
my $cmd = <<"CMD";;\
set $name job Filepath="/opt/fhem/www/tasmota.bin" https://ota.tasmota.com/tasmota/release/tasmota.bin;;\
set $name job Filepath="/opt/fhem/www/tasmota.bin.gz" https://ota.tasmota.com/tasmota/release/tasmota.bin.gz;;\
set $name job Filepath="/opt/fhem/www/tasmota-minimal.bin.gz" https://ota.tasmota.com/tasmota/release/tasmota-minimal.bin.gz;;\
set $name job Filepath="/opt/fhem/www/tasmota32-zbbrdgpro.bin" https://ota.tasmota.com/tasmota32/release/tasmota32-zbbrdgpro.bin\
CMD\
\
#verpacke das ganze sicher für den Transport als BASE64 und als RCE payload:\
$cmd = "{fhem(MIME::Base64::decode_base64('". encode_base64($cmd,'') ."'))}";;\
$cmd = urlEncode($cmd);;\
\
#Mit JS wird verhindert, dass der Browser navigiert:\
my $link = "<a href=\"#\" onclick=\"".\
           "FW_cmd(FW_root + '?XHR=1&cmd=${cmd}', function(data){log('📂: ' + data)});; return false;;".\
           "\">📂</a>";;\
\
return "<div>". ReadingsVal($name, "result", "???") ." $link</div>";;\
}
attr MyDownloader readingList job
attr MyDownloader setList job
attr MyDownloader userReadings result:job:.* {\
my $hash = $defs{$name};;\
my $job = ReadingsVal($name, "job", 'https://fhem.de/www/images/default/fhemicon.png');;\
my $path = AttrVal($name, "Filepath", "/opt/fhem/www/download.bin");;\
\
if ($job =~ s/(?:Filepath|Dateipfad)="(.*?)"//) {\
$path = $1;;\
}\
\
# Entferne Whitespaces:\
$job =~ s/^\s+|\s+$//g;;\
\
# Wenn busy aktiv ist, dann den Job ins Backlog schieben:\
if (ReadingsVal($name, "busy", 0) != 0) {\
my $backlog = ReadingsVal($name, "backlog", "");;\
$backlog .= "Filepath=\"${path}\" ${job}\n";;\
readingsBulkUpdate($hash, "backlog", $backlog, 1);;\
return "queued job ($job)";;\
}\
\
# ab hier sind wir busy mit dem request:\
readingsBulkUpdate($hash, "busy", 1, 1);;\
\
# definiere Callback:\
my $fnFct = sub {\
my ($param, $err, $data) = @_;;\
\
my $len = $data ? length($data) : 0;;\
my $fw_hash = {FileName => $path, ForceType => "file", NoNL => 1};;\
my $error = $err ? $err : FileWrite($fw_hash, $data);;\
\
readingsBeginUpdate($hash);;\
readingsBulkUpdate($hash, "result", "saved $len Bytes to $path", 1) if !$error;;\
readingsBulkUpdate($hash, "result", "ERROR ($error|$err|$path|$job)", 1) if $error;;\
\
# prüfe das Backlog:\
my $backlog = ReadingsVal($name, "backlog", "");;\
my @lines = grep { /\S/ } split(/\r?\n/, $backlog);;\
\
if (@lines) {\
my $next = shift @lines;;  # ersten Job holen\
my $newbacklog = join("\n", @lines);;\
readingsBulkUpdate($hash, "backlog", $newbacklog, 1);;\
\
# Nächsten Job starten wenn diese Funktion endet,\
# deswegen über "@cmdList","%sleepers" in fhem.pl\
fhem("sleep 0.0;; set $name job $next");;\
}\
\
readingsBulkUpdate($hash, "busy", 0, 1);;\
readingsEndUpdate($hash, 1);;\
};;\
\
# prepare and call HttpUtils:\
my $param = {\
url        => $job,\
timeout    => 10,\
hash       => $hash,\
method     => "GET",\
callback   => $fnFct\
};;\
HttpUtils_NonblockingGet($param);;\
\
return "download job started ($job)";;\
}