##############################################
# $Id: 98_USB_1Wire.pm  72788 2015-10-20  v3.1
# The file is taken from the FHEMduino project and modified in serval ways for processing the incomming messages
# see http://www.fhemwiki.de/wiki/USB_1Wire
# It was modified also to provide support for raw message handling which it's send from the USB_1Wire
# The purpos is to use it as addition to the USB_1Wire which runs on an arduno nano or arduino uno.
# It routes Messages serval Modules which are already integrated in FHEM. But there are also modules which comes with it.
# N. Butzek, S. Butzek, 2014-2015
#


package main;

use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
use Data::Dumper qw(Dumper);


sub USB_1Wire_Attr(@);
sub USB_1Wire_Clear($);
sub USB_1Wire_Read($);
sub USB_1Wire_Ready($);
sub USB_1Wire_Write($$$);


my $debug=0;

my %sets = (
  "raw"       => '',
#  "test"      => '',
  "flash"     => 'Nano_1_wire_2B_V_1.2.6.ino.hex,Nano_1_wire_3B_V_1.2.6.ino.hex',
);

sub
USB_1Wire_Initialize($)
{
  my ($hash) = @_;

  require "$attr{global}{modpath}/FHEM/DevIo.pm";

  $hash->{ReadFn}  				= "USB_1Wire_Read";
  $hash->{ReadyFn} 				= "USB_1Wire_Ready";
  $hash->{DefFn}  		 		= "USB_1Wire_Define";
  $hash->{UndefFn} 		 		= "USB_1Wire_Undef";
  $hash->{GetFn}   				= "USB_1Wire_Get";
  $hash->{SetFn}   				= "USB_1Wire_Set";
  $hash->{AttrFn}				= "USB_1Wire_Attr";
  $hash->{ShutdownFn}			= "USB_1Wire_Shutdown";
  
  $hash->{AttrList} =
  "intervall " .
  "Sensor_01 " .
  "Sensor_02 " .
  "Sensor_03 " .
  "Sensor_04 " .
  "Sensor_05 " .
  "Sensor_06 " .
  "Sensor_07 " .
  "Sensor_08 " .
  "Sensor_09 " .
  "Sensor_10 " .
  "Sensor_11 " .
  "Sensor_12 " .
  "Sensor_13 " .
  "Sensor_14 " .
  "Sensor_15 " .
  "Sensor_16 " .
  "Sensor_17 " .
  "Sensor_18 " .
  "Sensor_19 " .
  "Sensor_20 " .
  "Sensor_21 " .
  "Sensor_22 " .
  "Sensor_23 " .
  "Sensor_24 " .
  "Sensor_25 " .
  "Sensor_26 " .
  "Sensor_27 " .
  $readingFnAttributes;


}

#####################################
sub
USB_1Wire_Define($$)
{
  my ($hash, $def) = @_;
  my @a = split("[ \t][ \t]*", $def);

  if(@a != 3) {
    my $msg = "wrong syntax: define <name> USB_1Wire {none | devicename[\@baudrate] | devicename\@directio | hostname:port}";
    Log3 undef, 2, $msg;
    return $msg;
  }
  
  DevIo_CloseDev($hash);
  my $name = $a[0];
  my $dev = $a[2];
  
  if($dev eq "none") {
    Log3 $name, 1, "$name device is none, commands will be echoed only";
    $attr{$name}{dummy} = 1;
    #return undef;
  }
  
  $dev .= "\@115200" if( $dev ne "none" && $dev !~ m/\@/ );

  $hash->{DeviceName} = $dev;
  if($dev eq "none") {
  	return undef;
  }
  my $ret = DevIo_OpenDev($hash, 0, "USB_1Wire_DoInit");
  
  $hash->{"TIME"}=time();
  $hash->{VERSION_USB_1Wire}= "V_1.2.6";
  $hash->{VERSION_NANO}="0";
  $hash->{helper}{HILFE} = "";
  

}

