瀏覽代碼

Add files via upload

Ferrazzi 3 年之前
父節點
當前提交
3290c53d5d

二進制
Image/FlipperZero_IFTTT_Module.png


二進制
Image/ifttt.jpg


二進制
Image/webhooks_doc-960x487.png


+ 0 - 45
README.md

@@ -1,45 +0,0 @@
-# FlipperZero IFTTT Virtual Button
-
-<h3 align="center">
-<a href="https://github.com/Ferrazzi/FlipperZero_IFTTT_Virtual_Button">
-<img src="https://github.com/Ferrazzi/FlipperZero_IFTTT_Virtual_Button/blob/main/Image/ifttt.jpg" align="center" alt="fzCUSTOM" border="0">
-</a>
-</h3>
-
-FlipperZero IFTTT Virtual Button is an application that allows you to send IFTTT requests from FlipperZero through an esp8266 module to automate some actions.
-If This Than That, which could translate to "if this happens, then make that happen". As the phrase says, it allows you to automate activities by connecting to different online services.
-
-# First you need to prepare an additional card.
-you will need: 1 ESP8266 (I recommend ESP01 for the size) and 2 buttons.
-
-<img src="https://github.com/Ferrazzi/FlipperZero_IFTTT_Virtual_Button/blob/main/Image/FlipperZero_IFTTT_Module.png" align="center" alt="fzCUSTOM" border="0">
-
-# Let's now move on to the configuration and operation
-- First we need to create an account on IFTTT if you don't have one. Go to https://ifttt.com/ and create one account.
-- Now that we have an account, let's set up a test applet.
-- Let's create our first applet, or rather a mini sequence of instructions, by pressing the create button ”New applet“.
-- Click on the word "this" and search for the "Webhook" service, press the option receive a web request. In this way we can perform an action when the applet receives an HTTP request to a specific URL.
-- We type the name of the event "button_pressed" and create the trigger.
-- Now we press "that" and we create the mobile notification service through the app that we have installed on one of our phones. Here we can change the notification message and also with the EventName tag we will insert the name of the previously created event.
-- You should have your applet created after clicking "Finish"
-- Now let's go back to the main page https://ifttt.com/ and search "Webhooks" and enter the card "Documentation"
-- Here you can find your unique KEY API which you need to keep private.
-<img src="https://github.com/Ferrazzi/FlipperZero_IFTTT_Virtual_Button/blob/main/Image/webhooks_doc-960x487.png" align="center" alt="fzCUSTOM" border="0">
-- Type the name of the event, "button_pressed".
-- Final URL should appear at the bottom of the web page. We'll need to copy that URL.
-
-# Warnings for the first beta version
-In this version you have to configure the sketch for ESP8266 manually, compile it and flash it.
-Edit IFTTMode.cpp file in FlipperZero-IFTTT_WiFi_Module directory and change in this line with your credential.
-
-Change "triggername" and "keyID" with yours that you created before
-
-const char* resource = "https://maker.ifttt.com/trigger/triggername/json/with/key/keyID";
-
-Enter the SSID and password of your WiFi connection
-
-const char *ssidIFTTT = "SSID";
-
-const char *passwordIFTTT = "password";
-
-COMPILE AND FLASH YOUR ESP8266!!!

+ 14 - 0
application.fam

@@ -0,0 +1,14 @@
+App(
+    appid="ESP8266_IFTTT_Virtual_Button",
+    name="[ESP] IFTTT Virtual Button",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="ifttt_virtual_button_app",
+    cdefines=["APP_IFTTT_VIRTUAL_BUTTON"],
+    requires=[
+        "gui",
+    ],
+    stack_size=2 * 1024,
+    order=20,
+    fap_icon="icon.png",
+    fap_category="GPIO",
+)

二進制
icon.png


+ 253 - 0
ifttt_virtual_button.c

