jblanked 1 год назад
Родитель
Сommit
7e65757ee4

+ 2 - 2
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.

+ 3 - 6
flip_weather_i.h → 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));
 
 
@@ -112,6 +111,4 @@ static FlipWeatherApp *flip_weather_app_alloc()
     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
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
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
assets/01-home.png


+ 38 - 46
flip_weather_callback.h → callback/flip_weather_callback.c

@@ -1,55 +1,45 @@
-#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_request_error(Canvas *canvas)
 void flip_weather_request_error(Canvas *canvas)
 {
 {
-    if (fhttp.last_response == NULL)
+    if (fhttp.last_response != NULL)
     {
     {
-        if (fhttp.last_response != NULL)
+        if (strstr(fhttp.last_response, "[ERROR] Not connected to Wifi. Failed to reconnect.") != 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.");
-            }
+            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
+        else if (strstr(fhttp.last_response, "[ERROR] Failed to connect to Wifi.") != 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
+        {
+            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)
     {
     {
@@ -87,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)
     {
     {
@@ -133,11 +123,14 @@ 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(url, 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", lattitude, longitude);
         snprintf(url, 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", lattitude, 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;
@@ -150,8 +143,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);
         }
         }
@@ -171,7 +165,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)
     {
     {
@@ -199,7 +193,7 @@ static void flip_weather_view_draw_callback_gps(Canvas *canvas, void *model)
     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)
@@ -234,7 +228,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)
@@ -271,7 +265,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)
@@ -308,7 +302,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)
     {
     {
@@ -327,7 +321,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)
@@ -354,7 +348,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)
@@ -366,7 +360,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)
     {
     {
@@ -375,6 +369,4 @@ static uint32_t callback_to_wifi_settings(void *context)
     }
     }
     UNUSED(context);
     UNUSED(context);
     return FlipWeatherViewSettings;
     return FlipWeatherViewSettings;
-}
-
-#endif
+}

+ 29 - 0
callback/flip_weather_callback.h

@@ -0,0 +1,29 @@
+#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_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

+ 2 - 25
easy_flipper.h → easy_flipper/easy_flipper.c

@@ -1,25 +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>
-#include <uart_text_input.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
@@ -588,6 +567,4 @@ 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
+}

+ 262 - 0
easy_flipper/easy_flipper.h

@@ -0,0 +1,262 @@
+#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 <text_input/uart_text_input.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 UART_TextInput object
+ * @param uart_text_input The UART_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(
+    UART_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

+ 4 - 11
flip_weather_storage.h → 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(
+void save_settings(
     const char *ssid,
     const char *ssid,
     const char *password)
     const char *password)
 {
 {
@@ -49,7 +44,7 @@ static void save_settings(
     furi_record_close(RECORD_STORAGE);
     furi_record_close(RECORD_STORAGE);
 }
 }
 
 
-static bool load_settings(
+bool load_settings(
     char *ssid,
     char *ssid,
     size_t ssid_size,
     size_t ssid_size,
     char *password,
     char *password,
@@ -97,6 +92,4 @@ static bool load_settings(
     furi_record_close(RECORD_STORAGE);
     furi_record_close(RECORD_STORAGE);
 
 
     return true;
     return true;
-}
-
-#endif // FLIP_WEATHER_STORAGE_H
+}

+ 19 - 0
flip_storage/flip_weather_storage.h

@@ -0,0 +1,19 @@
+#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

+ 17 - 6
flip_weather_free.h → 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)
     {
     {
@@ -78,6 +91,4 @@ static void flip_weather_app_free(FlipWeatherApp *app)
     // free the app
     // free the app
     if (app)
     if (app)
         free(app);
         free(app);
-}
-
-#endif // FLIP_WEATHER_FREE_H
+}

+ 19 - 17
flip_weather_e.h → 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
@@ -51,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

+ 8 - 145
flipper_http.h → flipper_http/flipper_http.c

@@ -1,140 +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(
@@ -247,7 +114,7 @@ FuriString *flipper_http_load_from_file(char *file_path)
         storage_file_close(file);
         storage_file_close(file);
         storage_file_free(file);
         storage_file_free(file);
         furi_record_close(RECORD_STORAGE);
         furi_record_close(RECORD_STORAGE);
-        return false;
+        return NULL;
     }
     }
 
 
     // Append each byte to the FuriString
     // Append each byte to the FuriString
@@ -276,7 +143,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;
@@ -331,8 +198,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;
                     }
                     }
@@ -417,7 +282,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)
@@ -1557,6 +1422,4 @@ bool flipper_http_process_response_async(bool (*http_request)(void), bool (*pars
         return false;
         return false;
     }
     }
     return true;
     return true;
-}
-
-#endif // FLIPPER_HTTP_H
+}

+ 363 - 0
flipper_http/flipper_http.h

@@ -0,0 +1,363 @@
+// 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

+ 0 - 863
jsmn.h

