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 7
41 const char *CC_CODES[NUM_CC_CODES] =
42 {"SKC", "CLR", "FEW", "SCT", "BKN", "OVC", "TCU"};
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 //Check all tokens 2 chars long
141 if (strlen(token) == 2 ) {
143 //Check if token is a weather condition
144 for (i=0; i<2; i++) {
145 if (!isalpha(token[i])) break;
148 for(i=0; i<NUM_WC_CODES; i++) {
149 if (!strncmp(token, WC_CODES[i], 2)) {
158 if (!strcmp(token, "CB")) {
165 //Check all tokens 3 chars long
166 if (strlen(token) == 3 ) {
168 //Check if token is a modified weather condition
169 if ((token[0] == '+') || (token[0] == '-')) {
170 for (i=1; i<3; i++) {
171 if (!isalpha(token[i])) break;
174 for(i=0; i<NUM_WC_CODES; i++) {
175 if (!strncmp(&token[1], WC_CODES[i], 2)) {
185 if (!strcmp(token, "NCD")) {
192 //Check all tokens 4 chars long
193 if (strlen(token) == 4 ) {
195 //Check if token is an icao
196 for (i=0; i<4; i++) {
197 if (!isalpha(token[i])) break;
203 //Check all tokens 5 chars long
204 if (strlen(token) == 5 ) {
207 if (!strcmp(token, "CAVOK")) {
212 //Check if token is the temperature
213 for (i=0; i<2; i++) {
214 if (!isdigit(token[i])) break;
216 if ((i==2) && (token[2] == '/')) {
217 for (i=3; i<5; i++) {
218 if (!isdigit(token[i])) break;
221 //First 2 digits gives the air temperature
222 res->tmpC=atoi(token);
224 //4th and 5th digits gives the dew point temperature
225 res->dew=atoi(&token[3]);
228 res->hmid = rel_humidity(res->dew, res->tmpC);
230 //Convert to Fahrenheit (faster here than in conky.c)
231 res->tmpF = (res->tmpC*9)/5 + 32;
237 //Check if token is the pressure
238 if ((token[0] == 'Q') || (token[0] == 'A')) {
239 for (i=1; i<5; i++) {
240 if (!isdigit(token[i])) break;
243 if (token[0] == 'A') {
244 //Convert inches of mercury to mbar
245 res->bar = (int)(atoi(&token[1])*0.338637526f);
249 //Last 4 digits is pressure im mbar
250 res->bar = atoi(&token[1]);
256 //Check all tokens 6 chars long
257 if (strlen(token) == 6 ) {
259 //Check if token is the cloud cover
260 for (i=0; i<3; i++) {
261 if (!isalpha(token[i])) break;
264 for (i=3; i<6; i++) {
265 if (!isdigit(token[i])) break;
268 //Check if first 3 digits gives the cloud cover condition
269 for(i=0; i<NUM_CC_CODES; i++) {
270 if (!strncmp(token, CC_CODES[i], 3)) {
279 //Check if token is positive temp and negative dew
280 for (i=0; i<2; i++) {
281 if (!isdigit(token[i])) break;
283 if ((i==2) && (token[2] == '/') && (token[3] == 'M')) {
284 for (i=4; i<6; i++) {
285 if (!isdigit(token[i])) break;
288 //1st and 2nd digits gives the temperature
289 res->tmpC = atoi(token);
291 //5th and 6th digits gives the dew point temperature
292 res->dew = -atoi(&token[4]);
295 res->hmid = rel_humidity(res->dew, res->tmpC);
297 //Convert to Fahrenheit (faster here than in conky.c)
298 res->tmpF = (res->tmpC*9)/5 + 32;
305 //Check all tokens 7 chars long
306 if (strlen(token) == 7 ) {
308 //Check if token is the observation time
309 for (i=0; i<6; i++) {
310 if (!isdigit(token[i])) break;
312 if ((i==6) && (token[6] == 'Z')) return;
314 //Check if token is the wind speed/direction in knots
315 for (i=0; i<5; i++) {
316 if (!isdigit(token[i])) break;
318 if ((i==5) && (token[5] == 'K') && (token[6] == 'T')) {
320 //First 3 digits are wind direction
321 strncpy(s_tmp, token, 3);
322 res->wind_d=atoi(s_tmp);
324 //4th and 5th digit are wind speed in knots (convert to km/hr)
325 res->wind_s = (int)(atoi(&token[3])*1.852);
330 //Check if token is negative temperature
331 if ((token[0] == 'M') && (token[4] == 'M')) {
332 for (i=1; i<3; i++) {
333 if (!isdigit(token[i])) break;
335 if ((i==3) && (token[3] == '/')) {
336 for (i=5; i<7; i++) {
337 if (!isdigit(token[i])) break;
340 //2nd and 3rd digits gives the temperature
341 res->tmpC = -atoi(&token[1]);
343 //6th and 7th digits gives the dew point temperature
344 res->dew = -atoi(&token[5]);
347 res->hmid = rel_humidity(res->dew, res->tmpC);
349 //Convert to Fahrenheit (faster here than in conky.c)
350 res->tmpF = (res->tmpC*9)/5 + 32;
357 //Check if token is wind variability
358 for (i=0; i<3; i++) {
359 if (!isdigit(token[i])) break;
361 if ((i==3) && (token[3] == 'V')) {
362 for (i=4; i<7; i++) {
363 if (!isdigit(token[i])) break;
370 //Check all tokens 8 chars long
371 if (strlen(token) == 8 ) {
373 //Check if token is the wind speed/direction in m/s
374 for (i=0; i<5; i++) {
375 if (!isdigit(token[i])) break;
377 if ((i==5)&&(token[5] == 'M')&&(token[6] == 'P')&&(token[7] == 'S')) {
379 //First 3 digits are wind direction
380 strncpy(s_tmp, token, 3);
381 res->wind_d=atoi(s_tmp);
383 //4th and 5th digit are wind speed in m/s (convert to km/hr)
384 res->wind_s = (int)(atoi(&token[3])*3.6);
391 //printf("token : %s\n", token);
394 static inline PWEATHER *parse_weather(const char *data)
397 const char delim[] = " ";
399 PWEATHER *res = malloc(sizeof(PWEATHER));
400 memset(res, 0, sizeof(PWEATHER));
402 //Divide time stamp and metar data
403 if (sscanf(data, "%[^'\n']\n%[^'\n']", res->lastupd, s_tmp) == 2) {
409 if ((p_tok = strtok_r(s_tmp, delim, &p_save)) != NULL) {
412 parse_token(res, p_tok);
413 p_tok = strtok_r(NULL, delim, &p_save);
415 } while (p_tok != NULL);
424 PWEATHER *get_weather_info(char *uri, int delay)
429 // pointers to struct
430 location *curloc = NULL;
431 PWEATHER *curdata = NULL;
432 int *last_update = 0;
437 struct WMemoryStruct chunk;
442 // first seek for the uri in list
443 for (i = 0; i < num_locations; i++) {
444 if (locations[i].uri != NULL) {
445 if (!strcmp(locations[i].uri, uri)) {
446 curloc = &locations[i];
452 if (!curloc) { // new location
453 if (num_locations == MAX_LOCATIONS) {
456 curloc = &locations[num_locations];
457 curloc->uri = strndup(uri, text_buffer_size);
461 last_update = &curloc->last_update;
462 curdata = curloc->data;
464 // wait for delay to pass
465 if (!weather_delay(last_update, delay)) {
470 if (curdata != NULL) {
475 curl = curl_easy_init();
477 curl_easy_setopt(curl, CURLOPT_URL, uri);
478 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
479 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WWriteMemoryCallback);
480 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &chunk);
481 curl_easy_setopt(curl, CURLOPT_USERAGENT, "conky-weather/1.0");
483 res = curl_easy_perform(curl);
485 curdata = parse_weather(chunk.memory);
488 ERR("No data from server");
491 curl_easy_cleanup(curl);
494 curloc->data = curdata;