Просмотр исходного кода

Merge flip_weather from https://github.com/jblanked/FlipWeather

Willy-JL 1 год назад
Родитель
Сommit
11af7ad031

BIN
flip_weather/.DS_Store


+ 2 - 2
flip_weather/README.md

@@ -39,5 +39,5 @@ FlipWeather automatically allocates necessary resources and initializes settings
 
 
 ## Known Issues
 ## Known Issues
 
 
-1. **GPS Screen Delay**: Occasionally, the GPS screen may get stuck on "Loading Data" or take up to 30 seconds to display information.
-   - **Solution**: Restart your Flipper Zero if this occurs.
+1. **GPS Screen Delay**: Occasionally, the GPS or Weather screen may get stuck on "Loading Data".
+   - If it takes longer than 10 seconds, restart your Flipper Zero.

+ 19 - 5
flip_weather/flip_weather_i.h → flip_weather/alloc/flip_weather_alloc.c

@@ -1,8 +1,7 @@
-#ifndef FLIP_WEATHER_I_H
-#define FLIP_WEATHER_I_H
+#include <alloc/flip_weather_alloc.h>
 
 
 // Function to allocate resources for the FlipWeatherApp
 // Function to allocate resources for the FlipWeatherApp
-static FlipWeatherApp* flip_weather_app_alloc() {
+FlipWeatherApp* flip_weather_app_alloc() {
     FlipWeatherApp* app = (FlipWeatherApp*)malloc(sizeof(FlipWeatherApp));
     FlipWeatherApp* app = (FlipWeatherApp*)malloc(sizeof(FlipWeatherApp));
 
 
     Gui* gui = furi_record_open(RECORD_GUI);
     Gui* gui = furi_record_open(RECORD_GUI);
@@ -163,10 +162,25 @@ static FlipWeatherApp* flip_weather_app_alloc() {
         }
         }
     }
     }
 
 
+    // Popup
+    if(!easy_flipper_set_popup(
+           &app->popup_error,
+           FlipWeatherViewPopupError,
+           "[ERROR]",
+           0,
+           0,
+           "Wifi Dev Board disconnected.\nPlease connect to the board.\nIf your board is connected,\nmake sure you have flashed\nyour WiFi Devboard with the\nlatest FlipperHTTP flash.",
+           0,
+           7,
+           flip_weather_popup_callback,
+           callback_to_submenu,
+           &app->view_dispatcher,
+           app)) {
+        return NULL;
+    }
+
     // Switch to the main view
     // Switch to the main view
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewSubmenu);
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewSubmenu);
 
 
     return app;
     return app;
 }
 }
-
-#endif

+ 8 - 0
flip_weather/alloc/flip_weather_alloc.h

@@ -0,0 +1,8 @@
+#ifndef FLIP_WEATHER_I_H
+#define FLIP_WEATHER_I_H
+#include <flip_weather.h>
+#include <callback/flip_weather_callback.h>
+#include <flip_storage/flip_weather_storage.h>
+FlipWeatherApp* flip_weather_app_alloc();
+
+#endif

+ 2 - 6
flip_weather/app.c