@@ -1,863 +0,0 @@
-/*
- * 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:
- *
- * 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.
- */
-#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);
-
-#ifndef JSMN_HEADER
-  /**
-   * Allocates a fresh unused token from the token pool.
-   */
-  static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
-                                     const size_t num_tokens)
-  {
-    jsmntok_t *tok;
-    if (parser->toknext >= num_tokens)
-    {
-      return NULL;
-    }
-    tok = &tokens[parser->toknext++];
-    tok->start = tok->end = -1;
-    tok->size = 0;
-#ifdef JSMN_PARENT_LINKS
-    tok->parent = -1;
-#endif
-    return tok;
-  }
-
-  /**
-   * Fills token type and boundaries.
-   */
-  static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
-                              const int start, const int end)
-  {
-    token->type = type;
-    token->start = start;
-    token->end = end;
-    token->size = 0;
-  }
-
-  /**
-   * Fills next available token with JSON primitive.
-   */
-  static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
-                                  const size_t len, jsmntok_t *tokens,
-                                  const size_t num_tokens)
-  {
-    jsmntok_t *token;
-    int start;
-
-    start = parser->pos;
-
-    for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
-    {
-      switch (js[parser->pos])
-      {
-#ifndef JSMN_STRICT
-      /* In strict mode primitive must be followed by "," or "}" or "]" */
-      case ':':
-#endif
-      case '\t':
-      case '\r':
-      case '\n':
-      case ' ':
-      case ',':
-      case ']':
-      case '}':
-        goto found;
-      default:
-        /* to quiet a warning from gcc*/
-        break;
-      }
-      if (js[parser->pos] < 32 || js[parser->pos] >= 127)
-      {
-        parser->pos = start;
-        return JSMN_ERROR_INVAL;
-      }
-    }
-#ifdef JSMN_STRICT
-    /* In strict mode primitive must be followed by a comma/object/array */
-    parser->pos = start;
-    return JSMN_ERROR_PART;
-#endif
-
-  found:
-    if (tokens == NULL)
-    {
-      parser->pos--;
-      return 0;
-    }
-    token = jsmn_alloc_token(parser, tokens, num_tokens);
-    if (token == NULL)
-    {
-      parser->pos = start;
-      return JSMN_ERROR_NOMEM;
-    }
-    jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
-#ifdef JSMN_PARENT_LINKS
-    token->parent = parser->toksuper;
-#endif
-    parser->pos--;
-    return 0;
-  }
-
-  /**
-   * Fills next token with JSON string.
-   */
-  static int jsmn_parse_string(jsmn_parser *parser, const char *js,
-                               const size_t len, jsmntok_t *tokens,
-                               const size_t num_tokens)
-  {
-    jsmntok_t *token;
-
-    int start = parser->pos;
-
-    /* Skip starting quote */
-    parser->pos++;
-
-    for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
-    {
-      char c = js[parser->pos];
-
-      /* Quote: end of string */
-      if (c == '\"')
-      {
-        if (tokens == NULL)
-        {
-          return 0;
-        }
-        token = jsmn_alloc_token(parser, tokens, num_tokens);
-        if (token == NULL)
-        {
-          parser->pos = start;
-          return JSMN_ERROR_NOMEM;
-        }
-        jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
-#ifdef JSMN_PARENT_LINKS
-        token->parent = parser->toksuper;
-#endif
-        return 0;
-      }
-
-      /* Backslash: Quoted symbol expected */
-      if (c == '\\' && parser->pos + 1 < len)
-      {
-        int i;
-        parser->pos++;
-        switch (js[parser->pos])
-        {
-        /* Allowed escaped symbols */
-        case '\"':
-        case '/':
-        case '\\':
-        case 'b':
-        case 'f':
-        case 'r':
-        case 'n':
-        case 't':
-          break;
-        /* Allows escaped symbol \uXXXX */
-        case 'u':
-          parser->pos++;
-          for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
-               i++)
-          {
-            /* If it isn't a hex character we have an error */
-            if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
-                  (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
-                  (js[parser->pos] >= 97 && js[parser->pos] <= 102)))
-            { /* a-f */
-              parser->pos = start;
-              return JSMN_ERROR_INVAL;
-            }
-            parser->pos++;
-          }
-          parser->pos--;
-          break;
-        /* Unexpected symbol */
-        default:
-          parser->pos = start;
-          return JSMN_ERROR_INVAL;
-        }
-      }
-    }
-    parser->pos = start;
-    return JSMN_ERROR_PART;
-  }
-
-  /**
-   * Parse JSON string and fill tokens.
-   */
-  JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
-                          jsmntok_t *tokens, const unsigned int num_tokens)
-  {
-    int r;
-    int i;
-    jsmntok_t *token;
-    int count = parser->toknext;
-
-    for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
-    {
-      char c;
-      jsmntype_t type;
-
-      c = js[parser->pos];
-      switch (c)
-      {
-      case '{':
-      case '[':
-        count++;
-        if (tokens == NULL)
-        {
-          break;
-        }
-        token = jsmn_alloc_token(parser, tokens, num_tokens);
-        if (token == NULL)
-        {
-          return JSMN_ERROR_NOMEM;
-        }
-        if (parser->toksuper != -1)
-        {
-          jsmntok_t *t = &tokens[parser->toksuper];
-#ifdef JSMN_STRICT
-          /* In strict mode an object or array can't become a key */
-          if (t->type == JSMN_OBJECT)
-          {
-            return JSMN_ERROR_INVAL;
-          }
-#endif
-          t->size++;
-#ifdef JSMN_PARENT_LINKS
-          token->parent = parser->toksuper;
-#endif
-        }
-        token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
-        token->start = parser->pos;
-        parser->toksuper = parser->toknext - 1;
-        break;
-      case '}':
-      case ']':
-        if (tokens == NULL)
-        {
-          break;
-        }
-        type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
-#ifdef JSMN_PARENT_LINKS
-        if (parser->toknext < 1)
-        {
-          return JSMN_ERROR_INVAL;
-        }
-        token = &tokens[parser->toknext - 1];
-        for (;;)
-        {
-          if (token->start != -1 && token->end == -1)
-          {
-            if (token->type != type)
-            {
-              return JSMN_ERROR_INVAL;
-            }
-            token->end = parser->pos + 1;
-            parser->toksuper = token->parent;
-            break;
-          }
-          if (token->parent == -1)
-          {
-            if (token->type != type || parser->toksuper == -1)
-            {
-              return JSMN_ERROR_INVAL;
-            }
-            break;
-          }
-          token = &tokens[token->parent];
-        }
-#else
-        for (i = parser->toknext - 1; i >= 0; i--)
-        {
-          token = &tokens[i];
-          if (token->start != -1 && token->end == -1)
-          {
-            if (token->type != type)
-            {
-              return JSMN_ERROR_INVAL;
-            }
-            parser->toksuper = -1;
-            token->end = parser->pos + 1;
-            break;
-          }
-        }
-        /* Error if unmatched closing bracket */
-        if (i == -1)
-        {
-          return JSMN_ERROR_INVAL;
-        }
-        for (; i >= 0; i--)
-        {
-          token = &tokens[i];
-          if (token->start != -1 && token->end == -1)
-          {
-            parser->toksuper = i;
-            break;
-          }
-        }
-#endif
-        break;
-      case '\"':
-        r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
-        if (r < 0)
-        {
-          return r;
-        }
-        count++;
-        if (parser->toksuper != -1 && tokens != NULL)
-        {
-          tokens[parser->toksuper].size++;
-        }
-        break;
-      case '\t':
-      case '\r':
-      case '\n':
-      case ' ':
-        break;
-      case ':':
-        parser->toksuper = parser->toknext - 1;
-        break;
-      case ',':
-        if (tokens != NULL && parser->toksuper != -1 &&
-            tokens[parser->toksuper].type != JSMN_ARRAY &&
-            tokens[parser->toksuper].type != JSMN_OBJECT)
-        {
-#ifdef JSMN_PARENT_LINKS
-          parser->toksuper = tokens[parser->toksuper].parent;
-#else
-          for (i = parser->toknext - 1; i >= 0; i--)
-          {
-            if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT)
-            {
-              if (tokens[i].start != -1 && tokens[i].end == -1)
-              {
-                parser->toksuper = i;
-                break;
-              }
-            }
-          }
-#endif
-        }
-        break;
-#ifdef JSMN_STRICT
-      /* In strict mode primitives are: numbers and booleans */
-      case '-':
-      case '0':
-      case '1':
-      case '2':
-      case '3':
-      case '4':
-      case '5':
-      case '6':
-      case '7':
-      case '8':
-      case '9':
-      case 't':
-      case 'f':
-      case 'n':
-        /* And they must not be keys of the object */
-        if (tokens != NULL && parser->toksuper != -1)
-        {
-          const jsmntok_t *t = &tokens[parser->toksuper];
-          if (t->type == JSMN_OBJECT ||
-              (t->type == JSMN_STRING && t->size != 0))
-          {
-            return JSMN_ERROR_INVAL;
-          }
-        }
-#else
-      /* In non-strict mode every unquoted value is a primitive */
-      default:
-#endif
-        r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
-        if (r < 0)
-        {
-          return r;
-        }
-        count++;
-        if (parser->toksuper != -1 && tokens != NULL)
-        {
-          tokens[parser->toksuper].size++;
-        }
-        break;
-
-#ifdef JSMN_STRICT
-      /* Unexpected char in strict mode */
-      default:
-        return JSMN_ERROR_INVAL;
-#endif
-      }
-    }
-
-    if (tokens != NULL)
-    {
-      for (i = parser->toknext - 1; i >= 0; i--)
-      {
-        /* Unmatched opened object or array */
-        if (tokens[i].start != -1 && tokens[i].end == -1)
-        {
-          return JSMN_ERROR_PART;
-        }
-      }
-    }
-
-    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
-char *jsmn(const char *key, const char *value)
-{
-  int length = strlen(key) + strlen(value) + 8;         // Calculate required length
-  char *result = (char *)malloc(length * sizeof(char)); // Allocate memory
-  if (result == NULL)
-  {
-    return NULL; // Handle memory allocation failure
-  }
-  snprintf(result, length, "{\"%s\":\"%s\"}", key, value);
-  return result; // Caller is responsible for freeing this memory
-}
-
-// Helper function to compare JSON keys
-int jsoneq(const char *json, jsmntok_t *tok, const char *s)
-{
-  if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start &&
-      strncmp(json + tok->start, s, tok->end - tok->start) == 0)
-  {
-    return 0;
-  }
-  return -1;
-}
-
-// return the value of the key in the JSON data
-char *get_json_value(char *key, char *json_data, uint32_t max_tokens)
-{
-  // Parse the JSON feed
-  if (json_data != NULL)
-  {
-    jsmn_parser parser;
-    jsmn_init(&parser);
-
-    // Allocate tokens array on the heap
-    jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * max_tokens);
-    if (tokens == NULL)
-    {
-      FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
-      return NULL;
-    }
-
-    int ret = jsmn_parse(&parser, json_data, strlen(json_data), tokens, max_tokens);
-    if (ret < 0)
-    {
-      // Handle parsing errors
-      FURI_LOG_E("JSMM.H", "Failed to parse JSON: %d", ret);
-      free(tokens);
-      return NULL;
-    }
-
-    // Ensure that the root element is an object
-    if (ret < 1 || tokens[0].type != JSMN_OBJECT)
-    {
-      FURI_LOG_E("JSMM.H", "Root element is not an object.");
-      free(tokens);
-      return NULL;
-    }
-
-    // Loop through the tokens to find the key
-    for (int i = 1; i < ret; i++)
-    {
-      if (jsoneq(json_data, &tokens[i], key) == 0)
-      {
-        // We found the key. Now, return the associated value.
-        int length = tokens[i + 1].end - tokens[i + 1].start;
-        char *value = malloc(length + 1);
-        if (value == NULL)
-        {
-          FURI_LOG_E("JSMM.H", "Failed to allocate memory for value.");
-          free(tokens);
-          return NULL;
-        }
-        strncpy(value, json_data + tokens[i + 1].start, length);
-        value[length] = '\0'; // Null-terminate the string
-
-        free(tokens); // Free the token array
-        return value; // Return the extracted value
-      }
-    }
-
-    // Free the token array if key was not found
-    free(tokens);
-  }
-  else
-  {
-    FURI_LOG_E("JSMM.H", "JSON data is NULL");
-  }
-  FURI_LOG_E("JSMM.H", "Failed to find the key in the JSON.");
-  return NULL; // Return NULL if something goes wrong
-}
-
-// Revised get_json_array_value function
-char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t max_tokens)
-{
-  // Retrieve the array string for the given key
-  char *array_str = get_json_value(key, json_data, max_tokens);
-  if (array_str == NULL)
-  {
-    FURI_LOG_E("JSMM.H", "Failed to get array for key: %s", key);
-    return NULL;
-  }
-
-  // Initialize the JSON parser
-  jsmn_parser parser;
-  jsmn_init(&parser);
-
-  // Allocate memory for JSON tokens
-  jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * max_tokens);
-  if (tokens == NULL)
-  {
-    FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
-    free(array_str);
-    return NULL;
-  }
-
-  // Parse the JSON array
-  int ret = jsmn_parse(&parser, array_str, strlen(array_str), tokens, max_tokens);
-  if (ret < 0)
-  {
-    FURI_LOG_E("JSMM.H", "Failed to parse JSON array: %d", ret);
-    free(tokens);
-    free(array_str);
-    return NULL;
-  }
-
-  // Ensure the root element is an array
-  if (ret < 1 || tokens[0].type != JSMN_ARRAY)
-  {
-    FURI_LOG_E("JSMM.H", "Value for key '%s' is not an array.", key);
-    free(tokens);
-    free(array_str);
-    return NULL;
-  }
-
-  // Check if the index is within bounds
-  if (index >= (uint32_t)tokens[0].size)
-  {
-    FURI_LOG_E("JSMM.H", "Index %lu out of bounds for array with size %d.", (unsigned long)index, tokens[0].size);
-    free(tokens);
-    free(array_str);
-    return NULL;
-  }
-
-  // Locate the token corresponding to the desired array element
-  int current_token = 1; // Start after the array token
-  for (uint32_t i = 0; i < index; i++)
-  {
-    if (tokens[current_token].type == JSMN_OBJECT)
-    {
-      // For objects, skip all key-value pairs
-      current_token += 1 + 2 * tokens[current_token].size;
-    }
-    else if (tokens[current_token].type == JSMN_ARRAY)
-    {
-      // For nested arrays, skip all elements
-      current_token += 1 + tokens[current_token].size;
-    }
-    else
-    {
-      // For primitive types, simply move to the next token
-      current_token += 1;
-    }
-
-    // Safety check to prevent out-of-bounds
-    if (current_token >= ret)
-    {
-      FURI_LOG_E("JSMM.H", "Unexpected end of tokens while traversing array.");
-      free(tokens);
-      free(array_str);
-      return NULL;
-    }
-  }
-
-  // Extract the array element
-  jsmntok_t element = tokens[current_token];
-  int length = element.end - element.start;
-  char *value = malloc(length + 1);
-  if (value == NULL)
-  {
-    FURI_LOG_E("JSMM.H", "Failed to allocate memory for array element.");
-    free(tokens);
-    free(array_str);
-    return NULL;
-  }
-
-  // Copy the element value to a new string
-  strncpy(value, array_str + element.start, length);
-  value[length] = '\0'; // Null-terminate the string
-
-  // Clean up
-  free(tokens);
-  free(array_str);
-
-  return value;
-}
-
-// 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)
-{
-  // Retrieve the array string for the given key
-  char *array_str = get_json_value(key, json_data, max_tokens);
-  if (array_str == NULL)
-  {
-    FURI_LOG_E("JSMM.H", "Failed to get array for key: %s", key);
-    return NULL;
-  }
-
-  // Initialize the JSON parser
-  jsmn_parser parser;
-  jsmn_init(&parser);
-
-  // Allocate memory for JSON tokens
-  jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * max_tokens); // Allocate on the heap
-  if (tokens == NULL)
-  {
-    FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
-    free(array_str);
-    return NULL;
-  }
-
-  // Parse the JSON array
-  int ret = jsmn_parse(&parser, array_str, strlen(array_str), tokens, max_tokens);
-  if (ret < 0)
-  {
-    FURI_LOG_E("JSMM.H", "Failed to parse JSON array: %d", ret);
-    free(tokens);
-    free(array_str);
-    return NULL;
-  }
-
-  // Ensure the root element is an array
-  if (tokens[0].type != JSMN_ARRAY)
-  {
-    FURI_LOG_E("JSMM.H", "Value for key '%s' is not an array.", key);
-    free(tokens);
-    free(array_str);
-    return NULL;
-  }
-
-  // Allocate memory for the array of values (maximum possible)
-  int array_size = tokens[0].size;
-  char **values = malloc(array_size * sizeof(char *));
-  if (values == NULL)
-  {
-    FURI_LOG_E("JSMM.H", "Failed to allocate memory for array of values.");
-    free(tokens);
-    free(array_str);
-    return NULL;
-  }
-
-  int actual_num_values = 0;
-
-  // Traverse the array and extract all object values
-  int current_token = 1; // Start after the array token
-  for (int i = 0; i < array_size; i++)
-  {
-    if (current_token >= ret)
-    {
-      FURI_LOG_E("JSMM.H", "Unexpected end of tokens while traversing array.");
-      break;
-    }
-
-    jsmntok_t element = tokens[current_token];
-
-    if (element.type != JSMN_OBJECT)
-    {
-      FURI_LOG_E("JSMM.H", "Array element %d is not an object, skipping.", i);
-      // Skip this element
-      current_token += 1;
-      continue;
-    }
-
-    int length = element.end - element.start;
-
-    // Allocate a new string for the value and copy the data
-    char *value = malloc(length + 1);
-    if (value == NULL)
-    {
-      FURI_LOG_E("JSMM.H", "Failed to allocate memory for array element.");
-      for (int j = 0; j < actual_num_values; j++)
-      {
-        free(values[j]);
-      }
-      free(values);
-      free(tokens);
-      free(array_str);
-      return NULL;
-    }
-
-    strncpy(value, array_str + element.start, length);
-    value[length] = '\0'; // Null-terminate the string
-
-    values[actual_num_values] = value;
-    actual_num_values++;
-
-    // Skip all tokens related to this object to avoid misparsing
-    current_token += 1 + (2 * element.size); // Each key-value pair consumes two tokens
-  }
-
-  *num_values = actual_num_values;
-
-  // Reallocate the values array to actual_num_values if necessary
-  if (actual_num_values < array_size)
-  {
-    char **reduced_values = realloc(values, actual_num_values * sizeof(char *));
-    if (reduced_values != NULL)
-    {
-      values = reduced_values;
-    }
-
-    // Free the remaining values
-    for (int i = actual_num_values; i < array_size; i++)
-    {
-      free(values[i]);
-    }
-  }
-
-  // Clean up
-  free(tokens);
-  free(array_str);
-  return values;
-}
-
-#endif /* JB_JSMN_EDIT */

