Files
Momentum-Firmware/applications/main/subghz/subghz_history.c
Willy-JL 0e8703a7a1 Fix many SubGhz history bugs
No "history full" if "delete old signals"
Delete more old signals if history full
Fix visual offset bug with "delete old signals"
Fix led and sound for full history / not
"Delete old signals" in decode raw too
2024-01-27 19:12:59 +00:00

374 lines
13 KiB
C

#include "subghz_history.h"
#include <lib/subghz/receiver.h>
#include <rpc/rpc.h>
#include <furi.h>
#define SUBGHZ_HISTORY_MAX 65535 // uint16_t index max, ram limit below
#define SUBGHZ_HISTORY_FREE_HEAP (10240 * (3 - MIN(rpc_get_sessions_count(instance->rpc), 2U)))
#define TAG "SubGhzHistory"
typedef struct {
FuriString* item_str;
FlipperFormat* flipper_string;
uint8_t type;
SubGhzRadioPreset* preset;
FuriHalRtcDateTime datetime;
uint32_t hash_data;
uint16_t repeats;
float latitude;
float longitude;
} SubGhzHistoryItem;
ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST)
#define M_OPL_SubGhzHistoryItemArray_t() ARRAY_OPLIST(SubGhzHistoryItemArray, M_POD_OPLIST)
typedef struct {
SubGhzHistoryItemArray_t data;
} SubGhzHistoryStruct;
struct SubGhzHistory {
uint32_t last_update_timestamp;
uint16_t last_index_write;
uint32_t code_last_hash_data;
FuriString* tmp_string;
SubGhzHistoryStruct* history;
Rpc* rpc;
};
SubGhzHistory* subghz_history_alloc(void) {
SubGhzHistory* instance = malloc(sizeof(SubGhzHistory));
instance->tmp_string = furi_string_alloc();
instance->history = malloc(sizeof(SubGhzHistoryStruct));
SubGhzHistoryItemArray_init(instance->history->data);
instance->rpc = furi_record_open(RECORD_RPC);
return instance;
}
void subghz_history_free(SubGhzHistory* instance) {
furi_assert(instance);
furi_string_free(instance->tmp_string);
for
M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) {
furi_string_free(item->item_str);
furi_string_free(item->preset->name);
free(item->preset);
flipper_format_free(item->flipper_string);
item->type = 0;
}
SubGhzHistoryItemArray_clear(instance->history->data);
free(instance->history);
furi_record_close(RECORD_RPC);
free(instance);
}
uint32_t subghz_history_get_hash_data(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
return item->hash_data;
}
uint16_t subghz_history_get_repeats(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
return item->repeats;
}
uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
return item->preset->frequency;
}
SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
return item->preset;
}
const char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
return furi_string_get_cstr(item->preset->name);
}
float subghz_history_get_latitude(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
return item->latitude;
}
float subghz_history_get_longitude(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
return item->longitude;
}
void subghz_history_reset(SubGhzHistory* instance) {
furi_assert(instance);
furi_string_reset(instance->tmp_string);
for
M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) {
furi_string_free(item->item_str);
furi_string_free(item->preset->name);
free(item->preset);
flipper_format_free(item->flipper_string);
item->type = 0;
}
SubGhzHistoryItemArray_reset(instance->history->data);
instance->last_index_write = 0;
instance->code_last_hash_data = 0;
}
void subghz_history_delete_item(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
if(idx < SubGhzHistoryItemArray_size(instance->history->data)) {
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
furi_string_free(item->item_str);
furi_string_free(item->preset->name);
free(item->preset);
flipper_format_free(item->flipper_string);
item->type = 0;
SubGhzHistoryItemArray_remove_v(instance->history->data, idx, idx + 1);
instance->last_index_write--;
}
}
uint16_t subghz_history_get_item(SubGhzHistory* instance) {
furi_assert(instance);
return instance->last_index_write;
}
uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
return item->type;
}
const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
if(!item || !item->flipper_string) {
FURI_LOG_E(TAG, "Missing Item");
furi_string_reset(instance->tmp_string);
return furi_string_get_cstr(instance->tmp_string);
}
flipper_format_rewind(item->flipper_string);
if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) {
FURI_LOG_E(TAG, "Missing Protocol");
furi_string_reset(instance->tmp_string);
}
return furi_string_get_cstr(instance->tmp_string);
}
FuriHalRtcDateTime subghz_history_get_datetime(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
if(item) {
return item->datetime;
} else {
return (FuriHalRtcDateTime){};
}
}
FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
if(item->flipper_string) {
return item->flipper_string;
} else {
return NULL;
}
}
bool subghz_history_get_text_space_left(
SubGhzHistory* instance,
FuriString* output,
uint8_t sats,
bool ignore_full) {
furi_assert(instance);
if(!ignore_full) {
if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) {
if(output != NULL) furi_string_printf(output, " Memory is FULL");
return true;
}
if(instance->last_index_write == SUBGHZ_HISTORY_MAX) {
if(output != NULL) furi_string_printf(output, " History is FULL");
return true;
}
}
if(output != NULL) {
if(sats == 0) {
furi_string_printf(output, "%02u", instance->last_index_write);
return false;
} else {
FuriHalRtcDateTime datetime;
furi_hal_rtc_get_datetime(&datetime);
if(furi_hal_rtc_datetime_to_timestamp(&datetime) % 2) {
furi_string_printf(output, "%02u", instance->last_index_write);
} else {
furi_string_printf(output, "%d sats", sats);
}
}
}
return false;
}
uint16_t subghz_history_get_last_index(SubGhzHistory* instance) {
return instance->last_index_write;
}
void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) {
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
furi_string_set(output, item->item_str);
}
void subghz_history_get_time_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) {
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
FuriHalRtcDateTime* t = &item->datetime;
furi_string_printf(output, "%.2d:%.2d:%.2d ", t->hour, t->minute, t->second);
}
bool subghz_history_add_to_history(
SubGhzHistory* instance,
void* context,
SubGhzRadioPreset* preset) {
furi_assert(instance);
furi_assert(context);
if(subghz_history_full(instance)) return false;
SubGhzProtocolDecoderBase* decoder_base = context;
uint32_t hash_data = subghz_protocol_decoder_base_get_hash_data_long(decoder_base);
if((instance->code_last_hash_data == hash_data) &&
((furi_get_tick() - instance->last_update_timestamp) < 500)) {
instance->last_update_timestamp = furi_get_tick();
return false;
}
uint16_t repeats = 0;
SubGhzHistoryItemArray_it_t it;
SubGhzHistoryItemArray_it_last(it, instance->history->data);
while(!SubGhzHistoryItemArray_end_p(it)) {
SubGhzHistoryItem* search = SubGhzHistoryItemArray_ref(it);
if(search->hash_data == hash_data) {
repeats = search->repeats + 1;
break;
}
SubGhzHistoryItemArray_previous(it);
}
instance->code_last_hash_data = hash_data;
instance->last_update_timestamp = furi_get_tick();
FuriString* text = furi_string_alloc();
SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data);
item->preset = malloc(sizeof(SubGhzRadioPreset));
item->type = decoder_base->protocol->type;
item->preset->frequency = preset->frequency;
item->preset->name = furi_string_alloc();
furi_string_set(item->preset->name, preset->name);
item->preset->data = preset->data;
item->preset->data_size = preset->data_size;
furi_hal_rtc_get_datetime(&item->datetime);
item->hash_data = hash_data;
item->repeats = repeats;
item->latitude = preset->latitude;
item->longitude = preset->longitude;
item->item_str = furi_string_alloc();
item->flipper_string = flipper_format_string_alloc();
subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset);
do {
if(!flipper_format_rewind(item->flipper_string)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
if(!strcmp(furi_string_get_cstr(instance->tmp_string), "KeeLoq")) {
furi_string_set(instance->tmp_string, "KL ");
if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
furi_string_cat(instance->tmp_string, text);
} else if(!strcmp(furi_string_get_cstr(instance->tmp_string), "Star Line")) {
furi_string_set(instance->tmp_string, "SL ");
if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
furi_string_cat(instance->tmp_string, text);
}
if(!flipper_format_rewind(item->flipper_string)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
uint8_t key_data[sizeof(uint64_t)] = {0};
if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_D(TAG, "No Key");
}
uint64_t data = 0;
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
data = (data << 8) | key_data[i];
}
if(data != 0) {
if(!(uint32_t)(data >> 32)) {
furi_string_printf(
item->item_str,
"%s %lX",
furi_string_get_cstr(instance->tmp_string),
(uint32_t)(data & 0xFFFFFFFF));
} else {
furi_string_printf(
item->item_str,
"%s %lX%08lX",
furi_string_get_cstr(instance->tmp_string),
(uint32_t)(data >> 32),
(uint32_t)(data & 0xFFFFFFFF));
}
} else {
furi_string_printf(item->item_str, "%s", furi_string_get_cstr(instance->tmp_string));
}
} while(false);
furi_string_free(text);
instance->last_index_write++;
return true;
}
void subghz_history_remove_duplicates(SubGhzHistory* instance) {
furi_assert(instance);
SubGhzHistoryItemArray_it_t it;
SubGhzHistoryItemArray_it_last(it, instance->history->data);
while(!SubGhzHistoryItemArray_end_p(it)) {
SubGhzHistoryItem* i = SubGhzHistoryItemArray_ref(it);
SubGhzHistoryItemArray_it_t jt;
SubGhzHistoryItemArray_it_set(jt, it);
SubGhzHistoryItemArray_previous(jt);
while(!SubGhzHistoryItemArray_end_p(jt)) {
SubGhzHistoryItem* j = SubGhzHistoryItemArray_ref(jt);
if(j->hash_data == i->hash_data) {
subghz_history_delete_item(instance, jt->index);
}
SubGhzHistoryItemArray_previous(jt);
}
SubGhzHistoryItemArray_previous(it);
}
}
bool subghz_history_full(SubGhzHistory* instance) {
if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return true;
if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return true;
return false;
}