@@ -0,0 +1,253 @@
+#include "ifttt_virtual_button.h"
+
+#define IFTTT_FOLDER "/ext/ifttt"
+#define IFTTT_CONFIG_FOLDER "/ext/ifttt/config"
+const char *CONFIG_FILE_PATH = "/ext/ifttt/config/config.settings";
+
+#define FLIPPERZERO_SERIAL_BAUD 115200
+typedef enum ESerialCommand { ESerialCommand_Config } ESerialCommand;
+
+Settings save_settings(Settings settings) {
+    Storage *storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat *file = flipper_format_file_alloc(storage);
+    if (flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
+        flipper_format_update_string_cstr(file, CONF_SSID, settings.save_ssid);
+        flipper_format_update_string_cstr(file, CONF_PASSWORD, settings.save_password);
+        flipper_format_update_string_cstr(file, CONF_KEY, settings.save_key);
+        flipper_format_update_string_cstr(file, CONF_EVENT, settings.save_event);
+    }else{
+    }
+    flipper_format_file_close(file);
+    flipper_format_free(file);
+    furi_record_close(RECORD_STORAGE);
+    return settings;
+}
+
+void save_settings_file(FlipperFormat *file, Settings *settings) {
+    flipper_format_write_header_cstr(file, CONFIG_FILE_HEADER, CONFIG_FILE_VERSION);
+    flipper_format_write_comment_cstr(file, "Enter here the SSID of the wifi network");
+    flipper_format_write_string_cstr(file, CONF_SSID, settings->save_ssid);
+    flipper_format_write_comment_cstr(file, "Enter here the PASSWORD of the wifi network");
+    flipper_format_write_string_cstr(file, CONF_PASSWORD, settings->save_password);
+    flipper_format_write_comment_cstr(file, "Enter here the WEBHOOKS of your IFTTT account");
+    flipper_format_write_string_cstr(file, CONF_KEY, settings->save_key);
+    flipper_format_write_comment_cstr(file, "Enter here the EVENT name of your trigger");
+    flipper_format_write_string_cstr(file, CONF_EVENT, settings->save_event);
+}
+
+Settings *load_settings() {
+    Settings *settings=malloc(sizeof(Settings));
+
+    Storage *storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat *file = flipper_format_file_alloc(storage);
+
+    FuriString *string_value;
+    string_value = furi_string_alloc();
+    FuriString *text_ssid_value;
+    text_ssid_value = furi_string_alloc();
+    FuriString *text_password_value;
+    text_password_value = furi_string_alloc();
+    FuriString *text_key_value;
+    text_key_value = furi_string_alloc();
+    FuriString *text_event_value;
+    text_event_value = furi_string_alloc();
+
+    if (storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) {
+        if (!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) {
+            flipper_format_file_close(file);
+        } else {
+            settings->save_ssid = malloc( 1);
+            settings->save_password = malloc( 1);
+            settings->save_key = malloc( 1);
+            settings->save_event = malloc( 1);
+
+            settings->save_ssid[0]='\0';
+            settings->save_password[0]='\0';
+            settings->save_key[0]='\0';
+            settings->save_event[0]='\0';
+
+            save_settings_file(file, settings);
+            flipper_format_file_close(file);
+        }
+    } else {
+        if (!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
+            flipper_format_file_close(file);
+        } else {
+            uint32_t value;
+            if (!flipper_format_read_header(file, string_value, &value)) {
+            } else {
+                if (flipper_format_read_string(file, CONF_SSID, text_ssid_value)) {
+                    settings->save_ssid = malloc(furi_string_size(text_ssid_value) + 1);
+                    strcpy(settings->save_ssid, furi_string_get_cstr(text_ssid_value));
+                }
+                if (flipper_format_read_string(file, CONF_PASSWORD, text_password_value)) {
+                    settings->save_password = malloc(furi_string_size(text_password_value) + 1);
+                    strcpy(settings->save_password, furi_string_get_cstr(text_password_value));
+                }
+                if (flipper_format_read_string(file, CONF_KEY, text_key_value)) {
+                    settings->save_key = malloc(furi_string_size(text_key_value) + 1);
+                    strcpy(settings->save_key, furi_string_get_cstr(text_key_value));
+                }
+                if (flipper_format_read_string(file, CONF_EVENT, text_event_value)) {
+                    settings->save_event = malloc(furi_string_size(text_event_value) + 1);
+                    strcpy(settings->save_event, furi_string_get_cstr(text_event_value));
+                }
+            }
+            flipper_format_file_close(file);
+        }
+    }
+
+    furi_string_free(text_ssid_value);
+    furi_string_free(text_password_value);
+    furi_string_free(text_key_value);
+    furi_string_free(text_event_value);
+    flipper_format_free(file);
+    furi_record_close(RECORD_STORAGE);
+    return settings;
+}
+
+void send_serial_command_config(ESerialCommand command, Settings *settings) { 
+   uint8_t data[1] = {0};
+
+    char config_tmp[100];
+    strcpy(config_tmp,"config,");
+    strcat(config_tmp, settings->save_key);
+    char config_tmp2[5];
+    strcpy(config_tmp2, config_tmp);
+    strcat(config_tmp2,",");
+    char config_tmp3[100];
+    strcpy(config_tmp3, config_tmp2);
+    strcat(config_tmp3,settings->save_ssid);
+    char config_tmp4[5];
+    strcpy(config_tmp4, config_tmp3);
+    strcat(config_tmp4,",");
+    char config_tmp5[100];
+    strcpy(config_tmp5, config_tmp4);
+    strcat(config_tmp5,settings->save_password);
+    char config_tmp6[5];
+    strcpy(config_tmp6, config_tmp5);
+    strcat(config_tmp6,",");
+    char config[350];
+    strcpy(config, config_tmp6);
+    strcat(config,settings->save_event);
+
+   int length = strlen(config);
+   for (int i = 0; i < length; i++){
+    switch(command) {
+      case ESerialCommand_Config:
+        data[0] = config[i];
+        break;
+        default:
+        return;
+    };
+
+    furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
+   }
+}
+
+static bool ifttt_virtual_button_custom_event_callback(void* context, uint32_t event) {
+    furi_assert(context);
+    VirtualButtonApp* app = context;
+    return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+static bool ifttt_virtual_button_back_event_callback(void* context) {
+    furi_assert(context);
+    VirtualButtonApp* app = context;
+    return scene_manager_handle_back_event(app->scene_manager);
+}
+
+static void ifttt_virtual_button_tick_event_callback(void* context) {
+    furi_assert(context);
+    VirtualButtonApp* app = context;
+    scene_manager_handle_tick_event(app->scene_manager);
+}
+
+VirtualButtonApp* ifttt_virtual_button_app_alloc(uint32_t first_scene) {
+    VirtualButtonApp* app = malloc(sizeof(VirtualButtonApp));
+
+    // Records
+    app->gui = furi_record_open(RECORD_GUI);
+    app->power = furi_record_open(RECORD_POWER);
+
+    // View dispatcher
+    app->view_dispatcher = view_dispatcher_alloc();
+    app->scene_manager = scene_manager_alloc(&virtual_button_scene_handlers, app);
+    view_dispatcher_enable_queue(app->view_dispatcher);
+    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
+    view_dispatcher_set_custom_event_callback(
+        app->view_dispatcher, ifttt_virtual_button_custom_event_callback);
+    view_dispatcher_set_navigation_event_callback(
+        app->view_dispatcher, ifttt_virtual_button_back_event_callback);
+    view_dispatcher_set_tick_event_callback(
+        app->view_dispatcher, ifttt_virtual_button_tick_event_callback, 2000);
+    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+    // Views
+    app->sen_view = send_view_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, VirtualButtonAppViewSendView, send_view_get_view(app->sen_view));
+
+    app->abou_view = about_view_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher,
+        VirtualButtonAppViewAboutView,
+        about_view_get_view(app->abou_view));
+
+    app->submenu = submenu_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, VirtualButtonAppViewSubmenu, submenu_get_view(app->submenu));
+    app->dialog = dialog_ex_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, VirtualButtonAppViewDialog, dialog_ex_get_view(app->dialog));
+
+    // Set first scene
+    scene_manager_next_scene(app->scene_manager, first_scene);
+    return app;
+}
+
+void ifttt_virtual_button_app_free(VirtualButtonApp* app) {
+    furi_assert(app);
+
+    free(app->settings.save_ssid);
+    free(app->settings.save_password);
+    free(app->settings.save_key);
+
+    // Views
+    view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewSendView);
+    send_view_free(app->sen_view);
+    view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewAboutView);
+    about_view_free(app->abou_view);
+    view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewSubmenu);
+    submenu_free(app->submenu);
+    view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewDialog);
+    dialog_ex_free(app->dialog);
+    // View dispatcher
+    view_dispatcher_free(app->view_dispatcher);
+    scene_manager_free(app->scene_manager);
+    // Records
+    furi_record_close(RECORD_POWER);
+    furi_record_close(RECORD_GUI);
+
+    free(app);
+}
+
+int32_t ifttt_virtual_button_app(void* p) {
+    UNUSED(p);
+    
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    if(!storage_simply_mkdir(storage, IFTTT_FOLDER)) {
+    }
+    if(!storage_simply_mkdir(storage, IFTTT_CONFIG_FOLDER)) {
+    }
+    furi_record_close(RECORD_STORAGE);
+
+    uint32_t first_scene = VirtualButtonAppSceneStart;
+    VirtualButtonApp* app = ifttt_virtual_button_app_alloc(first_scene);
+    memcpy(&app->settings,load_settings(),sizeof(Settings));
+    send_serial_command_config(ESerialCommand_Config, &(app->settings));
+
+    view_dispatcher_run(app->view_dispatcher);
+    ifttt_virtual_button_app_free(app);
+    return 0;
+}

