Dieser Codeschnipsel ermöglicht es eine oder auch mehrere Dateien von Webservern herunterzuladen und lokal im Dateisystem zu speichern. Wenn ein Download noch aktiv ist, während ein weiterer hinzukommt, werden solche Aufträge im Reading "backlog" vermerkt und nacheinander abgearbeitet.
In diesem Snippet ist ein Link (das Ordner-Emoji), wenn man es anklickt werden mehrere Firmwaredateien in den FhemWeb-Ordner heruntergeladen. Dafür wird FW_cmd() genutzt und auch ganz nützlich ist IMHO die Methode mit der man mehrere FHEM-Befehle auf diese Art von Javascript an FHEM übergeben kann.
Zur Nutzung:
- set MyDownloader job Filepath="/opt/fhem/www/tasmota.bin" https://ota.tasmota.com/tasmota/release/tasmota.bin; Lädt die Ressource von einer Webseite herunter und speichert an dem Ort auf den Filepath verweist.
- set MyDownloader job Dateipfad="/opt/fhem/www/tasmota.bin" https://ota.tasmota.com/tasmota/release/tasmota.bin;Das Gleiche mit deutschem Wort für den Dateipfad...
- set MyDownloader job https://ota.tasmota.com/tasmota/release/tasmota.bin;Gibt man keinen Dateipfad an, wird das Attribut "Filepath" als Speicherort genutzt. Der Standardwert ist "/opt/fhem/www/download.bin"
Wie bei mir üblich, ohne viel Extra-Devices, alles schön kompakt beisammen in diesem Snippet:
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)";;\
}