98_Hyperion: new 'set binary restart/stop', non-blocking for 'get configFiles', 'set configFile' and 'set binary', change from killall to kill pidof

git-svn-id: https://svn.fhem.de/fhem/trunk@15462 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
deespe 2017-11-19 23:08:24 +00:00
parent fcd37afd9d
commit f10ec164a3
2 changed files with 211 additions and 88 deletions

View File

@ -1,5 +1,10 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- feature: 98_Hyperion: - new "set binary restart/stop"
- non-blocking for "get configFiles",
"set configFile" and "set binary"
- ATTENTION: change from command killall to kill,
please adjust your sudoers file (if applicable)
- new: 14_SD_RSL - basic support for conrad RSL - new: 14_SD_RSL - basic support for conrad RSL
- update: 14_Hideki: windsensor support added - update: 14_Hideki: windsensor support added
00_SIGNALduino: Updated to v3.3.2 00_SIGNALduino: Updated to v3.3.2

View File

@ -17,6 +17,7 @@ use Color;
use DevIo; use DevIo;
use JSON; use JSON;
use SetExtensions; use SetExtensions;
use Blocking;
my %Hyperion_sets = my %Hyperion_sets =
( (
@ -150,6 +151,7 @@ sub Hyperion_Undef($$)
{ {
my ($hash,$name) = @_; my ($hash,$name) = @_;
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
BlockingKill($hash->{helper}{RUNNING_PID}) if ($hash->{helper}{RUNNING_PID});
DevIo_CloseDev($hash); DevIo_CloseDev($hash);
return; return;
} }
@ -168,8 +170,8 @@ sub Hyperion_list2array($$)
sub Hyperion_isLocal($) sub Hyperion_isLocal($)
{ {
my ($hash) = @_; my ($ip) = @_;
return ($hash->{IP} =~ /^(localhost|127\.0{1,3}\.0{1,3}\.0{0,2}1|::1)$/) ? 1 : undef; return ($ip =~ /^(localhost|127\.0{1,3}\.0{1,3}\.0{0,2}1|::1)$/) ? 1 : undef;
} }
sub Hyperion_Get($@) sub Hyperion_Get($@)
@ -183,6 +185,7 @@ sub Hyperion_Get($@)
if (!$cmd); if (!$cmd);
if ($cmd eq "configFiles") if ($cmd eq "configFiles")
{ {
return "Work already/still in progress... Please wait for the current process to finish." if ($hash->{helper}{RUNNING_PID} && !$hash->{helper}{RUNNING_PID}{terminated});
Hyperion_GetConfigs($hash); Hyperion_GetConfigs($hash);
} }
elsif ($cmd eq "devStateIcon") elsif ($cmd eq "devStateIcon")
@ -230,8 +233,10 @@ sub Hyperion_Read($)
{ {
my $ver = (split /V/,(split " ",$data->{hyperion_build}->[0]->{version})[0])[1]; my $ver = (split /V/,(split " ",$data->{hyperion_build}->[0]->{version})[0])[1];
$ver =~ s/\.//g; $ver =~ s/\.//g;
$ver = $ver * 1;
my $rver = $Hyperion_requiredVersion; my $rver = $Hyperion_requiredVersion;
$rver =~ s/\.//g; $rver =~ s/\.//g;
$rver = $rver * 1;
$error = "Your version of hyperion (detected version: ".$data->{hyperion_build}->[0]->{version}.") is not (longer) supported by this module!" if ($ver<$rver); $error = "Your version of hyperion (detected version: ".$data->{hyperion_build}->[0]->{version}.") is not (longer) supported by this module!" if ($ver<$rver);
} }
if ($error) if ($error)
@ -383,65 +388,59 @@ sub Hyperion_Read($)
sub Hyperion_GetConfigs($) sub Hyperion_GetConfigs($)
{ {
my ($hash) = @_; my ($hash) = @_;
return "Not connected" if (!$hash->{FD});
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $ip = $hash->{IP}; my $ip = $hash->{IP};
my $dir = AttrVal($name,"hyperionConfigDir","/etc/hyperion/"); my $dir = AttrVal($name,"hyperionConfigDir","/etc/hyperion/");
my $com = "ls $dir 2>/dev/null"; my $com = "ls $dir";
my @files; if (!Hyperion_isLocal($ip))
if (Hyperion_isLocal($hash))
{
@files = Hyperion_listFilesInDir($hash,$com);
}
else
{ {
my $ssh = qx(which ssh);
chomp $ssh;
return "SSH client could NOT be found!" if (!$ssh);
my $user = AttrVal($name,"hyperionSshUser","pi"); my $user = AttrVal($name,"hyperionSshUser","pi");
my $cmd = qx(which ssh); $com = "$ssh $user\@$ip '$com'";
chomp $cmd;
$cmd .= " $user\@$ip $com";
@files = Hyperion_listFilesInDir($hash,$cmd);
} }
return "No files found on server \"$ip\" in directory \"$dir\".\nMaybe the wrong directory?\n\nIf SSH is used, has the user \"".AttrVal($name,"hyperionSshUser","pi")."\" been configured to log in without\nentering a password (http://www.linuxproblem.org/art_9.html)?" Log3 $name,4,"$name: lsCmd: $com";
if (@files == 0); $com = encode_base64($com);
if (@files > 1) $hash->{helper}{RUNNING_PID} = BlockingCall("Hyperion_ExecCmd","$name|$com","Hyperion_GetConfigs_finished");
{ return "Working in background...";
my $configs = join(",",@files);
readingsSingleUpdate($hash,".configs",$configs,1) if (ReadingsVal($name,".configs","") ne $configs);
$attr{$name}{webCmd} = $Hyperion_webCmd_config if (AttrVal($name,"webCmd","") eq $Hyperion_webCmd);
}
else
{
AnalyzeCommandChain(undef,"deletereading $name .configs") if (defined ReadingsVal($name,".configs",undef));
$attr{$name}{webCmd} = $Hyperion_webCmd if (AttrVal($name,"webCmd","") eq $Hyperion_webCmd_config);
return "Found just one config file.\nPlease add at least one more config file to properly use this function."
if (@files == 1);
return "No config files found!";
}
Hyperion_GetUpdate($hash);
return "Found ".@files." config files.\nPlease refresh this page to see the result.";
} }
sub Hyperion_listFilesInDir($$) sub Hyperion_GetConfigs_finished($)
{ {
my ($hash,$cmd) = @_; my ($string) = @_;
my $name = $hash->{NAME}; my @a = split /\|/,$string;
my $fh; my $name = $a[0];
my @files;
@files = split " ",$a[1] if ($a[1]);
my $hash = $defs{$name};
my $ip = $hash->{IP};
my $dir = AttrVal($name,"hyperionConfigDir","/etc/hyperion/");
delete $hash->{helper}{RUNNING_PID};
my @filelist; my @filelist;
if (open(FH,"$cmd|")) foreach (@files)
{ {
my @files = <FH>; my $file = $_;
for (my $i = 0; $i < @files; $i++) next if ($file !~ /^([-\.\w]+)\.config\.json$/);
{ $file = $1;
my $file = $files[$i]; push @filelist,$file;
chomp $file; Log3 $name,4,"$name: matching config file: \"$_\"";
next if ($file !~ /\w+\.config\.json$/);
$file =~ s/\.config\.json$//gm;
push @filelist,$file;
Log3 $name,4,"$name: Hyperion_listFilesInDir matching file: \"$file\"";
}
close FH;
} }
return @filelist; if (@filelist)
{
my $configs = join(",",@filelist);
readingsSingleUpdate($hash,".configs",$configs,0);
CommandAttr(undef,"$name webCmd $Hyperion_webCmd_config") if (AttrVal($name,"webCmd","") eq $Hyperion_webCmd && @filelist > 1);
CommandAttr(undef,"$name webCmd $Hyperion_webCmd") if (AttrVal($name,"webCmd","") eq $Hyperion_webCmd_config && @filelist < 2);
}
else
{
CommandDeleteReading(undef,"$name .configs") if (ReadingsVal($name,".configs",""));
CommandAttr(undef,"$name webCmd $Hyperion_webCmd") if (AttrVal($name,"webCmd","") eq $Hyperion_webCmd_config);
Log3 $name,3,"$name: No files found on server \"$ip\" in directory \"$dir\".\nMaybe the wrong directory?\n\nIf SSH is used, has the user \"".AttrVal($name,"hyperionSshUser","pi")."\" been configured to log in without\nentering a password (http://www.linuxproblem.org/art_9.html)?";
}
Hyperion_GetUpdate($hash);
return;
} }
sub Hyperion_GetUpdate(@) sub Hyperion_GetUpdate(@)
@ -460,6 +459,97 @@ sub Hyperion_GetUpdate(@)
return; return;
} }
sub Hyperion_ExecCmd($)
{
my ($string) = @_;
my @a = split /\|/,$string;
my $name = $a[0];
my $cmd = decode_base64($a[1]);
my $hash = $defs{$name};
my @qx = qx($cmd);
my @ret;
my $re = "";
foreach (@qx)
{
chomp $_;
$_ =~ s/^[\s\t]{1,}/ /;
push @ret,$_;
}
$re .= join " ",@ret if (@ret);
return "$name|$re";
}
sub Hyperion_Kill_finished($)
{
my ($string) = @_;
my @a = split /\|/,$string;
my $name = $a[0];
my $error = $a[1];
my $hash = $defs{$name};
delete $hash->{helper}{RUNNING_PID};
if ($error)
{
Log3 $name,3,"$name: Not able to stop Hyperion! Error: $error";
readingsSingleUpdate($hash,"lastError",$error,1);
}
else
{
Log3 $name,3,"$name: Hyperion has been stopped";
RemoveInternalTimer($hash);
DevIo_Disconnected($hash);
}
return undef;
}
sub Hyperion_Restart($)
{
my ($string) = @_;
my @a = split /\|/,$string;
my $name = $a[0];
my $error = $a[1];
my $hash = $defs{$name};
delete $hash->{helper}{RUNNING_PID};
if ($error)
{
Log3 $name,3,"$name: Not able to stop Hyperion! Error: $error";
readingsSingleUpdate($hash,"lastError",$error,1);
}
else
{
my $cmd = $hash->{helper}{startCmd};
$hash->{helper}{RUNNING_PID} = BlockingCall("Hyperion_ExecCmd","$name|$cmd","Hyperion_Restart_finished");
}
return undef;
}
sub Hyperion_Restart_finished($)
{
my ($string) = @_;
my @a = split /\|/,$string;
my $name = $a[0];
my $error = $a[1];
my $hash = $defs{$name};
delete $hash->{helper}{RUNNING_PID};
my $file = $hash->{helper}{configFile};
delete $hash->{helper}{configFile};
delete $hash->{helper}{startCmd};
if ($error)
{
Log3 $name,3,"$name: Hyperion could not be restarted! Error: $error";
readingsSingleUpdate($hash,"lastError",$error,1);
}
else
{
Log3 $name,3,"$name: Hyperion restarted with configFile $file";
RemoveInternalTimer($hash);
DevIo_Disconnected($hash);
$file =~ s/\.config\.json$//;
readingsSingleUpdate($hash,"configFile",$file,1);
InternalTimer(gettimeofday() + 3,"Hyperion_OpenDev",$hash);
}
return undef;
}
sub Hyperion_Set($@) sub Hyperion_Set($@)
{ {
my ($hash,$name,@aa) = @_; my ($hash,$name,@aa) = @_;
@ -473,7 +563,7 @@ sub Hyperion_Set($@)
if (ReadingsVal($name,".configs","")) if (ReadingsVal($name,".configs",""))
{ {
$Hyperion_sets_local{configFile} = ReadingsVal($name,".configs",""); $Hyperion_sets_local{configFile} = ReadingsVal($name,".configs","");
$attr{$name}{webCmd} = $Hyperion_webCmd_config if (AttrVal($name,"webCmd","") eq $Hyperion_webCmd); $Hyperion_sets_local{binary} = "restart,stop";
} }
$Hyperion_sets_local{adjustRed} = "textField" if (ReadingsVal($name,"adjustRed","")); $Hyperion_sets_local{adjustRed} = "textField" if (ReadingsVal($name,"adjustRed",""));
$Hyperion_sets_local{adjustGreen} = "textField" if (ReadingsVal($name,"adjustGreen","")); $Hyperion_sets_local{adjustGreen} = "textField" if (ReadingsVal($name,"adjustGreen",""));
@ -495,55 +585,70 @@ sub Hyperion_Set($@)
Log3 $name,4,"$name: Hyperion_Set cmd: $cmd"; Log3 $name,4,"$name: Hyperion_Set cmd: $cmd";
Log3 $name,4,"$name: Hyperion_Set value: $value" if ($value); Log3 $name,4,"$name: Hyperion_Set value: $value" if ($value);
Log3 $name,4,"$name: Hyperion_Set duration: $duration, priority: $priority" if ($cmd =~ /^rgb|dim|dimUp|dimDown|effect$/); Log3 $name,4,"$name: Hyperion_Set duration: $duration, priority: $priority" if ($cmd =~ /^rgb|dim|dimUp|dimDown|effect$/);
if ($cmd eq "configFile") if ($cmd =~ /^configFile|binary$/)
{ {
$value = $value.".config.json"; return "Work already/still in progress... Please wait for the current process to finish." if ($hash->{helper}{RUNNING_PID} && !$hash->{helper}{RUNNING_PID}{terminated});
my $confdir = AttrVal($name,"hyperionConfigDir","/etc/hyperion/");
my $binpath = AttrVal($name,"hyperionBin","/usr/bin/hyperiond"); my $binpath = AttrVal($name,"hyperionBin","/usr/bin/hyperiond");
my $bin = (split "/",$binpath)[scalar(split "/",$binpath) - 1]; my $bin = (split "/",$binpath)[scalar(split "/",$binpath) - 1];
$bin =~ s/\.sh$// if ($bin =~ /\.sh$/); $bin =~ s/\.sh$// if ($bin =~ /\.sh$/);
my $confdir = AttrVal($name,"hyperionConfigDir","/etc/hyperion/");
my $user = AttrVal($name,"hyperionSshUser","pi"); my $user = AttrVal($name,"hyperionSshUser","pi");
my $ip = $hash->{IP}; my $ip = $hash->{IP};
my $sudo = ($user eq "root" || int AttrVal($name,"hyperionNoSudo",0) == 1) ? "" : "sudo "; my $sudo = ($user eq "root" || int AttrVal($name,"hyperionNoSudo",0) == 1) ? "" : "sudo ";
my $command = $sudo."killall $bin; sleep 1; ".$sudo."$binpath $confdir$value > /dev/null 2>&1 &"; my $kill = $sudo."kill `pidof $bin`";
my $status; my $ssh;
my $fh; if (!Hyperion_isLocal($ip))
if (Hyperion_isLocal($hash))
{ {
if (open(FH,"$command|")) $ssh = qx(which ssh);
{ chomp $ssh;
$status = <FH>; return "SSH client could NOT be found!" if (!$ssh);
close FH; }
} my $com = Hyperion_isLocal($ip)?"":"$ssh $user\@$ip '";
if ($cmd eq "binary")
{
return "Value of $cmd has to be 'stop' or 'restart'" if ($value !~ /^(stop|restart)$/);
} }
else else
{ {
my $com = qx(which ssh); return "Value of $cmd must be given and must be an available config file!" if (!$value || !grep(/^$value$/,split /,/,ReadingsVal($name,".configs","")));
chomp $com; }
$com .= " $user\@$ip '$command'"; if ($cmd eq "binary" && $value eq "stop")
if (open(FH,"$com|")) {
$com .= $kill;
$com .= Hyperion_isLocal($ip)?"":"'";
Log3 $name,4,"$name: stopCmd: $com";
$com = encode_base64($com);
$hash->{helper}{RUNNING_PID} = BlockingCall("Hyperion_ExecCmd","$name|$com","Hyperion_Kill_finished");
}
elsif (($cmd eq "binary" && $value eq "restart") || $cmd eq "configFile")
{
my $file;
if ($value eq "restart")
{ {
$status = <FH>; $file = ReadingsVal($name,"configFile","")?ReadingsVal($name,"configFile",""):ReadingsVal($name,".configs","")?(split /,/,ReadingsVal($name,".configs",""))[0]:"";
close FH; return "No restart possible because no configFile is available." if (!$file);
} }
else
{
$file = $value;
}
my $start = $com;
$file .= ".config.json";
$com .= $kill;
$start .= "$sudo$binpath $confdir".$file." > /dev/null 2>&1 &";
if (!Hyperion_isLocal($ip))
{
$com .= "'";
$start .= "'";
}
Log3 $name,4,"$name: stopCmd: $com";
Log3 $name,4,"$name: startCmd: $start";
$com = encode_base64($com);
$hash->{helper}{configFile} = $file;
$hash->{helper}{startCmd} = encode_base64($start);
$hash->{helper}{RUNNING_PID} = BlockingCall("Hyperion_ExecCmd","$name|$com","Hyperion_Restart");
} }
if (!$status) return;
{
Log3 $name,4,"$name: restarted Hyperion with $binpath $confdir$value";
$value =~ s/\.config\.json$//;
readingsSingleUpdate($hash,"configFile",$value,1);
return;
}
else
{
Log3 $name,4,"$name: NOT restarted Hyperion with $binpath $confdir$value, status: $status";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"lastError",$status);
readingsBulkUpdate($hash,"serverResponse","ERROR");
readingsBulkUpdate($hash,"state","ERROR");
readingsEndUpdate($hash,1);
return "$name NOT restarted Hyperion with $binpath $confdir$value, status: $status";
}
} }
elsif ($cmd eq "rgb") elsif ($cmd eq "rgb")
{ {
@ -752,7 +857,7 @@ sub Hyperion_Set($@)
my $effs = AttrVal($name,"hyperionCustomEffects",""); my $effs = AttrVal($name,"hyperionCustomEffects","");
$effs .= "\r\n" if ($effs); $effs .= "\r\n" if ($effs);
$effs .= '{"name":"'.$value.'","oname":"'.$eff.'","args":'.ReadingsVal($name,"effectArgs","").'}'; $effs .= '{"name":"'.$value.'","oname":"'.$eff.'","args":'.ReadingsVal($name,"effectArgs","").'}';
$attr{$name}{hyperionCustomEffects} = $effs; CommandAttr(undef,"$name hyperionCustomEffects $effs");
return; return;
} }
elsif ($cmd eq "reopen") elsif ($cmd eq "reopen")
@ -775,7 +880,7 @@ sub Hyperion_Attr(@)
my ($cmd,$name,$attr_name,$attr_value) = @_; my ($cmd,$name,$attr_name,$attr_value) = @_;
my $hash = $defs{$name}; my $hash = $defs{$name};
my $err; my $err;
my $local = Hyperion_isLocal($hash); my $local = Hyperion_isLocal($hash->{IP});
if ($cmd eq "set") if ($cmd eq "set")
{ {
if ($attr_name eq "hyperionBin") if ($attr_name eq "hyperionBin")
@ -963,6 +1068,12 @@ sub Hyperion_devStateIcon($;$)
<ul> <ul>
<code>define Ambilight Hyperion 192.168.1.4 19444 10</code><br> <code>define Ambilight Hyperion 192.168.1.4 19444 10</code><br>
</ul> </ul>
<br><br>
To change config files on your running Hyperion server or to stop/restart your Hyperion server you have to put the following code into your sudoers file (/etc/sudoers) (visudo):
<br><br>
<ul>
<code>fhem ALL=(ALL) NOPASSWD:/usr/bin/hyperiond,/bin/kill</code>
</ul>
<br> <br>
<a name="Hyperion_set"></a> <a name="Hyperion_set"></a>
<p><b>set &lt;required&gt; [optional]</b></p> <p><b>set &lt;required&gt; [optional]</b></p>
@ -988,6 +1099,11 @@ sub Hyperion_devStateIcon($;$)
adjust each color of red separately (comma separated) (R,G,B)<br> adjust each color of red separately (comma separated) (R,G,B)<br>
values from 0 to 255 in steps of 1 values from 0 to 255 in steps of 1
</li> </li>
<li>
<i>binary &lt;restart/stop&gt;</i><br>
restart or stop the hyperion binary<br>
only available after successful "get &lt;name&gt; configFiles"
</li>
<li> <li>
<i>blacklevel &lt;0.00,0.00,0.00&gt;</i><br> <i>blacklevel &lt;0.00,0.00,0.00&gt;</i><br>
adjust blacklevel of each color separately (comma separated) (R,G,B)<br> adjust blacklevel of each color separately (comma separated) (R,G,B)<br>
@ -1110,7 +1226,9 @@ sub Hyperion_devStateIcon($;$)
<li> <li>
<i>configFiles</i><br> <i>configFiles</i><br>
get the available config files in directory from attribute hyperionConfigDir<br> get the available config files in directory from attribute hyperionConfigDir<br>
Will only work properly if at least two config files are found. File names must have no spaces and must end with .config.json . File names must have no spaces and must end with .config.json .<br>
For non-local Hyperion servers you have to configure passwordless SSH login for the user running fhem to the Hyperion server host (http://www.linuxproblem.org/art_9.html), with attribute hyperionSshUser you can set the SSH user for login.<br>
Please watch the log for possible errors while getting config files.
</li> </li>
<li> <li>
<i>devStateIcon</i><br> <i>devStateIcon</i><br>