mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-26 10:34:52 +00:00
30_tradfri.pm: new module
CoProcess.pm: new file git-svn-id: https://svn.fhem.de/fhem/trunk@18425 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
e013121eb5
commit
bd285f94a7
@ -1,5 +1,6 @@
|
|||||||
# 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.
|
||||||
|
- new: 30_tradfri: initial release
|
||||||
- new: 42_npmjs: Module to update Node.js modules via NPM package manager
|
- new: 42_npmjs: Module to update Node.js modules via NPM package manager
|
||||||
- feature: 98_Hyperion: add set active/inactive
|
- feature: 98_Hyperion: add set active/inactive
|
||||||
- bugfix: 22_HOMEMODE: fix zero devider in HOMEMODE_ContactOpenCheck
|
- bugfix: 22_HOMEMODE: fix zero devider in HOMEMODE_ContactOpenCheck
|
||||||
|
679
fhem/FHEM/30_tradfri.pm
Normal file
679
fhem/FHEM/30_tradfri.pm
Normal file
@ -0,0 +1,679 @@
|
|||||||
|
|
||||||
|
# $Id$
|
||||||
|
|
||||||
|
package main;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use CoProcess;
|
||||||
|
#require "30_HUEBridge.pm";
|
||||||
|
require "$attr{global}{modpath}/FHEM/30_HUEBridge.pm";
|
||||||
|
|
||||||
|
use JSON;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
|
use POSIX;
|
||||||
|
use Socket;
|
||||||
|
|
||||||
|
use vars qw(%modules);
|
||||||
|
use vars qw(%defs);
|
||||||
|
use vars qw(%attr);
|
||||||
|
use vars qw($readingFnAttributes);
|
||||||
|
use vars qw($FW_ME);
|
||||||
|
|
||||||
|
sub Log($$);
|
||||||
|
sub Log3($$$);
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Initialize($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
$hash->{ReadFn} = "tradfri_Read";
|
||||||
|
$hash->{WriteFn} = "tradfri_Write";
|
||||||
|
|
||||||
|
$hash->{DefFn} = "tradfri_Define";
|
||||||
|
$hash->{NotifyFn} = "tradfri_Notify";
|
||||||
|
$hash->{UndefFn} = "tradfri_Undefine";
|
||||||
|
$hash->{DelayedShutdownFn} = "tradfri_DelayedShutdown";
|
||||||
|
$hash->{ShutdownFn} = "tradfri_Shutdown";
|
||||||
|
$hash->{SetFn} = "tradfri_Set";
|
||||||
|
$hash->{GetFn} = "tradfri_Get";
|
||||||
|
$hash->{AttrFn} = "tradfri_Attr";
|
||||||
|
$hash->{AttrList} = "tradfriFHEM-cmd ".
|
||||||
|
"tradfriFHEM-params ".
|
||||||
|
"tradfriFHEM-securityCode ".
|
||||||
|
"tradfriFHEM-sshHost tradfriFHEM-sshUser ".
|
||||||
|
"disable:1 disabledForIntervals ".
|
||||||
|
"createGroupReadings:1,0 ".
|
||||||
|
$readingFnAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_AttrDefaults($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Define($$)
|
||||||
|
{
|
||||||
|
my ($hash, $def) = @_;
|
||||||
|
|
||||||
|
my @a = split("[ \t][ \t]*", $def);
|
||||||
|
|
||||||
|
return "Usage: define <name> tradfri" if(@a != 2);
|
||||||
|
|
||||||
|
my $name = $a[0];
|
||||||
|
$hash->{NAME} = $name;
|
||||||
|
|
||||||
|
my $d = $modules{$hash->{TYPE}}{defptr};
|
||||||
|
return "$hash->{TYPE} device already defined as $d->{NAME}." if( defined($d) && $name ne $d->{NAME} );
|
||||||
|
$modules{$hash->{TYPE}}{defptr} = $hash;
|
||||||
|
|
||||||
|
tradfri_AttrDefaults($hash);
|
||||||
|
|
||||||
|
$hash->{NOTIFYDEV} = "global";
|
||||||
|
|
||||||
|
if( !AttrVal($name, 'devStateIcon', undef ) ) {
|
||||||
|
CommandAttr(undef, "$name createGroupReadings 0");
|
||||||
|
CommandAttr(undef, "$name stateFormat tradfri-fhem");
|
||||||
|
CommandAttr(undef, "$name devStateIcon stopped:control_home\@red:start stopping:control_on_off\@orange running.*:control_on_off\@green:stop")
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash->{CoProcess} = { name => 'tradfri-fhem',
|
||||||
|
cmdFn => 'tradfri_getCmd',
|
||||||
|
};
|
||||||
|
|
||||||
|
$hash->{helper}{scenes} = {} if( !$hash->{helper}{scenes} );
|
||||||
|
|
||||||
|
if( $init_done ) {
|
||||||
|
CoProcess::start($hash);
|
||||||
|
} else {
|
||||||
|
$hash->{STATE} = 'active';
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Notify($$)
|
||||||
|
{
|
||||||
|
my ($hash,$dev) = @_;
|
||||||
|
|
||||||
|
return if($dev->{NAME} ne "global");
|
||||||
|
return if(!grep(m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}}));
|
||||||
|
|
||||||
|
CoProcess::start($hash);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Undefine($$)
|
||||||
|
{
|
||||||
|
my ($hash, $name) = @_;
|
||||||
|
|
||||||
|
CoProcess::terminate($hash);
|
||||||
|
|
||||||
|
delete $modules{$hash->{TYPE}}{defptr};
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
sub
|
||||||
|
tradfri_DelayedShutdown($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
if( $hash->{PID} ) {
|
||||||
|
$hash->{shutdown} = 1;
|
||||||
|
$hash->{shutdown} = $hash->{CL} if( $hash->{CL} );
|
||||||
|
|
||||||
|
$hash->{reason} = 'shutdown';
|
||||||
|
CoProcess::stop($hash);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Shutdown($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
CoProcess::terminate($hash);
|
||||||
|
|
||||||
|
delete $modules{$hash->{TYPE}}{defptr};
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_processEvent($$) {
|
||||||
|
my ($hash,$decoded) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
my $id = $decoded->{id} ;
|
||||||
|
|
||||||
|
if( $decoded->{r} eq 'scene' ) {
|
||||||
|
if( $decoded->{t} eq 'remove' ) {
|
||||||
|
delete $hash->{helper}{scenes}{$id};
|
||||||
|
Log3 $name, 4, "$name: deleted scene $id";
|
||||||
|
} else {
|
||||||
|
Log3 $name, 4, "$name: ". ($hash->{helper}{scenes}{$id}?'updated':'added') ." scene $id";
|
||||||
|
$hash->{helper}{scenes}{$id} = $decoded;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $code = '';
|
||||||
|
$code = $name ."-". $id if( $decoded->{r} eq 'lights' );
|
||||||
|
$code = $name ."-G". $id if( $decoded->{r} eq 'group' );
|
||||||
|
$code = $name ."-S". $id if( $decoded->{r} eq 'sensor' );
|
||||||
|
|
||||||
|
my $chash = $modules{HUEDevice}{defptr}{$code};
|
||||||
|
|
||||||
|
if( $decoded->{t} eq 'remove' ) {
|
||||||
|
if( $chash ) {
|
||||||
|
fhem( "delete $chash->{NAME} " );
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( defined($chash) ) {
|
||||||
|
HUEDevice_Parse($chash,$decoded);
|
||||||
|
HUEBridge_updateGroups($hash, $chash->{ID}) if( !$chash->{helper}{devtype} );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
my $group;
|
||||||
|
my $cname;
|
||||||
|
my $define;
|
||||||
|
if( $decoded->{r} eq 'lights' ) {
|
||||||
|
$group = 'HUEDevice';
|
||||||
|
$cname = "HUEDevice" . $id;
|
||||||
|
#$cname = $name ."_". $cname if( $hash->{helper}{count} );
|
||||||
|
$define= "$cname HUEDevice $id IODev=$name";
|
||||||
|
} elsif( $decoded->{r} eq 'group' ) {
|
||||||
|
$group = 'HUEGroup';
|
||||||
|
$cname = "HUEGroup" . $id;
|
||||||
|
#$cname = $name ."_". $cname if( $hash->{helper}{count} );
|
||||||
|
$define= "$cname HUEDevice group $id IODev=$name";
|
||||||
|
} elsif( $decoded->{r} eq 'sensor' ) {
|
||||||
|
$group = 'HUESensor';
|
||||||
|
$cname = "HUESensor" . $id;
|
||||||
|
#$cname = $name ."_". $cname if( $hash->{helper}{count} );
|
||||||
|
$define= "$cname HUEDevice sensor $id IODev=$name";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( $define ) {
|
||||||
|
Log3 $name, 4, "$name: create new device '$cname' for address '$id'";
|
||||||
|
Log3 $name, 5, "$name: $define";
|
||||||
|
|
||||||
|
if( my $ret = CommandDefine(undef,$define) ) {
|
||||||
|
Log3 $name, 1, "$name: Autocreate: An error occurred while creating device for id '$id': $ret";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
CommandAttr(undef,"$cname alias ".$decoded->{name});
|
||||||
|
CommandAttr(undef,"$cname room Tradfri");
|
||||||
|
#CommandAttr(undef,"$cname IODev $name");
|
||||||
|
|
||||||
|
CommandAttr(undef, "$name createGroupReadings 1") if( $decoded->{r} eq 'group' );
|
||||||
|
|
||||||
|
HUEDeviceSetIcon($cname);
|
||||||
|
$defs{$cname}{helper}{fromAutocreate} = 1 ;
|
||||||
|
|
||||||
|
CommandSave(undef,undef) if( AttrVal( "autocreate", "autosave", 1 ) );
|
||||||
|
|
||||||
|
my $chash = $modules{HUEDevice}{defptr}{$code};
|
||||||
|
if( defined($chash) ) {
|
||||||
|
HUEDevice_Parse($chash,$decoded);
|
||||||
|
HUEBridge_updateGroups($hash, $chash->{ID}) if( !$chash->{helper}{devtype} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log3 $name, 4, "$name: message for unknow device received: $code: ". Dumper $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Read($) {
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
my $buf = CoProcess::readFn($hash);
|
||||||
|
return undef if( !$buf );
|
||||||
|
|
||||||
|
my $data = $hash->{helper}{PARTIAL};
|
||||||
|
$data .= $buf;
|
||||||
|
|
||||||
|
while($data =~ m/\n/) {
|
||||||
|
($buf,$data) = split("\n", $data, 2);
|
||||||
|
|
||||||
|
Log3 $name, 5, "$name: read: $buf";
|
||||||
|
|
||||||
|
if( $buf =~ m/^\*\*\* ([^\s]+) (.+)/ ) {
|
||||||
|
my $service = $1;
|
||||||
|
my $message = $2;
|
||||||
|
|
||||||
|
if( $service eq 'FHEM:' ) {
|
||||||
|
if( $message =~ m/^connection failed(, (.*))?/ ) {
|
||||||
|
my $reason = $2;
|
||||||
|
|
||||||
|
$hash->{reason} = 'failed to connect to gateway';
|
||||||
|
$hash->{reason} .= ": $reason" if( $reason );
|
||||||
|
|
||||||
|
if( $reason eq 'credentials wrong' ) {
|
||||||
|
fhem( "deletereading $name identity" );
|
||||||
|
fhem( "deletereading $name psk" );
|
||||||
|
|
||||||
|
CoProcess::start($hash);
|
||||||
|
|
||||||
|
} elsif( $reason eq 'credentials missing' ) {
|
||||||
|
CoProcess::stop($hash);
|
||||||
|
|
||||||
|
} elsif( $reason ) { #secret wrong
|
||||||
|
CoProcess::stop($hash);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
CoProcess::start($hash);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} elsif( $message =~ m/^identity: (.*)/ ) {
|
||||||
|
my $identity = $1;
|
||||||
|
readingsSingleUpdate($hash, 'identity', tradfri_encrypt($identity), 1 );
|
||||||
|
|
||||||
|
} elsif( $message =~ m/^psk: (.*)/ ) {
|
||||||
|
my $psk = $1;
|
||||||
|
readingsSingleUpdate($hash, 'psk', tradfri_encrypt($psk), 1 );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log3 $name, 4, "$name: unhandled message: $message";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} elsif( $buf =~ m/this is tradfri-fhem (.*)/ ) {
|
||||||
|
$hash->{'tradfri-fhem version'} = $1;
|
||||||
|
|
||||||
|
} elsif( $buf =~ m/^\{.*\}/ ) {
|
||||||
|
if( my $decoded = eval { JSON->new->utf8(0)->decode($buf) } ) {
|
||||||
|
tradfri_processEvent($hash,$decoded);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log3 $name, 2, "$name: json error: $@ in $buf";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log3 $name, 4, "$name: $buf";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash->{PARTIAL} = $data;
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Write($@)
|
||||||
|
{
|
||||||
|
my ($hash,$chash,$cname,$id,$obj)= @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
return undef if( IsDisabled($name) );
|
||||||
|
|
||||||
|
#$id =~ s'/.*''g;
|
||||||
|
#$obj->{id} = $id;
|
||||||
|
|
||||||
|
$id = $1 if( $id =~ m/^G(\d+)/ );
|
||||||
|
$id = $1 if( $id =~ m/^(\d+)/ );
|
||||||
|
|
||||||
|
$obj->{id} = $id;
|
||||||
|
|
||||||
|
$obj->{t} = 'lights';
|
||||||
|
$obj->{t} = 'group' if( $chash->{helper}{devtype} eq 'G' );
|
||||||
|
|
||||||
|
if( $hash->{FH} ) {
|
||||||
|
my $encoded = encode_json($obj);
|
||||||
|
|
||||||
|
Log3 $name, 5, "$name: writing: $encoded";
|
||||||
|
|
||||||
|
$encoded .= "\n";
|
||||||
|
syswrite( $hash->{FH}, $encoded );
|
||||||
|
} else {
|
||||||
|
Log3 $name, 3, "$name: not connected";
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_getCmd($) {
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
return undef if( !$init_done );
|
||||||
|
|
||||||
|
my $ssh_cmd;
|
||||||
|
if( my $host = AttrVal($name, 'tradfriFHEM-sshHost', undef ) ) {
|
||||||
|
my $ssh = qx( which ssh ); chomp( $ssh );
|
||||||
|
if( my $user = AttrVal($name, 'tradfriFHEM-sshUser', undef ) ) {
|
||||||
|
$ssh_cmd = "$ssh $host -u $user";
|
||||||
|
} else {
|
||||||
|
$ssh_cmd = "$ssh $host";
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $name, 3, "$name: using ssh cmd $ssh_cmd";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $cmd;
|
||||||
|
if( $ssh_cmd ) {
|
||||||
|
$cmd = AttrVal( $name, "tradfriFHEM-cmd", qx( $ssh_cmd which tradfri-fhem ) );
|
||||||
|
} else {
|
||||||
|
$cmd = AttrVal( $name, "tradfriFHEM-cmd", qx( which tradfri-fhem ) );
|
||||||
|
}
|
||||||
|
chomp( $cmd );
|
||||||
|
|
||||||
|
if( !$ssh_cmd && !(-X $cmd) ) {
|
||||||
|
my $msg = "tradfri-fhem not installed. install with 'sudo npm install -g tradfri-fhem'.";
|
||||||
|
$msg = "$cmd does not exist" if( $cmd );
|
||||||
|
return (undef, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cmd = "$ssh_cmd $cmd" if( $ssh_cmd );
|
||||||
|
|
||||||
|
if( my $security_code = AttrVal($name, 'tradfriFHEM-securityCode', undef ) ) {
|
||||||
|
$cmd .= ' -s '. tradfri_decrypt($security_code);
|
||||||
|
} else {
|
||||||
|
my $msg = 'security code missing';
|
||||||
|
return (undef, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( my $identity = ReadingsVal($name, 'identity', undef ) ) {
|
||||||
|
$cmd .= " -i ". tradfri_decrypt($identity) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( my $psk = ReadingsVal($name, 'psk', undef ) ) {
|
||||||
|
$cmd .= " -p ". tradfri_decrypt($psk) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( my $params = AttrVal($name, 'tradfriFHEM-params', undef ) ) {
|
||||||
|
$cmd .= " $params";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( AttrVal( $name, 'verbose', 3 ) == 5 ) {
|
||||||
|
Log3 $name, 2, "$name: starting tradfri-fhem: $cmd";
|
||||||
|
} else {
|
||||||
|
my $msg = $cmd;
|
||||||
|
$msg =~ s/-s\s+[^\s]+/-s sssss/g;
|
||||||
|
$msg =~ s/-i\s+[^\s]+/-i iiiii/g;
|
||||||
|
$msg =~ s/-p\s+[^\s]+/-p ppppp/g;
|
||||||
|
Log3 $name, 2, "$name: starting tradfri-fhem: $msg";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Set($$@)
|
||||||
|
{
|
||||||
|
my ($hash, $name, $cmd, @args) = @_;
|
||||||
|
|
||||||
|
my $list = "";
|
||||||
|
|
||||||
|
if( $cmd eq 'scene' ) {
|
||||||
|
return "usage: scene <id>" if( @args != 1 );
|
||||||
|
|
||||||
|
my $id = $args[0];
|
||||||
|
if( !defined($hash->{helper}{scenes}{$id}) ) {
|
||||||
|
foreach my $key ( keys %{$hash->{helper}{scenes}} ) {
|
||||||
|
if( $id eq $hash->{helper}{scenes}{$key}{name} ) {
|
||||||
|
$id = $key;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "no such scene" if( !defined($hash->{helper}{scenes}{$id}) );
|
||||||
|
}
|
||||||
|
|
||||||
|
my $scene = $hash->{helper}{scenes}{$id};
|
||||||
|
|
||||||
|
my $obj = { 'sceneId' => 0+$id };
|
||||||
|
|
||||||
|
my $code = $name ."-G". $scene->{group};
|
||||||
|
my $chash = $modules{HUEDevice}{defptr}{$code};
|
||||||
|
tradfri_Write($hash, $chash, $chash->{NAME}, $chash->{ID} , $obj);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
|
||||||
|
} elsif( $cmd eq 'statusRequest' ) {
|
||||||
|
#unused
|
||||||
|
return undef;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
my $scenes;
|
||||||
|
foreach my $key ( sort {$a cmp $b} keys %{$hash->{helper}{scenes}} ) {
|
||||||
|
$scenes .=',' if( $scenes );
|
||||||
|
my $name = $hash->{helper}{scenes}{$key}{name};
|
||||||
|
$name =~ s/ /#/g;
|
||||||
|
$scenes .= $name;
|
||||||
|
}
|
||||||
|
if( $scenes ) {
|
||||||
|
$list .= " " if( $list );
|
||||||
|
$list .= " scene:$scenes";
|
||||||
|
}
|
||||||
|
|
||||||
|
return CoProcess::setCommands($hash, $list, $cmd, @args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Get($$@)
|
||||||
|
{
|
||||||
|
my ($hash, $name, $cmd) = @_;
|
||||||
|
|
||||||
|
my $list = 'scenes:noArg';
|
||||||
|
|
||||||
|
if( $cmd eq 'scenes' ) {
|
||||||
|
my $ret;
|
||||||
|
foreach my $key ( sort {$a cmp $b} keys %{$hash->{helper}{scenes}} ) {
|
||||||
|
my $scene = $hash->{helper}{scenes}{$key};
|
||||||
|
|
||||||
|
my $group = $scene->{group};
|
||||||
|
my $code = $name ."-G". $scene->{group};
|
||||||
|
if( my $chash = $modules{HUEDevice}{defptr}{$code} ) {
|
||||||
|
$group = AttrVal( $chash->{NAME}, 'alias', $group );
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret .= sprintf( "%-20s %-20s %-20s", $key, $group, $scene->{name} );
|
||||||
|
$ret .= sprintf( " %s\n", join( ",", @{$scene->{lights}} ) );
|
||||||
|
}
|
||||||
|
if( $ret ) {
|
||||||
|
my $header = sprintf( "%-20s %-20s %-20s", "ID", "GROUP", "NAME" );
|
||||||
|
$header .= sprintf( " %s\n", "LIGHTS" );
|
||||||
|
$ret = $header . $ret;
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown argument $cmd, choose one of $list";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Parse($$;$)
|
||||||
|
{
|
||||||
|
my ($hash,$data,$peerhost) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_encrypt($)
|
||||||
|
{
|
||||||
|
my ($decoded) = @_;
|
||||||
|
my $key = getUniqueId();
|
||||||
|
|
||||||
|
return "" if( !$decoded );
|
||||||
|
return $decoded if( $decoded =~ /^crypt:(.*)/ );
|
||||||
|
|
||||||
|
my $encoded;
|
||||||
|
for my $char (split //, $decoded) {
|
||||||
|
my $encode = chop($key);
|
||||||
|
$encoded .= sprintf("%.2x",ord($char)^ord($encode));
|
||||||
|
$key = $encode.$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'crypt:'. $encoded;
|
||||||
|
}
|
||||||
|
sub
|
||||||
|
tradfri_decrypt($)
|
||||||
|
{
|
||||||
|
my ($encoded) = @_;
|
||||||
|
my $key = getUniqueId();
|
||||||
|
|
||||||
|
return "" if( !$encoded );
|
||||||
|
|
||||||
|
$encoded = $1 if( $encoded =~ /^crypt:(.*)/ );
|
||||||
|
|
||||||
|
my $decoded;
|
||||||
|
for my $char (map { pack('C', hex($_)) } ($encoded =~ /(..)/g)) {
|
||||||
|
my $decode = chop($key);
|
||||||
|
$decoded .= chr(ord($char)^ord($decode));
|
||||||
|
$key = $decode.$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
tradfri_Attr($$$)
|
||||||
|
{
|
||||||
|
my ($cmd, $name, $attrName, $attrVal) = @_;
|
||||||
|
|
||||||
|
my $orig = $attrVal;
|
||||||
|
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
if( $attrName eq 'disable' ) {
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
if( $cmd eq "set" && $attrVal ne "0" ) {
|
||||||
|
$attrVal = 1;
|
||||||
|
CoProcess::stop($hash);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$attr{$name}{$attrName} = 0;
|
||||||
|
CoProcess::start($hash);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} elsif( $attrName eq 'disabledForIntervals' ) {
|
||||||
|
$attr{$name}{$attrName} = $attrVal;
|
||||||
|
|
||||||
|
CoProcess::start($hash);
|
||||||
|
|
||||||
|
} elsif( $attrName eq 'tradfriFHEM-params' ) {
|
||||||
|
$attr{$name}{$attrName} = $attrVal;
|
||||||
|
|
||||||
|
CoProcess::start($hash);
|
||||||
|
|
||||||
|
} elsif( $attrName eq 'tradfriFHEM-sshHost' ) {
|
||||||
|
$attr{$name}{$attrName} = $attrVal;
|
||||||
|
|
||||||
|
CoProcess::start($hash);
|
||||||
|
|
||||||
|
} elsif( $attrName eq 'tradfriFHEM-sshUser' ) {
|
||||||
|
$attr{$name}{$attrName} = $attrVal;
|
||||||
|
|
||||||
|
CoProcess::start($hash);
|
||||||
|
|
||||||
|
} elsif( $attrName eq 'tradfriFHEM-securityCode' ) {
|
||||||
|
if( $cmd eq "set" && $attrVal ) {
|
||||||
|
$attrVal = tradfri_encrypt($attrVal);
|
||||||
|
}
|
||||||
|
$attr{$name}{$attrName} = $attrVal;
|
||||||
|
|
||||||
|
CoProcess::start($hash);
|
||||||
|
|
||||||
|
if( $cmd eq "set" && $orig ne $attrVal ) {
|
||||||
|
$attr{$name}{$attrName} = $attrVal;
|
||||||
|
return "stored obfuscated security code";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( $cmd eq 'set' ) {
|
||||||
|
if( $orig ne $attrVal ) {
|
||||||
|
$attr{$name}{$attrName} = $attrVal;
|
||||||
|
return "stored modified value";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
delete $attr{$name}{$attrName};
|
||||||
|
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
InternalTimer(gettimeofday(), "tradfri_AttrDefaults", $hash, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=pod
|
||||||
|
=item summary Module to control the FHEM/tradfri integration
|
||||||
|
=item summary_DE Modul zur Konfiguration der FHEM/tradfri Integration
|
||||||
|
=begin html
|
||||||
|
|
||||||
|
<a name="tradfri"></a>
|
||||||
|
<h3>tradfri</h3>
|
||||||
|
<ul>
|
||||||
|
Module to control the integration of IKEA tradfri devices with FHEM.<br><br>
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
<ul>
|
||||||
|
<li>JSON has to be installed on the FHEM host.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a name="tradfri_Set"></a>
|
||||||
|
<b>Set</b>
|
||||||
|
<ul>
|
||||||
|
<li>scene <name|id><br>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a name="tradfri_Get"></a>
|
||||||
|
<b>Get</b>
|
||||||
|
<ul>
|
||||||
|
<li>scenes<br>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a name="tradfri_Attr"></a>
|
||||||
|
<b>Attr</b>
|
||||||
|
<ul>
|
||||||
|
<li>tradfriFHEM-securityCode<br>
|
||||||
|
the security code on the back of the gateway</li>
|
||||||
|
<li>tradfriFHEM-cmd<br>
|
||||||
|
The command to use as tradfri-fhem</li>
|
||||||
|
<li>tradfriFHEM-params<br>
|
||||||
|
Additional tradfri-fhem cmdline params.</li>
|
||||||
|
</ul>
|
||||||
|
</ul><br>
|
||||||
|
|
||||||
|
=end html
|
||||||
|
=cut
|
416
fhem/FHEM/CoProcess.pm
Normal file
416
fhem/FHEM/CoProcess.pm
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
|
||||||
|
# $Id$
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
use vars qw(%cmds);
|
||||||
|
use vars qw(%defs);
|
||||||
|
use vars qw($init_done);
|
||||||
|
use vars qw(%selectlist);
|
||||||
|
|
||||||
|
sub CoProcess::Info($$@);
|
||||||
|
|
||||||
|
my %hash = (
|
||||||
|
Fn => "CoProcess::Info",
|
||||||
|
Hlp => ",show info about processes started by CoProcess"
|
||||||
|
);
|
||||||
|
$cmds{coprocessinfo} = \%hash;
|
||||||
|
|
||||||
|
sub
|
||||||
|
CoProcess_Initialize() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
package CoProcess;
|
||||||
|
|
||||||
|
use POSIX;
|
||||||
|
use Socket;
|
||||||
|
|
||||||
|
sub
|
||||||
|
Info($$@) {
|
||||||
|
my @ret;
|
||||||
|
|
||||||
|
foreach my $d (keys %main::defs) {
|
||||||
|
my $h =$main::defs{$d};
|
||||||
|
next if( !defined($h->{CoProcess}) );
|
||||||
|
|
||||||
|
my $line;
|
||||||
|
|
||||||
|
$line = sprintf( "%-15s %-15s %-35s %-19s %-19s %8s %s", $h->{NAME}, $h->{CoProcess}{name}, $h->{CoProcess}{state}, $h->{LAST_START}, $h->{LAST_STOP}, $h->{PID}, $h->{logfile} );
|
||||||
|
|
||||||
|
push @ret, $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
unshift @ret, sprintf( "\n%-15s %-15s %-35s %-19s %-19s %8s %s", 'DEVICE', 'NAME', 'state', 'LAST START', 'LAST STOP', 'PID', 'logfile' ) if( @ret );
|
||||||
|
push @ret, "No CoProcesses are currently used" if(!@ret);
|
||||||
|
|
||||||
|
return join("\n", @ret) ."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub
|
||||||
|
callFn($$) {
|
||||||
|
my ($hash,$n,@params) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
if( !defined($hash->{CoProcess}) || !defined($hash->{CoProcess}{$n}) ) {
|
||||||
|
main::Log3 $name, 4, "$name: CoProcess: no such function: $n";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $fn = "main::$hash->{CoProcess}{$n}";
|
||||||
|
if(wantarray) {
|
||||||
|
no strict "refs";
|
||||||
|
my @ret = &{$fn}($hash, @params);
|
||||||
|
use strict "refs";
|
||||||
|
return @ret;
|
||||||
|
} else {
|
||||||
|
no strict "refs";
|
||||||
|
my $ret = &{$fn}($hash, @params);
|
||||||
|
use strict "refs";
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sub
|
||||||
|
openLogfile($;$) {
|
||||||
|
my ($hash,$logfile) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
closeLogfile($hash) if( $hash->{log} );
|
||||||
|
|
||||||
|
if( !$logfile ) {
|
||||||
|
$logfile = $hash->{logfile};
|
||||||
|
|
||||||
|
if( !$logfile ) {
|
||||||
|
$logfile = main::AttrVal($name, 'CoProc-log', undef);
|
||||||
|
$hash->{logfile} = $logfile if( $logfile );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $logfile && $logfile ne 'FHEM' ) {
|
||||||
|
$hash->{logfile} = $logfile;
|
||||||
|
my @t = localtime(time());
|
||||||
|
$logfile = main::ResolveDateWildcards($logfile, @t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $logfile && $logfile ne 'FHEM' ) {
|
||||||
|
$hash->{currentlogfile} = $logfile;
|
||||||
|
|
||||||
|
main::HandleArchiving($hash);
|
||||||
|
|
||||||
|
if( open( my $fh, ">>$logfile") ) {
|
||||||
|
$fh->autoflush(1);
|
||||||
|
|
||||||
|
$hash->{log} = $fh;
|
||||||
|
|
||||||
|
main::Log3 $name, 3, "$name: using logfile: $logfile";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
main::Log3 $name, 2, "$name: failed to open logile: $logfile: $!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main::Log3 $name, 3, "$name: using FHEM logfile" if( !$hash->{log} );
|
||||||
|
}
|
||||||
|
sub
|
||||||
|
closeLogfile($) {
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
close($hash->{log}) if( $hash->{log} );
|
||||||
|
delete $hash->{log};
|
||||||
|
|
||||||
|
delete $hash->{currentlogfile};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# start co process
|
||||||
|
sub
|
||||||
|
start($;$) {
|
||||||
|
my ($hash,$cmd) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
my $error;
|
||||||
|
($cmd,$error) = callFn($hash,'cmdFn') if( !$cmd );
|
||||||
|
|
||||||
|
if( $error ) {
|
||||||
|
$hash->{CoProcess}{state} = "stopped; $error";
|
||||||
|
main::readingsSingleUpdate($hash, $hash->{CoProcess}{name}, $hash->{CoProcess}{state}, 1 ) if( $hash->{CoProcess}{name} );
|
||||||
|
main::Log3 $name, 2, "$name: $error";
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef if( !$cmd );
|
||||||
|
return undef if( !$main::init_done );
|
||||||
|
return undef if( !$hash->{CoProcess} );
|
||||||
|
return undef if( main::IsDisabled($name) );
|
||||||
|
|
||||||
|
if( $hash->{PID} ) {
|
||||||
|
$hash->{restart} = 1;
|
||||||
|
stop($hash);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
delete $hash->{restart};
|
||||||
|
|
||||||
|
my ($child, $parent);
|
||||||
|
# SOCK_NONBLOCK ?
|
||||||
|
if( socketpair($child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) ) {
|
||||||
|
$child->autoflush(1);
|
||||||
|
$parent->autoflush(1);
|
||||||
|
|
||||||
|
my $pid = main::fhemFork();
|
||||||
|
|
||||||
|
if( !defined($pid) ) {
|
||||||
|
close $parent;
|
||||||
|
close $child;
|
||||||
|
|
||||||
|
main::Log3 $name, 1, "$name: Cannot fork: $!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $pid ) {
|
||||||
|
close $parent;
|
||||||
|
|
||||||
|
$hash->{STARTS}++;
|
||||||
|
|
||||||
|
$hash->{FH} = $child;
|
||||||
|
$hash->{FD} = fileno($child);
|
||||||
|
$hash->{PID} = $pid;
|
||||||
|
|
||||||
|
$main::selectlist{$name} = $hash;
|
||||||
|
|
||||||
|
main::Log3 $name, 3, "$name: starting";
|
||||||
|
$hash->{LAST_START} = main::FmtDateTime( time() );
|
||||||
|
$cmd = (split( ' ', $cmd, 2 ))[0];
|
||||||
|
$hash->{CoProcess}{state} = "running $cmd";
|
||||||
|
main::readingsSingleUpdate($hash, $hash->{CoProcess}{name}, $hash->{CoProcess}{state}, 1 ) if( $hash->{CoProcess}{name} );
|
||||||
|
|
||||||
|
openLogfile($hash);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
close $child;
|
||||||
|
|
||||||
|
close STDIN;
|
||||||
|
close STDOUT;
|
||||||
|
close STDERR;
|
||||||
|
|
||||||
|
my $fn = $parent->fileno();
|
||||||
|
open(STDIN, "<&$fn") or die "can't redirect STDIN $!";
|
||||||
|
open(STDOUT, ">&$fn") or die "can't redirect STDOUT $!";
|
||||||
|
open(STDERR, ">&$fn") or die "can't redirect STDERR $!";
|
||||||
|
|
||||||
|
STDOUT->autoflush(1);
|
||||||
|
STDERR->autoflush(1);
|
||||||
|
|
||||||
|
close $parent;
|
||||||
|
|
||||||
|
exec split( ' ', $cmd ) or main::Log3 $name, 1, "exec failed";
|
||||||
|
|
||||||
|
main::Log3 $name, 1, "set the alexaFHEM-cmd attribut to: <path>/alexa-fhem";
|
||||||
|
|
||||||
|
POSIX::_exit(0);;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
main::Log3 $name, 3, "$name: socketpair failed";
|
||||||
|
main::InternalTimer(time()+20, "CoProcess::start", $hash, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# stop coprocess
|
||||||
|
sub
|
||||||
|
stop($) {
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
main::RemoveInternalTimer($hash);
|
||||||
|
|
||||||
|
return undef if( !$hash->{PID} );
|
||||||
|
|
||||||
|
if( $hash->{PID} ) {
|
||||||
|
kill( SIGTERM, $hash->{PID} );
|
||||||
|
#kill( SIGkILL, $hash->{PID} );
|
||||||
|
# waitpid($hash->{PID}, 0);
|
||||||
|
# delete $hash->{PID};
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash->{CoProcess}{state} = 'stopping';
|
||||||
|
main::readingsSingleUpdate($hash, $hash->{CoProcess}{name}, $hash->{CoProcess}{state}, 1 ) if( $hash->{CoProcess}{name} );
|
||||||
|
|
||||||
|
main::InternalTimer(time()+5, "CoProcess::terminate", $hash, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
# kill co process imediately
|
||||||
|
sub
|
||||||
|
terminate($) {
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
main::RemoveInternalTimer($hash);
|
||||||
|
|
||||||
|
return undef if( !$hash->{PID} );
|
||||||
|
return undef if( !$hash->{FD} );
|
||||||
|
|
||||||
|
kill( SIGKILL, $hash->{PID} );
|
||||||
|
waitpid($hash->{PID}, 0);
|
||||||
|
delete $hash->{PID};
|
||||||
|
|
||||||
|
close($hash->{FH}) if($hash->{FH});
|
||||||
|
delete($hash->{FH});
|
||||||
|
delete($hash->{FD});
|
||||||
|
delete($main::selectlist{$name});
|
||||||
|
|
||||||
|
closeLogfile($hash) if( $hash->{log} );
|
||||||
|
|
||||||
|
$hash->{PARTIAL} = "" if( defined($hash->{PARTIAL}) );
|
||||||
|
|
||||||
|
main::Log3 $name, 3, "$name: stopped";
|
||||||
|
$hash->{LAST_STOP} = main::FmtDateTime( time() );
|
||||||
|
|
||||||
|
$hash->{CoProcess}{state} = 'stopped';
|
||||||
|
if( $hash->{reason} ) {
|
||||||
|
$hash->{CoProcess}{state} .= "; $hash->{reason}";
|
||||||
|
delete $hash->{reason};
|
||||||
|
}
|
||||||
|
main::readingsSingleUpdate($hash, $hash->{CoProcess}{name}, $hash->{CoProcess}{state}, 1 ) if( $hash->{CoProcess}{name} );
|
||||||
|
|
||||||
|
if( $hash->{undefine} ) {
|
||||||
|
my $cl = $hash->{undefine};
|
||||||
|
|
||||||
|
delete $hash->{undefine};
|
||||||
|
main::CommandDelete(undef, $name);
|
||||||
|
main::Log3 $name, 2, "$name: deleted";
|
||||||
|
|
||||||
|
main::asyncOutput( $cl, "$name: deleted\n" ) if( ref($cl) eq 'HASH' && $cl->{canAsyncOutput} );
|
||||||
|
|
||||||
|
} elsif( $hash->{shutdown} ) {
|
||||||
|
my $cl = $hash->{shutdown};
|
||||||
|
|
||||||
|
delete $hash->{shutdown};
|
||||||
|
main::asyncOutput( $cl, "$name: stopped\n" ) if( ref($cl) eq 'HASH' && $cl->{canAsyncOutput} );
|
||||||
|
main::CancelDelayedShutdown($name);
|
||||||
|
|
||||||
|
} elsif( $hash->{restart} ) {
|
||||||
|
start($hash)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#read from co process and handle logging
|
||||||
|
sub
|
||||||
|
readFn($) {
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
my $buf;
|
||||||
|
my $ret = sysread($hash->{FH}, $buf, 65536 );
|
||||||
|
|
||||||
|
if(!defined($ret) || $ret <= 0) {
|
||||||
|
main::Log3 $name, 3, "$name: read: error during sysread: $!" if(!defined($ret));
|
||||||
|
main::Log3 $name, 3, "$name: read: end of file reached while sysread" if( $ret <= 0);
|
||||||
|
|
||||||
|
my $oldstate = $hash->{CoProcess}{state};
|
||||||
|
|
||||||
|
terminate($hash);
|
||||||
|
|
||||||
|
return undef if( $oldstate !~ m/^running/ );
|
||||||
|
|
||||||
|
my $delay = 20;
|
||||||
|
if( $hash->{'LAST_START'} && $hash->{'LAST_STOP'} ) {
|
||||||
|
my $diff = main::time_str2num($hash->{'LAST_STOP'}) - main::time_str2num($hash->{'LAST_START'});
|
||||||
|
|
||||||
|
if( $diff > 60 ) {
|
||||||
|
$delay = 0;
|
||||||
|
main::Log3 $name, 4, "$name: last run duration was $diff sec, restarting imediately";
|
||||||
|
} else {
|
||||||
|
main::Log3 $name, 4, "$name: last run duration was only $diff sec, restarting with delay";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
main::Log3 $name, 4, "$name: last run duration unknown, restarting with delay";
|
||||||
|
}
|
||||||
|
main::InternalTimer(time()+$delay, "CoProcess::start", $hash, 0);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $hash->{logfile} ) {
|
||||||
|
if( $hash->{log} ) {
|
||||||
|
my @t = localtime(time());
|
||||||
|
my $logfile = main::ResolveDateWildcards($hash->{logfile}, @t);
|
||||||
|
openLogfile($hash, $logfile) if( $hash->{currentlogfile} ne $logfile );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $hash->{log} ) {
|
||||||
|
print {$hash->{log}} "$buf";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
#my $buf = $buf;
|
||||||
|
$buf =~ s/\n$//s;
|
||||||
|
main::Log3 $name, 3, "$name: $buf";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( my $disabled = main::IsDisabled($hash) && !$hash->{CoProcess}{state} eq 'stopping' ) {
|
||||||
|
$hash->{reason} = 'disabledForIntervals';
|
||||||
|
CoProcess::stop($hash);
|
||||||
|
|
||||||
|
if( $disabled == 2 ) {
|
||||||
|
$hash->{disabled} = 1;
|
||||||
|
#FIXME: add timer to restart coprocess if disabledForIntervals has elapsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
# add CoProcess specific commands
|
||||||
|
sub
|
||||||
|
setCommands($$@) {
|
||||||
|
my ($hash, $list, $cmd, @a) = @_;
|
||||||
|
|
||||||
|
#my %cp_list = (
|
||||||
|
# 'start' => ':noArg',
|
||||||
|
# 'stop' => ':noArg',
|
||||||
|
# 'restart' => ':noArg',
|
||||||
|
#);
|
||||||
|
|
||||||
|
|
||||||
|
if( $cmd eq 'start' ) {
|
||||||
|
start($hash);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
|
||||||
|
} elsif( $cmd eq 'stop' ) {
|
||||||
|
stop($hash);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
|
||||||
|
} elsif( $cmd eq 'restart' ) {
|
||||||
|
start($hash);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$list .= ' ' if( $list );
|
||||||
|
$list .= 'start:noArg stop:noArg restart:noArg';
|
||||||
|
#foreach my $key ( sort keys %cp_list ) {
|
||||||
|
# if( $list !~ m/\b$key\b/ ) {
|
||||||
|
# $list .= ' ' if( $list );
|
||||||
|
# $list .= $key.$cp_list{$key};
|
||||||
|
# }
|
||||||
|
#}
|
||||||
|
|
||||||
|
return "Unknown argument $cmd, choose one of $list";
|
||||||
|
}
|
||||||
|
|
||||||
|
# add CoProcess specific attributes
|
||||||
|
use vars qw($CoProcessAttributes);
|
||||||
|
#no warnings 'qw';
|
||||||
|
my @attrList = qw(
|
||||||
|
CoProc-cmd
|
||||||
|
CoProc-log
|
||||||
|
CoProc-params
|
||||||
|
CoProc-sshHost
|
||||||
|
CoProc-sshUser
|
||||||
|
);
|
||||||
|
$CoProcessAttributes = join(" ", @attrList);
|
||||||
|
|
||||||
|
#TODO...
|
@ -152,8 +152,8 @@ FHEM/24_TPLinkHS110.pm VolkerKettenbach Sonstige Systeme
|
|||||||
FHEM/26_tahoma.pm mike3436 Sonstige Systeme
|
FHEM/26_tahoma.pm mike3436 Sonstige Systeme
|
||||||
FHEM/30_DUOFERN telekatz Sonstige Systeme
|
FHEM/30_DUOFERN telekatz Sonstige Systeme
|
||||||
FHEM/30_ENECSYSGW.pm akw Sonstige Systeme
|
FHEM/30_ENECSYSGW.pm akw Sonstige Systeme
|
||||||
FHEM/30_HUEBridge.pm justme1968 Beleuchtung
|
FHEM/30_HUEBridge.pm justme1968 Zigbee
|
||||||
FHEM/30_LIGHTIFY.pm justme1968 Beleuchtung
|
FHEM/30_LIGHTIFY.pm justme1968 Zigbee
|
||||||
FHEM/30_MilightBridge.pm mattwire Beleuchtung
|
FHEM/30_MilightBridge.pm mattwire Beleuchtung
|
||||||
FHEM/30_pilight_contact.pm risiko Sonstige Systeme
|
FHEM/30_pilight_contact.pm risiko Sonstige Systeme
|
||||||
FHEM/30_pilight_dimmer.pm risiko Sonstige Systeme
|
FHEM/30_pilight_dimmer.pm risiko Sonstige Systeme
|
||||||
@ -161,9 +161,10 @@ FHEM/30_pilight_raw.pm risiko Sonstige Systeme
|
|||||||
FHEM/30_pilight_smoke.pm risiko Sonstige Systeme
|
FHEM/30_pilight_smoke.pm risiko Sonstige Systeme
|
||||||
FHEM/30_pilight_switch.pm risiko Sonstige Systeme
|
FHEM/30_pilight_switch.pm risiko Sonstige Systeme
|
||||||
FHEM/30_pilight_temp.pm risiko Sonstige Systeme
|
FHEM/30_pilight_temp.pm risiko Sonstige Systeme
|
||||||
|
FHEM/30_tradfri.pm justme1968 Zigbee
|
||||||
FHEM/31_Aurora.pm justme1968 Beleuchtung
|
FHEM/31_Aurora.pm justme1968 Beleuchtung
|
||||||
FHEM/31_ENECSYSINV.pm akw Sonstige Systeme
|
FHEM/31_ENECSYSINV.pm akw Sonstige Systeme
|
||||||
FHEM/31_HUEDevice.pm justme1968 Beleuchtung
|
FHEM/31_HUEDevice.pm justme1968 Zigbee
|
||||||
FHEM/31_LightScene.pm justme1968 Automatisierung
|
FHEM/31_LightScene.pm justme1968 Automatisierung
|
||||||
FHEM/31_MilightDevice.pm mattwire Beleuchtung
|
FHEM/31_MilightDevice.pm mattwire Beleuchtung
|
||||||
FHEM/31_Nello.pm neumann Sonstige Systeme
|
FHEM/31_Nello.pm neumann Sonstige Systeme
|
||||||
@ -521,6 +522,8 @@ FHEM/Blocking.pm rudolfkoenig Automatisierung
|
|||||||
FHEM/Color.pm justme1968 Sonstiges
|
FHEM/Color.pm justme1968 Sonstiges
|
||||||
FHEM/DarkSkyAPI.pm CoolTux Unterstuetzende Dienste/Wettermodule
|
FHEM/DarkSkyAPI.pm CoolTux Unterstuetzende Dienste/Wettermodule
|
||||||
FHEM/DevIo.pm rudolfkoenig Sonstiges
|
FHEM/DevIo.pm rudolfkoenig Sonstiges
|
||||||
|
FHEM/CoProcess.pm justme1968 Sonstiges
|
||||||
|
FHEM/Color.pm justme1968 Sonstiges
|
||||||
FHEM/FritzBoxUtils.pm rudolfkoenig FRITZ!Box
|
FHEM/FritzBoxUtils.pm rudolfkoenig FRITZ!Box
|
||||||
FHEM/GPUtils.pm ntruchsess FHEM Development
|
FHEM/GPUtils.pm ntruchsess FHEM Development
|
||||||
FHEM/HMCCUConf.pm zap HomeMatic
|
FHEM/HMCCUConf.pm zap HomeMatic
|
||||||
|
Loading…
x
Reference in New Issue
Block a user