Revert "Undid last 3 commits, see rest of the comment for the reason:"
authorCesare Tirabassi <norsetto@ubuntu.com>
Sat, 18 Jul 2009 17:13:37 +0000 (19:13 +0200)
committerCesare Tirabassi <norsetto@ubuntu.com>
Sat, 18 Jul 2009 17:13:37 +0000 (19:13 +0200)
First of all, we may or may not agree, but I consider reverting my
commits without prior discussion as a minimum unpolite.
I also don't like sites that oblige to register, thats the very reason
why I went with noaa first (and why I use that myself).
Howver, weather.com has a couple of nice features forom an user
viewpoint:

1. Their icons can be used to add a visual quality to the weather
report.
2. They have forecast data, which is not possible to have with noaa
(using TAF its an option, but its going to be very difficult and will
be limited in time and scope).

Nobody is obliged to do anything, people who likes noaa will use noaa,
people that don't mind register or wants the additional benefit will use
weather.com.
Having libxms2 as a dragged depends is, first of all, also with other
options (rss and eve), second we can try to work around it with an
additional compilation flag if really deemed necessary.

This reverts commit d872562942812a7c71245acf7cc5f028bd4b7b4d.

ChangeLog
configure.ac.in
doc/variables.xml
src/conky.c
src/weather.c
src/weather.h

