Skip to content

Commit 753f94a

Browse files
committed
Add support for weather forecast
1 parent cdb54f4 commit 753f94a

File tree

7 files changed

+188
-19
lines changed

7 files changed

+188
-19
lines changed

libZaeUtil/stack.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include "variant.h"
4+
#include <sys/param.h>
45
// This is the basic stack implementation for holding operands and operators
56

67
typedef struct stack_item_t
@@ -22,6 +23,11 @@ int __i__; \
2223
variant_t* _value = stack_peek_at(_stack, 0); \
2324
for(__i__ = 0; __i__ < _stack->count; __i__++, _value = stack_peek_at(_stack, __i__)) \
2425

26+
#define stack_for_each_range(_stack, _from, _to, _value) \
27+
int __i__; \
28+
variant_t* _value = stack_peek_at(_stack, 0); \
29+
for(__i__ = _from; __i__ <= MIN(_stack->count-1, _to); __i__++, _value = stack_peek_at(_stack, __i__)) \
30+
2531
variant_stack_t* stack_create();
2632
void stack_free(variant_stack_t* stack);
2733
void stack_empty(variant_stack_t* stack);

services/Weather/Weather.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ void service_create(service_t** service, int service_id)
1919
SERVICE_ADD_METHOD(GetPrecipitation, weather_get_precipitation, 0, "Get current precipitation");
2020
SERVICE_ADD_METHOD(GetHumidity, weather_get_humidity, 0, "Get current humidity");
2121
SERVICE_ADD_METHOD(Refresh, weather_refresh_cache, 0, "Update stored weather information");
22+
SERVICE_ADD_METHOD(GetForecast, weather_get_forecast, 1, "Return weather forecast, args: Hourly|Daily|Raw");
2223

2324
(*service)->get_config_callback = weather_cli_get_config;
2425
DT_WEATHER = service_id;

services/Weather/weather_cache.c

Lines changed: 103 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
weather_cache_t weather_cache;
1212
void weather_cache_parse_response(struct json_object* weather_response_obj);
13+
void weather_cache_parse_forecast(struct json_object* weather_response_obj);
14+
void weather_cache_parse_weather_entry(struct json_object* weather_entry_obj, weather_cache_t* cache_entry);
15+
void weather_entry_free(void* arg);
1316

1417
typedef struct response_memory_t
1518
{
@@ -43,6 +46,8 @@ void weather_cache_init()
4346
weather_cache.windspeed = 0;
4447
weather_cache.temp = 0;
4548
weather_cache.precipitation = NULL;
49+
weather_cache.raw_forecast = NULL;
50+
weather_cache.forecast_hourly = stack_create();
4651
}
4752

4853
void weather_cache_refresh()
@@ -103,6 +108,43 @@ void weather_cache_refresh()
103108
json_object_put(weather_response_obj);
104109
}
105110

