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

Merge pull request #21 from leedave/feature/stationSet

Feature/station set
David Lee 2 лет назад
Родитель
Сommit
42049b3011

+ 3 - 2
README.md

@@ -11,8 +11,9 @@ This app triggers restaurant pagers in a brute force manner, useful to test if d
 - Retekess TD174
 
 ### Features
-- Select range of stations (needs improving for full range)
-- Select range of pagers (needs improving for full range)
+- Send a Range of Signals
+- Select range of stations
+- Select range of pagers
 
 ## How to install on Flipper Zero
 - If you do not have one, download a firmware onto your PC via git<br>

+ 2 - 1
application.fam

@@ -7,7 +7,8 @@ App(
     fap_icon="icons/meal_pager_10px.png",
     fap_icon_assets="icons",
     fap_category="Sub-Ghz",
-    fap_version="1.0",
+    fap_version="1.1",
+    fap_libs=["assets"],
     fap_author="leedave",
     fap_weburl="https://github.com/leedave/flipper-zero-meal-pager",
     fap_description="This app triggers restaurant pagers in a brute force manner, useful to test if devices are still functional.",

+ 13 - 0
docs/changelog.md

@@ -1,3 +1,16 @@
+## v1.1
+- Created a new UI Input View as FW does not supply one for numbers
+- New UI to Set First Station
+- New UI to Set Last Station
+- New UI to Set First Page
+- New UI to Set Last Pager
+- Removed Vibro/Sound settings as not used
+
+Known issues
+- After setting last station, the settings view is weird. Some kindo of memory bug. But data remains correct. 
+- Extensive use can cause crashes, must be some memory leak left
+
+
 ## v1.0
 - Added support for TD174
 

+ 390 - 0
helpers/gui/int_input.c

@@ -0,0 +1,390 @@
+#include "int_input.h"
+
+#include <gui/elements.h>
+#include <furi.h>
+#include <assets_icons.h>
+
+/** IntInput type */
+struct IntInput {
+    View* view;
+};
+
+typedef struct {
+    const char text;
+    const uint8_t x;
+    const uint8_t y;
+} IntInputKey;
+
+typedef struct {
+    const char* header;
+    char* text_buffer;
+    size_t text_buffer_size;
+    bool clear_default_text;
+
+    IntInputCallback callback;
+    void* callback_context;
+
+    int8_t selected_row;
+    uint8_t selected_column;
+} IntInputModel;
+
+static const uint8_t keyboard_origin_x = 7;
+static const uint8_t keyboard_origin_y = 31;
+static const uint8_t keyboard_row_count = 2;
+static const uint8_t enter_symbol = '\r';
+static const uint8_t backspace_symbol = '\b';
+
+static const IntInputKey keyboard_keys_row_1[] = {
+    {'0', 0, 12},
+    {'1', 11, 12},
+    {'2', 22, 12},
+    {'3', 33, 12},
+    {'4', 44, 12},
+    {backspace_symbol, 103, 4},
+};
+
+static const IntInputKey keyboard_keys_row_2[] = {
+    {'5', 0, 26},
+    {'6', 11, 26},
+    {'7', 22, 26},
+    {'8', 33, 26},
+    {'9', 44, 26},
+    {enter_symbol, 95, 17},
+};
+
+/** Get row size
+ *
+ * @param      row_index  Index of row
+ *
+ * @return     uint8_t Row size
+ */
+static uint8_t int_input_get_row_size(uint8_t row_index) {
+    uint8_t row_size = 0;
+
+    switch(row_index + 1) {
+    case 1:
+        row_size = COUNT_OF(keyboard_keys_row_1);
+        break;
+    case 2:
+        row_size = COUNT_OF(keyboard_keys_row_2);
+        break;
+    default:
+        furi_crash();
+    }
+
+    return row_size;
+}
+
+/** Get row pointer
+ *
+ * @param      row_index  Index of row
+ *
+ * @return     const IntInputKey* Row pointer
+ */
+static const IntInputKey* int_input_get_row(uint8_t row_index) {
+    const IntInputKey* row = NULL;
+
+    switch(row_index + 1) {
+    case 1:
+        row = keyboard_keys_row_1;
+        break;
+    case 2:
+        row = keyboard_keys_row_2;
+        break;
+    default:
+        furi_crash();
+    }
+
+    return row;
+}
+
+/** Draw input box (common view)
+ *
+ * @param      canvas  The canvas
+ * @param      model   The model
+ */
+static void int_input_draw_input(Canvas* canvas, IntInputModel* model) {
+    const uint8_t text_x = 8;
+    const uint8_t text_y = 25;
+
+    elements_slightly_rounded_frame(canvas, 6, 14, 116, 15);
+
+    const char* text = model->text_buffer;
+    canvas_draw_str(canvas, text_x, text_y, text);
+}
+
+static void int_input_backspace_cb(IntInputModel* model) {
+    uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer);
+    if(text_length > 0) {
+        model->text_buffer[text_length - 1] = 0;
+    }
+}
+
+/** Handle up button
+ *
+ * @param      model  The model
+ */
+static void int_input_handle_up(IntInputModel* model) {
+    if(model->selected_row > 0) {
+        model->selected_row--;
+    }
+}
+
+/** Handle down button
+ *
+ * @param      model  The model
+ */
+static void int_input_handle_down(IntInputModel* model) {
+    if(model->selected_row < keyboard_row_count - 1) {
+        model->selected_row += 1;
+    }
+}
+
+/** Handle left button
+ *
+ * @param      model  The model
+ */
+static void int_input_handle_left(IntInputModel* model) {
+    if(model->selected_column > 0) {
+        model->selected_column--;
+    } else {
+        model->selected_column = int_input_get_row_size(model->selected_row) - 1;
+    }
+}
+
+/** Handle right button
+ *
+ * @param      model  The model
+ */
+static void int_input_handle_right(IntInputModel* model) {
+    if(model->selected_column < int_input_get_row_size(model->selected_row) - 1) {
+        model->selected_column++;
+    } else {
+        model->selected_column = 0;
+    }
+}
+
+/** Handle OK button
+ *
+ * @param      model  The model
+ */
+static void int_input_handle_ok(IntInputModel* model) {
+    char selected = int_input_get_row(model->selected_row)[model->selected_column].text;
+    size_t text_length = strlen(model->text_buffer);
+    if(selected == enter_symbol) {
+        model->callback(model->callback_context);
+    } else if(selected == backspace_symbol) {
+        int_input_backspace_cb(model);
+    } else {
+        if(model->clear_default_text) {
+            text_length = 0;
+        }
+        if(text_length < (model->text_buffer_size - 1)) {
+            model->text_buffer[text_length] = selected;
+            model->text_buffer[text_length + 1] = 0;
+        }
+    }
+    model->clear_default_text = false;
+}
+
+/** Draw callback
+ *
+ * @param      canvas  The canvas
+ * @param      _model  The model
+ */
+static void int_input_view_draw_callback(Canvas* canvas, void* _model) {
+    IntInputModel* model = _model;
+    uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0;
+    UNUSED(text_length);
+
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+
+    int_input_draw_input(canvas, model);
+
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str(canvas, 2, 9, model->header);
+    canvas_set_font(canvas, FontKeyboard);
+    // Draw keyboard
+    for(uint8_t row = 0; row < keyboard_row_count; row++) {
+        const uint8_t column_count = int_input_get_row_size(row);
+        const IntInputKey* keys = int_input_get_row(row);
+
+        for(size_t column = 0; column < column_count; column++) {
+            if(keys[column].text == enter_symbol) {
+                canvas_set_color(canvas, ColorBlack);
+                if(model->selected_row == row && model->selected_column == column) {
+                    canvas_draw_icon(
+                        canvas,
+                        keyboard_origin_x + keys[column].x,
+                        keyboard_origin_y + keys[column].y,
+                        &I_KeySaveSelected_24x11);
+                } else {
+                    canvas_draw_icon(
+                        canvas,
+                        keyboard_origin_x + keys[column].x,
+                        keyboard_origin_y + keys[column].y,
+                        &I_KeySave_24x11);
+                }
+            } else if(keys[column].text == backspace_symbol) {
+                canvas_set_color(canvas, ColorBlack);
+                if(model->selected_row == row && model->selected_column == column) {
+                    canvas_draw_icon(
+                        canvas,
+                        keyboard_origin_x + keys[column].x,
+                        keyboard_origin_y + keys[column].y,
+                        &I_KeyBackspaceSelected_16x9);
+                } else {
+                    canvas_draw_icon(
+                        canvas,
+                        keyboard_origin_x + keys[column].x,
+                        keyboard_origin_y + keys[column].y,
+                        &I_KeyBackspace_16x9);
+                }
+            } else {
+                if(model->selected_row == row && model->selected_column == column) {
+                    canvas_set_color(canvas, ColorBlack);
+                    canvas_draw_box(
+                        canvas,
+                        keyboard_origin_x + keys[column].x - 3,
+                        keyboard_origin_y + keys[column].y - 10,
+                        11,
+                        13);
+                    canvas_set_color(canvas, ColorWhite);
+                } else if(model->selected_row == -1 && row == 0 && model->selected_column == column) {
+                    canvas_set_color(canvas, ColorBlack);
+                    canvas_draw_frame(
+                        canvas,
+                        keyboard_origin_x + keys[column].x - 3,
+                        keyboard_origin_y + keys[column].y - 10,
+                        11,
+                        13);
+                } else {
+                    canvas_set_color(canvas, ColorBlack);
+                }
+
+                canvas_draw_glyph(
+                    canvas,
+                    keyboard_origin_x + keys[column].x,
+                    keyboard_origin_y + keys[column].y,
+                    keys[column].text);
+            }
+        }
+    }
+}
+
+/** Input callback
+ *
+ * @param      event    The event
+ * @param      context  The context
+ *
+ * @return     true
+ * @return     false
+ */
+static bool int_input_view_input_callback(InputEvent* event, void* context) {
+    IntInput* int_input = context;
+    furi_assert(int_input);
+
+    bool consumed = false;
+
+    // Fetch the model
+    IntInputModel* model = view_get_model(int_input->view);
+
+    if(event->type == InputTypeShort || event->type == InputTypeLong ||
+       event->type == InputTypeRepeat) {
+        consumed = true;
+        switch(event->key) {
+        case InputKeyLeft:
+            int_input_handle_left(model);
+            break;
+        case InputKeyRight:
+            int_input_handle_right(model);
+            break;
+        case InputKeyUp:
+            int_input_handle_up(model);
+            break;
+        case InputKeyDown:
+            int_input_handle_down(model);
+            break;
+        case InputKeyOk:
+            int_input_handle_ok(model);
+            break;
+        default:
+            consumed = false;
+            break;
+        }
+    }
+
+    // commit view
+    view_commit_model(int_input->view, consumed);
+
+    return consumed;
+}
+
+void int_input_reset(IntInput* int_input) {
+    FURI_LOG_D("INT_INPUT", "Resetting Model");
+    furi_assert(int_input);
+    with_view_model(
+        int_input->view,
+        IntInputModel * model,
+        {
+            model->header = "";
+            model->selected_row = 0;
+            model->selected_column = 0;
+            model->clear_default_text = false;
+            model->text_buffer = "";
+            model->text_buffer_size = 0;
+            model->callback = NULL;
+            model->callback_context = NULL;
+        },
+        true);
+}
+
+IntInput* int_input_alloc() {
+    IntInput* int_input = malloc(sizeof(IntInput));
+    int_input->view = view_alloc();
+    view_set_context(int_input->view, int_input);
+    view_allocate_model(int_input->view, ViewModelTypeLocking, sizeof(IntInputModel));
+    view_set_draw_callback(int_input->view, int_input_view_draw_callback);
+    view_set_input_callback(int_input->view, int_input_view_input_callback);
+
+    int_input_reset(int_input);
+
+    return int_input;
+}
+
+void int_input_free(IntInput* int_input) {
+    furi_assert(int_input);
+    view_free(int_input->view);
+    free(int_input);
+}
+
+View* int_input_get_view(IntInput* int_input) {
+    furi_assert(int_input);
+    return int_input->view;
+}
+
+void int_input_set_result_callback(
+    IntInput* int_input,
+    IntInputCallback callback,
+    void* callback_context,
+    char* text_buffer,
+    size_t text_buffer_size,
+    bool clear_default_text) {
+    with_view_model(
+        int_input->view,
+        IntInputModel * model,
+        {
+            model->callback = callback;
+            model->callback_context = callback_context;
+            model->text_buffer = text_buffer;
+            model->text_buffer_size = text_buffer_size;
+            model->clear_default_text = clear_default_text;
+        },
+        true);
+}
+
+void int_input_set_header_text(IntInput* int_input, const char* text) {
+    with_view_model(
+        int_input->view, IntInputModel * model, { model->header = text; }, true);
+}

