Przeglądaj źródła

update json, use Flipper API for everything

jblanked 1 rok temu
rodzic
commit
da7d5c535c

+ 81 - 177
apps/flip_store_apps.c

@@ -10,18 +10,32 @@ bool flip_store_saved_success = false;
 uint32_t flip_store_category_index = 0;
 
 // define the list of categories
+char *category_ids[] = {
+    "64a69817effe1f448a4053b4", // "Bluetooth",
+    "64971d11be1a76c06747de2f", // "Games",
+    "64971d106617ba37a4bc79b9", // "GPIO",
+    "64971d106617ba37a4bc79b6", // "Infrared",
+    "64971d11be1a76c06747de29", // "iButton",
+    "64971d116617ba37a4bc79bc", // "Media",
+    "64971d10be1a76c06747de26", // "NFC",
+    "64971d10577d519190ede5c2", // "RFID",
+    "64971d0f6617ba37a4bc79b3", // "Sub-GHz",
+    "64971d11577d519190ede5c5", // "Tools",
+    "64971d11be1a76c06747de2c", // "USB",
+};
+
 char *categories[] = {
-    "Bluetooth",
-    "Games",
-    "GPIO",
-    "Infrared",
-    "iButton",
-    "Media",
-    "NFC",
-    "RFID",
-    "Sub-GHz",
-    "Tools",
-    "USB",
+    "Bluetooth", // "64a69817effe1f448a4053b4"
+    "Games",     // "64971d11be1a76c06747de2f"
+    "GPIO",      // "64971d106617ba37a4bc79b9"
+    "Infrared",  // "64971d106617ba37a4bc79b6"
+    "iButton",   // "64971d11be1a76c06747de29"
+    "Media",     // "64971d116617ba37a4bc79bc"
+    "NFC",       // "64971d10be1a76c06747de26"
+    "RFID",      // "64971d10577d519190ede5c2"
+    "Sub-GHz",   // "64971d0f6617ba37a4bc79b3"
+    "Tools",     // "64971d11577d519190ede5c5"
+    "USB",       // "64971d11be1a76c06747de2c"
 };
 
 FlipStoreAppInfo *flip_catalog_alloc()
@@ -67,198 +81,88 @@ bool flip_store_process_app_list(FlipperHTTP *fhttp)
         return false;
     }
 
-    char *data_cstr = (char *)furi_string_get_cstr(feed_data);
-    if (data_cstr == NULL)
+    FuriString *json_data_str = furi_string_alloc();
+    if (!json_data_str)
     {
-        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
-        furi_string_free(feed_data);
-        return false;
+        FURI_LOG_E("Game", "Failed to allocate json_data string");
+        return NULL;
     }
 
-    // Parser state variables
-    bool in_string = false;
-    bool is_escaped = false;
-    bool reading_key = false;
-    bool reading_value = false;
-    bool inside_app_object = false;
-    bool found_name = false, found_id = false, found_build_id = false, found_version = false, found_description = false;
-    char current_key[MAX_KEY_LENGTH] = {0};
-    size_t key_index = 0;
-    char current_value[MAX_VALUE_LENGTH] = {0};
-    size_t value_index = 0;
+    furi_string_cat_str(json_data_str, "{\"json_data\":");
+    furi_string_cat(json_data_str, feed_data);
+    furi_string_free(feed_data);
+    furi_string_cat_str(json_data_str, "}");
+
     int app_count = 0;
-    enum ObjectState object_state = OBJECT_EXPECT_KEY;
-    enum
-    {
-        STATE_SEARCH_APPS_KEY,
-        STATE_SEARCH_ARRAY_START,
-        STATE_READ_ARRAY_ELEMENTS,
-        STATE_DONE
-    } state = STATE_SEARCH_APPS_KEY;
 
