From 4f77e5fc357ae3bce202fe32fe4a5333aa1ee446 Mon Sep 17 00:00:00 2001 From: Dirk Koopman Date: Fri, 19 Jul 2013 14:50:55 +0100 Subject: [PATCH] add (working) dirk.pl + other bits --- DWeather/lib/DWeather/Debug.pm | 2 +- DWeather/lib/DWeather/Serial.pm | 52 +- DWeather/lib/DWeather/Station.pm | 1 + DWeather/lib/DWeather/Station/Vantage.pm | 37 +- DWeather/lib/DWeather/Vantage.pm | 30 - dirk.pl | 546 ++++++++++++++++ yr.php | 763 +++++++++++++++++++++++ 7 files changed, 1374 insertions(+), 57 deletions(-) delete mode 100644 DWeather/lib/DWeather/Vantage.pm create mode 100755 dirk.pl create mode 100644 yr.php diff --git a/DWeather/lib/DWeather/Debug.pm b/DWeather/lib/DWeather/Debug.pm index 3535184..d67b702 100644 --- a/DWeather/lib/DWeather/Debug.pm +++ b/DWeather/lib/DWeather/Debug.pm @@ -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 $_"); } diff --git a/DWeather/lib/DWeather/Serial.pm b/DWeather/lib/DWeather/Serial.pm index 40ac160..cbd6cbe 100644 --- a/DWeather/lib/DWeather/Serial.pm +++ b/DWeather/lib/DWeather/Serial.pm @@ -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; diff --git a/DWeather/lib/DWeather/Station.pm b/DWeather/lib/DWeather/Station.pm index f9c45fe..7d05246 100644 --- a/DWeather/lib/DWeather/Station.pm +++ b/DWeather/lib/DWeather/Station.pm @@ -23,4 +23,5 @@ sub new return $self; } + 1; diff --git a/DWeather/lib/DWeather/Station/Vantage.pm b/DWeather/lib/DWeather/Station/Vantage.pm index 5f3749e..a8b91cf 100644 --- a/DWeather/lib/DWeather/Station/Vantage.pm +++ b/DWeather/lib/DWeather/Station/Vantage.pm @@ -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 index 54cbb4a..0000000 --- a/DWeather/lib/DWeather/Vantage.pm +++ /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 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 = ; # 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 index 0000000..22bb30d --- /dev/null +++ b/yr.php @@ -0,0 +1,763 @@ + + + ###### Changelog + + Versjon: 2.6 - Lennart André Rolland (lennart.andre.rolland@nrk.no) / NRK - 2008.11.11 11:48 + * Added option to remove banner ($yr_use_banner) + * Added option to allow any target for yr.no urls ($yr_link_target) + + Versjon: 2.5 - Lennart André Rolland (lennart.andre.rolland@nrk.no) / NRK - 2008.09.25 09:24 + * Cache will now update on parameter changes (cache file is prefixed with md5 digest of all relevant parameters) + This change will in the future make it easier to use the script for multiple locations in one go. + * Most relevant comments translated to english + + Versjon 2.4 - Sven-Ove Bjerkan (sven-ove@smart-media.no) / Smart-Media AS - 2008.10.22 12:14 + * Endret funksjonalitet ifbm med visning av PHP-feil (fjernet blant annet alle "@", dette styres av error_reporting()) + * 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 + * $yr_use_text, $yr_use_links og $yr_use_table ble overstyrt til "true" uavhengig av brukerens innstilling - rettet! + + Versjon: 2.3 - Lennart André Rolland (lennart.andre.rolland@nrk.no) / NRK - 2008.09.25 09:24 + * File permissions updated + * Caching is stored in HTML isntead of XML for security + * Other security and efficiency improvements + + + + ###### INSTRUCTIONS: + + 1. Only edit this script in editors with ISO-8859-1 or ISO-8859-15 character set. + 2. Edit the settings below + 3. Transfer the script to a folder in your webroot. + 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. + + */ + +/// /// /// /// /// /// /// /// /// /// /// /// /// /// /// / +/// /// /// /// /// Settings /// /// /// /// /// /// /// /// // +// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// + +// 1. Lenke: Lenke til stedet på yr.no (Uten siste skråstrek. Bruk vanlig æøå i lenka ) +// Link: Link to the url for the location on yr.no (Without the last Slash.) +$yr_url='http://www.yr.no/sted/Norge/Vest-Agder/Lyngdal/Kvås'; +$yr_url='http://www.yr.no/stad/Sverige/Jämtland/Lofsdalen'; + +// 2. Stedsnavnet: Skriv inn navnet på stedet. La stå tom for å falle tilbake til navnet i lenken +// Location: The name of the location. Leave empty to fallback to the location in the url. +$yr_name=''; + +// 3. Bruk header og footer: Velg om du vil ha med header og/eller footer +// Use Header and footers: Select to have HTML headers/footers wrapping the content (useful for debugging) +//PS: Header for HTML dokumentet er XHTML 1.0 Strict +// Skrus som regel av når du inlemmer i eksisterende dokument! +// +$yr_use_header=$yr_use_footer=true; + +// 4. Deler: Velg delene av varselet du vil ta med! +// Parts: Choose which parts of the forecast to include +$yr_use_banner=true; //yr.no Banner +$yr_use_text=false; //Tekstvarsel +$yr_use_links=true; //Lenker til varsel på yr.no +$yr_use_table=true; //Tabellen med varselet + +// 5. Mellomlagringstid: Antall sekunder før nytt varsel hentes fra yr.no. +// Cachetime: Number of seconds to keep forecast in local cache +// Den anbefalt verdien på 1200 vil oppdatere siden hver 20. minutt. +// +// PS: Vi ønsker at du setter 20 minutters mellomlagringstid fordi +// det vil gi høyere ytelse, både for yr.no og deg! MEN for å få til dette +// vil vi opprette en mappe og lagre en fil i denne mappen. Vi har gått +// gjennom scriptet veldig nøye for å forsikre oss om at det er feilfritt. +// Likevel er dette ikke helt uproblematisk i forhold til sikkerhet. +// Hvis du har problemer med dette kan du sette $yr_maxage til 0 for å skru +// av mellomlagringen helt! +$yr_maxage=1200; + +// 6. Utløpstid: Denne instillingen lar deg velge hvor lenge yr.no har på å +// levere varselet i sekunder. +// Timeout: How long before this script gives up fetching data from yr.no +// +// Hvis yr.no skulle være nede eller det er +// forstyrrelser i båndbredden ellers, vil varselet erstattes med en +// feilmelding til situasjonen er bedret igjen. PS: gjelder kun når nytt +// varsel hentes! Påvirker ikke varsel mens siden viser varsel fra +// mellomlageret. Den anbefalte verdien på 10 sekunder fungerer bra. +$yr_timeout=10; + +// 7. Mellomlagrinsmappe: Velg navn på mappen til mellomlagret data. +// Cachefolder: Where to put cache data +// +//Dette scriptet vil forsøke å opprette mappen om den ikke finnes. +$yr_datadir='yr_cache'; + + +// 8. Lenke mål: Velg hvilken target som skal brukes på lenker til yr.no +// Link target: Choose which target to use for links to yr.no +$yr_link_target='_top'; + +// 9. Vis feilmeldinger: Sett til "true" hvis du vil ha feilmeldinger. +// Show errors: Useful while debugging. +// +//greit ved feilsøking, men bør ikke være aktivert i drift. +$yr_vis_php_feilmeldinger=true; + + + + + + + + + + + + + + + +/// /// /// /// /// /// /// /// /// /// /// /// /// /// /// / +/// /// /// /// /// Code /// /// /// /// /// /// /// /// /// // +// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// +// Skru på feilmeldinger i starten +if($yr_vis_php_feilmeldinger) { + error_reporting(E_ALL); + ini_set('display_errors', true); +} +else { + error_reporting(0); + ini_set('display_errors', false); +} + +//Opprett en komunikasjon med yr +$yr_xmlparse = &new YRComms(); +//Opprett en presentasjon +$yr_xmldisplay = &new YRDisplay(); + +$yr_try_curl=true; + +//Gjenomfør oppdraget basta bom. +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)); + + +/// /// /// /// /// /// /// /// /// /// /// /// /// /// /// / +/// /// /// /// /// Hjelpekode starter her /// /// /// /// /// // +// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// + + +function retar($array, $html = false, $level = 0) { + if(is_array($array)){ + $space = $html ? " " : " "; + $newline = $html ? "
" : "\n"; + $spaces=''; + for ($i = 1; $i <= 3; $i++)$spaces .= $space; + $tabs=$spaces; + for ($i = 1; $i <= $level; $i++)$tabs .= $spaces; + $output = "Array(" . $newline . $newline; + $cnt=sizeof($array); + $j=0; + foreach($array as $key => $value) { + $j++; + if (is_array($value)) { + $level++; + $value = retar($value, $html, $level); + $level--; + } + else $value="'$value'"; + $output .= "$tabs'$key'=> $value"; + if($j<$cnt)$output .= ','; + $output .= $newline; + } + $output.=$tabs.')'.$newline; + } + else{ + $output="'$array'"; + } + return $output; +} + + +// Klasse for lesing og tilrettelegging av YR data +class YRComms{ + + //Generer gyldig yr.no array med værdata byttet ut med en enkel feilmelding + private function getYrDataErrorMessage($msg="Feil"){ + return Array( + '0'=> Array('tag'=> 'WEATHERDATA','type'=> 'open','level'=> '1'), + '1'=> Array('tag'=> 'LOCATION','type'=> 'open','level'=> '2'), + '2'=> Array('tag'=> 'NAME','type'=> 'complete','level'=> '3','value'=> $msg), + '3'=> Array('tag'=> 'LOCATION','type'=> 'complete','level'=> '3'), + '4'=> Array( 'tag'=> 'LOCATION', 'type'=> 'close', 'level'=> '2'), + '5'=> Array( 'tag'=> 'FORECAST', 'type'=> 'open', 'level'=> '2'), + '6'=> Array( 'tag'=> 'ERROR', 'type'=> 'complete', 'level'=> '3', 'value'=> $msg), + '7'=> Array( 'tag'=> 'FORECAST', 'type'=> 'close', 'level'=> '2'), + '8'=> Array( 'tag'=> 'WEATHERDATA', 'type'=> 'close', 'level'=> '1') + ); + } + + //Generer gyldig yr.no XML med værdata byttet ut med en enkel feilmelding + private function getYrXMLErrorMessage($msg="Feil"){ + $msg=$this->getXMLEntities($msg); + //die('errmsg:'.$msg); + $data=<< + + + $msg + + + + + + +EOT + ; + //die($data); + return $data; + } + + // Sørger for å laste ned XML fra yr.no og leverer data tilbake i en streng + private function loadXMLData($xml_url,$try_curl=true,$timeout=10){ + global $yr_datadir; + $xml_url.='/varsel.xml'; + // Lag en timeout på contexten + $ctx = stream_context_create(array( 'http' => array('timeout' => $timeout))); + + // Prøv å åpne direkte først + //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 + $data=file_get_contents($xml_url,0,$ctx); + + if(false!=$data){ + //Jippi vi klarte det med vanlig fopen url wrappers! + } + // Vanlig fopen_wrapper feilet, men vi har cURL tilgjengelig + else if($try_curl && function_exists('curl_init')){ + $lokal_xml_url = $yr_datadir .'/curl.temp.xml'; + $data=''; + $ch = curl_init($xml_url); + // Åpne den lokale temp filen for skrive tilgang (med cURL hooks enablet) + $fp = fopen($lokal_xml_url, "w"); + // Last fra yr.no til lokal kopi med curl + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_POSTFIELDS, ''); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_exec($ch); + curl_close($ch); + // Lukk lokal kopi + fclose($fp); + // Åpne lokal kopi igjen og les in alt innholdet + $data=file_get_contents($lokal_xml_url,0,$ctx); + //Slett temp data + unlink($lokal_xml_url); + // Sjekk for feil + 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'); + } + // Vi har verken fopen_wrappers eller cURL + else{ + $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'); + //die('
LO:'.retar($data));
+		}
+		//die('
XML for:'.$xml_url.' WAS: '.$data);
+		// 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
+		return $data;
+	}
+
+	// Last XML til en array struktur
+	private function parseXMLIntoStruct($data){
+		global $yr_datadir;
+		$parser = xml_parser_create('ISO-8859-1');
+		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.');
+		$vals = array();
+		//die('
'.retar($data).'
'); + 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.'); + 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.'); + 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.'); + //die('
'.retar($vals).'
'); + return $vals; + } + + + // Rense tekst data (av sikkerhetshensyn) + private function sanitizeString($in){ + //return $in; + if(is_array($in))return $in; + if(null==$in)return null; + return htmlentities(strip_tags($in)); + } + + // Rense tekst data (av sikkerhetshensyn) + public function reviveSafeTags($in){ + //$in=$in.'STRONG UNDERLINE BOLD ITALICS'; + return str_ireplace(array('<strong>','</strong>','<u>','</u>','<b>','</b>','<i>','</i>'),array('','','','','','','',''),$in); + } + + + + private function rearrangeChildren($vals, &$i) { + $children = array(); // Contains node data + // Sikkerhet: sørg for at all data som parses strippes for farlige ting + if (isset($vals[$i]['value']))$children['VALUE'] = $this->sanitizeString($vals[$i]['value']); + while (++$i < count($vals)){ + // Sikkerhet: sørg for at all data som parses strippes for farlige ting + if(isset($vals[$i]['value']))$val=$this->sanitizeString($vals[$i]['value']); + else unset($val); + if(isset($vals[$i]['type']))$typ=$this->sanitizeString($vals[$i]['type']); + else unset($typ); + if(isset($vals[$i]['attributes']))$atr=$this->sanitizeString($vals[$i]['attributes']); + else unset($atr); + if(isset($vals[$i]['tag']))$tag=$this->sanitizeString($vals[$i]['tag']); + else unset($tag); + // Fyll inn strukturen vær slik vi vil ha den + switch ($vals[$i]['type']){ + case 'cdata': $children['VALUE']=(isset($children['VALUE']))?$val:$children['VALUE'].$val; break; + case 'complete': + if (isset($atr)) { + $children[$tag][]['ATTRIBUTES'] = $atr; + $index = count($children[$tag])-1; + if (isset($val))$children[$tag][$index]['VALUE'] = $val; + else $children[$tag][$index]['VALUE'] = ''; + } else { + if (isset($val))$children[$tag][]['VALUE'] = $val; + else $children[$tag][]['VALUE'] = ''; + } + break; + case 'open': + if (isset($atr)) { + $children[$tag][]['ATTRIBUTES'] = $atr; + $index = count($children[$tag])-1; + $children[$tag][$index] = array_merge($children[$tag][$index],$this->rearrangeChildren($vals, $i)); + } else $children[$tag][] = $this->rearrangeChildren($vals, $i); + break; + case 'close': return $children; + } + } + } + // Ommøbler data til å passe vårt formål, og returner + private function rearrangeDataStruct($vals){ + //die('
'.$this->retar($vals).'<\pre>');
+		$tree = array();
+		$i = 0;
+		if (isset($vals[$i]['attributes'])) {
+			$tree[$vals[$i]['tag']][]['ATTRIBUTES']=$vals[$i]['attributes'];
+			$index=count($tree[$vals[$i]['tag']])-1;
+			$tree[$vals[$i]['tag']][$index]=array_merge($tree[$vals[$i]['tag']][$index], $this->rearrangeChildren($vals, $i));
+		} else $tree[$vals[$i]['tag']][] = $this->rearrangeChildren($vals, $i);
+		//die("
".retar($tree));
+		//Hent ut det vi bryr oss om
+		if(isset($tree['WEATHERDATA'][0]['FORECAST'][0]))return $tree['WEATHERDATA'][0]['FORECAST'][0];
+		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.');
+	}
+
+	// Hovedmetode. Laster XML fra en yr.no URI og parser denne
+	public function getXMLTree($xml_url, $try_curl, $timeout){
+		// Last inn XML fil og parse til et array hierarcki, ommøbler data til å passe vårt formål, og returner
+		return $this->rearrangeDataStruct($this->parseXMLIntoStruct($this->loadXMLData($xml_url,$try_curl,$timeout)));
+	}
+
+	// Statisk hjelper for å parse ut tid i yr format
+	public function parseTime($yr_time, $do24_00=false){
+		$yr_time=str_replace(":00:00", "", $yr_time);
+		if($do24_00)$yr_time=str_replace("00", "24", $yr_time);
+		return $yr_time;
+	}
+
+	// Statisk hjelper for å besørge riktig encoding ved å oversette spesielle ISO-8859-1 karakterer til HTML/XHTML entiteter
+	public function convertEncodingEntities($yrraw){
+		$conv=str_replace("æ", "æ", $yrraw);
+		$conv=str_replace("ø", "ø", $conv);
+		$conv=str_replace("å", "å", $conv);
+		$conv=str_replace("Æ", "Æ", $conv);
+		$conv=str_replace("Ø", "Ø", $conv);
+		$conv=str_replace("Å", "Å", $conv);
+		return $conv;
+	}
+
+	// Statisk hjelper for å besørge riktig encoding vedå oversette spesielle UTF karakterer til ISO-8859-1
+	public function convertEncodingUTF($yrraw){
+		$conv=str_replace("æ", "æ", $yrraw);
+		$conv=str_replace("ø", "ø", $conv);
+		$conv=str_replace("Ã¥", "å", $conv);
+		$conv=str_replace("Æ", "Æ", $conv);
+		$conv=str_replace("Ø", "Ø", $conv);
+		$conv=str_replace("Å", "Å", $conv);
+		return $conv;
+	}
+
+
+	public function getXMLEntities($string){
+		return preg_replace('/[^\x09\x0A\x0D\x20-\x7F]/e', '$this->_privateXMLEntities("$0")', $string);
+	}
+
+	private function _privateXMLEntities($num){
+		$chars = array(
+		128 => '€', 130 => '‚',
+		131 => 'ƒ', 132 => '„',
+		133 => '…', 134 => '†',
+		135 => '‡',136 => 'ˆ',
+		137 => '‰',138 => 'Š',
+		139 => '‹',140 => 'Œ',
+		142 => 'Ž', 145 => '‘',
+		146 => '’',147 => '“',
+		148 => '”',149 => '•',
+		150 => '–',151 => '—',
+		152 => '˜',153 => '™',
+		154 => 'š',155 => '›',
+		156 => 'œ',158 => 'ž',
+		159 => 'Ÿ');
+		$num = ord($num);
+		return (($num > 127 && $num < 160) ? $chars[$num] : "&#".$num.";" );
+	}
+}
+
+// Klasse for å vise data fra yr. Kompatibel med YRComms sin datastruktur
+class YRDisplay{
+
+	// Akkumulator variabl for å holde på generert HTML
+	var $ht='';
+	// Yr Url
+	var $yr_url='';
+	// Yr stedsnavn
+	var $yr_name='';
+	// Yr data
+	var $yr_data=Array();
+
+	//Filename for cached HTML. MD5 hash will be prepended to allow caching of several pages
+	var $datafile='yr.html';
+	//The complete path to the cache file
+	var $datapath='';
+
+	// Norsk grovinndeling av de 360 grader vindretning
+	var $yr_vindrettninger=array(
+    'nord','nord-nordøst','nordøst','øst-nordøst',
+    'øst','øst-sørøst','sørøst','sør-sørøst',
+    'sør','sør-sørvest', 'sørvest','vest-sørvest',
+    'vest', 'vest-nordvest','nordvest', 'nord-nordvest', 'nord');
+
+	// Hvor hentes bilder til symboler fra?
+	var $yr_imgpath='http://fil.nrk.no/yr/grafikk/sym/b38';
+
+
+	//Generer header for varselet
+	public function getHeader($use_full_html){
+		// Her kan du endre header til hva du vil. NB! Husk å skru det på, ved å endre instillingene i toppen av dokumentet
+		if($use_full_html){
+			$this->ht.=<<
+
+  
+    
+    Værvarsel fra yr.no
+    
+  
+  
+
+EOT
+			;
+		}
+		$this->ht.=<<
+
+EOT
+		;
+	}
+
+	//Generer footer for varselet
+	public function getFooter($use_full_html){
+		$this->ht.=<<
+
+EOT
+		;
+		// Her kan du endre footer til hva du vil. NB! Husk å skru det på, ved å endre instillingene i toppen av dokumentet
+		if($use_full_html){
+			$this->ht.=<<
+
+
+EOT
+			;
+		}
+	}
+
+
+   //Generer Copyright for data fra yr.no
+   public function getBanner($target='_top'){
+      $url=YRComms::convertEncodingEntities($this->yr_url);
+      $this->ht.=<<yr.no
+
+EOT
+      ;
+   }
+
+
+   //Generer Copyright for data fra yr.no
+   public function getCopyright($target='_top'){
+      $url=YRComms::convertEncodingEntities($this->yr_url);
+      /*
+       Du må ta med teksten nedenfor og ha med lenke til yr.no.
+       Om du fjerner denne teksten og lenkene, bryter du vilkårene for bruk av data fra yr.no.
+       Det er straffbart å bruke data fra yr.no i strid med vilkårene.
+       Du finner vilkårene på http://www.yr.no/verdata/1.3316805
+       */
+      $this->ht.=<<Værvarsel for $this->yr_name
+      

Værvarsel fra yr.no, levert av Meteorologisk institutt og NRK.

+ +EOT + ; + } + + + //Generer tekst for været + public function getWeatherText(){ + if((isset($this->yr_data['TEXT'])) && (isset($this->yr_data['TEXT'][0]['LOCATION']))&& (isset($this->yr_data['TEXT'][0]['LOCATION'][0]['ATTRIBUTES'])) ){ + $yr_place=$this->yr_data['TEXT'][0]['LOCATION'][0]['ATTRIBUTES']['NAME']; + if(!isset($this->yr_data['TEXT'][0]['LOCATION'][0]['TIME']))return; + foreach($this->yr_data['TEXT'][0]['LOCATION'][0]['TIME'] as $yr_var2){ + // Små bokstaver + $l=(YRComms::convertEncodingUTF($yr_var2['TITLE'][0]['VALUE'])); + // Rettet encoding + $e=YRComms::reviveSafeTags(YRComms::convertEncodingUTF($yr_var2['BODY'][0]['VALUE'])); + // Spytt ut! + $this->ht.=<<$yr_place $l:$e

+ +EOT + ; + } + } + } + + //Generer lenker til andre varsel + public function getLinks($target='_top'){ + // Rens url + $url=YRComms::convertEncodingEntities($this->yr_url); + // Spytt ut + $this->ht.=<<$this->yr_name på yr.no: + Varsel med kart + Time for time + Helg + Langtidsvarsel +

