gui for scene selection

This commit is contained in:
KlausMu 2024-03-25 22:08:51 +01:00 committed by Klaus Musch
parent 4568d0338e
commit 4488b1e589
36 changed files with 684 additions and 123 deletions

View File

@ -9,13 +9,22 @@ void save_preferences_HAL(void) {
}
std::string get_currentScene_HAL() {
return currentScene;
// if (currentScene == "") {
// // set here something if you need it for a test at startup
// return "Apple TV";
// } else
{return currentScene;}
}
void set_currentScene_HAL(std::string aCurrentScene) {
currentScene = aCurrentScene;
}
std::string get_currentGUIname_HAL(){
return currentGUIname;
// if (currentGUIname == "") {
// // set here something if you need it for a test at startup
// return "IR Receiver"; // "Numpad"; // "Apple TV";
// } else
{return currentGUIname;}
}
void set_currentGUIname_HAL(std::string aCurrentGUIname) {
currentGUIname = aCurrentGUIname;

View File

@ -23,6 +23,7 @@ build_flags =
-D ENABLE_KEYBOARD_MQTT=0
-D ENABLE_BLUETOOTH=1
-D ENABLE_KEYBOARD_BLE=1
-D USE_SCENE_SPECIFIC_GUI_LIST=1
-D SCR_WIDTH=${env.custom_screen_width}
-D SCR_HEIGHT=${env.custom_screen_heigth}
;-- lvgl ------------------------------------------------------------------
@ -68,6 +69,10 @@ build_flags =
; lvgl variant 2:
; or define where lv_conf.h is, relative to the `lvgl` folder
;-D LV_CONF_PATH=../../../../src/gui_general_and_keys/lv_conf.h
; --- interesting lvgl debug infos (OSD)
;-D LV_USE_PERF_MONITOR=1
;-D LV_USE_MEM_MONITOR=1
;-D LV_USE_REFR_DEBUG=1
[env:esp32]
platform = espressif32

View File

@ -12,6 +12,8 @@
// show received IR and MQTT messages
#include "guis/gui_irReceiver.h"
uint16_t COMMAND_UNKNOWN;
uint16_t KEYBOARD_DUMMY_UP ; //"Keyboard_dummy_up"
uint16_t KEYBOARD_DUMMY_DOWN ; //"Keyboard_dummy_down"
uint16_t KEYBOARD_DUMMY_RIGHT ; //"Keyboard_dummy_right"

View File

@ -7,6 +7,8 @@
#include "devices/keyboard/device_keyboard_mqtt/device_keyboard_mqtt.h"
#include "devices/keyboard/device_keyboard_ble/device_keyboard_ble.h"
extern uint16_t COMMAND_UNKNOWN;
/*
Depending on which keyboard is enabled (BLE or MQTT), we define KEYBOARD_UP, KEYBOARD_DOWN and so on.
These defines are used in keys.cpp, gui*.cpp and commandHandler.cpp

View File

@ -2,6 +2,9 @@
#include "applicationInternal/hardware/hardwarePresenter.h"
#include "applicationInternal/memoryUsage.h"
#include "applicationInternal/gui/guiMemoryOptimizer.h"
// for changing to scene Selection gui
#include "applicationInternal/commandHandler.h"
#include "scenes/scene__defaultKeys.h"
lv_color_t color_primary = lv_color_hex(0x303030); // gray
lv_obj_t* MemoryUsageLabel = NULL;
@ -26,10 +29,26 @@ lv_style_t style_red_border;
#endif
void guis_doTabCreationAtStartup();
void guis_doAfterSliding(int oldTabID, int newTabID);
void guis_doAfterSliding(int oldTabID, int newTabID, bool newGuiList);
// Helper Functions -----------------------------------------------------------------------------------------------------------------------
// callback when sceneLabel or pageIndicator was clicked
void sceneLabel_or_pageIndicator_event_cb(lv_event_t* e) {
Serial.println("- Scene selection: sceneLabel or pageIndicator clicked received for navigating to scene selection page");
executeCommand(SCENE_SELECTION);
}
// callback for swipe down event to navigate to the scene selection page
void screen_gesture_event_cb(lv_event_t* e) {
lv_obj_t* screen = lv_event_get_current_target(e);
lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_get_act());
if (dir == LV_DIR_BOTTOM) {
Serial.println("- Scene selection: swipe down received for navigating to scene selection page");
executeCommand(SCENE_SELECTION);
}
}
// Set the page indicator (panel) scroll position relative to the tabview content scroll position
// this is a callback if the CONTENT of the tabview is scrolled (LV_EVENT_SCROLL)
void tabview_content_is_scrolling_event_cb(lv_event_t* e){
@ -62,7 +81,7 @@ static void tabview_animation_ready_cb(lv_anim_t* a) {
// We have to wait some more milliseconds or at least one cycle of lv_timer_handler();
// calling lv_timer_handler(); here does not help
// lv_timer_handler();
// guis_doAfterSliding(oldTabID, currentTabID);
// guis_doAfterSliding(oldTabID, currentTabID, false);
waitBeforeActionAfterSlidingAnimationEnded = true;
waitBeforeActionAfterSlidingAnimationEnded_timerStart = millis();
@ -77,7 +96,7 @@ void tabview_tab_changed_event_cb(lv_event_t* e) {
oldTabID = currentTabID;
currentTabID = lv_tabview_get_tab_act(lv_event_get_target(e));
// Wait until the animation ended, then call "guis_doAfterSliding(oldTabID, currentTabID);"
// Wait until the animation ended, then call "guis_doAfterSliding(oldTabID, currentTabID, false);"
// https://forum.lvgl.io/t/delete-a-tab-after-the-tabview-scroll-animation-is-complete/3155/4
lv_obj_t* myTabview = lv_event_get_target(e);
lv_obj_t* tabContainer = lv_tabview_get_content(myTabview);
@ -91,7 +110,7 @@ void tabview_tab_changed_event_cb(lv_event_t* e) {
} else {
// Swipe is complete, no additional animation is needed. Most likely only possible in simulator
Serial.println("Change of tab detected, without animation at the end. Will directly do my job after sliding.");
guis_doAfterSliding(oldTabID, currentTabID);
guis_doAfterSliding(oldTabID, currentTabID, false);
}
}
}
@ -131,6 +150,10 @@ void init_gui(void) {
init_gui_memoryUsage_bar();
// status bar
init_gui_status_bar();
// register callback for swipe down event to navigate to the scene selection page
lv_obj_add_event_cb(lv_scr_act(), screen_gesture_event_cb, LV_EVENT_GESTURE, NULL);
}
int panelHeight;
@ -205,6 +228,7 @@ void init_gui_memoryUsage_bar() {
void init_gui_status_bar() {
// Create a status bar at the top -------------------------------------------------------------------------
statusbar = lv_btn_create(lv_scr_act());
lv_obj_clear_flag(statusbar, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(statusbar, SCR_WIDTH, statusbarHeight);
lv_obj_set_style_shadow_width(statusbar, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(statusbar, lv_color_black(), LV_PART_MAIN);
@ -231,6 +255,9 @@ void init_gui_status_bar() {
lv_label_set_text(SceneLabel, "");
lv_obj_align(SceneLabel, LV_ALIGN_TOP_MID, 0, labelsPositionTopStatusbar);
lv_obj_set_style_text_font(SceneLabel, &lv_font_montserrat_12, LV_PART_MAIN);
lv_obj_add_flag(SceneLabel, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(SceneLabel, sceneLabel_or_pageIndicator_event_cb, LV_EVENT_CLICKED, NULL);
// Battery ----------------------------------------------------------------------
BattPercentageLabel = lv_label_create(statusbar);
lv_label_set_text(BattPercentageLabel, "");
@ -251,12 +278,12 @@ void gui_loop(void) {
waitBeforeActionAfterSlidingAnimationEnded = false;
} else if (waitOneLoop) {
waitOneLoop = false;
guis_doAfterSliding(oldTabID, currentTabID);
guis_doAfterSliding(oldTabID, currentTabID, false);
};
// // as alternative, we could wait some milliseconds. But one cycle of gui_loop() works well.
// if (waitBeforeActionAfterSlidingAnimationEnded) {
// if (millis() - waitBeforeActionAfterSlidingAnimationEnded_timerStart >= 5) {
// guis_doAfterSliding(oldTabID, currentTabID);
// guis_doAfterSliding(oldTabID, currentTabID, false);
// waitBeforeActionAfterSlidingAnimationEnded = false;
// }
// }
@ -267,11 +294,13 @@ void gui_loop(void) {
void guis_doTabCreationAtStartup() {
gui_memoryOptimizer_prepare_startup();
guis_doAfterSliding(-1, -1);
guis_doAfterSliding(-1, -1, false);
}
void guis_doAfterSliding(int oldTabID, int newTabID) {
gui_memoryOptimizer_doAfterSliding_deletionAndCreation(&tabview, oldTabID, newTabID, &panel, &img1, &img2);
void guis_doAfterSliding(int oldTabID, int newTabID, bool newGuiList) {
// With parameter newGuiList it is signaled that we are changing from a scene specific list to the main list or vice versa
// In that case, we have to do special treatment because we are not simply sliding to the left or to the right, but we start newly with a different gui list.
gui_memoryOptimizer_doAfterSliding_deletionAndCreation(&tabview, oldTabID, newTabID, newGuiList, &panel, &img1, &img2);
doLogMemoryUsage();
}

View File

@ -30,8 +30,11 @@ void gui_loop(void);
// used by guiMemoryOptimizer.cpp
void tabview_content_is_scrolling_event_cb(lv_event_t* e);
void tabview_tab_changed_event_cb(lv_event_t* e);
void sceneLabel_or_pageIndicator_event_cb(lv_event_t* e);
void setActiveTab(uint32_t index, lv_anim_enable_t anim_en);
// used by memoryUsage.cpp
void showMemoryUsageBar(bool showBar);
// used by commandHandler to show WiFi status
void showWiFiConnected(bool connected);
void guis_doAfterSliding(int oldTabID, int newTabID, bool newGuiList);

View File

@ -1,13 +1,16 @@
#include <lvgl.h>
#include "applicationInternal/gui/guiBase.h"
#include "applicationInternal/gui/guiRegistry.h"
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/hardware/hardwarePresenter.h"
struct tab_in_memory {
lv_obj_t* tab;
int listIndex;
std::string guiName;
};
tab_in_memory tabs_in_memory[3] = {{NULL, -1}, {NULL, -1}, {NULL, -1}};
tab_in_memory tabs_in_memory[3] = {{NULL, -1, ""}, {NULL, -1, ""}, {NULL, -1, ""}};
// holds the ids of the tabs we had in memory before, so that we can determine the next or previous id
int tabs_in_memory_previous_listIndex[3]= {-1 , -1, -1};
void notify_active_tabs_before_delete() {
@ -19,7 +22,9 @@ void notify_active_tabs_before_delete() {
continue;
}
nameOfTab = list_of_guis_to_be_shown.at(tabs_in_memory[index].listIndex);
// For deletion, do not use the listIndex, but the name of the gui.
// The gui_list might have changed (when switching from a scene specific list to the main list or vice versa), so index could have changed as well.
nameOfTab = tabs_in_memory[index].guiName;
if (nameOfTab == "") {
Serial.printf(" Will not notify tab %d about deletion because it is not set\r\n", index);
} else if (registered_guis_byName_map.count(nameOfTab) == 0) {
@ -42,9 +47,9 @@ void clear_tabview(lv_obj_t* tabview) {
}
// set struct tabs_in_memory to NULL
tabs_in_memory[0] = {NULL, -1};
tabs_in_memory[1] = {NULL, -1};
tabs_in_memory[2] = {NULL, -1};
tabs_in_memory[0] = {NULL, -1, ""};
tabs_in_memory[1] = {NULL, -1, ""};
tabs_in_memory[2] = {NULL, -1, ""};
}
@ -80,6 +85,7 @@ lv_obj_t* create_panel() {
// Create a page indicator at the bottom ------------------------------------------------------------------
lv_obj_t* panel = lv_obj_create(lv_scr_act());
lv_obj_clear_flag(panel, LV_OBJ_FLAG_CLICKABLE); // This indicator will not be clickable
lv_obj_clear_flag(panel, LV_OBJ_FLAG_SCROLLABLE); // This indicator will not be scrollable
lv_obj_set_size(panel, SCR_WIDTH, panelHeight);
lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_ROW);
lv_obj_align(panel, LV_ALIGN_BOTTOM_MID, 0, 0);
@ -91,8 +97,8 @@ lv_obj_t* create_panel() {
std::string get_name_of_gui_to_be_shown(int index) {
if (index == -1) {
return "";
} else if (index <= list_of_guis_to_be_shown.size() -1) {
return list_of_guis_to_be_shown.at(index);
} else if (index <= get_gui_list(get_currentScene())->size() -1) {
return get_gui_list(get_currentScene())->at(index);
} else {
return "";
}
@ -107,6 +113,8 @@ void create_new_tab(lv_obj_t* tabview, uint32_t tabs_in_memory_index) {
Serial.printf(" Will not create new tab at index %d because name %s was not found in registry\r\n", tabs_in_memory_index, nameOfTab.c_str());
} else {
Serial.printf(" Will create tab with name \"%s\" at index %d\r\n", nameOfTab.c_str(), tabs_in_memory_index);
// save name of tab for deletion later
tabs_in_memory[tabs_in_memory_index].guiName = nameOfTab;
// create tab and save pointer to tab in tabs_in_memory
tabs_in_memory[tabs_in_memory_index].tab = lv_tabview_add_tab(tabview, nameOfTab.c_str());
// let the gui create it's content
@ -117,7 +125,7 @@ void create_new_tab(lv_obj_t* tabview, uint32_t tabs_in_memory_index) {
void doTabCreation_strategyMax3(lv_obj_t* tabview, uint32_t oldTabID, uint32_t newTabID) {
// create up to three tabs and the content of the tabs
/*
example: list_of_guis_to_be_shown: 0 1 2 3 4
example: gui_list: 0 1 2 3 4
in memory active
0 1 -1 0 <- first state, special case - also the initial state
0 1 2 1
@ -132,8 +140,8 @@ void doTabCreation_strategyMax3(lv_obj_t* tabview, uint32_t oldTabID, uint32_t n
if ((oldTabID == -1) && (newTabID == -1)) {
// This is the initialization after the ESP32 has booted.
if ((tabs_in_memory_previous_listIndex[0] < list_of_guis_to_be_shown.size()) && (tabs_in_memory_previous_listIndex[0] != -1)) {
// In gui_memoryOptimizer_prepare_startup, the index of get_currentGUIname() in list_of_guis_to_be_shown was saved, if found.
if ((tabs_in_memory_previous_listIndex[0] < get_gui_list(get_currentScene())->size()) && (tabs_in_memory_previous_listIndex[0] != -1)) {
// In gui_memoryOptimizer_prepare_startup, the index of get_currentGUIname() in gui_list was saved, if found.
// We can resume at this old state.
oldListIndex = tabs_in_memory_previous_listIndex[0] ;
if (oldListIndex == 0) {
@ -141,10 +149,10 @@ void doTabCreation_strategyMax3(lv_obj_t* tabview, uint32_t oldTabID, uint32_t n
Serial.printf(" Startup: will resume where we went to sleep with \"first state\"\r\n");
tabs_in_memory[0] = {NULL, 0};
// take care if there is only one gui in list
tabs_in_memory[1] = {NULL, list_of_guis_to_be_shown.size() >= 2 ? 1 : -1};
tabs_in_memory[1] = {NULL, get_gui_list(get_currentScene())->size() >= 2 ? 1 : -1};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 0;
} else if (oldListIndex == list_of_guis_to_be_shown.size() -1) {
} else if (oldListIndex == get_gui_list(get_currentScene())->size() -1) {
// last state
Serial.printf(" Startup: will resume where we went to sleep with \"last state\"\r\n");
tabs_in_memory[0] = {NULL, oldListIndex -1};
@ -160,11 +168,11 @@ void doTabCreation_strategyMax3(lv_obj_t* tabview, uint32_t oldTabID, uint32_t n
tabToBeActivated = 1;
}
} else {
Serial.printf(" Startup: cannot resume old state, so we will show the first tabs from \"list_of_guis_to_be_shown\" as initial state\r\n");
Serial.printf(" Startup: cannot resume old state, so we will show the first tabs from \"gui_list\" as initial state\r\n");
// take care if there is no gui in list
tabs_in_memory[0] = {NULL, list_of_guis_to_be_shown.size() != 0 ? 0 : -1};
tabs_in_memory[0] = {NULL, get_gui_list(get_currentScene())->size() != 0 ? 0 : -1};
// take care if there is only one gui in list
tabs_in_memory[1] = {NULL, list_of_guis_to_be_shown.size() >= 2 ? 1 : -1};
tabs_in_memory[1] = {NULL, get_gui_list(get_currentScene())->size() >= 2 ? 1 : -1};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 0;
}
@ -193,7 +201,7 @@ void doTabCreation_strategyMax3(lv_obj_t* tabview, uint32_t oldTabID, uint32_t n
} else {
oldListIndex = tabs_in_memory_previous_listIndex[1];
}
if (oldListIndex == list_of_guis_to_be_shown.size() -2) {
if (oldListIndex == get_gui_list(get_currentScene())->size() -2) {
// next state is the "last state"
tabs_in_memory[0] = {NULL, oldListIndex};
tabs_in_memory[1] = {NULL, oldListIndex +1};
@ -213,8 +221,8 @@ void doTabCreation_strategyMax3(lv_obj_t* tabview, uint32_t oldTabID, uint32_t n
create_new_tab(tabview, i);
}
if (list_of_guis_to_be_shown.size() > 0) {
std::string nameOfNewActiveTab = list_of_guis_to_be_shown.at(tabs_in_memory[tabToBeActivated].listIndex);
if (get_gui_list(get_currentScene())->size() > 0) {
std::string nameOfNewActiveTab = get_gui_list(get_currentScene())->at(tabs_in_memory[tabToBeActivated].listIndex);
Serial.printf(" New visible tab is \"%s\"\r\n", nameOfNewActiveTab.c_str());
// set the tab we swiped to as active
@ -227,10 +235,24 @@ void doTabCreation_strategyMax3(lv_obj_t* tabview, uint32_t oldTabID, uint32_t n
LV_IMG_DECLARE(gradientLeft);
LV_IMG_DECLARE(gradientRight);
void getBreadcrumpPosition(uint8_t* breadcrumpPosition, std::string nameOfTab) {
*breadcrumpPosition = 0;
gui_list currentGUIlist = get_gui_list(get_currentScene());
uint8_t counter = 0;
for (std::vector<std::string>::iterator it = currentGUIlist->begin() ; it != currentGUIlist->end(); ++it) {
counter++;
if (*it == nameOfTab) {
*breadcrumpPosition = counter;
return;
}
}
}
void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv_obj_t* img2) {
Serial.printf(" Will fill panel with page indicators\r\n");
if (list_of_guis_to_be_shown.size() == 0) {
if (get_gui_list(get_currentScene())->size() == 0) {
Serial.printf(" no tab available, so no page indicators\r\n");
// at least add the style
lv_obj_add_style(panel, &panel_style, 0);
@ -250,7 +272,7 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
There needs to be two more screen indicators because of their different size (158 for page indicator, 240 for whole tab)
In some cases they need to have color black, if they are before the first tab or after the last tab.
In all other cases, they have color "color_primary". See this list:
example: list_of_guis_to_be_shown: 0 1 2 3 4
example: gui_list: 0 1 2 3 4
in memory color active
0 1 -1 b p p p 0 <- first state, special case - also the initial state
0 1 2 b p p p p 1
@ -268,21 +290,54 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
}
uint8_t breadcrumpLength = get_gui_list(get_currentScene())->size();
uint8_t breadcrumpDotSize = 8; // should be an even number
uint8_t breadcrumpDotDistance = 2; // should be an even number
int8_t breadcrumpStartPositionX = (-1) * (breadcrumpLength -1) * (breadcrumpDotSize + breadcrumpDotDistance) / 2;
// create the panel content for the three guis (or less) which are currently in memory
std::string nameOfTab;
uint8_t breadcrumpPosition;
for (int i=0; i<3; i++) {
if (tabs_in_memory[i].listIndex != -1) {
nameOfTab = get_name_of_gui_to_be_shown(tabs_in_memory[i].listIndex);
nameOfTab = tabs_in_memory[i].guiName;
getBreadcrumpPosition(&breadcrumpPosition, nameOfTab);
// Create actual (non-clickable) buttons for every tab
// Create actual buttons for every tab
lv_obj_t* btn = lv_btn_create(panel);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
// only if this is the button for the currently active tab, make it clickable to get to scene selection gui
if (nameOfTab == get_currentGUIname()) {
lv_obj_add_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(btn, sceneLabel_or_pageIndicator_event_cb, LV_EVENT_CLICKED, NULL);
}
lv_obj_set_size(btn, 150, lv_pct(100));
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text_fmt(label, "%s", nameOfTab.c_str());
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_remove_style(btn, NULL, LV_STATE_PRESSED);
lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
// create a breadcrump dot for each gui in the list
for (int j=0; j<breadcrumpLength; j++) {
lv_obj_t* dot = lv_obj_create(btn);
lv_obj_set_size(dot, breadcrumpDotSize, breadcrumpDotSize);
lv_obj_set_style_radius(dot, LV_RADIUS_CIRCLE, LV_PART_MAIN);
// hightlight dot if it is the one for the currently active tab
if (j == (breadcrumpPosition-1)) {
lv_obj_set_style_bg_color(dot, lv_color_lighten(color_primary, 200), LV_PART_MAIN);
} else {
lv_obj_set_style_bg_color(dot, lv_color_lighten(color_primary, 100), LV_PART_MAIN);
}
lv_obj_align(dot, LV_ALIGN_TOP_MID, breadcrumpStartPositionX +j*(breadcrumpDotSize + breadcrumpDotDistance), -6);
// this dot needs to get clickable again
lv_obj_add_flag(dot, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(dot, LV_OBJ_FLAG_EVENT_BUBBLE);
}
lv_obj_t* label = lv_label_create(btn);
lv_obj_set_style_text_font(label, &lv_font_montserrat_10, LV_PART_MAIN);
lv_label_set_text_fmt(label, "%s", nameOfTab.c_str());
lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, 6);
}
}
@ -291,7 +346,7 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(btn, 150, lv_pct(100));
// 4 at last position 4 at middle position only one tab available overall
if ((tabs_in_memory[2].listIndex == list_of_guis_to_be_shown.size()-1) || (tabs_in_memory[1].listIndex == list_of_guis_to_be_shown.size()-1) || (tabs_in_memory[1].listIndex == -1)) {
if ((tabs_in_memory[2].listIndex == get_gui_list(get_currentScene())->size()-1) || (tabs_in_memory[1].listIndex == get_gui_list(get_currentScene())->size()-1) || (tabs_in_memory[1].listIndex == -1)) {
lv_obj_set_style_bg_color(btn, lv_color_black(), LV_PART_MAIN);
} else {
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
@ -333,40 +388,66 @@ void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv
}
void gui_memoryOptimizer_prepare_startup() {
// find index of get_currentGUIname() in list_of_guis_to_be_shown
for (int i=0; i<list_of_guis_to_be_shown.size(); i++) {
if (list_of_guis_to_be_shown[i] == get_currentGUIname()) {
Serial.printf("Startup: found GUI with name \"%s\" in \"list_of_guis_to_be_shown\" at position %d\r\n", get_currentGUIname().c_str(), i);
// find index of get_currentGUIname() in gui_list
for (int i=0; i<get_gui_list(get_currentScene())->size(); i++) {
if (get_gui_list(get_currentScene())->at(i) == get_currentGUIname()) {
Serial.printf("Startup: found GUI with name \"%s\" in \"gui_list\" at position %d\r\n", get_currentGUIname().c_str(), i);
// save position so that "guis_doAfterSliding" can use it
tabs_in_memory[0].listIndex = i;
}
}
// if the gui was not found in main_gui_list, try to find it in scene specific list
if (tabs_in_memory[0].listIndex == -1) {
useSceneGUIlist = true;
for (int i=0; i<get_gui_list(get_currentScene())->size(); i++) {
if (get_gui_list(get_currentScene())->at(i) == get_currentGUIname()) {
Serial.printf("Startup: found GUI with name \"%s\" in scene specific \"gui_list\" at position %d\r\n", get_currentGUIname().c_str(), i);
// save position so that "guis_doAfterSliding" can use it
tabs_in_memory[0].listIndex = i;
}
}
}
// if the gui was still not found, reset useSceneGUIlist
if (tabs_in_memory[0].listIndex == -1) {
useSceneGUIlist = false;
}
}
void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview, int oldTabID, int newTabID, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2) {
void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview, int oldTabID, int newTabID, bool newGuiList, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2) {
// Here the magic for dynamic creation and deletion of lvgl objects happens to keep memory usage low.
// The next and previous tab must always be available in the tabview, because they can already been seen during the animation.
// And you always need 3 tabs, otherwise you even could not slide to the next or previous tab.
// So we always have 3 tabs.
// After the animation, the tabview and hence all tabs are deleted and recreated.
// With parameter newGuiList it is signaled that we are changing from a scene specific list to the main list or vice versa
// In that case, we have to do special treatment because we are not simply sliding to the left or to the right, but we start newly with a different gui list.
// only called by guis_doTabCreationAtStartup():
// oldTabID = -1, newTabID = -1, newGuiList = false
// called by handleScene()
// oldTabID = -1, newTabID = -1, newGuiList = true
Serial.printf("--- Start of tab deletion and creation\r\n");
bool isInitialization = ((oldTabID == -1) && (newTabID == -1));
if (isInitialization) {
Serial.printf("Startup: will initially create the tabs to be shown\r\n");
} else {
Serial.printf("Changing from oldTabID %d \"%s\" to newTabID %d \"%s\"\r\n",
oldTabID, list_of_guis_to_be_shown.at(tabs_in_memory[oldTabID].listIndex).c_str(),
newTabID, list_of_guis_to_be_shown.at(tabs_in_memory[newTabID].listIndex).c_str());
oldTabID, get_gui_list(get_currentScene())->at(tabs_in_memory[oldTabID].listIndex).c_str(),
newTabID, get_gui_list(get_currentScene())->at(tabs_in_memory[newTabID].listIndex).c_str());
}
// save the ids of the tabs we had in memory before
// Save the ids of the tabs we had in memory before. This is only used by doTabCreation_strategyMax3() to know where we come from and where we have to go to.
for (int i=0; i<3; i++) {
tabs_in_memory_previous_listIndex[i] = tabs_in_memory[i].listIndex;
}
// the old tabs need to be notified that they will be deleted so that they can persist their state if needed
if (!isInitialization) notify_active_tabs_before_delete();
notify_active_tabs_before_delete();
// clear current tabview
clear_tabview(*tabview);
// clear current panel for page indicator
@ -374,13 +455,19 @@ void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview,
// only optional: delete and create the whole screen. Not necessary.
// Only used for a test. init_gui_status_bar() would need to be called again at a suitable place, because the status bar would also be deleted.
// lv_obj_t * oldscr = lv_scr_act();
// lv_obj_t* oldscr = lv_scr_act();
// // create new screen
// lv_obj_t * newscr = lv_obj_create(NULL);
// lv_obj_t* newscr = lv_obj_create(NULL);
// // load this new screen
// lv_scr_load(newscr);
// lv_obj_del(oldscr);
if (newGuiList) {
// If we are switching to a new gui list, then we need to set tabs_in_memory_previous_listIndex[0] = -1;
// Doing so, doTabCreation_strategyMax3() knows that we cannot resume an old state.
tabs_in_memory_previous_listIndex[0] = -1;
}
// recreate the tabview
lv_obj_t* newTabview = create_tabview();
*tabview = newTabview;
@ -401,4 +488,6 @@ void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview,
// Initialize scroll position of the page indicator
lv_event_send(lv_tabview_get_content(*tabview), LV_EVENT_SCROLL, NULL);
Serial.printf("------------ End of tab deletion and creation\r\n");
}

View File

@ -1,4 +1,4 @@
#pragma once
void gui_memoryOptimizer_prepare_startup();
void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview, int oldTabID, int newTabID, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2);
void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview, int oldTabID, int newTabID, bool newGuiList, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2);

View File

@ -6,13 +6,11 @@
#include "guiRegistry.h"
#include "applicationInternal/gui/guiBase.h"
#include "applicationInternal/hardware/hardwarePresenter.h"
#include "scenes/scene__defaultKeys.h"
// ------------------------------------------------------------------------------------
// this is a map of the registered_guis that can be accessed by name
std::map<std::string, gui_definition> registered_guis_byName_map;
// This is the list of the guis that we want to be available when swiping. Need not to be all the guis that have been registered, can be only a subset.
// You can swipe through these guis. Will be in the order you place them here in the vector.
std::vector<std::string> list_of_guis_to_be_shown;
// ------------------------------------------------------------------------------------
@ -29,8 +27,8 @@ void register_gui(std::string a_name, create_tab_content a_create_tab_content, n
// put the gui_definition in a map that can be accessed by name
registered_guis_byName_map[a_name] = new_gui_definition;
// By default, put all registered guis in the sequence of guis to be shown
// Later we will have scene specific sequences of guis
list_of_guis_to_be_shown.insert(list_of_guis_to_be_shown.end(), {std::string(a_name)});
// By default, put all registered guis in the sequence of guis to be shown of the default scene
// Can be overwritten by scenes to have their own gui_list.
main_gui_list.insert(main_gui_list.end(), {std::string(a_name)});
}

View File

@ -36,6 +36,5 @@ struct gui_definition {
};
extern std::map<std::string, gui_definition> registered_guis_byName_map;
extern std::vector<std::string> list_of_guis_to_be_shown;
void register_gui(std::string a_name, create_tab_content a_create_tab_content, notify_tab_before_delete a_notify_tab_before_delete);

View File

@ -1,7 +1,5 @@
#include <string>
#include "devices/misc/device_specialCommands.h"
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/scenes/sceneHandler.h"
#include "applicationInternal/commandHandler.h"
#include "applicationInternal/hardware/hardwarePresenter.h"

View File

@ -4,11 +4,30 @@
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/hardware/hardwarePresenter.h"
#include "applicationInternal/commandHandler.h"
#include "scenes/scene__defaultKeys.h"
void handleScene(uint16_t command, commandData commandData, std::string additionalPayload = "") {
// FORCE can be either as second payload in commandData
// e.g. register_command(&SCENE_TV_FORCE , makeCommandData(SCENE, {scene_name_TV, "FORCE"}));
// or as additionalPayload, used by gui_sceneSelection.cpp
// e.g. executeCommand(activate_scene_command, "FORCE");
auto current = commandData.commandPayloads.begin();
std::string scene_name = *current;
// we can have a second payload
std::string isForcePayload = "";
++current;
if (current != commandData.commandPayloads.end()) {
isForcePayload = *current;
}
// do not really switch scene, but show sceneSelection gui. From that on, we are in the main_gui_list.
if (scene_name == scene_name_selection) {
useSceneGUIlist = false;
guis_doAfterSliding(-1, -1, true);
return;
}
// check if we know the new scene
if (!sceneExists(scene_name)) {
@ -18,9 +37,23 @@ void handleScene(uint16_t command, commandData commandData, std::string addition
Serial.printf("scene: will switch from old scene %s to new scene %s\r\n", get_currentScene().c_str(), scene_name.c_str());
}
// do not activate the same scene again, only when forced to do so (e.g. by long press on the gui or when selected by hardware key)
bool callEndAndStartSequences;
if ((scene_name == get_currentScene()) && ((isForcePayload != "FORCE") && (additionalPayload != "FORCE"))) {
Serial.printf("scene: will not start scene again, because it is already active\r\n");
callEndAndStartSequences = false;
} else if ((scene_name == get_currentScene()) && ((isForcePayload == "FORCE") || (additionalPayload == "FORCE"))) {
Serial.printf("scene: scene is already active, but FORCE was set, so start scene again\r\n");
callEndAndStartSequences = true;
} else {
// this is the default when switching to a different scene
callEndAndStartSequences = true;
}
if (SceneLabel != NULL) {lv_label_set_text(SceneLabel, "changing...");}
gui_loop();
if (callEndAndStartSequences) {
// end old scene
if (!sceneExists(get_currentScene()) && (get_currentScene() != "")) {
Serial.printf("scene: WARNING: cannot end scene %s, because it is unknown\r\n", get_currentScene().c_str());
@ -36,12 +69,17 @@ void handleScene(uint16_t command, commandData commandData, std::string addition
// start new scene
Serial.printf("scene: will call start sequence for scene %s\r\n", scene_name.c_str());
scene_start_sequence_from_registry(scene_name);
}
set_currentScene(scene_name);
if (SceneLabel != NULL) {lv_label_set_text(SceneLabel, get_currentScene().c_str());}
Serial.printf("scene: scene handling finished, new scene %s is active\r\n", get_currentScene().c_str());
useSceneGUIlist = true;
// recreate the gui based on the current scene
guis_doAfterSliding(-1, -1, true);
}
void setLabelCurrentScene() {

View File

@ -1,11 +1,16 @@
#include <map>
#include <stdexcept>
#include "devices/misc/device_specialCommands.h"
#include "applicationInternal/commandHandler.h"
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/hardware/hardwarePresenter.h"
// scenes
#include "scenes/scene__defaultKeys.h"
// If useSceneGUIlist == true, then a scene is active and we are not in the main_gui_list (with the scene selector as first gui).
// In that case, we try to use the scene specific gui list, if the scene defined one.
// If useSceneGUIlist == false, then we are in the main_gui_list, either if a scene is active or not.
bool useSceneGUIlist = false;
// https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work
struct scene_definition {
scene_setKeys this_scene_setKeys;
@ -14,9 +19,12 @@ struct scene_definition {
key_repeatModes this_key_repeatModes;
key_commands_short this_key_commands_short;
key_commands_long this_key_commands_long;
gui_list this_gui_list;
uint16_t this_activate_scene_command;
};
std::map<std::string, scene_definition> registered_scenes;
t_scene_list scenes_on_sceneSelectionGUI;
void register_scene(
std::string a_scene_name,
@ -25,17 +33,27 @@ void register_scene(
scene_end_sequence a_scene_end_sequence,
key_repeatModes a_key_repeatModes,
key_commands_short a_key_commands_short,
key_commands_long a_key_commands_long) {
key_commands_long a_key_commands_long,
gui_list a_gui_list,
uint16_t a_activate_scene_command) {
// put the scene_definition in a map that can be accessed by name
registered_scenes[a_scene_name] = scene_definition{
a_scene_setKeys,
a_scene_start_sequence,
a_scene_end_sequence,
a_key_repeatModes,
a_key_commands_short,
a_key_commands_long
a_key_commands_long,
a_gui_list,
a_activate_scene_command
};
// Additionally, put all registered scenes in a sequence of scenes to be shown in the sceneSelection gui.
// Exactly in the order they have been registered.
// Can be overwritten in main.cpp
scenes_on_sceneSelectionGUI.insert(scenes_on_sceneSelectionGUI.end(), {std::string(a_scene_name)});
// Whenever a new scene is registered, normally a new scene command has been defined immediately before (e.g. see register_scene_TV()).
// But this new scene command could have been already been used before in the key definition of another scene. The command at this time was 0, which is undefined.
// So we have to set the keys again for all scenes that have been registered before.
@ -98,17 +116,17 @@ uint16_t get_command_short(std::string sceneName, char keyChar) {
try {
// look if the map of the current scene has a definition for it
if ((registered_scenes.count(sceneName) > 0) && (registered_scenes.at(sceneName).this_key_commands_short->count(keyChar) > 0)) {
Serial.printf("get_command_short: will use key from scene %s\r\n", sceneName.c_str());
// Serial.printf("get_command_short: will use key from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_key_commands_short->at(keyChar);
// look if there is a default definition
} else if (key_commands_short_default.count(keyChar) > 0) {
Serial.printf("get_command_short: will use default key\r\n");
// Serial.printf("get_command_short: will use default key\r\n");
return key_commands_short_default.at(keyChar);
// no key definition found
} else {
Serial.printf("get_command_short: WARNING no key definition found\r\n");
// Serial.printf("get_command_short: WARNING no key definition found\r\n");
return COMMAND_UNKNOWN;
}
}
@ -123,17 +141,17 @@ uint16_t get_command_long(std::string sceneName, char keyChar) {
try {
// look if the map of the current scene has a definition for it
if ((registered_scenes.count(sceneName) > 0) && (registered_scenes.at(sceneName).this_key_commands_long->count(keyChar) > 0)) {
Serial.printf("get_command_long: will use key from scene %s\r\n", sceneName.c_str());
// Serial.printf("get_command_long: will use key from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_key_commands_long->at(keyChar);
// look if there is a default definition
} else if (key_commands_long_default.count(keyChar) > 0) {
Serial.printf("get_command_long: will use default key\r\n");
// Serial.printf("get_command_long: will use default key\r\n");
return key_commands_long_default.at(keyChar);
// no key definition found
} else {
Serial.printf("get_command_long: WARNING no key definition found\r\n");
// Serial.printf("get_command_long: WARNING no key definition found\r\n");
return COMMAND_UNKNOWN;
}
}
@ -144,6 +162,71 @@ uint16_t get_command_long(std::string sceneName, char keyChar) {
}
gui_list get_gui_list(std::string sceneName) {
try {
// If useSceneGUIlist == true, then a scene is active and we are not in the main_gui_list (with the scene selector as first gui).
// In that case, we try to use the scene specific gui list, if the scene defined one.
// If useSceneGUIlist == false, then we are in the main_gui_list, either if a scene is active or not.
#if (USE_SCENE_SPECIFIC_GUI_LIST != 0)
// look if the current scene has a definition for a gui list
if (useSceneGUIlist &&
(registered_scenes.count(sceneName) > 0) && (registered_scenes.at(sceneName).this_gui_list != NULL)) {
// Serial.printf("get_gui_list: will use gui_list from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_gui_list;
// if there is no scene specific gui list, simply return the main_gui_list
} else {
// Serial.printf("get_gui_list: will use main_gui_list\r\n");
return &main_gui_list;
}
#else
// never use scene specific gui list
return &main_gui_list;
#endif
}
catch (const std::out_of_range& oor) {
Serial.printf("get_gui_list: internal error, sceneName not registered\r\n");
return NULL;
}
}
uint16_t get_activate_scene_command(std::string sceneName) {
try {
// look if the current scene is known
if ((registered_scenes.count(sceneName) > 0)) {
// Serial.printf("get_activate_scene_command: will use activate_scene_command from scene %s\r\n", sceneName.c_str());
return registered_scenes.at(sceneName).this_activate_scene_command;
// if the scene is not know, simply return 0
} else {
// Serial.printf("get_activate_scene_command: will return 0\r\n");
return 0;
}
}
catch (const std::out_of_range& oor) {
Serial.printf("get_activate_scene_command: internal error, sceneName not registered\r\n");
return 0;
}
}
scene_list get_scenes_on_sceneSelectionGUI() {
return &scenes_on_sceneSelectionGUI;
}
void set_scenes_on_sceneSelectionGUI(t_scene_list a_scene_list) {
scenes_on_sceneSelectionGUI.clear();
for (int i=0; i<a_scene_list.size(); i++) {
scenes_on_sceneSelectionGUI.insert(scenes_on_sceneSelectionGUI.end(), a_scene_list.at(i));
}
}
char KEY_OFF = 'o';
char KEY_STOP = '=';
char KEY_REWI = '<';

View File

@ -3,14 +3,22 @@
#include <string>
#include <stdint.h>
#include <map>
#include <vector>
#include "applicationInternal/keys.h"
extern bool useSceneGUIlist;
typedef std::vector<std::string> t_gui_list;
typedef std::vector<std::string> t_scene_list;
typedef void (*scene_setKeys)(void);
typedef void (*scene_start_sequence)(void);
typedef void (*scene_end_sequence)(void);
typedef std::map<char, repeatModes> *key_repeatModes;
typedef std::map<char, uint16_t> *key_commands_short;
typedef std::map<char, uint16_t> *key_commands_long;
typedef t_gui_list *gui_list;
typedef t_scene_list *scene_list;
void register_scene(
std::string a_scene_name,
@ -19,7 +27,9 @@ void register_scene(
scene_end_sequence a_scene_end_sequence,
key_repeatModes a_key_repeatModes,
key_commands_short a_key_commands_short,
key_commands_long a_key_commands_long);
key_commands_long a_key_commands_long,
gui_list a_gui_list = NULL,
uint16_t a_activate_scene_command = 0);
bool sceneExists(std::string sceneName);
void scene_start_sequence_from_registry(std::string sceneName);
@ -27,6 +37,10 @@ void scene_end_sequence_from_registry(std::string sceneName);
repeatModes get_key_repeatMode(std::string sceneName, char keyChar);
uint16_t get_command_short(std::string sceneName, char keyChar);
uint16_t get_command_long(std::string sceneName, char keyChar);
gui_list get_gui_list(std::string sceneName);
uint16_t get_activate_scene_command(std::string sceneName);
scene_list get_scenes_on_sceneSelectionGUI();
void set_scenes_on_sceneSelectionGUI(t_scene_list a_scene_list);
extern char KEY_OFF ;
extern char KEY_STOP ;

View File

@ -57,7 +57,7 @@ uint16_t SAMSUNG_POWER_OFF ; //"Samsung_power_off";
uint16_t SAMSUNG_POWER_ON ; //"Samsung_power_on";
uint16_t SAMSUNG_INPUT_HDMI_1 ; //"Samsung_input_hdmi_1";
uint16_t SAMSUNG_INPUT_HDMI_2 ; //"Samsung_input_hdmi_2";
// uint16_t SAMSUNG_INPUT_HDMI_3 ; //"Samsung_input_hdmi_3";
uint16_t SAMSUNG_INPUT_HDMI_3 ; //"Samsung_input_hdmi_3";
// uint16_t SAMSUNG_INPUT_HDMI_4 ; //"Samsung_input_hdmi_4";
// uint16_t SAMSUNG_INPUT_COMPONENT ; //"Samsung_input_component";
uint16_t SAMSUNG_INPUT_TV ; //"Samsung_input_tv";
@ -121,7 +121,7 @@ void register_device_samsungTV() {
register_command(&SAMSUNG_POWER_ON , makeCommandData(IR, {std::to_string(IR_PROTOCOL_SAMSUNG), "0xE0E09966"}));
register_command(&SAMSUNG_INPUT_HDMI_1 , makeCommandData(IR, {std::to_string(IR_PROTOCOL_SAMSUNG), "0xE0E09768"}));
register_command(&SAMSUNG_INPUT_HDMI_2 , makeCommandData(IR, {std::to_string(IR_PROTOCOL_SAMSUNG), "0xE0E07D82"}));
// register_command(&SAMSUNG_INPUT_HDMI_3 , makeCommandData(IR, {std::to_string(IR_PROTOCOL_SAMSUNG), "0xE0E043BC"}));
register_command(&SAMSUNG_INPUT_HDMI_3 , makeCommandData(IR, {std::to_string(IR_PROTOCOL_SAMSUNG), "0xE0E043BC"}));
// register_command(&SAMSUNG_INPUT_HDMI_4 , makeCommandData(IR, {std::to_string(IR_PROTOCOL_SAMSUNG), "0xE0E0A35C"}));
// register_command(&SAMSUNG_INPUT_COMPONENT , makeCommandData(IR, {std::to_string(IR_PROTOCOL_SAMSUNG), "0xE0E0619E"}));
register_command(&SAMSUNG_INPUT_TV , makeCommandData(IR, {std::to_string(IR_PROTOCOL_SAMSUNG), "0xE0E0D827"}));

View File

@ -54,7 +54,7 @@ extern uint16_t SAMSUNG_POWER_OFF;
extern uint16_t SAMSUNG_POWER_ON;
extern uint16_t SAMSUNG_INPUT_HDMI_1;
extern uint16_t SAMSUNG_INPUT_HDMI_2;
// extern uint16_t SAMSUNG_INPUT_HDMI_3;
extern uint16_t SAMSUNG_INPUT_HDMI_3;
// extern uint16_t SAMSUNG_INPUT_HDMI_4;
// extern uint16_t SAMSUNG_INPUT_COMPONENT;
extern uint16_t SAMSUNG_INPUT_TV;

View File

@ -35,7 +35,7 @@ static void smartHomeToggle_event_cb(lv_event_t* e){
// Smart Home Slider Event handler
static void smartHomeSlider_event_cb(lv_event_t* e){
lv_obj_t * slider = lv_event_get_target(e);
lv_obj_t* slider = lv_event_get_target(e);
char payload[8];
sprintf(payload, "%.2f", float(lv_slider_get_value(slider)));
std::string payload_str(payload);

View File

@ -1,7 +1,6 @@
#include "applicationInternal/commandHandler.h"
#include "device_specialCommands.h"
uint16_t COMMAND_UNKNOWN ;
uint16_t MY_SPECIAL_COMMAND; //"My_special_command";
// uint16_t TRIGGER_UPDATE_OF_OMOTE_SMART_HOME_DEVICES;

View File

@ -2,7 +2,6 @@
#include <stdint.h>
extern uint16_t COMMAND_UNKNOWN;
extern uint16_t MY_SPECIAL_COMMAND;
// extern uint16_t TRIGGER_UPDATE_OF_OMOTE_SMART_HOME_DEVICES;

View File

@ -0,0 +1,130 @@
#include <lvgl.h>
#include "applicationInternal/hardware/hardwarePresenter.h"
#include "applicationInternal/gui/guiBase.h"
#include "applicationInternal/gui/guiRegistry.h"
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/commandHandler.h"
#include "guis/gui_sceneSelection.h"
static uint16_t activate_scene_command;
static bool doForceScene;
//void activate_scene_async(void *command) {
// executeCommand(activate_scene_command);
//}
void activate_scene_cb(lv_timer_t *timer) {
uint16_t scene_command_including_force = (uintptr_t)(timer->user_data);
// get the force flag from the highest bit
uint16_t activate_scene_command = scene_command_including_force & 0x7FFF;
bool doForceScene = (scene_command_including_force & 0x8000) == 0x8000;
if (doForceScene) {
executeCommand(activate_scene_command, "FORCE");
} else {
executeCommand(activate_scene_command);
}
}
static int lastShortClickedReceived;
static unsigned long int lastShortClickedReceivedTime;
static void sceneSelection_event_cb(lv_event_t* e) {
int user_data = (intptr_t)(e->user_data);
// we will receive the following events in that order:
// LV_EVENT_PRESSED
// LV_EVENT_RELEASED
// only on short press: LV_EVENT_SHORT_CLICKED
// both on short press and long press: LV_EVENT_CLICKED
// if (lv_event_get_code(e) == LV_EVENT_PRESSED) {
// Serial.println("pressed");
// }
// if (lv_event_get_code(e) == LV_EVENT_RELEASED) {
// Serial.println("released");
// }
if (lv_event_get_code(e) == LV_EVENT_SHORT_CLICKED) {
lastShortClickedReceived = user_data;
lastShortClickedReceivedTime = millis();
// Serial.println("short clicked, will see what happens next");
return;
} else if (lv_event_get_code(e) == LV_EVENT_CLICKED) {
if ((lastShortClickedReceived == user_data) && (millis() - lastShortClickedReceivedTime < 10)) {
// Serial.println("clicked, will send short click");
doForceScene = false;
} else {
// Serial.println("clicked, will send long click");
doForceScene = true;
}
} else {
return;
}
std::string scene_name = get_scenes_on_sceneSelectionGUI()->at(user_data);
activate_scene_command = get_activate_scene_command(scene_name);
if (activate_scene_command != 0) {
// this line is needed
if (SceneLabel != NULL) {lv_label_set_text(SceneLabel, "changing...");}
// Problem: screen will not get updated and show "changing..." if "executeCommand(activate_scene_command);" is called here
// test 1 (does not work): call lv_timer_handler();
// lv_timer_handler();
// test 2 (does not work): async_call
// lv_async_call(activate_scene_async, &activate_scene_command);
// test 3: lv_timer_create()
// needs to run only once, and a very short period of 5 ms to wait until first run is enough
uint16_t scene_command_including_force;
if (doForceScene) {
// put the force flag into the highest bit
scene_command_including_force = activate_scene_command | 0x8000;
Serial.printf("Scene with index %d and name %s was FORCE selected\r\n", user_data, scene_name.c_str());
} else {
scene_command_including_force = activate_scene_command;
Serial.printf("Scene with index %d and name %s was selected\r\n", user_data, scene_name.c_str());
}
lv_timer_t *my_timer = lv_timer_create(activate_scene_cb, 20, (void *)(uintptr_t) scene_command_including_force);
lv_timer_set_repeat_count(my_timer, 1);
} else {
Serial.printf("Cannot activate scene %s, because command was not found\r\n", scene_name.c_str());
}
}
void create_tab_content_sceneSelection(lv_obj_t* tab) {
// Add content to the sceneSelection tab
lv_obj_set_layout(tab, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(tab, LV_FLEX_FLOW_COLUMN);
lv_obj_set_scrollbar_mode(tab, LV_SCROLLBAR_MODE_ACTIVE);
// -- create a button for each scene ----------------------------------------
scene_list scenes = get_scenes_on_sceneSelectionGUI();
if ((scenes != NULL) && (scenes->size() > 0)) {
for (int i=0; i<scenes->size(); i++) {
lv_obj_t* button = lv_btn_create(tab);
lv_obj_set_size(button, lv_pct(100), 42);
lv_obj_set_style_radius(button, 30, LV_PART_MAIN);
lv_obj_set_style_bg_color(button, color_primary, LV_PART_MAIN);
lv_obj_add_event_cb(button, sceneSelection_event_cb, LV_EVENT_CLICKED, (void *)(intptr_t)i);
lv_obj_add_event_cb(button, sceneSelection_event_cb, LV_EVENT_SHORT_CLICKED, (void *)(intptr_t)i);
//lv_obj_add_event_cb(button, sceneSelection_event_cb, LV_EVENT_PRESSED, (void *)(intptr_t)i);
//lv_obj_add_event_cb(button, sceneSelection_event_cb, LV_EVENT_RELEASED, (void *)(intptr_t)i);
lv_obj_t* label = lv_label_create(button);
lv_label_set_text(label, scenes->at(i).c_str());
lv_obj_center(label);
}
}
}
void notify_tab_before_delete_sceneSelection(void) {
// remember to set all pointers to lvgl objects to NULL if they might be accessed from outside.
// They must check if object is NULL and must not use it if so
}
void register_gui_sceneSelection(void){
register_gui(std::string(tabName_sceneSelection), & create_tab_content_sceneSelection, & notify_tab_before_delete_sceneSelection);
}

View File

@ -0,0 +1,6 @@
#pragma once
#include <lvgl.h>
const char * const tabName_sceneSelection = "Scene selection";
void register_gui_sceneSelection(void);

View File

@ -15,7 +15,7 @@ lv_obj_t* objBattSettingsPercentage;
// Slider Event handler
static void bl_slider_event_cb(lv_event_t* e){
lv_obj_t * slider = lv_event_get_target(e);
lv_obj_t* slider = lv_event_get_target(e);
int32_t slider_value = lv_slider_get_value(slider);
if (slider_value < 60) {slider_value = 60;}
if (slider_value > 255) {slider_value = 255;}
@ -29,7 +29,7 @@ static void WakeEnableSetting_event_cb(lv_event_t* e){
// timout event handler
static void timout_event_cb(lv_event_t* e){
lv_obj_t * drop = lv_event_get_target(e);
lv_obj_t* drop = lv_event_get_target(e);
uint16_t selected = lv_dropdown_get_selected(drop);
switch (selected) {
case 0: {set_sleepTimeout( 10000); break;}

View File

@ -16,6 +16,7 @@
// register gui and keys
#include "applicationInternal/gui/guiBase.h"
#include "applicationInternal/gui/guiRegistry.h"
#include "guis/gui_sceneSelection.h"
#include "guis/gui_irReceiver.h"
#include "guis/gui_settings.h"
#include "guis/gui_numpad.h"
@ -29,6 +30,7 @@
#include "scenes/scene_TV.h"
#include "scenes/scene_fireTV.h"
#include "scenes/scene_chromecast.h"
#include "scenes/scene_appleTV.h"
#include "applicationInternal/scenes/sceneHandler.h"
#if defined(ARDUINO)
@ -79,26 +81,35 @@ int main(int argc, char *argv[]) {
#endif
register_keyboardCommands();
// register the scenes and their key_commands_*
register_scene_defaultKeys();
register_scene_TV();
register_scene_fireTV();
register_scene_chromecast();
register_scene_appleTV();
register_scene_allOff();
// Only show these scenes on the sceneSelection gui. If you don't set this explicitely, by default all registered scenes are shown.
set_scenes_on_sceneSelectionGUI({scene_name_TV, scene_name_fireTV, scene_name_chromecast, scene_name_appleTV});
// register the GUIs. They will be displayed in the order they have been registered.
register_gui_sceneSelection();
register_gui_irReceiver();
register_gui_settings();
register_gui_appleTV();
register_gui_numpad();
register_gui_smarthome();
// Only show these GUIs in the main gui list. If you don't set this explicitely, by default all registered guis are shown.
#if (USE_SCENE_SPECIFIC_GUI_LIST != 0)
main_gui_list = {tabName_sceneSelection, tabName_smarthome, tabName_settings, tabName_irReceiver};
#endif
// init GUI - will initialize tft, touch and lvgl
init_gui();
setLabelCurrentScene();
gui_loop(); // Run the LVGL UI once before the loop takes over
// setup the Inertial Measurement Unit (IMU) for motion detection. Has to be after init_gui(), otherwise I2C will not work
init_IMU();
// register the scenes and their key_commands_*
register_scene_defaultKeys();
register_scene_allOff();
register_scene_TV();
register_scene_fireTV();
register_scene_chromecast();
setLabelCurrentScene();
// init WiFi - needs to be after init_gui() because WifiLabel must be available
#if (ENABLE_WIFI_AND_MQTT == 1)
init_mqtt();

View File

@ -7,8 +7,11 @@
#include "devices/TV/device_samsungTV/device_samsungTV.h"
#include "devices/AVreceiver/device_yamahaAmp/device_yamahaAmp.h"
#include "applicationInternal/commandHandler.h"
// guis
#include "guis/gui_numpad.h"
uint16_t SCENE_TV ; //"Scene_tv"
uint16_t SCENE_TV_FORCE ; //"Scene_tv_force"
std::map<char, repeatModes> key_repeatModes_TV;
std::map<char, uint16_t> key_commands_short_TV;
@ -64,9 +67,11 @@ void scene_end_sequence_TV(void) {
}
std::string scene_name_TV = "TV";
t_gui_list scene_TV_gui_list = {tabName_numpad};
void register_scene_TV(void) {
register_command(&SCENE_TV , makeCommandData(SCENE, {scene_name_TV}));
register_command(&SCENE_TV, makeCommandData(SCENE, {scene_name_TV}));
register_command(&SCENE_TV_FORCE, makeCommandData(SCENE, {scene_name_TV, "FORCE"}));
register_scene(
scene_name_TV,
@ -75,5 +80,7 @@ void register_scene_TV(void) {
& scene_end_sequence_TV,
& key_repeatModes_TV,
& key_commands_short_TV,
& key_commands_long_TV);
& key_commands_long_TV,
& scene_TV_gui_list,
SCENE_TV);
}

View File

@ -4,6 +4,8 @@
#include <stdint.h>
extern uint16_t SCENE_TV;
// FORCE sends the start sequence again even if scene is already active
extern uint16_t SCENE_TV_FORCE;
extern std::string scene_name_TV;
void register_scene_TV_commands(void);

View File

@ -1,19 +1,30 @@
#include <map>
#include "applicationInternal/keys.h"
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/commandHandler.h"
// devices
#include "devices/misc/device_specialCommands.h"
#include "devices/AVreceiver/device_yamahaAmp/device_yamahaAmp.h"
// scenes
#include "scene__defaultKeys.h"
#include "scenes/scene_allOff.h"
#include "scenes/scene_TV.h"
#include "scenes/scene_fireTV.h"
#include "scenes/scene_chromecast.h"
#include "scenes/scene_appleTV.h"
uint16_t SCENE_SELECTION;
std::string scene_name_selection = "sceneSelection";
std::map<char, repeatModes> key_repeatModes_default;
std::map<char, uint16_t> key_commands_short_default;
std::map<char, uint16_t> key_commands_long_default;
// This is the main list of guis we want to be shown when swiping. Need not to be all the guis that have been registered, can be only a subset.
// You can swipe through these guis. Will be in the order you place them here in the vector.
// By default, it is a list of the guis that have been registered in main.cpp
// If a scene defines a scene specific gui list, this will be used instead as long as the scene is active and we don't explicitely navigate back to main_gui_list
t_gui_list main_gui_list;
void register_scene_defaultKeys(void) {
key_repeatModes_default = {
{KEY_OFF, SHORT },
@ -29,16 +40,16 @@ void register_scene_defaultKeys(void) {
};
key_commands_short_default = {
{KEY_OFF, SCENE_ALLOFF },
{KEY_OFF, SCENE_ALLOFF_FORCE},
/*{KEY_STOP, COMMAND_UNKNOWN }, {KEY_REWI, COMMAND_UNKNOWN }, {KEY_PLAY, COMMAND_UNKNOWN }, {KEY_FORW, COMMAND_UNKNOWN },*/
/*{KEY_CONF, COMMAND_UNKNOWN }, {KEY_INFO, COMMAND_UNKNOWN },*/
/* {KEY_UP, COMMAND_UNKNOWN },*/
/* {KEY_LEFT, COMMAND_UNKNOWN }, {KEY_OK, COMMAND_UNKNOWN }, {KEY_RIGHT, COMMAND_UNKNOWN },*/
/* {KEY_DOWN, COMMAND_UNKNOWN },*/
/*{KEY_BACK, COMMAND_UNKNOWN }, {KEY_SRC, COMMAND_UNKNOWN },*/
{KEY_BACK, SCENE_SELECTION }, /*{KEY_SRC, COMMAND_UNKNOWN },*/
{KEY_VOLUP, YAMAHA_VOL_PLUS }, {KEY_MUTE, YAMAHA_MUTE_TOGGLE}, /*{KEY_CHUP, COMMAND_UNKNOWN },*/
{KEY_VOLDO, YAMAHA_VOL_MINUS }, /* {KEY_REC, COMMAND_UNKNOWN },*/ /*{KEY_CHDOW, COMMAND_UNKNOWN },*/
{KEY_RED, SCENE_TV }, {KEY_GREEN, SCENE_FIRETV }, {KEY_YELLO, SCENE_CHROMECAST }, {KEY_BLUE, YAMAHA_STANDARD },
{KEY_RED, SCENE_TV_FORCE }, {KEY_GREEN, SCENE_FIRETV_FORCE}, {KEY_YELLO, SCENE_CHROMECAST_FORCE},{KEY_BLUE, SCENE_APPLETV_FORCE},
};
key_commands_long_default = {
@ -46,4 +57,6 @@ void register_scene_defaultKeys(void) {
};
register_command(&SCENE_SELECTION , makeCommandData(SCENE, {scene_name_selection}));
}