+ 56 - 0
ifttt_virtual_button.h

@@ -0,0 +1,56 @@
+#pragma once
+
+#include <furi.h>
+#include <power/power_service/power.h>
+#include <gui/gui.h>
+#include <gui/view.h>
+#include <gui/view_dispatcher.h>
+#include <gui/scene_manager.h>
+#include "views/send_view.h"
+#include "views/about_view.h"
+#include <gui/modules/submenu.h>
+#include <gui/modules/dialog_ex.h>
+#include <flipper_format/flipper_format.h>
+#include <flipper_format/flipper_format_i.h>
+#include <storage/storage.h>
+#include <furi_hal_uart.h>
+#include "scenes/virtual_button_scene.h"
+
+#define APP_NAME "[ESP8266] IFTTT Virtual Button"
+
+#define CONF_SSID "wifi_ssid"
+#define CONF_PASSWORD "wifi_password"
+#define CONF_KEY "webhooks_key"
+#define CONF_EVENT "event"
+#define CONFIG_FILE_HEADER "IFTTT Virtual Button Config File"
+#define CONFIG_FILE_VERSION 1
+
+typedef struct{
+    char *save_ssid;
+    char *save_password;
+    char *save_key;
+    char *save_event;
+} Settings;
+
+typedef struct {
+    Power* power;
+    Gui* gui;
+    SceneManager* scene_manager;
+    ViewDispatcher* view_dispatcher;
+    SendView* sen_view;
+    AboutView* abou_view;
+    Submenu* submenu;
+    DialogEx* dialog;
+    PowerInfo info;
+    Settings settings;
+} VirtualButtonApp;
+
+typedef enum {
+    VirtualButtonAppViewSendView,
+    VirtualButtonAppViewAboutView,
+    VirtualButtonAppViewSubmenu,
+    VirtualButtonAppViewDialog,
+} VirtualButtonAppView;
+
+Settings save_settings(Settings settings);
+Settings *load_settings();