+ +EOT + ; + } + + //Generer header for værdatatabellen + public function getWeatherTableHeader(){ + $name=$this->yr_name; + $this->ht.=<< + + + Varsel for $name + Nedbør + Temp. + Vind + Vindstyrke + + + + +EOT + ; + } + + + //Generer innholdet i værdatatabellen + public function getWeatherTableContent(){ + $thisdate=''; + $dayctr=0; + if(!isset($this->yr_data['TABULAR'][0]['TIME']))return; + $a=$this->yr_data['TABULAR'][0]['TIME']; + + foreach($a as $yr_var3){ + list($fromdate, $fromtime)=explode('T', $yr_var3['ATTRIBUTES']['FROM']); + list($todate, $totime)=explode('T', $yr_var3['ATTRIBUTES']['TO']); + $fromtime=YRComms::parseTime($fromtime); + $totime=YRComms::parseTime($totime, 1); + if($fromdate!=$thisdate){ + $divider=<< + + + +EOT + ; + list($thisyear, $thismonth, $thisdate)=explode('-', $fromdate); + $displaydate=$thisdate.".".$thismonth.".".$thisyear; + $firstcellcont=$displaydate; + $thisdate=$fromdate; + ++$dayctr; + }else $divider=$firstcellcont=''; + + // Vis ny dato + if($dayctr<7){ + $this->ht.=$divider; + // Behandle symbol + $imgno=$yr_var3['SYMBOL'][0]['ATTRIBUTES']['NUMBER']; + if($imgno<10)$imgno='0'.$imgno; + switch($imgno){ + case '01': case '02': case '03': case '05': case '06': case '07': case '08': + $imgno.="d"; $do_daynight=1; break; + default: $do_daynight=0; + } + // Behandle regn + $rain=$yr_var3['PRECIPITATION'][0]['ATTRIBUTES']['VALUE']; + if($rain==0.0)$rain="0"; + else{ + $rain=intval($rain); + if($rain<1)$rain='<1'; + else $rain=round($rain); + } + $rain.=" mm"; + // Behandle vind + $winddir=round($yr_var3['WINDDIRECTION'][0]['ATTRIBUTES']['DEG']/22.5); + $winddirtext=$this->yr_vindrettninger[$winddir]; + // Behandle temperatur + $temper=round($yr_var3['TEMPERATURE'][0]['ATTRIBUTES']['VALUE']); + if($temper>=0)$tempclass='pluss'; + else $tempclass='minus'; + + // Rund av vindhastighet + $r=round($yr_var3['WINDSPEED'][0]['ATTRIBUTES']['MPS']); + // Så legger vi ut hele den ferdige linjen + $s=$yr_var3['SYMBOL'][0]['ATTRIBUTES']['NAME']; + $w=$yr_var3['WINDSPEED'][0]['ATTRIBUTES']['NAME']; + + $this->ht.=<< + $firstcellcont + $fromtime–$totime + $s + $rain + $temper° + $w fra $winddirtext + $r m/s + + +EOT + ; + } + } + } + + //Generer footer for værdatatabellen + public function getWeatherTableFooter($target='_top'){ + $this->ht.=<< + + + + +

