first rewrite part
This commit is contained in:
parent
8dfa21a773
commit
9e3bbcd38f
@ -2,7 +2,7 @@
|
||||
#
|
||||
# Developed with Kate
|
||||
#
|
||||
# (c) 2020-2021 Copyright: Marko Oldenburg (fhemdevelopment@cooltux.net)
|
||||
# (c) 2020-2021 Copyright: Marko Oldenburg (fhemdevelopment@cooltux.net)
|
||||
# All rights reserved
|
||||
#
|
||||
# Special thanks goes to:
|
||||
@ -181,9 +181,9 @@ sub backupToStorage_Initialize {
|
||||
],
|
||||
"release_status": "devepolment",
|
||||
"license": "GPL_2",
|
||||
"version": "v1.3.1",
|
||||
"version": "v1.4.0",
|
||||
"author": [
|
||||
"Marko Oldenburg <fhemsupport@cooltux.net>"
|
||||
"Marko Oldenburg <fhemdevelopment@cooltux.net>"
|
||||
],
|
||||
"x_fhem_maintainer": [
|
||||
"CoolTux"
|
||||
|
@ -1,2 +1,2 @@
|
||||
UPD 2021-11-09_13:08:21 6508 FHEM/98_backupToStorage.pm
|
||||
UPD 2021-11-09_13:55:09 24286 lib/FHEM/Services/backupToStorage.pm
|
||||
UPD 2022-03-10_06:48:27 6511 FHEM/98_backupToStorage.pm
|
||||
UPD 2022-03-10_08:41:03 23352 lib/FHEM/Services/backupToStorage.pm
|
||||
|
@ -1,8 +1,8 @@
|
||||
###############################################################################
|
||||
#
|
||||
# Developed with Kate
|
||||
# Developed with VSCodium and richterger perl plugin
|
||||
#
|
||||
# (c) 2020-2021 Copyright: Marko Oldenburg (fhemdevelopment@cooltux.net)
|
||||
# (c) 2020-2022 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net)
|
||||
# All rights reserved
|
||||
#
|
||||
# Special thanks goes to:
|
||||
@ -37,7 +37,15 @@ use utf8;
|
||||
|
||||
use GPUtils qw(GP_Import);
|
||||
|
||||
use Data::Dumper; #only for Debugging
|
||||
BEGIN {
|
||||
|
||||
# Import from main context
|
||||
GP_Import(
|
||||
qw( init_done
|
||||
defs
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
# try to use JSON::MaybeXS wrapper
|
||||
# for chance of better performance + open code
|
||||
@ -45,15 +53,11 @@ eval {
|
||||
require JSON::MaybeXS;
|
||||
import JSON::MaybeXS qw( decode_json encode_json );
|
||||
1;
|
||||
};
|
||||
|
||||
if ($@) {
|
||||
$@ = undef;
|
||||
} or do {
|
||||
|
||||
# try to use JSON wrapper
|
||||
# for chance of better performance
|
||||
eval {
|
||||
|
||||
# JSON preference order
|
||||
local $ENV{PERL_JSON_BACKEND} =
|
||||
'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP'
|
||||
@ -62,10 +66,7 @@ if ($@) {
|
||||
require JSON;
|
||||
import JSON qw( decode_json encode_json );
|
||||
1;
|
||||
};
|
||||
|
||||
if ($@) {
|
||||
$@ = undef;
|
||||
} or do {
|
||||
|
||||
# In rare cases, Cpanel::JSON::XS may
|
||||
# be installed but JSON|JSON::MaybeXS not ...
|
||||
@ -73,10 +74,7 @@ if ($@) {
|
||||
require Cpanel::JSON::XS;
|
||||
import Cpanel::JSON::XS qw(decode_json encode_json);
|
||||
1;
|
||||
};
|
||||
|
||||
if ($@) {
|
||||
$@ = undef;
|
||||
} or do {
|
||||
|
||||
# In rare cases, JSON::XS may
|
||||
# be installed but JSON not ...
|
||||
@ -84,10 +82,7 @@ if ($@) {
|
||||
require JSON::XS;
|
||||
import JSON::XS qw(decode_json encode_json);
|
||||
1;
|
||||
};
|
||||
|
||||
if ($@) {
|
||||
$@ = undef;
|
||||
} or do {
|
||||
|
||||
# Fallback to built-in JSON which SHOULD
|
||||
# be available since 5.014 ...
|
||||
@ -95,73 +90,41 @@ if ($@) {
|
||||
require JSON::PP;
|
||||
import JSON::PP qw(decode_json encode_json);
|
||||
1;
|
||||
};
|
||||
|
||||
if ($@) {
|
||||
$@ = undef;
|
||||
} or do {
|
||||
|
||||
# Fallback to JSON::backportPP in really rare cases
|
||||
require JSON::backportPP;
|
||||
import JSON::backportPP qw(decode_json encode_json);
|
||||
1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
## Import der FHEM Funktionen
|
||||
#-- Run before package compilation
|
||||
BEGIN {
|
||||
|
||||
# Import from main context
|
||||
GP_Import(
|
||||
qw(
|
||||
readingsSingleUpdate
|
||||
readingsBulkUpdate
|
||||
readingsBeginUpdate
|
||||
readingsEndUpdate
|
||||
ReadingsVal
|
||||
ReadingsAge
|
||||
gettimeofday
|
||||
InternalTimer
|
||||
defs
|
||||
modules
|
||||
IsDisabled
|
||||
setKeyValue
|
||||
getKeyValue
|
||||
getUniqueId
|
||||
Log3
|
||||
CommandAttr
|
||||
attr
|
||||
AttrVal
|
||||
deviceEvents
|
||||
init_done
|
||||
devspec2array
|
||||
DoTrigger
|
||||
HttpUtils_NonblockingGet)
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sub Define {
|
||||
use version 0.60;
|
||||
|
||||
my $hash = shift // return;
|
||||
my $aArg = shift // return;
|
||||
|
||||
return $@ unless ( FHEM::Meta::SetInternals($hash) );
|
||||
use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' );
|
||||
|
||||
$version = FHEM::Meta::Get( $hash, 'version' );
|
||||
our $VERSION = $version;
|
||||
|
||||
return q{only one backupToStorage instance allowed}
|
||||
if ( devspec2array('TYPE=backupToStorage') > 1 )
|
||||
if ( ::devspec2array('TYPE=backupToStorage') > 1 )
|
||||
; # es wird geprüft ob bereits eine Instanz unseres Modules existiert,wenn ja wird abgebrochen
|
||||
return q{too few parameters: define <name> backupToStorage}
|
||||
if ( scalar( @{$aArg} ) != 2 );
|
||||
|
||||
my $name = shift @$aArg;
|
||||
$hash->{VERSION} = version->parse($VERSION)->normal;
|
||||
$hash->{NOTIFYDEV} = 'global,' . $name;
|
||||
$hash->{STORAGETYPE} = AttrVal( $name, 'bTS_Type', 'Nextcloud' );
|
||||
$hash->{VERSION} = version->parse($VERSION)->normal;
|
||||
$hash->{NOTIFYDEV} = 'global,' . $name;
|
||||
$hash->{STORAGETYPE} = ::AttrVal( $name, 'bTS_Type', 'Nextcloud' );
|
||||
|
||||
Log3( $name, 3, qq{backupToStorage ($name) - defined} );
|
||||
::Log3( $name, 3, qq{backupToStorage ($name) - defined} );
|
||||
|
||||
return;
|
||||
}
|
||||
@ -170,7 +133,7 @@ sub Undef {
|
||||
my $hash = shift;
|
||||
my $name = shift;
|
||||
|
||||
Log3( $name, 3, q{qbackupToStorage ($name) - delete device $name} );
|
||||
::Log3( $name, 3, q{qbackupToStorage ($name) - delete device $name} );
|
||||
|
||||
return;
|
||||
}
|
||||
@ -180,7 +143,7 @@ sub Delete {
|
||||
my $name = shift;
|
||||
|
||||
HttpUtils_Close( $hash->{helper}->{HttpUtilsParam} )
|
||||
if ( defined($hash->{helper}->{HttpUtilsParam}) );
|
||||
if ( defined( $hash->{helper}->{HttpUtilsParam} ) );
|
||||
DeletePassword($hash);
|
||||
|
||||
return;
|
||||
@ -190,7 +153,7 @@ sub Shutdown {
|
||||
my $hash = shift;
|
||||
|
||||
HttpUtils_Close( $hash->{helper}->{HttpUtilsParam} )
|
||||
if ( defined($hash->{helper}->{HttpUtilsParam}) );
|
||||
if ( defined( $hash->{helper}->{HttpUtilsParam} ) );
|
||||
|
||||
return;
|
||||
}
|
||||
@ -202,35 +165,31 @@ sub Notify {
|
||||
my $name = $hash->{NAME};
|
||||
my $devname = $dev->{NAME};
|
||||
my $devtype = $dev->{TYPE};
|
||||
my $events = deviceEvents( $dev, 1 );
|
||||
|
||||
my $events = ::deviceEvents( $dev, 1 );
|
||||
|
||||
_CheckIsDisabledAfterSetAttr($hash)
|
||||
if ( (
|
||||
(
|
||||
grep m{^DELETEATTR.$name.(disable|disabledForIntervals)$}xms,
|
||||
@{$events}
|
||||
or grep m{^ATTR.$name.(disable|disabledForIntervals).\S+$}xms,
|
||||
@{$events}
|
||||
)
|
||||
&& $devname eq 'global'
|
||||
&& $init_done
|
||||
if (
|
||||
(
|
||||
(
|
||||
grep { /^DELETEATTR.$name.(disable|disabledForIntervals)$/x }
|
||||
@{$events}
|
||||
or grep { /^ATTR.$name.(disable|disabledForIntervals).\S+$/x }
|
||||
@{$events}
|
||||
)
|
||||
|| $devname eq $name
|
||||
);
|
||||
&& $devname eq 'global'
|
||||
&& $init_done
|
||||
)
|
||||
|| $devname eq $name
|
||||
);
|
||||
|
||||
return if ( !$events
|
||||
|| IsDisabled($name) );
|
||||
return if ( !$events
|
||||
|| ::IsDisabled($name) );
|
||||
|
||||
|
||||
Log3( $name, 4,
|
||||
qq{backupToStorage ($name) -
|
||||
Devname: $devname
|
||||
Name: $name
|
||||
Notify: } . Dumper $events
|
||||
); # mit Dumper
|
||||
::Log3( $name, 4,
|
||||
qq{backupToStorage ($name) - Devname: $devname Name: $name Notify: } );
|
||||
|
||||
PushToStorage($hash)
|
||||
if ( ( grep m{^backup.done(.+)?$}xms, @{$events} )
|
||||
if ( ( grep { /^backup.done(.+)?$/x } @{$events} )
|
||||
&& $devname eq 'global'
|
||||
&& $init_done );
|
||||
|
||||
@ -239,16 +198,16 @@ sub Notify {
|
||||
(
|
||||
(
|
||||
(
|
||||
grep m{^DELETEATTR.$name.(bTS_Host|bTS_User)$}xms,
|
||||
grep { /^DELETEATTR.$name.(bTS_Host|bTS_User)$/x }
|
||||
@{$events}
|
||||
or grep m{^ATTR.$name.(bTS_Host|bTS_User).\S+$}xms,
|
||||
or grep { /^ATTR.$name.(bTS_Host|bTS_User).\S+$/x }
|
||||
@{$events}
|
||||
)
|
||||
&& $devname eq 'global'
|
||||
)
|
||||
|| (
|
||||
(
|
||||
$devname eq $name && grep m{^password.(add|remove)$}xms,
|
||||
$devname eq $name && grep { /^password.(add|remove)$/x }
|
||||
@{$events}
|
||||
)
|
||||
)
|
||||
@ -260,8 +219,8 @@ sub Notify {
|
||||
$hash, 'state',
|
||||
(
|
||||
(
|
||||
AttrVal( $name, 'bTS_Host', 'none' ) eq 'none'
|
||||
|| AttrVal( $name, 'bTS_User', 'none' ) eq 'none'
|
||||
::AttrVal( $name, 'bTS_Host', 'none' ) eq 'none'
|
||||
|| ::AttrVal( $name, 'bTS_User', 'none' ) eq 'none'
|
||||
|| !defined( ReadPassword( $hash, $name ) )
|
||||
)
|
||||
? 'please set storage account credentials first'
|
||||
@ -271,16 +230,13 @@ sub Notify {
|
||||
)
|
||||
if (
|
||||
(
|
||||
( grep m{^DEFINED.$name$}xms, @{$events} )
|
||||
( grep { /^DEFINED.$name$/x } @{$events} )
|
||||
&& $devname eq 'global'
|
||||
&& $init_done
|
||||
)
|
||||
|| (
|
||||
grep m{^INITIALIZED$}xms,
|
||||
@{$events} or grep m{^REREADCFG$}xms,
|
||||
@{$events} or grep m{^MODIFIED.$name$}xms,
|
||||
@{$events}
|
||||
)
|
||||
|| ( grep { /^INITIALIZED$/x } @{$events}
|
||||
or grep { /^REREADCFG$/x } @{$events}
|
||||
or grep { /^MODIFIED.$name$/x } @{$events} )
|
||||
&& $devname eq 'global'
|
||||
);
|
||||
|
||||
@ -297,7 +253,7 @@ sub Set {
|
||||
|
||||
if ( lc $cmd eq 'addpassword' ) {
|
||||
return q{please set Attribut bTS_User first}
|
||||
if ( AttrVal( $name, 'bTS_User', 'none' ) eq 'none' );
|
||||
if ( ::AttrVal( $name, 'bTS_User', 'none' ) eq 'none' );
|
||||
return qq{usage: "$cmd" <password>}
|
||||
if ( scalar( @{$aArg} ) != 1 );
|
||||
|
||||
@ -312,22 +268,22 @@ sub Set {
|
||||
elsif ( lc $cmd eq 'active' ) {
|
||||
return qq{usage: $cmd}
|
||||
if ( scalar( @{$aArg} ) != 0 );
|
||||
|
||||
|
||||
readingsSingleUpdate( $hash, 'state', 'ready', 1 );
|
||||
}
|
||||
elsif ( lc $cmd eq 'inactive' ) {
|
||||
return qq{usage: $cmd}
|
||||
if ( scalar( @{$aArg} ) != 0 );
|
||||
|
||||
|
||||
readingsSingleUpdate( $hash, 'state', $cmd, 1 );
|
||||
}
|
||||
else {
|
||||
my $list = 'active:noArg inactive:noArg';
|
||||
$list .= (
|
||||
defined( ReadPassword( $hash, $name ) )
|
||||
? ' deletepassword:noArg'
|
||||
: ' addpassword'
|
||||
);
|
||||
my $list = 'active:noArg inactive:noArg';
|
||||
$list .= (
|
||||
defined( ReadPassword( $hash, $name ) )
|
||||
? ' deletepassword:noArg'
|
||||
: ' addpassword'
|
||||
);
|
||||
|
||||
return qq{Unknown argument "$cmd", choose one of $list};
|
||||
}
|
||||
@ -336,52 +292,68 @@ sub Set {
|
||||
}
|
||||
|
||||
sub Attr {
|
||||
my $cmd = shift;
|
||||
my $name = shift;
|
||||
my $cmd = shift;
|
||||
my $name = shift;
|
||||
|
||||
my $hash = $defs{$name};
|
||||
my $attrName = shift;
|
||||
my $attrVal = shift;
|
||||
my $hash = $defs{$name};
|
||||
my $attrName = shift;
|
||||
my $attrVal = shift;
|
||||
|
||||
if ( $attrName eq 'disable'
|
||||
|| $attrName eq 'disabledForIntervals' )
|
||||
{
|
||||
|
||||
if ( $attrName eq 'disable'
|
||||
|| $attrName eq 'disabledForIntervals' ) {
|
||||
|
||||
if ( $cmd eq 'set' ) {
|
||||
if ( $attrName eq 'disabledForIntervals' ) {
|
||||
return
|
||||
'check disabledForIntervals Syntax HH:MM-HH:MM or HH:MM-HH:MM HH:MM-HH:MM ...'
|
||||
if ( $attrVal !~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/ );
|
||||
Log3( $name, 3, qq{backupToStorage ($name) - disabledForIntervals} );
|
||||
'check disabledForIntervals Syntax HH:MM-HH:MM or HH:MM-HH:MM HH:MM-HH:MM ...'
|
||||
if ( $attrVal !~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/x );
|
||||
::Log3( $name, 3,
|
||||
qq{backupToStorage ($name) - disabledForIntervals} );
|
||||
}
|
||||
elsif ( $attrName eq 'disable' ) {
|
||||
Log3( $name, 3, qq{backupToStorage ($name) - disabled} );
|
||||
::Log3( $name, 3, qq{backupToStorage ($name) - disabled} );
|
||||
}
|
||||
}
|
||||
|
||||
InternalTimer( gettimeofday() + 1,
|
||||
'FHEM::Services::backupToStorage::_CheckIsDisabledAfterSetAttr', $hash, 0 );
|
||||
::InternalTimer(
|
||||
::gettimeofday() + 1,
|
||||
'FHEM::Services::backupToStorage::_CheckIsDisabledAfterSetAttr',
|
||||
$hash, 0
|
||||
);
|
||||
}
|
||||
elsif ( $attrName eq 'bTS_Type' ) {
|
||||
InternalTimer( gettimeofday() + 1,
|
||||
sub { $hash->{STORAGETYPE} = AttrVal($name,'bTS_Type','Nextcloud'); }, $hash, 0 );
|
||||
::InternalTimer(
|
||||
::gettimeofday() + 1,
|
||||
sub {
|
||||
$hash->{STORAGETYPE} =
|
||||
::AttrVal( $name, 'bTS_Type', 'Nextcloud' );
|
||||
},
|
||||
$hash,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub _CheckIsDisabledAfterSetAttr {
|
||||
my $hash = shift;
|
||||
my $hash = shift;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my $state = ( IsDisabled($name)
|
||||
? 'inactive'
|
||||
: 'ready' );
|
||||
|
||||
Log3( $name, 3, qq{backupToStorage ($name) - _CheckIsDisabledAfterSetAttr} );
|
||||
my $name = $hash->{NAME};
|
||||
my $state = (
|
||||
::IsDisabled($name)
|
||||
? 'inactive'
|
||||
: 'ready'
|
||||
);
|
||||
|
||||
readingsSingleUpdate($hash, 'state', $state, 1)
|
||||
if ( ReadingsVal($name, 'state', 'ready' ) ne $state );
|
||||
::Log3( $name, 3,
|
||||
qq{backupToStorage ($name) - _CheckIsDisabledAfterSetAttr} );
|
||||
|
||||
readingsSingleUpdate( $hash, 'state', $state, 1 )
|
||||
if ( ::ReadingsVal( $name, 'state', 'ready' ) ne $state );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Rename {
|
||||
@ -391,7 +363,7 @@ sub Rename {
|
||||
my $hash = $defs{$new};
|
||||
|
||||
StorePassword( $hash, $new, ReadPassword( $hash, $old ) );
|
||||
setKeyValue( $hash->{TYPE} . "_" . $old . "_passwd", undef );
|
||||
::setKeyValue( $hash->{TYPE} . "_" . $old . "_passwd", undef );
|
||||
|
||||
return;
|
||||
}
|
||||
@ -401,66 +373,67 @@ sub PushToStorage {
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
Log3( $name, 4, qq{backupToStorage ($name) - push to storage function} );
|
||||
|
||||
return Log3( $name, 4, qq{backupToStorage ($name) - fhemBackupFile Reading to old} )
|
||||
if ( ReadingsAge($name,'fhemBackupFile',1) > 3600 );
|
||||
|
||||
Log3( $name, 4, qq{backupToStorage ($name) - after readings age return} );
|
||||
::Log3( $name, 4, qq{backupToStorage ($name) - push to storage function} );
|
||||
|
||||
return ::Log3( $name, 4,
|
||||
qq{backupToStorage ($name) - fhemBackupFile Reading to old} )
|
||||
if ( ::ReadingsAge( $name, 'fhemBackupFile', 1 ) > 3600 );
|
||||
|
||||
::Log3( $name, 4, qq{backupToStorage ($name) - after readings age return} );
|
||||
|
||||
if ( $hash->{STORAGETYPE} eq 'SynologyFileStation' ) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
require "SubProcess.pm";
|
||||
require SubProcess;
|
||||
my $subprocess = SubProcess->new( { onRun => \&FileUpload } );
|
||||
|
||||
my $backupFile = ReadingsVal( $name, 'fhemBackupFile', 'none' );
|
||||
my $backupFile = ::ReadingsVal( $name, 'fhemBackupFile', 'none' );
|
||||
|
||||
my @fileNameAtStorage_array = split( '/', $backupFile );
|
||||
my $fileNameAtStorage = $fileNameAtStorage_array[$#fileNameAtStorage_array];
|
||||
my $fileNameAtStorage =
|
||||
$fileNameAtStorage_array[$#fileNameAtStorage_array];
|
||||
|
||||
$subprocess->{curl} = qx(which curl);
|
||||
chomp($subprocess->{curl});
|
||||
$subprocess->{fhemhost} = qx(hostname -f);
|
||||
chomp($subprocess->{fhemhost});
|
||||
$subprocess->{type} = $hash->{STORAGETYPE};
|
||||
$subprocess->{host} = AttrVal( $name, 'bTS_Host', '' );
|
||||
$subprocess->{user} = AttrVal( $name, 'bTS_User', '' );
|
||||
$subprocess->{pass} = ReadPassword( $hash, $name );
|
||||
$subprocess->{path} = AttrVal( $name, 'bTS_Path', '' );
|
||||
$subprocess->{backupfile} = $backupFile;
|
||||
$subprocess->{fileNameAtStorage} = $fileNameAtStorage;
|
||||
$subprocess->{proto} = AttrVal( $name, 'bTS_Proto', 'https' );
|
||||
$subprocess->{loglevel} = AttrVal( $name, 'verbose', 3 );
|
||||
$subprocess->{curl} = qx(which curl);
|
||||
chomp( $subprocess->{curl} );
|
||||
$subprocess->{fhemhost} = qx(hostname -f);
|
||||
chomp( $subprocess->{fhemhost} );
|
||||
$subprocess->{type} = $hash->{STORAGETYPE};
|
||||
$subprocess->{host} = ::AttrVal( $name, 'bTS_Host', '' );
|
||||
$subprocess->{user} = ::AttrVal( $name, 'bTS_User', '' );
|
||||
$subprocess->{pass} = ReadPassword( $hash, $name );
|
||||
$subprocess->{path} = ::AttrVal( $name, 'bTS_Path', '' );
|
||||
$subprocess->{backupfile} = $backupFile;
|
||||
$subprocess->{fileNameAtStorage} = $fileNameAtStorage;
|
||||
$subprocess->{proto} = ::AttrVal( $name, 'bTS_Proto', 'https' );
|
||||
$subprocess->{loglevel} = ::AttrVal( $name, 'verbose', 3 );
|
||||
|
||||
my $pid = $subprocess->run();
|
||||
|
||||
readingsSingleUpdate( $hash, 'state', ' file upload in progress', 1 );
|
||||
|
||||
if ( !defined($pid) ) {
|
||||
Log3( $name, 1,
|
||||
qq{backupToStorage ($name) - Cannot execute command asynchronously} );
|
||||
::Log3( $name, 1,
|
||||
qq{backupToStorage ($name) - Cannot execute command asynchronously}
|
||||
);
|
||||
|
||||
CleanSubprocess($hash);
|
||||
readingsSingleUpdate( $hash, 'state',
|
||||
'Cannot execute command asynchronously', 1 );
|
||||
return undef;
|
||||
return;
|
||||
}
|
||||
|
||||
Log3( $name, 4,
|
||||
qq{backupToStorage ($name) - execute command asynchronously (PID="$pid")}
|
||||
::Log3( $name, 4,
|
||||
qq{backupToStorage ($name) - execute command asynchronously (PID="$pid")}
|
||||
);
|
||||
|
||||
$hash->{".fhem"}{subprocess} = $subprocess;
|
||||
|
||||
InternalTimer( gettimeofday() + 1,
|
||||
::InternalTimer( ::gettimeofday() + 1,
|
||||
"FHEM::Services::backupToStorage::PollChild", $hash );
|
||||
}
|
||||
|
||||
Log3( $hash, 4,
|
||||
::Log3( $hash, 4,
|
||||
qq{backupToStorage ($name) - control passed back to main loop.} );
|
||||
|
||||
return;
|
||||
@ -471,59 +444,61 @@ sub KeepLastN {
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
Log3( $name, 4, qq{backupToStorage ($name) - Keep Last N at Storage function} );
|
||||
|
||||
::Log3( $name, 4,
|
||||
qq{backupToStorage ($name) - Keep Last N at Storage function} );
|
||||
|
||||
if ( $hash->{STORAGETYPE} eq 'SynologyFileStation' ) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
require "SubProcess.pm";
|
||||
require SubProcess;
|
||||
my $subprocess = SubProcess->new( { onRun => \&CleanUp } );
|
||||
|
||||
my $backupFile = ReadingsVal( $name, 'fhemBackupFile', 'none' );
|
||||
my $backupFile = ::ReadingsVal( $name, 'fhemBackupFile', 'none' );
|
||||
|
||||
my @fileNameAtStorage_array = split( '/', $backupFile );
|
||||
my $fileNameAtStorage = $fileNameAtStorage_array[$#fileNameAtStorage_array];
|
||||
my $fileNameAtStorage =
|
||||
$fileNameAtStorage_array[$#fileNameAtStorage_array];
|
||||
|
||||
$subprocess->{curl} = qx(which curl);
|
||||
chomp($subprocess->{curl});
|
||||
$subprocess->{type} = $hash->{STORAGETYPE};
|
||||
$subprocess->{host} = AttrVal( $name, 'bTS_Host', '' );
|
||||
$subprocess->{user} = AttrVal( $name, 'bTS_User', '' );
|
||||
$subprocess->{pass} = ReadPassword( $hash, $name );
|
||||
$subprocess->{path} = AttrVal( $name, 'bTS_Path', '' );
|
||||
$subprocess->{fileNameAtStorage} = $fileNameAtStorage;
|
||||
$subprocess->{proto} = AttrVal( $name, 'bTS_Proto', 'https' );
|
||||
$subprocess->{loglevel} = AttrVal( $name, 'verbose', 3 );
|
||||
$subprocess->{keeplastn} = AttrVal( $name, 'bTS_KeepLastBackups', 5 );
|
||||
$subprocess->{curl} = qx(which curl);
|
||||
chomp( $subprocess->{curl} );
|
||||
$subprocess->{type} = $hash->{STORAGETYPE};
|
||||
$subprocess->{host} = ::AttrVal( $name, 'bTS_Host', '' );
|
||||
$subprocess->{user} = ::AttrVal( $name, 'bTS_User', '' );
|
||||
$subprocess->{pass} = ReadPassword( $hash, $name );
|
||||
$subprocess->{path} = ::AttrVal( $name, 'bTS_Path', '' );
|
||||
$subprocess->{fileNameAtStorage} = $fileNameAtStorage;
|
||||
$subprocess->{proto} = ::AttrVal( $name, 'bTS_Proto', 'https' );
|
||||
$subprocess->{loglevel} = ::AttrVal( $name, 'verbose', 3 );
|
||||
$subprocess->{keeplastn} = ::AttrVal( $name, 'bTS_KeepLastBackups', 5 );
|
||||
|
||||
my $pid = $subprocess->run();
|
||||
|
||||
readingsSingleUpdate( $hash, 'state', ' clean up pass last N in progress', 1 );
|
||||
readingsSingleUpdate( $hash, 'state',
|
||||
' clean up pass last N in progress', 1 );
|
||||
|
||||
if ( !defined($pid) ) {
|
||||
Log3( $name, 1,
|
||||
qq{backupToStorage ($name) - Cannot execute command asynchronously} );
|
||||
::Log3( $name, 1,
|
||||
qq{backupToStorage ($name) - Cannot execute command asynchronously}
|
||||
);
|
||||
|
||||
CleanSubprocess($hash);
|
||||
readingsSingleUpdate( $hash, 'state',
|
||||
'Cannot execute command asynchronously', 1 );
|
||||
return undef;
|
||||
return;
|
||||
}
|
||||
|
||||
Log3( $name, 4,
|
||||
qq{backupToStorage ($name) - execute command asynchronously (PID="$pid")}
|
||||
::Log3( $name, 4,
|
||||
qq{backupToStorage ($name) - execute command asynchronously (PID="$pid")}
|
||||
);
|
||||
|
||||
$hash->{".fhem"}{subprocess} = $subprocess;
|
||||
|
||||
InternalTimer( gettimeofday() + 1,
|
||||
::InternalTimer( ::gettimeofday() + 1,
|
||||
"FHEM::Services::backupToStorage::PollChild", $hash );
|
||||
}
|
||||
|
||||
Log3( $hash, 4,
|
||||
::Log3( $hash, 4,
|
||||
qq{backupToStorage ($name) - control passed back to main loop.} );
|
||||
|
||||
return;
|
||||
@ -539,26 +514,29 @@ sub PollChild {
|
||||
my $json = $subprocess->readFromChild();
|
||||
|
||||
if ( !defined($json) ) {
|
||||
Log3( $name, 5,
|
||||
qq{backupToStorage ($name) - still waiting ($subprocess->{lasterror}).}
|
||||
::Log3( $name, 5,
|
||||
qq{backupToStorage ($name) - still waiting ($subprocess->{lasterror}).}
|
||||
);
|
||||
|
||||
InternalTimer( gettimeofday() + 1,
|
||||
::InternalTimer( ::gettimeofday() + 1,
|
||||
"FHEM::Services::backupToStorage::PollChild", $hash );
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Log3( $name, 4,
|
||||
qq{backupToStorage ($name) - got result from asynchronous parsing: $json} );
|
||||
::Log3( $name, 4,
|
||||
qq{backupToStorage ($name) - got result from asynchronous parsing: $json}
|
||||
);
|
||||
|
||||
$subprocess->wait();
|
||||
Log3( $name, 4,
|
||||
::Log3( $name, 4,
|
||||
qq{backupToStorage ($name) - asynchronous finished.} );
|
||||
|
||||
CleanSubprocess($hash);
|
||||
WriteReadings( $hash, $json );
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
######################################
|
||||
@ -569,23 +547,23 @@ sub FileUpload {
|
||||
my $response = {};
|
||||
|
||||
if ( $subprocess->{type} eq 'Nextcloud' ) {
|
||||
my ($returnString,$returnCode) = ExecuteNCupload($subprocess);
|
||||
|
||||
my ( $returnString, $returnCode ) = ExecuteNCupload($subprocess);
|
||||
|
||||
print 'backupToStorage File Upload - FileUpload Nextcloud, returnCode: '
|
||||
. $returnCode
|
||||
. ' , returnString: '
|
||||
. $returnString . "\n"
|
||||
. $returnCode
|
||||
. ' , returnString: '
|
||||
. $returnString . "\n"
|
||||
if ( $subprocess->{loglevel} > 4 );
|
||||
|
||||
|
||||
if ( $returnString =~ /100\s\s?[0-9].*\s100\s\s?[0-9].*/m
|
||||
and $returnString =~ /\s\s<o:hint xmlns:o="o:">(.*)<\/o:hint>/m ) {
|
||||
|
||||
if ( $returnString =~ /100\s\s?[0-9].*\s100\s\s?[0-9].*/xm
|
||||
and $returnString =~ /\s\s<o:hint xmlns:o="o:">(.*)<\/o:hint>/xm )
|
||||
{
|
||||
$response->{ncUpload} = $1;
|
||||
}
|
||||
elsif ( $returnString =~ /100\s\s?[0-9].*\s100\s\s?[0-9].*/m ) {
|
||||
elsif ( $returnString =~ /100\s\s?[0-9].*\s100\s\s?[0-9].*/xm ) {
|
||||
$response->{ncUpload} = 'upload successfully';
|
||||
}
|
||||
elsif ( $returnString =~ /(curl:\s.*)/ ){
|
||||
elsif ( $returnString =~ /(curl:\s.*)/x ) {
|
||||
$response->{ncUpload} = $1;
|
||||
}
|
||||
else {
|
||||
@ -596,7 +574,7 @@ sub FileUpload {
|
||||
my $json = eval { encode_json($response) };
|
||||
if ($@) {
|
||||
print 'backupToStorage File Upload backupToStorage - JSON error: $@'
|
||||
. "\n";
|
||||
. "\n";
|
||||
$json = '{"jsonerror":"$@"}';
|
||||
}
|
||||
|
||||
@ -618,7 +596,8 @@ sub ExecuteNCupload {
|
||||
$command .= $subprocess->{user};
|
||||
$command .= $subprocess->{path};
|
||||
$command .= '/';
|
||||
$command .= $subprocess->{fhemhost} . '-' . $subprocess->{fileNameAtStorage};
|
||||
$command .=
|
||||
$subprocess->{fhemhost} . '-' . $subprocess->{fileNameAtStorage};
|
||||
$command .= '"';
|
||||
|
||||
return ExecuteCommand($command);
|
||||
@ -629,23 +608,23 @@ sub CleanUp {
|
||||
my $response = {};
|
||||
|
||||
if ( $subprocess->{type} eq 'Nextcloud' ) {
|
||||
my ($returnString,$returnCode) = ExecuteCleanUp($subprocess);
|
||||
|
||||
my ( $returnString, $returnCode ) = ExecuteCleanUp($subprocess);
|
||||
|
||||
print 'backupToStorage File Upload - FileUpload Nextcloud, returnCode: '
|
||||
. $returnCode
|
||||
. ' , returnString: '
|
||||
. $returnString . "\n"
|
||||
. $returnCode
|
||||
. ' , returnString: '
|
||||
. $returnString . "\n"
|
||||
if ( $subprocess->{loglevel} > 4 );
|
||||
|
||||
|
||||
if ( $returnString =~ /100\s\s?[0-9].*\s100\s\s?[0-9].*/m
|
||||
and $returnString =~ /\s\s<o:hint xmlns:o="o:">(.*)<\/o:hint>/m ) {
|
||||
|
||||
if ( $returnString =~ /100\s\s?[0-9].*\s100\s\s?[0-9].*/xm
|
||||
and $returnString =~ /\s\s<o:hint xmlns:o="o:">(.*)<\/o:hint>/xm )
|
||||
{
|
||||
$response->{ncUpload} = $1;
|
||||
}
|
||||
elsif ( $returnString =~ /100\s\s?[0-9].*\s100\s\s?[0-9].*/m ) {
|
||||
elsif ( $returnString =~ /100\s\s?[0-9].*\s100\s\s?[0-9].*/xm ) {
|
||||
$response->{ncUpload} = 'upload successfully';
|
||||
}
|
||||
elsif ( $returnString =~ /(curl:\s.*)/ ){
|
||||
elsif ( $returnString =~ /(curl:\s.*)/x ) {
|
||||
$response->{ncUpload} = $1;
|
||||
}
|
||||
else {
|
||||
@ -656,7 +635,7 @@ sub CleanUp {
|
||||
my $json = eval { encode_json($response) };
|
||||
if ($@) {
|
||||
print 'backupToStorage File Upload backupToStorage - JSON error: $@'
|
||||
. "\n";
|
||||
. "\n";
|
||||
$json = '{"jsonerror":"$@"}';
|
||||
}
|
||||
|
||||
@ -676,7 +655,8 @@ sub ExecuteNCfetchFileList {
|
||||
$command .= '/remote.php/dav/files/';
|
||||
$command .= $subprocess->{user};
|
||||
$command .= $subprocess->{path};
|
||||
$command .= '" --data \'<?xml version="1.0" encoding="UTF-8"?><d:propfind xmlns:d="DAV:"><d:prop xmlns:oc="http://owncloud.org/ns"><d:getlastmodified/></d:prop></d:propfind>\'';
|
||||
$command .=
|
||||
'" --data \'<?xml version="1.0" encoding="UTF-8"?><d:propfind xmlns:d="DAV:"><d:prop xmlns:oc="http://owncloud.org/ns"><d:getlastmodified/></d:prop></d:propfind>\'';
|
||||
|
||||
return ExecuteCommand($command);
|
||||
}
|
||||
@ -700,7 +680,8 @@ sub ExecuteNCremoveFile {
|
||||
}
|
||||
|
||||
sub ExecuteCommand {
|
||||
my $command = join q{ }, @_;
|
||||
my @options = @_;
|
||||
my $command = join q{ }, @options;
|
||||
return ( $_ = qx{$command 2>&1}, $? >> 8 );
|
||||
}
|
||||
|
||||
@ -714,7 +695,9 @@ sub CleanSubprocess {
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
delete( $hash->{".fhem"}{subprocess} );
|
||||
Log3( $name, 4, qq{backupToStorage ($name) - clean Subprocess} );
|
||||
::Log3( $name, 4, qq{backupToStorage ($name) - clean Subprocess} );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub StorePassword {
|
||||
@ -723,10 +706,10 @@ sub StorePassword {
|
||||
my $password = shift;
|
||||
|
||||
my $index = $hash->{TYPE} . "_" . $name . "_passwd";
|
||||
my $key = getUniqueId() . $index;
|
||||
my $key = ::getUniqueId() . $index;
|
||||
my $enc_pwd = "";
|
||||
|
||||
if ( eval "use Digest::MD5;1" ) {
|
||||
if ( eval { use Digest::MD5; 1 } ) {
|
||||
|
||||
$key = Digest::MD5::md5_hex( unpack "H*", $key );
|
||||
$key .= Digest::MD5::md5_hex($key);
|
||||
@ -739,8 +722,8 @@ sub StorePassword {
|
||||
$key = $encode . $key;
|
||||
}
|
||||
|
||||
my $err = setKeyValue( $index, $enc_pwd );
|
||||
DoTrigger( $name, 'password add' );
|
||||
my $err = ::setKeyValue( $index, $enc_pwd );
|
||||
::DoTrigger( $name, 'password add' );
|
||||
|
||||
return qq{error while saving the password - $err}
|
||||
if ( defined($err) );
|
||||
@ -753,30 +736,31 @@ sub ReadPassword {
|
||||
my $name = shift;
|
||||
|
||||
my $index = $hash->{TYPE} . "_" . $name . "_passwd";
|
||||
my $key = getUniqueId() . $index;
|
||||
my $key = ::getUniqueId() . $index;
|
||||
my ( $password, $err );
|
||||
|
||||
Log3( $name, 4, qq{backupToStorage ($name) - Read password from file} );
|
||||
::Log3( $name, 4, qq{backupToStorage ($name) - Read password from file} );
|
||||
|
||||
( $err, $password ) = getKeyValue($index);
|
||||
( $err, $password ) = ::getKeyValue($index);
|
||||
|
||||
if ( defined($err) ) {
|
||||
|
||||
Log3( $name, 3,
|
||||
qq{backupToStorage ($name) - unable to read password from file: $err}
|
||||
::Log3( $name, 3,
|
||||
qq{backupToStorage ($name) - unable to read password from file: $err}
|
||||
);
|
||||
return undef;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( defined($password) ) {
|
||||
if ( eval "use Digest::MD5;1" ) {
|
||||
if ( eval { use Digest::MD5; 1 } ) {
|
||||
$key = Digest::MD5::md5_hex( unpack "H*", $key );
|
||||
$key .= Digest::MD5::md5_hex($key);
|
||||
}
|
||||
|
||||
my $dec_pwd = '';
|
||||
|
||||
for my $char ( map { pack( 'C', hex($_) ) } ( $password =~ /(..)/g ) ) {
|
||||
for my $char ( map { pack( 'C', hex($_) ) } ( $password =~ /(..)/xg ) )
|
||||
{
|
||||
|
||||
my $decode = chop($key);
|
||||
$dec_pwd .= chr( ord($char) ^ ord($decode) );
|
||||
@ -786,8 +770,8 @@ sub ReadPassword {
|
||||
return $dec_pwd;
|
||||
}
|
||||
else {
|
||||
Log3( $name, 3, qq{backupToStorage ($name) - No password in file} );
|
||||
return undef;
|
||||
::Log3( $name, 3, qq{backupToStorage ($name) - No password in file} );
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
@ -798,8 +782,8 @@ sub DeletePassword {
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
setKeyValue( $hash->{TYPE} . "_" . $name . "_passwd", undef );
|
||||
DoTrigger( $name, 'password remove' );
|
||||
::setKeyValue( $hash->{TYPE} . "_" . $name . "_passwd", undef );
|
||||
::DoTrigger( $name, 'password remove' );
|
||||
|
||||
return;
|
||||
}
|
||||
@ -809,21 +793,17 @@ sub CheckAttributsForCredentials {
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $ncUser = AttrVal( $name, 'bTS_User', 'none' );
|
||||
my $ncUser = ::AttrVal( $name, 'bTS_User', 'none' );
|
||||
my $ncPass = ReadPassword( $hash, $name );
|
||||
my $ncHost = AttrVal( $name, 'bTS_Host', 'none' );
|
||||
my $ncHost = ::AttrVal( $name, 'bTS_Host', 'none' );
|
||||
my $status = 'ready';
|
||||
|
||||
$status = ( $status eq 'ready'
|
||||
&& $ncUser eq 'none'
|
||||
? 'no user credential attribut'
|
||||
: $status eq 'ready'
|
||||
&& $ncHost eq 'none'
|
||||
? 'no host credential attribut'
|
||||
: $status eq 'ready'
|
||||
&& !defined($ncPass)
|
||||
? 'no password set'
|
||||
: $status
|
||||
$status = (
|
||||
$status eq 'ready' && $ncUser eq 'none' ? 'no user credential attribut'
|
||||
: $status eq 'ready'
|
||||
&& $ncHost eq 'none' ? 'no host credential attribut'
|
||||
: $status eq 'ready' && !defined($ncPass) ? 'no password set'
|
||||
: $status
|
||||
);
|
||||
|
||||
return readingsSingleUpdate( $hash, 'state', $status, 1 );
|
||||
@ -837,14 +817,16 @@ sub WriteReadings {
|
||||
|
||||
my $decode_json = eval { decode_json($json) };
|
||||
if ($@) {
|
||||
Log3( $name, 2, qq{backupToStorage ($name) - JSON error: $@} );
|
||||
::Log3( $name, 2, qq{backupToStorage ($name) - JSON error: $@} );
|
||||
return;
|
||||
}
|
||||
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate( $hash, 'state', 'ready' );
|
||||
readingsBulkUpdate( $hash, 'uploadState', $decode_json->{ncUpload} );
|
||||
readingsEndUpdate( $hash, 1 );
|
||||
::readingsBeginUpdate($hash);
|
||||
::readingsBulkUpdate( $hash, 'state', 'ready' );
|
||||
::readingsBulkUpdate( $hash, 'uploadState', $decode_json->{ncUpload} );
|
||||
::readingsEndUpdate( $hash, 1 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user