add (working) dirk.pl + other bits
authorDirk Koopman <djk@tobit.co.uk>
Fri, 19 Jul 2013 13:50:55 +0000 (14:50 +0100)
committerDirk Koopman <djk@tobit.co.uk>
Fri, 19 Jul 2013 13:50:55 +0000 (14:50 +0100)
DWeather/lib/DWeather/Debug.pm
DWeather/lib/DWeather/Serial.pm
DWeather/lib/DWeather/Station.pm
DWeather/lib/DWeather/Station/Vantage.pm
DWeather/lib/DWeather/Vantage.pm [deleted file]
dirk.pl [new file with mode: 0755]
yr.php [new file with mode: 0644]

index 3535184b3461918e1d853ea9a946f04de2417d1e..d67b70221022d02e129858c3622c2d417f9ba5d0 100644 (file)
@@ -61,7 +61,7 @@ sub dbg
                chomp $r;
                my @l = split /\n/, $r;
                for (@l) {
-                       s/([\x00-\x08\x0B-\x1f\x7f-\xff])/uc sprintf("%%%02x",ord($1))/eg;
+                       s/([\x00-\x1f\x7f-\xff])/uc sprintf("%%%02x",ord($1))/eg;
 #                      print "$_\n" if defined \*STDOUT;
                        $fp->writeunix($t, "$ts $_"); 
                }
index 40ac1607c343efc8b0c42c0e8c549a069942ae7a..cbd6cbebc244694485f9be027328b903937d2d5f 100644 (file)
@@ -9,30 +9,47 @@ package DWeather::Serial;
 use POSIX qw(:termios_h);
 use Fcntl;
 
-our @ISA = qw(IO::File);
+use AnyEvent;
+use base qw(AnyEvent::Handle);
 
 
-# Linux-specific Baud-Rates
+# Linux-specific Baud-Rates (for reference really)
 use constant B57600 => 0010001;
 use constant B115200 => 0010002;
 use constant B230400 => 0010003;
 use constant B460800 => 0010004;
 use constant CRTSCTS => 020000000000;
 
+#
+# my $h = DWeather::Serial->new("/dev/ttyXXX", 19200 [,cs7] [,odd] [,rtscts]);
+#
+# all parameters are optional
+#
+# you are expected to add AE callbacks as required, all this module
+# does is create the AE::Handle and associates an IO::File handle with it
+#
+# default is /dev/ttyS0, 9600 8N1 no handshaking
+#
+# the tty is set to raw mode.
+#
+# returns a subclassed AE::Handle
+#
 sub new
 {
        my $pkg = shift;
        my $class = ref $pkg || $pkg;
        my $device = shift || "/dev/ttyS0";
 
-       my $self = $pkg->SUPER::new($device, O_RDWR|O_NOCTTY|O_EXCL|O_NDELAY) || return;
+       my $fh = IO::File->new($device, O_RDWR|O_NOCTTY|O_EXCL|O_NDELAY) || return;
+       my $self = $class->new(fh => $fh);
 
        # get my attributes
-       $$self->{ORIGTERM} = POSIX::Termios->new();
-       my $term =  $$self->{TERM} = POSIX::Termios->new();
-       $$self->{ORIGTERM}->getattr(fileno($self));
-       $term->getattr(fileno($self));
+       $self->{ORIGTERM} = POSIX::Termios->new();
+       my $term =  $self->{TERM} = POSIX::Termios->new();
+       $self->{ORIGTERM} = $self->{ORIGTERM}->getattr(fileno($fh));
+       $term->getattr(fileno($fh));
        my ($speed) = grep {/^\d+$/} @_; 
+       $speed ||= 9600;
        my $baud;
        {
                no strict 'refs';
@@ -62,35 +79,30 @@ sub new
        $cflag |= CRTSCTS if grep /rtscts$/, $@;
        $term->setcflag($cflag); $term->setlflag($lflag);
        $term->setoflag($oflag); $term->setiflag($iflag);
-       $term->setattr(fileno($self), TCSANOW);
+       $term->setattr(fileno($fh), TCSANOW);
+       $self->{device} = $device;
+       $self->{speed} = $speed;
        return $self;
 }
 
 sub getattr
 {
        my $self = shift;
-       $$self->{TERM}->getattr;
-       return $$self->{TERM};
+       $self->{TERM}->getattr(fileno($self->fh));
+       return $self->{TERM};
 }
 
 sub setattr
 {
        my $self = shift;
-       my $attr = shift || $$self->{TERM};
-       $attr->setattr(fileno($self), &POSIX::TCSANOW);
-}
-
-sub close
-{
-       my $self = shift;
-       $self->setattr($$self->{ORIGTERM});
-       $self->SUPER::close;
+       my $attr = shift || $self->{TERM};
+       $attr->setattr(fileno($self->fh), &POSIX::TCSANOW);
 }
 
 sub DESTROY
 {
        my $self = shift;
-       $self->close;
+       $self->setattr($self->{ORIGTERM});
 }
 
 1;
index f9c45feed29f02dd310e495b2bb1eabafbb9880b..7d0524675eab97e1ace0608c53eeebae08b3ffa6 100644 (file)
@@ -23,4 +23,5 @@ sub new
        return $self;
 }
 
+
 1;
index 5f3749ee2b3c31d7f4ffd0df862c0240f9eacd41..a8b91cfe4b9e5512cdfc6f26f44c5c6972565fd2 100644 (file)
@@ -18,18 +18,43 @@ sub new
        my $class = ref $pkg || $pkg;
        my $device = shift;
        
-       my $d = $class->SUPER::new($device, 19200);
-       return $d;
+       my $self = $class->SUPER::new($device, 19200);
+       $self->on_read(sub{$d->process});
+       return $self;
 }
 
-sub reset
+sub process
 {
-
+       my $self = shift;
+       my $data = $self->{rbuf};
+       $self->{rbuf} = '';
+       
+       if (isdbg('raw')) {
+               dbg("I $self->{device} lth " . length $data);
+               dbgdump(data);
+       } elsif (isdbg('chan')) {
+               dbg("I $self->{device}: $data");
+       }
+       foreach my $ch (@ch) {
+               
+       }
 }
 
-sub poll
+sub send
 {
-
+       my $self= shift;
+       my $data = shift;
+       if (isdbg('raw')) {
+               dbg("O $self->{device} lth " . length $data);
+               dbgdump(data);
+       } elsif (isdbg('chan')) {
+               dbg("O $self->{device}: $data");
+       }
+       $self->push_write($data)
 }
 
+sub reset
+{
+       
+}
 1;
