1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2 * vim: ts=4 sw=4 noet ai cindent syntax=c
4 * Conky, a system monitor, based on torsmo
6 * Please see COPYING for details
8 * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
10 * All rights reserved.
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 #include "temphelper.h"
31 #include "text_object.h"
32 #include "ccurl_thread.h"
39 #include <libxml/parser.h>
40 #include <libxml/xpath.h>
44 typedef struct PWEATHER_ {
61 #define FORECAST_DAYS 5
62 typedef struct PWEATHER_FORECAST_ {
63 int hi[FORECAST_DAYS];
64 int low[FORECAST_DAYS];
65 char icon[FORECAST_DAYS][3];
66 char xoap_t[FORECAST_DAYS][32];
67 char day[FORECAST_DAYS][9];
68 char date[FORECAST_DAYS][7];
69 int wind_s[FORECAST_DAYS];
70 int wind_d[FORECAST_DAYS];
71 int hmid[FORECAST_DAYS];
72 int ppcp[FORECAST_DAYS];
75 /* Xpath expressions for XOAP xml parsing */
76 #define NUM_XPATH_EXPRESSIONS_CC 8
77 const char *xpath_expression_cc[NUM_XPATH_EXPRESSIONS_CC] = {
78 "/weather/cc/lsup", "/weather/cc/tmp", "/weather/cc/t",
79 "/weather/cc/bar/r", "/weather/cc/wind/s", "/weather/cc/wind/d",
80 "/weather/cc/hmid", "/weather/cc/icon"
83 #define NUM_XPATH_EXPRESSIONS_DF 10
84 const char *xpath_expression_df[NUM_XPATH_EXPRESSIONS_DF] = {
85 "/weather/dayf/day[*]/hi", "/weather/dayf/day[*]/low",
86 "/weather/dayf/day[*]/part[1]/icon", "/weather/dayf/day[*]/part[1]/t",
87 "/weather/dayf/day[*]/part[1]/wind/s","/weather/dayf/day[*]/part[1]/wind/d",
88 "/weather/dayf/day[*]/part[1]/ppcp", "/weather/dayf/day[*]/part[1]/hmid",
89 "/weather/dayf/day[*]/@t", "/weather/dayf/day[*]/@dt"
93 /* Possible sky conditions */
94 #define NUM_CC_CODES 6
95 const char *CC_CODES[NUM_CC_CODES] = {
96 "SKC", "CLR", "FEW", "SCT", "BKN", "OVC"
99 /* Possible weather modifiers */
100 #define NUM_WM_CODES 9
101 const char *WM_CODES[NUM_WM_CODES] = {
102 "VC", "MI", "BC", "PR", "TS", "BL",
106 /* Possible weather conditions */
107 #define NUM_WC_CODES 17
108 const char *WC_CODES[NUM_WC_CODES] = {
109 "DZ", "RA", "GR", "GS", "SN", "SG",
110 "FG", "HZ", "FU", "BR", "DU", "SA",
111 "FC", "PO", "SQ", "SS", "DS"
114 static ccurl_location_t *locations_head_cc = 0;
116 static ccurl_location_t *locations_head_df = 0;
119 struct weather_data {
126 struct weather_forecast_data {
134 void weather_free_info(void)
136 ccurl_free_locations(&locations_head_cc);
138 ccurl_free_locations(&locations_head_df);
142 int rel_humidity(int dew_point, int air) {
143 const float a = 17.27f;
144 const float b = 237.7f;
146 float diff = a*(dew_point/(b+dew_point)-air/(b+air));
148 return (int)(100.f*expf(diff));
150 return (int)(16.666667163372f*(6.f+diff*(6.f+diff*(3.f+diff))));
155 static void parse_df(PWEATHER_FORECAST *res, xmlXPathContextPtr xpathCtx)
159 xmlXPathObjectPtr xpathObj;
161 xpathObj = xmlXPathEvalExpression((const xmlChar *)"/error/err", xpathCtx);
162 if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 &&
163 xpathObj->nodesetval->nodeTab[0]->type == XML_ELEMENT_NODE) {
164 content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
165 NORM_ERR("XOAP error: %s", content);
167 xmlXPathFreeObject(xpathObj);
170 xmlXPathFreeObject(xpathObj);
172 for (i = 0; i < NUM_XPATH_EXPRESSIONS_DF; i++) {
173 xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath_expression_df[i], xpathCtx);
174 if (xpathObj != NULL) {
175 xmlNodeSetPtr nodes = xpathObj->nodesetval;
177 for (j = 0; j < nodes->nodeNr; ++j) {
178 if (nodes->nodeTab[j]->type == XML_ELEMENT_NODE) {
179 content = (char *)xmlNodeGetContent(nodes->nodeTab[k]);
182 res->hi[k] = atoi(content);
185 res->low[k] = atoi(content);
188 strncpy(res->icon[k], content, 2);
190 strncpy(res->xoap_t[k], content, 31);
193 res->wind_s[k] = atoi(content);
196 res->wind_d[k] = atoi(content);
199 res->ppcp[k] = atoi(content);
202 res->hmid[k] = atoi(content);
204 } else if (nodes->nodeTab[j]->type == XML_ATTRIBUTE_NODE) {
205 content = (char *)xmlNodeGetContent(nodes->nodeTab[k]);
208 strncpy(res->day[k], content, 8);
211 strncpy(res->date[k], content, 6);
215 if (++k == FORECAST_DAYS) break;
218 xmlXPathFreeObject(xpathObj);
223 static void parse_weather_forecast_xml(PWEATHER_FORECAST *res, const char *data)
226 xmlXPathContextPtr xpathCtx;
228 if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
229 NORM_ERR("weather_forecast: can't read xml data");
233 xpathCtx = xmlXPathNewContext(doc);
234 if(xpathCtx == NULL) {
235 NORM_ERR("weather_forecast: unable to create new XPath context");
240 parse_df(res, xpathCtx);
241 xmlXPathFreeContext(xpathCtx);
246 static void parse_cc(PWEATHER *res, xmlXPathContextPtr xpathCtx)
250 xmlXPathObjectPtr xpathObj;
252 xpathObj = xmlXPathEvalExpression((const xmlChar *)"/error/err", xpathCtx);
253 if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 &&
254 xpathObj->nodesetval->nodeTab[0]->type == XML_ELEMENT_NODE) {
255 content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
256 NORM_ERR("XOAP error: %s", content);
258 xmlXPathFreeObject(xpathObj);
261 xmlXPathFreeObject(xpathObj);
263 for (i = 0; i < NUM_XPATH_EXPRESSIONS_CC; i++) {
264 xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath_expression_cc[i], xpathCtx);
265 if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr >0 &&
266 xpathObj->nodesetval->nodeTab[0]->type ==
268 content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
271 strncpy(res->lastupd, content, 31);
274 res->temp = atoi(content);
277 strncpy(res->xoap_t, content, 31);
280 res->bar = atoi(content);
283 res->wind_s = atoi(content);
286 res->wind_d = atoi(content);
289 res->hmid = atoi(content);
292 strncpy(res->icon, content, 2);
296 xmlXPathFreeObject(xpathObj);
301 static void parse_weather_xml(PWEATHER *res, const char *data)
304 xmlXPathContextPtr xpathCtx;
306 if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
307 NORM_ERR("weather: can't read xml data");
311 xpathCtx = xmlXPathNewContext(doc);
312 if(xpathCtx == NULL) {
313 NORM_ERR("weather: unable to create new XPath context");
318 parse_cc(res, xpathCtx);
319 xmlXPathFreeContext(xpathCtx);
326 * Horrible hack to avoid using regexes
330 static inline void parse_token(PWEATHER *res, char *token) {
335 switch (strlen(token)) {
337 //Check all tokens 2 chars long
340 //Check if token is a weather condition
341 for (i=0; i<2; i++) {
342 if (!isalpha(token[i])) break;
345 for(i=0; i<NUM_WC_CODES; i++) {
346 if (!strncmp(token, WC_CODES[i], 2)) {
355 if (!strcmp(token, "CB")) {
362 //Check all tokens 3 chars long
365 //Check if token is a modified weather condition
366 if ((token[0] == '+') || (token[0] == '-')) {
367 for (i=1; i<3; i++) {
368 if (!isalpha(token[i])) break;
371 for(i=0; i<NUM_WC_CODES; i++) {
372 if (!strncmp(&token[1], WC_CODES[i], 2)) {
381 //Check for NCD or NSC
382 if ((!strcmp(token, "NCD")) || (!strcmp(token, "NSC"))) {
388 if (!strcmp(token, "TCU")) {
395 //Check all tokens 4 chars long
398 //Check if token is a modified weather condition
399 for(i=0; i<NUM_WM_CODES; i++) {
400 if (!strncmp(token, WM_CODES[i], 2)) {
401 for(i=0; i<NUM_WC_CODES; i++) {
402 if (!strncmp(&token[2], WC_CODES[i], 2)) {
413 //Check all tokens 5 chars long
417 if (!strcmp(token, "CAVOK")) {
422 //Check if token is the temperature
423 for (i=0; i<2; i++) {
424 if (!isdigit(token[i])) break;
426 if ((i==2) && (token[2] == '/')) {
427 for (i=3; i<5; i++) {
428 if (!isdigit(token[i])) break;
431 //First 2 digits gives the air temperature
432 res->temp=atoi(token);
434 //4th and 5th digits gives the dew point temperature
435 res->dew=atoi(&token[3]);
438 res->hmid = rel_humidity(res->dew, res->temp);
444 //Check if token is the pressure
445 if ((token[0] == 'Q') || (token[0] == 'A')) {
446 for (i=1; i<5; i++) {
447 if (!isdigit(token[i])) break;
450 if (token[0] == 'A') {
451 //Convert inches of mercury to mbar
452 res->bar = (int)(atoi(&token[1])*0.338637526f);
456 //Last 4 digits is pressure im mbar
457 res->bar = atoi(&token[1]);
462 //Check if token is a modified weather condition
463 if ((token[0] == '+') || (token[0] == '-')) {
464 for(i=0; i<NUM_WM_CODES; i++) {
465 if (!strncmp(&token[1], WM_CODES[i], 2)) {
466 for(i=0; i<NUM_WC_CODES; i++) {
467 if (!strncmp(&token[3], WC_CODES[i], 2)) {
478 //Check all tokens 6 chars long
481 //Check if token is the cloud cover
482 for (i=0; i<3; i++) {
483 if (!isalpha(token[i])) break;
486 for (i=3; i<6; i++) {
487 if (!isdigit(token[i])) break;
490 //Check if first 3 digits gives the cloud cover condition
491 for(i=0; i<NUM_CC_CODES; i++) {
492 if (!strncmp(token, CC_CODES[i], 3)) {
501 //Check if token is positive temp and negative dew
502 for (i=0; i<2; i++) {
503 if (!isdigit(token[i])) break;
505 if ((i==2) && (token[2] == '/') && (token[3] == 'M')) {
506 for (i=4; i<6; i++) {
507 if (!isdigit(token[i])) break;
510 //1st and 2nd digits gives the temperature
511 res->temp = atoi(token);
513 //5th and 6th digits gives the dew point temperature
514 res->dew = -atoi(&token[4]);
517 res->hmid = rel_humidity(res->dew, res->temp);
525 //Check all tokens 7 chars long
528 //Check if token is the observation time
529 for (i=0; i<6; i++) {
530 if (!isdigit(token[i])) break;
532 if ((i==6) && (token[6] == 'Z')) return;
534 //Check if token is the wind speed/direction in knots
535 for (i=0; i<5; i++) {
536 if (!isdigit(token[i])) break;
538 if ((i==5) && (token[5] == 'K') && (token[6] == 'T')) {
540 //First 3 digits are wind direction
541 strncpy(s_tmp, token, 3);
543 res->wind_d=atoi(s_tmp);
545 //4th and 5th digit are wind speed in knots (convert to km/hr)
546 res->wind_s = (int)(atoi(&token[3])*1.852);
551 //Check if token is negative temperature
552 if ((token[0] == 'M') && (token[4] == 'M')) {
553 for (i=1; i<3; i++) {
554 if (!isdigit(token[i])) break;
556 if ((i==3) && (token[3] == '/')) {
557 for (i=5; i<7; i++) {
558 if (!isdigit(token[i])) break;
561 //2nd and 3rd digits gives the temperature
562 res->temp = -atoi(&token[1]);
564 //6th and 7th digits gives the dew point temperature
565 res->dew = -atoi(&token[5]);
568 res->hmid = rel_humidity(res->dew, res->temp);
575 //Check if token is wind variability
576 for (i=0; i<3; i++) {
577 if (!isdigit(token[i])) break;
579 if ((i==3) && (token[3] == 'V')) {
580 for (i=4; i<7; i++) {
581 if (!isdigit(token[i])) break;
588 //Check all tokens 8 chars long
591 //Check if token is the wind speed/direction in m/s
592 for (i=0; i<5; i++) {
593 if (!isdigit(token[i])) break;
595 if ((i==5)&&(token[5] == 'M')&&(token[6] == 'P')&&(token[7] == 'S')) {
597 //First 3 digits are wind direction
598 strncpy(s_tmp, token, 3);
600 res->wind_d=atoi(s_tmp);
602 //4th and 5th digit are wind speed in m/s (convert to km/hr)
603 res->wind_s = (int)(atoi(&token[3])*3.6);
610 //printf("token : %s\n", token);
616 void parse_weather_forecast(void *result, const char *data)
618 PWEATHER_FORECAST *res = (PWEATHER_FORECAST*)result;
620 memset(res, 0, sizeof(PWEATHER_FORECAST));
622 //Check if it is an xml file
623 if ( strncmp(data, "<?xml ", 6) == 0 ) {
624 parse_weather_forecast_xml(res, data);
629 void parse_weather(void *result, const char *data)
631 PWEATHER *res = (PWEATHER*)result;
633 memset(res, 0, sizeof(PWEATHER));
636 //Check if it is an xml file
637 if ( strncmp(data, "<?xml ", 6) == 0 ) {
638 parse_weather_xml(res, data);
642 //We assume its a text file
644 const char delim[] = " ";
646 //Divide time stamp and metar data
647 if (sscanf(data, "%[^'\n']\n%[^'\n']", res->lastupd, s_tmp) == 2) {
653 if ((strtok_r(s_tmp, delim, &p_save)) != NULL) {
655 //Jump first token, must be icao
656 p_tok = strtok_r(NULL, delim, &p_save);
660 parse_token(res, p_tok);
661 p_tok = strtok_r(NULL, delim, &p_save);
663 } while (p_tok != NULL);
673 void wind_deg_to_dir(char *p, int p_max_size, int wind_deg) {
674 if ((wind_deg >= 349) || (wind_deg < 12)) {
675 strncpy(p, "N", p_max_size);
676 } else if (wind_deg < 33) {
677 strncpy(p, "NNE", p_max_size);
678 } else if (wind_deg < 57) {
679 strncpy(p, "NE", p_max_size);
680 } else if (wind_deg < 79) {
681 strncpy(p, "ENE", p_max_size);
682 } else if (wind_deg < 102) {
683 strncpy(p, "E", p_max_size);
684 } else if (wind_deg < 124) {
685 strncpy(p, "ESE", p_max_size);
686 } else if (wind_deg < 147) {
687 strncpy(p, "SE", p_max_size);
688 } else if (wind_deg < 169) {
689 strncpy(p, "SSE", p_max_size);
690 } else if (wind_deg < 192) {
691 strncpy(p, "S", p_max_size);
692 } else if (wind_deg < 214) {
693 strncpy(p, "SSW", p_max_size);
694 } else if (wind_deg < 237) {
695 strncpy(p, "SW", p_max_size);
696 } else if (wind_deg < 259) {
697 strncpy(p, "WSW", p_max_size);
698 } else if (wind_deg < 282) {
699 strncpy(p, "W", p_max_size);
700 } else if (wind_deg < 304) {
701 strncpy(p, "WNW", p_max_size);
702 } else if (wind_deg < 327) {
703 strncpy(p, "NW", p_max_size);
704 } else if (wind_deg < 349) {
705 strncpy(p, "NNW", p_max_size);
710 static void weather_forecast_process_info(char *p, int p_max_size, char *uri, unsigned int day, char *data_type, int interval)
712 PWEATHER_FORECAST *data;
714 ccurl_location_t *curloc = ccurl_find_location(&locations_head_df, uri);
715 if (!curloc->p_timed_thread) {
716 curloc->result = malloc(sizeof(PWEATHER_FORECAST));
717 memset(curloc->result, 0, sizeof(PWEATHER_FORECAST));
718 curloc->process_function = &parse_weather_forecast;
719 ccurl_init_thread(curloc, interval);
720 if (!curloc->p_timed_thread) {
721 NORM_ERR("error setting up weather_forecast thread");
725 timed_thread_lock(curloc->p_timed_thread);
726 data = (PWEATHER_FORECAST*)curloc->result;
727 if (strcmp(data_type, "hi") == EQUAL) {
728 temp_print(p, p_max_size, data->hi[day], TEMP_CELSIUS);
729 } else if (strcmp(data_type, "low") == EQUAL) {
730 temp_print(p, p_max_size, data->low[day], TEMP_CELSIUS);
731 } else if (strcmp(data_type, "icon") == EQUAL) {
732 strncpy(p, data->icon[day], p_max_size);
733 } else if (strcmp(data_type, "forecast") == EQUAL) {
734 strncpy(p, data->xoap_t[day], p_max_size);
735 } else if (strcmp(data_type, "wind_speed") == EQUAL) {
736 snprintf(p, p_max_size, "%d", data->wind_s[day]);
737 } else if (strcmp(data_type, "wind_dir") == EQUAL) {
738 wind_deg_to_dir(p, p_max_size, data->wind_d[day]);
739 } else if (strcmp(data_type, "wind_dir_DEG") == EQUAL) {
740 snprintf(p, p_max_size, "%d", data->wind_d[day]);
741 } else if (strcmp(data_type, "humidity") == EQUAL) {
742 snprintf(p, p_max_size, "%d", data->hmid[day]);
743 } else if (strcmp(data_type, "precipitation") == EQUAL) {
744 snprintf(p, p_max_size, "%d", data->ppcp[day]);
745 } else if (strcmp(data_type, "day") == EQUAL) {
746 strncpy(p, data->day[day], p_max_size);
747 } else if (strcmp(data_type, "date") == EQUAL) {
748 strncpy(p, data->date[day], p_max_size);
751 timed_thread_unlock(curloc->p_timed_thread);
755 static void weather_process_info(char *p, int p_max_size, char *uri, char *data_type, int interval)
757 static const char *wc[] = {
758 "", "drizzle", "rain", "hail", "soft hail",
759 "snow", "snow grains", "fog", "haze", "smoke",
760 "mist", "dust", "sand", "funnel cloud tornado",
761 "dust/sand", "squall", "sand storm", "dust storm"
765 ccurl_location_t *curloc = ccurl_find_location(&locations_head_cc, uri);
766 if (!curloc->p_timed_thread) {
767 curloc->result = malloc(sizeof(PWEATHER));
768 memset(curloc->result, 0, sizeof(PWEATHER));
769 curloc->process_function = &parse_weather;
770 ccurl_init_thread(curloc, interval);
771 if (!curloc->p_timed_thread) {
772 NORM_ERR("error setting up weather thread");
776 timed_thread_lock(curloc->p_timed_thread);
777 data = (PWEATHER*)curloc->result;
778 if (strcmp(data_type, "last_update") == EQUAL) {
779 strncpy(p, data->lastupd, p_max_size);
780 } else if (strcmp(data_type, "temperature") == EQUAL) {
781 temp_print(p, p_max_size, data->temp, TEMP_CELSIUS);
782 } else if (strcmp(data_type, "cloud_cover") == EQUAL) {
784 if (data->xoap_t[0] != '\0') {
786 strncpy(p, data->xoap_t, p_max_size);
794 strncpy(p, "", p_max_size);
795 } else if (data->cc < 3) {
796 strncpy(p, "clear", p_max_size);
797 } else if (data->cc < 5) {
798 strncpy(p, "partly cloudy", p_max_size);
799 } else if (data->cc == 5) {
800 strncpy(p, "cloudy", p_max_size);
801 } else if (data->cc == 6) {
802 strncpy(p, "overcast", p_max_size);
803 } else if (data->cc == 7) {
804 strncpy(p, "towering cumulus", p_max_size);
806 strncpy(p, "cumulonimbus", p_max_size);
809 } else if (strcmp(data_type, "icon") == EQUAL) {
810 strncpy(p, data->icon, p_max_size);
812 } else if (strcmp(data_type, "pressure") == EQUAL) {
813 snprintf(p, p_max_size, "%d", data->bar);
814 } else if (strcmp(data_type, "wind_speed") == EQUAL) {
815 snprintf(p, p_max_size, "%d", data->wind_s);
816 } else if (strcmp(data_type, "wind_dir") == EQUAL) {
817 wind_deg_to_dir(p, p_max_size, data->wind_d);
818 } else if (strcmp(data_type, "wind_dir_DEG") == EQUAL) {
819 snprintf(p, p_max_size, "%d", data->wind_d);
821 } else if (strcmp(data_type, "humidity") == EQUAL) {
822 snprintf(p, p_max_size, "%d", data->hmid);
823 } else if (strcmp(data_type, "weather") == EQUAL) {
824 strncpy(p, wc[data->wc], p_max_size);
827 timed_thread_unlock(curloc->p_timed_thread);
831 /* xoap suffix for weather from weather.com */
832 static char *xoap_cc = NULL;
833 static char *xoap_df = NULL;
836 static int process_weather_uri(char *uri, char *locID, int dayf UNUSED_ATTR)
838 /* locID MUST BE upper-case */
842 *tmp_p = toupper(*tmp_p);
846 /* Construct complete uri */
848 if (strstr(uri, "xoap.weather.com")) {
849 if ((dayf == 0) && (xoap_cc != NULL)) {
851 strcat(uri, xoap_cc);
852 } else if ((dayf == 1) && (xoap_df != NULL)) {
854 strcat(uri, xoap_df);
861 if (strstr(uri, "weather.noaa.gov")) {
864 } else if (!strstr(uri, "localhost") && !strstr(uri, "127.0.0.1")) {
873 * TODO: make the xoap keys file readable from the config file
874 * make the keys directly readable from the config file
875 * make the xoap keys file giveable as a command line option
877 void load_xoap_keys(void)
880 char *par = (char *) malloc(11 * sizeof(char));
881 char *key = (char *) malloc(17 * sizeof(char));
882 char *xoap = (char *) malloc(64 * sizeof(char));
884 to_real_path(xoap, XOAP_FILE);
885 fp = fopen(xoap, "r");
887 if (fscanf(fp, "%10s %16s", par, key) == 2) {
888 xoap_cc = (char *) malloc(128 * sizeof(char));
889 xoap_df = (char *) malloc(128 * sizeof(char));
891 strcpy(xoap_cc, "?cc=*&link=xoap&prod=xoap&par=");
892 strcat(xoap_cc, par);
893 strcat(xoap_cc, "&key=");
894 strcat(xoap_cc, key);
895 strcat(xoap_cc, "&unit=m");
897 /* TODO: Use FORECAST_DAYS instead of 5 */
898 strcpy(xoap_df, "?dayf=5&link=xoap&prod=xoap&par=");
899 strcat(xoap_df, par);
900 strcat(xoap_df, "&key=");
901 strcat(xoap_df, key);
902 strcat(xoap_df, "&unit=m");
911 void scan_weather_forecast_arg(struct text_object *obj, const char *arg, void *free_at_crash)
914 struct weather_forecast_data *wfd;
916 char *locID = (char *) malloc(9 * sizeof(char));
918 wfd = malloc(sizeof(struct weather_forecast_data));
919 memset(wfd, 0, sizeof(struct weather_forecast_data));
921 argc = sscanf(arg, "%119s %8s %1u %31s %f", wfd->uri, locID, &wfd->day, wfd->data_type, &interval);
926 CRIT_ERR(obj, free_at_crash, "wrong number of arguments for $weather_forecast");
928 if (process_weather_uri(wfd->uri, locID, 1)) {
931 CRIT_ERR(obj, free_at_crash, \
932 "could not recognize the weather forecast uri");
935 /* Limit the day between 0 (today) and FORECAST_DAYS */
936 if (wfd->day >= FORECAST_DAYS) {
937 wfd->day = FORECAST_DAYS-1;
940 /* Limit the data retrieval interval to 3 hours and an half */
941 if (interval < 210) {
945 /* Convert to seconds */
946 wfd->interval = interval * 60;
949 DBGP("weather_forecast: fetching %s for day %d from %s every %d seconds", \
950 wfd->data_type, wfd->day, wfd->uri, wfd->interval);
952 obj->data.opaque = wfd;
955 void print_weather_forecast(struct text_object *obj, char *p, int p_max_size)
957 struct weather_forecast_data *wfd = obj->data.opaque;
959 if (!wfd || !wfd->uri) {
960 NORM_ERR("error processing weather forecast data, check that you have a valid XOAP key if using XOAP.");
963 weather_forecast_process_info(p, p_max_size, wfd->uri, wfd->day, wfd->data_type, wfd->interval);
967 void scan_weather_arg(struct text_object *obj, const char *arg, void *free_at_crash)
970 struct weather_data *wd;
971 char *locID = (char *) malloc(9 * sizeof(char));
974 wd = malloc(sizeof(struct weather_data));
975 memset(wd, 0, sizeof(struct weather_data));
977 argc = sscanf(arg, "%119s %8s %31s %f", wd->uri, locID, wd->data_type, &interval);
982 CRIT_ERR(obj, free_at_crash, "wrong number of arguments for $weather");
984 if (process_weather_uri(wd->uri, locID, 0)) {
987 CRIT_ERR(obj, free_at_crash, \
988 "could not recognize the weather uri");
991 /* Limit the data retrieval interval to half hour min */
996 /* Convert to seconds */
997 wd->interval = interval * 60;
1000 DBGP("weather: fetching %s from %s every %d seconds", \
1001 wd->data_type, wd->uri, wd->interval);
1003 obj->data.opaque = wd;
1006 void print_weather(struct text_object *obj, char *p, int p_max_size)
1008 struct weather_data *wd = obj->data.opaque;
1010 if (!wd || !wd->uri) {
1011 NORM_ERR("error processing weather data, check that you have a valid XOAP key if using XOAP.");
1014 weather_process_info(p, p_max_size, wd->uri, wd->data_type, wd->interval);
1017 void free_weather(struct text_object *obj)
1019 if (obj->data.opaque) {
1020 free(obj->data.opaque);
1021 obj->data.opaque = NULL;