diff --git a/fhem/FHEM/70_SVDRP.pm b/fhem/FHEM/70_SVDRP.pm deleted file mode 100755 index 784108750..000000000 --- a/fhem/FHEM/70_SVDRP.pm +++ /dev/null @@ -1,980 +0,0 @@ -######################################################################################## -# $Id$ -# -# defmod VDRcontrol SVDRP 192.168.81.20 6419; attr VDRcontrol room Test -# -# SVDRP -# -# control VDR via SVDRP -# refer to http://www.vdr-wiki.de/wiki/index.php/VDR_Optionen -# -# version history -# 1.01.01 first released version for good-willing testers -# -######################################################################################## -# defmod VDRcontrol SVDRP 192.168.81.20; attr VDRcontrol room Beamer; attr VDRcontrol verbose 5 -######################################################################################## -# Useful for learning timer analysis via regexp: -# -# https://www.tutorialspoint.com/execute_perl_online.php -# -##!/usr/bin/perl -# -#$bar = "250 1 Tue Mar 15 09:50:00 2022"; -#if ($bar =~ /^250[ ]\d+[ ][A-Za-z]{3}[ ][A-Za-z]{3}[ ][1-9]{2}[ ][0-9]{2}:[0-9]{2}:[0-9]{2}[ ][0-9]{4}$/) { -# print "Found $bar\n"; -#} else { -# print "not found\n"; -#} -# -#$bar = "250 1760874MB 476308MB 72%"; -#if ($bar =~ /^250[ ]\d+MB[ ]\d+MB[ ]\d+%$/) { -# print "Found $bar\n"; -#} else { -# print "not found\n"; -#} -# -#$bar = "250 4 RTL Tele.visi_on-now45?k#"; -#if ($bar =~ /^250[ ]\d+[ ][A-Za-z0-9\h\.\-_?!#]+$/) { -# print "Found $bar\n"; -#} else { -# print "not found\n"; -#} -# -######################################################################################## -# -# This programm is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# The GNU General Public License can be found at -# http://www.gnu.org/copyleft/gpl.html. -# A copy is found in the textfile GPL.txt and important notices to the license -# from the author is found in LICENSE.txt distributed with these scripts. -# -# This script is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -######################################################################################## - -package main; -use strict; -use warnings; - -use Socket; # For constants like AF_INET and SOCK_STREAM -#use Encode qw(encode); - -use Blocking; -use Time::HiRes qw(gettimeofday); -use POSIX; - -my $version = "0.00.01"; - -my %SVDRP_gets = ( - # -); - -my %SVDRP_defaultsetsRaw = ( - "HITK" => "", - "LSTT" => ":get", - "LSTR" => ":get", - "NEXT" => ":get", - "STAT" => ":disk", - "UPDR" => ":get", - "CHAN" => ":+,-", - "DELT" => "", - "VOLU" =>":+,-,mute", - "cleanUp" => ":noArg", - "connect" => ":noArg" -); - -my %SVDRP_defaultsets = ( - "HitKey" => "", - "ListTimers" => ":get", - "NextTimer" => ":get", - "DiskStatus" => ":get", - "UpdateRecordings" => ":get", - "Channel" => ":+,-", - "DeleteTimer" => "", - "Volume" =>":+,-,mute", - "cleanUp" => ":noArg", - "connect" => ":noArg", - "PowerOff" => ":noArg" -); - -my %SVDRP_defaultsets_unused = ( - "ListRecordings" => ":get" -); - -my %SVDRP_cmdmap = ( - "HitKey" => "HITK", - "ListTimers" => "LSTT", - "NextTimer" => "NEXT", - "DiskStatus" => "STAT", - "UpdateRecordings" => "UPDR", - "Channel" => "CHAN", - "DeleteTimer" => "DELT", - "Volume" => "VOLU" -); - -my %SVDRP_cmdmap_unused = ( - "ListRecordings" => "LSTR" -); - -my %SVDRP_data = ( - # -); - -my %SVDRP_result; -my %SVDRPaddattrs; - -my %SVDRP_sets = %SVDRP_defaultsets; - -sub SVDRP_Define { - my ($hash, $def) = @_; - my @param = split('[ \t]+', $def); - - if(int(@param) < 3) { - return "too few parameters: define SVDRP []"; - } - $hash->{NAME} = $param[0]; - $hash->{IP_Address} = $param[2]; - if (!$param[3]){ - $hash->{port} = "6419"; - } - else{ - $hash->{port} = $param[3]; - } - $hash->{DeviceName} = $param[2].":".$hash->{port}; - - # prevent "reappeared" messages in loglevel 1 - $hash->{devioLoglevel} = 3; - # prevent DevIO from setting "STATE" at connect/disconnect - $hash->{devioNoSTATE} = 1; - # subscribe only to notify from global and self - $hash->{NOTIFYDEV} = "global,TYPE=SVDRP"; - - my $name = $hash->{NAME}; - - # clean up - RemoveInternalTimer($hash, "SVDRP_checkConnection"); - DevIo_CloseDev($hash); - - # force immediate reconnect - delete $hash->{NEXT_OPEN} if ( defined( $hash->{NEXT_OPEN} ) ); - # commented to not automatically connect... - #DevIo_OpenDev($hash, 0, "SVDRP_Init", "SVDRP_Callback"); - - return ; -} - -sub SVDRP_Undef { - my ($hash, $arg) = @_; - RemoveInternalTimer($hash); - BlockingKill( $hash->{helper}{RUNNING_PID} ) if ( defined( $hash->{helper}{RUNNING_PID} ) ); - DevIo_CloseDev($hash); - return ; -} - -sub SVDRP_Shutdown { - my ($hash) = @_; - my $name = $hash->{NAME}; - RemoveInternalTimer($hash); - DevIo_CloseDev($hash); - BlockingKill( $hash->{helper}{RUNNING_PID} ) if ( defined( $hash->{helper}{RUNNING_PID} ) ); - delete $hash->{helper}{nextConnectionCheck} if ( defined( $hash->{helper}{nextConnectionCheck} ) ); - delete $hash->{helper}{nextStatusCheck} if ( defined( $hash->{helper}{nextStatusCheck} ) ); - delete $hash->{helper}{RUNNING_PID} if ( defined( $hash->{helper}{RUNNING_PID} ) ); -} - -sub SVDRP_Initialize { - my ($hash) = @_; - - $hash->{DefFn} = \&SVDRP_Define; - $hash->{UndefFn} = \&SVDRP_Undef; - $hash->{SetFn} = \&SVDRP_Set; - $hash->{AttrFn} = \&SVDRP_Attr; - $hash->{ReadFn} = \&SVDRP_Read; - $hash->{ReadyFn} = \&SVDRP_Ready; - $hash->{NotifyFn} = \&SVDRP_Notify; - $hash->{StateFn} = \&SVDRP_SetState; - $hash->{ShutdownFn} = \&SVDRP_Shutdown; - #$hash->{GetFn} = \&SVDRP_Get; # not required - #$hash->{DeleteFn} = \&SVDRP_Delete; - #$hash->{RenameFn} = \&SVDRP_Rename; - #$hash->{DelayedShutdownFn} = \&SVDRP_DelayedShutdown; - - $hash->{AttrList} = - "delay:1,2,3,4,5 connectionCheck:off,1,15,30,60,120,300,600,3600 AdditionalSettings statusCheckCmd statusCheckInterval:off,1,5,10,15,30,60,300,600,3600 statusOfflineMsg disable:0,1 " - . $readingFnAttributes; -} - -sub SVDRP_Notify($$) { - my ($hash, $devHash) = @_; - my $name = $hash->{NAME}; # own name / hash - my $devName = $devHash->{NAME}; # Device that created the events - my $checkInterval; - my $next; - - if(IsDisabled($name)){ - main::Log3 $name, 5, "[$name]: Notify: $name is disabled by framework!"; - return; - } - - my $events = deviceEvents($devHash,1); - #return if( !$events ); - - # logging of notifies - #main::Log3 $name, 5, "[$name]: running notify from $devName for $name, event is @{$events}"; - - if($devName eq "global" && grep(m/^INITIALIZED|REREADCFG$/, @{$events})){ - - if ( defined( $hash->{AdditionalSettings} )) - { - SVDRP_Attr("set",$name,"AdditionalSettings",$hash->{AdditionalSettings}); - main::Log3 $name, 5, "adding attrs: $name, ".$hash->{AdditionalSettings}; - } - } - return; -} - -sub SVDRP_Attr { - my ($cmd,$name,$attr_name,$attr_value) = @_; - my $hash = $defs{$name}; - my $checkInterval; - my $next; - main::Log3 $name, 5,"[$name]: Attr: executing $cmd $attr_name to $attr_value"; - if($cmd eq "set") { - if ($attr_name eq "AdditionalSettings") { - my @valarray = split / /, $attr_value; - my $key; - my $newkey; - my $newkeyval; - %SVDRPaddattrs = (); - $hash->{AdditionalSettings} = $attr_value; - foreach $key (@valarray) { - #main::Log3 $name, 3,"[$name]: key is $key"; - $newkey = (split /:/, $key, 2)[0]; - $newkeyval = ":".(split /:/, $key, 2)[1]; - main::Log3 $name, 5,"[$name]: Attr: setting $attr_name, key is $newkey, val is $newkeyval"; - $SVDRPaddattrs{$newkey} = $newkeyval; - %SVDRP_sets = (%SVDRP_sets, %SVDRPaddattrs); - } - } - elsif ($attr_name eq "connectionCheck"){ - if ($attr_value eq "0") { - # avoid 0 timer - return "0 not allowed for $attr_name!"; - } - elsif ($attr_value eq "off"){ - RemoveInternalTimer($hash, "SVDRP_checkConnection"); - $hash->{helper}{nextConnectionCheck} = "off"; - } - else{ - RemoveInternalTimer($hash, "SVDRP_checkConnection"); - $checkInterval = $attr_value; - $next = gettimeofday() + $checkInterval; - $hash->{helper}{nextConnectionCheck} = $next; - InternalTimer( $next, "SVDRP_checkConnection", $hash); - main::Log3 $name, 5,"[$name]: Attr: set $attr_name interval to $attr_value"; - } - } - elsif ($attr_name eq "statusCheckInterval"){ - # timer to check status of device - if ($attr_value eq "0") { - # 0 means off - return "0 not allowed for $attr_name!"; - } - elsif ($attr_value eq "off"){ - RemoveInternalTimer($hash, "SVDRP_checkStatus"); - $hash->{helper}{nextStatusCheck} = "off"; - } - else{ - RemoveInternalTimer($hash, "SVDRP_checkStatus"); - $checkInterval = $attr_value; - $next = gettimeofday() + $checkInterval; - $hash->{helper}{nextStatusCheck} = $next; - InternalTimer( $next, "SVDRP_checkStatus", $hash); - main::Log3 $name, 5,"[$name]: Attr: set $attr_name interval to $attr_value"; - } - } - elsif ($attr_name eq "StatusCheckCmd"){ - # decided not to check for allowed commands, user's freedom to define... - } - } - elsif($cmd eq "del"){ - if($attr_name eq "AdditionalSettings") { - %SVDRPaddattrs = (); - %SVDRP_sets = %SVDRP_defaultsets; - main::Log3 $name, 5,"[$name]: Attr: deleting $attr_name"; - } - elsif($attr_name eq "connectionCheck") { - RemoveInternalTimer($hash, "SVDRP_checkConnection"); - delete $hash->{helper}{nextConnectionCheck} if (defined($hash->{helper}{nextConnectionCheck})); - # next 4 lines to set default value 600, timer running ech 600s - #my $next = gettimeofday() + "600"; - #$hash->{helper}{nextConnectionCheck} = $next; - #InternalTimer( $next, "SVDRP_checkConnection", $hash); - #main::Log3 $name, 5,"[$name]: Attr: $attr_name removed, timer set to +600"; - } - elsif($attr_name eq "statusCheckInterval") { - RemoveInternalTimer($hash, "SVDRP_checkStatus"); - delete $hash->{helper}{nextStatusCheck} if (defined($hash->{helper}{nextStatusCheck})); - # next 4 lines to set default value 600, timer running ech 600s - #my $next = gettimeofday() + "600"; - #$hash->{helper}{nextStatusCheck} = $next; - #InternalTimer( $next, "SVDRP_checkStatus", $hash); - #main::Log3 $name, 5,"[$name]: Attr: $attr_name removed, timer set to +600"; - } - elsif($attr_name eq "statusCheckInterval") { - # do nothing - } - } - return ; -} - -sub SVDRP_Ready($){ - my ($hash) = @_; - #return DevIo_OpenDev($hash, 1, undef ); -} - -sub SVDRP_SetState($$$$){ - my ($hash, $time, $readingName, $value) = @_; - my $name = $hash->{NAME}; - Log3 $name, 5, "[$name] SetState called"; - return undef; -} - -sub SVDRP_Get { - # return immediately, not required currently - return "none"; -} - -sub SVDRP_cleanUp { - my ($hash) = @_; - my $name = $hash->{NAME}; - main::Log3 $name, 5, "[$name]: cleanup: sending quit, close DevIo"; - DevIo_SimpleWrite($hash, "quit\r\n", "2"); - RemoveInternalTimer($hash); - BlockingKill( $hash->{helper}{RUNNING_PID} ) if ( defined( $hash->{helper}{RUNNING_PID} ) ); - delete $hash->{helper}{RUNNING_PID} if ( defined( $hash->{helper}{RUNNING_PID} ) ); - # give VDR 1 s to react before we close connection - #my $next = gettimeofday() + 3; - #InternalTimer( $next, "SVDRP_closeDev", $hash); - DevIo_CloseDev($hash); - $hash->{STATE} = "closed"; - $hash->{PARTIAL}=""; - return ; -} - -sub SVDRP_closeDev{ - my ($hash) = @_; - my $name = $hash->{NAME}; - main::Log3 $name, 5,"[$name]: closeDev: closing..."; - DevIo_CloseDev($hash); - $hash->{STATE} = "closed"; - $hash->{PARTIAL}=""; -} - -sub SVDRP_Init($){ - # default: no check - here we just could initializes connection check - my ($hash) = @_; - my $name = $hash->{NAME}; - # main::Log3 $name, 5,"[$name]: Init: DevIo successful, initialize connectionCheck"; - # my $checkInterval = AttrVal( $name, "connectionCheck", "60" ); - # #set checkInterval to 60 just for first check; - # if ($checkInterval eq "off"){$checkInterval = 60;} - - RemoveInternalTimer($hash, "SVDRP_checkConnection"); - - # my $next = gettimeofday() + $checkInterval; - # InternalTimer($next , "SVDRP_checkConnection", $hash); - # #SVDRP_singleWrite("VDRcontrol|STAT|disk"); - return undef; -} - -sub SVDRP_ReInit($){ - my ($hash) = @_; - my $name = $hash->{NAME}; - main::Log3 $name, 5,"[$name]: ReInit: DevIo ReInit done"; - return undef; -} - -sub SVDRP_Callback($){ - # will be executed if connection establishment fails (see DevIo_OpenDev()) - my ($hash, $error) = @_; - my $name = $hash->{NAME}; - - main::Log3 $name, 3, "[$name] DevIo callback error: $error" if ($error); - - my $status = $hash->{STATE}; - if ($status eq "disconnected"){ - # remove timers and pending setValue calls if device is disconnected - main::Log3 $name, 3, "[$name] DevIo callback error: STATE is $status"; - RemoveInternalTimer($hash); - delete $hash->{helper}{nextConnectionCheck} - if ( defined( $hash->{helper}{nextConnectionCheck} ) ); - delete $hash->{helper}{nextStatusCheck} - if ( defined( $hash->{helper}{nextStatusCheck} ) ); - BlockingKill( $hash->{helper}{RUNNING_PID} ) if ( defined( $hash->{helper}{RUNNING_PID} ) ); - - # check if we should update statusCheck - my $checkInterval = AttrVal( $name, "statusCheckInterval", "off" ); - my $checkcmd = AttrVal( $name, "statusCheckCmd", "DiskStatus" ); - my $offlineMsg = AttrVal( $name, "statusOfflineMsg", "offline" ); - - if ($checkInterval ne "off"){ - my $rv = readingsSingleUpdate($hash, $checkcmd, $offlineMsg, 1); - main::Log3 $name, 5,"[$name]: [$name] DevIo callback: $checkcmd set to $offlineMsg"; - return ; - } - } - return undef; -} - -sub SVDRP_Read($){ - # used by devio - my ($hash) = @_; - my $name = $hash->{NAME}; - - # read the available data - my $data = DevIo_SimpleRead($hash); - Log3 $name, 5, "[$name] Read function called"; - # stop processing if no data is available (device disconnected) - return if(!defined($data)); # connection lost - - #Log3 $name, 5, "[$name] Read received: $data"; - - my $buffer = $hash->{PARTIAL}; - #Log3 $name, 3, "[$name] Read: received $data (buffer contains: $buffer)"; - - # concat received data to $buffer - my $result = $data; - $buffer .= $result; - Log3 $name, 5, "[$name] Read: received: $result"; - Log3 $name, 5, "[$name] Read: buffer contains: $buffer"; - - # as long as the buffer contains newlines (complete datagramm) - my $msg = "none"; - while($buffer =~ m/\n/) - { - #my $msg; - # extract the complete message ($msg), everything else is assigned to $buffer - ($msg, $buffer) = split("\n", $buffer, 2); - # remove trailing whitespaces - chomp $msg; - # now we could parse the extracted message, not implemented, since I get no data... - SVDRP_parseMessage($hash, $msg); - } - # update $hash->{PARTIAL} with the current buffer content - $hash->{PARTIAL} = $buffer; - Log3 $name, 5, "[$name] Read: after LF check, msg is: $msg"; - Log3 $name, 5, "[$name] Read: after LF check, buffer contains: $buffer"; -} - -sub SVDRP_parseMessage { - # called from Read with $hash, $msg - # $msg contains one complete line - but one only! - my ($hash, $msg) = @_; - my $name = $hash->{NAME}; - #my ($input) = @_; - #Log3 "VDR", 5, "[VSR] Parse: input: $input"; - #my ($name, $msg) = split "|", $input; - Log3 $name, 5, "[$name] Parse: name: $name, msg: $msg"; - #my $hash = $defs{$name}; - #$msg = $hash->{PARTIAL}; - # strip last "|" - #$msg = substr $msg, 0, -1; - #my @resultarr = split("\\|", $msg); - my $reading = "info"; - my $data; - my $rv; - my $count = 0; - my $output; - my $timers = ""; - my $parsedmsg = ""; - my $code; - - readingsBeginUpdate($hash); - - ### now we should analyse which message was received, and put it to the right reading - if ($msg =~ /^22[0|1]/){ - # format: 220 VDR SVDRP VideoDiskRecorder 2.0.6; Sun Feb 13 17:33:10 2022; UTF-8 - $reading = "info"; - (my $code, $msg) = split (/ /, $msg, 2); - $rv = readingsSingleUpdate($hash, $reading, $msg, 1); - #Log3 $name, 5, "[$name] Parse: updated $reading with '$msg'"; - } - if ($msg =~ /^5\d\d/){ - # format: 5xx some error message - $reading = "error"; - (my $code, $msg) = split (/ /, $msg, 2); - $rv = readingsSingleUpdate($hash, $reading, $msg, 1); - #Log3 $name, 5, "[$name] Parse: updated $reading with '$msg'"; - } - - elsif ($msg =~ /^250[ ]\d+MB[ ]\d+MB[ ]\d+%\s$/){ - # disk status format: 250 1760874MB 476308MB 72% - $reading = "DiskStatus"; - #$rv = readingsSingleUpdate($hash, $reading, $msg, 1); - SVDRP_parseDiskStatus($hash, $reading, $msg); - #Log3 $name, 5, "[$name] Parse: updated $reading with '$msg'"; - } - elsif ($msg =~ /^250[ ]\d+[ ][A-Za-z]{3}[ ][A-Za-z]{3}[ ][1-9]{2}[ ][0-9]{2}:[0-9]{2}:[0-9]{2}[ ][0-9]{4}\s$/){ - # next timer format: 250 1 Tue Mar 15 09:50:00 2022 - $reading = "NextTimer"; - (my $code, $msg) = split (/ /, $msg, 2); - $rv = readingsSingleUpdate($hash, $reading, $msg, 1); - #Log3 $name, 5, "[$name] Parse: updated $reading with $msg"; - } - elsif ($msg =~ /^250[ ]\d+[ ][A-Za-z0-9\h\.\-_?!#]+\s$/){ - # Channel format: 250 4 RTL Television - $reading = "Channel"; - (my $code, $msg) = split (/ /, $msg, 2); - $rv = readingsSingleUpdate($hash, $reading, $msg, 1); - #Log3 $name, 5, "[$name] Parse: updated $reading with $msg" - } - elsif ($msg =~ /^250[ ]Audio[ ]volume[ ]is[ ][0-9]+|[mute]\s$/){ - # Vol format: 250 Audio volume is 245 - $reading = "Volume"; - (my $code, $msg) = split (/ /, $msg, 2); - $rv = readingsSingleUpdate($hash, $reading, $msg, 1); - #Log3 $name, 5, "[$name] Parse: updated $reading with $msg" - } - elsif ($msg =~ /^250[ ]Key[ ][A-Za-z0-9"]+[ ]accepted\s$/){ - # HitKey format: 250 Key "up" accepted - $reading = "HitKey"; - (my $code, $msg) = split (/ /, $msg, 2); - $rv = readingsSingleUpdate($hash, $reading, $msg, 1); - #Log3 $name, 5, "[$name] Parse: updated $reading with $msg" - } - elsif ($msg =~ /^250[-|\h]\d+[ ]\d+:\d+:[A-Za-z-]{7}/ || - $msg =~ /^250[-|\h]\d+[ ]\d+:\d+:\d{4}-\d{2}-\d{2}:\d{4}:\d{4}:\d{2}:\d{2}:[A-Za-z0-9-_!?\.\h]+:\s$/){ - # ListTimer formats: - # 250 1 1:1:MTWTF--@2022-03-15:0950:1115:50:99:Verrückt nach Meer (neu): - # 250 2 1:4:2022-02-13:1858:1915:50:99:RTL Aktuell - Das Wetter: - $reading = "ListTimers"; - # check if we got "250-n" - if (substr($msg, 3, 1) eq "-"){ - ($code, $msg) = split (/-/, $msg, 2); - #Log3 $name, 5, "[$name] Parse: v1: substring contains '-'"; - } - else{ - ($code, $msg) = split (/ /, $msg, 2); - } - $timers = ReadingsVal($name, $reading, ""); - $msg = SVDRP_parseTimer($name, $msg); - #Log3 $name, 5, "[$name] Parse: parseTimer returned $msg"; - $msg = $timers."\n".$msg; - $rv = readingsSingleUpdate($hash, $reading, $msg, 1); - #Log3 $name, 5, "[$name] Parse: v1: updated $reading with $parsedmsg" - } - Log3 $name, 5, "[$name] Parse: updated $reading with '$msg'"; -} - -sub SVDRP_parseDiskStatus{ - my ($hash,$reading,$resultarr) = @_; - my $name = $hash->{NAME}; - my ($code, $disksize, $diskfree, $diskspace) = (split (" ", $resultarr,4)); - my $sizeunit = "GB"; - my $freeunit = "GB"; - my $rv; - # strip unit "MB", keep only numbers - $disksize =~ tr/0-9//cd; - $diskfree =~ tr/0-9//cd; - Log3 $name, 5, "[$name] Parse: Disksize: $disksize, Diskfree: $diskfree"; - $disksize = $disksize / 1024; - if ($disksize > 1000){ - $disksize = sprintf ("%.1f", $disksize / 1024); - $sizeunit = "TB"; - } - else{ - $disksize = sprintf ("%.1f", $disksize); - } - $diskfree = $diskfree / 1024; - if ($diskfree > 1000){ - $diskfree = $diskfree / 1024; - $freeunit = "TB"; - } - else{ - $diskfree = sprintf ("%.1f", $diskfree); - } - my $returnval = "Size: ".$disksize.$sizeunit." | Free: ".$diskfree.$freeunit." | Used: ".$diskspace; - readingsBeginUpdate($hash); - $rv = readingsBulkUpdate($hash, "DiskUsed", $diskspace, 1); - $rv = readingsBulkUpdate($hash, $reading, $returnval, 1); - readingsEndUpdate($hash, 1); - #$rv = readingsBulkUpdate($hash, $reading, $resultarr[0], 1); -} - -sub SVDRP_parseTimer{ - my ($name, $msg) = @_; - #$count = 0; - #$output = ""; - my $parsedmsg = "none"; - my $timerid = "0"; - my $timerstr = "none"; - my $i1 = "0"; - my $i2 = "0", - my $day = "none"; - my $start = "0"; - my $end = "0"; - my $i3 = "0"; - my $i4 = "0"; - my $timername = "none"; - if (!defined($msg)){ - $parsedmsg = "error"; - } - else{ - # format variants: - # 1 1:1:MTWTF--@2022-03-15:0950:1115:50:99:Verrückt nach Meer (neu): - # 2 1:4:2022-02-13:1858:1915:50:99:RTL Aktuell - Das Wetter: - #Log3 $name, 5, "[$name] ParseTimer: reading: $reading, result: $resultarr[$count]"; - ($timerid, $timerstr) = split (" ", $msg,2); - ($i1, $i2, $day, $start, $end, $i3, $i4, $timername) = split (":", $timerstr, 8); - substr ($start, 2, 0) = ":"; - substr ($end, 2, 0) = ":"; - #$output .= "\n" if ($count > 0); # add LF only if first line is contained - $parsedmsg = "ID: ".sprintf("%2s",$timerid)." | Day: ".sprintf("%-10s",$day)." | Start: ".$start." | Stop: ".$end." | Name: ".$timername; - } - #Log3 $name, 5, "[$name] parseTimer: parsed output is $parsedmsg"; - return $parsedmsg; -} - -sub SVDRP_Set { - my ($hash, @param) = @_; - - return '"set SVDRP" needs at least one argument' if (int(@param) < 2); - - my $name = shift @param; - my $opt = shift @param; - my $value = join("", @param); - #my $value = shift @param; - my $msg; - my $msg2; - my $list = ""; - my $optorg = $opt; - my $next; - my $writecmd; - - $hash = $defs{$name}; - - # construct set list - my @cList = (keys %SVDRP_sets); - foreach my $key (@cList){ - $list = $list.$key.$SVDRP_sets{$key}." "; - } - if (!exists($SVDRP_sets{$opt})){ - return "Unknown argument $opt, please choose one of $list"; - } - - # return if device is disabled - if(IsDisabled($name)){ - main::Log3 $name, 5, "[$name]: Set: $name is disabled by framework!"; - return; - } - # empty reading error - readingsSingleUpdate($hash, "error", "", 1); - - if ($opt eq "cleanUp"){ - main::Log3 $name, 5, "[$name]: Set: $name cleanUp"; - SVDRP_cleanUp($hash); - return; - } - - if ($opt eq "connect"){ - main::Log3 $name, 5, "[$name]: Set: $name connect"; - DevIo_OpenDev($hash, 0, "SVDRP_Init", "SVDRP_Callback"); - return; - } - - # $opt is the nice name - read real command from SVDRP_cmdmap - if (exists($SVDRP_cmdmap{$opt})){ - $opt = $SVDRP_cmdmap{$opt}; - main::Log3 $name, 5, "[$name]: Set: converted command to $opt"; - } - # STAT has only one option "disk" - $value = "disk" if ($opt eq "STAT"); - - if ($opt eq "PowerOff"){ - $opt = "HITK"; - $value = "Power"; - } - - if ($opt eq "LSTT"){ - # delete ListTimers, will be re-filled completely - readingsSingleUpdate($hash, "ListTimers", "", 1); - main::Log3 $name, 5, "[$name]: Set: deleted ListTimers, value is now ".ReadingsVal($name,"ListTimers","none"); - } - - # get or no value will sent send $msg to the given command $opt - if ($value eq "get" || !$value){ - $msg = "$opt\r\n"; - $msg2 = $msg; - } - # construct command with value - else { - $msg = "$opt $value\r\n"; - $msg2 = $opt."|".$value."\r\n"; - } - - #delete $hash->{helper}{LastCmd}; - $hash->{STATE} = "query..."; - DevIo_OpenDev($hash, 0, "SVDRP_Init", "SVDRP_Callback"); - # Open connection returns welcome string like - # "220 VDR SVDRP VideoDiskRecorder 2.0.6; Sun Feb 6 21:16:36 2022; UTF-8" - # Read stores received data in $hash->{PARTIAL} - - my $delay = AttrVal( $name, "delay", "1" ); - # give VDR "delay" s to react before we send command - $writecmd = $name."|".$msg."|".$optorg; - $next = gettimeofday() + $delay; - InternalTimer( $next, "SVDRP_singleWrite", $writecmd); - - $msg =~ s/[\r\n]//g; - readingsSingleUpdate($hash, "LastCmd", $msg, 1); - - # give VDR 1 s to react before we close connection - $next = gettimeofday() + (2 * $delay); - InternalTimer( $next, "SVDRP_cleanUp", $hash); - return; -} - -sub SVDRP_singleWrite { - my ($writecmd) = @_; - my ( $name, $msg, $optorg ) = split( "\\|", $writecmd ); - my $hash = $defs{$name}; - #$hash->{helper}{LastCmd} = $optorg; - DevIo_SimpleWrite($hash, $msg, "2"); - main::Log3 $name, 5, "[$name]: singleWrite: sending $msg"; -} - -sub SVDRP_checkConnection ($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - - RemoveInternalTimer($hash, "SVDRP_checkConnection"); - - my $checkInterval = AttrVal( $name, "connectionCheck", "off" ); - - if ($checkInterval eq "off"){ - return ; - } - - # my $status = DevIo_IsOpen($hash); # would just tell if FD exists - # let's try to reopen the connection. If successful, FD is kept or created. - # if not successful, NEXT_OPEN is created. - # $status is always undef, since callback fn is given - my $status = DevIo_OpenDev($hash, 1, "SVDRP_ReInit", "SVDRP_Callback"); - - #delete $hash->{NEXT_OPEN} if ( defined( $hash->{NEXT_OPEN} ) ); - #delete $hash->{helper}{nextConnectionCheck} if ( defined( $hash->{helper}{nextConnectionCheck} ) ); - - if (!($hash->{FD}) && $hash->{NEXT_OPEN}) { - # device was connected, but TCP timeout reached - # DevIo tries to re-open after NEXT_OPEN - # no internal timer needed - delete $hash->{helper}{nextConnectionCheck} - if ( defined( $hash->{helper}{nextConnectionCheck} ) ); - main::Log3 $name, 3, "[$name]: DevIo_Open has no FD, NEXT_OPEN is $hash->{NEXT_OPEN}, no timer set"; - } - elsif (!($hash->{FD}) && !$hash->{NEXT_OPEN}){ - # not connected, DevIo not active, so device won't open again automatically - # should never happen, since we called DevIo_Open above! - # no internal timer needed, but should we ask DevIo again for opening the connection? - #DevIo_OpenDev($hash, 1, "SVDRP_Init", "SVDRP_Callback"); - main::Log3 $name, 3, "[$name]: DevIo_Open has no FD, no NEXT_OPEN, should not happen!"; - } - elsif ($hash->{FD} && $hash->{NEXT_OPEN}){ - # not connected - device was connected, but is not reachable currently - # DevIo tries to connect again at NEXT_OPEN - # should we try to clean up by closing and reopening? - # no internal timer needed - #DevIo_CloseDev($hash); - #DevIo_OpenDev($hash, 1, "SVDRP_Init", "SVDRP_Callback"); - delete $hash->{helper}{nextConnectionCheck} - if ( defined( $hash->{helper}{nextConnectionCheck} ) ); - main::Log3 $name, 3, "[$name]: DevIo_Open has FD and NEXT_OPEN, try to reconnect periodically"; - } - elsif ($hash->{FD} && !$hash->{NEXT_OPEN}){ - # device is connectd, or seems to be (since broken connection is not detected by DevIo!) - # normal state when device is on and reachable - # or when it was on, turned off, but DevIo did not recognize (TCP timeout not reached) - # internal timer makes sense to check, if device is really reachable - my $next = gettimeofday() + $checkInterval; # if checkInterval is off, we won't reach this line - $hash->{helper}{nextConnectionCheck} = $next; - InternalTimer( $next, "SVDRP_checkConnection", $hash); - main::Log3 $name, 3, "[$name]: DevIo_Open has FD but no NEXT_OPEN, next timer set"; - } -} - -sub SVDRP_checkStatus ($){ - my ($hash) = @_; - my $name = $hash->{NAME}; - - my $checkInterval = AttrVal( $name, "statusCheckInterval", "off" ); - my $checkcmd = AttrVal( $name, "statusCheckCmd", "PWR" ); - my $next; - - if ($checkInterval eq "off"){ - RemoveInternalTimer($hash, "SVDRP_checkStatus"); - main::Log3 $name, 5,"[$name]: checkStatus: status timer removed"; - return ; - } - else{ - my $value = "get"; - SVDRP_Set($hash, $name, $checkcmd, $value); - $next = gettimeofday() + $checkInterval; - $hash->{helper}{nextStatusCheck} = $next; - InternalTimer( $next, "SVDRP_checkStatus", $hash); - main::Log3 $name, 5,"[$name]: checkStatus: next status timer set"; - } -} - -################################################### -# end # -################################################### - - -1; - -=pod -=item summary control VDR by SVDRP via (W)Lan -=item summary_DE Steuerung von VDR mittels SVDRP über (W)Lan -=begin html - - -