#####################################
sub
USB_1Wire_Undef($$)
{
  my ($hash, $arg) = @_;
  my $name = $hash->{NAME};

  foreach my $d (sort keys %defs) {
    if(defined($defs{$d}) &&
       defined($defs{$d}{IODev}) &&
       $defs{$d}{IODev} == $hash)
      {
        my $lev = ($reread_active ? 4 : 2);
        Log3 $name, $lev, "deleting port for $d";
        delete $defs{$d}{IODev};
      }
  }

  USB_1Wire_Shutdown($hash);
  DevIo_CloseDev($hash); 
  RemoveInternalTimer($hash);    
  return undef;
}

#####################################
sub
USB_1Wire_Shutdown($)
{
  my ($hash) = @_;
  #USB_1Wire_SimpleWrite($hash, "X00");  # Switch reception off, it may hang up the USB_1Wire
  return undef;
}

##############################################################  GET ###########################################
sub USB_1Wire_Get($$@)
{
	#my ( $hash, $name, $opt, @args ) = @_;
    my ( $hash, $name, $opt ) = @_;
	return "\"get $name\" needs at least one argument" unless(defined($opt));

	#if($opt eq "Version") 
	#{
	#USB_1Wire_SimpleWrite($hash, "9", 2);
	#my ($ID_1,$Alias_1,$Offset_1) = split (" ",AttrVal($name, "Sensor_01", "Leer"));
	#my @zy = split (" ",($attr{$name}));
	#return $hash->{TYPE}."  ".$attr{$name}{Sensor_01}."  ".$name; #$Alias_1;  {AttrList}
	#}
	if($opt eq "Hilfe")
	{
	if ($hash->{helper}{HILFE} eq "") 
	{
	USB_1Wire_SimpleWrite($hash, "?", 2);
	$hash->{helper}{HILFE} = DevIo_TimeoutRead($hash, 1);
	}
	return $hash->{helper}{HILFE};
	}
	else
	{
		return "Unknown argument $opt, choose one of Hilfe:noArg ";
	}
}


