#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "input_gadgets.h" #include "humidity.h" #define fh_temperature_regex "\n\\s*Wetterdaten vom ([0-9]{2}\\.[0-9]{2}\\.[0-9]{4} um [0-9]{2}:[0-9]{2} MEZ)(.|\n)*>Temperatur\\s*\n\\s*]*>([^<]*)(.|\n)*>Luftfeuchtigkeit\\s*\n\\s*]*>([^<]*)" #define wetter_warnung_filename_regex "^2\\.49\\.0\\.1\\.276\\.0\\.DWD\\.PVW\\.([0-9]+)\\." #define warnings_future_timespan (60*60*10) #define kpi_regex "RH: (-?[0-9.]+) %\nT: (-?[0-9.]+) " static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; MemoryStruct *mem = (MemoryStruct *)userp; char *ptr = realloc(mem->memory, mem->size + realsize + 1); if(ptr == NULL) { /* out of memory! */ printf("not enough memory (realloc returned NULL)\n"); return 0; } mem->memory = ptr; memcpy(&(mem->memory[mem->size]), contents, realsize); mem->size += realsize; mem->memory[mem->size] = 0; return realsize; } int point_is_inside_polygon(double lat, double lon, char *text) { char *input = malloc(strlen(text)+1); if (input == NULL) { fprintf(stderr, "malloc failed to allocate %d bytes\n", strlen(text)+1); return -1; } strcpy(input, text); double *lats = malloc(1); if (lats == NULL) { fprintf(stderr, "malloc failed to allocate 1 byte\n"); free(input); return -1; } double *lons = malloc(1); if (lons == NULL) { fprintf(stderr, "malloc failed to allocate 1 byte\n"); free(lats); free(input); return -1; } int len = 0; int alloc_len = 0; char *q = NULL; char *p = strtok_r(input, ",", &q); while (p != NULL) { if (alloc_len<=len) { double *n; alloc_len = len + 16; n = realloc(lons, alloc_len*sizeof(lons[0])); if (n == NULL) { fprintf(stderr, "malloc failed to allocate %d byte\n", alloc_len*sizeof(lons[0])); free(input); free(lats); free(lons); return -1; } lons = n; n = realloc(lats, alloc_len*sizeof(lats[0])); if (n == NULL) { fprintf(stderr, "malloc failed to allocate %d byte\n", alloc_len*sizeof(lats[0])); free(input); free(lats); free(lons); return -1; } lats = n; } lats[len] = atof(p); p=strtok_r(NULL, " ", &q); if (p == NULL) { fprintf(stderr, "point_is_inside_polygon misses a space\n"); free(input); free(lats); free(lons); return -1; } lons[len] = atof(p); p=strtok_r(NULL, ",", &q); len++; } free(input); if ((lons[0] != lons[len-1]) || (lats[0] != lats[len-1])) { fprintf(stderr, "point_is_inside_polygon was given a non-closed polygon border\n"); free(lats); free(lons); return -1; } int is_inside = 0; for (int i=0; i lats[i]) && (lat >= lats[i+1])) continue; if (lats[i] == lats[i+1]) continue; if ((lat - lats[i]) / (lats[i+1] - lats[i]) * (lons[i+1] - lons[i]) + lons[i] < lon) is_inside = 1 - is_inside; } free(lats); free(lons); return is_inside; } size_t adaptive_strftime(char *s, size_t max, const struct tm *tm_print) { if (max<=0) return 0; time_t now = time(NULL); struct tm tm_now; memset(&tm_now, 0, sizeof(tm_now)); localtime_r(&now, &tm_now); if (tm_now . tm_year != tm_print -> tm_year) return strftime(s, max, "%Y-%m-%d %H:%M", tm_print); if (tm_now . tm_mon != tm_print -> tm_mon) return strftime(s, max, "%m-%d %H:%M", tm_print); if (tm_now . tm_mday != tm_print -> tm_mday) return strftime(s, max, "%d. %H:%M", tm_print); return strftime(s, max, "%H:%M", tm_print); } char *gadgets_retrieve_current_temperature(char *output, int max_len, double *temperature, double *humidity) { CURL *curl_handle; CURLcode res; MemoryStruct chunk; int ret_val; chunk.memory = malloc(1); chunk.size = 0; curl_global_init(CURL_GLOBAL_ALL); curl_handle = curl_easy_init(); if (!curl_handle) { perror("Failed to init curl"); free(chunk.memory); return NULL; } curl_easy_setopt(curl_handle, CURLOPT_URL, "http://wetter.mb.eah-jena.de/station/datenbank/php_giese/online.php"); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); res = curl_easy_perform(curl_handle); curl_easy_cleanup(curl_handle); if (res != CURLE_OK) { fprintf( stderr, "curl_easy_perform(%s) failed: %s\n", "http://wetter.mb.eah-jena.de/station/datenbank/php_giese/online.php", curl_easy_strerror(res) ); free(chunk.memory); if (res != CURLE_COULDNT_RESOLVE_HOST && res != CURLE_COULDNT_CONNECT && res != CURLE_PEER_FAILED_VERIFICATION) return NULL; char *ende = output; if (max_len > 0) ende += snprintf(ende, max_len, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", 0xEE, 0x01, 0xEE, 0x02, 0xEE, 0x01, 0xEE, 0x02, 0xEE, 0x05, 0xEE, 0x06, 0xEE, 0x05, 0xEE, 0x06); return ende; } regex_t re; regmatch_t rm[6]; if (regcomp(&re, fh_temperature_regex, REG_EXTENDED|REG_NEWLINE) != 0) { fprintf(stderr, "Failed to compile regex '%s'\n", fh_temperature_regex); free(chunk.memory); return NULL; } if (ret_val = regexec(&re, chunk.memory, 6, rm, 0)) { char *reg_err; reg_err = malloc(1024); regerror(ret_val, &re, reg_err, 1024); fprintf(stderr, "%d %s\n",ret_val,reg_err); regfree(&re); free(chunk.memory); return NULL; } regfree(&re); char* ende = output; if (max_len > 0) { struct tm time_struct; memset(&time_struct, 0, sizeof(time_struct)); if (strptime((char*)(chunk.memory + rm[1].rm_so),"%d.%m.%Y um %H:%M MEZ",&time_struct) == NULL) { fprintf(stderr, "Failed to parse time '%.*s'\n", (int)(rm[1].rm_eo - rm[1].rm_so), (char*)(chunk.memory + rm[1].rm_so)); free(chunk.memory); return NULL; } time_struct.tm_isdst = 0; // the fh gives CET all the time mktime(&time_struct); ende += snprintf(output, max_len, "%.*s", (int)(rm[3].rm_eo - rm[3].rm_so), (char*)(chunk.memory + rm[3].rm_so) // temperature ); char tmp_str[7]; snprintf(tmp_str, 6, "%.*s", (int)(rm[3].rm_eo - rm[3].rm_so - 3), (char*)(chunk.memory + rm[3].rm_so)); *temperature = atof(tmp_str); snprintf(tmp_str, 6, "%.*s", (int)(rm[5].rm_eo - rm[5].rm_so - 2), (char*)(chunk.memory + rm[5].rm_so)); *humidity = atof(tmp_str) / 100.; for (char *i = output; i < ende; i++) if (*i == '.') *i = ','; if (output + max_len - ende > 0) ende += snprintf(ende, output + max_len - ende, " ("); ende += adaptive_strftime(ende, output + max_len - ende, &time_struct); if (output + max_len - ende > 0) ende += snprintf(ende, output + max_len - ende, ")"); } free(chunk.memory); return ende; } void *gadgets_watch_current_temperature(void *param) { t_input_thread *p = param; time_t next_update = 0; time_t next_dns_query = 0; p->text_output = malloc(MAX_TEXT_OUTPUT_LEN); p->double_output = malloc(sizeof(double)*2); if ((p->text_output == NULL) || (p->double_output == NULL)) { fprintf(stderr, "malloc failed\n"); return NULL; } memset(p->text_output, 0, MAX_TEXT_OUTPUT_LEN); memset(p->double_output, 0, sizeof(double)*2); while (p->keep_running) { if (((time(NULL) >= next_update) || (p->force_update)) && (!p->output_ready)) { next_dns_query = time(NULL) + 30; // 1/2 minute ahead memset(p->text_output, 0, MAX_TEXT_OUTPUT_LEN); if (gadgets_retrieve_current_temperature( p->text_output, MAX_TEXT_OUTPUT_LEN-1, &p->double_output[0], &p->double_output[1] )) { next_update = time(NULL) + 600; // 10 minutes ahead p->last_update = time(NULL); } else { next_update = time(NULL) + 30; // 1/2 minute ahead fprintf(stderr, "gadgets_retrieve_current_temperature failed\n"); snprintf(p->text_output, MAX_TEXT_OUTPUT_LEN, "gadgets_retrieve_current_temperature failed"); p->double_output[0] = 0; p->double_output[1] = 0; } p->force_update = 0; p->output_ready = 1; } if (time(NULL) >= next_dns_query) { next_dns_query = time(NULL) + 30; // 1/2 minute ahead gethostbyname("wetter.mb.eah-jena.de"); } usleep(100000); } free(p->text_output); free(p->double_output); return NULL; } xmlNode *xml_find_node_by_name(xmlNode *cur_node, char *name) { while (cur_node) { if ((cur_node -> type == XML_ELEMENT_NODE) && (strcmp((char *)cur_node -> name, name) == 0)) return cur_node; cur_node = cur_node -> next; } return NULL; } xmlChar *xml_extract_string(xmlDoc *doc, xmlNode *cur_node, char *name) { xmlNode *sub_node = xml_find_node_by_name(cur_node, name); if (! sub_node) return NULL; else return xmlNodeListGetString(doc, sub_node -> xmlChildrenNode, 0); } int xml_strcmp_property_value(xmlNode *node, char *key, char *check_value) { xmlChar *is_value = xmlGetProp(node,(xmlChar *)key); int res = strcmp((char *)is_value, check_value); xmlFree(is_value); return res; } double xml_extract_float(xmlNode *node, char *key) { xmlChar *value = xmlGetProp(node, (xmlChar *)key); double res = atof((char *)value); xmlFree(value); return res; } size_t lookForExpirationHeader(char *buffer, size_t size, size_t nitems, time_t *userdata) { if (nitems * size <= 9) return nitems * size; if (memcmp(buffer, "Expires: ", 9) != 0) return nitems * size; buffer += 9; struct tm temp_data; memset(&temp_data, 0, sizeof(temp_data)); temp_data.tm_isdst = -1; strptime(buffer, "%a, %d %b %Y %H:%M:%S %Z", &temp_data); *userdata = mktime(&temp_data); memset(&temp_data, 0, sizeof(temp_data)); temp_data.tm_year = 70; temp_data.tm_mday = 1; temp_data.tm_isdst = -1; *userdata -= mktime(&temp_data); return nitems * size; } char *gadgets_retrieve_weather_forecast(MemoryStruct *chunk, time_t *expiration, char *output, int max_len) { if (*expiration < time(NULL)) { // cached version expired, we get a new one chunk->size = 0; CURL *curl_handle; CURLcode res; int ret_val; curl_global_init(CURL_GLOBAL_ALL); curl_handle = curl_easy_init(); if (!curl_handle) { perror("Failed to init curl"); return NULL; } curl_easy_setopt(curl_handle, CURLOPT_URL, "https://api.met.no/weatherapi/locationforecast/2.0/?lat=50.9336&lon=11.5621"); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl_handle, CURLOPT_ACCEPT_ENCODING, ""); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, chunk); curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, lookForExpirationHeader); curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, expiration); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "git.eckner.net/Erich/anzeige wetter@eckner.net"); res = curl_easy_perform(curl_handle); curl_easy_cleanup(curl_handle); if (res != CURLE_OK) { fprintf( stderr, "curl_easy_perform(%s) failed: %s\n", "https://api.met.no/weatherapi/locationforecast/1.9/?lat=50.9336&lon=11.5621&msl=170", curl_easy_strerror(res) ); if (res != CURLE_COULDNT_RESOLVE_HOST && res != CURLE_COULDNT_CONNECT && res != CURLE_PEER_FAILED_VERIFICATION) return NULL; char *ende = output; if (max_len > 0) ende += snprintf(ende, max_len, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", 0xEE, 0x01, 0xEE, 0x02, 0xEE, 0x01, 0xEE, 0x02, 0xEE, 0x05, 0xEE, 0x06, 0xEE, 0x05, 0xEE, 0x06); return ende; } } struct json_object *doc; doc = json_tokener_parse(chunk->memory); if (doc == NULL) { fprintf(stderr, "Failed to parse json\n"); return NULL; } struct json_object *props; if (!json_object_object_get_ex(doc, "properties", &props)) { fprintf(stderr, "No 'properties:' at the root.\n"); json_object_put(doc); return NULL; } if (!json_object_object_get_ex(props, "timeseries", &props)) { fprintf(stderr, "'properties:' has no 'timeseries:'\n"); json_object_put(doc); return NULL; } float rain_total = 0, temp_min = NAN, temp_max = NAN, wind_max = NAN, cur_val; char time_str[21]; time_t now; time(&now); now = ((int)(now/60/60))*60*60; strftime(time_str,21,"%Y-%m-%dT%H:%M:%SZ",gmtime(&now)); struct json_object *data_point; struct json_object *datum; struct json_object *details; for (int i=0; i temp_max)) temp_max = cur_val; if (isnan(temp_min) || (cur_val < temp_min)) temp_min = cur_val; if (!json_object_object_get_ex(datum, "wind_speed", &details)) { fprintf(stderr, "'timeseries:[%d] data: instant: details:' has no 'wind_speed:'\n", i); json_object_put(doc); return NULL; } cur_val = json_object_get_double(details); if (errno) { fprintf(stderr, "'timeseries:[%d] data: instant: details: wind_speed:' is no double\n", i); json_object_put(doc); return NULL; } if (isnan(wind_max) || (cur_val > wind_max)) wind_max = cur_val; if (!json_object_object_get_ex(data_point, "next_1_hours", &datum)) { fprintf(stderr, "'timeseries:[%d] data:' has no 'next_1_hours:'\n", i); json_object_put(doc); return NULL; } if (!json_object_object_get_ex(datum, "details", &datum)) { fprintf(stderr, "'timeseries:[%d] data: next_1_hours:' has no 'details:'\n", i); json_object_put(doc); return NULL; } if (!json_object_object_get_ex(datum, "precipitation_amount", &datum)) { fprintf(stderr, "'timeseries:[%d] data: next_1_hours: details:' has no 'precipitation_amount:'\n", i); json_object_put(doc); return NULL; } rain_total += json_object_get_double(datum); if (errno) { fprintf(stderr, "'timeseries:[%d] data: next_1_hours: details: precipitation_amount:' is no double\n", i); json_object_put(doc); return NULL; } } } } json_object_put(doc); if (max_len > 0) { int i = snprintf(output, max_len, "%0.1f ,, %0.1f °C; %0.1f mm; %0.1f m/s", temp_min, temp_max, rain_total, wind_max); char *ende = output + i; for (; i>=0; i--) if (output[i] == '.') output[i] = ','; else if (output[i] == ',') output[i] = '.'; return ende; } else return output; } void *gadgets_watch_weather_forecast(void *param) { t_input_thread *p = param; time_t next_update = 0; time_t next_dns_query = 0; MemoryStruct chunk; chunk.memory = malloc(1); chunk.size = 0; time_t expiration = 0; p->text_output = malloc(MAX_TEXT_OUTPUT_LEN); if (p->text_output == NULL) { fprintf(stderr, "malloc failed\n"); return NULL; } memset(p->text_output, 0, MAX_TEXT_OUTPUT_LEN); while (p->keep_running) { if (((time(NULL) >= next_update) || (p->force_update)) && (!p->output_ready)) { next_update = time(NULL) + 600; // 10 minutes ahead next_dns_query = time(NULL) + 30; // 1/2 minute ahead memset(p->text_output, 0, MAX_TEXT_OUTPUT_LEN); if (gadgets_retrieve_weather_forecast( &chunk, &expiration, p->text_output, MAX_TEXT_OUTPUT_LEN-1 )) { p->last_update = time(NULL); } else { fprintf(stderr, "gadgets_retrieve_weather_forecast failed\n"); snprintf(p->text_output, MAX_TEXT_OUTPUT_LEN, "gadgets_retrieve_weather_forecast failed"); } p->force_update = 0; p->output_ready = 1; } if (time(NULL) >= next_dns_query) { next_dns_query = time(NULL) + 30; // 1/2 minute ahead gethostbyname("api.met.no"); } usleep(100000); } free(p->text_output); free(chunk.memory); return NULL; } char *gadgets_retrieve_weather_warnings(char *output, int max_len) { CURL *curl_handle; CURLcode res; MemoryStruct chunk; int ret_val; chunk.memory = malloc(1); chunk.size = 0; curl_global_init(CURL_GLOBAL_ALL); curl_handle = curl_easy_init(); if (!curl_handle) { perror("Failed to init curl"); free(chunk.memory); return NULL; } curl_easy_setopt(curl_handle, CURLOPT_URL, "https://opendata.dwd.de/weather/alerts/cap/COMMUNEUNION_DWD_STAT/Z_CAP_C_EDZW_LATEST_PVW_STATUS_PREMIUMDWD_COMMUNEUNION_DE.zip"); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); res = curl_easy_perform(curl_handle); curl_easy_cleanup(curl_handle); if (res != CURLE_OK) { fprintf( stderr, "curl_easy_perform(%s) failed: %s\n", "https://opendata.dwd.de/weather/alerts/cap/COMMUNEUNION_DWD_STAT/Z_CAP_C_EDZW_LATEST_PVW_STATUS_PREMIUMDWD_COMMUNEUNION_DE.zip", curl_easy_strerror(res) ); free(chunk.memory); if (res != CURLE_COULDNT_RESOLVE_HOST && res != CURLE_COULDNT_CONNECT && res != CURLE_PEER_FAILED_VERIFICATION) return NULL; char *ende = output; if (max_len > 0) ende += snprintf(ende, max_len, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", 0xEE, 0x01, 0xEE, 0x02, 0xEE, 0x01, 0xEE, 0x02, 0xEE, 0x05, 0xEE, 0x06, 0xEE, 0x05, 0xEE, 0x06); return ende; } /* FILE *f = fopen("Z_CAP_C_EDZW_LATEST_PVW_STATUS_PREMIUMDWD_COMMUNEUNION_DE.zip","rb"); if (f == NULL) { perror("failed to open file"); return NULL; } fseek(f, 0, SEEK_END); chunk.size = ftell(f); chunk.memory = malloc(chunk.size); if (chunk.memory == NULL) { fprintf(stderr, "malloc failed to allocate %d bytes", chunk.size); fclose(f); return NULL; } fseek(f, 0, SEEK_SET); fread(chunk.memory, chunk.size, 1, f); fclose(f); */ zip_error_t error; zip_source_t *src; zip_t *za; zip_file_t *zf; zip_stat_t zs; zip_error_init(&error); if ((src = zip_source_buffer_create(chunk.memory, chunk.size, 0, &error)) == NULL) { fprintf(stderr, "can't create zip source: %s\n", zip_error_strerror(&error)); zip_error_fini(&error); free(chunk.memory); return NULL; } if ((za = zip_open_from_source(src, ZIP_RDONLY, &error)) == NULL) { fprintf(stderr, "can't open zip from source: %s\n", zip_error_strerror(&error)); zip_source_free(src); zip_error_fini(&error); free(chunk.memory); return NULL; } zip_error_fini(&error); LIBXML_TEST_VERSION int warnings_len = 0; warning_t *warnings = malloc(sizeof(warning_t)); if (warnings == NULL) { fprintf(stderr, "malloc failed allocating %d bytes\n", sizeof(warning_t)); zip_discard(za); free(chunk.memory); return NULL; } memset(warnings, 0, sizeof(warning_t)); for (int i=0; i\n"); free(chunk.memory); for (int i=0; i children; text = xml_extract_string(doc, cur_node, "status"); if (text == NULL) { fprintf(stderr, "Failed to enter \n"); free(chunk.memory); for (int i=0; i\n"); free(chunk.memory); for (int i=0; i is neither 'Alert' nor 'Cancel' nor 'Update'\n"); free(chunk.memory); for (int i=0; i\n"); free(chunk.memory); for (int i=0; i children; text = xml_extract_string(doc, cur_node, "category"); if (text == NULL) { fprintf(stderr, "Failed to enter %s\n",zs.name); free(chunk.memory); for (int i=0; i is neither 'Met' nor 'Health'\n"); free(chunk.memory); for (int i=0; i %s\n",zs.name); free(chunk.memory); for (int i=0; i is neither 'Met' nor 'Health'\n"); free(chunk.memory); for (int i=0; i %s\n",zs.name); free(chunk.memory); for (int i=0; i %s\n",zs.name); free(chunk.memory); for (int i=0; i next) { xmlNode *sub_sub_node; for (sub_sub_node = sub_node -> children; sub_sub_node = xml_find_node_by_name(sub_sub_node, "geocode"); sub_sub_node = sub_sub_node -> next) { text = xml_extract_string(doc, sub_sub_node -> children, "valueName"); if (text == NULL) { fprintf(stderr, "Failed to enter \n"); free(chunk.memory); for (int i=0; i children, "value"); if (text == NULL) { fprintf(stderr, "Failed to enter \n"); free(chunk.memory); for (int i=0; i children; sub_sub_node = xml_find_node_by_name(sub_sub_node, "polygon"); sub_sub_node = sub_sub_node -> next) { text = xmlNodeListGetString(doc, sub_sub_node -> children, 0); if (text == NULL) { fprintf(stderr, "Failed to read text of \n"); free(chunk.memory); for (int i=0; i= time(NULL) + warnings_future_timespan) is_relevant = 0; // start is too far ahead if (warnings[warnings_len].expires < time(NULL)) is_relevant = 0; // warning has expired if (!is_relevant) { xmlFreeDoc(doc); continue; } text = xml_extract_string(doc, cur_node, "headline"); if (text == NULL) { fprintf(stderr, "Failed to enter \n"); free(chunk.memory); for (int i=0; i\n"); free(chunk.memory); for (int i=0; i 0)) ende += snprintf(ende, max_len, "%s", "keine Warnungen"); for (int i=0; i time(NULL)) || (warnings[j].onset > time(NULL)))) continue; if ((warnings[i].expires != warnings[j].expires) && ((warnings[i].expires < time(NULL) + warnings_future_timespan) || (warnings[j].expires < time(NULL) + warnings_future_timespan))) continue; is_relevant = 0; } if (is_relevant && (output + max_len - ende > 0)) { ende += snprintf(ende, output + max_len - ende, "%s", warnings[i].event); if (output + max_len - ende > 0) { if (warnings[i].onset > time(NULL)) { if (output + max_len - ende > 0) ende += snprintf(ende, output + max_len - ende, " ab "); ende += adaptive_strftime(ende, output + max_len - ende, localtime(&warnings[i].onset)); } if (warnings[i].expires < time(NULL) + warnings_future_timespan) { if (output + max_len - ende > 0) ende += snprintf(ende, output + max_len - ende, " bis "); ende += adaptive_strftime(ende, output + max_len - ende, localtime(&warnings[i].expires)); } if ((i+1 0)) ende += snprintf(ende, output + max_len - ende, "; "); } } free(warnings[i].headline); free(warnings[i].event); } free(warnings); return ende; } void *gadgets_watch_weather_warnings(void *param) { t_input_thread *p = param; time_t next_update = 0; time_t next_dns_query = 0; p->text_output = malloc(MAX_TEXT_OUTPUT_LEN); if (p->text_output == NULL) { fprintf(stderr, "malloc failed\n"); return NULL; } memset(p->text_output, 0, MAX_TEXT_OUTPUT_LEN); while (p->keep_running) { if (((time(NULL) >= next_update) || (p->force_update)) && (!p->output_ready)) { next_update = time(NULL) + 600; // 10 minutes ahead next_dns_query = time(NULL) + 30; // 1/2 minute ahead memset(p->text_output, 0, MAX_TEXT_OUTPUT_LEN); if (gadgets_retrieve_weather_warnings( p->text_output, MAX_TEXT_OUTPUT_LEN-1 )) { p->last_update = time(NULL); } else { fprintf(stderr, "gadgets_retrieve_weather_warnings failed\n"); snprintf(p->text_output, MAX_TEXT_OUTPUT_LEN, "gadgets_retrieve_weather_warnings failed"); } p->force_update = 0; p->output_ready = 1; } if (time(NULL) >= next_dns_query) { next_dns_query = time(NULL) + 30; // 1/2 minute ahead gethostbyname("opendata.dwd.de"); } usleep(100000); } free(p->text_output); return NULL; } char *gadgets_retrieve_humidity(char *output, int max_len, double *temperature, double *humidity) { CURL *curl_handle; CURLcode res; MemoryStruct chunk; int ret_val; char *ende = output; chunk.memory = malloc(1); chunk.size = 0; curl_global_init(CURL_GLOBAL_ALL); curl_handle = curl_easy_init(); if (!curl_handle) { perror("Failed to init curl"); free(chunk.memory); return NULL; } curl_easy_setopt(curl_handle, CURLOPT_URL, "https://kpi.ddns.eckner.net/tmp/sensor"); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); res = curl_easy_perform(curl_handle); curl_easy_cleanup(curl_handle); if (res != CURLE_OK) { fprintf( stderr, "curl_easy_perform(%s) failed: %s\n", "https://kpi.ddns.eckner.net/tmp/sensor", curl_easy_strerror(res) ); free(chunk.memory); if (res != CURLE_COULDNT_RESOLVE_HOST && res != CURLE_COULDNT_CONNECT && res != CURLE_PEER_FAILED_VERIFICATION) return NULL; if (max_len > 0) ende += snprintf(ende, max_len, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", 0xEE, 0x01, 0xEE, 0x02, 0xEE, 0x01, 0xEE, 0x02, 0xEE, 0x05, 0xEE, 0x06, 0xEE, 0x05, 0xEE, 0x06); return ende; } regex_t re; regmatch_t rm[3]; if (regcomp(&re, kpi_regex, REG_EXTENDED|REG_NEWLINE) != 0) { fprintf(stderr, "Failed to compile regex '%s'\n", kpi_regex); free(chunk.memory); return NULL; } if (ret_val = regexec(&re, chunk.memory, 3, rm, 0)) { char *reg_err; reg_err = malloc(1024); regerror(ret_val, &re, reg_err, 1024); fprintf(stderr, "%d %s\n",ret_val,reg_err); regfree(&re); free(chunk.memory); return NULL; } regfree(&re); char tmp_str[12]; snprintf(tmp_str, 11, "%.*s", (int)(rm[1].rm_eo - rm[1].rm_so), (char*)(chunk.memory + rm[1].rm_so)); *humidity = atof(tmp_str) / 100.; snprintf(tmp_str, 11, "%.*s", (int)(rm[2].rm_eo - rm[2].rm_so), (char*)(chunk.memory + rm[2].rm_so)); *temperature = atof(tmp_str); free(chunk.memory); return ende; } char *gadgets_calculate_humidity_level(char *output, int max_len, double outside_temperature, double outside_humidity, double inside_temperature, double inside_humidity) { char *ende; ende = output; inside_humidity *= maximale_luftfeuchte(inside_temperature); outside_humidity *= maximale_luftfeuchte(outside_temperature); if ((output + max_len - ende > 0) && (inside_temperature <= taupunkt(outside_humidity)+2)) ende += snprintf(ende, max_len, "!TPa %.1f K; ", taupunkt(outside_humidity) - inside_temperature); if ((output + max_len - ende > 0) && (inside_temperature <= taupunkt(inside_humidity)+2)) ende += snprintf(ende, max_len, "!TPi %.1f K; ", taupunkt(inside_humidity) - inside_temperature); if (output + max_len - ende > 0) ende += snprintf(ende, max_len, "%.0f %%", inside_humidity * 100 / outside_humidity); while (output < ende) { if (*output == '.') *output = ','; output++; } return ende; } void *gadgets_watch_humidity(void *param) { t_input_thread *p = param; time_t next_update = 0; time_t next_dns_query = 0; p->text_output = malloc(MAX_TEXT_OUTPUT_LEN); p->double_output = malloc(sizeof(double)*2); if ((p->text_output == NULL) || (p->double_output == NULL)) { fprintf(stderr, "malloc failed\n"); return NULL; } memset(p->text_output, 0, MAX_TEXT_OUTPUT_LEN); memset(p->double_output, 0, sizeof(double)*2); while (p->keep_running) { if (((time(NULL) >= next_update) || (p->force_update)) && (!p->output_ready)) { next_update = time(NULL) + 600; // 10 minutes ahead next_dns_query = time(NULL) + 30; // 1/2 minute ahead if (gadgets_retrieve_humidity( p->text_output, MAX_TEXT_OUTPUT_LEN-1, &p->double_output[0], &p->double_output[1] )) { p->last_update = time(NULL); } else { fprintf(stderr, "gadgets_retrieve_humidity failed\n"); snprintf(p->text_output, MAX_TEXT_OUTPUT_LEN, "gadgets_retrieve_humidity failed\n"); p->double_output[0]=0; p->double_output[1]=0; } p->force_update = 0; p->output_ready = 1; } if (time(NULL) >= next_dns_query) { next_dns_query = time(NULL) + 30; // 1/2 minute ahead gethostbyname("kpi.ddns.eckner.net"); } usleep(100000); } free(p->text_output); free(p->double_output); return NULL; }