View File

@ -4,8 +4,15 @@
#include <stdint.h>
#include <map>
#include "applicationInternal/keys.h"
#include "applicationInternal/scenes/sceneRegistry.h"
extern uint16_t SCENE_SELECTION; // command
extern std::string scene_name_selection; // name of this fake default scene
extern std::map<char, repeatModes> key_repeatModes_default;
extern std::map<char, uint16_t> key_commands_short_default;
extern std::map<char, uint16_t> key_commands_long_default;
extern t_gui_list main_gui_list;
void register_scene_defaultKeys(void);

View File

@ -9,6 +9,7 @@
#include "applicationInternal/commandHandler.h"
uint16_t SCENE_ALLOFF ; //"Scene_allOff"
uint16_t SCENE_ALLOFF_FORCE; //"Scene_allOff_force"
std::map<char, repeatModes> key_repeatModes_allOff;
std::map<char, uint16_t> key_commands_short_allOff;
@ -78,6 +79,7 @@ std::string scene_name_allOff = "Off";
void register_scene_allOff(void) {
register_command(&SCENE_ALLOFF , makeCommandData(SCENE, {scene_name_allOff}));
register_command(&SCENE_ALLOFF_FORCE, makeCommandData(SCENE, {scene_name_allOff, "FORCE"}));
register_scene(
scene_name_allOff,
@ -86,5 +88,7 @@ void register_scene_allOff(void) {
& scene_end_sequence_allOff,
& key_repeatModes_allOff,
& key_commands_short_allOff,
& key_commands_long_allOff);
& key_commands_long_allOff,
NULL,
SCENE_ALLOFF);
}