+ 747 - 0
jsmn/jsmn.c

@@ -0,0 +1,747 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2010 Serge Zaitsev
+ *
+ * [License text continues...]
+ */
+
+#include <jsmn/jsmn.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Allocates a fresh unused token from the token pool.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
+                                   const size_t num_tokens)
+{
+    jsmntok_t *tok;
+
+    if (parser->toknext >= num_tokens)
+    {
+        return NULL;
+    }
+    tok = &tokens[parser->toknext++];
+    tok->start = tok->end = -1;
+    tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+    tok->parent = -1;
+#endif
+    return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
+                            const int start, const int end)
+{
+    token->type = type;
+    token->start = start;
+    token->end = end;
+    token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+                                const size_t len, jsmntok_t *tokens,
+                                const size_t num_tokens)
+{
+    jsmntok_t *token;
+    int start;
+
+    start = parser->pos;
+
+    for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
+    {
+        switch (js[parser->pos])
+        {
+#ifndef JSMN_STRICT
+        /* In strict mode primitive must be followed by "," or "}" or "]" */
+        case ':':
+#endif
+        case '\t':
+        case '\r':
+        case '\n':
+        case ' ':
+        case ',':
+        case ']':
+        case '}':
+            goto found;
+        default:
+            /* to quiet a warning from gcc*/
+            break;
+        }
+        if (js[parser->pos] < 32 || js[parser->pos] >= 127)
+        {
+            parser->pos = start;
+            return JSMN_ERROR_INVAL;
+        }
+    }
+#ifdef JSMN_STRICT
+    /* In strict mode primitive must be followed by a comma/object/array */
+    parser->pos = start;
+    return JSMN_ERROR_PART;
+#endif
+
+found:
+    if (tokens == NULL)
+    {
+        parser->pos--;
+        return 0;
+    }
+    token = jsmn_alloc_token(parser, tokens, num_tokens);
+    if (token == NULL)
+    {
+        parser->pos = start;
+        return JSMN_ERROR_NOMEM;
+    }
+    jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+    token->parent = parser->toksuper;
+#endif
+    parser->pos--;
+    return 0;
+}
+
+/**
+ * Fills next token with JSON string.
+ */
+static int jsmn_parse_string(jsmn_parser *parser, const char *js,
+                             const size_t len, jsmntok_t *tokens,
+                             const size_t num_tokens)
+{
+    jsmntok_t *token;
+
+    int start = parser->pos;
+
+    /* Skip starting quote */
+    parser->pos++;
+
+    for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
+    {
+        char c = js[parser->pos];
+
+        /* Quote: end of string */
+        if (c == '\"')
+        {
+            if (tokens == NULL)
+            {
+                return 0;
+            }
+            token = jsmn_alloc_token(parser, tokens, num_tokens);
+            if (token == NULL)
+            {
+                parser->pos = start;
+                return JSMN_ERROR_NOMEM;
+            }
+            jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+            token->parent = parser->toksuper;
+#endif
+            return 0;
+        }
+
+        /* Backslash: Quoted symbol expected */
+        if (c == '\\' && parser->pos + 1 < len)
+        {
+            int i;
+            parser->pos++;
+            switch (js[parser->pos])
+            {
+            /* Allowed escaped symbols */
+            case '\"':
+            case '/':
+            case '\\':
+            case 'b':
+            case 'f':
+            case 'r':
+            case 'n':
+            case 't':
+                break;
+            /* Allows escaped symbol \uXXXX */
+            case 'u':
+                parser->pos++;
+                for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++)
+                {
+                    /* If it isn't a hex character we have an error */
+                    if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+                          (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+                          (js[parser->pos] >= 97 && js[parser->pos] <= 102)))
+                    { /* a-f */
+                        parser->pos = start;
+                        return JSMN_ERROR_INVAL;
+                    }
+                    parser->pos++;
+                }
+                parser->pos--;
+                break;
+            /* Unexpected symbol */
+            default:
+                parser->pos = start;
+                return JSMN_ERROR_INVAL;
+            }
+        }
+    }
+    parser->pos = start;
+    return JSMN_ERROR_PART;
+}
+
+/**
+ * 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, const char *js, const size_t len,
+               jsmntok_t *tokens, const unsigned int num_tokens)
+{
+    int r;
+    int i;
+    jsmntok_t *token;
+    int count = parser->toknext;
+
+    for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
+    {
+        char c;
+        jsmntype_t type;
+
+        c = js[parser->pos];
+        switch (c)
+        {
+        case '{':
+        case '[':
+            count++;
+            if (tokens == NULL)
+            {
+                break;
+            }
+            token = jsmn_alloc_token(parser, tokens, num_tokens);
+            if (token == NULL)
+            {
+                return JSMN_ERROR_NOMEM;
+            }
+            if (parser->toksuper != -1)
+            {
+                jsmntok_t *t = &tokens[parser->toksuper];
+#ifdef JSMN_STRICT
+                /* In strict mode an object or array can't become a key */
+                if (t->type == JSMN_OBJECT)
+                {
+                    return JSMN_ERROR_INVAL;
+                }
+#endif
+                t->size++;
+#ifdef JSMN_PARENT_LINKS
+                token->parent = parser->toksuper;
+#endif
+            }
+            token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+            token->start = parser->pos;
+            parser->toksuper = parser->toknext - 1;
+            break;
+        case '}':
+        case ']':
+            if (tokens == NULL)
+            {
+                break;
+            }
+            type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+            if (parser->toknext < 1)
+            {
+                return JSMN_ERROR_INVAL;
+            }
+            token = &tokens[parser->toknext - 1];
+            for (;;)
+            {
+                if (token->start != -1 && token->end == -1)
+                {
+                    if (token->type != type)
+                    {
+                        return JSMN_ERROR_INVAL;
+                    }
+                    token->end = parser->pos + 1;
+                    parser->toksuper = token->parent;
+                    break;
+                }
+                if (token->parent == -1)
+                {
+                    if (token->type != type || parser->toksuper == -1)
+                    {
+                        return JSMN_ERROR_INVAL;
+                    }
+                    break;
+                }
+                token = &tokens[token->parent];
+            }
+#else
+            for (i = parser->toknext - 1; i >= 0; i--)
+            {
+                token = &tokens[i];
+                if (token->start != -1 && token->end == -1)
+                {
+                    if (token->type != type)
+                    {
+                        return JSMN_ERROR_INVAL;
+                    }
+                    parser->toksuper = -1;
+                    token->end = parser->pos + 1;
+                    break;
+                }
+            }
+            /* Error if unmatched closing bracket */
+            if (i == -1)
+            {
+                return JSMN_ERROR_INVAL;
+            }
+            for (; i >= 0; i--)
+            {
+                token = &tokens[i];
+                if (token->start != -1 && token->end == -1)
+                {
+                    parser->toksuper = i;
+                    break;
+                }
+            }
+#endif
+            break;
+        case '\"':
+            r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+            if (r < 0)
+            {
+                return r;
+            }
+            count++;
+            if (parser->toksuper != -1 && tokens != NULL)
+            {
+                tokens[parser->toksuper].size++;
+            }
+            break;
+        case '\t':
+        case '\r':
+        case '\n':
+        case ' ':
+            break;
+        case ':':
+            parser->toksuper = parser->toknext - 1;
+            break;
+        case ',':
+            if (tokens != NULL && parser->toksuper != -1 &&
+                tokens[parser->toksuper].type != JSMN_ARRAY &&
+                tokens[parser->toksuper].type != JSMN_OBJECT)
+            {
+#ifdef JSMN_PARENT_LINKS
+                parser->toksuper = tokens[parser->toksuper].parent;
+#else
+                for (i = parser->toknext - 1; i >= 0; i--)
+                {
+                    if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT)
+                    {
+                        if (tokens[i].start != -1 && tokens[i].end == -1)
+                        {
+                            parser->toksuper = i;
+                            break;
+                        }
+                    }
+                }
+#endif
+            }
+            break;
+#ifdef JSMN_STRICT
+        /* In strict mode primitives are: numbers and booleans */
+        case '-':
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case 't':
+        case 'f':
+        case 'n':
+            /* And they must not be keys of the object */
+            if (tokens != NULL && parser->toksuper != -1)
+            {
+                const jsmntok_t *t = &tokens[parser->toksuper];
+                if (t->type == JSMN_OBJECT ||
+                    (t->type == JSMN_STRING && t->size != 0))
+                {
+                    return JSMN_ERROR_INVAL;
+                }
+            }
+#else
+        /* In non-strict mode every unquoted value is a primitive */
+        default:
+#endif
+            r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+            if (r < 0)
+            {
+                return r;
+            }
+            count++;
+            if (parser->toksuper != -1 && tokens != NULL)
+            {
+                tokens[parser->toksuper].size++;
+            }
+            break;
+
+#ifdef JSMN_STRICT
+        /* Unexpected char in strict mode */
+        default:
+            return JSMN_ERROR_INVAL;
+#endif
+        }
+    }
+
+    if (tokens != NULL)
+    {
+        for (i = parser->toknext - 1; i >= 0; i--)
+        {
+            /* Unmatched opened object or array */
+            if (tokens[i].start != -1 && tokens[i].end == -1)
+            {
+                return JSMN_ERROR_PART;
+            }
+        }
+    }
+
+    return count;
+}
+
+// Helper function to create a JSON object
+char *jsmn(const char *key, const char *value)
+{
+    int length = strlen(key) + strlen(value) + 8;         // Calculate required length
+    char *result = (char *)malloc(length * sizeof(char)); // Allocate memory
+    if (result == NULL)
+    {
+        return NULL; // Handle memory allocation failure
+    }
+    snprintf(result, length, "{\"%s\":\"%s\"}", key, value);
+    return result; // Caller is responsible for freeing this memory
+}
+
+// Helper function to compare JSON keys
+int jsoneq(const char *json, jsmntok_t *tok, const char *s)
+{
+    if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start &&
+        strncmp(json + tok->start, s, tok->end - tok->start) == 0)
+    {
+        return 0;
+    }
+    return -1;
+}
+
+// Return the value of the key in the JSON data
+char *get_json_value(char *key, char *json_data, uint32_t max_tokens)
+{
+    // Parse the JSON feed
+    if (json_data != NULL)
+    {
+        jsmn_parser parser;
+        jsmn_init(&parser);
+
+        // Allocate tokens array on the heap
+        jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * max_tokens);
+        if (tokens == NULL)
+        {
+            FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
+            return NULL;
+        }
+
+        int ret = jsmn_parse(&parser, json_data, strlen(json_data), tokens, max_tokens);
+        if (ret < 0)
+        {
+            // Handle parsing errors
+            FURI_LOG_E("JSMM.H", "Failed to parse JSON: %d", ret);
+            free(tokens);
+            return NULL;
+        }
+
+        // Ensure that the root element is an object
+        if (ret < 1 || tokens[0].type != JSMN_OBJECT)
+        {
+            FURI_LOG_E("JSMM.H", "Root element is not an object.");
+            free(tokens);
+            return NULL;
+        }
+
+        // Loop through the tokens to find the key
+        for (int i = 1; i < ret; i++)
+        {
+            if (jsoneq(json_data, &tokens[i], key) == 0)
+            {
+                // We found the key. Now, return the associated value.
+                int length = tokens[i + 1].end - tokens[i + 1].start;
+                char *value = malloc(length + 1);
+                if (value == NULL)
+                {
+                    FURI_LOG_E("JSMM.H", "Failed to allocate memory for value.");
+                    free(tokens);
+                    return NULL;
+                }
+                strncpy(value, json_data + tokens[i + 1].start, length);
+                value[length] = '\0'; // Null-terminate the string
+
+                free(tokens); // Free the token array
+                return value; // Return the extracted value
+            }
+        }
+
+        // Free the token array if key was not found
+        free(tokens);
+    }
+    else
+    {
+        FURI_LOG_E("JSMM.H", "JSON data is NULL");
+    }
+    FURI_LOG_E("JSMM.H", "Failed to find the key in the JSON.");
+    return NULL; // Return NULL if something goes wrong
+}
+
+// Revised get_json_array_value function
+char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t max_tokens)
+{
+    // Retrieve the array string for the given key
+    char *array_str = get_json_value(key, json_data, max_tokens);
+    if (array_str == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to get array for key: %s", key);
+        return NULL;
+    }
+
+    // Initialize the JSON parser
+    jsmn_parser parser;
+    jsmn_init(&parser);
+
+    // Allocate memory for JSON tokens
+    jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * max_tokens);
+    if (tokens == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
+        free(array_str);
+        return NULL;
+    }
+
+    // Parse the JSON array
+    int ret = jsmn_parse(&parser, array_str, strlen(array_str), tokens, max_tokens);
+    if (ret < 0)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to parse JSON array: %d", ret);
+        free(tokens);
+        free(array_str);
+        return NULL;
+    }
+
+    // Ensure the root element is an array
+    if (ret < 1 || tokens[0].type != JSMN_ARRAY)
+    {
+        FURI_LOG_E("JSMM.H", "Value for key '%s' is not an array.", key);
+        free(tokens);
+        free(array_str);
+        return NULL;
+    }
+
+    // Check if the index is within bounds
+    if (index >= (uint32_t)tokens[0].size)
+    {
+        FURI_LOG_E("JSMM.H", "Index %lu out of bounds for array with size %d.", (unsigned long)index, tokens[0].size);
+        free(tokens);
+        free(array_str);
+        return NULL;
+    }
+
+    // Locate the token corresponding to the desired array element
+    int current_token = 1; // Start after the array token
+    for (uint32_t i = 0; i < index; i++)
+    {
+        if (tokens[current_token].type == JSMN_OBJECT)
+        {
+            // For objects, skip all key-value pairs
+            current_token += 1 + 2 * tokens[current_token].size;
+        }
+        else if (tokens[current_token].type == JSMN_ARRAY)
+        {
+            // For nested arrays, skip all elements
+            current_token += 1 + tokens[current_token].size;
+        }
+        else
+        {
+            // For primitive types, simply move to the next token
+            current_token += 1;
+        }
+
+        // Safety check to prevent out-of-bounds
+        if (current_token >= ret)
+        {
+            FURI_LOG_E("JSMM.H", "Unexpected end of tokens while traversing array.");
+            free(tokens);
+            free(array_str);
+            return NULL;
+        }
+    }
+
+    // Extract the array element
+    jsmntok_t element = tokens[current_token];
+    int length = element.end - element.start;
+    char *value = malloc(length + 1);
+    if (value == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to allocate memory for array element.");
+        free(tokens);
+        free(array_str);
+        return NULL;
+    }
+
+    // Copy the element value to a new string
+    strncpy(value, array_str + element.start, length);
+    value[length] = '\0'; // Null-terminate the string
+
+    // Clean up
+    free(tokens);
+    free(array_str);
+
+    return value;
+}
+
+// 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)
+{
+    // Retrieve the array string for the given key
+    char *array_str = get_json_value(key, json_data, max_tokens);
+    if (array_str == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to get array for key: %s", key);
+        return NULL;
+    }
+
+    // Initialize the JSON parser
+    jsmn_parser parser;
+    jsmn_init(&parser);
+
+    // Allocate memory for JSON tokens
+    jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * max_tokens); // Allocate on the heap
+    if (tokens == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
+        free(array_str);
+        return NULL;
+    }
+
+    // Parse the JSON array
+    int ret = jsmn_parse(&parser, array_str, strlen(array_str), tokens, max_tokens);
+    if (ret < 0)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to parse JSON array: %d", ret);
+        free(tokens);
+        free(array_str);
+        return NULL;
+    }
+
+    // Ensure the root element is an array
+    if (tokens[0].type != JSMN_ARRAY)
+    {
+        FURI_LOG_E("JSMM.H", "Value for key '%s' is not an array.", key);
+        free(tokens);
+        free(array_str);
+        return NULL;
+    }
+
+    // Allocate memory for the array of values (maximum possible)
+    int array_size = tokens[0].size;
+    char **values = malloc(array_size * sizeof(char *));
+    if (values == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to allocate memory for array of values.");
+        free(tokens);
+        free(array_str);
+        return NULL;
+    }
+
+    int actual_num_values = 0;
+
+    // Traverse the array and extract all object values
+    int current_token = 1; // Start after the array token
+    for (int i = 0; i < array_size; i++)
+    {
+        if (current_token >= ret)
+        {
+            FURI_LOG_E("JSMM.H", "Unexpected end of tokens while traversing array.");
+            break;
+        }
+
+        jsmntok_t element = tokens[current_token];
+
+        if (element.type != JSMN_OBJECT)
+        {
+            FURI_LOG_E("JSMM.H", "Array element %d is not an object, skipping.", i);
+            // Skip this element
+            current_token += 1;
+            continue;
+        }
+
+        int length = element.end - element.start;
+
+        // Allocate a new string for the value and copy the data
+        char *value = malloc(length + 1);
+        if (value == NULL)
+        {
+            FURI_LOG_E("JSMM.H", "Failed to allocate memory for array element.");
+            for (int j = 0; j < actual_num_values; j++)
+            {
+                free(values[j]);
+            }
+            free(values);
+            free(tokens);
+            free(array_str);
+            return NULL;
+        }
+
+        strncpy(value, array_str + element.start, length);
+        value[length] = '\0'; // Null-terminate the string
+
+        values[actual_num_values] = value;
+        actual_num_values++;
+
+        // Skip all tokens related to this object to avoid misparsing
+        current_token += 1 + (2 * element.size); // Each key-value pair consumes two tokens
+    }
+
+    *num_values = actual_num_values;
+
+    // Reallocate the values array to actual_num_values if necessary
+    if (actual_num_values < array_size)
+    {
+        char **reduced_values = realloc(values, actual_num_values * sizeof(char *));
+        if (reduced_values != NULL)
+        {
+            values = reduced_values;
+        }
+
+        // Free the remaining values
+        for (int i = actual_num_values; i < array_size; i++)
+        {
+            free(values[i]);
+        }
+    }
+
+    // Clean up
+    free(tokens);
+    free(array_str);
+    return values;
+}