+ 30 - 0
scenes/virtual_button_scene.c

@@ -0,0 +1,30 @@
+#include "virtual_button_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const virtual_button_on_enter_handlers[])(void*) = {
+#include "virtual_button_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_event handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
+bool (*const virtual_button_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "virtual_button_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
+void (*const virtual_button_on_exit_handlers[])(void* context) = {
+#include "virtual_button_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers virtual_button_scene_handlers = {
+    .on_enter_handlers = virtual_button_on_enter_handlers,
+    .on_event_handlers = virtual_button_on_event_handlers,
+    .on_exit_handlers = virtual_button_on_exit_handlers,
+    .scene_num = VirtualButtonAppSceneNum,
+};

+ 29 - 0
scenes/virtual_button_scene.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <gui/scene_manager.h>
+
+// Generate scene id and total number
+#define ADD_SCENE(prefix, name, id) VirtualButtonAppScene##id,
+typedef enum {
+#include "virtual_button_scene_config.h"
+    VirtualButtonAppSceneNum,
+} VirtualButtonAppScene;
+#undef ADD_SCENE
+
+extern const SceneManagerHandlers virtual_button_scene_handlers;
+
+// Generate scene on_enter handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
+#include "virtual_button_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_event handlers declaration
+#define ADD_SCENE(prefix, name, id) \
+    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
+#include "virtual_button_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
+#include "virtual_button_scene_config.h"
+#undef ADD_SCENE

+ 26 - 0
scenes/virtual_button_scene_about.c

@@ -0,0 +1,26 @@
+#include "../ifttt_virtual_button.h"
+
+static void virtual_button_scene_about_view_update_model(VirtualButtonApp* app) {
+    power_get_info(app->power, &app->info);
+}
+
+void virtual_button_scene_about_view_on_enter(void* context) {
+    VirtualButtonApp* app = context;
+    virtual_button_scene_about_view_update_model(app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, VirtualButtonAppViewAboutView);
+}
+
+bool virtual_button_scene_about_view_on_event(void* context, SceneManagerEvent event) {
+    VirtualButtonApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeTick) {
+        virtual_button_scene_about_view_update_model(app);
+        consumed = true;
+    }
+    return consumed;
+}
+
+void virtual_button_scene_about_view_on_exit(void* context) {
+    UNUSED(context);
+}

