Compare commits

..

1 Commits

Author SHA1 Message Date
David f4fe2d48c1 Improve SubGhz proto filter handling
Build Dev Firmware / build (push) Waiting to run
2026-07-15 12:03:08 +02:00
6 changed files with 344 additions and 120 deletions
@@ -1,105 +1,68 @@
#include "../subghz_i.h"
#include <lib/subghz/subghz_protocol_registry.h>
#include <stdlib.h>
#define TAG "SubGhzSceneProtocolList"
/* ── helpers ──────────────────────────────────────────────────────────────── */
typedef struct {
SubGhz* subghz;
const char* protocol_name;
} SubGhzProtocolListItemContext;
static bool proto_filter_contains(const char* filter, const char* name) {
const char* p = filter;
while(*p) {
const char* comma = strchr(p, ',');
size_t len = comma ? (size_t)(comma - p) : strlen(p);
if(len == strlen(name) && strncmp(p, name, len) == 0) return true;
if(!comma) break;
p = comma + 1;
}
return false;
static SubGhzProtocolListItemContext* protocol_list_item_contexts = NULL;
static void subghz_scene_protocol_list_free_contexts(void) {
free(protocol_list_item_contexts);
protocol_list_item_contexts = NULL;
}
static void proto_filter_toggle(char* filter, size_t filter_size, const char* name) {
if(proto_filter_contains(filter, name)) {
/* remove it */
char tmp[256] = {0};
const char* p = filter;
bool first = true;
while(*p) {
const char* comma = strchr(p, ',');
size_t len = comma ? (size_t)(comma - p) : strlen(p);
if(!(len == strlen(name) && strncmp(p, name, len) == 0)) {
if(!first) strncat(tmp, ",", sizeof(tmp) - strlen(tmp) - 1);
strncat(tmp, p, len < sizeof(tmp) - strlen(tmp) - 1 ? len : 0);
first = false;
}
if(!comma) break;
p = comma + 1;
}
strncpy(filter, tmp, filter_size - 1);
filter[filter_size - 1] = '\0';
} else {
/* add it */
if(filter[0] != '\0') strncat(filter, ",", filter_size - strlen(filter) - 1);
strncat(filter, name, filter_size - strlen(filter) - 1);
}
}
/* ── callbacks ────────────────────────────────────────────────────────────── */
static void subghz_scene_protocol_list_item_changed(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
uint8_t value_index = variable_item_get_current_value_index(item);
SubGhzProtocolListItemContext* item_context = variable_item_get_context(item);
if(!item_context || !item_context->subghz || !item_context->protocol_name) return;
uint32_t proto_idx =
scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneProtocolList);
size_t selected =
variable_item_list_get_selected_item_index(subghz->variable_item_list);
UNUSED(proto_idx);
SubGhz* subghz = item_context->subghz;
bool should_disable = variable_item_get_current_value_index(item) == 1;
const SubGhzProtocol* protocol =
subghz_protocol_registry_get_by_index(&subghz_protocol_registry, selected);
if(!protocol) return;
bool changed = subghz_last_settings_protocol_filter_set(
subghz->last_settings, item_context->protocol_name, should_disable);
bool is_disabled = subghz_last_settings_protocol_filter_contains(
subghz->last_settings, item_context->protocol_name);
const char* name = protocol->name;
bool currently_in =
proto_filter_contains(subghz->last_settings->protocol_filter, name);
if((value_index == 1) != currently_in) {
proto_filter_toggle(
subghz->last_settings->protocol_filter,
sizeof(subghz->last_settings->protocol_filter),
name);
}
variable_item_set_current_value_text(item, value_index ? "ONLY" : "---");
subghz_last_settings_save(subghz->last_settings);
variable_item_set_current_value_index(item, is_disabled ? 1 : 0);
variable_item_set_current_value_text(item, is_disabled ? "OFF" : "ON");
if(changed) subghz_last_settings_save(subghz->last_settings);
}
/* ── scene callbacks ──────────────────────────────────────────────────────── */
void subghz_scene_protocol_list_on_enter(void* context) {
SubGhz* subghz = context;
VariableItemList* list = subghz->variable_item_list;
variable_item_list_reset(list);
subghz_scene_protocol_list_free_contexts();
size_t protocol_count = subghz_protocol_registry_count(&subghz_protocol_registry);
protocol_list_item_contexts =
malloc(sizeof(SubGhzProtocolListItemContext) * protocol_count);
furi_check(protocol_list_item_contexts);
for(size_t i = 0; i < protocol_count; i++) {
const SubGhzProtocol* protocol =
subghz_protocol_registry_get_by_index(&subghz_protocol_registry, i);
if(!protocol) continue;
protocol_list_item_contexts[i].subghz = subghz;
protocol_list_item_contexts[i].protocol_name = protocol->name;
VariableItem* item = variable_item_list_add(
list,
protocol->name,
2,
subghz_scene_protocol_list_item_changed,
subghz);
&protocol_list_item_contexts[i]);
bool enabled = proto_filter_contains(
subghz->last_settings->protocol_filter, protocol->name);
variable_item_set_current_value_index(item, enabled ? 1 : 0);
variable_item_set_current_value_text(item, enabled ? "ONLY" : "---");
bool is_disabled =
subghz_last_settings_protocol_filter_contains(subghz->last_settings, protocol->name);
variable_item_set_current_value_index(item, is_disabled ? 1 : 0);
variable_item_set_current_value_text(item, is_disabled ? "OFF" : "ON");
}
variable_item_list_set_selected_item(
@@ -132,4 +95,5 @@ void subghz_scene_protocol_list_on_exit(void* context) {
SubGhzSceneProtocolList,
variable_item_list_get_selected_item_index(subghz->variable_item_list));
variable_item_list_reset(subghz->variable_item_list);
subghz_scene_protocol_list_free_contexts();
}
@@ -105,27 +105,10 @@ static void subghz_scene_add_to_history_callback(
SubGhz* subghz = context;
// The check can be moved to /lib/subghz/receiver.c, but may result in false positives
/* Protocol name allowlist filter — if non-empty, drop anything not in the list */
if(subghz->last_settings->protocol_filter[0] != '\0') {
const char* proto_name = decoder_base->protocol->name;
const char* filter = subghz->last_settings->protocol_filter;
bool allowed = false;
/* Walk the comma-separated list */
const char* p = filter;
while(*p) {
const char* comma = strchr(p, ',');
size_t len = comma ? (size_t)(comma - p) : strlen(p);
if(len == strlen(proto_name) && strncmp(p, proto_name, len) == 0) {
allowed = true;
break;
}
if(!comma) break;
p = comma + 1;
}
if(!allowed) {
FURI_LOG_D(TAG, "%s filtered by allowlist", proto_name);
return;
}
if(subghz_last_settings_protocol_filter_contains(
subghz->last_settings, decoder_base->protocol->name)) {
FURI_LOG_D(TAG, "%s filtered by protocol OFF list", decoder_base->protocol->name);
return;
}
if((decoder_base->protocol->flag & subghz->ignore_filter) == 0) {
@@ -679,15 +679,13 @@ void subghz_scene_receiver_config_on_enter(void* context) {
1,
NULL,
subghz);
if(subghz->last_settings->protocol_filter[0] == '\0') {
variable_item_set_current_value_text(item, "All");
uint8_t protocol_filter_count =
subghz_last_settings_protocol_filter_count(subghz->last_settings);
if(protocol_filter_count == 0) {
variable_item_set_current_value_text(item, "All ON");
} else {
uint8_t count = 1;
for(const char* p = subghz->last_settings->protocol_filter; *p; p++) {
if(*p == ',') count++;
}
static char filter_count_str[8];
snprintf(filter_count_str, sizeof(filter_count_str), "%u set", count);
snprintf(filter_count_str, sizeof(filter_count_str), "%u OFF", protocol_filter_count);
variable_item_set_current_value_text(item, filter_count_str);
}
@@ -7,6 +7,7 @@
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/custom_btn_i.h>
#include <string.h>
#include <stdio.h>
#define TAG "SubGhzSceneSignalSettings"
@@ -15,6 +16,8 @@ static uint32_t counter32 = 0x0;
static uint16_t counter16 = 0x0;
static uint8_t cnt_byte_count = 0;
static uint8_t* cnt_byte_ptr = NULL;
static uint32_t counter_value = 0;
static uint32_t counter_mask = 0xffffffffUL;
static FuriString* byte_input_text;
@@ -26,12 +29,24 @@ static uint8_t submenu_called = 0;
static bool button_uses_custom_btn = false;
static uint8_t button_custom_id = SUBGHZ_CUSTOM_BTN_OK;
static bool subghz_scene_signal_settings_rebuild_save_reload(
SubGhz* subghz,
bool use_custom_btn,
uint8_t custom_btn_id);
enum {
SignalSettingsIndexCounterMode,
SignalSettingsIndexCounter,
SignalSettingsIndexButton,
};
enum {
SignalSettingsCounterStepDown,
SignalSettingsCounterStepValue,
SignalSettingsCounterStepUp,
SignalSettingsCounterStepCount,
};
#define COUNTER_MODE_COUNT 8
static const char* const counter_mode_text[COUNTER_MODE_COUNT] = {
"System",
@@ -170,6 +185,60 @@ static bool subghz_scene_signal_settings_update_uint32_field(
return flipper_format_insert_or_update_uint32(fff, key, &value, 1);
}
static uint32_t subghz_scene_signal_settings_counter_get_mask(void) {
uint8_t bits = subghz_block_generic_global.cnt_length_bit;
if((bits == 0) || (bits >= 32)) {
return 0xffffffffUL;
}
return (1UL << bits) - 1UL;
}
static void subghz_scene_signal_settings_counter_sync_byte_input(void) {
if(cnt_byte_count == 4) {
counter32 = __bswap32(counter_value);
cnt_byte_ptr = (uint8_t*)&counter32;
} else if(cnt_byte_count == 2) {
counter16 = __bswap16((uint16_t)counter_value);
cnt_byte_ptr = (uint8_t*)&counter16;
}
}
static void subghz_scene_signal_settings_counter_set_value(uint32_t value) {
counter_value = value & counter_mask;
subghz_scene_signal_settings_counter_sync_byte_input();
}
static void subghz_scene_signal_settings_counter_format(FuriString* text) {
if(cnt_byte_count == 4) {
furi_string_printf(text, "%lX", (unsigned long)counter_value);
} else {
furi_string_printf(text, "%X", (unsigned int)(counter_value & 0xffffU));
}
}
static void subghz_scene_signal_settings_counter_update_item(VariableItem* item) {
char text[9] = {0};
variable_item_set_current_value_index(item, SignalSettingsCounterStepValue);
if(cnt_byte_count == 4) {
snprintf(text, sizeof(text), "%lX", (unsigned long)counter_value);
} else {
snprintf(text, sizeof(text), "%X", (unsigned int)(counter_value & 0xffffU));
}
variable_item_set_current_value_text(item, text);
}
static bool subghz_scene_signal_settings_counter_save(SubGhz* subghz) {
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
if(!subghz_scene_signal_settings_update_uint32_field(fff, "Cnt", counter_value)) {
FURI_LOG_E(TAG, "Error update/insert Cnt value");
dialog_message_show_storage_error(subghz->dialogs, "Cannot save\ncounter");
return false;
}
subghz_block_generic_global_counter_override_set(counter_value);
return subghz_scene_signal_settings_rebuild_save_reload(subghz, false, SUBGHZ_CUSTOM_BTN_OK);
}
static bool subghz_scene_signal_settings_hex_digit(char c, uint8_t* value) {
if(c >= '0' && c <= '9') {
*value = c - '0';
@@ -332,6 +401,28 @@ void subghz_scene_signal_settings_counter_mode_changed(VariableItem* item) {
}
}
void subghz_scene_signal_settings_counter_changed(VariableItem* item) {
if(!cnt_byte_ptr || cnt_byte_count == 0) return;
uint8_t index = variable_item_get_current_value_index(item);
if(index == SignalSettingsCounterStepValue) {
subghz_scene_signal_settings_counter_update_item(item);
return;
}
if(index == SignalSettingsCounterStepUp) {
subghz_scene_signal_settings_counter_set_value(counter_value + 1);
} else {
subghz_scene_signal_settings_counter_set_value(counter_value - 1);
}
subghz_scene_signal_settings_counter_update_item(item);
SubGhz* subghz = variable_item_get_context(item);
furi_assert(subghz);
subghz_scene_signal_settings_counter_save(subghz);
}
void subghz_scene_signal_settings_button_changed(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item);
if(index >= BUTTON_VALUE_COUNT) index = 0;
@@ -407,6 +498,8 @@ void subghz_scene_signal_settings_on_enter(void* context) {
counter16 = 0;
cnt_byte_count = 0;
cnt_byte_ptr = NULL;
counter_value = 0;
counter_mask = 0xffffffffUL;
button = 0;
btn_byte_count = 1;
btn_byte_ptr = NULL;
@@ -508,26 +601,26 @@ void subghz_scene_signal_settings_on_enter(void* context) {
FURI_LOG_D(TAG, "Counter mode and edit not available for this protocol");
} else {
counter_not_available = false;
counter_mask = subghz_scene_signal_settings_counter_get_mask();
// Check is there byte_count more than 2 hex bytes long or not
// To show hex value we must correct revert bytes for ByteInput view with __bswapХХ
// ByteInput stores the visible hex value as big-endian bytes.
if(subghz_block_generic_global.cnt_length_bit > 16) {
counter32 = subghz_block_generic_global.current_cnt;
furi_string_printf(tmp_text, "%lX", counter32);
counter32 = __bswap32(counter32);
cnt_byte_ptr = (uint8_t*)&counter32;
cnt_byte_count = 4;
} else {
counter16 = subghz_block_generic_global.current_cnt;
furi_string_printf(tmp_text, "%X", counter16);
counter16 = __bswap16(counter16);
cnt_byte_ptr = (uint8_t*)&counter16;
cnt_byte_count = 2;
}
subghz_scene_signal_settings_counter_set_value(subghz_block_generic_global.current_cnt);
subghz_scene_signal_settings_counter_format(tmp_text);
}
item = variable_item_list_add(variable_item_list, "Edit Counter", 1, NULL, subghz);
variable_item_set_current_value_index(item, 0);
item = variable_item_list_add(
variable_item_list,
"Edit Counter",
counter_not_available ? 1 : SignalSettingsCounterStepCount,
counter_not_available ? NULL : subghz_scene_signal_settings_counter_changed,
subghz);
variable_item_set_current_value_index(
item, counter_not_available ? 0 : SignalSettingsCounterStepValue);
variable_item_set_current_value_text(item, furi_string_get_cstr(tmp_text));
variable_item_set_locked(item, (counter_not_available), "Not available\nfor this\nprotocol !");
//
@@ -590,17 +683,13 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
switch(cnt_byte_count) {
case 2:
counter16 = __bswap16(counter16);
subghz_scene_signal_settings_update_uint32_field(fff, "Cnt", counter16);
subghz_block_generic_global_counter_override_set(counter16);
subghz_scene_signal_settings_rebuild_save_reload(
subghz, false, SUBGHZ_CUSTOM_BTN_OK);
subghz_scene_signal_settings_counter_set_value(counter16);
subghz_scene_signal_settings_counter_save(subghz);
break;
case 4:
counter32 = __bswap32(counter32);
subghz_scene_signal_settings_update_uint32_field(fff, "Cnt", counter32);
subghz_block_generic_global_counter_override_set(counter32);
subghz_scene_signal_settings_rebuild_save_reload(
subghz, false, SUBGHZ_CUSTOM_BTN_OK);
subghz_scene_signal_settings_counter_set_value(counter32);
subghz_scene_signal_settings_counter_save(subghz);
break;
default:
break;
@@ -23,7 +23,7 @@
#define SUBGHZ_LAST_SETTING_FIELD_LED_AND_POWER_AMP "LedAndPowerAmp"
#define SUBGHZ_LAST_SETTING_FIELD_TX_POWER "TXPower"
#define SUBGHZ_LAST_SETTING_FIELD_CUSTOM_CAR_EMULATE "CustomCarEmulate"
#define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILTER "ProtocolFilter"
#define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILTER "ProtocolFilterOff"
SubGhzLastSettings* subghz_last_settings_alloc(void) {
SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings));
@@ -187,6 +187,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
flipper_format_rewind(fff_data_file);
}
furi_string_free(filter_str);
subghz_last_settings_protocol_filter_normalize(instance);
} while(0);
} else {
+190 -1
View File
@@ -3,6 +3,7 @@
#include <furi_hal.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <storage/storage.h>
#include <lib/subghz/types.h>
@@ -13,6 +14,7 @@
#define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET_HOPPING_THRESHOLD (-80.0f)
#define SUBGHZ_LAST_SETTINGS_PROTOCOL_FILTER_SIZE 1024
typedef struct {
uint32_t frequency;
@@ -31,9 +33,196 @@ typedef struct {
bool leds_and_amp;
uint8_t tx_power;
bool custom_car_emulate;
char protocol_filter[256]; /* comma-separated allowlist, empty = disabled */
char protocol_filter[SUBGHZ_LAST_SETTINGS_PROTOCOL_FILTER_SIZE]; /* comma-separated disabled protocols, empty = all enabled */
} SubGhzLastSettings;
static inline void subghz_last_settings_protocol_filter_next_token(
const char** cursor,
const char** token,
size_t* token_len) {
const char* start = *cursor;
while((*start == ',') || (*start == ' ') || (*start == '\t')) {
start++;
}
const char* end = start;
while((*end != '\0') && (*end != ',')) {
end++;
}
const char* trim_end = end;
while((trim_end > start) && ((trim_end[-1] == ' ') || (trim_end[-1] == '\t'))) {
trim_end--;
}
*token = start;
*token_len = (size_t)(trim_end - start);
*cursor = (*end == ',') ? end + 1 : end;
}
static inline bool subghz_last_settings_protocol_filter_token_matches(
const char* token,
size_t token_len,
const char* name,
size_t name_len) {
return (token_len == name_len) && (strncmp(token, name, token_len) == 0);
}
static inline bool subghz_last_settings_protocol_filter_contains_token(
const char* filter,
const char* token,
size_t token_len) {
const char* cursor = filter;
const char* current = NULL;
size_t current_len = 0;
while(*cursor != '\0') {
subghz_last_settings_protocol_filter_next_token(&cursor, &current, &current_len);
if((current_len != 0) &&
subghz_last_settings_protocol_filter_token_matches(
current, current_len, token, token_len)) {
return true;
}
}
return false;
}
static inline bool subghz_last_settings_protocol_filter_append_token(
char* filter,
size_t filter_size,
const char* token,
size_t token_len) {
if(token_len == 0) return true;
size_t filter_len = strlen(filter);
size_t separator_len = filter_len == 0 ? 0 : 1;
if((filter_len + separator_len + token_len) >= filter_size) {
return false;
}
if(separator_len != 0) {
filter[filter_len++] = ',';
}
memcpy(&filter[filter_len], token, token_len);
filter[filter_len + token_len] = '\0';
return true;
}
static inline bool subghz_last_settings_protocol_filter_contains(
const SubGhzLastSettings* instance,
const char* protocol) {
if((instance == NULL) || (protocol == NULL) || (protocol[0] == '\0')) return false;
return subghz_last_settings_protocol_filter_contains_token(
instance->protocol_filter, protocol, strlen(protocol));
}
static inline bool subghz_last_settings_protocol_filter_normalize(
SubGhzLastSettings* instance) {
if(instance == NULL) return false;
char normalized[SUBGHZ_LAST_SETTINGS_PROTOCOL_FILTER_SIZE] = {0};
const char* cursor = instance->protocol_filter;
const char* token = NULL;
size_t token_len = 0;
while(*cursor != '\0') {
subghz_last_settings_protocol_filter_next_token(&cursor, &token, &token_len);
if((token_len == 0) ||
subghz_last_settings_protocol_filter_contains_token(normalized, token, token_len)) {
continue;
}
if(!subghz_last_settings_protocol_filter_append_token(
normalized, sizeof(normalized), token, token_len)) {
return false;
}
}
bool changed = strcmp(instance->protocol_filter, normalized) != 0;
if(changed) {
memcpy(instance->protocol_filter, normalized, sizeof(instance->protocol_filter));
}
return changed;
}
static inline bool subghz_last_settings_protocol_filter_set(
SubGhzLastSettings* instance,
const char* protocol,
bool disabled) {
if((instance == NULL) || (protocol == NULL) || (protocol[0] == '\0')) return false;
char updated[SUBGHZ_LAST_SETTINGS_PROTOCOL_FILTER_SIZE] = {0};
const char* cursor = instance->protocol_filter;
const char* token = NULL;
size_t token_len = 0;
const size_t protocol_len = strlen(protocol);
bool protocol_written = false;
while(*cursor != '\0') {
subghz_last_settings_protocol_filter_next_token(&cursor, &token, &token_len);
if(token_len == 0) continue;
bool is_target = subghz_last_settings_protocol_filter_token_matches(
token, token_len, protocol, protocol_len);
if(is_target) {
if(disabled && !protocol_written) {
if(!subghz_last_settings_protocol_filter_append_token(
updated, sizeof(updated), protocol, protocol_len)) {
return false;
}
protocol_written = true;
}
continue;
}
if(!subghz_last_settings_protocol_filter_contains_token(updated, token, token_len)) {
if(!subghz_last_settings_protocol_filter_append_token(
updated, sizeof(updated), token, token_len)) {
return false;
}
}
}
if(disabled && !protocol_written) {
if(!subghz_last_settings_protocol_filter_append_token(
updated, sizeof(updated), protocol, protocol_len)) {
return false;
}
}
bool changed = strcmp(instance->protocol_filter, updated) != 0;
if(changed) {
memcpy(instance->protocol_filter, updated, sizeof(instance->protocol_filter));
}
return changed;
}
static inline uint8_t subghz_last_settings_protocol_filter_count(
const SubGhzLastSettings* instance) {
if(instance == NULL) return 0;
char seen[SUBGHZ_LAST_SETTINGS_PROTOCOL_FILTER_SIZE] = {0};
const char* cursor = instance->protocol_filter;
const char* token = NULL;
size_t token_len = 0;
uint8_t count = 0;
while(*cursor != '\0') {
subghz_last_settings_protocol_filter_next_token(&cursor, &token, &token_len);
if((token_len == 0) ||
subghz_last_settings_protocol_filter_contains_token(seen, token, token_len)) {
continue;
}
if(!subghz_last_settings_protocol_filter_append_token(seen, sizeof(seen), token, token_len)) {
break;
}
count++;
}
return count;
}
SubGhzLastSettings* subghz_last_settings_alloc(void);
void subghz_last_settings_free(SubGhzLastSettings* instance);