-    // Iterate through the data
-    for (size_t i = 0; data_cstr[i] != '\0' && state != STATE_DONE; ++i)
+    // parse the JSON data
+    for (int i = 0; i < MAX_APP_COUNT; i++)
     {
-        char c = data_cstr[i];
-
-        if (is_escaped)
+        FuriString *json_data_array = get_json_array_value_furi("json_data", i, json_data_str);
+        if (!json_data_array)
         {
-            is_escaped = false;
-            if (reading_key && key_index < MAX_KEY_LENGTH - 1)
-            {
-                current_key[key_index++] = c;
-            }
-            else if (reading_value && value_index < MAX_VALUE_LENGTH - 1)
-            {
-                current_value[value_index++] = c;
-            }
-            continue;
+            FURI_LOG_I(TAG, "No more apps to process, i = %d", i);
+            break;
         }
 
-        if (c == '\\')
+        FuriString *app_id = get_json_value_furi("alias", json_data_array); // app_id is stored in the "alias" field
+        if (!app_id)
         {
-            is_escaped = true;
-            continue;
+            FURI_LOG_E(TAG, "Failed to get app_id.");
+            break;
         }
+        snprintf(flip_catalog[i].app_id, MAX_ID_LENGTH, "%s", furi_string_get_cstr(app_id));
+        furi_string_free(app_id);
 
-        if (c == '\"')
+        FuriString *current_version = get_json_value_furi("current_version", json_data_array);
+        if (!current_version)
         {
-            in_string = !in_string;
-            if (in_string)
-            {
-                if (!reading_key && !reading_value)
-                {
-                    if (state == STATE_SEARCH_APPS_KEY || object_state == OBJECT_EXPECT_KEY)
-                    {
-                        reading_key = true;
-                        key_index = 0;
-                        current_key[0] = '\0';
-                    }
-                    else if (object_state == OBJECT_EXPECT_VALUE)
-                    {
-                        reading_value = true;
-                        value_index = 0;
-                        current_value[0] = '\0';
-                    }
-                }
-            }
-            else
-            {
-                if (reading_key)
-                {
-                    reading_key = false;
-                    current_key[key_index] = '\0';
-                    if (state == STATE_SEARCH_APPS_KEY && strcmp(current_key, "apps") == 0)
-                    {
-                        state = STATE_SEARCH_ARRAY_START;
-                    }
-                    else if (inside_app_object)
-                    {
-                        object_state = OBJECT_EXPECT_COLON;
-                    }
-                }
-                else if (reading_value)
-                {
-                    reading_value = false;
-                    current_value[value_index] = '\0';
-
-                    if (inside_app_object)
-                    {
-                        if (strcmp(current_key, "name") == 0)
-                        {
-                            snprintf(flip_catalog[app_count].app_name, MAX_APP_NAME_LENGTH, "%.31s", current_value);
-                            found_name = true;
-                        }
-                        else if (strcmp(current_key, "id") == 0)
-                        {
-                            snprintf(flip_catalog[app_count].app_id, MAX_ID_LENGTH, "%.31s", current_value);
-                            found_id = true;
-                        }
-                        else if (strcmp(current_key, "build_id") == 0)
-                        {
-                            snprintf(flip_catalog[app_count].app_build_id, MAX_ID_LENGTH, "%.31s", current_value);
-                            found_build_id = true;
-                        }
-                        else if (strcmp(current_key, "version") == 0)
-                        {
-                            snprintf(flip_catalog[app_count].app_version, MAX_APP_VERSION_LENGTH, "%.3s", current_value);
-                            found_version = true;
-                        }
-                        else if (strcmp(current_key, "description") == 0)
-                        {
-                            snprintf(flip_catalog[app_count].app_description, MAX_APP_DESCRIPTION_LENGTH, "%.99s", current_value);
-                            found_description = true;
-                        }
-
-                        if (found_name && found_id && found_build_id && found_version && found_description)
-                        {
-                            app_count++;
-                            if (app_count >= MAX_APP_COUNT)
-                            {
-                                FURI_LOG_I(TAG, "Reached maximum app count.");
-                                state = STATE_DONE;
-                                break;
-                            }
-
-                            found_name = found_id = found_build_id = found_version = found_description = false;
-                        }
+            FURI_LOG_E(TAG, "Failed to get current_version.");
+            break;
+        }
 
-                        object_state = OBJECT_EXPECT_COMMA_OR_END;
-                    }
-                }
-            }
-            continue;
+        FuriString *app_name = get_json_value_furi("name", current_version);
+        if (!app_name)
+        {
+            FURI_LOG_E(TAG, "Failed to get app_name.");
+            break;
         }
+        snprintf(flip_catalog[i].app_name, MAX_APP_NAME_LENGTH, "%s", furi_string_get_cstr(app_name));
+        furi_string_free(app_name);
 
-        if (in_string)
+        FuriString *app_description = get_json_value_furi("short_description", current_version);
+        if (!app_description)
         {
-            if (reading_key && key_index < MAX_KEY_LENGTH - 1)
-            {
-                current_key[key_index++] = c;
-            }
-            else if (reading_value && value_index < MAX_VALUE_LENGTH - 1)
-            {
-                current_value[value_index++] = c;
-            }
-            continue;
+            FURI_LOG_E(TAG, "Failed to get app_description.");
+            break;
         }
+        snprintf(flip_catalog[i].app_description, MAX_APP_DESCRIPTION_LENGTH, "%s", furi_string_get_cstr(app_description));
+        furi_string_free(app_description);
 
-        if (state == STATE_SEARCH_ARRAY_START && c == '[')
+        FuriString *app_version = get_json_value_furi("version", current_version);
+        if (!app_version)
         {
-            state = STATE_READ_ARRAY_ELEMENTS;
-            continue;
+            FURI_LOG_E(TAG, "Failed to get app_version.");
+            break;
         }
+        snprintf(flip_catalog[i].app_version, MAX_APP_VERSION_LENGTH, "%s", furi_string_get_cstr(app_version));
+        furi_string_free(app_version);
 
-        if (state == STATE_READ_ARRAY_ELEMENTS)
+        FuriString *_id = get_json_value_furi("_id", current_version);
+        if (!_id)
         {
-            if (c == '{')
-            {
-                inside_app_object = true;
-                object_state = OBJECT_EXPECT_KEY;
-            }
-            else if (c == '}')
-            {
-                inside_app_object = false;
-            }
-            else if (c == ':')
-            {
-                object_state = OBJECT_EXPECT_VALUE;
-            }
-            else if (c == ',')
-            {
-                object_state = OBJECT_EXPECT_KEY;
-            }
-            else if (c == ']')
-            {
-                state = STATE_DONE;
-                break;
-            }
+            FURI_LOG_E(TAG, "Failed to get _id.");
+            break;
         }
+        snprintf(flip_catalog[i].app_build_id, MAX_ID_LENGTH, "%s", furi_string_get_cstr(_id));
+        furi_string_free(_id);
+
+        app_count++;
+        furi_string_free(json_data_array);
+        furi_string_free(current_version);
     }
 