index d574678..8827a7c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+2009-07-18
+       * www.weather.com can now be used as well as a source of weather data
+
 2009-07-11
        * Added support for $desktop, $desktop_number and $desktop_name (sf.net #2040528)
 
index a56bd6d..9c9facd 100644 (file)
@@ -356,9 +356,10 @@ AC_ARG_ENABLE([weather],
 #
 AM_CONDITIONAL(BUILD_WEATHER, test x$want_weather = xyes)
 if test x$want_weather = xyes; then
+       PKG_CHECK_MODULES([libxml2], libxml-2.0)
        PKG_CHECK_MODULES([libcurl], libcurl)
-       conky_CFLAGS="$conky_CFLAGS $libcurl_CFLAGS"
-       conky_LIBS="$conky_LIBS $libcurl_LIBS"
+       conky_CFLAGS="$conky_CFLAGS $libxml2_CFLAGS $libcurl_CFLAGS"
+       conky_LIBS="$conky_LIBS $libxml2_LIBS $libcurl_LIBS"
        AC_DEFINE(WEATHER, 1, [Define if you want weather support])
 fi
 
@@ -706,6 +707,7 @@ dnl
 
 AC_DEFINE(DEFAULTNETDEV, "eth0", [Default networkdevice])
 AC_DEFINE(CONFIG_FILE, "$HOME/.conkyrc", [Configfile of the user])
+AC_DEFINE(XOAP_FILE, "$HOME/.xoaprc", [User xoap keys file])
 AC_DEFINE(MAX_SPECIALS_DEFAULT, 512, [Default maximum number of special things, e.g. fonts, offsets, aligns, etc.])
 AC_DEFINE(MAX_USER_TEXT_DEFAULT, 16384, [Default maximum size of config TEXT buffer, i.e. below TEXT line.])
 AC_DEFINE(DEFAULT_TEXT_BUFFER_SIZE, 256, [Default size used for temporary, static text buffers])
index 214a959..0c69c85 100644 (file)
             <command>
                 <option>weather</option>
             </command>
-            <option>URI icao data_type (delay_in_minutes)</option>
+            <option>URI locID data_type (delay_in_minutes)</option>
         </term>
         <listitem>
             <para>Download, parse and display METAR data.</para>
-           <para>For the 'URI', right now only 
-           http://weather.noaa.gov/pub/data/observations/metar/stations/
-            is supported. Other sources might be supported in the future.
+           <para>For the 'URI', there are two possibilities:</para>
+           <simplelist>
+               <member>
+                   http://weather.noaa.gov/pub/data/observations/metar/stations/
+               </member>
+               <member>
+                   http://xoap.weather.com/weather/local/
+               </member>
+           </simplelist>
+           <para>The first one is free to use but the second requires you to
+           register and obtain your partner ID and license key.
+           These two must be written, separated by a space, into a file
+           called .xoaprc which needs to be placed into your home directory.
            </para>
-            <para>'icao' must be a valid icao for the required location
+            <para>'locID' must be a valid location identifier for the required
+           uri. For the NOAA site this must be a valid ICAO
             (see for instance https://pilotweb.nas.faa.gov/qryhtml/icao/).
+           For the weather.com site this must be a valid location ID
+            (see for instance http://aspnetresources.com/tools/locid.aspx).
             </para>
             <para>'data_type' must be one of the following:</para>
             <simplelist>
                 <member>
                     <command>last_update</command>
-                    <option>The date (yyyy/mm/dd) and time (UTC) of
-                    the last update</option>
+                    <option>The date and time stamp of the data.
+                   The result depends on the URI used. For the NOAA site
+                   it is date (yyyy/mm/dd) and UTC time. For the
+                   weather.com one it is date ([m]m/[d]d/yy) and Local Time
+                   of the station.
+                   </option>
                 </member>
                 <member>
                     <command>temperature</command>
                     <option>The highest cloud cover status</option>
                 </member>
                 <member>
-                    <command>pressurer</command>
+                    <command>pressure</command>
                     <option>Air pressure in millibar</option>
                 </member>
                 <member>
                 <member>
                     <command>weather</command>
                     <option>Any relevant weather event (rain, snow,
-                    etc.))</option>
+                    etc.). This is not used if you are querying the
+                   weather.com site since this data is aggregated
+                   into the cloud_cover one</option>
                 </member>
             </simplelist>
             <para>'delay_in_minutes' (optional, default 30) cannot be
index 9809943..84edfb3 100644 (file)
@@ -318,6 +318,11 @@ static int cpu_avg_samples, net_avg_samples, diskio_avg_samples;
 char *overwrite_file = NULL; FILE *overwrite_fpointer = NULL;
 char *append_file = NULL; FILE *append_fpointer = NULL;
 
+/* xoap suffix for weather from weather.com */
+#ifdef WEATHER
+static char *xoap = NULL;
+#endif /* WEATHER */
+
 #ifdef X11
 
 static int show_graph_scale;
@@ -2820,35 +2825,53 @@ static struct text_object *construct_text_object(const char *s,
        END OBJ_THREAD(weather, 0)
                if (arg) {
                        int argc, interval;
-                       char *icao = (char *) malloc(5 * sizeof(char));
+                       char *locID = (char *) malloc(9 * sizeof(char));
                        char *uri = (char *) malloc(128 * sizeof(char));
                        char *data_type = (char *) malloc(32 * sizeof(char));
                        char *tmp_p;
 
-                       argc = sscanf(arg, "%119s %4s %31s %d", uri, icao, data_type, &interval);
+                       argc = sscanf(arg, "%119s %8s %31s %d", uri, locID, data_type, &interval);
 
-                       //icao MUST BE upper-case
-                       tmp_p = icao;
+                       //locID MUST BE upper-case
+                       tmp_p = locID;
                        while (*tmp_p) {
-                               *tmp_p = toupper(*tmp_p);
-                               tmp_p++;
+                         *tmp_p = toupper(*tmp_p);
+                         tmp_p++;
+                       }
+
+                       //Construct complete uri
+                       if (strstr(uri, "xoap.weather.com")) {
+                         if(xoap != NULL) {
+                           strcat(uri, locID);
+                           strcat(uri, xoap);
+                         } else {
+                           free(uri);
+                           uri = NULL;
+                         }
+                       } else if (strstr(uri, "weather.noaa.gov")) {
+                           strcat(uri, locID);
+                           strcat(uri, ".TXT");
+                       } else  if (!strstr(uri, "localhost") && !strstr(uri, "127.0.0.1")) {
+                             CRIT_ERR(obj, free_at_crash, \
+                                      "could not recognize the weather uri");
                        }
 
-
-                       strcat(uri, icao);
-                       strcat(uri, ".TXT");
                        obj->data.weather.uri = uri;
-
                        obj->data.weather.data_type = data_type;
 
-                       // The data retrieval interval is limited to half an hour
+                       //Limit the data retrieval interval to half hour min
                        if (interval < 30) {
                                interval = 30;
                        }
-                       obj->data.weather.interval = interval * 60; // convert to seconds
-                       free(icao);
+
+                       //Convert to seconds
+                       obj->data.weather.interval = interval * 60;
+                       free(locID);
+
+                       DBGP("weather: fetching %s from %s every %d seconds", \
+                            data_type, uri, obj->data.weather.interval);
                } else {
-                       CRIT_ERR(obj, free_at_crash, "weather needs arguments: <uri> <icao> <data_type> [interval in minutes]");
+                       CRIT_ERR(obj, free_at_crash, "weather needs arguments: <uri> <locID> <data_type> [interval in minutes]");
                }
 #endif
 #ifdef HAVE_LUA
@@ -4647,7 +4670,11 @@ static void generate_text_internal(char *p, int p_max_size,
 #endif
 #ifdef WEATHER
                        OBJ(weather) {
-                               process_weather_info(p, p_max_size, obj->data.weather.uri, obj->data.weather.data_type, obj->data.weather.interval);
+                               if( obj->data.weather.uri != NULL ) {
+                                       process_weather_info(p, p_max_size, obj->data.weather.uri, obj->data.weather.data_type, obj->data.weather.interval);
+                               } else {
+                                 strncpy(p, "invalid xoap keys file",  p_max_size);
+                               }
                        }
 #endif
 #ifdef HAVE_LUA
@@ -8885,6 +8912,42 @@ static void load_config_file_x11(const char *f)
 }
 #endif /* X11 */
 
+#ifdef WEATHER
+/*
+ * TODO: make the xoap keys file readable from the config file
+ *       make the keys directly readable from the config file
+ *       make the xoap keys file giveable as a command line option
+ */
+static void load_xoap_keys(void)
+{
+  FILE *fp;
+  char *par = (char *) malloc(11 * sizeof(char));
+  char *key = (char *) malloc(17 * sizeof(char));
+
+  xoap = (char *) malloc(64 * sizeof(char));
+  to_real_path(xoap, XOAP_FILE);
+  fp = fopen(xoap, "r");
+  if (fp != NULL) {
+    if( fscanf(fp, "%10s %16s", par, key) == 2 ) {
+      strcpy(xoap, "?cc=*&link=xoap&prod=xoap&par=");
+      strcat(xoap, par);
+      strcat(xoap, "&key=");
+      strcat(xoap, key);
+      strcat(xoap, "&unit=m");
+    } else {
+      free(xoap);
+      xoap = NULL;
+    }
+    fclose(fp);
+  } else {
+    free(xoap);
+    xoap = NULL;
+  }
+  free(par);
+  free(key);
+}
+#endif /* WEATHER */
+
 static void print_help(const char *prog_name) {
        printf("Usage: %s [OPTION]...\n"
                        PACKAGE_NAME" is a system monitor that renders text on desktop or to own transparent\n"
@@ -9268,6 +9331,12 @@ int main(int argc, char **argv)
 #endif /* ! CONF_OUTPUT */
                }
        }
+
+#ifdef WEATHER
+       /* Load xoap keys, if existing */
+       load_xoap_keys();
+#endif /* WEATHER */
+
 #ifdef HAVE_SYS_INOTIFY_H
        inotify_fd = inotify_init();
 #endif /* HAVE_SYS_INOTIFY_H */
index 361bdfb..6d29027 100644 (file)
  *
  */
 
+/*
+ * TODO: Add weather forecast info from weather.com
+ *
+ */
+
 #include "conky.h"
 #include "logging.h"
 #include "weather.h"
@@ -33,6 +38,7 @@
 #include <curl/curl.h>
 #include <curl/types.h>
 #include <curl/easy.h>
+#include <libxml/parser.h>
 
 /* Possible sky conditions */
 #define NUM_CC_CODES 6
@@ -120,6 +126,75 @@ int rel_humidity(int dew_point, int air) {
 #endif /* MATH */
 }
 
+//TODO: Lets get rid of the recursion
+static void parse_cc(PWEATHER *res, xmlNodePtr cc)
+{
+
+  xmlNodePtr cur = NULL;
+
+  for (cur = cc; cur; cur = cur->next) {
+    if (cur->type == XML_ELEMENT_NODE) {
+      if (!xmlStrcmp(cur->name, (const xmlChar *) "lsup")) {
+       strncpy(res->lastupd, (char *)cur->children->content, 31);
+      } else if        (!xmlStrcmp(cur->name, (const xmlChar *) "tmp")) {
+       res->temp = atoi((char *)cur->children->content);
+      } else if (!xmlStrcmp(cur->name, (const xmlChar *) "t")) {
+       if(res->xoap_t[0] == '\0') {
+         strncpy(res->xoap_t, (char *)cur->children->content, 31);
+       }
+      } else if (!xmlStrcmp(cur->name, (const xmlChar *) "r")) {
+       res->bar = atoi((char *)cur->children->content);
+      } else if (!xmlStrcmp(cur->name, (const xmlChar *) "s")) {
+       res->wind_s = atoi((char *)cur->children->content);
+      } else if (!xmlStrcmp(cur->name, (const xmlChar *) "d")) {
+       if (isdigit((char)cur->children->content[0])) {
+           res->wind_d = atoi((char *)cur->children->content);
+         }
+      } else if (!xmlStrcmp(cur->name, (const xmlChar *) "hmid")) {
+       res->hmid = atoi((char *)cur->children->content);
+      }
+    }
+    parse_cc(res, cur->children);
+  }
+  return;
+}
+
+static void parse_weather_xml(PWEATHER *res, const char *data)
+{
+  xmlDocPtr doc;
+  xmlNodePtr cur;
+
+  if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
+    ERR("weather: can't read xml data");
+    return;
+  }
+
+  cur = xmlDocGetRootElement(doc);
+
+  while(cur) {
+    if (cur->type == XML_ELEMENT_NODE) {
+      if (!xmlStrcmp(cur->name, (const xmlChar *) "weather")) {
+       cur = cur->children;
+       while (cur != NULL) {
+         if (cur->type == XML_ELEMENT_NODE) {
+           if (!xmlStrcmp(cur->name, (const xmlChar *) "cc")) {
+             parse_cc(res, cur->children);
+             xmlFreeDoc(doc);
+             return;
+           }
+         }
+         cur = cur->next;
+       }
+      }
+    }
+    cur = cur->next;
+  }
+
+  ERR("weather: incorrect xml data");
+  xmlFreeDoc(doc);
+  return ;
+}
+
 /*
  * Horrible hack to avoid using regexes
  *
@@ -337,6 +412,7 @@ static inline void parse_token(PWEATHER *res, char *token) {
 
                                //First 3 digits are wind direction
                                strncpy(s_tmp, token, 3);
+                               s_tmp[3]='\0';
                                res->wind_d=atoi(s_tmp);
 
                                //4th and 5th digit are wind speed in knots (convert to km/hr)
@@ -393,6 +469,7 @@ static inline void parse_token(PWEATHER *res, char *token) {
 
                                //First 3 digits are wind direction
                                strncpy(s_tmp, token, 3);
+                               s_tmp[3]='\0';
                                res->wind_d=atoi(s_tmp);
 
                                //4th and 5th digit are wind speed in m/s (convert to km/hr)
@@ -410,38 +487,44 @@ static inline void parse_token(PWEATHER *res, char *token) {
 
 static void parse_weather(PWEATHER *res, const char *data)
 {
-       char s_tmp[256];
-       const char delim[] = " ";
-
+        //Reset results
        memset(res, 0, sizeof(PWEATHER));
 
-       //Divide time stamp and metar data
-       if (sscanf(data, "%[^'\n']\n%[^'\n']", res->lastupd, s_tmp) == 2) {
+       //Check if it is an xml file
+       if ( strncmp(data, "<?xml ", 6) == 0 ) {
+         parse_weather_xml(res, data);
+       } else {
+         //We assume its a text file
+         char s_tmp[256];
+         const char delim[] = " ";
 
-               //Process all tokens
-               char *p_tok = NULL;
-               char *p_save = NULL;
+         //Divide time stamp and metar data
+         if (sscanf(data, "%[^'\n']\n%[^'\n']", res->lastupd, s_tmp) == 2) {
 
-               if ((strtok_r(s_tmp, delim, &p_save)) != NULL) {
+           //Process all tokens
+           char *p_tok = NULL;
+           char *p_save = NULL;
 
-                       //Jump first token, must be icao
-                       p_tok = strtok_r(NULL, delim, &p_save);
+           if ((strtok_r(s_tmp, delim, &p_save)) != NULL) {
 
-                       do {
+             //Jump first token, must be icao
+             p_tok = strtok_r(NULL, delim, &p_save);
 
-                               parse_token(res, p_tok);
-                               p_tok = strtok_r(NULL, delim, &p_save);
+             do {
 
-                       } while (p_tok != NULL);
-               }
-               return;
-       }
-       else {
-               return;
+               parse_token(res, p_tok);
+               p_tok = strtok_r(NULL, delim, &p_save);
+
+             } while (p_tok != NULL);
+           }
+           return;
+         }
+         else {
+           return;
+         }
        }
 }
 
-
 void fetch_weather_info(location *curloc)
 {
        CURL *curl = NULL;
@@ -468,7 +551,7 @@ void fetch_weather_info(location *curloc)
                        timed_thread_unlock(curloc->p_timed_thread);
                        free(chunk.memory);
                } else {
-                       ERR("No data from server");
+                       ERR("weather: no data from server");
                }
 
                curl_easy_cleanup(curl);
@@ -485,12 +568,12 @@ void init_thread(location *curloc, int interval)
                timed_thread_create(&weather_thread,
                                (void *)curloc, interval * 1000000);
        if (!curloc->p_timed_thread) {
-               ERR("Error creating weather timed thread");
+               ERR("weather: error creating timed thread");
        }
        timed_thread_register(curloc->p_timed_thread,
                        &curloc->p_timed_thread);
        if (timed_thread_run(curloc->p_timed_thread)) {
-               ERR("Error running weather timed thread");
+               ERR("weather: error running timed thread");
        }
 }
 
@@ -507,14 +590,15 @@ void process_weather_info(char *p, int p_max_size, char *uri, char *data_type, i
        location *curloc = find_location(uri);
        if (!curloc->p_timed_thread) init_thread(curloc, interval);
 
-
        timed_thread_lock(curloc->p_timed_thread);
        if (strcmp(data_type, "last_update") == EQUAL) {
                strncpy(p, curloc->data.lastupd, p_max_size);
        } else if (strcmp(data_type, "temperature") == EQUAL) {
                temp_print(p, p_max_size, curloc->data.temp, TEMP_CELSIUS);
        } else if (strcmp(data_type, "cloud_cover") == EQUAL) {
-               if (curloc->data.cc == 0) {
+               if (curloc->data.xoap_t[0] != '\0') {
+                       strncpy(p, curloc->data.xoap_t, p_max_size);
+               } else if (curloc->data.cc == 0) {
                        strncpy(p, "", p_max_size);
                } else if (curloc->data.cc < 3) {
                        strncpy(p, "clear", p_max_size);
@@ -575,6 +659,7 @@ void process_weather_info(char *p, int p_max_size, char *uri, char *data_type, i
        } else if (strcmp(data_type, "weather") == EQUAL) {
                strncpy(p, wc[curloc->data.wc], p_max_size);
        }
+
        timed_thread_unlock(curloc->p_timed_thread);
 }
 
index ff2d687..3c9b9d4 100644 (file)
@@ -30,7 +30,8 @@
 
 /* WEATHER data */
 typedef struct PWEATHER_ {
-  char lastupd[17];
+  char lastupd[32];
+  char xoap_t[32];
   int temp;
   int dew;
   int cc;
@@ -39,6 +40,16 @@ typedef struct PWEATHER_ {
   int wind_d;
   int hmid;
   int wc;
+  /*
+   * TODO:
+   * Is it worth investigating about using icons from weather.com?
+   * We could use them for data from noaa as well.
+   * They can display nicely with cimlib_add_image (with appropriate
+   * #ifdefs on imlib2 and x11), and an additional input argoment for position.
+
+  char icon[3];
+
+  */
 } PWEATHER;
 
 /* Prototypes */