+ 3 - 0
scenes/virtual_button_scene_config.h

@@ -0,0 +1,3 @@
+ADD_SCENE(virtual_button, start, Start)
+ADD_SCENE(virtual_button, send_view, SendView)
+ADD_SCENE(virtual_button, about_view, AboutView)

+ 26 - 0
scenes/virtual_button_scene_send.c

@@ -0,0 +1,26 @@
+#include "../ifttt_virtual_button.h"
+
+static void virtual_button_scene_send_view_update_model(VirtualButtonApp* app) {
+    power_get_info(app->power, &app->info);
+}
+
+void virtual_button_scene_send_view_on_enter(void* context) {
+    VirtualButtonApp* app = context;
+    virtual_button_scene_send_view_update_model(app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, VirtualButtonAppViewSendView);
+}
+
+bool virtual_button_scene_send_view_on_event(void* context, SceneManagerEvent event) {
+    VirtualButtonApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeTick) {
+        virtual_button_scene_send_view_update_model(app);
+        consumed = true;
+    }
+    return consumed;
+}
+
+void virtual_button_scene_send_view_on_exit(void* context) {
+    UNUSED(context);
+}

+ 55 - 0
scenes/virtual_button_scene_start.c

@@ -0,0 +1,55 @@
+#include "../ifttt_virtual_button.h"
+
+enum VirtualButtonSubmenuIndex {
+    VirtualButtonSubmenuIndexSendView,
+    VirtualButtonSubmenuIndexAboutView,
+};
+
+static void virtual_button_scene_start_submenu_callback(void* context, uint32_t index) {
+    furi_assert(context);
+    VirtualButtonApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+void virtual_button_scene_start_on_enter(void* context) {
+    VirtualButtonApp* app = context;
+    Submenu* submenu = app->submenu;
+
+    submenu_add_item(
+        submenu,
+        "Send IFTTT command",
+        VirtualButtonSubmenuIndexSendView,
+        virtual_button_scene_start_submenu_callback,
+        app);
+    submenu_add_item(
+        submenu,
+        "About",
+        VirtualButtonSubmenuIndexAboutView,
+        virtual_button_scene_start_submenu_callback,
+        app);
+    submenu_set_selected_item(
+        submenu, scene_manager_get_scene_state(app->scene_manager, VirtualButtonAppSceneStart));
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, VirtualButtonAppViewSubmenu);
+}
+
+bool virtual_button_scene_start_on_event(void* context, SceneManagerEvent event) {
+    VirtualButtonApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == VirtualButtonSubmenuIndexSendView) {
+            scene_manager_next_scene(app->scene_manager, VirtualButtonAppSceneSendView);
+        } else if(event.event == VirtualButtonSubmenuIndexAboutView) {
+            scene_manager_next_scene(app->scene_manager, VirtualButtonAppSceneAboutView);
+        }
+        scene_manager_set_scene_state(app->scene_manager, VirtualButtonAppSceneStart, event.event);
+        consumed = true;
+    }
+    return consumed;
+}
+
+void virtual_button_scene_start_on_exit(void* context) {
+    VirtualButtonApp* app = context;
+    submenu_reset(app->submenu);
+}