-    // Clean up
-    furi_string_free(feed_data);
-    free(data_cstr);
+    furi_string_free(json_data_str);
     return app_count > 0;
 }
 

+ 1 - 0
apps/flip_store_apps.h

@@ -13,6 +13,7 @@
 #define MAX_APP_VERSION_LENGTH 5
 
 // define the list of categories
+extern char *category_ids[];
 extern char *categories[];
 
 typedef struct

+ 3 - 0
assets/CHANGELOG.md

@@ -1,5 +1,8 @@
 ## v0.8
 - Updated FlipperHTTP to the latest library.
+- Switched to use Flipper catalog API.
+- Added an option to download Video Game Module firmware (FlipperHTTP)
+- Added an option to download Github repositories.
 
 ## v0.7.2
 - Final memory allocation improvements

+ 80 - 86
callback/flip_store_callback.c

@@ -37,81 +37,6 @@ static void flip_store_dl_app_switch_to_view(FlipStoreApp *app)
     flip_store_generic_switch_to_view(app, flip_catalog[app_selected_index].app_name, flip_store_dl_app_fetch, flip_store_dl_app_parse, 1, callback_to_app_category_list, FlipStoreViewLoader);
 }
 //
-static bool flip_store_fetch_app_list(DataLoaderModel *model)
-{
-    if (!model->fhttp)
-    {
-        FURI_LOG_E(TAG, "FlipperHTTP is NULL");
-        return false;
-    }
-    model->fhttp->state = IDLE;
-    flip_catalog_free();
-    snprintf(
-        model->fhttp->file_path,
-        sizeof(model->fhttp->file_path),
-        STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s.json", categories[flip_store_category_index]);
-    model->fhttp->save_received_data = true;
-    model->fhttp->is_bytes_request = false;
-    char url[128];
-    snprintf(url, sizeof(url), "https://www.flipsocial.net/api/flipper/apps/%s/max/", categories[flip_store_category_index]);
-    return flipper_http_get_request_with_headers(model->fhttp, url, "{\"Content-Type\":\"application/json\"}");
-}
-static bool set_appropriate_list(FlipperHTTP *fhttp, Submenu **submenu, FlipStoreApp *app)
-{
-    if (!submenu || !fhttp || !app)
-    {
-        FURI_LOG_E(TAG, "Submenu, FlipperHTTP, or app is NULL");
-        return false;
-    }
-
-    if (!easy_flipper_set_submenu(submenu, FlipStoreViewAppListCategory, categories[flip_store_category_index], callback_to_app_list, &app->view_dispatcher))
-    {
-        return false;
-    }
-    if (flip_store_process_app_list(fhttp) && submenu && flip_catalog)
-    {
-        submenu_reset(*submenu);
-        // add each app name to submenu
-        for (int i = 0; i < MAX_APP_COUNT; i++)
-        {
-            if (strlen(flip_catalog[i].app_name) > 0)
-            {
-                submenu_add_item(*submenu, flip_catalog[i].app_name, FlipStoreSubmenuIndexStartAppList + i, callback_submenu_choices, app);
-            }
-            else
-            {
-                break;
-            }
-        }
-        view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppListCategory);
-        return true;
-    }
-    else
-    {
-        FURI_LOG_E(TAG, "Failed to process the app list");
-        return false;
-    }
-    return false;
-}
-static char *flip_store_parse_app_list(DataLoaderModel *model)
-{
-    if (!model->fhttp)
-    {
-        return "Failed to fetch app list.";
-    }
-    FlipStoreApp *app = (FlipStoreApp *)model->parser_context;
-    if (!app)
-    {
-        FURI_LOG_E(TAG, "FlipStoreApp is NULL");
-        return "Failed to fetch app list.";
-    }
-    return set_appropriate_list(model->fhttp, &app->submenu_app_list_category, app) ? "App list fetched successfully." : "Failed to fetch app list.";
-}
-static void flip_store_switch_to_app_list(FlipStoreApp *app)
-{
-    flip_store_generic_switch_to_view(app, categories[flip_store_category_index], flip_store_fetch_app_list, flip_store_parse_app_list, 1, callback_to_submenu, FlipStoreViewLoader);
-}
-//
 static bool flip_store_fetch_firmware(DataLoaderModel *model)
 {
     if (!model->fhttp)
@@ -813,6 +738,75 @@ uint32_t callback_exit_app(void *context)
     return VIEW_NONE; // Return VIEW_NONE to exit the app
 }
 