Værsymbolet og nedbørsvarselet gjelder for hele perioden, temperatur- og vindvarselet er for det første tidspunktet. <1 mm betyr at det vil komme mellom 0,1 og 0,9 mm nedbør.
+ Slik forstår du varslene fra yr.no.

+

Vil du også ha værvarsel fra yr.no på dine nettsider?

+EOT + ; + } + + + // Handle cache directory (re)creation and cachefile name selection + private function handleDataDir($clean_datadir=false,$summary=''){ + global $yr_datadir; + // The md5 sum is to avoid caching to the same file on parameter changes + $this->datapath=$yr_datadir .'/'. ($summary!='' ? (md5($summary).'['.$summary.']_') : '').$this->datafile; + // Delete cache dir + if ($clean_datadir) { + unlink($this->datapath); + rmdir($yr_datadir); + } + // Create new cache folder with correct permissions + if(!is_dir($yr_datadir))mkdir($yr_datadir,0300); + } + + + //Main with caching + 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'){ + //Default to the name in the url + if(null==$name||''==trim($name))$name=array_pop(explode('/',$url)); + $this->handleDataDir(false,htmlentities("$name.$useHtmlHeader.$useHtmlFooter.$useBanner.$useText.$useLinks.$useTable.$maxage.$timeout.$urlTarget")); + $yr_cached = $this->datapath; + // Clean name + $name=YRComms::convertEncodingUTF($name); + $name=YRComms::convertEncodingEntities($name); + // Clean URL + $url=YRComms::convertEncodingUTF($url); + // Er mellomlagring enablet, og trenger vi egentlig laste ny data, eller holder mellomlagret data? + if(($maxage>0)&&((file_exists($yr_cached))&&((time()-filemtime($yr_cached))<$maxage))){ + $data['value']=file_get_contents($yr_cached); + // Sjekk for feil + if(false==$data['value']){ + $data['value']='

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

