package Device::WH1091;

use Inline C => DATA => LIBS => '-lusb';
use strict;

BEGIN {
    use Exporter ();
    use vars qw($VERSION $VENDOR_ID $PRODUCT_ID $CONFIG_NO $INTERFACE_NO
    				$INTERFACES_NUM $REQTYPE $REQ $VAL $GET_SIZE $TIMEOUT
    				@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS 
    				);
    $VERSION     = '0.02';
    @ISA         = qw(Exporter);
    #Give a hoot don't pollute, do not export more than needed by default
    @EXPORT      = qw();
    @EXPORT_OK   = qw();
    %EXPORT_TAGS = ();
    $VENDOR_ID = 0x1941;
	$PRODUCT_ID = 0x8021;

	$CONFIG_NO = 1;
	$INTERFACE_NO = 0;
	
	$INTERFACES_NUM = 2;

	$REQTYPE = 0xA1;
	
	$REQ = 0x1;
	$VAL = 0x300;
	$GET_SIZE = 8;
	$TIMEOUT = 500;
  

}



sub new
{
    my ($class, %parameters) = @_;
	
	$class->{'error'} = undef;
	$class->{'data'} = undef;
	
    my $self = bless ({}, ref ($class) || $class);

    return $self;
}


sub error {
	my $self = shift;
	return $self->{'error'};
}


sub DESTROY {
	my $self = shift;

	if($self->{'dev'}) {
		$self->{'dev'}->release_interface($INTERFACE_NO);
	
	}
}


sub getdata {
	
	my $self = shift;
	
	my $buffer2;
	my $current={};
	my @dir = ['N','NNE','NE','ENE','E','SEE','SE','SSE','S','SSW','SW','WSW','W','WNW','NW','NNW'];
	    
	getweather($buffer2);
	
	$self->{tindoor} = (get_bufferval($buffer2,242,243)) / 10 ;
	$self->{toutdoor} = (get_bufferval($buffer2, 245, 246) ) / 10;
	$self->{hindoor} = get_bufferval($buffer2, 241);
	$self->{houtdoor} = get_bufferval($buffer2, 244);
	$self->{windspeed} = get_bufferval($buffer2, 249)/10;
	$self->{windgust} = get_bufferval($buffer2, 250)/10;
	$self->{winddir} = get_bufferval($buffer2, 252);
	$self->{winddirtext} = $dir[$self->{winddir}];  
	$self->{pressure} = get_bufferval($buffer2, 247, 248)/10;
	#$self->{rain24h} = (get_bufferval($buffer2,253) / get_bufferval($buffer2,254)) / 10;
	$self->{raintot} = get_bufferval($buffer2,285)*3/10;
	return 1;	
}




sub get_bufferval {
	
	my $buffer = shift;
	my $byte1 = shift;
	my $byte2 = shift;
	
	
	my $ret = ord(substr($buffer,$byte1,1));
	if( defined $byte2 ) {
		$ret = $ret + ord(substr($buffer,$byte2,1)) *256;
	}
	return $ret;
}






=head1 NAME

Device::WH1091 - Access data from the WH1081/1091 weather station.

=head1 SYNOPSIS

  use Device::WH1091;
  my $weather=WH1091->new();
  
  $weather->getdata();
  
  
  my $tindoor = $weather->{tindoor}; 		# Indoor Temp
  my $toutdoor = $weather->{toutdoor}; 		# Outdoor Temp
  my $hindoor = $weather->{hindoor};		# Indoor Humidity
  $houtdoor = $weather->{houtdoor};			# OutDoor Humidity
  $windspeed = $weather->{windspeed};		# WindSpeed (m/s)
  $windgust = $weather->{windgust};			# Wind Gust (m/s)
  $winddir = $weather->{winddir};			# Wind Direction Degrees
  $winddirtext = $weather->{winddirtext};	# Wind Direction Text
  $pressure = $weather->{pressure};			# Air Pressure
  $raintot = $weather->{raintot};			# Total Rain

=head1 DESCRIPTION

Provides an interface to the WH1081/WH1091 weather stations (and others based on the same hardware).

Requires libusb to be installed so essentially limited at this stage to Linux/Unix platforms that have libusb.
Currently, the usb code is inlined C, however this will bechanging to Perl USB at some stage so that this platform dependency is removed.


=head1 USAGE

Instatiate an instance by call WH1091->new();

Whenever you want data, call getdata() and the object variables mention in the SYNOPSIS will be populated.
When you want more data, call getdata() again.

Be aware that the usb weather station only gets wireless updates from the weather head every 30 seconds, so polling more often than that would be pointless. 

=head1 BUGS

I am still not 100% confident with the rain data. Your mileage may vary. 

I have had this running a continuous loop getting data on an NSLU2 with openwrt for over three months now.

=head1 AUTHOR

    David Peters
    CPAN ID: DAVIDP
    davidp@electronf.com
    http://www.electronf.com

=head1 COPYRIGHT

This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the
LICENSE file included with this module.


=head1 SEE ALSO

perl(1).

=cut

#################### main pod documentation end ###################


1;




__DATA__    

__C__

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include <ctype.h>
#include <usb.h>

struct usb_dev_handle *devh;
int	ret,mempos=0,showall=0,shownone=0,resetws=0,pdebug=0,postprocess=0;
int	o1,o2,o3,o4,o5,o6,o7,o8,o9,o10,o11,o12,o13,o14,o15;
char	buf[1000],*endptr;
char	buf2[400];
int other=0, i=0;

