From 57e733668832686146c0e4aede9a61fb20a7de01 Mon Sep 17 00:00:00 2001 From: EricDWertz Date: Mon, 25 Apr 2016 00:45:47 -0400 Subject: [PATCH] Added weather underground api support --- Makefile.am | 2 +- main.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 207 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index cbdcffd..6ad804a 100755 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ bin_PROGRAMS = deskclocky deskclocky_SOURCES = main.c -deskclocky_LDFLAGS = -lm `pkg-config --libs gtk+-3.0` +deskclocky_LDFLAGS = -ljansson -lm -lcurl `pkg-config --libs gtk+-3.0` INCLUDES = `pkg-config --cflags gtk+-3.0` diff --git a/main.c b/main.c index 2ffa263..52cc536 100644 --- a/main.c +++ b/main.c @@ -3,6 +3,10 @@ #include #include #include +#include + +#include +#include #define WINDOW_WIDTH 1280 #define WINDOW_HEIGHT 800 @@ -10,6 +14,13 @@ #define FONT_SIZE 64.0 #define GRID_SIZE FONT_SIZE*1.5 +#define URL_BASE "http://api.wunderground.com/api/565b48be709c806d/" +#define URL_POSTFIX "/q/40.4598,-78.5917.json" + +//10 minute timeout to refresh weather info +#define WEATHER_REFRESH_TIMEOUT 600 +int weather_update_timer = WEATHER_REFRESH_TIMEOUT; + char* weekday_names[7] = { "Sunday", @@ -42,6 +53,140 @@ GdkPixbuf* background_pixbuf; int tick = 0; +struct write_result +{ + char* data; + int pos; +}; + +typedef struct +{ + int hour; + int temp; + int code; + char ampm[4]; +} weather_info; + +//Hourly weather information +weather_info hourly_info[64]; + +static size_t write_curl_response( void* ptr, size_t size, size_t nmemb, void* stream ) +{ + struct write_result *result = (struct write_result *)stream; + + if(result->pos + size * nmemb >= 1000000 - 1) + { + fprintf(stderr, "error: too small buffer\n"); + return 0; + } + + memcpy(result->data + result->pos, ptr, size * nmemb); + result->pos += size * nmemb; + + return size * nmemb; +} +char* weather_api_request( const char* request ) +{ + char url[512]; + CURL* curl = curl_easy_init(); + CURLcode status; + char *data; + data = (char*)malloc(1000000); + + if( !curl ) + NULL; + + struct write_result write_result = { + .data = data, + .pos = 0, + }; + + sprintf( url, "%s%s%s", URL_BASE, request, URL_POSTFIX ); + printf( url ); + curl_easy_setopt( curl, CURLOPT_URL, url ); + curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_curl_response ); + curl_easy_setopt( curl, CURLOPT_WRITEDATA, &write_result ); + + status = curl_easy_perform(curl); + if(status != 0) + { + fprintf(stderr, "error: unable to request data from %s:\n", url ); + fprintf(stderr, "%s\n", curl_easy_strerror(status)); + return NULL; + } + + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); + if(status != 200) + { + fprintf(stderr, "error: server responded with code %ld\n", curl_easy_strerror(status) ); + return NULL; + } + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + /* zero-terminate the result */ + data[write_result.pos] = '\0'; + return data; +} + +void update_weather() +{ + char* data = weather_api_request( "hourly" ); + + //Now do the JSON stuff + json_error_t error; + json_t* root = json_loads( data, 0, &error ); + free( data ); + + //JSON structure + // hourly_forecast + // - FCTTIME + // - hour + // - min + // - temp + // - english: fahrenheit + // - condition + json_t* forecasts = json_object_get( root, "hourly_forecast" ); + + json_t *temp, *english, *array_data, *time; + for( int i=0; i < json_array_size( forecasts ); i++ ) + { + array_data = json_array_get( forecasts, i ); + + temp = json_object_get( array_data, "temp" ); + english = json_object_get( temp, "english" ); + + int temp = atoi( json_string_value( english ) ); + + time = json_object_get( array_data, "FCTTIME" ); + int hour = atoi( json_string_value( json_object_get( time, "hour" ) ) ); + if( hour >= 12 ) + hour -= 12; + if( hour == 0 ) hour = 12; + int minute = atoi( json_string_value( json_object_get( time, "min" ) ) ); + const char* ampm = json_string_value( json_object_get( time, "ampm" ) ); + hourly_info[i+1].ampm[1] = 'm'; hourly_info[i+1].ampm[2] = '\0'; + if( ampm[0] == 'A' ) + hourly_info[i+1].ampm[0] = 'a'; + else + hourly_info[i+1].ampm[0] = 'p'; + + printf( "time: %i%s temp: %i\n", hour, hourly_info[i+1].ampm, temp ); + hourly_info[i+1].hour = hour; + hourly_info[i+1].temp = temp; + } + + data = weather_api_request( "conditions" ); + printf( data ); + root = json_loads( data, 0, &error ); + free( data ); + + json_t* observation = json_object_get( root, "current_observation" ); + hourly_info[0].temp = (int)json_real_value( json_object_get( observation, "temp_f" ) ); + +} + void load_background_pixbuf( const char* path ) { GError* error=NULL; @@ -54,6 +199,42 @@ void load_background_pixbuf( const char* path ) } } +void draw_weather_icon( cairo_t* cr, double x, double y, int info_index ) +{ + char buffer[64]; + int text_x, text_y; + cairo_text_extents_t extents; + weather_info* info = &hourly_info[info_index]; + + cairo_set_font_size( cr, FONT_SIZE * 0.375 ); + cairo_move_to( cr, x, y ); + sprintf( buffer, "%i%s", info->hour, info->ampm ); + if( info_index == 0 ) strcpy( buffer, "now" ); + cairo_text_path( cr, buffer ); + cairo_fill( cr ); + + text_x = x + GRID_SIZE * 0.75; + text_y = y - GRID_SIZE * 0.5; + + cairo_set_font_size( cr, FONT_SIZE * 0.5 ); + sprintf( buffer,"%i", info->temp); + cairo_text_extents( cr, buffer, &extents ); + cairo_move_to( cr, text_x, text_y ); + cairo_text_path( cr, buffer ); + cairo_fill( cr ); + + text_x += extents.width + extents.x_bearing; + text_y -= extents.height; + + strcpy( buffer, "°F" ); + cairo_set_font_size( cr, FONT_SIZE * 0.25 ); + cairo_text_extents( cr, buffer, &extents ); + text_y += extents.height; + cairo_move_to( cr, text_x, text_y ); + cairo_text_path( cr, buffer ); + cairo_fill( cr ); +} + /* * This function draws the time and date strings, I have a feeling thought that it will soon draw everything */ @@ -86,12 +267,28 @@ void draw_timestring( cairo_t* cr ) double time_width = 0; + /* + * This part draws the background rectangles + * TODO: make it so the shade rectangles are drawn on their own surface then overlayed on the background pixbuf + */ cairo_set_source_rgba( cr, 0.0, 0.0, 0.0, 0.75 ); - cairo_rectangle( cr, GRID_SIZE, (WINDOW_HEIGHT*0.5) - GRID_SIZE * 3.0, WINDOW_WIDTH - (GRID_SIZE * 2.0), GRID_SIZE * 3.5 ); + cairo_rectangle( cr, GRID_SIZE, (WINDOW_HEIGHT*0.5) - GRID_SIZE * 3.0, WINDOW_WIDTH - (GRID_SIZE * 2.0), GRID_SIZE * 4.5 ); + //Draw Bottom rect + cairo_rectangle( cr, 0, WINDOW_HEIGHT - GRID_SIZE, WINDOW_WIDTH, GRID_SIZE ); cairo_fill( cr ); cairo_set_source_rgba( cr, 1.0, 1.0, 1.0, 0.75 ); + double x_move = GRID_SIZE * 2.0; + double x = WINDOW_WIDTH*0.5 - ( GRID_SIZE * 5.0 ); + int i; + int delta_hour = 3; + for( i = 0; i < 5; i++ ) + { + draw_weather_icon( cr, x, WINDOW_HEIGHT - FONT_SIZE * 0.25, i*delta_hour ); + x+= x_move; + } + char timestring[64]; char ampmstring[6]; char datestring[64]; @@ -151,6 +348,12 @@ gboolean draw( GtkWidget* widget, cairo_t* cr, gpointer user ) gboolean refresh_clock(gpointer data) { + weather_update_timer--; + if( weather_update_timer < 0 ) + { + weather_update_timer = WEATHER_REFRESH_TIMEOUT; + update_weather(); + } tick = !tick; gtk_widget_queue_draw(window); return TRUE; @@ -193,6 +396,8 @@ int main( int argc, char **argv ) g_timeout_add_seconds(1,refresh_clock,NULL); + update_weather(); + gtk_widget_show_all(window); gtk_main(); -- 2.47.0