# -*- perl -*- # ############################################################################### # This file is autogenerated by $0. DO NOT EDIT! ############################################################################### # package Device::LaCrosse::WS23xx::MemoryMap; use strict; use warnings; use Carp; my $_memory_map = <<'END_MEMORY_MAP'; 000F:1 Wind_unit 0=m/s, 1=knots, 2=beaufort, 3=km/h, 4=mph 0266:1 LCD_contrast $BCD+1 026B:1 Forecast 0=Rainy, 1=Cloudy, 2=Sunny 026C:1 Tendency 0=Steady, 1=Rising, 2=Falling 0346:4 Indoor_Temperature [C] $BCD / 100.0 - 30 034B:4 Min_Indoor_Temperature [C] $BCD / 100.0 - 30 0350:4 Max_Indoor_Temperature [C] $BCD / 100.0 - 30 0354:10 Min_Indoor_Temperature_datetime [time_t] _time_convert($BCD) 035E:10 Max_Indoor_Temperature_datetime [time_t] _time_convert($BCD) 0369:4 Low_Alarm_Indoor_Temperature [C] $BCD / 100.0 - 30 036E:4 High_Alarm_Indoor_Temperature [C] $BCD / 100.0 - 30 0373:4 Outdoor_Temperature [C] $BCD / 100.0 - 30 0378:4 Min_Outdoor_Temperature [C] $BCD / 100.0 - 30 037D:4 Max_Outdoor_Temperature [C] $BCD / 100.0 - 30 0381:10 Min_Outdoor_Temperature_datetime [time_t] _time_convert($BCD) 038B:10 Max_Outdoor_Temperature_datetime [time_t] _time_convert($BCD) 0396:4 Low_Alarm_Outdoor_Temperature [C] $BCD / 100.0 - 30 039B:4 High_Alarm_Outdoor_Temperature [C] $BCD / 100.0 - 30 03A0:4 Windchill [C] $BCD / 100.0 - 30 03A5:4 Min_Windchill [C] $BCD / 100.0 - 30 03AA:4 Max_Windchill [C] $BCD / 100.0 - 30 03AE:10 Min_Windchill_datetime [time_t] _time_convert($BCD) 03B8:10 Max_Windchill_datetime [time_t] _time_convert($BCD) 03C3:4 Low_Alarm_Windchill [C] $BCD / 100.0 - 30 03C8:4 High_Alarm_Windchill [C] $BCD / 100.0 - 30 03CE:4 Dewpoint [C] $BCD / 100.0 - 30 03D3:4 Min_Dewpoint [C] $BCD / 100.0 - 30 03D8:4 Max_Dewpoint [C] $BCD / 100.0 - 30 03DC:10 Min_Dewpoint_datetime [time_t] _time_convert($BCD) 03E6:10 Max_Dewpoint_datetime [time_t] _time_convert($BCD) 03F1:4 Low_Alarm_Dewpoint [C] $BCD / 100.0 - 30 03F6:4 High_Alarm_Dewpoint [C] $BCD / 100.0 - 30 03FB:2 Indoor_Humidity [%] $BCD 03FD:2 Min_Indoor_Humidity [%] $BCD 03FF:2 Max_Indoor_Humidity [%] $BCD 0401:10 Min_Indoor_Humidity_datetime [time_t] _time_convert($BCD) 040B:10 Max_Indoor_Humidity_datetime [time_t] _time_convert($BCD) 0415:2 Low_Alarm_Indoor_Humidity [%] $BCD 0417:2 High_Alarm_Indoor_Humidity [%] $BCD 0419:2 Outdoor_Humidity [%] $BCD 041B:2 Min_Outdoor_Humidity [%] $BCD 041D:2 Max_Outdoor_Humidity [%] $BCD 041F:10 Min_Outdoor_Humidity_datetime [time_t] _time_convert($BCD) 0429:10 Max_Outdoor_Humidity_datetime [time_t] _time_convert($BCD) 0433:2 Low_Alarm_Outdoor_Humidity [%] $BCD 0435:2 High_Alarm_Outdoor_Humidity [%] $BCD 0497:6 Rain_24hour [mm] $BCD / 100.0 049D:6 Max_Rain_24hour [mm] $BCD / 100.0 04A3:10 Max_Rain_24hour_datetime [time_t] _time_convert($BCD) 04B4:6 Rain_1hour [mm] $BCD / 100.0 04BA:6 Max_Rain_1hour [mm] $BCD / 100.0 04C0:10 Max_Rain_1hour_datetime [time_t] _time_convert($BCD) 04D2:6 Rain_Total [mm] $BCD / 100.0 04D8:10 Rain_Total_datetime [time_t] _time_convert($BCD) 04EE:4 Min__wind [m/s] hex($BCD) / 360.0 04F4:4 Max__wind [m/s] hex($BCD) / 360.0 04F8:10 Min_Date/Time_wind_datetime [time_t] _time_convert($BCD) 0502:10 Max_Date/Time_wind_datetime [time_t] _time_convert($BCD) 0529:3 Wind_Speed [m/s] hex($BCD) / 10.0 052C:1 Wind_Direction [degrees] hex($BCD) * 22.5 0533:3 Low_wind_alarm_setting [m/s] $BCD / 10.0 0538:3 High_wind_alarm_setting [m/s] $BCD / 10.0 054D:1 Connection_Type 0=Cable, 3=lost, F=Wireless 054F:2 Countdown_time_to_next_datBinary [seconds] hex($BCD) / 2.0 05D8:5 Absolute_Pressure [hPa] $BCD / 10.0 05E2:5 Relative_Pressure [hPa] $BCD / 10.0 05EC:5 Pressure_Correction [hPa] $BCD / 10.0- 1000 05F6:5 Min_Absolute_Pressure [hPa] $BCD / 10.0 0600:5 Min_Relative_Pressure [hPa] $BCD / 10.0 060A:5 Max_Absolute_Pressure [hPa] $BCD / 10.0 0614:5 Max_Relative_Pressure [hPa] $BCD / 10.0 061E:10 Min_Pressure_datetime [time_t] _time_convert($BCD) 0628:10 Max_Pressure_datetime [time_t] _time_convert($BCD) 063C:5 Low_Alarm_Pressure [hPa] $BCD / 10.0 0650:5 High_Alarm_Pressure [hPa] $BCD / 10.0 06B2:3 History_saving_interval [minutes] hex($BCD) 06B5:3 Countdown_to_next_saving [minutes] hex($BCD) 06B8:10 Date/Time_last_record_datetime [time_t] _time_convert($BCD) 06C2:2 Pointer_to_last_written_Record hex($BCD) 06C4:2 Number_of_Records hex($BCD) END_MEMORY_MAP my $Canonical = <<'END_CANONICAL'; Max Maximum | Maximal Min Minimum | Minimal Indoor Indoors | Inside | In Outdoor Outdoors | Outside | Out Pressure Press | Air Pressure Temperature Temp Humidity Hum | Relative Humidity | Rel Humidity Windchill Wind Chill Wind_Speed Wind Speed | Windspeed Dewpoint Dew Point Rain Rainfall | Rain END_CANONICAL sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = { fields => {}, }; # Read and parse the memory map at top. for my $line (split "\n", $_memory_map) { next if $line =~ m!^\s*$!; # Skip blank lines next if $line =~ m!^\s*#!; # Skip comments $line =~ /^(\S+):(\S+)\s+(\S+)(\s+\[(.*?)\])?\s+(\S.*\S)/ or die "Internal error: Cannot grok '$line'"; $self->{fields}{lc $3} = { address => hex($1), count => $2, name => $3, units => $5 || '?', expr => $6, }; } return bless $self, $class; } sub find_field { my $self = shift; my $field = shift; # Canonicalize the requested field name, e.g. # 'Indoor Temp Max' => Max_Indoor_Temperature my $canonical_field = _canonical_name($field); if (! exists $self->{fields}->{lc $canonical_field}) { (my $re = lc $field) =~ s/[ _]+/.*/g; my @match = grep { /$re/i } keys %{$self->{fields}}; if (@match == 1) { $canonical_field = $match[0]; } } # Get the field info. # FIXME: If there's no such field, return undef instead of croaking? return $self->{fields}->{lc $canonical_field} or croak "No such value, '$field'"; } ################ # # canonical_name # sub _canonical_name { my $desc = shift; my $canonical_name = ''; $desc =~ s/_/ /g; # Min or Max? if ($desc =~ s/\bmin(imum)?\b/ /i) { $canonical_name .= 'Min_'; } elsif ($desc =~ s/\bmax(imum)?\b/ /i) { $canonical_name .= 'Max_'; } elsif ($desc =~ s/\b(High|Low)\s*Alarm\b/ /i) { $canonical_name .= ucfirst(lc($1)) . '_Alarm_'; } elsif ($desc =~ s/\bCurrent\b/ /i) { # do nothing } # Where? if ($desc =~ s/\b(in|out)(doors?)?(\b|$)/ /i) { $canonical_name .= ucfirst(lc($1) . 'door') . '_'; } # What: Temperature, Windchill, Pressure, ... if ($desc =~ s/\btemp(erature)?\b/ /i) { $canonical_name .= 'Temperature'; } elsif ($desc =~ s/\bPress(ure)?\b/ /i) { $desc =~ s/\bair\b/ /i; if ($desc =~ s/\bAbs(olute)?\b/ /i) { $canonical_name .= 'Absolute_'; } elsif ($desc =~ s/\bRel(ative)?\b/ /i) { $canonical_name .= 'Relative_'; } $canonical_name .= 'Pressure'; if ($desc =~ s/\bCorrection\b/ /i) { $canonical_name .= '_Correction'; } } elsif ($desc =~ s/\b(Humidity|Windchill|Dewpoint)\b/ /i) { $canonical_name .= ucfirst(lc($1)); $desc =~ s/\bRel(ative)?\b/ /i; } elsif ($desc =~ s/\b(Rain)\b//i) { $canonical_name .= "Rain"; if ($desc =~ s/\b(1|24)(\s*h(ou)?r?)?\b//i) { $canonical_name .= "_$1hour"; } elsif ($desc =~ s/\btotal\b//i) { $canonical_name .= "_Total"; } } else { (my $tmp = $desc) =~ s/\s+/_/g; $canonical_name .= $tmp; # FIXME: warn? } # Is this a date/time field? if ($desc =~ s!\bDate/?Time\b! !i) { $canonical_name .= '_datetime'; } if ($desc =~ /\S/) { # warn "leftover: $desc\n"; } $canonical_name =~ s/_$//; return $canonical_name; } # END canonical_name =head1 NAME Device::LaCrosse::WS23xx::MemoryMap - Weather station address meanings =head1 SYNOPSIS use Device::LaCrosse::WS23xx::MemoryMap; my $map = Device::LaCrosse::WS23xx::MemoryMap->new(); This is NOT intended as a user-visible module. It is used internally by Device::LaCrosse::WS23xx. This interface is subject to change without notice. =head1 DESCRIPTION =head1 CONSTRUCTOR =over 4 =item B() Parses the data table contained in the module itself. =back =head1 METHODS =over 4 =item B( FIELD ) Canonicalizes B and looks it up. If found, returns a hashref with the following elements: =over 8 =item name Canonical field name. =item units Units of the measurement. See Units below. =item address Starting address of this field in the WS-23xx memory map =item count Length, in nybbles, of the field. =item expr Perl expression used to convert data nybbles to a useful form. =back If FIELD is not found, returns undef. =back =head2 Known Fields The known data fields -- i.e., what you can use as an argument to Device::LaCrosse::WS23xx->get() -- are: Wind_unit LCD_contrast Forecast Tendency Indoor_Temperature C Min_Indoor_Temperature C Max_Indoor_Temperature C Min_Indoor_Temperature_datetime time_t Max_Indoor_Temperature_datetime time_t Low_Alarm_Indoor_Temperature C High_Alarm_Indoor_Temperature C Outdoor_Temperature C Min_Outdoor_Temperature C Max_Outdoor_Temperature C Min_Outdoor_Temperature_datetime time_t Max_Outdoor_Temperature_datetime time_t Low_Alarm_Outdoor_Temperature C High_Alarm_Outdoor_Temperature C Windchill C Min_Windchill C Max_Windchill C Min_Windchill_datetime time_t Max_Windchill_datetime time_t Low_Alarm_Windchill C High_Alarm_Windchill C Dewpoint C Min_Dewpoint C Max_Dewpoint C Min_Dewpoint_datetime time_t Max_Dewpoint_datetime time_t Low_Alarm_Dewpoint C High_Alarm_Dewpoint C Indoor_Humidity % Min_Indoor_Humidity % Max_Indoor_Humidity % Min_Indoor_Humidity_datetime time_t Max_Indoor_Humidity_datetime time_t Low_Alarm_Indoor_Humidity % High_Alarm_Indoor_Humidity % Outdoor_Humidity % Min_Outdoor_Humidity % Max_Outdoor_Humidity % Min_Outdoor_Humidity_datetime time_t Max_Outdoor_Humidity_datetime time_t Low_Alarm_Outdoor_Humidity % High_Alarm_Outdoor_Humidity % Rain_24hour mm Max_Rain_24hour mm Max_Rain_24hour_datetime time_t Rain_1hour mm Max_Rain_1hour mm Max_Rain_1hour_datetime time_t Rain_Total mm Rain_Total_datetime time_t Min__wind m/s Max__wind m/s Min_Date/Time_wind_datetime time_t Max_Date/Time_wind_datetime time_t Wind_Speed m/s Wind_Direction degrees Low_wind_alarm_setting m/s High_wind_alarm_setting m/s Connection_Type Countdown_time_to_next_datBinary seconds Absolute_Pressure hPa Relative_Pressure hPa Pressure_Correction hPa Min_Absolute_Pressure hPa Min_Relative_Pressure hPa Max_Absolute_Pressure hPa Max_Relative_Pressure hPa Min_Pressure_datetime time_t Max_Pressure_datetime time_t Low_Alarm_Pressure hPa High_Alarm_Pressure hPa History_saving_interval minutes Countdown_to_next_saving minutes Date/Time_last_record_datetime time_t Pointer_to_last_written_Record Number_of_Records Where applicable, units are displayed to the right of each field. =head2 Units The WS-23xx devices return data in the following units: =over 8 =item B Degrees Centigrade (temperature) =item B<%> Percent (humidity) =item B hectoPascals (pressure) =item B Meters per Second (wind speed) =item B Millimeters (rainfall) =item B Compass degrees, 0-359, (wind direction) =item B Minutes. =item B Seconds. =item B Seconds since the Epoch; you probably want to use it as an argument to B(). =back =head1 AUTHOR Ed Santiago =head1 SEE ALSO L =cut 1;