# $Id: 98_geodata.pm 5089 2014-03-01 21:06:35Z betateilchen $ package main; use strict; use warnings; use Time::HiRes qw(gettimeofday); use feature qw/say switch/; require XML::Simple; require LWP::UserAgent; use Data::Dumper; sub geodata_Initialize($){ my ($hash) = @_; $hash->{DefFn} = "geodata_Define"; $hash->{UndefFn} = "geodata_Undefine"; $hash->{AttrFn} = "geodata_Attr"; $hash->{ShutdownFn} = "geodata_Shutdown"; $hash->{AttrList} = "geo_wuApiKey geo_googleApiKey ". "geo_language:de,en geo_owoGetUrl ". $readingFnAttributes; } sub geodata_Define($$){ my ($hash, $def) = @_; my $name = $hash->{NAME}; my @a = split("[ \t][ \t]*", $def); return 'usage: define geodata ' if @a < 4; Log3($name, 4, 'Setting global attributes lat/lon/alt'); $attr{global}{latitude} = $a[2]; $attr{global}{longitude} = $a[3]; # $attr{global}{altitude} = $a[4]; Log3($name, 4, 'Updating readings lat/lon/alt/state'); readingsBeginUpdate($hash); readingsBulkUpdate($hash, 'latitude', $a[2]); readingsBulkUpdate($hash, 'longitude', $a[3]); # readingsBulkUpdate($hash, 'altitude', $a[4]); readingsBulkUpdate($hash, 'state', 'defined'); readingsEndUpdate($hash,1); geodata_collectData($hash); return; } sub geodata_Undefine($$){ my($hash, $name) = @_; RemoveInternalTimer($hash); return; } sub geodata_Attr($){ my @a = @_; my $hash = $defs{$a[1]}; my (undef, $name, $attrName, $attrValue) = @a; given($attrName){ when("geo_googleApiKey"){ $attr{$name}{$attrName} = $attrValue; geodata_collectData($hash); break; } when("geo_wuApiKey"){ $attr{$name}{$attrName} = $attrValue; geodata_collectData($hash); break; } default { $attr{$name}{$attrName} = $attrValue; } } return ""; return; } sub geodata_Shutdown($) { my ($hash) = @_; my $name = $hash->{NAME}; Log3 ($name,4,"geodata $name: shutdown requested"); return undef; } sub geodata_collectData($){ my ($hash) = @_; my $name = $hash->{NAME}; my $ua = LWP::UserAgent->new; $ua->timeout(10); # test $ua->env_proxy; _geodata_owoInfo($hash, $ua); my $apiKey; $apiKey = AttrVal($name, 'geo_wuApiKey', undef); _wu_geolookup($hash, $ua, $apiKey) if(defined($apiKey)); $apiKey = AttrVal($name, 'geo_googleApiKey', undef); _geodata_googledata($hash, $ua, $apiKey) if(defined($apiKey)); InternalTimer(gettimeofday()+3600, "geodata_collectData", $hash, 0); return; } sub _wu_geolookup($$$) { my ($hash, $ua, $wuapikey) = @_; my $name = $hash->{NAME}; my $lat = ReadingsVal($name, 'latitude', ''); my $lon = ReadingsVal($name, 'longitude', ''); my $geolookupUrl = "http://api.wunderground.com/api/$wuapikey/geolookup/q/$lat,$lon.xml"; my $xml = new XML::Simple; my ($response, $htmldata, $query, $dummy); #---------------------------------------------------------- # retrieving: geolookup data # eval {$response = $ua->get("$geolookupUrl")}; ## ToDo: errorhandling $htmldata = $xml->XMLin($response->decoded_content, KeyAttr => '' ); $dummy = $htmldata->{location}{country}; $query = "/lang:$dummy".$htmldata->{location}{l}; $hash->{HELPER}{WUQUERY} = $query; readingsBeginUpdate($hash); readingsBulkUpdate($hash, 'wuTerms', $htmldata->{termsofService}); readingsBulkUpdate($hash, 'wuVersion', $htmldata->{version}); readingsBulkUpdate($hash, 'nextAirportCity', $htmldata->{location}{nearby_weather_stations}{airport}{station}[0]{city}); readingsBulkUpdate($hash, 'nextAirportICAO', $htmldata->{location}{nearby_weather_stations}{airport}{station}[0]{icao}); #---------------------------------------------------------- # retrieving astronomy data # $geolookupUrl = "http://api.wunderground.com/api/$wuapikey/astronomy/$query.xml"; eval {$response = $ua->get("$geolookupUrl")}; ## ToDo: errorhandling $htmldata = $xml->XMLin($response->decoded_content, KeyAttr => '' ); $dummy = $htmldata->{sun_phase}{sunrise}{hour}.':'.$htmldata->{sun_phase}{sunrise}{minute}; readingsBulkUpdate($hash, 'sunrise', $dummy); $dummy = $htmldata->{sun_phase}{sunset}{hour}.':'.$htmldata->{sun_phase}{sunset}{minute}; readingsBulkUpdate($hash, 'sunset', $dummy); readingsBulkUpdate($hash, 'moon_phase', $htmldata->{moon_phase}{percentIlluminated}); readingsBulkUpdate($hash, 'moon_age_days', $htmldata->{moon_phase}{ageOfMoon}); #---------------------------------------------------------- # retrieving conditions data # $geolookupUrl = "http://api.wunderground.com/api/$wuapikey/conditions/$query.xml"; eval {$response = $ua->get("$geolookupUrl")}; ## ToDo: errorhandling $htmldata = $xml->XMLin($response->decoded_content, KeyAttr => '' ); $dummy = sprintf("%.0f", $htmldata->{current_observation}{display_location}{elevation}); readingsBulkUpdate($hash, 'altitude', $dummy); readingsBulkUpdate($hash, 'city', $htmldata->{current_observation}{display_location}{city}); readingsBulkUpdate($hash, 'country', $htmldata->{current_observation}{display_location}{country}); readingsBulkUpdate($hash, 'country_iso', $htmldata->{current_observation}{display_location}{country_iso3166}); readingsBulkUpdate($hash, 'country_name', $htmldata->{current_observation}{display_location}{state_name}); readingsBulkUpdate($hash, 'observation', $htmldata->{current_observation}{observation_epoch}); readingsBulkUpdate($hash, 'tz_long', $htmldata->{current_observation}{local_tz_long}); readingsBulkUpdate($hash, 'tz_offset', $htmldata->{current_observation}{local_tz_offset}); readingsBulkUpdate($hash, 'tz_short', $htmldata->{current_observation}{local_tz_short}); $dummy = $htmldata->{current_observation}{display_location}{zip}; readingsBulkUpdate($hash, 'zip', $dummy) if $dummy ne '00000'; #Debug(Dumper($htmldata)); readingsBulkUpdate($hash, 'state', 'active'); readingsEndUpdate($hash, 1); return; } sub _geodata_googledata($$$) { my ($hash, $ua, $apiKey) = @_; my $name = $hash->{NAME}; if(!defined($apiKey)) { Log3($name, 2, 'Api key for elevation not found!'); return; } my $urlString = AttrVal($name, 'geo_elevationUrl','https://maps.googleapis.com/maps/api/elevation/xml'); my $lat = ReadingsVal($name, 'latitude', ''); my $lon = ReadingsVal($name, 'longitude', ''); my $xml = new XML::Simple; my ($response, $owodata, $dummy); my $urlString2 = "?sensor=false&locations=$lat,$lon&key=$apiKey"; $urlString .= $urlString2; eval {$response = $ua->get("$urlString")}; readingsBeginUpdate($hash); $owodata = $xml->XMLin($response->decoded_content, KeyAttr => 'result' ); $dummy = $owodata->{result}{elevation}; $dummy = sprintf("%.0f", $dummy); readingsBulkUpdate($hash, 'altitude', $dummy); $attr{global}{altitude} = $dummy; $urlString = AttrVal($name, 'geo_timezoneUrl', 'https://maps.googleapis.com/maps/api/timezone/xml'); $urlString2 = "?sensor=false&location=$lat,$lon&key=$apiKey×tamp=".time; $urlString2 .= "&language=".AttrVal($name, 'geo_language', 'en'); $urlString .= $urlString2; eval {$response = $ua->get("$urlString")}; $owodata = $xml->XMLin($response->decoded_content, KeyAttr => '' ); readingsBulkUpdate($hash, 'timeZoneId', $owodata->{time_zone_id}); readingsBulkUpdate($hash, 'dstOffset', $owodata->{dst_offset}); readingsBulkUpdate($hash, 'timeZoneName', latin1ToUtf8($owodata->{time_zone_name})); readingsBulkUpdate($hash, 'rawOffset', $owodata->{raw_offset}); readingsEndUpdate($hash, 1); return; } sub _geodata_owoInfo($$){ my ($hash,$ua) = @_; my $name = $hash->{NAME}; my $urlString = AttrVal($name, "geo_owoGetUrl", "http://api.openweathermap.org/data/2.5/weather"); my $lat = ReadingsVal($name, 'latitude', ''); my $lon = ReadingsVal($name, 'longitude', ''); my $xml = new XML::Simple; my ($response, $owodata, $dummy); $urlString .= "?lat=$lat&lon=$lon&mode=xml"; eval {$response = $ua->get("$urlString")}; # # error handling for not found stations (error 404 from server) # # if($response->decoded_content =~ m/error/i){ do errorhandling} $owodata = $xml->XMLin($response->decoded_content, KeyAttr => 'current' ); if(defined($owodata)){ (undef, $dummy) = split(/T/, $owodata->{city}{sun}{rise}); readingsBeginUpdate($hash); readingsBulkUpdate($hash, "sunriseToday", $dummy); # $owodata->{city}{sun}{rise}); (undef, $dummy) = split(/T/, $owodata->{city}{sun}{set}); readingsBulkUpdate($hash, "sunsetToday", $dummy); #$owodata->{city}{sun}{set}); readingsBulkUpdate($hash, "country", $owodata->{city}{country}); readingsBulkUpdate($hash, "state", "active"); readingsEndUpdate($hash, 1); } return; } 1; # $attr{$name}{geo_owoGetUrl} = "http://api.openweathermap.org/data/2.5/weather"; =pod =begin html

geodata

    Collect some location based data from various internet sources.
    Data will be updated once an hour.

    • openweathermap will always be used.
    • wunderground api will be used, if wunderground api key is provided by attribute.
    • google api will be used, if google api key is provided by attribute.
      Currently google's elevation api and timezone api are used, so check access to those apis for your api key.

    Define

      define <location> latitude longitude

    Set
      n/a


    Get
      n/a


    Attributes

    • geo_owoGetUrl - used to correct owo api url manually, normally not needed.
    • geo_wuApiKey - enter your wunderground api key to access wunderground data.
    • geo_googleApiKey - enter your google api key to access google api.
    • geo_language:de,en - select language to be used if supported by api.


=end html