@@ -1,9 +1,5 @@
-#include <flip_weather_e.h>
-#include <flip_weather_storage.h>
-#include "flip_weather_parse.h"
-#include <flip_weather_callback.h>
-#include <flip_weather_i.h>
-#include <flip_weather_free.h>
+#include <flip_weather.h>
+#include <alloc/flip_weather_alloc.h>
 
 
 // Entry point for the FlipWeather application
 // Entry point for the FlipWeather application
 int32_t flip_weather_app(void* p) {
 int32_t flip_weather_app(void* p) {

BIN
flip_weather/assets/01-home.png


+ 56 - 46
flip_weather/flip_weather_callback.h → flip_weather/callback/flip_weather_callback.c

@@ -1,44 +1,53 @@
-#ifndef FLIP_WEATHER_CALLBACK_H
-#define FLIP_WEATHER_CALLBACK_H
+#include "callback/flip_weather_callback.h"
 
 
-static bool weather_request_success = false;
-static bool sent_weather_request = false;
-static bool got_weather_data = false;
+bool weather_request_success = false;
+bool sent_weather_request = false;
+bool got_weather_data = false;
+
+void flip_weather_popup_callback(void* context) {
+    FlipWeatherApp* app = (FlipWeatherApp*)context;
+    if(!app) {
+        FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
+        return;
+    }
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewSubmenu);
+}
 
 
 void flip_weather_request_error(Canvas* canvas) {
 void flip_weather_request_error(Canvas* canvas) {
-    if(fhttp.last_response == NULL) {
-        if(fhttp.last_response != NULL) {
-            if(strstr(fhttp.last_response, "[ERROR] Not connected to Wifi. Failed to reconnect.") !=
-               NULL) {
-                canvas_clear(canvas);
-                canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
-                canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
-                canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
-            } else if(strstr(fhttp.last_response, "[ERROR] Failed to connect to Wifi.") != NULL) {
-                canvas_clear(canvas);
-                canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
-                canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
-                canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
-            } else {
-                canvas_clear(canvas);
-                FURI_LOG_E(TAG, "Received an error: %s", fhttp.last_response);
-                canvas_draw_str(canvas, 0, 10, "[ERROR] Unusual error...");
-                canvas_draw_str(canvas, 0, 60, "Press BACK and retry.");
-            }
-        } else {
+    if(fhttp.last_response != NULL) {
+        if(strstr(fhttp.last_response, "[ERROR] Not connected to Wifi. Failed to reconnect.") !=
+           NULL) {
             canvas_clear(canvas);
             canvas_clear(canvas);
-            canvas_draw_str(canvas, 0, 10, "[ERROR] Unknown error.");
+            canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
             canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
             canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
             canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
             canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
+        } else if(strstr(fhttp.last_response, "[ERROR] Failed to connect to Wifi.") != NULL) {
+            canvas_clear(canvas);
+            canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
+            canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
+            canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
+        } else if(
+            strstr(fhttp.last_response, "[ERROR] GET request failed or returned empty data.") !=
+            NULL) {
+            canvas_clear(canvas);
+            canvas_draw_str(canvas, 0, 10, "[ERROR] WiFi error.");
+            canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
+            canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
+        } else {
+            canvas_clear(canvas);
+            FURI_LOG_E(TAG, "Received an error: %s", fhttp.last_response);
+            canvas_draw_str(canvas, 0, 10, "[ERROR] Unusual error...");
+            canvas_draw_str(canvas, 0, 60, "Press BACK and retry.");
         }
         }
     } else {
     } else {
         canvas_clear(canvas);
         canvas_clear(canvas);
-        canvas_draw_str(canvas, 0, 10, "Failed to receive data.");
+        canvas_draw_str(canvas, 0, 10, "[ERROR] Unknown error.");
+        canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
         canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
         canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
     }
     }
 }
 }
 
 
-static void flip_weather_handle_gps_draw(Canvas* canvas, bool show_gps_data) {
+void flip_weather_handle_gps_draw(Canvas* canvas, bool show_gps_data) {
     if(sent_get_request) {
     if(sent_get_request) {
         if(fhttp.state == RECEIVING) {
         if(fhttp.state == RECEIVING) {
             if(show_gps_data) {
             if(show_gps_data) {
@@ -68,7 +77,7 @@ static void flip_weather_handle_gps_draw(Canvas* canvas, bool show_gps_data) {
 }
 }
 
 
 // Callback for drawing the weather screen
 // Callback for drawing the weather screen
-static void flip_weather_view_draw_callback_weather(Canvas* canvas, void* model) {
+void flip_weather_view_draw_callback_weather(Canvas* canvas, void* model) {
     if(!canvas) {
     if(!canvas) {
         return;
         return;
     }
     }
@@ -90,7 +99,7 @@ static void flip_weather_view_draw_callback_weather(Canvas* canvas, void* model)
     // handle geo location until it's processed and then handle weather
     // handle geo location until it's processed and then handle weather
 
 
     // start the process
     // start the process
-    if(!send_geo_location_request()) {
+    if(!send_geo_location_request() || fhttp.state == ISSUE) {
         flip_weather_request_error(canvas);
         flip_weather_request_error(canvas);
     }
     }
     // wait until geo location is processed
     // wait until geo location is processed
@@ -107,16 +116,18 @@ static void flip_weather_view_draw_callback_weather(Canvas* canvas, void* model)
         char url[512];
         char url[512];
         char* lattitude = lat_data + 10;
         char* lattitude = lat_data + 10;
         char* longitude = lon_data + 11;
         char* longitude = lon_data + 11;
+        char* headers = jsmn("Content-Type", "application/json");
         snprintf(
         snprintf(
             url,
             url,
             512,
             512,
             "https://api.open-meteo.com/v1/forecast?latitude=%s&longitude=%s&current=temperature_2m,precipitation,rain,showers,snowfall&temperature_unit=celsius&wind_speed_unit=mph&precipitation_unit=inch&forecast_days=1",
             "https://api.open-meteo.com/v1/forecast?latitude=%s&longitude=%s&current=temperature_2m,precipitation,rain,showers,snowfall&temperature_unit=celsius&wind_speed_unit=mph&precipitation_unit=inch&forecast_days=1",
             lattitude,
             lattitude,
             longitude);
             longitude);
-        weather_request_success =
-            flipper_http_get_request_with_headers(url, "{\"Content-Type\": \"application/json\"}");
+        weather_request_success = flipper_http_get_request_with_headers(url, headers);
+        free(headers);
         if(!weather_request_success) {
         if(!weather_request_success) {
             FURI_LOG_E(TAG, "Failed to send GET request");
             FURI_LOG_E(TAG, "Failed to send GET request");
+            fhttp.state = ISSUE;
             flip_weather_request_error(canvas);
             flip_weather_request_error(canvas);
         }
         }
         fhttp.state = RECEIVING;
         fhttp.state = RECEIVING;
@@ -126,8 +137,9 @@ static void flip_weather_view_draw_callback_weather(Canvas* canvas, void* model)
             canvas_draw_str(canvas, 0, 22, "Receiving...");
             canvas_draw_str(canvas, 0, 22, "Receiving...");
             return;
             return;
         }
         }
+
         // check status
         // check status
-        else if(fhttp.state == ISSUE || !weather_request_success || fhttp.last_response == NULL) {
+        if(fhttp.state == ISSUE || !weather_request_success || fhttp.last_response == NULL) {
             flip_weather_request_error(canvas);
             flip_weather_request_error(canvas);
         } else {
         } else {
             // success, draw weather
             // success, draw weather
@@ -144,7 +156,7 @@ static void flip_weather_view_draw_callback_weather(Canvas* canvas, void* model)
 }
 }
 
 
 // Callback for drawing the GPS screen
 // Callback for drawing the GPS screen
-static void flip_weather_view_draw_callback_gps(Canvas* canvas, void* model) {
+void flip_weather_view_draw_callback_gps(Canvas* canvas, void* model) {
     if(!canvas) {
     if(!canvas) {
         return;
         return;
     }
     }
@@ -161,14 +173,14 @@ static void flip_weather_view_draw_callback_gps(Canvas* canvas, void* model) {
         return;
         return;
     }
     }
 
 
-    if(!send_geo_location_request()) {
+    if(!send_geo_location_request() || fhttp.state == ISSUE) {
         flip_weather_request_error(canvas);
         flip_weather_request_error(canvas);
     }
     }
 
 
     flip_weather_handle_gps_draw(canvas, true);
     flip_weather_handle_gps_draw(canvas, true);
 }
 }
 
 
-static void callback_submenu_choices(void* context, uint32_t index) {
+void callback_submenu_choices(void* context, uint32_t index) {
     FlipWeatherApp* app = (FlipWeatherApp*)context;
     FlipWeatherApp* app = (FlipWeatherApp*)context;
     if(!app) {
     if(!app) {
         FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
         FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
@@ -177,13 +189,13 @@ static void callback_submenu_choices(void* context, uint32_t index) {
     switch(index) {
     switch(index) {
     case FlipWeatherSubmenuIndexWeather:
     case FlipWeatherSubmenuIndexWeather:
         if(!flip_weather_handle_ip_address()) {
         if(!flip_weather_handle_ip_address()) {
-            return;
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewPopupError);
         }
         }
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewWeather);
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewWeather);
         break;
         break;
     case FlipWeatherSubmenuIndexGPS:
     case FlipWeatherSubmenuIndexGPS:
         if(!flip_weather_handle_ip_address()) {
         if(!flip_weather_handle_ip_address()) {
-            return;
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewPopupError);
         }
         }
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewGPS);
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewGPS);
         break;
         break;
@@ -198,7 +210,7 @@ static void callback_submenu_choices(void* context, uint32_t index) {
     }
     }
 }
 }
 
 
-static void text_updated_ssid(void* context) {
+void text_updated_ssid(void* context) {
     FlipWeatherApp* app = (FlipWeatherApp*)context;
     FlipWeatherApp* app = (FlipWeatherApp*)context;
     if(!app) {
     if(!app) {
         FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
         FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
@@ -236,7 +248,7 @@ static void text_updated_ssid(void* context) {
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewSettings);
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewSettings);
 }
 }
 
 
-static void text_updated_password(void* context) {
+void text_updated_password(void* context) {
     FlipWeatherApp* app = (FlipWeatherApp*)context;
     FlipWeatherApp* app = (FlipWeatherApp*)context;
     if(!app) {
     if(!app) {
         FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
         FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
@@ -274,7 +286,7 @@ static void text_updated_password(void* context) {
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewSettings);
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewSettings);
 }
 }
 
 
-static uint32_t callback_to_submenu(void* context) {
+uint32_t callback_to_submenu(void* context) {
     if(!context) {
     if(!context) {
         FURI_LOG_E(TAG, "Context is NULL");
         FURI_LOG_E(TAG, "Context is NULL");
         return VIEW_NONE;
         return VIEW_NONE;
@@ -291,7 +303,7 @@ static uint32_t callback_to_submenu(void* context) {
     return FlipWeatherViewSubmenu;
     return FlipWeatherViewSubmenu;
 }
 }
 
 
-static void settings_item_selected(void* context, uint32_t index) {
+void settings_item_selected(void* context, uint32_t index) {
     FlipWeatherApp* app = (FlipWeatherApp*)context;
     FlipWeatherApp* app = (FlipWeatherApp*)context;
     if(!app) {
     if(!app) {
         FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
         FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
@@ -315,7 +327,7 @@ static void settings_item_selected(void* context, uint32_t index) {
  * @param context The context - unused
  * @param context The context - unused
  * @return next view id (VIEW_NONE to exit the app)
  * @return next view id (VIEW_NONE to exit the app)
  */
  */
-static uint32_t callback_exit_app(void* context) {
+uint32_t callback_exit_app(void* context) {
     // Exit the application
     // Exit the application
     if(!context) {
     if(!context) {
         FURI_LOG_E(TAG, "Context is NULL");
         FURI_LOG_E(TAG, "Context is NULL");
@@ -325,7 +337,7 @@ static uint32_t callback_exit_app(void* context) {
     return VIEW_NONE; // Return VIEW_NONE to exit the app
     return VIEW_NONE; // Return VIEW_NONE to exit the app
 }
 }
 
 
-static uint32_t callback_to_wifi_settings(void* context) {
+uint32_t callback_to_wifi_settings(void* context) {
     if(!context) {
     if(!context) {
         FURI_LOG_E(TAG, "Context is NULL");
         FURI_LOG_E(TAG, "Context is NULL");
         return VIEW_NONE;
         return VIEW_NONE;
@@ -333,5 +345,3 @@ static uint32_t callback_to_wifi_settings(void* context) {
     UNUSED(context);
     UNUSED(context);
     return FlipWeatherViewSettings;
     return FlipWeatherViewSettings;
 }
 }
-
-#endif

+ 30 - 0
flip_weather/callback/flip_weather_callback.h

@@ -0,0 +1,30 @@
+#ifndef FLIP_WEATHER_CALLBACK_H
+#define FLIP_WEATHER_CALLBACK_H
+
+#include "flip_weather.h"
+#include <parse/flip_weather_parse.h>
+#include <flip_storage/flip_weather_storage.h>
+
+extern bool weather_request_success;
+extern bool sent_weather_request;
+extern bool got_weather_data;
+
+void flip_weather_popup_callback(void* context);
+void flip_weather_request_error(Canvas* canvas);
+void flip_weather_handle_gps_draw(Canvas* canvas, bool show_gps_data);
+void flip_weather_view_draw_callback_weather(Canvas* canvas, void* model);
+void flip_weather_view_draw_callback_gps(Canvas* canvas, void* model);
+void callback_submenu_choices(void* context, uint32_t index);
+void text_updated_ssid(void* context);
+void text_updated_password(void* context);
+uint32_t callback_to_submenu(void* context);
+void settings_item_selected(void* context, uint32_t index);
+
+/**
+ * @brief Navigation callback for exiting the application
+ * @param context The context - unused
+ * @return next view id (VIEW_NONE to exit the app)
+ */
+uint32_t callback_exit_app(void* context);
+uint32_t callback_to_wifi_settings(void* context);
+#endif

+ 1 - 23
flip_weather/easy_flipper.h → flip_weather/easy_flipper/easy_flipper.c

@@ -1,24 +1,4 @@
-#ifndef EASY_FLIPPER_H
-#define EASY_FLIPPER_H
-
-#include <malloc.h>
-#include <furi.h>
-#include <furi_hal.h>
-#include <gui/gui.h>
-#include <gui/view.h>
-#include <gui/modules/submenu.h>
-#include <gui/view_dispatcher.h>
-#include <gui/modules/menu.h>
-#include <gui/modules/submenu.h>
-#include <gui/modules/widget.h>
-#include <gui/modules/text_input.h>
-#include <gui/modules/text_box.h>
-#include <gui/modules/variable_item_list.h>
-#include <gui/modules/dialog_ex.h>
-#include <gui/modules/popup.h>
-#include <gui/modules/loading.h>
-
-#define EASY_TAG "EasyFlipper"
+#include <easy_flipper/easy_flipper.h>
 
 
 /**
 /**
  * @brief Navigation callback for exiting the application
  * @brief Navigation callback for exiting the application
@@ -530,5 +510,3 @@ bool easy_flipper_set_char_to_furi_string(FuriString** furi_string, char* buffer
     furi_string_set_str(*furi_string, buffer);
     furi_string_set_str(*furi_string, buffer);
     return true;
     return true;
 }
 }
-
-#endif // EASY_FLIPPER_H

+ 261 - 0
flip_weather/easy_flipper/easy_flipper.h

@@ -0,0 +1,261 @@
+#ifndef EASY_FLIPPER_H
+#define EASY_FLIPPER_H
+
+#include <malloc.h>
+#include <furi.h>
+#include <furi_hal.h>
+#include <gui/gui.h>
+#include <gui/view.h>
+#include <gui/modules/submenu.h>
+#include <gui/view_dispatcher.h>
+#include <gui/modules/menu.h>
+#include <gui/modules/submenu.h>
+#include <gui/modules/widget.h>
+#include <gui/modules/text_input.h>
+#include <gui/modules/text_box.h>
+#include <gui/modules/variable_item_list.h>
+#include <gui/modules/dialog_ex.h>
+#include <gui/modules/popup.h>
+#include <gui/modules/loading.h>
+#include <stdio.h>
+#include <string.h>
+#include <jsmn/jsmn.h>
+
+#define EASY_TAG "EasyFlipper"
+
+/**
+ * @brief Navigation callback for exiting the application
+ * @param context The context - unused
+ * @return next view id (VIEW_NONE to exit the app)
+ */
+uint32_t easy_flipper_callback_exit_app(void* context);
+/**
+ * @brief Initialize a buffer
+ * @param buffer The buffer to initialize
+ * @param buffer_size The size of the buffer
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_buffer(char** buffer, uint32_t buffer_size);
+/**
+ * @brief Initialize a View object
+ * @param view The View object to initialize
+ * @param view_id The ID/Index of the view
+ * @param draw_callback The draw callback function (set to NULL if not needed)
+ * @param input_callback The input callback function (set to NULL if not needed)
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_view(
+    View** view,
+    int32_t view_id,
+    void draw_callback(Canvas*, void*),
+    bool input_callback(InputEvent*, void*),
+    uint32_t (*previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a ViewDispatcher object
+ * @param view_dispatcher The ViewDispatcher object to initialize
+ * @param gui The GUI object
+ * @param context The context to pass to the event callback
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_view_dispatcher(ViewDispatcher** view_dispatcher, Gui* gui, void* context);
+
+/**
+ * @brief Initialize a Submenu object
+ * @note This does not set the items in the submenu
+ * @param submenu The Submenu object to initialize
+ * @param view_id The ID/Index of the view
+ * @param title The title/header of the submenu
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_submenu(
+    Submenu** submenu,
+    int32_t view_id,
+    char* title,
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher);
+
+/**
+ * @brief Initialize a Menu object
+ * @note This does not set the items in the menu
+ * @param menu The Menu object to initialize
+ * @param view_id The ID/Index of the view
+ * @param item_callback The item callback function
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_menu(
+    Menu** menu,
+    int32_t view_id,
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher);
+
+/**
+ * @brief Initialize a Widget object
+ * @param widget The Widget object to initialize
+ * @param view_id The ID/Index of the view
+ * @param text The text to display in the widget
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_widget(
+    Widget** widget,
+    int32_t view_id,
+    char* text,
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher);
+
+/**
+ * @brief Initialize a VariableItemList object
+ * @note This does not set the items in the VariableItemList
+ * @param variable_item_list The VariableItemList object to initialize
+ * @param view_id The ID/Index of the view
+ * @param enter_callback The enter callback function (can be set to NULL)
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @param context The context to pass to the enter callback (usually the app)
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_variable_item_list(
+    VariableItemList** variable_item_list,
+    int32_t view_id,
+    void (*enter_callback)(void*, uint32_t),
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a TextInput object
+ * @param text_input The TextInput object to initialize
+ * @param view_id The ID/Index of the view
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_text_input(
+    TextInput** text_input,
+    int32_t view_id,
+    char* header_text,
+    char* text_input_temp_buffer,
+    uint32_t text_input_buffer_size,
+    void (*result_callback)(void*),
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a TextInput object with extra symbols
+ * @param uart_text_input The TextInput object to initialize
+ * @param view_id The ID/Index of the view
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_uart_text_input(
+    TextInput** uart_text_input,
+    int32_t view_id,
+    char* header_text,
+    char* uart_text_input_temp_buffer,
+    uint32_t uart_text_input_buffer_size,
+    void (*result_callback)(void*),
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a DialogEx object
+ * @param dialog_ex The DialogEx object to initialize
+ * @param view_id The ID/Index of the view
+ * @param header The header of the dialog
+ * @param header_x The x coordinate of the header
+ * @param header_y The y coordinate of the header
+ * @param text The text of the dialog
+ * @param text_x The x coordinate of the dialog
+ * @param text_y The y coordinate of the dialog
+ * @param left_button_text The text of the left button
+ * @param right_button_text The text of the right button
+ * @param center_button_text The text of the center button
+ * @param result_callback The result callback function
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @param context The context to pass to the result callback
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_dialog_ex(
+    DialogEx** dialog_ex,
+    int32_t view_id,
+    char* header,
+    uint16_t header_x,
+    uint16_t header_y,
+    char* text,
+    uint16_t text_x,
+    uint16_t text_y,
+    char* left_button_text,
+    char* right_button_text,
+    char* center_button_text,
+    void (*result_callback)(DialogExResult, void*),
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a Popup object
+ * @param popup The Popup object to initialize
+ * @param view_id The ID/Index of the view
+ * @param header The header of the dialog
+ * @param header_x The x coordinate of the header
+ * @param header_y The y coordinate of the header
+ * @param text The text of the dialog
+ * @param text_x The x coordinate of the dialog
+ * @param text_y The y coordinate of the dialog
+ * @param result_callback The result callback function
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @param context The context to pass to the result callback
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_popup(
+    Popup** popup,
+    int32_t view_id,
+    char* header,
+    uint16_t header_x,
+    uint16_t header_y,
+    char* text,
+    uint16_t text_x,
+    uint16_t text_y,
+    void (*result_callback)(void*),
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a Loading object
+ * @param loading The Loading object to initialize
+ * @param view_id The ID/Index of the view
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_loading(
+    Loading** loading,
+    int32_t view_id,
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher);
+
+/**
+ * @brief Set a char butter to a FuriString
+ * @param furi_string The FuriString object
+ * @param buffer The buffer to copy the string to
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_char_to_furi_string(FuriString** furi_string, char* buffer);
+
+#endif

+ 3 - 10
flip_weather/flip_weather_storage.h → flip_weather/flip_storage/flip_weather_storage.c

@@ -1,12 +1,7 @@
-#ifndef FLIP_WEATHER_STORAGE_H
-#define FLIP_WEATHER_STORAGE_H
 
 
-#include <furi.h>
-#include <storage/storage.h>
+#include "flip_storage/flip_weather_storage.h"
 
 
-#define SETTINGS_PATH STORAGE_EXT_PATH_PREFIX "/apps_data/flip_weather/settings.bin"
-
-static void save_settings(const char* ssid, const char* password) {
+void save_settings(const char* ssid, const char* password) {
     // Create the directory for saving settings
     // Create the directory for saving settings
     char directory_path[256];
     char directory_path[256];
     snprintf(
     snprintf(
@@ -44,7 +39,7 @@ static void save_settings(const char* ssid, const char* password) {
     furi_record_close(RECORD_STORAGE);
     furi_record_close(RECORD_STORAGE);
 }
 }
 
 
-static bool load_settings(char* ssid, size_t ssid_size, char* password, size_t password_size) {
+bool load_settings(char* ssid, size_t ssid_size, char* password, size_t password_size) {
     Storage* storage = furi_record_open(RECORD_STORAGE);
     Storage* storage = furi_record_open(RECORD_STORAGE);
     File* file = storage_file_alloc(storage);
     File* file = storage_file_alloc(storage);
 
 
@@ -86,5 +81,3 @@ static bool load_settings(char* ssid, size_t ssid_size, char* password, size_t p
 
 
     return true;
     return true;
 }
 }
-
-#endif // FLIP_WEATHER_STORAGE_H

+ 13 - 0
flip_weather/flip_storage/flip_weather_storage.h

@@ -0,0 +1,13 @@
+#ifndef FLIP_WEATHER_STORAGE_H
+#define FLIP_WEATHER_STORAGE_H
+
+#include <furi.h>
+#include <storage/storage.h>
+#include <flip_weather.h>
+
+#define SETTINGS_PATH STORAGE_EXT_PATH_PREFIX "/apps_data/flip_weather/settings.bin"
+
+void save_settings(const char* ssid, const char* password);
+
+bool load_settings(char* ssid, size_t ssid_size, char* password, size_t password_size);
+#endif // FLIP_WEATHER_STORAGE_H

+ 22 - 5
flip_weather/flip_weather_free.h → flip_weather/flip_weather.c

@@ -1,8 +1,21 @@
-#ifndef FLIP_WEATHER_FREE_H
-#define FLIP_WEATHER_FREE_H
+#include "flip_weather.h"
+
+char city_data[48];
+char region_data[48];
+char country_data[48];
+char lat_data[32];
+char lon_data[32];
+char ip_data[32];
+char temperature_data[32];
+char precipitation_data[32];
+char rain_data[32];
+char showers_data[32];
+char snowfall_data[32];
+char time_data[32];
+char ip_address[16];
 
 
 // Function to free the resources used by FlipWeatherApp
 // Function to free the resources used by FlipWeatherApp
-static void flip_weather_app_free(FlipWeatherApp* app) {
+void flip_weather_app_free(FlipWeatherApp* app) {
     if(!app) {
     if(!app) {
         FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
         FURI_LOG_E(TAG, "FlipWeatherApp is NULL");
         return;
         return;
@@ -46,6 +59,12 @@ static void flip_weather_app_free(FlipWeatherApp* app) {
         text_input_free(app->uart_text_input_password);
         text_input_free(app->uart_text_input_password);
     }
     }
 
 
+    // Free Popup(s)
+    if(app->popup_error) {
+        view_dispatcher_remove_view(app->view_dispatcher, FlipWeatherViewPopupError);
+        popup_free(app->popup_error);
+    }
+
     // Free the text input buffer
     // Free the text input buffer
     if(app->uart_text_input_buffer_ssid) free(app->uart_text_input_buffer_ssid);
     if(app->uart_text_input_buffer_ssid) free(app->uart_text_input_buffer_ssid);
     if(app->uart_text_input_temp_buffer_ssid) free(app->uart_text_input_temp_buffer_ssid);
     if(app->uart_text_input_temp_buffer_ssid) free(app->uart_text_input_temp_buffer_ssid);
@@ -64,5 +83,3 @@ static void flip_weather_app_free(FlipWeatherApp* app) {
     // free the app
     // free the app
     if(app) free(app);
     if(app) free(app);
 }
 }
-
-#endif // FLIP_WEATHER_FREE_H

+ 23 - 18
flip_weather/flip_weather_e.h → flip_weather/flip_weather.h

@@ -1,11 +1,12 @@
 #ifndef FLIP_WEATHER_E_H
 #ifndef FLIP_WEATHER_E_H
 #define FLIP_WEATHER_E_H
 #define FLIP_WEATHER_E_H
 
 
-#include <flipper_http.h>
-#include <easy_flipper.h>
-#include <jsmn.h>
+#include <flipper_http/flipper_http.h>
+#include <easy_flipper/easy_flipper.h>
+#include <jsmn/jsmn.h>
 
 
-#define TAG "FlipWeather"
+#define TAG        "FlipWeather"
+#define MAX_TOKENS 64 // Adjust based on expected JSON size (50)
 
 
 // Define the submenu items for our FlipWeather application
 // Define the submenu items for our FlipWeather application
 typedef enum {
 typedef enum {
@@ -24,6 +25,8 @@ typedef enum {
     FlipWeatherViewSettings, // The wifi settings screen
     FlipWeatherViewSettings, // The wifi settings screen
     FlipWeatherViewTextInputSSID, // The text input screen for SSID
     FlipWeatherViewTextInputSSID, // The text input screen for SSID
     FlipWeatherViewTextInputPassword, // The text input screen for password
     FlipWeatherViewTextInputPassword, // The text input screen for password
+    //
+    FlipWeatherViewPopupError, // The error popup screen
 } FlipWeatherView;
 } FlipWeatherView;
 
 
 // Each screen will have its own view
 // Each screen will have its own view
@@ -33,6 +36,7 @@ typedef struct {
     View* view_gps; // The GPS view
     View* view_gps; // The GPS view
     Submenu* submenu; // The main submenu
     Submenu* submenu; // The main submenu
     Widget* widget; // The widget (about)
     Widget* widget; // The widget (about)
+    Popup* popup_error; // The error popup
     VariableItemList* variable_item_list; // The variable item list (settngs)
     VariableItemList* variable_item_list; // The variable item list (settngs)
     VariableItem* variable_item_ssid; // The variable item
     VariableItem* variable_item_ssid; // The variable item
     VariableItem* variable_item_password; // The variable item
     VariableItem* variable_item_password; // The variable item
@@ -48,20 +52,21 @@ typedef struct {
     uint32_t uart_text_input_buffer_size_password; // Size of the text input buffer
     uint32_t uart_text_input_buffer_size_password; // Size of the text input buffer
 } FlipWeatherApp;
 } FlipWeatherApp;
 
 
-static char city_data[48];
-static char region_data[48];
-static char country_data[48];
-static char lat_data[32];
-static char lon_data[32];
-static char ip_data[32];
-static char temperature_data[32];
-static char precipitation_data[32];
-static char rain_data[32];
-static char showers_data[32];
-static char snowfall_data[32];
-static char time_data[32];
-static char ip_address[16];
+extern char city_data[48];
+extern char region_data[48];
+extern char country_data[48];
+extern char lat_data[32];
+extern char lon_data[32];
+extern char ip_data[32];
+extern char temperature_data[32];
+extern char precipitation_data[32];
+extern char rain_data[32];
+extern char showers_data[32];
+extern char snowfall_data[32];
+extern char time_data[32];
+extern char ip_address[16];
 
 
-#define MAX_TOKENS 64 // Adjust based on expected JSON size (50)
+// Function to free the resources used by FlipWeatherApp
+void flip_weather_app_free(FlipWeatherApp* app);
 
 
 #endif
 #endif

+ 6 - 140
flip_weather/flipper_http.h → flip_weather/flipper_http/flipper_http.c

@@ -1,137 +1,7 @@
-// flipper_http.h
-#ifndef FLIPPER_HTTP_H
-#define FLIPPER_HTTP_H
-
-#include <furi.h>
-#include <furi_hal.h>
-#include <furi_hal_gpio.h>
-#include <furi_hal_serial.h>
-#include <storage/storage.h>
-
-// STORAGE_EXT_PATH_PREFIX is defined in the Furi SDK as /ext
-
-#define HTTP_TAG               "FlipWeather" // change this to your app name
-#define http_tag               "flip_weather" // change this to your app id
-#define UART_CH                (FuriHalSerialIdUsart) // UART channel
-#define TIMEOUT_DURATION_TICKS (5 * 1000) // 5 seconds
-#define BAUDRATE               (115200) // UART baudrate
-#define RX_BUF_SIZE            1024 // UART RX buffer size
-#define RX_LINE_BUFFER_SIZE    4096 // UART RX line buffer size (increase for large responses)
-#define MAX_FILE_SHOW          4096 // Maximum data from file to show
-
-// Forward declaration for callback
-typedef void (*FlipperHTTP_Callback)(const char* line, void* context);
-
-// Functions
-bool flipper_http_init(FlipperHTTP_Callback callback, void* context);
-void flipper_http_deinit();
-//---
-void flipper_http_rx_callback(const char* line, void* context);
-bool flipper_http_send_data(const char* data);
-//---
-bool flipper_http_connect_wifi();
-bool flipper_http_disconnect_wifi();
-bool flipper_http_ping();
-bool flipper_http_scan_wifi();
-bool flipper_http_save_wifi(const char* ssid, const char* password);
-bool flipper_http_ip_wifi();
-bool flipper_http_ip_address();
-//---
-bool flipper_http_list_commands();
-bool flipper_http_led_on();
-bool flipper_http_led_off();
-bool flipper_http_parse_json(const char* key, const char* json_data);
-bool flipper_http_parse_json_array(const char* key, int index, const char* json_data);
-//---
-bool flipper_http_get_request(const char* url);
-bool flipper_http_get_request_with_headers(const char* url, const char* headers);
-bool flipper_http_post_request_with_headers(
-    const char* url,
-    const char* headers,
-    const char* payload);
-bool flipper_http_put_request_with_headers(
-    const char* url,
-    const char* headers,
-    const char* payload);
-bool flipper_http_delete_request_with_headers(
-    const char* url,
-    const char* headers,
-    const char* payload);
-//---
-bool flipper_http_get_request_bytes(const char* url, const char* headers);
-bool flipper_http_post_request_bytes(const char* url, const char* headers, const char* payload);
-//
-bool flipper_http_append_to_file(
-    const void* data,
-    size_t data_size,
-    bool start_new_file,
-    char* file_path);
-
-FuriString* flipper_http_load_from_file(char* file_path);
-static char* trim(const char* str);
-//
-bool flipper_http_process_response_async(bool (*http_request)(void), bool (*parse_json)(void));
-
-// State variable to track the UART state
-typedef enum {
-    INACTIVE, // Inactive state
-    IDLE, // Default state
-    RECEIVING, // Receiving data
-    SENDING, // Sending data
-    ISSUE, // Issue with connection
-} SerialState;
-
-// Event Flags for UART Worker Thread
-typedef enum {
-    WorkerEvtStop = (1 << 0),
-    WorkerEvtRxDone = (1 << 1),
-} WorkerEvtFlags;
-
-// FlipperHTTP Structure
-typedef struct {
-    FuriStreamBuffer* flipper_http_stream; // Stream buffer for UART communication
-    FuriHalSerialHandle* serial_handle; // Serial handle for UART communication
-    FuriThread* rx_thread; // Worker thread for UART
-    FuriThreadId rx_thread_id; // Worker thread ID
-    FlipperHTTP_Callback handle_rx_line_cb; // Callback for received lines
-    void* callback_context; // Context for the callback
-    SerialState state; // State of the UART
-
-    // variable to store the last received data from the UART
-    char* last_response;
-    char file_path[256]; // Path to save the received data
-
-    // Timer-related members
-    FuriTimer* get_timeout_timer; // Timer for HTTP request timeout
-
-    bool started_receiving_get; // Indicates if a GET request has started
-    bool just_started_get; // Indicates if GET data reception has just started
-
-    bool started_receiving_post; // Indicates if a POST request has started
-    bool just_started_post; // Indicates if POST data reception has just started
-
-    bool started_receiving_put; // Indicates if a PUT request has started
-    bool just_started_put; // Indicates if PUT data reception has just started
-
-    bool started_receiving_delete; // Indicates if a DELETE request has started
-    bool just_started_delete; // Indicates if DELETE data reception has just started
-
-    // Buffer to hold the raw bytes received from the UART
-    uint8_t* received_bytes;
-    size_t received_bytes_len; // Length of the received bytes
-    bool is_bytes_request; // Flag to indicate if the request is for bytes
-    bool save_bytes; // Flag to save the received data to a file
-    bool save_received_data; // Flag to save the received data to a file
-} FlipperHTTP;
-
-static FlipperHTTP fhttp;
-// Global static array for the line buffer
-static char rx_line_buffer[RX_LINE_BUFFER_SIZE];
-#define FILE_BUFFER_SIZE 512
-static uint8_t file_buffer[FILE_BUFFER_SIZE];
-
-// fhttp.last_response holds the last received data from the UART
-
+#include <flipper_http/flipper_http.h>
+FlipperHTTP fhttp;
+char rx_line_buffer[RX_LINE_BUFFER_SIZE];
+uint8_t file_buffer[FILE_BUFFER_SIZE];
 // Function to append received data to file
 // Function to append received data to file
 // make sure to initialize the file path before calling this function
 // make sure to initialize the file path before calling this function
 bool flipper_http_append_to_file(
 bool flipper_http_append_to_file(
@@ -258,7 +128,7 @@ FuriString* flipper_http_load_from_file(char* file_path) {
  * @note       This function will handle received data asynchronously via the callback.
  * @note       This function will handle received data asynchronously via the callback.
  */
  */
 // UART worker thread
 // UART worker thread
-static int32_t flipper_http_worker(void* context) {
+int32_t flipper_http_worker(void* context) {
     UNUSED(context);
     UNUSED(context);
     size_t rx_line_pos = 0;
     size_t rx_line_pos = 0;
     static size_t file_buffer_len = 0;
     static size_t file_buffer_len = 0;
@@ -302,8 +172,6 @@ static int32_t flipper_http_worker(void* context) {
                         // Invoke the callback with the complete line
                         // Invoke the callback with the complete line
                         fhttp.handle_rx_line_cb(rx_line_buffer, fhttp.callback_context);
                         fhttp.handle_rx_line_cb(rx_line_buffer, fhttp.callback_context);
 
 
-                        // save the received data
-
                         // Reset the line buffer position
                         // Reset the line buffer position
                         rx_line_pos = 0;
                         rx_line_pos = 0;
                     } else {
                     } else {
@@ -376,7 +244,7 @@ void get_timeout_timer_callback(void* context) {
  * @param      context   The context to pass to the callback.
  * @param      context   The context to pass to the callback.
  * @note       This function will handle received data asynchronously via the callback.
  * @note       This function will handle received data asynchronously via the callback.
  */
  */
-static void _flipper_http_rx_callback(
+void _flipper_http_rx_callback(
     FuriHalSerialHandle* handle,
     FuriHalSerialHandle* handle,
     FuriHalSerialRxEvent event,
     FuriHalSerialRxEvent event,
     void* context) {
     void* context) {
@@ -1388,5 +1256,3 @@ bool flipper_http_process_response_async(bool (*http_request)(void), bool (*pars
     }
     }
     return true;
     return true;
 }
 }
-
-#endif // FLIPPER_HTTP_H

+ 360 - 0
flip_weather/flipper_http/flipper_http.h

@@ -0,0 +1,360 @@
+// flipper_http.h
+#ifndef FLIPPER_HTTP_H
+#define FLIPPER_HTTP_H
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi_hal_gpio.h>
+#include <furi_hal_serial.h>
+#include <storage/storage.h>
+
+// STORAGE_EXT_PATH_PREFIX is defined in the Furi SDK as /ext
+
+#define HTTP_TAG               "FlipWeather" // change this to your app name
+#define http_tag               "flip_weather" // change this to your app id
+#define UART_CH                (FuriHalSerialIdUsart) // UART channel
+#define TIMEOUT_DURATION_TICKS (5 * 1000) // 5 seconds
+#define BAUDRATE               (115200) // UART baudrate
+#define RX_BUF_SIZE            1024 // UART RX buffer size
+#define RX_LINE_BUFFER_SIZE    4096 // UART RX line buffer size (increase for large responses)
+#define MAX_FILE_SHOW          4096 // Maximum data from file to show
+#define FILE_BUFFER_SIZE       512 // File buffer size
+
+// Forward declaration for callback
+typedef void (*FlipperHTTP_Callback)(const char* line, void* context);
+
+// State variable to track the UART state
+typedef enum {
+    INACTIVE, // Inactive state
+    IDLE, // Default state
+    RECEIVING, // Receiving data
+    SENDING, // Sending data
+    ISSUE, // Issue with connection
+} SerialState;
+
+// Event Flags for UART Worker Thread
+typedef enum {
+    WorkerEvtStop = (1 << 0),
+    WorkerEvtRxDone = (1 << 1),
+} WorkerEvtFlags;
+
+// FlipperHTTP Structure
+typedef struct {
+    FuriStreamBuffer* flipper_http_stream; // Stream buffer for UART communication
+    FuriHalSerialHandle* serial_handle; // Serial handle for UART communication
+    FuriThread* rx_thread; // Worker thread for UART
+    FuriThreadId rx_thread_id; // Worker thread ID
+    FlipperHTTP_Callback handle_rx_line_cb; // Callback for received lines
+    void* callback_context; // Context for the callback
+    SerialState state; // State of the UART
+
+    // variable to store the last received data from the UART
+    char* last_response;
+    char file_path[256]; // Path to save the received data
+
+    // Timer-related members
+    FuriTimer* get_timeout_timer; // Timer for HTTP request timeout
+
+    bool started_receiving_get; // Indicates if a GET request has started
+    bool just_started_get; // Indicates if GET data reception has just started
+
+    bool started_receiving_post; // Indicates if a POST request has started
+    bool just_started_post; // Indicates if POST data reception has just started
+
+    bool started_receiving_put; // Indicates if a PUT request has started
+    bool just_started_put; // Indicates if PUT data reception has just started
+
+    bool started_receiving_delete; // Indicates if a DELETE request has started
+    bool just_started_delete; // Indicates if DELETE data reception has just started
+
+    // Buffer to hold the raw bytes received from the UART
+    uint8_t* received_bytes;
+    size_t received_bytes_len; // Length of the received bytes
+    bool is_bytes_request; // Flag to indicate if the request is for bytes
+    bool save_bytes; // Flag to save the received data to a file
+    bool save_received_data; // Flag to save the received data to a file
+} FlipperHTTP;
+
+extern FlipperHTTP fhttp;
+// Global static array for the line buffer
+extern char rx_line_buffer[RX_LINE_BUFFER_SIZE];
+extern uint8_t file_buffer[FILE_BUFFER_SIZE];
+
+// fhttp.last_response holds the last received data from the UART
+
+// Function to append received data to file
+// make sure to initialize the file path before calling this function
+bool flipper_http_append_to_file(
+    const void* data,
+    size_t data_size,
+    bool start_new_file,
+    char* file_path);
+
+FuriString* flipper_http_load_from_file(char* file_path);
+
+// UART worker thread
+/**
+ * @brief      Worker thread to handle UART data asynchronously.
+ * @return     0
+ * @param      context   The context to pass to the callback.
+ * @note       This function will handle received data asynchronously via the callback.
+ */
+// UART worker thread
+int32_t flipper_http_worker(void* context);
+
+// Timer callback function
+/**
+ * @brief      Callback function for the GET timeout timer.
+ * @return     0
+ * @param      context   The context to pass to the callback.
+ * @note       This function will be called when the GET request times out.
+ */
+void get_timeout_timer_callback(void* context);
+
+// UART RX Handler Callback (Interrupt Context)
+/**
+ * @brief      A private callback function to handle received data asynchronously.
+ * @return     void
+ * @param      handle    The UART handle.
+ * @param      event     The event type.
+ * @param      context   The context to pass to the callback.
+ * @note       This function will handle received data asynchronously via the callback.
+ */
+void _flipper_http_rx_callback(
+    FuriHalSerialHandle* handle,
+    FuriHalSerialRxEvent event,
+    void* context);
+
+// UART initialization function
+/**
+ * @brief      Initialize UART.
+ * @return     true if the UART was initialized successfully, false otherwise.
+ * @param      callback  The callback function to handle received data (ex. flipper_http_rx_callback).
+ * @param      context   The context to pass to the callback.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_init(FlipperHTTP_Callback callback, void* context);
+
+// Deinitialize UART
+/**
+ * @brief      Deinitialize UART.
+ * @return     void
+ * @note       This function will stop the asynchronous RX, release the serial handle, and free the resources.
+ */
+void flipper_http_deinit();
+
+// Function to send data over UART with newline termination
+/**
+ * @brief      Send data over UART with newline termination.
+ * @return     true if the data was sent successfully, false otherwise.
+ * @param      data  The data to send over UART.
+ * @note       The data will be sent over UART with a newline character appended.
+ */
+bool flipper_http_send_data(const char* data);
+
+// Function to send a PING request
+/**
+ * @brief      Send a PING request to check if the Wifi Dev Board is connected.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ * @note       This is best used to check if the Wifi Dev Board is connected.
+ * @note       The state will remain INACTIVE until a PONG is received.
+ */
+bool flipper_http_ping();
+
+// Function to list available commands
+/**
+ * @brief      Send a command to list available commands.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_list_commands();
+
+// Function to turn on the LED
+/**
+ * @brief      Allow the LED to display while processing.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_led_on();
+
+// Function to turn off the LED
+/**
+ * @brief      Disable the LED from displaying while processing.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_led_off();
+
+// Function to parse JSON data
+/**
+ * @brief      Parse JSON data.
+ * @return     true if the JSON data was parsed successfully, false otherwise.
+ * @param      key       The key to parse from the JSON data.
+ * @param      json_data The JSON data to parse.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_parse_json(const char* key, const char* json_data);
+
+// Function to parse JSON array data
+/**
+ * @brief      Parse JSON array data.
+ * @return     true if the JSON array data was parsed successfully, false otherwise.
+ * @param      key       The key to parse from the JSON array data.
+ * @param      index     The index to parse from the JSON array data.
+ * @param      json_data The JSON array data to parse.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_parse_json_array(const char* key, int index, const char* json_data);
+
+// Function to scan for WiFi networks
+/**
+ * @brief      Send a command to scan for WiFi networks.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_scan_wifi();
+
+// Function to save WiFi settings (returns true if successful)
+/**
+ * @brief      Send a command to save WiFi settings.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_save_wifi(const char* ssid, const char* password);
+
+// Function to get IP address of WiFi Devboard
+/**
+ * @brief      Send a command to get the IP address of the WiFi Devboard
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_ip_address();
+
+// Function to get IP address of the connected WiFi network
+/**
+ * @brief      Send a command to get the IP address of the connected WiFi network.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_ip_wifi();
+
+// Function to disconnect from WiFi (returns true if successful)
+/**
+ * @brief      Send a command to disconnect from WiFi.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_disconnect_wifi();
+
+// Function to connect to WiFi (returns true if successful)
+/**
+ * @brief      Send a command to connect to WiFi.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_connect_wifi();
+
+// Function to send a GET request
+/**
+ * @brief      Send a GET request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the GET request to.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_get_request(const char* url);
+
+// Function to send a GET request with headers
+/**
+ * @brief      Send a GET request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the GET request to.
+ * @param      headers  The headers to send with the GET request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_get_request_with_headers(const char* url, const char* headers);
+
+// Function to send a GET request with headers and return bytes
+/**
+ * @brief      Send a GET request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the GET request to.
+ * @param      headers  The headers to send with the GET request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_get_request_bytes(const char* url, const char* headers);
+
+// Function to send a POST request with headers
+/**
+ * @brief      Send a POST request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the POST request to.
+ * @param      headers  The headers to send with the POST request.
+ * @param      data  The data to send with the POST request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_post_request_with_headers(
+    const char* url,
+    const char* headers,
+    const char* payload);
+
+// Function to send a POST request with headers and return bytes
+/**
+ * @brief      Send a POST request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the POST request to.
+ * @param      headers  The headers to send with the POST request.
+ * @param      payload  The data to send with the POST request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_post_request_bytes(const char* url, const char* headers, const char* payload);
+
+// Function to send a PUT request with headers
+/**
+ * @brief      Send a PUT request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the PUT request to.
+ * @param      headers  The headers to send with the PUT request.
+ * @param      data  The data to send with the PUT request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_put_request_with_headers(
+    const char* url,
+    const char* headers,
+    const char* payload);
+
+// Function to send a DELETE request with headers
+/**
+ * @brief      Send a DELETE request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the DELETE request to.
+ * @param      headers  The headers to send with the DELETE request.
+ * @param      data  The data to send with the DELETE request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_delete_request_with_headers(
+    const char* url,
+    const char* headers,
+    const char* payload);
+
+// Function to handle received data asynchronously
+/**
+ * @brief      Callback function to handle received data asynchronously.
+ * @return     void
+ * @param      line     The received line.
+ * @param      context  The context passed to the callback.
+ * @note       The received data will be handled asynchronously via the callback and handles the state of the UART.
+ */
+void flipper_http_rx_callback(const char* line, void* context);
+
+// Function to trim leading and trailing spaces and newlines from a constant string
+char* trim(const char* str);
+/**
+ * @brief Process requests and parse JSON data asynchronously
+ * @param http_request The function to send the request
+ * @param parse_json The function to parse the JSON
+ * @return true if successful, false otherwise
+ */
+bool flipper_http_process_response_async(bool (*http_request)(void), bool (*parse_json)(void));
+
+#endif // FLIPPER_HTTP_H

+ 26 - 140
flip_weather/jsmn.h → flip_weather/jsmn/jsmn.c

@@ -3,113 +3,20 @@
  *
  *
  * Copyright (c) 2010 Serge Zaitsev
  * Copyright (c) 2010 Serge Zaitsev
  *
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * [License text continues...]
  */
  */
-#ifndef JSMN_H
-#define JSMN_H
-
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef JSMN_STATIC
-#define JSMN_API static
-#else
-#define JSMN_API extern
-#endif
-
-/**
-   * JSON type identifier. Basic types are:
-   * 	o Object
-   * 	o Array
-   * 	o String
-   * 	o Other primitive: number, boolean (true/false) or null
-   */
-typedef enum {
-    JSMN_UNDEFINED = 0,
-    JSMN_OBJECT = 1 << 0,
-    JSMN_ARRAY = 1 << 1,
-    JSMN_STRING = 1 << 2,
-    JSMN_PRIMITIVE = 1 << 3
-} jsmntype_t;
-
-enum jsmnerr {
-    /* Not enough tokens were provided */
-    JSMN_ERROR_NOMEM = -1,
-    /* Invalid character inside JSON string */
-    JSMN_ERROR_INVAL = -2,
-    /* The string is not a full JSON packet, more bytes expected */
-    JSMN_ERROR_PART = -3
-};
-
-/**
-   * JSON token description.
-   * type		type (object, array, string etc.)
-   * start	start position in JSON data string
-   * end		end position in JSON data string
-   */
-typedef struct jsmntok {
-    jsmntype_t type;
-    int start;
-    int end;
-    int size;
-#ifdef JSMN_PARENT_LINKS
-    int parent;
-#endif
-} jsmntok_t;
-
-/**
-   * JSON parser. Contains an array of token blocks available. Also stores
-   * the string being parsed now and current position in that string.
-   */
-typedef struct jsmn_parser {
-    unsigned int pos; /* offset in the JSON string */
-    unsigned int toknext; /* next token to allocate */
-    int toksuper; /* superior token node, e.g. parent object or array */
-} jsmn_parser;
 
 
-/**
-   * Create JSON parser over an array of tokens
-   */
-JSMN_API void jsmn_init(jsmn_parser* parser);
-
-/**
-   * Run JSON parser. It parses a JSON data string into and array of tokens, each
-   * describing
-   * a single JSON object.
-   */
-JSMN_API int jsmn_parse(
-    jsmn_parser* parser,
-    const char* js,
-    const size_t len,
-    jsmntok_t* tokens,
-    const unsigned int num_tokens);
+#include <jsmn/jsmn.h>
+#include <stdlib.h>
+#include <string.h>
 
 
-#ifndef JSMN_HEADER
 /**
 /**
-   * Allocates a fresh unused token from the token pool.
-   */
+ * Allocates a fresh unused token from the token pool.
+ */
 static jsmntok_t*
 static jsmntok_t*
     jsmn_alloc_token(jsmn_parser* parser, jsmntok_t* tokens, const size_t num_tokens) {
     jsmn_alloc_token(jsmn_parser* parser, jsmntok_t* tokens, const size_t num_tokens) {
     jsmntok_t* tok;
     jsmntok_t* tok;
+
     if(parser->toknext >= num_tokens) {
     if(parser->toknext >= num_tokens) {
         return NULL;
         return NULL;
     }
     }
@@ -123,8 +30,8 @@ static jsmntok_t*
 }
 }
 
 
 /**
 /**
-   * Fills token type and boundaries.
-   */
+ * Fills token type and boundaries.
+ */
 static void
 static void
     jsmn_fill_token(jsmntok_t* token, const jsmntype_t type, const int start, const int end) {
     jsmn_fill_token(jsmntok_t* token, const jsmntype_t type, const int start, const int end) {
     token->type = type;
     token->type = type;
@@ -134,8 +41,8 @@ static void
 }
 }
 
 
 /**
 /**
-   * Fills next available token with JSON primitive.
-   */
+ * Fills next available token with JSON primitive.
+ */
 static int jsmn_parse_primitive(
 static int jsmn_parse_primitive(
     jsmn_parser* parser,
     jsmn_parser* parser,
     const char* js,
     const char* js,
@@ -195,8 +102,8 @@ found:
 }
 }
 
 
 /**
 /**
-   * Fills next token with JSON string.
-   */
+ * Fills next token with JSON string.
+ */
 static int jsmn_parse_string(
 static int jsmn_parse_string(
     jsmn_parser* parser,
     jsmn_parser* parser,
     const char* js,
     const char* js,
@@ -272,9 +179,18 @@ static int jsmn_parse_string(
 }
 }
 
 
 /**
 /**
-   * Parse JSON string and fill tokens.
-   */
-JSMN_API int jsmn_parse(
+ * Create JSON parser over an array of tokens
+ */
+void jsmn_init(jsmn_parser* parser) {
+    parser->pos = 0;
+    parser->toknext = 0;
+    parser->toksuper = -1;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+int jsmn_parse(
     jsmn_parser* parser,
     jsmn_parser* parser,
     const char* js,
     const char* js,
     const size_t len,
     const size_t len,
@@ -464,34 +380,6 @@ JSMN_API int jsmn_parse(
     return count;
     return count;
 }
 }
 
 
-/**
-   * Creates a new parser based over a given buffer with an array of tokens
-   * available.
-   */
-JSMN_API void jsmn_init(jsmn_parser* parser) {
-    parser->pos = 0;
-    parser->toknext = 0;
-    parser->toksuper = -1;
-}
-
-#endif /* JSMN_HEADER */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* JSMN_H */
-
-#ifndef JB_JSMN_EDIT
-#define JB_JSMN_EDIT
-/* Added in by JBlanked on 2024-10-16 for use in Flipper Zero SDK*/
-
-#include <string.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <furi.h>
-
 // Helper function to create a JSON object
 // Helper function to create a JSON object
 char* jsmn(const char* key, const char* value) {
 char* jsmn(const char* key, const char* value) {
     int length = strlen(key) + strlen(value) + 8; // Calculate required length
     int length = strlen(key) + strlen(value) + 8; // Calculate required length
@@ -512,7 +400,7 @@ int jsoneq(const char* json, jsmntok_t* tok, const char* s) {
     return -1;
     return -1;
 }
 }
 
 
-// return the value of the key in the JSON data
+// Return the value of the key in the JSON data
 char* get_json_value(char* key, char* json_data, uint32_t max_tokens) {
 char* get_json_value(char* key, char* json_data, uint32_t max_tokens) {
     // Parse the JSON feed
     // Parse the JSON feed
     if(json_data != NULL) {
     if(json_data != NULL) {
@@ -776,5 +664,3 @@ char** get_json_array_values(char* key, char* json_data, uint32_t max_tokens, in
     free(array_str);
     free(array_str);
     return values;
     return values;
 }
 }
-
-#endif /* JB_JSMN_EDIT */

+ 131 - 0
flip_weather/jsmn/jsmn.h

@@ -0,0 +1,131 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2010 Serge Zaitsev
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * [License text continues...]
+ */
+
+#ifndef JSMN_H
+#define JSMN_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef JSMN_STATIC
+#define JSMN_API static
+#else
+#define JSMN_API extern
+#endif
+
+/**
+     * JSON type identifier. Basic types are:
+     * 	o Object
+     * 	o Array
+     * 	o String
+     * 	o Other primitive: number, boolean (true/false) or null
+     */
+typedef enum {
+    JSMN_UNDEFINED = 0,
+    JSMN_OBJECT = 1 << 0,
+    JSMN_ARRAY = 1 << 1,
+    JSMN_STRING = 1 << 2,
+    JSMN_PRIMITIVE = 1 << 3
+} jsmntype_t;
+
+enum jsmnerr {
+    /* Not enough tokens were provided */
+    JSMN_ERROR_NOMEM = -1,
+    /* Invalid character inside JSON string */
+    JSMN_ERROR_INVAL = -2,
+    /* The string is not a full JSON packet, more bytes expected */
+    JSMN_ERROR_PART = -3
+};
+
+/**
+     * JSON token description.
+     * type		type (object, array, string etc.)
+     * start	start position in JSON data string
+     * end		end position in JSON data string
+     */
+typedef struct {
+    jsmntype_t type;
+    int start;
+    int end;
+    int size;
+#ifdef JSMN_PARENT_LINKS
+    int parent;
+#endif
+} jsmntok_t;
+
+/**
+     * JSON parser. Contains an array of token blocks available. Also stores
+     * the string being parsed now and current position in that string.
+     */
+typedef struct {
+    unsigned int pos; /* offset in the JSON string */
+    unsigned int toknext; /* next token to allocate */
+    int toksuper; /* superior token node, e.g. parent object or array */
+} jsmn_parser;
+
+/**
+     * Create JSON parser over an array of tokens
+     */
+JSMN_API void jsmn_init(jsmn_parser* parser);
+
+/**
+     * Run JSON parser. It parses a JSON data string into and array of tokens, each
+     * describing a single JSON object.
+     */
+JSMN_API int jsmn_parse(
+    jsmn_parser* parser,
+    const char* js,
+    const size_t len,
+    jsmntok_t* tokens,
+    const unsigned int num_tokens);
+
+#ifndef JSMN_HEADER
+/* Implementation has been moved to jsmn.c */
+#endif /* JSMN_HEADER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* JSMN_H */
+
+/* Custom Helper Functions */
+#ifndef JB_JSMN_EDIT
+#define JB_JSMN_EDIT
+/* Added in by JBlanked on 2024-10-16 for use in Flipper Zero SDK*/
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <furi.h>
+
+// Helper function to create a JSON object
+char* jsmn(const char* key, const char* value);
+// Helper function to compare JSON keys
+int jsoneq(const char* json, jsmntok_t* tok, const char* s);
+
+// Return the value of the key in the JSON data
+char* get_json_value(char* key, char* json_data, uint32_t max_tokens);
+
+// Revised get_json_array_value function
+char* get_json_array_value(char* key, uint32_t index, char* json_data, uint32_t max_tokens);
+
+// Revised get_json_array_values function with correct token skipping
+char** get_json_array_values(char* key, char* json_data, uint32_t max_tokens, int* num_values);
+#endif /* JB_JSMN_EDIT */

+ 28 - 15
flip_weather/flip_weather_parse.h → flip_weather/parse/flip_weather_parse.c

@@ -1,12 +1,12 @@
-#pragma once
+#include "parse/flip_weather_parse.h"
 
 
-static bool sent_get_request = false;
-static bool get_request_success = false;
-static bool got_ip_address = false;
-static bool geo_information_processed = false;
-static bool weather_information_processed = false;
+bool sent_get_request = false;
+bool get_request_success = false;
+bool got_ip_address = false;
+bool geo_information_processed = false;
+bool weather_information_processed = false;
 
 
-static bool flip_weather_parse_ip_address() {
+bool flip_weather_parse_ip_address() {
     // load the received data from the saved file
     // load the received data from the saved file
     FuriString* returned_data = flipper_http_load_from_file(fhttp.file_path);
     FuriString* returned_data = flipper_http_load_from_file(fhttp.file_path);
     if(returned_data == NULL) {
     if(returned_data == NULL) {
@@ -30,14 +30,18 @@ static bool flip_weather_parse_ip_address() {
         return false;
         return false;
     }
     }
     snprintf(ip_address, 16, "%s", ip);
     snprintf(ip_address, 16, "%s", ip);
-    ip_address[15] = '\0';
     free(ip);
     free(ip);
     furi_string_free(returned_data);
     furi_string_free(returned_data);
     return true;
     return true;
 }
 }
 
 
 // handle the async-to-sync process to get and set the IP address
 // handle the async-to-sync process to get and set the IP address
-static bool flip_weather_handle_ip_address() {
+bool flip_weather_handle_ip_address() {
+    if(fhttp.state == INACTIVE) {
+        FURI_LOG_E(TAG, "Board is INACTIVE");
+        flipper_http_ping(); // ping the device
+        return false;
+    }
     if(!got_ip_address) {
     if(!got_ip_address) {
         got_ip_address = true;
         got_ip_address = true;
         snprintf(
         snprintf(
@@ -48,6 +52,7 @@ static bool flip_weather_handle_ip_address() {
         fhttp.save_received_data = true;
         fhttp.save_received_data = true;
         if(!flipper_http_get_request("https://httpbin.org/get")) {
         if(!flipper_http_get_request("https://httpbin.org/get")) {
             FURI_LOG_E(TAG, "Failed to get IP address");
             FURI_LOG_E(TAG, "Failed to get IP address");
+            fhttp.state = ISSUE;
             return false;
             return false;
         } else {
         } else {
             fhttp.state = RECEIVING;
             fhttp.state = RECEIVING;
@@ -55,7 +60,7 @@ static bool flip_weather_handle_ip_address() {
         }
         }
         while(fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0) {
         while(fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0) {
             // Wait for the feed to be received
             // Wait for the feed to be received
-            furi_delay_ms(100);
+            furi_delay_ms(10);
         }
         }
         furi_timer_stop(fhttp.get_timeout_timer);
         furi_timer_stop(fhttp.get_timeout_timer);
         if(!flip_weather_parse_ip_address()) {
         if(!flip_weather_parse_ip_address()) {
@@ -69,13 +74,21 @@ static bool flip_weather_handle_ip_address() {
     return true;
     return true;
 }
 }
 
 
-static bool send_geo_location_request() {
+bool send_geo_location_request() {
+    if(fhttp.state == INACTIVE) {
+        FURI_LOG_E(TAG, "Board is INACTIVE");
+        flipper_http_ping(); // ping the device
+        return false;
+    }
     if(!sent_get_request && fhttp.state == IDLE) {
     if(!sent_get_request && fhttp.state == IDLE) {
         sent_get_request = true;
         sent_get_request = true;
-        get_request_success = flipper_http_get_request_with_headers(
-            "https://ipwhois.app/json/", jsmn("Content-Type", "application/json"));
+        char* headers = jsmn("Content-Type", "application/json");
+        get_request_success =
+            flipper_http_get_request_with_headers("https://ipwhois.app/json/", headers);
+        free(headers);
         if(!get_request_success) {
         if(!get_request_success) {
             FURI_LOG_E(TAG, "Failed to send GET request");
             FURI_LOG_E(TAG, "Failed to send GET request");
+            fhttp.state = ISSUE;
             return false;
             return false;
         }
         }
         fhttp.state = RECEIVING;
         fhttp.state = RECEIVING;
@@ -83,7 +96,7 @@ static bool send_geo_location_request() {
     return true;
     return true;
 }
 }
 
 
-static void process_geo_location() {
+void process_geo_location() {
     if(!geo_information_processed && fhttp.last_response != NULL) {
     if(!geo_information_processed && fhttp.last_response != NULL) {
         geo_information_processed = true;
         geo_information_processed = true;
         char* city = get_json_value("city", fhttp.last_response, MAX_TOKENS);
         char* city = get_json_value("city", fhttp.last_response, MAX_TOKENS);
@@ -103,7 +116,7 @@ static void process_geo_location() {
     }
     }
 }
 }
 
 
-static void process_weather() {
+void process_weather() {
     if(!weather_information_processed && fhttp.last_response != NULL) {
     if(!weather_information_processed && fhttp.last_response != NULL) {
         weather_information_processed = true;
         weather_information_processed = true;
         char* current_data = get_json_value("current", fhttp.last_response, MAX_TOKENS);
         char* current_data = get_json_value("current", fhttp.last_response, MAX_TOKENS);

+ 16 - 0
flip_weather/parse/flip_weather_parse.h

@@ -0,0 +1,16 @@
+#ifndef FLIP_WEATHER_PARSE_H
+#define FLIP_WEATHER_PARSE_H
+#include <flip_weather.h>
+extern bool sent_get_request;
+extern bool get_request_success;
+extern bool got_ip_address;
+extern bool geo_information_processed;
+extern bool weather_information_processed;
+
+bool flip_weather_parse_ip_address();
+bool flip_weather_handle_ip_address();
+bool send_geo_location_request();
+void process_geo_location();
+void process_weather();
+
+#endif