View File

@ -4,6 +4,8 @@
#include <stdint.h>
extern uint16_t SCENE_ALLOFF;
// FORCE sends the start sequence again even if scene is already active
extern uint16_t SCENE_ALLOFF_FORCE;
extern std::string scene_name_allOff;
void register_scene_allOff_commands(void);

View File

@ -0,0 +1,86 @@
#include <map>
#include "scenes/scene_appleTV.h"
#include "applicationInternal/keys.h"
#include "applicationInternal/scenes/sceneRegistry.h"
#include "applicationInternal/hardware/hardwarePresenter.h"
// devices
#include "devices/TV/device_samsungTV/device_samsungTV.h"
#include "devices/AVreceiver/device_yamahaAmp/device_yamahaAmp.h"
#include "applicationInternal/commandHandler.h"
// guis
#include "devices/mediaPlayer/device_appleTV/gui_appleTV.h"
uint16_t SCENE_APPLETV ; //"Scene_appleTV"
uint16_t SCENE_APPLETV_FORCE; //"Scene_appleTV_force"
std::map<char, repeatModes> key_repeatModes_appleTV;
std::map<char, uint16_t> key_commands_short_appleTV;
std::map<char, uint16_t> key_commands_long_appleTV;
void scene_setKeys_appleTV() {
key_repeatModes_appleTV = {
};
key_commands_short_appleTV = {
};
key_commands_long_appleTV = {
};
}
void scene_start_sequence_appleTV(void) {
executeCommand(SAMSUNG_POWER_ON);
delay(500);
executeCommand(YAMAHA_POWER_ON);
delay(1500);
executeCommand(YAMAHA_INPUT_DVD);
delay(3000);
executeCommand(SAMSUNG_INPUT_HDMI_3);
}
void scene_end_sequence_appleTV(void) {
}
std::string scene_name_appleTV = "Apple TV";
t_gui_list scene_appleTV_gui_list = {tabName_appleTV};
void register_scene_appleTV(void) {
register_command(&SCENE_APPLETV, makeCommandData(SCENE, {scene_name_appleTV}));
register_command(&SCENE_APPLETV_FORCE, makeCommandData(SCENE, {scene_name_appleTV, "FORCE"}));
register_scene(
scene_name_appleTV,
& scene_setKeys_appleTV,
& scene_start_sequence_appleTV,
& scene_end_sequence_appleTV,
& key_repeatModes_appleTV,
& key_commands_short_appleTV,
& key_commands_long_appleTV,
& scene_appleTV_gui_list,
SCENE_APPLETV);
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include <stdint.h>
extern uint16_t SCENE_APPLETV;
// FORCE sends the start sequence again even if scene is already active
extern uint16_t SCENE_APPLETV_FORCE;
extern std::string scene_name_appleTV;
void register_scene_appleTV_commands(void);
void register_scene_appleTV(void);

View File

@ -9,6 +9,7 @@
#include "applicationInternal/commandHandler.h"
uint16_t SCENE_CHROMECAST ; //"Scene_chromecast"
uint16_t SCENE_CHROMECAST_FORCE; //"Scene_chromecast_force"
std::map<char, repeatModes> key_repeatModes_chromecast;
std::map<char, uint16_t> key_commands_short_chromecast;
@ -66,7 +67,8 @@ void scene_end_sequence_chromecast(void) {
std::string scene_name_chromecast = "Chromecast";
void register_scene_chromecast(void) {
register_command(&SCENE_CHROMECAST , makeCommandData(SCENE, {scene_name_chromecast}));
register_command(&SCENE_CHROMECAST, makeCommandData(SCENE, {scene_name_chromecast}));
register_command(&SCENE_CHROMECAST_FORCE, makeCommandData(SCENE, {scene_name_chromecast, "FORCE"}));
register_scene(
scene_name_chromecast,
@ -75,5 +77,7 @@ void register_scene_chromecast(void) {
& scene_end_sequence_chromecast,
& key_repeatModes_chromecast,
& key_commands_short_chromecast,
& key_commands_long_chromecast);
& key_commands_long_chromecast,
NULL,
SCENE_CHROMECAST);
}

View File

@ -4,6 +4,8 @@
#include <stdint.h>
extern uint16_t SCENE_CHROMECAST;
// FORCE sends the start sequence again even if scene is already active
extern uint16_t SCENE_CHROMECAST_FORCE;
extern std::string scene_name_chromecast;
void register_scene_chromecast_commands(void);

View File

@ -7,8 +7,11 @@
#include "devices/TV/device_samsungTV/device_samsungTV.h"
#include "devices/AVreceiver/device_yamahaAmp/device_yamahaAmp.h"
#include "applicationInternal/commandHandler.h"
// guis
#include "guis/gui_numpad.h"
uint16_t SCENE_FIRETV ; //"Scene_firetv"
uint16_t SCENE_FIRETV_FORCE; //"Scene_firetv_force"
std::map<char, repeatModes> key_repeatModes_fireTV;
std::map<char, uint16_t> key_commands_short_fireTV;
@ -73,9 +76,11 @@ void scene_end_sequence_fireTV(void) {
}
std::string scene_name_fireTV = "Fire TV";
t_gui_list scene_fireTV_gui_list = {tabName_numpad};
void register_scene_fireTV(void) {
register_command(&SCENE_FIRETV , makeCommandData(SCENE, {scene_name_fireTV}));
register_command(&SCENE_FIRETV, makeCommandData(SCENE, {scene_name_fireTV}));
register_command(&SCENE_FIRETV_FORCE, makeCommandData(SCENE, {scene_name_fireTV, "FORCE"}));
register_scene(
scene_name_fireTV,
@ -84,5 +89,7 @@ void register_scene_fireTV(void) {
& scene_end_sequence_fireTV,
& key_repeatModes_fireTV,
& key_commands_short_fireTV,
& key_commands_long_fireTV);
& key_commands_long_fireTV,
& scene_fireTV_gui_list,
SCENE_FIRETV);
}

View File

@ -4,6 +4,8 @@
#include <stdint.h>
extern uint16_t SCENE_FIRETV;
// FORCE sends the start sequence again even if scene is already active
extern uint16_t SCENE_FIRETV_FORCE;
extern std::string scene_name_fireTV;
void register_scene_fireTV_commands(void);

View File

@ -74,9 +74,8 @@ The [housing and buttons](https://github.com/CoretechR/OMOTE/tree/main/CAD) can
Short term goals
- [x] simulator for creating pages in Windows, WSL2 and Linux
- [ ] scene selector page as start page
- [ ] available gui pages based on the currently active scene. Hide pages not needed in a scene
- [ ] make gui actions context sensitive for the currently active scene
- [x] scene selector page as start page
- [x] available gui pages based on the currently active scene. Hide pages not needed in a scene
Long term goals (not yet scheduled)
- [ ] Easier configuration