'; + $data['error'] = true; + } + } + // Vi kjører live, og saver samtidig en versjon til mellomlager + else{ + $data=$this->generateHTML($url,$name,$xml->getXMLTree($url, $try_curl, $timeout),$useHtmlHeader,$useHtmlFooter,$useBanner,$useText,$useLinks,$useTable,$urlTarget); + // Lagre til mellomlager + if($maxage>0 && !$data['error'] ){ + $f=fopen($yr_cached,"w"); + if(null!=$f){ + fwrite($f,$data['value']); + fclose($f); + } + } + } + // Returner resultat + return $data['value']; + } + + private function getErrorMessage(){ + if(isset($this->yr_data['ERROR'])){ + $error=$this->yr_data['ERROR'][0]['VALUE']; + //die(retar($error)); + $this->ht.='

' .$error.'

'; + return true; + } + return false; + } + + //Main + public function generateHTML($url,$name,$data,$useHtmlHeader=true,$useHtmlFooter=true,$useBanner=true,$useText=true,$useLinks=true,$useTable=true,$urlTarget='_top'){ + // Fyll inn data fra parametrene + $this->ht=''; + $this->yr_url=$url; + $this->yr_name=$name; + $this->yr_data=$data; + + // Generer HTML i $ht + $this->getHeader($useHtmlHeader); + $data['error'] = $this->getErrorMessage(); + if($useBanner)$this->getBanner($urlTarget); + $this->getCopyright($urlTarget); + if($useText)$this->getWeatherText(); + if($useLinks)$this->getLinks($urlTarget); + if($useTable){ + $this->getWeatherTableHeader(); + $this->getWeatherTableContent(); + $this->getWeatherTableFooter($urlTarget); + } + $this->getFooter($useHtmlFooter); + + // Returner resultat + //return YRComms::convertEncodingEntities($this->ht); + $data['value'] = $this->ht; + return $data; + } +} + +?> \ No newline at end of file -- 2.43.0