+static bool set_appropriate_list(FlipperHTTP *fhttp, FlipStoreApp *app)
+{
+    if (!fhttp || !app)
+    {
+        FURI_LOG_E(TAG, "FlipperHTTP oor app is NULL");
+        return false;
+    }
+
+    if (!easy_flipper_set_submenu(&app->submenu_app_list_category, FlipStoreViewAppListCategory, categories[flip_store_category_index], callback_to_app_list, &app->view_dispatcher))
+    {
+        FURI_LOG_E(TAG, "Failed to set submenu");
+        return false;
+    }
+    if (flip_store_process_app_list(fhttp) && app->submenu_app_list_category && flip_catalog)
+    {
+        submenu_reset(app->submenu_app_list_category);
+        submenu_set_header(app->submenu_app_list_category, categories[flip_store_category_index]);
+        // add each app name to submenu
+        for (int i = 0; i < MAX_APP_COUNT; i++)
+        {
+            if (strlen(flip_catalog[i].app_name) > 0)
+            {
+                submenu_add_item(app->submenu_app_list_category, flip_catalog[i].app_name, FlipStoreSubmenuIndexStartAppList + i, callback_submenu_choices, app);
+            }
+            else
+            {
+                break;
+            }
+        }
+        return true;
+    }
+    else
+    {
+        FURI_LOG_E(TAG, "Failed to process the app list");
+        return false;
+    }
+    return false;
+}
+
+static void fetch_appropiate_app_list(FlipStoreApp *app)
+{
+    FlipperHTTP *fhttp = flipper_http_alloc();
+    if (!fhttp)
+    {
+        FURI_LOG_E(TAG, "Failed to allocate FlipperHTTP");
+        return;
+    }
+    bool fetch_app_list()
+    {
+        fhttp->state = IDLE;
+        flip_catalog_free();
+        snprintf(
+            fhttp->file_path,
+            sizeof(fhttp->file_path),
+            STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s.json", categories[flip_store_category_index]);
+        fhttp->save_received_data = true;
+        fhttp->is_bytes_request = false;
+        char url[256];
+        snprintf(url, sizeof(url), "https://catalog.flipperzero.one/api/v0/0/application?limit=10&is_latest_release_version=true&offset=0&sort_by=updated_at&sort_order=-1&category_id=%s", category_ids[flip_store_category_index]);
+        return flipper_http_get_request_with_headers(fhttp, url, "{\"Content-Type\":\"application/json\"}");
+    }
+    bool parse_app_list()
+    {
+        return set_appropriate_list(fhttp, app);
+    }
+    flipper_http_loading_task(fhttp, fetch_app_list, parse_app_list, FlipStoreViewAppListCategory, FlipStoreViewSubmenuOptions, &app->view_dispatcher);
+    flipper_http_free(fhttp);
+}
+
 void callback_submenu_choices(void *context, uint32_t index)
 {
     FlipStoreApp *app = (FlipStoreApp *)context;
@@ -870,67 +864,67 @@ void callback_submenu_choices(void *context, uint32_t index)
         free_all_views(app, true);
         flip_store_category_index = 0;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     case FlipStoreSubmenuIndexAppListGames:
         free_all_views(app, true);
         flip_store_category_index = 1;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     case FlipStoreSubmenuIndexAppListGPIO:
         free_all_views(app, true);
         flip_store_category_index = 2;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     case FlipStoreSubmenuIndexAppListInfrared:
         free_all_views(app, true);
         flip_store_category_index = 3;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     case FlipStoreSubmenuIndexAppListiButton:
         free_all_views(app, true);
         flip_store_category_index = 4;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     case FlipStoreSubmenuIndexAppListMedia:
         free_all_views(app, true);
         flip_store_category_index = 5;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     case FlipStoreSubmenuIndexAppListNFC:
         free_all_views(app, true);
         flip_store_category_index = 6;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     case FlipStoreSubmenuIndexAppListRFID:
         free_all_views(app, true);
         flip_store_category_index = 7;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     case FlipStoreSubmenuIndexAppListSubGHz:
         free_all_views(app, true);
         flip_store_category_index = 8;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     case FlipStoreSubmenuIndexAppListTools:
         free_all_views(app, true);
         flip_store_category_index = 9;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     case FlipStoreSubmenuIndexAppListUSB:
         free_all_views(app, true);
         flip_store_category_index = 10;
         flip_store_app_does_exist = false;
-        flip_store_switch_to_app_list(app);
+        fetch_appropiate_app_list(app);
         break;
     default:
         // Check if the index is within the firmwares list range

+ 1 - 0
flip_store.h

@@ -12,6 +12,7 @@
 #include <notification/notification.h>
 #include <dialogs/dialogs.h>
 #include <jsmn/jsmn.h>
+#include <jsmn/jsmn_furi.h>
 #include <flip_store_icons.h>
 
 #define TAG "FlipStore"

+ 10 - 0
flipper_http/flipper_http.c

@@ -107,6 +107,16 @@ FuriString *flipper_http_load_from_file(char *file_path)
     // Reset the FuriString to ensure it's empty before reading
     furi_string_reset(str_result);
 
+    if (memmgr_get_free_heap() < MAX_FILE_SHOW)
+    {
+        FURI_LOG_E(HTTP_TAG, "Not enough heap to read file.");
+        furi_string_free(str_result);
+        storage_file_close(file);
+        storage_file_free(file);
+        furi_record_close(RECORD_STORAGE);
+        return NULL;
+    }
+
     // Define a buffer to hold the read data
     uint8_t *buffer = (uint8_t *)malloc(MAX_FILE_SHOW);
     if (!buffer)

+ 2 - 2
flipper_http/flipper_http.h

@@ -22,8 +22,8 @@
 #define TIMEOUT_DURATION_TICKS (5 * 1000) // 5 seconds
 #define BAUDRATE (115200)                 // UART baudrate
 #define RX_BUF_SIZE 2048                  // UART RX buffer size
-#define RX_LINE_BUFFER_SIZE 8192          // UART RX line buffer size (increase for large responses)
-#define MAX_FILE_SHOW 8192                // Maximum data from file to show
+#define RX_LINE_BUFFER_SIZE 16000         // UART RX line buffer size (increase for large responses)
+#define MAX_FILE_SHOW 16000               // Maximum data from file to show
 #define FILE_BUFFER_SIZE 512              // File buffer size
 
 // Forward declaration for callback

+ 18 - 1
jsmn/jsmn.c

@@ -454,6 +454,11 @@ char *get_json_value(char *key, const char *json_data)
         jsmn_parser parser;
         jsmn_init(&parser);
         uint32_t max_tokens = json_token_count(json_data);
+        if (!jsmn_memory_check(max_tokens))
+        {
+            FURI_LOG_E("JSMM.H", "Insufficient memory for JSON tokens.");
+            return NULL;
+        }
         // Allocate tokens array on the heap
         jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * max_tokens);
         if (tokens == NULL)
@@ -572,6 +577,12 @@ char *get_json_array_value(char *key, uint32_t index, const char *json_data)
         return NULL;
     }
     uint32_t max_tokens = json_token_count(array_str);
