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 conditions */
45 #define NUM_WC_CODES 17
46 const char *WC_CODES[NUM_WC_CODES] =
47 {"DZ", "RA", "GR", "GS", "SN", "SG", "FG", "HZ", "FU", "BR", "DU", "SA",
48 "FC", "PO", "SQ", "SS", "DS"};
51 * TODO: This could be made common with the one used in prss.c
55 struct WMemoryStruct {
60 typedef struct location_ {
66 int num_locations = 0;
67 location locations[MAX_LOCATIONS];
70 * TODO: This could be made common with the one used in prss.c
74 size_t WWriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
76 size_t realsize = size * nmemb;
77 struct WMemoryStruct *mem = (struct WMemoryStruct *) data;
79 mem->memory = (char *) realloc(mem->memory, mem->size + realsize + 1);
81 memcpy(&(mem->memory[mem->size]), ptr, realsize);
82 mem->size += realsize;
83 mem->memory[mem->size] = 0;
88 int weather_delay(int *last, int delay)
90 time_t now = time(NULL);
92 if ((!*last) || (now >= *last + delay)) {
100 void init_weather_info(void)
104 for (i = 0; i < MAX_LOCATIONS; i++) {
105 locations[i].uri = NULL;
106 locations[i].data = NULL;
107 locations[i].last_update = 0;
111 void free_weather_info(void)
115 for (i = 0; i < num_locations; i++) {
116 if (locations[i].uri != NULL) {
117 free(locations[i].uri);
122 int rel_humidity(int dew_point, int air) {
123 const float a = 17.27f;
124 const float b = 237.7f;
126 float g = a*dew_point/(b+dew_point);
127 return (int)(100.f*expf(g-a*air/(b+air)));
131 * Horrible hack to avoid using regexes
135 static inline void parse_token(PWEATHER *res, char *token) {
140 switch (strlen(token)) {
142 //Check all tokens 2 chars long
145 //Check if token is a weather condition
146 for (i=0; i<2; i++) {
147 if (!isalpha(token[i])) break;
150 for(i=0; i<NUM_WC_CODES; i++) {
151 if (!strncmp(token, WC_CODES[i], 2)) {
160 if (!strcmp(token, "CB")) {
167 //Check all tokens 3 chars long
170 //Check if token is a modified weather condition
171 if ((token[0] == '+') || (token[0] == '-')) {
172 for (i=1; i<3; i++) {
173 if (!isalpha(token[i])) break;
176 for(i=0; i<NUM_WC_CODES; i++) {
177 if (!strncmp(&token[1], WC_CODES[i], 2)) {
186 //Check for NCD or NCD
187 if ((!strcmp(token, "NCD")) || (!strcmp(token, "NSC"))) {
193 if (!strcmp(token, "TCU")) {
200 //Check all tokens 4 chars long
203 //Check if token is an icao
204 for (i=0; i<4; i++) {
205 if (!isalpha(token[i])) break;
211 //Check all tokens 5 chars long
215 if (!strcmp(token, "CAVOK")) {
220 //Check if token is the temperature
221 for (i=0; i<2; i++) {
222 if (!isdigit(token[i])) break;
224 if ((i==2) && (token[2] == '/')) {
225 for (i=3; i<5; i++) {
226 if (!isdigit(token[i])) break;
229 //First 2 digits gives the air temperature
230 res->tmpC=atoi(token);
232 //4th and 5th digits gives the dew point temperature
233 res->dew=atoi(&token[3]);
236 res->hmid = rel_humidity(res->dew, res->tmpC);
238 //Convert to Fahrenheit (faster here than in conky.c)
239 res->tmpF = (res->tmpC*9)/5 + 32;
245 //Check if token is the pressure
246 if ((token[0] == 'Q') || (token[0] == 'A')) {
247 for (i=1; i<5; i++) {
248 if (!isdigit(token[i])) break;
251 if (token[0] == 'A') {
252 //Convert inches of mercury to mbar
253 res->bar = (int)(atoi(&token[1])*0.338637526f);
257 //Last 4 digits is pressure im mbar
258 res->bar = atoi(&token[1]);
265 //Check all tokens 6 chars long
268 //Check if token is the cloud cover
269 for (i=0; i<3; i++) {
270 if (!isalpha(token[i])) break;
273 for (i=3; i<6; i++) {
274 if (!isdigit(token[i])) break;
277 //Check if first 3 digits gives the cloud cover condition
278 for(i=0; i<NUM_CC_CODES; i++) {
279 if (!strncmp(token, CC_CODES[i], 3)) {
288 //Check if token is positive temp and negative dew
289 for (i=0; i<2; i++) {
290 if (!isdigit(token[i])) break;
292 if ((i==2) && (token[2] == '/') && (token[3] == 'M')) {
293 for (i=4; i<6; i++) {
294 if (!isdigit(token[i])) break;
297 //1st and 2nd digits gives the temperature
298 res->tmpC = atoi(token);
300 //5th and 6th digits gives the dew point temperature
301 res->dew = -atoi(&token[4]);
304 res->hmid = rel_humidity(res->dew, res->tmpC);
306 //Convert to Fahrenheit (faster here than in conky.c)
307 res->tmpF = (res->tmpC*9)/5 + 32;
315 //Check all tokens 7 chars long
318 //Check if token is the observation time
319 for (i=0; i<6; i++) {
320 if (!isdigit(token[i])) break;
322 if ((i==6) && (token[6] == 'Z')) return;
324 //Check if token is the wind speed/direction in knots
325 for (i=0; i<5; i++) {
326 if (!isdigit(token[i])) break;
328 if ((i==5) && (token[5] == 'K') && (token[6] == 'T')) {
330 //First 3 digits are wind direction
331 strncpy(s_tmp, token, 3);
332 res->wind_d=atoi(s_tmp);
334 //4th and 5th digit are wind speed in knots (convert to km/hr)
335 res->wind_s = (int)(atoi(&token[3])*1.852);
340 //Check if token is negative temperature
341 if ((token[0] == 'M') && (token[4] == 'M')) {
342 for (i=1; i<3; i++) {
343 if (!isdigit(token[i])) break;
345 if ((i==3) && (token[3] == '/')) {
346 for (i=5; i<7; i++) {
347 if (!isdigit(token[i])) break;
350 //2nd and 3rd digits gives the temperature
351 res->tmpC = -atoi(&token[1]);
353 //6th and 7th digits gives the dew point temperature
354 res->dew = -atoi(&token[5]);
357 res->hmid = rel_humidity(res->dew, res->tmpC);
359 //Convert to Fahrenheit (faster here than in conky.c)
360 res->tmpF = (res->tmpC*9)/5 + 32;
367 //Check if token is wind variability
368 for (i=0; i<3; i++) {
369 if (!isdigit(token[i])) break;
371 if ((i==3) && (token[3] == 'V')) {
372 for (i=4; i<7; i++) {
373 if (!isdigit(token[i])) break;
380 //Check all tokens 8 chars long
383 //Check if token is the wind speed/direction in m/s
384 for (i=0; i<5; i++) {
385 if (!isdigit(token[i])) break;
387 if ((i==5)&&(token[5] == 'M')&&(token[6] == 'P')&&(token[7] == 'S')) {
389 //First 3 digits are wind direction
390 strncpy(s_tmp, token, 3);
391 res->wind_d=atoi(s_tmp);
393 //4th and 5th digit are wind speed in m/s (convert to km/hr)
394 res->wind_s = (int)(atoi(&token[3])*3.6);
401 //printf("token : %s\n", token);
406 static inline PWEATHER *parse_weather(const char *data)
409 const char delim[] = " ";
411 PWEATHER *res = malloc(sizeof(PWEATHER));
412 memset(res, 0, sizeof(PWEATHER));
414 //Divide time stamp and metar data
415 if (sscanf(data, "%[^'\n']\n%[^'\n']", res->lastupd, s_tmp) == 2) {
421 if ((p_tok = strtok_r(s_tmp, delim, &p_save)) != NULL) {
424 parse_token(res, p_tok);
425 p_tok = strtok_r(NULL, delim, &p_save);
427 } while (p_tok != NULL);
436 PWEATHER *get_weather_info(char *uri, int delay)
441 // pointers to struct
442 location *curloc = NULL;
443 PWEATHER *curdata = NULL;
444 int *last_update = 0;
449 struct WMemoryStruct chunk;
454 // first seek for the uri in list
455 for (i = 0; i < num_locations; i++) {
456 if (locations[i].uri != NULL) {
457 if (!strcmp(locations[i].uri, uri)) {
458 curloc = &locations[i];
464 if (!curloc) { // new location
465 if (num_locations == MAX_LOCATIONS) {
468 curloc = &locations[num_locations];
469 curloc->uri = strndup(uri, text_buffer_size);
473 last_update = &curloc->last_update;
474 curdata = curloc->data;
476 // wait for delay to pass
477 if (!weather_delay(last_update, delay)) {
482 if (curdata != NULL) {
487 curl = curl_easy_init();
489 curl_easy_setopt(curl, CURLOPT_URL, uri);
490 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
491 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WWriteMemoryCallback);
492 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &chunk);
493 curl_easy_setopt(curl, CURLOPT_USERAGENT, "conky-weather/1.0");
495 res = curl_easy_perform(curl);
497 curdata = parse_weather(chunk.memory);
500 ERR("No data from server");
503 curl_easy_cleanup(curl);
506 curloc->data = curdata;