Bläddra i källkod

Merge flip_wifi from https://github.com/jblanked/FlipWiFi

Willy-JL 1 år sedan
förälder
incheckning
5c339d6b4e

BIN
flip_wifi/.DS_Store


+ 4 - 0
flip_wifi/CHANGELOG.md

@@ -1,2 +1,6 @@
+## v1.1
+- Fixed freeze when configs didn't exist (thanks WillyJL)
+- Improved memory allocation
+
 ## v1.0
 - Initial Release

+ 1 - 2
flip_wifi/README.md

@@ -7,7 +7,6 @@ FlipWiFi is the companion app for the popular FlipperHTTP flash, originally intr
 - WiFi Dev Board or Raspberry Pi Pico W for Flipper Zero with FlipperHTTP Flash: https://github.com/jblanked/FlipperHTTP
 - WiFi Access Point
 
-
 ## Features
 
 - **Scan**: Discover nearby WiFi networks and add them to your list.
@@ -17,7 +16,7 @@ FlipWiFi is the companion app for the popular FlipperHTTP flash, originally intr
 
 FlipWiFi automatically allocates the necessary resources and initializes settings upon launch. If WiFi settings have been previously configured, they are loaded automatically for easy access. You can also edit the list of WiFi settings by downloading and modifying the "wifi_list.txt" file located in the "/SD/apps_data/flip_wifi/" directory. To use the app:
 
-1. **Flash the WiFi Dev Board**: Follow the instructions to flash the WiFi Dev Board with FlipperHTTP: https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP
+1. **Flash the WiFi Dev Board**: Follow the instructions to flash the WiFi Dev Board with FlipperHTTP: https://github.com/jblanked/FlipperHTTP
 2. **Install the App**: Download FlipWiFi from the App Store.
 3. **Launch FlipWiFi**: Open the app on your Flipper Zero.
 4. Connect, review, and save WiFi networks.

+ 14 - 9
flip_wifi/flip_wifi_i.h → flip_wifi/alloc/flip_wifi_alloc.c

@@ -1,8 +1,7 @@
-#ifndef FLIP_WIFI_I_H
-#define FLIP_WIFI_I_H
+#include <alloc/flip_wifi_alloc.h>
 
 // Function to allocate resources for the FlipWiFiApp