111+
// Now get the forecast
112+
free(chunk.memory);
113+
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
114+
chunk.size = 0; /* no data at this point */
115+
116+
snprintf(urlbuf, 511, "http://api.openweathermap.org/data/2.5/forecast?zip=%d,%s&units=%s&APPID=%s", weather_zip, weather_country_code, weather_temp_units, "337b07da05ad8ebf391e2252f02196cf");
117+
118+
LOG_DEBUG(DT_WEATHER, "Weather URL %s", urlbuf);
119+
120+
curl_easy_setopt(curl_handle, CURLOPT_URL, urlbuf);
121+
122+
/* send all data to this function */
123+
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_cb);
124+
125+
/* we pass our 'chunk' struct to the callback function */
126+
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
127+
128+
/* some servers don't like requests that are made without a user-agent
129+
field, so we provide one */
130+
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
131+
132+
/* get it! */
133+
res = curl_easy_perform(curl_handle);
134+
135+
/* check for errors */
136+
if(res != CURLE_OK)
137+
{
138+
LOG_ERROR(DT_WEATHER, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
139+
}
140+
else
141+
{
142+
LOG_DEBUG(DT_WEATHER, "%lu bytes retrieved", (long)chunk.size);
143+
struct json_object* weather_response_obj = json_tokener_parse(chunk.memory);
144+
weather_cache_parse_forecast(weather_response_obj);
145+
json_object_put(weather_response_obj);
146+
}
147+
106148
/* cleanup curl stuff */
107149
curl_easy_cleanup(curl_handle);
108150

@@ -118,7 +160,49 @@ void weather_cache_parse_response(struct json_object* weather_response_obj)
118160
free(weather_cache.precipitation);
119161
weather_cache.precipitation = 0;
120162

121-
json_object_object_foreach(weather_response_obj, key, value)
163+
weather_cache_parse_weather_entry(weather_response_obj, &weather_cache);
164+
165+
LOG_DEBUG(DT_WEATHER, "Updated weather values - precipitation: %s, temp: %f, humidity: %d, wind: %f",
166+
weather_cache.precipitation,
167+
weather_cache.temp,
168+
weather_cache.humidity,
169+
weather_cache.windspeed);
170+
}
171+
172+
void weather_cache_parse_forecast(struct json_object* weather_response_obj)
173+
{
174+
//printf("jobj from str:\n---\n%s\n---\n", json_object_to_json_string_ext(weather_response_obj, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY));
175+
stack_empty(weather_cache.forecast_hourly);
176+
177+
free(weather_cache.raw_forecast);
178+
179+
json_object* entries_array;
180+
json_object_object_get_ex(weather_response_obj, "list", &entries_array);
181+
int weather_entries = json_object_array_length(entries_array);
182+
183+
for(int i = 0; i < weather_entries; i++)
184+
{
185+
json_object* entry = json_object_array_get_idx(entries_array, i);
186+
json_object* date_obj;
187+
json_object_object_get_ex(entry, "dt", &date_obj);
188+
189+
weather_forecast_cache_t* forecast_entry = calloc(1, sizeof(weather_forecast_cache_t));
190+
forecast_entry->timestamp = json_object_get_int(date_obj);
191+
192+
weather_cache_parse_weather_entry(entry, &forecast_entry->weather_entry);
193+
194+
variant_t* forecast_entry_variant = variant_create_ptr(DT_PTR, forecast_entry, &weather_entry_free);
195+
stack_push_back(weather_cache.forecast_hourly, forecast_entry_variant);
196+
}
197+
198+
weather_cache.raw_forecast = strdup(json_object_to_json_string_ext(weather_response_obj, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY));
199+
}
200+
201+
void weather_cache_parse_weather_entry(struct json_object* weather_entry_obj, weather_cache_t* cache_entry)
202+
{
203+
cache_entry->precipitation = 0;
204+
205+
json_object_object_foreach(weather_entry_obj, key, value)
122206
{
123207
if(strcmp(key, "weather") == 0)
124208
{
@@ -131,19 +215,19 @@ void weather_cache_parse_response(struct json_object* weather_response_obj)
131215
{
132216
const char* weather_string = json_object_get_string(weather_value);
133217
int old_len = 0;
134-
if(NULL != weather_cache.precipitation)
218+
if(NULL != cache_entry->precipitation)
135219
{
136-
old_len = strlen(weather_cache.precipitation);
220+
old_len = strlen(cache_entry->precipitation);
137221
}
138-
weather_cache.precipitation = realloc(weather_cache.precipitation, (old_len + 2 + strlen(weather_string))*sizeof(char));
139-
if(NULL != weather_cache.precipitation)
222+
cache_entry->precipitation = realloc(cache_entry->precipitation, (old_len + 2 + strlen(weather_string))*sizeof(char));
223+
if(NULL != cache_entry->precipitation)
140224
{
141-
memset(weather_cache.precipitation + (old_len), 0, 2 + strlen(weather_string));
142-
strcat(weather_cache.precipitation, weather_string);
225+
memset(cache_entry->precipitation + (old_len), 0, 2 + strlen(weather_string));
226+
strcat(cache_entry->precipitation, weather_string);
143227

144228
if(i < weather_entries-1)
145229
{
146-
strcat(weather_cache.precipitation, "|");
230+
strcat(cache_entry->precipitation, "|");
147231
}
148232
}
149233
}
@@ -154,28 +238,30 @@ void weather_cache_parse_response(struct json_object* weather_response_obj)
154238
struct json_object* temp_data;
155239
if(json_object_object_get_ex(value, "temp", &temp_data) == TRUE)
156240
{
157-
weather_cache.temp = json_object_get_double(temp_data);
241+
cache_entry->temp = json_object_get_double(temp_data);
158242
}
159243
struct json_object* humidity_data;
160244
if(json_object_object_get_ex(value, "humidity", &humidity_data) == TRUE)
161245
{
162-
weather_cache.humidity = json_object_get_int(humidity_data);
246+
cache_entry->humidity = json_object_get_int(humidity_data);
163247
}
164248
}
165249
else if(strcmp(key, "wind") == 0)
166250
{
167251
struct json_object* wind_data;
168252
if(json_object_object_get_ex(value, "speed", &wind_data) == TRUE)
169253
{
170-
weather_cache.windspeed = json_object_get_double(wind_data);
254+
cache_entry->windspeed = json_object_get_double(wind_data);
171255
}
172256
}
173257
}
174-
175-
LOG_DEBUG(DT_WEATHER, "Updated weather values - precipitation: %s, temp: %f, humidity: %d, wind: %f",
176-
weather_cache.precipitation,
177-
weather_cache.temp,
178-
weather_cache.humidity,
179-
weather_cache.windspeed);
180258
}
181259

260+
void weather_entry_free(void* arg)
261+
{
262+
weather_forecast_cache_t* entry = (weather_forecast_cache_t*)arg;
263+
free(entry->weather_entry.precipitation);
264+
free(entry->weather_entry.raw_forecast);
265+
stack_free(entry->weather_entry.forecast_hourly);
266+
free(entry);
267+
}