void _close_readw() {
    ret = usb_release_interface(devh, 0);
    if (ret!=0) printf("could not release interface: %d\n", ret);
    ret = usb_close(devh);
    if (ret!=0) printf("Error closing interface: %d\n", ret);
}

struct usb_device *find_device(int vendor, int product) {
    struct usb_bus *bus;
    
    for (bus = usb_get_busses(); bus; bus = bus->next) {
	struct usb_device *dev;
	
	for (dev = bus->devices; dev; dev = dev->next) {
	    if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product)
			return dev;
		}
    }
    return NULL;
}


void _open_readw() {
    struct usb_device *dev;
    int vendor, product;

    usb_init();

    usb_find_busses();
    usb_find_devices();

    vendor = 0x1941;
    product = 0x8021; 

    dev = find_device(vendor, product);
    assert(dev);
    devh = usb_open(dev);
    assert(devh);
    signal(SIGTERM, _close_readw);
    ret = usb_get_driver_np(devh, 0, buf, sizeof(buf));
    if (ret == 0) {
		ret = usb_detach_kernel_driver_np(devh, 0);
    }
    ret = usb_claim_interface(devh, 0);
    if (ret != 0) {
		printf("Could not open usb device, errorcode - %d\n", ret);
		exit(1);
    }
    ret = usb_set_altinterface(devh, 0);
    assert(ret >= 0);
}


void _init_wread() {
	char tbuf[1000];
	ret = usb_get_descriptor(devh, 1, 0, tbuf, 0x12);

	ret = usb_get_descriptor(devh, 2, 0, tbuf, 9);

	ret = usb_get_descriptor(devh, 2, 0, tbuf, 0x22);

	ret = usb_release_interface(devh, 0);
	if (ret != 0) printf("failed to release interface before set_configuration: %d\n", ret);
	ret = usb_set_configuration(devh, 1);
	ret = usb_claim_interface(devh, 0);
	if (ret != 0) printf("claim after set_configuration failed with error %d\n", ret);
	ret = usb_set_altinterface(devh, 0);

	ret = usb_control_msg(devh, USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0xa, 0, 0, tbuf, 0, 1000);

	ret = usb_get_descriptor(devh, 0x22, 0, tbuf, 0x74);
}

void _send_usb_msg( char msg1[1],char msg2[1],char msg3[1],char msg4[1],char msg5[1],char msg6[1],char msg7[1],char msg8[1] ) {
	char tbuf[1000];
	tbuf[0] = msg1[0];
	tbuf[1] = msg2[0];
	tbuf[2] = msg3[0];
	tbuf[3] = msg4[0];
	tbuf[4] = msg5[0];
	tbuf[5] = msg6[0];
	tbuf[6] = msg7[0];
	tbuf[7] = msg8[0];

	ret = usb_control_msg(devh, USB_TYPE_CLASS + USB_RECIP_INTERFACE, 9, 0x200, 0, tbuf, 8, 1000);

}

void _read_usb_msg(char *buffer) {
   char tbuf[1000];
   usb_interrupt_read(devh, 0x81, tbuf, 0x20, 1000);
   memcpy(buffer, tbuf, 0x20);

}



int getweather(SV* buffer2) {
    
    int	buftemp;
    char ec='n';
    
	_open_readw();
  	_init_wread();

  	_send_usb_msg("\xa1","\x00","\x00","\x20","\xa1","\x00","\x00","\x20");
  	_read_usb_msg(buf2);
    _send_usb_msg("\xa1","\x00","\x20","\x20","\xa1","\x00","\x20","\x20");
    _read_usb_msg(buf2+32);
    _send_usb_msg("\xa1","\x00","\x40","\x20","\xa1","\x00","\x40","\x20");
    _read_usb_msg(buf2+64);
    _send_usb_msg("\xa1","\x00","\x60","\x20","\xa1","\x00","\x60","\x20");
    _read_usb_msg(buf2+96);
  	_send_usb_msg("\xa1","\x00","\x80","\x20","\xa1","\x00","\x80","\x20");
  	_read_usb_msg(buf2+128);
  	_send_usb_msg("\xa1","\x00","\xa0","\x20","\xa1","\x00","\xa0","\x20");
  	_read_usb_msg(buf2+160);
  	_send_usb_msg("\xa1","\x00","\xc0","\x20","\xa1","\x00","\xc0","\x20");
  	_read_usb_msg(buf2+192);
  	_send_usb_msg("\xa1","\x00","\xe0","\x20","\xa1","\x00","\xe0","\x20");
 	_read_usb_msg(buf2+224);
   
	int offset;
	offset = (unsigned char) buf2[30] + ( 256 * buf2[31] );
   
   	if (mempos!=0) offset = mempos;
   	
   	buftemp = 0;
	if (offset!=0) buftemp = offset - 0x10;
	
	buf[1] = ( buftemp >>8 & 0xFF ) ;
	buf[2] = buftemp & 0xFF;
	buf[3] = ( buftemp >>8 & 0xFF ) ;
	buf[4] = buftemp & 0xFF;
	
	_send_usb_msg("\xa1",buf+1,buf+2,"\x20","\xa1",buf+3,buf+4,"\x20");
	_read_usb_msg(buf2+224);
	
	ret = usb_control_msg(devh, USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x0000009, 0x0000200, 0x0000000, buf, 0x0000008, 1000);
	ret = usb_interrupt_read(devh, 0x00000081, buf, 0x0000020, 1000);
	memcpy(buf2+256, buf, 0x0000020);
	
	_close_readw();
		
	sv_setpvn(buffer2,buf2,1000);
	return 0;
}


__END__