############################################# Set ###################################################
sub
USB_1Wire_Set($@)
{
  my ( $hash, $name, $cmd, @args ) = @_;

	return "\"set $name\" needs at least one parameter" unless(defined($cmd));  #if(@args < 1);
	#return "\"set $name\" needs at least two arguments choose one of raw flash:Nano_1_wire_2B_V_1.1.4.ino.hex,Nano_1_wire_3B_V_1.1.4.ino.hex" unless(defined($cmd));

my %my_sets = %sets;
  %my_sets = ( %my_sets,  %{$hash->{additionalSets}} ) if ( defined($hash->{additionalSets}) );
  
  if (!defined($my_sets{$cmd})) {
    my $arguments = ' ';
    foreach my $arg (sort keys %my_sets) {
      $arguments.= $arg . ($my_sets{$arg} ? (':' . $my_sets{$arg}) : '') . ' ';
    }
    #Log3 $hash, 3, "$name: Set, arg = $my_sets{'flash'}";
    return "Unknown argument choose one of " . $arguments;
  }

	if($cmd eq "flash")
	{
	
	my $filename = "./FHEM/firmware/$args[0]";
	if (-e $filename)                        # überprüfen ob die Datei exestiert
	{
    ; 
	} else {
    return "File $filename nicht gefunden  probiere update all https://wismar-wetter.lima-city.de/pi/controls_USB_1Wire.txt" ;
	}
	
	my $flashCommand;
	my $logFile = AttrVal("global", "logdir", "./log/") . "$hash->{TYPE}-Flash.log";
	my  $log .= "flashing Arduino $name\n";
	my @deviceName = split('@', $hash->{DeviceName});
	my $port = $deviceName[0];
	my @flash_Version = split(",", $my_sets{'flash'});                       #my @z = split("=", $rmsg)
	   if(($args[0] eq $flash_Version[0]) or ($args[0] eq $flash_Version[1]))
		{
		$flashCommand = "avrdude -c arduino -b 57600 -P $port -p atmega328p -vv -U flash:w:./FHEM/firmware/$args[0] 2>./log/USB_1Wire-Flash.log";
		$log .= "hex file: $args[0]\n";
	    $log .= "port: $port\n";
	    $log .= "log file: $logFile\n";
		if($flashCommand ne "") {
	      if (-e $logFile) {
	        unlink $logFile;
	      }
	
	      DevIo_CloseDev($hash);
	      $hash->{STATE} = "FIRMWARE UPDATE running";
	      $log .= "$name closed\n";
	
	      my $avrdude = $flashCommand;
	      $log .= "command: $avrdude\n\n";
	      `$avrdude`;
	
	      local $/=undef;
	      if (-e $logFile) {
	        open FILE, $logFile;
	        my $logText = <FILE>;
	        close FILE;
	        $log .= "--- AVRDUDE ---------------------------------------------------------------------------------\n";
	        $log .= $logText;
	        $log .= "--- AVRDUDE ---------------------------------------------------------------------------------\n\n";
	      }
	      else {
	        $log .= "WARNING: avrdude created no log file\n\n";
	      }
	
	    }
	    else {
	      $log .= "\n\nNo flashCommand found. Please define this attribute.\n\n";
	    }
	
	    my $ret = DevIo_OpenDev($hash, 0, "USB_1Wire_DoInit");
	    $log .= "$name opened\n";
		
		}
	   else
	   {
	      return "Unknown value $args[0] for $cmd, choose one of $my_sets{'flash'}";
	   }   
	}
	
	elsif($cmd eq "raw")
	{
	   if (!defined ($args[0])) {
	   return "Unknown value for raw choose one of ... see help";
	}
	   my $sendData = $args[0];
	   USB_1Wire_SimpleWrite($hash, $sendData);
	    
	}
	else
	{
		return "Unknown argument $cmd, choose one of flash raw";
	}
}
#####################################
sub
USB_1Wire_Clear($)
{
  my $hash = shift;

  # Clear the pipe
  $hash->{RA_Timeout} = 0.1;
  for(;;) {
    my ($err, undef) = USB_1Wire_ReadAnswer($hash, "Clear", 0, undef);
    last if($err && $err =~ m/^Timeout/);
  }
  delete($hash->{RA_Timeout});
}

#####################################
sub
USB_1Wire_ResetDevice($)
{
  my ($hash) = @_;

  DevIo_CloseDev($hash);
  my $ret = DevIo_OpenDev($hash, 0, "USB_1Wire_DoInit");

  return $ret;
}

#####################################
sub
USB_1Wire_DoInit($)
{
	
	my $hash = shift;
	my $name = $hash->{NAME};
	my $err;
	my $msg = undef;
	#my @deviceName = split('@', $hash->{DeviceName});

	Log3 $name, 4, ">>>USB_1Wire_DoInit";

  	Log3 $name, 1, $hash->{DEF};
	#Log3 $name, 3, $deviceName[0];
	
  	
	#USB_1Wire_SimpleWrite($hash, "027" ,2);  # Zicki noch über attr definieren
	readingsSingleUpdate($hash, "state", "opened", 1);

	return undef;
}