+ 49 - 0
views/about_view.c

@@ -0,0 +1,49 @@
+#include "about_view.h"
+#include <furi.h>
+#include <gui/elements.h>
+#include <notification/notification.h>
+#include <notification/notification_messages.h>
+
+struct AboutView {
+    View* view;
+};
+
+typedef struct {
+    bool connected;
+} AboutViewModel;
+
+static void about_view_draw_callback(Canvas* canvas, void* context) {
+    furi_assert(context);
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+    canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "IFTTT Virtual button");
+    canvas_draw_str_aligned(canvas, 0, 15, AlignLeft, AlignTop, "Version 0.2");
+    canvas_draw_str_aligned(canvas, 0, 50, AlignLeft, AlignTop, "press back");
+}
+
+
+AboutView* about_view_alloc() {
+    AboutView* about_view = malloc(sizeof(AboutView));
+    about_view->view = view_alloc();
+    view_set_context(about_view->view, about_view);
+    view_allocate_model(about_view->view, ViewModelTypeLocking, sizeof(AboutViewModel));
+    view_set_draw_callback(about_view->view, about_view_draw_callback);
+    return about_view;
+}
+
+void about_view_free(AboutView* about_view) {
+    furi_assert(about_view);
+    view_free(about_view->view);
+    free(about_view);
+}
+
+View* about_view_get_view(AboutView* about_view) {
+    furi_assert(about_view);
+    return about_view->view;
+}
+
+void about_view_set_data(AboutView* about_view, bool connected) {
+    furi_assert(about_view);
+    with_view_model(
+        about_view->view, AboutViewModel * model, { model->connected = connected; }, true);
+}

+ 11 - 0
views/about_view.h

@@ -0,0 +1,11 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct AboutView AboutView;
+
+AboutView* about_view_alloc();
+
+void about_view_free(AboutView* about_view);
+
+View* about_view_get_view(AboutView* about_view);

+ 137 - 0
views/send_view.c