+    if (!jsmn_memory_check(max_tokens))
+    {
+        FURI_LOG_E("JSMM.H", "Insufficient memory for JSON tokens.");
+        free(array_str);
+        return NULL;
+    }
 
     jsmn_parser parser;
     jsmn_init(&parser);
@@ -602,7 +613,7 @@ char *get_json_array_value(char *key, uint32_t index, const char *json_data)
 
     if (index >= (uint32_t)tokens[0].size)
     {
-        FURI_LOG_E("JSMM.H", "Index %lu out of bounds for array with size %u.", index, tokens[0].size);
+        // FURI_LOG_E("JSMM.H", "Index %lu out of bounds for array with size %u.", index, tokens[0].size);
         free(tokens);
         free(array_str);
         return NULL;
@@ -654,6 +665,12 @@ char **get_json_array_values(char *key, char *json_data, int *num_values)
         return NULL;
     }
     uint32_t max_tokens = json_token_count(array_str);
+    if (!jsmn_memory_check(max_tokens))
+    {
+        FURI_LOG_E("JSMM.H", "Insufficient memory for JSON tokens.");
+        free(array_str);
+        return NULL;
+    }
     // Initialize the JSON parser
     jsmn_parser parser;
     jsmn_init(&parser);

+ 18 - 1
jsmn/jsmn_furi.c

