diff --git a/app/ble/gadget_bridge.c b/app/ble/gadget_bridge.c index c790d47..56393a2 100644 --- a/app/ble/gadget_bridge.c +++ b/app/ble/gadget_bridge.c @@ -11,11 +11,74 @@ */ #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_FOUND_ID_SRC, + GADGET_BRIDGE_PARSER_FSM_FOUND_SRC_BODY, + + 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_HRM, + GADGET_BRIDGE_PARSER_FSM_FOUND_STP, + GADGET_BRIDGE_PARSER_FSM_FOUND_HRM_INT, + + 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 */ -const char *_gadget_bridge_toast_type_2_str(gadget_bridge_toast_type_e toast_type); +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 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) { @@ -25,15 +88,777 @@ bool gadget_bridge_send_toast(gadget_bridge_toast_type_e toast_type, const char 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 *)"\",\"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", 3); + 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; +} + +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 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; + } + // If we did not find the rest but found GB again, the 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:")) + && (end = strstr(_gadget_bridge_internals.buffer, ",body"))) + { + //printf("###Found BODY\n"); + + _parser_extract_char_str(start + 7, 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; + 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_SRC_BODY: + if((start = strstr(_gadget_bridge_internals.buffer, "body:")) + && (end = strstr(_gadget_bridge_internals.buffer, "})"))) + { + //printf("###NOTIFICATION Type one done\n"); + + _parser_extract_char_str(start + 6, 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 + } + 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_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 + } + 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 + } + 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 + } + 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 + } + 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) + 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"; + } +} + +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 */ -const char *_gadget_bridge_toast_type_2_str(gadget_bridge_toast_type_e toast_type) +static const char *_gadget_bridge_toast_type_2_str(gadget_bridge_toast_type_e toast_type) { switch(toast_type) { @@ -45,4 +870,198 @@ const char *_gadget_bridge_toast_type_2_str(gadget_bridge_toast_type_e toast_typ default: return "info"; } -} \ No newline at end of file +} + +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 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 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; + default: + break; + } +} diff --git a/app/ble/gadget_bridge.h b/app/ble/gadget_bridge.h index 6f70468..0020186 100644 --- a/app/ble/gadget_bridge.h +++ b/app/ble/gadget_bridge.h @@ -14,6 +14,15 @@ #define GADGET_BRIDGE_H #include "wm_type_def.h" +#include + +/** + * @brief Size of the internal buffer used to store incoming data + * which needs to be parsed. + * + */ +#define GADGET_BRIDGE_PARSER_BUFFER_SIZE (300) +#define GADGET_BRIDGE_PARSER_BUFFER_THRESHOLD (100) typedef enum gadget_bridge_toast_type { @@ -44,6 +53,7 @@ typedef enum gadget_bridge_call_action 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 @@ -66,17 +76,92 @@ typedef enum gadget_bridge_http_request_method } 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_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 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; + }; +} gadget_bridge_event_data_t; + +typedef void (*parser_event_callback_t)(const gadget_bridge_event_data_t *gadget_bridge_event_data); + bool gadget_bridge_send_toast(gadget_bridge_toast_type_e toast_type, const char *message); bool gadget_bridge_send_firmware_version(const char *fw1, const char *fw2); -bool gadget_bridge_send_battery_status(uint8_t battery_level_in_percent, float battery_level_in_mV, bool is_charging); +bool gadget_bridge_send_battery_status(uint8_t battery_level_in_percent, float battery_level_in_V, bool is_charging); bool gadget_bridge_send_find_phone(bool find_phone); @@ -84,10 +169,24 @@ bool gadget_bridge_send_music_control(gadget_bridge_music_control_e music_contro bool gadget_bridge_handle_call(gadget_bridge_call_action_e call_action); -bool gadget_bridge_handle_notification(gadget_bridge_call_action_e notification_action, uint64_t handle, const char *phone_number, const char *message); +bool gadget_bridge_handle_notification(gadget_bridge_call_action_e notification_action, uint32_t handle, const char *phone_number, const char *message); bool gadget_bridge_send_activity_data(uint16_t heart_rate_in_bpm, uint32_t step_count); 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); -#endif //GADGET_BRIDGE_H \ No newline at end of file +void gadget_bridge_parser_register_event_callback(parser_event_callback_t parser_event_callback); + +gadget_bridge_parser_code_e gadget_bridge_parser_feed(const char *data, uint16_t length); + +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); + +void gadget_bridge_parser_debug(void); + +#endif //GADGET_BRIDGE_H diff --git a/app/nano_shell_command.c b/app/nano_shell_command.c index b471b72..ab6aa3d 100644 --- a/app/nano_shell_command.c +++ b/app/nano_shell_command.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,6 +15,7 @@ #include "wm_bt.h" #include "ble_modem.h" #include "ble_service.h" +#include "gadget_bridge.h" extern int wm_printf(const char *fmt,...); extern u32 tls_mem_get_avail_heapsize(void); @@ -97,10 +99,63 @@ static void tls_rtc_irq_cb(void *arg) tls_rtc_timer_stop(); } +static void parser_event_cb(const gadget_bridge_event_data_t *gadget_bridge_event_data) +{ + shell_printf("----------->Event of type : %s\n", gadget_bridge_event_type_2_str(gadget_bridge_event_data->event_type)); + + switch(gadget_bridge_event_data->event_type) + { + case GADGET_BRIDGE_EVENT_TYPE_SET_TIME: + shell_printf("%d:%d:%d %d/%d/%d\n%d\n", + gadget_bridge_event_data->time.local_time.tm_hour, + gadget_bridge_event_data->time.local_time.tm_min, + gadget_bridge_event_data->time.local_time.tm_sec, + gadget_bridge_event_data->time.local_time.tm_mday, + gadget_bridge_event_data->time.local_time.tm_mon, + gadget_bridge_event_data->time.local_time.tm_year + 1900, + gadget_bridge_event_data->time.time_zone); + break; + case GADGET_BRIDGE_EVENT_TYPE_NOTIFY: + shell_printf("%u\n%s\n%s\n%s\n", + gadget_bridge_event_data->notification.handle, + gadget_bridge_notification_type_2_str(gadget_bridge_event_data->notification.notification_type), + gadget_bridge_event_data->notification.title, + gadget_bridge_event_data->notification.body); + break; + break; + case GADGET_BRIDGE_EVENT_TYPE_CALL: + shell_printf("%u\n%s\n%s\n", + gadget_bridge_event_data->call.call_action, + gadget_bridge_event_data->call.contact, + gadget_bridge_event_data->call.phone_number); + break; + case GADGET_BRIDGE_EVENT_TYPE_WEATHER: + shell_printf("%.2f\n%u\n%s\n%.2f\n%u\n%s\n", + gadget_bridge_event_data->weather.temperature_celsius, + gadget_bridge_event_data->weather.humidity, + gadget_bridge_event_data->weather.weather_desc, + gadget_bridge_event_data->weather.wind_speed_kmh, + gadget_bridge_event_data->weather.wind_dir, + gadget_bridge_event_data->weather.location); + break; + case GADGET_BRIDGE_EVENT_TYPE_FIND: + shell_printf("%d\n",gadget_bridge_event_data->find.find); + break; + case GADGET_BRIDGE_EVENT_TYPE_ACT: + shell_printf("%d\n%d\n%u\n", + gadget_bridge_event_data->act.heart_rate_monitor, + gadget_bridge_event_data->act.steps, + gadget_bridge_event_data->act.heart_rate_interval); + break; + default: + shell_printf("Unhandled\n"); + } +} + static void nus_data_rx_cb(const uint8_t *data, uint16_t length) { //shell_printf("%s, received data : "NEW_LINE"#", __FUNCTION__); - + //shell_putc('"'); for (uint16_t i = 0; i < length; i++) { if (data[i] < 32) @@ -108,6 +163,12 @@ static void nus_data_rx_cb(const uint8_t *data, uint16_t length) else shell_putc(data[i]); } + //shell_printf("\","NEW_LINE); + shell_printf("# "); + gadget_bridge_parser_feed((const char *)data, length); + gadget_bridge_parser_code_e code; + while((code = gadget_bridge_parser_run()) == GADGET_BRIDGE_PARSER_CODE_PARSING); + shell_printf("Gadget bridge parser code : %s"NEW_LINE, gadget_bridge_parser_code_2_str(code)); //shell_puts("#"NEW_LINE); } @@ -590,7 +651,10 @@ int _bluetooth(const shell_cmd_t *pcmd, int argc, char *const argv[]) shell_printf("Enabling bluetooth : %d with bt only : %d and service : %d"NEW_LINE, result, btOnly, service); if(result) + { + gadget_bridge_parser_register_event_callback(&(parser_event_cb)); ble_service_register_nus_data_rx_cb(&(nus_data_rx_cb)); + } } else if(strcmp(argv[1], "disable") == 0) { @@ -618,46 +682,89 @@ int _bluetooth(const shell_cmd_t *pcmd, int argc, char *const argv[]) { char cmd[200] = ""; bool found = false; - if(strcmp(argv[2], "toast") == 0) + bool to_return = false; + if(strcmp(argv[2], "toast") == 0 && argc == 5) { - sprintf(cmd, "{\"t\":\"info\",\"msg\":\"%s\"} \n", argv[3]); - found = true; + gadget_bridge_toast_type_e toast_type = GADGET_BRIDGE_TOAST_TYPE_INFO; + + if(strcmp(argv[3], "warn") == 0) + { + toast_type = GADGET_BRIDGE_TOAST_TYPE_WARN; + } + else if(strcmp(argv[3], "error") == 0) + { + toast_type = GADGET_BRIDGE_TOAST_TYPE_ERROR; + } + + to_return = gadget_bridge_send_toast(toast_type, argv[4]); } - else if(strcmp(argv[2], "bat") == 0) + if(strcmp(argv[2], "ver") == 0 && argc == 5) + { + to_return = gadget_bridge_send_firmware_version(argv[3], argv[4]); + } + else if(strcmp(argv[2], "bat") == 0 && argc == 6) { sprintf(cmd, "{\"t\":\"status\",\"bat\":%s,\"chg\":%s,\"volt\":%s} \n", argv[3], argv[4], argv[5]); - found = true; + uint8_t percent = atoi(argv[3]); + bool charging = atoi(argv[4]); + float volt = strtof(argv[5], NULL); + to_return = gadget_bridge_send_battery_status(percent, volt, charging); } - else if(strcmp(argv[2], "find_phone") == 0) + else if(strcmp(argv[2], "find_phone") == 0 && argc == 4) { - sprintf(cmd, "{\"t\":\"findPhone\",\"n\":%s} \n", argv[3]); - found = true; + to_return = gadget_bridge_send_find_phone(strcmp(argv[3], "true") == 0); } - else if(strcmp(argv[2], "music") == 0) + else if(strcmp(argv[2], "music") == 0 && argc == 4) { - sprintf(cmd, "{\"t\":\"music\",\"n\":\"%s\"} \n", argv[3]); - found = true; + gadget_bridge_music_control_e music_control = GADGET_BRIDGE_MUSIC_CONTROL_PAUSE; + + if(strcmp(argv[3], "pause") == 0) + music_control = GADGET_BRIDGE_MUSIC_CONTROL_PAUSE; + else if(strcmp(argv[3], "play") == 0) + music_control = GADGET_BRIDGE_MUSIC_CONTROL_PLAY; + else if(strcmp(argv[3], "playpause") == 0) + music_control = GADGET_BRIDGE_MUSIC_CONTROL_PLAYPAUSE; + else if(strcmp(argv[3], "next") == 0) + music_control = GADGET_BRIDGE_MUSIC_CONTROL_NEXT; + else if(strcmp(argv[3], "previous") == 0) + music_control = GADGET_BRIDGE_MUSIC_CONTROL_PREVIOUS; + else if(strcmp(argv[3], "volumeup") == 0) + music_control = GADGET_BRIDGE_MUSIC_CONTROL_VOLUMEUP; + else if(strcmp(argv[3], "volumedown") == 0) + music_control = GADGET_BRIDGE_MUSIC_CONTROL_VOLUMEDOWN; + else if(strcmp(argv[3], "forward") == 0) + music_control = GADGET_BRIDGE_MUSIC_CONTROL_FORWARD; + else if(strcmp(argv[3], "rewind") == 0) + music_control = GADGET_BRIDGE_MUSIC_CONTROL_REWIND; + + to_return = gadget_bridge_send_music_control(music_control); } else if(strcmp(argv[2], "notify") == 0) { sprintf(cmd, "{\"t\":\"notify\",\"n\":\"%s\",\"id\":%s,\"tel\":\"%s\",\"msg\":\"%s\"} \n", argv[3], argv[4], argv[5], argv[6]); found = true; } - else if(strcmp(argv[2], "act") == 0) + else if(strcmp(argv[2], "act") == 0 && argc == 5) { - sprintf(cmd, "{\"t\":\"act\",\"hrm\":%s,\"stp\":%s} \n", argv[3], argv[4]); - found = true; + to_return = gadget_bridge_send_activity_data(atoi(argv[3]), atoi(argv[4])); } + else + { + shell_printf("Unknown %s action, list of send_ble_notif actions :"NEW_LINE"toast info|warn|error \"msg\""NEW_LINE"bat \"XX%%\" 1|0 X.X"NEW_LINE"find_phone true|false"NEW_LINE"music play|pause|playpause|previous|next|volumeup|volumedown|forward|rewind"NEW_LINE"notify reply|dismiss_all id \"tel\" \"msg\""NEW_LINE + "act hrm steps"NEW_LINE, argv[2]); + } + + shell_printf("Sending ble ntf : %d"NEW_LINE, to_return); if(found) { shell_printf("Sending ble ntf with content : #%s# -> %s"NEW_LINE, cmd, ble_service_send_nus_data((const uint8_t *)cmd, strlen(cmd)) ? "success" : "failure"); } - else + /*else { shell_printf("Unknown %s action, list of send_ble_notif actions :"NEW_LINE"toast \"msg\""NEW_LINE"bat \"XX%%\" 1|0 X.X"NEW_LINE"find_phone true|false"NEW_LINE"music play|pause|previous|next"NEW_LINE"notify reply|dismiss_all id \"tel\" \"msg\""NEW_LINE "act hrm steps"NEW_LINE, argv[2]); - } + }*/ } else if(strcmp(argv[1], "up_conn_param") == 0) {