@@ -0,0 +1,137 @@
+#include "send_view.h"
+#include <furi.h>
+#include <gui/elements.h>
+#include <notification/notification.h>
+#include <notification/notification_messages.h>
+#include <furi_hal_uart.h>
+#include <string.h>
+#include <stdio.h>
+
+#define FLIPPERZERO_SERIAL_BAUD 115200
+
+typedef enum ESerialCommand { ESerialCommand_Send } ESerialCommand;
+
+struct SendView {
+    View* view;
+};
+
+typedef struct {
+    bool right_pressed;
+    bool connected;
+} SendViewModel;
+
+static void Shake(void) {
+    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
+    notification_message(notification, &sequence_single_vibro);
+    furi_record_close(RECORD_NOTIFICATION);
+}
+
+void send_serial_command_send(ESerialCommand command) {
+    uint8_t data[1] = {0};
+
+   char name[10] = "send";
+   int length = strlen(name);
+   for (int i = 0; i < length; i++){
+    switch(command) {
+      case ESerialCommand_Send:
+        data[0] = name[i];
+        break;
+        default:
+        return;
+    };
+
+    furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
+   }
+}
+
+static void send_view_draw_callback(Canvas* canvas, void* context) {
+    furi_assert(context);
+    SendViewModel* model = context;
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+    canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "SEND MODULE");
+    canvas_draw_line(canvas, 0, 10, 128, 10);
+    canvas_draw_str_aligned(canvas, 64, 15, AlignCenter, AlignTop, "Press right to send IFTTT");
+    canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignTop, "command or press and hold");
+    canvas_draw_str_aligned(canvas, 64, 35, AlignCenter, AlignTop, "back to return to the menu");
+
+    // Right
+    if(model->right_pressed) {
+    }
+}
+
+static void send_view_process(SendView* send_view, InputEvent* event) {
+    with_view_model(
+        send_view->view,
+        SendViewModel * model,
+        {
+            if(event->type == InputTypePress) {
+                if(event->key == InputKeyUp) {
+                } else if(event->key == InputKeyDown) {
+                } else if(event->key == InputKeyLeft) {
+                } else if(event->key == InputKeyRight) {
+                    model->right_pressed = true;
+                    Shake();
+                    send_serial_command_send(ESerialCommand_Send);
+                } else if(event->key == InputKeyOk) {
+                } else if(event->key == InputKeyBack) {
+                }
+            } else if(event->type == InputTypeRelease) {
+                if(event->key == InputKeyUp) {
+                } else if(event->key == InputKeyDown) {
+                } else if(event->key == InputKeyLeft) {
+                } else if(event->key == InputKeyRight) {
+                    model->right_pressed = false;
+                } else if(event->key == InputKeyOk) {
+                } else if(event->key == InputKeyBack) {
+                }
+            } else if(event->type == InputTypeShort) {
+                if(event->key == InputKeyBack) {
+                }
+            }
+        },
+        true);
+}
+
+static bool send_view_input_callback(InputEvent* event, void* context) {
+    furi_assert(context);
+    SendView* send_view = context;
+    bool consumed = false;
+
+    if(event->type == InputTypeLong && event->key == InputKeyBack) {
+    } else {
+        send_view_process(send_view, event);
+        consumed = true;
+    }
+
+    return consumed;
+}
+
+SendView* send_view_alloc() {
+    SendView* send_view = malloc(sizeof(SendView));
+    send_view->view = view_alloc();
+    view_set_context(send_view->view, send_view);
+    view_allocate_model(send_view->view, ViewModelTypeLocking, sizeof(SendViewModel));
+    view_set_draw_callback(send_view->view, send_view_draw_callback);
+    view_set_input_callback(send_view->view, send_view_input_callback);
+    furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD);
+
+    return send_view;
+}
+
+void send_view_free(SendView* send_view) {
+    furi_assert(send_view);
+    view_free(send_view->view);
+    free(send_view);
+}
+
+View* send_view_get_view(SendView* send_view) {
+    furi_assert(send_view);
+    return send_view->view;
+}
+
+void send_view_set_data(SendView* send_view, bool connected) {
+    furi_assert(send_view);
+    with_view_model(
+        send_view->view, SendViewModel * model, { model->connected = connected; }, true);
+}

+ 11 - 0
views/send_view.h

@@ -0,0 +1,11 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct SendView SendView;
+
+SendView* send_view_alloc();
+
+void send_view_free(SendView* send_view);
+
+View* send_view_get_view(SendView* send_view);