From 8c1627b9b252eca4b045e528d1243b52d1ba4abd Mon Sep 17 00:00:00 2001 From: Th3maz1ng Date: Mon, 10 Apr 2023 21:26:25 +0200 Subject: [PATCH] Officially added the gadget bridge parser I developed to the W800_Smart_Watch firmware --- src/W800_SDK_v1.00.10/app/ble/gadget_bridge.c | 1424 +++++++++++++++++ src/W800_SDK_v1.00.10/app/ble/gadget_bridge.h | 321 ++++ 2 files changed, 1745 insertions(+) create mode 100644 src/W800_SDK_v1.00.10/app/ble/gadget_bridge.c create mode 100644 src/W800_SDK_v1.00.10/app/ble/gadget_bridge.h diff --git a/src/W800_SDK_v1.00.10/app/ble/gadget_bridge.c b/src/W800_SDK_v1.00.10/app/ble/gadget_bridge.c new file mode 100644 index 0000000..b8caa7f --- /dev/null +++ b/src/W800_SDK_v1.00.10/app/ble/gadget_bridge.c @@ -0,0 +1,1424 @@ +/** + * @file gadget_bridge.c + * @author Anatole SCHRAMM-HENRY + * @brief Source file implementing the API used to communicate/interact with the GadgetBridge Android application + * over BLE. + * @version 0.1 + * @date 2023-04-05 + * + * @copyright MIT + * + */ + +#include "gadget_bridge.h" +#include +#include +#include +#include "ble_service.h" +#include "wm_mem.h" + +/* Internal enum definition */ +typedef enum gadget_bridge_parser_fsm +{ + GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE = 0, + GADGET_BRIDGE_PARSER_FSM_FOUND_GB, + + GADGET_BRIDGE_PARSER_FSM_FOUND_NOTIFY, + GADGET_BRIDGE_PARSER_FSM_FOUND_ID_BODY, + GADGET_BRIDGE_PARSER_FSM_FOUND_SENDER, + GADGET_BRIDGE_PARSER_FSM_FOUND_TEL, + GADGET_BRIDGE_PARSER_FSM_FOUND_TITLE, + GADGET_BRIDGE_PARSER_FSM_PARSING_TITLE_CONTENT, + + GADGET_BRIDGE_PARSER_FSM_FOUND_ID_SRC, + GADGET_BRIDGE_PARSER_FSM_FOUND_SRC_BODY, + GADGET_BRIDGE_PARSER_FSM_PARSING_BODY_CONTENT, + + GADGET_BRIDGE_PARSER_FSM_FOUND_CALL, + GADGET_BRIDGE_PARSER_FSM_FOUND_NAME, + GADGET_BRIDGE_PARSER_FSM_FOUND_NUMBER, + + GADGET_BRIDGE_PARSER_FSM_FOUND_WEATHER, + GADGET_BRIDGE_PARSER_FSM_FOUND_TEMP, + GADGET_BRIDGE_PARSER_FSM_FOUND_HUM, + GADGET_BRIDGE_PARSER_FSM_FOUND_TXT, + GADGET_BRIDGE_PARSER_FSM_FOUND_WIND, + GADGET_BRIDGE_PARSER_FSM_FOUND_WINDDIR, + GADGET_BRIDGE_PARSER_FSM_FOUND_LOC, + + GADGET_BRIDGE_PARSER_FSM_FOUND_FIND, + + GADGET_BRIDGE_PARSER_FSM_FOUND_ACT, + GADGET_BRIDGE_PARSER_FSM_FOUND_STP, + GADGET_BRIDGE_PARSER_FSM_FOUND_HRM_INT, + + GADGET_BRIDGE_PARSER_FSM_FOUND_MUSICSTATE, + GADGET_BRIDGE_PARSER_FSM_FOUND_POSITION, + GADGET_BRIDGE_PARSER_FSM_FOUND_SHUFFLE, + GADGET_BRIDGE_PARSER_FSM_FOUND_REPEAT, + + GADGET_BRIDGE_PARSER_FSM_FOUND_MUSICINFO, + GADGET_BRIDGE_PARSER_FSM_FOUND_TRACK, + GADGET_BRIDGE_PARSER_FSM_FOUND_DURATION, + + GADGET_BRIDGE_PARSER_FSM_ERROR, +} gadget_bridge_parser_fsm_e; + +/* Internal variable definition */ +struct +{ + char buffer[GADGET_BRIDGE_PARSER_BUFFER_SIZE + 1]; // The +1 is for the string termination character. + uint16_t buffer_content_size; // Without counting the string termination character. + gadget_bridge_parser_fsm_e gadget_bridge_parser_fsm; + bool new_data; + parser_event_callback_t parser_event_callback; + gadget_bridge_event_data_t event_data; +} _gadget_bridge_internals = +{ + .buffer_content_size = 0, + .gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE, + .new_data = false, + .parser_event_callback = NULL, + .event_data = { .event_type = GADGET_BRIDGE_EVENT_TYPE_NONE}, +}; + +time_t _unix_timestamp = 0; + +/* Internal function definition */ +static const char *_gadget_bridge_toast_type_2_str(gadget_bridge_toast_type_e toast_type); +static const char *_gadget_bridge_music_control_2_str(gadget_bridge_music_control_e music_control); +static const char *_gadget_bridge_http_request_method_2_str(gadget_bridge_http_request_method_e http_request_method); +static void _parser_free_buffer(uint16_t length); +static void _free_event_data(void); + +bool gadget_bridge_send_toast(gadget_bridge_toast_type_e toast_type, const char *message) +{ + if(!message || !strlen(message)) + return true; + + bool to_return = true; + to_return &= ble_service_send_nus_data((const uint8_t *)"{\"t\":\"", 6); + to_return &= ble_service_send_nus_data((const uint8_t *)_gadget_bridge_toast_type_2_str(toast_type), strlen(_gadget_bridge_toast_type_2_str(toast_type))); + to_return &= ble_service_send_nus_data((const uint8_t *)"\",\"msg\":\"", 9); + to_return &= ble_service_send_nus_data((const uint8_t *)message, strlen(message)); + to_return &= ble_service_send_nus_data((const uint8_t *)"\"} \n", 4); + + return to_return; +} + +bool gadget_bridge_send_firmware_version(const char *fw1, const char *fw2) +{ + if(!fw1 && !fw2) + return true; + + bool to_return = true; + to_return &= ble_service_send_nus_data((const uint8_t *)"{\"t\":\"ver\",\"fw1\":\"", 18); + if(fw1) + { + to_return &= ble_service_send_nus_data((const uint8_t *)fw1, strlen(fw1)); + } + to_return &= ble_service_send_nus_data((const uint8_t *)"\",\"fw2\":\"", 9); + if(fw2) + { + to_return &= ble_service_send_nus_data((const uint8_t *)fw2, strlen(fw2)); + } + to_return &= ble_service_send_nus_data((const uint8_t *)"\"} \n", 4); + + return to_return; +} + +bool gadget_bridge_send_battery_status(uint8_t battery_level_in_percent, float battery_level_in_V, bool is_charging) +{ + bool to_return = true; + char num_2_str[30] = ""; + to_return &= ble_service_send_nus_data((const uint8_t *)"{\"t\":\"status\",\"bat\":", 20); + sprintf(num_2_str, "%u,\"chg\":%u,\"volt\":%.3f} \n", battery_level_in_percent, is_charging, battery_level_in_V); + to_return &= ble_service_send_nus_data((const uint8_t *)num_2_str, strlen(num_2_str)); + + return to_return; +} + +bool gadget_bridge_send_find_phone(bool find_phone) +{ + bool to_return = true; + to_return &= ble_service_send_nus_data((const uint8_t *)"{\"t\":\"findPhone\",\"n\":", 21); + if(find_phone) + to_return &= ble_service_send_nus_data((const uint8_t *)"true} \n", 7); + else + to_return &= ble_service_send_nus_data((const uint8_t *)"false} \n", 8); + + return to_return; +} + +bool gadget_bridge_send_music_control(gadget_bridge_music_control_e music_control) +{ + bool to_return = true; + to_return &= ble_service_send_nus_data((const uint8_t *)"{\"t\":\"music\",\"n\":\"", 18); + to_return &= ble_service_send_nus_data((const uint8_t *)_gadget_bridge_music_control_2_str(music_control), strlen(_gadget_bridge_music_control_2_str(music_control))); + to_return &= ble_service_send_nus_data((const uint8_t *)"\"} \n", 4); + + return to_return; +} + +bool gadget_bridge_send_activity_data(uint16_t heart_rate_in_bpm, uint32_t step_count) +{ + bool to_return = true; + char num_2_str[11] = ""; + //{\"t\":\"act\",\"hrm\":%s,\"stp\":%s} \n + to_return &= ble_service_send_nus_data((const uint8_t *)"{\"t\":\"act\",\"hrm\":", 17); + + sprintf(num_2_str, "%u", heart_rate_in_bpm); + to_return &= ble_service_send_nus_data((const uint8_t *)num_2_str, strlen(num_2_str)); + + to_return &= ble_service_send_nus_data((const uint8_t *)",\"stp\":", 7); + + sprintf(num_2_str, "%u", step_count); + to_return &= ble_service_send_nus_data((const uint8_t *)num_2_str, strlen(num_2_str)); + + to_return &= ble_service_send_nus_data((const uint8_t *)"} \n", 3); + + return to_return; +} + +bool gadget_bridge_send_http_request(uint32_t id, const char *url, gadget_bridge_http_request_method_e http_request_method, const char *http_body, const http_header_t *http_headers) +{ + bool to_return = true; + char num_2_str[11] = ""; + sprintf(num_2_str, "%u", id); + + to_return &= ble_service_send_nus_data((const uint8_t *)"{\"t\":\"http\",\"id\":\"", 18); + to_return &= ble_service_send_nus_data((const uint8_t *)num_2_str, strlen(num_2_str)); + to_return &= ble_service_send_nus_data((const uint8_t *)"\",\"url\":\"", 9); + to_return &= ble_service_send_nus_data((const uint8_t *)url, strlen(url)); + to_return &= ble_service_send_nus_data((const uint8_t *)"\",\"method\":\"", 12); + to_return &= ble_service_send_nus_data((const uint8_t *)_gadget_bridge_http_request_method_2_str(http_request_method), strlen(_gadget_bridge_http_request_method_2_str(http_request_method))); + to_return &= ble_service_send_nus_data((const uint8_t *)"\"} \n", 4); + return to_return; +} + +/*bool gadget_bridge_send_force_calendar_sync(void) +{ + bool to_return = true; + to_return &= ble_service_send_nus_data((const uint8_t *)"{\"t\":\"force_calendar_sync\"} \n", 29); + + return to_return; +}*/ + +void gadget_bridge_parser_register_event_callback(parser_event_callback_t parser_event_callback) +{ + _gadget_bridge_internals.parser_event_callback = parser_event_callback; +} + +gadget_bridge_parser_code_e gadget_bridge_parser_feed(const char *data, uint16_t length) +{ + // First we check if there is still space left in the parser buffer + if(_gadget_bridge_internals.buffer_content_size >= GADGET_BRIDGE_PARSER_BUFFER_SIZE) + return GADGET_BRIDGE_PARSER_CODE_BUFFER_FULL; + + // We check if the incoming data is not too long for the buffer + if(_gadget_bridge_internals.buffer_content_size + length > GADGET_BRIDGE_PARSER_BUFFER_SIZE) + return GADGET_BRIDGE_PARSER_CODE_DATA_TOO_LONG; + + strncat(_gadget_bridge_internals.buffer, data, length); + _gadget_bridge_internals.buffer_content_size += length; + _gadget_bridge_internals.new_data = true; + + return GADGET_BRIDGE_PARSER_CODE_OK; +} + +static bool _parser_extract_time(char *start, char *end); +static bool _parser_extract_handle(char *start, char *end); +//static void _parser_extract_body(char *start, char *end); +//static void _parser_extract_sender(char *start, char *end); +//static void _parser_extract_tel(char *start, char *end); +static void _parser_extract_src(char *start, char *end); +static void _parser_extract_char_str(char *start, char *end, char **data); +static void _parser_extract_cmd(char *start, char *end); +static void _parser_extract_state(char *start, char *end); +static bool _parser_extract_uint8_t(char *start, char *end, uint8_t *data); +static bool _parser_extract_float(char *start, char *end, float *data); +static bool _parser_extract_uint16_t(char *start, char *end, uint16_t *data); +static bool _parser_extract_bool(char *start, char *end, bool *data); + +gadget_bridge_parser_code_e gadget_bridge_parser_run(void) +{ + if(!_gadget_bridge_internals.new_data) return GADGET_BRIDGE_PARSER_CODE_OK; + char *start = NULL, *end = NULL; + bool free_some_space = false; + gadget_bridge_parser_code_e to_return = GADGET_BRIDGE_PARSER_CODE_PARSING; + + switch(_gadget_bridge_internals.gadget_bridge_parser_fsm) + { + case GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE: + if((start = strstr(_gadget_bridge_internals.buffer, "setTime(")) + && (end = strstr(_gadget_bridge_internals.buffer, ");"))) + { + //printf("###Found setTime\n"); + _parser_extract_time(start + 8, end); + + // We remove the parsed part from the buffer + end += 2; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + } + else if((start = strstr(_gadget_bridge_internals.buffer, "setTimeZone(")) + && (end = strstr(_gadget_bridge_internals.buffer, ");"))) + { + //printf("###Found setTimeZone\n"); + float time_zone = 0.0; + if(_parser_extract_float(start + 12, end, &time_zone)) + { + _gadget_bridge_internals.event_data.event_type = GADGET_BRIDGE_EVENT_TYPE_SET_TIME; + _gadget_bridge_internals.event_data.time.time_zone = time_zone; + // Set the local time + if(_unix_timestamp) + { + _unix_timestamp += _gadget_bridge_internals.event_data.time.time_zone*3600; + _gadget_bridge_internals.event_data.time.local_time = *gmtime(&_unix_timestamp); + } + + // If a callback was registered, we call it and pass the data to it + if(_gadget_bridge_internals.parser_event_callback) + { + _gadget_bridge_internals.parser_event_callback(&_gadget_bridge_internals.event_data); + } + } + + // We remove the parsed part from the buffer + end += 2; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + { + //printf("###Found GB\n"); + start += 3; + _parser_free_buffer( + start -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_GB; + } + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else + _gadget_bridge_internals.new_data = false; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_GB: + if((start = strstr(_gadget_bridge_internals.buffer, "t:\"notify\","))) + { + //printf("###Found notify\n"); + _gadget_bridge_internals.event_data.event_type = GADGET_BRIDGE_EVENT_TYPE_NOTIFY; + // We remove the parsed part from the buffer + start += 11; + _parser_free_buffer( + start -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_NOTIFY; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "t:\"call\","))) + { + //printf("###Found call\n"); + _gadget_bridge_internals.event_data.event_type = GADGET_BRIDGE_EVENT_TYPE_CALL; + // We remove the parsed part from the buffer + start += 9; + _parser_free_buffer( + start -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_CALL; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "t:\"weather\","))) + { + //printf("###Found weather\n"); + _gadget_bridge_internals.event_data.event_type = GADGET_BRIDGE_EVENT_TYPE_WEATHER; + // We remove the parsed part from the buffer + start += 12; + _parser_free_buffer( + start -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_WEATHER; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "t:\"find\","))) + { + //printf("###Found find\n"); + _gadget_bridge_internals.event_data.event_type = GADGET_BRIDGE_EVENT_TYPE_FIND; + // We remove the parsed part from the buffer + start += 9; + _parser_free_buffer( + start -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_FIND; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "t:\"act\","))) + { + //printf("###Found act\n"); + _gadget_bridge_internals.event_data.event_type = GADGET_BRIDGE_EVENT_TYPE_ACT; + // We remove the parsed part from the buffer + start += 8; + _parser_free_buffer( + start -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_ACT; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "t:\"musicstate\","))) + { + //printf("###Found musicstate\n"); + _gadget_bridge_internals.event_data.event_type = GADGET_BRIDGE_EVENT_TYPE_MUSIC_STATE; + // We remove the parsed part from the buffer + start += 15; + _parser_free_buffer( + start -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_MUSICSTATE; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "t:\"musicinfo\","))) + { + //printf("###Found musicinfo\n"); + _gadget_bridge_internals.event_data.event_type = GADGET_BRIDGE_EVENT_TYPE_MUSIC_INFO; + // We remove the parsed part from the buffer + start += 14; + _parser_free_buffer( + start -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_MUSICINFO; + } + // If we did not find the rest but found GB again, then some payload was lost... + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + // Here we parse the notify type of payloads + case GADGET_BRIDGE_PARSER_FSM_FOUND_NOTIFY: + if((start = strstr(_gadget_bridge_internals.buffer, "id:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",src"))) + { + //printf("###Found ID SRC\n"); + + _parser_extract_handle(start + 3, end); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_ID_SRC; + } + /*else if((start = strstr(_gadget_bridge_internals.buffer, "id:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",body"))) + { + printf("###Found ID BODY\n"); + _parser_extract_handle(start + 3, end); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_ID_BODY; + }*/ + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_ID_SRC: + if((start = strstr(_gadget_bridge_internals.buffer, "src:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",title"))) + { + //printf("###Found TITLE\n"); + + _parser_extract_src(start + 5, end - 1); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_TITLE; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_TITLE: + if((start = strstr(_gadget_bridge_internals.buffer, "title:"))) + { + //printf("###Parsing TITLE content\n"); + + // We remove the parsed part from the buffer + start += 7; + _parser_free_buffer( + start -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_PARSING_TITLE_CONTENT; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_PARSING_TITLE_CONTENT: + { + end = strstr(_gadget_bridge_internals.buffer, ",body"); + if(end) + { + //printf("###Found BODY\n"); + + _parser_extract_char_str(_gadget_bridge_internals.buffer, end - 1, &_gadget_bridge_internals.event_data.notification.title); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_SRC_BODY; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + // Then we have a very long title, in this case we juste keep the max set up + else if(!end && _gadget_bridge_internals.buffer_content_size >= GADGET_BRIDGE_PARSER_MAX_TITLE_SIZE) + { + printf("###NOTIFICATION (MAX TITLE SIZE)\n"); + + _parser_extract_char_str(_gadget_bridge_internals.buffer, _gadget_bridge_internals.buffer + GADGET_BRIDGE_PARSER_MAX_TITLE_SIZE, &_gadget_bridge_internals.event_data.notification.title); + + // We remove the parsed part from the buffer + _parser_free_buffer( + GADGET_BRIDGE_PARSER_MAX_TITLE_SIZE+1 + ); + + // The end of the road for this object + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_SRC_BODY; + } + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + } + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_SRC_BODY: + if((start = strstr(_gadget_bridge_internals.buffer, "body:"))) + { + //printf("###Parsing BODY content\n"); + + // We remove the : "body":" part + start += 6; + _parser_free_buffer( + start -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_PARSING_BODY_CONTENT; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_PARSING_BODY_CONTENT: + { + end = strstr(_gadget_bridge_internals.buffer, "})"); + if(end) + { + //printf("###NOTIFICATION Type done\n"); + + _parser_extract_char_str(_gadget_bridge_internals.buffer, end - 1, &_gadget_bridge_internals.event_data.notification.body); + + // We remove the parsed part from the buffer + end += 2; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + + // If a callback was registered, we call it and pass the data to it + if(_gadget_bridge_internals.parser_event_callback) + { + _gadget_bridge_internals.parser_event_callback(&_gadget_bridge_internals.event_data); + } + + // Free the allocated data + _free_event_data(); + + // The end of the road for this object + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + // Then we have a very long body, in this case we juste keep the max set up + else if(!end && _gadget_bridge_internals.buffer_content_size >= GADGET_BRIDGE_PARSER_MAX_BODY_SIZE) + { + //printf("###NOTIFICATION (MAX BODY SIZE) Type done\n"); + + _parser_extract_char_str(_gadget_bridge_internals.buffer, _gadget_bridge_internals.buffer + GADGET_BRIDGE_PARSER_MAX_BODY_SIZE, &_gadget_bridge_internals.event_data.notification.body); + + // We remove the parsed part from the buffer + _parser_free_buffer( + GADGET_BRIDGE_PARSER_MAX_BODY_SIZE + 1 + ); + + // If a callback was registered, we call it and pass the data to it + if(_gadget_bridge_internals.parser_event_callback) + { + _gadget_bridge_internals.parser_event_callback(&_gadget_bridge_internals.event_data); + } + + // Free the allocated data + _free_event_data(); + + // The end of the road for this object + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + } + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + } + break; + /*case GADGET_BRIDGE_PARSER_FSM_FOUND_ID_BODY: + if((start = strstr(_gadget_bridge_internals.buffer, "body:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",sender"))) + { + printf("###Found SENDER\n"); + _parser_extract_body(start + 6, end - 1); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_SENDER; + } + else + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_SENDER: + if((start = strstr(_gadget_bridge_internals.buffer, "sender:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",tel"))) + { + printf("###Found TEL\n"); + _parser_extract_sender(start + 8, end - 1); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_TEL; + } + else + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_TEL: + if((start = strstr(_gadget_bridge_internals.buffer, "tel:")) + && (end = strstr(_gadget_bridge_internals.buffer, "})"))) + { + printf("###SMS Type one done\n"); + _parser_extract_tel(start + 5, end - 1); + + // We remove the parsed part from the buffer + end += 2; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + + // If a callback was registered, we call it and pass the data to it + if(_gadget_bridge_internals.parser_event_callback) + _gadget_bridge_internals.parser_event_callback(&_gadget_bridge_internals.event_data); + // The end of the road for this object + } + + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + break;*/ + case GADGET_BRIDGE_PARSER_FSM_FOUND_CALL: + if((start = strstr(_gadget_bridge_internals.buffer, "cmd:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",name"))) + { + //printf("###Found NAME\n"); + _parser_extract_cmd(start + 5, end - 1); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_NAME; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_NAME: + if((start = strstr(_gadget_bridge_internals.buffer, "name:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",number"))) + { + //printf("###Found NUMBER\n"); + _parser_extract_char_str(start + 6, end - 1, &_gadget_bridge_internals.event_data.call.contact); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_NUMBER; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_NUMBER: + if((start = strstr(_gadget_bridge_internals.buffer, "number:")) + && (end = strstr(_gadget_bridge_internals.buffer, "})"))) + { + //printf("###CALL Type done\n"); + _parser_extract_char_str(start + 8, end - 1, &_gadget_bridge_internals.event_data.call.phone_number); + + // We remove the parsed part from the buffer + end += 2; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + + // If a callback was registered, we call it and pass the data to it + if(_gadget_bridge_internals.parser_event_callback) + { + _gadget_bridge_internals.parser_event_callback(&_gadget_bridge_internals.event_data); + } + + // Free the allocated data + _free_event_data(); + + // The end of the road for this object + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_WEATHER: + if((start = strstr(_gadget_bridge_internals.buffer, "temp:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",hum"))) + { + //printf("###Found HUM\n"); + _parser_extract_float(start + 5, end, &_gadget_bridge_internals.event_data.weather.temperature_celsius); + // Do get °C instead of kelvins + _gadget_bridge_internals.event_data.weather.temperature_celsius += -273.15; + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_HUM; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_HUM: + if((start = strstr(_gadget_bridge_internals.buffer, "hum:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",code"))) + { + // We don't care about code... + //printf("###Found TXT\n"); + _parser_extract_uint8_t(start + 4, end, &_gadget_bridge_internals.event_data.weather.humidity); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_TXT; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_TXT: + if((start = strstr(_gadget_bridge_internals.buffer, "txt:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",wind"))) + { + // We don't care about code... + //printf("###Found WIND\n"); + _parser_extract_char_str(start + 5, end - 1, &_gadget_bridge_internals.event_data.weather.weather_desc); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_WIND; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_WIND: + if((start = strstr(_gadget_bridge_internals.buffer, "wind:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",wdir"))) + { + // We don't care about code... + //printf("###Found WIND DIR\n"); + _parser_extract_float(start + 5, end, &_gadget_bridge_internals.event_data.weather.wind_speed_kmh); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_WINDDIR; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_WINDDIR: + if((start = strstr(_gadget_bridge_internals.buffer, "wdir:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",loc"))) + { + // We don't care about code... + //printf("###Found LOC\n"); + _parser_extract_uint16_t(start + 5, end, &_gadget_bridge_internals.event_data.weather.wind_dir); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_LOC; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_LOC: + if((start = strstr(_gadget_bridge_internals.buffer, "loc:")) + && (end = strstr(_gadget_bridge_internals.buffer, "})"))) + { + //printf("###WEATHER Type done\n"); + _parser_extract_char_str(start + 5, end - 1, &_gadget_bridge_internals.event_data.weather.location); + + // We remove the parsed part from the buffer + end += 2; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + + // If a callback was registered, we call it and pass the data to it + if(_gadget_bridge_internals.parser_event_callback) + { + _gadget_bridge_internals.parser_event_callback(&_gadget_bridge_internals.event_data); + } + + // Free the allocated data + _free_event_data(); + + // The end of the road for this object + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_FIND: + if((start = strstr(_gadget_bridge_internals.buffer, "n:")) + && (end = strstr(_gadget_bridge_internals.buffer, "})"))) + { + //printf("###FIND Type done\n"); + _parser_extract_bool(start + 2, end, &_gadget_bridge_internals.event_data.find.find); + + // We remove the parsed part from the buffer + end += 2; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + + // If a callback was registered, we call it and pass the data to it + if(_gadget_bridge_internals.parser_event_callback) + { + _gadget_bridge_internals.parser_event_callback(&_gadget_bridge_internals.event_data); + } + // The end of the road for this object + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_ACT: + if((start = strstr(_gadget_bridge_internals.buffer, "hrm:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",stp"))) + { + //printf("###Found STP\n"); + _parser_extract_bool(start + 4, end, &_gadget_bridge_internals.event_data.act.heart_rate_monitor); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_STP; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_STP: + if((start = strstr(_gadget_bridge_internals.buffer, "stp:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",int"))) + { + //printf("###Found INT\n"); + _parser_extract_bool(start + 4, end, &_gadget_bridge_internals.event_data.act.steps); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_HRM_INT; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_HRM_INT: + if((start = strstr(_gadget_bridge_internals.buffer, "int:")) + && (end = strstr(_gadget_bridge_internals.buffer, "})"))) + { + //printf("###ACT Type done\n"); + _parser_extract_uint8_t(start + 4, end, &_gadget_bridge_internals.event_data.act.heart_rate_interval); + + // We remove the parsed part from the buffer + end += 2; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + + // If a callback was registered, we call it and pass the data to it + if(_gadget_bridge_internals.parser_event_callback) + { + _gadget_bridge_internals.parser_event_callback(&_gadget_bridge_internals.event_data); + } + + // The end of the road for this object + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_MUSICSTATE: + if((start = strstr(_gadget_bridge_internals.buffer, "state:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",position"))) + { + //printf("###Found POSITION\n"); + _parser_extract_state(start + 7, end - 1); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_POSITION; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_POSITION: + if((start = strstr(_gadget_bridge_internals.buffer, "position:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",shuffle"))) + { + //printf("###Found SHUFFLE\n"); + _parser_extract_uint16_t(start + 9, end, &_gadget_bridge_internals.event_data.music_state.position_in_seconds); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_SHUFFLE; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_SHUFFLE: + if((start = strstr(_gadget_bridge_internals.buffer, "shuffle:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",repeat"))) + { + //printf("###Found REPEAT\n"); + _parser_extract_uint8_t(start + 8, end, &_gadget_bridge_internals.event_data.music_state.shuffle); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_REPEAT; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_REPEAT: + if((start = strstr(_gadget_bridge_internals.buffer, "repeat:")) + && (end = strstr(_gadget_bridge_internals.buffer, "})"))) + { + //printf("###MUSICSTATE Type done\n"); + _parser_extract_uint8_t(start + 7, end, &_gadget_bridge_internals.event_data.music_state.repeat); + + // We remove the parsed part from the buffer + end += 2; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + + // If a callback was registered, we call it and pass the data to it + if(_gadget_bridge_internals.parser_event_callback) + { + _gadget_bridge_internals.parser_event_callback(&_gadget_bridge_internals.event_data); + } + + // The end of the road for this object + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_MUSICINFO: + if((start = strstr(_gadget_bridge_internals.buffer, "artist:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",track"))) + { + //printf("###Found TRACK\n"); + _parser_extract_char_str(start + 8, end - 1, &_gadget_bridge_internals.event_data.music_info.artist); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_TRACK; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_TRACK: + if((start = strstr(_gadget_bridge_internals.buffer, "track:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",dur"))) + { + //printf("###Found DURATION\n"); + _parser_extract_char_str(start + 7, end - 1, &_gadget_bridge_internals.event_data.music_info.track); + + // We remove the parsed part from the buffer + end += 1; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_FOUND_DURATION; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_FOUND_DURATION: + if((start = strstr(_gadget_bridge_internals.buffer, "dur:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",c"))) + { + //printf("###MUSICINFO Type done\n"); + _parser_extract_uint16_t(start + 4, end, &_gadget_bridge_internals.event_data.music_info.duration_in_seconds); + + // We remove the parsed part from the buffer + end += 2; + _parser_free_buffer( + end -_gadget_bridge_internals.buffer + ); + + // If a callback was registered, we call it and pass the data to it + if(_gadget_bridge_internals.parser_event_callback) + { + _gadget_bridge_internals.parser_event_callback(&_gadget_bridge_internals.event_data); + } + + // Free the allocated data + _free_event_data(); + + // The end of the road for this object + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + } + else if((start = strstr(_gadget_bridge_internals.buffer, "GB("))) + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + else if(_gadget_bridge_internals.buffer_content_size > GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD) + free_some_space = true; + else to_return = GADGET_BRIDGE_PARSER_CODE_OK; + break; + case GADGET_BRIDGE_PARSER_FSM_ERROR: + //printf("###Parser error\n"); + default: + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + } + + if(free_some_space) + { + //printf("###Freeing some space\n"); + uint16_t free_buffer_from_size = _gadget_bridge_internals.buffer_content_size - GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD; + _parser_free_buffer(free_buffer_from_size); + _gadget_bridge_internals.gadget_bridge_parser_fsm = GADGET_BRIDGE_PARSER_FSM_NEW_MESSAGE; + } + + return to_return; +} + +#ifndef CASE_RETURN_STR +#define CASE_RETURN_STR(const) case const: return #const; +#endif + +const char *gadget_bridge_parser_code_2_str(gadget_bridge_parser_code_e parser_code) +{ + switch(parser_code) + { + CASE_RETURN_STR(GADGET_BRIDGE_PARSER_CODE_OK) + CASE_RETURN_STR(GADGET_BRIDGE_PARSER_CODE_PARSING) + CASE_RETURN_STR(GADGET_BRIDGE_PARSER_CODE_BUFFER_FULL) + CASE_RETURN_STR(GADGET_BRIDGE_PARSER_CODE_DATA_TOO_LONG) + default: + return "Unknown parser code"; + } +} + +const char *gadget_bridge_event_type_2_str(gadget_bridge_event_type_e event_type) +{ + switch(event_type) + { + CASE_RETURN_STR(GADGET_BRIDGE_EVENT_TYPE_NONE) + CASE_RETURN_STR(GADGET_BRIDGE_EVENT_TYPE_SET_TIME) + CASE_RETURN_STR(GADGET_BRIDGE_EVENT_TYPE_NOTIFY) + CASE_RETURN_STR(GADGET_BRIDGE_EVENT_TYPE_CALL) + CASE_RETURN_STR(GADGET_BRIDGE_EVENT_TYPE_WEATHER) + CASE_RETURN_STR(GADGET_BRIDGE_EVENT_TYPE_FIND) + CASE_RETURN_STR(GADGET_BRIDGE_EVENT_TYPE_ACT) + CASE_RETURN_STR(GADGET_BRIDGE_EVENT_TYPE_MUSIC_INFO) + CASE_RETURN_STR(GADGET_BRIDGE_EVENT_TYPE_MUSIC_STATE) + default: + return "Unknown event type"; + } +} + +const char *gadget_bridge_notification_type_2_str(gadget_bridge_notification_type_e notification_type) +{ + switch(notification_type) + { + CASE_RETURN_STR(GADGET_BRIDGE_NOTIFICATION_TYPE_SMS) + CASE_RETURN_STR(GADGET_BRIDGE_NOTIFICATION_TYPE_EMAIL) + CASE_RETURN_STR(GADGET_BRIDGE_NOTIFICATION_TYPE_UNKNOWN) + default: + return "Unknown notification type"; + } +} + +const char *gadget_bridge_music_state_2_str(gadget_bridge_music_state_e music_state) +{ + switch(music_state) + { + CASE_RETURN_STR(GADGET_BRIDGE_MUSIC_STATE_PAUSE) + CASE_RETURN_STR(GADGET_BRIDGE_MUSIC_STATE_PLAY) + CASE_RETURN_STR(GADGET_BRIDGE_MUSIC_STATE_UNKNOWN) + default: + return "Unknown music state"; + } +} + +void gadget_bridge_parser_debug(void) +{ + //printf("Buffer size : %u\nBuffer content : #%s#\n", _gadget_bridge_internals.buffer_content_size, _gadget_bridge_internals.buffer); +} + +/* Internal function declaration */ +static const char *_gadget_bridge_toast_type_2_str(gadget_bridge_toast_type_e toast_type) +{ + switch(toast_type) + { + case GADGET_BRIDGE_TOAST_TYPE_ERROR: + return "error"; + case GADGET_BRIDGE_TOAST_TYPE_WARN: + return "warn"; + case GADGET_BRIDGE_TOAST_TYPE_INFO: + default: + return "info"; + } +} + +static const char *_gadget_bridge_music_control_2_str(gadget_bridge_music_control_e music_control) +{ + switch(music_control) + { + case GADGET_BRIDGE_MUSIC_CONTROL_PLAY: + return "play"; + case GADGET_BRIDGE_MUSIC_CONTROL_PLAYPAUSE: + return "playpause"; + case GADGET_BRIDGE_MUSIC_CONTROL_NEXT: + return "next"; + case GADGET_BRIDGE_MUSIC_CONTROL_PREVIOUS: + return "previous"; + case GADGET_BRIDGE_MUSIC_CONTROL_VOLUMEUP: + return "volumeup"; + case GADGET_BRIDGE_MUSIC_CONTROL_VOLUMEDOWN: + return "volumedown"; + case GADGET_BRIDGE_MUSIC_CONTROL_FORWARD: + return "forward"; + case GADGET_BRIDGE_MUSIC_CONTROL_REWIND: + return "rewind"; + case GADGET_BRIDGE_MUSIC_CONTROL_PAUSE: + default : + return "pause"; + } +} + +static const char *_gadget_bridge_http_request_method_2_str(gadget_bridge_http_request_method_e http_request_method) +{ + switch(http_request_method) + { + case GADGET_BRIDGE_HTTP_REQUEST_POST: + return "post"; + case GADGET_BRIDGE_HTTP_REQUEST_HEAD: + return "head"; + case GADGET_BRIDGE_HTTP_REQUEST_PUT: + return "put"; + case GADGET_BRIDGE_HTTP_REQUEST_PATCH: + return "patch"; + case GADGET_BRIDGE_HTTP_REQUEST_DELETE: + return "delete"; + case GADGET_BRIDGE_HTTP_REQUEST_GET: + default: + return "get"; + } +} + +static bool _parser_extract_time(char *start, char *end) +{ + *end = '\0'; + char *is_valid = NULL; + _unix_timestamp = strtoul(start, &is_valid, 10); + + // We set the type of the data and its content + _gadget_bridge_internals.event_data.event_type = GADGET_BRIDGE_EVENT_TYPE_SET_TIME; + // Turn the timestamp into a date and time + _gadget_bridge_internals.event_data.time.gm_time = *gmtime(&_unix_timestamp); + return *is_valid == '\0'; +} + +static bool _parser_extract_handle(char *start, char *end) +{ + *end = '\0'; + char *is_valid = NULL; + uint32_t handle = strtoul(start, &is_valid, 10); + + // We set the type of the data and its content + _gadget_bridge_internals.event_data.notification.handle = handle; + //printf("###Extracted handle : %u\n", handle); + + return *is_valid == '\0'; +} + +static bool _parser_extract_uint8_t(char *start, char *end, uint8_t *data) +{ + *end = '\0'; + char *is_valid = NULL; + *data = strtoul(start, &is_valid, 10); + + //printf("###Extracted uint8_t : %u\n", *data); + + return *is_valid == '\0'; +} + +static bool _parser_extract_float(char *start, char *end, float *data) +{ + *end = '\0'; + char *is_valid = NULL; + *data = strtof(start, &is_valid); + + //printf("###Extracted float : %.2f\n", *data); + + return *is_valid == '\0'; +} + +static bool _parser_extract_uint16_t(char *start, char *end, uint16_t *data) +{ + *end = '\0'; + char *is_valid = NULL; + *data = strtoul(start, &is_valid, 10); + + //printf("###Extracted uint16_t : %u\n", *data); + + return *is_valid == '\0'; +} + +/*static void _parser_extract_body(char *start, char *end) +{ + *end = '\0'; + _gadget_bridge_internals.event_data.sms.body = strdup(start); + _gadget_bridge_internals.event_data.event_type = GADGET_BRIDGE_EVENT_TYPE_SMS; + + printf("###Extracted body : %s\n", _gadget_bridge_internals.event_data.sms.body); +} + +static void _parser_extract_sender(char *start, char *end) +{ + *end = '\0'; + _gadget_bridge_internals.event_data.sms.contact = strdup(start); + + printf("###Extracted sender : %s\n", _gadget_bridge_internals.event_data.sms.contact); +} + +static void _parser_extract_tel(char *start, char *end) +{ + *end = '\0'; + _gadget_bridge_internals.event_data.sms.phone_number = strdup(start); + + printf("###Extracted tel : %s\n", _gadget_bridge_internals.event_data.sms.phone_number); +}*/ + +static void _parser_extract_src(char *start, char *end) +{ + *end = '\0'; + if(strcmp(start, "Messages") == 0) + _gadget_bridge_internals.event_data.notification.notification_type = GADGET_BRIDGE_NOTIFICATION_TYPE_SMS; + else if(strcmp(start, "E-mail") == 0) + _gadget_bridge_internals.event_data.notification.notification_type = GADGET_BRIDGE_NOTIFICATION_TYPE_EMAIL; + else + _gadget_bridge_internals.event_data.notification.notification_type = GADGET_BRIDGE_NOTIFICATION_TYPE_UNKNOWN; + + //printf("###Source is : %s\n", start); +} + +static void _parser_extract_char_str(char *start, char *end, char **data) +{ + *end = '\0'; + *data = strdup(start); + + //printf("###Extracted char str : #%s#\n", *data); +} + +static void _parser_extract_cmd(char *start, char *end) +{ + *end = '\0'; + + if(strcmp(start, "incoming") == 0) + _gadget_bridge_internals.event_data.call.call_action = GADGET_BRIDGE_CALL_ACTION_INCOMING; + else if(strcmp(start, "end") == 0) + _gadget_bridge_internals.event_data.call.call_action = GADGET_BRIDGE_CALL_ACTION_END; + else + _gadget_bridge_internals.event_data.call.call_action = GADGET_BRIDGE_CALL_ACTION_UNKNOWN; + + //printf("###Commande is : %s\n", start); +} + +static void _parser_extract_state(char *start, char *end) +{ + *end = '\0'; + + if(strcmp(start, "play") == 0) + _gadget_bridge_internals.event_data.music_state.music_state = GADGET_BRIDGE_MUSIC_STATE_PLAY; + else if(strcmp(start, "pause") == 0) + _gadget_bridge_internals.event_data.music_state.music_state = GADGET_BRIDGE_MUSIC_STATE_PAUSE; + else + _gadget_bridge_internals.event_data.music_state.music_state = GADGET_BRIDGE_MUSIC_STATE_UNKNOWN; + + //printf("###State is : %s\n", start); +} + +static bool _parser_extract_bool(char *start, char *end, bool *data) +{ + *end = '\0'; + if(strcmp(start, "true") == 0) + *data = true; + else if(strcmp(start, "false") == 0) + *data = false; + else + return false; + + //printf("###Extracted bool : %s\n", *data ? "true" : "false"); + return true; +} + +static void _parser_free_buffer(uint16_t length) +{ + length = length > _gadget_bridge_internals.buffer_content_size ? _gadget_bridge_internals.buffer_content_size : length; + + memmove(_gadget_bridge_internals.buffer, _gadget_bridge_internals.buffer + length, _gadget_bridge_internals.buffer_content_size - length + 1); // +1 to include \0 + _gadget_bridge_internals.buffer_content_size -= length; +} + +static void _free_event_data(void) +{ + switch(_gadget_bridge_internals.event_data.event_type) + { + case GADGET_BRIDGE_EVENT_TYPE_NOTIFY: + tls_mem_free(_gadget_bridge_internals.event_data.notification.title); + _gadget_bridge_internals.event_data.notification.title = NULL; + tls_mem_free(_gadget_bridge_internals.event_data.notification.body); + _gadget_bridge_internals.event_data.notification.body = NULL; + break; + case GADGET_BRIDGE_EVENT_TYPE_CALL: + tls_mem_free(_gadget_bridge_internals.event_data.call.phone_number); + _gadget_bridge_internals.event_data.call.phone_number = NULL; + tls_mem_free(_gadget_bridge_internals.event_data.call.contact); + _gadget_bridge_internals.event_data.call.contact = NULL; + break; + case GADGET_BRIDGE_EVENT_TYPE_WEATHER: + tls_mem_free(_gadget_bridge_internals.event_data.weather.location); + _gadget_bridge_internals.event_data.weather.location = NULL; + tls_mem_free(_gadget_bridge_internals.event_data.weather.weather_desc); + _gadget_bridge_internals.event_data.weather.weather_desc = NULL; + break; + case GADGET_BRIDGE_EVENT_TYPE_MUSIC_INFO: + tls_mem_free(_gadget_bridge_internals.event_data.music_info.artist); + _gadget_bridge_internals.event_data.music_info.artist = NULL; + tls_mem_free(_gadget_bridge_internals.event_data.music_info.track); + _gadget_bridge_internals.event_data.music_info.track = NULL; + break; + default: + break; + } +} diff --git a/src/W800_SDK_v1.00.10/app/ble/gadget_bridge.h b/src/W800_SDK_v1.00.10/app/ble/gadget_bridge.h new file mode 100644 index 0000000..cc45711 --- /dev/null +++ b/src/W800_SDK_v1.00.10/app/ble/gadget_bridge.h @@ -0,0 +1,321 @@ +/** + * @file gadget_bridge.h + * @author Anatole SCHRAMM-HENRY + * @brief Header file exposing the API used to communicate/interact with the GadgetBridge Android application + * over BLE. + * @version 0.1 + * @date 2023-04-04 + * + * @copyright MIT + * + */ + +#ifndef GADGET_BRIDGE_H +#define GADGET_BRIDGE_H + +#include "wm_type_def.h" +#include + +/** + * @brief GADGET_BRIDGE_PARSER_BUFFER_SIZE allows to set the size of the buffer + * which is internally used by the parser to do it's job. + * + */ +#define GADGET_BRIDGE_PARSER_BUFFER_SIZE (300) + +/** + * @brief GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD permits to set a size threshold used to free up + * some space in the parser's internal buffer when the threshold is reached. + * This ensures that we can keep on feeding new data and not get stuck. + * + */ +#define GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD (100) + +/** + * @brief GADGET_BRIDGE_PARSER_MAX_BODY_SIZE defines the max body size that will be saved in the event_data + * structure when parsing the body of a notification. + * + */ +#define GADGET_BRIDGE_PARSER_MAX_BODY_SIZE (200) + +/** + * @brief GADGET_BRIDGE_PARSER_MAX_TITLE_SIZE defines the max title size that will be saved in the event_data + * structure when parsing the title of a notification. + * + */ +#define GADGET_BRIDGE_PARSER_MAX_TITLE_SIZE (100) + +typedef enum gadget_bridge_toast_type +{ + GADGET_BRIDGE_TOAST_TYPE_INFO = 0, + GADGET_BRIDGE_TOAST_TYPE_WARN, + GADGET_BRIDGE_TOAST_TYPE_ERROR, +} gadget_bridge_toast_type_e; + +typedef enum gadget_bridge_music_control +{ + GADGET_BRIDGE_MUSIC_CONTROL_PLAY = 0, + GADGET_BRIDGE_MUSIC_CONTROL_PAUSE, + GADGET_BRIDGE_MUSIC_CONTROL_PLAYPAUSE, + GADGET_BRIDGE_MUSIC_CONTROL_NEXT, + GADGET_BRIDGE_MUSIC_CONTROL_PREVIOUS, + GADGET_BRIDGE_MUSIC_CONTROL_VOLUMEUP, + GADGET_BRIDGE_MUSIC_CONTROL_VOLUMEDOWN, + GADGET_BRIDGE_MUSIC_CONTROL_FORWARD, + GADGET_BRIDGE_MUSIC_CONTROL_REWIND, +} gadget_bridge_music_control_e; + +typedef enum gadget_bridge_call_action +{ + GADGET_BRIDGE_CALL_ACTION_ACCEPT = 0, + GADGET_BRIDGE_CALL_ACTION_END, + GADGET_BRIDGE_CALL_ACTION_INCOMING, + GADGET_BRIDGE_CALL_ACTION_OUTGOING, + GADGET_BRIDGE_CALL_ACTION_REJECT, + GADGET_BRIDGE_CALL_ACTION_START, + GADGET_BRIDGE_CALL_ACTION_IGNORE, + GADGET_BRIDGE_CALL_ACTION_UNKNOWN, +} gadget_bridge_call_action_e; + +typedef enum gadget_bridge_notification_action +{ + GADGET_BRIDGE_NOTIFICATION_ACTION_DISMISS = 0, + GADGET_BRIDGE_NOTIFICATION_ACTION_DISMISS_ALL, + GADGET_BRIDGE_NOTIFICATION_ACTION_OPEN, + GADGET_BRIDGE_NOTIFICATION_ACTION_MUTE, + GADGET_BRIDGE_NOTIFICATION_ACTION_REPLY, +} gadget_bridge_notification_action_e; + +typedef enum gadget_bridge_http_request_method +{ + GADGET_BRIDGE_HTTP_REQUEST_GET = 0, + GADGET_BRIDGE_HTTP_REQUEST_POST, + GADGET_BRIDGE_HTTP_REQUEST_HEAD, + GADGET_BRIDGE_HTTP_REQUEST_PUT, + GADGET_BRIDGE_HTTP_REQUEST_PATCH, + GADGET_BRIDGE_HTTP_REQUEST_DELETE, + +} gadget_bridge_http_request_method_e; + +typedef enum gadget_bridge_parser_code +{ + GADGET_BRIDGE_PARSER_CODE_OK = 0, + GADGET_BRIDGE_PARSER_CODE_PARSING, + GADGET_BRIDGE_PARSER_CODE_BUFFER_FULL, + GADGET_BRIDGE_PARSER_CODE_DATA_TOO_LONG, +} gadget_bridge_parser_code_e; + +typedef struct http_header +{ + const char *key; + const char *value; +} http_header_t; + +typedef enum gadget_bridge_event_type +{ + GADGET_BRIDGE_EVENT_TYPE_NONE = 0, + GADGET_BRIDGE_EVENT_TYPE_SET_TIME, + GADGET_BRIDGE_EVENT_TYPE_NOTIFY, + GADGET_BRIDGE_EVENT_TYPE_CALL, + GADGET_BRIDGE_EVENT_TYPE_WEATHER, + GADGET_BRIDGE_EVENT_TYPE_FIND, + GADGET_BRIDGE_EVENT_TYPE_ACT, + GADGET_BRIDGE_EVENT_TYPE_MUSIC_INFO, + GADGET_BRIDGE_EVENT_TYPE_MUSIC_STATE, + GADGET_BRIDGE_EVENT_TYPE_UNKNOWN, +} gadget_bridge_event_type_e; + +typedef enum gadget_bridge_notification_type +{ + GADGET_BRIDGE_NOTIFICATION_TYPE_SMS = 0, + GADGET_BRIDGE_NOTIFICATION_TYPE_EMAIL, + GADGET_BRIDGE_NOTIFICATION_TYPE_UNKNOWN, +} gadget_bridge_notification_type_e; + +typedef enum +{ + GADGET_BRIDGE_MUSIC_STATE_PAUSE = 0, + GADGET_BRIDGE_MUSIC_STATE_PLAY, + GADGET_BRIDGE_MUSIC_STATE_UNKNOWN, +} gadget_bridge_music_state_e; + +typedef struct gadget_bridge_event_data +{ + gadget_bridge_event_type_e event_type; + union + { + struct + { + struct tm gm_time; + struct tm local_time; + int8_t time_zone; + } time; + struct + { + uint32_t handle; + gadget_bridge_notification_type_e notification_type; + char *title; + char *body; + } notification; + struct + { + char *phone_number; + char *contact; + gadget_bridge_call_action_e call_action; + } call; + struct + { + float temperature_celsius; + uint8_t humidity; + char *weather_desc; + float wind_speed_kmh; + uint16_t wind_dir; + char *location; + } weather; + struct + { + bool find; + } find; + struct + { + bool heart_rate_monitor; + bool steps; + uint8_t heart_rate_interval; + } act; + struct + { + gadget_bridge_music_state_e music_state; + uint16_t position_in_seconds; + uint8_t shuffle; + uint8_t repeat; + } music_state; + struct + { + char *artist; + char *track; + uint16_t duration_in_seconds; + } music_info; + }; +} gadget_bridge_event_data_t; + +typedef void (*parser_event_callback_t)(const gadget_bridge_event_data_t *gadget_bridge_event_data); + +/** + * @brief Sends an Android toast to GadgetBridge to be displayed on the phone. + * + * @param toast_type the type of the toast (INFO, WARN or ERROR). + * @param message a string representing the message to display. + * @return true if the command was successfully sent. + * @return false otherwise. + */ +bool gadget_bridge_send_toast(gadget_bridge_toast_type_e toast_type, const char *message); + +/** + * @brief Sends up to two firmwares version to GadgetBridge. + * These are displayed in the display details section of the watch in GadgetBridge. + * + * @param fw1 a string representing the first firmware version. + * @param fw2 a string representing the second firmware version. + * @return true if the command was successfully sent. + * @return false otherwise. + */ +bool gadget_bridge_send_firmware_version(const char *fw1, const char *fw2); + +/** + * @brief Sends the current battery status to GadgetBridge. + * + * @param battery_level_in_percent the current battery level from 0 to 100%. + * @param battery_level_in_V the current battery voltage in volts (3.942 for example). + * @param is_charging a boolean which indicates if the battery is currently charging or not. + * @return true if the command was successfully sent. + * @return false otherwise. + */ +bool gadget_bridge_send_battery_status(uint8_t battery_level_in_percent, float battery_level_in_V, bool is_charging); + +/** + * @brief Sends the find phone command to GagdetBridge, this will make the phone ring and vibrate + * so that you can locate it. + * + * @param find_phone a boolean which indicates to make the phone rind and vibrate or not. + * @return true if the command was successfully sent. + * @return false otherwise. + */ +bool gadget_bridge_send_find_phone(bool find_phone); + +/** + * @brief Sends a command to control the music playback of the phone through GadgetBridge. + * + * @param music_control an enumeration value indicating the action to perform: + * PLAY, PAUSE, NEXT, PREVIOUS, VOLUMEUP etc.. + * @return true if the command was successfully sent. + * @return false otherwise. + */ +bool gadget_bridge_send_music_control(gadget_bridge_music_control_e music_control); + +bool gadget_bridge_handle_call(gadget_bridge_call_action_e call_action); + +bool gadget_bridge_handle_notification(gadget_bridge_call_action_e notification_action, uint32_t handle, const char *phone_number, const char *message); + +/** + * @brief Sends the provided activity data to GadgetBridge. This will then be displayed + * on the app in the activity section. + * + * @param heart_rate_in_bpm the current heart rate in beat per minute + * @param step_count the number of new steps since the last time the count was sent. + * @return true if the command was successfully sent. + * @return false otherwise. + */ +bool gadget_bridge_send_activity_data(uint16_t heart_rate_in_bpm, uint32_t step_count); + +/** + * @brief Tells GadgetBridge to perform an HTTP request for us. + * @note THIS DOES NOT WORK as GadgetBridge don't and will never have network permission... what a pitty ! + * + * @param id an unsigned integer representing the ID of the http request + * @param url a string representing the URL to fetch + * @param http_request_method a enumeration value specifying the http verb to use : GET, POST, PATCH etc.. + * @param http_body the body to include in the request (not implemented yet) + * @param http_headers various headers to include in the request (not implemented yet) + * @return true if the request has been successfully sent to GadgetBridge + * @return false otherwise + */ +bool gadget_bridge_send_http_request(uint32_t id, const char *url, gadget_bridge_http_request_method_e http_request_method, const char *http_body, const http_header_t *http_headers); + +//bool gadget_bridge_send_force_calendar_sync(void); + +/** + * @brief Registers a callback function used to listen for GadgetBridge events. + * + * @param parser_event_callback + */ +void gadget_bridge_parser_register_event_callback(parser_event_callback_t parser_event_callback); + +/** + * @brief Feeds new data to the GadgetBridge parser. + * + * @param data the new chunk of data to parse, it will be copied to the parser's internal buffer, + * so you can free the memory containing the string after calling the function. + * @param length the length in bytes of the new chunk. + * @return gadget_bridge_parser_code_e GADGET_BRIDGE_PARSER_CODE_OK if no error occured. + */ +gadget_bridge_parser_code_e gadget_bridge_parser_feed(const char *data, uint16_t length); + +/** + * @brief Call this function to run the parser. + * It should be safe to call if in a loop like : while((code = gadget_bridge_parser_run()) == GADGET_BRIDGE_PARSER_CODE_PARSING); + * + * @return gadget_bridge_parser_code_e the parser's execution status code. + */ +gadget_bridge_parser_code_e gadget_bridge_parser_run(void); + +const char *gadget_bridge_parser_code_2_str(gadget_bridge_parser_code_e parser_code); + +const char *gadget_bridge_event_type_2_str(gadget_bridge_event_type_e event_type); + +const char *gadget_bridge_notification_type_2_str(gadget_bridge_notification_type_e notification_type); + +const char *gadget_bridge_music_state_2_str(gadget_bridge_music_state_e music_state); + +void gadget_bridge_parser_debug(void); + +#endif //GADGET_BRIDGE_H