Redesign action property

This commit is contained in:
fvanroie 2023-01-11 13:46:58 +01:00
parent b0ba6a7f2b
commit 8209625f1e
7 changed files with 191 additions and 99 deletions

View File

@ -787,12 +787,13 @@ typedef void* lv_font_user_data_t;
/*Declare the type of the user data of object (can be e.g. `void *`, `int`, `struct`)*/ /*Declare the type of the user data of object (can be e.g. `void *`, `int`, `struct`)*/
typedef struct { typedef struct {
uint8_t id:8; uint8_t id:8;
uint8_t objid:8; uint8_t objid:6;
uint8_t transitionid:4; uint8_t transitionid:4;
uint8_t actionid:4; uint8_t actionid:4;
uint8_t groupid:4; uint8_t groupid:4;
uint8_t swipeid:4; uint8_t swipeid:4;
void* tag; void* tag;
char* action;
} lv_obj_user_data_t; } lv_obj_user_data_t;
/*1: enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/ /*1: enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/

View File

@ -1630,6 +1630,18 @@ static hasp_attribute_type_t attribute_common_tag(lv_obj_t* obj, uint16_t attr_h
} }
break; // attribute_found break; // attribute_found
case ATTR_ACTION:
if(update) {
my_obj_set_action(obj, payload);
} else {
if(my_obj_get_action(obj)) {
*text = (char*)my_obj_get_action(obj);
} else {
strcpy_P(*text, "null"); // TODO : Literal String
}
}
break; // attribute_found
default: default:
return HASP_ATTR_TYPE_NOT_FOUND; return HASP_ATTR_TYPE_NOT_FOUND;
} }
@ -2636,6 +2648,7 @@ void hasp_process_obj_attribute(lv_obj_t* obj, const char* attribute, const char
ret = attribute_common_align(obj, attribute, payload, &text, update); ret = attribute_common_align(obj, attribute, payload, &text, update);
break; break;
case ATTR_TAG: case ATTR_TAG:
case ATTR_ACTION:
ret = attribute_common_tag(obj, attr_hash, payload, &text, update); ret = attribute_common_tag(obj, attr_hash, payload, &text, update);
break; break;
case ATTR_JSONL: case ATTR_JSONL:
@ -2662,13 +2675,13 @@ void hasp_process_obj_attribute(lv_obj_t* obj, const char* attribute, const char
// case ATTR_BTN_POS: // case ATTR_BTN_POS:
case ATTR_ACTION: /* case ATTR_ACTION:
if(update) if(update)
obj->user_data.actionid = Parser::get_action_id(payload); obj->user_data.actionid = Parser::get_action_id(payload);
else else
val = obj->user_data.actionid; val = obj->user_data.actionid;
ret = HASP_ATTR_TYPE_INT; ret = HASP_ATTR_TYPE_INT;
break; break;*/
// case ATTR_SYMBOL: // case ATTR_SYMBOL:
// (update) ? lv_dropdown_set_symbol(obj, payload) : // (update) ? lv_dropdown_set_symbol(obj, payload) :

View File

@ -16,6 +16,7 @@ lv_chart_series_t* my_chart_get_series(lv_obj_t* chart, uint8_t ser_num);
void my_obj_set_value_str_text(lv_obj_t* obj, uint8_t part, lv_state_t state, const char* text); void my_obj_set_value_str_text(lv_obj_t* obj, uint8_t part, lv_state_t state, const char* text);
void my_obj_set_tag(lv_obj_t* obj, const char* tag); void my_obj_set_tag(lv_obj_t* obj, const char* tag);
void my_obj_set_action(lv_obj_t* obj, const char* tag);
const char* my_obj_get_tag(lv_obj_t* obj); const char* my_obj_get_tag(lv_obj_t* obj);
void my_btnmatrix_map_clear(lv_obj_t* obj); void my_btnmatrix_map_clear(lv_obj_t* obj);
void my_msgbox_map_clear(lv_obj_t* obj); void my_msgbox_map_clear(lv_obj_t* obj);
@ -482,8 +483,8 @@ _HASP_ATTRIBUTE(SCALE_END_LINE_WIDTH, scale_end_line_width, lv_style_int_t)
// Spinner // Spinner
#define ATTR_SPEED 14375 #define ATTR_SPEED 14375
#define ATTR_THICKNESS 24180 #define ATTR_THICKNESS 24180
//#define ATTR_ARC_LENGTH 755 - use ATTR_ANGLE // #define ATTR_ARC_LENGTH 755 - use ATTR_ANGLE
// #define ATTR_DIRECTION 32415 - see Dropdown // #define ATTR_DIRECTION 32415 - see Dropdown
// Line // Line
#define ATTR_POINTS 8643 #define ATTR_POINTS 8643

