diff --git a/src/hasp.cpp b/src/hasp.cpp index 352928b7..24cf2125 100644 --- a/src/hasp.cpp +++ b/src/hasp.cpp @@ -14,6 +14,7 @@ #include "lv_fs_if.h" #include "hasp_debug.h" #include "hasp_config.h" +#include "hasp_object.h" #include "hasp_dispatch.h" //#include "hasp_filesystem.h" included in hasp_conf.h #include "hasp_wifi.h" @@ -133,131 +134,6 @@ bool get_page_id(lv_obj_t * obj, uint8_t * pageid) return false; } -lv_obj_t * hasp_find_obj_from_id(lv_obj_t * parent, uint8_t objid) -{ - if(objid == 0 || parent == nullptr) return parent; - - lv_obj_t * child; - child = lv_obj_get_child(parent, NULL); - while(child) { - if(child->user_data && (lv_obj_user_data_t)objid == child->user_data) return child; // object found - - /* check grandchildren */ - lv_obj_t * grandchild = hasp_find_obj_from_id(child, objid); - if(grandchild) return grandchild; - - /* check tabs */ - if(check_obj_type(child, LV_HASP_TABVIEW)) { - uint16_t tabcount = lv_tabview_get_tab_count(child); - for(uint16_t i = 0; i < tabcount; i++) { - lv_obj_t * tab = lv_tabview_get_tab(child, i); - Log.verbose(TAG_HASP, "Found tab %i", i); - if(tab->user_data && (lv_obj_user_data_t)objid == tab->user_data) return tab; // object found - - grandchild = hasp_find_obj_from_id(tab, objid); - if(grandchild) return grandchild; - } - } - - /* next sibling */ - child = lv_obj_get_child(parent, child); - } - return NULL; -} - -lv_obj_t * hasp_find_obj_from_id(uint8_t pageid, uint8_t objid) -{ - return hasp_find_obj_from_id(get_page_obj(pageid), objid); -} - -bool FindIdFromObj(lv_obj_t * obj, uint8_t * pageid, lv_obj_user_data_t * objid) -{ - if(!get_page_id(obj, pageid)) return false; - if(!(obj->user_data > 0)) return false; - memcpy(objid, &obj->user_data, sizeof(lv_obj_user_data_t)); - return true; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -void hasp_send_obj_attribute_str(lv_obj_t * obj, const char * attribute, const char * data) -{ - uint8_t pageid; - uint8_t objid; - - if(FindIdFromObj(obj, &pageid, &objid)) { - dispatch_send_obj_attribute_str(pageid, objid, attribute, data); - } -} - -void hasp_send_obj_attribute_int(lv_obj_t * obj, const char * attribute, int32_t val) -{ - char data[64]; - itoa(val, data, 10); - hasp_send_obj_attribute_str(obj, attribute, data); -} - -void hasp_send_obj_attribute_color(lv_obj_t * obj, const char * attribute, lv_color_t color) -{ - char buffer[128]; - lv_color32_t c32; - c32.full = lv_color_to32(color); - snprintf(buffer, sizeof(buffer), PSTR("#%02x%02x%02x"), c32.ch.red, c32.ch.green, c32.ch.blue); - hasp_send_obj_attribute_str(obj, attribute, buffer); -} - -/** Senders for event handlers **/ - -static void hasp_send_obj_attribute_P(lv_obj_t * obj, const char * attr, const char * data) -{ - char * buffer; - buffer = (char *)malloc(strlen_P(attr) + 1); - strcpy_P(buffer, attr); - hasp_send_obj_attribute_str(obj, buffer, data); - free(buffer); -} - -static inline void hasp_send_obj_attribute_val(lv_obj_t * obj, int32_t val) -{ - char data[64]; - itoa(val, data, 10); - hasp_send_obj_attribute_P(obj, PSTR("val"), data); -} - -/*static inline void hasp_send_obj_attribute_val(lv_obj_t * obj, int16_t val) -{ - char data[64]; - itoa(val, data, 10); - hasp_send_obj_attribute_P(obj, PSTR("val"), data); -}*/ - -static inline void hasp_send_obj_attribute_event(lv_obj_t * obj, const char * event) -{ - hasp_send_obj_attribute_P(obj, PSTR("event"), event); -} - -static inline void hasp_send_obj_attribute_txt(lv_obj_t * obj, const char * txt) -{ - hasp_send_obj_attribute_P(obj, PSTR("txt"), txt); -} - -/*static void hasp_send_obj_attribute_txt(lv_obj_t * obj, String & txt) -{ - hasp_send_obj_attribute_P(obj, PSTR("txt"), txt.c_str()); -}*/ - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -// Used in the dispatcher -void hasp_process_attribute(uint8_t pageid, uint8_t objid, const char * attr, const char * payload) -{ - if(lv_obj_t * obj = hasp_find_obj_from_id(pageid, objid)) { - hasp_process_obj_attribute(obj, attr, payload, strlen(payload) > 0); - } else { - Log.warning(TAG_HASP, F("Unknown object p[%d].b[%d]"), pageid, objid); - } -} - //////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -599,125 +475,6 @@ void hasp_background(uint16_t pageid, uint16_t imageid) /////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * Called when a a list button is clicked on the List tab - * @param btn pointer to a list button - * @param event type of event that occured - */ -void IRAM_ATTR btn_event_handler(lv_obj_t * obj, lv_event_t event) -{ - uint8_t eventid; - char buffer[64]; - - switch(event) { - case LV_EVENT_PRESSED: - eventid = HASP_EVENT_DOWN; - memcpy_P(buffer, PSTR("DOWN"), sizeof(buffer)); - break; - case LV_EVENT_CLICKED: - // UP = the same object was release then was pressed and press was not lost! - eventid = HASP_EVENT_UP; - memcpy_P(buffer, PSTR("UP"), sizeof(buffer)); - break; - case LV_EVENT_SHORT_CLICKED: - eventid = HASP_EVENT_SHORT; - memcpy_P(buffer, PSTR("SHORT"), sizeof(buffer)); - break; - case LV_EVENT_LONG_PRESSED: - eventid = HASP_EVENT_LONG; - memcpy_P(buffer, PSTR("LONG"), sizeof(buffer)); - break; - case LV_EVENT_LONG_PRESSED_REPEAT: - eventid = HASP_EVENT_HOLD; - memcpy_P(buffer, PSTR("HOLD"), sizeof(buffer)); - break; - case LV_EVENT_PRESS_LOST: - eventid = HASP_EVENT_LOST; - memcpy_P(buffer, PSTR("LOST"), sizeof(buffer)); - break; - case LV_EVENT_PRESSING: - case LV_EVENT_FOCUSED: - case LV_EVENT_DEFOCUSED: - case LV_EVENT_RELEASED: - return; - - case LV_EVENT_VALUE_CHANGED: - Log.warning(TAG_HASP, F("Value changed Event %d occured"), event); - return; - - case LV_EVENT_DELETE: - Log.verbose(TAG_HASP, F("Object deleted Event %d occured"), event); - return; - default: - Log.warning(TAG_HASP, F("Unknown Event %d occured"), event); - return; - } - - if(obj == lv_disp_get_layer_sys(NULL)) { -#if HASP_USE_MQTT > 0 - mqtt_send_state(F("wakeuptouch"), buffer); -#endif - } else { - // hasp_send_obj_attribute_event(obj, buffer); - dispatch_send_object_event(current_page, (uint8_t)obj->user_data, eventid); - } -} - -// ##################### Event Handlers by Version ######################################################## - -/*static void btnmap_event_handler(lv_obj_t * obj, lv_event_t event) -{ - if(event == LV_EVENT_VALUE_CHANGED) haspSendNewValue(obj, lv_btnmatrix_get_pressed_btn(obj)); -}*/ - -void IRAM_ATTR toggle_event_handler(lv_obj_t * obj, lv_event_t event) -{ - bool toggled = - lv_btn_get_state(obj) == LV_BTN_STATE_CHECKED_PRESSED || lv_btn_get_state(obj) == LV_BTN_STATE_CHECKED_RELEASED; - if(event == LV_EVENT_VALUE_CHANGED) hasp_send_obj_attribute_val(obj, toggled); -} - -static void switch_event_handler(lv_obj_t * obj, lv_event_t event) -{ - if(event == LV_EVENT_VALUE_CHANGED) hasp_send_obj_attribute_val(obj, lv_switch_get_state(obj)); -} - -static void checkbox_event_handler(lv_obj_t * obj, lv_event_t event) -{ - if(event == LV_EVENT_VALUE_CHANGED) hasp_send_obj_attribute_val(obj, lv_checkbox_is_checked(obj)); -} - -static void ddlist_event_handler(lv_obj_t * obj, lv_event_t event) -{ - if(event == LV_EVENT_VALUE_CHANGED) { - hasp_send_obj_attribute_val(obj, lv_dropdown_get_selected(obj)); - char buffer[128]; - lv_dropdown_get_selected_str(obj, buffer, sizeof(buffer)); - hasp_send_obj_attribute_txt(obj, buffer); - } -} - -static void slider_event_handler(lv_obj_t * obj, lv_event_t event) -{ - if(event == LV_EVENT_VALUE_CHANGED) hasp_send_obj_attribute_val(obj, lv_slider_get_value(obj)); -} - -static void cpicker_event_handler(lv_obj_t * obj, lv_event_t event) -{ - if(event == LV_EVENT_VALUE_CHANGED) - hasp_send_obj_attribute_color(obj, "color", lv_cpicker_get_color(obj)); // Literial string -} - -static void roller_event_handler(lv_obj_t * obj, lv_event_t event) -{ - if(event == LV_EVENT_VALUE_CHANGED) { - hasp_send_obj_attribute_val(obj, lv_roller_get_selected(obj)); - char buffer[128]; - lv_roller_get_selected_str(obj, buffer, sizeof(buffer)); - hasp_send_obj_attribute_txt(obj, buffer); - } -} - /////////////////////////////////////////////////////////////////////////////////////////////////////////// String haspGetVersion() @@ -775,250 +532,6 @@ void hasp_set_group_objects(uint8_t groupid, uint8_t eventid, lv_obj_t * src_obj } } -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void haspNewObject(const JsonObject & config, uint8_t & saved_page_id) -{ - /* Validate page */ - uint8_t pageid = config[F("page")].isNull() ? current_page : config[F("page")].as(); - - /* Page selection */ - lv_obj_t * page = get_page_obj(pageid); - if(!page) { - Log.warning(TAG_HASP, F("Page ID %u not defined"), pageid); - return; - } - /* save the current pageid */ - saved_page_id = pageid; - - /* Validate type */ - if(config[F("objid")].isNull()) return; // comments - - lv_obj_t * parent_obj = page; - if(!config[F("parentid")].isNull()) { - uint8_t parentid = config[F("parentid")].as(); - parent_obj = hasp_find_obj_from_id(page, parentid); - if(!parent_obj) { - Log.warning(TAG_HASP, F("Parent ID p[%u].b[%u] not found"), pageid, parentid); - parent_obj = page; // create on the page instead ?? - } else { - Log.verbose(TAG_HASP, F("Parent ID p[%u].b[%u] found"), pageid, parentid); - } - } - - uint8_t objid = config[F("objid")].as(); - uint8_t id = config[F("id")].as(); - - /* Define Objects*/ - lv_obj_t * obj = hasp_find_obj_from_id(parent_obj, id); - if(obj) { - Log.warning(TAG_HASP, F("Object ID %u already exists!"), id); - return; - } - - switch(objid) { - /* ----- Basic Objects ------ */ - case LV_HASP_BUTTON: { - obj = lv_btn_create(parent_obj, NULL); - lv_label_create(obj, NULL); - lv_obj_set_event_cb(obj, btn_event_handler); - break; - } - case LV_HASP_CHECKBOX: { - obj = lv_checkbox_create(parent_obj, NULL); - lv_obj_set_event_cb(obj, checkbox_event_handler); - break; - } - case LV_HASP_LABEL: { - obj = lv_label_create(parent_obj, NULL); - /* click area padding */ - uint8_t padh = config[F("padh")].as(); - uint8_t padv = config[F("padv")].as(); - /* text align */ - if(padh > 0 || padv > 0) { - lv_obj_set_ext_click_area(obj, padh, padh, padv, padv); - } - if(!config[F("align")].isNull()) { - lv_label_set_align(obj, LV_LABEL_ALIGN_CENTER); - } - lv_obj_set_event_cb(obj, btn_event_handler); - break; - } - case LV_HASP_IMAGE: { - obj = lv_img_create(parent_obj, NULL); - lv_obj_set_event_cb(obj, btn_event_handler); - break; - } - case LV_HASP_ARC: { - obj = lv_arc_create(parent_obj, NULL); - lv_obj_set_event_cb(obj, btn_event_handler); - break; - } - case LV_HASP_CONTAINER: { - obj = lv_cont_create(parent_obj, NULL); - lv_obj_set_event_cb(obj, btn_event_handler); - break; - } - case LV_HASP_OBJECT: { - obj = lv_obj_create(parent_obj, NULL); - lv_obj_set_event_cb(obj, btn_event_handler); - break; - } - case LV_HASP_PAGE: { - obj = lv_page_create(parent_obj, NULL); - break; - } - case LV_HASP_TABVIEW: { - obj = lv_tabview_create(parent_obj, NULL); - lv_obj_t * tab; - tab = lv_tabview_add_tab(obj, "tab 1"); - lv_obj_set_user_data(tab, id + 1); - tab = lv_tabview_add_tab(obj, "tab 2"); - lv_obj_set_user_data(tab, id + 2); - tab = lv_tabview_add_tab(obj, "tab 3"); - lv_obj_set_user_data(tab, id + 3); - break; - } - case LV_HASP_TILEVIEW: { - obj = lv_tileview_create(parent_obj, NULL); - break; - } - - /* ----- Color Objects ------ */ - case LV_HASP_CPICKER: { - obj = lv_cpicker_create(parent_obj, NULL); - lv_obj_set_event_cb(obj, cpicker_event_handler); - break; - } - -#if LV_USE_PRELOAD != 0 - case LV_HASP_PRELOADER: { - obj = lv_spinner_create(parent_obj, NULL); - break; - } -#endif - /* ----- Range Objects ------ */ - case LV_HASP_SLIDER: { - obj = lv_slider_create(parent_obj, NULL); - lv_slider_set_range(obj, 0, 100); - lv_obj_set_event_cb(obj, slider_event_handler); - // bool knobin = config[F("knobin")].as() | true; - // lv_slider_set_knob_in(obj, knobin); - break; - } - case LV_HASP_GAUGE: { - obj = lv_gauge_create(parent_obj, NULL); - lv_gauge_set_range(obj, 0, 100); - lv_obj_set_event_cb(obj, btn_event_handler); - break; - } - case LV_HASP_BAR: { - obj = lv_bar_create(parent_obj, NULL); - lv_bar_set_range(obj, 0, 100); - lv_obj_set_event_cb(obj, btn_event_handler); - break; - } - case LV_HASP_LMETER: { - obj = lv_linemeter_create(parent_obj, NULL); - lv_linemeter_set_range(obj, 0, 100); - lv_obj_set_event_cb(obj, btn_event_handler); - break; - } - case LV_HASP_CHART: { - obj = lv_chart_create(parent_obj, NULL); - lv_chart_set_range(obj, 0, 100); - lv_obj_set_event_cb(obj, btn_event_handler); - - lv_chart_add_series(obj, LV_COLOR_RED); - lv_chart_add_series(obj, LV_COLOR_GREEN); - lv_chart_add_series(obj, LV_COLOR_BLUE); - - lv_chart_series_t * ser = lv_chart_get_series(obj, 2); - lv_chart_set_next(obj, ser, 10); - lv_chart_set_next(obj, ser, 20); - lv_chart_set_next(obj, ser, 30); - lv_chart_set_next(obj, ser, 40); - - break; - } - - /* ----- On/Off Objects ------ */ - case LV_HASP_SWITCH: { - obj = lv_switch_create(parent_obj, NULL); - lv_obj_set_event_cb(obj, switch_event_handler); - break; - } - case LV_HASP_LED: { - obj = lv_led_create(parent_obj, NULL); - lv_obj_set_event_cb(obj, btn_event_handler); - break; - } - /**/ - case LV_HASP_DDLIST: { - obj = lv_dropdown_create(parent_obj, NULL); - // lv_dropdown_set_fix_width(obj, width); - lv_dropdown_set_draw_arrow(obj, true); - // lv_dropdown_set_anim_time(obj, 200); - lv_obj_set_top(obj, true); - // lv_obj_align(obj, NULL, LV_ALIGN_IN_TOP_MID, 0, 20); - lv_obj_set_event_cb(obj, ddlist_event_handler); - break; - } - case LV_HASP_ROLLER: { - obj = lv_roller_create(parent_obj, NULL); - // lv_roller_set_fix_width(obj, width); - // lv_obj_align(obj, NULL, LV_ALIGN_IN_TOP_MID, 0, 20); - lv_obj_set_event_cb(obj, roller_event_handler); - break; - } - - /* ----- Other Object ------ */ - default: - Log.warning(TAG_HASP, F("Unsupported Object ID %u"), objid); - return; - } - - if(!obj) { - Log.warning(TAG_HASP, F("Object is NULL, skipping...")); - return; - } - - lv_obj_set_user_data(obj, id); - - /* do not process these attributes */ - config.remove(F("page")); - config.remove(F("id")); - config.remove(F("objid")); - config.remove(F("parentid")); - String v; - - for(JsonPair keyValue : config) { - v = keyValue.value().as(); - hasp_process_obj_attribute(obj, keyValue.key().c_str(), v.c_str(), true); - // Log.verbose(TAG_HASP,F(" * %s => %s"), keyValue.key().c_str(), v.c_str()); - } - - /** testing start **/ - lv_obj_user_data_t temp; - if(!FindIdFromObj(obj, &pageid, &temp)) { - Log.error(TAG_HASP, F("Lost track of the created object, not found!")); - return; - } - /** testing end **/ - - lv_obj_type_t list; - lv_obj_get_type(obj, &list); - Log.verbose(TAG_HASP, F(" * p[%u].b[%u] = %s"), pageid, temp, list.type[0]); - - /* Double-check */ - lv_obj_t * test = hasp_find_obj_from_id(pageid, (uint8_t)temp); - if(test != obj) { - Log.error(TAG_HASP, F("Objects DO NOT match!")); - } else { - // Log.verbose(TAG_HASP,F("Objects match!")); - } -} - void haspLoadPage(const char * pages) { #if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0 @@ -1047,7 +560,7 @@ void haspLoadPage(const char * pages) Log.notice(TAG_HASP, F("Loading jsonl from EEPROM...")); EepromStream eepromStream(4096, 1024); dispatchJsonl(eepromStream); - Log.tr (TAG_HASP, F("Loaded jsonl from EEPROM")); + Log.tr(TAG_HASP, F("Loaded jsonl from EEPROM")); #endif #endif @@ -1077,7 +590,7 @@ bool haspGetConfig(const JsonObject & settings) if(strcmp(haspPagesPath, settings[FPSTR(F_CONFIG_PAGES)].as().c_str()) != 0) changed = true; settings[FPSTR(F_CONFIG_PAGES)] = haspPagesPath; - if(changed) configOutput(settings,TAG_HASP); + if(changed) configOutput(settings, TAG_HASP); return changed; } diff --git a/src/hasp.h b/src/hasp.h index 89bc6c5b..f3664535 100644 --- a/src/hasp.h +++ b/src/hasp.h @@ -42,47 +42,6 @@ enum hasp_event_t { // even = released, odd = pressed HASP_EVENT_DOUBLE = 8 }; -enum lv_hasp_obj_type_t { - LV_HASP_BTNMATRIX = 1, - LV_HASP_TABLE = 2, - - LV_HASP_BUTTON = 10, - LV_HASP_CHECKBOX = 11, - LV_HASP_LABEL = 12, - - LV_HASP_CPICKER = 20, - LV_HASP_PRELOADER = 21, - LV_HASP_ARC = 22, - - LV_HASP_SLIDER = 30, - LV_HASP_GAUGE = 31, - LV_HASP_BAR = 32, - LV_HASP_LMETER = 33, - - LV_HASP_SWITCH = 40, - LV_HASP_LED = 41, - - LV_HASP_DDLIST = 50, - LV_HASP_ROLLER = 51, - - LV_HASP_IMAGE = 60, - LV_HASP_IMGBTN = 61, - LV_HASP_CANVAS = 62, - - LV_HASP_TILEVIEW = 70, - LV_HASP_TABVIEW = 71, - LV_HASP_TAB = 72, - - LV_HASP_CHART = 80, - LV_HASP_CALENDER = 81, - - LV_HASP_CONTAINER = 90, - LV_HASP_OBJECT = 91, - LV_HASP_PAGE = 92, - LV_HASP_MSGBOX = 93, - LV_HASP_WINDOW = 94, -}; - /********************** * GLOBAL PROTOTYPES **********************/ @@ -93,17 +52,17 @@ enum lv_hasp_obj_type_t { void haspSetup(); void haspLoop(void); +lv_obj_t * get_page_obj(uint8_t pageid); +bool get_page_id(lv_obj_t * obj, uint8_t * pageid); + void haspSetPage(uint8_t id); uint8_t haspGetPage(); void haspClearPage(uint16_t pageid); + String haspGetNodename(); String haspGetVersion(); void haspBackground(uint16_t pageid, uint16_t imageid); -void hasp_send_obj_attribute_str(lv_obj_t * obj, const char * attribute, const char * data); -void hasp_send_obj_attribute_int(lv_obj_t * obj, const char * attribute, int32_t val); -void hasp_send_obj_attribute_color(lv_obj_t * obj, const char * attribute, lv_color_t color); -void hasp_process_attribute(uint8_t pageid, uint8_t objid, const char * attr, const char * payload); void hasp_set_group_objects(uint8_t groupid, uint8_t eventid, lv_obj_t * src_obj); void haspNewObject(const JsonObject & config, uint8_t & saved_page_id); @@ -116,12 +75,8 @@ void haspProgressVal(uint8_t val); bool haspGetConfig(const JsonObject & settings); bool haspSetConfig(const JsonObject & settings); -lv_obj_t * hasp_find_obj_from_id(lv_obj_t * parent, uint8_t objid); lv_font_t * hasp_get_font(uint8_t fontid); -void IRAM_ATTR btn_event_handler(lv_obj_t * obj, lv_event_t event); -void IRAM_ATTR toggle_event_handler(lv_obj_t * obj, lv_event_t event); - /********************** * MACROS **********************/ diff --git a/src/hasp_attribute.cpp b/src/hasp_attribute.cpp index d1e46ce5..43c07c8a 100644 --- a/src/hasp_attribute.cpp +++ b/src/hasp_attribute.cpp @@ -5,6 +5,7 @@ #include "lv_conf.h" #include "hasp.h" +#include "hasp_object.h" #include "hasp_dispatch.h" #include "hasp_attribute.h" @@ -12,6 +13,9 @@ LV_FONT_DECLARE(unscii_8_icon); extern lv_font_t * haspFonts[8]; static inline bool only_digits(const char * s); +static inline void hasp_out_int(lv_obj_t * obj, const char * attr, uint32_t val); +static inline void hasp_out_str(lv_obj_t * obj, const char * attr, const char * data); +static inline void hasp_out_color(lv_obj_t * obj, const char * attr, lv_color_t color); /* 16-bit hashing function http://www.cse.yorku.ca/~oz/hash.html */ /* all possible attributes are hashed and checked if they are unique */ @@ -614,6 +618,86 @@ static void hasp_process_gauge_attribute(lv_obj_t * obj, const char * attr_p, ui Log.warning(TAG_ATTR, F("Unknown property %s"), attr_p); } +static void hasp_process_btnmatrix_attribute(lv_obj_t * obj, const char * attr_p, uint16_t attr_hash, + const char * payload, bool update) +{ + char * attr = (char *)attr_p; + if(*attr == '.') attr++; // strip leading '.' + + switch(attr_hash) { + case ATTR_MAP: { + const char ** map_p = lv_btnmatrix_get_map_array(obj); + if(update) { + // Free previous map + // lv_mem_free(*map_p); + + // if(map_p != lv_btnmatrix_def_map) { + // } + + // Create new map + + // Reserve memory for JsonDocument + size_t maxsize = (128u * ((strlen(payload) / 128) + 1)) + 256; + DynamicJsonDocument map_doc(maxsize); + DeserializationError jsonError = deserializeJson(map_doc, payload); + + if(jsonError) { // Couldn't parse incoming JSON payload + return Log.warning(TAG_ATTR, F("JSON: Failed to parse incoming button map with error: %s"), + jsonError.c_str()); + } + + JsonArray arr = map_doc.as(); // Parse payload + + size_t tot_len = sizeof(char *) * (arr.size() + 1); + const char ** map_arr = (const char **)lv_mem_alloc(tot_len); + if(map_arr == NULL) { + return Log.error(TAG_ATTR, F("Out of memory while creating button map")); + } + memset(map_arr, 0, tot_len); + + // Create buffer + tot_len = 0; + size_t pos = 0; + for(JsonVariant btn : arr) { + tot_len += btn.as().length() + 1; + } + tot_len++; // trailing '\0' + Log.verbose(TAG_ATTR, F("Array Size = %d, Map Length = %d"), arr.size(), tot_len); + + char * buffer = (char *)lv_mem_alloc(tot_len); + if(map_arr == NULL) { + lv_mem_free(map_arr); + return Log.error(TAG_ATTR, F("Out of memory while creating button map")); + } + memset(buffer, 0, tot_len); // Important, last index needs to be 0 + + // Fill buffer + size_t index = 0; + for(JsonVariant btn : arr) { + size_t len = btn.as().length() + 1; + Log.verbose(TAG_ATTR, F(" * Adding button: %s (%d bytes)"), btn.as().c_str(), len); + memccpy(buffer + pos, btn.as().c_str(), 0, len); // Copy the labels into the buffer + map_arr[index++] = buffer + pos; // save pointer to start of the label + pos += len; + } + map_arr[index] = buffer + pos; // save pointer to the last \0 byte + + lv_btnmatrix_set_map(obj, map_arr); + + // TO DO : free & destroy previous buttonmap! + + } else { + hasp_out_str(obj, attr, *map_p); + } + return; + } // ATTR_MAP + } + + Log.warning(TAG_ATTR, F("Unknown property %s"), attr_p); +} + +// ##################### Common Attributes ######################################################## + // OK static void hasp_process_obj_attribute_txt(lv_obj_t * obj, const char * attr, const char * payload, bool update) { @@ -800,10 +884,12 @@ static void hasp_process_obj_attribute_range(lv_obj_t * obj, const char * attr, return update ? lv_chart_set_range(obj, set_min ? val : min, set_max ? val : max) : hasp_out_int(obj, attr, set_min ? min : max); } - + Log.warning(TAG_ATTR, F("Unknown property %s"), attr); } +// ##################### Default Attributes ######################################################## + /** * Change or Retrieve the value of the attribute of an object * @param obj lv_obj_t*: the object to get/set the attribute @@ -902,6 +988,18 @@ void hasp_process_obj_attribute(lv_obj_t * obj, const char * attr_p, const char return update ? lv_roller_set_visible_row_count(obj, (uint8_t)val) : hasp_out_int(obj, attr, lv_roller_get_visible_row_count(obj)); } + + if(check_obj_type(obj, LV_HASP_TABLE)) { + return update ? lv_table_set_row_cnt(obj, (uint8_t)val) + : hasp_out_int(obj, attr, lv_table_get_row_cnt(obj)); + } + break; + + case ATTR_COLS: + if(check_obj_type(obj, LV_HASP_TABLE)) { + return update ? lv_table_set_col_cnt(obj, (uint8_t)val) + : hasp_out_int(obj, attr, lv_table_get_col_cnt(obj)); + } break; // case ATTR_RECT: @@ -980,6 +1078,10 @@ void hasp_process_obj_attribute(lv_obj_t * obj, const char * attr_p, const char case ATTR_DELETE: return lv_obj_del_async(obj); + case ATTR_MAP: + if(check_obj_type(obj, LV_HASP_BTNMATRIX)) { + return hasp_process_btnmatrix_attribute(obj, attr_p, attr_hash, payload, update); + } // default: // hasp_local_style_attr(obj, attr, payload, update); } @@ -1000,106 +1102,19 @@ static inline bool only_digits(const char * s) return strlen(s) == digits; } -void inline hasp_out_int(lv_obj_t * obj, const char * attr, uint32_t val) +// ##################### Value Senders ######################################################## + +static inline void hasp_out_int(lv_obj_t * obj, const char * attr, uint32_t val) { hasp_send_obj_attribute_int(obj, attr, val); } -void inline hasp_out_str(lv_obj_t * obj, const char * attr, const char * data) +static inline void hasp_out_str(lv_obj_t * obj, const char * attr, const char * data) { hasp_send_obj_attribute_str(obj, attr, data); } -void inline hasp_out_color(lv_obj_t * obj, const char * attr, lv_color_t color) +static inline void hasp_out_color(lv_obj_t * obj, const char * attr, lv_color_t color) { hasp_send_obj_attribute_color(obj, attr, color); } - -/** - * Check if an lvgl object typename corresponds to a given HASP object ID - * @param lvobjtype a char* to a string - * @param haspobjtype the HASP object ID to check against - * @return true or false wether the types match - * @note - */ -bool check_obj_type(const char * lvobjtype, lv_hasp_obj_type_t haspobjtype) -{ - lvobjtype += 3; // skip "lv_" - - switch(haspobjtype) { - case LV_HASP_BTNMATRIX: - return (strcmp_P(lvobjtype, PSTR("btnmatrix")) == 0); - case LV_HASP_TABLE: - return (strcmp_P(lvobjtype, PSTR("table")) == 0); - case LV_HASP_BUTTON: - return (strcmp_P(lvobjtype, PSTR("btn")) == 0); - case LV_HASP_LABEL: - return (strcmp_P(lvobjtype, PSTR("label")) == 0); - case LV_HASP_CHECKBOX: - return (strcmp_P(lvobjtype, PSTR("checkbox")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cb")) == 0); - case LV_HASP_DDLIST: - return (strcmp_P(lvobjtype, PSTR("dropdown")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_ddlist")) == 0); - case LV_HASP_CPICKER: - return (strcmp_P(lvobjtype, PSTR("cpicker")) == 0); - case LV_HASP_PRELOADER: - return (strcmp_P(lvobjtype, PSTR("spinner")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_preload")) == 0); - case LV_HASP_SLIDER: - return (strcmp_P(lvobjtype, PSTR("slider")) == 0); - case LV_HASP_GAUGE: - return (strcmp_P(lvobjtype, PSTR("gauge")) == 0); - case LV_HASP_ARC: - return (strcmp_P(lvobjtype, PSTR("arc")) == 0); - case LV_HASP_BAR: - return (strcmp_P(lvobjtype, PSTR("bar")) == 0); - case LV_HASP_LMETER: - return (strcmp_P(lvobjtype, PSTR("linemeter")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_lmeter")) == 0) - case LV_HASP_ROLLER: - return (strcmp_P(lvobjtype, PSTR("roller")) == 0); - case LV_HASP_SWITCH: - return (strcmp_P(lvobjtype, PSTR("switch")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_sw")) == 0) - case LV_HASP_LED: - return (strcmp_P(lvobjtype, PSTR("led")) == 0); - case LV_HASP_IMAGE: - return (strcmp_P(lvobjtype, PSTR("img")) == 0); - case LV_HASP_IMGBTN: - return (strcmp_P(lvobjtype, PSTR("imgbtn")) == 0); - case LV_HASP_CONTAINER: - return (strcmp_P(lvobjtype, PSTR("container")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cont")) == 0) - case LV_HASP_OBJECT: - return (strcmp_P(lvobjtype, PSTR("page")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cont")) == 0) - case LV_HASP_PAGE: - return (strcmp_P(lvobjtype, PSTR("obj")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cont")) == 0) - case LV_HASP_TABVIEW: - return (strcmp_P(lvobjtype, PSTR("tabview")) == 0); - case LV_HASP_TILEVIEW: - return (strcmp_P(lvobjtype, PSTR("tileview")) == 0); - case LV_HASP_CHART: - return (strcmp_P(lvobjtype, PSTR("chart")) == 0); - case LV_HASP_CANVAS: - return (strcmp_P(lvobjtype, PSTR("canvas")) == 0); - case LV_HASP_CALENDER: - return (strcmp_P(lvobjtype, PSTR("calender")) == 0); - case LV_HASP_MSGBOX: - return (strcmp_P(lvobjtype, PSTR("msgbox")) == 0); - case LV_HASP_WINDOW: - return (strcmp_P(lvobjtype, PSTR("window")) == 0); - - default: - return false; - } -} - -/** - * Check if an lvgl objecttype name corresponds to a given HASP object ID - * @param obj an lv_obj_t* of the object to check its type - * @param haspobjtype the HASP object ID to check against - * @return true or false wether the types match - * @note - */ -bool check_obj_type(lv_obj_t * obj, lv_hasp_obj_type_t haspobjtype) -{ - lv_obj_type_t list; - lv_obj_get_type(obj, &list); - const char * objtype = list.type[0]; - return check_obj_type(objtype, haspobjtype); -} \ No newline at end of file diff --git a/src/hasp_attribute.h b/src/hasp_attribute.h index 4c9d8f65..bce499b6 100644 --- a/src/hasp_attribute.h +++ b/src/hasp_attribute.h @@ -18,13 +18,6 @@ void hasp_process_obj_attribute(lv_obj_t * obj, const char * attr_p, const char } /* extern "C" */ #endif -void hasp_out_int(lv_obj_t * obj, const char * attr, uint32_t val); -void hasp_out_str(lv_obj_t * obj, const char * attr, const char * data); -void hasp_out_color(lv_obj_t * obj, const char * attr, lv_color_t color); - -bool check_obj_type(const char * lvobjtype, lv_hasp_obj_type_t haspobjtype); -bool check_obj_type(lv_obj_t * obj, lv_hasp_obj_type_t haspobjtype); - #define _HASP_ATTRIBUTE(prop_name, func_name, value_type) \ static inline void attribute_##func_name(lv_obj_t * obj, uint8_t part, lv_state_t state, bool update, \ const char * attr, value_type val) \ @@ -34,7 +27,7 @@ bool check_obj_type(lv_obj_t * obj, lv_hasp_obj_type_t haspobjtype); } else { \ value_type temp = lv_obj_get_style_##func_name(obj, part); \ /*lv_obj_get_style_##func_name(obj, part, state, &temp);*/ \ - return hasp_out_int(obj, attr, temp); \ + return hasp_send_obj_attribute_int(obj, attr, temp); \ } \ } @@ -253,6 +246,7 @@ _HASP_ATTRIBUTE(SCALE_END_LINE_WIDTH, scale_end_line_width, lv_style_int_t) #define ATTR_MODE 45891 // #define ATTR_RECT 11204 #define ATTR_ROWS 52153 +#define ATTR_COLS 36307 #define ATTR_MIN 46130 #define ATTR_MAX 45636 #define ATTR_VAL 15809 @@ -267,4 +261,7 @@ _HASP_ATTRIBUTE(SCALE_END_LINE_WIDTH, scale_end_line_width, lv_style_int_t) #define ATTR_LABEL_COUNT 20356 #define ATTR_LINE_COUNT 57860 #define ATTR_FORMAT 38871 + +// Buttonmatric +#define ATTR_MAP 45628 #endif diff --git a/src/hasp_dispatch.cpp b/src/hasp_dispatch.cpp index cacaf46c..b55c9679 100644 --- a/src/hasp_dispatch.cpp +++ b/src/hasp_dispatch.cpp @@ -8,6 +8,7 @@ #include "hasp_dispatch.h" #include "hasp_config.h" #include "hasp_debug.h" +#include "hasp_object.h" #include "hasp_gui.h" #include "hasp_oobe.h" #include "hasp_hal.h" @@ -109,7 +110,7 @@ void dispatchParseJsonl(Stream & stream) while(deserializeJson(jsonl, stream) == DeserializationError::Ok) { // serializeJson(jsonl, Serial); // Serial.println(); - haspNewObject(jsonl.as(), savedPage); + hasp_new_object(jsonl.as(), savedPage); } } diff --git a/src/hasp_object.cpp b/src/hasp_object.cpp new file mode 100644 index 00000000..afbefe7e --- /dev/null +++ b/src/hasp_object.cpp @@ -0,0 +1,655 @@ +/* ******************************************************************************************** + * + * HASP Object Handlers + * - Object Finders : Convert from object pointers to and from p[x].b[y] IDs + * - Value Dispatchers : Forward output data to the dispatcher + * - Value Senders : Convert values and events into topic/payload before forwarding + * - Event Handlers : Callbacks for object event processing + * - Attribute processor : Decide if an attribute needs updating or querying and forward + * - Object creator : Creates an object from a line of jsonl + * + ******************************************************************************************** */ + +#include "ArduinoLog.h" + +#include "hasp.h" +#include "hasp_object.h" +#include "hasp_dispatch.h" +#include "hasp_attribute.h" + +// ##################### Object Finders ######################################################## + +lv_obj_t * hasp_find_obj_from_id(lv_obj_t * parent, uint8_t objid) +{ + if(objid == 0 || parent == nullptr) return parent; + + lv_obj_t * child; + child = lv_obj_get_child(parent, NULL); + while(child) { + /* child found, return it */ + if(child->user_data && (lv_obj_user_data_t)objid == child->user_data) return child; + + /* check grandchildren */ + lv_obj_t * grandchild = hasp_find_obj_from_id(child, objid); + if(grandchild) return grandchild; /* grandchild found, return it */ + + /* check tabs */ + if(check_obj_type(child, LV_HASP_TABVIEW)) { + uint16_t tabcount = lv_tabview_get_tab_count(child); + for(uint16_t i = 0; i < tabcount; i++) { + lv_obj_t * tab = lv_tabview_get_tab(child, i); + Log.verbose(TAG_HASP, "Found tab %i", i); + if(tab->user_data && (lv_obj_user_data_t)objid == tab->user_data) return tab; /* tab found, return it */ + + /* check grandchildren */ + grandchild = hasp_find_obj_from_id(tab, objid); + if(grandchild) return grandchild; /* grandchild found, return it */ + } + } + + /* try next sibling */ + child = lv_obj_get_child(parent, child); + } + return NULL; +} + +lv_obj_t * hasp_find_obj_from_id(uint8_t pageid, uint8_t objid) +{ + return hasp_find_obj_from_id(get_page_obj(pageid), objid); +} + +bool hasp_find_id_from_obj(lv_obj_t * obj, uint8_t * pageid, lv_obj_user_data_t * objid) +{ + if(!get_page_id(obj, pageid)) return false; + if(!(obj->user_data > 0)) return false; + memcpy(objid, &obj->user_data, sizeof(lv_obj_user_data_t)); + return true; +} + +/** + * Check if an lvgl object typename corresponds to a given HASP object ID + * @param lvobjtype a char* to a string + * @param haspobjtype the HASP object ID to check against + * @return true or false wether the types match + * @note + */ +bool check_obj_type(const char * lvobjtype, lv_hasp_obj_type_t haspobjtype) +{ + lvobjtype += 3; // skip "lv_" + + switch(haspobjtype) { + case LV_HASP_BTNMATRIX: + return (strcmp_P(lvobjtype, PSTR("btnmatrix")) == 0); + case LV_HASP_TABLE: + return (strcmp_P(lvobjtype, PSTR("table")) == 0); + case LV_HASP_BUTTON: + return (strcmp_P(lvobjtype, PSTR("btn")) == 0); + case LV_HASP_LABEL: + return (strcmp_P(lvobjtype, PSTR("label")) == 0); + case LV_HASP_CHECKBOX: + return (strcmp_P(lvobjtype, PSTR("checkbox")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cb")) == 0); + case LV_HASP_DDLIST: + return (strcmp_P(lvobjtype, PSTR("dropdown")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_ddlist")) == 0); + case LV_HASP_CPICKER: + return (strcmp_P(lvobjtype, PSTR("cpicker")) == 0); + case LV_HASP_PRELOADER: + return (strcmp_P(lvobjtype, PSTR("spinner")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_preload")) == 0); + case LV_HASP_SLIDER: + return (strcmp_P(lvobjtype, PSTR("slider")) == 0); + case LV_HASP_GAUGE: + return (strcmp_P(lvobjtype, PSTR("gauge")) == 0); + case LV_HASP_ARC: + return (strcmp_P(lvobjtype, PSTR("arc")) == 0); + case LV_HASP_BAR: + return (strcmp_P(lvobjtype, PSTR("bar")) == 0); + case LV_HASP_LMETER: + return (strcmp_P(lvobjtype, PSTR("linemeter")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_lmeter")) == 0) + case LV_HASP_ROLLER: + return (strcmp_P(lvobjtype, PSTR("roller")) == 0); + case LV_HASP_SWITCH: + return (strcmp_P(lvobjtype, PSTR("switch")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_sw")) == 0) + case LV_HASP_LED: + return (strcmp_P(lvobjtype, PSTR("led")) == 0); + case LV_HASP_IMAGE: + return (strcmp_P(lvobjtype, PSTR("img")) == 0); + case LV_HASP_IMGBTN: + return (strcmp_P(lvobjtype, PSTR("imgbtn")) == 0); + case LV_HASP_CONTAINER: + return (strcmp_P(lvobjtype, PSTR("container")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cont")) == 0) + case LV_HASP_OBJECT: + return (strcmp_P(lvobjtype, PSTR("page")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cont")) == 0) + case LV_HASP_PAGE: + return (strcmp_P(lvobjtype, PSTR("obj")) == 0); // || (strcmp_P(lvobjtype, PSTR("lv_cont")) == 0) + case LV_HASP_TABVIEW: + return (strcmp_P(lvobjtype, PSTR("tabview")) == 0); + case LV_HASP_TILEVIEW: + return (strcmp_P(lvobjtype, PSTR("tileview")) == 0); + case LV_HASP_CHART: + return (strcmp_P(lvobjtype, PSTR("chart")) == 0); + case LV_HASP_CANVAS: + return (strcmp_P(lvobjtype, PSTR("canvas")) == 0); + case LV_HASP_CALENDER: + return (strcmp_P(lvobjtype, PSTR("calender")) == 0); + case LV_HASP_MSGBOX: + return (strcmp_P(lvobjtype, PSTR("msgbox")) == 0); + case LV_HASP_WINDOW: + return (strcmp_P(lvobjtype, PSTR("window")) == 0); + + default: + return false; + } +} + +/** + * Check if an lvgl objecttype name corresponds to a given HASP object ID + * @param obj an lv_obj_t* of the object to check its type + * @param haspobjtype the HASP object ID to check against + * @return true or false wether the types match + * @note + */ +bool check_obj_type(lv_obj_t * obj, lv_hasp_obj_type_t haspobjtype) +{ + lv_obj_type_t list; + lv_obj_get_type(obj, &list); + const char * objtype = list.type[0]; + return check_obj_type(objtype, haspobjtype); +} + +// ##################### Value Dispatchers ######################################################## + +void hasp_send_obj_attribute_str(lv_obj_t * obj, const char * attribute, const char * data) +{ + uint8_t pageid; + uint8_t objid; + + if(hasp_find_id_from_obj(obj, &pageid, &objid)) { + dispatch_send_obj_attribute_str(pageid, objid, attribute, data); + } +} + +void hasp_send_obj_attribute_int(lv_obj_t * obj, const char * attribute, int32_t val) +{ + char data[64]; + itoa(val, data, 10); + hasp_send_obj_attribute_str(obj, attribute, data); +} + +void hasp_send_obj_attribute_color(lv_obj_t * obj, const char * attribute, lv_color_t color) +{ + char buffer[128]; + lv_color32_t c32; + c32.full = lv_color_to32(color); + snprintf(buffer, sizeof(buffer), PSTR("#%02x%02x%02x"), c32.ch.red, c32.ch.green, c32.ch.blue); + hasp_send_obj_attribute_str(obj, attribute, buffer); +} + +// ##################### Value Senders ######################################################## + +static void hasp_send_obj_attribute_P(lv_obj_t * obj, const char * attr, const char * data) +{ + char * buffer; + buffer = (char *)malloc(strlen_P(attr) + 1); + strcpy_P(buffer, attr); + hasp_send_obj_attribute_str(obj, buffer, data); + free(buffer); +} + +static inline void hasp_send_obj_attribute_val(lv_obj_t * obj, int32_t val) +{ + char data[32]; + itoa(val, data, DEC); + hasp_send_obj_attribute_P(obj, PSTR("val"), data); +} + +static inline void hasp_send_obj_attribute_event(lv_obj_t * obj, const char * event) +{ + hasp_send_obj_attribute_P(obj, PSTR("event"), event); +} + +static inline void hasp_send_obj_attribute_txt(lv_obj_t * obj, const char * txt) +{ + hasp_send_obj_attribute_P(obj, PSTR("txt"), txt); +} + +// ##################### Event Handlers ######################################################## + +/** + * Called when a button-style object is clicked + * @param obj pointer to a button object + * @param event type of event that occured + */ +void IRAM_ATTR btn_event_handler(lv_obj_t * obj, lv_event_t event) +{ + uint8_t eventid; + char buffer[64]; + + switch(event) { + case LV_EVENT_PRESSED: + eventid = HASP_EVENT_DOWN; + memcpy_P(buffer, PSTR("DOWN"), sizeof(buffer)); + break; + case LV_EVENT_CLICKED: + // UP = the same object was release then was pressed and press was not lost! + eventid = HASP_EVENT_UP; + memcpy_P(buffer, PSTR("UP"), sizeof(buffer)); + break; + case LV_EVENT_SHORT_CLICKED: + eventid = HASP_EVENT_SHORT; + memcpy_P(buffer, PSTR("SHORT"), sizeof(buffer)); + break; + case LV_EVENT_LONG_PRESSED: + eventid = HASP_EVENT_LONG; + memcpy_P(buffer, PSTR("LONG"), sizeof(buffer)); + break; + case LV_EVENT_LONG_PRESSED_REPEAT: + eventid = HASP_EVENT_HOLD; + memcpy_P(buffer, PSTR("HOLD"), sizeof(buffer)); + break; + case LV_EVENT_PRESS_LOST: + eventid = HASP_EVENT_LOST; + memcpy_P(buffer, PSTR("LOST"), sizeof(buffer)); + break; + case LV_EVENT_PRESSING: + case LV_EVENT_FOCUSED: + case LV_EVENT_DEFOCUSED: + case LV_EVENT_RELEASED: + return; + + case LV_EVENT_VALUE_CHANGED: + Log.warning(TAG_HASP, F("Value changed Event %d occured"), event); + return; + + case LV_EVENT_DELETE: + Log.verbose(TAG_HASP, F("Object deleted Event %d occured"), event); + // TODO:free and destroy persistent memory allocated for certain objects + return; + default: + Log.warning(TAG_HASP, F("Unknown Event %d occured"), event); + return; + } + + if(obj == lv_disp_get_layer_sys(NULL)) { +#if HASP_USE_MQTT > 0 + mqtt_send_state(F("wakeuptouch"), buffer); +#endif + } else { + // hasp_send_obj_attribute_event(obj, buffer); + dispatch_send_object_event(haspGetPage(), (uint8_t)obj->user_data, eventid); + } +} + +/** + * Called when a button matrix object is clicked + * @param obj pointer to a button matrix + * @param event type of event that occured + */ +static void btnmap_event_handler(lv_obj_t * obj, lv_event_t event) +{ + if(event == LV_EVENT_VALUE_CHANGED) hasp_send_obj_attribute_val(obj, lv_btnmatrix_get_active_btn(obj)); +} + +/** + * Called when a table object is clicked + * @param obj pointer to a table + * @param event type of event that occured + */ +static void table_event_handler(lv_obj_t * obj, lv_event_t event) +{ + if(event == LV_EVENT_VALUE_CHANGED) { + uint16_t row; + uint16_t col; + if(lv_table_get_pressed_cell(obj, &row, &col) == LV_RES_OK) { + hasp_send_obj_attribute_val(obj, row); + } + } +} + +/** + * Called when a toggle button is clicked + * @param obj pointer to a button + * @param event type of event that occured + */ +void IRAM_ATTR toggle_event_handler(lv_obj_t * obj, lv_event_t event) +{ + bool toggled = + lv_btn_get_state(obj) == LV_BTN_STATE_CHECKED_PRESSED || lv_btn_get_state(obj) == LV_BTN_STATE_CHECKED_RELEASED; + if(event == LV_EVENT_VALUE_CHANGED) hasp_send_obj_attribute_val(obj, toggled); +} + +/** + * Called when a switch is toggled + * @param obj pointer to a switch object + * @param event type of event that occured + */ +static void switch_event_handler(lv_obj_t * obj, lv_event_t event) +{ + if(event == LV_EVENT_VALUE_CHANGED) hasp_send_obj_attribute_val(obj, lv_switch_get_state(obj)); +} + +/** + * Called when a checkboxed is clicked + * @param obj pointer to a checkbox + * @param event type of event that occured + */ +static void checkbox_event_handler(lv_obj_t * obj, lv_event_t event) +{ + if(event == LV_EVENT_VALUE_CHANGED) hasp_send_obj_attribute_val(obj, lv_checkbox_is_checked(obj)); +} + +/** + * Called when a dropdown list is clicked + * @param obj pointer to a dropdown list + * @param event type of event that occured + */ +static void ddlist_event_handler(lv_obj_t * obj, lv_event_t event) +{ + if(event == LV_EVENT_VALUE_CHANGED) { + hasp_send_obj_attribute_val(obj, lv_dropdown_get_selected(obj)); + char buffer[128]; + lv_dropdown_get_selected_str(obj, buffer, sizeof(buffer)); + hasp_send_obj_attribute_txt(obj, buffer); + } +} + +/** + * Called when a slider is clicked + * @param obj pointer to a slider + * @param event type of event that occured + */ +static void slider_event_handler(lv_obj_t * obj, lv_event_t event) +{ + if(event == LV_EVENT_VALUE_CHANGED) hasp_send_obj_attribute_val(obj, lv_slider_get_value(obj)); +} + +/** + * Called when a color picker is clicked + * @param obj pointer to a color picker + * @param event type of event that occured + */ +static void cpicker_event_handler(lv_obj_t * obj, lv_event_t event) +{ + if(event == LV_EVENT_VALUE_CHANGED) + hasp_send_obj_attribute_color(obj, "color", lv_cpicker_get_color(obj)); // Literial string +} + +/** + * Called when a roller object is clicked + * @param obj pointer to a roller object + * @param event type of event that occured + */ +static void roller_event_handler(lv_obj_t * obj, lv_event_t event) +{ + if(event == LV_EVENT_VALUE_CHANGED) { + hasp_send_obj_attribute_val(obj, lv_roller_get_selected(obj)); + char buffer[128]; + lv_roller_get_selected_str(obj, buffer, sizeof(buffer)); + hasp_send_obj_attribute_txt(obj, buffer); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// Used in the dispatcher & hasp_new_object +void hasp_process_attribute(uint8_t pageid, uint8_t objid, const char * attr, const char * payload) +{ + if(lv_obj_t * obj = hasp_find_obj_from_id(pageid, objid)) { + hasp_process_obj_attribute(obj, attr, payload, strlen(payload) > 0); + } else { + Log.warning(TAG_HASP, F("Unknown object p[%d].b[%d]"), pageid, objid); + } +} + +// ##################### Obhject Creator ######################################################## + +/** + * Create a new object according to the json config + * @param config Json representation for this object + * @param saved_page_id the pageid to use when no pageid is specified in the Json, updated when it is specified so + * following objects in the file can share the pageid + */ +void hasp_new_object(const JsonObject & config, uint8_t & saved_page_id) +{ + /* Validate page */ + uint8_t pageid = config[F("page")].isNull() ? haspGetPage() : config[F("page")].as(); + + /* Page selection */ + lv_obj_t * page = get_page_obj(pageid); + if(!page) { + return Log.warning(TAG_HASP, F("Page ID %u not defined"), pageid); + } else { + saved_page_id = pageid; /* save the current pageid */ + } + + /* Validate type */ + if(config[F("objid")].isNull()) return; // comments + + lv_obj_t * parent_obj = page; + if(!config[F("parentid")].isNull()) { + uint8_t parentid = config[F("parentid")].as(); + parent_obj = hasp_find_obj_from_id(page, parentid); + if(!parent_obj) { + return Log.warning(TAG_HASP, F("Parent ID p[%u].b[%u] not found, skipping..."), pageid, parentid); + // parent_obj = page; // don't create on the page instead ?? + } else { + Log.verbose(TAG_HASP, F("Parent ID p[%u].b[%u] found"), pageid, parentid); + } + } + + uint8_t objid = config[F("objid")].as(); + uint8_t id = config[F("id")].as(); + + /* Define Objects*/ + lv_obj_t * obj = hasp_find_obj_from_id(parent_obj, id); + if(obj) { + return Log.warning(TAG_HASP, F("Object ID %u already exists!"), id); + } + + switch(objid) { + /* ----- Basic Objects ------ */ + case LV_HASP_BTNMATRIX: + obj = lv_btnmatrix_create(parent_obj, NULL); + lv_obj_set_event_cb(obj, btnmap_event_handler); + break; + case LV_HASP_TABLE: + obj = lv_table_create(parent_obj, NULL); + lv_obj_set_event_cb(obj, table_event_handler); + break; + + case LV_HASP_BUTTON: + obj = lv_btn_create(parent_obj, NULL); + lv_label_create(obj, NULL); + lv_obj_set_event_cb(obj, btn_event_handler); + break; + + case LV_HASP_CHECKBOX: + obj = lv_checkbox_create(parent_obj, NULL); + lv_obj_set_event_cb(obj, checkbox_event_handler); + break; + + case LV_HASP_LABEL: { + obj = lv_label_create(parent_obj, NULL); + /* click area padding */ + uint8_t padh = config[F("padh")].as(); + uint8_t padv = config[F("padv")].as(); + /* text align */ + if(padh > 0 || padv > 0) { + lv_obj_set_ext_click_area(obj, padh, padh, padv, padv); + } + if(!config[F("align")].isNull()) { + lv_label_set_align(obj, LV_LABEL_ALIGN_CENTER); + } + lv_obj_set_event_cb(obj, btn_event_handler); + break; + } + case LV_HASP_IMAGE: { + obj = lv_img_create(parent_obj, NULL); + lv_obj_set_event_cb(obj, btn_event_handler); + break; + } + case LV_HASP_ARC: { + obj = lv_arc_create(parent_obj, NULL); + lv_obj_set_event_cb(obj, btn_event_handler); + break; + } + case LV_HASP_CONTAINER: { + obj = lv_cont_create(parent_obj, NULL); + lv_obj_set_event_cb(obj, btn_event_handler); + break; + } + case LV_HASP_OBJECT: { + obj = lv_obj_create(parent_obj, NULL); + lv_obj_set_event_cb(obj, btn_event_handler); + break; + } + case LV_HASP_PAGE: { + obj = lv_page_create(parent_obj, NULL); + // No event handler for pages + break; + } + case LV_HASP_TABVIEW: { + obj = lv_tabview_create(parent_obj, NULL); + // No event handler for tabs + lv_obj_t * tab; + tab = lv_tabview_add_tab(obj, "tab 1"); + lv_obj_set_user_data(tab, id + 1); + tab = lv_tabview_add_tab(obj, "tab 2"); + lv_obj_set_user_data(tab, id + 2); + tab = lv_tabview_add_tab(obj, "tab 3"); + lv_obj_set_user_data(tab, id + 3); + break; + } + case LV_HASP_TILEVIEW: { + obj = lv_tileview_create(parent_obj, NULL); + // No event handler for tileviews + break; + } + + /* ----- Color Objects ------ */ + case LV_HASP_CPICKER: { + obj = lv_cpicker_create(parent_obj, NULL); + lv_obj_set_event_cb(obj, cpicker_event_handler); + break; + } + +#if LV_USE_PRELOAD != 0 + case LV_HASP_PRELOADER: { + obj = lv_spinner_create(parent_obj, NULL); + break; + } +#endif + /* ----- Range Objects ------ */ + case LV_HASP_SLIDER: { + obj = lv_slider_create(parent_obj, NULL); + lv_slider_set_range(obj, 0, 100); + lv_obj_set_event_cb(obj, slider_event_handler); + // bool knobin = config[F("knobin")].as() | true; + // lv_slider_set_knob_in(obj, knobin); + break; + } + case LV_HASP_GAUGE: { + obj = lv_gauge_create(parent_obj, NULL); + lv_gauge_set_range(obj, 0, 100); + lv_obj_set_event_cb(obj, btn_event_handler); + break; + } + case LV_HASP_BAR: { + obj = lv_bar_create(parent_obj, NULL); + lv_bar_set_range(obj, 0, 100); + lv_obj_set_event_cb(obj, btn_event_handler); + break; + } + case LV_HASP_LMETER: { + obj = lv_linemeter_create(parent_obj, NULL); + lv_linemeter_set_range(obj, 0, 100); + lv_obj_set_event_cb(obj, btn_event_handler); + break; + } + case LV_HASP_CHART: { + obj = lv_chart_create(parent_obj, NULL); + lv_chart_set_range(obj, 0, 100); + lv_obj_set_event_cb(obj, btn_event_handler); + + lv_chart_add_series(obj, LV_COLOR_RED); + lv_chart_add_series(obj, LV_COLOR_GREEN); + lv_chart_add_series(obj, LV_COLOR_BLUE); + + lv_chart_series_t * ser = lv_chart_get_series(obj, 2); + lv_chart_set_next(obj, ser, 10); + lv_chart_set_next(obj, ser, 20); + lv_chart_set_next(obj, ser, 30); + lv_chart_set_next(obj, ser, 40); + + break; + } + + /* ----- On/Off Objects ------ */ + case LV_HASP_SWITCH: { + obj = lv_switch_create(parent_obj, NULL); + lv_obj_set_event_cb(obj, switch_event_handler); + break; + } + case LV_HASP_LED: { + obj = lv_led_create(parent_obj, NULL); + lv_obj_set_event_cb(obj, btn_event_handler); + break; + } + /* ----- List Object ------- */ + case LV_HASP_DDLIST: { + obj = lv_dropdown_create(parent_obj, NULL); + lv_dropdown_set_draw_arrow(obj, true); + // lv_dropdown_set_anim_time(obj, 200); + lv_obj_set_top(obj, true); + // lv_obj_align(obj, NULL, LV_ALIGN_IN_TOP_MID, 0, 20); + lv_obj_set_event_cb(obj, ddlist_event_handler); + break; + } + case LV_HASP_ROLLER: { + obj = lv_roller_create(parent_obj, NULL); + // lv_obj_align(obj, NULL, LV_ALIGN_IN_TOP_MID, 0, 20); + lv_obj_set_event_cb(obj, roller_event_handler); + break; + } + + /* ----- Other Object ------ */ + // default: + // return Log.warning(TAG_HASP, F("Unsupported Object ID %u"), objid); + } + + /* No object was actually created */ + if(!obj) { + return Log.warning(TAG_HASP, F("Object ID %u is NULL, skipping..."), id); + } + + /* id tag the object */ + lv_obj_set_user_data(obj, id); + + /* do not process these attributes */ + config.remove(F("page")); + config.remove(F("id")); + config.remove(F("objid")); + config.remove(F("parentid")); + String v; + + for(JsonPair keyValue : config) { + v = keyValue.value().as(); + hasp_process_obj_attribute(obj, keyValue.key().c_str(), v.c_str(), true); + // Log.verbose(TAG_HASP,F(" * %s => %s"), keyValue.key().c_str(), v.c_str()); + } + + /** testing start **/ + lv_obj_user_data_t temp; + if(!hasp_find_id_from_obj(obj, &pageid, &temp)) { + return Log.error(TAG_HASP, F("Lost track of the created object, not found!")); + } + + /** verbose reporting **/ + lv_obj_type_t list; + lv_obj_get_type(obj, &list); + Log.verbose(TAG_HASP, F(" * p[%u].b[%u] = %s"), pageid, temp, list.type[0]); + + /* test double-check */ + lv_obj_t * test = hasp_find_obj_from_id(pageid, (uint8_t)temp); + if(test != obj) { + return Log.error(TAG_HASP, F("Objects DO NOT match!")); + } +} diff --git a/src/hasp_object.h b/src/hasp_object.h new file mode 100644 index 00000000..909cd104 --- /dev/null +++ b/src/hasp_object.h @@ -0,0 +1,64 @@ +#ifndef HASP_OBJECT_H +#define HASP_OBJECT_H + +#include +#include "lvgl.h" + +enum lv_hasp_obj_type_t { + LV_HASP_BTNMATRIX = 1, + LV_HASP_TABLE = 2, + + LV_HASP_BUTTON = 10, + LV_HASP_CHECKBOX = 11, + LV_HASP_LABEL = 12, + + LV_HASP_CPICKER = 20, + LV_HASP_PRELOADER = 21, + LV_HASP_ARC = 22, + + LV_HASP_SLIDER = 30, + LV_HASP_GAUGE = 31, + LV_HASP_BAR = 32, + LV_HASP_LMETER = 33, + + LV_HASP_SWITCH = 40, + LV_HASP_LED = 41, + + LV_HASP_DDLIST = 50, + LV_HASP_ROLLER = 51, + + LV_HASP_IMAGE = 60, + LV_HASP_IMGBTN = 61, + LV_HASP_CANVAS = 62, + + LV_HASP_TILEVIEW = 70, + LV_HASP_TABVIEW = 71, + LV_HASP_TAB = 72, + + LV_HASP_CHART = 80, + LV_HASP_CALENDER = 81, + + LV_HASP_CONTAINER = 90, + LV_HASP_OBJECT = 91, + LV_HASP_PAGE = 92, + LV_HASP_MSGBOX = 93, + LV_HASP_WINDOW = 94, +}; + +void hasp_new_object(const JsonObject & config, uint8_t & saved_page_id); + +lv_obj_t * hasp_find_obj_from_id(lv_obj_t * parent, uint8_t objid); +lv_obj_t * hasp_find_obj_from_id(uint8_t pageid, uint8_t objid); +bool hasp_find_id_from_obj(lv_obj_t * obj, uint8_t * pageid, lv_obj_user_data_t * objid); +bool check_obj_type(const char * lvobjtype, lv_hasp_obj_type_t haspobjtype); +bool check_obj_type(lv_obj_t * obj, lv_hasp_obj_type_t haspobjtype); + +void hasp_send_obj_attribute_str(lv_obj_t * obj, const char * attribute, const char * data); +void hasp_send_obj_attribute_int(lv_obj_t * obj, const char * attribute, int32_t val); +void hasp_send_obj_attribute_color(lv_obj_t * obj, const char * attribute, lv_color_t color); +void hasp_process_attribute(uint8_t pageid, uint8_t objid, const char * attr, const char * payload); + +void IRAM_ATTR btn_event_handler(lv_obj_t * obj, lv_event_t event); +void IRAM_ATTR toggle_event_handler(lv_obj_t * obj, lv_event_t event); + +#endif \ No newline at end of file