#####################################
sub
USB_1Wire_Write($$$)
{
  my ($hash,$fn,$msg) = @_;

  my $name = $hash->{NAME};

  Log3 $name, 5, "$hash->{NAME} sending $fn$msg";
  my $bstring = "$fn$msg";

  USB_1Wire_SimpleWrite($hash, $bstring);

}
#####################################
# called from the global loop, when the select for hash->{FD} reports data
sub
USB_1Wire_Read($)
{
  my ($hash) = @_;

  my $buf = DevIo_SimpleRead($hash);
  return "" if(!defined($buf));
  my $name = $hash->{NAME};
  
  if ($buf =~ m"\x02") {
  $buf = $';
  $buf =~ tr/\x02//d;
  $hash->{PARTIAL} = "";
  
  }

  my $USB_1Wiredata = $hash->{PARTIAL};
  $USB_1Wiredata .= $buf;
  $hash->{PARTIAL_Length} = length($USB_1Wiredata);
  Log3 $name, 4, "RAW READ:  $buf"; 
  Log3 $name, 4, "USB_1Wiredata:  $USB_1Wiredata"; 
  $hash->{PARTIAL} = $USB_1Wiredata;

	if ($USB_1Wiredata =~ m"\x03") {
	$USB_1Wiredata =~ tr/\x03\x0A//;
	$hash->{PARTIAL} = $USB_1Wiredata;

  while($USB_1Wiredata =~ m/\n/) {
    my $rmsg;
    ($rmsg,$USB_1Wiredata) = split("\n", $USB_1Wiredata, 2);
    $rmsg =~ s/\r//;
    Log3 $name, 4, "USB_1Wire/msg READ: $rmsg"; 

###################################################################  Sensor.. und Temperatur  #########################################
    if($rmsg =~ m"^[A-Z]=") {		# ist A= bis Z=
	my @z = split("=", $rmsg);			 #Zeichenkette bei = gespittet und im Array z[0] und z[1] abgelegt
    readingsSingleUpdate($hash, "Sensor_".$z[0], $z[1], 1);
	}
#############################################################  ID und Temperatur  #################
	elsif($rmsg =~ m"^ID:") {			# Ist ID: dann
	my ($ID,$Temperatur) = split("=", $');			# In S' gesamte Zeichenkette nach ID: wird bei = gespittet und in $ID und $Temperatur abgelegt
	if (!defined $Temperatur){
	return;
	}
	for(my $Nummer = 1;$Nummer < 27;$Nummer ++) {
	my $Sensor = "Sensor_".sprintf("%02d", $Nummer);
	if(!defined($ID)) {Log3 $name, 3, "USB_1Wire ".$name." Fehler ".$ID." Ende"
	}
	if ($attr{$name}{$Sensor} =~ m/$ID/) { 
	my ($ATTR_ID,$Alias,$Offset) = split (" ",$attr{$name}{$Sensor});
	$ID = $Alias; $Temperatur = sprintf("%.2f",($Temperatur + $Offset)); #readingsSingleUpdate($hash, $Alias, $z[1]+$Offset, 1);
	last;
	}
	}
    readingsSingleUpdate($hash, $ID, $Temperatur, 1);
	}
	 elsif($rmsg =~ m"^Pins=") {			# Ist Pins= dann
	 my ($B16,$B15,$B14,$B13,$B12,$B11,$B10,$B09,$B08,$B07,$B06,$B05,$B04,$B03,$B02,$B01) = split(//, $');
	 readingsBeginUpdate($hash);
	 readingsBulkUpdate($hash, "D02", $B01, 1);
	 readingsBulkUpdate($hash, "D03", $B02, 1);
	 readingsBulkUpdate($hash, "D04", $B03, 1);
	 readingsBulkUpdate($hash, "D05", $B04, 1);
	 readingsBulkUpdate($hash, "D06", $B05, 1);
	 readingsBulkUpdate($hash, "D07", $B06, 1);
	 readingsBulkUpdate($hash, "D08", $B07, 1);
	 readingsBulkUpdate($hash, "D09", $B08, 1);
	 readingsBulkUpdate($hash, "D10", $B09, 1);
	 readingsBulkUpdate($hash, "A00", $B10, 1);
	 readingsBulkUpdate($hash, "A01", $B11, 1);
	 readingsBulkUpdate($hash, "A02", $B12, 1);
	 readingsBulkUpdate($hash, "A03", $B13, 1);
	 readingsBulkUpdate($hash, "A04", $B14, 1);
	 readingsBulkUpdate($hash, "A05", $B15, 1);
	 
	 readingsEndUpdate($hash, 1);
	 }
	elsif($rmsg =~ m"Ready") {
	Log3 $name, 3, ">>>USB_1Wire_Ready";
	my $Intervall = AttrVal($name, "intervall", "30");
	USB_1Wire_SimpleWrite($hash, "0".$Intervall ,2);  # Zicki noch über attr definieren
	USB_1Wire_SimpleWrite($hash, "9" ,2);  # Zicki noch über attr definieren
	Log3 $name, 3, "USB_1Wire ".$name." Set Intervall ".$Intervall." Sekunden";
	
	}
	elsif ($rmsg =~ m"Nano\\") {
	my @z = split(/\x5c/, $'); # In S' gesamte Zeichenkette nach ID: wird bei \ gespittet und im Array z[0] und z[1] abgelegt
	$hash->{VERSION_NANO}= $z[1];
	}
	
	elsif ($rmsg =~ m"Hilfe") {
	$hash->{helper}{HILFE} = $hash->{PARTIAL};
	}
	}
	}
	}

#####################################
sub
USB_1Wire_Ready($)
{
  my ($hash) = @_;

  return DevIo_OpenDev($hash, 1, "USB_1Wire_DoInit")
                if($hash->{STATE} eq "disconnected");

  # This is relevant for windows/USB only
  my $po = $hash->{USBDev};
  my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
  if($po) {
    ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  }
  return ($InBytes && $InBytes>0);
}

########################
sub
USB_1Wire_SimpleWrite(@)
{
  my ($hash, $msg, $nonl) = @_;
  return if(!$hash);
  if($hash->{TYPE} eq "USB_1Wire_RFR") {
    # Prefix $msg with RRBBU and return the corresponding USB_1Wire hash.
    ($hash, $msg) = USB_1Wire_RFR_AddPrefix($hash, $msg); 
  }

  my $name = $hash->{NAME};
  Log3 $name, 5, "SW: $msg";

  $msg .= "\n" unless($nonl);

  $hash->{USBDev}->write($msg)    if($hash->{USBDev});
  syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev});
  syswrite($hash->{DIODev}, $msg) if($hash->{DIODev});

  # Some linux installations are broken with 0.001, T01 returns no answer
  select(undef, undef, undef, 0.01);
}

sub
USB_1Wire_Attr(@)
{
	my ($cmd,$name,$aName,$aVal) = @_;
	my $hash = $defs{$name};
	#my @a = split(" ", $aVal);
	if ($cmd eq "set") {
		if ($aName eq "intervall") {
		Log3 $name, 4, "USB_1Wire Check $name attr $aName";
		if (10 <= $aVal) {
		USB_1Wire_SimpleWrite($hash, "0".$aVal ,2); 
		}
		else {
		return " Wert von 10 bis 300 Sekunden";
		}
		}
		if ($aName  =~ m"Sensor_*[1-2]*[0-9]") {
		Log3 $name, 4, "USB_1Wire Check $name attr $aName";
		my @a = split(" ", $aVal);
		my $anzahl_elemente = @a;
		if($anzahl_elemente != 3) {
		return " Es werden genau 3 Argumente benoetigt Sensor_ID Sensor_Alias Sensor_Offset z.B 10-A4-DA-25-02-08-00-50 Wohnzimmer 0.0";
		}
		}
		}
		
		#delete($attr{$name}{$aName});
  	return undef;
}


1;

#=pod
=begin html

<a name="USB_1Wire"></a>
<h3>USB_1Wire</h3>

<br> Hier Text </br>
	
=end html


=begin html_DE

<a name="USB_1Wire"></a>
<h3>USB_1Wire</h3>

<br> Hier Text </br>
	
=end html_DE

#=cut