services/Weather/weather_cache.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#include "stack.h"
2+
#include <time.h>
3+
14
typedef struct weather_cache_t
25
{
36
int cache_age;
@@ -6,8 +9,16 @@ typedef struct weather_cache_t
69
double windspeed;
710
char* precipitation;
811
int humidity;
12+
char* raw_forecast;
13+
variant_stack_t* forecast_hourly;
914
} weather_cache_t;
1015

16+
typedef struct weather_forecast_cache_t
17+
{
18+
time_t timestamp;
19+
weather_cache_t weather_entry;
20+
} weather_forecast_cache_t;
21+
1122
extern weather_cache_t weather_cache;
1223

1324
void weather_cache_init();

services/Weather/weather_methods.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "weather_methods.h"
22
#include "weather_cache.h"
3+
#include <stdio.h>
4+
#include <time.h>
35

46
variant_t* weather_get_temp(service_method_t* method, va_list args)
57
{
@@ -37,3 +39,66 @@ variant_t* weather_get_humidity(service_method_t* method, va_list args)
3739
return variant_create_int32(DT_INT32, weather_cache.humidity);
3840
}
3941

42+
variant_t* weather_get_forecast(service_method_t* method, va_list args)
43+
{
44+
weather_cache_refresh();
45+
variant_t* range_variant = va_arg(args, variant_t*);
46+
char* range;
47+
variant_to_string(range_variant, &range);
48+
49+
char* formatted_output = NULL;
50+
if(NULL != range)
51+
{
52+
if(0 == strcmp(range, "Hourly") && weather_cache.forecast_hourly->count > 0)
53+
{
54+
stack_for_each_range(weather_cache.forecast_hourly, 0, 8, forecast_entry_variant)
55+
{
56+
weather_forecast_cache_t* forecast_entry = VARIANT_GET_PTR(weather_forecast_cache_t, forecast_entry_variant);
57+
//variant_t* vv = variant_create_float(forecast_entry->weather_entry.temp);
58+
char time_string[256] = {0};
59+
struct tm* time_tm = localtime(&forecast_entry->timestamp);
60+
size_t time_size = strftime(time_string, 255, "%l%P", time_tm);
61+
62+
int old_len = 0;
63+
if(NULL != formatted_output)
64+
{
65+
old_len = strlen(formatted_output);
66+
}
67+
68+
formatted_output = realloc(formatted_output, old_len + 255);
69+
snprintf(formatted_output+old_len, 254, "%s %.1fF %s\n", time_string, forecast_entry->weather_entry.temp, forecast_entry->weather_entry.precipitation);
70+
}
71+
return variant_create_string(formatted_output);
72+
}
73+
else if(0 == strcmp(range, "Daily") && weather_cache.forecast_hourly->count > 0)
74+
{
75+
stack_for_each(weather_cache.forecast_hourly, forecast_entry_variant)
76+
{
77+
weather_forecast_cache_t* forecast_entry = VARIANT_GET_PTR(weather_forecast_cache_t, forecast_entry_variant);
78+
//variant_t* vv = variant_create_float(forecast_entry->weather_entry.temp);
79+
char time_string[256] = {0};
80+
struct tm* time_tm = localtime(&forecast_entry->timestamp);
81+
size_t time_size = strftime(time_string, 255, "%a %e %l%P", time_tm);
82+
83+
int old_len = 0;
84+
if(NULL != formatted_output)
85+
{
86+
old_len = strlen(formatted_output);
87+
}
88+
89+
formatted_output = realloc(formatted_output, old_len + 255);
90+
snprintf(formatted_output+old_len, 254, "%s %.1fF %s\n", time_string, forecast_entry->weather_entry.temp, forecast_entry->weather_entry.precipitation);
91+
}
92+
return variant_create_string(formatted_output);
93+
}
94+
else if(0 == strcmp(range, "Raw") && NULL != weather_cache.raw_forecast)
95+
{
96+
return variant_create_string(strdup(weather_cache.raw_forecast));
97+
}
98+
else
99+
{
100+
return variant_create_string(strdup("no data"));
101+
}
102+
}
103+
return NULL;
104+
}

services/Weather/weather_methods.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ variant_t* weather_get_windspeed(service_method_t* method, va_list args);
55
variant_t* weather_get_precipitation(service_method_t* method, va_list args);
66
variant_t* weather_refresh_cache(service_method_t* method, va_list args);
77
variant_t* weather_get_humidity(service_method_t* method, va_list args);
8-
8+
variant_t* weather_get_forecast(service_method_t* method, va_list args);

vty_io.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <stdbool.h>
99
#include "http_server.h"
1010

11-
#define BUFSIZE 8096
11+
#define BUFSIZE 64000
1212

1313
void file_write_cb(vty_t* vty, const char* format, va_list args);
1414
char* file_read_cb(vty_t* vty);

0 commit comments

Comments
 (0)