+ 71 - 0
helpers/gui/int_input.h

@@ -0,0 +1,71 @@
+/**
+ * @file int_input.h
+ * GUI: Integer keyboard view module API
+ */
+
+#pragma once
+
+#include <gui/view.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Int input anonymous structure  */
+typedef struct IntInput IntInput;
+
+/** callback that is executed on save button press */
+typedef void (*IntInputCallback)(void* context);
+
+/** callback that is executed when byte buffer is changed */
+typedef void (*IntChangedCallback)(void* context);
+
+/** Allocate and initialize Int input. This Int input is used to enter Ints.
+ *
+ * @return     IntInput instance pointer
+ */
+IntInput* int_input_alloc();
+
+/** Deinitialize and free byte input
+ *
+ * @param      int_input  Int input instance
+ */
+void int_input_free(IntInput* int_input);
+
+/** Get byte input view
+ *
+ * @param      int_input  byte input instance
+ *
+ * @return     View instance that can be used for embedding
+ */
+View* int_input_get_view(IntInput* int_input);
+
+/** Set byte input result callback
+ *
+ * @param      int_input          byte input instance
+ * @param      input_callback     input callback fn
+ * @param      changed_callback   changed callback fn
+ * @param      callback_context   callback context
+ * @param      text_buffer        buffer to use
+ * @param      text_buffer_size   buffer length
+ * @param      clear_default_text clear previous entry
+ */
+
+void int_input_set_result_callback(
+    IntInput* int_input,
+    IntInputCallback input_callback,
+    void* callback_context,
+    char* text_buffer,
+    size_t text_buffer_size,
+    bool clear_default_text);
+
+/** Set byte input header text
+ *
+ * @param      int_input  byte input instance
+ * @param      text        text to be shown
+ */
+void int_input_set_header_text(IntInput* int_input, const char* text);
+
+#ifdef __cplusplus
+}
+#endif

+ 2 - 0
helpers/meal_pager_custom_event.h

@@ -23,6 +23,8 @@ typedef enum {
     Meal_PagerCustomEventViewTransmitterSendStart,
     Meal_PagerCustomEventViewTransmitterSendStop,
     Meal_PagerCustomEventViewTransmitterError,
+    Meal_PagerCustomerEventIntInput,
+    Meal_PagerCustomEventViewIntInputOk,
 } Meal_PagerCustomEvent;
 
 enum Meal_PagerCustomEventType {

+ 8 - 4
helpers/meal_pager_led.c

@@ -2,14 +2,18 @@
 
 void meal_pager_blink_start_subghz(Meal_Pager* app) {
     furi_assert(app);
-    notification_message(app->notification, &sequence_blink_stop);
-    notification_message(app->notification, &sequence_blink_start_magenta);
+    if(app->led == 1) {
+        notification_message(app->notification, &sequence_blink_stop);
+        notification_message(app->notification, &sequence_blink_start_magenta);
+    }
 }
 
 void meal_pager_blink_start_compile(Meal_Pager* app) {
     furi_assert(app);
-    notification_message(app->notification, &sequence_blink_stop);
-    notification_message(app->notification, &sequence_blink_start_yellow);
+    if(app->led == 1) {
+        notification_message(app->notification, &sequence_blink_stop);
+        notification_message(app->notification, &sequence_blink_start_yellow);
+    }
 }
 
 void meal_pager_blink_stop(Meal_Pager* app) {

+ 32 - 7
helpers/meal_pager_storage.c

@@ -15,7 +15,11 @@ static void meal_pager_close_config_file(FlipperFormat* file) {
     flipper_format_free(file);
 }
 
-bool meal_pager_save_subghz_buffer_file_start(void* context, FlipperFormat* ff, Storage* storage, char* frequency) {
+bool meal_pager_save_subghz_buffer_file_start(
+    void* context,
+    FlipperFormat* ff,
+    Storage* storage,
+    char* frequency) {
     // SubGhz TXRX can only be loaded with files, makes sense as to save RAM
     Meal_Pager* app = context;
     UNUSED(app);
@@ -53,12 +57,11 @@ bool meal_pager_save_subghz_buffer_file_start(void* context, FlipperFormat* ff,
         return success;
     }
 
-    success =
-        flipper_format_write_header_cstr(
-            ff, MEAL_PAGER_SUBGHZ_FILE_TYPE, MEAL_PAGER_SUBGHZ_FILE_VERSION) &&
-        flipper_format_write_string_cstr(ff, "Frequency", frequency) &&
-        flipper_format_write_string_cstr(ff, "Preset", MEAL_PAGER_SUBGHZ_FILE_PRESET) &&
-        flipper_format_write_string_cstr(ff, "Protocol", MEAL_PAGER_SUBGHZ_FILE_Protocol);
+    success = flipper_format_write_header_cstr(
+                  ff, MEAL_PAGER_SUBGHZ_FILE_TYPE, MEAL_PAGER_SUBGHZ_FILE_VERSION) &&
+              flipper_format_write_string_cstr(ff, "Frequency", frequency) &&
+              flipper_format_write_string_cstr(ff, "Preset", MEAL_PAGER_SUBGHZ_FILE_PRESET) &&
+              flipper_format_write_string_cstr(ff, "Protocol", MEAL_PAGER_SUBGHZ_FILE_Protocol);
     return success;
 }
 
@@ -178,6 +181,7 @@ void meal_pager_read_settings(void* context) {
     }
 
     flipper_format_read_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_PAGER_TYPE, &app->pager_type, 1);
+    meal_pager_set_max_values(app);
     flipper_format_read_uint32(
         fff_file, MEAL_PAGER_SETTINGS_KEY_FIRST_STATION, &app->first_station, 1);
     flipper_format_read_uint32(
@@ -194,6 +198,27 @@ void meal_pager_read_settings(void* context) {
 
     flipper_format_rewind(fff_file);
 
+    furi_string_free(temp_str);
+
     meal_pager_close_config_file(fff_file);
     meal_pager_close_storage();
+}
+
+void meal_pager_set_max_values(void* context) {
+    Meal_Pager* app = context;
+    switch(app->pager_type) {
+    case Meal_PagerPagerTypeT119:
+    case Meal_PagerPagerTypeTD165:
+        app->max_station = 8191;
+        app->max_pager = 999;
+        break;
+    case Meal_PagerPagerTypeTD174:
+        app->max_station = 8191;
+        app->max_pager = 10;
+        break;
+    case Meal_PagerPagerTypeTD157:
+        app->max_station = 1023;
+        app->max_pager = 999;
+        break;
+    }
 }

+ 7 - 2
helpers/meal_pager_storage.h

@@ -29,9 +29,14 @@
 #define MEAL_PAGER_SUBGHZ_FILE_PRESET "FuriHalSubGhzPresetOok650Async"
 #define MEAL_PAGER_SUBGHZ_FILE_Protocol "RAW"
 
-bool meal_pager_save_subghz_buffer_file_start(void* context, FlipperFormat* ff, Storage* storage, char* frequency);
+bool meal_pager_save_subghz_buffer_file_start(
+    void* context,
+    FlipperFormat* ff,
+    Storage* storage,
+    char* frequency);
 
 void meal_pager_save_subghz_buffer_stop(void* context, FlipperFormat* ff);
 
 void meal_pager_save_settings(void* context);
-void meal_pager_read_settings(void* context);
+void meal_pager_read_settings(void* context);
+void meal_pager_set_max_values(void* context);

+ 2 - 1
helpers/retekess/meal_pager_retekess_t119.c

@@ -107,7 +107,8 @@ bool meal_pager_retekess_t119_generate_all(void* context) {
 
     Storage* storage = furi_record_open(RECORD_STORAGE);
     FlipperFormat* ff = flipper_format_file_alloc(storage);
-    bool success = meal_pager_save_subghz_buffer_file_start(app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_FREQUENCY);
+    bool success = meal_pager_save_subghz_buffer_file_start(
+        app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_FREQUENCY);
 
     if(!success) {
         FURI_LOG_D(TAG, "failed to save to buffer");

+ 2 - 1
helpers/retekess/meal_pager_retekess_td157.c

@@ -93,7 +93,8 @@ bool meal_pager_retekess_td157_generate_all(void* context) {
 
     Storage* storage = furi_record_open(RECORD_STORAGE);
     FlipperFormat* ff = flipper_format_file_alloc(storage);
-    bool success = meal_pager_save_subghz_buffer_file_start(app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_FREQUENCY);
+    bool success = meal_pager_save_subghz_buffer_file_start(
+        app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_FREQUENCY);
 
     if(!success) {
         FURI_LOG_D(TAG, "failed to save to buffer");

+ 2 - 1
helpers/retekess/meal_pager_retekess_td165.c

@@ -95,7 +95,8 @@ bool meal_pager_retekess_td165_generate_all(void* context) {
 
     Storage* storage = furi_record_open(RECORD_STORAGE);
     FlipperFormat* ff = flipper_format_file_alloc(storage);
-    bool success = meal_pager_save_subghz_buffer_file_start(app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_FREQUENCY);
+    bool success = meal_pager_save_subghz_buffer_file_start(
+        app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_FREQUENCY);
 
     if(!success) {
         FURI_LOG_D(TAG, "failed to save to buffer");

+ 2 - 1
helpers/retekess/meal_pager_retekess_td174.c

@@ -98,7 +98,8 @@ bool meal_pager_retekess_td174_generate_all(void* context) {
 
     Storage* storage = furi_record_open(RECORD_STORAGE);
     FlipperFormat* ff = flipper_format_file_alloc(storage);
-    bool success = meal_pager_save_subghz_buffer_file_start(app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_ALT_FREQUENCY);
+    bool success = meal_pager_save_subghz_buffer_file_start(
+        app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_ALT_FREQUENCY);
 
     if(!success) {
         FURI_LOG_D(TAG, "failed to save to buffer");

+ 18 - 0
meal_pager.c

@@ -58,6 +58,10 @@ Meal_Pager* meal_pager_app_alloc() {
     app->stop_transmit = false;
     app->repeats = 1;
     app->repeats_char = "1";
+    app->max_station = 8191;
+    app->max_pager = 999;
+
+    snprintf(app->text_store[0], 32, "%lu", app->first_station);
 
     // Used for File Browser
     app->dialogs = furi_record_open(RECORD_DIALOGS);
@@ -65,6 +69,9 @@ Meal_Pager* meal_pager_app_alloc() {
 
     app->subghz = subghz_alloc();
 
+    // Custom made int keyboard
+    app->int_input = int_input_alloc();
+
     // Load configs
     meal_pager_read_settings(app);
 
@@ -87,8 +94,17 @@ Meal_Pager* meal_pager_app_alloc() {
         Meal_PagerViewIdSettings,
         variable_item_list_get_view(app->variable_item_list));
 
+    app->int_input = int_input_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, Meal_PagerViewIdIntInput, int_input_get_view(app->int_input));
+
     //End Scene Additions
 
+    snprintf(app->text_store[0], 20, "%lu", app->first_station);
+    snprintf(app->text_store[1], 20, "%lu", app->last_station);
+    snprintf(app->text_store[2], 20, "%lu", app->first_pager);
+    snprintf(app->text_store[3], 20, "%lu", app->last_pager);
+
     return app;
 }
 
@@ -102,7 +118,9 @@ void meal_pager_app_free(Meal_Pager* app) {
     view_dispatcher_remove_view(app->view_dispatcher, Meal_PagerViewIdMenu);
     view_dispatcher_remove_view(app->view_dispatcher, Meal_PagerViewIdTransmit);
     view_dispatcher_remove_view(app->view_dispatcher, Meal_PagerViewIdSettings);
+    view_dispatcher_remove_view(app->view_dispatcher, Meal_PagerViewIdIntInput);
     submenu_free(app->submenu);
+    int_input_free(app->int_input);
 
     view_dispatcher_free(app->view_dispatcher);
     furi_record_close(RECORD_GUI);

+ 1 - 1
meal_pager.h

@@ -1,2 +1,2 @@
 #pragma once
-#include "meal_pager_i.h"
+#include "meal_pager_i.h"

+ 7 - 0
meal_pager_i.h

@@ -19,6 +19,7 @@
 #include "helpers/meal_pager_storage.h"
 #include "helpers/subghz/subghz_types.h"
 #include "helpers/subghz/subghz.h"
+#include "helpers/gui/int_input.h"
 
 #define TAG "Meal_Pager"
 
@@ -59,6 +60,11 @@ typedef struct {
     bool stop_transmit;
     uint32_t repeats;
     char* repeats_char;
+    IntInput* int_input;
+    char* text_buffer;
+    uint32_t max_station;
+    uint32_t max_pager;
+    char text_store[6][129];
 } Meal_Pager;
 
 typedef enum {
@@ -66,6 +72,7 @@ typedef enum {
     Meal_PagerViewIdMenu,
     Meal_PagerViewIdTransmit,
     Meal_PagerViewIdSettings,
+    Meal_PagerViewIdIntInput,
 } Meal_PagerViewId;
 
 typedef enum {

+ 5 - 1
scenes/meal_pager_scene_config.h

@@ -1,4 +1,8 @@
 ADD_SCENE(meal_pager, startscreen, Startscreen)
 ADD_SCENE(meal_pager, menu, Menu)
 ADD_SCENE(meal_pager, transmit, Transmit)
-ADD_SCENE(meal_pager, settings, Settings)
+ADD_SCENE(meal_pager, settings, Settings)
+ADD_SCENE(meal_pager, set_first_station, SetFirstStation)
+ADD_SCENE(meal_pager, set_last_station, SetLastStation)
+ADD_SCENE(meal_pager, set_first_pager, SetFirstPager)
+ADD_SCENE(meal_pager, set_last_pager, SetLastPager)

+ 48 - 1
scenes/meal_pager_scene_menu.c

@@ -4,7 +4,10 @@
 
 enum SubmenuIndex {
     SubmenuIndexTransmit = 10,
-    SubmenuIndexScene2,
+    SubmenuIndexSetFirstStation,
+    SubmenuIndexSetLastStation,
+    SubmenuIndexSetFirstPager,
+    SubmenuIndexSetLastPager,
     SubmenuIndexScene3,
     SubmenuIndexScene4,
     SubmenuIndexScene5,
@@ -25,6 +28,30 @@ void meal_pager_scene_menu_on_enter(void* context) {
         SubmenuIndexTransmit,
         meal_pager_scene_menu_submenu_callback,
         app);
+    submenu_add_item(
+        app->submenu,
+        "Set First Station",
+        SubmenuIndexSetFirstStation,
+        meal_pager_scene_menu_submenu_callback,
+        app);
+    submenu_add_item(
+        app->submenu,
+        "Set Last Station",
+        SubmenuIndexSetLastStation,
+        meal_pager_scene_menu_submenu_callback,
+        app);
+    submenu_add_item(
+        app->submenu,
+        "Set First Pager",
+        SubmenuIndexSetFirstPager,
+        meal_pager_scene_menu_submenu_callback,
+        app);
+    submenu_add_item(
+        app->submenu,
+        "Set Last Pager",
+        SubmenuIndexSetLastPager,
+        meal_pager_scene_menu_submenu_callback,
+        app);
     submenu_add_item(
         app->submenu,
         "Settings",
@@ -62,6 +89,26 @@ bool meal_pager_scene_menu_on_event(void* context, SceneManagerEvent event) {
             subghz_txrx_stop(app->subghz->txrx);
             FURI_LOG_D(TAG, "Stop Event from Menu");
             return true;
+        } else if(event.event == SubmenuIndexSetFirstStation) {
+            scene_manager_set_scene_state(
+                app->scene_manager, Meal_PagerSceneSetFirstStation, SubmenuIndexSetFirstStation);
+            scene_manager_next_scene(app->scene_manager, Meal_PagerSceneSetFirstStation);
+            return true;
+        } else if(event.event == SubmenuIndexSetLastStation) {
+            scene_manager_set_scene_state(
+                app->scene_manager, Meal_PagerSceneSetLastStation, SubmenuIndexSetLastStation);
+            scene_manager_next_scene(app->scene_manager, Meal_PagerSceneSetLastStation);
+            return true;
+        } else if(event.event == SubmenuIndexSetFirstPager) {
+            scene_manager_set_scene_state(
+                app->scene_manager, Meal_PagerSceneSetFirstPager, SubmenuIndexSetFirstPager);
+            scene_manager_next_scene(app->scene_manager, Meal_PagerSceneSetFirstPager);
+            return true;
+        } else if(event.event == SubmenuIndexSetLastPager) {
+            scene_manager_set_scene_state(
+                app->scene_manager, Meal_PagerSceneSetLastPager, SubmenuIndexSetLastPager);
+            scene_manager_next_scene(app->scene_manager, Meal_PagerSceneSetLastPager);
+            return true;
         }
     } else if(event.type == SceneManagerEventTypeTick) {
         if(app->state_notifications == SubGhzNotificationStateTx) {

+ 63 - 0
scenes/meal_pager_scene_set_first_pager.c

@@ -0,0 +1,63 @@
+#include "../meal_pager_i.h"
+#include "../helpers/meal_pager_custom_event.h"
+#include "../helpers/meal_pager_led.h"
+#include <dolphin/dolphin.h>
+
+void meal_pager_set_first_pager_callback(void* context) {
+    furi_assert(context);
+    Meal_Pager* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, Meal_PagerCustomerEventIntInput);
+}
+
+void meal_pager_scene_set_first_pager_on_enter(void* context) {
+    furi_assert(context);
+    Meal_Pager* app = context;
+    IntInput* int_input = app->int_input;
+    size_t enter_name_length = 5;
+    meal_pager_set_max_values(app);
+    char* str = "Set First Pager (0 - 999)";
+    const char* constStr = str;
+    snprintf(str, 36, "Set First Pager (0 - %lu)", app->max_pager);
+
+    int_input_set_header_text(int_input, constStr);
+
+    int_input_set_result_callback(
+        int_input,
+        meal_pager_set_first_pager_callback,
+        context,
+        app->text_store[2],
+        enter_name_length,
+        false);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdIntInput);
+}
+
+bool meal_pager_scene_set_first_pager_on_event(void* context, SceneManagerEvent event) {
+    Meal_Pager* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        scene_manager_previous_scene(app->scene_manager);
+        return true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        app->first_pager = atoi(app->text_store[2]);
+        app->first_pager_char = app->text_store[2];
+        scene_manager_previous_scene(app->scene_manager);
+        return true;
+    } else if(event.type == SceneManagerEventTypeTick) {
+        if(app->state_notifications == SubGhzNotificationStateTx) {
+            app->state_notifications = SubGhzNotificationStateIDLE;
+            subghz_txrx_stop(app->subghz->txrx);
+            meal_pager_blink_stop(app);
+            meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 0);
+        }
+        return true;
+    }
+
+    return consumed;
+}
+
+void meal_pager_scene_set_first_pager_on_exit(void* context) {
+    Meal_Pager* app = context;
+    UNUSED(app);
+}

+ 63 - 0
scenes/meal_pager_scene_set_first_station.c

@@ -0,0 +1,63 @@
+#include "../meal_pager_i.h"
+#include "../helpers/meal_pager_custom_event.h"
+#include "../helpers/meal_pager_led.h"
+#include <dolphin/dolphin.h>
+
+void meal_pager_set_first_station_callback(void* context) {
+    furi_assert(context);
+    Meal_Pager* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, Meal_PagerCustomerEventIntInput);
+}
+
+void meal_pager_scene_set_first_station_on_enter(void* context) {
+    furi_assert(context);
+    Meal_Pager* app = context;
+    IntInput* int_input = app->int_input;
+    size_t enter_name_length = 5;
+    meal_pager_set_max_values(app);
+    char* str = "Set First Station (0 - 9999)";
+    const char* constStr = str;
+    snprintf(str, 36, "Set First Station (0 - %lu)", app->max_station);
+
+    int_input_set_header_text(int_input, constStr);
+
+    int_input_set_result_callback(
+        int_input,
+        meal_pager_set_first_station_callback,
+        context,
+        app->text_store[0],
+        enter_name_length,
+        false);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdIntInput);
+}
+
+bool meal_pager_scene_set_first_station_on_event(void* context, SceneManagerEvent event) {
+    Meal_Pager* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        scene_manager_previous_scene(app->scene_manager);
+        return true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        app->first_station = atoi(app->text_store[0]);
+        app->first_station_char = app->text_store[0];
+        scene_manager_previous_scene(app->scene_manager);
+        return true;
+    } else if(event.type == SceneManagerEventTypeTick) {
+        if(app->state_notifications == SubGhzNotificationStateTx) {
+            app->state_notifications = SubGhzNotificationStateIDLE;
+            subghz_txrx_stop(app->subghz->txrx);
+            meal_pager_blink_stop(app);
+            meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 0);
+        }
+        return true;
+    }
+
+    return consumed;
+}
+
+void meal_pager_scene_set_first_station_on_exit(void* context) {
+    Meal_Pager* app = context;
+    UNUSED(app);
+}

+ 63 - 0
scenes/meal_pager_scene_set_last_pager.c

@@ -0,0 +1,63 @@
+#include "../meal_pager_i.h"
+#include "../helpers/meal_pager_custom_event.h"
+#include "../helpers/meal_pager_led.h"
+#include <dolphin/dolphin.h>
+
+void meal_pager_set_last_pager_callback(void* context) {
+    furi_assert(context);
+    Meal_Pager* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, Meal_PagerCustomerEventIntInput);
+}
+
+void meal_pager_scene_set_last_pager_on_enter(void* context) {
+    furi_assert(context);
+    Meal_Pager* app = context;
+    IntInput* int_input = app->int_input;
+    size_t enter_name_length = 5;
+    meal_pager_set_max_values(app);
+    char* str = "Set Last Pager (0 - 999)";
+    const char* constStr = str;
+    snprintf(str, 36, "Set Last Pager (%lu - %lu)", app->first_pager, app->max_pager);
+
+    int_input_set_header_text(int_input, constStr);
+
+    int_input_set_result_callback(
+        int_input,
+        meal_pager_set_last_pager_callback,
+        context,
+        app->text_store[3],
+        enter_name_length,
+        false);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdIntInput);
+}
+
+bool meal_pager_scene_set_last_pager_on_event(void* context, SceneManagerEvent event) {
+    Meal_Pager* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        scene_manager_previous_scene(app->scene_manager);
+        return true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        app->last_pager = atoi(app->text_store[3]);
+        app->last_pager_char = app->text_store[3];
+        scene_manager_previous_scene(app->scene_manager);
+        return true;
+    } else if(event.type == SceneManagerEventTypeTick) {
+        if(app->state_notifications == SubGhzNotificationStateTx) {
+            app->state_notifications = SubGhzNotificationStateIDLE;
+            subghz_txrx_stop(app->subghz->txrx);
+            meal_pager_blink_stop(app);
+            meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 0);
+        }
+        return true;
+    }
+
+    return consumed;
+}
+
+void meal_pager_scene_set_last_pager_on_exit(void* context) {
+    Meal_Pager* app = context;
+    UNUSED(app);
+}

+ 63 - 0
scenes/meal_pager_scene_set_last_station.c

@@ -0,0 +1,63 @@
+#include "../meal_pager_i.h"
+#include "../helpers/meal_pager_custom_event.h"
+#include "../helpers/meal_pager_led.h"
+#include <dolphin/dolphin.h>
+
+void meal_pager_set_last_station_callback(void* context) {
+    furi_assert(context);
+    Meal_Pager* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, Meal_PagerCustomerEventIntInput);
+}
+
+void meal_pager_scene_set_last_station_on_enter(void* context) {
+    furi_assert(context);
+    Meal_Pager* app = context;
+    IntInput* int_input = app->int_input;
+    size_t enter_name_length = 5;
+    meal_pager_set_max_values(app);
+    char* str = "Set Last Station (0 - 9999)";
+    const char* constStr = str;
+    snprintf(str, 36, "Set Last Station (%lu - %lu)", app->last_station, app->max_station);
+
+    int_input_set_header_text(int_input, constStr);
+
+    int_input_set_result_callback(
+        int_input,
+        meal_pager_set_last_station_callback,
+        context,
+        app->text_store[1],
+        enter_name_length,
+        false);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdIntInput);
+}
+
+bool meal_pager_scene_set_last_station_on_event(void* context, SceneManagerEvent event) {
+    Meal_Pager* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        scene_manager_previous_scene(app->scene_manager);
+        return true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        app->last_station = atoi(app->text_store[1]);
+        app->last_station_char = app->text_store[1];
+        scene_manager_previous_scene(app->scene_manager);
+        return true;
+    } else if(event.type == SceneManagerEventTypeTick) {
+        if(app->state_notifications == SubGhzNotificationStateTx) {
+            app->state_notifications = SubGhzNotificationStateIDLE;
+            subghz_txrx_stop(app->subghz->txrx);
+            meal_pager_blink_stop(app);
+            meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 0);
+        }
+        return true;
+    }
+
+    return consumed;
+}
+
+void meal_pager_scene_set_last_station_on_exit(void* context) {
+    Meal_Pager* app = context;
+    UNUSED(app);
+}

+ 15 - 33
scenes/meal_pager_scene_settings.c

@@ -66,21 +66,11 @@ static void meal_pager_scene_settings_set_pager_type(VariableItem* item) {
 }
 
 static void meal_pager_scene_settings_set_first_station(VariableItem* item) {
-    Meal_Pager* app = variable_item_get_context(item);
-    uint32_t index = variable_item_get_current_value_index(item);
-
-    snprintf(app->first_station_char, 20, "%lu", index);
-    variable_item_set_current_value_text(item, app->first_station_char);
-    app->first_station = index;
+    UNUSED(item);
 }
 
 static void meal_pager_scene_settings_set_last_station(VariableItem* item) {
-    Meal_Pager* app = variable_item_get_context(item);
-    uint32_t index = variable_item_get_current_value_index(item);
-
-    snprintf(app->last_station_char, 20, "%lu", index);
-    variable_item_set_current_value_text(item, app->last_station_char);
-    app->last_station = index;
+    UNUSED(item);
 }
 
 static void meal_pager_scene_settings_set_first_pager(VariableItem* item) {
@@ -110,7 +100,7 @@ static void meal_pager_scene_settings_set_repeats(VariableItem* item) {
     app->repeats = index;
 }
 
-static void meal_pager_scene_settings_set_haptic(VariableItem* item) {
+/*static void meal_pager_scene_settings_set_haptic(VariableItem* item) {
     Meal_Pager* app = variable_item_get_context(item);
     uint8_t index = variable_item_get_current_value_index(item);
 
@@ -123,7 +113,7 @@ static void meal_pager_scene_settings_set_speaker(VariableItem* item) {
     uint8_t index = variable_item_get_current_value_index(item);
     variable_item_set_current_value_text(item, speaker_text[index]);
     app->speaker = speaker_value[index];
-}
+}*/
 
 static void meal_pager_scene_settings_set_led(VariableItem* item) {
     Meal_Pager* app = variable_item_get_context(item);
@@ -151,11 +141,7 @@ void meal_pager_scene_settings_on_enter(void* context) {
 
     // Pager Type
     item = variable_item_list_add(
-        app->variable_item_list,
-        "Pager Type:",
-        4,
-        meal_pager_scene_settings_set_pager_type,
-        app);
+        app->variable_item_list, "Pager Type:", 4, meal_pager_scene_settings_set_pager_type, app);
     value_index = value_index_uint32(app->pager_type, pager_type_value, 4);
     variable_item_set_current_value_index(item, value_index);
     variable_item_set_current_value_text(item, pager_type_text[value_index]);
@@ -164,10 +150,9 @@ void meal_pager_scene_settings_on_enter(void* context) {
     item = variable_item_list_add(
         app->variable_item_list,
         "First Station",
-        255,
+        1,
         meal_pager_scene_settings_set_first_station,
         app);
-    variable_item_set_current_value_index(item, app->first_station);
     snprintf(app->first_pager_char, 20, "%lu", app->first_station);
     variable_item_set_current_value_text(item, app->first_station_char);
 
@@ -175,24 +160,21 @@ void meal_pager_scene_settings_on_enter(void* context) {
     item = variable_item_list_add(
         app->variable_item_list,
         "Last Station",
-        255,
+        1,
         meal_pager_scene_settings_set_last_station,
         app);
-    variable_item_set_current_value_index(item, app->last_station);
     snprintf(app->last_station_char, 20, "%lu", app->last_station);
     variable_item_set_current_value_text(item, app->last_station_char);
 
     // First Pager
     item = variable_item_list_add(
-        app->variable_item_list, "First Pager", 99, meal_pager_scene_settings_set_first_pager, app);
-    variable_item_set_current_value_index(item, app->first_pager);
+        app->variable_item_list, "First Pager", 1, meal_pager_scene_settings_set_first_pager, app);
     snprintf(app->first_pager_char, 20, "%lu", app->first_pager);
     variable_item_set_current_value_text(item, app->first_pager_char);
 
     // Last Pager
     item = variable_item_list_add(
-        app->variable_item_list, "Last Pager", 99, meal_pager_scene_settings_set_last_pager, app);
-    variable_item_set_current_value_index(item, app->last_pager);
+        app->variable_item_list, "Last Pager", 1, meal_pager_scene_settings_set_last_pager, app);
     snprintf(app->last_pager_char, 20, "%lu", app->last_pager);
     variable_item_set_current_value_text(item, app->last_pager_char);
 
@@ -203,19 +185,19 @@ void meal_pager_scene_settings_on_enter(void* context) {
     snprintf(app->repeats_char, 20, "%lu", app->repeats);
     variable_item_set_current_value_text(item, app->repeats_char);
 
-    // Vibro on/off
-    item = variable_item_list_add(
+    // Vibro on/off Disabled until used
+    /*item = variable_item_list_add(
         app->variable_item_list, "Vibro/Haptic:", 2, meal_pager_scene_settings_set_haptic, app);
     value_index = value_index_uint32(app->haptic, haptic_value, 2);
     variable_item_set_current_value_index(item, value_index);
-    variable_item_set_current_value_text(item, haptic_text[value_index]);
+    variable_item_set_current_value_text(item, haptic_text[value_index]);*/
 
-    // Sound on/off
-    item = variable_item_list_add(
+    // Sound on/off Disabled until used
+    /*item = variable_item_list_add(
         app->variable_item_list, "Sound:", 2, meal_pager_scene_settings_set_speaker, app);
     value_index = value_index_uint32(app->speaker, speaker_value, 2);
     variable_item_set_current_value_index(item, value_index);
-    variable_item_set_current_value_text(item, speaker_text[value_index]);
+    variable_item_set_current_value_text(item, speaker_text[value_index]);*/
 
     // LED Effects on/off
     item = variable_item_list_add(

+ 1 - 1
scenes/meal_pager_scene_transmit.c

@@ -93,7 +93,7 @@ bool meal_pager_scene_transmit_on_event(void* context, SceneManagerEvent event)
             break;
         }
     } else if(event.type == SceneManagerEventTypeTick) {
-        if(app->state_notifications == SubGhzNotificationStateTx) {
+        if(app->state_notifications == SubGhzNotificationStateTx && app->led == 1) {
             notification_message(app->notification, &sequence_blink_magenta_10);
         }
         return true;