View File

@ -95,6 +95,42 @@ const char* my_obj_get_tag(lv_obj_t* obj)
return (char*)obj->user_data.tag; return (char*)obj->user_data.tag;
} }
// the action data is stored as SERIALIZED JSON data
void my_obj_set_action(lv_obj_t* obj, const char* action)
{
// release old action
if(obj->user_data.action) {
hasp_free(obj->user_data.action);
obj->user_data.action = NULL;
}
// new action is blank
if(action == NULL || action[0] == '\0') return;
// create new action
{
StaticJsonDocument<512> doc;
// size_t len = action ? strlen(action) : 0;
// check if it is a proper JSON object
DeserializationError error = deserializeJson(doc, action /*, len*/);
if(error != DeserializationError::Ok) doc.set(action); // use tag as-is
const size_t size = measureJson(doc) + 1;
if(char* str = (char*)hasp_malloc(size)) {
size_t len = serializeJson(doc, str, size); // tidy-up the json object
obj->user_data.action = str;
LOG_VERBOSE(TAG_ATTR, "new json: %s", str);
}
}
}
// the tag data is stored as SERIALIZED JSON data
const char* my_obj_get_action(lv_obj_t* obj)
{
return obj->user_data.action;
}
lv_label_align_t my_textarea_get_text_align(lv_obj_t* ta) lv_label_align_t my_textarea_get_text_align(lv_obj_t* ta)
{ {
lv_textarea_ext_t* ext = (lv_textarea_ext_t*)lv_obj_get_ext_attr(ta); lv_textarea_ext_t* ext = (lv_textarea_ext_t*)lv_obj_get_ext_attr(ta);

View File

@ -4,13 +4,13 @@
#include <time.h> #include <time.h>
#include <sys/time.h> #include <sys/time.h>
//#include "ArduinoLog.h" // #include "ArduinoLog.h"
#include "hasplib.h" #include "hasplib.h"
#include "dev/device.h" #include "dev/device.h"
#include "drv/tft/tft_driver.h" #include "drv/tft/tft_driver.h"
//#include "hasp_gui.h" // #include "hasp_gui.h"
#if HASP_USE_DEBUG > 0 #if HASP_USE_DEBUG > 0
#include "../hasp_debug.h" #include "../hasp_debug.h"
@ -272,7 +272,7 @@ static void dispatch_output(const char* topic, const char* payload)
} }
// objectattribute=value // objectattribute=value
void dispatch_command(const char* topic, const char* payload, bool update, uint8_t source) static void dispatch_command(const char* topic, const char* payload, bool update, uint8_t source)
{ {
/* ================================= Standard payload commands ======================================= */ /* ================================= Standard payload commands ======================================= */
@ -323,47 +323,14 @@ void dispatch_command(const char* topic, const char* payload, bool update, uint8
#endif // HASP_USE_CONFIG #endif // HASP_USE_CONFIG
} else { } else {
if(strlen(payload) == 0) { if(strlen(payload) == 0) {
// dispatch_text_line(topic); // Could cause an infinite loop! // dispatch_simple_text_command(topic); // Could cause an infinite loop!
} }
LOG_WARNING(TAG_MSGR, F(D_DISPATCH_COMMAND_NOT_FOUND " => %s"), topic, payload); LOG_WARNING(TAG_MSGR, F(D_DISPATCH_COMMAND_NOT_FOUND " => %s"), topic, payload);
} }
} }
// Strip command/config prefix from the topic and process the payload
void dispatch_topic_payload(const char* topic, const char* payload, bool update, uint8_t source)
{
if(!strcmp_P(topic, PSTR(MQTT_TOPIC_COMMAND))) {
dispatch_text_line((char*)payload, source);
return;
}
if(topic == strstr_P(topic, PSTR(MQTT_TOPIC_COMMAND "/"))) { // startsWith command/
topic += 8u;
dispatch_command(topic, (char*)payload, update, source);
return;
}
#if HASP_USE_CONFIG > 0
if(topic == strstr_P(topic, PSTR("config/"))) { // startsWith config/
topic += 7u;
dispatch_config(topic, (char*)payload, source);
return;
}
#endif
#if defined(HASP_USE_CUSTOM)
if(topic == strstr_P(topic, PSTR(MQTT_TOPIC_CUSTOM "/"))) { // startsWith custom
topic += 7u;
custom_topic_payload(topic, (char*)payload, source);
return;
}
#endif
dispatch_command(topic, (char*)payload, update, source); // dispatch as is
}
// Parse one line of text and execute the command // Parse one line of text and execute the command
void dispatch_text_line(const char* cmnd, uint8_t source) static void dispatch_simple_text_command(const char* cmnd, uint8_t source)
{ {
while(cmnd[0] == ' ' || cmnd[0] == '\t') cmnd++; // skip leading spaces while(cmnd[0] == ' ' || cmnd[0] == '\t') cmnd++; // skip leading spaces
if(cmnd[0] == '/' && cmnd[1] == '/') return; // comment if(cmnd[0] == '/' && cmnd[1] == '/') return; // comment
@ -381,9 +348,9 @@ void dispatch_text_line(const char* cmnd, uint8_t source)
dispatch_command("json", cmnd, false, source); dispatch_command("json", cmnd, false, source);
break; // comment break; // comment
case ' ': // case ' ':
dispatch_text_line(cmnd, source); // dispatch_simple_text_command(cmnd, source);
break; // break;
default: { default: {
size_t pos1 = std::string(cmnd).find("="); size_t pos1 = std::string(cmnd).find("=");
@ -425,6 +392,39 @@ void dispatch_text_line(const char* cmnd, uint8_t source)
} }
} }
// Strip command/config prefix from the topic and process the payload
void dispatch_topic_payload(const char* topic, const char* payload, bool update, uint8_t source)
{
if(!strcmp_P(topic, PSTR(MQTT_TOPIC_COMMAND))) {
dispatch_simple_text_command((char*)payload, source);
return;
}
if(topic == strstr_P(topic, PSTR(MQTT_TOPIC_COMMAND "/"))) { // startsWith command/
topic += 8u;
dispatch_command(topic, (char*)payload, update, source);
return;
}
#if HASP_USE_CONFIG > 0
if(topic == strstr_P(topic, PSTR("config/"))) { // startsWith config/
topic += 7u;
dispatch_config(topic, (char*)payload, source);
return;
}
#endif
#if defined(HASP_USE_CUSTOM)
if(topic == strstr_P(topic, PSTR(MQTT_TOPIC_CUSTOM "/"))) { // startsWith custom
topic += 7u;
custom_topic_payload(topic, (char*)payload, source);
return;
}
#endif
dispatch_command(topic, (char*)payload, update, source); // dispatch as is
}
// void dispatch_output_group_state(uint8_t groupid, uint16_t state) // void dispatch_output_group_state(uint8_t groupid, uint16_t state)
// { // {
// char payload[64]; // char payload[64];
@ -593,54 +593,69 @@ void dispatch_screenshot(const char*, const char* filename, uint8_t source)
#endif #endif
} }
void dispatch_parse_json(const char*, const char* payload, uint8_t source) bool dispatch_json_variant(JsonVariant& json, uint8_t& savedPage, uint8_t source)
{ // Parse an incoming JSON array into individual commands {
/* if(strPayload.endsWith(",]")) { if(json.is<JsonArray>()) { // handle json as an array of commands
// Trailing null array elements are an artifact of older Home Assistant automations
// and need to be removed before parsing by ArduinoJSON 6+
strPayload.remove(strPayload.length() - 2, 2);
strPayload.concat("]");
}*/
// size_t maxsize = (128u * ((strlen(payload) / 128) + 1)) + 512;
// DynamicJsonDocument json(maxsize);
StaticJsonDocument<1024> json;
// Note: Deserialization needs to be (const char *) so the objects WILL be copied
// this uses more memory but otherwise the mqtt receive buffer can get overwritten by the send buffer !!
DeserializationError jsonError = deserializeJson(json, payload);
// json.shrinkToFit();
if(jsonError) { // Couldn't parse incoming JSON command
dispatch_json_error(TAG_MSGR, jsonError);
} else if(json.is<JsonArray>()) { // handle json as an array of commands
JsonArray arr = json.as<JsonArray>(); JsonArray arr = json.as<JsonArray>();
// guiStop(); LOG_WARNING(TAG_MSGR, "TEXT = ARRAY");
for(JsonVariant command : arr) { for(JsonVariant command : arr) {
dispatch_text_line(command.as<const char*>(), source); dispatch_json_variant(command, savedPage, source);
} }
// guiStart();
} else if(json.is<JsonObject>()) { // handle json as a jsonl } else if(json.is<JsonObject>()) { // handle json as a jsonl
uint8_t savedPage = haspPages.get(); LOG_WARNING(TAG_MSGR, "TEXT = OBJECT");
hasp_new_object(json.as<JsonObject>(), savedPage); hasp_new_object(json.as<JsonObject>(), savedPage);
// #ifdef ARDUINO
// } else if(json.is<String>()) { // handle json as a single command
// dispatch_text_line(json.as<String>().c_str());
// #else
} else if(json.is<std::string>()) { // handle json as a single command } else if(json.is<std::string>()) { // handle json as a single command
dispatch_text_line(json.as<std::string>().c_str(), source); LOG_WARNING(TAG_MSGR, "TEXT = %s", json.as<std::string>().c_str());
// #endif dispatch_simple_text_command(json.as<std::string>().c_str(), source);
} else if(json.is<const char*>()) { // handle json as a single command } else if(json.is<const char*>()) { // handle json as a single command
dispatch_text_line(json.as<const char*>(), source); LOG_WARNING(TAG_MSGR, "TEXT = %s", json.as<const char*>());
dispatch_simple_text_command(json.as<const char*>(), source);
// } else if(json.is<char*>()) { // handle json as a single command
// dispatch_text_line(json.as<char*>());
} else { } else {
LOG_WARNING(TAG_MSGR, F(D_DISPATCH_COMMAND_NOT_FOUND), payload); LOG_WARNING(TAG_MSGR, "TEXT = unknown type");
return false;
} }
return true;
}
void dispatch_text_line(const char* payload, uint8_t source)
{
{
// size_t maxsize = (128u * ((strlen(payload) / 128) + 1)) + 512;
// DynamicJsonDocument json(maxsize);
StaticJsonDocument<1024> doc;
// Note: Deserialization needs to be (const char *) so the objects WILL be copied
// this uses more memory but otherwise the mqtt receive buffer can get overwritten by the send buffer !!
DeserializationError jsonError = deserializeJson(doc, payload);
// json.shrinkToFit();
if(jsonError) {
// dispatch_json_error(TAG_MSGR, jsonError);
} else {
JsonVariant json = doc.as<JsonVariant>();
uint8_t savedPage = haspPages.get();
if(!dispatch_json_variant(json, savedPage, source)) {
LOG_WARNING(TAG_MSGR, F(D_DISPATCH_COMMAND_NOT_FOUND), payload);
// dispatch_simple_text_command(payload, source);
}
return;
}
}
// Could not parse as json
dispatch_simple_text_command(payload, source);
}
void dispatch_parse_json(const char*, const char* payload, uint8_t source)
{ // Parse an incoming JSON array into individual commands
dispatch_simple_text_command(payload, source);
} }
#ifdef ARDUINO #ifdef ARDUINO
@ -730,7 +745,7 @@ void dispatch_run_script(const char*, const char* payload, uint8_t source)
index++; index++;
} }
if(index > 0 && buffer.charAt(0) != '#') { // Check for comments if(index > 0 && buffer.charAt(0) != '#') { // Check for comments
dispatch_text_line(buffer.c_str(), TAG_FILE); dispatch_simple_text_command(buffer.c_str(), TAG_FILE);
} }
} }
@ -752,7 +767,7 @@ void dispatch_run_script(const char*, const char* payload, uint8_t source)
std::string line; std::string line;
while(std::getline(f, line)) { while(std::getline(f, line)) {
LOG_VERBOSE(TAG_HASP, line.c_str()); LOG_VERBOSE(TAG_HASP, line.c_str());
if(!line.empty() && line[0] != '#') dispatch_text_line(line.c_str(), TAG_FILE); // # for comments if(!line.empty() && line[0] != '#') dispatch_simple_text_command(line.c_str(), TAG_FILE); // # for comments
} }
} else { } else {
LOG_ERROR(TAG_MSGR, F(D_FILE_LOAD_FAILED), payload); LOG_ERROR(TAG_MSGR, F(D_FILE_LOAD_FAILED), payload);
@ -841,17 +856,10 @@ void dispatch_page(const char*, const char* payload, uint8_t source)
dispatch_set_page(pageid, animation, time, delay); dispatch_set_page(pageid, animation, time, delay);
} }
// Clears all fonts
void dispatch_clear_font(const char*, const char* payload, uint8_t source)
{
hasp_init();
font_clear_list(payload);
}
// Clears a page id or the current page if empty // Clears a page id or the current page if empty
void dispatch_clear_page(const char*, const char* page, uint8_t source) void dispatch_clear_page(const char*, const char* page, uint8_t source)
{ {
if(!strcasecmp_P(page, PSTR("all"))) { if(!strcasecmp(page, "all")) {
hasp_init(); hasp_init();
return; return;
} }
@ -865,6 +873,14 @@ void dispatch_clear_page(const char*, const char* page, uint8_t source)
haspPages.clear(pageid); haspPages.clear(pageid);
} }
// Clears all fonts
void dispatch_clear_font(const char*, const char* payload, uint8_t source)
{
dispatch_clear_page(NULL, "all", source);
hasp_init();
font_clear_list(payload);
}
void dispatch_dim(const char*, const char* level) void dispatch_dim(const char*, const char* level)
{ {
// Set the current state // Set the current state

View File

@ -54,6 +54,7 @@ void dispatch_parse_jsonl(Stream& stream, uint8_t& saved_page_id);
#else #else
void dispatch_parse_jsonl(std::istream& stream, uint8_t& saved_page_id); void dispatch_parse_jsonl(std::istream& stream, uint8_t& saved_page_id);
#endif #endif
bool dispatch_json_variant(JsonVariant& json, uint8_t& savedPage, uint8_t source);
void dispatch_clear_page(const char* page); void dispatch_clear_page(const char* page);
void dispatch_json_error(uint8_t tag, DeserializationError& jsonError); void dispatch_json_error(uint8_t tag, DeserializationError& jsonError);

View File

@ -86,6 +86,7 @@ void delete_event_handler(lv_obj_t* obj, lv_event_t event)
my_obj_set_value_str_text(obj, part, LV_STATE_DISABLED + LV_STATE_CHECKED, NULL); my_obj_set_value_str_text(obj, part, LV_STATE_DISABLED + LV_STATE_CHECKED, NULL);
} }
my_obj_set_tag(obj, (char*)NULL); my_obj_set_tag(obj, (char*)NULL);
my_obj_set_action(obj, (char*)NULL);
} }
/* ============================== Timer Event ============================ */ /* ============================== Timer Event ============================ */
@ -491,8 +492,31 @@ void generic_event_handler(lv_obj_t* obj, lv_event_t event)
if(last_value_sent == HASP_EVENT_LOST) return; if(last_value_sent == HASP_EVENT_LOST) return;
/* If an actionid is attached, perform that action on UP event only */ if(obj->user_data.action) {
if(obj->user_data.actionid) { // if(last_value_sent == HASP_EVENT_UP || last_value_sent == HASP_EVENT_RELEASE) {
// dispatch_text_line(obj->user_data.action, TAG_EVENT);
StaticJsonDocument<256> doc;
StaticJsonDocument<64> filter;
char eventname[8];
Parser::get_event_name(last_value_sent, eventname, sizeof(eventname));
filter[eventname] = true;
DeserializationError jsonError =
deserializeJson(doc, (const char*)obj->user_data.action, DeserializationOption::Filter(filter));
if(!jsonError) {
JsonVariant json = doc[eventname].as<JsonVariant>();
uint8_t savedPage = haspPages.get();
if(!dispatch_json_variant(json, savedPage, TAG_EVENT)) {
LOG_WARNING(TAG_MSGR, F(D_DISPATCH_COMMAND_NOT_FOUND), eventname);
// dispatch_simple_text_command(payload, source);
}
}
// }
} else if(obj->user_data.actionid) {
/* If an actionid is attached, perform that action on UP event only */
if(last_value_sent == HASP_EVENT_UP || last_value_sent == HASP_EVENT_RELEASE) { if(last_value_sent == HASP_EVENT_UP || last_value_sent == HASP_EVENT_RELEASE) {
lv_scr_load_anim_t transitionid = (lv_scr_load_anim_t)obj->user_data.transitionid; lv_scr_load_anim_t transitionid = (lv_scr_load_anim_t)obj->user_data.transitionid;
switch(obj->user_data.actionid) { switch(obj->user_data.actionid) {