diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d3ce6bdf..e473509fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,11 @@ ### New changes -* PR: SubGHz bruteforcer plugin - fixes and speed-up (-2 min) (by @derskythe | PR #76) -* Fix nfc device typo - key_a_mask was used for key B -* OFW: Applications loader: do not use view dispatcher queue -* OFW: Power: Also ask charger if charge done -* OFW: Fast flash programming mode (faster firmware flash) +* PR: SubGHz new feature -> press Ok in Frequency analyzer to use frequency in Read modes (by @derskythe | PR #77) +* PR: SubGHz save last settings (frequency and modulation) (by @derskythe | PR #77) +* Plugins: SubGHz Bruteforcer - add chamberlain 9bit 300mhz, removed unnecessary checks (made it a bit faster), fixed title position & menu text +* SubGHz: Fix Add Manually item names, Add BETT protocol in add manually +* Infrared: Update assets (by @Amec0e) +* OFW: Refactor infrared brute force code +* OFW: Add formatting to DESfire data dump #### **DFU files no longer included in releases to avoid issues with wrong manual installation of assets - use .tgz file with qFlipper, or install automatically via web updater or use microSD update package** diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 3d45c0bc6..39acf5fd6 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -14,6 +14,7 @@ typedef enum { SubmenuIndexNiceFlo24bit, SubmenuIndexCAME12bit, SubmenuIndexCAME24bit, + SubmenuIndexBETT_433, SubmenuIndexCAMETwee, SubmenuIndexNeroSketch, SubmenuIndexNeroRadio, diff --git a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c index 7e3965ac9..a78fccd3f 100644 --- a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -1,5 +1,4 @@ #include "../subghz_i.h" -#include "../views/subghz_frequency_analyzer.h" #include void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* context) { @@ -18,16 +17,16 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) { bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneAnalyzerLock) { - notification_message(subghz->notifications, &sequence_set_green_255); - notification_message(subghz->notifications, &sequence_set_vibro_on); - return true; - } else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) { - notification_message(subghz->notifications, &sequence_reset_rgb); - notification_message(subghz->notifications, &sequence_reset_vibro); - return true; + if(event.type == SceneManagerEventTypeCustom && + event.event == SubGhzCustomEventViewReceiverOK) { + uint32_t frequency = + subghz_frequency_analyzer_get_frequency_to_save(subghz->subghz_frequency_analyzer); + if(frequency > 0) { + subghz->last_settings->frequency = frequency; + subghz_last_settings_save(subghz->last_settings); } + + return true; } return false; } diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c index 53bffedc8..6f04be669 100644 --- a/applications/main/subghz/scenes/subghz_scene_need_saving.c +++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c @@ -50,8 +50,8 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_preset_init( subghz, - "AM650", - subghz_setting_get_default_frequency(subghz->setting), + subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset), + subghz->last_settings->frequency, NULL, 0); scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 9075a0af4..1f0ccdc3e 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -137,8 +137,8 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { //Restore default setting subghz_preset_init( subghz, - "AM650", - subghz_setting_get_default_frequency(subghz->setting), + subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset), + subghz->last_settings->frequency, NULL, 0); if(!scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index a58777d15..4a35c5f2c 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -1,6 +1,8 @@ #include "../subghz_i.h" #include "../views/receiver.h" +#define TAG "SubGhzSceneReceiver" + const NotificationSequence subghz_sequence_rx = { &message_green_255, @@ -103,7 +105,11 @@ void subghz_scene_receiver_on_enter(void* context) { if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) { subghz_preset_init( - subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); + subghz, + subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset), + subghz->last_settings->frequency, + NULL, + 0); subghz_history_reset(subghz->txrx->history); subghz->txrx->rx_key_state = SubGhzRxKeyStateStart; } @@ -169,8 +175,8 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_preset_init( subghz, - "AM650", - subghz_setting_get_default_frequency(subghz->setting), + subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset), + subghz->last_settings->frequency, NULL, 0); scene_manager_search_and_switch_to_previous_scene( @@ -188,6 +194,8 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->txrx->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzViewIdReceiver, SubGhzCustomEventManagerSet); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); consumed = true; break; diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index d565666a1..0175cd0fd 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -2,6 +2,8 @@ #include +#define TAG "SubGhzSceneReceiverConfig" + enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexHopping, @@ -145,6 +147,8 @@ static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { (subghz_setting_get_frequency(subghz->setting, index) % 1000000) / 10000); variable_item_set_current_value_text(item, text_buf); subghz->txrx->preset->frequency = subghz_setting_get_frequency(subghz->setting, index); + subghz->last_settings->frequency = subghz->txrx->preset->frequency; + subghz_setting_set_default_frequency(subghz->setting, subghz->txrx->preset->frequency); } else { variable_item_set_current_value_index( item, subghz_setting_get_frequency_default_index(subghz->setting)); @@ -154,11 +158,13 @@ static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { static void subghz_scene_receiver_config_set_preset(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text( - item, subghz_setting_get_preset_name(subghz->setting, index)); + const char* preset_name = subghz_setting_get_preset_name(subghz->setting, index); + variable_item_set_current_value_text(item, preset_name); + subghz->last_settings->preset = index; + subghz_preset_init( subghz, - subghz_setting_get_preset_name(subghz->setting, index), + preset_name, subghz->txrx->preset->frequency, subghz_setting_get_preset_data(subghz->setting, index), subghz_setting_get_preset_data_size(subghz->setting, index)); @@ -238,6 +244,13 @@ void subghz_scene_receiver_config_on_enter(void* context) { VariableItem* item; uint8_t value_index; +#if FURI_DEBUG + FURI_LOG_D( + TAG, + "last frequency: %d, preset: %d", + subghz->last_settings->frequency, + subghz->last_settings->preset); +#endif item = variable_item_list_add( subghz->variable_item_list, "Frequency:", @@ -258,20 +271,6 @@ void subghz_scene_receiver_config_on_enter(void* context) { (subghz_setting_get_frequency(subghz->setting, value_index) % 1000000) / 10000); variable_item_set_current_value_text(item, text_buf); - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != - SubGhzCustomEventManagerSet) { - item = variable_item_list_add( - subghz->variable_item_list, - "Hopping:", - HOPPING_COUNT, - subghz_scene_receiver_config_set_hopping_running, - subghz); - value_index = subghz_scene_receiver_config_hopper_value_index( - subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, hopping_text[value_index]); - } - item = variable_item_list_add( subghz->variable_item_list, "Modulation:", @@ -286,6 +285,19 @@ void subghz_scene_receiver_config_on_enter(void* context) { if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != SubGhzCustomEventManagerSet) { + // Hopping + item = variable_item_list_add( + subghz->variable_item_list, + "Hopping:", + HOPPING_COUNT, + subghz_scene_receiver_config_set_hopping_running, + subghz); + value_index = subghz_scene_receiver_config_hopper_value_index( + subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, hopping_text[value_index]); + + // Detect Raw item = variable_item_list_add( subghz->variable_item_list, "Detect Raw:", @@ -298,10 +310,8 @@ void subghz_scene_receiver_config_on_enter(void* context) { DETECT_RAW_COUNT); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, detect_raw_text[value_index]); - } - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != - SubGhzCustomEventManagerSet) { + // RSSI item = variable_item_list_add( subghz->variable_item_list, "RSSI for Raw:", @@ -315,10 +325,8 @@ void subghz_scene_receiver_config_on_enter(void* context) { RSSI_THRESHOLD_COUNT); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, rssi_threshold_text[value_index]); - } - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != - SubGhzCustomEventManagerSet) { + // Lock keyboard variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL); variable_item_list_set_enter_callback( subghz->variable_item_list, @@ -347,6 +355,7 @@ void subghz_scene_receiver_config_on_exit(void* context) { SubGhz* subghz = context; variable_item_list_set_selected_item(subghz->variable_item_list, 0); variable_item_list_reset(subghz->variable_item_list); + subghz_last_settings_save(subghz->last_settings); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); } diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 6cb54f157..93734023d 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -69,61 +69,67 @@ void subghz_scene_set_type_on_enter(void* context) { submenu_add_item( subghz->submenu, - "Faac SLH_868", + "Faac SLH 868MHz", SubmenuIndexFaacSLH_868, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Faac SLH_433", + "Faac SLH 433MHz", SubmenuIndexFaacSLH_433, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "BFT Mitto", + "BFT Mitto 433MHz", SubmenuIndexBFT, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Princeton_433", + "Princeton 433MHz", SubmenuIndexPricenton, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Nice Flo 12bit_433", + "Nice Flo 12bit 433MHz", SubmenuIndexNiceFlo12bit, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Nice Flo 24bit_433", + "Nice Flo 24bit 433MHz", SubmenuIndexNiceFlo24bit, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "CAME 12bit_433", + "CAME 12bit 433MHz", SubmenuIndexCAME12bit, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "CAME 24bit_433", + "CAME 24bit 433MHz", SubmenuIndexCAME24bit, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Linear_300", + "BETT 433MHz", + SubmenuIndexBETT_433, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Linear 300MHz", SubmenuIndexLinear_300_00, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "CAME TWEE", + "CAME TWEE 433MHz", SubmenuIndexCAMETwee, subghz_scene_set_type_submenu_callback, subghz); @@ -133,49 +139,49 @@ void subghz_scene_set_type_on_enter(void* context) { // subghz->submenu, "Nero Radio", SubmenuIndexNeroRadio, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Gate TX_433", + "Gate TX 433MHz", SubmenuIndexGateTX, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "DoorHan_315", + "DoorHan 315MHz", SubmenuIndexDoorHan_315_00, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "DoorHan_433", + "DoorHan 433MHz", SubmenuIndexDoorHan_433_92, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "LiftMaster_315", + "Security+1.0 315MHz", SubmenuIndexLiftMaster_315_00, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "LiftMaster_390", + "Security+1.0 390MHz", SubmenuIndexLiftMaster_390_00, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Security+2.0_310", + "Security+2.0 310MHz", SubmenuIndexSecPlus_v2_310_00, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Security+2.0_315", + "Security+2.0 315MHz", SubmenuIndexSecPlus_v2_315_00, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Security+2.0_390", + "Security+2.0 390MHz", SubmenuIndexSecPlus_v2_390_00, subghz_scene_set_type_submenu_callback, subghz); @@ -247,6 +253,13 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { generated_protocol = true; } break; + case SubmenuIndexBETT_433: + key = (key & 0x0000FFF0); + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, SUBGHZ_PROTOCOL_BETT_NAME, key, 18, 433920000, "AM650")) { + generated_protocol = true; + } + break; case SubmenuIndexCAMETwee: key = (key & 0x0FFFFFF0); key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 4336bd8e6..d50f4d275 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -5,6 +5,8 @@ #include #include "subghz_i.h" +#define TAG "SubGhzApp" + bool subghz_custom_event_callback(void* context, uint32_t event) { furi_assert(context); SubGhz* subghz = context; @@ -174,13 +176,30 @@ SubGhz* subghz_alloc() { subghz->setting = subghz_setting_alloc(); subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user.txt")); + // Load last used values for Read, Read RAW, etc. or default + subghz->last_settings = subghz_last_settings_alloc(); + subghz_last_settings_load( + subghz->last_settings, subghz_setting_get_preset_count(subghz->setting)); +#if FURI_DEBUG + FURI_LOG_D( + TAG, + "last frequency: %d, preset: %d", + subghz->last_settings->frequency, + subghz->last_settings->preset); +#endif + subghz_setting_set_default_frequency(subghz->setting, subghz->last_settings->frequency); + //init Worker & Protocol & History & KeyBoard subghz->lock = SubGhzLockOff; subghz->txrx = malloc(sizeof(SubGhzTxRx)); subghz->txrx->preset = malloc(sizeof(SubGhzPresetDefinition)); string_init(subghz->txrx->preset->name); subghz_preset_init( - subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); + subghz, + subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset), + subghz->last_settings->frequency, + NULL, + 0); subghz->txrx->txrx_state = SubGhzTxRxStateSleep; subghz->txrx->hopper_state = SubGhzHopperStateOFF; @@ -287,6 +306,7 @@ void subghz_free(SubGhz* subghz) { //setting subghz_setting_free(subghz->setting); + subghz_last_settings_free(subghz->last_settings); //Worker & Protocol & History subghz_receiver_free(subghz->txrx->receiver); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 945228a45..8872d31f6 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -33,6 +33,7 @@ #include "subghz_history.h" #include "subghz_setting.h" +#include "subghz_last_settings.h" #include #include @@ -100,10 +101,10 @@ struct SubGhz { SubGhzTestPacket* subghz_test_packet; string_t error_str; SubGhzSetting* setting; + SubGhzLastSettings* last_settings; SubGhzLock lock; bool in_decoder_scene; - void* rpc_ctx; }; diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c new file mode 100644 index 000000000..b24273cf3 --- /dev/null +++ b/applications/main/subghz/subghz_last_settings.c @@ -0,0 +1,105 @@ +#include "subghz_last_settings.h" +#include + +#define TAG "SubGhzLastSettings" + +#define SUBGHZ_LAST_SETTING_FILE_TYPE "Flipper SubGhz Last Setting File" +#define SUBGHZ_LAST_SETTING_FILE_VERSION 1 +#define SUBGHZ_LAST_SETTINGS_PATH EXT_PATH("subghz/assets/last_subghz.settings") +// 1 = "AM650" +// "AM270", "AM650", "FM238", "FM476", +#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1 +#define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000 + +SubGhzLastSettings* subghz_last_settings_alloc(void) { + SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); + return instance; +} + +void subghz_last_settings_free(SubGhzLastSettings* instance) { + furi_assert(instance); + free(instance); +} + +void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count) { + furi_assert(instance); +#if FURI_DEBUG + FURI_LOG_I(TAG, "subghz_last_settings_load"); +#endif + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + + uint32_t temp_frequency = 0; + int32_t temp_preset = 0; + + if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH && + flipper_format_file_open_existing(fff_data_file, SUBGHZ_LAST_SETTINGS_PATH)) { + flipper_format_read_int32(fff_data_file, "Preset", (int32_t*)&temp_preset, 1); + flipper_format_read_uint32(fff_data_file, "Frequency", (uint32_t*)&temp_frequency, 1); + } else { + FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); + } + + if(temp_frequency == 0 || !furi_hal_subghz_is_tx_allowed(temp_frequency)) { + FURI_LOG_W(TAG, "Last used frequency not found or can't be used!"); + instance->frequency = SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY; + instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET; + } else { + instance->frequency = temp_frequency; + + if(temp_preset > (int32_t)preset_count - 1 || temp_preset < 0) { + FURI_LOG_W(TAG, "Last used preset no found"); + instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET; + } else { + instance->preset = temp_preset; + } + } + + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); +} + +bool subghz_last_settings_save(SubGhzLastSettings* instance) { + furi_assert(instance); +#if FURI_DEBUG + FURI_LOG_I(TAG, "subghz_last_settings_save"); +#endif + + bool saved = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + if(FSE_OK != storage_sd_status(storage)) { + break; + } + + // Open file + if(!flipper_format_file_open_always(file, SUBGHZ_LAST_SETTINGS_PATH)) break; + + // Write header + if(!flipper_format_write_header_cstr( + file, SUBGHZ_LAST_SETTING_FILE_TYPE, SUBGHZ_LAST_SETTING_FILE_VERSION)) + break; + + if(!flipper_format_insert_or_update_int32(file, "Preset", &instance->preset, 1)) { + break; + } + if(!flipper_format_insert_or_update_uint32(file, "Frequency", &instance->frequency, 1)) { + break; + } + saved = true; + } while(0); + + if(!saved) { + FURI_LOG_E(TAG, "Error save file %s", SUBGHZ_LAST_SETTINGS_PATH); + } + + flipper_format_file_close(file); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + + return saved; +} \ No newline at end of file diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h new file mode 100644 index 000000000..8067bb66a --- /dev/null +++ b/applications/main/subghz/subghz_last_settings.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include +#include + +typedef struct { + uint32_t frequency; + int32_t preset; +} SubGhzLastSettings; + +SubGhzLastSettings* subghz_last_settings_alloc(void); + +void subghz_last_settings_free(SubGhzLastSettings* instance); + +void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count); + +bool subghz_last_settings_save(SubGhzLastSettings* instance); \ No newline at end of file diff --git a/applications/main/subghz/subghz_setting.c b/applications/main/subghz/subghz_setting.c index b8f3794eb..2e94011cf 100644 --- a/applications/main/subghz/subghz_setting.c +++ b/applications/main/subghz/subghz_setting.c @@ -260,13 +260,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { break; } if(flipper_format_read_uint32(fff_data_file, "Default_frequency", &temp_data32, 1)) { - for - M_EACH(frequency, instance->frequencies, FrequencyList_t) { - *frequency &= FREQUENCY_MASK; - if(*frequency == temp_data32) { - *frequency |= FREQUENCY_FLAG_DEFAULT; - } - } + subghz_setting_set_default_frequency(instance, temp_data32); } // custom preset (optional) @@ -294,6 +288,16 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { } } +void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup) { + for + M_EACH(frequency, instance->frequencies, FrequencyList_t) { + *frequency &= FREQUENCY_MASK; + if(*frequency == frequency_to_setup) { + *frequency |= FREQUENCY_FLAG_DEFAULT; + } + } +} + size_t subghz_setting_get_frequency_count(SubGhzSetting* instance) { furi_assert(instance); return FrequencyList_size(instance->frequencies); @@ -311,6 +315,9 @@ size_t subghz_setting_get_preset_count(SubGhzSetting* instance) { const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) { furi_assert(instance); + if(idx >= SubGhzSettingCustomPresetItemArray_size(instance->preset->data)) { + idx = 0; + } SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); return string_get_cstr(item->custom_preset_name); diff --git a/applications/main/subghz/subghz_setting.h b/applications/main/subghz/subghz_setting.h index 0590cf499..d6fde1bd6 100644 --- a/applications/main/subghz/subghz_setting.h +++ b/applications/main/subghz/subghz_setting.h @@ -46,3 +46,5 @@ uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance); uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance); + +void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup); diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index b9360ea01..4afeffc5c 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -18,6 +18,44 @@ #define RSSI_SCALE 2 #define TRIGGER_STEP 1 +static const NotificationSequence sequence_hw_blink = { + &message_blink_start_10, + &message_blink_set_color_cyan, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence sequence_hw_blink_stop = { + &message_blink_stop, + NULL, +}; + +static const NotificationSequence sequence_saved = { + &message_blink_stop, + &message_blue_0, + &message_green_255, + &message_red_0, + &message_vibro_on, + &message_delay_100, + &message_vibro_off, + NULL, +}; +static const NotificationSequence sequence_not_saved = { + &message_blink_stop, + &message_green_255, + &message_blue_255, + &message_red_255, + NULL, +}; + +static const uint32_t subghz_frequency_list[] = { + 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, + 309000000, 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, + 314350000, 315000000, 318000000, 345000000, 348000000, 387000000, 390000000, + 418000000, 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, + 434176948, 434420000, 434775000, 438900000, 464000000, 779000000, 868350000, + 868400000, 868950000, 906400000, 915000000, 925000000, 928000000}; + typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, } SubGhzFrequencyAnalyzerStatus; @@ -36,6 +74,7 @@ struct SubGhzFrequencyAnalyzer { typedef struct { uint32_t frequency; uint32_t frequency_last; + uint32_t frequency_to_save; float rssi; float rssi_last; float trigger; @@ -133,6 +172,33 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel elements_button_right(canvas, "T+"); } +uint32_t subghz_frequency_find_correct(uint32_t input) { + uint32_t prev_freq = 0; + uint32_t current = 0; + uint32_t result = 0; +#if FURI_DEBUG + FURI_LOG_D(TAG, "input: %d", input); +#endif + for(size_t i = 0; i < sizeof(subghz_frequency_list); i++) { + current = subghz_frequency_list[i]; + if(current == input) { + result = current; + break; + } + if(current > input && prev_freq < input) { + if(current - input < input - prev_freq) { + result = current; + } else { + result = prev_freq; + } + break; + } + prev_freq = current; + } + + return result; +} + bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { furi_assert(context); SubGhzFrequencyAnalyzer* instance = context; @@ -161,6 +227,53 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { need_redraw = true; } + if(event->type == InputTypePress && event->key == InputKeyDown) { + if(instance->feedback_level == 0) { + instance->feedback_level = 2; + } else { + instance->feedback_level--; + } + FURI_LOG_D(TAG, "feedback_level = %d", instance->feedback_level); + need_redraw = true; + } + + if(event->type == InputTypeShort && event->key == InputKeyOk) { + with_view_model( + instance->view, (SubGhzFrequencyAnalyzerModel * model) { + uint32_t prev_freq_to_save = model->frequency_to_save; + uint32_t frequency_candidate = 0; + if(model->frequency != 0) { + frequency_candidate = model->frequency; + } else if(model->frequency_last != 0) { + frequency_candidate = model->frequency_last; + } + if(frequency_candidate == 0 || + !furi_hal_subghz_is_frequency_valid(frequency_candidate) || + prev_freq_to_save == frequency_candidate) { + frequency_candidate = 0; + } else { + frequency_candidate = subghz_frequency_find_correct(frequency_candidate); + } + if(frequency_candidate > 0 && frequency_candidate != model->frequency_to_save) { +#if FURI_DEBUG + FURI_LOG_D( + TAG, + "frequency_to_save: %d, candidate: %d", + model->frequency_to_save, + frequency_candidate); +#endif + model->frequency_to_save = frequency_candidate; + notification_message(instance->notifications, &sequence_saved); + instance->callback(SubGhzCustomEventViewReceiverOK, instance->context); + notification_message(instance->notifications, &sequence_hw_blink); + } else { + notification_message(instance->notifications, &sequence_not_saved); + notification_message(instance->notifications, &sequence_hw_blink); + } + return true; + }); + } + if(need_redraw) { SubGhzFrequencyAnalyzer* instance = context; with_view_model( @@ -256,6 +369,7 @@ void subghz_frequency_analyzer_enter(void* context) { model->rssi_last = 0; model->frequency = 0; model->frequency_last = 0; + model->frequency_to_save = 0; model->trigger = RSSI_MIN; return true; }); @@ -312,3 +426,15 @@ View* subghz_frequency_analyzer_get_view(SubGhzFrequencyAnalyzer* instance) { furi_assert(instance); return instance->view; } + +uint32_t subghz_frequency_analyzer_get_frequency_to_save(SubGhzFrequencyAnalyzer* instance) { + furi_assert(instance); + uint32_t frequency; + with_view_model( + instance->view, (SubGhzFrequencyAnalyzerModel * model) { + frequency = model->frequency_to_save; + return false; + }); + + return frequency; +} diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.h b/applications/main/subghz/views/subghz_frequency_analyzer.h index 3de003bf8..5e00c6444 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.h +++ b/applications/main/subghz/views/subghz_frequency_analyzer.h @@ -17,3 +17,5 @@ SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc(); void subghz_frequency_analyzer_free(SubGhzFrequencyAnalyzer* subghz_static); View* subghz_frequency_analyzer_get_view(SubGhzFrequencyAnalyzer* subghz_static); + +uint32_t subghz_frequency_analyzer_get_frequency_to_save(SubGhzFrequencyAnalyzer* instance); diff --git a/applications/plugins/subbrute/helpers/subbrute_worker.c b/applications/plugins/subbrute/helpers/subbrute_worker.c index 369ffd81c..80275698e 100644 --- a/applications/plugins/subbrute/helpers/subbrute_worker.c +++ b/applications/plugins/subbrute/helpers/subbrute_worker.c @@ -106,10 +106,10 @@ bool subbrute_worker_start( furi_hal_subghz_set_frequency_and_path(instance->frequency); furi_hal_subghz_flush_rx(); - if(furi_hal_subghz_is_tx_allowed(frequency)) { - instance->frequency = frequency; - res = true; - } + //if(furi_hal_subghz_is_tx_allowed(frequency)) { + instance->frequency = frequency; + res = true; + //} instance->worker_running = res; #ifdef FURI_DEBUG @@ -245,13 +245,13 @@ bool subbrute_worker_init_manual_transmit( furi_hal_subghz_set_frequency_and_path(instance->frequency); furi_hal_subghz_flush_rx(); - if(!furi_hal_subghz_is_tx_allowed(frequency)) { + /*if(!furi_hal_subghz_is_tx_allowed(frequency)) { FURI_LOG_E(TAG, "Frequency: %d invalid!", frequency); instance->frequency = frequency; instance->worker_manual_mode = false; return false; - } + }*/ #ifdef FURI_DEBUG FURI_LOG_I(TAG, "Frequency: %d", frequency); diff --git a/applications/plugins/subbrute/subbrute.c b/applications/plugins/subbrute/subbrute.c index 5bdacde63..f9c6e67dc 100644 --- a/applications/plugins/subbrute/subbrute.c +++ b/applications/plugins/subbrute/subbrute.c @@ -21,31 +21,31 @@ #define TAG "SubBruteApp" static const char* subbrute_menu_names[] = { - [SubBruteAttackCAME12bit307] = "CAME 12bit 307mhz", - [SubBruteAttackCAME12bit433] = "CAME 12bit 433mhz", - [SubBruteAttackCAME12bit868] = "CAME 12bit 868mhz", - [SubBruteAttackChamberlain9bit300] = "Chamberlain 9bit 300mhz", - [SubBruteAttackChamberlain9bit315] = "Chamberlain 9bit 315mhz", - [SubBruteAttackChamberlain9bit390] = "Chamberlain 9bit 390mhz", - [SubBruteAttackLinear10bit300] = "Linear 10bit 300mhz", - [SubBruteAttackLinear10bit310] = "Linear 10bit 310mhz", - [SubBruteAttackNICE12bit433] = "NICE 12bit 433mhz", - [SubBruteAttackNICE12bit868] = "NICE 12bit 868mhz", + [SubBruteAttackCAME12bit307] = "CAME 12bit 307MHz", + [SubBruteAttackCAME12bit433] = "CAME 12bit 433MHz", + [SubBruteAttackCAME12bit868] = "CAME 12bit 868MHz", + [SubBruteAttackNICE12bit433] = "NICE 12bit 433MHz", + [SubBruteAttackNICE12bit868] = "NICE 12bit 868MHz", + [SubBruteAttackChamberlain9bit300] = "Chamberlain 9bit 300MHz", + [SubBruteAttackChamberlain9bit315] = "Chamberlain 9bit 315MHz", + [SubBruteAttackChamberlain9bit390] = "Chamberlain 9bit 390MHz", + [SubBruteAttackLinear10bit300] = "Linear 10bit 300MHz", + [SubBruteAttackLinear10bit310] = "Linear 10bit 310MHz", [SubBruteAttackLoadFile] = "BF existing dump", [SubBruteAttackTotalCount] = "Total Count", }; static const char* subbrute_menu_names_small[] = { - [SubBruteAttackCAME12bit307] = "CAME 307mhz", - [SubBruteAttackCAME12bit433] = "CAME 433mhz", - [SubBruteAttackCAME12bit868] = "CAME 868mhz", - [SubBruteAttackChamberlain9bit300] = "Cham 300mhz", - [SubBruteAttackChamberlain9bit315] = "Cham 315mhz", - [SubBruteAttackChamberlain9bit390] = "Cham 390mhz", - [SubBruteAttackLinear10bit300] = "Linear 300mhz", - [SubBruteAttackLinear10bit310] = "Linear 310mhz", - [SubBruteAttackNICE12bit433] = "NICE 433mhz", - [SubBruteAttackNICE12bit868] = "NICE 868mhz", + [SubBruteAttackCAME12bit307] = "CAME 307MHz", + [SubBruteAttackCAME12bit433] = "CAME 433MHz", + [SubBruteAttackCAME12bit868] = "CAME 868MHz", + [SubBruteAttackNICE12bit433] = "NICE 433MHz", + [SubBruteAttackNICE12bit868] = "NICE 868MHz", + [SubBruteAttackChamberlain9bit300] = "Cham 300MHz", + [SubBruteAttackChamberlain9bit315] = "Cham 315MHz", + [SubBruteAttackChamberlain9bit390] = "Cham 390MHz", + [SubBruteAttackLinear10bit300] = "Linear 300MHz", + [SubBruteAttackLinear10bit310] = "Linear 310MHz", [SubBruteAttackLoadFile] = "Existing", [SubBruteAttackTotalCount] = "Total Count", }; diff --git a/applications/plugins/subbrute/subbrute_device.c b/applications/plugins/subbrute/subbrute_device.c index f1ca6962a..d5712d96b 100644 --- a/applications/plugins/subbrute/subbrute_device.c +++ b/applications/plugins/subbrute/subbrute_device.c @@ -307,19 +307,15 @@ SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBrute string_set_str(instance->preset_name, preset_ook650_async); break; case SubBruteAttackChamberlain9bit300: - instance->frequency = 300000000; - instance->bit = 9; - string_set_str(instance->protocol_name, protocol_cham_code); - string_set_str(instance->preset_name, preset_ook650_async); - break; case SubBruteAttackChamberlain9bit315: - instance->frequency = 315000000; - instance->bit = 9; - string_set_str(instance->protocol_name, protocol_cham_code); - string_set_str(instance->preset_name, preset_ook650_async); - break; case SubBruteAttackChamberlain9bit390: - instance->frequency = 390000000; + if(type == SubBruteAttackChamberlain9bit300) { + instance->frequency = 300000000; + } else if(type == SubBruteAttackChamberlain9bit315) { + instance->frequency = 315000000; + } else /* ALWAYS TRUE if(type == SubBruteAttackChamberlain9bit390) */ { + instance->frequency = 390000000; + } instance->bit = 9; string_set_str(instance->protocol_name, protocol_cham_code); string_set_str(instance->preset_name, preset_ook650_async); @@ -353,10 +349,10 @@ SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBrute return SubBruteFileResultProtocolNotFound; // RETURN } - if(!furi_hal_subghz_is_tx_allowed(instance->frequency)) { + /*if(!furi_hal_subghz_is_tx_allowed(instance->frequency)) { FURI_LOG_E(TAG, "Frequency invalid: %d", instance->frequency); return SubBruteFileResultMissingOrIncorrectFrequency; // RETURN - } + }*/ // For non-file types we didn't set SubGhzProtocolDecoderBase instance->receiver = subghz_receiver_alloc_init(instance->environment); diff --git a/applications/plugins/subbrute/subbrute_device.h b/applications/plugins/subbrute/subbrute_device.h index 5419c10de..46a1d2598 100644 --- a/applications/plugins/subbrute/subbrute_device.h +++ b/applications/plugins/subbrute/subbrute_device.h @@ -20,13 +20,13 @@ typedef enum { SubBruteAttackCAME12bit307, SubBruteAttackCAME12bit433, SubBruteAttackCAME12bit868, + SubBruteAttackNICE12bit433, + SubBruteAttackNICE12bit868, SubBruteAttackChamberlain9bit300, SubBruteAttackChamberlain9bit315, SubBruteAttackChamberlain9bit390, SubBruteAttackLinear10bit300, SubBruteAttackLinear10bit310, - SubBruteAttackNICE12bit433, - SubBruteAttackNICE12bit868, SubBruteAttackLoadFile, SubBruteAttackTotalCount, } SubBruteAttacks; diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 3f9a5f8a4..1ba36ab17 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,12 +1,18 @@ Filetype: IR library file Version: 1 -# Last Updated 17th Sept, 2022 +# Last Updated 27th Sept, 2022 # # ON name: POWER type: raw frequency: 38000 duty_cycle: 0.330000 +data: 3522 1701 472 426 444 1269 472 426 444 426 443 427 443 427 443 426 444 427 443 426 444 427 442 428 441 429 440 431 438 1304 437 433 437 433 438 433 437 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1305 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 459 411 459 411 459 411 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 459 411 459 411 1330 411 1330 411 460 410 1330 411 1330 411 1331 410 1330 411 74392 3516 1736 436 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 435 435 1305 436 435 435 435 435 1306 435 435 435 435 435 435 435 436 434 436 434 436 434 435 435 436 434 436 434 436 434 1330 411 1331 410 1330 411 1330 411 1330 411 459 411 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 435 436 434 436 1306 435 435 435 435 435 1306 435 435 435 435 435 435 435 435 435 435 435 436 434 436 434 435 435 436 434 435 435 1306 435 1330 411 1307 434 1331 410 1308 433 436 434 436 434 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 435 435 434 436 434 436 434 436 434 436 434 436 1306 435 435 435 435 435 435 435 1306 435 435 435 436 434 1306 435 435 435 436 434 436 434 435 435 436 434 436 434 460 410 460 410 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 434 436 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 435 435 435 435 434 436 1306 435 434 436 435 435 435 435 1306 435 436 434 435 435 1306 435 435 435 436 434 436 434 436 434 436 434 460 410 437 433 459 411 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3514 1736 437 434 436 1304 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 435 435 434 436 434 436 435 435 434 436 1305 436 435 435 435 435 435 435 1306 435 435 435 435 435 1306 435 435 435 436 434 435 435 459 411 436 434 435 435 459 411 459 411 459 411 459 411 1330 411 1306 435 1330 411 1330 411 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# ON +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 data: 529 7218 126 6585 219 703 180 5362 427 18618 177 # name: VOL+ @@ -410,4 +416,9 @@ type: parsed protocol: Samsung32 address: 07 00 00 00 command: 0F 00 00 00 -# +# OFF +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3523 1701 472 426 444 1269 472 426 444 426 442 429 443 427 443 426 444 426 444 426 443 427 442 429 440 430 439 432 438 1304 437 433 437 432 438 432 438 433 437 433 437 433 437 433 437 433 437 433 437 1304 437 433 437 433 437 433 437 1304 437 433 437 433 437 1304 437 433 437 434 436 433 437 434 436 434 436 434 436 433 437 433 437 434 436 1304 437 1305 436 1305 436 1305 436 1305 436 1305 436 434 436 434 436 1305 436 1305 436 1305 436 434 436 1305 436 1305 436 1306 435 1306 435 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 1304 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 436 434 435 435 1307 434 1331 410 1307 434 1307 434 1330 411 1307 434 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 434 436 433 437 433 437 1304 437 434 436 434 436 434 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 435 435 434 436 1305 436 434 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1306 435 1307 434 1307 434 1307 434 1331 410 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 1304 437 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 437 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410