Advertisement
Guest User

98_UVR16x2.pm

a guest
Jan 5th, 2022
268
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 21.19 KB | None | 0 0
  1. ############################################################################
  2. # fhem Modul zur Ansteuerung von UVR16x2 via CAN (candump/cansend)
  3. ##############################################################################
  4.  
  5. package main;
  6.  
  7. use strict;
  8. use warnings;
  9.  
  10. use IO::Select;
  11.  
  12. use Time::HiRes qw(time); # Required for sending can-time
  13. use Time::Local;
  14.  
  15. my %Units = ( # Type => [Factor, Unit]
  16. "Dimensionslos" => [1, ""],
  17. "Dimensionslos_(.5)" => [0.5, ""],
  18. "Dimensionslos_(.1)" => [0.1, ""],
  19. "Arbeitszahl" => [0.01, ""],
  20. "Temperatur_(°C)" => [0.1, "°C"],
  21. "Globalstrahlung" => [1, "W/m²"],
  22. "Prozent" => [0.1, "%"],
  23. "Absolute_Feuchte" => [0.1, "g/m³"],
  24. "Druck_bar" => [0.01, "bar"],
  25. "Druck_mbar" => [0.1, "mbar"],
  26. "Druck_Pascal" => [1, "Pascal"],
  27. "Durchfluss_l/min" => [1, "l/min"],
  28. "Durchfluss_l/h" => [1, "l/h"],
  29. "Durchfluss_l/d" => [1, "l/d"],
  30. "Durchfluss_m³/min" => [1, "m³/min"],
  31. "Durchfluss_m³/h" => [1, "m³/h"],
  32. "Durchfluss_m³/d" => [1, "m³/d"],
  33. "Leistung" => [0.1, "kW"],
  34. "Spannung" => [0.01, "V"],
  35. "Stromstärke_mA" => [0.1, "mA"],
  36. "Stromstärke_A" => [0.1, "A"],
  37. "Widerstand" => [0.01, "kΩ"],
  38. "Geschwindigkeit_km/h" => [1, "km/h"],
  39. "Geschwindigkeit_m/s" => [1, "m/s"],
  40. "Winkel_(Grad)" => [0.1, "°"],
  41. );
  42.  
  43. #
  44. # FHEM module intitialisation
  45. # defines the functions to be called from FHEM
  46. #########################################################################
  47. sub UVR16x2_Initialize($)
  48. {
  49.     my ($hash) = @_;
  50.  
  51.     $hash->{ReadFn}  = "UVR16x2_Read";
  52.     $hash->{ReadyFn}  = "UVR16x2_Ready";
  53.     $hash->{DefFn}   = "UVR16x2_Define";
  54.     $hash->{UndefFn} = "UVR16x2_Undef";
  55.     $hash->{NotifyFn} = "UVR16x2_Notify";
  56.     $hash->{GetFn}   = "UVR16x2_Get";
  57.     $hash->{SetFn}   = "UVR16x2_Set";
  58.     $hash->{AttrFn}  = "UVR16x2_Attr";
  59.     $hash->{AttrList} = "SendAsNodeId ".
  60.                         "SendInterval ".
  61.                         "SendNewTimeInterval ";
  62.     my $units = "";
  63.     for my $unit (keys %Units) {
  64.         $units .= "," unless $units eq "";
  65.         $units .= $unit
  66.     }
  67.     for (my $i = 1; $i < 33; $i++) {
  68.         $hash->{AttrList} .= sprintf("GetFactorAnalog%02d", $i).":".$units." "; # Names of unit
  69.         $hash->{AttrList} .= sprintf("SetFactorAnalog%02d", $i).":".$units." ";
  70.     }
  71. }
  72.  
  73. #
  74. # Define command
  75. #########################################################################                                   #
  76. sub UVR16x2_Define($$)
  77. {
  78.     my ( $hash, $def ) = @_;
  79.     my @a = split( "[ \t][ \t]*", $def );
  80.  
  81.     return "wrong syntax: define <name> UVR16x2 can-device(can0) node-id(of UVR)"
  82.       if ( @a < 4 );
  83.  
  84.     my $name   = $a[0];
  85.     my $dev    = $a[2];
  86.     my $nodeid = $a[3];
  87.  
  88.     $hash->{DeviceName} = $dev;
  89.     $hash->{NodeId} = $nodeid;
  90.  
  91.     my $ret = UVR16x2_InitDev($hash); # Open process and assign to FD
  92.     return $ret;
  93. }
  94.  
  95.  
  96. sub UVR16x2_InitDev($)
  97. {
  98.     my ($hash) = @_;
  99.     my $name = $hash->{NAME};
  100.     my $dev = $hash->{DeviceName};
  101.  
  102.     $hash->{candumppid} = open(my $FD, "candump $dev |");
  103.     $hash->{FD} = $FD; # Must be FD because of Select loop of fhem
  104.  
  105.     return "Failed to open candump" unless $hash->{candumppid};
  106.     $selectlist{$name.$dev} = $hash; # add to loop of selects
  107.     $hash->{STATE} = "Initialized";
  108.     $hash->{select} = IO::Select->new([$hash->{FD}]); # Make our own select to use at Read and Ready
  109.    
  110.     UVR16x2_SendNewTime($hash);
  111.    
  112.     return undef;
  113. }
  114.  
  115. sub UVR16x2_SendNewTime($) # Sends a time broadcast onto the can-network.
  116. {
  117.     my ($hash) = @_;
  118.     my $name = $hash->{NAME};
  119.     my $dev = $hash->{DeviceName};
  120.  
  121.     my $can_epoch = 441763200;
  122.  
  123.     my $time = time();
  124.     my $diff = ($time *1000) %1000;
  125.     my $localepochinms = (timegm(localtime($time))*1000 + $diff);
  126.     my $ms = $localepochinms % 86400000;
  127.     my $cmd = "cansend $dev 100#";
  128.     for (my $x = 0; $x < 4; $x++) { # First 4 bytes of ms since 00:00
  129.       $cmd .= sprintf("%02x", ($ms >> ($x * 8)) & hex "FF");
  130.     }
  131.    
  132.     # Then 2 bytes days since 1.1.1984 ($can_epoch)
  133.    
  134.     my $days = ($localepochinms - $can_epoch * 1000 - $ms) / 86400000;
  135.    
  136.     $cmd .= sprintf("%02x", $days & hex "FF") . sprintf("%02x", $days >> 8);
  137.    
  138.     Log3 $name, 4, "$name: Executing: $cmd (Current local epoch (ms): ${localepochinms}, ms since 00:00: ${ms}, days since 1.1.1984: $days";
  139.     system($cmd);
  140.  
  141.     my $interval = AttrVal($name, "SendNewTimeInterval", undef); # If not set, DO NOT make a new timer.
  142.     InternalTimer(gettimeofday() + $interval, "UVR16x2_SendNewTime", $hash, 0) if $interval;
  143. }
  144.  
  145.  
  146. #
  147. # undefine command when device is deleted
  148. #########################################################################
  149. sub UVR16x2_Undef($$)
  150. {
  151.     my ( $hash, $arg ) = @_;
  152.     UVR16x2_CloseDev($hash); # Kill process
  153.     RemoveInternalTimer($hash, "UVR16x2_SendInputs");
  154.     RemoveInternalTimer($hash, "UVR16x2_SendNewTime");
  155.     return undef;
  156. }
  157.  
  158. sub UVR16x2_Notify($$$)
  159. {
  160.     my ($own_hash, $dev_hash) = @_;
  161.     my $ownName = $own_hash->{NAME}; # own name / hash
  162.  
  163.     return "" if(IsDisabled($ownName)); # Return without any further action if the module is disabled
  164.  
  165.     my $devName = $dev_hash->{NAME}; # Device that created the events
  166.  
  167.     return unless $devName eq "global"; # we need just globals for shutdown/initialized
  168.  
  169.     my $events = deviceEvents($dev_hash,1);
  170.     return if( !$events );
  171.  
  172.     foreach my $event (@{$events}) {
  173.         $event = "" if(!defined($event));
  174.  
  175.         if ($event eq "SHUTDOWN") {
  176.             UVR16x2_CloseDev($own_hash); # Kill process
  177.         } elsif ($event eq "INITIALIZED") {
  178.             UVR16x2_SendInputs($own_hash); # Sending Inputs before initialied = DEATH
  179.         }
  180.     }
  181. }
  182.  
  183. sub UVR16x2_CloseDev($) # Param: hash Called when to kill candump.
  184. {
  185.     my ( $hash ) = @_;
  186.     my $name = $hash->{NAME};
  187.     my $dev = $hash->{DeviceName}; # eg can0.
  188.     kill "KILL", $hash->{candumppid};
  189.     close $hash->{FD}; # Will wait for process exit, but sends no signal: kill needed
  190.     $hash->{candumppid} = undef;
  191.     delete($hash->{select}); # No process = Nothing to read
  192.     delete($selectlist{$name.$dev}); # remove from select list
  193. }
  194.  
  195. # Attr command
  196. #########################################################################
  197. sub
  198. UVR16x2_Attr(@) # TODO: Check units
  199. {
  200.     my ($cmd,$name,$aName,$aVal) = @_;
  201.     # $cmd can be "del" or "set"
  202.     # $name is device name
  203.     # aName and aVal are Attribute name and value
  204.    
  205.     my $hash = $defs{$name};
  206.     Log3 $name, 4, "$name: Attr called with @_";
  207.    
  208.     if ($aName eq "SendInterval") {
  209.         if ($cmd eq "set") {
  210.             if ($aVal !~ /^\d+$/ or int($aVal) < 5 or int($aVal) > 600) {
  211.                 return "Interval not numeric or in a valid range (5-600)"
  212.             }
  213.         }
  214.         RemoveInternalTimer($hash, "UVR16x2_SendInputs");
  215.         UVR16x2_SendInputs($hash); # This will set a new Timer
  216.     } elsif ($aName eq "SendAsNodeId") {
  217.         if ($cmd eq "del") {
  218.             RemoveInternalTimer($hash, "UVR16x2_SendInputs");
  219.         } else {
  220.             if ($aVal !~ /^\d+$/ or int($aVal) < 1 or int($aVal) > 63) {
  221.                 return "NodeId not numeric or in a valid range (1-63)"
  222.             }
  223.         }
  224.     } elsif ($aName eq "SendNewTimeInterval") {
  225.         if ($cmd eq "del") {
  226.             RemoveInternalTimer($hash, "UVR16x2_SendNewTime");
  227.         } elsif ($aVal !~ /^\d+$/ or int($aVal) < 5 or int($aVal) > 86400) {
  228.             return "Interval not numeric or in a valid range (5-86400)"
  229.         } else {
  230.             RemoveInternalTimer($hash, "UVR16x2_SendNewTime");
  231.             UVR16x2_SendNewTime($hash);
  232.         }
  233.     } elsif ($aName =~ /^(Get|Set)FactorAnalog\d\d$/) {
  234.         my $valid = 0; # Valid unit
  235.         my $validAttr = "";
  236.         for my $key (keys %Units) {
  237.             if ($aVal eq $key) {
  238.                 $valid = 1;
  239.                 last;
  240.             } elsif ($validAttr ne "") {
  241.                 $validAttr .= ", ";
  242.             }
  243.             $validAttr .= $key; # Build a valid units array
  244.         }
  245.         return "Unknown unit $aVal, choose one of $validAttr" unless $valid;
  246.     }
  247.     return undef;
  248. }
  249.  
  250. my $UVR16x2_SetVals = "SendNewTime:noArg SendInputs:noArg "; # Help
  251. for (my $UVR16x2_SetValsI = 1; $UVR16x2_SetValsI < 33; $UVR16x2_SetValsI++) {
  252.   $UVR16x2_SetVals .= sprintf("Analog%02d", $UVR16x2_SetValsI) . " ";
  253.   $UVR16x2_SetVals .= sprintf("Digital%02d", $UVR16x2_SetValsI) . ":0,1 ";
  254. }
  255. # SET command
  256. #########################################################################
  257. sub UVR16x2_Set($@)
  258. {
  259.     my ( $hash, @a ) = @_;
  260.     if ( @a == 2 && $a[1] =~ /(SendNewTime|SendInputs)/) {
  261.         if ($1 eq "SendNewTime") {
  262.             RemoveInternalTimer($hash, "UVR16x2_SendNewTime");
  263.             UVR16x2_SendNewTime($hash);
  264.         } else {
  265.             RemoveInternalTimer($hash, "UVR16x2_SendInputs");
  266.             UVR16x2_SendInputs($hash);
  267.         }
  268.         return undef;
  269.     }
  270.     return "Unknown argument ?, choose one of $UVR16x2_SetVals" if @a != 3;
  271.  
  272.     # @a is an array with DeviceName, SetName, Rest of Set Line
  273.     my $name = shift @a;
  274.     my $attr = shift @a;
  275.     my $val  = shift @a;
  276.  
  277.     return "Not a valid number" unless $val =~ /^\d+$/; # TODO: Maybe allow units?
  278.  
  279.     return "Unknown argument $attr, choose one of $UVR16x2_SetVals" if ($attr !~ /^(Analog|Digital)(\d\d)$/);
  280.     my $digital = $1 eq "Digital"; # First group of regex
  281.     my $inputnum = int($2); # and second
  282.  
  283.     return "Unknown argument $attr, choose one of $UVR16x2_SetVals" if $inputnum < 1 or $inputnum > 32;
  284.     return "Unknown argument $attr, choose one of $UVR16x2_SetVals" if $digital and $val ne "0" and $val ne "1";
  285.  
  286.     readingsSingleUpdate($hash, "Set$attr", $val, 1); # Store setting, as ALWAYS all digital values and all analog values are send. If we dont send the other digital vals f.e., we kill the other values to 0 again (which isnt wanted,)
  287.  
  288.     RemoveInternalTimer($hash, "UVR16x2_SendInputs"); # Kill old Timer to start a new one afterwards
  289.     UVR16x2_SendInputs($hash);
  290.     return undef;
  291. }
  292.  
  293. sub UVR16x2_SendInputs($) # Called on initialized and from then every Attr: SendInterval seconds, also on set command.
  294. {
  295.     my ( $hash ) = @_;
  296.  
  297.     my $name = $hash->{NAME};
  298.    
  299.     return (Log3 $name, 2, "$name: Skipping SendInputs due to no AttrVal(SendAsNodeId) set!") unless AttrVal($name, "SendAsNodeId", undef);
  300.  
  301.     #Digital
  302.     my $num = 0;
  303.     for (my $i = 0; $i < 4; $i++) {
  304.         for (my $ii = 8; $ii > 0; $ii--) {
  305.             my $current = $i * 8 + $ii; # Input num, begins at 1
  306.             my $bitpos = 31 - $i * 8 - 8 + $ii; # Position of bit in the 4 bytes, 31 . . . . . . 24 | 23 . . . . . . 16 | 15 . . . . . . 8 | 7 . . . . . . 0
  307.             $num += ReadingsVal($name, sprintf("SetDigital%02d", $current), 0) << $bitpos;
  308.             Log3 $name, 6, "$name: $i $ii ". $current . " ".$bitpos;
  309.         }
  310.     }
  311.  
  312.     my $cmd = "cansend $hash->{DeviceName} " . sprintf("%03x", (hex "180") + int(AttrVal($name, "SendAsNodeId", undef))) . "#" . sprintf("%08x", $num)."00000000"; # id # Digital bytes + padding/zeroes. Length must be 8 bytes
  313.    
  314.     Log3 $name, 4, "$name: Executing: " . $cmd;
  315.     system($cmd); # send!
  316.  
  317.     #Analog
  318.     my @sends = (hex "200", hex "280", hex "300", hex "380", hex "240", hex "2C0", hex "340", hex "3C0");
  319.     for (my $i = 0; $i < 8; $i++) {
  320.         $sends[$i] += int(AttrVal($name, "SendAsNodeId", 63));
  321.         my $txt = ""; # Our compiled can-message
  322.         for (my $ii = 0; $ii < 4; $ii++) { # For loop for the 4 inputs within the 8 parts.
  323.             my $input = $i * 4 + $ii + 1; # Calculate current Input to send
  324.             my $val = ReadingsVal($name, sprintf("SetAnalog%02d", $input), 0); # get the value
  325.             my $factor = AttrVal($name, sprintf("SetFactorAnalog%02d", $input), undef);
  326.             if ($factor) { # If a factor is set,
  327.                 $val = $val / $Units{$factor}[0]; # reverse it
  328.             } else {$factor = "";}
  329.             $val = unpack("S", pack("s", $val)); # Make signed to unsigned. Pack is confisung.... :D
  330.             $txt .= sprintf("%02x", $val & hex "FF") . sprintf("%02x", $val >> 8); # Swichting Bytes, Low byte is first...
  331.             Log3 $name, 6, "$name: $i * 4 + $ii + 1 = $input = $val $factor $txt";
  332.         }
  333.         $cmd = "cansend $hash->{DeviceName} " . sprintf("%03x", $sends[$i]) . "#$txt";
  334.         Log3 $name, 4, "$name: Executing: " . $cmd;
  335.         system($cmd); # send!
  336.     }
  337.                                                                                                     # 0 = Do not wait for Init done. 1 = wait for init done.
  338.     InternalTimer(gettimeofday() + AttrVal($name, "SendInterval", 30), "UVR16x2_SendInputs", $hash, 0);
  339. }
  340.  
  341. # GET command
  342. #########################################################################
  343. sub UVR16x2_Get($@) # TODO: Maybe manual values? like idx + subidx?
  344. {
  345.     my ( $hash, @a ) = @_;
  346.     return "\"set UVR16x2\" needs at least one argument" if ( @a < 2 );
  347.  
  348.     # @a is an array with DeviceName, GetName
  349.     my $name = shift @a;
  350.     my $attr = shift @a;
  351.  
  352.     return undef;
  353. }
  354.  
  355.  
  356. #########################################################################
  357. # called from the global loop, when the select for hash->{FD} reports data
  358. sub UVR16x2_Read($)
  359. {
  360.     my ($hash) = @_;
  361.     my $name = $hash->{NAME};
  362.     my $handle = $hash->{FD};
  363.     readingsBeginUpdate($hash);
  364.     while ($hash->{select}->can_read(0)) { # if we just directly do <$handle>, we block fhem - unwanted.
  365.         my $line = <$handle>;
  366.         $line =~ s/(\r|\n)//g; # kill newlines
  367.         my @words = split / /, $line, 10;
  368.         my $id = hex $words[4]; # target id.
  369.         my @bytes = split / /, $words[9]; # our hex/text bytes
  370.         my @hbytes = []; # our numeric bytes
  371.         for (my $i = 0; $i < scalar @bytes; $i++) {
  372.             $hbytes[$i] = hex $bytes[$i];
  373.         }
  374.         next if ($id & hex "3F") != $hash->{NodeId}; # Skip if not targeted at us.
  375.         Log3 $name, 5, "$name: $words[4] with $words[9] in space " . sprintf("%03x", $id & hex "7C0");
  376.  
  377.         my $space = $id & hex "7C0"; # which target space, e.g. Digital/Analog
  378.         if ($space == hex "700") { # Heartbeat
  379.             if ($hbytes[0] == hex "00") {
  380.                 readingsBulkUpdate($hash, "UVRstate", "BootUp");
  381.             } elsif ($hbytes[0] == hex "04") {
  382.                 readingsBulkUpdate($hash, "UVRstate", "Stopped");
  383.             } elsif ($hbytes[0] == hex "05") {
  384.                 readingsBulkUpdate($hash, "UVRstate", "Operational");
  385.             } elsif ($hbytes[0] == hex "7F") {
  386.                 readingsBulkUpdate($hash, "UVRstate", "Pre-Operational");
  387.             }
  388.         } elsif ($space == hex "180") { # Digital
  389.             for (my $i = 0; $i < 4; $i++) {
  390.                 my $byte = $hbytes[$i];
  391.                 for (my $ii = 8; $ii > 0; $ii--) {
  392.                     my $bit = ($byte >> ($ii - 1)) & 1;
  393.                     my $current = $i * 8 + $ii;
  394.                     if ($bit or ReadingsVal($name, sprintf("Digital%02d", $current), 55) != 55) { # skip updating val if never used or now used.
  395.                         readingsBulkUpdate($hash, sprintf("Digital%02d", $current), $bit); # update val
  396.                     }
  397.                 }
  398.             }
  399.         } elsif (($space & hex "200") == hex "200") {# Probably Analog
  400.             my $i = -55; # Dummy.
  401.             if ($space == hex "200") { # Offset *4
  402.                 $i = 0;
  403.             } elsif ($space == hex "280") {
  404.                 $i = 1;
  405.             } elsif ($space == hex "300") {
  406.                 $i = 2;
  407.             } elsif ($space == hex "380") {
  408.                 $i = 3;
  409.             } elsif ($space == hex "240") {
  410.                 $i = 4;
  411.             } elsif ($space == hex "2C0") {
  412.                 $i = 5;
  413.             } elsif ($space == hex "340") {
  414.                 $i = 6;
  415.             } elsif ($space == hex "3C0") {
  416.                 $i = 7;
  417.             }
  418.             unless ($i == -55) { # Definitly Analog
  419.                 for (my $ii = 0; $ii < 4; $ii++) { # Pos + 1 in byte array.
  420.                     my $current = $i * 4 + $ii + 1;
  421.                     next unless my $factor = AttrVal($name, sprintf("GetFactorAnalog%02d", $current), undef); # Skip if no Factor defined
  422.                     Log3 $name, 6, "$name: " . hex($bytes[2 * $ii + 1].$bytes[2 * $ii]). " " . $bytes[2 * $ii + 1].$bytes[2 * $ii];
  423.                     my $val = unpack("s", pack("S", hex $bytes[2 * $ii + 1].$bytes[2 * $ii])) * $Units{$factor}[0]; # Make signed value and apply factor.
  424.                     readingsBulkUpdate($hash, sprintf("Analog%02d", $current), $val . " ".$Units{$factor}[1]); # update val
  425.                 }
  426.             }
  427.         }
  428.     }
  429.     readingsEndUpdate( $hash, 1 );
  430.     return "";
  431. }
  432.  
  433. sub UVR16x2_Ready($) # Dunno if select works on windows
  434. {
  435.     my ($hash) = @_;
  436.     # try to reopen if state is disconnected
  437.     return UVR16x2_InitDev($hash)
  438.       unless ( $hash->{FD});
  439.      
  440.     my $ret = $hash->{select}->can_read(0);
  441.     Log3 $hash->{NAME}, 6, "$hash->{NAME} called ReadyB with ${ret}!";
  442.     return $ret;
  443. }
  444.  
  445. 1; # Required!!!
  446.  
  447. =pod
  448. =begin html
  449.  
  450. <a name="UVR16x2"></a>
  451. <h3>UVR16x2</h3>
  452.  
  453. <ul>
  454.     This module implements an Interface to an UVR16x2, shipped by Technische Alternative (ta.co.at), via CAN-Bus.
  455.     When defined, this module will set the time on every UVR16x2 on the defined can-network to the raspi's one. (UVR16x2 save clock on shutdown and read on bootup, and clock will be time behind)
  456.     <br><br>
  457.     <b>Prerequisites</b>
  458.     <ul>
  459.         <br>
  460.         <li>
  461.             This module requires an UVR16x2. Maybe it works with an UVR1611, but there are only 16 inputs/outputs.
  462.         </li>
  463.     </ul>
  464.     <br>
  465.  
  466.     <a name="UVR16x2define"></a>
  467.     <b>Define</b>
  468.     <ul>
  469.         <br>
  470.         <code>define &lt;name&gt; ArduCounter &lt;device&gt; &lt;node-id&gt;</code>
  471.         <br>
  472.         &lt;device&gt; specifies the can network interface (e.g. can0) to communicate with the CAN-Network.<br>
  473.        
  474.         Node-Id is the CAN-Node-Id defined in the CAN-Settings of your UVR. Min. 1 max. 63.
  475.         <br>
  476.         Example:<br>
  477.         <br>
  478.         <ul><code>define UVR UVR16x2 can0 44</code></ul>
  479.         This will create a device listening on can0 for can-outputs of node 44.
  480.     </ul>
  481.     <br>
  482.  
  483.     <a name="UVR16x2configuration"></a>
  484.     <b>Configuration of UVR16x2</b><br><br>
  485.     <ul>
  486.         The only thing to <u>receive</u> values to do is setting an unit for the analogue outputs which are used by the UVR.
  487.         Digital outputs are created in fhem when they are first set ON!
  488.        
  489.         To <u>send/set</u> values you need to specify a SendAsNodeId attribute, for every analogue output to use a unit using attribute SetFactorAnalog?? and (optionally) a SendInterval (defaults to 30).
  490.         Then you can send values, f.e. <pre>set UVR Analog01 33.4</pre> to send a value of 33.4 to the first analogue output of the virtual UVR created by the module.
  491.         <br><br>
  492.         Full example:<br>
  493.         <pre>
  494.         define UVR UVR16x2 can0 44
  495.         attr UVR SendAsNodeId 45
  496.         attr UVR SendInterval 60 # Send every 60 seconds = minute ALL inputs
  497.         attr UVR SetFactorAnalog01 Temperatur_(°C)
  498.         attr UVR GetFactorAnalog01 Temperatur_(°C)
  499.         set UVR Analog01 24.5 # Set first analogue output of virtual uvr to 24.5 °C
  500.         </pre>
  501.     </ul>
  502.     <br>
  503.  
  504.     <a name="UVR16x2set"></a>
  505.     <b>Set-Commands</b><br>
  506.     <ul>
  507.         <li><b>SendInputs</b></li>
  508.             This command sends ALL inputs again. Useful after detecting if an UVR is back online
  509.         <li><b>SendNewTime</b></li>
  510.             This command sends a new Time sync to the network. Useful after detecting that an UVR is back online
  511.         <li><b>&lt;InputName&gt;</b></li>
  512.             send the value to the UVR16x2. <br>
  513.             InputName is Digital/Analog + a input number, formatted as 2 digits. e.g. Digital06<br>
  514.             After InputName the value is required. Digital = 0 for Off, 1 for On.<br>
  515.     </ul>
  516.     <br>
  517.     <!--<a name="UVR16x2get"></a>
  518.     <b>Get-Commands</b><br>
  519.     <ul>
  520.         <li><b>info</b></li>
  521.             send the internal command <code>show</code> to the Arduino board to get current counts<br>
  522.             this is not needed for normal operation but might be useful sometimes for debugging<br>
  523.     </ul>-->
  524.     <br>
  525.     <a name="UVR16x2attr"></a>
  526.     <b>Attributes</b><br><br>
  527.     <ul>
  528.         <li><b>GetFactorAnalog*</b></li>
  529.             Defines an unit for the incoming value. See the attr-help to get a list of supported units.<br>
  530.         <li><b>SetFactorAnalog*</b></li>
  531.             Defines an unit for the outgoing value. See the attr-help to get a list of supported units.<br>
  532.         <li><b>SendAsNodeId</b></li>
  533.             Defines the nodeid for the virtual UVR.<br>
  534.         <li><b>SendInterval</b></li>
  535.             Defines the interval to send all outputs of the virtual UVR.<br>
  536.         <li><b>SendNewTimeInterval</b></li>
  537.             Defines the interval to send a new time to the network.<br>
  538.         <li><b>verbose</b></li>
  539.             Defines the loglevel of module. Recommended while setting up: 4, operational: 3. EXTREM debugging: 6. <br>
  540.             On 3, it will log just skipped inputs send.<br>
  541.             On 4, it will also log every command executed (every can-message send)<br>
  542.             On 5, it will also log every can-message received (coming from the real UVR)<br>
  543.             On 6, it will log for EVERY input/output one line, just for debugging while changing the script.<br>
  544.     </ul>
  545.     <br>
  546.     <b>Readings / Events</b><br>
  547.     <ul>
  548.         The module creates the following readings and events for each digital input and defined analogue input, and for changing the send values:
  549.         <li><b>Analog*</b> and <b>Digital*</b></li>
  550.             the current value on the output of the real UVR
  551.         <li><b>SetAnalog*</b> and <b>SetDigital*</b></li>
  552.             the current set value of the virtual UVR's output.
  553.         <li><b>UVRstate</b></li>
  554.             the current state of the real UVR. AFAIK, it always sends Operational, and never Stopped. Watch the time of this, if more than 10 secs passed, then it is probably down.
  555.     </ul>
  556.     <br>
  557. </ul>
  558.  
  559. =end html
  560. =cut
  561.  
  562.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement