1 /* Conky, a system monitor, based on torsmo
3 * Any original torsmo code is licensed under the BSD license
5 * All code written since the fork of torsmo is licensed under the GPL
7 * Please see COPYING for details
9 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10 * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
12 * All rights reserved.
14 * This program is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33 #include <curl/curl.h>
34 #include <curl/types.h>
35 #include <curl/easy.h>
37 #define MAX_LOCATIONS 3
39 /* Possible sky conditions */
40 #define NUM_CC_CODES 6
41 const char *CC_CODES[NUM_CC_CODES] =
42 {"SKC", "CLR", "FEW", "SCT", "BKN", "OVC"};
44 /* Possible weather modifiers */
45 #define NUM_WM_CODES 9
46 const char *WM_CODES[NUM_WM_CODES] =
47 {"VC", "MI", "BC", "PR", "TS", "BL", "SH", "DR", "FZ"};
49 /* Possible weather conditions */
50 #define NUM_WC_CODES 17
51 const char *WC_CODES[NUM_WC_CODES] =
52 {"DZ", "RA", "GR", "GS", "SN", "SG", "FG", "HZ", "FU", "BR", "DU", "SA",
53 "FC", "PO", "SQ", "SS", "DS"};
56 * TODO: This could be made common with the one used in prss.c
60 struct WMemoryStruct {
65 typedef struct location_ {
71 int num_locations = 0;
72 location locations[MAX_LOCATIONS];
75 * TODO: This could be made common with the one used in prss.c
79 size_t WWriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
81 size_t realsize = size * nmemb;
82 struct WMemoryStruct *mem = (struct WMemoryStruct *) data;
84 mem->memory = (char *) realloc(mem->memory, mem->size + realsize + 1);
86 memcpy(&(mem->memory[mem->size]), ptr, realsize);
87 mem->size += realsize;
88 mem->memory[mem->size] = 0;
93 int weather_delay(int *last, int delay)
95 time_t now = time(NULL);
97 if ((!*last) || (now >= *last + delay)) {
105 void init_weather_info(void)
109 for (i = 0; i < MAX_LOCATIONS; i++) {
110 locations[i].uri = NULL;
111 locations[i].data = NULL;
112 locations[i].last_update = 0;
116 void free_weather_info(void)
120 for (i = 0; i < num_locations; i++) {
121 if (locations[i].uri != NULL) {
122 free(locations[i].uri);
127 int rel_humidity(int dew_point, int air) {
128 const float a = 17.27f;
129 const float b = 237.7f;
131 float g = a*dew_point/(b+dew_point);
132 return (int)(100.f*expf(g-a*air/(b+air)));
136 * Horrible hack to avoid using regexes
140 static inline void parse_token(PWEATHER *res, char *token) {
145 switch (strlen(token)) {
147 //Check all tokens 2 chars long
150 //Check if token is a weather condition
151 for (i=0; i<2; i++) {
152 if (!isalpha(token[i])) break;
155 for(i=0; i<NUM_WC_CODES; i++) {
156 if (!strncmp(token, WC_CODES[i], 2)) {
165 if (!strcmp(token, "CB")) {
172 //Check all tokens 3 chars long
175 //Check if token is a modified weather condition
176 if ((token[0] == '+') || (token[0] == '-')) {
177 for (i=1; i<3; i++) {
178 if (!isalpha(token[i])) break;
181 for(i=0; i<NUM_WC_CODES; i++) {
182 if (!strncmp(&token[1], WC_CODES[i], 2)) {
191 //Check for NCD or NSC
192 if ((!strcmp(token, "NCD")) || (!strcmp(token, "NSC"))) {
198 if (!strcmp(token, "TCU")) {
205 //Check all tokens 4 chars long
208 //Check if token is a modified weather condition
209 for(i=0; i<NUM_WM_CODES; i++) {
210 if (!strncmp(token, WM_CODES[i], 2)) {
211 for(i=0; i<NUM_WC_CODES; i++) {
212 if (!strncmp(&token[2], WC_CODES[i], 2)) {
223 //Check all tokens 5 chars long
227 if (!strcmp(token, "CAVOK")) {
232 //Check if token is the temperature
233 for (i=0; i<2; i++) {
234 if (!isdigit(token[i])) break;
236 if ((i==2) && (token[2] == '/')) {
237 for (i=3; i<5; i++) {
238 if (!isdigit(token[i])) break;
241 //First 2 digits gives the air temperature
242 res->tmpC=atoi(token);
244 //4th and 5th digits gives the dew point temperature
245 res->dew=atoi(&token[3]);
248 res->hmid = rel_humidity(res->dew, res->tmpC);
250 //Convert to Fahrenheit (faster here than in conky.c)
251 res->tmpF = (res->tmpC*9)/5 + 32;
257 //Check if token is the pressure
258 if ((token[0] == 'Q') || (token[0] == 'A')) {
259 for (i=1; i<5; i++) {
260 if (!isdigit(token[i])) break;
263 if (token[0] == 'A') {
264 //Convert inches of mercury to mbar
265 res->bar = (int)(atoi(&token[1])*0.338637526f);
269 //Last 4 digits is pressure im mbar
270 res->bar = atoi(&token[1]);
275 //Check if token is a modified weather condition
276 if ((token[0] == '+') || (token[0] == '-')) {
277 for(i=0; i<NUM_WM_CODES; i++) {
278 if (!strncmp(&token[1], WM_CODES[i], 2)) {
279 for(i=0; i<NUM_WC_CODES; i++) {
280 if (!strncmp(&token[3], WC_CODES[i], 2)) {
291 //Check all tokens 6 chars long
294 //Check if token is the cloud cover
295 for (i=0; i<3; i++) {
296 if (!isalpha(token[i])) break;
299 for (i=3; i<6; i++) {
300 if (!isdigit(token[i])) break;
303 //Check if first 3 digits gives the cloud cover condition
304 for(i=0; i<NUM_CC_CODES; i++) {
305 if (!strncmp(token, CC_CODES[i], 3)) {
314 //Check if token is positive temp and negative dew
315 for (i=0; i<2; i++) {
316 if (!isdigit(token[i])) break;
318 if ((i==2) && (token[2] == '/') && (token[3] == 'M')) {
319 for (i=4; i<6; i++) {
320 if (!isdigit(token[i])) break;
323 //1st and 2nd digits gives the temperature
324 res->tmpC = atoi(token);
326 //5th and 6th digits gives the dew point temperature
327 res->dew = -atoi(&token[4]);
330 res->hmid = rel_humidity(res->dew, res->tmpC);
332 //Convert to Fahrenheit (faster here than in conky.c)
333 res->tmpF = (res->tmpC*9)/5 + 32;
341 //Check all tokens 7 chars long
344 //Check if token is the observation time
345 for (i=0; i<6; i++) {
346 if (!isdigit(token[i])) break;
348 if ((i==6) && (token[6] == 'Z')) return;
350 //Check if token is the wind speed/direction in knots
351 for (i=0; i<5; i++) {
352 if (!isdigit(token[i])) break;
354 if ((i==5) && (token[5] == 'K') && (token[6] == 'T')) {
356 //First 3 digits are wind direction
357 strncpy(s_tmp, token, 3);
358 res->wind_d=atoi(s_tmp);
360 //4th and 5th digit are wind speed in knots (convert to km/hr)
361 res->wind_s = (int)(atoi(&token[3])*1.852);
366 //Check if token is negative temperature
367 if ((token[0] == 'M') && (token[4] == 'M')) {
368 for (i=1; i<3; i++) {
369 if (!isdigit(token[i])) break;
371 if ((i==3) && (token[3] == '/')) {
372 for (i=5; i<7; i++) {
373 if (!isdigit(token[i])) break;
376 //2nd and 3rd digits gives the temperature
377 res->tmpC = -atoi(&token[1]);
379 //6th and 7th digits gives the dew point temperature
380 res->dew = -atoi(&token[5]);
383 res->hmid = rel_humidity(res->dew, res->tmpC);
385 //Convert to Fahrenheit (faster here than in conky.c)
386 res->tmpF = (res->tmpC*9)/5 + 32;
393 //Check if token is wind variability
394 for (i=0; i<3; i++) {
395 if (!isdigit(token[i])) break;
397 if ((i==3) && (token[3] == 'V')) {
398 for (i=4; i<7; i++) {
399 if (!isdigit(token[i])) break;
406 //Check all tokens 8 chars long
409 //Check if token is the wind speed/direction in m/s
410 for (i=0; i<5; i++) {
411 if (!isdigit(token[i])) break;
413 if ((i==5)&&(token[5] == 'M')&&(token[6] == 'P')&&(token[7] == 'S')) {
415 //First 3 digits are wind direction
416 strncpy(s_tmp, token, 3);
417 res->wind_d=atoi(s_tmp);
419 //4th and 5th digit are wind speed in m/s (convert to km/hr)
420 res->wind_s = (int)(atoi(&token[3])*3.6);
427 //printf("token : %s\n", token);
432 static inline PWEATHER *parse_weather(const char *data)
435 const char delim[] = " ";
437 PWEATHER *res = malloc(sizeof(PWEATHER));
438 memset(res, 0, sizeof(PWEATHER));
440 //Divide time stamp and metar data
441 if (sscanf(data, "%[^'\n']\n%[^'\n']", res->lastupd, s_tmp) == 2) {
447 if ((strtok_r(s_tmp, delim, &p_save)) != NULL) {
449 //Jump first token, must be icao
450 p_tok = strtok_r(NULL, delim, &p_save);
454 parse_token(res, p_tok);
455 p_tok = strtok_r(NULL, delim, &p_save);
457 } while (p_tok != NULL);
466 PWEATHER *get_weather_info(char *uri, int delay)
471 // pointers to struct
472 location *curloc = NULL;
473 PWEATHER *curdata = NULL;
474 int *last_update = 0;
479 struct WMemoryStruct chunk;
484 // first seek for the uri in list
485 for (i = 0; i < num_locations; i++) {
486 if (locations[i].uri != NULL) {
487 if (!strcmp(locations[i].uri, uri)) {
488 curloc = &locations[i];
494 if (!curloc) { // new location
495 if (num_locations == MAX_LOCATIONS) {
498 curloc = &locations[num_locations];
499 curloc->uri = strndup(uri, text_buffer_size);
503 last_update = &curloc->last_update;
504 curdata = curloc->data;
506 // wait for delay to pass
507 if (!weather_delay(last_update, delay)) {
512 if (curdata != NULL) {
517 curl = curl_easy_init();
519 curl_easy_setopt(curl, CURLOPT_URL, uri);
520 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
521 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WWriteMemoryCallback);
522 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &chunk);
523 curl_easy_setopt(curl, CURLOPT_USERAGENT, "conky-weather/1.0");
525 res = curl_easy_perform(curl);
527 curdata = parse_weather(chunk.memory);
530 ERR("No data from server");
533 curl_easy_cleanup(curl);
536 curloc->data = curdata;