1 /* Copyright (C) 2019-2021 Logaev Maxim (turbocat2001), GPLv2 */
4 Info: App uses api from openweathermap.org.
5 The standard configuration uses my token and the city of Moscow.
6 You can always change it in the weather.json file.
7 weather.json configuration example:
10 "Celsius": false, // Enabled fahrenheit (Optional)
11 "Location": "Berlin", // city Berlin
12 "Token": "19ffa14b3dc0e238175829461d1788b8", // OpenWeatherMap token
13 "Lang": "ru", // Language (Optional)
14 "AutoUpdate": 5 // In minutes. 0 - disabled (Optional)
23 #include "json/json.h"
25 #include <clayer/http.h>
26 #include <clayer/libimg.h>
28 #define VERSION "Weather 1.6e"
38 #define JSON_OBJ(X) value->u.object.values[X]
41 unsigned WINDOW_W
= 230;
43 #define API "api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=%s&lang=%s"
44 #define IMAGE_URL "openweathermap.org/img/w/%s.png"
47 const char *config1_name
= "/sys/Settings/weather.json";
48 const char *config2_name
= "/kolibrios/Settings/weather.json";
50 unsigned char char_size
=1;
51 uint64_t auto_update_time
= 0;
53 char *wind_speed_str
, *pressure_str
, *visibility_str
, *humidity_str
, *update_str
, *wind_deg_str
;
56 char format_temp_str
[6];
58 char full_url_image
[256];
62 ksys_colors_table_t sys_color_table
;
67 struct open_weather_data
{
81 void notify_show(char *text
){
82 _ksys_exec("/sys/@notify", text
);
85 void* safe_malloc(size_t size
)
89 notify_show("'Memory allocation error!' -E");
96 void draw_format_text_sys(int x
, int y
, ksys_color_t color
, const char *format_str
, ... ) // Форматированный вывод в окно
100 va_start (ap
, format_str
);
101 vsnprintf(tmp_buff
, sizeof tmp_buff
,format_str
, ap
);
103 _ksys_draw_text(tmp_buff
, x
, y
, 0, color
);
106 void find_and_set(json_value
*value
, struct open_weather_data
* weather
) // Ищем значения в json и заполняем структуру "myw"
108 for(int i
=0; i
<value
->u
.object
.length
; i
++){
109 if(!strcmp(JSON_OBJ(i
).name
, "main")){
110 if(JSON_OBJ(i
).value
->u
.object
.values
[0].value
->type
==json_double
)
112 weather
->temp
= (int)JSON_OBJ(i
).value
->u
.object
.values
[0].value
->u
.dbl
;
114 weather
->temp
= JSON_OBJ(i
).value
->u
.object
.values
[0].value
->u
.integer
;
116 weather
->pressure
= JSON_OBJ(i
).value
->u
.object
.values
[4].value
->u
.integer
;
117 weather
->humidity
= JSON_OBJ(i
).value
->u
.object
.values
[5].value
->u
.integer
;
119 if(!strcmp(JSON_OBJ(i
).name
, "name")){
120 if(!strcmp(&JSON_OBJ(i
).value
->u
.string
.ptr
[JSON_OBJ(i
).value
->u
.string
.length
-3], "’")){
121 strncpy(weather
->city
, JSON_OBJ(i
).value
->u
.string
.ptr
, JSON_OBJ(i
).value
->u
.string
.length
-3);
123 strcpy(weather
->city
, JSON_OBJ(i
).value
->u
.string
.ptr
);
126 if(!strcmp(JSON_OBJ(i
).name
, "weather")){
127 strcpy(weather
->weath_desc
, JSON_OBJ(i
).value
->u
.array
.values
[0]->u
.object
.values
[2].value
->u
.string
.ptr
);
128 strcpy(weather
->image_code
, JSON_OBJ(i
).value
->u
.array
.values
[0]->u
.object
.values
[3].value
->u
.string
.ptr
);
130 if(!strcmp(JSON_OBJ(i
).name
, "wind")){
131 weather
->wind_deg
= JSON_OBJ(i
).value
->u
.object
.values
[1].value
->u
.integer
;
132 if(JSON_OBJ(i
).value
->u
.object
.values
[0].value
->type
==json_double
)
134 weather
->wind_speed
= (int)JSON_OBJ(i
).value
->u
.object
.values
[0].value
->u
.dbl
;
136 weather
->wind_speed
= JSON_OBJ(i
).value
->u
.object
.values
[0].value
->u
.integer
;
139 if(!strcmp(JSON_OBJ(i
).name
, "visibility")){
140 weather
->visibility
= JSON_OBJ(i
).value
->u
.integer
;
142 if(!strcmp(JSON_OBJ(i
).name
, "timezone")){
143 weather
->timezone
= JSON_OBJ(i
).value
->u
.integer
/60/60;
145 if(!strcmp(JSON_OBJ(i
).name
, "message")){
146 char *errmsg
= safe_malloc(weather
->timezone
= JSON_OBJ(i
).value
->u
.string
.length
+6);
147 sprintf(errmsg
,"'%s!' -E", JSON_OBJ(i
).value
->u
.string
.ptr
);
154 http_msg
* get_json(char *city
, char *Token
, char* Units
)
156 sprintf(full_url
, API
, city
, Token
, Units
, lang
);
157 http_msg
*h
= http_get(full_url
, 0, HTTP_FLAG_BLOCK
, "");
158 http_long_receive(h
);
159 if (h
->status
== OK
|| h
->status
== 404) {
167 void get_image() // Функция загрузки изображения
169 sprintf(full_url_image
, IMAGE_URL
, myw
.image_code
);
170 http_msg
*h
= http_get(full_url_image
, 0, HTTP_FLAG_BLOCK
, "");
171 http_long_receive(h
);
173 if (h
->status
== OK
) {
174 Image
*image
= img_decode(h
->content_ptr
, h
->content_length
, 0); // Декодирование RAW данных в данные изображения
175 if (image
->Type
!= IMAGE_BPP32
) {
176 image
= img_convert(image
, NULL
, IMAGE_BPP32
, 0, 0); // Конвертируем картику в BPP32
178 notify_show("'Convetring image error!' -E");
182 blend
= img_create(64, 64, IMAGE_BPP32
); // Создаём фон для картинки
183 img_fill_color(blend
, 64, 64, sys_color_table
.work_area
); // Заливаем фон цветом окна
184 Image
* image2
= img_scale(image
, 0, 0, 50, 50, NULL
, LIBIMG_SCALE_STRETCH
, LIBIMG_INTER_BILINEAR
, 64, 64); // Растягиваем изображение
185 img_blend(blend
, image2
, 0, 0, 0, 0, 64, 64); // Смешиваем растянутую картинку и фон для получения прозрачности
186 img_destroy(image
); // Уничтожаем исходную картинку
187 img_destroy(image2
); // Уничтажаем растянутую картинку
189 notify_show("'Image not loaded!!' -W");
196 void redraw_gui() // Перересовываем интерфейс
198 _ksys_start_draw(); // Начинам прорисовку
200 int new_win_w
= (strlen(myw
.city
)/char_size
+12)*(UTF8_W
+char_size
-1); // Если название города не влезает в окно
201 if(new_win_w
<WINDOW_W
){
205 _ksys_create_window(win_pos
.x
, win_pos
.y
, new_win_w
, START_YPOS
+220, VERSION
, sys_color_table
.work_area
, 0x14);
206 // Выводим жирным шрифтом название локации и временной зоны
207 draw_format_text_sys(20, START_YPOS
, 0xB0000000 | sys_color_table
.work_text
, "%s (UTC%+d)", myw
.city
, myw
.timezone
);
208 draw_format_text_sys(21, START_YPOS
, 0xB0000000 | sys_color_table
.work_text
, "%s (UTC%+d)", myw
.city
, myw
.timezone
);
209 // Выводим изображение
210 img_draw(blend
, 10, START_YPOS
+30, 64,64,0,0);
211 // Выводим жирным шрифтом название локации и временной зоны
212 draw_format_text_sys(20, START_YPOS
+20, 0xb0000000 | sys_color_table
.work_text
, myw
.weath_desc
);
213 draw_format_text_sys(21, START_YPOS
+20, 0xb0000000 | sys_color_table
.work_text
, myw
.weath_desc
);
214 // Выводим жирным шрифтом название локации и временной зоны
215 draw_format_text_sys(100, START_YPOS
+45, 0xb1000000 | sys_color_table
.work_text
, format_temp_str
, myw
.temp
);
216 draw_format_text_sys(101, START_YPOS
+46, 0xb1000000 | sys_color_table
.work_text
, format_temp_str
, myw
.temp
);
217 // Выводим обычным шрифтом
218 draw_format_text_sys(20, START_YPOS
+80, 0xb0000000 | sys_color_table
.work_text
, pressure_str
,myw
.pressure
);
219 draw_format_text_sys(20, START_YPOS
+100, 0xb0000000 | sys_color_table
.work_text
, humidity_str
, myw
.humidity
, "%");
220 draw_format_text_sys(20, START_YPOS
+120, 0xb0000000 | sys_color_table
.work_text
, wind_speed_str
, myw
.wind_speed
);
221 draw_format_text_sys(20, START_YPOS
+140, 0xb0000000 | sys_color_table
.work_text
, wind_deg_str
, myw
.wind_deg
);
222 draw_format_text_sys(20, START_YPOS
+160, 0xb0000000 | sys_color_table
.work_text
, visibility_str
, myw
.visibility
);
224 _ksys_define_button(new_win_w
/2-60, START_YPOS
+180, 120, 30, BTN_UPDATE
, sys_color_table
.work_button
);
225 _ksys_draw_text(update_str
, (new_win_w
/2)-(UTF8_W
*strlen(update_str
)/2/char_size
), START_YPOS
+190, 0, 0xb0000000 | sys_color_table
.work_button_text
);
229 void get_config(char **city
, char **token
, char **units
) // Загружаем конфиг
231 ksys_ufile_t config_j
;
232 config_j
= _ksys_load_file(config1_name
);
234 config_j
= _ksys_load_file(config2_name
);
236 notify_show("'Configuration file not found!' -E");
241 json_value
* value
=json_parse (config_j
.data
, config_j
.size
); // Парсим конфиг
242 for(int i
=0; i
<value
->u
.object
.length
; i
++){
243 if(!strcmp(JSON_OBJ(i
).name
, "Location") && JSON_OBJ(i
).value
->type
==json_string
){
244 *city
= JSON_OBJ(i
).value
->u
.string
.ptr
; // Получаем название города
246 else if(!strcmp(JSON_OBJ(i
).name
, "Token") && JSON_OBJ(i
).value
->type
==json_string
){
247 *token
= JSON_OBJ(i
).value
->u
.string
.ptr
; // Получаем токен
249 else if(!strcmp(JSON_OBJ(i
).name
, "Celsius") && JSON_OBJ(i
).value
->type
==json_boolean
){
250 if(JSON_OBJ(i
).value
->u
.boolean
){
258 else if(!strcmp(JSON_OBJ(i
).name
, "Lang") && JSON_OBJ(i
).value
->type
==json_string
){
259 strncpy(lang
, JSON_OBJ(i
).value
->u
.string
.ptr
,2); // Получаем язык
261 else if(!strcmp(JSON_OBJ(i
).name
, "AutoUpdate") && JSON_OBJ(i
).value
->type
==json_integer
){
262 auto_update_time
= JSON_OBJ(i
).value
->u
.integer
; // Получаем время автообновлений данных
265 if(*city
==NULL
|| *token
==NULL
){
266 notify_show("'Invalid config!' -E");
272 void update(char* city
, char* token
, char* units
) // Обновление данных
275 img_destroy(blend
); // Уничтожение картинику с прозрачностью
278 memset(&myw
, 0, sizeof myw
); // Обнуляем структуру
279 strcpy(myw
.city
,"None");
280 strcpy(myw
.weath_desc
,"unknown");
281 http_msg
*json_file
= get_json(city
, token
, units
); // Получаем данные о погоде в формате json
282 if(json_file
!= NULL
){
283 json_value
* value
=json_parse(json_file
->content_ptr
, json_file
->content_length
); // Парсим json файл
284 find_and_set(value
, &myw
); // Ищем значения в json
285 sprintf(format_temp_str
, "%s°%c","%d",temp_char
); // Формируем строку для вывода температуры
286 get_image(); // Получаем картинку
287 json_value_free(value
); // Уничтожаем полученные json значения
288 http_free(json_file
);
290 notify_show("'Connection error!' -E");
296 if(!strcmp(lang
, "ru")){
297 wind_speed_str
= "Скорость ветра: %d м/с";
298 pressure_str
= "Давление: %d гПa";
299 visibility_str
= "Видимость: %d м";
300 humidity_str
= "Влажность: %d %s";
301 update_str
= "Обновить";
302 wind_deg_str
= "Направление ветра: %d°";
305 }else if(!strcmp(lang
, "de")){
306 wind_speed_str
= "Windgeschwindigkeit: %d m/s";
307 pressure_str
= "Druck: %d hPa";
308 visibility_str
= "Sichtbarkeit: %d m";
309 humidity_str
= "Luftfeuchtigkeit: %d %s";
310 wind_deg_str
= "Windrichtung %d°";
312 update_str
= "Aktualisieren";
314 pressure_str
= "Pressure: %d hPa";
315 humidity_str
= "Humidity: %d %s";
316 visibility_str
= "Visibility: %d m";
317 wind_speed_str
= "Wind speed: %d m/s";
318 wind_deg_str
= "Wind direction: %d°";
319 update_str
= "Refresh";
325 win_pos
= _ksys_get_mouse_pos(KSYS_MOUSE_SCREEN_POS
); // Получаем позицию курсора
326 _ksys_get_system_colors(&sys_color_table
); // Получаем таблица цветов
328 char *city
=NULL
, *token
=NULL
, *units
=NULL
; // Указатели на токен, название города, систему мер
330 get_config(&city
, &token
, &units
); // Загружаем конфиг
331 set_lang(); // Установить язык приложения
332 update(city
, token
, units
);
336 if(auto_update_time
<=0){
337 event
= _ksys_get_event
;
339 event
= _ksys_wait_event
;
343 switch(event(6000*auto_update_time
)){ // Получаем системное событие
344 case KSYS_EVENT_NONE
: // Нет события
345 update(city
, token
, units
);
346 debug_printf("Weather: Update\n");
348 case KSYS_EVENT_REDRAW
: // Событие перерисовки
351 case KSYS_EVENT_BUTTON
: // Событие кнопок
352 switch (_ksys_get_button()){
354 update(city
, token
, units
);
357 case BTN_QUIT
: // Кнопка выхода