+ 132 - 0
jsmn/jsmn.h

@@ -0,0 +1,132 @@
+/*
+ * 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 */

+ 17 - 14
flip_weather_parse.h → 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);
@@ -34,14 +34,13 @@ 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 (!got_ip_address)
     if (!got_ip_address)
     {
     {
@@ -55,6 +54,7 @@ static bool flip_weather_handle_ip_address()
         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
@@ -65,7 +65,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())
@@ -80,15 +80,18 @@ static bool flip_weather_handle_ip_address()
     return true;
     return true;
 }
 }
 
 
-static bool send_geo_location_request()
+bool send_geo_location_request()
 {
 {
     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;
@@ -96,7 +99,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)
     {
     {
@@ -118,7 +121,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)
     {
     {

+ 16 - 0
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

+ 1 - 20
uart_text_input.h → text_input/uart_text_input.c

@@ -1,26 +1,9 @@
 // from https://github.com/xMasterX/all-the-plugins/blob/dev/base_pack/uart_terminal/uart_text_input.c
 // from https://github.com/xMasterX/all-the-plugins/blob/dev/base_pack/uart_terminal/uart_text_input.c
 // all credits to xMasterX for the code
 // all credits to xMasterX for the code
-#ifndef UART_TEXT_INPUT_H
-#define UART_TEXT_INPUT_H
-
+#include "uart_text_input.h"
 #include <gui/elements.h>
 #include <gui/elements.h>
 #include "flip_weather_icons.h"
 #include "flip_weather_icons.h"
 #include <furi.h>
 #include <furi.h>
-#include <furi.h>
-#include <furi_hal.h>
-#include <gui/gui.h>
-#include <gui/view.h>
-#include <core/common_defines.h>
-
-/** Text input anonymous structure */
-typedef struct UART_TextInput UART_TextInput;
-typedef void (*UART_TextInputCallback)(void *context);
-typedef bool (*UART_TextInputValidatorCallback)(const char *text, FuriString *error, void *context);
-
-UART_TextInputValidatorCallback
-uart_text_input_get_validator_callback(UART_TextInput *uart_text_input);
-
-void uart_text_input_reset(UART_TextInput *uart_text_input);
 
 
 struct UART_TextInput
 struct UART_TextInput
 {
 {
@@ -799,5 +782,3 @@ void uart_text_input_set_header_text(UART_TextInput *uart_text_input, const char
     with_view_model(
     with_view_model(
         uart_text_input->view, UART_TextInputModel * model, { model->header = text; }, true);
         uart_text_input->view, UART_TextInputModel * model, { model->header = text; }, true);
 }
 }
-
-#endif // UART_TEXT_INPUT_H

+ 83 - 0
text_input/uart_text_input.h

@@ -0,0 +1,83 @@
+#pragma once
+// from https://github.com/xMasterX/all-the-plugins/blob/dev/base_pack/uart_terminal/uart_text_input.c
+// all credits to xMasterX for the code
+#include <gui/view.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+    /** Text input anonymous structure */
+    typedef struct UART_TextInput UART_TextInput;
+    typedef void (*UART_TextInputCallback)(void *context);
+    typedef bool (*UART_TextInputValidatorCallback)(const char *text, FuriString *error, void *context);
+
+    /** Allocate and initialize text input
+     *
+     * This text input is used to enter string
+     *
+     * @return     UART_TextInput instance
+     */
+    UART_TextInput *uart_text_input_alloc();
+
+    /** Deinitialize and free text input
+     *
+     * @param      uart_text_input  UART_TextInput instance
+     */
+    void uart_text_input_free(UART_TextInput *uart_text_input);
+
+    /** Clean text input view Note: this function does not free memory
+     *
+     * @param      uart_text_input  Text input instance
+     */
+    void uart_text_input_reset(UART_TextInput *uart_text_input);
+
+    /** Get text input view
+     *
+     * @param      uart_text_input  UART_TextInput instance
+     *
+     * @return     View instance that can be used for embedding
+     */
+    View *uart_text_input_get_view(UART_TextInput *uart_text_input);
+
+    /** Set text input result callback
+     *
+     * @param      uart_text_input          UART_TextInput instance
+     * @param      callback            callback fn
+     * @param      callback_context    callback context
+     * @param      text_buffer         pointer to YOUR text buffer, that we going
+     *                                 to modify
+     * @param      text_buffer_size    YOUR text buffer size in bytes. Max string
+     *                                 length will be text_buffer_size-1.
+     * @param      clear_default_text  clear text from text_buffer on first OK
+     *                                 event
+     */
+    void uart_text_input_set_result_callback(
+        UART_TextInput *uart_text_input,
+        UART_TextInputCallback callback,
+        void *callback_context,
+        char *text_buffer,
+        size_t text_buffer_size,
+        bool clear_default_text);
+
+    void uart_text_input_set_validator(
+        UART_TextInput *uart_text_input,
+        UART_TextInputValidatorCallback callback,
+        void *callback_context);
+
+    UART_TextInputValidatorCallback
+    uart_text_input_get_validator_callback(UART_TextInput *uart_text_input);
+
+    void *uart_text_input_get_validator_callback_context(UART_TextInput *uart_text_input);
+
+    /** Set text input header text
+     *
+     * @param      uart_text_input  UART_TextInput instance
+     * @param      text        text to be shown
+     */
+    void uart_text_input_set_header_text(UART_TextInput *uart_text_input, const char *text);
+
+#ifdef __cplusplus
+}
+#endif