SVDRP

- -
    - SVDRP implements SVDRP to control VDR via (W)Lan. -

    - - Define -
      - define <name> SVDRP <IP_Address> [<port>] -
      -
      This module helps you for basic control of your VDR. -
      Only a reasonable subset of SVDRP commands in implemented. -
      E.g. it does not make sense to set timers via fhem - vdradmin is much more convenient. -

      -
        -
      • IP_Address - the IP Address of your VDR -
      • -
      • port - ... guess? Yes, the port. If not given, VDR standard port 6419 is used. -
      • -
      • Example: define VDRcontrol SVDRP 10.10.0.1 6419 -
      • -
      -
    -
    - - - Set -
    -
      -
      Available set commands are taken from http://www.vdr-wiki.de/wiki/index.php/SVDRP. -
      For the predefined "raw" commands, "nice" names will be shown for the readings, e.g. DiskStatus instead of STAT disk. -
      Default set commands are -

      -
    • Channel -
      set value can be "+" or "-" or any channel number you want to switch to. -
      set <name> Channel will get you the channel VDR is currently tuned to. -
    • -
      -
    • DeleteTimer -
      set <name> DeleteTimer <number> will delete ... hm, guess? -
      (you can get the timer numbers via ListTimers) -
    • -
      -
    • DiskStatus -
      no value or get will display the current disk usage in DiskStatus -
      Additionally, the reading DiskUsed will be set to the disk fill level. -
    • -
      -
    • HitKey -
      Enables you to send any Key defined by http://www.vdr-wiki.de/wiki/index.php/SVDRP -
      E.g.set <name> HitKey Power will cleanly power off VDR. -
    • -
      -
    • PowerOff -
      A shortcut to cleanly power off VDR, same as set <name> HitKey Power -
    • -
      -
    • ListTimers -
      no value or get will query all timers from VDR. -
      raw answer from VDR will be parsed into a little bit nicer format. -
    • -
    • NextTimer -
      no value or get will exactly get what it says. -
    • -
    • UpdateRecordings -
      no value or get will trigger VDR to re-read the recordings. -
      (No output to fhem - no sense to show all recordings here) -
    • -
    • Volume -
      set value can be "+" or "-" or mute or any Volume (0-255) you want to set. -
      set <name> Volume will get you VDR's current Volume setting. -
    • -
    • connect -
      just connects to VDR, no further action. -
      Reading "info" will be updated. -
      Attention: As long as connection to VDR is open, no other SVDRP client can connect! -
      You might want to use "cleanup" to be able to reconnect other clients. -
    • -
    • cleanup -
      closes connection to VDR, no further action. -
      Reading "info" will be updated. -
    • -
    -
    - - - Attributes -
    -
      -
    • AdditionalSettings -
      cmd1:val_1,...,val_n cmd2:val_1,...,val_n -
      You can specify own set commands here, they will be added to the set list. -
      Multiple own sets can be specified, separated by a blank. -
      command and values are separated by ":", values are separated by ",". -
      Example: HITK:up,down,Power MESG -
    • -
      -
    • connectionCheck -
      off|(value in seconds) -
      value defines the intervall in seconds to perform an connection check. -
      Normally you won't need that. Use at your own risk... -
      Default value is "off". -
    • -
      -
    • statusCheckIntervall -
      off|(value in seconds) -
      value defines the intervall in seconds to perform an status check. -
      Each interval the VDR is queried with the command defined by statusCheckCmd (default: DiskStatus). -
      Default value is off. -
    • -
      -
    • statusCheckCmd -
      (any command(s) you set) -
      Defines the command(s) used by statusCheckIntervall. -
    • -
      -
    • statusOfflineMsg -
      (any message text you set) -
      Defines the message to set in the Reading related to statusCheckCmd when the device goes offline. -
      Status of device will be checked after each statusCheckIntervall (default: off), querying the statusCheckCmd command (default: DiskStatus), and if STATE is disconnected the Reading of statusCheckCmd will be set to this message. Default: closed. -
    • -
    • delay -
      delay time in seconds -
      Depending on the answering speed of your VDR, it might be necessary to grant a certain delay beween opening the connection (and getting the initial answer shown in reading "info"), sending a command, receiving the result and closing the connection. -
      Default: 1. -
    • -
      -
    -
-=end html -=cut