@@ -481,6 +481,11 @@ FuriString *get_json_value_furi(const char *key, const FuriString *json_data)
         return NULL;
     }
     uint32_t max_tokens = json_token_count_furi(json_data);
+    if (!jsmn_memory_check(max_tokens))
+    {
+        FURI_LOG_E("JSMM.H", "Insufficient memory for JSON tokens.");
+        return NULL;
+    }
     // Create a temporary FuriString from key
     FuriString *key_str = furi_string_alloc();
     furi_string_cat_str(key_str, key);
@@ -546,6 +551,12 @@ FuriString *get_json_array_value_furi(const char *key, uint32_t index, const Fur
         return NULL;
     }
     uint32_t max_tokens = json_token_count_furi(array_str);
+    if (!jsmn_memory_check(max_tokens))
+    {
+        FURI_LOG_E("JSMM.H", "Insufficient memory for JSON tokens.");
+        furi_string_free(array_str);
+        return NULL;
+    }
     jsmn_parser parser;
     jsmn_init_furi(&parser);
 
@@ -576,7 +587,7 @@ FuriString *get_json_array_value_furi(const char *key, uint32_t index, const Fur
 
     if (index >= (uint32_t)tokens[0].size)
     {
-        FURI_LOG_E("JSMM.H", "Index %lu out of bounds for array with size %u.", index, tokens[0].size);
+        // FURI_LOG_E("JSMM.H", "Index %lu out of bounds for array with size %u.", index, tokens[0].size);
         free(tokens);
         furi_string_free(array_str);
         return NULL;
@@ -622,6 +633,12 @@ FuriString **get_json_array_values_furi(const char *key, const FuriString *json_
     }
 
     uint32_t max_tokens = json_token_count_furi(array_str);
+    if (!jsmn_memory_check(max_tokens))
+    {
+        FURI_LOG_E("JSMM.H", "Insufficient memory for JSON tokens.");
+        furi_string_free(array_str);
+        return NULL;
+    }
     jsmn_parser parser;
     jsmn_init_furi(&parser);
 

+ 1 - 0
jsmn/jsmn_h.c

@@ -12,3 +12,4 @@ FuriString *char_to_furi_string(const char *str)
     }
     return furi_str;
 }
+bool jsmn_memory_check(size_t heap_size) { return memmgr_get_free_heap() > (heap_size + 1024); }

+ 4 - 1
jsmn/jsmn_h.h

@@ -50,4 +50,7 @@ typedef struct
     FuriString *value;
 } FuriJSON;
 
-FuriString *char_to_furi_string(const char *str);
+FuriString *char_to_furi_string(const char *str);
+
+// check memory
+bool jsmn_memory_check(size_t heap_size);