diff --git a/DWeather/lib/DWeather/Vantage.pm b/DWeather/lib/DWeather/Vantage.pm
deleted file mode 100644 (file)
index 54cbb4a..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Vantage Pro 2 interface for DWeather
-#
-#
-
-use strict;
-use warnings;
-
-use base qw(DWeather::Serial);
-use AnyEvent;
-
-sub new
-{
-       my $pkg = shift;
-       my $class = ref $pkg || $pkg;
-       my $device = shift || '/dev/ttyS0';
-       
-       my $self = $class->SUPER::new($device, 19200);
-       return $self;
-}
-
-sub send
-{
-       
-}
-
-sub run
-{
-
-}
diff --git a/dirk.pl b/dirk.pl
new file mode 100755 (executable)
index 0000000..d727057
--- /dev/null
+++ b/dirk.pl
@@ -0,0 +1,546 @@
+#!/usr/bin/perl
+
+use DBI;
+use strict;
+use Device::SerialPort;
+use Time::HiRes;
+
+my $devname = "/dev/davis";
+my $dbsort = "SQLite";         
+my $db="weather.db";
+my $host="";
+my $userid="";
+my $passwd="";
+my $connectionInfo="dbi:$dbsort:$db";
+$connectionInfo .= ":$host" if $host;
+
+# make connection to database
+my %attr = ( PrintError => 0, RaiseError => 0 );
+my $dbh = DBI->connect($connectionInfo,$userid,$passwd,\%attr) or die "Couldn't connect to database: " . DBI->errstr;
+
+
+my ($count, $result);
+my %hsh;
+my @crc_table=();
+my %bar_trend=();
+load_crc_table();
+
+my $ob = Device::SerialPort->new ($devname) || die;
+
+$ob->user_msg(1);                              # misc. warnings
+$ob->error_msg(1);                             # hardware and data errors
+
+$ob->baudrate(19200);
+$ob->parity("none");
+#$ob->parity_enable(1);        # for any parity except "none"
+$ob->databits(8);
+$ob->stopbits(1);
+$ob->handshake('none');
+$ob->read_const_time(15000);   # ultimate timeout (15 seconds)
+$ob->write_settings||die"setting failed";
+
+my $awake=0;
+my $attempts=0;
+$ob->write("\n");                              # initial wake
+
+while ($awake==0) {
+       $ob->write("\n");                       # wake for real
+       $ob->read_interval(600);        # wait for a max of 600ms
+       ($count, $result) = $ob->read(10); # read up to 10 chars
+       if ($result eq "\n\r") {
+               print "awoke on attempt $attempts :)\n"; 
+               $awake=1;
+       }
+       else {
+               print "wake error on attempt $attempts :(\n";
+       }
+       $attempts++;
+       dienice("failed to wake device - tried $attempts times") unless $attempts<6;
+}
+
+$ob->write("LAMPS 0\n");
+$ob->read_interval(300);               # wait for a max of 300ms
+($count, $result) = $ob->read(8); # read up to 8 chars
+$result=~s/(\r|\n)//g;
+if ($result eq "OK") {
+       print "lamps on :)\n";
+}
+else {
+       dienice("lamp error '$result'");
+}
+
+
+
+my $ref=gettime();
+$ref->[5]+=1900;
+print "$ref->[2]:$ref->[1]:$ref->[0] $ref->[3]/$ref->[4]/$ref->[5]\n";
+
+#settime();
+
+#do_dmpaft();
+
+while (1) {
+       get_loop();                                     # if you do anything after here - you need to wake up the device again
+       print "** do something else\n";
+       sleep 5;
+       $ob->write("\r");                       #  wake
+       $ob->read_interval(500);        # wait for a max of 300ms
+       my ($count, $result) = $ob->read(4096); # read any crap up to 4096 chars
+}
+
+#print "count=$count $result\n";
+#for(my $i=0; $i<$count; $i++)
+#{ printf("%02d ",ord(substr($result,$i,1))); }
+
+#$ob->write("WRD\x12\x4d\n");
+
+undef $ob; 
+exit 0;
+
+sub dienice
+{
+       my $err=shift;
+       print "$err\n";
+       undef $ob;
+       exit 9;
+}
+
+sub parse_loop_blck 
+{
+       my $blk = shift; 
+       my $loo =  substr $blk,0,3;  
+       unless ( $loo eq 'LOO') {
+               warn("Block invalid loo -> $loo\n"); return "";
+       } 
+       my $t;
+    
+       #$hsh{'next_rec'}     = unpack("s", substr $blk,5,2);  
+  
+       $hsh{'Barometric_Trend'}    = unpack("C", substr $blk,3,1);
+       $hsh{'Barometric_Trend_txt'} = $bar_trend{$hsh{'Barometric_Trend'}};
+       $t = unpack("s", substr $blk,7,2) / 1000; 
+#      $hsh{'Barometric_Press_hg'}          = $t;
+       $hsh{'Barometric_Press_mb'} = sprintf("%.2f",$t*33.8637526); 
+  
+
+       $t = unpack("s", substr $blk,9,2) / 10;
+#      $hsh{'Air_Temp_Inside_f'}      = $t;
+       $hsh{'Air_Temp_Inside_c'} = sprintf("%.1f",($t - 32) * 5/9);
+       my $tf  = unpack("s", substr $blk,12,2) / 10; 
+#      $hsh{'Air_Temp_Outside_f'}     = $tf;
+       $hsh{'Air_Temp_Outside_c'}  = sprintf("%.1f",($tf - 32) * 5/9);
+  
+       $hsh{'Wind_Speed_mph'}   = unpack("C", substr $blk,14,1); 
+#      $hsh{'Wind_Speed_mps'} = sprintf("%.1f",$hsh{'Wind_Speed_mph'}*0.44704);
+       $hsh{'Wind_Speed_10min_Avg_mph'} = unpack("C", substr $blk,15,1);
+#      $hsh{'Wind_Speed_10min_Avg_mps'} = sprintf("%.1f",$hsh{'Wind_Speed_10min_Avg_mph'}*0.44704);
+       $hsh{'Wind_Dir'}     = unpack("s", substr $blk,16,2);
+  
+  
+       $hsh{'Humidity_Outside'} = unpack("C", substr $blk,33,1);
+       $hsh{'Humidity_Inside'}  = unpack("C", substr $blk,11,1);
+       $hsh{'Dew_Point'}  = dew_point($tf, $hsh{'Humidity_Outside'});
+  
+#      $hsh{'UV'}         = unpack("C", substr $blk,43,1);
+#      $hsh{'Solar'}  = unpack("s", substr $blk,44,2); # watt/m**2
+  
+       $hsh{'Rain_Rate'}  = (unpack("s", substr $blk,41,2) / 100) * 25.4; # Inches per hr converted to mm
+       $hsh{'Rain_Storm'} = (unpack("s", substr $blk,46,2) / 100) * 25.4; # Inches per storm
+       #$hsh{'Storm_Date'} = unpack("s", substr $blk,48,2);  # Need to parse data (not sure what this is)
+       $hsh{'Rain_Day'}   = (unpack("s", substr $blk,50,2)/100) * 25.4;  
+       $hsh{'Rain_Month'}  = (unpack("s", substr $blk,52,2)/100) * 25.4;  
+       $hsh{'Rain_Year'}  = (unpack("s", substr $blk,54,2)/100) * 25.4; 
+  
+       $hsh{'ET_Day'}   = unpack("s", substr $blk,56,2)/1000;  
+       $hsh{'ET_Month'}  = unpack("s", substr $blk,58,2)/100;  
+       $hsh{'ET_Year'}  = unpack("s", substr $blk,60,2)/100;  
+  
+       #$hsh{'Alarms_Inside'}  = unpack("b8", substr $blk,70,1);  
+       #$hsh{'Alarms_Rain'}  = unpack("b8", substr $blk,70,1);  
+       #$hsh{'Alarms_Outside'}  = unpack("b8", substr $blk,70,1);  
+  
+       $hsh{'Batt_Transmitter'}  = unpack("C", substr $blk,86,1); #  * 0.005859375
+       $hsh{'Batt_Console'}  = unpack("s", substr $blk,87,2) * 0.005859375; 
+  
+       $hsh{'Forecast_Icon'}  = unpack("C", substr $blk,89,1);  
+       $hsh{'Forecast_Rule'}  = unpack("C", substr $blk,90,1); 
+  
+       $hsh{'Sunrise'}  = sprintf( "%04d", unpack("S", substr $blk,91,2) );  
+       $hsh{'Sunrise'}  =~ s/(\d{2})(\d{2})/$1:$2/; 
+       $hsh{'Sunset'}   = sprintf( "%04d", unpack("S", substr $blk,93,2) );  
+       $hsh{'Sunset'}  =~ s/(\d{2})(\d{2})/$1:$2/;
+  
+       #my $nl  =  ord substr $blk,95,1;  
+       #my $cr  =  ord substr $blk,96,1;   
+
+       my $crc = unpack "%n", substr($blk,97,2); 
+       my $crc_calc = CRC_CCITT($blk); 
+  
+       if ($crc_calc==0) {
+               return 0;
+       }
+       else {
+               print "CRC check failed for LOOP data!\n";
+               return 1;
+       }
+       #delete @hsh{'crc', 'crc_calc', 'next_rec'};
+       #delete($hsh{crc})||die"cant delete crc";
+       #delete($hsh{crc_calc})||die"cant delete crc_calc";
+       #delete($hsh{next_rec})||die"cant delete next_rec";
+  
+}
+
+sub dew_point
+{
+       my $temp = shift @_; 
+       my $rh   = shift @_; 
+  
+       #  Using the simplified approximation for dew point 
+       #  Accurate to 1 degree C for humidities > 50 %  
+       #  http://en.wikipedia.org/wiki/Dew_point
+
+       my $dew_point = $temp - ( (100 - $rh)/5 ); 
+    
+       return $dew_point; 
+}
+
+sub CRC_CCITT
+{
+    # Expects packed data... 
+    my $data_str = shift @_;
+
+       my $crc = 0;
+       my @lst = split //, $data_str;
+       foreach my $data (@lst) {
+               my $data = unpack("c",$data); 
+       
+               my $crc_prev = $crc;
+               my $index = $crc >> 8 ^ $data;
+               my $lhs = $crc_table[$index];
+               #print "lhs=$lhs, crc=$crc\n";
+               my $rhs = ($crc << 8) & 0xFFFF;
+               $crc = $lhs ^ $rhs;
+       
+          
+       }
+               
+       return $crc;
+}
+
+sub load_crc_table
+{
+
+       @crc_table = (
+                                 0x0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+                                 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+                                 0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+                                 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+                                 0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+                                 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+                                 0x3653, 0x2672, 0x1611, 0x630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+                                 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+                                 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823,
+                                 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+                                 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12,
+                                 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+                                 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41,
+                                 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+                                 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0xe70,
+                                 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+                                 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+                                 0x1080, 0xa1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+                                 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+                                 0x2b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+                                 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+                                 0x34e2, 0x24c3, 0x14a0, 0x481, 0x7466, 0x6447, 0x5424, 0x4405,
+                                 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+                                 0x26d3, 0x36f2, 0x691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+                                 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+                                 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x8e1, 0x3882, 0x28a3,
+                                 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+                                 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0xaf1, 0x1ad0, 0x2ab3, 0x3a92,
+                                 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+                                 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0xcc1,
+                                 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+                                 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0xed1, 0x1ef0
+                                );
+
+       $bar_trend{-60} = "Falling Rapidly"; 
+       $bar_trend{196} = "Falling Rapidly";
+       $bar_trend{-20} = "Falling Slowly"; 
+       $bar_trend{236} = "Falling Slowly";
+       $bar_trend{0} = "Steady";
+       $bar_trend{20} = "Rising Slowly";
+       $bar_trend{60} = "Rising Rapidly";
+}
+
+sub gettime
+{
+    $ob->write("GETTIME\n");
+       $ob->read_interval(200);
+       my($cnt_in, $str) = $ob->read(9);
+       if ($cnt_in==0) {
+               dienice("read error cnt_in=$cnt_in, str='$str'");
+       }
+       
+       my $ck = CRC_CCITT(substr($str,1,9));
+       if ( $ck ) {
+               warn "checksum error"; return 0;
+       }
+       my @rsp_lst =  split //, $str;
+       shift @rsp_lst; 
+       @rsp_lst = map ord, @rsp_lst; 
+    return \@rsp_lst; 
+}
+
+sub settime
+{
+    my $s_time = [ localtime() ]; 
+    $s_time->[4] += 1; 
+    
+       $ob->write("SETTIME\n");
+       $ob->read_interval(300);
+       my ($cnt_in, $str) = $ob->read(1);
+       my $ack = ord $str; 
+       if ( $ack != 6 ) {
+               warn "SETTIME not set ack $ack !"; return 0;
+       }
+       my ($sec, $min, $hour, $day, $mon, $yr) = @{$s_time};
+       $str = join "", map chr, ($sec, $min, $hour, $day, $mon, $yr);  
+       my $ck = CRC_CCITT($str);
+       $str = $str . pack("n",$ck); 
+       $ob->write($str);
+       ($cnt_in, $str) = $ob->read(1);
+       if ( ord($str) != 6 ) {
+               warn "SETTIME not set!"; return 0;
+       }
+       sleep 3;                                        # The console seems to need to some time here... 
+}
+
+sub get_loop
+{
+       print "** get loop at ", scalar localtime, "\n";
+       my $loops=1;
+       $ob->write("LOOP $loops\n");
+       $ob->read_interval(0);
+       ($count, $result) = $ob->read(1);
+       for (my $i=0; $i<$loops; $i+=1) {
+               my $rc=loop_dump(); 
+               if ($rc!=0) {
+                       last;
+               }
+       }
+       return;
+
+}
+
+sub loop_dump
+{
+       $ob->read_interval(0);
+       ($count, $result) = $ob->read(99);
+       if ($count != 99) {
+               print "loop error - got $count bytes, expected 99\n";
+               return 1;
+       }
+       print "LOOP data received\n";
+
+       if (ord(substr($result,0,1))==6) {
+               $result=substr($result,1);
+       }
+
+       my $rc=parse_loop_blck($result);
+       if ($rc!=0) {
+               return;
+       }                                                       # bad crc so goto next
+       foreach my $key (sort keys %hsh) {
+               print "$key = $hsh{$key}\n";
+       }
+
+       my $stmt = 'INSERT INTO current (Date_Time,' . join(',', keys %hsh) . ') VALUES (NOW(),' . join(',', ('?') x keys %hsh) . ')';
+
+       $dbh->do( $stmt, undef, values %hsh);
+
+       %hsh=();
+       return 0;
+}
+
+
+sub do_dmpaft
+{
+       #my $self = shift @_;
+       open(DMP,">dump.txt");
+       my $vDateStamp = shift @_;
+       my $vTimeStamp = shift @_; 
+
+       # If not date/time stamp then assume 0 which will down load the entire archive
+       unless ( $vDateStamp ) {
+               $vDateStamp = 0;
+       } 
+       unless ( $vTimeStamp ) {
+               $vTimeStamp = 0;
+       } 
+       #my $port_obj = $self->{'port_obj'}; 
+  
+       my $datetime = pack("ss",$vDateStamp, $vTimeStamp); 
+       my $crc = CRC_CCITT($datetime);
+       my $cmd = pack("ssn",$vDateStamp,$vTimeStamp,$crc); 
+
+       #-----------------------  
+       #my $str = unpack("H*", $cmd); 
+       #$str =~ s/(\w{2})/$1 /g; 
+       # Documentation is wrong! The example should be <0xC6><0x06><0xA2><0x03> in section X
+       #print "cmd : $str \n";exit; 
+       #-----------------------  
+
+       #sleep 2; # Needed after loop 
+       #$self->wake_up();  
+       # Ok let's start the communication sequence.... 
+       my $cnt_out = $ob->write("DMPAFT\n");
+       unless ($cnt_out) {
+               warn "write failed\n";
+       }
+       ;
+       $ob->read_interval(300);
+       my ($cnt_in, $str) = $ob->read(1);
+  
+       my $ack = ord $str; 
+       unless ($ack == 6) {
+               warn "Ack not received on DMPAFT command: $ack"; exit -1;
+       }
+       $cnt_out = $ob->write($cmd);
+       unless ($cnt_out) {
+               warn "write failed\n";
+       }
+       ;
+       ($cnt_in, $str) = $ob->read(7); 
+       $ack = ord substr($str,0,1);    
+    
+       my $ls = unpack("H20",substr($str,1,4) ); 
+       $ls =~ s/(\w{2})/$1 /g; 
+       my $pages = unpack("s",substr($str,1,2) ); 
+       my $rec_start = unpack("s",substr($str,3,2) ); 
+  
+       $crc = CRC_CCITT(substr($str,1,6) );
+
+       print "Pages = $pages : rec = $rec_start Datestamp $vDateStamp $crc\n"; 
+       
+       $cnt_out = $ob->write( pack("h", 0x06) );
+       #if ($pages == 513 ) { return -1 }
+       my @arc_rec_lst;          
+       foreach my $page (1..$pages) {
+               my $page_sz = 267;      
+               $ob->read_interval(0);
+               my ($cnt_in, $str) = $ob->read($page_sz); #,3
+               printf("len=%s\n",length($str));
+               if ($cnt_in!=$page_sz) {
+                       dienice("hmm, dmpaft only got $cnt_in bytes. was expecting $page_sz");
+               }
+               print "Page $page\n"; 
+               #print DMP $str,"\n";
+               #print "ACK receipt of page $page\n";
+               #$ob->write( pack("h", 0x06) );
+               #next;
+               my $calc_crc = CRC_CCITT($str);
+               my $crc = unpack "%n", substr($str,265,2);
+               print "page crc=$crc, calc_crc=$calc_crc\n";
+               my $rec_sz = 52;
+               my $date_prev = 0;      
+               my %hsh;
+         
+               foreach my $rec ( 0..4 ) {
+                       if ( ($page == 1) && ($rec < $rec_start ) ) {
+                               next;
+                       }                                       # Find the right starting point... 
+
+                       my $start_ptr = 1 + ($rec * $rec_sz );    
+                       my $rec_str = substr($str, $start_ptr ,52);
+                 
+                       #print "$start_ptr \t > " . unpack( "h*", $rec_str) . "\n"; 
+                 
+                       my $date = substr($rec_str,0,2);  
+                       my $date_curr =  unpack "s", $date;
+                 
+                       # Check if we have wrapped... 
+                       if ( $date_curr < $date_prev ) {
+                               last;
+                       }       
+                       $date_prev = $date_curr;       
+                 
+                       $hsh{'date_stamp'} =  $date_curr; 
+                       $hsh{'time_stamp'} =  unpack "s", substr($rec_str,2,2); 
+                  
+                       $hsh{'day'}    = unpack( "c", $date & pack("c",0x1F) ); 
+                       $hsh{'month'}  = ( $hsh{'date_stamp'} >> 5) & 0xF; 
+                       $hsh{'year'}  =  ( $hsh{'date_stamp'} >> 9) + 2000; 
+               
+                       $hsh{'hour'}  = sprintf("%02d", int ( $hsh{'time_stamp'} / 100 )); 
+                 
+                       $hsh{'min'}  =  $hsh{'time_stamp'} - ($hsh{'hour'} * 100);  
+                       $hsh{'min'}  =  sprintf("%02d", $hsh{'min'}); 
+               
+                       $hsh{'time_stamp_fmt'}  =  "$hsh{'hour'}:$hsh{'min'}:00"; 
+                       $hsh{'date_stamp_fmt'}  =  "$hsh{'year'}_$hsh{'month'}_$hsh{'day'}"; 
+
+                       #$hsh{'unixtime'} = timelocal(0,$hsh{min}, $hsh{hour}, $hsh{day}, $hsh{month}-1, $hsh{year}-1900);
+                                 
+                       $hsh{'Air_Temp'} = unpack("s", substr($rec_str,4,2)) / 10; 
+                       $hsh{'Air_Temp_Hi'} = unpack("s", substr($rec_str,6,2)) / 10; 
+                       $hsh{'Air_Temp_Lo'} = unpack("s", substr($rec_str,8,2)) / 10;
+                       $hsh{'Rain_Clicks'} = unpack("s", substr($rec_str,10,2));
+                       $hsh{'Rain_Rate'}   = unpack("s", substr($rec_str,12,2)) / 100; # Inches per hour
+                       $hsh{'Barometric_Press'}   = unpack("s", substr $rec_str,14,2) / 1000;  
+                       $hsh{'Solar'}   = unpack("s", substr $rec_str,16,2); # watt/m**2
+                       $hsh{'Wind_Samples'}  = unpack("s", substr $rec_str,18,2);   
+                       $hsh{'Air_Temp_Inside'}  = unpack("s", substr $rec_str,20,2) / 10;  
+
+                       $hsh{'Relative_Humidity_Inside'}  = unpack("C", substr $rec_str,22,1);
+                       $hsh{'Relative_Humidity'} = unpack("C", substr $rec_str,23,1);
+
+                       $hsh{'Wind_Speed'}    =  unpack("C", substr($rec_str,24,1)); 
+                       $hsh{'Wind_Gust_Max'} = unpack("C", substr($rec_str,25,1));
+                       $hsh{'Wind_Dir_Max'}  = unpack("C", substr($rec_str,26,1));
+                       $hsh{'Wind_Dir'}      = unpack("C", substr($rec_str,27,1));
+
+                       $hsh{'UV'} = unpack("C", substr($rec_str,28,1)) / 10;
+                       $hsh{'ET'} = unpack("C", substr($rec_str,29,1)) / 1000;
+
+                       $hsh{'Solar_Max'} = unpack("s", substr($rec_str,30,2)); 
+                       $hsh{'UV_Max'} = unpack("C", substr($rec_str,32,1));
+                 
+                       $hsh{'Forecast_Rule'} = unpack("C", substr($rec_str,33,1));
+               
+#                      $hsh{'Dew_Point'}  = _dew_point($hsh{'Air_Temp'},$hsh{'Relative_Humidity'}); 
+                                       
+                       # Miscellaneous others omitted for now
+                                       
+                       print "date> $hsh{'Air_Temp'} $hsh{'time_stamp'} $hsh{'time_stamp_fmt'}  $hsh{'date_stamp'} $hsh{'date_stamp_fmt'}\n";                  
+                       #print Dumper \%hsh; 
+                 
+                       push @arc_rec_lst, {%hsh}; 
+               }       
+                 
+               #$in = <STDIN>; # Testing step through facility
+               #if ($in =~ /q/i ) {  $port_obj->write( pack("h", 0x1B) ); last; }
+               #else              {  $port_obj->write( pack("h", 0x06) ); }
+               print "ACK receipt of page\n";
+               $ob->write( pack("h", 0x06) );
+         
+       }
+       close DMP;
+}
+
+package Device::SerialPort;
+
+sub read_interval
+{
+
+}
diff --git a/yr.php b/yr.php
new file mode 100644 (file)
index 0000000..22bb30d
--- /dev/null
+++ b/yr.php
@@ -0,0 +1,763 @@
+<?php\r
+/*\r
+ yr.php  -  YR.no forecast on YOUR page!\r
\r
+ This script was downloaded from http://www.yr.no/verdata/1.5542682\r
+ Please read the tips on that page on how you would/should use this script\r
+\r
+ You need a webserver with PHP version 5 or later to run this script.\r
+ A lot of comments are in Norwegian only. We will be translating to english whenever we have the opportunity.\r
+ For feedback / bug repports / feature requests, please contact:  Lennart André Rolland <lennart.andre.rolland@nrk.no>\r
+\r
+ ###### Changelog\r
+\r
+ Versjon: 2.6 - Lennart André Rolland (lennart.andre.rolland@nrk.no) / NRK - 2008.11.11 11:48\r
+ * Added option to remove banner ($yr_use_banner)\r
+ * Added option to allow any target for yr.no urls ($yr_link_target)\r
+\r
+ Versjon: 2.5 - Lennart André Rolland (lennart.andre.rolland@nrk.no) / NRK - 2008.09.25 09:24\r
+ * Cache will now update on parameter changes (cache file is prefixed with md5 digest of all relevant parameters)\r
+   This change will in the future make it easier to use the script for multiple locations in one go.   \r
+ * Most relevant comments translated to english\r
+\r
+ Versjon 2.4 - Sven-Ove Bjerkan (sven-ove@smart-media.no) / Smart-Media AS - 2008.10.22 12:14\r
+ * Endret funksjonalitet ifbm med visning av PHP-feil (fjernet blant annet alle "@", dette styres av error_reporting())\r
+ * Ved feilmelding så ble denne lagret i lokal cache slik at man fikk opp feilmld hver gang inntil "$yr_maxage" inntreffer og den forsøker å laste på nytt - den cacher nå ikke hvis det oppstår en feil\r
+ * $yr_use_text, $yr_use_links og $yr_use_table ble overstyrt til "true" uavhengig av brukerens innstilling - rettet!\r
+\r
+ Versjon: 2.3 - Lennart André Rolland (lennart.andre.rolland@nrk.no) / NRK - 2008.09.25 09:24\r
+ * File permissions updated\r
+ * Caching is stored in HTML isntead of XML for security\r
+ * Other security and efficiency improvements\r
+\r
+\r
+\r
+ ###### INSTRUCTIONS:\r
+\r
+ 1. Only edit this script in editors with ISO-8859-1 or ISO-8859-15 character set.\r
+ 2. Edit the settings below\r
+ 3. Transfer the script to a folder in your webroot.\r
+ 4. Make sure that the webserver has write access to the folder where thsi script is placed. It will create a folder called yr-cache and place cached HTML data in that directory.\r
+\r
+ */\r
+\r
+///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  /\r
+///  ///  ///  ///  ///  Settings  ///  ///  ///  ///  ///  ///  ///  ///  //\r
+//  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///\r
+\r
+// 1. Lenke: Lenke til stedet på yr.no (Uten siste skråstrek. Bruk vanlig æøå i lenka )\r
+//    Link: Link to the url for the location on yr.no (Without the last Slash.)\r
+$yr_url='http://www.yr.no/sted/Norge/Vest-Agder/Lyngdal/Kvås';\r
+$yr_url='http://www.yr.no/stad/Sverige/Jämtland/Lofsdalen';\r
+\r
+// 2. Stedsnavnet: Skriv inn navnet på stedet. La stå tom for å falle tilbake til navnet i lenken\r
+//    Location: The name of the location. Leave empty to fallback to the location in the url.\r
+$yr_name='';\r
+\r
+// 3. Bruk header og footer: Velg om du vil ha med header og/eller  footer\r
+//    Use Header and footers: Select to have HTML headers/footers wrapping the content (useful for debugging)\r
+//PS: Header for HTML dokumentet er XHTML 1.0 Strict\r
+//    Skrus som regel av når du inlemmer i eksisterende dokument!\r
+//\r
+$yr_use_header=$yr_use_footer=true;\r
+\r
+// 4. Deler: Velg delene av varselet du vil ta med!\r
+//    Parts: Choose which parts of the forecast to include\r
+$yr_use_banner=true; //yr.no Banner\r
+$yr_use_text=false;   //Tekstvarsel\r
+$yr_use_links=true;  //Lenker til varsel på yr.no\r
+$yr_use_table=true;  //Tabellen med varselet\r
+\r
+// 5. Mellomlagringstid: Antall sekunder før nytt varsel hentes fra yr.no.\r
+//    Cachetime: Number of seconds to keep forecast in local cache\r
+//    Den anbefalt verdien på 1200 vil oppdatere siden hver 20. minutt.\r
+//\r
+//    PS: Vi ønsker at du setter 20 minutters mellomlagringstid fordi\r
+//    det vil gi høyere ytelse, både for yr.no og deg! MEN for å få til dette\r
+//    vil vi opprette en mappe og lagre en fil i denne mappen. Vi har gått\r
+//    gjennom scriptet veldig nøye for å forsikre oss om at det er feilfritt.\r
+//    Likevel er dette ikke helt uproblematisk i forhold til sikkerhet.\r
+//    Hvis du har problemer med dette kan du sette $yr_maxage til 0 for å skru\r
+//    av mellomlagringen helt!\r
+$yr_maxage=1200;\r
+\r
+// 6. Utløpstid: Denne instillingen lar deg velge hvor lenge yr.no har på å\r
+//    levere varselet i sekunder.\r
+//    Timeout: How long before this script gives up fetching data from yr.no\r
+//\r
+//    Hvis yr.no skulle være nede eller det er\r
+//    forstyrrelser i båndbredden ellers, vil varselet erstattes med en\r
+//    feilmelding til situasjonen er bedret igjen. PS: gjelder kun når nytt\r
+//    varsel hentes! Påvirker ikke varsel mens siden viser varsel fra\r
+//    mellomlageret. Den anbefalte verdien på 10 sekunder fungerer bra.\r
+$yr_timeout=10;\r
+\r
+// 7. Mellomlagrinsmappe: Velg navn på mappen til mellomlagret data.\r
+//    Cachefolder: Where to put cache data\r
+//\r
+//Dette scriptet vil forsøke å opprette mappen om den ikke finnes.\r
+$yr_datadir='yr_cache';\r
+\r
+\r
+// 8. Lenke mål: Velg hvilken target som skal brukes på lenker til yr.no\r
+//    Link target: Choose which target to use for links to yr.no\r
+$yr_link_target='_top';\r
+\r
+// 9. Vis feilmeldinger: Sett til "true" hvis du vil ha feilmeldinger.\r
+//    Show errors: Useful while debugging.\r
+//\r
+//greit ved feilsøking, men bør ikke være aktivert i drift.\r
+$yr_vis_php_feilmeldinger=true;\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  /\r
+///  ///  ///  ///  ///  Code ///  ///  ///  ///  ///  ///  ///  ///  ///  //\r
+//  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///\r
+// Skru på feilmeldinger i starten\r
+if($yr_vis_php_feilmeldinger) {\r
+       error_reporting(E_ALL);\r
+       ini_set('display_errors', true);\r
+}\r
+else {\r
+       error_reporting(0);\r
+       ini_set('display_errors', false);\r
+}\r
+\r
+//Opprett en komunikasjon med yr\r
+$yr_xmlparse = &new YRComms();\r
+//Opprett en presentasjon\r
+$yr_xmldisplay = &new YRDisplay();\r
+\r
+$yr_try_curl=true;\r
+\r
+//Gjenomfør oppdraget basta bom.\r
+die($yr_xmldisplay->generateHTMLCached($yr_url, $yr_name, $yr_xmlparse, $yr_url, $yr_try_curl, $yr_use_header, $yr_use_footer, $yr_use_banner, $yr_use_text, $yr_use_links, $yr_use_table, $yr_maxage, $yr_timeout, $yr_link_target));\r
+\r
+\r
+///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  /\r
+///  ///  ///  ///  ///  Hjelpekode starter her   ///  ///  ///  ///  ///  //\r
+//  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///  ///\r
+\r
+\r
+function retar($array, $html = false, $level = 0) {\r
+       if(is_array($array)){\r
+               $space = $html ? "&nbsp;" : " ";\r
+               $newline = $html ? "<br />" : "\n";\r
+               $spaces='';\r
+               for ($i = 1; $i <= 3; $i++)$spaces .= $space;\r
+               $tabs=$spaces;\r
+               for ($i = 1; $i <= $level; $i++)$tabs .= $spaces;\r
+               $output = "Array(" . $newline . $newline;\r
+               $cnt=sizeof($array);\r
+               $j=0;\r
+               foreach($array as $key => $value) {\r
+                       $j++;\r
+                       if (is_array($value)) {\r
+                               $level++;\r
+                               $value = retar($value, $html, $level);\r
+                               $level--;\r
+                       }\r
+                       else $value="'$value'";\r
+                       $output .=  "$tabs'$key'=> $value";\r
+                       if($j<$cnt)$output .=  ',';\r
+                       $output .=  $newline;\r
+               }\r
+               $output.=$tabs.')'.$newline;\r
+       }\r
+       else{\r
+               $output="'$array'";\r
+       }\r
+       return $output;\r
+}\r
+\r
+\r
+// Klasse for lesing og tilrettelegging av YR data\r
+class YRComms{\r
+\r
+       //Generer gyldig yr.no array med værdata byttet ut med en enkel feilmelding\r
+       private function getYrDataErrorMessage($msg="Feil"){\r
+               return Array(\r
+      '0'=> Array('tag'=> 'WEATHERDATA','type'=> 'open','level'=> '1'),\r
+      '1'=> Array('tag'=> 'LOCATION','type'=> 'open','level'=> '2'),\r
+      '2'=> Array('tag'=> 'NAME','type'=> 'complete','level'=> '3','value'=> $msg),\r
+      '3'=> Array('tag'=> 'LOCATION','type'=> 'complete','level'=> '3'),\r
+      '4'=> Array( 'tag'=> 'LOCATION', 'type'=> 'close', 'level'=> '2'),\r
+      '5'=> Array( 'tag'=> 'FORECAST', 'type'=> 'open', 'level'=> '2'),\r
+      '6'=> Array( 'tag'=> 'ERROR', 'type'=> 'complete', 'level'=> '3', 'value'=> $msg),\r
+      '7'=> Array( 'tag'=> 'FORECAST', 'type'=> 'close', 'level'=> '2'),\r
+      '8'=> Array( 'tag'=> 'WEATHERDATA', 'type'=> 'close', 'level'=> '1')\r
+               );\r
+       }\r
+\r
+       //Generer gyldig yr.no XML med værdata byttet ut med en enkel feilmelding\r
+       private function getYrXMLErrorMessage($msg="Feil"){\r
+               $msg=$this->getXMLEntities($msg);\r
+               //die('errmsg:'.$msg);\r
+               $data=<<<EOT\r
+<weatherdata>\r
+  <location />\r
+  <forecast>\r
+  <error>$msg</error>\r
+    <text>\r
+      <location />\r
+    </text>\r
+  </forecast>\r
+</weatherdata>\r
+\r
+EOT\r
+               ;\r
+               //die($data);\r
+               return $data;\r
+       }\r
+       \r
+       // Sørger for å laste ned XML fra yr.no og leverer data tilbake i en streng\r
+       private function loadXMLData($xml_url,$try_curl=true,$timeout=10){\r
+               global $yr_datadir;\r
+               $xml_url.='/varsel.xml';\r
+               // Lag en timeout på contexten\r
+               $ctx = stream_context_create(array( 'http' => array('timeout' => $timeout)));\r
+\r
+               // Prøv å åpne direkte først\r
+               //NOTE: This will spew ugly errors even when they are handled later. There is no way to avoid this but prefixing with @ (slow) or turning off error reporting\r
+               $data=file_get_contents($xml_url,0,$ctx);\r
+\r
+               if(false!=$data){\r
+                       //Jippi vi klarte det med vanlig fopen url wrappers!\r
+               }\r
+               // Vanlig fopen_wrapper feilet, men vi har cURL tilgjengelig\r
+               else if($try_curl && function_exists('curl_init')){\r
+                       $lokal_xml_url = $yr_datadir .'/curl.temp.xml';\r
+                       $data='';\r
+                       $ch = curl_init($xml_url);\r
+                       // Åpne den lokale temp filen for skrive tilgang (med cURL hooks enablet)\r
+                       $fp = fopen($lokal_xml_url, "w");\r
+                       // Last fra yr.no til lokal kopi med curl\r
+                       curl_setopt($ch, CURLOPT_FILE, $fp);\r
+                       curl_setopt($ch, CURLOPT_HEADER, 0);\r
+                       curl_setopt($ch, CURLOPT_POSTFIELDS, '');\r
+                       curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);\r
+                       curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);\r
+                       curl_exec($ch);\r
+                       curl_close($ch);\r
+                       // Lukk lokal kopi\r
+                       fclose($fp);\r
+                       // Åpne lokal kopi igjen og les in alt innholdet\r
+                       $data=file_get_contents($lokal_xml_url,0,$ctx);\r
+                       //Slett temp data\r
+                       unlink($lokal_xml_url);\r
+                       // Sjekk for feil\r
+                       if(false==$data)$data=$this->getYrXMLErrorMessage('Det oppstod en feil mens værdata ble lest fra yr.no. Teknisk info: Mest antakelig: kobling feilet. Nest mest antakelig: Det mangler støtte for fopen wrapper, og cURL feilet også. Minst antakelig: cURL har ikke rettigheter til å lagre temp.xml');\r
+               }\r
+               // Vi har verken fopen_wrappers eller cURL\r
+               else{\r
+                       $data=$this->getYrXMLErrorMessage('Det oppstod en feil mens værdata ble forsøkt lest fra yr.no. Teknisk info: Denne PHP-installasjon har verken URL enablede fopen_wrappers eller cURL. Dette gjør det umulig å hente ned værdata. Se imiddlertid følgende dokumentasjon: http://no.php.net/manual/en/wrappers.php, http://no.php.net/manual/en/book.curl.php');\r
+                       //die('<pre>LO:'.retar($data));\r
+               }\r
+               //die('<pre>XML for:'.$xml_url.' WAS: '.$data);\r
+               // Når vi har kommet hit er det noe som tyder på at vi har lykkes med å laste værdata, ller i det minste lage en teilmelding som beskriver eventuelle problemer\r
+               return $data;\r
+       }\r
+\r
+       // Last XML til en array struktur\r
+       private function parseXMLIntoStruct($data){\r
+               global $yr_datadir;\r
+               $parser = xml_parser_create('ISO-8859-1');\r
+               if((0==$parser)||(FALSE==$parser))return $this->getYrDataErrorMessage('Det oppstod en feil mens værdata ble forsøkt hentet fra yr.no. Teknisk info: Kunne ikke lage XML parseren.');\r
+               $vals = array();\r
+               //die('<pre>'.retar($data).'</pre>');\r
+               if(FALSE==xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1))return $this->getYrDataErrorMessage('Det oppstod en feil mens værdata ble forsøkt hentet fra yr.no. Teknisk info: Kunne ikke stille inn XML-parseren.');\r
+               if(0==xml_parse_into_struct($parser, $data, $vals, $index))return $this->getYrDataErrorMessage('Det oppstod en feil mens værdata ble forsøkt hentet fra yr.no. Teknisk info: Parsing av XML feilet.');\r
+               if(FALSE==xml_parser_free($parser))return $this->getYrDataErrorMessage('Det oppstod en feil mens værdata ble forsøkt hentet fra yr.no. Kunne ikke frigjøre XML-parseren.');\r
+               //die('<pre>'.retar($vals).'</pre>');\r
+               return $vals;\r
+       }\r
+\r
+\r
+       // Rense tekst data (av sikkerhetshensyn)\r
+       private function sanitizeString($in){\r
+               //return $in;\r
+               if(is_array($in))return $in;\r
+               if(null==$in)return null;\r
+               return htmlentities(strip_tags($in));\r
+       }\r
+\r
+       // Rense tekst data (av sikkerhetshensyn)\r
+       public function reviveSafeTags($in){\r
+               //$in=$in.'<strong>STRONG</strong> <u>UNDERLINE</u> <b>BOLD</b> <i>ITALICS</i>';\r
+               return str_ireplace(array('&lt;strong&gt;','&lt;/strong&gt;','&lt;u&gt;','&lt;/u&gt;','&lt;b&gt;','&lt;/b&gt;','&lt;i&gt;','&lt;/i&gt;'),array('<strong>','</strong>','<u>','</u>','<b>','</b>','<i>','</i>'),$in);\r
+       }\r
+\r
+\r
+\r
+       private function rearrangeChildren($vals, &$i) {\r
+               $children = array(); // Contains node data\r
+               // Sikkerhet: sørg for at all data som parses strippes for farlige ting\r
+               if (isset($vals[$i]['value']))$children['VALUE'] = $this->sanitizeString($vals[$i]['value']);\r
+               while (++$i < count($vals)){\r
+                       // Sikkerhet: sørg for at all data som parses strippes for farlige ting\r
+                       if(isset($vals[$i]['value']))$val=$this->sanitizeString($vals[$i]['value']);\r
+                       else unset($val);\r
+                       if(isset($vals[$i]['type']))$typ=$this->sanitizeString($vals[$i]['type']);\r
+                       else unset($typ);\r
+                       if(isset($vals[$i]['attributes']))$atr=$this->sanitizeString($vals[$i]['attributes']);\r
+                       else unset($atr);\r
+                       if(isset($vals[$i]['tag']))$tag=$this->sanitizeString($vals[$i]['tag']);\r
+                       else unset($tag);\r
+                       // Fyll inn strukturen vær slik vi vil ha den\r
+                       switch ($vals[$i]['type']){\r
+                               case 'cdata': $children['VALUE']=(isset($children['VALUE']))?$val:$children['VALUE'].$val; break;\r
+                               case 'complete':\r
+                                       if (isset($atr)) {\r
+                                               $children[$tag][]['ATTRIBUTES'] = $atr;\r
+                                               $index = count($children[$tag])-1;\r
+                                               if (isset($val))$children[$tag][$index]['VALUE'] = $val;\r
+                                               else $children[$tag][$index]['VALUE'] = '';\r
+                                       } else {\r
+                                               if (isset($val))$children[$tag][]['VALUE'] = $val;\r
+                                               else $children[$tag][]['VALUE'] = '';\r
+                                       }\r
+                                       break;\r
+                               case 'open':\r
+                                       if (isset($atr)) {\r
+                                               $children[$tag][]['ATTRIBUTES'] = $atr;\r
+                                               $index = count($children[$tag])-1;\r
+                                               $children[$tag][$index] = array_merge($children[$tag][$index],$this->rearrangeChildren($vals, $i));\r
+                                       } else $children[$tag][] = $this->rearrangeChildren($vals, $i);\r
+                                       break;\r
+                               case 'close': return $children;\r
+                       }\r
+               }\r
+       }\r
+       // Ommøbler data til å passe vårt formål, og returner\r
+       private function rearrangeDataStruct($vals){\r
+               //die('<pre>'.$this->retar($vals).'<\pre>');\r
+               $tree = array();\r
+               $i = 0;\r
+               if (isset($vals[$i]['attributes'])) {\r
+                       $tree[$vals[$i]['tag']][]['ATTRIBUTES']=$vals[$i]['attributes'];\r
+                       $index=count($tree[$vals[$i]['tag']])-1;\r
+                       $tree[$vals[$i]['tag']][$index]=array_merge($tree[$vals[$i]['tag']][$index], $this->rearrangeChildren($vals, $i));\r
+               } else $tree[$vals[$i]['tag']][] = $this->rearrangeChildren($vals, $i);\r
+               //die("<pre>".retar($tree));\r
+               //Hent ut det vi bryr oss om\r
+               if(isset($tree['WEATHERDATA'][0]['FORECAST'][0]))return $tree['WEATHERDATA'][0]['FORECAST'][0];\r
+               else return YrComms::getYrDataErrorMessage('Det oppstod en feil ved behandling av data fra yr.no. Vennligst gjør administrator oppmerksom på dette! Teknisk: data har feil format.');\r
+       }\r
+\r
+       // Hovedmetode. Laster XML fra en yr.no URI og parser denne\r
+       public function getXMLTree($xml_url, $try_curl, $timeout){\r
+               // Last inn XML fil og parse til et array hierarcki, ommøbler data til å passe vårt formål, og returner\r
+               return $this->rearrangeDataStruct($this->parseXMLIntoStruct($this->loadXMLData($xml_url,$try_curl,$timeout)));\r
+       }\r
+\r
+       // Statisk hjelper for å parse ut tid i yr format\r
+       public function parseTime($yr_time, $do24_00=false){\r
+               $yr_time=str_replace(":00:00", "", $yr_time);\r
+               if($do24_00)$yr_time=str_replace("00", "24", $yr_time);\r
+               return $yr_time;\r
+       }\r
+\r
+       // Statisk hjelper for å besørge riktig encoding ved å oversette spesielle ISO-8859-1 karakterer til HTML/XHTML entiteter\r
+       public function convertEncodingEntities($yrraw){\r
+               $conv=str_replace("æ", "&aelig;", $yrraw);\r
+               $conv=str_replace("ø", "&oslash;", $conv);\r
+               $conv=str_replace("å", "&aring;", $conv);\r
+               $conv=str_replace("Æ", "&AElig;", $conv);\r
+               $conv=str_replace("Ø", "&Oslash;", $conv);\r
+               $conv=str_replace("Å", "&Aring;", $conv);\r
+               return $conv;\r
+       }\r
+\r
+       // Statisk hjelper for å besørge riktig encoding vedå oversette spesielle UTF karakterer til ISO-8859-1\r
+       public function convertEncodingUTF($yrraw){\r
+               $conv=str_replace("æ", "æ", $yrraw);\r
+               $conv=str_replace("ø", "ø", $conv);\r
+               $conv=str_replace("Ã¥", "å", $conv);\r
+               $conv=str_replace("Ã\86", "Æ", $conv);\r
+               $conv=str_replace("Ã\98", "Ø", $conv);\r
+               $conv=str_replace("Ã\85", "Å", $conv);\r
+               return $conv;\r
+       }\r
+\r
+\r
+       public function getXMLEntities($string){\r
+               return preg_replace('/[^\x09\x0A\x0D\x20-\x7F]/e', '$this->_privateXMLEntities("$0")', $string);\r
+       }\r
+\r
+       private function _privateXMLEntities($num){\r
+               $chars = array(\r
+               128 => '&#8364;', 130 => '&#8218;',\r
+               131 => '&#402;', 132 => '&#8222;',\r
+               133 => '&#8230;', 134 => '&#8224;',\r
+               135 => '&#8225;',136 => '&#710;',\r
+               137 => '&#8240;',138 => '&#352;',\r
+               139 => '&#8249;',140 => '&#338;',\r
+               142 => '&#381;', 145 => '&#8216;',\r
+               146 => '&#8217;',147 => '&#8220;',\r
+               148 => '&#8221;',149 => '&#8226;',\r
+               150 => '&#8211;',151 => '&#8212;',\r
+               152 => '&#732;',153 => '&#8482;',\r
+               154 => '&#353;',155 => '&#8250;',\r
+               156 => '&#339;',158 => '&#382;',\r
+               159 => '&#376;');\r
+               $num = ord($num);\r
+               return (($num > 127 && $num < 160) ? $chars[$num] : "&#".$num.";" );\r
+       }\r
+}\r
+\r
+// Klasse for å vise data fra yr. Kompatibel med YRComms sin datastruktur\r
+class YRDisplay{\r
+\r
+       // Akkumulator variabl for å holde på generert HTML\r
+       var $ht='';\r
+       // Yr Url\r
+       var $yr_url='';\r
+       // Yr stedsnavn\r
+       var $yr_name='';\r
+       // Yr data\r
+       var $yr_data=Array();\r
+\r
+       //Filename for cached HTML. MD5 hash will be prepended to allow caching of several pages\r
+       var $datafile='yr.html';\r
+       //The complete path to the cache file\r
+       var $datapath='';\r
+\r
+       // Norsk grovinndeling av de 360 grader vindretning\r
+       var $yr_vindrettninger=array(\r
+    'nord','nord-nord&oslash;st','nord&oslash;st','&oslash;st-nord&oslash;st',\r
+    '&oslash;st','&oslash;st-s&oslash;r&oslash;st','s&oslash;r&oslash;st','s&oslash;r-s&oslash;r&oslash;st',\r
+    's&oslash;r','s&oslash;r-s&oslash;rvest', 's&oslash;rvest','vest-s&oslash;rvest',\r
+    'vest', 'vest-nordvest','nordvest', 'nord-nordvest', 'nord');\r
+\r
+       // Hvor hentes bilder til symboler fra?\r
+       var $yr_imgpath='http://fil.nrk.no/yr/grafikk/sym/b38';\r
+\r
+\r
+       //Generer header for varselet\r
+       public function getHeader($use_full_html){\r
+               // Her kan du endre header til hva du vil. NB! Husk å skru det på, ved å endre instillingene i toppen av dokumentet\r
+               if($use_full_html){\r
+                       $this->ht.=<<<EOT\r
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml">\r
+  <head>\r
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
+    <title>V&aelig;rvarsel fra yr.no</title>\r
+    <link href="http://www12.nrk.no/yr.no/yr-php.css" rel="stylesheet" type="text/css" />\r
+  </head>\r
+  <body>\r
+\r
+EOT\r
+                       ;\r
+               }\r
+               $this->ht.=<<<EOT\r
+    <div id="yr-varsel">\r
+\r
+EOT\r
+               ;\r
+       }\r
+\r
+       //Generer footer for varselet\r
+       public function getFooter($use_full_html){\r
+               $this->ht.=<<<EOT\r
+    </div>\r
+\r
+EOT\r
+               ;\r
+               // Her kan du endre footer til hva du vil. NB! Husk å skru det på, ved å endre instillingene i toppen av dokumentet\r
+               if($use_full_html){\r
+                       $this->ht.=<<<EOT\r
+  </body>\r
+</html>\r
+\r
+EOT\r
+                       ;\r
+               }\r
+       }\r
+\r
+\r
+   //Generer Copyright for data fra yr.no\r
+   public function getBanner($target='_top'){\r
+      $url=YRComms::convertEncodingEntities($this->yr_url);\r
+      $this->ht.=<<<EOT\r
+      <h1><a href="http://www.yr.no/" target="$target"><img src="http://fil.nrk.no/yr/grafikk/php-varsel/topp.png" alt="yr.no" title="yr.no er en tjeneste fra Meteorologisk institutt og NRK" /></a></h1>\r
+\r
+EOT\r
+      ;\r
+   }\r
+\r
+\r
+   //Generer Copyright for data fra yr.no\r
+   public function getCopyright($target='_top'){\r
+      $url=YRComms::convertEncodingEntities($this->yr_url);\r
+      /*\r
+       Du må ta med teksten nedenfor og ha med lenke til yr.no.\r
+       Om du fjerner denne teksten og lenkene, bryter du vilkårene for bruk av data fra yr.no.\r
+       Det er straffbart å bruke data fra yr.no i strid med vilkårene.\r
+       Du finner vilkårene på http://www.yr.no/verdata/1.3316805\r
+       */\r
+      $this->ht.=<<<EOT\r
+      <h2><a href="$url" target="$target">V&aelig;rvarsel for $this->yr_name</a></h2>\r
+      <p><a href="http://www.yr.no/" target="$target"><strong>V&aelig;rvarsel fra yr.no, levert av Meteorologisk institutt og NRK.</strong></a></p>\r
+\r
+EOT\r
+      ;\r
+   }\r
+\r
+\r
+   //Generer tekst for været\r
+       public function getWeatherText(){\r
+               if((isset($this->yr_data['TEXT'])) && (isset($this->yr_data['TEXT'][0]['LOCATION']))&& (isset($this->yr_data['TEXT'][0]['LOCATION'][0]['ATTRIBUTES'])) ){\r
+                       $yr_place=$this->yr_data['TEXT'][0]['LOCATION'][0]['ATTRIBUTES']['NAME'];\r
+                       if(!isset($this->yr_data['TEXT'][0]['LOCATION'][0]['TIME']))return;\r
+                       foreach($this->yr_data['TEXT'][0]['LOCATION'][0]['TIME'] as $yr_var2){\r
+                               // Små bokstaver\r
+                               $l=(YRComms::convertEncodingUTF($yr_var2['TITLE'][0]['VALUE']));\r
+                               // Rettet encoding\r
+                               $e=YRComms::reviveSafeTags(YRComms::convertEncodingUTF($yr_var2['BODY'][0]['VALUE']));\r
+                               // Spytt ut!\r
+                               $this->ht.=<<<EOT\r
+      <p><strong>$yr_place $l</strong>:$e</p>\r
+\r
+EOT\r
+                               ;\r
+                       }\r
+               }\r
+       }\r
+\r
+       //Generer lenker til andre varsel\r
+       public function getLinks($target='_top'){\r
+               // Rens url\r
+               $url=YRComms::convertEncodingEntities($this->yr_url);\r
+               // Spytt ut\r
+               $this->ht.=<<<EOT\r
+      <p class="yr-lenker">$this->yr_name p&aring; yr.no:\r
+        <a href="$url/" target="$target">Varsel med kart</a>\r
+        <a href="$url/time_for_time.html" target="$target">Time for time</a>\r
+        <a href="$url/helg.html" target="$target">Helg</a>\r
+        <a href="$url/langtidsvarsel.html" target="$target">Langtidsvarsel</a>\r
+      </p>\r
+\r
+EOT\r
+               ;\r
+       }\r
+\r
+       //Generer header for værdatatabellen\r
+       public function getWeatherTableHeader(){\r
+               $name=$this->yr_name;\r
+               $this->ht.=<<<EOT\r
+      <table summary="V&aelig;rvarsel for $name fra yr.no">\r
+        <thead>\r
+          <tr>\r
+            <th class="v" colspan="3"><strong>Varsel for $name</strong></th>\r
+            <th>Nedb&oslash;r</th>\r
+            <th>Temp.</th>\r
+            <th class="v">Vind</th>\r
+            <th>Vindstyrke</th>\r
+          </tr>\r
+        </thead>\r
+        <tbody>\r
+\r
+EOT\r
+               ;\r
+       }\r
+\r
+\r
+       //Generer innholdet i værdatatabellen\r
+       public function getWeatherTableContent(){\r
+               $thisdate='';\r
+               $dayctr=0;\r
+               if(!isset($this->yr_data['TABULAR'][0]['TIME']))return;\r
+               $a=$this->yr_data['TABULAR'][0]['TIME'];\r
+\r
+               foreach($a as $yr_var3){\r
+                       list($fromdate, $fromtime)=explode('T', $yr_var3['ATTRIBUTES']['FROM']);\r
+                       list($todate, $totime)=explode('T', $yr_var3['ATTRIBUTES']['TO']);\r
+                       $fromtime=YRComms::parseTime($fromtime);\r
+                       $totime=YRComms::parseTime($totime, 1);\r
+                       if($fromdate!=$thisdate){\r
+                               $divider=<<<EOT\r
+          <tr>\r
+            <td colspan="7" class="skilje"></td>\r
+          </tr>\r
+\r
+EOT\r
+                               ;\r
+                               list($thisyear, $thismonth, $thisdate)=explode('-', $fromdate);\r
+                               $displaydate=$thisdate.".".$thismonth.".".$thisyear;\r
+                               $firstcellcont=$displaydate;\r
+                               $thisdate=$fromdate;\r
+                               ++$dayctr;\r
+                       }else $divider=$firstcellcont='';\r
+\r
+                       // Vis ny dato\r
+                       if($dayctr<7){\r
+                               $this->ht.=$divider;\r
+                               // Behandle symbol\r
+                               $imgno=$yr_var3['SYMBOL'][0]['ATTRIBUTES']['NUMBER'];\r
+                               if($imgno<10)$imgno='0'.$imgno;\r
+                               switch($imgno){\r
+                                       case '01': case '02': case '03': case '05': case '06': case '07': case '08':\r
+                                               $imgno.="d"; $do_daynight=1; break;\r
+                                       default: $do_daynight=0;\r
+                               }\r
+                               // Behandle regn\r
+                               $rain=$yr_var3['PRECIPITATION'][0]['ATTRIBUTES']['VALUE'];\r
+                               if($rain==0.0)$rain="0";\r
+                               else{\r
+                                       $rain=intval($rain);\r
+                                       if($rain<1)$rain='&lt;1';\r
+                                       else $rain=round($rain);\r
+                               }\r
+                               $rain.=" mm";\r
+                               // Behandle vind\r
+                               $winddir=round($yr_var3['WINDDIRECTION'][0]['ATTRIBUTES']['DEG']/22.5);\r
+                               $winddirtext=$this->yr_vindrettninger[$winddir];\r
+                               // Behandle temperatur\r
+                               $temper=round($yr_var3['TEMPERATURE'][0]['ATTRIBUTES']['VALUE']);\r
+                               if($temper>=0)$tempclass='pluss';\r
+                               else $tempclass='minus';\r
+\r
+                               // Rund av vindhastighet\r
+                               $r=round($yr_var3['WINDSPEED'][0]['ATTRIBUTES']['MPS']);\r
+                               // Så legger vi ut hele den ferdige linjen\r
+                               $s=$yr_var3['SYMBOL'][0]['ATTRIBUTES']['NAME'];\r
+                               $w=$yr_var3['WINDSPEED'][0]['ATTRIBUTES']['NAME'];\r
+\r
+                               $this->ht.=<<<EOT\r
+          <tr>\r
+            <th>$firstcellcont</th>\r
+            <th>$fromtime&#8211;$totime</th>\r
+            <td><img src="$this->yr_imgpath/$imgno.png" width="38" height="38" alt="$s" /></td>\r
+            <td>$rain</td>\r
+            <td class="$tempclass">$temper&deg;</td>\r
+            <td class="v">$w fra $winddirtext</td>\r
+            <td>$r m/s</td>\r
+          </tr>\r
+\r
+EOT\r
+                               ;\r
+                       }\r
+               }\r
+       }\r
+\r
+       //Generer footer for værdatatabellen\r
+       public function getWeatherTableFooter($target='_top'){\r
+               $this->ht.=<<<EOT\r
+          <tr>\r
+            <td colspan="7" class="skilje"></td>\r
+          </tr>\r
+        </tbody>\r
+      </table>\r
+      <p>V&aelig;rsymbolet og nedb&oslash;rsvarselet gjelder for hele perioden, temperatur- og vindvarselet er for det f&oslash;rste tidspunktet. &lt;1 mm betyr at det vil komme mellom 0,1 og 0,9 mm nedb&oslash;r.<br />\r
+      <a href="http://www.yr.no/1.3362862" target="$target">Slik forst&aring;r du varslene fra yr.no</a>.</p>\r
+      <p>Vil du ogs&aring; ha <a href="http://www.yr.no/verdata/" target="$target">v&aelig;rvarsel fra yr.no p&aring; dine nettsider</a>?</p>\r
+EOT\r
+               ;\r
+       }\r
+\r
+\r
+       // Handle cache directory (re)creation and cachefile name selection\r
+       private function handleDataDir($clean_datadir=false,$summary=''){\r
+               global $yr_datadir;\r
+               // The md5 sum is to avoid caching to the same file on parameter changes\r
+               $this->datapath=$yr_datadir .'/'. ($summary!='' ? (md5($summary).'['.$summary.']_') : '').$this->datafile;\r
+               // Delete cache dir\r
+               if ($clean_datadir) {\r
+                       unlink($this->datapath);\r
+                       rmdir($yr_datadir);\r
+               }\r
+               // Create new cache folder with correct permissions\r
+               if(!is_dir($yr_datadir))mkdir($yr_datadir,0300);\r
+       }\r
+\r
+\r
+       //Main with caching\r
+       public function generateHTMLCached($url,$name,$xml, $url, $try_curl, $useHtmlHeader=true, $useHtmlFooter=true, $useBanner=true, $useText=true, $useLinks=true, $useTable=true, $maxage=0, $timeout=10, $urlTarget='_top'){\r
+               //Default to the name in the url\r
+               if(null==$name||''==trim($name))$name=array_pop(explode('/',$url));\r
+               $this->handleDataDir(false,htmlentities("$name.$useHtmlHeader.$useHtmlFooter.$useBanner.$useText.$useLinks.$useTable.$maxage.$timeout.$urlTarget"));\r
+               $yr_cached = $this->datapath;\r
+               // Clean name\r
+               $name=YRComms::convertEncodingUTF($name);\r
+               $name=YRComms::convertEncodingEntities($name);\r
+               // Clean URL\r
+               $url=YRComms::convertEncodingUTF($url);\r
+               // Er mellomlagring enablet, og trenger vi egentlig laste ny data, eller holder mellomlagret data?\r
+               if(($maxage>0)&&((file_exists($yr_cached))&&((time()-filemtime($yr_cached))<$maxage))){\r
+                       $data['value']=file_get_contents($yr_cached);\r
+                       // Sjekk for feil\r
+                       if(false==$data['value']){\r
+               $data['value']='<p>Det oppstod en feil mens værdata ble lest fra lokalt mellomlager. Vennligst gjør administrator oppmerksom på dette! Teknisk: Sjekk at rettighetene er i orden som beskrevet i bruksanvisningen for dette scriptet</p>';\r
+               $data['error'] = true;\r
+         }\r
+               }\r
+               // Vi kjører live, og saver samtidig en versjon til mellomlager\r
+               else{\r
+                       $data=$this->generateHTML($url,$name,$xml->getXMLTree($url, $try_curl, $timeout),$useHtmlHeader,$useHtmlFooter,$useBanner,$useText,$useLinks,$useTable,$urlTarget);\r
+                       // Lagre til mellomlager\r
+                       if($maxage>0 && !$data['error'] ){\r
+                               $f=fopen($yr_cached,"w");\r
+                               if(null!=$f){\r
+                                       fwrite($f,$data['value']);\r
+                                       fclose($f);\r
+                               }\r
+                       }\r
+               }\r
+               // Returner resultat\r
+               return $data['value'];\r
+       }\r
+\r
+       private function getErrorMessage(){\r
+               if(isset($this->yr_data['ERROR'])){\r
+                       $error=$this->yr_data['ERROR'][0]['VALUE'];\r
+                       //die(retar($error));\r
+                       $this->ht.='<p style="color:red; background:black; font-weight:900px">' .$error.'</p>';\r
+         return true;\r
+               }\r
+               return false;\r
+       }\r
+\r
+       //Main\r
+       public function generateHTML($url,$name,$data,$useHtmlHeader=true,$useHtmlFooter=true,$useBanner=true,$useText=true,$useLinks=true,$useTable=true,$urlTarget='_top'){\r
+               // Fyll inn data fra parametrene\r
+               $this->ht='';\r
+               $this->yr_url=$url;\r
+               $this->yr_name=$name;\r
+               $this->yr_data=$data;\r
+\r
+               // Generer HTML i $ht\r
+               $this->getHeader($useHtmlHeader);\r
+               $data['error'] = $this->getErrorMessage();\r
+               if($useBanner)$this->getBanner($urlTarget);\r
+               $this->getCopyright($urlTarget);\r
+               if($useText)$this->getWeatherText();\r
+               if($useLinks)$this->getLinks($urlTarget);\r
+               if($useTable){\r
+                       $this->getWeatherTableHeader();\r
+                       $this->getWeatherTableContent();\r
+                       $this->getWeatherTableFooter($urlTarget);\r
+               }\r
+               $this->getFooter($useHtmlFooter);\r
+\r
+               // Returner resultat\r
+               //return YRComms::convertEncodingEntities($this->ht);\r
+               $data['value'] = $this->ht;\r
+               return $data;\r
+       }\r
+}\r
+\r
+?>
\ No newline at end of file