Subversion Repositories Kolibri OS

Rev

Rev 8555 | Rev 8566 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /* Copyright (C) 2019-2021 Logaev Maxim (turbocat2001), GPLv2 */
  2.  
  3. /*
  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.     If you use UTF-8 encoding, then city names can be entered in different languages!
  8. */
  9.  
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include "json/json.h"
  13. #include <kos32sys1.h>
  14. #include <kolibrisys.h>
  15. #include <clayer/http.h>
  16. #include <clayer/libimg.h>
  17.  
  18. #define VERSION  "Weather 1.4"
  19.  
  20. enum BUTTONS{
  21.     BTN_QUIT = 1,
  22.     BTN_UPDATE = 2
  23. };
  24.  
  25. #define START_YPOS 34
  26. #define UTF8_W 8
  27. #define CP866_W 6
  28. #define JSON_OBJ(X) value->u.object.values[X]
  29. #define OK 200
  30.  
  31. unsigned WINDOW_W = 230;
  32.  
  33. #define API       "api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=%s&lang=%s"
  34. #define IMAGE_URL "openweathermap.org/img/w/%s.png"
  35.  
  36. Image *blend;
  37.  
  38. unsigned char char_size=1;
  39.  
  40. char *wind_speed_str, *pressure_str, *visibility_str, *humidity_str, *update_str, *wind_deg_str;
  41.        
  42. char lang[3]="en";
  43. char format_temp_str[6];
  44. char full_url[512];
  45. char full_url_image[256];
  46.  
  47. char temp_char='K';
  48.  
  49. struct kolibri_system_colors sys_color_table;
  50.  
  51. pos_t win_pos;
  52.  
  53. #pragma pack(push,1)
  54. struct open_weather_data{
  55.     char    City[100];
  56.     int     wind_speed;
  57.     int     wind_deg;
  58.     int     pressure;
  59.     int     humidity;
  60.     char    weath_desc[100];
  61.     int     visibility;
  62.     int     timezone;
  63.     char    image_code[4];
  64.     int     temp;
  65. }myw;
  66. #pragma pack(pop)
  67.  
  68. void notify_show(char *text)
  69. {
  70.    start_app("/sys/@notify", text);
  71. }
  72.  
  73. void* safe_malloc(size_t size)
  74. {
  75.     void *p=user_alloc(size);
  76.     if(p==NULL){
  77.        notify_show("'Memory allocation error!' -E");
  78.        exit(0);
  79.     }else{
  80.         return p;
  81.     }
  82. }
  83.  
  84. char tmp_buff[100];
  85.  
  86. void draw_format_text_sys(int x, int y, color_t color, const char *format_str, ... )
  87. {
  88.     va_list ap;
  89.     va_start (ap, format_str);
  90.     vsnprintf(tmp_buff, sizeof tmp_buff ,format_str, ap);
  91.     va_end(ap);
  92.     draw_text_sys(tmp_buff, x, y , 0, color);
  93. }
  94.  
  95. void find_and_set(json_value *value, struct open_weather_data* weather)
  96. {
  97.     for(int i=0; i<value->u.object.length; i++){
  98.         if(!strcmp(JSON_OBJ(i).name, "main")){
  99.             if(JSON_OBJ(i).value->u.object.values[0].value->type==json_double)
  100.             {
  101.                 weather->temp = (int)JSON_OBJ(i).value->u.object.values[0].value->u.dbl;
  102.             }else{
  103.                 weather->temp = JSON_OBJ(i).value->u.object.values[0].value->u.integer;
  104.             }
  105.             weather->pressure = JSON_OBJ(i).value->u.object.values[4].value->u.integer;
  106.             weather->humidity = JSON_OBJ(i).value->u.object.values[5].value->u.integer;
  107.         }
  108.         if(!strcmp(JSON_OBJ(i).name, "name")){
  109.             strcpy(weather->City,JSON_OBJ(i).value->u.string.ptr);
  110.         }
  111.         if(!strcmp(JSON_OBJ(i).name, "weather")){
  112.            strcpy(weather->weath_desc, JSON_OBJ(i).value->u.array.values[0]->u.object.values[2].value->u.string.ptr);
  113.            strcpy(weather->image_code, JSON_OBJ(i).value->u.array.values[0]->u.object.values[3].value->u.string.ptr);
  114.         }
  115.         if(!strcmp(JSON_OBJ(i).name, "wind")){
  116.             weather->wind_deg = JSON_OBJ(i).value->u.object.values[1].value->u.integer;
  117.             if(JSON_OBJ(i).value->u.object.values[0].value->type==json_double)
  118.             {
  119.                 weather->wind_speed = (int)JSON_OBJ(i).value->u.object.values[0].value->u.dbl;
  120.             }else{
  121.                 weather->wind_speed = JSON_OBJ(i).value->u.object.values[0].value->u.integer;
  122.             }  
  123.         }
  124.         if(!strcmp(JSON_OBJ(i).name, "visibility")){
  125.             weather->visibility = JSON_OBJ(i).value->u.integer;
  126.         }
  127.         if(!strcmp(JSON_OBJ(i).name, "timezone")){
  128.             weather->timezone = JSON_OBJ(i).value->u.integer/60/60;
  129.         }
  130.         if(!strcmp(JSON_OBJ(i).name, "message")){
  131.             char *errmsg = safe_malloc(weather->timezone = JSON_OBJ(i).value->u.string.length+6);
  132.             sprintf(errmsg,"'%s!' -E", JSON_OBJ(i).value->u.string.ptr);
  133.             notify_show(errmsg);
  134.             user_free(errmsg);
  135.         }
  136.     }
  137. }
  138.  
  139. http_msg* get_json(char *City, char *Token, char* Units)
  140. {
  141.     sprintf(full_url, API, City, Token, Units, lang);
  142.     http_msg *h = http_get(full_url, 0,  HTTP_FLAG_BLOCK, "");
  143.     http_long_receive(h);
  144.     if (h->status == OK || h->status == 404) {
  145.         return h;
  146.     } else {
  147.         user_free(h->content_ptr);
  148.         user_free(h);
  149.         return NULL;
  150.     }
  151. }
  152.  
  153. void get_image(){  // Функция загрузки изображения
  154.     sprintf(full_url_image, IMAGE_URL, myw.image_code);
  155.     http_msg *h= http_get(full_url_image, 0,  HTTP_FLAG_BLOCK, "");
  156.     http_long_receive(h);
  157.    
  158.     if (h->status == OK) {
  159.         Image *image = img_decode(h->content_ptr, h->content_length, 0); // Decode RAW data to Image data
  160.         if (image->Type != IMAGE_BPP32) {
  161.             image = img_convert(image, NULL, IMAGE_BPP32, 0, 0); // Convert image to format BPP32
  162.                 if (!image) {
  163.                 notify_show("'Convetring image error!' -E");  
  164.                 exit(0);
  165.             }
  166.         }
  167.         blend = img_create(64, 64, IMAGE_BPP32);  // Create an empty layer
  168.         img_fill_color(blend, 64, 64, sys_color_table.work_area); // Fill the layer with one color
  169.         Image* image2 = img_scale(image, 0, 0, 50, 50, NULL, LIBIMG_SCALE_STRETCH , LIBIMG_INTER_BILINEAR, 64, 64);
  170.         img_blend(blend, image2, 0, 0, 0, 0, 64, 64);  // Blending images to display the alpha channel.
  171.         // Уничтожаем ненужные структуры изображений
  172.         img_destroy(image);
  173.         img_destroy(image2);
  174.     }else{
  175.        notify_show("'Image not loaded!!' -W");
  176.     }
  177.     user_free(h->content_ptr);
  178.     user_free(h);
  179.     h=NULL;
  180. }
  181.  
  182. void RedrawGUI() // Перересовываем интерфейс
  183. {
  184.     begin_draw();   // Начинам прорисовку
  185.    
  186.     int new_win_w = (strlen(myw.City)/char_size+10)*(UTF8_W+char_size-1); // Если название города не влезает
  187.     if(new_win_w<WINDOW_W){
  188.         new_win_w=WINDOW_W;
  189.     }
  190.     // Рисуем окно
  191.     sys_create_window(win_pos.x, win_pos.y, new_win_w, START_YPOS+220, VERSION, sys_color_table.work_area, 0x14);
  192.     // Выводим жирным шрифтом название локации и временной зоны
  193.     draw_format_text_sys(20, START_YPOS, 0xB0000000 | sys_color_table.work_text, "%s (UTC%+d)", myw.City, myw.timezone);
  194.     draw_format_text_sys(21, START_YPOS, 0xB0000000 | sys_color_table.work_text, "%s (UTC%+d)", myw.City, myw.timezone);
  195.     // Выводим изображение
  196.     img_draw(blend, 10, START_YPOS+30, 64,64,0,0);
  197.     // Выводим жирным шрифтом название локации и временной зоны
  198.     draw_format_text_sys(20, START_YPOS+20, 0xb0000000 | sys_color_table.work_text, myw.weath_desc);
  199.     draw_format_text_sys(21, START_YPOS+20, 0xb0000000 | sys_color_table.work_text, myw.weath_desc);
  200.     // Выводим жирным шрифтом название локации и временной зоны
  201.     draw_format_text_sys(100, START_YPOS+45, 0xb1000000 | sys_color_table.work_text, format_temp_str, myw.temp);  
  202.     draw_format_text_sys(101, START_YPOS+46, 0xb1000000 | sys_color_table.work_text, format_temp_str, myw.temp);
  203.     // Выводим обычным шрифтом
  204.     draw_format_text_sys(20, START_YPOS+80,  0xb0000000 | sys_color_table.work_text, pressure_str,myw.pressure);
  205.     draw_format_text_sys(20, START_YPOS+100, 0xb0000000 | sys_color_table.work_text, humidity_str, myw.humidity, "%");
  206.     draw_format_text_sys(20, START_YPOS+120, 0xb0000000 | sys_color_table.work_text, wind_speed_str, myw.wind_speed);
  207.     draw_format_text_sys(20, START_YPOS+140, 0xb0000000 | sys_color_table.work_text, wind_deg_str, myw.wind_deg);
  208.     draw_format_text_sys(20, START_YPOS+160, 0xb0000000 | sys_color_table.work_text, visibility_str, myw.visibility);
  209.     // Определяем кнопку
  210.     define_button(X_W(new_win_w/2-60,120), Y_H(START_YPOS+180,30), BTN_UPDATE, sys_color_table.work_button);
  211.     draw_text_sys(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);
  212.     end_draw();
  213. }
  214.  
  215. void get_config(char **City, char **Token, char **Units) // Загружаем конфиг
  216. {
  217.     FILE *config_j = fopen("weather.json", "rb");
  218.     if(config_j==NULL){
  219.         notify_show("'Configuration file not found!' -E");
  220.         exit(0);
  221.     }
  222.     size_t size = _ksys_get_filesize("weather.json"); // Получаем размер файла
  223.     char *config_buff = safe_malloc(size+1);
  224.     if(size != fread(config_buff, sizeof(char), size, config_j)){
  225.         notify_show("'The configuration file was not fully read!' -E");
  226.         exit(0);    
  227.     }
  228.     json_value* value =json_parse (config_buff, size); // Парсим конфиг
  229.     for(int i=0; i<value->u.object.length; i++){
  230.         if(!strcmp(JSON_OBJ(i).name, "Location")){  
  231.             *City = JSON_OBJ(i).value->u.string.ptr;  // Получаем название города
  232.         }
  233.         if(!strcmp(JSON_OBJ(i).name, "Token")){
  234.             *Token = JSON_OBJ(i).value->u.string.ptr; // Получаем токен
  235.         }
  236.         if(!strcmp(JSON_OBJ(i).name, "Celsius")){
  237.             if(JSON_OBJ(i).value->u.boolean){
  238.                 *Units = "metric";
  239.                 temp_char = 'C';
  240.             }else{
  241.                 *Units = "imperial";
  242.                 temp_char = 'F';
  243.             }
  244.         }
  245.         if(!strcmp(JSON_OBJ(i).name, "Lang")){
  246.             strncpy(lang, JSON_OBJ(i).value->u.string.ptr,2); // Получам язык
  247.         }
  248.     }
  249.     if(*City==NULL || *Token ==NULL){
  250.          notify_show("'Invalid config!' -E");
  251.          exit(0);
  252.     }
  253.     user_free(config_buff);
  254.     fclose(config_j);
  255. }
  256.  
  257. void Update(char* city, char* token, char* units) // Обновление данных
  258. {
  259.     if(blend!=NULL){
  260.         img_destroy(blend); // Уничтожение картинки с прозрачностью
  261.         blend = NULL;
  262.     }
  263.     memset(&myw, 0, sizeof myw); // Обнуляем структуру
  264.     strcpy(myw.City,"None");
  265.     strcpy(myw.weath_desc,"unknown");
  266.     http_msg *json_file = get_json(city, token, units); // Получаем данные о погоде в формате json
  267.     if(json_file != NULL){
  268.         json_value* value=json_parse (json_file->content_ptr, json_file->content_length); // Парсим json файл
  269.         find_and_set(value, &myw);  //  Ищем значения в json
  270.         sprintf(format_temp_str, "%s°%c","%d",temp_char); // Формируем строку для вывода температуры
  271.         get_image(); // Получаем изображение
  272.         json_value_free(value); // Очищаем  ненужные данные
  273.         user_free(json_file->content_ptr);
  274.         user_free(json_file);
  275.     }else{
  276.        notify_show("'Connection error!' -E");
  277.     }
  278. }
  279.  
  280. void set_lang()
  281. {
  282.     if(!strcmp(lang, "ru")){
  283.         wind_speed_str = "Скорость ветра:    %d м/с";
  284.         pressure_str   = "Давление:          %d гПa";
  285.         visibility_str = "Видимость:         %d м";
  286.         humidity_str   = "Влажность:         %d %s";
  287.         update_str     = "Обновить";
  288.         wind_deg_str   = "Направление ветра: %d°";
  289.         WINDOW_W = 250;
  290.         char_size = 2;
  291.     }else if(!strcmp(lang, "de")){
  292.         wind_speed_str = "Windgeschwindigkeit: %d m/s";
  293.         pressure_str   = "Druck:               %d hPa";
  294.         visibility_str = "Sichtbarkeit:        %d m";
  295.         humidity_str   = "Luftfeuchtigkeit:    %d %s";
  296.         wind_deg_str   = "Windrichtung         %d°";
  297.         WINDOW_W = 270;
  298.         update_str     = "Aktualisieren";
  299.     }else{
  300.         pressure_str   = "Pressure:       %d hPa";
  301.         humidity_str   = "Humidity:       %d %s";
  302.         visibility_str = "Visibility:     %d m";
  303.         wind_speed_str = "Wind speed:     %d m/s";
  304.         wind_deg_str   = "Wind direction: %d°";
  305.         update_str     = "Refresh";
  306.     }
  307. }
  308.  
  309. int main(){
  310.     win_pos = get_mouse_pos(0); // Получаем позицию курсора
  311.     if(!kolibri_libimg_init()){ // Загружаем libimg.obj
  312.         notify_show("Libimg.obj not loaded!' -E");  
  313.         exit(0);
  314.     }
  315.     get_system_colors(&sys_color_table); // Получаем таблица цветов
  316.  
  317.     char *City, *Token, *Units; // Указатели на токен, название города, систему мер
  318.  
  319.     get_config(&City, &Token, &Units); // Загружаем конфиг
  320.     set_lang();  // Установить язык приложения
  321.     Update(City,Token, Units);  // Обновить данные
  322.  
  323.     while(1){
  324.         switch(get_os_event()){ // Получаем системное событие
  325.             case KOLIBRI_EVENT_NONE:    // Нет события
  326.                 break;
  327.             case KOLIBRI_EVENT_REDRAW:  // Событие перерисовки
  328.                 RedrawGUI();
  329.                 break;        
  330.             case KOLIBRI_EVENT_BUTTON:  // Событие кнопок
  331.                 switch (get_os_button()){
  332.                     case BTN_UPDATE:    // Кнопка обновить
  333.                         Update(City, Token, Units);
  334.                         RedrawGUI();
  335.                         break;
  336.                     case BTN_QUIT:      // Кнопка выхода
  337.                         exit(0);
  338.                         break;
  339.                 }
  340.         }
  341.     }
  342.     return 0;
  343. }
  344.