-static FlipWiFiApp* flip_wifi_app_alloc() {
+FlipWiFiApp* flip_wifi_app_alloc() {
     FlipWiFiApp* app = (FlipWiFiApp*)malloc(sizeof(FlipWiFiApp));
 
     Gui* gui = furi_record_open(RECORD_GUI);
@@ -89,7 +88,7 @@ static FlipWiFiApp* flip_wifi_app_alloc() {
     if(!easy_flipper_set_widget(
            &app->widget_info,
            FlipWiFiViewAbout,
-           "FlipWiFi v1.0\n-----\nFlipperHTTP companion app.\nScan and save WiFi networks.\n-----\nwww.github.com/jblanked",
+           "FlipWiFi v1.1\n-----\nFlipperHTTP companion app.\nScan and save WiFi networks.\n-----\nwww.github.com/jblanked",
            callback_to_submenu_main,
            &app->view_dispatcher)) {
         return NULL;
@@ -149,7 +148,7 @@ static FlipWiFiApp* flip_wifi_app_alloc() {
     if(!easy_flipper_set_submenu(
            &app->submenu_main,
            FlipWiFiViewSubmenuMain,
-           "FlipWiFi v1.0",
+           "FlipWiFi v1.1",
            easy_flipper_callback_exit_app,
            &app->view_dispatcher)) {
         return NULL;
@@ -201,6 +200,16 @@ static FlipWiFiApp* flip_wifi_app_alloc() {
     // Load the playlist from storage
     if(!load_playlist(&app->wifi_playlist)) {
         FURI_LOG_E(TAG, "Failed to load playlist");
+
+        // playlist is empty?
+        submenu_reset(app->submenu_wifi_saved);
+        submenu_set_header(app->submenu_wifi_saved, "Saved APs");
+        submenu_add_item(
+            app->submenu_wifi_saved,
+            "[Add Network]",
+            FlipWiFiSubmenuIndexWiFiSavedAddSSID,
+            callback_submenu_choices,
+            app);
     } else {
         // Update the submenu
         flip_wifi_redraw_submenu_saved(app);
@@ -209,9 +218,5 @@ static FlipWiFiApp* flip_wifi_app_alloc() {
     // Switch to the main view
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenuMain);
 
-    app_instance = app;
-
     return app;
 }
-
-#endif // FLIP_WIFI_I_H

+ 11 - 0
flip_wifi/alloc/flip_wifi_alloc.h

@@ -0,0 +1,11 @@
+#ifndef FLIP_WIFI_I_H
+#define FLIP_WIFI_I_H
+
+#include <flip_wifi.h>
+#include <flip_storage/flip_wifi_storage.h>
+#include <callback/flip_wifi_callback.h>
+
+// Function to allocate resources for the FlipWiFiApp
+FlipWiFiApp* flip_wifi_app_alloc();
+
+#endif // FLIP_WIFI_I_H

+ 6 - 9
flip_wifi/app.c

@@ -1,8 +1,5 @@
-#include <flip_wifi_e.h>
-#include <flip_wifi_storage.h>
-#include <flip_wifi_callback.h>
-#include <flip_wifi_i.h>
-#include <flip_wifi_free.h>
+#include <flip_wifi.h>
+#include <alloc/flip_wifi_alloc.h>
 
 // Entry point for the FlipWiFi application
 int32_t flip_wifi_main(void* p) {
@@ -10,8 +7,8 @@ int32_t flip_wifi_main(void* p) {
     UNUSED(p);
 
     // Initialize the FlipWiFi application
-    FlipWiFiApp* app = flip_wifi_app_alloc();
-    if(!app) {
+    app_instance = flip_wifi_app_alloc();
+    if(!app_instance) {
         FURI_LOG_E(TAG, "Failed to allocate FlipWiFiApp");
         return -1;
     }
@@ -22,10 +19,10 @@ int32_t flip_wifi_main(void* p) {
     }
 
     // Run the view dispatcher
-    view_dispatcher_run(app->view_dispatcher);
+    view_dispatcher_run(app_instance->view_dispatcher);
 
     // Free the resources used by the FlipWiFi application
-    flip_wifi_app_free(app);
+    flip_wifi_app_free(app_instance);
 
     // Return 0 to indicate success
     return 0;

+ 1 - 1
flip_wifi/application.fam

@@ -9,6 +9,6 @@ App(
     fap_icon_assets="assets",
     fap_author="JBlanked",
     fap_weburl="https://github.com/jblanked/FlipWiFi",
-    fap_version="1.0",
+    fap_version="1.1",
     fap_description="FlipperHTTP companion app.",
 )

BIN
flip_wifi/assets/01-home.png


+ 12 - 20
flip_wifi/flip_wifi_callback.h → flip_wifi/callback/flip_wifi_callback.c

@@ -1,15 +1,9 @@
-#ifndef FLIP_WIFI_CALLBACK_H
-#define FLIP_WIFI_CALLBACK_H
-#include "flip_wifi_icons.h"
+#include <callback/flip_wifi_callback.h>
 
-FlipWiFiApp* app_instance;
-
-static void callback_submenu_choices(void* context, uint32_t index);
-// array to store each SSID
 char* ssid_list[64];
 uint32_t ssid_index = 0;
 
-static void flip_wifi_redraw_submenu_saved(FlipWiFiApp* app) {
+void flip_wifi_redraw_submenu_saved(FlipWiFiApp* app) {
     // re draw the saved submenu
     submenu_reset(app->submenu_wifi_saved);
     submenu_set_header(app->submenu_wifi_saved, "Saved APs");
@@ -29,7 +23,7 @@ static void flip_wifi_redraw_submenu_saved(FlipWiFiApp* app) {
     }
 }
 
-static uint32_t callback_to_submenu_main(void* context) {
+uint32_t callback_to_submenu_main(void* context) {
     if(!context) {
         FURI_LOG_E(TAG, "Context is NULL");
         return VIEW_NONE;
@@ -38,7 +32,7 @@ static uint32_t callback_to_submenu_main(void* context) {
     ssid_index = 0;
     return FlipWiFiViewSubmenuMain;
 }
-static uint32_t callback_to_submenu_scan(void* context) {
+uint32_t callback_to_submenu_scan(void* context) {
     if(!context) {
         FURI_LOG_E(TAG, "Context is NULL");
         return VIEW_NONE;
@@ -47,7 +41,7 @@ static uint32_t callback_to_submenu_scan(void* context) {
     ssid_index = 0;
     return FlipWiFiViewSubmenuScan;
 }
-static uint32_t callback_to_submenu_saved(void* context) {
+uint32_t callback_to_submenu_saved(void* context) {
     if(!context) {
         FURI_LOG_E(TAG, "Context is NULL");
         return VIEW_NONE;
@@ -74,7 +68,7 @@ void popup_callback_main(void* context) {
 }
 
 // Callback for drawing the main screen
-static void flip_wifi_view_draw_callback_scan(Canvas* canvas, void* model) {
+void flip_wifi_view_draw_callback_scan(Canvas* canvas, void* model) {
     UNUSED(model);
     canvas_clear(canvas);
     canvas_set_font(canvas, FontPrimary);
@@ -84,7 +78,7 @@ static void flip_wifi_view_draw_callback_scan(Canvas* canvas, void* model) {
     canvas_draw_icon(canvas, 96, 53, &I_ButtonRight_4x7);
     canvas_draw_str_aligned(canvas, 103, 54, AlignLeft, AlignTop, "Add");
 }
-static void flip_wifi_view_draw_callback_saved(Canvas* canvas, void* model) {
+void flip_wifi_view_draw_callback_saved(Canvas* canvas, void* model) {
     UNUSED(model);
     canvas_clear(canvas);
     canvas_set_font(canvas, FontPrimary);
@@ -284,7 +278,7 @@ bool flip_wifi_handle_scan(FlipWiFiApp* app) {
 
     return true;
 }
-static void callback_submenu_choices(void* context, uint32_t index) {
+void callback_submenu_choices(void* context, uint32_t index) {
     FlipWiFiApp* app = (FlipWiFiApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipWiFiApp is NULL");
@@ -358,7 +352,7 @@ static void callback_submenu_choices(void* context, uint32_t index) {
     }
 }
 
-static void flip_wifi_text_updated_password_scan(void* context) {
+void flip_wifi_text_updated_password_scan(void* context) {
     FlipWiFiApp* app = (FlipWiFiApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipWiFiApp is NULL");
@@ -389,7 +383,7 @@ static void flip_wifi_text_updated_password_scan(void* context) {
     // switch to back to the scan view
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenuScan);
 }
-static void flip_wifi_text_updated_password_saved(void* context) {
+void flip_wifi_text_updated_password_saved(void* context) {
     FlipWiFiApp* app = (FlipWiFiApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipWiFiApp is NULL");
@@ -416,7 +410,7 @@ static void flip_wifi_text_updated_password_saved(void* context) {
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenuSaved);
 }
 
-static void flip_wifi_text_updated_add_ssid(void* context) {
+void flip_wifi_text_updated_add_ssid(void* context) {
     FlipWiFiApp* app = (FlipWiFiApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipWiFiApp is NULL");
@@ -435,7 +429,7 @@ static void flip_wifi_text_updated_add_ssid(void* context) {
     // do nothing for now, go to the next text input to set the password
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInputSavedAddPassword);
 }
-static void flip_wifi_text_updated_add_password(void* context) {
+void flip_wifi_text_updated_add_password(void* context) {
     FlipWiFiApp* app = (FlipWiFiApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipWiFiApp is NULL");
@@ -467,5 +461,3 @@ static void flip_wifi_text_updated_add_password(void* context) {
     // switch to back to the saved view
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenuSaved);
 }
-
-#endif // FLIP_WIFI_CALLBACK_H

+ 51 - 0
flip_wifi/callback/flip_wifi_callback.h

@@ -0,0 +1,51 @@
+#ifndef FLIP_WIFI_CALLBACK_H
+#define FLIP_WIFI_CALLBACK_H
+
+#include <flip_wifi.h>
+#include <flip_storage/flip_wifi_storage.h>
+#include <flip_wifi_icons.h>
+
+// array to store each SSID
+extern char* ssid_list[64];
+extern uint32_t ssid_index;
+
+void flip_wifi_redraw_submenu_saved(FlipWiFiApp* app);
+
+uint32_t callback_to_submenu_main(void* context);
+
+uint32_t callback_to_submenu_scan(void* context);
+
+uint32_t callback_to_submenu_saved(void* context);
+
+void popup_callback_saved(void* context);
+
+void popup_callback_main(void* context);
+
+// Callback for drawing the main screen
+void flip_wifi_view_draw_callback_scan(Canvas* canvas, void* model);
+
+void flip_wifi_view_draw_callback_saved(Canvas* canvas, void* model);
+
+// Input callback for the view (async input handling)
+bool flip_wifi_view_input_callback_scan(InputEvent* event, void* context);
+
+// Input callback for the view (async input handling)
+bool flip_wifi_view_input_callback_saved(InputEvent* event, void* context);
+
+// Function to trim leading and trailing whitespace
+// Returns the trimmed start pointer and updates the length
+char* trim_whitespace(char* start, size_t* length);
+
+bool flip_wifi_handle_scan(FlipWiFiApp* app);
+
+void callback_submenu_choices(void* context, uint32_t index);
+
+void flip_wifi_text_updated_password_scan(void* context);
+
+void flip_wifi_text_updated_password_saved(void* context);
+
+void flip_wifi_text_updated_add_ssid(void* context);
+
+void flip_wifi_text_updated_add_password(void* context);
+
+#endif // FLIP_WIFI_CALLBACK_H

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

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

+ 261 - 0
flip_wifi/easy_flipper/easy_flipper.h

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

+ 13 - 17
flip_wifi/flip_wifi_storage.h → flip_wifi/flip_storage/flip_wifi_storage.c

@@ -1,11 +1,17 @@
-#ifndef FLIP_WIFI_STORAGE_H
-#define FLIP_WIFI_STORAGE_H
+#include <flip_storage/flip_wifi_storage.h>
 
-// define the paths for all of the FlipperHTTP apps
-#define WIFI_SSID_LIST_PATH STORAGE_EXT_PATH_PREFIX "/apps_data/flip_wifi/wifi_list.txt"
+char* app_ids[8] = {
+    "flip_wifi",
+    "flip_store",
+    "flip_social",
+    "flip_trader",
+    "flip_weather",
+    "flip_library",
+    "web_crawler",
+    "flip_rss"};
 
 // Function to save the playlist
-void save_playlist(const WiFiPlaylist* playlist) {
+void save_playlist(WiFiPlaylist* playlist) {
     if(!playlist) {
         FURI_LOG_E(TAG, "Playlist is NULL");
         return;
@@ -192,20 +198,11 @@ bool load_playlist(WiFiPlaylist* playlist) {
     return true;
 }
 
-char* app_ids[7] = {
-    "flip_wifi",
-    "flip_store",
-    "flip_social",
-    "flip_trader",
-    "flip_weather",
-    "flip_library",
-    "web_crawler"};
-
 void save_settings(const char* ssid, const char* password) {
     char edited_directory_path[128];
     char edited_file_path[128];
 
-    for(size_t i = 0; i < 7; i++) {
+    for(size_t i = 0; i < 8; i++) {
         // Construct the directory and file paths for the current app
         snprintf(
             edited_directory_path,
@@ -270,6 +267,7 @@ void save_settings(const char* ssid, const char* password) {
             file_size = 0;
             buffer = NULL;
         }
+
         storage_file_free(file);
 
         // Prepare new SSID and Password
@@ -389,5 +387,3 @@ void save_settings(const char* ssid, const char* password) {
         furi_record_close(RECORD_STORAGE);
     }
 }
-
-#endif

+ 18 - 0
flip_wifi/flip_storage/flip_wifi_storage.h

@@ -0,0 +1,18 @@
+#ifndef FLIP_WIFI_STORAGE_H
+#define FLIP_WIFI_STORAGE_H
+
+#include <flip_wifi.h>
+
+// define the paths for all of the FlipperHTTP apps
+#define WIFI_SSID_LIST_PATH STORAGE_EXT_PATH_PREFIX "/apps_data/flip_wifi/wifi_list.txt"
+
+extern char* app_ids[8];
+
+// Function to save the playlist
+void save_playlist(WiFiPlaylist* playlist);
+
+// Function to load the playlist
+bool load_playlist(WiFiPlaylist* playlist);
+
+void save_settings(const char* ssid, const char* password);
+#endif

+ 4 - 5
flip_wifi/flip_wifi_free.h → flip_wifi/flip_wifi.c

@@ -1,8 +1,9 @@
-#ifndef FLIP_WIFI_FREE_H
-#define FLIP_WIFI_FREE_H
+#include "flip_wifi.h"
+
+FlipWiFiApp* app_instance = NULL;
 
 // Function to free the resources used by FlipWiFiApp
-static void flip_wifi_app_free(FlipWiFiApp* app) {
+void flip_wifi_app_free(FlipWiFiApp* app) {
     if(!app) {
         FURI_LOG_E(TAG, "FlipWiFiApp is NULL");
         return;
@@ -80,5 +81,3 @@ static void flip_wifi_app_free(FlipWiFiApp* app) {
     // free the app
     if(app) free(app);
 }
-
-#endif // FLIP_WIFI_FREE_H

+ 7 - 2
flip_wifi/flip_wifi_e.h → flip_wifi/flip_wifi.h

@@ -1,8 +1,8 @@
 #ifndef FLIP_WIFI_E_H
 #define FLIP_WIFI_E_H
 
-#include <flipper_http.h>
-#include <easy_flipper.h>
+#include <flipper_http/flipper_http.h>
+#include <easy_flipper/easy_flipper.h>
 #include <storage/storage.h>
 
 #define TAG               "FlipWiFi"
@@ -82,4 +82,9 @@ typedef struct {
     WiFiPlaylist wifi_playlist; // The playlist of wifi networks
 } FlipWiFiApp;
 
+extern FlipWiFiApp* app_instance;
+
+// Function to free the resources used by FlipWiFiApp
+void flip_wifi_app_free(FlipWiFiApp* app);
+
 #endif // FLIP_WIFI_E_H

+ 395 - 376
flip_wifi/flipper_http.h → flip_wifi/flipper_http/flipper_http.c

@@ -1,116 +1,219 @@
-// 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               "FlipWiFi" // change this to your app name
-#define http_tag               "flip_wifi" // 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            128 // UART RX buffer size
-#define RX_LINE_BUFFER_SIZE    256 // UART RX line buffer size (increase for large responses)
-
-// 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_save_received_data(size_t bytes_received, const char line_buffer[]);
-static char* trim(const char* str);
-
-// 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
-    uint8_t rx_buf[RX_BUF_SIZE]; // Buffer for received data
-    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;
-
-    // Timer-related members
-    FuriTimer* get_timeout_timer; // Timer for HTTP request timeout
-    char* received_data; // Buffer to store received data
-
-    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
-} FlipperHTTP;
-
+#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
+// 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) {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    File* file = storage_file_alloc(storage);
+
+    if(start_new_file) {
+        // Open the file in write mode
+        if(!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+            FURI_LOG_E(HTTP_TAG, "Failed to open file for writing: %s", file_path);
+            storage_file_free(file);
+            furi_record_close(RECORD_STORAGE);
+            return false;
+        }
+    } else {
+        // Open the file in append mode
+        if(!storage_file_open(file, file_path, FSAM_WRITE, FSOM_OPEN_APPEND)) {
+            FURI_LOG_E(HTTP_TAG, "Failed to open file for appending: %s", file_path);
+            storage_file_free(file);
+            furi_record_close(RECORD_STORAGE);
+            return false;
+        }
+    }
+
+    // Write the data to the file
+    if(storage_file_write(file, data, data_size) != data_size) {
+        FURI_LOG_E(HTTP_TAG, "Failed to append data to file");
+        storage_file_close(file);
+        storage_file_free(file);
+        furi_record_close(RECORD_STORAGE);
+        return false;
+    }
+
+    storage_file_close(file);
+    storage_file_free(file);
+    furi_record_close(RECORD_STORAGE);
+    return true;
+}
+
+FuriString* flipper_http_load_from_file(char* file_path) {
+    // Open the storage record
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    if(!storage) {
+        FURI_LOG_E(HTTP_TAG, "Failed to open storage record");
+        return NULL;
+    }
+
+    // Allocate a file handle
+    File* file = storage_file_alloc(storage);
+    if(!file) {
+        FURI_LOG_E(HTTP_TAG, "Failed to allocate storage file");
+        furi_record_close(RECORD_STORAGE);
+        return NULL;
+    }
+
+    // Open the file for reading
+    if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
+        storage_file_free(file);
+        furi_record_close(RECORD_STORAGE);
+        return NULL; // Return false if the file does not exist
+    }
+
+    // Allocate a FuriString to hold the received data
+    FuriString* str_result = furi_string_alloc();
+    if(!str_result) {
+        FURI_LOG_E(HTTP_TAG, "Failed to allocate FuriString");
+        storage_file_close(file);
+        storage_file_free(file);
+        furi_record_close(RECORD_STORAGE);
+        return NULL;
+    }
+
+    // Reset the FuriString to ensure it's empty before reading
+    furi_string_reset(str_result);
+
+    // Define a buffer to hold the read data
+    uint8_t* buffer = (uint8_t*)malloc(MAX_FILE_SHOW);
+    if(!buffer) {
+        FURI_LOG_E(HTTP_TAG, "Failed to allocate buffer");
+        furi_string_free(str_result);
+        storage_file_close(file);
+        storage_file_free(file);
+        furi_record_close(RECORD_STORAGE);
+        return NULL;
+    }
+
+    // Read data into the buffer
+    size_t read_count = storage_file_read(file, buffer, MAX_FILE_SHOW);
+    if(storage_file_get_error(file) != FSE_OK) {
+        FURI_LOG_E(HTTP_TAG, "Error reading from file.");
+        furi_string_free(str_result);
+        storage_file_close(file);
+        storage_file_free(file);
+        furi_record_close(RECORD_STORAGE);
+        return NULL;
+    }
+
+    // Append each byte to the FuriString
+    for(size_t i = 0; i < read_count; i++) {
+        furi_string_push_back(str_result, buffer[i]);
+    }
+
+    // Check if there is more data beyond the maximum size
+    char extra_byte;
+    storage_file_read(file, &extra_byte, 1);
+
+    // Clean up
+    storage_file_close(file);
+    storage_file_free(file);
+    furi_record_close(RECORD_STORAGE);
+    free(buffer);
+    return str_result;
+}
+
+// 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) {
+    UNUSED(context);
+    size_t rx_line_pos = 0;
+    static size_t file_buffer_len = 0;
+
+    while(1) {
+        uint32_t events = furi_thread_flags_wait(
+            WorkerEvtStop | WorkerEvtRxDone, FuriFlagWaitAny, FuriWaitForever);
+        if(events & WorkerEvtStop) break;
+        if(events & WorkerEvtRxDone) {
+            // Continuously read from the stream buffer until it's empty
+            while(!furi_stream_buffer_is_empty(fhttp.flipper_http_stream)) {
+                // Read one byte at a time
+                char c = 0;
+                size_t received = furi_stream_buffer_receive(fhttp.flipper_http_stream, &c, 1, 0);
+
+                if(received == 0) {
+                    // No more data to read
+                    break;
+                }
+
+                // Append the received byte to the file if saving is enabled
+                if(fhttp.save_bytes) {
+                    // Add byte to the buffer
+                    file_buffer[file_buffer_len++] = c;
+                    // Write to file if buffer is full
+                    if(file_buffer_len >= FILE_BUFFER_SIZE) {
+                        if(!flipper_http_append_to_file(
+                               file_buffer, file_buffer_len, false, fhttp.file_path)) {
+                            FURI_LOG_E(HTTP_TAG, "Failed to append data to file");
+                        }
+                        file_buffer_len = 0;
+                    }
+                }
+
+                // Handle line buffering only if callback is set (text data)
+                if(fhttp.handle_rx_line_cb) {
+                    // Handle line buffering
+                    if(c == '\n' || rx_line_pos >= RX_LINE_BUFFER_SIZE - 1) {
+                        rx_line_buffer[rx_line_pos] = '\0'; // Null-terminate the line
+
+                        // Invoke the callback with the complete line
+                        fhttp.handle_rx_line_cb(rx_line_buffer, fhttp.callback_context);
+
+                        // Reset the line buffer position
+                        rx_line_pos = 0;
+                    } else {
+                        rx_line_buffer[rx_line_pos++] = c; // Add character to the line buffer
+                    }
+                }
+            }
+        }
+    }
 
-// fhttp.received_data holds the received data from HTTP requests
-// fhttp.last_response holds the last received data from the UART, which could be [GET/END], [POST/END], [PUT/END], [DELETE/END], etc
+    if(fhttp.save_bytes) {
+        // Write the remaining data to the file
+        if(file_buffer_len > 0) {
+            if(!flipper_http_append_to_file(file_buffer, file_buffer_len, false, fhttp.file_path)) {
+                FURI_LOG_E(HTTP_TAG, "Failed to append remaining data to file");
+            }
+        }
+    }
 
+    // remove [POST/END] and/or [GET/END] from the file
+    if(fhttp.save_bytes) {
+        char* end = NULL;
+        if((end = strstr(fhttp.file_path, "[POST/END]")) != NULL) {
+            *end = '\0';
+        } else if((end = strstr(fhttp.file_path, "[GET/END]")) != NULL) {
+            *end = '\0';
+        }
+    }
+
+    // remove newline from the from the end of the file
+    if(fhttp.save_bytes) {
+        char* end = NULL;
+        if((end = strstr(fhttp.file_path, "\n")) != NULL) {
+            *end = '\0';
+        }
+    }
+
+    // Reset the file buffer length
+    file_buffer_len = 0;
+
+    return 0;
+}
 // Timer callback function
 /**
  * @brief      Callback function for the GET timeout timer.
@@ -128,12 +231,6 @@ void get_timeout_timer_callback(void* context) {
     fhttp.started_receiving_put = false;
     fhttp.started_receiving_delete = false;
 
-    // Free received data if any
-    if(fhttp.received_data) {
-        free(fhttp.received_data);
-        fhttp.received_data = NULL;
-    }
-
     // Update UART state
     fhttp.state = ISSUE;
 }
@@ -147,7 +244,7 @@ void get_timeout_timer_callback(void* context) {
  * @param      context   The context to pass to 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,
     FuriHalSerialRxEvent event,
     void* context) {
@@ -159,54 +256,6 @@ static void _flipper_http_rx_callback(
     }
 }
 
-// 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.
- */
-static int32_t flipper_http_worker(void* context) {
-    UNUSED(context);
-    size_t rx_line_pos = 0;
-    char* rx_line_buffer = (char*)malloc(RX_LINE_BUFFER_SIZE);
-
-    if(!rx_line_buffer) {
-        // Handle malloc failure
-        FURI_LOG_E(HTTP_TAG, "Failed to allocate memory for rx_line_buffer");
-        return -1;
-    }
-
-    while(1) {
-        uint32_t events = furi_thread_flags_wait(
-            WorkerEvtStop | WorkerEvtRxDone, FuriFlagWaitAny, FuriWaitForever);
-        if(events & WorkerEvtStop) break;
-        if(events & WorkerEvtRxDone) {
-            size_t len = furi_stream_buffer_receive(
-                fhttp.flipper_http_stream, fhttp.rx_buf, RX_BUF_SIZE, 0);
-            for(size_t i = 0; i < len; i++) {
-                char c = fhttp.rx_buf[i];
-                if(c == '\n' || rx_line_pos >= RX_LINE_BUFFER_SIZE - 1) {
-                    rx_line_buffer[rx_line_pos] = '\0';
-                    // Invoke the callback with the complete line
-                    if(fhttp.handle_rx_line_cb) {
-                        fhttp.handle_rx_line_cb(rx_line_buffer, fhttp.callback_context);
-                    }
-                    // Reset the line buffer
-                    rx_line_pos = 0;
-                } else {
-                    rx_line_buffer[rx_line_pos++] = c;
-                }
-            }
-        }
-    }
-
-    // Free the allocated memory before exiting the thread
-    free(rx_line_buffer);
-
-    return 0;
-}
-
 // UART initialization function
 /**
  * @brief      Initialize UART.
@@ -299,6 +348,12 @@ bool flipper_http_init(FlipperHTTP_Callback callback, void* context) {
     // Set the timer thread priority if needed
     furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated);
 
+    fhttp.last_response = (char*)malloc(RX_BUF_SIZE);
+    if(!fhttp.last_response) {
+        FURI_LOG_E(HTTP_TAG, "Failed to allocate memory for last_response.");
+        return false;
+    }
+
     // FURI_LOG_I(HTTP_TAG, "UART initialized successfully.");
     return true;
 }
@@ -338,12 +393,6 @@ void flipper_http_deinit() {
         fhttp.get_timeout_timer = NULL;
     }
 
-    // Free received data if any
-    if(fhttp.received_data) {
-        free(fhttp.received_data);
-        fhttp.received_data = NULL;
-    }
-
     // Free the last response
     if(fhttp.last_response) {
         free(fhttp.last_response);
@@ -369,13 +418,13 @@ bool flipper_http_send_data(const char* data) {
 
     // Create a buffer with data + '\n'
     size_t send_length = data_length + 1; // +1 for '\n'
-    if(send_length > 256) { // Ensure buffer size is sufficient
+    if(send_length > 512) { // Ensure buffer size is sufficient
         FURI_LOG_E("FlipperHTTP", "Data too long to send over FHTTP.");
         return false;
     }
 
-    char send_buffer[257]; // 256 + 1 for safety
-    strncpy(send_buffer, data, 256);
+    char send_buffer[513]; // 512 + 1 for safety
+    strncpy(send_buffer, data, 512);
     send_buffer[data_length] = '\n'; // Append newline
     send_buffer[data_length + 1] = '\0'; // Null-terminate
 
@@ -693,7 +742,7 @@ bool flipper_http_get_request_with_headers(const char* url, const char* headers)
     }
 
     // Prepare GET request command with headers
-    char command[256];
+    char command[512];
     int ret = snprintf(
         command, sizeof(command), "[GET/HTTP]{\"url\":\"%s\",\"headers\":%s}", url, headers);
     if(ret < 0 || ret >= (int)sizeof(command)) {
@@ -710,6 +759,38 @@ bool flipper_http_get_request_with_headers(const char* url, const char* headers)
     // The response will be handled asynchronously via the callback
     return true;
 }
+// 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) {
+    if(!url || !headers) {
+        FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_get_request_bytes.");
+        return false;
+    }
+
+    // Prepare GET request command with headers
+    char command[256];
+    int ret = snprintf(
+        command, sizeof(command), "[GET/BYTES]{\"url\":\"%s\",\"headers\":%s}", url, headers);
+    if(ret < 0 || ret >= (int)sizeof(command)) {
+        FURI_LOG_E("FlipperHTTP", "Failed to format GET request command with headers.");
+        return false;
+    }
+
+    // Send GET request via UART
+    if(!flipper_http_send_data(command)) {
+        FURI_LOG_E("FlipperHTTP", "Failed to send GET request command with headers.");
+        return false;
+    }
+
+    // The response will be handled asynchronously via the callback
+    return true;
+}
 // Function to send a POST request with headers
 /**
  * @brief      Send a POST request to the specified URL.
@@ -753,6 +834,45 @@ bool flipper_http_post_request_with_headers(
     // The response will be handled asynchronously via the callback
     return true;
 }
+// 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) {
+    if(!url || !headers || !payload) {
+        FURI_LOG_E(
+            "FlipperHTTP", "Invalid arguments provided to flipper_http_post_request_bytes.");
+        return false;
+    }
+
+    // Prepare POST request command with headers and data
+    char command[256];
+    int ret = snprintf(
+        command,
+        sizeof(command),
+        "[POST/BYTES]{\"url\":\"%s\",\"headers\":%s,\"payload\":%s}",
+        url,
+        headers,
+        payload);
+    if(ret < 0 || ret >= (int)sizeof(command)) {
+        FURI_LOG_E("FlipperHTTP", "Failed to format POST request command with headers and data.");
+        return false;
+    }
+
+    // Send POST request via UART
+    if(!flipper_http_send_data(command)) {
+        FURI_LOG_E("FlipperHTTP", "Failed to send POST request command with headers and data.");
+        return false;
+    }
+
+    // The response will be handled asynchronously via the callback
+    return true;
+}
 // Function to send a PUT request with headers
 /**
  * @brief      Send a PUT request to the specified URL.
@@ -856,7 +976,13 @@ void flipper_http_rx_callback(const char* line, void* context) {
     // Trim the received line to check if it's empty
     char* trimmed_line = trim(line);
     if(trimmed_line != NULL && trimmed_line[0] != '\0') {
-        fhttp.last_response = (char*)line;
+        // if the line is not [GET/END] or [POST/END] or [PUT/END] or [DELETE/END]
+        if(strstr(trimmed_line, "[GET/END]") == NULL &&
+           strstr(trimmed_line, "[POST/END]") == NULL &&
+           strstr(trimmed_line, "[PUT/END]") == NULL &&
+           strstr(trimmed_line, "[DELETE/END]") == NULL) {
+            strncpy(fhttp.last_response, trimmed_line, RX_BUF_SIZE);
+        }
     }
     free(trimmed_line); // Free the allocated memory for trimmed_line
 
@@ -865,7 +991,7 @@ void flipper_http_rx_callback(const char* line, void* context) {
     }
 
     // Uncomment below line to log the data received over UART
-    //(HTTP_TAG, "Received UART line: %s", line);
+    // FURI_LOG_I(HTTP_TAG, "Received UART line: %s", line);
 
     // Check if we've started receiving data from a GET request
     if(fhttp.started_receiving_get) {
@@ -876,44 +1002,24 @@ void flipper_http_rx_callback(const char* line, void* context) {
             FURI_LOG_I(HTTP_TAG, "GET request completed.");
             // Stop the timer since we've completed the GET request
             furi_timer_stop(fhttp.get_timeout_timer);
-
-            if(fhttp.received_data) {
-                // uncomment if you want to save the received data to the external storage
-                // flipper_http_save_received_data(strlen(fhttp.received_data), fhttp.received_data);
-                fhttp.started_receiving_get = false;
-                fhttp.just_started_get = false;
-                fhttp.state = IDLE;
-                return;
-            } else {
-                FURI_LOG_E(HTTP_TAG, "No data received.");
-                fhttp.started_receiving_get = false;
-                fhttp.just_started_get = false;
-                fhttp.state = IDLE;
-                return;
-            }
+            fhttp.started_receiving_get = false;
+            fhttp.just_started_get = false;
+            fhttp.state = IDLE;
+            fhttp.save_bytes = false;
+            fhttp.is_bytes_request = false;
+            fhttp.save_received_data = false;
+            return;
         }
 
         // Append the new line to the existing data
-        if(fhttp.received_data == NULL) {
-            fhttp.received_data =
-                (char*)malloc(strlen(line) + 2); // +2 for newline and null terminator
-            if(fhttp.received_data) {
-                strcpy(fhttp.received_data, line);
-                fhttp.received_data[strlen(line)] = '\n'; // Add newline
-                fhttp.received_data[strlen(line) + 1] = '\0'; // Null terminator
-            }
-        } else {
-            size_t current_len = strlen(fhttp.received_data);
-            size_t new_size = current_len + strlen(line) + 2; // +2 for newline and null terminator
-            fhttp.received_data = (char*)realloc(fhttp.received_data, new_size);
-            if(fhttp.received_data) {
-                memcpy(
-                    fhttp.received_data + current_len,
-                    line,
-                    strlen(line)); // Copy line at the end of the current data
-                fhttp.received_data[current_len + strlen(line)] = '\n'; // Add newline
-                fhttp.received_data[current_len + strlen(line) + 1] = '\0'; // Null terminator
-            }
+        if(fhttp.save_received_data &&
+           !flipper_http_append_to_file(
+               line, strlen(line), !fhttp.just_started_get, fhttp.file_path)) {
+            FURI_LOG_E(HTTP_TAG, "Failed to append data to file.");
+            fhttp.started_receiving_get = false;
+            fhttp.just_started_get = false;
+            fhttp.state = IDLE;
+            return;
         }
 
         if(!fhttp.just_started_get) {
@@ -931,44 +1037,24 @@ void flipper_http_rx_callback(const char* line, void* context) {
             FURI_LOG_I(HTTP_TAG, "POST request completed.");
             // Stop the timer since we've completed the POST request
             furi_timer_stop(fhttp.get_timeout_timer);
-
-            if(fhttp.received_data) {
-                // uncomment if you want to save the received data to the external storage
-                // flipper_http_save_received_data(strlen(fhttp.received_data), fhttp.received_data);
-                fhttp.started_receiving_post = false;
-                fhttp.just_started_post = false;
-                fhttp.state = IDLE;
-                return;
-            } else {
-                FURI_LOG_E(HTTP_TAG, "No data received.");
-                fhttp.started_receiving_post = false;
-                fhttp.just_started_post = false;
-                fhttp.state = IDLE;
-                return;
-            }
+            fhttp.started_receiving_post = false;
+            fhttp.just_started_post = false;
+            fhttp.state = IDLE;
+            fhttp.save_bytes = false;
+            fhttp.is_bytes_request = false;
+            fhttp.save_received_data = false;
+            return;
         }
 
         // Append the new line to the existing data
-        if(fhttp.received_data == NULL) {
-            fhttp.received_data =
-                (char*)malloc(strlen(line) + 2); // +2 for newline and null terminator
-            if(fhttp.received_data) {
-                strcpy(fhttp.received_data, line);
-                fhttp.received_data[strlen(line)] = '\n'; // Add newline
-                fhttp.received_data[strlen(line) + 1] = '\0'; // Null terminator
-            }
-        } else {
-            size_t current_len = strlen(fhttp.received_data);
-            size_t new_size = current_len + strlen(line) + 2; // +2 for newline and null terminator
-            fhttp.received_data = (char*)realloc(fhttp.received_data, new_size);
-            if(fhttp.received_data) {
-                memcpy(
-                    fhttp.received_data + current_len,
-                    line,
-                    strlen(line)); // Copy line at the end of the current data
-                fhttp.received_data[current_len + strlen(line)] = '\n'; // Add newline
-                fhttp.received_data[current_len + strlen(line) + 1] = '\0'; // Null terminator
-            }
+        if(fhttp.save_received_data &&
+           !flipper_http_append_to_file(
+               line, strlen(line), !fhttp.just_started_post, fhttp.file_path)) {
+            FURI_LOG_E(HTTP_TAG, "Failed to append data to file.");
+            fhttp.started_receiving_post = false;
+            fhttp.just_started_post = false;
+            fhttp.state = IDLE;
+            return;
         }
 
         if(!fhttp.just_started_post) {
@@ -986,44 +1072,24 @@ void flipper_http_rx_callback(const char* line, void* context) {
             FURI_LOG_I(HTTP_TAG, "PUT request completed.");
             // Stop the timer since we've completed the PUT request
             furi_timer_stop(fhttp.get_timeout_timer);
-
-            if(fhttp.received_data) {
-                // uncomment if you want to save the received data to the external storage
-                // flipper_http_save_received_data(strlen(fhttp.received_data), fhttp.received_data);
-                fhttp.started_receiving_put = false;
-                fhttp.just_started_put = false;
-                fhttp.state = IDLE;
-                return;
-            } else {
-                FURI_LOG_E(HTTP_TAG, "No data received.");
-                fhttp.started_receiving_put = false;
-                fhttp.just_started_put = false;
-                fhttp.state = IDLE;
-                return;
-            }
+            fhttp.started_receiving_put = false;
+            fhttp.just_started_put = false;
+            fhttp.state = IDLE;
+            fhttp.save_bytes = false;
+            fhttp.is_bytes_request = false;
+            fhttp.save_received_data = false;
+            return;
         }
 
         // Append the new line to the existing data
-        if(fhttp.received_data == NULL) {
-            fhttp.received_data =
-                (char*)malloc(strlen(line) + 2); // +2 for newline and null terminator
-            if(fhttp.received_data) {
-                strcpy(fhttp.received_data, line);
-                fhttp.received_data[strlen(line)] = '\n'; // Add newline
-                fhttp.received_data[strlen(line) + 1] = '\0'; // Null terminator
-            }
-        } else {
-            size_t current_len = strlen(fhttp.received_data);
-            size_t new_size = current_len + strlen(line) + 2; // +2 for newline and null terminator
-            fhttp.received_data = (char*)realloc(fhttp.received_data, new_size);
-            if(fhttp.received_data) {
-                memcpy(
-                    fhttp.received_data + current_len,
-                    line,
-                    strlen(line)); // Copy line at the end of the current data
-                fhttp.received_data[current_len + strlen(line)] = '\n'; // Add newline
-                fhttp.received_data[current_len + strlen(line) + 1] = '\0'; // Null terminator
-            }
+        if(fhttp.save_received_data &&
+           !flipper_http_append_to_file(
+               line, strlen(line), !fhttp.just_started_put, fhttp.file_path)) {
+            FURI_LOG_E(HTTP_TAG, "Failed to append data to file.");
+            fhttp.started_receiving_put = false;
+            fhttp.just_started_put = false;
+            fhttp.state = IDLE;
+            return;
         }
 
         if(!fhttp.just_started_put) {
@@ -1041,44 +1107,24 @@ void flipper_http_rx_callback(const char* line, void* context) {
             FURI_LOG_I(HTTP_TAG, "DELETE request completed.");
             // Stop the timer since we've completed the DELETE request
             furi_timer_stop(fhttp.get_timeout_timer);
-
-            if(fhttp.received_data) {
-                // uncomment if you want to save the received data to the external storage
-                // flipper_http_save_received_data(strlen(fhttp.received_data), fhttp.received_data);
-                fhttp.started_receiving_delete = false;
-                fhttp.just_started_delete = false;
-                fhttp.state = IDLE;
-                return;
-            } else {
-                FURI_LOG_E(HTTP_TAG, "No data received.");
-                fhttp.started_receiving_delete = false;
-                fhttp.just_started_delete = false;
-                fhttp.state = IDLE;
-                return;
-            }
+            fhttp.started_receiving_delete = false;
+            fhttp.just_started_delete = false;
+            fhttp.state = IDLE;
+            fhttp.save_bytes = false;
+            fhttp.is_bytes_request = false;
+            fhttp.save_received_data = false;
+            return;
         }
 
         // Append the new line to the existing data
-        if(fhttp.received_data == NULL) {
-            fhttp.received_data =
-                (char*)malloc(strlen(line) + 2); // +2 for newline and null terminator
-            if(fhttp.received_data) {
-                strcpy(fhttp.received_data, line);
-                fhttp.received_data[strlen(line)] = '\n'; // Add newline
-                fhttp.received_data[strlen(line) + 1] = '\0'; // Null terminator
-            }
-        } else {
-            size_t current_len = strlen(fhttp.received_data);
-            size_t new_size = current_len + strlen(line) + 2; // +2 for newline and null terminator
-            fhttp.received_data = (char*)realloc(fhttp.received_data, new_size);
-            if(fhttp.received_data) {
-                memcpy(
-                    fhttp.received_data + current_len,
-                    line,
-                    strlen(line)); // Copy line at the end of the current data
-                fhttp.received_data[current_len + strlen(line)] = '\n'; // Add newline
-                fhttp.received_data[current_len + strlen(line) + 1] = '\0'; // Null terminator
-            }
+        if(fhttp.save_received_data &&
+           !flipper_http_append_to_file(
+               line, strlen(line), !fhttp.just_started_delete, fhttp.file_path)) {
+            FURI_LOG_E(HTTP_TAG, "Failed to append data to file.");
+            fhttp.started_receiving_delete = false;
+            fhttp.just_started_delete = false;
+            fhttp.state = IDLE;
+            return;
         }
 
         if(!fhttp.just_started_delete) {
@@ -1089,7 +1135,7 @@ void flipper_http_rx_callback(const char* line, void* context) {
 
     // Handle different types of responses
     if(strstr(line, "[SUCCESS]") != NULL || strstr(line, "[CONNECTED]") != NULL) {
-        // FURI_LOG_I(HTTP_TAG, "Operation succeeded.");
+        FURI_LOG_I(HTTP_TAG, "Operation succeeded.");
     } else if(strstr(line, "[INFO]") != NULL) {
         FURI_LOG_I(HTTP_TAG, "Received info: %s", line);
 
@@ -1101,28 +1147,28 @@ void flipper_http_rx_callback(const char* line, void* context) {
         fhttp.started_receiving_get = true;
         furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
         fhttp.state = RECEIVING;
-        fhttp.received_data = NULL;
+        // for GET request, save data only if it's a bytes request
+        fhttp.save_bytes = fhttp.is_bytes_request;
         return;
     } else if(strstr(line, "[POST/SUCCESS]") != NULL) {
         FURI_LOG_I(HTTP_TAG, "POST request succeeded.");
         fhttp.started_receiving_post = true;
         furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
         fhttp.state = RECEIVING;
-        fhttp.received_data = NULL;
+        // for POST request, save data only if it's a bytes request
+        fhttp.save_bytes = fhttp.is_bytes_request;
         return;
     } else if(strstr(line, "[PUT/SUCCESS]") != NULL) {
         FURI_LOG_I(HTTP_TAG, "PUT request succeeded.");
         fhttp.started_receiving_put = true;
         furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
         fhttp.state = RECEIVING;
-        fhttp.received_data = NULL;
         return;
     } else if(strstr(line, "[DELETE/SUCCESS]") != NULL) {
         FURI_LOG_I(HTTP_TAG, "DELETE request succeeded.");
         fhttp.started_receiving_delete = true;
         furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
         fhttp.state = RECEIVING;
-        fhttp.received_data = NULL;
         return;
     } else if(strstr(line, "[DISCONNECTED]") != NULL) {
         FURI_LOG_I(HTTP_TAG, "WiFi disconnected successfully.");
@@ -1148,60 +1194,7 @@ void flipper_http_rx_callback(const char* line, void* context) {
         fhttp.state = IDLE;
     }
 }
-// Function to save received data to a file
-/**
- * @brief      Save the received data to a file.
- * @return     true if the data was saved successfully, false otherwise.
- * @param      bytes_received  The number of bytes received.
- * @param      line_buffer     The buffer containing the received data.
- * @note       The data will be saved to a file in the STORAGE_EXT_PATH_PREFIX "/apps_data/" http_tag "/received_data.txt" directory.
- */
-bool flipper_http_save_received_data(size_t bytes_received, const char line_buffer[]) {
-    const char* output_file_path = STORAGE_EXT_PATH_PREFIX "/apps_data/" http_tag
-                                                           "/received_data.txt";
-
-    // Ensure the directory exists
-    char directory_path[128];
-    snprintf(
-        directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/" http_tag);
-
-    Storage* _storage = NULL;
-    File* _file = NULL;
-    // Open the storage if not opened already
-    // Initialize storage and create the directory if it doesn't exist
-    _storage = furi_record_open(RECORD_STORAGE);
-    storage_common_mkdir(_storage, directory_path); // Create directory if it doesn't exist
-    _file = storage_file_alloc(_storage);
-
-    // Open file for writing and append data line by line
-    if(!storage_file_open(_file, output_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
-        FURI_LOG_E(HTTP_TAG, "Failed to open output file for writing.");
-        storage_file_free(_file);
-        furi_record_close(RECORD_STORAGE);
-        return false;
-    }
-
-    // Write each line received from the UART to the file
-    if(bytes_received > 0 && _file) {
-        storage_file_write(_file, line_buffer, bytes_received);
-        storage_file_write(_file, "\n", 1); // Add a newline after each line
-    } else {
-        FURI_LOG_E(HTTP_TAG, "No data received.");
-        return false;
-    }
-
-    if(_file) {
-        storage_file_close(_file);
-        storage_file_free(_file);
-        _file = NULL;
-    }
-    if(_storage) {
-        furi_record_close(RECORD_STORAGE);
-        _storage = NULL;
-    }
 
-    return true;
-}
 // Function to trim leading and trailing spaces and newlines from a constant string
 char* trim(const char* str) {
     const char* end;
@@ -1236,4 +1229,30 @@ char* trim(const char* str) {
     return trimmed_str;
 }
 
-#endif // FLIPPER_HTTP_H
+/**
+ * @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)) {
+    if(http_request()) // start the async request
+    {
+        furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
+        fhttp.state = RECEIVING;
+    } else {
+        FURI_LOG_E(HTTP_TAG, "Failed to send request");
+        return false;
+    }
+    while(fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0) {
+        // Wait for the request to be received
+        furi_delay_ms(100);
+    }
+    furi_timer_stop(fhttp.get_timeout_timer);
+    if(!parse_json()) // parse the JSON before switching to the view (synchonous)
+    {
+        FURI_LOG_E(HTTP_TAG, "Failed to parse the JSON...");
+        return false;
+    }
+    return true;
+}

+ 360 - 0
flip_wifi/flipper_http/flipper_http.h

@@ -0,0 +1,360 @@
+// flipper_http.h
+#ifndef FLIPPER_HTTP_H
+#define FLIPPER_HTTP_H
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi_hal_gpio.h>
+#include <furi_hal_serial.h>
+#include <storage/storage.h>
+
+// STORAGE_EXT_PATH_PREFIX is defined in the Furi SDK as /ext
+
+#define HTTP_TAG               "FlipWiFi" // change this to your app name
+#define http_tag               "flip_wifi" // 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

+ 35 - 138
flip_wifi/jsmn.h → flip_wifi/jsmn/jsmn.c

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

+ 131 - 0
flip_wifi/jsmn/jsmn.h

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